xref: /trunk/main/sw/source/core/text/widorp.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 
32 #include "hintids.hxx"
33 
34 #include "layfrm.hxx"
35 #include "ftnboss.hxx"
36 #include "ndtxt.hxx"
37 #include "paratr.hxx"
38 #include <editeng/orphitem.hxx>
39 #include <editeng/widwitem.hxx>
40 #include <editeng/keepitem.hxx>
41 #include <editeng/spltitem.hxx>
42 #include <frmatr.hxx>
43 #include <txtftn.hxx>
44 #include <fmtftn.hxx>
45 #include <rowfrm.hxx>
46 
47 #include "txtcfg.hxx"
48 #include "widorp.hxx"
49 #include "txtfrm.hxx"
50 #include "itrtxt.hxx"
51 #include "sectfrm.hxx"  //SwSectionFrm
52 #include "ftnfrm.hxx"
53 
54 #undef WIDOWTWIPS
55 
56 
57 /*************************************************************************
58  *                  inline IsNastyFollow()
59  *************************************************************************/
60 // Ein Follow, der auf der selben Seite steht, wie sein Master ist nasty.
61 inline sal_Bool IsNastyFollow( const SwTxtFrm *pFrm )
62 {
63     ASSERT( !pFrm->IsFollow() || !pFrm->GetPrev() ||
64             ((const SwTxtFrm*)pFrm->GetPrev())->GetFollow() == pFrm,
65             "IsNastyFollow: Was ist denn hier los?" );
66     return  pFrm->IsFollow() && pFrm->GetPrev();
67 }
68 
69 /*************************************************************************
70  *                  SwTxtFrmBreak::SwTxtFrmBreak()
71  *************************************************************************/
72 
73 SwTxtFrmBreak::SwTxtFrmBreak( SwTxtFrm *pNewFrm, const SwTwips nRst )
74     : nRstHeight(nRst), pFrm(pNewFrm)
75 {
76     SWAP_IF_SWAPPED( pFrm )
77     SWRECTFN( pFrm )
78     nOrigin = (pFrm->*fnRect->fnGetPrtTop)();
79     SwSectionFrm* pSct;
80     bKeep = !pFrm->IsMoveable() || IsNastyFollow( pFrm ) ||
81             ( pFrm->IsInSct() && (pSct=pFrm->FindSctFrm())->Lower()->IsColumnFrm()
82               && !pSct->MoveAllowed( pFrm ) ) ||
83             !pFrm->GetTxtNode()->GetSwAttrSet().GetSplit().GetValue() ||
84             pFrm->GetTxtNode()->GetSwAttrSet().GetKeep().GetValue();
85     bBreak = sal_False;
86 
87     if( !nRstHeight && !pFrm->IsFollow() && pFrm->IsInFtn() && pFrm->HasPara() )
88     {
89         nRstHeight = pFrm->GetFtnFrmHeight();
90         nRstHeight += (pFrm->Prt().*fnRect->fnGetHeight)() -
91                       (pFrm->Frm().*fnRect->fnGetHeight)();
92         if( nRstHeight < 0 )
93             nRstHeight = 0;
94     }
95 
96     UNDO_SWAP( pFrm )
97 }
98 
99 /* BP 18.6.93: Widows.
100  * Im Gegensatz zur ersten Implementierung werden die Widows nicht
101  * mehr vorausschauend berechnet, sondern erst beim Formatieren des
102  * gesplitteten Follows festgestellt. Im Master faellt die Widows-
103  * Berechnung also generell weg (nWidows wird manipuliert).
104  * Wenn der Follow feststellt, dass die Widowsregel zutrifft,
105  * verschickt er an seinen Vorgaenger ein Prepare.
106  * Ein besonderes Problem ergibt sich, wenn die Widows zuschlagen,
107  * aber im Master noch ein paar Zeilen zur Verfuegung stehen.
108  *
109  */
110 
111 /*************************************************************************
112  *                  SwTxtFrmBreak::IsInside()
113  *************************************************************************/
114 
115 /* BP(22.07.92): Berechnung von Witwen und Waisen.
116  * Die Methode liefert sal_True zurueck, wenn eine dieser Regelung zutrifft.
117  *
118  * Eine Schwierigkeit gibt es im Zusammenhang mit Widows und
119  * unterschiedlichen Formaten zwischen Master- und Folgeframes:
120  * Beispiel: Wenn die erste Spalte 3cm und die zweite 4cm breit ist
121  * und Widows auf sagen wir 3 gesetzt ist, so ist erst bei der Formatierung
122  * des Follows entscheidbar, ob die Widowsbedingung einhaltbar ist oder
123  * nicht. Leider ist davon abhaengig, ob der Absatz als Ganzes auf die
124  * naechste Seite rutscht.
125  */
126 
127 sal_Bool SwTxtFrmBreak::IsInside( SwTxtMargin &rLine ) const
128 {
129     sal_Bool bFit = sal_False;
130 
131     SWAP_IF_SWAPPED( pFrm )
132     SWRECTFN( pFrm )
133     // nOrigin is an absolut value, rLine referes to the swapped situation.
134 
135     SwTwips nTmpY;
136     if ( pFrm->IsVertical() )
137         nTmpY = pFrm->SwitchHorizontalToVertical( rLine.Y() + rLine.GetLineHeight() );
138     else
139         nTmpY = rLine.Y() + rLine.GetLineHeight();
140 
141     SwTwips nLineHeight = (*fnRect->fnYDiff)( nTmpY , nOrigin );
142 
143     // 7455 und 6114: Raum fuer die Umrandung unten einkalkulieren.
144     nLineHeight += (pFrm->*fnRect->fnGetBottomMargin)();
145 
146     if( nRstHeight )
147         bFit = nRstHeight >= nLineHeight;
148     else
149     {
150         // Der Frm besitzt eine Hoehe, mit der er auf die Seite passt.
151         SwTwips nHeight =
152             (*fnRect->fnYDiff)( (pFrm->GetUpper()->*fnRect->fnGetPrtBottom)(), nOrigin );
153         // Wenn sich alles innerhalb des bestehenden Frames abspielt,
154         // ist das Ergebnis sal_True;
155         bFit = nHeight >= nLineHeight;
156 
157         // --> OD #i103292#
158         if ( !bFit )
159         {
160             if ( rLine.GetNext() &&
161                  pFrm->IsInTab() && !pFrm->GetFollow() && !pFrm->GetIndNext() )
162             {
163                 // add additional space taken as lower space as last content in a table
164                 // for all text lines except the last one.
165                 nHeight += pFrm->CalcAddLowerSpaceAsLastInTableCell();
166                 bFit = nHeight >= nLineHeight;
167             }
168         }
169         // <--
170         if( !bFit )
171         {
172             // Die LineHeight sprengt die aktuelle Frm-Hoehe.
173             // Nun rufen wir ein Probe-Grow, um zu ermitteln, ob der
174             // Frame um den gewuenschten Bereich wachsen wuerde.
175             nHeight += pFrm->GrowTst( LONG_MAX );
176 
177             // Das Grow() returnt die Hoehe, um die der Upper des TxtFrm
178             // den TxtFrm wachsen lassen wuerde.
179             // Der TxtFrm selbst darf wachsen wie er will.
180             bFit = nHeight >= nLineHeight;
181         }
182     }
183 
184     UNDO_SWAP( pFrm );
185 
186     return bFit;
187 }
188 
189 /*************************************************************************
190  *                  SwTxtFrmBreak::IsBreakNow()
191  *************************************************************************/
192 
193 sal_Bool SwTxtFrmBreak::IsBreakNow( SwTxtMargin &rLine )
194 {
195     SWAP_IF_SWAPPED( pFrm )
196 
197     // bKeep ist staerker als IsBreakNow()
198     // Ist noch genug Platz ?
199     if( bKeep || IsInside( rLine ) )
200         bBreak = sal_False;
201     else
202     {
203         /* Diese Klasse geht davon aus, dass der SwTxtMargin von Top nach Bottom
204          * durchgearbeitet wird. Aus Performancegruenden wird in folgenden
205          * Faellen der Laden fuer das weitere Aufspalten dicht gemacht:
206          * Wenn eine einzige Zeile nicht mehr passt.
207          * Sonderfall: bei DummyPortions ist LineNr == 1, obwohl wir splitten
208          * wollen.
209          */
210         // 6010: DropLines mit einbeziehen
211 
212         sal_Bool bFirstLine = 1 == rLine.GetLineNr() && !rLine.GetPrev();
213         bBreak = sal_True;
214         if( ( bFirstLine && pFrm->GetIndPrev() )
215             || ( rLine.GetLineNr() <= rLine.GetDropLines() ) )
216         {
217             bKeep = sal_True;
218             bBreak = sal_False;
219         }
220         else if(bFirstLine && pFrm->IsInFtn() && !pFrm->FindFtnFrm()->GetPrev())
221         {
222             SwLayoutFrm* pTmp = pFrm->FindFtnBossFrm()->FindBodyCont();
223             if( !pTmp || !pTmp->Lower() )
224                 bBreak = sal_False;
225         }
226     }
227 
228     UNDO_SWAP( pFrm )
229 
230     return bBreak;
231 }
232 
233 // OD 2004-02-27 #106629# - no longer inline
234 void SwTxtFrmBreak::SetRstHeight( const SwTxtMargin &rLine )
235 {
236     // OD, FME 2004-02-27 #106629# - consider bottom margin
237     SWRECTFN( pFrm )
238 
239     nRstHeight = (pFrm->*fnRect->fnGetBottomMargin)();
240 
241     if ( bVert )
242     //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
243     {
244         if ( pFrm->IsVertLR() )
245             nRstHeight = (*fnRect->fnYDiff)( pFrm->SwitchHorizontalToVertical( rLine.Y() ) , nOrigin );
246         else
247             nRstHeight += nOrigin - pFrm->SwitchHorizontalToVertical( rLine.Y() );
248     }
249     else
250         nRstHeight += rLine.Y() - nOrigin;
251 }
252 
253 /*************************************************************************
254  *                  WidowsAndOrphans::WidowsAndOrphans()
255  *************************************************************************/
256 
257 WidowsAndOrphans::WidowsAndOrphans( SwTxtFrm *pNewFrm, const SwTwips nRst,
258     sal_Bool bChkKeep   )
259     : SwTxtFrmBreak( pNewFrm, nRst ), nWidLines( 0 ), nOrphLines( 0 )
260 {
261     SWAP_IF_SWAPPED( pFrm )
262 
263     if( bKeep )
264     {
265         // 5652: bei Absaetzen, die zusammengehalten werden sollen und
266         // groesser sind als die Seite wird bKeep aufgehoben.
267         if( bChkKeep && !pFrm->GetPrev() && !pFrm->IsInFtn() &&
268             pFrm->IsMoveable() &&
269             ( !pFrm->IsInSct() || pFrm->FindSctFrm()->MoveAllowed(pFrm) ) )
270             bKeep = sal_False;
271         //Auch bei gesetztem Keep muessen Orphans beachtet werden,
272         //z.B. bei verketteten Rahmen erhaelt ein Follow im letzten Rahmen ein Keep,
273         //da er nicht (vorwaerts) Moveable ist,
274         //er darf aber trotzdem vom Master Zeilen anfordern wg. der Orphanregel.
275         if( pFrm->IsFollow() )
276             nWidLines = pFrm->GetTxtNode()->GetSwAttrSet().GetWidows().GetValue();
277     }
278     else
279     {
280         const SwAttrSet& rSet = pFrm->GetTxtNode()->GetSwAttrSet();
281         const SvxOrphansItem  &rOrph = rSet.GetOrphans();
282         if ( rOrph.GetValue() > 1 )
283             nOrphLines = rOrph.GetValue();
284         if ( pFrm->IsFollow() )
285             nWidLines = rSet.GetWidows().GetValue();
286 
287     }
288 
289     if ( bKeep || nWidLines || nOrphLines )
290     {
291         bool bResetFlags = false;
292 
293         if ( pFrm->IsInTab() )
294         {
295             // For compatibility reasons, we disable Keep/Widows/Orphans
296             // inside splittable row frames:
297             if ( pFrm->GetNextCellLeaf( MAKEPAGE_NONE ) || pFrm->IsInFollowFlowRow() )
298             {
299                 const SwFrm* pTmpFrm = pFrm->GetUpper();
300                 while ( !pTmpFrm->IsRowFrm() )
301                     pTmpFrm = pTmpFrm->GetUpper();
302                 if ( static_cast<const SwRowFrm*>(pTmpFrm)->IsRowSplitAllowed() )
303                     bResetFlags = true;
304             }
305         }
306 
307         if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
308         {
309             // Innerhalb von Fussnoten gibt es gute Gruende, das Keep-Attribut und
310             // die Widows/Orphans abzuschalten.
311             SwFtnFrm *pFtn = pFrm->FindFtnFrm();
312             sal_Bool bFt = !pFtn->GetAttr()->GetFtn().IsEndNote();
313             if( !pFtn->GetPrev() &&
314                 pFtn->FindFtnBossFrm( bFt ) != pFtn->GetRef()->FindFtnBossFrm( bFt )
315                 && ( !pFrm->IsInSct() || pFrm->FindSctFrm()->MoveAllowed(pFrm) ) )
316             {
317                 bResetFlags = true;
318             }
319         }
320 
321         if ( bResetFlags )
322         {
323             bKeep = sal_False;
324             nOrphLines = 0;
325             nWidLines = 0;
326         }
327     }
328 
329     UNDO_SWAP( pFrm )
330 }
331 
332 /*************************************************************************
333  *                  WidowsAndOrphans::FindBreak()
334  *************************************************************************/
335 
336 /* Die Find*-Methoden suchen nicht nur, sondern stellen den SwTxtMargin auf
337  * die Zeile ein, wo der Absatz gebrochen werden soll und kuerzen ihn dort.
338  * FindBreak()
339  */
340 
341 sal_Bool WidowsAndOrphans::FindBreak( SwTxtFrm *pFrame, SwTxtMargin &rLine,
342     sal_Bool bHasToFit )
343 {
344     // OD 2004-02-25 #i16128# - Why member <pFrm> _*and*_ parameter <pFrame>??
345     // Thus, assertion on situation, that these are different to figure out why.
346     ASSERT( pFrm == pFrame, "<WidowsAndOrphans::FindBreak> - pFrm != pFrame" );
347 
348     SWAP_IF_SWAPPED( pFrm )
349 
350     sal_Bool bRet = sal_True;
351     MSHORT nOldOrphans = nOrphLines;
352     if( bHasToFit )
353         nOrphLines = 0;
354     rLine.Bottom();
355     // OD 2004-02-25 #i16128# - method renamed
356     if( !IsBreakNowWidAndOrp( rLine ) )
357         bRet = sal_False;
358     if( !FindWidows( pFrame, rLine ) )
359     {
360         sal_Bool bBack = sal_False;
361         // OD 2004-02-25 #i16128# - method renamed
362         while( IsBreakNowWidAndOrp( rLine ) )
363         {
364             if( rLine.PrevLine() )
365                 bBack = sal_True;
366             else
367                 break;
368         }
369         // Eigentlich werden bei HasToFit Schusterjungen (Orphans) nicht
370         // beruecksichtigt, wenn allerdings Dummy-Lines im Spiel sind und
371         // die Orphansregel verletzt wird, machen wir mal eine Ausnahme:
372         // Wir lassen einfach eine Dummyline zurueck und wandern mit dem Text
373         // komplett auf die naechste Seite/Spalte.
374         if( rLine.GetLineNr() <= nOldOrphans &&
375             rLine.GetInfo().GetParaPortion()->IsDummy() &&
376             ( ( bHasToFit && bRet ) || IsBreakNow( rLine ) ) )
377             rLine.Top();
378 
379         rLine.TruncLines( sal_True );
380         bRet = bBack;
381     }
382     nOrphLines = nOldOrphans;
383 
384     UNDO_SWAP( pFrm )
385 
386     return bRet;
387 }
388 
389 /*************************************************************************
390  *                  WidowsAndOrphans::FindWidows()
391  *************************************************************************/
392 
393 /*  FindWidows positioniert den SwTxtMargin des Masters auf die umzubrechende
394  *  Zeile, indem der Follow formatiert und untersucht wird.
395  *  Liefert sal_True zurueck, wenn die Widows-Regelung in Kraft tritt,
396  *  d.h. der Absatz _zusammengehalten_ werden soll !
397  */
398 
399 sal_Bool WidowsAndOrphans::FindWidows( SwTxtFrm *pFrame, SwTxtMargin &rLine )
400 {
401     ASSERT( ! pFrame->IsVertical() || ! pFrame->IsSwapped(),
402             "WidowsAndOrphans::FindWidows with swapped frame" )
403 
404     if( !nWidLines || !pFrame->IsFollow() )
405         return sal_False;
406 
407     rLine.Bottom();
408 
409     // Wir koennen noch was abzwacken
410     SwTxtFrm *pMaster = pFrame->FindMaster();
411     ASSERT(pMaster, "+WidowsAndOrphans::FindWidows: Widows in a master?");
412     if( !pMaster )
413         return sal_False;
414 
415     // 5156: Wenn die erste Zeile des Follows nicht passt, wird der Master
416     // wohl voll mit Dummies sein. In diesem Fall waere ein PREP_WIDOWS fatal.
417     if( pMaster->GetOfst() == pFrame->GetOfst() )
418         return sal_False;
419 
420     // Resthoehe des Masters
421     SWRECTFN( pFrame )
422 
423     const SwTwips nDocPrtTop = (pFrame->*fnRect->fnGetPrtTop)();
424     SwTwips nOldHeight;
425     SwTwips nTmpY = rLine.Y() + rLine.GetLineHeight();
426 
427     if ( bVert )
428     {
429         nTmpY = pFrame->SwitchHorizontalToVertical( nTmpY );
430         nOldHeight = -(pFrame->Prt().*fnRect->fnGetHeight)();
431     }
432     else
433         nOldHeight = (pFrame->Prt().*fnRect->fnGetHeight)();
434 
435     const SwTwips nChg = (*fnRect->fnYDiff)( nTmpY, nDocPrtTop + nOldHeight );
436 
437     // Unterhalb der Widows-Schwelle...
438     if( rLine.GetLineNr() >= nWidLines )
439     {
440         // 8575: Follow to Master I
441         // Wenn der Follow *waechst*, so besteht fuer den Master die Chance,
442         // Zeilen entgegenzunehmen, die er vor Kurzem gezwungen war an den
443         // Follow abzugeben: Prepare(Need); diese Abfrage unterhalb von nChg!
444         // (0W, 2O, 2M, 2F) + 1F = 3M, 2F
445         if( rLine.GetLineNr() > nWidLines && pFrame->IsJustWidow() )
446         {
447             // Wenn der Master gelockt ist, so hat er vermutlich gerade erst
448             // eine Zeile an uns abgegeben, diese geben nicht zurueck, nur
449             // weil bei uns daraus mehrere geworden sind (z.B. durch Rahmen).
450             if( !pMaster->IsLocked() && pMaster->GetUpper() )
451             {
452                 const SwTwips nTmpRstHeight = (pMaster->Frm().*fnRect->fnBottomDist)
453                             ( (pMaster->GetUpper()->*fnRect->fnGetPrtBottom)() );
454                 if ( nTmpRstHeight >=
455                      SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) )
456                 {
457                     pMaster->Prepare( PREP_ADJUST_FRM );
458                     pMaster->_InvalidateSize();
459                     pMaster->InvalidatePage();
460                 }
461             }
462 
463             pFrame->SetJustWidow( sal_False );
464         }
465         return sal_False;
466     }
467 
468     // 8575: Follow to Master II
469     // Wenn der Follow *schrumpft*, so besteht fuer den Master die Chance,
470     // den kompletten Orphan zu inhalieren.
471     // (0W, 2O, 2M, 1F) - 1F = 3M, 0F     -> PREP_ADJUST_FRM
472     // (0W, 2O, 3M, 2F) - 1F = 2M, 2F     -> PREP_WIDOWS
473 
474     if( 0 > nChg && !pMaster->IsLocked() && pMaster->GetUpper() )
475     {
476         SwTwips nTmpRstHeight = (pMaster->Frm().*fnRect->fnBottomDist)
477                              ( (pMaster->GetUpper()->*fnRect->fnGetPrtBottom)() );
478         if( nTmpRstHeight >= SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) )
479         {
480             pMaster->Prepare( PREP_ADJUST_FRM );
481             pMaster->_InvalidateSize();
482             pMaster->InvalidatePage();
483             pFrame->SetJustWidow( sal_False );
484             return sal_False;
485         }
486     }
487 
488     // Master to Follow
489     // Wenn der Follow nach seiner Formatierung weniger Zeilen enthaelt
490     // als Widows, so besteht noch die Chance, einige Zeilen des Masters
491     // abzuzwacken. Wenn dadurch die Orphans-Regel des Masters in Kraft
492     // tritt muss im CalcPrep() des Master-Frame der Frame so vergroessert
493     // werden, dass er nicht mehr auf seine urspruengliche Seite passt.
494     // Wenn er noch ein paar Zeilen entbehren kann, dann muss im CalcPrep()
495     // ein Shrink() erfolgen, der Follow mit dem Widows rutscht dann auf
496     // die Seite des Masters, haelt sich aber zusammen, so dass er (endlich)
497     // auf die naechste Seite rutscht. - So die Theorie!
498 
499 
500     // Wir fordern nur noch ein Zeile zur Zeit an, weil eine Zeile des Masters
501     // bei uns durchaus mehrere Zeilen ergeben koennten.
502     // Dafuer behaelt CalcFollow solange die Kontrolle, bis der Follow alle
503     // notwendigen Zeilen bekommen hat.
504     MSHORT nNeed = 1; // frueher: nWidLines - rLine.GetLineNr();
505 
506     // Special case: Master cannot give lines to follow
507     // --> FME 2008-09-16 #i91421#
508     if ( !pMaster->GetIndPrev() )
509     {
510         sal_uLong nLines = pMaster->GetThisLines();
511         if(nLines == 0 && pMaster->HasPara())
512         {
513             const SwParaPortion *pMasterPara = pMaster->GetPara();
514             if(pMasterPara && pMasterPara->GetNext())
515                 nLines = 2;
516         }
517         if( nLines <= nNeed )
518             return sal_False;
519     }
520 
521     pMaster->Prepare( PREP_WIDOWS, (void*)&nNeed );
522     return sal_True;
523 }
524 
525 /*************************************************************************
526  *                  WidowsAndOrphans::WouldFit()
527  *************************************************************************/
528 
529 sal_Bool WidowsAndOrphans::WouldFit( SwTxtMargin &rLine, SwTwips &rMaxHeight, sal_Bool bTst )
530 {
531     // Here it does not matter, if pFrm is swapped or not.
532     // IsInside() takes care for itself
533 
534     // Wir erwarten, dass rLine auf der letzten Zeile steht!!
535     ASSERT( !rLine.GetNext(), "WouldFit: aLine::Bottom missed!" );
536     MSHORT nLineCnt = rLine.GetLineNr();
537 
538     // Erstmal die Orphansregel und den Initialenwunsch erfuellen ...
539     const MSHORT nMinLines = Max( GetOrphansLines(), rLine.GetDropLines() );
540     if ( nLineCnt < nMinLines )
541         return sal_False;
542 
543     rLine.Top();
544     SwTwips nLineSum = rLine.GetLineHeight();
545 
546     while( nMinLines > rLine.GetLineNr() )
547     {
548         DBG_LOOP;
549         if( !rLine.NextLine() )
550             return sal_False;
551         nLineSum += rLine.GetLineHeight();
552     }
553 
554     // Wenn wir jetzt schon nicht mehr passen ...
555     if( !IsInside( rLine ) )
556         return sal_False;
557 
558     // Jetzt noch die Widows-Regel ueberpruefen
559     if( !nWidLines && !pFrm->IsFollow() )
560     {
561         // I.A. brauchen Widows nur ueberprueft werden, wenn wir ein Follow
562         // sind. Bei WouldFit muss aber auch fuer den Master die Regel ueber-
563         // prueft werden, weil wir ja gerade erst die Trennstelle ermitteln.
564         // Im Ctor von WidowsAndOrphans wurde nWidLines aber nur fuer Follows
565         // aus dem AttrSet ermittelt, deshalb holen wir es hier nach:
566         const SwAttrSet& rSet = pFrm->GetTxtNode()->GetSwAttrSet();
567         nWidLines = rSet.GetWidows().GetValue();
568     }
569 
570     // Sind nach Orphans/Initialen noch genug Zeilen fuer die Widows uebrig?
571     // #111937#: If we are currently doing a test formatting, we may not
572     // consider the widows rule for two reasons:
573     // 1. The columns may have different widths.
574     //    Widow lines would have wrong width.
575     // 2. Test formatting is only done up to the given space.
576     //    we do not have any lines for widows at all.
577     if( bTst || nLineCnt - nMinLines >= GetWidowsLines() )
578     {
579         if( rMaxHeight >= nLineSum )
580         {
581             rMaxHeight -= nLineSum;
582             return sal_True;
583         }
584     }
585     return sal_False;
586 }
587 
588