merge the formfield patch from ooo-build
[ooovba.git] / sw / source / core / text / itrform2.cxx
blob88e62b9f3f66cd345337101e38a0aac351256795
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: itrform2.cxx,v $
10 * $Revision: 1.107.20.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
34 #include "hintids.hxx"
36 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
37 #include <com/sun/star/i18n/ScriptType.hdl>
38 #endif
39 #include <svx/lspcitem.hxx>
40 #include <txtftn.hxx>
41 #include <fmtftn.hxx>
42 #include <ftninfo.hxx>
43 #include <charfmt.hxx>
44 #include <svx/charrotateitem.hxx>
45 #include <layfrm.hxx> // GetFrmRstHeight, etc
46 #include <viewsh.hxx>
47 #include <viewopt.hxx> // SwViewOptions
48 #include <paratr.hxx> // SwFmtDrop
49 #include <txtcfg.hxx>
50 #include <itrform2.hxx>
51 #include <porrst.hxx>
52 #include <portab.hxx> // pLastTab->
53 #include <porfly.hxx> // CalcFlyWidth
54 #include <portox.hxx> // WhichTxtPortion
55 #include <porref.hxx> // WhichTxtPortion
56 #include <porfld.hxx> // SwNumberPortion fuer CalcAscent()
57 #include <porftn.hxx> // SwFtnPortion
58 #include <porhyph.hxx>
59 #include <guess.hxx>
60 #include <blink.hxx> // pBlink
61 #include <ftnfrm.hxx> // WhichFirstPortion() -> mal Verlagern.
62 #include <redlnitr.hxx> // SwRedlineItr
63 #include <pagefrm.hxx>
64 #include <pagedesc.hxx> // SwPageDesc
65 #include <tgrditem.hxx>
66 #include <doc.hxx> // SwDoc
67 #include <pormulti.hxx> // SwMultiPortion
68 #define _SVSTDARR_LONGS
69 #include <svtools/svstdarr.hxx>
70 #include <unotools/charclass.hxx>
72 #if OSL_DEBUG_LEVEL > 1
73 #include <ndtxt.hxx> // pSwpHints, Ausgabeoperator
74 #endif
76 using namespace ::com::sun::star;
78 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
79 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos );
81 #define MAX_TXTPORLEN 300
83 inline void ClearFly( SwTxtFormatInfo &rInf )
85 if( rInf.GetFly() )
87 delete rInf.GetFly();
88 rInf.SetFly(0);
92 /*************************************************************************
93 * SwTxtFormatter::CtorInitTxtFormatter()
94 *************************************************************************/
96 void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf )
98 CtorInitTxtPainter( pNewFrm, pNewInf );
99 pInf = pNewInf;
100 pDropFmt = GetInfo().GetDropFmt();
101 pMulti = NULL;
103 bOnceMore = sal_False;
104 bChanges = sal_False;
105 bTruncLines = sal_False;
106 nCntEndHyph = 0;
107 nCntMidHyph = 0;
108 nLeftScanIdx = STRING_LEN;
109 nRightScanIdx = 0;
110 m_nHintEndIndex = 0;
112 if( nStart > GetInfo().GetTxt().Len() )
114 ASSERT( !this, "+SwTxtFormatter::CTOR: bad offset" );
115 nStart = GetInfo().GetTxt().Len();
120 /*************************************************************************
121 * SwTxtFormatter::DTOR
122 *************************************************************************/
124 SwTxtFormatter::~SwTxtFormatter()
126 // Auesserst unwahrscheinlich aber denkbar.
127 // z.B.: Feld spaltet sich auf, Widows schlagen zu
128 if( GetInfo().GetRest() )
130 delete GetInfo().GetRest();
131 GetInfo().SetRest(0);
135 /*************************************************************************
136 * SwTxtFormatter::Insert()
137 *************************************************************************/
139 void SwTxtFormatter::Insert( SwLineLayout *pLay )
141 // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element.
142 if ( pCurr )
144 pLay->SetNext( pCurr->GetNext() );
145 pCurr->SetNext( pLay );
147 else
148 pCurr = pLay;
151 /*************************************************************************
152 * SwTxtFormatter::GetFrmRstHeight()
153 *************************************************************************/
155 KSHORT SwTxtFormatter::GetFrmRstHeight() const
157 // 8725: Uns interessiert die Resthoehe bezogen auf die Seite.
158 // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht
159 // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn
160 // gerufen.
161 // Falsch: const SwFrm *pUpper = pFrm->GetUpper();
162 const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm();
163 const SwTwips nHeight = pPage->Frm().Top()
164 + pPage->Prt().Top()
165 + pPage->Prt().Height() - Y();
166 if( 0 > nHeight )
167 return pCurr->Height();
168 else
169 return KSHORT( nHeight );
172 /*************************************************************************
173 * SwTxtFormatter::UnderFlow()
174 *************************************************************************/
176 SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf )
178 // Werte sichern und rInf initialisieren.
179 SwLinePortion *pUnderFlow = rInf.GetUnderFlow();
180 if( !pUnderFlow )
181 return 0;
183 // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der
184 // naechsten Zeile durchaus noch einmal drankommen koennen.
185 // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt.
187 const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos();
188 const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos();
190 // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF
191 // 3983: Nicht ClearFly(rInf) !
192 SwFlyPortion *pFly = rInf.GetFly();
193 rInf.SetFly( 0 );
195 FeedInf( rInf );
196 rInf.SetLast( pCurr );
197 // pUnderFlow braucht nicht deletet werden, weil es im folgenden
198 // Truncate() untergehen wird.
199 rInf.SetUnderFlow(0);
200 rInf.SetSoftHyphPos( nSoftHyphPos );
201 rInf.SetUnderScorePos( nUnderScorePos );
202 rInf.SetPaintOfst( GetLeftMargin() );
204 // Wir suchen die Portion mit der Unterlaufposition
205 SwLinePortion *pPor = pCurr->GetFirstPortion();
206 if( pPor != pUnderFlow )
208 // pPrev wird die letzte Portion vor pUnderFlow,
209 // die noch eine echte Breite hat.
210 // Ausnahme: SoftHyphPortions duerfen dabei natuerlich
211 // nicht vergessen werden, obwohl sie keine Breite haben.
212 SwLinePortion *pTmpPrev = pPor;
213 while( pPor && pPor != pUnderFlow )
215 DBG_LOOP;
216 if( !pPor->IsKernPortion() &&
217 ( pPor->Width() || pPor->IsSoftHyphPortion() ) )
219 while( pTmpPrev != pPor )
221 pTmpPrev->Move( rInf );
222 rInf.SetLast( pTmpPrev );
223 pTmpPrev = pTmpPrev->GetPortion();
224 ASSERT( pTmpPrev, "UnderFlow: Loosing control!" );
227 pPor = pPor->GetPortion();
229 pPor = pTmpPrev;
230 if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen
231 ( pPor->IsFlyPortion() || pPor->IsDropPortion() ||
232 pPor->IsFlyCntPortion() ) )
234 pPor->Move( rInf );
235 rInf.SetLast( pPor );
236 rInf.SetStopUnderFlow( sal_True );
237 pPor = pUnderFlow;
241 // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ?
242 ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" );
244 // OD 2004-05-26 #i29529# - correction: no delete of footnotes
245 // if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() )
246 // {
247 // SwLinePortion *pTmp = pPor->GetPortion();
248 // while( pTmp )
249 // {
250 // if( pTmp->IsFtnPortion() )
251 // ((SwFtnPortion*)pTmp)->ClearFtn();
252 // pTmp = pTmp->GetPortion();
253 // }
254 // }
256 /*-----------------14.12.94 09:45-------------------
257 * 9849: Schnellschuss
258 * --------------------------------------------------*/
259 if ( pPor==rInf.GetLast() )
261 // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich
262 // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber
263 // mehrere Zeilen geht und in der zweiten Zeile in einen Fly
264 // hineinlaeuft!
265 rInf.SetFly( pFly ); // wg. 28300
266 pPor->Truncate();
267 return pPor; // Reicht das?
269 /*---------------------------------------------------
270 * Ende des Schnellschusses wg. 9849
271 * --------------------------------------------------*/
273 // 4656: X + Width == 0 bei SoftHyph > Zeile ?!
274 if( !pPor || !(rInf.X() + pPor->Width()) )
276 delete pFly;
277 return 0;
280 // Vorbereitungen auf's Format()
281 // Wir muessen die Kette hinter pLast abknipsen, weil
282 // nach dem Format() ein Insert erfolgt.
283 SeekAndChg( rInf );
285 // line width is adjusted, so that pPor does not fit to current
286 // line anymore
287 rInf.Width( (USHORT)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) );
288 rInf.SetLen( pPor->GetLen() );
289 rInf.SetFull( sal_False );
290 if( pFly )
292 // Aus folgendem Grund muss die FlyPortion neu berechnet werden:
293 // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie
294 // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht,
295 // so hat die FlyPortion eine falsche Groesse/Fixsize.
296 rInf.SetFly( pFly );
297 CalcFlyWidth( rInf );
299 rInf.GetLast()->SetPortion(0);
301 // Eine Ausnahme bildet das SwLineLayout, dass sich beim
302 // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg:
303 if( rInf.GetLast() == pCurr )
305 if( pPor->InTxtGrp() && !pPor->InExpGrp() )
307 MSHORT nOldWhich = pCurr->GetWhichPor();
308 *(SwLinePortion*)pCurr = *pPor;
309 pCurr->SetPortion( pPor->GetPortion() );
310 pCurr->SetWhichPor( nOldWhich );
311 pPor->SetPortion( 0 );
312 delete pPor;
313 pPor = pCurr;
316 pPor->Truncate();
317 SwLinePortion *const pRest( rInf.GetRest() );
318 if (pRest && pRest->InFldGrp() &&
319 static_cast<SwFldPortion*>(pRest)->IsNoLength())
321 // HACK: decrement again, so we pick up the suffix in next line!
322 --m_nHintEndIndex;
324 delete pRest;
325 rInf.SetRest(0);
326 return pPor;
329 /*************************************************************************
330 * SwTxtFormatter::InsertPortion()
331 *************************************************************************/
333 void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf,
334 SwLinePortion *pPor ) const
336 // Die neue Portion wird eingefuegt,
337 // bei dem LineLayout ist allerdings alles anders...
338 if( pPor == pCurr )
340 if( pCurr->GetPortion() )
341 pPor = pCurr->GetPortion();
343 else
345 SwLinePortion *pLast = rInf.GetLast();
346 if( pLast->GetPortion() )
348 while( pLast->GetPortion() )
349 pLast = pLast->GetPortion();
350 rInf.SetLast( pLast );
352 pLast->Insert( pPor );
354 // Maxima anpassen:
355 if( pCurr->Height() < pPor->Height() )
356 pCurr->Height( pPor->Height() );
357 if( pCurr->GetAscent() < pPor->GetAscent() )
358 pCurr->SetAscent( pPor->GetAscent() );
361 // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate)
362 rInf.SetLast( pPor );
363 while( pPor )
365 DBG_LOOP;
366 pPor->Move( rInf );
367 rInf.SetLast( pPor );
368 pPor = pPor->GetPortion();
372 /*************************************************************************
373 * SwTxtFormatter::BuildPortion()
374 *************************************************************************/
376 void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf )
378 ASSERT( rInf.GetTxt().Len() < STRING_LEN,
379 "SwTxtFormatter::BuildPortions: bad text length in info" );
381 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
383 // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet.
384 // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt
385 // wird. In CalcAscent geschieht dies automatisch.
386 rInf.SetLast( pCurr );
387 rInf.ForcedLeftMargin( 0 );
389 ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" );
391 if( !pCurr->GetAscent() && !pCurr->Height() )
392 CalcAscent( rInf, pCurr );
394 SeekAndChg( rInf );
396 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
397 ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" );
398 CalcFlyWidth( rInf );
399 SwFlyPortion *pFly = rInf.GetFly();
400 if( pFly )
402 if ( 0 < pFly->Fix() )
403 ClearFly( rInf );
404 else
405 rInf.SetFull(sal_True);
408 SwLinePortion *pPor = NewPortion( rInf );
410 // Asian grid stuff
411 GETGRID( pFrm->FindPageFrm() )
412 const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() &&
413 GRID_LINES_CHARS == pGrid->GetGridType();
415 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
416 const USHORT nGridWidth = bHasGrid ?
417 GETGRIDWIDTH(pGrid,pDoc) : 0; //for textgrid refactor
419 // used for grid mode only:
420 // the pointer is stored, because after formatting of non-asian text,
421 // the width of the kerning portion has to be adjusted
422 SwKernPortion* pGridKernPortion = 0;
424 sal_Bool bFull;
425 SwTwips nUnderLineStart = 0;
426 rInf.Y( Y() );
428 while( pPor && !rInf.IsStop() )
430 ASSERT( rInf.GetLen() < STRING_LEN &&
431 rInf.GetIdx() <= rInf.GetTxt().Len(),
432 "SwTxtFormatter::BuildPortions: bad length in info" );
433 DBG_LOOP;
435 // We have to check the script for fields in order to set the
436 // correct nActual value for the font.
437 if( pPor->InFldGrp() )
438 ((SwFldPortion*)pPor)->CheckScript( rInf );
440 if( ! bHasGrid && rInf.HasScriptSpace() &&
441 rInf.GetLast() && rInf.GetLast()->InTxtGrp() &&
442 rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() )
444 BYTE nNxtActual = rInf.GetFont()->GetActual();
445 BYTE nLstActual = nNxtActual;
446 USHORT nLstHeight = (USHORT)rInf.GetFont()->GetHeight();
447 sal_Bool bAllowBefore = sal_False;
448 sal_Bool bAllowBehind = sal_False;
449 const CharClass& rCC = GetAppCharClass();
451 // are there any punctuation characters on both sides
452 // of the kerning portion?
453 if ( pPor->InFldGrp() )
455 XubString aAltTxt;
456 if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) &&
457 aAltTxt.Len() )
459 bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 );
461 const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont();
462 if ( pTmpFnt )
463 nNxtActual = pTmpFnt->GetActual();
466 else
467 bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() );
469 const SwLinePortion* pLast = rInf.GetLast();
470 if ( bAllowBehind && pLast )
472 if ( pLast->InFldGrp() )
474 XubString aAltTxt;
475 if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) &&
476 aAltTxt.Len() )
478 bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 );
480 const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont();
481 if ( pTmpFnt )
483 nLstActual = pTmpFnt->GetActual();
484 nLstHeight = (USHORT)pTmpFnt->GetHeight();
488 else if ( rInf.GetIdx() )
490 bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 );
491 // Note: ScriptType returns values in [1,4]
492 if ( bAllowBefore )
493 nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1;
496 nLstHeight /= 5;
497 // does the kerning portion still fit into the line?
498 if( bAllowBefore && ( nLstActual != nNxtActual ) &&
499 nLstHeight && rInf.X() + nLstHeight <= rInf.Width() )
501 SwKernPortion* pKrn =
502 new SwKernPortion( *rInf.GetLast(), nLstHeight,
503 pLast->InFldGrp() && pPor->InFldGrp() );
504 rInf.GetLast()->SetPortion( NULL );
505 InsertPortion( rInf, pKrn );
509 else if ( bHasGrid && ! pGridKernPortion && ! pMulti )
511 // insert a grid kerning portion
512 if ( ! pGridKernPortion )
513 pGridKernPortion = pPor->IsKernPortion() ?
514 (SwKernPortion*)pPor :
515 new SwKernPortion( *pCurr );
517 // if we have a new GridKernPortion, we initially calculate
518 // its size so that its ends on the grid
519 const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
520 const SwLayoutFrm* pBody = pPageFrm->FindBodyCont();
521 SWRECTFN( pPageFrm )
523 const long nGridOrigin = pBody ?
524 (pBody->*fnRect->fnGetPrtLeft)() :
525 (pPageFrm->*fnRect->fnGetPrtLeft)();
527 SwTwips nStartX = rInf.X() + GetLeftMargin();
528 if ( bVert )
530 Point aPoint( nStartX, 0 );
531 pFrm->SwitchHorizontalToVertical( aPoint );
532 nStartX = aPoint.Y();
535 const SwTwips nOfst = nStartX - nGridOrigin;
536 if ( nOfst )
538 const ULONG i = ( nOfst > 0 ) ?
539 ( ( nOfst - 1 ) / nGridWidth + 1 ) :
541 const SwTwips nKernWidth = i * nGridWidth - nOfst;
542 const SwTwips nRestWidth = rInf.Width() - rInf.X();
544 if ( nKernWidth <= nRestWidth )
545 pGridKernPortion->Width( (USHORT)nKernWidth );
548 if ( pGridKernPortion != pPor )
549 InsertPortion( rInf, pGridKernPortion );
552 // the multi-portion has it's own format function
553 if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) )
554 bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) );
555 else
556 bFull = pPor->Format( rInf );
558 if( rInf.IsRuby() && !rInf.GetRest() )
559 bFull = sal_True;
561 // if we are underlined, we store the beginning of this underlined
562 // segment for repaint optimization
563 if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart )
564 nUnderLineStart = GetLeftMargin() + rInf.X();
566 if ( pPor->IsFlyPortion() )
567 pCurr->SetFly( sal_True );
568 // some special cases, where we have to take care for the repaint
569 // offset:
570 // 1. Underlined portions due to special underline feature
571 // 2. Right Tab
572 // 3. BidiPortions
573 // 4. other Multiportions
574 // 5. DropCaps
575 // 6. Grid Mode
576 else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) &&
577 // 1. Underlined portions
578 nUnderLineStart &&
579 // reformat is at end of an underlined portion and next portion
580 // is not underlined
581 ( ( rInf.GetReformatStart() == rInf.GetIdx() &&
582 UNDERLINE_NONE == pFnt->GetUnderline()
583 ) ||
584 // reformat is inside portion and portion is underlined
585 ( rInf.GetReformatStart() >= rInf.GetIdx() &&
586 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() &&
587 UNDERLINE_NONE != pFnt->GetUnderline() ) ) )
588 rInf.SetPaintOfst( nUnderLineStart );
589 else if ( ! rInf.GetPaintOfst() &&
590 // 2. Right Tab
591 ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) ||
592 // 3. BidiPortions
593 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) ||
594 // 4. Multi Portion and 5. Drop Caps
595 ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) &&
596 rInf.GetReformatStart() >= rInf.GetIdx() &&
597 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() )
598 // 6. Grid Mode
599 || ( bHasGrid && SW_CJK != pFnt->GetActual() )
602 // we store the beginning of the critical portion as our
603 // paint offset
604 rInf.SetPaintOfst( GetLeftMargin() + rInf.X() );
606 // under one of these conditions we are allowed to delete the
607 // start of the underline portion
608 if ( IsUnderlineBreak( *pPor, *pFnt ) )
609 nUnderLineStart = 0;
611 if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() &&
612 ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) )
613 SetFlyInCntBase();
614 // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim
615 // naechsten Softhyphen wieder umgebrochen!
616 if ( !bFull )
618 rInf.ClrUnderFlow();
619 if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() &&
620 pPor->GetLen() && !pPor->InFldGrp() )
622 // The distance between two different scripts is set
623 // to 20% of the fontheight.
624 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
625 if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) &&
626 nTmp != rInf.GetTxt().Len() )
628 USHORT nDist = (USHORT)(rInf.GetFont()->GetHeight()/5);
630 if( nDist )
632 // we do not want a kerning portion if any end
633 // would be a punctuation character
634 const CharClass& rCC = GetAppCharClass();
635 if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) &&
636 rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) )
638 // does the kerning portion still fit into the line?
639 if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() )
640 new SwKernPortion( *pPor, nDist );
641 else
642 bFull = sal_True;
649 if ( bHasGrid && pPor != pGridKernPortion && ! pMulti )
651 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
652 const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
654 const BYTE nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() );
655 const BYTE nNextScript = nTmp >= rInf.GetTxt().Len() ?
656 SW_CJK :
657 SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo );
659 // snap non-asian text to grid if next portion is ASIAN or
660 // there are no more portions in this line
661 // be careful when handling an underflow event: the gridkernportion
662 // could have been deleted
663 if ( nRestWidth > 0 && SW_CJK != nCurrScript &&
664 ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) )
666 ASSERT( pGridKernPortion, "No GridKernPortion available" )
668 // calculate size
669 SwLinePortion* pTmpPor = pGridKernPortion->GetPortion();
670 USHORT nSumWidth = pPor->Width();
671 while ( pTmpPor )
673 nSumWidth = nSumWidth + pTmpPor->Width();
674 pTmpPor = pTmpPor->GetPortion();
677 const USHORT i = nSumWidth ?
678 ( nSumWidth - 1 ) / nGridWidth + 1 :
680 const SwTwips nTmpWidth = i * nGridWidth;
681 const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth),
682 nRestWidth );
683 const USHORT nKernWidth_1 = (USHORT)(nKernWidth / 2);
685 ASSERT( nKernWidth <= nRestWidth,
686 "Not enough space left for adjusting non-asian text in grid mode" )
688 pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
689 rInf.X( rInf.X() + nKernWidth_1 );
691 if ( ! bFull )
692 new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1),
693 sal_False, sal_True );
695 pGridKernPortion = 0;
697 else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() ||
698 pPor->IsFlyCntPortion() || pPor->InNumberGrp() ||
699 pPor->InFldGrp() || nCurrScript != nNextScript )
700 // next portion should snap to grid
701 pGridKernPortion = 0;
704 rInf.SetFull( bFull );
706 // Restportions von mehrzeiligen Feldern haben bisher noch
707 // nicht den richtigen Ascent.
708 if ( !pPor->GetLen() && !pPor->IsFlyPortion()
709 && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp()
710 && !pPor->IsMultiPortion() )
711 CalcAscent( rInf, pPor );
713 InsertPortion( rInf, pPor );
714 pPor = NewPortion( rInf );
717 if( !rInf.IsStop() )
719 // der letzte rechte, zentrierte, dezimale Tab
720 SwTabPortion *pLastTab = rInf.GetLastTab();
721 if( pLastTab )
722 pLastTab->FormatEOL( rInf );
723 else if( rInf.GetLast() && rInf.LastKernPortion() )
724 rInf.GetLast()->FormatEOL( rInf );
726 if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp()
727 && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() )
728 rInf.SetNumDone( sal_False );
730 // 3260, 3860: Fly auf jeden Fall loeschen!
731 ClearFly( rInf );
734 /*************************************************************************
735 * SwTxtFormatter::CalcAdjustLine()
736 *************************************************************************/
738 void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
740 if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti)
742 pCurrent->SetFormatAdj(sal_True);
743 if( IsFlyInCntBase() )
745 CalcAdjLine( pCurrent );
746 // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint
747 // auf jeden Fall umsetzen, deshalb bAllWays = sal_True
748 UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True );
753 /*************************************************************************
754 * SwTxtFormatter::CalcAscent()
755 *************************************************************************/
757 void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor )
759 if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() )
761 // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten,
762 // dann ist ihre Groesse unabhaengig von harten Attributierungen.
763 SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt;
764 SwFontSave aSave( rInf, pFldFnt );
765 ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
766 ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) );
768 // --> OD 2008-06-05 #i89179#
769 // tab portion representing the list tab of a list label gets the
770 // same height and ascent as the corresponding number portion
771 else if ( pPor->InTabGrp() && pPor->GetLen() == 0 &&
772 rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
773 static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() )
775 const SwLinePortion* pLast = rInf.GetLast();
776 pPor->Height( pLast->Height() );
777 pPor->SetAscent( pLast->GetAscent() );
779 // <--
780 else
782 const SwLinePortion *pLast = rInf.GetLast();
783 sal_Bool bChg;
785 // Fallunterscheidung: in leeren Zeilen werden die Attribute
786 // per SeekStart angeschaltet.
787 const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
788 if ( pPor->IsQuoVadisPortion() )
789 bChg = SeekStartAndChg( rInf, sal_True );
790 else
792 if( bFirstPor )
794 if( rInf.GetTxt().Len() )
796 if ( pPor->GetLen() || !rInf.GetIdx()
797 || ( pCurr != pLast && !pLast->IsFlyPortion() )
798 || !pCurr->IsRest() ) // statt !rInf.GetRest()
799 bChg = SeekAndChg( rInf );
800 else
801 bChg = SeekAndChgBefore( rInf );
803 else if ( pMulti )
804 // do not open attributes starting at 0 in empty multi
805 // portions (rotated numbering followed by a footnote
806 // can cause trouble, because the footnote attribute
807 // starts at 0, but if we open it, the attribute handler
808 // cannot handle it.
809 bChg = sal_False;
810 else
811 bChg = SeekStartAndChg( rInf );
813 else
814 bChg = SeekAndChg( rInf );
816 if( bChg || bFirstPor || !pPor->GetAscent()
817 || !rInf.GetLast()->InTxtGrp() )
819 pPor->SetAscent( rInf.GetAscent() );
820 pPor->Height( rInf.GetTxtHeight() );
822 else
824 pPor->Height( pLast->Height() );
825 pPor->SetAscent( pLast->GetAscent() );
830 /*************************************************************************
831 * class SwMetaPortion
832 *************************************************************************/
834 class SwMetaPortion : public SwTxtPortion
836 public:
837 inline SwMetaPortion() { SetWhichPor( POR_META ); }
838 virtual void Paint( const SwTxtPaintInfo &rInf ) const;
839 // OUTPUT_OPERATOR
842 //CLASSIO( SwMetaPortion )
844 /*************************************************************************
845 * virtual SwMetaPortion::Paint()
846 *************************************************************************/
848 void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const
850 if ( Width() )
852 rInf.DrawViewOpt( *this, POR_META );
853 SwTxtPortion::Paint( rInf );
858 /*************************************************************************
859 * SwTxtFormatter::WhichTxtPor()
860 *************************************************************************/
862 SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const
864 SwTxtPortion *pPor = 0;
865 if( GetFnt()->IsTox() )
866 pPor = new SwToxPortion;
867 else
869 if( GetFnt()->IsRef() )
870 pPor = new SwRefPortion;
871 else if (GetFnt()->IsMeta())
873 pPor = new SwMetaPortion;
875 else
877 // Erst zum Schluss !
878 // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben,
879 // z.B. bei nicht darstellbaren Zeichen.
880 if( rInf.GetLen() > 0 )
882 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART )
883 pPor = new SwFieldMarkPortion();
884 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND )
885 pPor = new SwFieldMarkPortion();
886 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT )
887 pPor = new SwFieldFormPortion();
889 if( !pPor )
891 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() )
892 pPor = pCurr;
893 else
895 pPor = new SwTxtPortion;
896 if( GetFnt()->IsURL() )
897 pPor->SetWhichPor( POR_URL );
902 return pPor;
905 /*************************************************************************
906 * SwTxtFormatter::NewTxtPortion()
907 *************************************************************************/
908 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert:
909 // 1) Tabs
910 // 2) Linebreaks
911 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
912 // 4) naechster Attributwechsel
914 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf )
916 // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr
917 // Wenn pCurr nicht von SwTxtPortion abgeleitet ist,
918 // muessen wir duplizieren ...
919 Seek( rInf.GetIdx() );
920 SwTxtPortion *pPor = WhichTxtPor( rInf );
922 // until next attribute change:
923 const xub_StrLen nNextAttr = GetNextAttr();
924 xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() );
926 // end of script type:
927 const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() );
928 nNextChg = Min( nNextChg, nNextScript );
930 // end of direction:
931 const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() );
932 nNextChg = Min( nNextChg, nNextDir );
934 // 7515, 7516, 3470, 6441 : Turbo-Boost
935 // Es wird unterstellt, dass die Buchstaben eines Fonts nicht
936 // groesser als doppelt so breit wie hoch sind.
937 // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen.
938 // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe
939 // ergibt sich erst im CalcAscent!
940 // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times
941 // New Roman besitzt einen Ascent von 182, eine Hoehe von 200
942 // und eine Breite von 53! Daraus folgt, dass eine Zeile mit
943 // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von
944 // Faktor 2 auf 8 (wg. negativen Kernings).
946 pPor->SetLen(1);
947 CalcAscent( rInf, pPor );
949 const SwFont* pTmpFnt = rInf.GetFont();
950 KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ),
951 KSHORT( pPor->GetAscent() ) ) / 8;
952 if ( !nExpect )
953 nExpect = 1;
954 nExpect = (USHORT)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect));
955 if( nExpect > rInf.GetIdx() && nNextChg > nExpect )
956 nNextChg = Min( nExpect, rInf.GetTxt().Len() );
958 // we keep an invariant during method calls:
959 // there are no portion ending characters like hard spaces
960 // or tabs in [ nLeftScanIdx, nRightScanIdx ]
961 if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx )
963 if ( nNextChg > nRightScanIdx )
964 nNextChg = nRightScanIdx =
965 rInf.ScanPortionEnd( nRightScanIdx, nNextChg );
967 else
969 nLeftScanIdx = rInf.GetIdx();
970 nNextChg = nRightScanIdx =
971 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
974 pPor->SetLen( nNextChg - rInf.GetIdx() );
975 rInf.SetLen( pPor->GetLen() );
976 return pPor;
980 /*************************************************************************
981 * SwTxtFormatter::WhichFirstPortion()
982 *************************************************************************/
984 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf)
986 SwLinePortion *pPor = 0;
988 if( rInf.GetRest() )
990 // 5010: Tabs und Felder
991 if( '\0' != rInf.GetHookChar() )
992 return 0;
994 pPor = rInf.GetRest();
995 if( pPor->IsErgoSumPortion() )
996 rInf.SetErgoDone(sal_True);
997 else
998 if( pPor->IsFtnNumPortion() )
999 rInf.SetFtnDone(sal_True);
1000 else
1001 if( pPor->InNumberGrp() )
1002 rInf.SetNumDone(sal_True);
1003 if( pPor )
1005 rInf.SetRest(0);
1006 pCurr->SetRest( sal_True );
1007 return pPor;
1011 // ???? und ????: im Follow duerfen wir schon stehen,
1012 // entscheidend ist, ob pFrm->GetOfst() == 0 ist!
1013 if( rInf.GetIdx() )
1015 // Nun koennen auch FtnPortions und ErgoSumPortions
1016 // verlaengert werden.
1018 // 1) Die ErgoSumTexte
1019 if( !rInf.IsErgoDone() )
1021 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1022 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1023 rInf.SetErgoDone( sal_True );
1026 // 2) Arrow portions
1027 if( !pPor && !rInf.IsArrowDone() )
1029 if( pFrm->GetOfst() && !pFrm->IsFollow() &&
1030 rInf.GetIdx() == pFrm->GetOfst() )
1031 pPor = new SwArrowPortion( *pCurr );
1032 rInf.SetArrowDone( sal_True );
1035 // 3) Kerning portions at beginning of line in grid mode
1036 if ( ! pPor && ! pCurr->GetPortion() )
1038 GETGRID( GetTxtFrm()->FindPageFrm() )
1039 if ( pGrid )
1040 pPor = new SwKernPortion( *pCurr );
1043 // 4) Die Zeilenreste (mehrzeilige Felder)
1044 if( !pPor )
1046 pPor = rInf.GetRest();
1047 // 6922: Nur bei pPor natuerlich.
1048 if( pPor )
1050 pCurr->SetRest( sal_True );
1051 rInf.SetRest(0);
1055 else
1057 // 5) Die Fussnotenzahlen
1058 if( !rInf.IsFtnDone() )
1060 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1061 "Rotated number portion trouble" )
1063 sal_Bool bFtnNum = pFrm->IsFtnNumFrm();
1064 rInf.GetParaPortion()->SetFtnNum( bFtnNum );
1065 if( bFtnNum )
1066 pPor = (SwLinePortion*)NewFtnNumPortion( rInf );
1067 rInf.SetFtnDone( sal_True );
1070 // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster,
1071 // entscheidend ist, ob der SwFtnFrm ein Follow ist.
1072 if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
1074 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1075 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1076 rInf.SetErgoDone( sal_True );
1079 // 7) Die Numerierungen
1080 if( !rInf.IsNumDone() && !pPor )
1082 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1083 "Rotated number portion trouble" )
1085 // Wenn wir im Follow stehen, dann natuerlich nicht.
1086 if( GetTxtFrm()->GetTxtNode()->GetNumRule() )
1087 pPor = (SwLinePortion*)NewNumberPortion( rInf );
1088 rInf.SetNumDone( sal_True );
1090 // 8) Die DropCaps
1091 if( !pPor && GetDropFmt() && ! rInf.IsMulti() )
1092 pPor = (SwLinePortion*)NewDropPortion( rInf );
1094 // 9) Kerning portions at beginning of line in grid mode
1095 if ( !pPor && !pCurr->GetPortion() )
1097 GETGRID( GetTxtFrm()->FindPageFrm() )
1098 if ( pGrid )
1099 pPor = new SwKernPortion( *pCurr );
1103 // 10) Decimal tab portion at the beginning of each line in table cells
1104 if ( !pPor && !pCurr->GetPortion() &&
1105 GetTxtFrm()->IsInTab() &&
1106 GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) )
1108 pPor = NewTabPortion( rInf, true );
1111 // 11) suffix of meta-field
1112 if (!pPor)
1114 pPor = TryNewNoLengthPortion(rInf);
1117 return pPor;
1120 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr )
1122 if( !pCurr->GetNext() )
1123 return sal_False;
1124 const SwLinePortion *pPor = pCurr->GetNext()->GetPortion();
1125 sal_Bool bRet = sal_False;
1126 while( pPor && !bRet )
1128 bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) ||
1129 (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld());
1130 if( !pPor->GetLen() )
1131 break;
1132 pPor = pPor->GetPortion();
1134 return bRet;
1137 /*************************************************************************
1138 * SwTxtFormatter::NewPortion()
1139 *************************************************************************/
1141 /* NewPortion stellt rInf.nLen ein.
1142 * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr,
1143 * attrwechsel.
1144 * Drei Faelle koennen eintreten:
1145 * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert
1146 * -> return 0;
1147 * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert
1148 * -> Breite neu einstellen und return new FlyPortion
1149 * 3) Es muss eine neue Portion gebaut werden.
1150 * -> CalcFlyWidth emuliert ggf. die Breite und return Portion
1153 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf )
1155 // Underflow hat Vorrang
1156 rInf.SetStopUnderFlow( sal_False );
1157 if( rInf.GetUnderFlow() )
1159 ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" );
1160 return UnderFlow( rInf );
1163 // Wenn die Zeile voll ist, koennten noch Flys oder
1164 // UnderFlow-LinePortions warten ...
1165 if( rInf.IsFull() )
1167 // ????: LineBreaks und Flys (bug05.sdw)
1168 // 8450: IsDummy()
1169 if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) )
1170 return 0;
1172 // Wenn der Text an den Fly gestossen ist, oder wenn
1173 // der Fly als erstes drankommt, weil er ueber dem linken
1174 // Rand haengt, wird GetFly() returnt.
1175 // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's
1176 // naturgemaesz eine 0.
1177 if( rInf.GetFly() )
1179 if( rInf.GetLast()->IsBreakPortion() )
1181 delete rInf.GetFly();
1182 rInf.SetFly( 0 );
1185 return rInf.GetFly();
1187 // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den
1188 // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest
1189 // bekanntgeben, damit SwTxtFrm::Format nicht abbricht
1190 // (die Textmasse wurde ja durchformatiert).
1191 if( rInf.GetRest() )
1192 rInf.SetNewLine( sal_True );
1193 else
1195 // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt,
1196 // jetzt aber kein Rest mehr anliegt,
1197 // muss sie auf jeden Fall neu formatiert werden!
1198 if( lcl_OldFieldRest( GetCurr() ) )
1199 rInf.SetNewLine( sal_True );
1200 else
1202 SwLinePortion *pFirst = WhichFirstPortion( rInf );
1203 if( pFirst )
1205 rInf.SetNewLine( sal_True );
1206 if( pFirst->InNumberGrp() )
1207 rInf.SetNumDone( sal_False) ;
1208 delete pFirst;
1213 return 0;
1216 SwLinePortion *pPor = WhichFirstPortion( rInf );
1218 // Check for Hidden Portion:
1219 if ( !pPor )
1221 xub_StrLen nEnd = rInf.GetIdx();
1222 if ( lcl_BuildHiddenPortion( rInf, nEnd ) )
1223 pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
1226 if( !pPor )
1228 if( ( !pMulti || pMulti->IsBidi() ) &&
1229 // --> FME 2005-02-14 #i42734#
1230 // No multi portion if there is a hook character waiting:
1231 ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
1232 // <--
1234 // We open a multiportion part, if we enter a multi-line part
1235 // of the paragraph.
1236 xub_StrLen nEnd = rInf.GetIdx();
1237 SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti );
1238 if( pCreate )
1240 SwMultiPortion* pTmp = NULL;
1242 if ( SW_MC_BIDI == pCreate->nId )
1243 pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
1244 else if ( SW_MC_RUBY == pCreate->nId )
1246 Seek( rInf.GetIdx() );
1247 sal_Bool bRubyTop;
1248 sal_Bool* pRubyPos = 0;
1250 if ( rInf.SnapToGrid() )
1252 GETGRID( GetTxtFrm()->FindPageFrm() )
1253 if ( pGrid )
1255 bRubyTop = ! pGrid->GetRubyTextBelow();
1256 pRubyPos = &bRubyTop;
1260 pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
1261 *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(),
1262 nEnd, 0, pRubyPos );
1264 else if( SW_MC_ROTATE == pCreate->nId )
1265 pTmp = new SwRotatedPortion( *pCreate, nEnd,
1266 GetTxtFrm()->IsRightToLeft() );
1267 else
1268 pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
1270 delete pCreate;
1271 CalcFlyWidth( rInf );
1273 return pTmp;
1276 // 5010: Tabs und Felder
1277 xub_Unicode cChar = rInf.GetHookChar();
1279 if( cChar )
1281 /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das
1282 * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile
1283 * gewandert ist ( so geschehen hinter Rahmen ).
1284 * Wenn allerdings eine FldPortion im Rest wartet, muessen wir
1285 * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei
1286 * DezimalTabs und Feldern (22615)
1288 if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() )
1289 cChar = rInf.GetChar( rInf.GetIdx() );
1290 rInf.ClearHookChar();
1292 else
1294 if( rInf.GetIdx() >= rInf.GetTxt().Len() )
1296 rInf.SetFull(sal_True);
1297 CalcFlyWidth( rInf );
1298 return pPor;
1300 cChar = rInf.GetChar( rInf.GetIdx() );
1303 switch( cChar )
1305 case CH_TAB:
1306 pPor = NewTabPortion( rInf, false ); break;
1308 case CH_BREAK:
1309 pPor = new SwBreakPortion( *rInf.GetLast() ); break;
1311 case CHAR_SOFTHYPHEN: // soft hyphen
1312 pPor = new SwSoftHyphPortion; break;
1314 case CHAR_HARDBLANK: // no-break space
1315 pPor = new SwBlankPortion( ' ' ); break;
1317 case CHAR_HARDHYPHEN: // non-breaking hyphen
1318 pPor = new SwBlankPortion( '-' ); break;
1320 case CHAR_ZWSP: // zero width space
1321 case CHAR_ZWNBSP : // word joiner
1322 // case CHAR_RLM : // right to left mark
1323 // case CHAR_LRM : // left to right mark
1324 pPor = new SwControlCharPortion( cChar ); break;
1326 case CH_TXTATR_BREAKWORD:
1327 case CH_TXTATR_INWORD:
1328 if( rInf.HasHint( rInf.GetIdx() ) )
1330 pPor = NewExtraPortion( rInf );
1331 break;
1333 // No break
1334 default :
1336 SwTabPortion* pLastTabPortion = rInf.GetLastTab();
1337 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
1339 // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full:
1340 // We have a decimal tab portion in the line and the next character has to be
1341 // aligned at the tab stop position. We store the width from the beginning of
1342 // the tab stop portion up to the portion containint the decimal separator:
1343 if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
1344 POR_TABDECIMAL == pLastTabPortion->GetWhichPor() )
1346 ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" )
1347 const USHORT nWidthOfPortionsUpToDecimalPosition = (USHORT)(rInf.X() - pLastTabPortion->Fix() );
1348 static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
1349 rInf.SetTabDecimal( 0 );
1351 // <--
1352 else
1353 rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
1356 if( rInf.GetRest() )
1358 if( rInf.IsFull() )
1360 rInf.SetNewLine(sal_True);
1361 return 0;
1363 pPor = rInf.GetRest();
1364 rInf.SetRest(0);
1366 else
1368 if( rInf.IsFull() )
1369 return 0;
1370 pPor = NewTxtPortion( rInf );
1372 break;
1376 // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht,
1377 // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet
1378 // hat, weil z.B. ein Tab enthalten ist.
1379 if( pPor && rInf.GetRest() )
1380 pPor->SetLen( 0 );
1382 // robust:
1383 if( !pPor || rInf.IsStop() )
1385 delete pPor;
1386 return 0;
1390 // Special portions containing numbers (footnote anchor, footnote number,
1391 // numbering) can be contained in a rotated portion, if the user
1392 // choose a rotated character attribute.
1393 if ( pPor && ! pMulti )
1395 if ( pPor->IsFtnPortion() )
1397 const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn();
1399 if ( pTxtFtn )
1401 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1402 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1403 const SwEndNoteInfo* pInfo;
1404 if( rFtn.IsEndNote() )
1405 pInfo = &pDoc->GetEndNoteInfo();
1406 else
1407 pInfo = &pDoc->GetFtnInfo();
1408 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1410 const SfxPoolItem* pItem;
1411 USHORT nDir = 0;
1412 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1413 sal_True, &pItem ))
1414 nDir = ((SvxCharRotateItem*)pItem)->GetValue();
1416 if ( 0 != nDir )
1418 delete pPor;
1419 pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ?
1420 DIR_BOTTOM2TOP :
1421 DIR_TOP2BOTTOM );
1425 else if ( pPor->InNumberGrp() )
1427 const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont();
1429 if ( pNumFnt )
1431 USHORT nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
1432 if ( 0 != nDir )
1434 delete pPor;
1435 pPor = new SwRotatedPortion( 0, 900 == nDir ?
1436 DIR_BOTTOM2TOP :
1437 DIR_TOP2BOTTOM );
1439 rInf.SetNumDone( sal_False );
1440 rInf.SetFtnDone( sal_False );
1446 // Der Font wird im Outputdevice eingestellt,
1447 // der Ascent und die Hoehe werden berechnet.
1448 if( !pPor->GetAscent() && !pPor->Height() )
1449 CalcAscent( rInf, pPor );
1450 rInf.SetLen( pPor->GetLen() );
1452 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
1453 CalcFlyWidth( rInf );
1455 // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige
1456 // Werte bereithalten muss:
1457 if( !pCurr->Height() )
1459 ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" );
1460 pCurr->Height( pPor->Height() );
1461 pCurr->SetAscent( pPor->GetAscent() );
1464 ASSERT( !pPor || pPor->Height(),
1465 "SwTxtFormatter::NewPortion: something went wrong");
1466 if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
1468 delete pPor;
1469 pPor = rInf.GetFly();
1471 return pPor;
1474 /*************************************************************************
1475 * SwTxtFormatter::FormatLine()
1476 *************************************************************************/
1478 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos )
1480 ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
1481 "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" );
1483 // For the formatting routines, we set pOut to the reference device.
1484 SwHookOut aHook( GetInfo() );
1485 if( GetInfo().GetLen() < GetInfo().GetTxt().Len() )
1486 GetInfo().SetLen( GetInfo().GetTxt().Len() );
1488 sal_Bool bBuild = sal_True;
1489 SetFlyInCntBase( sal_False );
1490 GetInfo().SetLineHeight( 0 );
1491 GetInfo().SetLineNettoHeight( 0 );
1493 // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden
1494 // und auch bei geaendertem Ascent (Absenken der Grundlinie).
1495 const KSHORT nOldHeight = pCurr->Height();
1496 const KSHORT nOldAscent = pCurr->GetAscent();
1498 pCurr->SetEndHyph( sal_False );
1499 pCurr->SetMidHyph( sal_False );
1501 // fly positioning can make it necessary format a line several times
1502 // for this, we have to keep a copy of our rest portion
1503 SwLinePortion* pFld = GetInfo().GetRest();
1504 SwFldPortion* pSaveFld = 0;
1506 if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() )
1507 pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) );
1509 // for an optimal repaint rectangle, we want to compare fly portions
1510 // before and after the BuildPortions call
1511 const sal_Bool bOptimizeRepaint = AllowRepaintOpt();
1512 const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen();
1513 SvLongs* pFlyStart = 0;
1515 // these are the conditions for a fly position comparison
1516 if ( bOptimizeRepaint && pCurr->IsFly() )
1518 pFlyStart = new SvLongs;
1519 SwLinePortion* pPor = pCurr->GetFirstPortion();
1520 long nPOfst = 0;
1521 USHORT nCnt = 0;
1523 while ( pPor )
1525 if ( pPor->IsFlyPortion() )
1526 // insert start value of fly portion
1527 pFlyStart->Insert( nPOfst, nCnt++ );
1529 nPOfst += pPor->Width();
1530 pPor = pPor->GetPortion();
1534 // Hier folgt bald die Unterlaufpruefung.
1535 while( bBuild )
1537 GetInfo().SetFtnInside( sal_False );
1539 // These values must not be reset by FormatReset();
1540 sal_Bool bOldNumDone = GetInfo().IsNumDone();
1541 sal_Bool bOldArrowDone = GetInfo().IsArrowDone();
1542 sal_Bool bOldErgoDone = GetInfo().IsErgoDone();
1544 // besides other things, this sets the repaint offset to 0
1545 FormatReset( GetInfo() );
1547 GetInfo().SetNumDone( bOldNumDone );
1548 GetInfo().SetArrowDone( bOldArrowDone );
1549 GetInfo().SetErgoDone( bOldErgoDone );
1551 // build new portions for this line
1552 BuildPortions( GetInfo() );
1554 if( GetInfo().IsStop() )
1556 pCurr->SetLen( 0 );
1557 pCurr->Height( GetFrmRstHeight() + 1 );
1558 pCurr->SetRealHeight( GetFrmRstHeight() + 1 );
1559 pCurr->Width(0);
1560 pCurr->Truncate();
1561 return nStartPos;
1563 else if( GetInfo().IsDropInit() )
1565 DropInit();
1566 GetInfo().SetDropInit( sal_False );
1569 pCurr->CalcLine( *this, GetInfo() );
1570 CalcRealHeight( GetInfo().IsNewLine() );
1572 if ( IsFlyInCntBase() && !IsQuick() )
1574 KSHORT nTmpAscent, nTmpHeight;
1575 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
1576 AlignFlyInCntBase( Y() + long( nTmpAscent ) );
1577 pCurr->CalcLine( *this, GetInfo() );
1578 CalcRealHeight();
1581 // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird
1582 if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
1584 pCurr->SetRealHeight( GetInfo().GetLineHeight() );
1585 bBuild = sal_False;
1587 else
1589 bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow( GetInfo() )
1590 || GetInfo().CheckFtnPortion( pCurr ) );
1591 if( bBuild )
1593 GetInfo().SetNumDone( bOldNumDone );
1594 GetInfo().ResetMaxWidthDiff();
1596 // delete old rest
1597 if ( GetInfo().GetRest() )
1599 delete GetInfo().GetRest();
1600 GetInfo().SetRest( 0 );
1603 // set original rest portion
1604 if ( pSaveFld )
1605 GetInfo().SetRest( new SwFldPortion( *pSaveFld ) );
1607 pCurr->SetLen( 0 );
1608 pCurr->Width(0);
1609 pCurr->Truncate();
1614 // calculate optimal repaint rectangle
1615 if ( bOptimizeRepaint )
1617 GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) );
1618 if ( pFlyStart )
1619 delete pFlyStart;
1621 else
1622 // Special case: We do not allow an optimitation of the repaint
1623 // area, but during formatting the repaint offset is set to indicate
1624 // a maximum value for the offset. This value has to be reset:
1625 GetInfo().SetPaintOfst( 0 );
1627 // This corrects the start of the reformat range if something has
1628 // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
1629 // will give us a wrong result if we have to reformat another line
1630 GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() );
1632 // delete master copy of rest portion
1633 if ( pSaveFld )
1634 delete pSaveFld;
1636 xub_StrLen nNewStart = nStartPos + pCurr->GetLen();
1638 // adjust text if kana compression is enabled
1639 if ( GetInfo().CompressLine() )
1641 SwTwips nRepaintOfst = CalcKanaAdj( pCurr );
1643 // adjust repaint offset
1644 if ( nRepaintOfst < GetInfo().GetPaintOfst() )
1645 GetInfo().SetPaintOfst( nRepaintOfst );
1648 CalcAdjustLine( pCurr );
1650 if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() )
1652 SetFlyInCntBase();
1653 GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling
1654 // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind
1655 // auch formatiert werden.
1656 GetInfo().SetShift( sal_True );
1659 if ( IsFlyInCntBase() && !IsQuick() )
1660 UpdatePos( pCurr, GetTopLeft(), GetStart() );
1662 return nNewStart;
1665 /*************************************************************************
1666 * SwTxtFormatter::RecalcRealHeight()
1667 *************************************************************************/
1669 void SwTxtFormatter::RecalcRealHeight()
1671 sal_Bool bMore = sal_True;
1672 while(bMore)
1674 DBG_LOOP;
1675 CalcRealHeight();
1676 bMore = Next() != 0;
1680 /*************************************************************************
1681 * SwTxtFormatter::CalcRealHeight()
1682 *************************************************************************/
1684 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine )
1686 KSHORT nLineHeight = pCurr->Height();
1687 pCurr->SetClipping( sal_False );
1689 GETGRID( pFrm->FindPageFrm() )
1690 if ( pGrid && GetInfo().SnapToGrid() )
1692 const USHORT nGridWidth = pGrid->GetBaseHeight();
1693 const USHORT nRubyHeight = pGrid->GetRubyHeight();
1694 const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow();
1696 nLineHeight = nGridWidth + nRubyHeight;
1697 USHORT nLineDist = nLineHeight;
1699 while ( pCurr->Height() > nLineHeight )
1700 nLineHeight = nLineHeight + nLineDist;
1702 KSHORT nAsc = pCurr->GetAscent() +
1703 ( bRubyTop ?
1704 ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 :
1705 ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 );
1707 pCurr->Height( nLineHeight );
1708 pCurr->SetAscent( nAsc );
1709 pInf->GetParaPortion()->SetFixLineHeight();
1711 // we ignore any line spacing options except from ...
1712 const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing();
1713 if ( ! IsParaLine() && pSpace &&
1714 SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() )
1716 ULONG nTmp = pSpace->GetPropLineSpace();
1718 if( nTmp < 100 )
1719 nTmp = 100;
1721 nTmp *= nLineHeight;
1722 nLineHeight = (USHORT)(nTmp / 100);
1725 pCurr->SetRealHeight( nLineHeight );
1726 return;
1729 // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese
1730 // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere
1731 // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem
1732 // Shift-Return), die das Register durchaus beachten soll.
1733 if( !pCurr->IsDummy() || ( !pCurr->GetNext() &&
1734 GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) )
1736 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
1737 if( pSpace )
1739 switch( pSpace->GetLineSpaceRule() )
1741 case SVX_LINE_SPACE_AUTO:
1742 if (pSpace->GetInterLineSpaceRule()==SVX_INTER_LINE_SPACE_PROP) {
1743 long nTmp = pSpace->GetPropLineSpace();
1744 if (nTmp<100) { // code adaped from fixed line height
1745 nTmp *= nLineHeight;
1746 nTmp /= 100;
1747 if( !nTmp )
1748 ++nTmp;
1749 nLineHeight = (KSHORT)nTmp;
1751 //@TODO figure out how WW maps ascent and descent
1752 //in case of prop line spacing <100%
1753 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80%
1754 if( nAsc < pCurr->GetAscent() ||
1755 nLineHeight - nAsc < pCurr->Height() -
1756 pCurr->GetAscent() )
1757 pCurr->SetClipping( sal_True );
1758 pCurr->SetAscent( nAsc );
1760 pCurr->Height( nLineHeight );
1761 pInf->GetParaPortion()->SetFixLineHeight();
1764 break;
1765 case SVX_LINE_SPACE_MIN:
1767 if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) )
1768 nLineHeight = pSpace->GetLineHeight();
1769 break;
1771 case SVX_LINE_SPACE_FIX:
1773 nLineHeight = pSpace->GetLineHeight();
1774 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80%
1775 if( nAsc < pCurr->GetAscent() ||
1776 nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() )
1777 pCurr->SetClipping( sal_True );
1778 pCurr->Height( nLineHeight );
1779 pCurr->SetAscent( nAsc );
1780 pInf->GetParaPortion()->SetFixLineHeight();
1782 break;
1783 default: ASSERT( sal_False, ": unknown LineSpaceRule" );
1785 if( !IsParaLine() )
1786 switch( pSpace->GetInterLineSpaceRule() )
1788 case SVX_INTER_LINE_SPACE_OFF:
1789 break;
1790 case SVX_INTER_LINE_SPACE_PROP:
1792 long nTmp = pSpace->GetPropLineSpace();
1793 // 50% ist das Minimum, bei 0% schalten wir auf
1794 // den Defaultwert 100% um ...
1795 if( nTmp < 50 )
1796 nTmp = nTmp ? 50 : 100;
1798 nTmp *= nLineHeight;
1799 nTmp /= 100;
1800 if( !nTmp )
1801 ++nTmp;
1802 nLineHeight = (KSHORT)nTmp;
1803 break;
1805 case SVX_INTER_LINE_SPACE_FIX:
1807 nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
1808 break;
1810 default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
1813 #if OSL_DEBUG_LEVEL > 1
1814 KSHORT nDummy = nLineHeight + 1;
1815 (void)nDummy;
1816 #endif
1818 if( IsRegisterOn() )
1820 SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height();
1821 SWRECTFN( pFrm )
1822 if ( bVert )
1823 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY );
1824 nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() );
1825 KSHORT nDiff = KSHORT( nTmpY % RegDiff() );
1826 if( nDiff )
1827 nLineHeight += RegDiff() - nDiff;
1830 pCurr->SetRealHeight( nLineHeight );
1833 /*************************************************************************
1834 * SwTxtFormatter::FeedInf()
1835 *************************************************************************/
1837 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const
1839 // 3260, 3860: Fly auf jeden Fall loeschen!
1840 ClearFly( rInf );
1841 rInf.Init();
1843 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
1844 rInf.SetRoot( pCurr );
1845 rInf.SetLineStart( nStart );
1846 rInf.SetIdx( nStart );
1848 // Handle overflows:
1849 // --> FME 2004-11-25 #i34348# Changed type from USHORT to SwTwips
1850 SwTwips nTmpLeft = Left();
1851 SwTwips nTmpRight = Right();
1852 SwTwips nTmpFirst = FirstLeft();
1853 // <--
1855 if ( nTmpLeft > USHRT_MAX ||
1856 nTmpRight > USHRT_MAX ||
1857 nTmpFirst > USHRT_MAX )
1859 SWRECTFN( rInf.GetTxtFrm() )
1860 nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)();
1861 nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)();
1862 nTmpFirst = nTmpLeft;
1865 rInf.Left( nTmpLeft );
1866 rInf.Right( nTmpRight );
1867 rInf.First( nTmpFirst );
1869 rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) );
1870 rInf.Width( rInf.RealWidth() );
1871 if( ((SwTxtFormatter*)this)->GetRedln() )
1873 ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() );
1874 ((SwTxtFormatter*)this)->GetRedln()->Reset();
1878 /*************************************************************************
1879 * SwTxtFormatter::FormatReset()
1880 *************************************************************************/
1882 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf )
1884 pCurr->Truncate();
1885 pCurr->Init();
1886 if( pBlink && pCurr->IsBlinking() )
1887 pBlink->Delete( pCurr );
1889 // delete pSpaceAdd und pKanaComp
1890 pCurr->FinishSpaceAdd();
1891 pCurr->FinishKanaComp();
1892 pCurr->ResetFlags();
1893 FeedInf( rInf );
1896 /*************************************************************************
1897 * SwTxtFormatter::CalcOnceMore()
1898 *************************************************************************/
1900 sal_Bool SwTxtFormatter::CalcOnceMore()
1902 if( pDropFmt )
1904 const KSHORT nOldDrop = GetDropHeight();
1905 CalcDropHeight( pDropFmt->GetLines() );
1906 bOnceMore = nOldDrop != GetDropHeight();
1908 else
1909 bOnceMore = sal_False;
1910 return bOnceMore;
1913 /*************************************************************************
1914 * SwTxtFormatter::CalcBottomLine()
1915 *************************************************************************/
1917 SwTwips SwTxtFormatter::CalcBottomLine() const
1919 SwTwips nRet = Y() + GetLineHeight();
1920 SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom();
1921 if( nMin && ++nMin > nRet )
1923 SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height()
1924 - pFrm->Prt().Top();
1925 if( nRet + nDist < nMin )
1927 sal_Bool bRepaint = HasTruncLines() &&
1928 GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1;
1929 nRet = nMin - nDist;
1930 if( bRepaint )
1932 ((SwRepaint*)GetInfo().GetParaPortion()
1933 ->GetRepaint())->Bottom( nRet-1 );
1934 ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 );
1938 return nRet;
1941 /*************************************************************************
1942 * SwTxtFormatter::_CalcFitToContent()
1944 * FME/OD: This routine does a limited text formatting.
1945 *************************************************************************/
1947 SwTwips SwTxtFormatter::_CalcFitToContent()
1949 FormatReset( GetInfo() );
1950 BuildPortions( GetInfo() );
1951 pCurr->CalcLine( *this, GetInfo() );
1952 return pCurr->Width();
1955 /*************************************************************************
1956 * SwTxtFormatter::AllowRepaintOpt()
1958 * determines if the calculation of a repaint offset is allowed
1959 * otherwise each line is painted from 0 (this is a copy of the beginning
1960 * of the former SwTxtFormatter::Recycle() function
1961 *************************************************************************/
1962 sal_Bool SwTxtFormatter::AllowRepaintOpt() const
1964 // reformat position in front of current line? Only in this case
1965 // we want to set the repaint offset
1966 sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() &&
1967 pCurr->GetLen();
1969 // a special case is the last line of a block adjusted paragraph:
1970 if ( bOptimizeRepaint )
1972 switch( GetAdjust() )
1974 case SVX_ADJUST_BLOCK:
1976 if( IsLastBlock() || IsLastCenter() )
1977 bOptimizeRepaint = sal_False;
1978 else
1980 // ????: Blank in der letzten Masterzeile (blocksat.sdw)
1981 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow();
1982 if ( bOptimizeRepaint )
1984 SwLinePortion *pPos = pCurr->GetFirstPortion();
1985 while ( pPos && !pPos->IsFlyPortion() )
1986 pPos = pPos->GetPortion();
1987 bOptimizeRepaint = !pPos;
1990 break;
1992 case SVX_ADJUST_CENTER:
1993 case SVX_ADJUST_RIGHT:
1994 bOptimizeRepaint = sal_False;
1995 break;
1996 default: ;
2000 // Schon wieder ein Sonderfall: unsichtbare SoftHyphs
2001 const xub_StrLen nReformat = GetInfo().GetReformatStart();
2002 if( bOptimizeRepaint && STRING_LEN != nReformat )
2004 const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat );
2005 bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
2006 || ! GetInfo().HasHint( nReformat );
2009 return bOptimizeRepaint;
2012 /*************************************************************************
2013 * SwTxtFormatter::CalcOptRepaint()
2015 * calculates an optimal repaint offset for the current line
2016 *************************************************************************/
2017 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd,
2018 const SvLongs* pFlyStart )
2020 if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() )
2021 // the reformat position is behind our new line, that means
2022 // something of our text has moved to the next line
2023 return 0;
2025 xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd );
2027 // in case we do not have any fly in our line, our repaint position
2028 // is the changed position - 1
2029 if ( ! pFlyStart && ! pCurr->IsFly() )
2031 // this is the maximum repaint offset determined during formatting
2032 // for example: the beginning of the first right tab stop
2033 // if this value is 0, this means that we do not have an upper
2034 // limit for the repaint offset
2035 const long nFormatRepaint = GetInfo().GetPaintOfst();
2037 if ( nReformat < GetInfo().GetLineStart() + 3 )
2038 return 0;
2040 // step back two positions for smoother repaint
2041 nReformat -= 2;
2043 #ifndef QUARTZ
2044 #ifndef ENABLE_GRAPHITE
2045 // --> FME 2004-09-27 #i28795#, #i34607#, #i38388#
2046 // step back six(!) more characters for complex scripts
2047 // this is required e.g., for Khmer (thank you, Javier!)
2048 const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
2049 xub_StrLen nMaxContext = 0;
2050 if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) )
2051 nMaxContext = 6;
2052 #else
2053 // Some Graphite fonts need context for scripts not marked as complex
2054 static const xub_StrLen nMaxContext = 10;
2055 #endif
2056 #else
2057 // some fonts like Quartz's Zapfino need more context
2058 // TODO: query FontInfo for maximum unicode context
2059 static const xub_StrLen nMaxContext = 8;
2060 #endif
2061 if( nMaxContext > 0 )
2063 if ( nReformat > GetInfo().GetLineStart() + nMaxContext )
2064 nReformat = nReformat - nMaxContext;
2065 else
2066 nReformat = GetInfo().GetLineStart();
2068 // <--
2070 // Weird situation: Our line used to end with a hole portion
2071 // and we delete some characters at the end of our line. We have
2072 // to take care for repainting the blanks which are not anymore
2073 // covered by the hole portion
2074 while ( nReformat > GetInfo().GetLineStart() &&
2075 CH_BLANK == GetInfo().GetChar( nReformat ) )
2076 --nReformat;
2078 ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" );
2079 SwRect aRect;
2081 // Note: GetChareRect is not const. It definitely changes the
2082 // bMulti flag. We have to save and resore the old value.
2083 sal_Bool bOldMulti = GetInfo().IsMulti();
2084 GetCharRect( &aRect, nReformat );
2085 GetInfo().SetMulti( bOldMulti );
2087 return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) :
2088 aRect.Left();
2090 else
2092 // nReformat may be wrong, if something around flys has changed:
2093 // we compare the former and the new fly positions in this line
2094 // if anything has changed, we carefully have to adjust the right
2095 // repaint position
2096 long nPOfst = 0;
2097 USHORT nCnt = 0;
2098 USHORT nX = 0;
2099 USHORT nIdx = GetInfo().GetLineStart();
2100 SwLinePortion* pPor = pCurr->GetFirstPortion();
2102 while ( pPor )
2104 if ( pPor->IsFlyPortion() )
2106 // compare start of fly with former start of fly
2107 if ( pFlyStart &&
2108 nCnt < pFlyStart->Count() &&
2109 nX == (*pFlyStart)[ nCnt ] &&
2110 nIdx < nReformat
2112 // found fix position, nothing has changed left from nX
2113 nPOfst = nX + pPor->Width();
2114 else
2115 break;
2117 nCnt++;
2119 nX = nX + pPor->Width();
2120 nIdx = nIdx + pPor->GetLen();
2121 pPor = pPor->GetPortion();
2124 return nPOfst + GetLeftMargin();
2128 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos )
2130 // Only if hidden text should not be shown:
2131 if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
2132 return false;
2134 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
2135 xub_StrLen nHiddenStart;
2136 xub_StrLen nHiddenEnd;
2137 rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
2138 if ( nHiddenEnd )
2140 rPos = nHiddenEnd;
2141 return true;
2144 return false;