2

I need to duplicate a table row in a RTF file. I am struggling to understand the definition of what I need to copy and duplicate.

My process at the moment is as follows:

  1. I search in the RTF data for a string that I know is in a table cell. In this case <<[QL]

  2. This row has two cells.

  3. I then search backwards from this to find the \trowd control character

  4. From this point I search backwards again to find the start of the group i.e. “{“

  5. Then from my tag <<[QL>> I search for the end of the row. The \row control character

  6. From this point I search for the closing “}” of the group

  7. I then copy this string as the row template

  8. I then create another string by appending by duplicating the row template , for each appending I change \irowN and \irowbandN with the next number i.e. irow1

  9. I also check if there is a control character \lastrow if it is not the last row I remove this.

  10. I now expect this string to have four rows of data.

  11. I duplicate these by replacing the row template with 4 of these i.e. appending this row template 4 times

  12. I then write back the file contents.

When I open the RTF file I get an error and the repair tool indicates an error of “Table end-of-cell-markers”

My row template looks like this :

"{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 \trowd \irow0\irowband0\ltrrow\ts15\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr
\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10 \trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid16582897\tbllkhdrrows\tbllkhdrcols\tbllknocolband\tblind0\tblindtype3 
\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth5057\clshdrawnil \cellx4949\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr
\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth4185\clshdrawnil \cellx9134\row \ltrrow}\pard\plain \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\yts15 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 
\f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 <<[QL]itemDec>>\cell }\pard \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\pararsid16582897\yts15 {
\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 <<[QL]item}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 Qty}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 >>}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 \cell }\pard\plain \ltrpar
\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 
\insrsid16582897 \trowd \irow1\irowband1\lastrow \ltrrow\ts15\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10 
\trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid16582897\tbllkhdrrows\tbllkhdrcols\tbllknocolband\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb
\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth5057\clshdrawnil \cellx4949\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth4185\clshdrawnil \cellx9134
\row }"

My duplicated rows now look like this :

" {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 \trowd \irow0\irowband0\ltrrow\ts15\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr
\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10 \trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid16582897\tbllkhdrrows\tbllkhdrcols\tbllknocolband\tblind0\tblindtype3 
\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth5057\clshdrawnil \cellx4949\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr
\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth4185\clshdrawnil \cellx9134\row \ltrrow}\pard\plain \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\yts15 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 
\f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 <<[QL]_0itemDec>>\cell }\pard \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\pararsid16582897\yts15 {
\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 <<[QL]_0item}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 Qty}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 >>}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 \cell }\pard\plain \ltrpar
\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 
\insrsid16582897 \trowd \irow1\irowband1 \ltrrow\ts15\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10 
\trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid16582897\tbllkhdrrows\tbllkhdrcols\tbllknocolband\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb
\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth5057\clshdrawnil \cellx4949\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth4185\clshdrawnil \cellx9134
\row } {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 \trowd \irow0\irowband0\ltrrow\ts15\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr
\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10 \trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid16582897\tbllkhdrrows\tbllkhdrcols\tbllknocolband\tblind0\tblindtype3 
\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth5057\clshdrawnil \cellx4949\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr
\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth4185\clshdrawnil \cellx9134\row \ltrrow}\pard\plain \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\yts15 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 
\f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 <<[QL]_1itemDec>>\cell }\pard \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\pararsid16582897\yts15 {
\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 <<[QL]_1item}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 Qty}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 >>}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16582897 \cell }\pard\plain \ltrpar
\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 
\insrsid16582897 \trowd \irow1\irowband1\lastrow \ltrrow\ts15\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10 
\trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid16582897\tbllkhdrrows\tbllkhdrcols\tbllknocolband\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb
\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth5057\clshdrawnil \cellx4949\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth4185\clshdrawnil \cellx9134
\row }"

My specific questions are

  1. Is this the correct way to identify a row of data in RTF

  2. When I duplicate do I need something else between the rows? If I look a the source of a RTF file is see some \pard data but even putting this in does not help

  3. Any idea why this is invalid RTF?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129

2 Answers2

0

Take a look at this:

https://stackoverflow.com/a/13321729/1543816

A row is delimted with \trowd ... \row

Community
  • 1
  • 1
Jerry
  • 4,258
  • 3
  • 31
  • 58
  • Thanks, yes I take this into account and so in my example it should be valid, but it is not. It has something todo with invalid end-of-cell-markers – Scott Nettleton Nov 08 '13 at 09:20
0

Maybe a little late, but since I came into a similar problem and I managed to somewhat solve it I'd like to share. While writing a piece of application that writes reports from a rtf template I came into this rtf tables nightmare.

BTW I'm using the nrtftree library to open the rtf documents, it provides nice DOM/SAX like methods to manipulate rtf files.

I needed to duplicate and fill rows inside a table, so the idea was to search for a specific placeholder in the first row, then scan left for a "trowd" tag and right for a "row" command, then clone every node between those 2 and then append the cloned ones after the last "row" tag. The code using nrtftree looks like this:

            //PlaceHolderNode is the text node containing the placeholder text.

            clonedRowNodes = new RtfNodeCollection();
            placeHolder    = nodes[0].ParentNode;
            trowd          = placeHolder;

            //Scanning left until a trowd is found
            while (trowd.NodeKey != "trowd")
            {
                trowd = trowd.PreviousSibling;
            }

            //Scanning right from trowd for a row tag and adding in a list the clones of every node that is inside the row
            for (row = trowd; row.PreviousSibling.NodeKey != "row"; row = row.NextSibling)
            {
                clonedRowNodes.Add(row.CloneNode());
            }

            //Do something here with the cloned nodes..

            //Inserting the cloned row right after the previous one
            for (int i = 0; i < rowNodes.Count; ++i)
            {
                row.ParentNode.InsertChild(row.Index + i + 1, clonedRowNodes[i]);
            }

Using this code the template i created wasn't correctly edited and sometimes the application crashed too not finding the corresponding trowd or row tags.

I then opened the rtf file with a text editor and counted the occurrences of trowd and row and found out that not only I had 28 trowd and 18 row but they weren't even opening and closing eachother and that's a little odd since RTF specification 1.9.1 states at page 93 that a table row starts with trowd and ends with row. At this point I may be missing something written in the specification. You can read it here. You can easly verify this behaviour creating a 2 rows by 3 columns table inside Word and saving it as RTF, the trowd count should be 3 and rows count should be 2 and the sequence would look somethign like this:

\trowd ... \trowd [cell 1 content] [cell 2 content] [cell 3 content] \row .. \trowd [cell 4 content] [cell 5 content] [cell 6 content] \row

This text block is what is inside the RTF created by Word.

Long story short: it appears that Word uses a different way to delimit table rows (I'm still trying to know what it exactly uses). I managed to make my code work editing and saving the template with Open/LibreOffice (even Wordpad respects the trowd/row delimiting).

MastErAldo
  • 634
  • 3
  • 12
  • 29