1 /************************************************************************/
2 /* */
3 /* Save a BufferDocument into an RTF file. */
4 /* */
5 /************************************************************************/
6
7 # include "docRtfConfig.h"
8
9 # include <stdio.h>
10 # include <ctype.h>
11
12 # include <appDebugon.h>
13
14 # include <docTreeType.h>
15 # include <docTreeScanner.h>
16 # include <docTreeNode.h>
17 # include <docNodeTree.h>
18
19 # include "docRtfWriterImpl.h"
20 # include "docRtfTags.h"
21 # include "docRtfFlags.h"
22
23 /************************************************************************/
24
docRtfSavePropertiesOfRow(RtfWriter * rw,const RowProperties * rpSet,const DocumentSelection * ds)25 static int docRtfSavePropertiesOfRow(
26 RtfWriter * rw,
27 const RowProperties * rpSet,
28 const DocumentSelection * ds )
29 {
30 int rval= 0;
31 int col0= -1;
32 int col1= -1;
33
34 RowProperties rpRef;
35 PropertyMask rpDifMask;
36 PropertyMask rpAllMask;
37
38 const DocumentAttributeMap * const dam0= (const DocumentAttributeMap *)0;
39
40 docInitRowProperties( &rpRef );
41 utilPropMaskClear( &rpDifMask );
42 utilPropMaskClear( &rpAllMask );
43
44 utilPropMaskFill( &rpAllMask, RPprop_FULL_COUNT );
45
46 if ( ds )
47 { col0= ds->dsCol0; col1= ds->dsCol1; }
48
49 docRowPropertyDifference( &rpDifMask, &rpRef, &rpAllMask, rpSet, dam0 );
50
51 if ( rw->rwRpExtraMask )
52 { utilPropMaskOr( &rpDifMask, &rpDifMask, rw->rwRpExtraMask ); }
53
54 docRtfWriteTag( rw, "trowd" );
55
56 docRtfSaveRowProperties( rw, &rpDifMask, rpSet, col0, col1 );
57
58 if ( docCopyRowProperties( &(rw->rwRowProperties), rpSet, dam0 ) )
59 { LDEB(rpSet->rpCellCount); rval= -1; goto ready; }
60
61 ready:
62
63 docCleanRowProperties( &rpRef );
64
65 return rval;
66 }
67
68 /************************************************************************/
69 /* */
70 /* Enter/Leave a table. */
71 /* */
72 /************************************************************************/
73
docRtfPushTable(RtfWriter * rw,const BufferItem * rowNode,const DocumentSelection * ds)74 static int docRtfPushTable( RtfWriter * rw,
75 const BufferItem * rowNode,
76 const DocumentSelection * ds )
77 {
78 PropertyMask ppSetMask;
79
80 docRtfWriteNextLine( rw );
81 docRtfWriteDestinationBegin( rw, "" );
82
83 rw->rwTableNesting++;
84
85 if ( docRtfPushAttribute( rw ) )
86 { LDEB(1); return -1; }
87
88 utilPropMaskClear( &ppSetMask );
89 utilPropMaskFill( &ppSetMask, PPprop_FULL_COUNT );
90
91 if ( docUpdParaProperties( (PropertyMask *)0,
92 &(rw->rwcOutsideTableParagraphProperties),
93 &ppSetMask, &(rw->rwcParagraphProperties),
94 (const DocumentAttributeMap *)0 ) )
95 { LDEB(1); return -1; }
96
97 docRtfSavePropertiesOfRow( rw, &(rowNode->biRowProperties), ds );
98
99 return 0;
100 }
101
docRtfPopTable(RtfWriter * rw)102 static int docRtfPopTable( RtfWriter * rw )
103 {
104 PropertyMask ppSetMask;
105
106 docRtfWriteDestinationEnd( rw );
107 docRtfWriteNextLine( rw );
108
109 rw->rwTableNesting--;
110
111 docCleanRowProperties( &(rw->rwRowProperties) );
112 docInitRowProperties( &(rw->rwRowProperties) );
113
114 if ( docRtfPopAttribute( rw ) )
115 { LDEB(1); return -1; }
116
117 utilPropMaskClear( &ppSetMask );
118 utilPropMaskFill( &ppSetMask, PPprop_FULL_COUNT );
119
120 if ( docUpdParaProperties( (PropertyMask *)0,
121 &(rw->rwcParagraphProperties), &ppSetMask,
122 &(rw->rwcOutsideTableParagraphProperties),
123 (const DocumentAttributeMap *)0 ) )
124 { LDEB(1); return -1; }
125
126 return 0;
127 }
128
129 /************************************************************************/
130 /* */
131 /* Save a Row level node. */
132 /* */
133 /************************************************************************/
134
docRtfEnterRowNode(RtfWriter * rw,const BufferItem * rowNode,const DocumentSelection * ds)135 static int docRtfEnterRowNode( RtfWriter * rw,
136 const BufferItem * rowNode,
137 const DocumentSelection * ds )
138 {
139 int tableNesting= docTableNesting( rowNode );
140
141 int flattenCell= 0;
142
143 if ( ! ( rw->rwSaveFlags & RTFflagEMIT_CELL ) )
144 {
145 if ( ds && docPositionsInsideCell( &(ds->dsHead), &(ds->dsTail) ) )
146 { flattenCell= 1; }
147 }
148
149 if ( tableNesting == 1 && ! flattenCell )
150 {
151 PropertyMask rpDifMask;
152 PropertyMask rpAllMask;
153
154 const DocumentAttributeMap * const dam0= (const DocumentAttributeMap *)0;
155
156 utilPropMaskClear( &rpDifMask );
157 utilPropMaskClear( &rpAllMask );
158
159 utilPropMaskFill( &rpAllMask, RPprop_FULL_COUNT );
160
161 docRowPropertyDifference( &rpDifMask, &(rw->rwRowProperties),
162 &rpAllMask, &(rowNode->biRowProperties), dam0 );
163
164 if ( ! utilPropMaskIsEmpty( &rpDifMask ) )
165 {
166 if ( rw->rwTableNesting == 1 && docRtfPopTable( rw ) )
167 { LDEB(1); }
168 }
169
170 if ( rw->rwTableNesting == 0 &&
171 docRtfPushTable( rw, rowNode, ds ) )
172 { LDEB(1); }
173 }
174
175 return ADVICEtsOK;
176 }
177
docRtfLeaveRowNode(RtfWriter * rw,const BufferItem * rowNode,const DocumentSelection * ds)178 static int docRtfLeaveRowNode( RtfWriter * rw,
179 const BufferItem * rowNode,
180 const DocumentSelection * ds )
181 {
182 int tableNesting= docTableNesting( rowNode );
183
184 if ( tableNesting > 1 )
185 {
186 int nip= rowNode->biNumberInParent;
187 const BufferItem * cellBi= rowNode->biParent;
188
189 docRtfWriteNextLine( rw );
190 docRtfWriteDestinationBegin( rw, "*\\nesttableprops" );
191
192 docRtfSavePropertiesOfRow( rw, &(rowNode->biRowProperties), ds );
193
194 docRtfWriteTag( rw, "nestrow" );
195 docRtfWriteNextLine( rw );
196
197 docRtfWriteDestinationEnd( rw );
198 docRtfWriteNextLine( rw );
199
200 if ( nip == cellBi->biChildCount- 1 ||
201 ( nip < cellBi->biChildCount- 1 &&
202 cellBi->biChildren[nip+1]->biLevel != DOClevROW ) )
203 {
204 docRtfSavePropertiesOfRow( rw,
205 &(cellBi->biParent->biRowProperties), ds );
206 }
207 }
208 else{
209 docRtfWriteTag( rw, "row" );
210 docRtfWriteNextLine( rw );
211 }
212
213 return 0;
214 }
215
216 /************************************************************************/
217 /* */
218 /* Start a paragraph node. */
219 /* */
220 /* 0) While saving a selection.. Make sure that section properties */
221 /* are saved before the first paragraph in the document. */
222 /* 1) When we are saving a selection, and the selection is inside a */
223 /* table cell, do not set the \intbl flag. This is only activated */
224 /* for copy/paste where it is essential and for undo/redo where it */
225 /* is irrelevant. */
226 /* 2) To make WP 8 happy, always save 'intbl' for the first paragraph */
227 /* in a table row. */
228 /* */
229 /************************************************************************/
230
docRtfEnterParaNode(RtfWriter * rw,const BufferItem * paraNode,const DocumentSelection * ds)231 static int docRtfEnterParaNode( RtfWriter * rw,
232 const BufferItem * paraNode,
233 const DocumentSelection * ds )
234 {
235 int flattenCell= 0;
236 int firstInRow= 0;
237
238 /* 0 */
239 if ( ds &&
240 paraNode->biTreeType == DOCinBODY &&
241 ! rw->rwSectionPropertiesSaved )
242 {
243 const BufferItem * sectNode;
244
245 sectNode= docGetSectNode( (BufferItem *)paraNode );
246 if ( ! sectNode )
247 { XDEB(sectNode); return -1; }
248
249 if ( docRtfSaveSectionPropertiesOfNode( rw, ds, sectNode ) )
250 { LDEB(1); return -1; }
251 }
252
253 /* 1 */
254 if ( ! ( rw->rwSaveFlags & RTFflagEMIT_CELL ) )
255 {
256 if ( ds && docPositionsInsideCell( &(ds->dsHead), &(ds->dsTail) ) )
257 { flattenCell= 1; }
258 }
259 else{
260 if ( paraNode->biParaTableNesting > 0 &&
261 ds &&
262 docPositionsInsideCell( &(ds->dsHead), &(ds->dsTail) ) )
263 {
264 const BufferItem * rowNode;
265
266 rowNode= docGetRowNode( (BufferItem *)paraNode );
267 if ( ! rowNode )
268 { XDEB(rowNode); return -1; }
269
270 if ( rw->rwTableNesting == 0 &&
271 docRtfPushTable( rw, rowNode, ds ) )
272 { LDEB(1); }
273 }
274 }
275
276 if ( paraNode->biParaTableNesting > 0 )
277 {
278 BufferItem * rowNode;
279 DocumentPosition dpFirst;
280
281 rowNode= docGetRowNode( (BufferItem *)paraNode );
282 if ( ! rowNode ||
283 docHeadPosition( &dpFirst, rowNode ) )
284 { XDEB(rowNode); }
285 else{ firstInRow= paraNode == dpFirst.dpNode; }
286 }
287
288 if ( docRtfSaveParaNode( rw, paraNode, ds, flattenCell, firstInRow ) )
289 { LDEB(1); return -1; }
290
291 return 0;
292 }
293
294 /************************************************************************/
295 /* */
296 /* Start an arbitrary node. */
297 /* */
298 /************************************************************************/
299
docRtfEnterNode(BufferItem * node,const DocumentSelection * ds,const BufferItem * bodySectNode,void * voidrw)300 static int docRtfEnterNode( BufferItem * node,
301 const DocumentSelection * ds,
302 const BufferItem * bodySectNode,
303 void * voidrw )
304 {
305 RtfWriter * rw= (RtfWriter *)voidrw;
306
307 switch( node->biLevel )
308 {
309 case DOClevBODY:
310 case DOClevCELL:
311 break;
312
313 case DOClevSECT:
314
315 if ( node->biTreeType == DOCinBODY &&
316 docRtfSaveSectionPropertiesOfNode( rw, ds, node ) )
317 { LDEB(1); return -1; }
318
319 if ( ! ds &&
320 node->biTreeType == DOCinBODY &&
321 docRtfSaveSectHeadersFooters( rw, node ) )
322 { LDEB(1); return -1; }
323 break;
324
325 case DOClevROW:
326 if ( docIsRowNode( node ) )
327 {
328 if ( docRtfEnterRowNode( rw, node, ds ) )
329 { LDEB(1); return -1; }
330 }
331 else{
332 if ( rw->rwTableNesting > 0 && docRtfPopTable( rw ) )
333 { LDEB(rw->rwTableNesting); }
334 }
335 break;
336
337 case DOClevPARA:
338 if ( docRtfEnterParaNode( rw, node, ds ) )
339 { LDEB(1); return -1; }
340 break;
341
342 default:
343 LDEB(node->biLevel); return -1;
344 }
345
346 return 0;
347 }
348
docRtfLeaveNode(BufferItem * node,const DocumentSelection * ds,const BufferItem * bodySectNode,void * voidrw)349 static int docRtfLeaveNode( BufferItem * node,
350 const DocumentSelection * ds,
351 const BufferItem * bodySectNode,
352 void * voidrw )
353 {
354 RtfWriter * rw= (RtfWriter *)voidrw;
355
356 switch( node->biLevel )
357 {
358 BufferItem * parentNode;
359
360 case DOClevBODY:
361 break;
362
363 case DOClevCELL:
364 parentNode= node->biParent;
365
366 if ( docIsRowNode( parentNode ) )
367 {
368 int tableNesting= docTableNesting( parentNode );
369
370 if ( rw->rwcLastNodeLevel != DOClevPARA )
371 { docRtfSaveParaTableNesting( rw, tableNesting ); }
372
373 if ( tableNesting > 1 )
374 { docRtfWriteTag( rw, "nestcell" ); }
375 else{ docRtfWriteTag( rw, "cell" ); }
376
377 if ( node->biNumberInParent != parentNode->biChildCount- 1 )
378 { docRtfWriteNextLine( rw ); }
379 }
380 break;
381
382 case DOClevSECT:
383 while( rw->rwTableNesting > 0 )
384 {
385 if ( docRtfPopTable( rw ) )
386 { LDEB(rw->rwTableNesting); }
387 }
388
389 if ( node->biParent &&
390 node->biNumberInParent < node->biParent->biChildCount- 1 )
391 { docRtfWriteTag( rw, "sect" ); }
392
393 docRtfWriteNextLine( rw );
394 break;
395
396 case DOClevROW:
397 if ( docIsRowNode( node ) )
398 {
399 if ( docRtfLeaveRowNode( rw, node, ds ) )
400 { LDEB(1); return -1; }
401 }
402 break;
403
404 case DOClevPARA:
405 break;
406
407 default:
408 LDEB(node->biLevel); return -1;
409 }
410
411 rw->rwcLastNodeLevel= node->biLevel;
412 return 0;
413 }
414
415 /************************************************************************/
416 /* */
417 /* Save an inidividual header, footer, or footnote. */
418 /* */
419 /************************************************************************/
420
docRtfSaveDocumentTree(RtfWriter * rw,const char * tag,const DocumentTree * dt,int evenIfAbsent,int forcePar)421 int docRtfSaveDocumentTree( RtfWriter * rw,
422 const char * tag,
423 const DocumentTree * dt,
424 int evenIfAbsent,
425 int forcePar )
426 {
427 int savedTableNesting= rw->rwTableNesting;
428 const int flags= FLAGtsSCAN_MERGED_CELLS;
429
430 if ( ! dt->dtRoot )
431 {
432 if ( evenIfAbsent )
433 {
434 docRtfWriteDestinationBegin( rw, tag );
435 docRtfWriteDestinationEnd( rw );
436 }
437
438 return 0;
439 }
440
441 if ( dt->dtRoot->biLevel != DOClevSECT )
442 { SDEB(docLevelStr(dt->dtRoot->biLevel)); return -1; }
443
444 docRtfWriteDestinationBegin( rw, tag );
445 if ( docRtfPushAttribute( rw ) )
446 { LDEB(1); return -1; }
447
448 docCleanParagraphProperties( &(rw->rwcParagraphProperties) );
449 docInitParagraphProperties( &(rw->rwcParagraphProperties) );
450
451 rw->rwTableNesting= 0;
452
453 docRtfWriteTag( rw, RTFtag_pard );
454 docRtfWriteSwitchToPlain( rw );
455 docRtfWriteNextLine( rw );
456
457 if ( docScanTree( rw->rwDocument, dt,
458 docRtfEnterNode, docRtfLeaveNode, flags, (void *)rw ) < 0 )
459 { LDEB(1); return -1; }
460
461 /********************************************************************/
462 /* MS-Word does not pick up paragraph properties of headers and */
463 /* footers without a \par */
464 /********************************************************************/
465 if ( forcePar )
466 {
467 DocumentPosition dp;
468
469 if ( ! docTailPosition( &dp, dt->dtRoot ) &&
470 dp.dpNode->biParaTableNesting == 0 )
471 { docRtfWriteTag( rw, RTFtag_par ); }
472 }
473
474 docCleanParagraphProperties( &(rw->rwcParagraphProperties) );
475 docInitParagraphProperties( &(rw->rwcParagraphProperties) );
476 utilInitTextAttribute( &(rw->rwTextAttribute) );
477 rw->rwTextCharset= FONTcharsetANSI;
478
479 docRtfWriteDestinationEnd( rw );
480 docRtfWriteNextLine( rw );
481
482 if ( docRtfPopAttribute( rw ) )
483 { LDEB(1); return -1; }
484 rw->rwTableNesting= savedTableNesting;
485
486 return 0;
487 }
488
docRtfWriteSelection(RtfWriter * rw,const DocumentSelection * ds)489 int docRtfWriteSelection( RtfWriter * rw,
490 const DocumentSelection * ds )
491 {
492 const int flags= FLAGtsSCAN_MERGED_CELLS;
493
494 if ( ds )
495 {
496 if ( docScanSelection( rw->rwDocument, ds,
497 docRtfEnterNode, docRtfLeaveNode, flags, (void *)rw ) < 0 )
498 { LDEB(1); return -1; }
499 }
500 else{
501 if ( docScanTree( rw->rwDocument, &(rw->rwDocument->bdBody),
502 docRtfEnterNode, docRtfLeaveNode, flags, (void *)rw ) < 0 )
503 { LDEB(1); return -1; }
504 }
505
506 while( rw->rwTableNesting > 0 )
507 {
508 if ( docRtfPopTable( rw ) )
509 { LDEB(rw->rwTableNesting); }
510 }
511
512 return 0;
513 }
514