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 <com/sun/star/i18n/ScriptType.hpp>
21 : #include <vcl/outdev.hxx>
22 : #include <IDocumentSettingAccess.hxx>
23 :
24 : #include "frame.hxx"
25 : #include "paratr.hxx"
26 : #include "itrtxt.hxx"
27 : #include "porglue.hxx"
28 : #include "porlay.hxx"
29 : #include "porfly.hxx"
30 : #include "pordrop.hxx"
31 : #include "pormulti.hxx"
32 : #include <portab.hxx>
33 :
34 : #define MIN_TAB_WIDTH 60
35 :
36 : using namespace ::com::sun::star;
37 :
38 1206 : void SwTxtAdjuster::FormatBlock( )
39 : {
40 : // Block format does not apply to the last line.
41 : // And for tabs it doesn't exist out of tradition
42 : // If we have Flys we continue.
43 :
44 1206 : const SwLinePortion *pFly = 0;
45 :
46 2410 : bool bSkip = !IsLastBlock() &&
47 2410 : nStart + pCurr->GetLen() >= GetInfo().GetTxt().getLength();
48 :
49 : // Multi-line fields are tricky, because we need to check whether there are
50 : // any other text portions in the paragraph.
51 1206 : if( bSkip )
52 : {
53 616 : const SwLineLayout *pLay = pCurr->GetNext();
54 1232 : while( pLay && !pLay->GetLen() )
55 : {
56 0 : const SwLinePortion *pPor = pCurr->GetFirstPortion();
57 0 : while( pPor && bSkip )
58 : {
59 0 : if( pPor->InTxtGrp() )
60 0 : bSkip = false;
61 0 : pPor = pPor->GetPortion();
62 : }
63 0 : pLay = bSkip ? pLay->GetNext() : 0;
64 : }
65 : }
66 :
67 1206 : if( bSkip )
68 : {
69 616 : if( !GetInfo().GetParaPortion()->HasFly() )
70 : {
71 556 : if( IsLastCenter() )
72 0 : CalcFlyAdjust( pCurr );
73 556 : pCurr->FinishSpaceAdd();
74 556 : return;
75 : }
76 : else
77 : {
78 60 : const SwLinePortion *pTmpFly = NULL;
79 :
80 : // End at the last Fly
81 60 : const SwLinePortion *pPos = pCurr->GetFirstPortion();
82 288 : while( pPos )
83 : {
84 : // Look for the last Fly which has text coming after it:
85 168 : if( pPos->IsFlyPortion() )
86 0 : pTmpFly = pPos; // Found a Fly
87 168 : else if ( pTmpFly && pPos->InTxtGrp() )
88 : {
89 0 : pFly = pTmpFly; // A Fly with follow-up text!
90 0 : pTmpFly = NULL;
91 : }
92 168 : pPos = pPos->GetPortion();
93 : }
94 : // End if we didn't find one
95 60 : if( !pFly )
96 : {
97 60 : if( IsLastCenter() )
98 0 : CalcFlyAdjust( pCurr );
99 60 : pCurr->FinishSpaceAdd();
100 60 : return;
101 : }
102 : }
103 : }
104 :
105 590 : const sal_Int32 nOldIdx = GetInfo().GetIdx();
106 590 : GetInfo().SetIdx( nStart );
107 590 : CalcNewBlock( pCurr, pFly );
108 590 : GetInfo().SetIdx( nOldIdx );
109 590 : GetInfo().GetParaPortion()->GetRepaint().SetOfst(0);
110 : }
111 :
112 0 : static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr,
113 : sal_Int32& rKashidas, sal_Int32& nGluePortion )
114 : {
115 : // i60594 validate Kashida justification
116 0 : sal_Int32 nIdx = rItr.GetStart();
117 0 : sal_Int32 nEnd = rItr.GetEnd();
118 :
119 : // Note on calling KashidaJustify():
120 : // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
121 : // total number of kashida positions, or the number of kashida positions after some positions
122 : // have been dropped.
123 : // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
124 0 : rKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 );
125 :
126 0 : if (rKashidas <= 0) // nothing to do
127 0 : return true;
128 :
129 : // kashida positions found in SwScriptInfo are not necessarily valid in every font
130 : // if two characters are replaced by a ligature glyph, there will be no place for a kashida
131 0 : sal_Int32* pKashidaPos = new sal_Int32[ rKashidas ];
132 0 : sal_Int32* pKashidaPosDropped = new sal_Int32[ rKashidas ];
133 0 : rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos );
134 0 : sal_Int32 nKashidaIdx = 0;
135 0 : while ( rKashidas && nIdx < nEnd )
136 : {
137 0 : rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
138 0 : sal_Int32 nNext = rItr.GetNextAttr();
139 :
140 : // is there also a script change before?
141 : // if there is, nNext should point to the script change
142 0 : sal_Int32 nNextScript = rSI.NextScriptChg( nIdx );
143 0 : if( nNextScript < nNext )
144 0 : nNext = nNextScript;
145 :
146 0 : if ( nNext == COMPLETE_STRING || nNext > nEnd )
147 0 : nNext = nEnd;
148 0 : sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
149 0 : if (nKashidasInAttr > 0)
150 : {
151 : // Kashida glyph looks suspicious, skip Kashida justification
152 0 : if ( rInf.GetOut()->GetMinKashida() <= 0 )
153 : {
154 0 : delete[] pKashidaPos;
155 0 : delete[] pKashidaPosDropped;
156 0 : return false;
157 : }
158 :
159 0 : sal_Int32 nKashidasDropped = 0;
160 0 : if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
161 : {
162 0 : nKashidasDropped = nKashidasInAttr;
163 0 : rKashidas -= nKashidasDropped;
164 : }
165 : else
166 : {
167 0 : ComplexTextLayoutMode nOldLayout = rInf.GetOut()->GetLayoutMode();
168 0 : rInf.GetOut()->SetLayoutMode ( nOldLayout | TEXT_LAYOUT_BIDI_RTL );
169 0 : nKashidasDropped = rInf.GetOut()->ValidateKashidas ( rInf.GetTxt(), nIdx, nNext - nIdx,
170 : nKashidasInAttr, pKashidaPos + nKashidaIdx,
171 0 : pKashidaPosDropped );
172 0 : rInf.GetOut()->SetLayoutMode ( nOldLayout );
173 0 : if ( nKashidasDropped )
174 : {
175 0 : rSI.MarkKashidasInvalid(nKashidasDropped, pKashidaPosDropped);
176 0 : rKashidas -= nKashidasDropped;
177 0 : nGluePortion -= nKashidasDropped;
178 : }
179 : }
180 0 : nKashidaIdx += nKashidasInAttr;
181 : }
182 0 : nIdx = nNext;
183 : }
184 0 : delete[] pKashidaPos;
185 0 : delete[] pKashidaPosDropped;
186 :
187 : // return false if all kashidas have been eliminated
188 0 : return (rKashidas > 0);
189 : }
190 :
191 0 : static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, sal_Int32& rKashidas,
192 : sal_Int32& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd )
193 : {
194 : // check kashida width
195 : // if width is smaller than minimal kashida width allowed by fonts in the current line
196 : // drop one kashida after the other until kashida width is OK
197 : bool bAddSpaceChanged;
198 0 : while (rKashidas)
199 : {
200 0 : bAddSpaceChanged = false;
201 0 : sal_Int32 nIdx = rItr.GetStart();
202 0 : sal_Int32 nEnd = rItr.GetEnd();
203 0 : while ( nIdx < nEnd )
204 : {
205 0 : rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
206 0 : sal_Int32 nNext = rItr.GetNextAttr();
207 :
208 : // is there also a script change before?
209 : // if there is, nNext should point to the script change
210 0 : sal_Int32 nNextScript = rSI.NextScriptChg( nIdx );
211 0 : if( nNextScript < nNext )
212 0 : nNext = nNextScript;
213 :
214 0 : if ( nNext == COMPLETE_STRING || nNext > nEnd )
215 0 : nNext = nEnd;
216 0 : sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
217 :
218 0 : long nFontMinKashida = rInf.GetOut()->GetMinKashida();
219 0 : if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
220 : {
221 0 : sal_Int32 nKashidasDropped = 0;
222 0 : while ( rKashidas && nGluePortion && nKashidasInAttr > 0 &&
223 0 : nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
224 : {
225 0 : --nGluePortion;
226 0 : --rKashidas;
227 0 : --nKashidasInAttr;
228 0 : ++nKashidasDropped;
229 0 : if( !rKashidas || !nGluePortion ) // nothing left, return false to
230 0 : return false; // do regular blank justification
231 :
232 0 : nSpaceAdd = nGluePortionWidth / nGluePortion;
233 0 : bAddSpaceChanged = true;
234 : }
235 0 : if( nKashidasDropped )
236 0 : rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
237 : }
238 0 : if ( bAddSpaceChanged )
239 0 : break; // start all over again
240 0 : nIdx = nNext;
241 : }
242 0 : if ( !bAddSpaceChanged )
243 0 : break; // everything was OK
244 : }
245 0 : return true;
246 : }
247 :
248 : // CalcNewBlock() must only be called _after_ CalcLine()!
249 : // We always span between two RandPortions or FixPortions (Tabs and Flys).
250 : // We count the Glues and call ExpandBlock.
251 610 : void SwTxtAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
252 : const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
253 : {
254 : OSL_ENSURE( GetInfo().IsMulti() || SVX_ADJUST_BLOCK == GetAdjust(),
255 : "CalcNewBlock: Why?" );
256 : OSL_ENSURE( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" );
257 :
258 610 : pCurrent->InitSpaceAdd();
259 610 : sal_Int32 nGluePortion = 0;
260 610 : sal_Int32 nCharCnt = 0;
261 610 : sal_uInt16 nSpaceIdx = 0;
262 :
263 : // i60591: hennerdrews
264 610 : SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
265 610 : SwTxtSizeInfo aInf ( GetTxtFrm() );
266 1220 : SwTxtIter aItr ( GetTxtFrm(), &aInf );
267 :
268 610 : if ( rSI.CountKashida() )
269 : {
270 0 : while (aItr.GetCurr() != pCurrent && aItr.GetNext())
271 0 : aItr.Next();
272 :
273 0 : if( bSkipKashida )
274 : {
275 0 : rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
276 : }
277 : else
278 : {
279 0 : rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
280 0 : rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
281 : }
282 : }
283 :
284 : // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
285 610 : if (!bSkipKashida)
286 590 : CalcRightMargin( pCurrent, nReal );
287 :
288 : // #i49277#
289 : const bool bDoNotJustifyLinesWithManualBreak =
290 610 : GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK);
291 :
292 610 : SwLinePortion *pPos = pCurrent->GetPortion();
293 :
294 3660 : while( pPos )
295 : {
296 2440 : if ( bDoNotJustifyLinesWithManualBreak &&
297 2440 : pPos->IsBreakPortion() && !IsLastBlock() )
298 : {
299 0 : pCurrent->FinishSpaceAdd();
300 0 : break;
301 : }
302 :
303 2440 : if ( pPos->InTxtGrp() )
304 1204 : nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
305 1236 : else if( pPos->IsMultiPortion() )
306 : {
307 0 : SwMultiPortion* pMulti = (SwMultiPortion*)pPos;
308 : // a multiportion with a tabulator inside breaks the text adjustment
309 : // a ruby portion will not be stretched by text adjustment
310 : // a double line portion takes additional space for each blank
311 : // in the wider line
312 0 : if( pMulti->HasTabulator() )
313 : {
314 0 : if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
315 0 : pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
316 :
317 0 : nSpaceIdx++;
318 0 : nGluePortion = 0;
319 0 : nCharCnt = 0;
320 : }
321 0 : else if( pMulti->IsDouble() )
322 0 : nGluePortion = nGluePortion + ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt();
323 0 : else if ( pMulti->IsBidi() )
324 0 : nGluePortion = nGluePortion + ((SwBidiPortion*)pMulti)->GetSpaceCnt( GetInfo() ); // i60594
325 : }
326 :
327 2440 : if( pPos->InGlueGrp() )
328 : {
329 610 : if( pPos->InFixMargGrp() )
330 : {
331 610 : if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
332 20 : pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
333 :
334 610 : const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
335 610 : SPACING_PRECISION_FACTOR;
336 :
337 610 : sal_Int32 nKashidas = 0;
338 610 : if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
339 : {
340 : // kashida positions found in SwScriptInfo are not necessarily valid in every font
341 : // if two characters are replaced by a ligature glyph, there will be no place for a kashida
342 0 : if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
343 : {
344 : // all kashida positions are invalid
345 : // do regular blank justification
346 0 : pCurrent->FinishSpaceAdd();
347 0 : GetInfo().SetIdx( nStart );
348 0 : CalcNewBlock( pCurrent, pStopAt, nReal, true );
349 0 : return;
350 : }
351 : }
352 :
353 610 : if( nGluePortion )
354 : {
355 582 : long nSpaceAdd = nGluePortionWidth / nGluePortion;
356 :
357 : // i60594
358 582 : if( rSI.CountKashida() && !bSkipKashida )
359 : {
360 0 : if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
361 : {
362 : // no kashidas left
363 : // do regular blank justification
364 0 : pCurrent->FinishSpaceAdd();
365 0 : GetInfo().SetIdx( nStart );
366 0 : CalcNewBlock( pCurrent, pStopAt, nReal, true );
367 0 : return;
368 : }
369 : }
370 :
371 582 : pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
372 582 : pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
373 : }
374 28 : else if ( IsOneBlock() && nCharCnt > 1 )
375 : {
376 0 : const long nSpaceAdd = - nGluePortionWidth / ( nCharCnt - 1 );
377 0 : pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
378 0 : pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
379 : }
380 :
381 610 : nSpaceIdx++;
382 610 : nGluePortion = 0;
383 610 : nCharCnt = 0;
384 : }
385 : else
386 0 : ++nGluePortion;
387 : }
388 2440 : GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
389 2440 : if ( pPos == pStopAt )
390 : {
391 0 : pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
392 0 : break;
393 : }
394 2440 : pPos = pPos->GetPortion();
395 610 : }
396 : }
397 :
398 0 : SwTwips SwTxtAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
399 : {
400 : OSL_ENSURE( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" );
401 : OSL_ENSURE( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
402 :
403 0 : std::deque<sal_uInt16> *pNewKana = new std::deque<sal_uInt16>();
404 0 : pCurrent->SetKanaComp( pNewKana );
405 :
406 0 : const sal_uInt16 nNull = 0;
407 0 : size_t nKanaIdx = 0;
408 0 : long nKanaDiffSum = 0;
409 0 : SwTwips nRepaintOfst = 0;
410 0 : SwTwips nX = 0;
411 0 : bool bNoCompression = false;
412 :
413 : // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
414 0 : CalcRightMargin( pCurrent, 0 );
415 :
416 0 : SwLinePortion* pPos = pCurrent->GetPortion();
417 :
418 0 : while( pPos )
419 : {
420 0 : if ( pPos->InTxtGrp() )
421 : {
422 : // get maximum portion width from info structure, calculated
423 : // during text formatting
424 0 : sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
425 :
426 : // check, if information is stored under other key
427 0 : if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
428 0 : nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
429 :
430 : // calculate difference between portion width and max. width
431 0 : nKanaDiffSum += nMaxWidthDiff;
432 :
433 : // we store the beginning of the first compressable portion
434 : // for repaint
435 0 : if ( nMaxWidthDiff && !nRepaintOfst )
436 0 : nRepaintOfst = nX + GetLeftMargin();
437 : }
438 0 : else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
439 : {
440 0 : if ( nKanaIdx == pCurrent->GetKanaComp().size() )
441 0 : pCurrent->GetKanaComp().push_back( nNull );
442 :
443 : sal_uInt16 nRest;
444 :
445 0 : if ( pPos->InTabGrp() )
446 : {
447 0 : nRest = ! bNoCompression &&
448 0 : ( pPos->Width() > MIN_TAB_WIDTH ) ?
449 0 : pPos->Width() - MIN_TAB_WIDTH :
450 0 : 0;
451 :
452 : // for simplifying the handling of left, right ... tabs,
453 : // we do expand portions, which are lying behind
454 : // those special tabs
455 0 : bNoCompression = !pPos->IsTabLeftPortion();
456 : }
457 : else
458 : {
459 0 : nRest = ! bNoCompression ?
460 0 : ((SwGluePortion*)pPos)->GetPrtGlue() :
461 0 : 0;
462 :
463 0 : bNoCompression = false;
464 : }
465 :
466 0 : if( nKanaDiffSum )
467 : {
468 0 : sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
469 :
470 0 : if ( nCompress >= 10000 )
471 : // kanas can be expanded to 100%, and there is still
472 : // some space remaining
473 0 : nCompress = 0;
474 :
475 : else
476 0 : nCompress = 10000 - nCompress;
477 :
478 0 : ( pCurrent->GetKanaComp() )[ nKanaIdx ] = (sal_uInt16)nCompress;
479 0 : nKanaDiffSum = 0;
480 : }
481 :
482 0 : nKanaIdx++;
483 : }
484 :
485 0 : nX += pPos->Width();
486 0 : pPos = pPos->GetPortion();
487 : }
488 :
489 : // set portion width
490 0 : nKanaIdx = 0;
491 0 : sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
492 0 : pPos = pCurrent->GetPortion();
493 0 : long nDecompress = 0;
494 0 : nKanaDiffSum = 0;
495 :
496 0 : while( pPos )
497 : {
498 0 : if ( pPos->InTxtGrp() )
499 : {
500 0 : const sal_uInt16 nMinWidth = pPos->Width();
501 :
502 : // get maximum portion width from info structure, calculated
503 : // during text formatting
504 0 : sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
505 :
506 : // check, if information is stored under other key
507 0 : if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
508 0 : nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
509 0 : nKanaDiffSum += nMaxWidthDiff;
510 : pPos->Width( nMinWidth +
511 0 : ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
512 0 : nDecompress += pPos->Width() - nMinWidth;
513 : }
514 0 : else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
515 : {
516 0 : if( nCompress )
517 : {
518 0 : nKanaDiffSum *= nCompress;
519 0 : nKanaDiffSum /= 10000;
520 : }
521 :
522 0 : pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) );
523 :
524 0 : if ( pPos->InTabGrp() )
525 : // set fix width to width
526 0 : ((SwTabPortion*)pPos)->SetFixWidth( pPos->Width() );
527 :
528 0 : if ( ++nKanaIdx < pCurrent->GetKanaComp().size() )
529 0 : nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
530 :
531 0 : nKanaDiffSum = 0;
532 0 : nDecompress = 0;
533 : }
534 0 : pPos = pPos->GetPortion();
535 : }
536 :
537 0 : return nRepaintOfst;
538 : }
539 :
540 2544 : SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
541 : SwTwips nReal )
542 : {
543 : long nRealWidth;
544 2544 : const sal_uInt16 nRealHeight = GetLineHeight();
545 2544 : const sal_uInt16 nLineHeight = pCurrent->Height();
546 :
547 2544 : sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
548 2544 : SwLinePortion *pLast = pCurrent->FindLastPortion();
549 :
550 2544 : if( GetInfo().IsMulti() )
551 0 : nRealWidth = nReal;
552 : else
553 : {
554 2544 : nRealWidth = GetLineWidth();
555 : // For each FlyFrm extending into the right margin, we create a FlyPortion.
556 2544 : const long nLeftMar = GetLeftMargin();
557 2544 : SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
558 5088 : nRealWidth - nPrtWidth, nLineHeight );
559 :
560 2544 : SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
561 5096 : while( pFly && long( nPrtWidth )< nRealWidth )
562 : {
563 8 : pLast->Append( pFly );
564 8 : pLast = pFly;
565 8 : if( pFly->Fix() > nPrtWidth )
566 4 : pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1);
567 8 : nPrtWidth += pFly->Width() + 1;
568 8 : aCurrRect.Left( nLeftMar + nPrtWidth );
569 8 : pFly = CalcFlyPortion( nRealWidth, aCurrRect );
570 : }
571 2544 : delete pFly;
572 : }
573 :
574 2544 : SwMarginPortion *pRight = new SwMarginPortion( 0 );
575 2544 : pLast->Append( pRight );
576 :
577 2544 : if( long( nPrtWidth )< nRealWidth )
578 2522 : pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
579 :
580 : // pCurrent->Width() is set to the real size, because we attach the
581 : // MarginPortions.
582 : // This trick gives miraculous results:
583 : // If pCurrent->Width() == nRealWidth, then the adjustment gets overruled
584 : // implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
585 : // line filled with chars.
586 :
587 2544 : pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
588 2544 : return pRight;
589 : }
590 :
591 1954 : void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent )
592 : {
593 : // 1) We insert a left margin:
594 1954 : SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
595 1954 : SwGluePortion *pGlue = pLeft; // the last GluePortion
596 :
597 : // 2) We attach a right margin:
598 : // CalcRightMargin also calculates a possible overlap with FlyFrms.
599 1954 : CalcRightMargin( pCurrent );
600 :
601 1954 : SwLinePortion *pPos = pLeft->GetPortion();
602 1954 : sal_Int32 nLen = 0;
603 :
604 : // If we only have one line, the text portion is consecutive and we center, then ...
605 1954 : bool bComplete = 0 == nStart;
606 1954 : const bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT);
607 1954 : bool bMultiTab = false;
608 :
609 8142 : while( pPos )
610 : {
611 4234 : if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() )
612 0 : bMultiTab = true;
613 6212 : else if( pPos->InFixMargGrp() &&
614 1978 : ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
615 : {
616 : // in tab compat mode we do not want to change tab portions
617 : // in non tab compat mode we do not want to change margins if we
618 : // found a multi portion with tabs
619 1962 : if( SVX_ADJUST_RIGHT == GetAdjust() )
620 262 : ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
621 : else
622 : {
623 : // We set the first text portion to right-aligned and the last one
624 : // to left-aligned.
625 : // The first text portion gets the whole Glue, but only if we have
626 : // more than one line.
627 1700 : if( bComplete && GetInfo().GetTxt().getLength() == nLen )
628 1444 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
629 : else
630 : {
631 256 : if ( ! bTabCompat )
632 : {
633 0 : if( pLeft == pGlue )
634 : {
635 : // If we only have a left and right margin, the
636 : // margins share the Glue.
637 0 : if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
638 0 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
639 : else
640 0 : ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
641 : }
642 : else
643 : {
644 : // The last text portion retains its Glue.
645 0 : if( !pPos->IsMarginPortion() )
646 0 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
647 : }
648 : }
649 : else
650 256 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
651 : }
652 : }
653 :
654 1962 : pGlue = (SwGluePortion*)pPos;
655 1962 : bComplete = false;
656 : }
657 4234 : nLen = nLen + pPos->GetLen();
658 4234 : pPos = pPos->GetPortion();
659 : }
660 :
661 1954 : if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() )
662 : // portions are moved to the right if possible
663 0 : pLeft->AdjustRight( pCurrent );
664 1954 : }
665 :
666 3160 : void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent )
667 : {
668 : OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
669 :
670 3160 : pCurrent->SetFormatAdj(false);
671 :
672 3160 : SwParaPortion* pPara = GetInfo().GetParaPortion();
673 :
674 3160 : switch( GetAdjust() )
675 : {
676 : case SVX_ADJUST_RIGHT:
677 : case SVX_ADJUST_CENTER:
678 : {
679 1954 : CalcFlyAdjust( pCurrent );
680 1954 : pPara->GetRepaint().SetOfst( 0 );
681 1954 : break;
682 : }
683 : case SVX_ADJUST_BLOCK:
684 : {
685 1206 : FormatBlock();
686 1206 : break;
687 : }
688 0 : default : return;
689 : }
690 : }
691 :
692 : // This is a quite complicated calculation: nCurrWidth is the width _before_
693 : // adding the word, that still fits onto the line! For this reason the FlyPortion's
694 : // width is still correct if we get a deadlock-situation of:
695 : // bFirstWord && !WORDFITS
696 2552 : SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth,
697 : const SwRect &rCurrRect )
698 : {
699 2552 : SwTxtFly aTxtFly( GetTxtFrm() );
700 :
701 2552 : const sal_uInt16 nCurrWidth = pCurr->PrtWidth();
702 2552 : SwFlyPortion *pFlyPortion = 0;
703 :
704 2552 : SwRect aLineVert( rCurrRect );
705 2552 : if ( GetTxtFrm()->IsRightToLeft() )
706 34 : GetTxtFrm()->SwitchLTRtoRTL( aLineVert );
707 2552 : if ( GetTxtFrm()->IsVertical() )
708 0 : GetTxtFrm()->SwitchHorizontalToVertical( aLineVert );
709 :
710 : // aFlyRect is document-global!
711 2552 : SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) );
712 :
713 2552 : if ( GetTxtFrm()->IsRightToLeft() )
714 34 : GetTxtFrm()->SwitchRTLtoLTR( aFlyRect );
715 2552 : if ( GetTxtFrm()->IsVertical() )
716 0 : GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect );
717 :
718 : // If a Frame overlapps we open a Portion
719 2552 : if( aFlyRect.HasArea() )
720 : {
721 : // aLocal is frame-local
722 16 : SwRect aLocal( aFlyRect );
723 16 : aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
724 16 : if( nCurrWidth > aLocal.Left() )
725 8 : aLocal.Left( nCurrWidth );
726 :
727 : // If the rect is wider than the line, we adjust it to the right size
728 16 : const long nLocalWidth = aLocal.Left() + aLocal.Width();
729 16 : if( nRealWidth < nLocalWidth )
730 16 : aLocal.Width( nRealWidth - aLocal.Left() );
731 16 : GetInfo().GetParaPortion()->SetFly( true );
732 16 : pFlyPortion = new SwFlyPortion( aLocal );
733 16 : pFlyPortion->Height( sal_uInt16( rCurrRect.Height() ) );
734 : // The Width could be smaller than the FixWidth, thus:
735 16 : pFlyPortion->AdjFixWidth();
736 : }
737 2552 : return pFlyPortion;
738 : }
739 :
740 : // CalcDropAdjust is called at the end by Format() if needed
741 0 : void SwTxtAdjuster::CalcDropAdjust()
742 : {
743 : OSL_ENSURE( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(),
744 : "CalcDropAdjust: No reason for DropAdjustment." );
745 :
746 0 : const sal_uInt16 nLineNumber = GetLineNr();
747 :
748 : // 1) Skip dummies
749 0 : Top();
750 :
751 0 : if( !pCurr->IsDummy() || NextLine() )
752 : {
753 : // Adjust first
754 0 : GetAdjusted();
755 :
756 0 : SwLinePortion *pPor = pCurr->GetFirstPortion();
757 :
758 : // 2) Make sure we include the ropPortion
759 : // 3) pLeft is the GluePor preceding the DropPor
760 0 : if( pPor->InGlueGrp() && pPor->GetPortion()
761 0 : && pPor->GetPortion()->IsDropPortion() )
762 : {
763 0 : const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion();
764 0 : SwGluePortion *pLeft = (SwGluePortion*) pPor;
765 :
766 : // 4) pRight: Find the GluePor coming after the DropPor
767 0 : pPor = pPor->GetPortion();
768 0 : while( pPor && !pPor->InFixMargGrp() )
769 0 : pPor = pPor->GetPortion();
770 :
771 0 : SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
772 0 : (SwGluePortion*) pPor : 0;
773 0 : if( pRight && pRight != pLeft )
774 : {
775 : // 5) Calculate nMinLeft. Who is the most to left?
776 : const sal_uInt16 nDropLineStart =
777 0 : sal_uInt16(GetLineStart()) + pLeft->Width() + pDropPor->Width();
778 0 : sal_uInt16 nMinLeft = nDropLineStart;
779 0 : for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
780 : {
781 0 : if( NextLine() )
782 : {
783 : // Adjust first
784 0 : GetAdjusted();
785 :
786 0 : pPor = pCurr->GetFirstPortion();
787 0 : const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
788 0 : (SwMarginPortion*)pPor : 0;
789 0 : if( !pMar )
790 0 : nMinLeft = 0;
791 : else
792 : {
793 : const sal_uInt16 nLineStart =
794 0 : sal_uInt16(GetLineStart()) + pMar->Width();
795 0 : if( nMinLeft > nLineStart )
796 0 : nMinLeft = nLineStart;
797 : }
798 : }
799 : }
800 :
801 : // 6) Distribute the Glue anew between pLeft and pRight
802 0 : if( nMinLeft < nDropLineStart )
803 : {
804 : // The Glue is always passed from pLeft to pRight, so that
805 : // the text moves to the left.
806 0 : const short nGlue = nDropLineStart - nMinLeft;
807 0 : if( !nMinLeft )
808 0 : pLeft->MoveAllGlue( pRight );
809 : else
810 0 : pLeft->MoveGlue( pRight, nGlue );
811 : }
812 : }
813 : }
814 : }
815 :
816 0 : if( nLineNumber != GetLineNr() )
817 : {
818 0 : Top();
819 0 : while( nLineNumber != GetLineNr() && Next() )
820 : ;
821 : }
822 0 : }
823 :
824 0 : void SwTxtAdjuster::CalcDropRepaint()
825 : {
826 0 : Top();
827 0 : SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
828 0 : if( rRepaint.Top() > Y() )
829 0 : rRepaint.Top( Y() );
830 0 : for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
831 0 : NextLine();
832 0 : const SwTwips nBottom = Y() + GetLineHeight() - 1;
833 0 : if( rRepaint.Bottom() < nBottom )
834 0 : rRepaint.Bottom( nBottom );
835 270 : }
836 :
837 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|