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