Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "hintids.hxx"
21 :
22 : #include <boost/scoped_ptr.hpp>
23 : #include <com/sun/star/i18n/ScriptType.hpp>
24 : #include <editeng/lspcitem.hxx>
25 : #include <txtflcnt.hxx>
26 : #include <txtftn.hxx>
27 : #include <flyfrms.hxx>
28 : #include <fmtflcnt.hxx>
29 : #include <fmtftn.hxx>
30 : #include <ftninfo.hxx>
31 : #include <charfmt.hxx>
32 : #include <editeng/charrotateitem.hxx>
33 : #include <layfrm.hxx>
34 : #include <viewsh.hxx>
35 : #include <viewopt.hxx>
36 : #include <paratr.hxx>
37 : #include <itrform2.hxx>
38 : #include <porrst.hxx>
39 : #include <portab.hxx>
40 : #include <porfly.hxx>
41 : #include <portox.hxx>
42 : #include <porref.hxx>
43 : #include <porfld.hxx>
44 : #include <porftn.hxx>
45 : #include <porhyph.hxx>
46 : #include <pordrop.hxx>
47 : #include <guess.hxx>
48 : #include <blink.hxx>
49 : #include <ftnfrm.hxx>
50 : #include <redlnitr.hxx>
51 : #include <pagefrm.hxx>
52 : #include <pagedesc.hxx>
53 : #include <tgrditem.hxx>
54 : #include <doc.hxx>
55 : #include <pormulti.hxx>
56 : #include <unotools/charclass.hxx>
57 : #include <xmloff/odffields.hxx>
58 : #include <IDocumentSettingAccess.hxx>
59 :
60 : #include <vector>
61 :
62 : #include <config_graphite.h>
63 :
64 : #if OSL_DEBUG_LEVEL > 1
65 : #include <ndtxt.hxx>
66 : #endif
67 :
68 : using namespace ::com::sun::star;
69 :
70 : namespace {
71 : //! Calculates and sets optimal repaint offset for the current line
72 : static long lcl_CalcOptRepaint( SwTxtFormatter &rThis,
73 : SwLineLayout &rCurr,
74 : const sal_Int32 nOldLineEnd,
75 : const std::vector<long> &rFlyStarts );
76 : //! Determine if we need to build hidden portions
77 : static bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, sal_Int32 &rPos );
78 :
79 : // Check whether the two font has the same border
80 : static bool lcl_HasSameBorder(const SwFont& rFirst, const SwFont& rSecond);
81 : }
82 :
83 287216 : inline void ClearFly( SwTxtFormatInfo &rInf )
84 : {
85 287216 : delete rInf.GetFly();
86 287216 : rInf.SetFly(0);
87 287216 : }
88 :
89 97770 : void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf )
90 : {
91 97770 : CtorInitTxtPainter( pNewFrm, pNewInf );
92 97770 : pInf = pNewInf;
93 97770 : pDropFmt = GetInfo().GetDropFmt();
94 97770 : pMulti = NULL;
95 :
96 97770 : bOnceMore = false;
97 97770 : bFlyInCntBase = false;
98 97770 : bChanges = false;
99 97770 : bTruncLines = false;
100 97770 : nCntEndHyph = 0;
101 97770 : nCntMidHyph = 0;
102 97770 : nLeftScanIdx = COMPLETE_STRING;
103 97770 : nRightScanIdx = 0;
104 97770 : m_nHintEndIndex = 0;
105 97770 : m_pFirstOfBorderMerge = 0;
106 :
107 97770 : if( nStart > GetInfo().GetTxt().getLength() )
108 : {
109 : OSL_ENSURE( false, "+SwTxtFormatter::CTOR: bad offset" );
110 0 : nStart = GetInfo().GetTxt().getLength();
111 : }
112 :
113 97770 : }
114 :
115 195540 : SwTxtFormatter::~SwTxtFormatter()
116 : {
117 : // Extremly unlikely, but still possible
118 : // e.g.: field splits up, widows start to matter
119 97770 : if( GetInfo().GetRest() )
120 : {
121 0 : delete GetInfo().GetRest();
122 0 : GetInfo().SetRest(0);
123 : }
124 97770 : }
125 :
126 28496 : void SwTxtFormatter::Insert( SwLineLayout *pLay )
127 : {
128 : // Insert BEHIND the current element
129 28496 : if ( pCurr )
130 : {
131 28496 : pLay->SetNext( pCurr->GetNext() );
132 28496 : pCurr->SetNext( pLay );
133 : }
134 : else
135 0 : pCurr = pLay;
136 28496 : }
137 :
138 0 : sal_uInt16 SwTxtFormatter::GetFrmRstHeight() const
139 : {
140 : // We want the rest height relative to the page.
141 : // If we're in a table, then pFrm->GetUpper() is not the page.
142 :
143 : // GetFrmRstHeight() is being called with Ftn.
144 : // Wrong: const SwFrm *pUpper = pFrm->GetUpper();
145 0 : const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm();
146 0 : const SwTwips nHeight = pPage->Frm().Top()
147 0 : + pPage->Prt().Top()
148 0 : + pPage->Prt().Height() - Y();
149 0 : if( 0 > nHeight )
150 0 : return pCurr->Height();
151 : else
152 0 : return sal_uInt16( nHeight );
153 : }
154 :
155 254 : SwLinePortion *SwTxtFormatter::Underflow( SwTxtFormatInfo &rInf )
156 : {
157 : // Save values and initialize rInf
158 254 : SwLinePortion *pUnderflow = rInf.GetUnderflow();
159 254 : if( !pUnderflow )
160 0 : return 0;
161 :
162 : // We format backwards, i.e. attribute changes can happen the next
163 : // line again.
164 : // Can be seen in 8081.sdw, if you enter text in the first line
165 :
166 254 : const sal_Int32 nSoftHyphPos = rInf.GetSoftHyphPos();
167 254 : const sal_Int32 nUnderScorePos = rInf.GetUnderScorePos();
168 :
169 : // Save flys and set to 0, or else segmentation fault
170 : // Not ClearFly(rInf) !
171 254 : SwFlyPortion *pFly = rInf.GetFly();
172 254 : rInf.SetFly( 0 );
173 :
174 254 : FeedInf( rInf );
175 254 : rInf.SetLast( pCurr );
176 : // pUnderflow does not need to be deleted, because it will drown in the following
177 : // Truncate()
178 254 : rInf.SetUnderflow(0);
179 254 : rInf.SetSoftHyphPos( nSoftHyphPos );
180 254 : rInf.SetUnderScorePos( nUnderScorePos );
181 254 : rInf.SetPaintOfst( GetLeftMargin() );
182 :
183 : // We look for the portion with the under-flow position
184 254 : SwLinePortion *pPor = pCurr->GetFirstPortion();
185 254 : if( pPor != pUnderflow )
186 : {
187 : // pPrev will be the last portion before pUnderflow,
188 : // which still has a real width.
189 : // Exception: SoftHyphPortions must not be forgotten, of course!
190 : // Although they don't have a width.
191 254 : SwLinePortion *pTmpPrev = pPor;
192 1690 : while( pPor && pPor != pUnderflow )
193 : {
194 3486 : if( !pPor->IsKernPortion() &&
195 1242 : ( pPor->Width() || pPor->IsSoftHyphPortion() ) )
196 : {
197 3172 : while( pTmpPrev != pPor )
198 : {
199 928 : pTmpPrev->Move( rInf );
200 928 : rInf.SetLast( pTmpPrev );
201 928 : pTmpPrev = pTmpPrev->GetPortion();
202 : OSL_ENSURE( pTmpPrev, "Underflow: Loosing control!" );
203 : };
204 : }
205 1182 : pPor = pPor->GetPortion();
206 : }
207 254 : pPor = pTmpPrev;
208 508 : if( pPor && // Flies + Initialen werden nicht beim Underflow mitgenommen
209 762 : ( pPor->IsFlyPortion() || pPor->IsDropPortion() ||
210 254 : pPor->IsFlyCntPortion() ) )
211 : {
212 0 : pPor->Move( rInf );
213 0 : rInf.SetLast( pPor );
214 0 : rInf.SetStopUnderflow( true );
215 0 : pPor = pUnderflow;
216 : }
217 : }
218 :
219 : // What? The under-flow portion is not in the portion chain?
220 : OSL_ENSURE( pPor, "SwTxtFormatter::Underflow: overflow but underflow" );
221 :
222 : // OD 2004-05-26 #i29529# - correction: no delete of footnotes
223 : // if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() )
224 : // {
225 : // SwLinePortion *pTmp = pPor->GetPortion();
226 : // while( pTmp )
227 : // {
228 : // if( pTmp->IsFtnPortion() )
229 : // ((SwFtnPortion*)pTmp)->ClearFtn();
230 : // pTmp = pTmp->GetPortion();
231 : // }
232 : // }
233 :
234 : // Snapshot
235 254 : if ( pPor==rInf.GetLast() )
236 : {
237 : // We end up here, if the portion triggering the under-flow
238 : // spans over the whole line. E.g. if a word spans across
239 : // multiple lines and flows into a fly in the second line.
240 0 : rInf.SetFly( pFly );
241 0 : pPor->Truncate();
242 0 : return pPor; // Is that enough?
243 : }
244 : // End the snapshot
245 :
246 : // X + Width == 0 with SoftHyph > Line?!
247 254 : if( !pPor || !(rInf.X() + pPor->Width()) )
248 : {
249 0 : delete pFly;
250 0 : return 0;
251 : }
252 :
253 : // Preparing for Format()
254 : // We need to chip off the chain behind pLast, because we Insert after the Format()
255 254 : SeekAndChg( rInf );
256 :
257 : // line width is adjusted, so that pPor does not fit to current
258 : // line anymore
259 254 : rInf.Width( (sal_uInt16)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) );
260 254 : rInf.SetLen( pPor->GetLen() );
261 254 : rInf.SetFull( false );
262 254 : if( pFly )
263 : {
264 : // We need to recalculate the FlyPortion due to the following reason:
265 : // If the base line is lowered by a big font in the middle of the line,
266 : // causing overlapping with a fly, the FlyPortion has a wrong size/fixed
267 : // size.
268 2 : rInf.SetFly( pFly );
269 2 : CalcFlyWidth( rInf );
270 : }
271 254 : rInf.GetLast()->SetPortion(0);
272 :
273 : // The SwLineLayout is an exception to this, which splits at the first
274 : // portion change.
275 : // Here inly the other way around:
276 254 : if( rInf.GetLast() == pCurr )
277 : {
278 40 : if( pPor->InTxtGrp() && !pPor->InExpGrp() )
279 : {
280 40 : const sal_uInt16 nOldWhich = pCurr->GetWhichPor();
281 40 : *(SwLinePortion*)pCurr = *pPor;
282 40 : pCurr->SetPortion( pPor->GetPortion() );
283 40 : pCurr->SetWhichPor( nOldWhich );
284 40 : pPor->SetPortion( 0 );
285 40 : delete pPor;
286 40 : pPor = pCurr;
287 : }
288 : }
289 254 : pPor->Truncate();
290 254 : SwLinePortion *const pRest( rInf.GetRest() );
291 272 : if (pRest && pRest->InFldGrp() &&
292 18 : static_cast<SwFldPortion*>(pRest)->IsNoLength())
293 : {
294 : // HACK: decrement again, so we pick up the suffix in next line!
295 0 : --m_nHintEndIndex;
296 : }
297 254 : delete pRest;
298 254 : rInf.SetRest(0);
299 254 : return pPor;
300 : }
301 :
302 206923 : void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf,
303 : SwLinePortion *pPor ) const
304 : {
305 : // The new portion is inserted, but everything's different for
306 : // LineLayout ...
307 206923 : if( pPor == pCurr )
308 : {
309 103827 : if ( pCurr->GetPortion() )
310 : {
311 5829 : pPor = pCurr->GetPortion();
312 : }
313 :
314 : // #i112181#
315 103827 : rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() );
316 : }
317 : else
318 : {
319 103096 : SwLinePortion *pLast = rInf.GetLast();
320 103096 : if( pLast->GetPortion() )
321 : {
322 882 : while( pLast->GetPortion() )
323 414 : pLast = pLast->GetPortion();
324 234 : rInf.SetLast( pLast );
325 : }
326 103096 : pLast->Insert( pPor );
327 :
328 103096 : rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() );
329 :
330 : // Adjust maxima
331 103096 : if( pCurr->Height() < pPor->Height() )
332 10714 : pCurr->Height( pPor->Height() );
333 103096 : if( pCurr->GetAscent() < pPor->GetAscent() )
334 4456 : pCurr->SetAscent( pPor->GetAscent() );
335 : }
336 :
337 : // Sometimes chains are constructed (e.g. by hyphenate)
338 206923 : rInf.SetLast( pPor );
339 628468 : while( pPor )
340 : {
341 214622 : pPor->Move( rInf );
342 214622 : rInf.SetLast( pPor );
343 214622 : pPor = pPor->GetPortion();
344 : }
345 206923 : }
346 :
347 143254 : void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf )
348 : {
349 : OSL_ENSURE( rInf.GetTxt().getLength() < COMPLETE_STRING,
350 : "SwTxtFormatter::BuildPortions: bad text length in info" );
351 :
352 143254 : rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
353 :
354 : // First NewTxtPortion() decides whether pCurr ends up in pPor.
355 : // We need to make sure that the font is being set in any case.
356 : // This is done automatically in CalcAscent.
357 143254 : rInf.SetLast( pCurr );
358 143254 : rInf.ForcedLeftMargin( 0 );
359 :
360 : OSL_ENSURE( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" );
361 :
362 143254 : if( !pCurr->GetAscent() && !pCurr->Height() )
363 143254 : CalcAscent( rInf, pCurr );
364 :
365 143254 : SeekAndChg( rInf );
366 :
367 : // Width() is shortened in CalcFlyWidth if we have a FlyPortion
368 : OSL_ENSURE( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" );
369 143254 : CalcFlyWidth( rInf );
370 143254 : SwFlyPortion *pFly = rInf.GetFly();
371 143254 : if( pFly )
372 : {
373 1430 : if ( 0 < pFly->Fix() )
374 870 : ClearFly( rInf );
375 : else
376 560 : rInf.SetFull(true);
377 : }
378 :
379 143254 : SwLinePortion *pPor = NewPortion( rInf );
380 :
381 : // Asian grid stuff
382 143254 : SwTextGridItem const*const pGrid(GetGridItem(pFrm->FindPageFrm()));
383 143334 : const bool bHasGrid = pGrid && rInf.SnapToGrid() &&
384 143334 : GRID_LINES_CHARS == pGrid->GetGridType();
385 :
386 143254 : const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
387 143254 : const sal_uInt16 nGridWidth = (bHasGrid) ? GetGridWidth(*pGrid, *pDoc) : 0;
388 :
389 : // used for grid mode only:
390 : // the pointer is stored, because after formatting of non-asian text,
391 : // the width of the kerning portion has to be adjusted
392 143254 : SwKernPortion* pGridKernPortion = 0;
393 :
394 143254 : bool bFull = false;
395 143254 : SwTwips nUnderLineStart = 0;
396 143254 : rInf.Y( Y() );
397 :
398 493431 : while( pPor && !rInf.IsStop() )
399 : {
400 : OSL_ENSURE( rInf.GetLen() < COMPLETE_STRING &&
401 : rInf.GetIdx() <= rInf.GetTxt().getLength(),
402 : "SwTxtFormatter::BuildPortions: bad length in info" );
403 :
404 : // We have to check the script for fields in order to set the
405 : // correct nActual value for the font.
406 206923 : if( pPor->InFldGrp() )
407 22944 : ((SwFldPortion*)pPor)->CheckScript( rInf );
408 :
409 827442 : if( ! bHasGrid && rInf.HasScriptSpace() &&
410 591145 : rInf.GetLast() && rInf.GetLast()->InTxtGrp() &&
411 435440 : rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() )
412 : {
413 41978 : sal_uInt8 nNxtActual = rInf.GetFont()->GetActual();
414 41978 : sal_uInt8 nLstActual = nNxtActual;
415 41978 : sal_uInt16 nLstHeight = (sal_uInt16)rInf.GetFont()->GetHeight();
416 41978 : bool bAllowBefore = false;
417 41978 : bool bAllowBehind = false;
418 41978 : const CharClass& rCC = GetAppCharClass();
419 :
420 : // are there any punctuation characters on both sides
421 : // of the kerning portion?
422 41978 : if ( pPor->InFldGrp() )
423 : {
424 2972 : OUString aAltTxt;
425 5944 : if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) &&
426 2972 : !aAltTxt.isEmpty() )
427 : {
428 2572 : bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 );
429 :
430 2572 : const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont();
431 2572 : if ( pTmpFnt )
432 2 : nNxtActual = pTmpFnt->GetActual();
433 2972 : }
434 : }
435 : else
436 : {
437 39006 : const OUString& rTxt = rInf.GetTxt();
438 39006 : sal_Int32 nIdx = rInf.GetIdx();
439 39006 : bAllowBehind = nIdx < rTxt.getLength() && rCC.isLetterNumeric(rTxt, nIdx);
440 : }
441 :
442 41978 : const SwLinePortion* pLast = rInf.GetLast();
443 41978 : if ( bAllowBehind && pLast )
444 : {
445 16723 : if ( pLast->InFldGrp() )
446 : {
447 282 : OUString aAltTxt;
448 564 : if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) &&
449 282 : !aAltTxt.isEmpty() )
450 : {
451 282 : bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.getLength() - 1 );
452 :
453 282 : const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont();
454 282 : if ( pTmpFnt )
455 : {
456 0 : nLstActual = pTmpFnt->GetActual();
457 0 : nLstHeight = (sal_uInt16)pTmpFnt->GetHeight();
458 : }
459 282 : }
460 : }
461 16441 : else if ( rInf.GetIdx() )
462 : {
463 16429 : bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 );
464 : // Note: ScriptType returns values in [1,4]
465 16429 : if ( bAllowBefore )
466 3016 : nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1;
467 : }
468 :
469 16723 : nLstHeight /= 5;
470 : // does the kerning portion still fit into the line?
471 20015 : if( bAllowBefore && ( nLstActual != nNxtActual ) &&
472 16723 : nLstHeight && rInf.X() + nLstHeight <= rInf.Width() )
473 : {
474 : SwKernPortion* pKrn =
475 : new SwKernPortion( *rInf.GetLast(), nLstHeight,
476 0 : pLast->InFldGrp() && pPor->InFldGrp() );
477 0 : rInf.GetLast()->SetPortion( NULL );
478 0 : InsertPortion( rInf, pKrn );
479 : }
480 : }
481 : }
482 164945 : else if ( bHasGrid && ! pGridKernPortion && ! pMulti )
483 : {
484 : // insert a grid kerning portion
485 0 : if ( ! pGridKernPortion )
486 0 : pGridKernPortion = pPor->IsKernPortion() ?
487 : (SwKernPortion*)pPor :
488 0 : new SwKernPortion( *pCurr );
489 :
490 : // if we have a new GridKernPortion, we initially calculate
491 : // its size so that its ends on the grid
492 0 : const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
493 0 : const SwLayoutFrm* pBody = pPageFrm->FindBodyCont();
494 0 : SWRECTFN( pPageFrm )
495 :
496 : const long nGridOrigin = pBody ?
497 0 : (pBody->*fnRect->fnGetPrtLeft)() :
498 0 : (pPageFrm->*fnRect->fnGetPrtLeft)();
499 :
500 0 : SwTwips nStartX = rInf.X() + GetLeftMargin();
501 0 : if ( bVert )
502 : {
503 0 : Point aPoint( nStartX, 0 );
504 0 : pFrm->SwitchHorizontalToVertical( aPoint );
505 0 : nStartX = aPoint.Y();
506 : }
507 :
508 0 : const SwTwips nOfst = nStartX - nGridOrigin;
509 0 : if ( nOfst )
510 : {
511 : const sal_uLong i = ( nOfst > 0 ) ?
512 0 : ( ( nOfst - 1 ) / nGridWidth + 1 ) :
513 0 : 0;
514 0 : const SwTwips nKernWidth = i * nGridWidth - nOfst;
515 0 : const SwTwips nRestWidth = rInf.Width() - rInf.X();
516 :
517 0 : if ( nKernWidth <= nRestWidth )
518 0 : pGridKernPortion->Width( (sal_uInt16)nKernWidth );
519 : }
520 :
521 0 : if ( pGridKernPortion != pPor )
522 0 : InsertPortion( rInf, pGridKernPortion );
523 : }
524 :
525 206923 : if( pPor->IsDropPortion() )
526 24 : MergeCharacterBorder(*static_cast<SwDropPortion*>(pPor));
527 :
528 : // the multi-portion has it's own format function
529 206923 : if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) )
530 500 : bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) );
531 : else
532 206423 : bFull = pPor->Format( rInf );
533 :
534 206923 : if( rInf.IsRuby() && !rInf.GetRest() )
535 336 : bFull = true;
536 :
537 : // if we are underlined, we store the beginning of this underlined
538 : // segment for repaint optimization
539 206923 : if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart )
540 12100 : nUnderLineStart = GetLeftMargin() + rInf.X();
541 :
542 206923 : if ( pPor->IsFlyPortion() )
543 820 : pCurr->SetFly( true );
544 : // some special cases, where we have to take care for the repaint
545 : // offset:
546 : // 1. Underlined portions due to special underline feature
547 : // 2. Right Tab
548 : // 3. BidiPortions
549 : // 4. other Multiportions
550 : // 5. DropCaps
551 : // 6. Grid Mode
552 420716 : else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) &&
553 : // 1. Underlined portions
554 225061 : nUnderLineStart &&
555 : // reformat is at end of an underlined portion and next portion
556 : // is not underlined
557 18912 : ( ( rInf.GetReformatStart() == rInf.GetIdx() &&
558 2604 : UNDERLINE_NONE == pFnt->GetUnderline()
559 16308 : ) ||
560 : // reformat is inside portion and portion is underlined
561 19098 : ( rInf.GetReformatStart() >= rInf.GetIdx() &&
562 5444 : rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() &&
563 2654 : UNDERLINE_NONE != pFnt->GetUnderline() ) ) )
564 2650 : rInf.SetPaintOfst( nUnderLineStart );
565 402226 : else if ( ! rInf.GetPaintOfst() &&
566 : // 2. Right Tab
567 412954 : ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) ||
568 : // 3. BidiPortions
569 382890 : ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) ||
570 : // 4. Multi Portion and 5. Drop Caps
571 382766 : ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) &&
572 474 : rInf.GetReformatStart() >= rInf.GetIdx() &&
573 230 : rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() )
574 : // 6. Grid Mode
575 191113 : || ( bHasGrid && SW_CJK != pFnt->GetActual() )
576 : )
577 : )
578 : // we store the beginning of the critical portion as our
579 : // paint offset
580 3830 : rInf.SetPaintOfst( GetLeftMargin() + rInf.X() );
581 :
582 : // under one of these conditions we are allowed to delete the
583 : // start of the underline portion
584 206923 : if ( IsUnderlineBreak( *pPor, *pFnt ) )
585 194791 : nUnderLineStart = 0;
586 :
587 207423 : if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() &&
588 500 : ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) )
589 4112 : SetFlyInCntBase();
590 : // bUnderflow needs to be reset or we wrap again at the next softhyphen
591 206923 : if ( !bFull )
592 : {
593 148717 : rInf.ClrUnderflow();
594 560984 : if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() &&
595 361523 : pPor->GetLen() && !pPor->InFldGrp() )
596 : {
597 : // The distance between two different scripts is set
598 : // to 20% of the fontheight.
599 92273 : sal_Int32 nTmp = rInf.GetIdx() + pPor->GetLen();
600 143394 : if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) &&
601 51121 : nTmp != rInf.GetTxt().getLength() )
602 : {
603 20 : const sal_uInt16 nDist = (sal_uInt16)(rInf.GetFont()->GetHeight()/5);
604 :
605 20 : if( nDist )
606 : {
607 : // we do not want a kerning portion if any end
608 : // would be a punctuation character
609 20 : const CharClass& rCC = GetAppCharClass();
610 26 : if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) &&
611 6 : rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) )
612 : {
613 : // does the kerning portion still fit into the line?
614 6 : if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() )
615 6 : new SwKernPortion( *pPor, nDist );
616 : else
617 0 : bFull = true;
618 : }
619 : }
620 : }
621 : }
622 : }
623 :
624 206923 : if ( bHasGrid && pPor != pGridKernPortion && ! pMulti )
625 : {
626 0 : sal_Int32 nTmp = rInf.GetIdx() + pPor->GetLen();
627 0 : const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
628 :
629 0 : const sal_uInt8 nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() );
630 0 : const sal_uInt8 nNextScript = nTmp >= rInf.GetTxt().getLength() ?
631 : SW_CJK :
632 0 : SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo );
633 :
634 : // snap non-asian text to grid if next portion is ASIAN or
635 : // there are no more portions in this line
636 : // be careful when handling an underflow event: the gridkernportion
637 : // could have been deleted
638 0 : if ( nRestWidth > 0 && SW_CJK != nCurrScript &&
639 0 : ! rInf.IsUnderflow() && ( bFull || SW_CJK == nNextScript ) )
640 : {
641 : OSL_ENSURE( pGridKernPortion, "No GridKernPortion available" );
642 :
643 : // calculate size
644 0 : SwLinePortion* pTmpPor = pGridKernPortion->GetPortion();
645 0 : sal_uInt16 nSumWidth = pPor->Width();
646 0 : while ( pTmpPor )
647 : {
648 0 : nSumWidth = nSumWidth + pTmpPor->Width();
649 0 : pTmpPor = pTmpPor->GetPortion();
650 : }
651 :
652 : const SwTwips i = nSumWidth ?
653 0 : ( nSumWidth - 1 ) / nGridWidth + 1 :
654 0 : 0;
655 0 : const SwTwips nTmpWidth = i * nGridWidth;
656 0 : const SwTwips nKernWidth = std::min(nTmpWidth - nSumWidth, nRestWidth);
657 0 : const sal_uInt16 nKernWidth_1 = (sal_uInt16)(nKernWidth / 2);
658 :
659 : OSL_ENSURE( nKernWidth <= nRestWidth,
660 : "Not enough space left for adjusting non-asian text in grid mode" );
661 :
662 0 : pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
663 0 : rInf.X( rInf.X() + nKernWidth_1 );
664 :
665 0 : if ( ! bFull )
666 : new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1),
667 0 : false, true );
668 :
669 0 : pGridKernPortion = 0;
670 : }
671 0 : else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() ||
672 0 : pPor->IsFlyCntPortion() || pPor->InNumberGrp() ||
673 0 : pPor->InFldGrp() || nCurrScript != nNextScript )
674 : // next portion should snap to grid
675 0 : pGridKernPortion = 0;
676 : }
677 :
678 206923 : rInf.SetFull( bFull );
679 :
680 206923 : if( !pPor->IsDropPortion() )
681 206899 : MergeCharacterBorder(*pPor, rInf);
682 :
683 : // Restportions from fields with multiple lines don't yet have the right ascent
684 442008 : if ( !pPor->GetLen() && !pPor->IsFlyPortion()
685 27342 : && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp()
686 217455 : && !pPor->IsMultiPortion() )
687 10530 : CalcAscent( rInf, pPor );
688 :
689 206923 : InsertPortion( rInf, pPor );
690 206923 : pPor = NewPortion( rInf );
691 : }
692 :
693 143254 : if( !rInf.IsStop() )
694 : {
695 : // The last right centered, decimal tab
696 143254 : SwTabPortion *pLastTab = rInf.GetLastTab();
697 143254 : if( pLastTab )
698 3582 : pLastTab->FormatEOL( rInf );
699 139672 : else if( rInf.GetLast() && rInf.LastKernPortion() )
700 566 : rInf.GetLast()->FormatEOL( rInf );
701 : }
702 328512 : if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp()
703 152044 : && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() )
704 0 : rInf.SetNumDone( false );
705 :
706 : // Delete fly in any case
707 143254 : ClearFly( rInf );
708 :
709 : // Reinit the tab overflow flag after the line
710 143254 : rInf.SetTabOverflow( false );
711 143254 : }
712 :
713 136772 : void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
714 : {
715 136772 : if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti)
716 : {
717 30234 : pCurrent->SetFormatAdj(true);
718 30234 : if( IsFlyInCntBase() )
719 : {
720 412 : CalcAdjLine( pCurrent );
721 : // For e.g. centered fly we need to switch the RefPoint
722 : // That's why bAlways = true
723 412 : UpdatePos( pCurrent, GetTopLeft(), GetStart(), true );
724 : }
725 : }
726 136772 : }
727 :
728 353869 : void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor )
729 : {
730 353869 : bool bCalc = false;
731 353869 : if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() )
732 : {
733 : // Numbering + InterNetFlds can keep an own font, then their size is
734 : // independent from hard attribute values
735 17224 : SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt;
736 17224 : SwFontSave aSave( rInf, pFldFnt );
737 17224 : pPor->Height( rInf.GetTxtHeight() );
738 17224 : pPor->SetAscent( rInf.GetAscent() );
739 17224 : bCalc = true;
740 : }
741 : // #i89179#
742 : // tab portion representing the list tab of a list label gets the
743 : // same height and ascent as the corresponding number portion
744 725968 : else if ( pPor->InTabGrp() && pPor->GetLen() == 0 &&
745 384629 : rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
746 15968 : static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() )
747 : {
748 15688 : const SwLinePortion* pLast = rInf.GetLast();
749 15688 : pPor->Height( pLast->Height() );
750 15688 : pPor->SetAscent( pLast->GetAscent() );
751 : }
752 : else
753 : {
754 320957 : const SwLinePortion *pLast = rInf.GetLast();
755 320957 : bool bChg = false;
756 :
757 : // In empty lines the attributes are switched on via SeekStart
758 320957 : const bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
759 320957 : if ( pPor->IsQuoVadisPortion() )
760 0 : bChg = SeekStartAndChg( rInf, true );
761 : else
762 : {
763 320957 : if( bFirstPor )
764 : {
765 257107 : if( !rInf.GetTxt().isEmpty() )
766 : {
767 586423 : if ( pPor->GetLen() || !rInf.GetIdx()
768 59680 : || ( pCurr != pLast && !pLast->IsFlyPortion() )
769 292038 : || !pCurr->IsRest() ) // instead of !rInf.GetRest()
770 232358 : bChg = SeekAndChg( rInf );
771 : else
772 0 : bChg = SeekAndChgBefore( rInf );
773 : }
774 24749 : else if ( pMulti )
775 : // do not open attributes starting at 0 in empty multi
776 : // portions (rotated numbering followed by a footnote
777 : // can cause trouble, because the footnote attribute
778 : // starts at 0, but if we open it, the attribute handler
779 : // cannot handle it.
780 0 : bChg = false;
781 : else
782 24749 : bChg = SeekStartAndChg( rInf );
783 : }
784 : else
785 63850 : bChg = SeekAndChg( rInf );
786 : }
787 321547 : if( bChg || bFirstPor || !pPor->GetAscent()
788 320959 : || !rInf.GetLast()->InTxtGrp() )
789 : {
790 320957 : pPor->SetAscent( rInf.GetAscent() );
791 320957 : pPor->Height( rInf.GetTxtHeight() );
792 320957 : bCalc = true;
793 : }
794 : else
795 : {
796 0 : pPor->Height( pLast->Height() );
797 0 : pPor->SetAscent( pLast->GetAscent() );
798 : }
799 : }
800 :
801 353869 : if( pPor->InTxtGrp() && bCalc )
802 : {
803 316843 : pPor->SetAscent(pPor->GetAscent() +
804 316843 : rInf.GetFont()->GetTopBorderSpace());
805 316843 : pPor->Height(pPor->Height() +
806 316843 : rInf.GetFont()->GetTopBorderSpace() +
807 316843 : rInf.GetFont()->GetBottomBorderSpace() );
808 : }
809 353869 : }
810 :
811 1152 : class SwMetaPortion : public SwTxtPortion
812 : {
813 : public:
814 576 : inline SwMetaPortion() { SetWhichPor( POR_META ); }
815 : virtual void Paint( const SwTxtPaintInfo &rInf ) const SAL_OVERRIDE;
816 : // OUTPUT_OPERATOR
817 : };
818 :
819 624 : void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const
820 : {
821 624 : if ( Width() )
822 : {
823 624 : rInf.DrawViewOpt( *this, POR_META );
824 624 : SwTxtPortion::Paint( rInf );
825 : }
826 624 : }
827 :
828 : namespace sw { namespace mark {
829 2 : OUString ExpandFieldmark(IFieldmark* pBM)
830 : {
831 2 : const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
832 2 : sal_Int32 nCurrentIdx = 0;
833 2 : const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(OUString(ODF_FORMDROPDOWN_RESULT));
834 2 : if(pResult != pParameters->end())
835 2 : pResult->second >>= nCurrentIdx;
836 :
837 2 : const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(OUString(ODF_FORMDROPDOWN_LISTENTRY));
838 2 : if (pListEntries != pParameters->end())
839 : {
840 2 : uno::Sequence< OUString > vListEntries;
841 2 : pListEntries->second >>= vListEntries;
842 2 : if (nCurrentIdx < vListEntries.getLength())
843 2 : return vListEntries[nCurrentIdx];
844 : }
845 0 : return OUString();
846 : }
847 : } }
848 :
849 146419 : SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const
850 : {
851 146419 : SwTxtPortion *pPor = 0;
852 146419 : if( GetFnt()->IsTox() )
853 : {
854 42 : pPor = new SwToxPortion;
855 : }
856 146377 : else if ( GetFnt()->IsInputField() )
857 : {
858 32 : pPor = new SwTxtInputFldPortion();
859 : }
860 : else
861 : {
862 146345 : if( GetFnt()->IsRef() )
863 36 : pPor = new SwRefPortion;
864 146309 : else if (GetFnt()->IsMeta())
865 : {
866 576 : pPor = new SwMetaPortion;
867 : }
868 : else
869 : {
870 : // Only at the End!
871 : // If pCurr does not have a width, it can however aready have content.
872 : // E.g. for non-displayable characters
873 145733 : if (rInf.GetTxt()[rInf.GetIdx()]==CH_TXT_ATR_FIELDSTART)
874 204 : pPor = new SwFieldMarkPortion();
875 145529 : else if (rInf.GetTxt()[rInf.GetIdx()]==CH_TXT_ATR_FIELDEND)
876 208 : pPor = new SwFieldMarkPortion();
877 145321 : else if (rInf.GetTxt()[rInf.GetIdx()]==CH_TXT_ATR_FORMELEMENT)
878 : {
879 20 : SwTxtNode *pNd = const_cast<SwTxtNode *>(rInf.GetTxtFrm()->GetTxtNode());
880 20 : const SwDoc *doc = pNd->GetDoc();
881 20 : SwIndex aIndex(pNd, rInf.GetIdx());
882 40 : SwPosition aPosition(*pNd, aIndex);
883 20 : sw::mark::IFieldmark *pBM = doc->getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
884 : OSL_ENSURE(pBM != NULL, "Where is my form field bookmark???");
885 20 : if (pBM != NULL)
886 : {
887 18 : if (pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
888 : {
889 18 : pPor = new SwFieldFormCheckboxPortion();
890 : }
891 0 : else if (pBM->GetFieldname( ) == ODF_FORMDROPDOWN)
892 : {
893 0 : pPor = new SwFieldFormDropDownPortion(sw::mark::ExpandFieldmark(pBM));
894 : }
895 : /* we need to check for ODF_FORMTEXT for scenario having FormFields inside FORMTEXT.
896 : * Otherwise file will crash on open.
897 : */
898 0 : else if (pBM->GetFieldname( ) == ODF_FORMTEXT)
899 : {
900 0 : pPor = new SwFieldMarkPortion();
901 : }
902 : else
903 : {
904 : assert( false ); // unknown type...
905 : }
906 20 : }
907 : }
908 145733 : if( !pPor )
909 : {
910 145303 : if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() )
911 103787 : pPor = pCurr;
912 : else
913 : {
914 41516 : pPor = new SwTxtPortion;
915 41516 : if ( GetFnt()->IsURL() )
916 : {
917 0 : pPor->SetWhichPor( POR_URL );
918 : }
919 : }
920 : }
921 : }
922 : }
923 146419 : return pPor;
924 : }
925 :
926 : // We calculate the length, the following portion limits are defined:
927 : // 1) Tabs
928 : // 2) Linebreaks
929 : // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
930 : // 4) next attribute change
931 :
932 146419 : SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf )
933 : {
934 : // If we're at the line's beginning, we take pCurr
935 : // If pCurr is not derived from SwTxtPortion, we need to duplicate
936 146419 : Seek( rInf.GetIdx() );
937 146419 : SwTxtPortion *pPor = WhichTxtPor( rInf );
938 :
939 : // until next attribute change:
940 146419 : const sal_Int32 nNextAttr = GetNextAttr();
941 146419 : sal_Int32 nNextChg = std::min( nNextAttr, rInf.GetTxt().getLength() );
942 :
943 : // end of script type:
944 146419 : const sal_Int32 nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() );
945 146419 : nNextChg = std::min( nNextChg, nNextScript );
946 :
947 : // end of direction:
948 146419 : const sal_Int32 nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() );
949 146419 : nNextChg = std::min( nNextChg, nNextDir );
950 :
951 : // Turbo boost:
952 : // We assume that a font's characters are not larger than twice
953 : // as wide as heigh.
954 : // Very crazy: We need to take the ascent into account.
955 :
956 : // Mind the trap! GetSize() contains the wished-for height, the real height
957 : // is only known in CalcAscent!
958 :
959 : // The ratio is even crazier: a blank in Times New Roman has an ascent of
960 : // 182, a height of 200 and a width of 53!
961 : // It follows that a line with a lot of blanks is processed incorrectly.
962 : // Therefore we increase from factor 2 to 8 (due to negative kerning).
963 :
964 146419 : pPor->SetLen(1);
965 146419 : CalcAscent( rInf, pPor );
966 :
967 146419 : const SwFont* pTmpFnt = rInf.GetFont();
968 146419 : sal_Int32 nExpect = std::min( sal_Int32( ((vcl::Font *)pTmpFnt)->GetSize().Height() ),
969 292838 : sal_Int32( pPor->GetAscent() ) ) / 8;
970 146419 : if ( !nExpect )
971 0 : nExpect = 1;
972 146419 : nExpect = rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect);
973 146419 : if( nExpect > rInf.GetIdx() && nNextChg > nExpect )
974 31182 : nNextChg = std::min( nExpect, rInf.GetTxt().getLength() );
975 :
976 : // we keep an invariant during method calls:
977 : // there are no portion ending characters like hard spaces
978 : // or tabs in [ nLeftScanIdx, nRightScanIdx ]
979 146419 : if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx )
980 : {
981 77975 : if ( nNextChg > nRightScanIdx )
982 : nNextChg = nRightScanIdx =
983 54079 : rInf.ScanPortionEnd( nRightScanIdx, nNextChg );
984 : }
985 : else
986 : {
987 68444 : nLeftScanIdx = rInf.GetIdx();
988 : nNextChg = nRightScanIdx =
989 68444 : rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
990 : }
991 :
992 146419 : pPor->SetLen( nNextChg - rInf.GetIdx() );
993 146419 : rInf.SetLen( pPor->GetLen() );
994 146419 : return pPor;
995 : }
996 :
997 348269 : SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf)
998 : {
999 348269 : SwLinePortion *pPor = 0;
1000 :
1001 348269 : if( rInf.GetRest() )
1002 : {
1003 : // Tabs and fields
1004 16364 : if( '\0' != rInf.GetHookChar() )
1005 7984 : return 0;
1006 :
1007 8380 : pPor = rInf.GetRest();
1008 8380 : if( pPor->IsErgoSumPortion() )
1009 0 : rInf.SetErgoDone(true);
1010 : else
1011 8380 : if( pPor->IsFtnNumPortion() )
1012 0 : rInf.SetFtnDone(true);
1013 : else
1014 8380 : if( pPor->InNumberGrp() )
1015 8020 : rInf.SetNumDone(true);
1016 :
1017 8380 : rInf.SetRest(0);
1018 8380 : pCurr->SetRest( true );
1019 8380 : return pPor;
1020 : }
1021 :
1022 : // We can stand in the follow, it's crucial that
1023 : // pFrm->GetOfst() == 0!
1024 331905 : if( rInf.GetIdx() )
1025 : {
1026 : // We now too can elongate FtnPortions and ErgoSumPortions
1027 :
1028 : // 1. The ErgoSumTexts
1029 239381 : if( !rInf.IsErgoDone() )
1030 : {
1031 5979 : if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1032 0 : pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1033 5979 : rInf.SetErgoDone( true );
1034 : }
1035 :
1036 : // 2. Arrow portions
1037 239381 : if( !pPor && !rInf.IsArrowDone() )
1038 : {
1039 64260 : if( pFrm->GetOfst() && !pFrm->IsFollow() &&
1040 0 : rInf.GetIdx() == pFrm->GetOfst() )
1041 0 : pPor = new SwArrowPortion( *pCurr );
1042 64260 : rInf.SetArrowDone( true );
1043 : }
1044 :
1045 : // 3. Kerning portions at beginning of line in grid mode
1046 239381 : if ( ! pPor && ! pCurr->GetPortion() )
1047 : {
1048 : SwTextGridItem const*const pGrid(
1049 156952 : GetGridItem(GetTxtFrm()->FindPageFrm()));
1050 156952 : if ( pGrid )
1051 24 : pPor = new SwKernPortion( *pCurr );
1052 : }
1053 :
1054 : // 4. The line rests (multiline fields)
1055 239381 : if( !pPor )
1056 : {
1057 239357 : pPor = rInf.GetRest();
1058 : // Only for pPor of course
1059 239357 : if( pPor )
1060 : {
1061 0 : pCurr->SetRest( true );
1062 0 : rInf.SetRest(0);
1063 : }
1064 : }
1065 : }
1066 : else
1067 : {
1068 : // 5. The foot note count
1069 92524 : if( !rInf.IsFtnDone() )
1070 : {
1071 : OSL_ENSURE( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1072 : "Rotated number portion trouble" );
1073 :
1074 82954 : const bool bFtnNum = pFrm->IsFtnNumFrm();
1075 82954 : rInf.GetParaPortion()->SetFtnNum( bFtnNum );
1076 82954 : if( bFtnNum )
1077 236 : pPor = (SwLinePortion*)NewFtnNumPortion( rInf );
1078 82954 : rInf.SetFtnDone( true );
1079 : }
1080 :
1081 : // 6. The ErgoSumTexts of course also exist in the TextMaster,
1082 : // it's crucial whether the SwFtnFrm is aFollow
1083 92524 : if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
1084 : {
1085 82860 : if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1086 236 : pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1087 82860 : rInf.SetErgoDone( true );
1088 : }
1089 :
1090 : // 7. The numbering
1091 92524 : if( !rInf.IsNumDone() && !pPor )
1092 : {
1093 : OSL_ENSURE( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1094 : "Rotated number portion trouble" );
1095 :
1096 : // If we're in the follow, then of course not
1097 82644 : if( GetTxtFrm()->GetTxtNode()->GetNumRule() )
1098 8612 : pPor = (SwLinePortion*)NewNumberPortion( rInf );
1099 82644 : rInf.SetNumDone( true );
1100 : }
1101 : // 8. The DropCaps
1102 92524 : if( !pPor && GetDropFmt() && ! rInf.IsMulti() )
1103 24 : pPor = (SwLinePortion*)NewDropPortion( rInf );
1104 :
1105 : // 9. Kerning portions at beginning of line in grid mode
1106 92524 : if ( !pPor && !pCurr->GetPortion() )
1107 : {
1108 : SwTextGridItem const*const pGrid(
1109 74378 : GetGridItem(GetTxtFrm()->FindPageFrm()));
1110 74378 : if ( pGrid )
1111 116 : pPor = new SwKernPortion( *pCurr );
1112 : }
1113 : }
1114 :
1115 : // 10. Decimal tab portion at the beginning of each line in table cells
1116 886046 : if ( !pPor && !pCurr->GetPortion() &&
1117 600749 : GetTxtFrm()->IsInTab() &&
1118 37654 : GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) )
1119 : {
1120 37644 : pPor = NewTabPortion( rInf, true );
1121 : }
1122 :
1123 : // 11. suffix of meta-field
1124 331905 : if (!pPor)
1125 : {
1126 322951 : pPor = TryNewNoLengthPortion(rInf);
1127 : }
1128 :
1129 331905 : return pPor;
1130 : }
1131 :
1132 56858 : static bool lcl_OldFieldRest( const SwLineLayout* pCurr )
1133 : {
1134 56858 : if( !pCurr->GetNext() )
1135 29769 : return false;
1136 27089 : const SwLinePortion *pPor = pCurr->GetNext()->GetPortion();
1137 27089 : bool bRet = false;
1138 54558 : while( pPor && !bRet )
1139 : {
1140 768 : bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) ||
1141 768 : (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld());
1142 384 : if( !pPor->GetLen() )
1143 4 : break;
1144 380 : pPor = pPor->GetPortion();
1145 : }
1146 27089 : return bRet;
1147 : }
1148 :
1149 : /* NewPortion sets rInf.nLen
1150 : * A SwTxtPortion is limited by a tab, break, txtatr or attr change
1151 : * We can have three cases:
1152 : * 1) The line is full and the wrap was not emulated
1153 : * -> return 0;
1154 : * 2) The line is full and a wrap was emulated
1155 : * -> Reset width and return new FlyPortion
1156 : * 3) We need to construct a new portion
1157 : * -> CalcFlyWidth emulates the width and return portion, if needed
1158 : */
1159 :
1160 350177 : SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf )
1161 : {
1162 : // Underflow takes precedence
1163 350177 : rInf.SetStopUnderflow( false );
1164 350177 : if( rInf.GetUnderflow() )
1165 : {
1166 : OSL_ENSURE( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" );
1167 254 : return Underflow( rInf );
1168 : }
1169 :
1170 : // If the line is full, flys and Underflow portions could be waiting ...
1171 349923 : if( rInf.IsFull() )
1172 : {
1173 : // LineBreaks and Flys (bug05.sdw)
1174 : // IsDummy()
1175 58512 : if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) )
1176 794 : return 0;
1177 :
1178 : // Wenn der Text an den Fly gestossen ist, oder wenn
1179 : // der Fly als erstes drankommt, weil er ueber dem linken
1180 : // Rand haengt, wird GetFly() returnt.
1181 : // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's
1182 : // naturgemaesz eine 0.
1183 57718 : if( rInf.GetFly() )
1184 : {
1185 836 : if( rInf.GetLast()->IsBreakPortion() )
1186 : {
1187 16 : delete rInf.GetFly();
1188 16 : rInf.SetFly( 0 );
1189 : }
1190 :
1191 836 : return rInf.GetFly();
1192 : }
1193 : // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den
1194 : // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest
1195 : // bekanntgeben, damit SwTxtFrm::Format nicht abbricht
1196 : // (die Textmasse wurde ja durchformatiert).
1197 56882 : if( rInf.GetRest() )
1198 24 : rInf.SetNewLine( true );
1199 : else
1200 : {
1201 : // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt,
1202 : // jetzt aber kein Rest mehr anliegt,
1203 : // muss sie auf jeden Fall neu formatiert werden!
1204 56858 : if( lcl_OldFieldRest( GetCurr() ) )
1205 0 : rInf.SetNewLine( true );
1206 : else
1207 : {
1208 56858 : SwLinePortion *pFirst = WhichFirstPortion( rInf );
1209 56858 : if( pFirst )
1210 : {
1211 0 : rInf.SetNewLine( true );
1212 0 : if( pFirst->InNumberGrp() )
1213 0 : rInf.SetNumDone( false) ;
1214 0 : delete pFirst;
1215 : }
1216 : }
1217 : }
1218 :
1219 56882 : return 0;
1220 : }
1221 :
1222 291411 : SwLinePortion *pPor = WhichFirstPortion( rInf );
1223 :
1224 : // Check for Hidden Portion:
1225 291411 : if ( !pPor )
1226 : {
1227 274037 : sal_Int32 nEnd = rInf.GetIdx();
1228 274037 : if ( ::lcl_BuildHiddenPortion( rInf, nEnd ) )
1229 8 : pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
1230 : }
1231 :
1232 291411 : if( !pPor )
1233 : {
1234 811439 : if( ( !pMulti || pMulti->IsBidi() ) &&
1235 : // #i42734#
1236 : // No multi portion if there is a hook character waiting:
1237 280681 : ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
1238 : {
1239 : // We open a multiportion part, if we enter a multi-line part
1240 : // of the paragraph.
1241 264713 : sal_Int32 nEnd = rInf.GetIdx();
1242 264713 : SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti );
1243 264713 : if( pCreate )
1244 : {
1245 486 : SwMultiPortion* pTmp = NULL;
1246 :
1247 486 : if ( SW_MC_BIDI == pCreate->nId )
1248 50 : pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
1249 436 : else if ( SW_MC_RUBY == pCreate->nId )
1250 : {
1251 336 : Seek( rInf.GetIdx() );
1252 336 : bool bRubyTop = false;
1253 336 : bool* pRubyPos = 0;
1254 :
1255 336 : if ( rInf.SnapToGrid() )
1256 : {
1257 : SwTextGridItem const*const pGrid(
1258 336 : GetGridItem(GetTxtFrm()->FindPageFrm()));
1259 336 : if ( pGrid )
1260 : {
1261 0 : bRubyTop = ! pGrid->GetRubyTextBelow();
1262 0 : pRubyPos = &bRubyTop;
1263 : }
1264 : }
1265 :
1266 336 : pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
1267 336 : *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(),
1268 672 : nEnd, 0, pRubyPos );
1269 : }
1270 100 : else if( SW_MC_ROTATE == pCreate->nId )
1271 : pTmp = new SwRotatedPortion( *pCreate, nEnd,
1272 80 : GetTxtFrm()->IsRightToLeft() );
1273 : else
1274 20 : pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
1275 :
1276 486 : delete pCreate;
1277 486 : CalcFlyWidth( rInf );
1278 :
1279 486 : return pTmp;
1280 : }
1281 : }
1282 : // 5010: Tabs und Felder
1283 273543 : sal_Unicode cChar = rInf.GetHookChar();
1284 :
1285 273543 : if( cChar )
1286 : {
1287 : /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das
1288 : * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile
1289 : * gewandert ist ( so geschehen hinter Rahmen ).
1290 : * Wenn allerdings eine FldPortion im Rest wartet, muessen wir
1291 : * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei
1292 : * DezimalTabs und Feldern (22615)
1293 : */
1294 17544 : if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() )
1295 9560 : cChar = rInf.GetChar( rInf.GetIdx() );
1296 17544 : rInf.ClearHookChar();
1297 : }
1298 : else
1299 : {
1300 255999 : if( rInf.GetIdx() >= rInf.GetTxt().getLength() )
1301 : {
1302 85562 : rInf.SetFull(true);
1303 85562 : CalcFlyWidth( rInf );
1304 85562 : return pPor;
1305 : }
1306 170437 : cChar = rInf.GetChar( rInf.GetIdx() );
1307 : }
1308 :
1309 187981 : switch( cChar )
1310 : {
1311 : case CH_TAB:
1312 28646 : pPor = NewTabPortion( rInf, false ); break;
1313 :
1314 : case CH_BREAK:
1315 886 : pPor = new SwBreakPortion( *rInf.GetLast() ); break;
1316 :
1317 : case CHAR_SOFTHYPHEN: // soft hyphen
1318 74 : pPor = new SwSoftHyphPortion; break;
1319 :
1320 : case CHAR_HARDBLANK: // no-break space
1321 1310 : pPor = new SwBlankPortion( ' ' ); break;
1322 :
1323 : case CHAR_HARDHYPHEN: // non-breaking hyphen
1324 58 : pPor = new SwBlankPortion( '-' ); break;
1325 :
1326 : case CHAR_ZWSP: // zero width space
1327 : case CHAR_ZWNBSP : // word joiner
1328 0 : pPor = new SwControlCharPortion( cChar ); break;
1329 :
1330 : case CH_TXTATR_BREAKWORD:
1331 : case CH_TXTATR_INWORD:
1332 10588 : if( rInf.HasHint( rInf.GetIdx() ) )
1333 : {
1334 10588 : pPor = NewExtraPortion( rInf );
1335 10588 : break;
1336 : }
1337 : // No break
1338 : default :
1339 : {
1340 146419 : SwTabPortion* pLastTabPortion = rInf.GetLastTab();
1341 146419 : if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
1342 : {
1343 : // #127428# Abandon dec. tab position if line is full
1344 : // We have a decimal tab portion in the line and the next character has to be
1345 : // aligned at the tab stop position. We store the width from the beginning of
1346 : // the tab stop portion up to the portion containint the decimal separator:
1347 16 : if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
1348 8 : POR_TABDECIMAL == pLastTabPortion->GetWhichPor() )
1349 : {
1350 : OSL_ENSURE( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" );
1351 8 : const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(rInf.X() - pLastTabPortion->Fix() );
1352 8 : static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
1353 8 : rInf.SetTabDecimal( 0 );
1354 : }
1355 : else
1356 0 : rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
1357 : }
1358 :
1359 146419 : if( rInf.GetRest() )
1360 : {
1361 0 : if( rInf.IsFull() )
1362 : {
1363 0 : rInf.SetNewLine(true);
1364 0 : return 0;
1365 : }
1366 0 : pPor = rInf.GetRest();
1367 0 : rInf.SetRest(0);
1368 : }
1369 : else
1370 : {
1371 146419 : if( rInf.IsFull() )
1372 0 : return 0;
1373 146419 : pPor = NewTxtPortion( rInf );
1374 : }
1375 146419 : break;
1376 : }
1377 : }
1378 :
1379 : // if a portion is created despite there being a pending RestPortion,
1380 : // then it is a field which has been split (e.g. because it contains a Tab)
1381 187981 : if( pPor && rInf.GetRest() )
1382 7984 : pPor->SetLen( 0 );
1383 :
1384 : // robust:
1385 187981 : if( !pPor || rInf.IsStop() )
1386 : {
1387 0 : delete pPor;
1388 0 : return 0;
1389 : }
1390 : }
1391 :
1392 : // Special portions containing numbers (footnote anchor, footnote number,
1393 : // numbering) can be contained in a rotated portion, if the user
1394 : // choose a rotated character attribute.
1395 205363 : if ( pPor && ! pMulti )
1396 : {
1397 204113 : if ( pPor->IsFtnPortion() )
1398 : {
1399 268 : const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn();
1400 :
1401 268 : if ( pTxtFtn )
1402 : {
1403 268 : SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1404 268 : const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1405 : const SwEndNoteInfo* pInfo;
1406 268 : if( rFtn.IsEndNote() )
1407 80 : pInfo = &pDoc->GetEndNoteInfo();
1408 : else
1409 188 : pInfo = &pDoc->GetFtnInfo();
1410 268 : const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1411 :
1412 : const SfxPoolItem* pItem;
1413 268 : sal_uInt16 nDir = 0;
1414 268 : if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1415 268 : true, &pItem ))
1416 0 : nDir = ((SvxCharRotateItem*)pItem)->GetValue();
1417 :
1418 268 : if ( 0 != nDir )
1419 : {
1420 0 : delete pPor;
1421 0 : pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ?
1422 : DIR_BOTTOM2TOP :
1423 0 : DIR_TOP2BOTTOM );
1424 : }
1425 : }
1426 : }
1427 203845 : else if ( pPor->InNumberGrp() )
1428 : {
1429 16810 : const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont();
1430 :
1431 16810 : if ( pNumFnt )
1432 : {
1433 16530 : sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
1434 16530 : if ( 0 != nDir )
1435 : {
1436 0 : delete pPor;
1437 : pPor = new SwRotatedPortion( 0, 900 == nDir ?
1438 : DIR_BOTTOM2TOP :
1439 0 : DIR_TOP2BOTTOM );
1440 :
1441 0 : rInf.SetNumDone( false );
1442 0 : rInf.SetFtnDone( false );
1443 : }
1444 : }
1445 : }
1446 : }
1447 :
1448 : // Der Font wird im Outputdevice eingestellt,
1449 : // der Ascent und die Hoehe werden berechnet.
1450 205363 : if( !pPor->GetAscent() && !pPor->Height() )
1451 53666 : CalcAscent( rInf, pPor );
1452 205363 : rInf.SetLen( pPor->GetLen() );
1453 :
1454 : // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
1455 205363 : CalcFlyWidth( rInf );
1456 :
1457 : // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige
1458 : // Werte bereithalten muss:
1459 205363 : if( !pCurr->Height() )
1460 : {
1461 : OSL_ENSURE( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" );
1462 0 : pCurr->Height( pPor->Height() );
1463 0 : pCurr->SetAscent( pPor->GetAscent() );
1464 : }
1465 :
1466 : OSL_ENSURE( !pPor || pPor->Height(),
1467 : "SwTxtFormatter::NewPortion: something went wrong");
1468 205363 : if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
1469 : {
1470 0 : delete pPor;
1471 0 : pPor = rInf.GetFly();
1472 : }
1473 205363 : return pPor;
1474 : }
1475 :
1476 136772 : sal_Int32 SwTxtFormatter::FormatLine(const sal_Int32 nStartPos)
1477 : {
1478 : OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(),
1479 : "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" );
1480 :
1481 : // For the formatting routines, we set pOut to the reference device.
1482 136772 : SwHookOut aHook( GetInfo() );
1483 136772 : if( GetInfo().GetLen() < GetInfo().GetTxt().getLength() )
1484 48746 : GetInfo().SetLen( GetInfo().GetTxt().getLength() );
1485 :
1486 136772 : bool bBuild = true;
1487 136772 : SetFlyInCntBase( false );
1488 136772 : GetInfo().SetLineHeight( 0 );
1489 136772 : GetInfo().SetLineNetHeight( 0 );
1490 :
1491 : // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden
1492 : // und auch bei geaendertem Ascent (Absenken der Grundlinie).
1493 136772 : const sal_uInt16 nOldHeight = pCurr->Height();
1494 136772 : const sal_uInt16 nOldAscent = pCurr->GetAscent();
1495 :
1496 136772 : pCurr->SetEndHyph( false );
1497 136772 : pCurr->SetMidHyph( false );
1498 :
1499 : // fly positioning can make it necessary format a line several times
1500 : // for this, we have to keep a copy of our rest portion
1501 136772 : SwLinePortion* pFld = GetInfo().GetRest();
1502 273544 : boost::scoped_ptr<SwFldPortion> xSaveFld;
1503 :
1504 136772 : if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() )
1505 10 : xSaveFld.reset(new SwFldPortion( *((SwFldPortion*)pFld) ));
1506 :
1507 : // for an optimal repaint rectangle, we want to compare fly portions
1508 : // before and after the BuildPortions call
1509 136772 : const bool bOptimizeRepaint = AllowRepaintOpt();
1510 136772 : const sal_Int32 nOldLineEnd = nStartPos + pCurr->GetLen();
1511 273544 : std::vector<long> flyStarts;
1512 :
1513 : // these are the conditions for a fly position comparison
1514 136772 : if ( bOptimizeRepaint && pCurr->IsFly() )
1515 : {
1516 0 : SwLinePortion* pPor = pCurr->GetFirstPortion();
1517 0 : long nPOfst = 0;
1518 0 : while ( pPor )
1519 : {
1520 0 : if ( pPor->IsFlyPortion() )
1521 : // insert start value of fly portion
1522 0 : flyStarts.push_back( nPOfst );
1523 :
1524 0 : nPOfst += pPor->Width();
1525 0 : pPor = pPor->GetPortion();
1526 : }
1527 : }
1528 :
1529 : // Hier folgt bald die Unterlaufpruefung.
1530 410342 : while( bBuild )
1531 : {
1532 136798 : GetInfo().SetFtnInside( false );
1533 136798 : GetInfo().SetOtherThanFtnInside( false );
1534 :
1535 : // These values must not be reset by FormatReset();
1536 136798 : const bool bOldNumDone = GetInfo().IsNumDone();
1537 136798 : const bool bOldArrowDone = GetInfo().IsArrowDone();
1538 136798 : const bool bOldErgoDone = GetInfo().IsErgoDone();
1539 :
1540 : // besides other things, this sets the repaint offset to 0
1541 136798 : FormatReset( GetInfo() );
1542 :
1543 136798 : GetInfo().SetNumDone( bOldNumDone );
1544 136798 : GetInfo().SetArrowDone( bOldArrowDone );
1545 136798 : GetInfo().SetErgoDone( bOldErgoDone );
1546 :
1547 : // build new portions for this line
1548 136798 : BuildPortions( GetInfo() );
1549 :
1550 136798 : if( GetInfo().IsStop() )
1551 : {
1552 0 : pCurr->SetLen( 0 );
1553 0 : pCurr->Height( GetFrmRstHeight() + 1 );
1554 0 : pCurr->SetRealHeight( GetFrmRstHeight() + 1 );
1555 0 : pCurr->Width(0);
1556 0 : pCurr->Truncate();
1557 0 : return nStartPos;
1558 : }
1559 136798 : else if( GetInfo().IsDropInit() )
1560 : {
1561 12 : DropInit();
1562 12 : GetInfo().SetDropInit( false );
1563 : }
1564 :
1565 136798 : pCurr->CalcLine( *this, GetInfo() );
1566 136798 : CalcRealHeight( GetInfo().IsNewLine() );
1567 :
1568 : //#i120864# For Special case that at the first calculation couldn't get
1569 : //correct height. And need to recalculate for the right height.
1570 136798 : SwLinePortion* pPorTmp = pCurr->GetPortion();
1571 136798 : if ( IsFlyInCntBase() && (!IsQuick() || (pPorTmp && pPorTmp->IsFlyCntPortion() && !pPorTmp->GetPortion() &&
1572 0 : pCurr->Height() > pPorTmp->Height())))
1573 : {
1574 : sal_uInt16 nTmpAscent, nTmpHeight;
1575 3518 : CalcAscentAndHeight( nTmpAscent, nTmpHeight );
1576 3518 : AlignFlyInCntBase( Y() + long( nTmpAscent ) );
1577 3518 : pCurr->CalcLine( *this, GetInfo() );
1578 3518 : CalcRealHeight();
1579 : }
1580 :
1581 : // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird
1582 136798 : if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
1583 : {
1584 28 : pCurr->SetRealHeight( GetInfo().GetLineHeight() );
1585 28 : bBuild = false;
1586 : }
1587 : else
1588 : {
1589 150922 : bBuild = ( GetInfo().GetTxtFly().IsOn() && ChkFlyUnderflow(GetInfo()) )
1590 273532 : || GetInfo().CheckFtnPortion(pCurr);
1591 136770 : if( bBuild )
1592 : {
1593 26 : GetInfo().SetNumDone( bOldNumDone );
1594 26 : GetInfo().ResetMaxWidthDiff();
1595 :
1596 : // delete old rest
1597 26 : if ( GetInfo().GetRest() )
1598 : {
1599 0 : delete GetInfo().GetRest();
1600 0 : GetInfo().SetRest( 0 );
1601 : }
1602 :
1603 : // set original rest portion
1604 26 : if ( xSaveFld )
1605 0 : GetInfo().SetRest( new SwFldPortion( *xSaveFld ) );
1606 :
1607 26 : pCurr->SetLen( 0 );
1608 26 : pCurr->Width(0);
1609 26 : pCurr->Truncate();
1610 : }
1611 : }
1612 : }
1613 :
1614 : // In case of compat mode, it's possible that a tab portion is wider after
1615 : // formatting than before. If this is the case, we also have to make sure
1616 : // the SwLineLayout is wider as well.
1617 136772 : if (GetInfo().GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_OVER_MARGIN))
1618 : {
1619 87938 : sal_uInt16 nSum = 0;
1620 87938 : SwLinePortion* pPor = pCurr->GetFirstPortion();
1621 :
1622 324602 : while (pPor)
1623 : {
1624 148726 : nSum += pPor->Width();
1625 148726 : pPor = pPor->GetPortion();
1626 : }
1627 :
1628 87938 : if (nSum > pCurr->Width())
1629 296 : pCurr->Width(nSum);
1630 : }
1631 :
1632 : // calculate optimal repaint rectangle
1633 136772 : if ( bOptimizeRepaint )
1634 : {
1635 3371 : GetInfo().SetPaintOfst( ::lcl_CalcOptRepaint( *this, *pCurr, nOldLineEnd, flyStarts ) );
1636 3371 : flyStarts.clear();
1637 : }
1638 : else
1639 : // Special case: We do not allow an optimitation of the repaint
1640 : // area, but during formatting the repaint offset is set to indicate
1641 : // a maximum value for the offset. This value has to be reset:
1642 133401 : GetInfo().SetPaintOfst( 0 );
1643 :
1644 : // This corrects the start of the reformat range if something has
1645 : // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
1646 : // will give us a wrong result if we have to reformat another line
1647 136772 : GetInfo().GetParaPortion()->GetReformat().LeftMove( GetInfo().GetIdx() );
1648 :
1649 : // delete master copy of rest portion
1650 136772 : xSaveFld.reset();
1651 :
1652 136772 : sal_Int32 nNewStart = nStartPos + pCurr->GetLen();
1653 :
1654 : // adjust text if kana compression is enabled
1655 136772 : if ( GetInfo().CompressLine() )
1656 : {
1657 0 : SwTwips nRepaintOfst = CalcKanaAdj( pCurr );
1658 :
1659 : // adjust repaint offset
1660 0 : if ( nRepaintOfst < GetInfo().GetPaintOfst() )
1661 0 : GetInfo().SetPaintOfst( nRepaintOfst );
1662 : }
1663 :
1664 136772 : CalcAdjustLine( pCurr );
1665 :
1666 136772 : if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() )
1667 : {
1668 99679 : SetFlyInCntBase();
1669 99679 : GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling
1670 : // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind
1671 : // auch formatiert werden.
1672 99679 : GetInfo().SetShift( true );
1673 : }
1674 :
1675 136772 : if ( IsFlyInCntBase() && !IsQuick() )
1676 99550 : UpdatePos( pCurr, GetTopLeft(), GetStart() );
1677 :
1678 273544 : return nNewStart;
1679 : }
1680 :
1681 2 : void SwTxtFormatter::RecalcRealHeight()
1682 : {
1683 2 : do
1684 : {
1685 2 : CalcRealHeight();
1686 2 : } while (Next());
1687 2 : }
1688 :
1689 180809 : void SwTxtFormatter::CalcRealHeight( bool bNewLine )
1690 : {
1691 180809 : sal_uInt16 nLineHeight = pCurr->Height();
1692 180809 : pCurr->SetClipping( false );
1693 :
1694 180809 : SwTextGridItem const*const pGrid(GetGridItem(pFrm->FindPageFrm()));
1695 180809 : if ( pGrid && GetInfo().SnapToGrid() )
1696 : {
1697 80 : const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
1698 80 : const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
1699 80 : const bool bRubyTop = ! pGrid->GetRubyTextBelow();
1700 :
1701 80 : nLineHeight = nGridWidth + nRubyHeight;
1702 80 : const sal_uInt16 nAmpRatio = (pCurr->Height() + nLineHeight - 1)/nLineHeight;
1703 80 : nLineHeight *= nAmpRatio;
1704 :
1705 80 : const sal_uInt16 nAsc = pCurr->GetAscent() +
1706 : ( bRubyTop ?
1707 80 : ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 :
1708 160 : ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 );
1709 :
1710 80 : pCurr->Height( nLineHeight );
1711 80 : pCurr->SetAscent( nAsc );
1712 80 : pInf->GetParaPortion()->SetFixLineHeight();
1713 :
1714 : // we ignore any line spacing options except from ...
1715 80 : const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing();
1716 104 : if ( ! IsParaLine() && pSpace &&
1717 24 : SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() )
1718 : {
1719 0 : sal_uLong nTmp = pSpace->GetPropLineSpace();
1720 :
1721 0 : if( nTmp < 100 )
1722 0 : nTmp = 100;
1723 :
1724 0 : nTmp *= nLineHeight;
1725 0 : nLineHeight = (sal_uInt16)(nTmp / 100);
1726 : }
1727 :
1728 80 : pCurr->SetRealHeight( nLineHeight );
1729 180889 : return;
1730 : }
1731 :
1732 : // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese
1733 : // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere
1734 : // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem
1735 : // Shift-Return), die das Register durchaus beachten soll.
1736 382593 : if( !pCurr->IsDummy() || ( !pCurr->GetNext() &&
1737 42844 : GetStart() >= GetTxtFrm()->GetTxt().getLength() && !bNewLine ) )
1738 : {
1739 180253 : const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
1740 180253 : if( pSpace )
1741 : {
1742 180253 : switch( pSpace->GetLineSpaceRule() )
1743 : {
1744 : case SVX_LINE_SPACE_AUTO:
1745 : // shrink first line of paragraph too on spacing < 100%
1746 446917 : if (IsParaLine() &&
1747 111879 : pSpace->GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP
1748 185397 : && GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::PROP_LINE_SPACING_SHRINKS_FIRST_LINE))
1749 : {
1750 17570 : long nTmp = pSpace->GetPropLineSpace();
1751 : // Word will render < 50% too but it's just not readable
1752 17570 : if( nTmp < 50 )
1753 0 : nTmp = nTmp ? 50 : 100;
1754 17570 : if (nTmp<100) { // code adaped from fixed line height
1755 218 : nTmp *= nLineHeight;
1756 218 : nTmp /= 100;
1757 218 : if( !nTmp )
1758 0 : ++nTmp;
1759 218 : nLineHeight = (sal_uInt16)nTmp;
1760 218 : sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
1761 : #if 0
1762 : // could do clipping here (like Word does)
1763 : // but at 0.5 its unreadable either way...
1764 : if( nAsc < pCurr->GetAscent() ||
1765 : nLineHeight - nAsc < pCurr->Height() -
1766 : pCurr->GetAscent() )
1767 : pCurr->SetClipping( true );
1768 : #endif
1769 218 : pCurr->SetAscent( nAsc );
1770 218 : pCurr->Height( nLineHeight );
1771 218 : pInf->GetParaPortion()->SetFixLineHeight();
1772 : }
1773 : }
1774 167519 : break;
1775 : case SVX_LINE_SPACE_MIN:
1776 : {
1777 6686 : if( nLineHeight < sal_uInt16( pSpace->GetLineHeight() ) )
1778 3148 : nLineHeight = pSpace->GetLineHeight();
1779 6686 : break;
1780 : }
1781 : case SVX_LINE_SPACE_FIX:
1782 : {
1783 6048 : nLineHeight = pSpace->GetLineHeight();
1784 6048 : const sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
1785 9562 : if( nAsc < pCurr->GetAscent() ||
1786 3514 : nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() )
1787 2702 : pCurr->SetClipping( true );
1788 6048 : pCurr->Height( nLineHeight );
1789 6048 : pCurr->SetAscent( nAsc );
1790 6048 : pInf->GetParaPortion()->SetFixLineHeight();
1791 : }
1792 6048 : break;
1793 : default: OSL_FAIL( ": unknown LineSpaceRule" );
1794 : }
1795 : // Note: for the _first_ line the line spacing of the previous
1796 : // paragraph is applied in SwFlowFrm::CalcUpperSpace()
1797 180253 : if( !IsParaLine() )
1798 58580 : switch( pSpace->GetInterLineSpaceRule() )
1799 : {
1800 : case SVX_INTER_LINE_SPACE_OFF:
1801 52656 : break;
1802 : case SVX_INTER_LINE_SPACE_PROP:
1803 : {
1804 5924 : long nTmp = pSpace->GetPropLineSpace();
1805 : // 50% ist das Minimum, bei 0% schalten wir auf
1806 : // den Defaultwert 100% um ...
1807 5924 : if( nTmp < 50 )
1808 0 : nTmp = nTmp ? 50 : 100;
1809 :
1810 5924 : nTmp *= nLineHeight;
1811 5924 : nTmp /= 100;
1812 5924 : if( !nTmp )
1813 0 : ++nTmp;
1814 5924 : nLineHeight = (sal_uInt16)nTmp;
1815 5924 : break;
1816 : }
1817 : case SVX_INTER_LINE_SPACE_FIX:
1818 : {
1819 0 : nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
1820 0 : break;
1821 : }
1822 : default: OSL_FAIL( ": unknown InterLineSpaceRule" );
1823 : }
1824 : }
1825 : #if OSL_DEBUG_LEVEL > 1
1826 : sal_uInt16 nDummy = nLineHeight + 1;
1827 : (void)nDummy;
1828 : #endif
1829 :
1830 180253 : if( IsRegisterOn() )
1831 : {
1832 0 : SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height();
1833 0 : SWRECTFN( pFrm )
1834 0 : if ( bVert )
1835 0 : nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY );
1836 0 : nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() );
1837 0 : const sal_uInt16 nDiff = sal_uInt16( nTmpY % RegDiff() );
1838 0 : if( nDiff )
1839 0 : nLineHeight += RegDiff() - nDiff;
1840 : }
1841 : }
1842 180729 : pCurr->SetRealHeight( nLineHeight );
1843 : }
1844 :
1845 143092 : void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const
1846 : {
1847 : // 3260, 3860: Fly auf jeden Fall loeschen!
1848 143092 : ClearFly( rInf );
1849 143092 : rInf.Init();
1850 :
1851 143092 : rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
1852 143092 : rInf.SetRoot( pCurr );
1853 143092 : rInf.SetLineStart( nStart );
1854 143092 : rInf.SetIdx( nStart );
1855 :
1856 : // Handle overflows:
1857 : // #i34348# Changed type from sal_uInt16 to SwTwips
1858 143092 : SwTwips nTmpLeft = Left();
1859 143092 : SwTwips nTmpRight = Right();
1860 143092 : SwTwips nTmpFirst = FirstLeft();
1861 :
1862 143092 : if ( nTmpLeft > USHRT_MAX ||
1863 143092 : nTmpRight > USHRT_MAX ||
1864 : nTmpFirst > USHRT_MAX )
1865 : {
1866 0 : SWRECTFN( rInf.GetTxtFrm() )
1867 0 : nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)();
1868 0 : nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)();
1869 0 : nTmpFirst = nTmpLeft;
1870 : }
1871 :
1872 143092 : rInf.Left( nTmpLeft );
1873 143092 : rInf.Right( nTmpRight );
1874 143092 : rInf.First( nTmpFirst );
1875 :
1876 143092 : rInf.RealWidth( sal_uInt16(rInf.Right() - GetLeftMargin()) );
1877 143092 : rInf.Width( rInf.RealWidth() );
1878 143092 : if( ((SwTxtFormatter*)this)->GetRedln() )
1879 : {
1880 985 : ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() );
1881 985 : ((SwTxtFormatter*)this)->GetRedln()->Reset();
1882 : }
1883 143092 : }
1884 :
1885 142838 : void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf )
1886 : {
1887 142838 : pCurr->Truncate();
1888 142838 : pCurr->Init();
1889 142838 : if( pBlink && pCurr->IsBlinking() )
1890 0 : pBlink->Delete( pCurr );
1891 :
1892 : // delete pSpaceAdd und pKanaComp
1893 142838 : pCurr->FinishSpaceAdd();
1894 142838 : pCurr->FinishKanaComp();
1895 142838 : pCurr->ResetFlags();
1896 142838 : FeedInf( rInf );
1897 142838 : }
1898 :
1899 24 : bool SwTxtFormatter::CalcOnceMore()
1900 : {
1901 24 : if( pDropFmt )
1902 : {
1903 24 : const sal_uInt16 nOldDrop = GetDropHeight();
1904 24 : CalcDropHeight( pDropFmt->GetLines() );
1905 24 : bOnceMore = nOldDrop != GetDropHeight();
1906 : }
1907 : else
1908 0 : bOnceMore = false;
1909 24 : return bOnceMore;
1910 : }
1911 :
1912 91923 : SwTwips SwTxtFormatter::CalcBottomLine() const
1913 : {
1914 91923 : SwTwips nRet = Y() + GetLineHeight();
1915 91923 : SwTwips nMin = GetInfo().GetTxtFly().GetMinBottom();
1916 91923 : if( nMin && ++nMin > nRet )
1917 : {
1918 0 : SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height()
1919 0 : - pFrm->Prt().Top();
1920 0 : if( nRet + nDist < nMin )
1921 : {
1922 0 : const bool bRepaint = HasTruncLines() &&
1923 0 : GetInfo().GetParaPortion()->GetRepaint().Bottom() == nRet-1;
1924 0 : nRet = nMin - nDist;
1925 0 : if( bRepaint )
1926 : {
1927 0 : ((SwRepaint&)GetInfo().GetParaPortion()
1928 0 : ->GetRepaint()).Bottom( nRet-1 );
1929 0 : ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 );
1930 : }
1931 : }
1932 : }
1933 91923 : return nRet;
1934 : }
1935 :
1936 : // FME/OD: This routine does a limited text formatting.
1937 5380 : SwTwips SwTxtFormatter::_CalcFitToContent()
1938 : {
1939 5380 : FormatReset( GetInfo() );
1940 5380 : BuildPortions( GetInfo() );
1941 5380 : pCurr->CalcLine( *this, GetInfo() );
1942 5380 : return pCurr->Width();
1943 : }
1944 :
1945 : // determines if the calculation of a repaint offset is allowed
1946 : // otherwise each line is painted from 0 (this is a copy of the beginning
1947 : // of the former SwTxtFormatter::Recycle() function
1948 136772 : bool SwTxtFormatter::AllowRepaintOpt() const
1949 : {
1950 : // reformat position in front of current line? Only in this case
1951 : // we want to set the repaint offset
1952 141405 : bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() &&
1953 141405 : pCurr->GetLen();
1954 :
1955 : // a special case is the last line of a block adjusted paragraph:
1956 136772 : if ( bOptimizeRepaint )
1957 : {
1958 4633 : switch( GetAdjust() )
1959 : {
1960 : case SVX_ADJUST_BLOCK:
1961 : {
1962 2 : if( IsLastBlock() || IsLastCenter() )
1963 0 : bOptimizeRepaint = false;
1964 : else
1965 : {
1966 : // ????: Blank in der letzten Masterzeile (blocksat.sdw)
1967 2 : bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow();
1968 2 : if ( bOptimizeRepaint )
1969 : {
1970 2 : SwLinePortion *pPos = pCurr->GetFirstPortion();
1971 14 : while ( pPos && !pPos->IsFlyPortion() )
1972 10 : pPos = pPos->GetPortion();
1973 2 : bOptimizeRepaint = !pPos;
1974 : }
1975 : }
1976 2 : break;
1977 : }
1978 : case SVX_ADJUST_CENTER:
1979 : case SVX_ADJUST_RIGHT:
1980 90 : bOptimizeRepaint = false;
1981 90 : break;
1982 : default: ;
1983 : }
1984 : }
1985 :
1986 : // Schon wieder ein Sonderfall: unsichtbare SoftHyphs
1987 136772 : const sal_Int32 nReformat = GetInfo().GetReformatStart();
1988 136772 : if( bOptimizeRepaint && COMPLETE_STRING != nReformat )
1989 : {
1990 4543 : const sal_Unicode cCh = nReformat >= GetInfo().GetTxt().getLength() ? 0 : GetInfo().GetTxt()[ nReformat ];
1991 3437 : bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
1992 5715 : || ! GetInfo().HasHint( nReformat );
1993 : }
1994 :
1995 136772 : return bOptimizeRepaint;
1996 : }
1997 :
1998 1458 : void SwTxtFormatter::CalcUnclipped( SwTwips& rTop, SwTwips& rBottom )
1999 : {
2000 : OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(),
2001 : "SwTxtFormatter::CalcUnclipped with unswapped frame" );
2002 :
2003 : long nFlyAsc, nFlyDesc;
2004 1458 : pCurr->MaxAscentDescent( rTop, rBottom, nFlyAsc, nFlyDesc );
2005 1458 : rTop = Y() + GetCurr()->GetAscent();
2006 1458 : rBottom = rTop + nFlyDesc;
2007 1458 : rTop -= nFlyAsc;
2008 1458 : }
2009 :
2010 99962 : void SwTxtFormatter::UpdatePos( SwLineLayout *pCurrent, Point aStart,
2011 : sal_Int32 nStartIdx, bool bAlways ) const
2012 : {
2013 : OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(),
2014 : "SwTxtFormatter::UpdatePos with unswapped frame" );
2015 :
2016 99962 : if( GetInfo().IsTest() )
2017 99962 : return;
2018 99962 : SwLinePortion *pFirst = pCurrent->GetFirstPortion();
2019 99962 : SwLinePortion *pPos = pFirst;
2020 99962 : SwTxtPaintInfo aTmpInf( GetInfo() );
2021 99962 : aTmpInf.SetpSpaceAdd( pCurrent->GetpLLSpaceAdd() );
2022 99962 : aTmpInf.ResetSpaceIdx();
2023 99962 : aTmpInf.SetKanaComp( pCurrent->GetpKanaComp() );
2024 99962 : aTmpInf.ResetKanaIdx();
2025 :
2026 : // The frame's size
2027 99962 : aTmpInf.SetIdx( nStartIdx );
2028 99962 : aTmpInf.SetPos( aStart );
2029 :
2030 : long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
2031 99962 : pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
2032 :
2033 99962 : const sal_uInt16 nTmpHeight = pCurrent->GetRealHeight();
2034 99962 : sal_uInt16 nAscent = pCurrent->GetAscent() + nTmpHeight - pCurrent->Height();
2035 99962 : objectpositioning::AsCharFlags nFlags = AS_CHAR_ULSPACE;
2036 99962 : if( GetMulti() )
2037 : {
2038 0 : aTmpInf.SetDirection( GetMulti()->GetDirection() );
2039 0 : if( GetMulti()->HasRotation() )
2040 : {
2041 0 : nFlags |= AS_CHAR_ROTATE;
2042 0 : if( GetMulti()->IsRevers() )
2043 : {
2044 0 : nFlags |= AS_CHAR_REVERSE;
2045 0 : aTmpInf.X( aTmpInf.X() - nAscent );
2046 : }
2047 : else
2048 0 : aTmpInf.X( aTmpInf.X() + nAscent );
2049 : }
2050 : else
2051 : {
2052 0 : if ( GetMulti()->IsBidi() )
2053 0 : nFlags |= AS_CHAR_BIDI;
2054 0 : aTmpInf.Y( aTmpInf.Y() + nAscent );
2055 : }
2056 : }
2057 : else
2058 99962 : aTmpInf.Y( aTmpInf.Y() + nAscent );
2059 :
2060 367509 : while( pPos )
2061 : {
2062 : // We only know one case where changing the position (caused by the
2063 : // adjustment) could be relevant for a portion: We need to SetRefPoint
2064 : // for FlyCntPortions.
2065 498487 : if( ( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() )
2066 171923 : && ( bAlways || !IsQuick() ) )
2067 : {
2068 4338 : pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos );
2069 :
2070 4338 : if( pPos->IsGrfNumPortion() )
2071 : {
2072 70 : if( !nFlyAsc && !nFlyDesc )
2073 : {
2074 0 : nTmpAscent = nAscent;
2075 0 : nFlyAsc = nAscent;
2076 0 : nTmpDescent = nTmpHeight - nAscent;
2077 0 : nFlyDesc = nTmpDescent;
2078 : }
2079 : ((SwGrfNumPortion*)pPos)->SetBase( nTmpAscent, nTmpDescent,
2080 70 : nFlyAsc, nFlyDesc );
2081 : }
2082 : else
2083 : {
2084 4268 : Point aBase( aTmpInf.GetPos() );
2085 4268 : if ( GetInfo().GetTxtFrm()->IsVertical() )
2086 0 : GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aBase );
2087 :
2088 4268 : ((SwFlyCntPortion*)pPos)->SetBase( *aTmpInf.GetTxtFrm(),
2089 : aBase, nTmpAscent, nTmpDescent, nFlyAsc,
2090 8536 : nFlyDesc, nFlags );
2091 : }
2092 : }
2093 167585 : if( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasFlyInCntnt() )
2094 : {
2095 : OSL_ENSURE( !GetMulti(), "Too much multi" );
2096 0 : ((SwTxtFormatter*)this)->pMulti = (SwMultiPortion*)pPos;
2097 0 : SwLineLayout *pLay = &GetMulti()->GetRoot();
2098 0 : Point aSt( aTmpInf.X(), aStart.Y() );
2099 :
2100 0 : if ( GetMulti()->HasBrackets() )
2101 : {
2102 : OSL_ENSURE( GetMulti()->IsDouble(), "Brackets only for doubles");
2103 0 : aSt.X() += ((SwDoubleLinePortion*)GetMulti())->PreWidth();
2104 : }
2105 0 : else if( GetMulti()->HasRotation() )
2106 : {
2107 0 : aSt.Y() += pCurrent->GetAscent() - GetMulti()->GetAscent();
2108 0 : if( GetMulti()->IsRevers() )
2109 0 : aSt.X() += GetMulti()->Width();
2110 : else
2111 0 : aSt.Y() += GetMulti()->Height();
2112 : }
2113 0 : else if ( GetMulti()->IsBidi() )
2114 : // jump to end of the bidi portion
2115 0 : aSt.X() += pLay->Width();
2116 :
2117 0 : sal_Int32 nStIdx = aTmpInf.GetIdx();
2118 0 : do
2119 : {
2120 0 : UpdatePos( pLay, aSt, nStIdx, bAlways );
2121 0 : nStIdx = nStIdx + pLay->GetLen();
2122 0 : aSt.Y() += pLay->Height();
2123 0 : pLay = pLay->GetNext();
2124 : } while ( pLay );
2125 0 : ((SwTxtFormatter*)this)->pMulti = NULL;
2126 : }
2127 167585 : pPos->Move( aTmpInf );
2128 167585 : pPos = pPos->GetPortion();
2129 99962 : }
2130 : }
2131 :
2132 3518 : void SwTxtFormatter::AlignFlyInCntBase( long nBaseLine ) const
2133 : {
2134 : OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(),
2135 : "SwTxtFormatter::AlignFlyInCntBase with unswapped frame" );
2136 :
2137 3518 : if( GetInfo().IsTest() )
2138 3518 : return;
2139 3518 : SwLinePortion *pFirst = pCurr->GetFirstPortion();
2140 3518 : SwLinePortion *pPos = pFirst;
2141 3518 : objectpositioning::AsCharFlags nFlags = AS_CHAR_NOFLAG;
2142 3518 : if( GetMulti() && GetMulti()->HasRotation() )
2143 : {
2144 0 : nFlags |= AS_CHAR_ROTATE;
2145 0 : if( GetMulti()->IsRevers() )
2146 0 : nFlags |= AS_CHAR_REVERSE;
2147 : }
2148 :
2149 : long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
2150 :
2151 12112 : while( pPos )
2152 : {
2153 5076 : if( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() )
2154 : {
2155 3864 : pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos );
2156 :
2157 3864 : if( pPos->IsGrfNumPortion() )
2158 : ((SwGrfNumPortion*)pPos)->SetBase( nTmpAscent, nTmpDescent,
2159 0 : nFlyAsc, nFlyDesc );
2160 : else
2161 : {
2162 3864 : Point aBase;
2163 3864 : if ( GetInfo().GetTxtFrm()->IsVertical() )
2164 : {
2165 0 : nBaseLine = GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( nBaseLine );
2166 0 : aBase = Point( nBaseLine, ((SwFlyCntPortion*)pPos)->GetRefPoint().Y() );
2167 : }
2168 : else
2169 3864 : aBase = Point( ((SwFlyCntPortion*)pPos)->GetRefPoint().X(), nBaseLine );
2170 :
2171 3864 : ((SwFlyCntPortion*)pPos)->SetBase( *GetInfo().GetTxtFrm(), aBase, nTmpAscent, nTmpDescent,
2172 7728 : nFlyAsc, nFlyDesc, nFlags );
2173 : }
2174 : }
2175 5076 : pPos = pPos->GetPortion();
2176 : }
2177 : }
2178 :
2179 14152 : bool SwTxtFormatter::ChkFlyUnderflow( SwTxtFormatInfo &rInf ) const
2180 : {
2181 : OSL_ENSURE( rInf.GetTxtFly().IsOn(), "SwTxtFormatter::ChkFlyUnderflow: why?" );
2182 14152 : if( GetCurr() )
2183 : {
2184 : // First we check, whether a fly overlaps with the line.
2185 : // = GetLineHeight()
2186 14152 : const sal_uInt16 nHeight = GetCurr()->GetRealHeight();
2187 14152 : SwRect aLine( GetLeftMargin(), Y(), rInf.RealWidth(), nHeight );
2188 :
2189 14152 : SwRect aLineVert( aLine );
2190 14152 : if ( pFrm->IsVertical() )
2191 0 : pFrm->SwitchHorizontalToVertical( aLineVert );
2192 14152 : SwRect aInter( rInf.GetTxtFly().GetFrm( aLineVert ) );
2193 14152 : if ( pFrm->IsVertical() )
2194 0 : pFrm->SwitchVerticalToHorizontal( aInter );
2195 :
2196 14152 : if( !aInter.HasArea() )
2197 25440 : return false;
2198 :
2199 : // We now check every portion that could have lowered for overlapping
2200 : // with the fly.
2201 1436 : const SwLinePortion *pPos = GetCurr()->GetFirstPortion();
2202 1436 : aLine.Pos().Y() = Y() + GetCurr()->GetRealHeight() - GetCurr()->Height();
2203 1436 : aLine.Height( GetCurr()->Height() );
2204 :
2205 5538 : while( pPos )
2206 : {
2207 2674 : aLine.Width( pPos->Width() );
2208 :
2209 2674 : aLineVert = aLine;
2210 2674 : if ( pFrm->IsVertical() )
2211 0 : pFrm->SwitchHorizontalToVertical( aLineVert );
2212 2674 : aInter = rInf.GetTxtFly().GetFrm( aLineVert );
2213 2674 : if ( pFrm->IsVertical() )
2214 0 : pFrm->SwitchVerticalToHorizontal( aInter );
2215 :
2216 : // New flys from below?
2217 2674 : if( !pPos->IsFlyPortion() )
2218 : {
2219 1866 : if( aInter.IsOver( aLine ) )
2220 : {
2221 2 : aInter._Intersection( aLine );
2222 2 : if( aInter.HasArea() )
2223 : {
2224 : // To be evaluated during reformat of this line:
2225 : // RealHeight including spacing
2226 2 : rInf.SetLineHeight( nHeight );
2227 : // Height without extra spacing
2228 2 : rInf.SetLineNetHeight( pCurr->Height() );
2229 2 : return true;
2230 : }
2231 : }
2232 : }
2233 : else
2234 : {
2235 : // The fly portion is not intersected by a fly anymore
2236 808 : if ( ! aInter.IsOver( aLine ) )
2237 : {
2238 0 : rInf.SetLineHeight( nHeight );
2239 0 : rInf.SetLineNetHeight( pCurr->Height() );
2240 0 : return true;
2241 : }
2242 : else
2243 : {
2244 808 : aInter._Intersection( aLine );
2245 :
2246 : // No area means a fly has become invalid because of
2247 : // lowering the line => reformat the line
2248 : // we also have to reformat the line, if the fly size
2249 : // differs from the intersection interval's size.
2250 1616 : if( ! aInter.HasArea() ||
2251 808 : ((SwFlyPortion*)pPos)->GetFixWidth() != aInter.Width() )
2252 : {
2253 6 : rInf.SetLineHeight( nHeight );
2254 6 : rInf.SetLineNetHeight( pCurr->Height() );
2255 6 : return true;
2256 : }
2257 : }
2258 : }
2259 :
2260 2666 : aLine.Left( aLine.Left() + pPos->Width() );
2261 2666 : pPos = pPos->GetPortion();
2262 : }
2263 : }
2264 1428 : return false;
2265 : }
2266 :
2267 434667 : void SwTxtFormatter::CalcFlyWidth( SwTxtFormatInfo &rInf )
2268 : {
2269 434667 : if( GetMulti() || rInf.GetFly() )
2270 357778 : return;
2271 :
2272 431455 : SwTxtFly& rTxtFly = rInf.GetTxtFly();
2273 431455 : if( !rTxtFly.IsOn() || rInf.IsIgnoreFly() )
2274 351354 : return;
2275 :
2276 80101 : const SwLinePortion *pLast = rInf.GetLast();
2277 :
2278 : long nAscent;
2279 80101 : long nTop = Y();
2280 : long nHeight;
2281 :
2282 80101 : if( rInf.GetLineHeight() )
2283 : {
2284 : // Real line height has already been calculated, we only have to
2285 : // search for intersections in the lower part of the strip
2286 20 : nAscent = pCurr->GetAscent();
2287 20 : nHeight = rInf.GetLineNetHeight();
2288 20 : nTop += rInf.GetLineHeight() - nHeight;
2289 : }
2290 : else
2291 : {
2292 80081 : nAscent = pLast->GetAscent();
2293 80081 : nHeight = pLast->Height();
2294 :
2295 : // We make a first guess for the lines real height
2296 80081 : if ( ! pCurr->GetRealHeight() )
2297 40201 : CalcRealHeight();
2298 :
2299 80081 : if ( pCurr->GetRealHeight() > nHeight )
2300 5476 : nTop += pCurr->GetRealHeight() - nHeight;
2301 : else
2302 : // Important for fixed space between lines
2303 74605 : nHeight = pCurr->GetRealHeight();
2304 : }
2305 :
2306 80101 : const long nLeftMar = GetLeftMargin();
2307 80101 : const long nLeftMin = (rInf.X() || GetDropLeft()) ? nLeftMar : GetLeftMin();
2308 :
2309 160202 : SwRect aLine( rInf.X() + nLeftMin, nTop, rInf.RealWidth() - rInf.X()
2310 160202 : + nLeftMar - nLeftMin , nHeight );
2311 :
2312 80101 : SwRect aLineVert( aLine );
2313 80101 : if ( pFrm->IsRightToLeft() )
2314 24 : pFrm->SwitchLTRtoRTL( aLineVert );
2315 :
2316 80101 : if ( pFrm->IsVertical() )
2317 0 : pFrm->SwitchHorizontalToVertical( aLineVert );
2318 80101 : SwRect aInter( rTxtFly.GetFrm( aLineVert ) );
2319 :
2320 80101 : if ( pFrm->IsRightToLeft() )
2321 24 : pFrm->SwitchRTLtoLTR( aInter );
2322 :
2323 80101 : if ( pFrm->IsVertical() )
2324 0 : pFrm->SwitchVerticalToHorizontal( aInter );
2325 :
2326 80101 : if( aInter.IsOver( aLine ) )
2327 : {
2328 2350 : aLine.Left( rInf.X() + nLeftMar );
2329 2350 : bool bForced = false;
2330 2350 : if( aInter.Left() <= nLeftMin )
2331 : {
2332 560 : SwTwips nFrmLeft = GetTxtFrm()->Frm().Left();
2333 560 : if( GetTxtFrm()->Prt().Left() < 0 )
2334 0 : nFrmLeft += GetTxtFrm()->Prt().Left();
2335 560 : if( aInter.Left() < nFrmLeft )
2336 450 : aInter.Left( nFrmLeft );
2337 :
2338 560 : long nAddMar = 0;
2339 560 : if ( pFrm->IsRightToLeft() )
2340 : {
2341 0 : nAddMar = pFrm->Frm().Right() - Right();
2342 0 : if ( nAddMar < 0 )
2343 0 : nAddMar = 0;
2344 : }
2345 : else
2346 560 : nAddMar = nLeftMar - nFrmLeft;
2347 :
2348 560 : aInter.Width( aInter.Width() + nAddMar );
2349 : // For a negative first line indent, we set this flag to show
2350 : // that the indentation/margin has been moved.
2351 : // This needs to be respected by the DefaultTab at the zero position.
2352 560 : if( IsFirstTxtLine() && HasNegFirst() )
2353 0 : bForced = true;
2354 : }
2355 2350 : aInter.Intersection( aLine );
2356 2350 : if( !aInter.HasArea() )
2357 0 : return;
2358 :
2359 2916 : const bool bFullLine = aLine.Left() == aInter.Left() &&
2360 2916 : aLine.Right() == aInter.Right();
2361 :
2362 : // Although no text is left, we need to format another line,
2363 : // because also empty lines need to avoid a Fly with no wrapping.
2364 2350 : if( bFullLine && rInf.GetIdx() == rInf.GetTxt().getLength() )
2365 : {
2366 92 : rInf.SetNewLine( true );
2367 : // 8221: We know that for dummies, it holds ascent == height
2368 92 : pCurr->SetDummy(true);
2369 : }
2370 :
2371 : // aInter becomes frame-local
2372 2350 : aInter.Pos().X() -= nLeftMar;
2373 2350 : SwFlyPortion *pFly = new SwFlyPortion( aInter );
2374 2350 : if( bForced )
2375 : {
2376 0 : pCurr->SetForcedLeftMargin( true );
2377 0 : rInf.ForcedLeftMargin( (sal_uInt16)aInter.Width() );
2378 : }
2379 :
2380 2350 : if( bFullLine )
2381 : {
2382 : // 8110: In order to properly flow around Flys with different
2383 : // wrapping attributes, we need to increase by units of line height.
2384 : // The last avoiding line should be adjusted in height, so that
2385 : // we don't get a frame spacing effect.
2386 : // 8221: It is important that ascent == height, because the FlyPortion
2387 : // values are transferred to pCurr in CalcLine and IsDummy() relies
2388 : // on this behaviour.
2389 : // To my knowledge we only have two places where DummyLines can be
2390 : // created: here and in MakeFlyDummies.
2391 : // IsDummy() is evaluated in IsFirstTxtLine(), when moving lines
2392 : // and in relation with DropCaps.
2393 168 : pFly->Height( sal_uInt16(aInter.Height()) );
2394 :
2395 : // nNextTop now contains the margin's bottom edge, which we avoid
2396 : // or the next margin's top edge, which we need to respect.
2397 : // That means we can comfortably grow up to this value; that's how
2398 : // we save a few empty lines.
2399 168 : long nNextTop = rTxtFly.GetNextTop();
2400 168 : if ( pFrm->IsVertical() )
2401 0 : nNextTop = pFrm->SwitchVerticalToHorizontal( nNextTop );
2402 168 : if( nNextTop > aInter.Bottom() )
2403 : {
2404 158 : SwTwips nH = nNextTop - aInter.Top();
2405 158 : if( nH < USHRT_MAX )
2406 158 : pFly->Height( sal_uInt16( nH ) );
2407 : }
2408 168 : if( nAscent < pFly->Height() )
2409 166 : pFly->SetAscent( sal_uInt16(nAscent) );
2410 : else
2411 2 : pFly->SetAscent( pFly->Height() );
2412 : }
2413 : else
2414 : {
2415 2182 : if( rInf.GetIdx() == rInf.GetTxt().getLength() )
2416 : {
2417 : // Don't use nHeight, or we have a huge descent
2418 934 : pFly->Height( pLast->Height() );
2419 934 : pFly->SetAscent( pLast->GetAscent() );
2420 : }
2421 : else
2422 : {
2423 1248 : pFly->Height( sal_uInt16(aInter.Height()) );
2424 1248 : if( nAscent < pFly->Height() )
2425 1102 : pFly->SetAscent( sal_uInt16(nAscent) );
2426 : else
2427 146 : pFly->SetAscent( pFly->Height() );
2428 : }
2429 : }
2430 :
2431 2350 : rInf.SetFly( pFly );
2432 :
2433 2350 : if( pFly->Fix() < rInf.Width() )
2434 1480 : rInf.Width( pFly->Fix() );
2435 :
2436 2350 : SwTextGridItem const*const pGrid(GetGridItem(pFrm->FindPageFrm()));
2437 2350 : if ( pGrid )
2438 : {
2439 0 : const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
2440 0 : const SwLayoutFrm* pBody = pPageFrm->FindBodyCont();
2441 :
2442 0 : SWRECTFN( pPageFrm )
2443 :
2444 : const long nGridOrigin = pBody ?
2445 0 : (pBody->*fnRect->fnGetPrtLeft)() :
2446 0 : (pPageFrm->*fnRect->fnGetPrtLeft)();
2447 :
2448 0 : const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
2449 0 : const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
2450 :
2451 0 : SwTwips nStartX = GetLeftMargin();
2452 0 : if ( bVert )
2453 : {
2454 0 : Point aPoint( nStartX, 0 );
2455 0 : pFrm->SwitchHorizontalToVertical( aPoint );
2456 0 : nStartX = aPoint.Y();
2457 : }
2458 :
2459 0 : const SwTwips nOfst = nStartX - nGridOrigin;
2460 0 : const SwTwips nTmpWidth = rInf.Width() + nOfst;
2461 :
2462 0 : const sal_uLong i = nTmpWidth / nGridWidth + 1;
2463 :
2464 0 : const long nNewWidth = ( i - 1 ) * nGridWidth - nOfst;
2465 0 : if ( nNewWidth > 0 )
2466 0 : rInf.Width( (sal_uInt16)nNewWidth );
2467 : else
2468 0 : rInf.Width( 0 );
2469 : }
2470 : }
2471 : }
2472 :
2473 4112 : SwFlyCntPortion *SwTxtFormatter::NewFlyCntPortion( SwTxtFormatInfo &rInf,
2474 : SwTxtAttr *pHint ) const
2475 : {
2476 4112 : SwFlyCntPortion *pRet = 0;
2477 4112 : const SwFrm *pFrame = (SwFrm*)pFrm;
2478 :
2479 : SwFlyInCntFrm *pFly;
2480 4112 : SwFrmFmt* pFrmFmt = ((SwTxtFlyCnt*)pHint)->GetFlyCnt().GetFrmFmt();
2481 4112 : if( RES_FLYFRMFMT == pFrmFmt->Which() )
2482 2530 : pFly = ((SwTxtFlyCnt*)pHint)->GetFlyFrm(pFrame);
2483 : else
2484 1582 : pFly = NULL;
2485 : // aBase is the document-global position, from which the new extra portion is placed
2486 : // aBase.X() = Offset in in the line after the current position
2487 : // aBase.Y() = LineIter.Y() + Ascent of the current position
2488 :
2489 : long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
2490 : // OD 08.01.2004 #i11859# - use new method <SwLineLayout::MaxAscentDescent(..)>
2491 : //SwLinePortion *pPos = pCurr->GetFirstPortion();
2492 : //lcl_MaxAscDescent( pPos, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
2493 4112 : pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
2494 :
2495 : // If the ascent of the frame is larger than the ascent of the current position,
2496 : // we use this one when calculating the base, or the frame would be positioned
2497 : // too much to the top, sliding down after all causing a repaint in an area
2498 : // he actually never was in.
2499 4112 : sal_uInt16 nAscent = 0;
2500 :
2501 4112 : const bool bTxtFrmVertical = GetInfo().GetTxtFrm()->IsVertical();
2502 :
2503 5153 : const bool bUseFlyAscent = pFly && pFly->GetValidPosFlag() &&
2504 2409 : 0 != ( bTxtFrmVertical ?
2505 0 : pFly->GetRefPoint().X() :
2506 6521 : pFly->GetRefPoint().Y() );
2507 :
2508 4112 : if ( bUseFlyAscent )
2509 : nAscent = static_cast<sal_uInt16>( std::abs( int( bTxtFrmVertical ?
2510 1041 : pFly->GetRelPos().X() :
2511 2082 : pFly->GetRelPos().Y() ) ) );
2512 :
2513 : // Check if be prefer to use the ascent of the last portion:
2514 12282 : if ( IsQuick() ||
2515 5127 : !bUseFlyAscent ||
2516 1015 : nAscent < rInf.GetLast()->GetAscent() )
2517 : {
2518 3119 : nAscent = rInf.GetLast()->GetAscent();
2519 : }
2520 993 : else if( nAscent > nFlyAsc )
2521 991 : nFlyAsc = nAscent;
2522 :
2523 4112 : Point aBase( GetLeftMargin() + rInf.X(), Y() + nAscent );
2524 4112 : objectpositioning::AsCharFlags nMode = IsQuick() ? AS_CHAR_QUICK : 0;
2525 4112 : if( GetMulti() && GetMulti()->HasRotation() )
2526 : {
2527 0 : nMode |= AS_CHAR_ROTATE;
2528 0 : if( GetMulti()->IsRevers() )
2529 0 : nMode |= AS_CHAR_REVERSE;
2530 : }
2531 :
2532 4112 : Point aTmpBase( aBase );
2533 4112 : if ( GetInfo().GetTxtFrm()->IsVertical() )
2534 0 : GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aTmpBase );
2535 :
2536 4112 : if( pFly )
2537 : {
2538 2530 : pRet = new SwFlyCntPortion( *GetInfo().GetTxtFrm(), pFly, aTmpBase,
2539 2530 : nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode );
2540 : // We need to make sure that our font is set again in the OutputDevice
2541 : // It could be that the FlyInCnt was added anew and GetFlyFrm() would
2542 : // in turn cause, that it'd be created anew again.
2543 : // This one's frames get formatted right away, which change the font and
2544 : // we have a bug (3322).
2545 2530 : rInf.SelectFont();
2546 2530 : if( pRet->GetAscent() > nAscent )
2547 : {
2548 1159 : aBase.Y() = Y() + pRet->GetAscent();
2549 1159 : nMode |= AS_CHAR_ULSPACE;
2550 1159 : if( !rInf.IsTest() )
2551 : {
2552 1117 : aTmpBase = aBase;
2553 1117 : if ( GetInfo().GetTxtFrm()->IsVertical() )
2554 0 : GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aTmpBase );
2555 :
2556 1117 : pRet->SetBase( *rInf.GetTxtFrm(), aTmpBase, nTmpAscent,
2557 2234 : nTmpDescent, nFlyAsc, nFlyDesc, nMode );
2558 : }
2559 : }
2560 : }
2561 : else
2562 : {
2563 1582 : pRet = new SwFlyCntPortion( *rInf.GetTxtFrm(), (SwDrawContact*)pFrmFmt->FindContactObj(),
2564 1582 : aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode );
2565 : }
2566 4112 : return pRet;
2567 : }
2568 :
2569 : /* Drop portion is a special case, because it has parts which aren't portions
2570 : but we have handle them just like portions */
2571 24 : void SwTxtFormatter::MergeCharacterBorder( SwDropPortion& rPortion )
2572 : {
2573 24 : if( rPortion.GetLines() > 1 )
2574 : {
2575 12 : SwDropPortionPart* pCurrPart = rPortion.GetPart();
2576 36 : while( pCurrPart )
2577 : {
2578 12 : if( pCurrPart->GetFollow() &&
2579 0 : ::lcl_HasSameBorder(pCurrPart->GetFont(), pCurrPart->GetFollow()->GetFont()) )
2580 : {
2581 0 : pCurrPart->SetJoinBorderWithNext(true);
2582 0 : pCurrPart->GetFollow()->SetJoinBorderWithPrev(true);
2583 : }
2584 12 : pCurrPart = pCurrPart->GetFollow();
2585 : }
2586 : }
2587 24 : }
2588 :
2589 206899 : void SwTxtFormatter::MergeCharacterBorder( SwLinePortion& rPortion, SwTxtFormatInfo& rInf )
2590 : {
2591 206899 : const SwFont aCurFont = *rInf.GetFont();
2592 206899 : if( aCurFont.HasBorder() )
2593 : {
2594 : // The current portion isn't inserted into the portion chain yet, so the info's
2595 : // last portion will be the previous one
2596 112 : if( rInf.GetLast() && rInf.GetLast() != &rPortion && // For para portion (special case)
2597 42 : rInf.GetLast()->GetJoinBorderWithNext() )
2598 : {
2599 : // In some case border merge is called twice to the portion
2600 10 : if( !rPortion.GetJoinBorderWithPrev() )
2601 : {
2602 10 : rPortion.SetJoinBorderWithPrev(true);
2603 10 : if( rPortion.InTxtGrp() && rPortion.Width() > aCurFont.GetLeftBorderSpace() )
2604 8 : rPortion.Width(rPortion.Width() - aCurFont.GetLeftBorderSpace());
2605 : }
2606 : }
2607 : else
2608 : {
2609 60 : rPortion.SetJoinBorderWithPrev(false);
2610 60 : m_pFirstOfBorderMerge = &rPortion;
2611 : }
2612 :
2613 : // Get next portion's font
2614 70 : bool bSeek = false;
2615 140 : if( !rInf.IsFull() && // Not the last portion of the line (in case of line break)
2616 70 : rInf.GetIdx() + rPortion.GetLen() != rInf.GetTxt().getLength() ) // Not the last portion of the paragraph
2617 28 : bSeek = Seek(rInf.GetIdx() + rPortion.GetLen());
2618 :
2619 : // If next portion has the same border then merge
2620 70 : if( bSeek && GetFnt()->HasBorder() && ::lcl_HasSameBorder(aCurFont, *GetFnt()) )
2621 : {
2622 : // In some case border merge is called twice to the portion
2623 10 : if( !rPortion.GetJoinBorderWithNext() )
2624 : {
2625 10 : rPortion.SetJoinBorderWithNext(true);
2626 10 : if( rPortion.InTxtGrp() && rPortion.Width() > aCurFont.GetRightBorderSpace() )
2627 0 : rPortion.Width(rPortion.Width() - aCurFont.GetRightBorderSpace());
2628 : }
2629 : }
2630 : // If this is the last portion of the merge group then make the real height merge
2631 : else
2632 : {
2633 60 : rPortion.SetJoinBorderWithNext(false);
2634 60 : if( m_pFirstOfBorderMerge != &rPortion )
2635 : {
2636 : // Calculate maximum height and ascent
2637 8 : SwLinePortion* pActPor = m_pFirstOfBorderMerge;
2638 8 : sal_uInt16 nMaxAscent = 0;
2639 8 : sal_uInt16 nMaxHeight = 0;
2640 8 : bool bReachCurrent = false;
2641 34 : while( pActPor )
2642 : {
2643 18 : if( nMaxHeight < pActPor->Height() )
2644 16 : nMaxHeight = pActPor->Height();
2645 18 : if( nMaxAscent < pActPor->GetAscent() )
2646 16 : nMaxAscent = pActPor->GetAscent();
2647 :
2648 18 : pActPor = pActPor->GetPortion();
2649 18 : if( !pActPor && !bReachCurrent )
2650 : {
2651 8 : pActPor = &rPortion;
2652 8 : bReachCurrent = true;
2653 : }
2654 : }
2655 :
2656 : // Change all portion's height and ascent
2657 8 : pActPor = m_pFirstOfBorderMerge;
2658 8 : bReachCurrent = false;
2659 34 : while( pActPor )
2660 : {
2661 18 : if( nMaxHeight > pActPor->Height() )
2662 10 : pActPor->Height(nMaxHeight);
2663 18 : if( nMaxAscent > pActPor->GetAscent() )
2664 10 : pActPor->SetAscent(nMaxAscent);
2665 :
2666 18 : pActPor = pActPor->GetPortion();
2667 18 : if( !pActPor && !bReachCurrent )
2668 : {
2669 8 : pActPor = &rPortion;
2670 8 : bReachCurrent = true;
2671 : }
2672 : }
2673 8 : m_pFirstOfBorderMerge = 0;
2674 : }
2675 : }
2676 70 : Seek(rInf.GetIdx());
2677 206899 : }
2678 206899 : }
2679 :
2680 : namespace {
2681 : // calculates and sets optimal repaint offset for the current line
2682 3371 : long lcl_CalcOptRepaint( SwTxtFormatter &rThis,
2683 : SwLineLayout &rCurr,
2684 : const sal_Int32 nOldLineEnd,
2685 : const std::vector<long> &rFlyStarts )
2686 : {
2687 3371 : SwTxtFormatInfo txtFmtInfo = rThis.GetInfo();
2688 3371 : if ( txtFmtInfo.GetIdx() < txtFmtInfo.GetReformatStart() )
2689 : // the reformat position is behind our new line, that means
2690 : // something of our text has moved to the next line
2691 156 : return 0;
2692 :
2693 3215 : sal_Int32 nReformat = std::min<sal_Int32>( txtFmtInfo.GetReformatStart(), nOldLineEnd );
2694 :
2695 : // in case we do not have any fly in our line, our repaint position
2696 : // is the changed position - 1
2697 3215 : if ( rFlyStarts.empty() && ! rCurr.IsFly() )
2698 : {
2699 : // this is the maximum repaint offset determined during formatting
2700 : // for example: the beginning of the first right tab stop
2701 : // if this value is 0, this means that we do not have an upper
2702 : // limit for the repaint offset
2703 3215 : const long nFormatRepaint = txtFmtInfo.GetPaintOfst();
2704 :
2705 3215 : if ( nReformat < txtFmtInfo.GetLineStart() + 3 )
2706 50 : return 0;
2707 :
2708 : // step back two positions for smoother repaint
2709 3165 : nReformat -= 2;
2710 :
2711 : #ifndef MACOSX
2712 : #if ! ENABLE_GRAPHITE
2713 : // #i28795#, #i34607#, #i38388#
2714 : // step back six(!) more characters for complex scripts
2715 : // this is required e.g., for Khmer (thank you, Javier!)
2716 : const SwScriptInfo& rSI = txtFmtInfo.GetParaPortion()->GetScriptInfo();
2717 : sal_Int32 nMaxContext = 0;
2718 : if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) )
2719 : nMaxContext = 6;
2720 : #else
2721 : // Some Graphite fonts need context for scripts not marked as complex
2722 : static const sal_Int32 nMaxContext = 10;
2723 : #endif
2724 : #else
2725 : // some fonts like Quartz's Zapfino need more context
2726 : // TODO: query FontInfo for maximum unicode context
2727 : static const sal_Int32 nMaxContext = 8;
2728 : #endif
2729 : if( nMaxContext > 0 )
2730 : {
2731 3165 : if ( nReformat > txtFmtInfo.GetLineStart() + nMaxContext )
2732 2817 : nReformat = nReformat - nMaxContext;
2733 : else
2734 348 : nReformat = txtFmtInfo.GetLineStart();
2735 : }
2736 :
2737 : // Weird situation: Our line used to end with a hole portion
2738 : // and we delete some characters at the end of our line. We have
2739 : // to take care for repainting the blanks which are not anymore
2740 : // covered by the hole portion
2741 9275 : while ( nReformat > txtFmtInfo.GetLineStart() &&
2742 2881 : CH_BLANK == txtFmtInfo.GetChar( nReformat ) )
2743 64 : --nReformat;
2744 :
2745 : OSL_ENSURE( nReformat < txtFmtInfo.GetIdx(), "Reformat too small for me!" );
2746 3165 : SwRect aRect;
2747 :
2748 : // Note: GetChareRect is not const. It definitely changes the
2749 : // bMulti flag. We have to save and resore the old value.
2750 3165 : bool bOldMulti = txtFmtInfo.IsMulti();
2751 3165 : rThis.GetCharRect( &aRect, nReformat );
2752 3165 : txtFmtInfo.SetMulti( bOldMulti );
2753 :
2754 3357 : return nFormatRepaint ? std::min( aRect.Left(), nFormatRepaint ) :
2755 6394 : aRect.Left();
2756 : }
2757 : else
2758 : {
2759 : // nReformat may be wrong, if something around flys has changed:
2760 : // we compare the former and the new fly positions in this line
2761 : // if anything has changed, we carefully have to adjust the right
2762 : // repaint position
2763 0 : long nPOfst = 0;
2764 0 : size_t nCnt = 0;
2765 0 : long nX = 0;
2766 0 : sal_Int32 nIdx = rThis.GetInfo().GetLineStart();
2767 0 : SwLinePortion* pPor = rCurr.GetFirstPortion();
2768 :
2769 0 : while ( pPor )
2770 : {
2771 0 : if ( pPor->IsFlyPortion() )
2772 : {
2773 : // compare start of fly with former start of fly
2774 0 : if (nCnt < rFlyStarts.size() &&
2775 0 : nX == rFlyStarts[ nCnt ] &&
2776 : nIdx < nReformat
2777 : )
2778 : // found fix position, nothing has changed left from nX
2779 0 : nPOfst = nX + pPor->Width();
2780 : else
2781 0 : break;
2782 :
2783 0 : nCnt++;
2784 : }
2785 0 : nX = nX + pPor->Width();
2786 0 : nIdx = nIdx + pPor->GetLen();
2787 0 : pPor = pPor->GetPortion();
2788 : }
2789 :
2790 0 : return nPOfst + rThis.GetLeftMargin();
2791 3371 : }
2792 : }
2793 :
2794 : // Determine if we need to build hidden portions
2795 274037 : bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, sal_Int32 &rPos )
2796 : {
2797 : // Only if hidden text should not be shown:
2798 : // if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
2799 274037 : const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar();
2800 274037 : const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( true ) && rInf.GetOpt().IsPrinting();
2801 274037 : if (bShowInDocView || bShowForPrinting)
2802 0 : return false;
2803 :
2804 274037 : const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
2805 : sal_Int32 nHiddenStart;
2806 : sal_Int32 nHiddenEnd;
2807 274037 : rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
2808 274037 : if ( nHiddenEnd )
2809 : {
2810 8 : rPos = nHiddenEnd;
2811 8 : return true;
2812 : }
2813 :
2814 274029 : return false;
2815 : }
2816 :
2817 10 : bool lcl_HasSameBorder(const SwFont& rFirst, const SwFont& rSecond)
2818 : {
2819 : return
2820 20 : rFirst.GetTopBorder() == rSecond.GetTopBorder() &&
2821 20 : rFirst.GetBottomBorder() == rSecond.GetBottomBorder() &&
2822 20 : rFirst.GetLeftBorder() == rSecond.GetLeftBorder() &&
2823 20 : rFirst.GetRightBorder() == rSecond.GetRightBorder() &&
2824 20 : rFirst.GetTopBorderDist() == rSecond.GetTopBorderDist() &&
2825 20 : rFirst.GetBottomBorderDist() == rSecond.GetBottomBorderDist() &&
2826 20 : rFirst.GetLeftBorderDist() == rSecond.GetLeftBorderDist() &&
2827 20 : rFirst.GetRightBorderDist() == rSecond.GetRightBorderDist() &&
2828 20 : rFirst.GetOrientation() == rSecond.GetOrientation() &&
2829 20 : rFirst.GetShadowColor() == rSecond.GetShadowColor() &&
2830 30 : rFirst.GetShadowWidth() == rSecond.GetShadowWidth() &&
2831 20 : rFirst.GetShadowLocation() == rSecond.GetShadowLocation();
2832 : }
2833 :
2834 270 : } //end unnamed namespace
2835 :
2836 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|