update dev300-m58
[ooovba.git] / sw / source / core / text / itrform2.cxx
blobafdc54a3ded6b5633c17b92caa39791d8207bd61
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;
111 if( nStart > GetInfo().GetTxt().Len() )
113 ASSERT( !this, "+SwTxtFormatter::CTOR: bad offset" );
114 nStart = GetInfo().GetTxt().Len();
119 /*************************************************************************
120 * SwTxtFormatter::DTOR
121 *************************************************************************/
123 SwTxtFormatter::~SwTxtFormatter()
125 // Auesserst unwahrscheinlich aber denkbar.
126 // z.B.: Feld spaltet sich auf, Widows schlagen zu
127 if( GetInfo().GetRest() )
129 delete GetInfo().GetRest();
130 GetInfo().SetRest(0);
134 /*************************************************************************
135 * SwTxtFormatter::Insert()
136 *************************************************************************/
138 void SwTxtFormatter::Insert( SwLineLayout *pLay )
140 // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element.
141 if ( pCurr )
143 pLay->SetNext( pCurr->GetNext() );
144 pCurr->SetNext( pLay );
146 else
147 pCurr = pLay;
150 /*************************************************************************
151 * SwTxtFormatter::GetFrmRstHeight()
152 *************************************************************************/
154 KSHORT SwTxtFormatter::GetFrmRstHeight() const
156 // 8725: Uns interessiert die Resthoehe bezogen auf die Seite.
157 // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht
158 // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn
159 // gerufen.
160 // Falsch: const SwFrm *pUpper = pFrm->GetUpper();
161 const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm();
162 const SwTwips nHeight = pPage->Frm().Top()
163 + pPage->Prt().Top()
164 + pPage->Prt().Height() - Y();
165 if( 0 > nHeight )
166 return pCurr->Height();
167 else
168 return KSHORT( nHeight );
171 /*************************************************************************
172 * SwTxtFormatter::UnderFlow()
173 *************************************************************************/
175 SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf )
177 // Werte sichern und rInf initialisieren.
178 SwLinePortion *pUnderFlow = rInf.GetUnderFlow();
179 if( !pUnderFlow )
180 return 0;
182 // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der
183 // naechsten Zeile durchaus noch einmal drankommen koennen.
184 // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt.
186 const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos();
187 const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos();
189 // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF
190 // 3983: Nicht ClearFly(rInf) !
191 SwFlyPortion *pFly = rInf.GetFly();
192 rInf.SetFly( 0 );
194 FeedInf( rInf );
195 rInf.SetLast( pCurr );
196 // pUnderFlow braucht nicht deletet werden, weil es im folgenden
197 // Truncate() untergehen wird.
198 rInf.SetUnderFlow(0);
199 rInf.SetSoftHyphPos( nSoftHyphPos );
200 rInf.SetUnderScorePos( nUnderScorePos );
201 rInf.SetPaintOfst( GetLeftMargin() );
203 // Wir suchen die Portion mit der Unterlaufposition
204 SwLinePortion *pPor = pCurr->GetFirstPortion();
205 if( pPor != pUnderFlow )
207 // pPrev wird die letzte Portion vor pUnderFlow,
208 // die noch eine echte Breite hat.
209 // Ausnahme: SoftHyphPortions duerfen dabei natuerlich
210 // nicht vergessen werden, obwohl sie keine Breite haben.
211 SwLinePortion *pTmpPrev = pPor;
212 while( pPor && pPor != pUnderFlow )
214 DBG_LOOP;
215 if( !pPor->IsKernPortion() &&
216 ( pPor->Width() || pPor->IsSoftHyphPortion() ) )
218 while( pTmpPrev != pPor )
220 pTmpPrev->Move( rInf );
221 rInf.SetLast( pTmpPrev );
222 pTmpPrev = pTmpPrev->GetPortion();
223 ASSERT( pTmpPrev, "UnderFlow: Loosing control!" );
226 pPor = pPor->GetPortion();
228 pPor = pTmpPrev;
229 if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen
230 ( pPor->IsFlyPortion() || pPor->IsDropPortion() ||
231 pPor->IsFlyCntPortion() ) )
233 pPor->Move( rInf );
234 rInf.SetLast( pPor );
235 rInf.SetStopUnderFlow( sal_True );
236 pPor = pUnderFlow;
240 // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ?
241 ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" );
243 // OD 2004-05-26 #i29529# - correction: no delete of footnotes
244 // if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() )
245 // {
246 // SwLinePortion *pTmp = pPor->GetPortion();
247 // while( pTmp )
248 // {
249 // if( pTmp->IsFtnPortion() )
250 // ((SwFtnPortion*)pTmp)->ClearFtn();
251 // pTmp = pTmp->GetPortion();
252 // }
253 // }
255 /*-----------------14.12.94 09:45-------------------
256 * 9849: Schnellschuss
257 * --------------------------------------------------*/
258 if ( pPor==rInf.GetLast() )
260 // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich
261 // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber
262 // mehrere Zeilen geht und in der zweiten Zeile in einen Fly
263 // hineinlaeuft!
264 rInf.SetFly( pFly ); // wg. 28300
265 pPor->Truncate();
266 return pPor; // Reicht das?
268 /*---------------------------------------------------
269 * Ende des Schnellschusses wg. 9849
270 * --------------------------------------------------*/
272 // 4656: X + Width == 0 bei SoftHyph > Zeile ?!
273 if( !pPor || !(rInf.X() + pPor->Width()) )
275 delete pFly;
276 return 0;
279 // Vorbereitungen auf's Format()
280 // Wir muessen die Kette hinter pLast abknipsen, weil
281 // nach dem Format() ein Insert erfolgt.
282 SeekAndChg( rInf );
284 // line width is adjusted, so that pPor does not fit to current
285 // line anymore
286 rInf.Width( (USHORT)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) );
287 rInf.SetLen( pPor->GetLen() );
288 rInf.SetFull( sal_False );
289 if( pFly )
291 // Aus folgendem Grund muss die FlyPortion neu berechnet werden:
292 // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie
293 // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht,
294 // so hat die FlyPortion eine falsche Groesse/Fixsize.
295 rInf.SetFly( pFly );
296 CalcFlyWidth( rInf );
298 rInf.GetLast()->SetPortion(0);
300 // Eine Ausnahme bildet das SwLineLayout, dass sich beim
301 // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg:
302 if( rInf.GetLast() == pCurr )
304 if( pPor->InTxtGrp() && !pPor->InExpGrp() )
306 MSHORT nOldWhich = pCurr->GetWhichPor();
307 *(SwLinePortion*)pCurr = *pPor;
308 pCurr->SetPortion( pPor->GetPortion() );
309 pCurr->SetWhichPor( nOldWhich );
310 pPor->SetPortion( 0 );
311 delete pPor;
312 pPor = pCurr;
315 pPor->Truncate();
316 delete rInf.GetRest();
317 rInf.SetRest(0);
318 return pPor;
321 /*************************************************************************
322 * SwTxtFormatter::InsertPortion()
323 *************************************************************************/
325 void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf,
326 SwLinePortion *pPor ) const
328 // Die neue Portion wird eingefuegt,
329 // bei dem LineLayout ist allerdings alles anders...
330 if( pPor == pCurr )
332 if( pCurr->GetPortion() )
333 pPor = pCurr->GetPortion();
335 else
337 SwLinePortion *pLast = rInf.GetLast();
338 if( pLast->GetPortion() )
340 while( pLast->GetPortion() )
341 pLast = pLast->GetPortion();
342 rInf.SetLast( pLast );
344 pLast->Insert( pPor );
346 // Maxima anpassen:
347 if( pCurr->Height() < pPor->Height() )
348 pCurr->Height( pPor->Height() );
349 if( pCurr->GetAscent() < pPor->GetAscent() )
350 pCurr->SetAscent( pPor->GetAscent() );
353 // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate)
354 rInf.SetLast( pPor );
355 while( pPor )
357 DBG_LOOP;
358 pPor->Move( rInf );
359 rInf.SetLast( pPor );
360 pPor = pPor->GetPortion();
364 /*************************************************************************
365 * SwTxtFormatter::BuildPortion()
366 *************************************************************************/
368 void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf )
370 ASSERT( rInf.GetTxt().Len() < STRING_LEN,
371 "SwTxtFormatter::BuildPortions: bad text length in info" );
373 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
375 // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet.
376 // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt
377 // wird. In CalcAscent geschieht dies automatisch.
378 rInf.SetLast( pCurr );
379 rInf.ForcedLeftMargin( 0 );
381 ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" );
383 if( !pCurr->GetAscent() && !pCurr->Height() )
384 CalcAscent( rInf, pCurr );
386 SeekAndChg( rInf );
388 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
389 ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" );
390 CalcFlyWidth( rInf );
391 SwFlyPortion *pFly = rInf.GetFly();
392 if( pFly )
394 if ( 0 < pFly->Fix() )
395 ClearFly( rInf );
396 else
397 rInf.SetFull(sal_True);
400 SwLinePortion *pPor = NewPortion( rInf );
402 // Asian grid stuff
403 GETGRID( pFrm->FindPageFrm() )
404 const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() &&
405 GRID_LINES_CHARS == pGrid->GetGridType();
407 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
408 const USHORT nGridWidth = bHasGrid ?
409 GETGRIDWIDTH(pGrid,pDoc) : 0; //for textgrid refactor
411 // used for grid mode only:
412 // the pointer is stored, because after formatting of non-asian text,
413 // the width of the kerning portion has to be adjusted
414 SwKernPortion* pGridKernPortion = 0;
416 sal_Bool bFull;
417 SwTwips nUnderLineStart = 0;
418 rInf.Y( Y() );
420 while( pPor && !rInf.IsStop() )
422 ASSERT( rInf.GetLen() < STRING_LEN &&
423 rInf.GetIdx() <= rInf.GetTxt().Len(),
424 "SwTxtFormatter::BuildPortions: bad length in info" );
425 DBG_LOOP;
427 // We have to check the script for fields in order to set the
428 // correct nActual value for the font.
429 if( pPor->InFldGrp() )
430 ((SwFldPortion*)pPor)->CheckScript( rInf );
432 if( ! bHasGrid && rInf.HasScriptSpace() &&
433 rInf.GetLast() && rInf.GetLast()->InTxtGrp() &&
434 rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() )
436 BYTE nNxtActual = rInf.GetFont()->GetActual();
437 BYTE nLstActual = nNxtActual;
438 USHORT nLstHeight = (USHORT)rInf.GetFont()->GetHeight();
439 sal_Bool bAllowBefore = sal_False;
440 sal_Bool bAllowBehind = sal_False;
441 const CharClass& rCC = GetAppCharClass();
443 // are there any punctuation characters on both sides
444 // of the kerning portion?
445 if ( pPor->InFldGrp() )
447 XubString aAltTxt;
448 if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) &&
449 aAltTxt.Len() )
451 bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 );
453 const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont();
454 if ( pTmpFnt )
455 nNxtActual = pTmpFnt->GetActual();
458 else
459 bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() );
461 const SwLinePortion* pLast = rInf.GetLast();
462 if ( bAllowBehind && pLast )
464 if ( pLast->InFldGrp() )
466 XubString aAltTxt;
467 if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) &&
468 aAltTxt.Len() )
470 bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 );
472 const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont();
473 if ( pTmpFnt )
475 nLstActual = pTmpFnt->GetActual();
476 nLstHeight = (USHORT)pTmpFnt->GetHeight();
480 else if ( rInf.GetIdx() )
482 bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 );
483 // Note: ScriptType returns values in [1,4]
484 if ( bAllowBefore )
485 nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1;
488 nLstHeight /= 5;
489 // does the kerning portion still fit into the line?
490 if( bAllowBefore && ( nLstActual != nNxtActual ) &&
491 nLstHeight && rInf.X() + nLstHeight <= rInf.Width() )
493 SwKernPortion* pKrn =
494 new SwKernPortion( *rInf.GetLast(), nLstHeight,
495 pLast->InFldGrp() && pPor->InFldGrp() );
496 rInf.GetLast()->SetPortion( NULL );
497 InsertPortion( rInf, pKrn );
501 else if ( bHasGrid && ! pGridKernPortion && ! pMulti )
503 // insert a grid kerning portion
504 if ( ! pGridKernPortion )
505 pGridKernPortion = pPor->IsKernPortion() ?
506 (SwKernPortion*)pPor :
507 new SwKernPortion( *pCurr );
509 // if we have a new GridKernPortion, we initially calculate
510 // its size so that its ends on the grid
511 const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
512 const SwLayoutFrm* pBody = pPageFrm->FindBodyCont();
513 SWRECTFN( pPageFrm )
515 const long nGridOrigin = pBody ?
516 (pBody->*fnRect->fnGetPrtLeft)() :
517 (pPageFrm->*fnRect->fnGetPrtLeft)();
519 SwTwips nStartX = rInf.X() + GetLeftMargin();
520 if ( bVert )
522 Point aPoint( nStartX, 0 );
523 pFrm->SwitchHorizontalToVertical( aPoint );
524 nStartX = aPoint.Y();
527 const SwTwips nOfst = nStartX - nGridOrigin;
528 if ( nOfst )
530 const ULONG i = ( nOfst > 0 ) ?
531 ( ( nOfst - 1 ) / nGridWidth + 1 ) :
533 const SwTwips nKernWidth = i * nGridWidth - nOfst;
534 const SwTwips nRestWidth = rInf.Width() - rInf.X();
536 if ( nKernWidth <= nRestWidth )
537 pGridKernPortion->Width( (USHORT)nKernWidth );
540 if ( pGridKernPortion != pPor )
541 InsertPortion( rInf, pGridKernPortion );
544 // the multi-portion has it's own format function
545 if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) )
546 bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) );
547 else
548 bFull = pPor->Format( rInf );
550 if( rInf.IsRuby() && !rInf.GetRest() )
551 bFull = sal_True;
553 // if we are underlined, we store the beginning of this underlined
554 // segment for repaint optimization
555 if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart )
556 nUnderLineStart = GetLeftMargin() + rInf.X();
558 if ( pPor->IsFlyPortion() )
559 pCurr->SetFly( sal_True );
560 // some special cases, where we have to take care for the repaint
561 // offset:
562 // 1. Underlined portions due to special underline feature
563 // 2. Right Tab
564 // 3. BidiPortions
565 // 4. other Multiportions
566 // 5. DropCaps
567 // 6. Grid Mode
568 else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) &&
569 // 1. Underlined portions
570 nUnderLineStart &&
571 // reformat is at end of an underlined portion and next portion
572 // is not underlined
573 ( ( rInf.GetReformatStart() == rInf.GetIdx() &&
574 UNDERLINE_NONE == pFnt->GetUnderline()
575 ) ||
576 // reformat is inside portion and portion is underlined
577 ( rInf.GetReformatStart() >= rInf.GetIdx() &&
578 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() &&
579 UNDERLINE_NONE != pFnt->GetUnderline() ) ) )
580 rInf.SetPaintOfst( nUnderLineStart );
581 else if ( ! rInf.GetPaintOfst() &&
582 // 2. Right Tab
583 ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) ||
584 // 3. BidiPortions
585 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) ||
586 // 4. Multi Portion and 5. Drop Caps
587 ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) &&
588 rInf.GetReformatStart() >= rInf.GetIdx() &&
589 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() )
590 // 6. Grid Mode
591 || ( bHasGrid && SW_CJK != pFnt->GetActual() )
594 // we store the beginning of the critical portion as our
595 // paint offset
596 rInf.SetPaintOfst( GetLeftMargin() + rInf.X() );
598 // under one of these conditions we are allowed to delete the
599 // start of the underline portion
600 if ( IsUnderlineBreak( *pPor, *pFnt ) )
601 nUnderLineStart = 0;
603 if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() &&
604 ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) )
605 SetFlyInCntBase();
606 // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim
607 // naechsten Softhyphen wieder umgebrochen!
608 if ( !bFull )
610 rInf.ClrUnderFlow();
611 if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() &&
612 pPor->GetLen() && !pPor->InFldGrp() )
614 // The distance between two different scripts is set
615 // to 20% of the fontheight.
616 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
617 if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) &&
618 nTmp != rInf.GetTxt().Len() )
620 USHORT nDist = (USHORT)(rInf.GetFont()->GetHeight()/5);
622 if( nDist )
624 // we do not want a kerning portion if any end
625 // would be a punctuation character
626 const CharClass& rCC = GetAppCharClass();
627 if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) &&
628 rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) )
630 // does the kerning portion still fit into the line?
631 if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() )
632 new SwKernPortion( *pPor, nDist );
633 else
634 bFull = sal_True;
641 if ( bHasGrid && pPor != pGridKernPortion && ! pMulti )
643 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
644 const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
646 const BYTE nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() );
647 const BYTE nNextScript = nTmp >= rInf.GetTxt().Len() ?
648 SW_CJK :
649 SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo );
651 // snap non-asian text to grid if next portion is ASIAN or
652 // there are no more portions in this line
653 // be careful when handling an underflow event: the gridkernportion
654 // could have been deleted
655 if ( nRestWidth > 0 && SW_CJK != nCurrScript &&
656 ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) )
658 ASSERT( pGridKernPortion, "No GridKernPortion available" )
660 // calculate size
661 SwLinePortion* pTmpPor = pGridKernPortion->GetPortion();
662 USHORT nSumWidth = pPor->Width();
663 while ( pTmpPor )
665 nSumWidth = nSumWidth + pTmpPor->Width();
666 pTmpPor = pTmpPor->GetPortion();
669 const USHORT i = nSumWidth ?
670 ( nSumWidth - 1 ) / nGridWidth + 1 :
672 const SwTwips nTmpWidth = i * nGridWidth;
673 const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth),
674 nRestWidth );
675 const USHORT nKernWidth_1 = (USHORT)(nKernWidth / 2);
677 ASSERT( nKernWidth <= nRestWidth,
678 "Not enough space left for adjusting non-asian text in grid mode" )
680 pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
681 rInf.X( rInf.X() + nKernWidth_1 );
683 if ( ! bFull )
684 new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1),
685 sal_False, sal_True );
687 pGridKernPortion = 0;
689 else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() ||
690 pPor->IsFlyCntPortion() || pPor->InNumberGrp() ||
691 pPor->InFldGrp() || nCurrScript != nNextScript )
692 // next portion should snap to grid
693 pGridKernPortion = 0;
696 rInf.SetFull( bFull );
698 // Restportions von mehrzeiligen Feldern haben bisher noch
699 // nicht den richtigen Ascent.
700 if ( !pPor->GetLen() && !pPor->IsFlyPortion()
701 && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp()
702 && !pPor->IsMultiPortion() )
703 CalcAscent( rInf, pPor );
705 InsertPortion( rInf, pPor );
706 pPor = NewPortion( rInf );
709 if( !rInf.IsStop() )
711 // der letzte rechte, zentrierte, dezimale Tab
712 SwTabPortion *pLastTab = rInf.GetLastTab();
713 if( pLastTab )
714 pLastTab->FormatEOL( rInf );
715 else if( rInf.GetLast() && rInf.LastKernPortion() )
716 rInf.GetLast()->FormatEOL( rInf );
718 if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp()
719 && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() )
720 rInf.SetNumDone( sal_False );
722 // 3260, 3860: Fly auf jeden Fall loeschen!
723 ClearFly( rInf );
726 /*************************************************************************
727 * SwTxtFormatter::CalcAdjustLine()
728 *************************************************************************/
730 void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
732 if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti)
734 pCurrent->SetFormatAdj(sal_True);
735 if( IsFlyInCntBase() )
737 CalcAdjLine( pCurrent );
738 // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint
739 // auf jeden Fall umsetzen, deshalb bAllWays = sal_True
740 UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True );
745 /*************************************************************************
746 * SwTxtFormatter::CalcAscent()
747 *************************************************************************/
749 void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor )
751 if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() )
753 // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten,
754 // dann ist ihre Groesse unabhaengig von harten Attributierungen.
755 SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt;
756 SwFontSave aSave( rInf, pFldFnt );
757 ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
758 ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) );
760 // --> OD 2008-06-05 #i89179#
761 // tab portion representing the list tab of a list label gets the
762 // same height and ascent as the corresponding number portion
763 else if ( pPor->InTabGrp() && pPor->GetLen() == 0 &&
764 rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
765 static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() )
767 const SwLinePortion* pLast = rInf.GetLast();
768 pPor->Height( pLast->Height() );
769 pPor->SetAscent( pLast->GetAscent() );
771 // <--
772 else
774 const SwLinePortion *pLast = rInf.GetLast();
775 sal_Bool bChg;
777 // Fallunterscheidung: in leeren Zeilen werden die Attribute
778 // per SeekStart angeschaltet.
779 const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
780 if ( pPor->IsQuoVadisPortion() )
781 bChg = SeekStartAndChg( rInf, sal_True );
782 else
784 if( bFirstPor )
786 if( rInf.GetTxt().Len() )
788 if ( pPor->GetLen() || !rInf.GetIdx()
789 || ( pCurr != pLast && !pLast->IsFlyPortion() )
790 || !pCurr->IsRest() ) // statt !rInf.GetRest()
791 bChg = SeekAndChg( rInf );
792 else
793 bChg = SeekAndChgBefore( rInf );
795 else if ( pMulti )
796 // do not open attributes starting at 0 in empty multi
797 // portions (rotated numbering followed by a footnote
798 // can cause trouble, because the footnote attribute
799 // starts at 0, but if we open it, the attribute handler
800 // cannot handle it.
801 bChg = sal_False;
802 else
803 bChg = SeekStartAndChg( rInf );
805 else
806 bChg = SeekAndChg( rInf );
808 if( bChg || bFirstPor || !pPor->GetAscent()
809 || !rInf.GetLast()->InTxtGrp() )
811 pPor->SetAscent( rInf.GetAscent() );
812 pPor->Height( rInf.GetTxtHeight() );
814 else
816 pPor->Height( pLast->Height() );
817 pPor->SetAscent( pLast->GetAscent() );
822 /*************************************************************************
823 * SwTxtFormatter::WhichTxtPor()
824 *************************************************************************/
826 SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const
828 SwTxtPortion *pPor = 0;
829 if( GetFnt()->IsTox() )
830 pPor = new SwToxPortion;
831 else
833 if( GetFnt()->IsRef() )
834 pPor = new SwRefPortion;
835 else
837 // Erst zum Schluss !
838 // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben,
839 // z.B. bei nicht darstellbaren Zeichen.
840 if( rInf.GetLen() > 0 )
842 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART )
843 pPor = new SwFieldMarkPortion();
844 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND )
845 pPor = new SwFieldMarkPortion();
846 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT )
847 pPor = new SwFieldFormPortion();
849 if( !pPor )
851 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() )
852 pPor = pCurr;
853 else
855 pPor = new SwTxtPortion;
856 if( GetFnt()->IsURL() )
857 pPor->SetWhichPor( POR_URL );
862 return pPor;
865 /*************************************************************************
866 * SwTxtFormatter::NewTxtPortion()
867 *************************************************************************/
868 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert:
869 // 1) Tabs
870 // 2) Linebreaks
871 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
872 // 4) naechster Attributwechsel
874 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf )
876 // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr
877 // Wenn pCurr nicht von SwTxtPortion abgeleitet ist,
878 // muessen wir duplizieren ...
879 Seek( rInf.GetIdx() );
880 SwTxtPortion *pPor = WhichTxtPor( rInf );
882 // until next attribute change:
883 const xub_StrLen nNextAttr = GetNextAttr();
884 xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() );
886 // end of script type:
887 const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() );
888 nNextChg = Min( nNextChg, nNextScript );
890 // end of direction:
891 const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() );
892 nNextChg = Min( nNextChg, nNextDir );
894 // 7515, 7516, 3470, 6441 : Turbo-Boost
895 // Es wird unterstellt, dass die Buchstaben eines Fonts nicht
896 // groesser als doppelt so breit wie hoch sind.
897 // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen.
898 // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe
899 // ergibt sich erst im CalcAscent!
900 // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times
901 // New Roman besitzt einen Ascent von 182, eine Hoehe von 200
902 // und eine Breite von 53! Daraus folgt, dass eine Zeile mit
903 // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von
904 // Faktor 2 auf 8 (wg. negativen Kernings).
906 pPor->SetLen(1);
907 CalcAscent( rInf, pPor );
909 const SwFont* pTmpFnt = rInf.GetFont();
910 KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ),
911 KSHORT( pPor->GetAscent() ) ) / 8;
912 if ( !nExpect )
913 nExpect = 1;
914 nExpect = (USHORT)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect));
915 if( nExpect > rInf.GetIdx() && nNextChg > nExpect )
916 nNextChg = Min( nExpect, rInf.GetTxt().Len() );
918 // we keep an invariant during method calls:
919 // there are no portion ending characters like hard spaces
920 // or tabs in [ nLeftScanIdx, nRightScanIdx ]
921 if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx )
923 if ( nNextChg > nRightScanIdx )
924 nNextChg = nRightScanIdx =
925 rInf.ScanPortionEnd( nRightScanIdx, nNextChg );
927 else
929 nLeftScanIdx = rInf.GetIdx();
930 nNextChg = nRightScanIdx =
931 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
934 pPor->SetLen( nNextChg - rInf.GetIdx() );
935 rInf.SetLen( pPor->GetLen() );
936 return pPor;
940 /*************************************************************************
941 * SwTxtFormatter::WhichFirstPortion()
942 *************************************************************************/
944 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf)
946 SwLinePortion *pPor = 0;
948 if( rInf.GetRest() )
950 // 5010: Tabs und Felder
951 if( '\0' != rInf.GetHookChar() )
952 return 0;
954 pPor = rInf.GetRest();
955 if( pPor->IsErgoSumPortion() )
956 rInf.SetErgoDone(sal_True);
957 else
958 if( pPor->IsFtnNumPortion() )
959 rInf.SetFtnDone(sal_True);
960 else
961 if( pPor->InNumberGrp() )
962 rInf.SetNumDone(sal_True);
963 if( pPor )
965 rInf.SetRest(0);
966 pCurr->SetRest( sal_True );
967 return pPor;
971 // ???? und ????: im Follow duerfen wir schon stehen,
972 // entscheidend ist, ob pFrm->GetOfst() == 0 ist!
973 if( rInf.GetIdx() )
975 // Nun koennen auch FtnPortions und ErgoSumPortions
976 // verlaengert werden.
978 // 1) Die ErgoSumTexte
979 if( !rInf.IsErgoDone() )
981 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
982 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
983 rInf.SetErgoDone( sal_True );
986 // 2) Arrow portions
987 if( !pPor && !rInf.IsArrowDone() )
989 if( pFrm->GetOfst() && !pFrm->IsFollow() &&
990 rInf.GetIdx() == pFrm->GetOfst() )
991 pPor = new SwArrowPortion( *pCurr );
992 rInf.SetArrowDone( sal_True );
995 // 3) Kerning portions at beginning of line in grid mode
996 if ( ! pPor && ! pCurr->GetPortion() )
998 GETGRID( GetTxtFrm()->FindPageFrm() )
999 if ( pGrid )
1000 pPor = new SwKernPortion( *pCurr );
1003 // 4) Die Zeilenreste (mehrzeilige Felder)
1004 if( !pPor )
1006 pPor = rInf.GetRest();
1007 // 6922: Nur bei pPor natuerlich.
1008 if( pPor )
1010 pCurr->SetRest( sal_True );
1011 rInf.SetRest(0);
1015 else
1017 // 5) Die Fussnotenzahlen
1018 if( !rInf.IsFtnDone() )
1020 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1021 "Rotated number portion trouble" )
1023 sal_Bool bFtnNum = pFrm->IsFtnNumFrm();
1024 rInf.GetParaPortion()->SetFtnNum( bFtnNum );
1025 if( bFtnNum )
1026 pPor = (SwLinePortion*)NewFtnNumPortion( rInf );
1027 rInf.SetFtnDone( sal_True );
1030 // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster,
1031 // entscheidend ist, ob der SwFtnFrm ein Follow ist.
1032 if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
1034 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1035 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1036 rInf.SetErgoDone( sal_True );
1039 // 7) Die Numerierungen
1040 if( !rInf.IsNumDone() && !pPor )
1042 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1043 "Rotated number portion trouble" )
1045 // Wenn wir im Follow stehen, dann natuerlich nicht.
1046 if( GetTxtFrm()->GetTxtNode()->GetNumRule() )
1047 pPor = (SwLinePortion*)NewNumberPortion( rInf );
1048 rInf.SetNumDone( sal_True );
1050 // 8) Die DropCaps
1051 if( !pPor && GetDropFmt() && ! rInf.IsMulti() )
1052 pPor = (SwLinePortion*)NewDropPortion( rInf );
1054 // 9) Kerning portions at beginning of line in grid mode
1055 if ( !pPor && !pCurr->GetPortion() )
1057 GETGRID( GetTxtFrm()->FindPageFrm() )
1058 if ( pGrid )
1059 pPor = new SwKernPortion( *pCurr );
1063 // 10) Decimal tab portion at the beginning of each line in table cells
1064 if ( !pPor && !pCurr->GetPortion() &&
1065 GetTxtFrm()->IsInTab() &&
1066 GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) )
1068 pPor = NewTabPortion( rInf, true );
1071 return pPor;
1074 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr )
1076 if( !pCurr->GetNext() )
1077 return sal_False;
1078 const SwLinePortion *pPor = pCurr->GetNext()->GetPortion();
1079 sal_Bool bRet = sal_False;
1080 while( pPor && !bRet )
1082 bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) ||
1083 (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld());
1084 if( !pPor->GetLen() )
1085 break;
1086 pPor = pPor->GetPortion();
1088 return bRet;
1091 /*************************************************************************
1092 * SwTxtFormatter::NewPortion()
1093 *************************************************************************/
1095 /* NewPortion stellt rInf.nLen ein.
1096 * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr,
1097 * attrwechsel.
1098 * Drei Faelle koennen eintreten:
1099 * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert
1100 * -> return 0;
1101 * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert
1102 * -> Breite neu einstellen und return new FlyPortion
1103 * 3) Es muss eine neue Portion gebaut werden.
1104 * -> CalcFlyWidth emuliert ggf. die Breite und return Portion
1107 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf )
1109 // Underflow hat Vorrang
1110 rInf.SetStopUnderFlow( sal_False );
1111 if( rInf.GetUnderFlow() )
1113 ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" );
1114 return UnderFlow( rInf );
1117 // Wenn die Zeile voll ist, koennten noch Flys oder
1118 // UnderFlow-LinePortions warten ...
1119 if( rInf.IsFull() )
1121 // ????: LineBreaks und Flys (bug05.sdw)
1122 // 8450: IsDummy()
1123 if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) )
1124 return 0;
1126 // Wenn der Text an den Fly gestossen ist, oder wenn
1127 // der Fly als erstes drankommt, weil er ueber dem linken
1128 // Rand haengt, wird GetFly() returnt.
1129 // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's
1130 // naturgemaesz eine 0.
1131 if( rInf.GetFly() )
1133 if( rInf.GetLast()->IsBreakPortion() )
1135 delete rInf.GetFly();
1136 rInf.SetFly( 0 );
1139 return rInf.GetFly();
1141 // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den
1142 // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest
1143 // bekanntgeben, damit SwTxtFrm::Format nicht abbricht
1144 // (die Textmasse wurde ja durchformatiert).
1145 if( rInf.GetRest() )
1146 rInf.SetNewLine( sal_True );
1147 else
1149 // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt,
1150 // jetzt aber kein Rest mehr anliegt,
1151 // muss sie auf jeden Fall neu formatiert werden!
1152 if( lcl_OldFieldRest( GetCurr() ) )
1153 rInf.SetNewLine( sal_True );
1154 else
1156 SwLinePortion *pFirst = WhichFirstPortion( rInf );
1157 if( pFirst )
1159 rInf.SetNewLine( sal_True );
1160 if( pFirst->InNumberGrp() )
1161 rInf.SetNumDone( sal_False) ;
1162 delete pFirst;
1167 return 0;
1170 SwLinePortion *pPor = WhichFirstPortion( rInf );
1172 // Check for Hidden Portion:
1173 if ( !pPor )
1175 xub_StrLen nEnd = rInf.GetIdx();
1176 if ( lcl_BuildHiddenPortion( rInf, nEnd ) )
1177 pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
1180 if( !pPor )
1182 if( ( !pMulti || pMulti->IsBidi() ) &&
1183 // --> FME 2005-02-14 #i42734#
1184 // No multi portion if there is a hook character waiting:
1185 ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
1186 // <--
1188 // We open a multiportion part, if we enter a multi-line part
1189 // of the paragraph.
1190 xub_StrLen nEnd = rInf.GetIdx();
1191 SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti );
1192 if( pCreate )
1194 SwMultiPortion* pTmp = NULL;
1196 if ( SW_MC_BIDI == pCreate->nId )
1197 pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
1198 else if ( SW_MC_RUBY == pCreate->nId )
1200 Seek( rInf.GetIdx() );
1201 sal_Bool bRubyTop;
1202 sal_Bool* pRubyPos = 0;
1204 if ( rInf.SnapToGrid() )
1206 GETGRID( GetTxtFrm()->FindPageFrm() )
1207 if ( pGrid )
1209 bRubyTop = ! pGrid->GetRubyTextBelow();
1210 pRubyPos = &bRubyTop;
1214 pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
1215 *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(),
1216 nEnd, 0, pRubyPos );
1218 else if( SW_MC_ROTATE == pCreate->nId )
1219 pTmp = new SwRotatedPortion( *pCreate, nEnd,
1220 GetTxtFrm()->IsRightToLeft() );
1221 else
1222 pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
1224 delete pCreate;
1225 CalcFlyWidth( rInf );
1227 return pTmp;
1230 // 5010: Tabs und Felder
1231 xub_Unicode cChar = rInf.GetHookChar();
1233 if( cChar )
1235 /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das
1236 * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile
1237 * gewandert ist ( so geschehen hinter Rahmen ).
1238 * Wenn allerdings eine FldPortion im Rest wartet, muessen wir
1239 * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei
1240 * DezimalTabs und Feldern (22615)
1242 if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() )
1243 cChar = rInf.GetChar( rInf.GetIdx() );
1244 rInf.ClearHookChar();
1246 else
1248 if( rInf.GetIdx() >= rInf.GetTxt().Len() )
1250 rInf.SetFull(sal_True);
1251 CalcFlyWidth( rInf );
1252 return pPor;
1254 cChar = rInf.GetChar( rInf.GetIdx() );
1257 switch( cChar )
1259 case CH_TAB:
1260 pPor = NewTabPortion( rInf, false ); break;
1262 case CH_BREAK:
1263 pPor = new SwBreakPortion( *rInf.GetLast() ); break;
1265 case CHAR_SOFTHYPHEN: // soft hyphen
1266 pPor = new SwSoftHyphPortion; break;
1268 case CHAR_HARDBLANK: // no-break space
1269 pPor = new SwBlankPortion( ' ' ); break;
1271 case CHAR_HARDHYPHEN: // non-breaking hyphen
1272 pPor = new SwBlankPortion( '-' ); break;
1274 case CHAR_ZWSP: // zero width space
1275 case CHAR_ZWNBSP : // word joiner
1276 // case CHAR_RLM : // right to left mark
1277 // case CHAR_LRM : // left to right mark
1278 pPor = new SwControlCharPortion( cChar ); break;
1280 case CH_TXTATR_BREAKWORD:
1281 case CH_TXTATR_INWORD:
1282 if( rInf.HasHint( rInf.GetIdx() ) )
1284 pPor = NewExtraPortion( rInf );
1285 break;
1287 // No break
1288 default :
1290 SwTabPortion* pLastTabPortion = rInf.GetLastTab();
1291 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
1293 // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full:
1294 // We have a decimal tab portion in the line and the next character has to be
1295 // aligned at the tab stop position. We store the width from the beginning of
1296 // the tab stop portion up to the portion containint the decimal separator:
1297 if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
1298 POR_TABDECIMAL == pLastTabPortion->GetWhichPor() )
1300 ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" )
1301 const USHORT nWidthOfPortionsUpToDecimalPosition = (USHORT)(rInf.X() - pLastTabPortion->Fix() );
1302 static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
1303 rInf.SetTabDecimal( 0 );
1305 // <--
1306 else
1307 rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
1310 if( rInf.GetRest() )
1312 if( rInf.IsFull() )
1314 rInf.SetNewLine(sal_True);
1315 return 0;
1317 pPor = rInf.GetRest();
1318 rInf.SetRest(0);
1320 else
1322 if( rInf.IsFull() )
1323 return 0;
1324 pPor = NewTxtPortion( rInf );
1326 break;
1330 // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht,
1331 // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet
1332 // hat, weil z.B. ein Tab enthalten ist.
1333 if( pPor && rInf.GetRest() )
1334 pPor->SetLen( 0 );
1336 // robust:
1337 if( !pPor || rInf.IsStop() )
1339 delete pPor;
1340 return 0;
1344 // Special portions containing numbers (footnote anchor, footnote number,
1345 // numbering) can be contained in a rotated portion, if the user
1346 // choose a rotated character attribute.
1347 if ( pPor && ! pMulti )
1349 if ( pPor->IsFtnPortion() )
1351 const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn();
1353 if ( pTxtFtn )
1355 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1356 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1357 const SwEndNoteInfo* pInfo;
1358 if( rFtn.IsEndNote() )
1359 pInfo = &pDoc->GetEndNoteInfo();
1360 else
1361 pInfo = &pDoc->GetFtnInfo();
1362 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1364 const SfxPoolItem* pItem;
1365 USHORT nDir = 0;
1366 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1367 sal_True, &pItem ))
1368 nDir = ((SvxCharRotateItem*)pItem)->GetValue();
1370 if ( 0 != nDir )
1372 delete pPor;
1373 pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ?
1374 DIR_BOTTOM2TOP :
1375 DIR_TOP2BOTTOM );
1379 else if ( pPor->InNumberGrp() )
1381 const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont();
1383 if ( pNumFnt )
1385 USHORT nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
1386 if ( 0 != nDir )
1388 delete pPor;
1389 pPor = new SwRotatedPortion( 0, 900 == nDir ?
1390 DIR_BOTTOM2TOP :
1391 DIR_TOP2BOTTOM );
1393 rInf.SetNumDone( sal_False );
1394 rInf.SetFtnDone( sal_False );
1400 // Der Font wird im Outputdevice eingestellt,
1401 // der Ascent und die Hoehe werden berechnet.
1402 if( !pPor->GetAscent() && !pPor->Height() )
1403 CalcAscent( rInf, pPor );
1404 rInf.SetLen( pPor->GetLen() );
1406 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
1407 CalcFlyWidth( rInf );
1409 // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige
1410 // Werte bereithalten muss:
1411 if( !pCurr->Height() )
1413 ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" );
1414 pCurr->Height( pPor->Height() );
1415 pCurr->SetAscent( pPor->GetAscent() );
1418 ASSERT( !pPor || pPor->Height(),
1419 "SwTxtFormatter::NewPortion: something went wrong");
1420 if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
1422 delete pPor;
1423 pPor = rInf.GetFly();
1425 return pPor;
1428 /*************************************************************************
1429 * SwTxtFormatter::FormatLine()
1430 *************************************************************************/
1432 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos )
1434 ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
1435 "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" );
1437 // For the formatting routines, we set pOut to the reference device.
1438 SwHookOut aHook( GetInfo() );
1439 if( GetInfo().GetLen() < GetInfo().GetTxt().Len() )
1440 GetInfo().SetLen( GetInfo().GetTxt().Len() );
1442 sal_Bool bBuild = sal_True;
1443 SetFlyInCntBase( sal_False );
1444 GetInfo().SetLineHeight( 0 );
1445 GetInfo().SetLineNettoHeight( 0 );
1447 // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden
1448 // und auch bei geaendertem Ascent (Absenken der Grundlinie).
1449 const KSHORT nOldHeight = pCurr->Height();
1450 const KSHORT nOldAscent = pCurr->GetAscent();
1452 pCurr->SetEndHyph( sal_False );
1453 pCurr->SetMidHyph( sal_False );
1455 // fly positioning can make it necessary format a line several times
1456 // for this, we have to keep a copy of our rest portion
1457 SwLinePortion* pFld = GetInfo().GetRest();
1458 SwFldPortion* pSaveFld = 0;
1460 if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() )
1461 pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) );
1463 // for an optimal repaint rectangle, we want to compare fly portions
1464 // before and after the BuildPortions call
1465 const sal_Bool bOptimizeRepaint = AllowRepaintOpt();
1466 const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen();
1467 SvLongs* pFlyStart = 0;
1469 // these are the conditions for a fly position comparison
1470 if ( bOptimizeRepaint && pCurr->IsFly() )
1472 pFlyStart = new SvLongs;
1473 SwLinePortion* pPor = pCurr->GetFirstPortion();
1474 long nPOfst = 0;
1475 USHORT nCnt = 0;
1477 while ( pPor )
1479 if ( pPor->IsFlyPortion() )
1480 // insert start value of fly portion
1481 pFlyStart->Insert( nPOfst, nCnt++ );
1483 nPOfst += pPor->Width();
1484 pPor = pPor->GetPortion();
1488 // Hier folgt bald die Unterlaufpruefung.
1489 while( bBuild )
1491 GetInfo().SetFtnInside( sal_False );
1493 // These values must not be reset by FormatReset();
1494 sal_Bool bOldNumDone = GetInfo().IsNumDone();
1495 sal_Bool bOldArrowDone = GetInfo().IsArrowDone();
1496 sal_Bool bOldErgoDone = GetInfo().IsErgoDone();
1498 // besides other things, this sets the repaint offset to 0
1499 FormatReset( GetInfo() );
1501 GetInfo().SetNumDone( bOldNumDone );
1502 GetInfo().SetArrowDone( bOldArrowDone );
1503 GetInfo().SetErgoDone( bOldErgoDone );
1505 // build new portions for this line
1506 BuildPortions( GetInfo() );
1508 if( GetInfo().IsStop() )
1510 pCurr->SetLen( 0 );
1511 pCurr->Height( GetFrmRstHeight() + 1 );
1512 pCurr->SetRealHeight( GetFrmRstHeight() + 1 );
1513 pCurr->Width(0);
1514 pCurr->Truncate();
1515 return nStartPos;
1517 else if( GetInfo().IsDropInit() )
1519 DropInit();
1520 GetInfo().SetDropInit( sal_False );
1523 pCurr->CalcLine( *this, GetInfo() );
1524 CalcRealHeight( GetInfo().IsNewLine() );
1526 if ( IsFlyInCntBase() && !IsQuick() )
1528 KSHORT nTmpAscent, nTmpHeight;
1529 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
1530 AlignFlyInCntBase( Y() + long( nTmpAscent ) );
1531 pCurr->CalcLine( *this, GetInfo() );
1532 CalcRealHeight();
1535 // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird
1536 if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
1538 pCurr->SetRealHeight( GetInfo().GetLineHeight() );
1539 bBuild = sal_False;
1541 else
1543 bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow( GetInfo() )
1544 || GetInfo().CheckFtnPortion( pCurr ) );
1545 if( bBuild )
1547 GetInfo().SetNumDone( bOldNumDone );
1548 GetInfo().ResetMaxWidthDiff();
1550 // delete old rest
1551 if ( GetInfo().GetRest() )
1553 delete GetInfo().GetRest();
1554 GetInfo().SetRest( 0 );
1557 // set original rest portion
1558 if ( pSaveFld )
1559 GetInfo().SetRest( new SwFldPortion( *pSaveFld ) );
1561 pCurr->SetLen( 0 );
1562 pCurr->Width(0);
1563 pCurr->Truncate();
1568 // calculate optimal repaint rectangle
1569 if ( bOptimizeRepaint )
1571 GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) );
1572 if ( pFlyStart )
1573 delete pFlyStart;
1575 else
1576 // Special case: We do not allow an optimitation of the repaint
1577 // area, but during formatting the repaint offset is set to indicate
1578 // a maximum value for the offset. This value has to be reset:
1579 GetInfo().SetPaintOfst( 0 );
1581 // This corrects the start of the reformat range if something has
1582 // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
1583 // will give us a wrong result if we have to reformat another line
1584 GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() );
1586 // delete master copy of rest portion
1587 if ( pSaveFld )
1588 delete pSaveFld;
1590 xub_StrLen nNewStart = nStartPos + pCurr->GetLen();
1592 // adjust text if kana compression is enabled
1593 if ( GetInfo().CompressLine() )
1595 SwTwips nRepaintOfst = CalcKanaAdj( pCurr );
1597 // adjust repaint offset
1598 if ( nRepaintOfst < GetInfo().GetPaintOfst() )
1599 GetInfo().SetPaintOfst( nRepaintOfst );
1602 CalcAdjustLine( pCurr );
1604 if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() )
1606 SetFlyInCntBase();
1607 GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling
1608 // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind
1609 // auch formatiert werden.
1610 GetInfo().SetShift( sal_True );
1613 if ( IsFlyInCntBase() && !IsQuick() )
1614 UpdatePos( pCurr, GetTopLeft(), GetStart() );
1616 return nNewStart;
1619 /*************************************************************************
1620 * SwTxtFormatter::RecalcRealHeight()
1621 *************************************************************************/
1623 void SwTxtFormatter::RecalcRealHeight()
1625 sal_Bool bMore = sal_True;
1626 while(bMore)
1628 DBG_LOOP;
1629 CalcRealHeight();
1630 bMore = Next() != 0;
1634 /*************************************************************************
1635 * SwTxtFormatter::CalcRealHeight()
1636 *************************************************************************/
1638 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine )
1640 KSHORT nLineHeight = pCurr->Height();
1641 pCurr->SetClipping( sal_False );
1643 GETGRID( pFrm->FindPageFrm() )
1644 if ( pGrid && GetInfo().SnapToGrid() )
1646 const USHORT nGridWidth = pGrid->GetBaseHeight();
1647 const USHORT nRubyHeight = pGrid->GetRubyHeight();
1648 const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow();
1650 nLineHeight = nGridWidth + nRubyHeight;
1651 USHORT nLineDist = nLineHeight;
1653 while ( pCurr->Height() > nLineHeight )
1654 nLineHeight = nLineHeight + nLineDist;
1656 KSHORT nAsc = pCurr->GetAscent() +
1657 ( bRubyTop ?
1658 ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 :
1659 ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 );
1661 pCurr->Height( nLineHeight );
1662 pCurr->SetAscent( nAsc );
1663 pInf->GetParaPortion()->SetFixLineHeight();
1665 // we ignore any line spacing options except from ...
1666 const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing();
1667 if ( ! IsParaLine() && pSpace &&
1668 SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() )
1670 ULONG nTmp = pSpace->GetPropLineSpace();
1672 if( nTmp < 100 )
1673 nTmp = 100;
1675 nTmp *= nLineHeight;
1676 nLineHeight = (USHORT)(nTmp / 100);
1679 pCurr->SetRealHeight( nLineHeight );
1680 return;
1683 // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese
1684 // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere
1685 // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem
1686 // Shift-Return), die das Register durchaus beachten soll.
1687 if( !pCurr->IsDummy() || ( !pCurr->GetNext() &&
1688 GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) )
1690 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
1691 if( pSpace )
1693 switch( pSpace->GetLineSpaceRule() )
1695 case SVX_LINE_SPACE_AUTO:
1696 if (pSpace->GetInterLineSpaceRule()==SVX_INTER_LINE_SPACE_PROP) {
1697 long nTmp = pSpace->GetPropLineSpace();
1698 if (nTmp<100) { // code adaped from fixed line height
1699 nTmp *= nLineHeight;
1700 nTmp /= 100;
1701 if( !nTmp )
1702 ++nTmp;
1703 nLineHeight = (KSHORT)nTmp;
1705 //@TODO figure out how WW maps ascent and descent
1706 //in case of prop line spacing <100%
1707 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80%
1708 if( nAsc < pCurr->GetAscent() ||
1709 nLineHeight - nAsc < pCurr->Height() -
1710 pCurr->GetAscent() )
1711 pCurr->SetClipping( sal_True );
1712 pCurr->SetAscent( nAsc );
1714 pCurr->Height( nLineHeight );
1715 pInf->GetParaPortion()->SetFixLineHeight();
1718 break;
1719 case SVX_LINE_SPACE_MIN:
1721 if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) )
1722 nLineHeight = pSpace->GetLineHeight();
1723 break;
1725 case SVX_LINE_SPACE_FIX:
1727 nLineHeight = pSpace->GetLineHeight();
1728 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80%
1729 if( nAsc < pCurr->GetAscent() ||
1730 nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() )
1731 pCurr->SetClipping( sal_True );
1732 pCurr->Height( nLineHeight );
1733 pCurr->SetAscent( nAsc );
1734 pInf->GetParaPortion()->SetFixLineHeight();
1736 break;
1737 default: ASSERT( sal_False, ": unknown LineSpaceRule" );
1739 if( !IsParaLine() )
1740 switch( pSpace->GetInterLineSpaceRule() )
1742 case SVX_INTER_LINE_SPACE_OFF:
1743 break;
1744 case SVX_INTER_LINE_SPACE_PROP:
1746 long nTmp = pSpace->GetPropLineSpace();
1747 // 50% ist das Minimum, bei 0% schalten wir auf
1748 // den Defaultwert 100% um ...
1749 if( nTmp < 50 )
1750 nTmp = nTmp ? 50 : 100;
1752 nTmp *= nLineHeight;
1753 nTmp /= 100;
1754 if( !nTmp )
1755 ++nTmp;
1756 nLineHeight = (KSHORT)nTmp;
1757 break;
1759 case SVX_INTER_LINE_SPACE_FIX:
1761 nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
1762 break;
1764 default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
1767 #if OSL_DEBUG_LEVEL > 1
1768 KSHORT nDummy = nLineHeight + 1;
1769 (void)nDummy;
1770 #endif
1772 if( IsRegisterOn() )
1774 SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height();
1775 SWRECTFN( pFrm )
1776 if ( bVert )
1777 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY );
1778 nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() );
1779 KSHORT nDiff = KSHORT( nTmpY % RegDiff() );
1780 if( nDiff )
1781 nLineHeight += RegDiff() - nDiff;
1784 pCurr->SetRealHeight( nLineHeight );
1787 /*************************************************************************
1788 * SwTxtFormatter::FeedInf()
1789 *************************************************************************/
1791 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const
1793 // 3260, 3860: Fly auf jeden Fall loeschen!
1794 ClearFly( rInf );
1795 rInf.Init();
1797 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
1798 rInf.SetRoot( pCurr );
1799 rInf.SetLineStart( nStart );
1800 rInf.SetIdx( nStart );
1802 // Handle overflows:
1803 // --> FME 2004-11-25 #i34348# Changed type from USHORT to SwTwips
1804 SwTwips nTmpLeft = Left();
1805 SwTwips nTmpRight = Right();
1806 SwTwips nTmpFirst = FirstLeft();
1807 // <--
1809 if ( nTmpLeft > USHRT_MAX ||
1810 nTmpRight > USHRT_MAX ||
1811 nTmpFirst > USHRT_MAX )
1813 SWRECTFN( rInf.GetTxtFrm() )
1814 nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)();
1815 nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)();
1816 nTmpFirst = nTmpLeft;
1819 rInf.Left( nTmpLeft );
1820 rInf.Right( nTmpRight );
1821 rInf.First( nTmpFirst );
1823 rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) );
1824 rInf.Width( rInf.RealWidth() );
1825 if( ((SwTxtFormatter*)this)->GetRedln() )
1827 ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() );
1828 ((SwTxtFormatter*)this)->GetRedln()->Reset();
1832 /*************************************************************************
1833 * SwTxtFormatter::FormatReset()
1834 *************************************************************************/
1836 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf )
1838 pCurr->Truncate();
1839 pCurr->Init();
1840 if( pBlink && pCurr->IsBlinking() )
1841 pBlink->Delete( pCurr );
1843 // delete pSpaceAdd und pKanaComp
1844 pCurr->FinishSpaceAdd();
1845 pCurr->FinishKanaComp();
1846 pCurr->ResetFlags();
1847 FeedInf( rInf );
1850 /*************************************************************************
1851 * SwTxtFormatter::CalcOnceMore()
1852 *************************************************************************/
1854 sal_Bool SwTxtFormatter::CalcOnceMore()
1856 if( pDropFmt )
1858 const KSHORT nOldDrop = GetDropHeight();
1859 CalcDropHeight( pDropFmt->GetLines() );
1860 bOnceMore = nOldDrop != GetDropHeight();
1862 else
1863 bOnceMore = sal_False;
1864 return bOnceMore;
1867 /*************************************************************************
1868 * SwTxtFormatter::CalcBottomLine()
1869 *************************************************************************/
1871 SwTwips SwTxtFormatter::CalcBottomLine() const
1873 SwTwips nRet = Y() + GetLineHeight();
1874 SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom();
1875 if( nMin && ++nMin > nRet )
1877 SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height()
1878 - pFrm->Prt().Top();
1879 if( nRet + nDist < nMin )
1881 sal_Bool bRepaint = HasTruncLines() &&
1882 GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1;
1883 nRet = nMin - nDist;
1884 if( bRepaint )
1886 ((SwRepaint*)GetInfo().GetParaPortion()
1887 ->GetRepaint())->Bottom( nRet-1 );
1888 ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 );
1892 return nRet;
1895 /*************************************************************************
1896 * SwTxtFormatter::_CalcFitToContent()
1898 * FME/OD: This routine does a limited text formatting.
1899 *************************************************************************/
1901 SwTwips SwTxtFormatter::_CalcFitToContent()
1903 FormatReset( GetInfo() );
1904 BuildPortions( GetInfo() );
1905 pCurr->CalcLine( *this, GetInfo() );
1906 return pCurr->Width();
1909 /*************************************************************************
1910 * SwTxtFormatter::AllowRepaintOpt()
1912 * determines if the calculation of a repaint offset is allowed
1913 * otherwise each line is painted from 0 (this is a copy of the beginning
1914 * of the former SwTxtFormatter::Recycle() function
1915 *************************************************************************/
1916 sal_Bool SwTxtFormatter::AllowRepaintOpt() const
1918 // reformat position in front of current line? Only in this case
1919 // we want to set the repaint offset
1920 sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() &&
1921 pCurr->GetLen();
1923 // a special case is the last line of a block adjusted paragraph:
1924 if ( bOptimizeRepaint )
1926 switch( GetAdjust() )
1928 case SVX_ADJUST_BLOCK:
1930 if( IsLastBlock() || IsLastCenter() )
1931 bOptimizeRepaint = sal_False;
1932 else
1934 // ????: Blank in der letzten Masterzeile (blocksat.sdw)
1935 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow();
1936 if ( bOptimizeRepaint )
1938 SwLinePortion *pPos = pCurr->GetFirstPortion();
1939 while ( pPos && !pPos->IsFlyPortion() )
1940 pPos = pPos->GetPortion();
1941 bOptimizeRepaint = !pPos;
1944 break;
1946 case SVX_ADJUST_CENTER:
1947 case SVX_ADJUST_RIGHT:
1948 bOptimizeRepaint = sal_False;
1949 break;
1950 default: ;
1954 // Schon wieder ein Sonderfall: unsichtbare SoftHyphs
1955 const xub_StrLen nReformat = GetInfo().GetReformatStart();
1956 if( bOptimizeRepaint && STRING_LEN != nReformat )
1958 const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat );
1959 bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
1960 || ! GetInfo().HasHint( nReformat );
1963 return bOptimizeRepaint;
1966 /*************************************************************************
1967 * SwTxtFormatter::CalcOptRepaint()
1969 * calculates an optimal repaint offset for the current line
1970 *************************************************************************/
1971 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd,
1972 const SvLongs* pFlyStart )
1974 if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() )
1975 // the reformat position is behind our new line, that means
1976 // something of our text has moved to the next line
1977 return 0;
1979 xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd );
1981 // in case we do not have any fly in our line, our repaint position
1982 // is the changed position - 1
1983 if ( ! pFlyStart && ! pCurr->IsFly() )
1985 // this is the maximum repaint offset determined during formatting
1986 // for example: the beginning of the first right tab stop
1987 // if this value is 0, this means that we do not have an upper
1988 // limit for the repaint offset
1989 const long nFormatRepaint = GetInfo().GetPaintOfst();
1991 if ( nReformat < GetInfo().GetLineStart() + 3 )
1992 return 0;
1994 // step back two positions for smoother repaint
1995 nReformat -= 2;
1997 #ifndef QUARTZ
1998 #ifndef ENABLE_GRAPHITE
1999 // --> FME 2004-09-27 #i28795#, #i34607#, #i38388#
2000 // step back six(!) more characters for complex scripts
2001 // this is required e.g., for Khmer (thank you, Javier!)
2002 const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
2003 xub_StrLen nMaxContext = 0;
2004 if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) )
2005 nMaxContext = 6;
2006 #else
2007 // Some Graphite fonts need context for scripts not marked as complex
2008 static const xub_StrLen nMaxContext = 10;
2009 #endif
2010 #else
2011 // some fonts like Quartz's Zapfino need more context
2012 // TODO: query FontInfo for maximum unicode context
2013 static const xub_StrLen nMaxContext = 8;
2014 #endif
2015 if( nMaxContext > 0 )
2017 if ( nReformat > GetInfo().GetLineStart() + nMaxContext )
2018 nReformat = nReformat - nMaxContext;
2019 else
2020 nReformat = GetInfo().GetLineStart();
2022 // <--
2024 // Weird situation: Our line used to end with a hole portion
2025 // and we delete some characters at the end of our line. We have
2026 // to take care for repainting the blanks which are not anymore
2027 // covered by the hole portion
2028 while ( nReformat > GetInfo().GetLineStart() &&
2029 CH_BLANK == GetInfo().GetChar( nReformat ) )
2030 --nReformat;
2032 ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" );
2033 SwRect aRect;
2035 // Note: GetChareRect is not const. It definitely changes the
2036 // bMulti flag. We have to save and resore the old value.
2037 sal_Bool bOldMulti = GetInfo().IsMulti();
2038 GetCharRect( &aRect, nReformat );
2039 GetInfo().SetMulti( bOldMulti );
2041 return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) :
2042 aRect.Left();
2044 else
2046 // nReformat may be wrong, if something around flys has changed:
2047 // we compare the former and the new fly positions in this line
2048 // if anything has changed, we carefully have to adjust the right
2049 // repaint position
2050 long nPOfst = 0;
2051 USHORT nCnt = 0;
2052 USHORT nX = 0;
2053 USHORT nIdx = GetInfo().GetLineStart();
2054 SwLinePortion* pPor = pCurr->GetFirstPortion();
2056 while ( pPor )
2058 if ( pPor->IsFlyPortion() )
2060 // compare start of fly with former start of fly
2061 if ( pFlyStart &&
2062 nCnt < pFlyStart->Count() &&
2063 nX == (*pFlyStart)[ nCnt ] &&
2064 nIdx < nReformat
2066 // found fix position, nothing has changed left from nX
2067 nPOfst = nX + pPor->Width();
2068 else
2069 break;
2071 nCnt++;
2073 nX = nX + pPor->Width();
2074 nIdx = nIdx + pPor->GetLen();
2075 pPor = pPor->GetPortion();
2078 return nPOfst + GetLeftMargin();
2082 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos )
2084 // Only if hidden text should not be shown:
2085 if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
2086 return false;
2088 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
2089 xub_StrLen nHiddenStart;
2090 xub_StrLen nHiddenEnd;
2091 rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
2092 if ( nHiddenEnd )
2094 rPos = nHiddenEnd;
2095 return true;
2098 return false;