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