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