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