1 /************************************************************************/
2 /* */
3 /* Layout of a document. Layout of a series of paragraphs in a common */
4 /* parent. */
5 /* */
6 /************************************************************************/
7
8 # include "docLayoutConfig.h"
9
10 # include <stddef.h>
11
12 # include "docLayout.h"
13 # include <docPageGrid.h>
14 # include <docTreeType.h>
15 # include <docTreeNode.h>
16 # include <docNodeTree.h>
17 # include <docTextLine.h>
18
19 # include <appDebugon.h>
20
21 /************************************************************************/
22 /* */
23 /* Preparation for vertical alignment of tabe cells. It is also used */
24 /* to position the contents of a frame. */
25 /* */
26 /************************************************************************/
27
docRedoParaStripLayout(const LayoutJob * lj,BlockFrame * bf,const LayoutPosition * lpFrom,BufferItem * cellNode,int childFrom,int childUpto)28 void docRedoParaStripLayout( const LayoutJob * lj,
29 BlockFrame * bf,
30 const LayoutPosition * lpFrom,
31 BufferItem * cellNode,
32 int childFrom,
33 int childUpto )
34 {
35 const LayoutContext * lc= &(lj->ljContext);
36 int stopCode= FORMATstopREADY;
37 int para;
38 BufferItem * paraBi0= cellNode->biChildren[childFrom];
39
40 int xStopCode= 0;
41
42 ParagraphLayoutPosition plpRedo;
43 const int isRedo= 1;
44
45 docInitParagraphLayoutPosition( &plpRedo );
46
47 docStripLayoutStartChild( &plpRedo, childFrom );
48 plpRedo.plpPos= *lpFrom;
49
50 /* 1 */
51 if ( docLayoutStartParagraph( lj, &xStopCode, paraBi0, bf, &plpRedo ) )
52 { LDEB(1); return; }
53
54 if ( xStopCode != FORMATstopREADY )
55 { LDEB(xStopCode); return; }
56
57 for ( para= childFrom; para < childUpto; para++ )
58 {
59 int changed= 0;
60 int accepted;
61 BufferItem * paraNode= cellNode->biChildren[para];
62
63 docStripLayoutStartChild( &plpRedo, para );
64
65 paraNode->biTopPosition= plpRedo.plpPos;
66
67 accepted= docLayoutParaLines( &stopCode, isRedo,
68 &(plpRedo.plpParagraphFrame),
69 &(plpRedo.plpPos), &(plpRedo.pspLine),
70 bf, lj, paraNode, plpRedo.pspPart );
71 if ( accepted < 0 )
72 { LDEB(accepted); break; }
73
74 docLayoutSetNodeBottom( &changed, paraNode, &(plpRedo.plpPos),
75 lc, lj->ljChangedRectanglePixels );
76 }
77
78 return;
79 }
80
81 /************************************************************************/
82
docLayoutChildInStrip(int * pStopCode,ParagraphLayoutPosition * plp,BlockFrame * bf,const LayoutJob * lj,int cellTopInset,BufferItem * childNode)83 static int docLayoutChildInStrip(
84 int * pStopCode,
85 ParagraphLayoutPosition * plp,
86 BlockFrame * bf,
87 const LayoutJob * lj,
88 int cellTopInset,
89 BufferItem * childNode )
90 {
91 switch( childNode->biLevel )
92 {
93 case DOClevPARA:
94 return docLayoutParagraphInStrip( pStopCode, plp, bf, lj,
95 cellTopInset, childNode );
96 case DOClevROW:
97 return docLayoutRowInStrip( pStopCode, plp, bf, lj,
98 cellTopInset, childNode );
99 default:
100 LDEB(childNode->biLevel); return -1;
101 }
102 }
103
104 /************************************************************************/
105
docLayoutGetChildFrame(int * pFrameNumber,FrameProperties * fp,const BufferDocument * bd,const BufferItem * childNode)106 static int docLayoutGetChildFrame( int * pFrameNumber,
107 FrameProperties * fp,
108 const BufferDocument * bd,
109 const BufferItem * childNode )
110 {
111 int isFrame= 0;
112 int frameNumber;
113
114 switch( childNode->biLevel )
115 {
116 case DOClevPARA:
117 frameNumber= childNode->biParaFrameNumber;
118 break;
119 case DOClevROW:
120 frameNumber= childNode->biRowFrameNumber;
121 break;
122 default:
123 LDEB(childNode->biLevel);
124 frameNumber= -1;
125 break;
126 }
127
128 if ( frameNumber >= 0 )
129 {
130 docGetFramePropertiesByNumber( fp, bd, frameNumber );
131 isFrame= DOCisFRAME( fp );
132 }
133
134 *pFrameNumber= frameNumber;
135 return isFrame;
136 }
137
138 /************************************************************************/
139 /* */
140 /* Layout as much of a series of paragraph as fits on the current */
141 /* page. (Actually the column on the page). */
142 /* */
143 /* a) Do not format children that belong to different frames. */
144 /* */
145 /************************************************************************/
146
docLayoutStripChildren(int * pStopCode,ParagraphLayoutJob * plj,BlockFrame * bfFlow,const LayoutJob * lj,BufferItem * cellNode)147 int docLayoutStripChildren( int * pStopCode,
148 ParagraphLayoutJob * plj,
149 BlockFrame * bfFlow,
150 const LayoutJob * lj,
151 BufferItem * cellNode )
152 {
153 const LayoutContext * lc= &(lj->ljContext);
154 ParagraphLayoutPosition * plp= &(plj->pljPos);
155 const int childFrom= plp->pspChild;
156 BufferItem * childNode= cellNode->biChildren[childFrom];
157
158 BlockFrame * bf= bfFlow;
159
160 const BufferDocument * bd= lc->lcDocument;
161
162 FrameProperties fpBegin;
163 int frameBegin;
164 int wasFrame;
165
166 FrameProperties fpRow;
167 int frameRow= -1;
168 int rowIsFrame= 0;
169
170 BufferItem * rowBi;
171
172 if ( ! docIsCellNode( cellNode ) )
173 { SDEB(docLevelStr(cellNode->biLevel)); return -1; }
174 if ( childFrom >= cellNode->biChildCount )
175 { LLDEB(childFrom,cellNode->biChildCount); return -1; }
176
177 rowBi= docGetRowNode( cellNode );
178 if ( rowBi )
179 {
180 DocumentPosition dp;
181
182 if ( ! docHeadPosition( &dp, rowBi ) )
183 {
184 rowIsFrame= docLayoutGetChildFrame( &frameRow, &fpRow,
185 bd, dp.dpNode );
186 }
187 }
188
189 /* a */
190 childNode= cellNode->biChildren[plj->pljPos.pspChild];
191 wasFrame= docLayoutGetChildFrame( &frameBegin, &fpBegin, bd, childNode );
192 if ( wasFrame && ( ! rowIsFrame || frameRow != frameBegin ) )
193 {
194 BlockFrame bfTextFrame;
195 ParagraphLayoutPosition plpTextFrame;
196 int frameHeight= fpBegin.fpHighTwips;
197
198 if ( frameHeight < 0 )
199 { frameHeight= -frameHeight; }
200
201 plpTextFrame= *plp;
202 plp= &plpTextFrame;
203
204 docLayoutInitBlockFrame( &bfTextFrame );
205 docLayoutSetTextFrame( &bfTextFrame, &(plp->plpPos),
206 bf, &fpBegin, frameHeight );
207
208 plp->plpPos.lpPageYTwips= bfTextFrame.bfContentRect.drY0;
209 bf= &bfTextFrame;
210 }
211
212 while( plp->pspChild < plj->pljChildUpto )
213 {
214 int stopCode= FORMATstopREADY;
215 int ret;
216
217 FrameProperties fpHere;
218 int frameHere;
219 int isFrame;
220
221 childNode= cellNode->biChildren[plp->pspChild];
222 isFrame= docLayoutGetChildFrame( &frameHere, &fpHere, bd, childNode );
223
224 /* a */
225 if ( isFrame != wasFrame ||
226 ( wasFrame && ( frameHere != frameBegin ) ) )
227 {
228 if ( wasFrame )
229 {
230 docLayoutFinishFrame( &fpBegin, bf, bfFlow, lj,
231 &(plj->pljPos), cellNode, childFrom, plp->pspChild );
232
233 plj->pljPos= *plp;
234 }
235
236 *pStopCode= FORMATstopNEXT_FRAME;
237 break;
238 }
239
240 ret= docLayoutChildInStrip( &stopCode, plp, bf, lj,
241 cellNode->biCellTopInset, childNode );
242 if ( ret < 0 )
243 { LDEB(ret); return -1; }
244
245 switch( ret )
246 {
247 case FORMATstopBLOCK_FULL:
248 case FORMATstopPAGE_BREAK:
249 case FORMATstopCOLUMN_BREAK:
250 *pStopCode= stopCode;
251 break;
252
253 case FORMATstopPARTIAL:
254 case FORMATstopREADY:
255 continue;
256
257 default:
258 LDEB(ret); continue;
259 }
260
261 break;
262 }
263
264 if ( docLayoutStripDone( plp, plj ) )
265 { *pStopCode= FORMATstopREADY; }
266
267 return 0;
268 }
269
270 /************************************************************************/
271 /* */
272 /* Find a start point for the formatter to layout lines. A certain */
273 /* work ahead is necessary to decide whether work on line layout can */
274 /* be used or not. docCommitStripLayout() is used to decide what */
275 /* work is final and what possibly is to be redone. */
276 /* */
277 /* docCommitStripLayout() is called when a column is full. In */
278 /* this case, everything that is not final yet needs to be moved to */
279 /* the next page. */
280 /* */
281 /* - Insure that the constraints comming from Widow/Orphan control */
282 /* are been obeyed. */
283 /* - Insure that paragraphs that should not be divided over pages */
284 /* are not split. */
285 /* - Insure that paragraphs that should be with the next one as a */
286 /* header really are with it. (Experimenting with Word shows that */
287 /* paragraphs that should be with the next one should be */
288 /* completely with it.) */
289 /* */
290 /* When any of the above is violated, find a solution that respects */
291 /* the requirements by pushing offending paragraphs or lines down. */
292 /* */
293 /* docCommitStripLayout() shifts 'plp0' to the last position that is */
294 /* not below 'plp1' that is not influenced by the layout of position */
295 /* on 'plp1' or below. Everything before 'plp1' is formatted and */
296 /* plp1->plpPos is the position where formatting would continue if */
297 /* docCommitStripLayout() moved 'plp0' onto 'plp1'. (As it often does) */
298 /* */
299 /* REGULAR APPROACH */
300 /* ================ */
301 /* a) Avoid superfluous work and the handling of this exception */
302 /* below: Commit an explit page break. */
303 /* b) If the current position is a legal position for a page break.. */
304 /* break here. */
305 /* */
306 /* 'Keep With Next' and 'Widow/Orphan Contol' */
307 /* ========================================== */
308 /* 1) Find the place to begin. Anything on a previous page is final: */
309 /* It will not be shifted to the next page. (Paragraph) */
310 /* 2) Idem: Line. The line found here might very well be irrelevant */
311 /* because a different paragraph to commit is found below. */
312 /* 3) Commit all paragraphs before the current one that do not have */
313 /* the 'Keep with Next' flag. */
314 /* flag. */
315 /* 4) If the paragraph to be committed is before the current one */
316 /* commit the position we have found. This can be: */
317 /* - The first line on the current page (from 2) */
318 /* [ This case is however excluded by the exception code (0)]. */
319 /* - The head of a series of KeepWithNext paragraphs. (from 3) */
320 /* 5) When this paragraph is to be kept together, restart from its */
321 /* beginning. */
322 /* 6) When Widow/Orphan control is active.. Do not commit the first */
323 /* line before the whole paragraph is committed. */
324 /* 7) The special cases of the last line of a paragraph with widow */
325 /* orphan control and a paragraph with three lines are covered */
326 /* in docLayoutStripChildren() at the end of a paragraph. */
327 /* So no exceptions have to be made here. Also note the */
328 /* preparations in docAdjustParaLayout(). */
329 /* */
330 /* 8) And finally when no exception applies, start from where we are */
331 /* now. */
332 /* */
333 /************************************************************************/
334
docCommitStripLayout_x(ParagraphLayoutPosition * plp0,const ParagraphLayoutPosition * plp1,int advanceAnyway,int page,int column,const BufferItem * cellNode)335 static void docCommitStripLayout_x(
336 ParagraphLayoutPosition * plp0,
337 const ParagraphLayoutPosition * plp1,
338 int advanceAnyway,
339 int page,
340 int column,
341 const BufferItem * cellNode )
342 {
343 const BufferItem * paraBi0;
344 const BufferItem * paraBi1;
345 const TextLine * tl;
346
347 int para;
348
349 int line0;
350
351 paraBi0= cellNode->biChildren[plp0->pspChild];
352 paraBi1= (const BufferItem *)0;
353
354 if ( paraBi0->biLevel != DOClevPARA )
355 { LSDEB(paraBi0->biLevel,docLevelStr(paraBi0->biLevel)); return; }
356
357 /* a */
358 if ( plp1->pspChild < cellNode->biChildCount )
359 {
360 int line1;
361
362 paraBi1= cellNode->biChildren[plp1->pspChild];
363
364 line1= plp1->pspLine;
365
366 if ( line1- 1 >= 0 &&
367 line1- 1 < paraBi1->biParaLineCount )
368 {
369 const TextLine * tl= paraBi1->biParaLines+ line1- 1;
370
371 if ( tl->tlFlags & TLflagBLOCKBREAK )
372 {
373 *plp0= *plp1;
374 return;
375 }
376 }
377 }
378
379 /* b */
380 if ( paraBi1 &&
381 paraBi1->biParaKeepOnPage &&
382 ! paraBi1->biParaKeepWithNext &&
383 plp1->pspLine > 0 )
384 {
385 int line= 1;
386
387 if ( paraBi1->biParaWidowControl )
388 { line++; }
389
390 if ( plp1->pspLine >= line )
391 {
392 *plp0= *plp1;
393 return;
394 }
395 }
396
397 /* 1 */
398 while( plp0->pspChild < plp1->pspChild )
399 {
400 if ( paraBi0->biBelowPosition.lpPage > page )
401 { break; }
402 if ( paraBi0->biBelowPosition.lpPage == page &&
403 paraBi0->biBelowPosition.lpColumn >= column )
404 { break; }
405
406 docStripLayoutNextChild( plp0 );
407 paraBi0= cellNode->biChildren[plp0->pspChild];
408 }
409
410 /* 2 */
411 if ( plp0->pspChild < plp1->pspChild )
412 { line0= paraBi0->biParaLineCount; }
413 else{ line0= plp1->pspLine; }
414
415 tl= paraBi0->biParaLines+ plp0->pspLine;
416 while( plp0->pspLine < line0 && plp0->pspLine < paraBi0->biParaLineCount )
417 {
418 if ( tl->tlTopPosition.lpPage > page )
419 { break; }
420 if ( tl->tlTopPosition.lpPage == page &&
421 tl->tlTopPosition.lpColumn >= column )
422 { break; }
423
424 plp0->pspLine++; tl++;
425 }
426
427 /* 3 */
428 for ( para= plp0->pspChild; para < plp1->pspChild; para++ )
429 {
430 const BufferItem * paraNode= cellNode->biChildren[para];
431
432 if ( advanceAnyway || ! paraNode->biParaKeepWithNext )
433 {
434 docStripLayoutStartChild( plp0, para+ 1 );
435
436 if ( plp0->pspChild < cellNode->biChildCount )
437 { paraBi0= cellNode->biChildren[plp0->pspChild]; }
438 /* else .. return below */
439 }
440 }
441
442 /* 4 */
443 if ( plp0->pspChild < plp1->pspChild )
444 { return; }
445
446 if ( plp0->pspChild != plp1->pspChild )
447 { LLLLDEB(page,column,plp0->pspChild,plp1->pspChild ); }
448
449 if ( plp0->pspChild >= cellNode->biChildCount )
450 { return; }
451
452 /* 5 */
453 if ( ! advanceAnyway &&
454 ( paraBi0->biParaKeepOnPage ||
455 paraBi0->biParaKeepWithNext ) )
456 {
457 docStripLayoutStartChild( plp0, plp0->pspChild );
458 return;
459 }
460
461 /* 6 */
462 if ( ! advanceAnyway &&
463 paraBi1 &&
464 paraBi1->biParaWidowControl &&
465 plp1->pspLine == 1 &&
466 paraBi1->biParaLineCount >= 1 )
467 {
468 tl= paraBi1->biParaLines+ 0;
469
470 if ( tl->tlFirstParticule+ tl->tlParticuleCount <
471 paraBi1->biParaParticuleCount )
472 {
473 docStripLayoutStartChild( plp0, plp0->pspChild );
474 return;
475 }
476 }
477
478 /* 7 */
479
480 /* 8 */
481 *plp0= *plp1;
482
483 return;
484 }
485
486 /************************************************************************/
487 /* */
488 /* Find out whether the fact that layout has proceeded to plj->pljPos */
489 /* makes any layout final. If so.. Move pljPos0 past what has become */
490 /* final. */
491 /* */
492 /************************************************************************/
493
494 # define docSetLayoutProgress( plpt, plpf ) \
495 { \
496 (plpt)->pspChild= (plpf)->pspChild; \
497 (plpt)->pspChildAdvanced= (plpf)->pspChildAdvanced; \
498 (plpt)->pspPart= (plpf)->pspPart; \
499 (plpt)->pspLine= (plpf)->pspLine; \
500 }
501
docCompareLayoutProgress(const ParagraphLayoutPosition * plp0,const ParagraphLayoutPosition * plp1)502 static int docCompareLayoutProgress(
503 const ParagraphLayoutPosition * plp0,
504 const ParagraphLayoutPosition * plp1 )
505 {
506 if ( plp1->pspChild > plp0->pspChild )
507 { return 1; }
508 if ( plp1->pspChild < plp0->pspChild )
509 { return -1; }
510
511 if ( plp1->pspPart > plp0->pspPart )
512 { return 1; }
513 if ( plp1->pspPart < plp0->pspPart )
514 { return -1; }
515
516 return 0;
517 }
518
docCommitStripLayout(int * pAdvanced,int advanceAnyway,ParagraphLayoutJob * plj,int page,int column,const BufferItem * cellNode)519 void docCommitStripLayout(
520 int * pAdvanced,
521 int advanceAnyway,
522 ParagraphLayoutJob * plj,
523 int page,
524 int column,
525 const BufferItem * cellNode )
526 {
527 int advanced= 0;
528 ParagraphLayoutPosition plp0;
529
530 plp0= plj->pljPos0;
531
532 docCommitStripLayout_x( &plp0, &(plj->pljPos),
533 advanceAnyway, page, column, cellNode );
534
535 advanced= ( docCompareLayoutProgress( &plp0, &(plj->pljPos0) ) < 0 );
536 if ( advanced )
537 {
538 docSetLayoutProgress( &(plj->pljPos0), &plp0 );
539 docSetLayoutProgress( &(plj->pljPos), &plp0 );
540 }
541 else{
542 # if 0
543 int cmp= docCompareLayoutProgress( &(plj->pljPos0), &(plj->pljPos) );
544 if ( cmp >= 0 )
545 { LDEB(cmp); }
546 # endif
547 /* Prevent loops: Be sure to advance */
548 docSetLayoutProgress( &(plj->pljPos0), &(plj->pljPos) );
549 }
550
551 *pAdvanced= advanced;
552 }
553
554 /************************************************************************/
555 /* */
556 /* Find out where to start formatting a strip. */
557 /* After some thought: That is the position that would be committed */
558 /* if the strip were formatted upto the stat position. */
559 /* */
560 /************************************************************************/
561
docFindStripLayoutOrigin(ParagraphLayoutJob * plj,int page,int column,const BufferItem * cellNode)562 void docFindStripLayoutOrigin( ParagraphLayoutJob * plj,
563 int page,
564 int column,
565 const BufferItem * cellNode )
566 {
567 int advanceAnyway= 0;
568
569 docCommitStripLayout_x( &(plj->pljPos0), &(plj->pljPos),
570 advanceAnyway, page, column, cellNode );
571
572 return;
573 }
574
575 /************************************************************************/
576 /* */
577 /* Format the lines in a series of paragraphs. On the way keep an */
578 /* administration on where to restart formatting at a page break. */
579 /* */
580 /* 1) Place as much as fits on the first page. */
581 /* 2) While unformatted paragraphs remain: place some more on */
582 /* subsequent pages. */
583 /* 3) First place the footnotes on the page. */
584 /* 4) Then find out where to restart the layout job on the next page */
585 /* Widow/Orphan control etc can cause some of the text that we */
586 /* already formatted to go to the next page. */
587 /* 5) Skip to the next page. */
588 /* 6) Determine available space on the next page. */
589 /* 7) Place as much as fits on the next page. */
590 /* */
591 /************************************************************************/
592
docLayoutStackedStrip(BufferItem * cellNode,BlockFrame * bf,const LayoutJob * lj,ParagraphLayoutJob * plj)593 int docLayoutStackedStrip( BufferItem * cellNode,
594 BlockFrame * bf,
595 const LayoutJob * lj,
596 ParagraphLayoutJob * plj )
597 {
598 LayoutPosition lpBefore;
599
600 int prevAdvanced= 1;
601 int advanceAnyway= 0;
602 int stopCode= FORMATstopREADY;
603 int prevStopCode= FORMATstopREADY;
604
605 lpBefore= plj->pljPos.plpPos;
606
607 /* 1 */
608 if ( docLayoutStripChildren( &stopCode, plj, bf, lj, cellNode ) )
609 { LDEB(1); return -1; }
610
611 /* 2 */
612 while( stopCode != FORMATstopREADY &&
613 stopCode != FORMATstopFRAME_FULL )
614 {
615 int advanced;
616 const int belowText= 0;
617 LayoutPosition lpBelowNotes;
618
619 /* 3 */
620 if ( BF_HAS_FOOTNOTES( bf ) &&
621 ( cellNode->biTreeType == DOCinBODY ||
622 cellNode->biTreeType == DOCinENDNOTE ) &&
623 ! bf->bfFootnotesPlaced &&
624 docLayoutFootnotesForColumn( &lpBelowNotes,
625 &(plj->pljPos.plpPos), bf, belowText, lj ) )
626 { LDEB(1); return -1; }
627
628 /* 4 */
629 docCommitStripLayout( &advanced, advanceAnyway, plj,
630 lpBefore.lpPage, lpBefore.lpColumn,
631 cellNode );
632
633 /* 5 */
634 switch( stopCode )
635 {
636 case FORMATstopBLOCK_FULL:
637 case FORMATstopCOLUMN_BREAK:
638 docLayoutToNextColumn( &(plj->pljPos.plpPos), bf, cellNode, lj );
639 break;
640
641 case FORMATstopPAGE_BREAK:
642 docLayoutToNextColumn( &(plj->pljPos.plpPos), bf, cellNode, lj );
643 while( plj->pljPos.plpPos.lpColumn > 0 )
644 {
645 docLayoutToNextColumn( &(plj->pljPos.plpPos),
646 bf, cellNode, lj );
647 }
648 break;
649
650 default:
651 break;
652 }
653
654 if ( ! advanced )
655 {
656 if ( ! prevAdvanced )
657 {
658 if ( advanceAnyway )
659 {
660 LLLDEB(prevAdvanced,advanced,advanceAnyway);
661 LLDEB(prevStopCode,stopCode);
662 RECTDEB(&(plj->pljPos.plpParagraphFrame.pfParaContentRect));
663
664 docStripLayoutNextChild( &(plj->pljPos) );
665 docSetLayoutProgress( &(plj->pljPos0), &(plj->pljPos) );
666 advanced= 1;
667
668 if ( docLayoutStripDone( &(plj->pljPos), plj ) )
669 { break; }
670 }
671 else{ advanceAnyway= 1; }
672 }
673 }
674
675 prevAdvanced= advanced;
676
677 /* 6 */
678 docCellStripFrame( cellNode, bf, plj );
679
680 /* 7 */
681 lpBefore= plj->pljPos.plpPos;
682
683 prevStopCode= stopCode;
684 if ( docLayoutStripChildren( &stopCode, plj, bf, lj, cellNode ) )
685 { LDEB(1); return -1; }
686 }
687
688 if ( cellNode->biChildCount > 0 )
689 { cellNode->biTopPosition= cellNode->biChildren[0]->biTopPosition; }
690
691 return 0;
692 }
693
694 /************************************************************************/
695 /* */
696 /* Calculate the formatting frame for a paragraph layout. */
697 /* */
698 /************************************************************************/
699
docCellStripFrame(BufferItem * cellNode,const BlockFrame * bf,ParagraphLayoutJob * plj)700 void docCellStripFrame( BufferItem * cellNode,
701 const BlockFrame * bf,
702 ParagraphLayoutJob * plj )
703
704 {
705 BufferItem * childNode;
706 ParagraphFrame * pf= &(plj->pljPos.plpParagraphFrame);
707
708 childNode= cellNode->biChildren[plj->pljPos.pspChild];
709
710 if ( childNode->biLevel == DOClevPARA )
711 { docParagraphFrameTwips( pf, bf, childNode ); }
712 else{ docCellFrameTwips( pf, bf, cellNode ); }
713
714 return;
715 }
716
717