Strange behaviour of 'for RichText in string' loop.

I've been experimenting with some code that is based on the following forum post:

https://www.ibm.com/developerworks/community/forums/html/topic?id=4725bcb5-ac1a-4ddc-85b2-77835afcef9e&ps=25

I've encountered a strange problem when processing a chunk of richtext that represents an embedded OLE object. The perm bool isRichText(string s) returns true, but if I then process the string using a for RichText rtf in string s do loop, the loop does not encounter any RichText.

Here is my code:

pragma runLim, 0

Regexp reObj = regexp2("({\\\\object)|({\\\\pict)");
Regexp reBrace = regexp2("[{}]");

void exportObject(OleAutoObj doc, OleAutoObj sel, Object o)
{
  Buffer b = create();
  b = richTextWithOle o."Object Text"   
  
  /*
  print(identifier(o) "\n");
  print(tempStringOf(b));
  print("------------\n");
  */
  
  int pos = 0;
  int iLen = length(b);
  int oleStart = 0;
  
  Buffer fragment = create();

  // Look for the start of an OLE object or picture
  while (search(reObj, b, pos)) 
  {
    oleStart = pos + start(0);
         
    // add to the result the part before the OLE object
    if (oleStart > pos)
    {
      setempty(fragment);
      combine(fragment, b, pos, oleStart-1);
      setRichClip(fragment);
      oleMethod(sel, "Paste");
    }

    // look for the ending brace of the OLE object
    int braceLevel = 1; // the start of the OLE had one opening brace
    int bracePos = oleStart+1;
    while (braceLevel > 0 && search(reBrace, b,bracePos)) 
    {
      char ch = b[bracePos + start 0];
      braceLevel += (ch == '{') ? 1 : (-1);
      bracePos += 1 + end 0
    }
    
    if (braceLevel != 0) 
    {
      error "Invalid RTF!";
    }
    else
    {      
      print "Found OLE object from " oleStart " to " bracePos "\n";
      
      setempty(fragment);
      combine(fragment, b, oleStart, bracePos);
    
      string s = tempStringOf(fragment);
      bool isRtf = isRichText(s);
      if (isRtf)
      {
        print("Fragment is RichText\n");
      }
    
      RichText rtf;
      for rtf in s do
      {
        print("Got RichText chunk.\n");
        if (rtf.isOle)
        {
          EmbeddedOleObject ole = rtf.getEmbeddedOle;
          print("Got EmbeddedOleObject\n");
          oleCopy(ole);
          oleMethod(sel, "Paste");
        }
      }
    }
    
    pos = bracePos;
    
       
    // make sure you use an RTF group ({...}) here!
    // you might need to add a space otherwise
  }

  setempty(fragment);
  combine (fragment, b, pos); // add the rest...
  setRichClip(fragment);
  oleMethod(sel, "Paste");    

  delete (fragment);
  delete (b);
}

void wordExport()
{  
  OleAutoObj word = oleCreateAutoObject "Word.Application";
  olePut(word, "Visible", true);
  if (null word) 
  {
    error("Please start word ...");
  }

  OleAutoObj docs, doc, sel, shps, shp
  oleGet(word, "Documents", docs);
  oleMethod(docs, "Add");
  oleGet(word, "ActiveDocument", doc);
  oleGet(word, "Selection", sel);

  Object obj;
  for obj in current Module do
  {
    exportObject(doc, sel, obj);
  }
}

wordExport();
delete (reObj);
delete (reBrace);



  

 


TheNybbler - Mon Mar 18 11:23:28 EDT 2019

Re: Strange behaviour of 'for RichText in string' loop.
llandale - Thu Mar 21 17:51:11 EDT 2019

I think you are saying you get a print at line #62 but not at #68.  Don't know.  The only think I can think of is that the "tempStringOf" does something I don't understand.  Try "stringOf" at line #58 and see if that resolves it.  I only ever use "tempStringOf" when I immediately "dispose" of the string (print it, set a dialog element, export, set to attribute).  I suppose it possible the richText commands don't work on strings that are still stored in a buffer, and not stored in the string table.

I wonder, though, if your main loop should not be "for rt in b do"; get the embedded OLE, put it in a buffer using the "oleRTF" command; then look to see if it is the kind you want (object or pict) before exporting it.

I do notice a deficiency: IIRC from years ago, "setempty(buf)" doesn't work quite right if the buffer contains any characters of value 00 (the null character).  I now have all my "setempty" commands followed by a buf = ""; which seemed to resolve the problem the few times it surfaced.  Normal string buffer use won't have any null characters, but OLE diagrams are not ascii and indeed contain lots of character zero.

-Louie

Re: Strange behaviour of 'for RichText in string' loop.
TheNybbler - Mon Mar 25 04:44:26 EDT 2019

llandale - Thu Mar 21 17:51:11 EDT 2019

I think you are saying you get a print at line #62 but not at #68.  Don't know.  The only think I can think of is that the "tempStringOf" does something I don't understand.  Try "stringOf" at line #58 and see if that resolves it.  I only ever use "tempStringOf" when I immediately "dispose" of the string (print it, set a dialog element, export, set to attribute).  I suppose it possible the richText commands don't work on strings that are still stored in a buffer, and not stored in the string table.

I wonder, though, if your main loop should not be "for rt in b do"; get the embedded OLE, put it in a buffer using the "oleRTF" command; then look to see if it is the kind you want (object or pict) before exporting it.

I do notice a deficiency: IIRC from years ago, "setempty(buf)" doesn't work quite right if the buffer contains any characters of value 00 (the null character).  I now have all my "setempty" commands followed by a buf = ""; which seemed to resolve the problem the few times it surfaced.  Normal string buffer use won't have any null characters, but OLE diagrams are not ascii and indeed contain lots of character zero.

-Louie

Yes, I am saying that the script prints out the line #62 but not #68.

I have tried updating the script to set fragment="" immediately after each setempty(fragment) call. I have also tried replacing tempStringOf with stringOf, but the behaviour does not change.

Maybe it would help if I explain what I'm trying to achieve in case you can think of a better way of doing this...

I'm trying to export DOORS objects to MS Word that contain one or more OLE objects. I wish to retain the rich text formatting and the positioning of the embedded OLE objects within the text. I also want to be able to interrogate the OLE object being exported to determine if it should be embedded into the MS Word document as an icon, or as a full size OLE. 

 

 

Re: Strange behaviour of 'for RichText in string' loop.
morast - Tue Mar 26 12:33:32 EDT 2019

TheNybbler - Mon Mar 25 04:44:26 EDT 2019

Yes, I am saying that the script prints out the line #62 but not #68.

I have tried updating the script to set fragment="" immediately after each setempty(fragment) call. I have also tried replacing tempStringOf with stringOf, but the behaviour does not change.

Maybe it would help if I explain what I'm trying to achieve in case you can think of a better way of doing this...

I'm trying to export DOORS objects to MS Word that contain one or more OLE objects. I wish to retain the rich text formatting and the positioning of the embedded OLE objects within the text. I also want to be able to interrogate the OLE object being exported to determine if it should be embedded into the MS Word document as an icon, or as a full size OLE. 

 

 

The code below works.

Please note I have not cleaned up or done anything clever to optimize.

Only a minimal modification of your code to prove Louie's point regarding using the "for rt in b do" loop. Had to add a couple of counters to cope with multiple OLEs in an object.

pragma runLim, 0

Regexp reObj = regexp2("({\\\\object)|({\\\\pict)");
Regexp reBrace = regexp2("[{}]");

void exportObject(OleAutoObj doc, OleAutoObj sel, Object o)
{
  Buffer b = create();
  b = richTextWithOle o."Object Text"   
  
  /*
  print(identifier(o) "\n");
  print(tempStringOf(b));
  print("------------\n");
  */
  
  int pos = 0;
  int iLen = length(b);
  int oleStart = 0;
        int thisOle = 0
  
  Buffer fragment = create();

  // Look for the start of an OLE object or picture
  while (search(reObj, b, pos)) 
  {
          thisOle++
    oleStart = pos + start(0);
         
    // add to the result the part before the OLE object
    if (oleStart > pos)
    {
      setempty(fragment);
      combine(fragment, b, pos, oleStart-1);
      setRichClip(fragment);
      oleMethod(sel, "Paste");
    }

    // look for the ending brace of the OLE object
    int braceLevel = 1; // the start of the OLE had one opening brace
    int bracePos = oleStart+1;
    while (braceLevel > 0 && search(reBrace, b,bracePos)) 
    {
      char ch = b[bracePos + start 0];
      braceLevel += (ch == '{') ? 1 : (-1);
      bracePos += 1 + end 0
    }
    
    if (braceLevel != 0) 
    {
      error "Invalid RTF!";
    }
    else
    {      
      print "Found OLE object from " oleStart " to " bracePos "\n";
      
      setempty(fragment);
      combine(fragment, b, oleStart, bracePos);
    
      string s = tempStringOf(fragment);
      bool isRtf = isRichText(s);
      if (isRtf)
      {
        print("Fragment is RichText\n");
      }
    
      RichText rtf;
                        int countOle = 0
      for rtf in tempStringOf(b) do
      {
        print("Got RichText chunk.\n");
        if (rtf.isOle)
        {
                                  countOle++
                                        if ( countOle == thisOle )
                                        {
                                                EmbeddedOleObject ole = rtf.getEmbeddedOle;
                                                print("Got EmbeddedOleObject\n");
                                                oleCopy(ole);
                                                oleMethod(sel, "Paste");
                                  }
        }
      }
    }
    
    pos = bracePos;
    
       
    // make sure you use an RTF group ({...}) here!
    // you might need to add a space otherwise
  }

  setempty(fragment);
  combine (fragment, b, pos); // add the rest...
  setRichClip(fragment);
  oleMethod(sel, "Paste");    

  delete (fragment);
  delete (b);
}

void wordExport()
{  
  OleAutoObj word = oleCreateAutoObject "Word.Application";
  olePut(word, "Visible", true);
  if (null word) 
  {
    error("Please start word ...");
  }

  OleAutoObj docs, doc, sel, shps, shp
  oleGet(word, "Documents", docs);
  oleMethod(docs, "Add");
  oleGet(word, "ActiveDocument", doc);
  oleGet(word, "Selection", sel);

  Object obj;
  for obj in current Module do
  {
    exportObject(doc, sel, obj);
  }
}

wordExport();
delete (reObj);
delete (reBrace);