Skip to main content
Known Participant
September 11, 2009
Question

Table problem using FDK

  • September 11, 2009
  • 1 reply
  • 1342 views

Hi there

I'm having an issue with tables using the FrameMaker 8 API.I'm trying to process a document which has about 7 or 8 tables in it. Part of what I do is delete all the strings in a Frame Document and replace them with their equivalents in another language. The deletion is performed by querying the text items in a paragraph, iterating through each of them, finding the FTI_String objects, seleting them, and clearing them. However, I'm finding that the API is also deleting some of the tables in the document.

The code I use to do string deletion is :

F_TextItemsT textItems = F_ApiGetText(m_hDocument, hParagraph, -1);

int nItem = (int)textItems.len;

while (textItems.len > 0 && nItem >= 0)
{
    F_TextItemT thisTextItem = textItems.val[nItem];

    if (thisTextItem.dataType == FTI_String)
    {
       StringT string = thisTextItem.u.sdata;          //get text item string
       CString szText = ConvertFromStringT(string);    //convert to CString (for debugger)
           
       F_TextRangeT range;
       range.beg.objId = hParagraph;
       range.end.objId = hParagraph;
      


              range.beg.offset = thisTextItem.offset;        //get start,end offsets of current text item

       int nTextLength = szText.GetLength();

       range.end.offset = thisTextItem.offset + nTextLength;
                   
       F_ApiSetTextRange(FV_SessionId,

                                                  m_hDocument,

                                                  FP_TextSelection,

                                                  &range);         // I only want to select strings, but some tables are selected too
      

          F_ApiClear(m_hDocument, 0);
    }

}

However, with some (but not all) of the tables, F_ApiSetTextRange is selecting entire tables as well, for reasons I can't understand. and deleting them.

I was wondering if it's a problem in my approach, or if it's a possible problem with the document. I did notice that it was originally authored in FrameMaker 7, but it's being parsed with the FDK8 but I'm assuming that shouldn't make a difference.

Thanks

Eric

    This topic has been closed for replies.

    1 reply

    Legend
    September 14, 2009

    Hi Eric,

    I haven't had do deal with this problem myself, but I have a few ideas.

    I can't see any problem in your code that would directly cause this... a table anchor resides somewhere in the previous paragraph, wherever the insertion point was when it was inserted. From a F_TextRange perspective, it occupies a single "space," ie, a single character space. To select it, you would use an offset range of 1, as if selecting a single character.

    In your code, when you set the offset for text selection, it seems like it should work. Somehow, though, your code must be adding an additional value to the end offset, causing an anchor to be selected. Or, something like that. Here are two ideas:

    - Instead of using the GetLength() method to calculate the end offset, consider using the offset directly from the next text item. For example:

    range.end.offset = textItems.val[nItem + 1].offset;

    ...of course you would need provisions in case you are actually at the last item, otherwise big crashes cometh.    I don't think that would happen in this case, though, because you should always at least have a paragraph mark at the end of the array to handle a string before it.

    - Rearchitect your documents so that all tables have a single, empty paragraph reserved for the respective anchor. This is how I set up all my documents, because tables behave so much better this way, for reasons other than API manipulation. I use a simple 2pt format and insert a single table into each one, wherever I need a table. By getting the anchors out of text-containing paragraphs, you could avoid this problem altogether.

    I hope this helps some. I wish I could tell you exactly why your code is not working right, but I can't really see it. Also, BTW, the use of the while loop seems a little strange... consider a for loop based on textItems.len instead.

    Good luck,

    Russ

    eric247Author
    Known Participant
    September 14, 2009

    Hi Russ

    Thanks very much for taking the time to reply

    I tried your first suggestion of calculating the current text item's end offset based on the next text item's start offset.

    It didn't solve my problem but it did point me to the unusual set of circumstances which I believe is causing my problem.

    I noticed that all of the tables being wiped were preceded by a paragraph containing a certain word which seems to use unusual characters or glyphs.

    The word looks like this:

    What seems to be happening is that the FAPI breaks this word into two separate text items which are:

    "411" and the strange character immediately preceding it

    &

    "cobas<weirdchar>e"

    So it should require two iterations to properly delete this word, using the loop I showed you before. However, when we perform the first iteration and try to select the first text item,  "411" and the strange character immediately preceding it (we're parsing in reverse remember ) F_ApiSetTextRange () actually selects the entire word, therefore causing  F_ApiClear () to clear the entire word.

    When we go to delete the next text item which should be "cobas<weirdchar>e", we try to select it, but it's already been deleted during the previous iteration so the FAPI actually selects the table after where the word we just deleted was, and wipes it.

    I'm wondering what this character is, if the FAPI is capable of handling them, or if there is a workaround in dealing with them

    Thanks

    Eric

    Legend
    September 14, 2009

    Hi Russ

    I resolved it!

    It seems that when I made the initial call to get the text items for that paragraph, that list remains static even after deletions are performed, so

    F_ApiSetTextRange was working with old information.

    I added a line of code at the bottom of my loop to keep the text item list up to date as deletions are performed. So even though the selection being performed still doesnt match the beginning and end offsets in the textrange for the current text item, in the next loop iteration, the API will not try and grab text it thinks exists but no longer does.

    So my deletion loop looks like this:

                //we go through all the paragraph text items
                //removing everything except crossreferences
               

                             nItem = (int)textItems.len;
               

                             while (textItems.len > 0 && nItem >= 0)
                {
                    F_TextItemT thisTextItem = textItems.val[nItem];

                    if (thisTextItem.dataType == FTI_String)
                    {
                        StringT string = thisTextItem.u.sdata;
                        CString szText = ConvertFromStringT(string);
               
                        F_TextRangeT range;
                        range.beg.objId = hParagraph;
                        range.end.objId = hParagraph;
                        range.beg.offset = thisTextItem.offset;

                        int nTextLength = szText.GetLength();

                        range.end.offset = thisTextItem.offset + nTextLength;
                       
                        F_ApiSetTextRange(FV_SessionId, m_hDocument, FP_TextSelection, &range);
                        F_ApiClear(m_hDocument, 0);
                    }
                    else if (thisTextItem.dataType == FTI_LineEnd)
                    {
                        if (thisTextItem.u.idata == FTI_HardLineEnd)
                        {
                            F_TextRangeT range;
                            range.beg.objId = hParagraph;
                            range.end.objId = hParagraph;
                            range.beg.offset = thisTextItem.offset;
                            range.end.offset = thisTextItem.offset+1;
                            F_ApiSetTextRange(FV_SessionId, m_hDocument, FP_TextSelection, &range);
                            F_ApiClear(m_hDocument, 0);
                        }
                    }
                  // Skip cross refs i.e. leave them in place,
                  // we will cut them out and replace them later when we find the tag
                  else if (thisTextItem.dataType == FTI_XRefEnd)
                    {
                        while (thisTextItem.dataType != FTI_XRefBegin && nItem >= 0)
                        {
                            nItem--;
                            thisTextItem = textItems.val[nItem];
                        }
                    }
                    /* update text item list to reflect deletions we just made*/
                    textItems = F_ApiGetText(m_hDocument, hParagraph, -1);
                    nItem--;
                }

    Hopefully this is a robust solution! Those mystery characters in the string I discussed in my previous post are device control characters, with a value of 0x11. I'm not too sure of their purpose, but I don't need to worry about them now anyway it seems.

    Thanks

    Eric


    Ah yes, that makes sense. I should have thought of that, since you are making multiple changes in the same span of text. Indeed, that textItems list is just a statically-populated data structure... it has no dynamic link with the original document after it is allocated.

    One more thing... you probably know what you are doing here, but I'd like to add a cautionary note about copying pointers, such as in:

    StringT string = thisTextItem.u.sdata;

    If you aren't careful with these, it is super-easy to get crashes and/or memory leaks. In my personal work, I would either just use the array member itself, or copy the item and deallocate it when done. Such as:

    strBuf = F_StrCopyString(thisTextItem.u.sdata);

    . . .

    F_ApiDeallocateString(&strBuf);

    Having said that, I'm not a developer by trade, so it's certainly probable that your methodology is fine for someone who knows what they are doing.

    Russ