1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26
27 #include "hintids.hxx"
28
29 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
30 #include <com/sun/star/i18n/ScriptType.hdl>
31 #endif
32 #include <editeng/lspcitem.hxx>
33 #include <txtftn.hxx>
34 #include <fmtftn.hxx>
35 #include <ftninfo.hxx>
36 #include <charfmt.hxx>
37 #include <editeng/charrotateitem.hxx>
38 #include <layfrm.hxx> // GetFrmRstHeight, etc
39 #include <viewsh.hxx>
40 #include <viewopt.hxx> // SwViewOptions
41 #include <paratr.hxx> // SwFmtDrop
42 #include <txtcfg.hxx>
43 #include <itrform2.hxx>
44 #include <porrst.hxx>
45 #include <portab.hxx> // pLastTab->
46 #include <porfly.hxx> // CalcFlyWidth
47 #include <portox.hxx> // WhichTxtPortion
48 #include <porref.hxx> // WhichTxtPortion
49 #include <porfld.hxx> // SwNumberPortion fuer CalcAscent()
50 #include <porftn.hxx> // SwFtnPortion
51 #include <porhyph.hxx>
52 #include <guess.hxx>
53 #include <blink.hxx> // pBlink
54 #include <ftnfrm.hxx> // WhichFirstPortion() -> mal Verlagern.
55 #include <redlnitr.hxx> // SwRedlineItr
56 #include <pagefrm.hxx>
57 #include <pagedesc.hxx> // SwPageDesc
58 #include <tgrditem.hxx>
59 #include <doc.hxx> // SwDoc
60 #include <pormulti.hxx> // SwMultiPortion
61 #define _SVSTDARR_LONGS
62 #include <svl/svstdarr.hxx>
63 #include <unotools/charclass.hxx>
64
65 #if OSL_DEBUG_LEVEL > 1
66 #include <ndtxt.hxx> // pSwpHints, Ausgabeoperator
67 #endif
68
69 using namespace ::com::sun::star;
70
71 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
72 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos );
73
74 #define MAX_TXTPORLEN 300
75
ClearFly(SwTxtFormatInfo & rInf)76 inline void ClearFly( SwTxtFormatInfo &rInf )
77 {
78 if( rInf.GetFly() )
79 {
80 delete rInf.GetFly();
81 rInf.SetFly(0);
82 }
83 }
84
85 /*************************************************************************
86 * SwTxtFormatter::CtorInitTxtFormatter()
87 *************************************************************************/
88
CtorInitTxtFormatter(SwTxtFrm * pNewFrm,SwTxtFormatInfo * pNewInf)89 void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf )
90 {
91 CtorInitTxtPainter( pNewFrm, pNewInf );
92 pInf = pNewInf;
93 pDropFmt = GetInfo().GetDropFmt();
94 pMulti = NULL;
95
96 bOnceMore = sal_False;
97 bFlyInCntBase = sal_False;
98 bChanges = sal_False;
99 bTruncLines = sal_False;
100 nCntEndHyph = 0;
101 nCntMidHyph = 0;
102 nLeftScanIdx = STRING_LEN;
103 nRightScanIdx = 0;
104 m_nHintEndIndex = 0;
105
106 if( nStart > GetInfo().GetTxt().Len() )
107 {
108 ASSERT( sal_False, "+SwTxtFormatter::CTOR: bad offset" );
109 nStart = GetInfo().GetTxt().Len();
110 }
111
112 }
113
114 /*************************************************************************
115 * SwTxtFormatter::DTOR
116 *************************************************************************/
117
~SwTxtFormatter()118 SwTxtFormatter::~SwTxtFormatter()
119 {
120 // Auesserst unwahrscheinlich aber denkbar.
121 // z.B.: Feld spaltet sich auf, Widows schlagen zu
122 if( GetInfo().GetRest() )
123 {
124 delete GetInfo().GetRest();
125 GetInfo().SetRest(0);
126 }
127 }
128
129 /*************************************************************************
130 * SwTxtFormatter::Insert()
131 *************************************************************************/
132
Insert(SwLineLayout * pLay)133 void SwTxtFormatter::Insert( SwLineLayout *pLay )
134 {
135 // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element.
136 if ( pCurr )
137 {
138 pLay->SetNext( pCurr->GetNext() );
139 pCurr->SetNext( pLay );
140 }
141 else
142 pCurr = pLay;
143 }
144
145 /*************************************************************************
146 * SwTxtFormatter::GetFrmRstHeight()
147 *************************************************************************/
148
GetFrmRstHeight() const149 KSHORT SwTxtFormatter::GetFrmRstHeight() const
150 {
151 // 8725: Uns interessiert die Resthoehe bezogen auf die Seite.
152 // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht
153 // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn
154 // gerufen.
155 // Falsch: const SwFrm *pUpper = pFrm->GetUpper();
156 const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm();
157 const SwTwips nHeight = pPage->Frm().Top()
158 + pPage->Prt().Top()
159 + pPage->Prt().Height() - Y();
160 if( 0 > nHeight )
161 return pCurr->Height();
162 else
163 return KSHORT( nHeight );
164 }
165
166 /*************************************************************************
167 * SwTxtFormatter::UnderFlow()
168 *************************************************************************/
169
UnderFlow(SwTxtFormatInfo & rInf)170 SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf )
171 {
172 // Werte sichern und rInf initialisieren.
173 SwLinePortion *pUnderFlow = rInf.GetUnderFlow();
174 if( !pUnderFlow )
175 return 0;
176
177 // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der
178 // naechsten Zeile durchaus noch einmal drankommen koennen.
179 // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt.
180
181 const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos();
182 const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos();
183
184 // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF
185 // 3983: Nicht ClearFly(rInf) !
186 SwFlyPortion *pFly = rInf.GetFly();
187 rInf.SetFly( 0 );
188
189 FeedInf( rInf );
190 rInf.SetLast( pCurr );
191 // pUnderFlow braucht nicht deletet werden, weil es im folgenden
192 // Truncate() untergehen wird.
193 rInf.SetUnderFlow(0);
194 rInf.SetSoftHyphPos( nSoftHyphPos );
195 rInf.SetUnderScorePos( nUnderScorePos );
196 rInf.SetPaintOfst( GetLeftMargin() );
197
198 // Wir suchen die Portion mit der Unterlaufposition
199 SwLinePortion *pPor = pCurr->GetFirstPortion();
200 if( pPor != pUnderFlow )
201 {
202 // pPrev wird die letzte Portion vor pUnderFlow,
203 // die noch eine echte Breite hat.
204 // Ausnahme: SoftHyphPortions duerfen dabei natuerlich
205 // nicht vergessen werden, obwohl sie keine Breite haben.
206 SwLinePortion *pTmpPrev = pPor;
207 while( pPor && pPor != pUnderFlow )
208 {
209 DBG_LOOP;
210 if( !pPor->IsKernPortion() &&
211 ( pPor->Width() || pPor->IsSoftHyphPortion() ) )
212 {
213 while( pTmpPrev != pPor )
214 {
215 pTmpPrev->Move( rInf );
216 rInf.SetLast( pTmpPrev );
217 pTmpPrev = pTmpPrev->GetPortion();
218 ASSERT( pTmpPrev, "UnderFlow: Losing control!" );
219 };
220 }
221 pPor = pPor->GetPortion();
222 }
223 pPor = pTmpPrev;
224 if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen
225 ( pPor->IsFlyPortion() || pPor->IsDropPortion() ||
226 pPor->IsFlyCntPortion() ) )
227 {
228 pPor->Move( rInf );
229 rInf.SetLast( pPor );
230 rInf.SetStopUnderFlow( sal_True );
231 pPor = pUnderFlow;
232 }
233 }
234
235 // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ?
236 ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" );
237
238 // OD 2004-05-26 #i29529# - correction: no delete of footnotes
239 // if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() )
240 // {
241 // SwLinePortion *pTmp = pPor->GetPortion();
242 // while( pTmp )
243 // {
244 // if( pTmp->IsFtnPortion() )
245 // ((SwFtnPortion*)pTmp)->ClearFtn();
246 // pTmp = pTmp->GetPortion();
247 // }
248 // }
249
250 /*-----------------14.12.94 09:45-------------------
251 * 9849: Schnellschuss
252 * --------------------------------------------------*/
253 if ( pPor==rInf.GetLast() )
254 {
255 // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich
256 // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber
257 // mehrere Zeilen geht und in der zweiten Zeile in einen Fly
258 // hineinlaeuft!
259 rInf.SetFly( pFly ); // wg. 28300
260 pPor->Truncate();
261 return pPor; // Reicht das?
262 }
263 /*---------------------------------------------------
264 * Ende des Schnellschusses wg. 9849
265 * --------------------------------------------------*/
266
267 // 4656: X + Width == 0 bei SoftHyph > Zeile ?!
268 if( !pPor || !(rInf.X() + pPor->Width()) )
269 {
270 delete pFly;
271 return 0;
272 }
273
274 // Vorbereitungen auf's Format()
275 // Wir muessen die Kette hinter pLast abknipsen, weil
276 // nach dem Format() ein Insert erfolgt.
277 SeekAndChg( rInf );
278
279 // line width is adjusted, so that pPor does not fit to current
280 // line anymore
281 rInf.Width( (sal_uInt16)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) );
282 rInf.SetLen( pPor->GetLen() );
283 rInf.SetFull( sal_False );
284 if( pFly )
285 {
286 // Aus folgendem Grund muss die FlyPortion neu berechnet werden:
287 // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie
288 // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht,
289 // so hat die FlyPortion eine falsche Groesse/Fixsize.
290 rInf.SetFly( pFly );
291 CalcFlyWidth( rInf );
292 }
293 rInf.GetLast()->SetPortion(0);
294
295 // Eine Ausnahme bildet das SwLineLayout, dass sich beim
296 // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg:
297 if( rInf.GetLast() == pCurr )
298 {
299 if( pPor->InTxtGrp() && !pPor->InExpGrp() )
300 {
301 MSHORT nOldWhich = pCurr->GetWhichPor();
302 *(SwLinePortion*)pCurr = *pPor;
303 pCurr->SetPortion( pPor->GetPortion() );
304 pCurr->SetWhichPor( nOldWhich );
305 pPor->SetPortion( 0 );
306 delete pPor;
307 pPor = pCurr;
308 }
309 }
310 pPor->Truncate();
311 SwLinePortion *const pRest( rInf.GetRest() );
312 if (pRest && pRest->InFldGrp() &&
313 static_cast<SwFldPortion*>(pRest)->IsNoLength())
314 {
315 // HACK: decrement again, so we pick up the suffix in next line!
316 --m_nHintEndIndex;
317 }
318 delete pRest;
319 rInf.SetRest(0);
320 return pPor;
321 }
322
323 /*************************************************************************
324 * SwTxtFormatter::InsertPortion()
325 *************************************************************************/
326
InsertPortion(SwTxtFormatInfo & rInf,SwLinePortion * pPor) const327 void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf,
328 SwLinePortion *pPor ) const
329 {
330 // Die neue Portion wird eingefuegt,
331 // bei dem LineLayout ist allerdings alles anders...
332 if( pPor == pCurr )
333 {
334 if ( pCurr->GetPortion() )
335 {
336 pPor = pCurr->GetPortion();
337 }
338
339 // --> OD 2010-07-07 #i112181#
340 rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() );
341 // <--
342 }
343 else
344 {
345 SwLinePortion *pLast = rInf.GetLast();
346 if( pLast->GetPortion() )
347 {
348 while( pLast->GetPortion() )
349 pLast = pLast->GetPortion();
350 rInf.SetLast( pLast );
351 }
352 pLast->Insert( pPor );
353
354 rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() );
355
356 // Maxima anpassen:
357 if( pCurr->Height() < pPor->Height() )
358 pCurr->Height( pPor->Height() );
359 if( pCurr->GetAscent() < pPor->GetAscent() )
360 pCurr->SetAscent( pPor->GetAscent() );
361 }
362
363 // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate)
364 rInf.SetLast( pPor );
365 while( pPor )
366 {
367 DBG_LOOP;
368 pPor->Move( rInf );
369 rInf.SetLast( pPor );
370 pPor = pPor->GetPortion();
371 }
372 }
373
374 /*************************************************************************
375 * SwTxtFormatter::BuildPortion()
376 *************************************************************************/
377
BuildPortions(SwTxtFormatInfo & rInf)378 void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf )
379 {
380 ASSERT( rInf.GetTxt().Len() < STRING_LEN,
381 "SwTxtFormatter::BuildPortions: bad text length in info" );
382
383 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
384
385 // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet.
386 // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt
387 // wird. In CalcAscent geschieht dies automatisch.
388 rInf.SetLast( pCurr );
389 rInf.ForcedLeftMargin( 0 );
390
391 ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" );
392
393 if( !pCurr->GetAscent() && !pCurr->Height() )
394 CalcAscent( rInf, pCurr );
395
396 SeekAndChg( rInf );
397
398 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
399 ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" );
400 CalcFlyWidth( rInf );
401 SwFlyPortion *pFly = rInf.GetFly();
402 if( pFly )
403 {
404 if ( 0 < pFly->Fix() )
405 ClearFly( rInf );
406 else
407 rInf.SetFull(sal_True);
408 }
409
410 SwLinePortion *pPor = NewPortion( rInf );
411
412 // Asian grid stuff
413 GETGRID( pFrm->FindPageFrm() )
414 const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() &&
415 GRID_LINES_CHARS == pGrid->GetGridType();
416
417 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
418 const sal_uInt16 nGridWidth = bHasGrid ?
419 GETGRIDWIDTH(pGrid,pDoc) : 0; //for textgrid refactor
420
421 // used for grid mode only:
422 // the pointer is stored, because after formatting of non-asian text,
423 // the width of the kerning portion has to be adjusted
424 SwKernPortion* pGridKernPortion = 0;
425
426 sal_Bool bFull;
427 SwTwips nUnderLineStart = 0;
428 rInf.Y( Y() );
429
430 while( pPor && !rInf.IsStop() )
431 {
432 ASSERT( rInf.GetLen() < STRING_LEN &&
433 rInf.GetIdx() <= rInf.GetTxt().Len(),
434 "SwTxtFormatter::BuildPortions: bad length in info" );
435 DBG_LOOP;
436
437 // We have to check the script for fields in order to set the
438 // correct nActual value for the font.
439 if( pPor->InFldGrp() )
440 ((SwFldPortion*)pPor)->CheckScript( rInf );
441
442 if( ! bHasGrid && rInf.HasScriptSpace() &&
443 rInf.GetLast() && rInf.GetLast()->InTxtGrp() &&
444 rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() )
445 {
446 sal_uInt8 nNxtActual = rInf.GetFont()->GetActual();
447 sal_uInt8 nLstActual = nNxtActual;
448 sal_uInt16 nLstHeight = (sal_uInt16)rInf.GetFont()->GetHeight();
449 sal_Bool bAllowBefore = sal_False;
450 sal_Bool bAllowBehind = sal_False;
451 const CharClass& rCC = GetAppCharClass();
452
453 // are there any punctuation characters on both sides
454 // of the kerning portion?
455 if ( pPor->InFldGrp() )
456 {
457 XubString aAltTxt;
458 if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) &&
459 aAltTxt.Len() )
460 {
461 bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 );
462
463 const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont();
464 if ( pTmpFnt )
465 nNxtActual = pTmpFnt->GetActual();
466 }
467 }
468 else
469 bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() );
470
471 const SwLinePortion* pLast = rInf.GetLast();
472 if ( bAllowBehind && pLast )
473 {
474 if ( pLast->InFldGrp() )
475 {
476 XubString aAltTxt;
477 if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) &&
478 aAltTxt.Len() )
479 {
480 bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 );
481
482 const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont();
483 if ( pTmpFnt )
484 {
485 nLstActual = pTmpFnt->GetActual();
486 nLstHeight = (sal_uInt16)pTmpFnt->GetHeight();
487 }
488 }
489 }
490 else if ( rInf.GetIdx() )
491 {
492 bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 );
493 // Note: ScriptType returns values in [1,4]
494 if ( bAllowBefore )
495 nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1;
496 }
497
498 nLstHeight /= 5;
499 // does the kerning portion still fit into the line?
500 if( bAllowBefore && ( nLstActual != nNxtActual ) &&
501 nLstHeight && rInf.X() + nLstHeight <= rInf.Width() )
502 {
503 SwKernPortion* pKrn =
504 new SwKernPortion( *rInf.GetLast(), nLstHeight,
505 pLast->InFldGrp() && pPor->InFldGrp() );
506 rInf.GetLast()->SetPortion( NULL );
507 InsertPortion( rInf, pKrn );
508 }
509 }
510 }
511 else if ( bHasGrid && ! pGridKernPortion && ! pMulti )
512 {
513 // insert a grid kerning portion
514 if ( ! pGridKernPortion )
515 pGridKernPortion = pPor->IsKernPortion() ?
516 (SwKernPortion*)pPor :
517 new SwKernPortion( *pCurr );
518
519 // if we have a new GridKernPortion, we initially calculate
520 // its size so that its ends on the grid
521 const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
522 const SwLayoutFrm* pBody = pPageFrm->FindBodyCont();
523 SWRECTFN( pPageFrm )
524
525 const long nGridOrigin = pBody ?
526 (pBody->*fnRect->fnGetPrtLeft)() :
527 (pPageFrm->*fnRect->fnGetPrtLeft)();
528
529 SwTwips nStartX = rInf.X() + GetLeftMargin();
530 if ( bVert )
531 {
532 Point aPoint( nStartX, 0 );
533 pFrm->SwitchHorizontalToVertical( aPoint );
534 nStartX = aPoint.Y();
535 }
536
537 const SwTwips nOfst = nStartX - nGridOrigin;
538 if ( nOfst )
539 {
540 const sal_uLong i = ( nOfst > 0 ) ?
541 ( ( nOfst - 1 ) / nGridWidth + 1 ) :
542 0;
543 const SwTwips nKernWidth = i * nGridWidth - nOfst;
544 const SwTwips nRestWidth = rInf.Width() - rInf.X();
545
546 if ( nKernWidth <= nRestWidth )
547 pGridKernPortion->Width( (sal_uInt16)nKernWidth );
548 }
549
550 if ( pGridKernPortion != pPor )
551 InsertPortion( rInf, pGridKernPortion );
552 }
553
554 // the multi-portion has it's own format function
555 if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) )
556 bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) );
557 else
558 bFull = pPor->Format( rInf );
559
560 if( rInf.IsRuby() && !rInf.GetRest() )
561 bFull = sal_True;
562
563 // if we are underlined, we store the beginning of this underlined
564 // segment for repaint optimization
565 if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart )
566 nUnderLineStart = GetLeftMargin() + rInf.X();
567
568 if ( pPor->IsFlyPortion() )
569 pCurr->SetFly( sal_True );
570 // some special cases, where we have to take care for the repaint
571 // offset:
572 // 1. Underlined portions due to special underline feature
573 // 2. Right Tab
574 // 3. BidiPortions
575 // 4. other Multiportions
576 // 5. DropCaps
577 // 6. Grid Mode
578 else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) &&
579 // 1. Underlined portions
580 nUnderLineStart &&
581 // reformat is at end of an underlined portion and next portion
582 // is not underlined
583 ( ( rInf.GetReformatStart() == rInf.GetIdx() &&
584 UNDERLINE_NONE == pFnt->GetUnderline()
585 ) ||
586 // reformat is inside portion and portion is underlined
587 ( rInf.GetReformatStart() >= rInf.GetIdx() &&
588 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() &&
589 UNDERLINE_NONE != pFnt->GetUnderline() ) ) )
590 rInf.SetPaintOfst( nUnderLineStart );
591 else if ( ! rInf.GetPaintOfst() &&
592 // 2. Right Tab
593 ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) ||
594 // 3. BidiPortions
595 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) ||
596 // 4. Multi Portion and 5. Drop Caps
597 ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) &&
598 rInf.GetReformatStart() >= rInf.GetIdx() &&
599 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() )
600 // 6. Grid Mode
601 || ( bHasGrid && SW_CJK != pFnt->GetActual() )
602 )
603 )
604 // we store the beginning of the critical portion as our
605 // paint offset
606 rInf.SetPaintOfst( GetLeftMargin() + rInf.X() );
607
608 // under one of these conditions we are allowed to delete the
609 // start of the underline portion
610 if ( IsUnderlineBreak( *pPor, *pFnt ) )
611 nUnderLineStart = 0;
612
613 if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() &&
614 ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) )
615 SetFlyInCntBase();
616 // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim
617 // naechsten Softhyphen wieder umgebrochen!
618 if ( !bFull )
619 {
620 rInf.ClrUnderFlow();
621 if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() &&
622 pPor->GetLen() && !pPor->InFldGrp() )
623 {
624 // The distance between two different scripts is set
625 // to 20% of the fontheight.
626 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
627 if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) &&
628 nTmp != rInf.GetTxt().Len() )
629 {
630 sal_uInt16 nDist = (sal_uInt16)(rInf.GetFont()->GetHeight()/5);
631
632 if( nDist )
633 {
634 // we do not want a kerning portion if any end
635 // would be a punctuation character
636 const CharClass& rCC = GetAppCharClass();
637 if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) &&
638 rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) )
639 {
640 // does the kerning portion still fit into the line?
641 if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() )
642 new SwKernPortion( *pPor, nDist );
643 else
644 bFull = sal_True;
645 }
646 }
647 }
648 }
649 }
650
651 if ( bHasGrid && pPor != pGridKernPortion && ! pMulti )
652 {
653 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
654 const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
655
656 const sal_uInt8 nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() );
657 const sal_uInt8 nNextScript = nTmp >= rInf.GetTxt().Len() ?
658 SW_CJK :
659 SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo );
660
661 // snap non-asian text to grid if next portion is ASIAN or
662 // there are no more portions in this line
663 // be careful when handling an underflow event: the gridkernportion
664 // could have been deleted
665 if ( nRestWidth > 0 && SW_CJK != nCurrScript &&
666 ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) )
667 {
668 ASSERT( pGridKernPortion, "No GridKernPortion available" )
669
670 // calculate size
671 SwLinePortion* pTmpPor = pGridKernPortion->GetPortion();
672 sal_uInt16 nSumWidth = pPor->Width();
673 while ( pTmpPor )
674 {
675 nSumWidth = nSumWidth + pTmpPor->Width();
676 pTmpPor = pTmpPor->GetPortion();
677 }
678
679 const sal_uInt16 i = nSumWidth ?
680 ( nSumWidth - 1 ) / nGridWidth + 1 :
681 0;
682 const SwTwips nTmpWidth = i * nGridWidth;
683 const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth),
684 nRestWidth );
685 const sal_uInt16 nKernWidth_1 = (sal_uInt16)(nKernWidth / 2);
686
687 ASSERT( nKernWidth <= nRestWidth,
688 "Not enough space left for adjusting non-asian text in grid mode" )
689
690 pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
691 rInf.X( rInf.X() + nKernWidth_1 );
692
693 if ( ! bFull )
694 new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1),
695 sal_False, sal_True );
696
697 pGridKernPortion = 0;
698 }
699 else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() ||
700 pPor->IsFlyCntPortion() || pPor->InNumberGrp() ||
701 pPor->InFldGrp() || nCurrScript != nNextScript )
702 // next portion should snap to grid
703 pGridKernPortion = 0;
704 }
705
706 rInf.SetFull( bFull );
707
708 // Restportions von mehrzeiligen Feldern haben bisher noch
709 // nicht den richtigen Ascent.
710 if ( !pPor->GetLen() && !pPor->IsFlyPortion()
711 && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp()
712 && !pPor->IsMultiPortion() )
713 CalcAscent( rInf, pPor );
714
715 InsertPortion( rInf, pPor );
716 pPor = NewPortion( rInf );
717 }
718
719 if( !rInf.IsStop() )
720 {
721 // der letzte rechte, zentrierte, dezimale Tab
722 SwTabPortion *pLastTab = rInf.GetLastTab();
723 if( pLastTab )
724 pLastTab->FormatEOL( rInf );
725 else if( rInf.GetLast() && rInf.LastKernPortion() )
726 rInf.GetLast()->FormatEOL( rInf );
727 }
728 if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp()
729 && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() )
730 rInf.SetNumDone( sal_False );
731
732 // 3260, 3860: Fly auf jeden Fall loeschen!
733 ClearFly( rInf );
734 }
735
736 /*************************************************************************
737 * SwTxtFormatter::CalcAdjustLine()
738 *************************************************************************/
739
CalcAdjustLine(SwLineLayout * pCurrent)740 void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
741 {
742 if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti)
743 {
744 pCurrent->SetFormatAdj(sal_True);
745 if( IsFlyInCntBase() )
746 {
747 CalcAdjLine( pCurrent );
748 // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint
749 // auf jeden Fall umsetzen, deshalb bAllWays = sal_True
750 UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True );
751 }
752 }
753 }
754
755 /*************************************************************************
756 * SwTxtFormatter::CalcAscent()
757 *************************************************************************/
758
CalcAscent(SwTxtFormatInfo & rInf,SwLinePortion * pPor)759 void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor )
760 {
761 if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() )
762 {
763 // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten,
764 // dann ist ihre Groesse unabhaengig von harten Attributierungen.
765 SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt;
766 SwFontSave aSave( rInf, pFldFnt );
767 ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
768 ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) );
769 }
770 // --> OD 2008-06-05 #i89179#
771 // tab portion representing the list tab of a list label gets the
772 // same height and ascent as the corresponding number portion
773 else if ( pPor->InTabGrp() && pPor->GetLen() == 0 &&
774 rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
775 static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() )
776 {
777 const SwLinePortion* pLast = rInf.GetLast();
778 pPor->Height( pLast->Height() );
779 pPor->SetAscent( pLast->GetAscent() );
780 }
781 // <--
782 else
783 {
784 const SwLinePortion *pLast = rInf.GetLast();
785 sal_Bool bChg;
786
787 // Fallunterscheidung: in leeren Zeilen werden die Attribute
788 // per SeekStart angeschaltet.
789 const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
790 if ( pPor->IsQuoVadisPortion() )
791 bChg = SeekStartAndChg( rInf, sal_True );
792 else
793 {
794 if( bFirstPor )
795 {
796 if( rInf.GetTxt().Len() )
797 {
798 if ( pPor->GetLen() || !rInf.GetIdx()
799 || ( pCurr != pLast && !pLast->IsFlyPortion() )
800 || !pCurr->IsRest() ) // statt !rInf.GetRest()
801 bChg = SeekAndChg( rInf );
802 else
803 bChg = SeekAndChgBefore( rInf );
804 }
805 else if ( pMulti )
806 // do not open attributes starting at 0 in empty multi
807 // portions (rotated numbering followed by a footnote
808 // can cause trouble, because the footnote attribute
809 // starts at 0, but if we open it, the attribute handler
810 // cannot handle it.
811 bChg = sal_False;
812 else
813 bChg = SeekStartAndChg( rInf );
814 }
815 else
816 bChg = SeekAndChg( rInf );
817 }
818 if( bChg || bFirstPor || !pPor->GetAscent()
819 || !rInf.GetLast()->InTxtGrp() )
820 {
821 pPor->SetAscent( rInf.GetAscent() );
822 pPor->Height( rInf.GetTxtHeight() );
823 }
824 else
825 {
826 pPor->Height( pLast->Height() );
827 pPor->SetAscent( pLast->GetAscent() );
828 }
829 }
830 }
831
832 /*************************************************************************
833 * class SwMetaPortion
834 *************************************************************************/
835
836 class SwMetaPortion : public SwTxtPortion
837 {
838 public:
SwMetaPortion()839 inline SwMetaPortion() { SetWhichPor( POR_META ); }
840 virtual void Paint( const SwTxtPaintInfo &rInf ) const;
841 // OUTPUT_OPERATOR
842 };
843
844 //CLASSIO( SwMetaPortion )
845
846 /*************************************************************************
847 * virtual SwMetaPortion::Paint()
848 *************************************************************************/
849
Paint(const SwTxtPaintInfo & rInf) const850 void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const
851 {
852 if ( Width() )
853 {
854 rInf.DrawViewOpt( *this, POR_META );
855 SwTxtPortion::Paint( rInf );
856 }
857 }
858
859
860 /*************************************************************************
861 * SwTxtFormatter::WhichTxtPor()
862 *************************************************************************/
863
WhichTxtPor(SwTxtFormatInfo & rInf) const864 SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const
865 {
866 SwTxtPortion *pPor = 0;
867 if( GetFnt()->IsTox() )
868 {
869 pPor = new SwToxPortion;
870 }
871 else if ( GetFnt()->IsInputField() )
872 {
873 pPor = new SwTxtInputFldPortion();
874 }
875 else
876 {
877 if( GetFnt()->IsRef() )
878 pPor = new SwRefPortion;
879 else if (GetFnt()->IsMeta())
880 {
881 pPor = new SwMetaPortion;
882 }
883 else
884 {
885 // Erst zum Schluss !
886 // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben,
887 // z.B. bei nicht darstellbaren Zeichen.
888 if( rInf.GetLen() > 0 )
889 {
890 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART )
891 pPor = new SwFieldMarkPortion();
892 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND )
893 pPor = new SwFieldMarkPortion();
894 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT )
895 pPor = new SwFieldFormPortion();
896 }
897 if( !pPor )
898 {
899 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() )
900 pPor = pCurr;
901 else
902 {
903 pPor = new SwTxtPortion;
904 if ( GetFnt()->IsURL() )
905 {
906 pPor->SetWhichPor( POR_URL );
907 }
908 }
909 }
910 }
911 }
912 return pPor;
913 }
914
915 /*************************************************************************
916 * SwTxtFormatter::NewTxtPortion()
917 *************************************************************************/
918 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert:
919 // 1) Tabs
920 // 2) Linebreaks
921 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
922 // 4) naechster Attributwechsel
923
NewTxtPortion(SwTxtFormatInfo & rInf)924 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf )
925 {
926 // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr
927 // Wenn pCurr nicht von SwTxtPortion abgeleitet ist,
928 // muessen wir duplizieren ...
929 Seek( rInf.GetIdx() );
930 SwTxtPortion *pPor = WhichTxtPor( rInf );
931
932 // until next attribute change:
933 const xub_StrLen nNextAttr = GetNextAttr();
934 xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() );
935
936 // end of script type:
937 const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() );
938 nNextChg = Min( nNextChg, nNextScript );
939
940 // end of direction:
941 const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() );
942 nNextChg = Min( nNextChg, nNextDir );
943
944 // 7515, 7516, 3470, 6441 : Turbo-Boost
945 // Es wird unterstellt, dass die Buchstaben eines Fonts nicht
946 // groesser als doppelt so breit wie hoch sind.
947 // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen.
948 // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe
949 // ergibt sich erst im CalcAscent!
950 // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times
951 // New Roman besitzt einen Ascent von 182, eine Hoehe von 200
952 // und eine Breite von 53! Daraus folgt, dass eine Zeile mit
953 // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von
954 // Faktor 2 auf 8 (wg. negativen Kernings).
955
956 pPor->SetLen(1);
957 CalcAscent( rInf, pPor );
958
959 const SwFont* pTmpFnt = rInf.GetFont();
960 KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ),
961 KSHORT( pPor->GetAscent() ) ) / 8;
962 if ( !nExpect )
963 nExpect = 1;
964 nExpect = (sal_uInt16)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect));
965 if( nExpect > rInf.GetIdx() && nNextChg > nExpect )
966 nNextChg = Min( nExpect, rInf.GetTxt().Len() );
967
968 // we keep an invariant during method calls:
969 // there are no portion ending characters like hard spaces
970 // or tabs in [ nLeftScanIdx, nRightScanIdx ]
971 if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx )
972 {
973 if ( nNextChg > nRightScanIdx )
974 nNextChg = nRightScanIdx =
975 rInf.ScanPortionEnd( nRightScanIdx, nNextChg );
976 }
977 else
978 {
979 nLeftScanIdx = rInf.GetIdx();
980 nNextChg = nRightScanIdx =
981 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
982 }
983
984 pPor->SetLen( nNextChg - rInf.GetIdx() );
985 rInf.SetLen( pPor->GetLen() );
986 return pPor;
987 }
988
989
990 /*************************************************************************
991 * SwTxtFormatter::WhichFirstPortion()
992 *************************************************************************/
993
WhichFirstPortion(SwTxtFormatInfo & rInf)994 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf)
995 {
996 SwLinePortion *pPor = 0;
997
998 if( rInf.GetRest() )
999 {
1000 // 5010: Tabs und Felder
1001 if( '\0' != rInf.GetHookChar() )
1002 return 0;
1003
1004 pPor = rInf.GetRest();
1005 if( pPor->IsErgoSumPortion() )
1006 rInf.SetErgoDone(sal_True);
1007 else
1008 if( pPor->IsFtnNumPortion() )
1009 rInf.SetFtnDone(sal_True);
1010 else
1011 if( pPor->InNumberGrp() )
1012 rInf.SetNumDone(sal_True);
1013 if( pPor )
1014 {
1015 rInf.SetRest(0);
1016 pCurr->SetRest( sal_True );
1017 return pPor;
1018 }
1019 }
1020
1021 // ???? und ????: im Follow duerfen wir schon stehen,
1022 // entscheidend ist, ob pFrm->GetOfst() == 0 ist!
1023 if( rInf.GetIdx() )
1024 {
1025 // Nun koennen auch FtnPortions und ErgoSumPortions
1026 // verlaengert werden.
1027
1028 // 1) Die ErgoSumTexte
1029 if( !rInf.IsErgoDone() )
1030 {
1031 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1032 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1033 rInf.SetErgoDone( sal_True );
1034 }
1035
1036 // 2) Arrow portions
1037 if( !pPor && !rInf.IsArrowDone() )
1038 {
1039 if( pFrm->GetOfst() && !pFrm->IsFollow() &&
1040 rInf.GetIdx() == pFrm->GetOfst() )
1041 pPor = new SwArrowPortion( *pCurr );
1042 rInf.SetArrowDone( sal_True );
1043 }
1044
1045 // 3) Kerning portions at beginning of line in grid mode
1046 if ( ! pPor && ! pCurr->GetPortion() )
1047 {
1048 GETGRID( GetTxtFrm()->FindPageFrm() )
1049 if ( pGrid )
1050 pPor = new SwKernPortion( *pCurr );
1051 }
1052
1053 // 4) Die Zeilenreste (mehrzeilige Felder)
1054 if( !pPor )
1055 {
1056 pPor = rInf.GetRest();
1057 // 6922: Nur bei pPor natuerlich.
1058 if( pPor )
1059 {
1060 pCurr->SetRest( sal_True );
1061 rInf.SetRest(0);
1062 }
1063 }
1064 }
1065 else
1066 {
1067 // 5) Die Fussnotenzahlen
1068 if( !rInf.IsFtnDone() )
1069 {
1070 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1071 "Rotated number portion trouble" )
1072
1073 sal_Bool bFtnNum = pFrm->IsFtnNumFrm();
1074 rInf.GetParaPortion()->SetFtnNum( bFtnNum );
1075 if( bFtnNum )
1076 pPor = (SwLinePortion*)NewFtnNumPortion( rInf );
1077 rInf.SetFtnDone( sal_True );
1078 }
1079
1080 // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster,
1081 // entscheidend ist, ob der SwFtnFrm ein Follow ist.
1082 if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
1083 {
1084 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1085 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1086 rInf.SetErgoDone( sal_True );
1087 }
1088
1089 // 7) Die Numerierungen
1090 if( !rInf.IsNumDone() && !pPor )
1091 {
1092 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1093 "Rotated number portion trouble" )
1094
1095 // Wenn wir im Follow stehen, dann natuerlich nicht.
1096 if( GetTxtFrm()->GetTxtNode()->GetNumRule() )
1097 pPor = (SwLinePortion*)NewNumberPortion( rInf );
1098 rInf.SetNumDone( sal_True );
1099 }
1100 // 8) Die DropCaps
1101 if( !pPor && GetDropFmt() && ! rInf.IsMulti() )
1102 pPor = (SwLinePortion*)NewDropPortion( rInf );
1103
1104 // 9) Kerning portions at beginning of line in grid mode
1105 if ( !pPor && !pCurr->GetPortion() )
1106 {
1107 GETGRID( GetTxtFrm()->FindPageFrm() )
1108 if ( pGrid )
1109 pPor = new SwKernPortion( *pCurr );
1110 }
1111 }
1112
1113 // 10) Decimal tab portion at the beginning of each line in table cells
1114 if ( !pPor && !pCurr->GetPortion() &&
1115 GetTxtFrm()->IsInTab() &&
1116 GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) )
1117 {
1118 pPor = NewTabPortion( rInf, true );
1119 }
1120
1121 // 11) suffix of meta-field
1122 if (!pPor)
1123 {
1124 pPor = TryNewNoLengthPortion(rInf);
1125 }
1126
1127 return pPor;
1128 }
1129
lcl_OldFieldRest(const SwLineLayout * pCurr)1130 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr )
1131 {
1132 if( !pCurr->GetNext() )
1133 return sal_False;
1134 const SwLinePortion *pPor = pCurr->GetNext()->GetPortion();
1135 sal_Bool bRet = sal_False;
1136 while( pPor && !bRet )
1137 {
1138 bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) ||
1139 (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld());
1140 if( !pPor->GetLen() )
1141 break;
1142 pPor = pPor->GetPortion();
1143 }
1144 return bRet;
1145 }
1146
1147 /*************************************************************************
1148 * SwTxtFormatter::NewPortion()
1149 *************************************************************************/
1150
1151 /* NewPortion stellt rInf.nLen ein.
1152 * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr,
1153 * attrwechsel.
1154 * Drei Faelle koennen eintreten:
1155 * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert
1156 * -> return 0;
1157 * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert
1158 * -> Breite neu einstellen und return new FlyPortion
1159 * 3) Es muss eine neue Portion gebaut werden.
1160 * -> CalcFlyWidth emuliert ggf. die Breite und return Portion
1161 */
1162
NewPortion(SwTxtFormatInfo & rInf)1163 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf )
1164 {
1165 // Underflow hat Vorrang
1166 rInf.SetStopUnderFlow( sal_False );
1167 if( rInf.GetUnderFlow() )
1168 {
1169 ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" );
1170 return UnderFlow( rInf );
1171 }
1172
1173 // Wenn die Zeile voll ist, koennten noch Flys oder
1174 // UnderFlow-LinePortions warten ...
1175 if( rInf.IsFull() )
1176 {
1177 // ????: LineBreaks und Flys (bug05.sdw)
1178 // 8450: IsDummy()
1179 if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) )
1180 return 0;
1181
1182 // Wenn der Text an den Fly gestossen ist, oder wenn
1183 // der Fly als erstes drankommt, weil er ueber dem linken
1184 // Rand haengt, wird GetFly() returnt.
1185 // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's
1186 // naturgemaesz eine 0.
1187 if( rInf.GetFly() )
1188 {
1189 if( rInf.GetLast()->IsBreakPortion() )
1190 {
1191 delete rInf.GetFly();
1192 rInf.SetFly( 0 );
1193 }
1194
1195 return rInf.GetFly();
1196 }
1197 // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den
1198 // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest
1199 // bekanntgeben, damit SwTxtFrm::Format nicht abbricht
1200 // (die Textmasse wurde ja durchformatiert).
1201 if( rInf.GetRest() )
1202 rInf.SetNewLine( sal_True );
1203 else
1204 {
1205 // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt,
1206 // jetzt aber kein Rest mehr anliegt,
1207 // muss sie auf jeden Fall neu formatiert werden!
1208 if( lcl_OldFieldRest( GetCurr() ) )
1209 rInf.SetNewLine( sal_True );
1210 else
1211 {
1212 SwLinePortion *pFirst = WhichFirstPortion( rInf );
1213 if( pFirst )
1214 {
1215 rInf.SetNewLine( sal_True );
1216 if( pFirst->InNumberGrp() )
1217 rInf.SetNumDone( sal_False) ;
1218 delete pFirst;
1219 }
1220 }
1221 }
1222
1223 return 0;
1224 }
1225
1226 SwLinePortion *pPor = WhichFirstPortion( rInf );
1227
1228 // Check for Hidden Portion:
1229 if ( !pPor )
1230 {
1231 xub_StrLen nEnd = rInf.GetIdx();
1232 if ( lcl_BuildHiddenPortion( rInf, nEnd ) )
1233 pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
1234 }
1235
1236 if( !pPor )
1237 {
1238 if( ( !pMulti || pMulti->IsBidi() ) &&
1239 // --> FME 2005-02-14 #i42734#
1240 // No multi portion if there is a hook character waiting:
1241 ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
1242 // <--
1243 {
1244 // We open a multiportion part, if we enter a multi-line part
1245 // of the paragraph.
1246 xub_StrLen nEnd = rInf.GetIdx();
1247 SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti );
1248 if( pCreate )
1249 {
1250 SwMultiPortion* pTmp = NULL;
1251
1252 if ( SW_MC_BIDI == pCreate->nId )
1253 pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
1254 else if ( SW_MC_RUBY == pCreate->nId )
1255 {
1256 Seek( rInf.GetIdx() );
1257 sal_Bool bRubyTop;
1258 sal_Bool* pRubyPos = 0;
1259
1260 if ( rInf.SnapToGrid() )
1261 {
1262 GETGRID( GetTxtFrm()->FindPageFrm() )
1263 if ( pGrid )
1264 {
1265 bRubyTop = ! pGrid->GetRubyTextBelow();
1266 pRubyPos = &bRubyTop;
1267 }
1268 }
1269
1270 pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
1271 *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(),
1272 nEnd, 0, pRubyPos );
1273 }
1274 else if( SW_MC_ROTATE == pCreate->nId )
1275 pTmp = new SwRotatedPortion( *pCreate, nEnd,
1276 GetTxtFrm()->IsRightToLeft() );
1277 else
1278 pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
1279
1280 delete pCreate;
1281 CalcFlyWidth( rInf );
1282
1283 return pTmp;
1284 }
1285 }
1286 // 5010: Tabs und Felder
1287 xub_Unicode cChar = rInf.GetHookChar();
1288
1289 if( cChar )
1290 {
1291 /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das
1292 * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile
1293 * gewandert ist ( so geschehen hinter Rahmen ).
1294 * Wenn allerdings eine FldPortion im Rest wartet, muessen wir
1295 * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei
1296 * DezimalTabs und Feldern (22615)
1297 */
1298 if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() )
1299 cChar = rInf.GetChar( rInf.GetIdx() );
1300 rInf.ClearHookChar();
1301 }
1302 else
1303 {
1304 if( rInf.GetIdx() >= rInf.GetTxt().Len() )
1305 {
1306 rInf.SetFull(sal_True);
1307 CalcFlyWidth( rInf );
1308 return pPor;
1309 }
1310 cChar = rInf.GetChar( rInf.GetIdx() );
1311 }
1312
1313 switch( cChar )
1314 {
1315 case CH_TAB:
1316 pPor = NewTabPortion( rInf, false ); break;
1317
1318 case CH_BREAK:
1319 pPor = new SwBreakPortion( *rInf.GetLast() ); break;
1320
1321 case CHAR_SOFTHYPHEN: // soft hyphen
1322 pPor = new SwSoftHyphPortion; break;
1323
1324 case CHAR_HARDBLANK: // no-break space
1325 pPor = new SwBlankPortion( ' ' ); break;
1326
1327 case CHAR_HARDHYPHEN: // non-breaking hyphen
1328 pPor = new SwBlankPortion( '-' ); break;
1329
1330 case CHAR_ZWSP: // zero width space
1331 case CHAR_ZWNBSP : // word joiner
1332 pPor = new SwControlCharPortion( cChar ); break;
1333
1334 case CH_TXTATR_BREAKWORD:
1335 case CH_TXTATR_INWORD:
1336 if( rInf.HasHint( rInf.GetIdx() ) )
1337 {
1338 pPor = NewExtraPortion( rInf );
1339 break;
1340 }
1341 // No break
1342 default :
1343 {
1344 SwTabPortion* pLastTabPortion = rInf.GetLastTab();
1345 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
1346 {
1347 // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full:
1348 // We have a decimal tab portion in the line and the next character has to be
1349 // aligned at the tab stop position. We store the width from the beginning of
1350 // the tab stop portion up to the portion containint the decimal separator:
1351 if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
1352 POR_TABDECIMAL == pLastTabPortion->GetWhichPor() )
1353 {
1354 ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" )
1355 const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(rInf.X() - pLastTabPortion->Fix() );
1356 static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
1357 rInf.SetTabDecimal( 0 );
1358 }
1359 // <--
1360 else
1361 rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
1362 }
1363
1364 if( rInf.GetRest() )
1365 {
1366 if( rInf.IsFull() )
1367 {
1368 rInf.SetNewLine(sal_True);
1369 return 0;
1370 }
1371 pPor = rInf.GetRest();
1372 rInf.SetRest(0);
1373 }
1374 else
1375 {
1376 if( rInf.IsFull() )
1377 return 0;
1378 pPor = NewTxtPortion( rInf );
1379 }
1380 break;
1381 }
1382 }
1383
1384 // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht,
1385 // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet
1386 // hat, weil z.B. ein Tab enthalten ist.
1387 if( pPor && rInf.GetRest() )
1388 pPor->SetLen( 0 );
1389
1390 // robust:
1391 if( !pPor || rInf.IsStop() )
1392 {
1393 delete pPor;
1394 return 0;
1395 }
1396 }
1397
1398 // Special portions containing numbers (footnote anchor, footnote number,
1399 // numbering) can be contained in a rotated portion, if the user
1400 // choose a rotated character attribute.
1401 if ( pPor && ! pMulti )
1402 {
1403 if ( pPor->IsFtnPortion() )
1404 {
1405 const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn();
1406
1407 if ( pTxtFtn )
1408 {
1409 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1410 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1411 const SwEndNoteInfo* pInfo;
1412 if( rFtn.IsEndNote() )
1413 pInfo = &pDoc->GetEndNoteInfo();
1414 else
1415 pInfo = &pDoc->GetFtnInfo();
1416 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1417
1418 const SfxPoolItem* pItem;
1419 sal_uInt16 nDir = 0;
1420 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1421 sal_True, &pItem ))
1422 nDir = ((SvxCharRotateItem*)pItem)->GetValue();
1423
1424 if ( 0 != nDir )
1425 {
1426 delete pPor;
1427 pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ?
1428 DIR_BOTTOM2TOP :
1429 DIR_TOP2BOTTOM );
1430 }
1431 }
1432 }
1433 else if ( pPor->InNumberGrp() )
1434 {
1435 const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont();
1436
1437 if ( pNumFnt )
1438 {
1439 sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
1440 if ( 0 != nDir )
1441 {
1442 delete pPor;
1443 pPor = new SwRotatedPortion( 0, 900 == nDir ?
1444 DIR_BOTTOM2TOP :
1445 DIR_TOP2BOTTOM );
1446
1447 rInf.SetNumDone( sal_False );
1448 rInf.SetFtnDone( sal_False );
1449 }
1450 }
1451 }
1452 }
1453
1454 // Der Font wird im Outputdevice eingestellt,
1455 // der Ascent und die Hoehe werden berechnet.
1456 if( !pPor->GetAscent() && !pPor->Height() )
1457 CalcAscent( rInf, pPor );
1458 rInf.SetLen( pPor->GetLen() );
1459
1460 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
1461 CalcFlyWidth( rInf );
1462
1463 // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige
1464 // Werte bereithalten muss:
1465 if( !pCurr->Height() )
1466 {
1467 ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" );
1468 pCurr->Height( pPor->Height() );
1469 pCurr->SetAscent( pPor->GetAscent() );
1470 }
1471
1472 ASSERT( !pPor || pPor->Height(),
1473 "SwTxtFormatter::NewPortion: something went wrong");
1474 if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
1475 {
1476 delete pPor;
1477 pPor = rInf.GetFly();
1478 }
1479 return pPor;
1480 }
1481
1482 /*************************************************************************
1483 * SwTxtFormatter::FormatLine()
1484 *************************************************************************/
1485
FormatLine(const xub_StrLen nStartPos)1486 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos )
1487 {
1488 ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
1489 "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" );
1490
1491 // For the formatting routines, we set pOut to the reference device.
1492 SwHookOut aHook( GetInfo() );
1493 if( GetInfo().GetLen() < GetInfo().GetTxt().Len() )
1494 GetInfo().SetLen( GetInfo().GetTxt().Len() );
1495
1496 sal_Bool bBuild = sal_True;
1497 SetFlyInCntBase( sal_False );
1498 GetInfo().SetLineHeight( 0 );
1499 GetInfo().SetLineNettoHeight( 0 );
1500
1501 // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden
1502 // und auch bei geaendertem Ascent (Absenken der Grundlinie).
1503 const KSHORT nOldHeight = pCurr->Height();
1504 const KSHORT nOldAscent = pCurr->GetAscent();
1505
1506 pCurr->SetEndHyph( sal_False );
1507 pCurr->SetMidHyph( sal_False );
1508
1509 // fly positioning can make it necessary format a line several times
1510 // for this, we have to keep a copy of our rest portion
1511 SwLinePortion* pFld = GetInfo().GetRest();
1512 SwFldPortion* pSaveFld = 0;
1513
1514 if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() )
1515 pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) );
1516
1517 // for an optimal repaint rectangle, we want to compare fly portions
1518 // before and after the BuildPortions call
1519 const sal_Bool bOptimizeRepaint = AllowRepaintOpt();
1520 const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen();
1521 SvLongs* pFlyStart = 0;
1522
1523 // these are the conditions for a fly position comparison
1524 if ( bOptimizeRepaint && pCurr->IsFly() )
1525 {
1526 pFlyStart = new SvLongs;
1527 SwLinePortion* pPor = pCurr->GetFirstPortion();
1528 long nPOfst = 0;
1529 sal_uInt16 nCnt = 0;
1530
1531 while ( pPor )
1532 {
1533 if ( pPor->IsFlyPortion() )
1534 // insert start value of fly portion
1535 pFlyStart->Insert( nPOfst, nCnt++ );
1536
1537 nPOfst += pPor->Width();
1538 pPor = pPor->GetPortion();
1539 }
1540 }
1541
1542 // Hier folgt bald die Unterlaufpruefung.
1543 while( bBuild )
1544 {
1545 GetInfo().SetFtnInside( sal_False );
1546 GetInfo().SetOtherThanFtnInside( sal_False );
1547
1548 // These values must not be reset by FormatReset();
1549 sal_Bool bOldNumDone = GetInfo().IsNumDone();
1550 sal_Bool bOldArrowDone = GetInfo().IsArrowDone();
1551 sal_Bool bOldErgoDone = GetInfo().IsErgoDone();
1552
1553 // besides other things, this sets the repaint offset to 0
1554 FormatReset( GetInfo() );
1555
1556 GetInfo().SetNumDone( bOldNumDone );
1557 GetInfo().SetArrowDone( bOldArrowDone );
1558 GetInfo().SetErgoDone( bOldErgoDone );
1559
1560 // build new portions for this line
1561 BuildPortions( GetInfo() );
1562
1563 if( GetInfo().IsStop() )
1564 {
1565 pCurr->SetLen( 0 );
1566 pCurr->Height( GetFrmRstHeight() + 1 );
1567 pCurr->SetRealHeight( GetFrmRstHeight() + 1 );
1568 pCurr->Width(0);
1569 pCurr->Truncate();
1570 return nStartPos;
1571 }
1572 else if( GetInfo().IsDropInit() )
1573 {
1574 DropInit();
1575 GetInfo().SetDropInit( sal_False );
1576 }
1577
1578 pCurr->CalcLine( *this, GetInfo() );
1579 CalcRealHeight( GetInfo().IsNewLine() );
1580
1581 //Bug 120864:For Special case that at the first caculation couldn't get correct height. And need to recaculate for the right height.
1582 SwLinePortion* pPorTmp = pCurr->GetPortion();
1583 if ( IsFlyInCntBase() && (!IsQuick() || (pPorTmp && pPorTmp->IsFlyCntPortion() && !pPorTmp->GetPortion() &&
1584 pCurr->Height() > pPorTmp->Height())))
1585 //Bug 120864(End)
1586 {
1587 KSHORT nTmpAscent, nTmpHeight;
1588 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
1589 AlignFlyInCntBase( Y() + long( nTmpAscent ) );
1590 pCurr->CalcLine( *this, GetInfo() );
1591 CalcRealHeight();
1592 }
1593
1594 // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird
1595 if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
1596 {
1597 pCurr->SetRealHeight( GetInfo().GetLineHeight() );
1598 bBuild = sal_False;
1599 }
1600 else
1601 {
1602 bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow(GetInfo()) )
1603 || GetInfo().CheckFtnPortion(pCurr);
1604 if( bBuild )
1605 {
1606 GetInfo().SetNumDone( bOldNumDone );
1607 GetInfo().ResetMaxWidthDiff();
1608
1609 // delete old rest
1610 if ( GetInfo().GetRest() )
1611 {
1612 delete GetInfo().GetRest();
1613 GetInfo().SetRest( 0 );
1614 }
1615
1616 // set original rest portion
1617 if ( pSaveFld )
1618 GetInfo().SetRest( new SwFldPortion( *pSaveFld ) );
1619
1620 pCurr->SetLen( 0 );
1621 pCurr->Width(0);
1622 pCurr->Truncate();
1623 }
1624 }
1625 }
1626
1627 // calculate optimal repaint rectangle
1628 if ( bOptimizeRepaint )
1629 {
1630 GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) );
1631 if ( pFlyStart )
1632 delete pFlyStart;
1633 }
1634 else
1635 // Special case: We do not allow an optimitation of the repaint
1636 // area, but during formatting the repaint offset is set to indicate
1637 // a maximum value for the offset. This value has to be reset:
1638 GetInfo().SetPaintOfst( 0 );
1639
1640 // This corrects the start of the reformat range if something has
1641 // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
1642 // will give us a wrong result if we have to reformat another line
1643 GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() );
1644
1645 // delete master copy of rest portion
1646 if ( pSaveFld )
1647 delete pSaveFld;
1648
1649 xub_StrLen nNewStart = nStartPos + pCurr->GetLen();
1650
1651 // adjust text if kana compression is enabled
1652 if ( GetInfo().CompressLine() )
1653 {
1654 SwTwips nRepaintOfst = CalcKanaAdj( pCurr );
1655
1656 // adjust repaint offset
1657 if ( nRepaintOfst < GetInfo().GetPaintOfst() )
1658 GetInfo().SetPaintOfst( nRepaintOfst );
1659 }
1660
1661 CalcAdjustLine( pCurr );
1662
1663 if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() )
1664 {
1665 SetFlyInCntBase();
1666 GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling
1667 // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind
1668 // auch formatiert werden.
1669 GetInfo().SetShift( sal_True );
1670 }
1671
1672 if ( IsFlyInCntBase() && !IsQuick() )
1673 UpdatePos( pCurr, GetTopLeft(), GetStart() );
1674
1675 return nNewStart;
1676 }
1677
1678 /*************************************************************************
1679 * SwTxtFormatter::RecalcRealHeight()
1680 *************************************************************************/
1681
RecalcRealHeight()1682 void SwTxtFormatter::RecalcRealHeight()
1683 {
1684 sal_Bool bMore = sal_True;
1685 while(bMore)
1686 {
1687 DBG_LOOP;
1688 CalcRealHeight();
1689 bMore = Next() != 0;
1690 }
1691 }
1692
1693 /*************************************************************************
1694 * SwTxtFormatter::CalcRealHeight()
1695 *************************************************************************/
1696
CalcRealHeight(sal_Bool bNewLine)1697 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine )
1698 {
1699 KSHORT nLineHeight = pCurr->Height();
1700 pCurr->SetClipping( sal_False );
1701
1702 GETGRID( pFrm->FindPageFrm() )
1703 if ( pGrid && GetInfo().SnapToGrid() )
1704 {
1705 const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
1706 const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
1707 const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow();
1708
1709 nLineHeight = nGridWidth + nRubyHeight;
1710 sal_uInt16 nLineDist = nLineHeight;
1711
1712 while ( pCurr->Height() > nLineHeight )
1713 nLineHeight = nLineHeight + nLineDist;
1714
1715 KSHORT nAsc = pCurr->GetAscent() +
1716 ( bRubyTop ?
1717 ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 :
1718 ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 );
1719
1720 pCurr->Height( nLineHeight );
1721 pCurr->SetAscent( nAsc );
1722 pInf->GetParaPortion()->SetFixLineHeight();
1723
1724 // we ignore any line spacing options except from ...
1725 const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing();
1726 if ( ! IsParaLine() && pSpace &&
1727 SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() )
1728 {
1729 sal_uLong nTmp = pSpace->GetPropLineSpace();
1730
1731 if( nTmp < 100 )
1732 nTmp = 100;
1733
1734 nTmp *= nLineHeight;
1735 nLineHeight = (sal_uInt16)(nTmp / 100);
1736 }
1737
1738 pCurr->SetRealHeight( nLineHeight );
1739 return;
1740 }
1741
1742 // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese
1743 // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere
1744 // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem
1745 // Shift-Return), die das Register durchaus beachten soll.
1746 if( !pCurr->IsDummy() || ( !pCurr->GetNext() &&
1747 GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) )
1748 {
1749 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
1750 if( pSpace )
1751 {
1752 switch( pSpace->GetLineSpaceRule() )
1753 {
1754 case SVX_LINE_SPACE_AUTO:
1755 break;
1756 case SVX_LINE_SPACE_MIN:
1757 {
1758 if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) )
1759 nLineHeight = pSpace->GetLineHeight();
1760 break;
1761 }
1762 case SVX_LINE_SPACE_FIX:
1763 {
1764 nLineHeight = pSpace->GetLineHeight();
1765 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80%
1766 if( nAsc < pCurr->GetAscent() ||
1767 nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() )
1768 pCurr->SetClipping( sal_True );
1769 pCurr->Height( nLineHeight );
1770 pCurr->SetAscent( nAsc );
1771 pInf->GetParaPortion()->SetFixLineHeight();
1772 }
1773 break;
1774 default: ASSERT( sal_False, ": unknown LineSpaceRule" );
1775 }
1776 if( !IsParaLine() )
1777 switch( pSpace->GetInterLineSpaceRule() )
1778 {
1779 case SVX_INTER_LINE_SPACE_OFF:
1780 break;
1781 case SVX_INTER_LINE_SPACE_PROP:
1782 {
1783 long nTmp = pSpace->GetPropLineSpace();
1784 // 50% ist das Minimum, bei 0% schalten wir auf
1785 // den Defaultwert 100% um ...
1786 if( nTmp < 50 )
1787 nTmp = nTmp ? 50 : 100;
1788
1789 nTmp *= nLineHeight;
1790 nTmp /= 100;
1791 if( !nTmp )
1792 ++nTmp;
1793 nLineHeight = (KSHORT)nTmp;
1794 break;
1795 }
1796 case SVX_INTER_LINE_SPACE_FIX:
1797 {
1798 nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
1799 break;
1800 }
1801 default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
1802 }
1803 }
1804 #if OSL_DEBUG_LEVEL > 1
1805 KSHORT nDummy = nLineHeight + 1;
1806 (void)nDummy;
1807 #endif
1808
1809 if( IsRegisterOn() )
1810 {
1811 SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height();
1812 SWRECTFN( pFrm )
1813 if ( bVert )
1814 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY );
1815 nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() );
1816 KSHORT nDiff = KSHORT( nTmpY % RegDiff() );
1817 if( nDiff )
1818 nLineHeight += RegDiff() - nDiff;
1819 }
1820 }
1821 pCurr->SetRealHeight( nLineHeight );
1822 }
1823
1824 /*************************************************************************
1825 * SwTxtFormatter::FeedInf()
1826 *************************************************************************/
1827
FeedInf(SwTxtFormatInfo & rInf) const1828 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const
1829 {
1830 // 3260, 3860: Fly auf jeden Fall loeschen!
1831 ClearFly( rInf );
1832 rInf.Init();
1833
1834 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
1835 rInf.SetRoot( pCurr );
1836 rInf.SetLineStart( nStart );
1837 rInf.SetIdx( nStart );
1838
1839 // Handle overflows:
1840 // --> FME 2004-11-25 #i34348# Changed type from sal_uInt16 to SwTwips
1841 SwTwips nTmpLeft = Left();
1842 SwTwips nTmpRight = Right();
1843 SwTwips nTmpFirst = FirstLeft();
1844 // <--
1845
1846 if ( nTmpLeft > USHRT_MAX ||
1847 nTmpRight > USHRT_MAX ||
1848 nTmpFirst > USHRT_MAX )
1849 {
1850 SWRECTFN( rInf.GetTxtFrm() )
1851 nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)();
1852 nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)();
1853 nTmpFirst = nTmpLeft;
1854 }
1855
1856 rInf.Left( nTmpLeft );
1857 rInf.Right( nTmpRight );
1858 rInf.First( nTmpFirst );
1859
1860 rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) );
1861 rInf.Width( rInf.RealWidth() );
1862 if( ((SwTxtFormatter*)this)->GetRedln() )
1863 {
1864 ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() );
1865 ((SwTxtFormatter*)this)->GetRedln()->Reset();
1866 }
1867 }
1868
1869 /*************************************************************************
1870 * SwTxtFormatter::FormatReset()
1871 *************************************************************************/
1872
FormatReset(SwTxtFormatInfo & rInf)1873 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf )
1874 {
1875 pCurr->Truncate();
1876 pCurr->Init();
1877 if( pBlink && pCurr->IsBlinking() )
1878 pBlink->Delete( pCurr );
1879
1880 // delete pSpaceAdd und pKanaComp
1881 pCurr->FinishSpaceAdd();
1882 pCurr->FinishKanaComp();
1883 pCurr->ResetFlags();
1884 FeedInf( rInf );
1885 }
1886
1887 /*************************************************************************
1888 * SwTxtFormatter::CalcOnceMore()
1889 *************************************************************************/
1890
CalcOnceMore()1891 sal_Bool SwTxtFormatter::CalcOnceMore()
1892 {
1893 if( pDropFmt )
1894 {
1895 const KSHORT nOldDrop = GetDropHeight();
1896 CalcDropHeight( pDropFmt->GetLines() );
1897 bOnceMore = nOldDrop != GetDropHeight();
1898 }
1899 else
1900 bOnceMore = sal_False;
1901 return bOnceMore;
1902 }
1903
1904 /*************************************************************************
1905 * SwTxtFormatter::CalcBottomLine()
1906 *************************************************************************/
1907
CalcBottomLine() const1908 SwTwips SwTxtFormatter::CalcBottomLine() const
1909 {
1910 SwTwips nRet = Y() + GetLineHeight();
1911 SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom();
1912 if( nMin && ++nMin > nRet )
1913 {
1914 SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height()
1915 - pFrm->Prt().Top();
1916 if( nRet + nDist < nMin )
1917 {
1918 sal_Bool bRepaint = HasTruncLines() &&
1919 GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1;
1920 nRet = nMin - nDist;
1921 if( bRepaint )
1922 {
1923 ((SwRepaint*)GetInfo().GetParaPortion()
1924 ->GetRepaint())->Bottom( nRet-1 );
1925 ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 );
1926 }
1927 }
1928 }
1929 return nRet;
1930 }
1931
1932 /*************************************************************************
1933 * SwTxtFormatter::_CalcFitToContent()
1934 *
1935 * FME/OD: This routine does a limited text formatting.
1936 *************************************************************************/
1937
_CalcFitToContent()1938 SwTwips SwTxtFormatter::_CalcFitToContent()
1939 {
1940 FormatReset( GetInfo() );
1941 BuildPortions( GetInfo() );
1942 pCurr->CalcLine( *this, GetInfo() );
1943 return pCurr->Width();
1944 }
1945
1946 /*************************************************************************
1947 * SwTxtFormatter::AllowRepaintOpt()
1948 *
1949 * determines if the calculation of a repaint offset is allowed
1950 * otherwise each line is painted from 0 (this is a copy of the beginning
1951 * of the former SwTxtFormatter::Recycle() function
1952 *************************************************************************/
AllowRepaintOpt() const1953 sal_Bool SwTxtFormatter::AllowRepaintOpt() const
1954 {
1955 // reformat position in front of current line? Only in this case
1956 // we want to set the repaint offset
1957 sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() &&
1958 pCurr->GetLen();
1959
1960 // a special case is the last line of a block adjusted paragraph:
1961 if ( bOptimizeRepaint )
1962 {
1963 switch( GetAdjust() )
1964 {
1965 case SVX_ADJUST_BLOCK:
1966 {
1967 if( IsLastBlock() || IsLastCenter() )
1968 bOptimizeRepaint = sal_False;
1969 else
1970 {
1971 // ????: Blank in der letzten Masterzeile (blocksat.sdw)
1972 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow();
1973 if ( bOptimizeRepaint )
1974 {
1975 SwLinePortion *pPos = pCurr->GetFirstPortion();
1976 while ( pPos && !pPos->IsFlyPortion() )
1977 pPos = pPos->GetPortion();
1978 bOptimizeRepaint = !pPos;
1979 }
1980 }
1981 break;
1982 }
1983 case SVX_ADJUST_CENTER:
1984 case SVX_ADJUST_RIGHT:
1985 bOptimizeRepaint = sal_False;
1986 break;
1987 default: ;
1988 }
1989 }
1990
1991 // Schon wieder ein Sonderfall: unsichtbare SoftHyphs
1992 const xub_StrLen nReformat = GetInfo().GetReformatStart();
1993 if( bOptimizeRepaint && STRING_LEN != nReformat )
1994 {
1995 const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat );
1996 bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
1997 || ! GetInfo().HasHint( nReformat );
1998 }
1999
2000 return bOptimizeRepaint;
2001 }
2002
2003 /*************************************************************************
2004 * SwTxtFormatter::CalcOptRepaint()
2005 *
2006 * calculates an optimal repaint offset for the current line
2007 *************************************************************************/
CalcOptRepaint(xub_StrLen nOldLineEnd,const SvLongs * pFlyStart)2008 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd,
2009 const SvLongs* pFlyStart )
2010 {
2011 if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() )
2012 // the reformat position is behind our new line, that means
2013 // something of our text has moved to the next line
2014 return 0;
2015
2016 xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd );
2017
2018 // in case we do not have any fly in our line, our repaint position
2019 // is the changed position - 1
2020 if ( ! pFlyStart && ! pCurr->IsFly() )
2021 {
2022 // this is the maximum repaint offset determined during formatting
2023 // for example: the beginning of the first right tab stop
2024 // if this value is 0, this means that we do not have an upper
2025 // limit for the repaint offset
2026 const long nFormatRepaint = GetInfo().GetPaintOfst();
2027
2028 if ( nReformat < GetInfo().GetLineStart() + 3 )
2029 return 0;
2030
2031 // step back two positions for smoother repaint
2032 nReformat -= 2;
2033
2034 #ifndef QUARTZ
2035 #ifndef ENABLE_GRAPHITE
2036 // --> FME 2004-09-27 #i28795#, #i34607#, #i38388#
2037 // step back six(!) more characters for complex scripts
2038 // this is required e.g., for Khmer (thank you, Javier!)
2039 const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
2040 xub_StrLen nMaxContext = 0;
2041 if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) )
2042 nMaxContext = 6;
2043 #else
2044 // Some Graphite fonts need context for scripts not marked as complex
2045 static const xub_StrLen nMaxContext = 10;
2046 #endif
2047 #else
2048 // some fonts like Quartz's Zapfino need more context
2049 // TODO: query FontInfo for maximum unicode context
2050 static const xub_StrLen nMaxContext = 8;
2051 #endif
2052 if( nMaxContext > 0 )
2053 {
2054 if ( nReformat > GetInfo().GetLineStart() + nMaxContext )
2055 nReformat = nReformat - nMaxContext;
2056 else
2057 nReformat = GetInfo().GetLineStart();
2058 }
2059 // <--
2060
2061 // Weird situation: Our line used to end with a hole portion
2062 // and we delete some characters at the end of our line. We have
2063 // to take care for repainting the blanks which are not anymore
2064 // covered by the hole portion
2065 while ( nReformat > GetInfo().GetLineStart() &&
2066 CH_BLANK == GetInfo().GetChar( nReformat ) )
2067 --nReformat;
2068
2069 ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" );
2070 SwRect aRect;
2071
2072 // Note: GetChareRect is not const. It definitely changes the
2073 // bMulti flag. We have to save and resore the old value.
2074 sal_Bool bOldMulti = GetInfo().IsMulti();
2075 GetCharRect( &aRect, nReformat );
2076 GetInfo().SetMulti( bOldMulti );
2077
2078 return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) :
2079 aRect.Left();
2080 }
2081 else
2082 {
2083 // nReformat may be wrong, if something around flys has changed:
2084 // we compare the former and the new fly positions in this line
2085 // if anything has changed, we carefully have to adjust the right
2086 // repaint position
2087 long nPOfst = 0;
2088 sal_uInt16 nCnt = 0;
2089 sal_uInt16 nX = 0;
2090 sal_uInt16 nIdx = GetInfo().GetLineStart();
2091 SwLinePortion* pPor = pCurr->GetFirstPortion();
2092
2093 while ( pPor )
2094 {
2095 if ( pPor->IsFlyPortion() )
2096 {
2097 // compare start of fly with former start of fly
2098 if ( pFlyStart &&
2099 nCnt < pFlyStart->Count() &&
2100 nX == (*pFlyStart)[ nCnt ] &&
2101 nIdx < nReformat
2102 )
2103 // found fix position, nothing has changed left from nX
2104 nPOfst = nX + pPor->Width();
2105 else
2106 break;
2107
2108 nCnt++;
2109 }
2110 nX = nX + pPor->Width();
2111 nIdx = nIdx + pPor->GetLen();
2112 pPor = pPor->GetPortion();
2113 }
2114
2115 return nPOfst + GetLeftMargin();
2116 }
2117 }
2118
lcl_BuildHiddenPortion(const SwTxtSizeInfo & rInf,xub_StrLen & rPos)2119 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos )
2120 {
2121 // Only if hidden text should not be shown:
2122 // if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
2123 const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar();
2124 const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( sal_True ) && rInf.GetOpt().IsPrinting();
2125 if (bShowInDocView || bShowForPrinting)
2126 return false;
2127
2128 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
2129 xub_StrLen nHiddenStart;
2130 xub_StrLen nHiddenEnd;
2131 rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
2132 if ( nHiddenEnd )
2133 {
2134 rPos = nHiddenEnd;
2135 return true;
2136 }
2137
2138 return false;
2139 }
2140