1 /************************************************************************/
2 /*									*/
3 /*  Layout of a document.						*/
4 /*									*/
5 /************************************************************************/
6 
7 #   include	"docLayoutConfig.h"
8 
9 #   include	<stddef.h>
10 
11 #   include	"docLayout.h"
12 #   include	"docParticuleData.h"
13 
14 #   include	<docDebug.h>
15 #   include	<appDebugon.h>
16 #   include	<docObjectProperties.h>
17 #   include	<docParaParticules.h>
18 #   include	<docShape.h>
19 
20 #   define	SHOW_LINE_CHANGES	0
21 
22 /************************************************************************/
23 /*									*/
24 /*  Layout successive lines of a paragraph.				*/
25 /*  Claim space above a particular line.				*/
26 /*									*/
27 /*  2)  Do not claim 'space before' at the top of the page.		*/
28 /*									*/
29 /************************************************************************/
30 
docAboveLine(int * pStopCode,LayoutPosition * lp,const BufferItem * paraNode,int part,const BlockFrame * bf,const ParagraphFrame * pf)31 static int docAboveLine(	int *				pStopCode,
32 				LayoutPosition *		lp,
33 				const BufferItem *		paraNode,
34 				int				part,
35 				const BlockFrame *		bf,
36 				const ParagraphFrame *		pf )
37     {
38     int			spaceAboveLineTwips= 0;
39 
40     if  ( part == 0 )
41 	{
42 	spaceAboveLineTwips= paraNode->biParaTopInset;
43 
44 	/*  2  */
45 	if  ( lp->lpAtTopOfColumn )
46 	    { spaceAboveLineTwips -= paraNode->biParaSpaceBeforeTwips;	}
47 	}
48 
49     if  ( lp->lpPageYTwips+ spaceAboveLineTwips >= bf->bfContentRect.drY1 )
50 	{ *pStopCode= FORMATstopBLOCK_FULL; return 1;	}
51 
52     if  ( lp->lpPageYTwips+ spaceAboveLineTwips >= pf->pfParaContentRect.drY1 )
53 	{ *pStopCode= FORMATstopFRAME_FULL; return 1;	}
54 
55     lp->lpPageYTwips += spaceAboveLineTwips;
56     /* still at top */
57 
58     return 0;
59     }
60 
61 /************************************************************************/
62 /*									*/
63 /*  Layout successive lines of a paragraph.				*/
64 /*  Claim space below a particular line.				*/
65 /*									*/
66 /*  Also decide whether it will fit in the current formatting frame.	*/
67 /*									*/
68 /*  1)  Only use the space after paragraph property and the insets	*/
69 /*	of the table when the paragraph is in a table.			*/
70 /*  2)  Push the position for the next line down until below any shapes	*/
71 /*	on this line. Until we implement the various wrap modes, this	*/
72 /*	is the best we can do.						*/
73 /*									*/
74 /************************************************************************/
75 
docBelowLine(int * pStopCode,LayoutPosition * lp,const LayoutPosition * lpLineTop,int lineStride,const BufferItem * paraNode,int partFrom,int partUpto,const BlockFrame * bf,const ParagraphFrame * pf,const NotesReservation * nrLine)76 static int docBelowLine(	int *				pStopCode,
77 				LayoutPosition *		lp,
78 				const LayoutPosition *		lpLineTop,
79 				int				lineStride,
80 				const BufferItem *		paraNode,
81 				int				partFrom,
82 				int				partUpto,
83 				const BlockFrame *		bf,
84 				const ParagraphFrame *		pf,
85 				const NotesReservation *	nrLine )
86     {
87     int				spaceBelowLineTwips= 0;
88     int				lineBottom;
89     int				lineHeight;
90     LayoutPosition		lpBelowLine;
91 
92     int				flowY1WithNotes;
93 
94     int				footnoteHeight;
95 
96     /*  1  */
97     if  ( partUpto == paraNode->biParaParticuleCount			&&
98 	  paraNode->biParaTableNesting > 0				&&
99 	  paraNode->biNumberInParent ==
100 				    paraNode->biParent->biChildCount- 1	)
101 	{ spaceBelowLineTwips += paraNode->biParaBottomInset; }
102 
103     lpBelowLine= *lpLineTop;
104     lpBelowLine.lpPageYTwips += lineStride;
105 					/********************************/
106 					/*  But add spacing to find	*/
107 					/*  position for next line	*/
108 					/********************************/
109 
110     lineBottom= lpBelowLine.lpPageYTwips+ spaceBelowLineTwips;
111     lineHeight= lineBottom- lpLineTop->lpPageYTwips;
112 
113     footnoteHeight= nrLine->nrFtnsepHeight+ nrLine->nrFootnoteHeight;
114     flowY1WithNotes= bf->bfFlowRect.drY1- footnoteHeight;
115 
116     if  ( lineBottom > flowY1WithNotes					&&
117 	  lineHeight < bf->bfContentRect.drY1- bf->bfContentRect.drY0	)
118 	{ *pStopCode= FORMATstopBLOCK_FULL; return 1;	}
119 
120     if  ( lineBottom > pf->pfParaContentRect.drY1			&&
121 	  lineHeight < pf->pfParaContentRect.drY1- pf->pfParaContentRect.drY0 )
122 	{ *pStopCode= FORMATstopFRAME_FULL; return 1;	}
123 
124     *lp= lpBelowLine;
125     lp->lpAtTopOfColumn= 0;
126 
127     if  ( partUpto == paraNode->biParaParticuleCount )
128 	{
129 	spaceBelowLineTwips= paraNode->biParaBottomInset;
130 
131 	lp->lpPageYTwips += spaceBelowLineTwips;
132 	lp->lpAtTopOfColumn= 0;
133 	}
134 
135     return 0;
136     }
137 
138 /************************************************************************/
139 /*									*/
140 /*  Calculations on line inserts.					*/
141 /*									*/
142 /************************************************************************/
143 
docPlaceShape(const BufferItem * paraNode,const LayoutPosition * lpLineTop,InsertedObject * io,const ParagraphFrame * pf,const BlockFrame * bf,int xChar)144 static int docPlaceShape(	const BufferItem *	paraNode,
145 				const LayoutPosition *	lpLineTop,
146 				InsertedObject *	io,
147 				const ParagraphFrame *	pf,
148 				const BlockFrame *	bf,
149 				int			xChar )
150     {
151     DrawingShape *		ds= io->ioDrawingShape;
152     int				x1;
153     LayoutPosition		lpBelowShape;
154 
155     if  ( ! ds )
156 	{ XDEB(ds); return -1;	}
157 
158     docShapePageRectangle( &(io->ioY0Position), &lpBelowShape,
159 				&(io->ioX0Twips), &x1,
160 				ds, paraNode, lpLineTop, pf, bf, xChar );
161 
162     docDrawingShapeInvalidateTextLayout( ds );
163 
164     return 0;
165     }
166 
docPlaceShapeY(const BufferItem * paraNode,const LayoutPosition * lpLineTop,InsertedObject * io,const BlockFrame * bf)167 static int docPlaceShapeY(	const BufferItem *	paraNode,
168 				const LayoutPosition *	lpLineTop,
169 				InsertedObject *	io,
170 				const BlockFrame *	bf )
171     {
172     DrawingShape *		ds= io->ioDrawingShape;
173     LayoutPosition		lpBelowShape;
174 
175     BlockFrame			bfShape;
176 
177     docLayoutInitBlockFrame( &bfShape );
178 
179     docShapePageY( &(io->ioY0Position), &lpBelowShape, &bfShape,
180 						ds, paraNode, lpLineTop, bf );
181 
182     docDrawingShapeInvalidateTextLayout( ds );
183 
184     docLayoutCleanBlockFrame( &bfShape );
185 
186     return 0;
187     }
188 
docPlaceLineInserts(BufferDocument * bd,const BufferItem * paraNode,const TextLine * tl,const ParticuleData * pd,const ParagraphFrame * pf,const BlockFrame * bf,const LayoutPosition * lp)189 static int docPlaceLineInserts(	BufferDocument *	bd,
190 				const BufferItem *	paraNode,
191 				const TextLine *	tl,
192 				const ParticuleData *	pd,
193 				const ParagraphFrame *	pf,
194 				const BlockFrame *	bf,
195 				const LayoutPosition *	lp )
196     {
197     int				i;
198     const TextParticule *	tp;
199 
200     tp= paraNode->biParaParticules+ tl->tlFirstParticule;
201     for ( i= 0; i < tl->tlParticuleCount; tp++, i++ )
202 	{
203 	if  ( tp->tpKind == DOCkindFIELDHEAD )
204 	    {
205 	    if  ( docSetPageOfField( &(bd->bdFieldList),
206 					tp->tpObjectNumber,  lp->lpPage ) )
207 		{ LDEB(lp->lpPage); docListNode(0,paraNode,0);	}
208 	    }
209 
210 	if  ( tp->tpKind == DOCkindOBJECT )
211 	    {
212 	    InsertedObject *	io;
213 
214 	    io= docGetObject( bd, tp->tpObjectNumber );
215 	    if  ( ! io )
216 		{ LPDEB(tp->tpObjectNumber,io);	}
217 
218 	    if  ( io && io->ioKind == DOCokDRAWING_SHAPE )
219 		{
220 		if  ( docPlaceShape( paraNode, &(tl->tlTopPosition),
221 						    io, pf, bf, pd->pdX0 ) )
222 		    { LDEB(1);	}
223 		}
224 	    }
225 	}
226 
227     return 0;
228     }
229 
docPlaceLineInsertsY(BufferDocument * bd,const BufferItem * paraNode,const TextLine * tl,const BlockFrame * bf,const LayoutPosition * lp)230 static int docPlaceLineInsertsY(BufferDocument *	bd,
231 				const BufferItem *	paraNode,
232 				const TextLine *	tl,
233 				const BlockFrame *	bf,
234 				const LayoutPosition *	lp )
235     {
236     int				i;
237     const TextParticule *	tp;
238 
239     tp= paraNode->biParaParticules+ tl->tlFirstParticule;
240     for ( i= 0; i < tl->tlParticuleCount; tp++, i++ )
241 	{
242 	if  ( tp->tpKind == DOCkindFIELDHEAD )
243 	    {
244 	    docSetPageOfField( &(bd->bdFieldList),
245 					tp->tpObjectNumber,  lp->lpPage );
246 	    }
247 
248 	if  ( tp->tpKind == DOCkindOBJECT )
249 	    {
250 	    InsertedObject *	io;
251 
252 	    io= docGetObject( bd, tp->tpObjectNumber );
253 	    if  ( ! io )
254 		{ LPDEB(tp->tpObjectNumber,io);	}
255 
256 	    if  ( io && io->ioKind == DOCokDRAWING_SHAPE )
257 		{
258 		if  ( docPlaceShapeY( paraNode, &(tl->tlTopPosition), io, bf ) )
259 		    { LDEB(1);	}
260 		}
261 	    }
262 	}
263 
264     return 0;
265     }
266 
267 /************************************************************************/
268 /*									*/
269 /*  Layout successive lines of a paragraph.				*/
270 /*									*/
271 /*  1)  Cope with the output of sgmls: Ignore enormous space before's	*/
272 /*	in footers.							*/
273 /*									*/
274 /************************************************************************/
275 
docLayout_Line(int * pStopCode,TextLine * resTl,NotesReservation * pNrLine,const BlockFrame * bf,BufferItem * paraNode,int redoLineLayout,int part,ParticuleData * pd,const LayoutJob * lj,const ParagraphFrame * pf,const LayoutPosition * lpTop,LayoutPosition * lpBottom)276 static int docLayout_Line(	int *				pStopCode,
277 				TextLine *			resTl,
278 				NotesReservation *		pNrLine,
279 				const BlockFrame *		bf,
280 				BufferItem *			paraNode,
281 				int				redoLineLayout,
282 				int				part,
283 				ParticuleData *			pd,
284 				const LayoutJob *		lj,
285 				const ParagraphFrame *		pf,
286 				const LayoutPosition *		lpTop,
287 				LayoutPosition *		lpBottom )
288     {
289     const LayoutContext *	lc= &(lj->ljContext);
290     BufferDocument *		bd= lc->lcDocument;
291 
292     int				accepted;
293     int				res;
294 
295     TextParticule *		tp= paraNode->biParaParticules+ part;
296 
297     TextLine			workTl;
298     TextLine *			tl= &(workTl);
299 
300     LayoutPosition		lpHere;
301 
302     NotesReservation		nrLine;
303 
304     lpHere= *lpTop;
305 
306     if  ( redoLineLayout )
307 	{
308 	docInitTextLine( tl );
309 	tl->tlStroff= tp->tpStroff;
310 	}
311     else{
312 	workTl= *resTl;
313 	}
314     tl->tlFirstParticule= part;
315 
316     docInitNotesReservation( &nrLine );
317 
318     /*  1  */
319     res= docAboveLine( pStopCode, &lpHere, paraNode, part, bf, pf );
320     if  ( res < 0 )
321 	{ LDEB(res); return -1;	}
322     if  ( res > 0 )
323 	{ return 0; }
324 
325     tl->tlTopPosition= lpHere;
326     if  ( redoLineLayout )
327 	{ accepted= docLayoutLineBox( tl, paraNode, part, lc, pd, pf );	}
328     else{ accepted= tl->tlParticuleCount;				}
329 
330     if  ( accepted < 1 )
331 	{ LDEB(accepted); return -1;	}
332 
333     if  ( docLayoutCollectParaFootnoteHeight( &nrLine,
334 		    tl->tlTopPosition.lpPage, tl->tlTopPosition.lpColumn,
335 		    bd, lj->ljBodySectNode, paraNode,
336 		    tl->tlFirstParticule, tl->tlFirstParticule+ accepted ) )
337 	{ LDEB(1); return -1;	}
338 
339     res= docBelowLine( pStopCode, &lpHere,
340 				    &(tl->tlTopPosition), tl->tlLineStride,
341 				    paraNode, part, part+ accepted,
342 				    bf, pf, &nrLine );
343     if  ( res < 0 )
344 	{ LDEB(res); return -1;	}
345     if  ( res > 0 )
346 	{ return 0; }
347 
348     if  ( redoLineLayout )
349 	{
350 	tl->tlStrlen=
351 	    tp[accepted-1].tpStroff+ tp[accepted-1].tpStrlen- tp->tpStroff;
352 	tl->tlParticuleCount= accepted;
353 	tl->tlFlowWidthTwips=
354 		    pf->pfParaContentRect.drX1- pf->pfParaContentRect.drX0;
355 
356 	if  ( lj->ljLayoutScreenLine					&&
357 	      (*lj->ljLayoutScreenLine)( tl, paraNode, lc, pf, pd ) < 0	)
358 	    { LDEB(accepted); return -1;	}
359 
360 	if  ( docPlaceLineInserts( bd, paraNode, tl, pd, pf, bf, &lpHere ) )
361 	    { LDEB(accepted); }
362 	}
363     else{
364 	if  ( docPlaceLineInsertsY( bd, paraNode, tl, bf, &lpHere ) )
365 	    { LDEB(accepted); }
366 	}
367 
368     *lpBottom= lpHere;
369     *pNrLine= nrLine;
370     *resTl= *tl;
371 
372     return accepted;
373     }
374 
375 /************************************************************************/
376 /*									*/
377 /*  Add the rectangle of the old and the new locations of a line to the	*/
378 /*  rectangle that must be redrawn.					*/
379 /*									*/
380 /************************************************************************/
381 
docLayoutAddLineToExpose(DocumentRectangle * drChanged,const LayoutJob * lj,const ParagraphFrame * pf,const TextLine * boxLine,const TextLine * tlLine)382 static void docLayoutAddLineToExpose(
383 				DocumentRectangle *		drChanged,
384 				const LayoutJob *		lj,
385 				const ParagraphFrame *		pf,
386 				const TextLine *		boxLine,
387 				const TextLine *		tlLine )
388     {
389     const LayoutContext *	lc= &(lj->ljContext);
390     DocumentRectangle		drBox;
391 
392     LayoutPosition		lpTop;
393     LayoutPosition		lpBottom;
394 
395     lpTop= boxLine->tlTopPosition;
396     lpBottom= lpTop;
397     lpBottom.lpPageYTwips += boxLine->tlLineStride;
398 
399     docGetPixelRectForPos( &drBox, lc,
400 			    pf->pfRedrawX0Twips, pf->pfRedrawX1Twips,
401 			    &lpTop, &lpBottom );
402 
403     geoUnionRectangle( drChanged, drChanged, &drBox );
404 
405     if  ( tlLine )
406 	{
407 	DocumentRectangle	drTl;
408 
409 	lpTop= tlLine->tlTopPosition;
410 	lpBottom= lpTop;
411 	lpBottom.lpPageYTwips += tlLine->tlLineStride;
412 
413 	docGetPixelRectForPos( &drTl, lc,
414 			    pf->pfRedrawX0Twips, pf->pfRedrawX1Twips,
415 			    &lpTop, &lpBottom );
416 
417 	if  ( drTl.drX0 != drBox.drX0	||
418 	      drTl.drX1 != drBox.drX1	||
419 	      drTl.drY0 != drBox.drY0	||
420 	      drTl.drY1 != drBox.drY1	)
421 	    {
422 	    geoUnionRectangle( drChanged, drChanged, &drTl );
423 	    geoUnionRectangle( drChanged, drChanged, &drBox );
424 
425 #	    if SHOW_LINE_CHANGES
426 	    appDebug( "EXPOSE [%4d..%4d x %4d..%4d]\n",
427 				    drChanged->drX0, drChanged->drX1,
428 				    drChanged->drY0, drChanged->drY1 );
429 #	    endif
430 	    }
431 	}
432     else{
433 	geoUnionRectangle( drChanged, drChanged, &drBox );
434 
435 #	if SHOW_LINE_CHANGES
436 	appDebug( "EXPOSE [%4d..%4d x %4d..%4d]\n",
437 				    drChanged->drX0, drChanged->drX1,
438 				    drChanged->drY0, drChanged->drY1 );
439 #	endif
440 	}
441     }
442 
443 /************************************************************************/
444 /*									*/
445 /*  Layout a series of lines in a paragraph.				*/
446 /*									*/
447 /*  1)  Make sure that we have scratch space for the layout routines.	*/
448 /*  2)  As long as there are any particules to be placed/formatted	*/
449 /*  3)  Can the current line be reused?					*/
450 /*  4)  If so just place it at a new position.				*/
451 /*  5)  Otherwise recalculate layout.					*/
452 /*  6)  If the line does not fit on this page (column) stop.		*/
453 /*  7)  Cause the line to be redrawn if either it is reformatted, or it	*/
454 /*	is moved.							*/
455 /*  8)  Subtract the space needed for the footnotes in this line from	*/
456 /*	the space left on this page. (column)				*/
457 /*  9)  Insert into administration.					*/
458 /*  10) If the line ends in a page break, make sure nothing will fit on	*/
459 /*	this page (in this column) anymore.				*/
460 /*  11) Truncate the number of lines when the paragraph is completely	*/
461 /*	formatted.							*/
462 /*									*/
463 /************************************************************************/
464 
docLayoutParaLines(int * pStopCode,int isRedo,const ParagraphFrame * pf,LayoutPosition * lpHere,int * pLine,BlockFrame * bf,const LayoutJob * lj,BufferItem * paraNode,int part)465 int docLayoutParaLines(		int *				pStopCode,
466 				int				isRedo,
467 				const ParagraphFrame *		pf,
468 				LayoutPosition *		lpHere,
469 				int *				pLine,
470 				BlockFrame *			bf,
471 				const LayoutJob *		lj,
472 				BufferItem *			paraNode,
473 				int				part )
474     {
475     int				stopCode= FORMATstopREADY;
476 
477     int				line= (*pLine);
478     int				done= 0;
479 
480     ParticuleData *		pd;
481 
482     LayoutPosition		lp= (*lpHere);
483 
484     /*  1  */
485     if  ( docPsClaimParticuleData( paraNode->biParaParticuleCount, &pd ) )
486 	{ LDEB(paraNode->biParaParticuleCount); return -1;	}
487 
488     /*  2  */
489     while( part < paraNode->biParaParticuleCount )
490 	{
491 	int			accepted;
492 	TextLine		boxLine;
493 
494 	int			placeOldLine= 0;
495 	TextLine *		tlLine= (TextLine *)0;
496 
497 	NotesReservation	nrLine;
498 
499 	DocumentRectangle *	drChanged= lj->ljChangedRectanglePixels;
500 
501 	int			flowWidth;
502 
503 	docInitNotesReservation( &nrLine );
504 
505 	flowWidth= pf->pfParaContentRect.drX1- pf->pfParaContentRect.drX0;
506 
507 	docInitTextLine( &boxLine );
508 
509 	/*  3  */
510 	if  ( line < paraNode->biParaLineCount )
511 	    {
512 	    const TextParticule *	tp= paraNode->biParaParticules+ part;
513 
514 	    tlLine= paraNode->biParaLines+ line;
515 	    placeOldLine= 1;
516 	    boxLine= *tlLine;
517 
518 	    if  ( tlLine->tlFirstParticule+ tlLine->tlParticuleCount >
519 					paraNode->biParaParticuleCount	||
520 		  tlLine->tlStroff != tp->tpStroff			||
521 		  tlLine->tlFlowWidthTwips != flowWidth			)
522 		{ placeOldLine= 0;	}
523 	    }
524 
525 	/*  4, 5  */
526 	accepted= docLayout_Line( &stopCode, &boxLine, &nrLine, bf,
527 			paraNode, ! placeOldLine, part, pd, lj, pf, &lp, &lp );
528 
529 	if  ( accepted < 0 )
530 	    { LDEB(accepted); return -1;	}
531 
532 	/*  6  */
533 	if  ( accepted == 0 )
534 	    { break;	}
535 
536 	/*  7  */
537 	if  ( drChanged	)
538 	    {
539 	    docLayoutAddLineToExpose( drChanged, lj, pf, &boxLine, tlLine );
540 	    }
541 
542 	/*  8  */
543 	if  ( ! isRedo )
544 	    { docLayoutReserveNoteHeight( bf, &nrLine );	}
545 
546 	/*  9  */
547 	if  ( line >= paraNode->biParaLineCount )
548 	    {
549 	    tlLine= docInsertTextLine( paraNode, -1 );
550 	    if  ( ! tlLine )
551 		{ XDEB(tlLine); return -1;		}
552 	    }
553 	else{
554 	    tlLine= paraNode->biParaLines+ line;
555 	    }
556 
557 	*tlLine= boxLine;
558 
559 	part += accepted; done += accepted; line++;
560 
561 	/*  10  */
562 	if  ( boxLine.tlFlags & TLflagBLOCKBREAK )
563 	    {
564 	    int		part;
565 
566 	    part= boxLine.tlFirstParticule+ boxLine.tlParticuleCount- 1;
567 
568 	    switch( paraNode->biParaParticules[part].tpKind )
569 		{
570 		case DOCkindPAGEBREAK:
571 		    stopCode= FORMATstopPAGE_BREAK;
572 		    break;
573 		case DOCkindCOLUMNBREAK:
574 		    stopCode= FORMATstopCOLUMN_BREAK;
575 		    break;
576 		default:
577 		    LDEB(paraNode->biParaParticules[part].tpKind);
578 		    stopCode= FORMATstopPAGE_BREAK;
579 		    break;
580 		}
581 	    break;
582 	    }
583 	}
584 
585     *pLine= line;
586     *lpHere= lp;
587     *pStopCode= stopCode;
588 
589     /*  11  */
590     if  ( part >= paraNode->biParaParticuleCount	&&
591 	  paraNode->biParaLineCount > line	)
592 	{ paraNode->biParaLineCount=  line; }
593 
594     return done;
595     }
596 
597