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" // CalcFlyAdjust()
25 : #include "paratr.hxx"
26 : #include "itrtxt.hxx"
27 : #include "porglue.hxx"
28 : #include "porlay.hxx"
29 : #include "porfly.hxx" // CalcFlyAdjust()
30 : #include "pordrop.hxx" // CalcFlyAdjust()
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 27 : 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 27 : const SwLinePortion *pFly = 0;
49 :
50 54 : sal_Bool bSkip = !IsLastBlock() &&
51 54 : 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 27 : if( bSkip )
56 : {
57 11 : const SwLineLayout *pLay = pCurr->GetNext();
58 22 : 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 = sal_False;
65 0 : pPor = pPor->GetPortion();
66 : }
67 0 : pLay = bSkip ? pLay->GetNext() : 0;
68 : }
69 : }
70 :
71 27 : if( bSkip )
72 : {
73 11 : if( !GetInfo().GetParaPortion()->HasFly() )
74 : {
75 10 : if( IsLastCenter() )
76 0 : CalcFlyAdjust( pCurr );
77 10 : pCurr->FinishSpaceAdd();
78 10 : return;
79 : }
80 : else
81 : {
82 1 : const SwLinePortion *pTmpFly = NULL;
83 :
84 : // End at the last Fly
85 1 : const SwLinePortion *pPos = pCurr->GetFirstPortion();
86 3 : while( pPos )
87 : {
88 : // Look for the last Fly which has text coming after it:
89 1 : if( pPos->IsFlyPortion() )
90 0 : pTmpFly = pPos; // Found a Fly
91 1 : else if ( pTmpFly && pPos->InTxtGrp() )
92 : {
93 0 : pFly = pTmpFly; // A Fly with follow-up text!
94 0 : pTmpFly = NULL;
95 : }
96 1 : pPos = pPos->GetPortion();
97 : }
98 : // End if we didn't find one
99 1 : if( !pFly )
100 : {
101 1 : if( IsLastCenter() )
102 0 : CalcFlyAdjust( pCurr );
103 1 : pCurr->FinishSpaceAdd();
104 1 : return;
105 : }
106 : }
107 : }
108 :
109 16 : const xub_StrLen nOldIdx = GetInfo().GetIdx();
110 16 : GetInfo().SetIdx( nStart );
111 16 : CalcNewBlock( pCurr, pFly );
112 16 : GetInfo().SetIdx( nOldIdx );
113 16 : 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 : xub_StrLen& nKashidas, xub_StrLen& nGluePortion )
121 : {
122 : // i60594 validate Kashida justification
123 0 : xub_StrLen nIdx = rItr.GetStart();
124 0 : xub_StrLen 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 : nKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 );
132 :
133 0 : if (!nKashidas) // 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 : xub_StrLen* pKashidaPos = new xub_StrLen [ nKashidas ];
139 0 : xub_StrLen* pKashidaPosDropped = new xub_StrLen [ nKashidas ];
140 0 : rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos );
141 0 : xub_StrLen nKashidaIdx = 0;
142 0 : while ( nKashidas && nIdx < nEnd )
143 : {
144 0 : rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
145 0 : xub_StrLen 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 : xub_StrLen nNextScript = rSI.NextScriptChg( nIdx );
150 0 : if( nNextScript < nNext )
151 0 : nNext = nNextScript;
152 :
153 0 : if ( nNext == STRING_LEN || nNext > nEnd )
154 0 : nNext = nEnd;
155 0 : xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
156 0 : if ( nKashidasInAttr )
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 : xub_StrLen nKashidasDropped = 0;
167 0 : if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
168 : {
169 0 : nKashidasDropped = nKashidasInAttr;
170 0 : nKashidas -= 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 : nKashidas -= 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 (nKashidas > 0);
196 : }
197 :
198 : /*************************************************************************
199 : * lcl_CheckKashidaWidth()
200 : *************************************************************************/
201 0 : static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, xub_StrLen& nKashidas,
202 : xub_StrLen& 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 ( nKashidas )
209 : {
210 0 : bAddSpaceChanged = false;
211 0 : xub_StrLen nIdx = rItr.GetStart();
212 0 : xub_StrLen nEnd = rItr.GetEnd();
213 0 : while ( nIdx < nEnd )
214 : {
215 0 : rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
216 0 : xub_StrLen 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 : xub_StrLen nNextScript = rSI.NextScriptChg( nIdx );
221 0 : if( nNextScript < nNext )
222 0 : nNext = nNextScript;
223 :
224 0 : if ( nNext == STRING_LEN || nNext > nEnd )
225 0 : nNext = nEnd;
226 0 : xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
227 :
228 0 : long nFontMinKashida = rInf.GetOut()->GetMinKashida();
229 0 : if ( nFontMinKashida && nKashidasInAttr && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
230 : {
231 0 : xub_StrLen nKashidasDropped = 0;
232 0 : while ( nKashidas && nGluePortion && nKashidasInAttr &&
233 0 : nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
234 : {
235 0 : --nGluePortion;
236 0 : --nKashidas;
237 0 : --nKashidasInAttr;
238 0 : ++nKashidasDropped;
239 0 : if( !nKashidas || !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 26 : 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 26 : pCurrent->InitSpaceAdd();
274 26 : xub_StrLen nGluePortion = 0;
275 26 : sal_Int32 nCharCnt = 0;
276 26 : MSHORT nSpaceIdx = 0;
277 :
278 : // i60591: hennerdrews
279 26 : SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
280 26 : SwTxtSizeInfo aInf ( GetTxtFrm() );
281 52 : SwTxtIter aItr ( GetTxtFrm(), &aInf );
282 :
283 26 : 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 26 : if (!bSkipKashida)
301 26 : CalcRightMargin( pCurrent, nReal );
302 :
303 : // #i49277#
304 : const sal_Bool bDoNotJustifyLinesWithManualBreak =
305 26 : GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK);
306 :
307 26 : SwLinePortion *pPos = pCurrent->GetPortion();
308 :
309 141 : while( pPos )
310 : {
311 89 : if ( bDoNotJustifyLinesWithManualBreak &&
312 89 : pPos->IsBreakPortion() && !IsLastBlock() )
313 : {
314 0 : pCurrent->FinishSpaceAdd();
315 0 : break;
316 : }
317 :
318 89 : if ( pPos->InTxtGrp() )
319 49 : nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
320 40 : 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 89 : if( pPos->InGlueGrp() )
343 : {
344 26 : if( pPos->InFixMargGrp() )
345 : {
346 26 : if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
347 0 : pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
348 :
349 26 : const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
350 26 : SPACING_PRECISION_FACTOR;
351 :
352 26 : xub_StrLen nKashidas = 0;
353 26 : 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 26 : if( nGluePortion )
369 : {
370 14 : long nSpaceAdd = nGluePortionWidth / nGluePortion;
371 :
372 : // i60594
373 14 : 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 14 : pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
387 14 : pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
388 : }
389 12 : 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 26 : nSpaceIdx++;
397 26 : nGluePortion = 0;
398 26 : nCharCnt = 0;
399 : }
400 : else
401 0 : ++nGluePortion;
402 : }
403 89 : GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
404 89 : if ( pPos == pStopAt )
405 : {
406 0 : pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
407 0 : break;
408 : }
409 89 : pPos = pPos->GetPortion();
410 26 : }
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 : sal_Bool bNoCompression = sal_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 : nRest = ! bNoCompression ?
479 0 : ((SwGluePortion*)pPos)->GetPrtGlue() :
480 0 : 0;
481 :
482 0 : bNoCompression = sal_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 500 : SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
564 : SwTwips nReal )
565 : {
566 : long nRealWidth;
567 500 : const sal_uInt16 nRealHeight = GetLineHeight();
568 500 : const sal_uInt16 nLineHeight = pCurrent->Height();
569 :
570 500 : KSHORT nPrtWidth = pCurrent->PrtWidth();
571 500 : SwLinePortion *pLast = pCurrent->FindLastPortion();
572 :
573 500 : if( GetInfo().IsMulti() )
574 10 : nRealWidth = nReal;
575 : else
576 : {
577 490 : nRealWidth = GetLineWidth();
578 : // For each FlyFrm extending into the right margin, we create a FlyPortion.
579 490 : const long nLeftMar = GetLeftMargin();
580 490 : SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
581 980 : nRealWidth - nPrtWidth, nLineHeight );
582 :
583 490 : SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
584 983 : while( pFly && long( nPrtWidth )< nRealWidth )
585 : {
586 3 : pLast->Append( pFly );
587 3 : pLast = pFly;
588 3 : if( pFly->Fix() > nPrtWidth )
589 3 : pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1);
590 3 : nPrtWidth += pFly->Width() + 1;
591 3 : aCurrRect.Left( nLeftMar + nPrtWidth );
592 3 : pFly = CalcFlyPortion( nRealWidth, aCurrRect );
593 : }
594 490 : delete pFly;
595 : }
596 :
597 500 : SwMarginPortion *pRight = new SwMarginPortion( 0 );
598 500 : pLast->Append( pRight );
599 :
600 500 : if( long( nPrtWidth )< nRealWidth )
601 496 : 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 500 : pCurrent->PrtWidth( KSHORT( nRealWidth ) );
611 500 : return pRight;
612 : }
613 :
614 : /*************************************************************************
615 : * SwTxtAdjuster::CalcFlyAdjust()
616 : *************************************************************************/
617 :
618 474 : void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent )
619 : {
620 : // 1) We insert a left margin:
621 474 : SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
622 474 : SwGluePortion *pGlue = pLeft; // the last GluePortion
623 :
624 :
625 : // 2) We attach a right margin:
626 : // CalcRightMargin also calculates a possible overlap with FlyFrms.
627 474 : CalcRightMargin( pCurrent );
628 :
629 474 : SwLinePortion *pPos = pLeft->GetPortion();
630 474 : xub_StrLen nLen = 0;
631 :
632 : // If we only have one line, the text portion is consecutive and we center, then ...
633 474 : sal_Bool bComplete = 0 == nStart;
634 474 : const sal_Bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT);
635 474 : sal_Bool bMultiTab = sal_False;
636 :
637 1901 : while( pPos )
638 : {
639 953 : if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() )
640 0 : bMultiTab = sal_True;
641 1430 : else if( pPos->InFixMargGrp() &&
642 477 : ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
643 : {
644 : // in tab compat mode we do not want to change tab portions
645 : // in non tab compat mode we do not want to change margins if we
646 : // found a multi portion with tabs
647 477 : if( SVX_ADJUST_RIGHT == GetAdjust() )
648 6 : ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
649 : else
650 : {
651 : // We set the first text portion to right-aligned and the last one
652 : // to left-aligned.
653 : // The first text portion gets the whole Glue, but only if we have
654 : // more than one line.
655 471 : if( bComplete && GetInfo().GetTxt().getLength() == nLen )
656 458 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
657 : else
658 : {
659 13 : if ( ! bTabCompat )
660 : {
661 0 : if( pLeft == pGlue )
662 : {
663 : // If we only have a left and right margin, the
664 : // margins share the Glue.
665 0 : if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
666 0 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
667 : else
668 0 : ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
669 : }
670 : else
671 : {
672 : // The last text portion retains its Glue.
673 0 : if( !pPos->IsMarginPortion() )
674 0 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
675 : }
676 : }
677 : else
678 13 : ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
679 : }
680 : }
681 :
682 477 : pGlue = (SwFlyPortion*)pPos;
683 477 : bComplete = sal_False;
684 : }
685 953 : nLen = nLen + pPos->GetLen();
686 953 : pPos = pPos->GetPortion();
687 : }
688 :
689 474 : if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() )
690 : // portions are moved to the right if possible
691 0 : pLeft->AdjustRight( pCurrent );
692 474 : }
693 :
694 : /*************************************************************************
695 : * SwTxtAdjuster::CalcAdjLine()
696 : *************************************************************************/
697 :
698 501 : void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent )
699 : {
700 : OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
701 :
702 501 : pCurrent->SetFormatAdj(sal_False);
703 :
704 501 : SwParaPortion* pPara = GetInfo().GetParaPortion();
705 :
706 501 : switch( GetAdjust() )
707 : {
708 : case SVX_ADJUST_RIGHT:
709 : case SVX_ADJUST_CENTER:
710 : {
711 474 : CalcFlyAdjust( pCurrent );
712 474 : pPara->GetRepaint()->SetOfst( 0 );
713 474 : break;
714 : }
715 : case SVX_ADJUST_BLOCK:
716 : {
717 27 : FormatBlock();
718 27 : break;
719 : }
720 0 : default : return;
721 : }
722 : }
723 :
724 : /*************************************************************************
725 : * SwTxtAdjuster::CalcFlyPortion()
726 : *
727 : * This is a quite complicated calculation: nCurrWidth is the width _before_
728 : * adding the word, that still fits onto the line! For this reason the FlyPortion's
729 : * width is still correct if we get a deadlock-situation of:
730 : * bFirstWord && !WORDFITS
731 : *************************************************************************/
732 :
733 493 : SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth,
734 : const SwRect &rCurrRect )
735 : {
736 493 : SwTxtFly aTxtFly( GetTxtFrm() );
737 :
738 493 : const KSHORT nCurrWidth = pCurr->PrtWidth();
739 493 : SwFlyPortion *pFlyPortion = 0;
740 :
741 493 : SwRect aLineVert( rCurrRect );
742 493 : if ( GetTxtFrm()->IsRightToLeft() )
743 2 : GetTxtFrm()->SwitchLTRtoRTL( aLineVert );
744 493 : if ( GetTxtFrm()->IsVertical() )
745 0 : GetTxtFrm()->SwitchHorizontalToVertical( aLineVert );
746 :
747 : // aFlyRect is document-global!
748 493 : SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) );
749 :
750 493 : if ( GetTxtFrm()->IsRightToLeft() )
751 2 : GetTxtFrm()->SwitchRTLtoLTR( aFlyRect );
752 493 : if ( GetTxtFrm()->IsVertical() )
753 0 : GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect );
754 :
755 : // If a Frame overlapps we open a Portion
756 493 : if( aFlyRect.HasArea() )
757 : {
758 : // aLocal is frame-local
759 6 : SwRect aLocal( aFlyRect );
760 6 : aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
761 6 : if( nCurrWidth > aLocal.Left() )
762 0 : aLocal.Left( nCurrWidth );
763 :
764 : // If the rect is wider than the line, we adjust it to the right size
765 6 : KSHORT nLocalWidth = KSHORT( aLocal.Left() + aLocal.Width() );
766 6 : if( nRealWidth < long( nLocalWidth ) )
767 6 : aLocal.Width( nRealWidth - aLocal.Left() );
768 6 : GetInfo().GetParaPortion()->SetFly( sal_True );
769 6 : pFlyPortion = new SwFlyPortion( aLocal );
770 6 : pFlyPortion->Height( KSHORT( rCurrRect.Height() ) );
771 : // The Width could be smaller than the FixWidth, thus:
772 6 : pFlyPortion->AdjFixWidth();
773 : }
774 493 : return pFlyPortion;
775 : }
776 :
777 : /*************************************************************************
778 : * SwTxtPainter::_CalcDropAdjust()
779 : * Drops and Adjustment
780 : * CalcDropAdjust is called at the end by Format() if needed
781 : *************************************************************************/
782 :
783 0 : void SwTxtAdjuster::CalcDropAdjust()
784 : {
785 : OSL_ENSURE( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(),
786 : "CalcDropAdjust: No reason for DropAdjustment." );
787 :
788 0 : const MSHORT nLineNumber = GetLineNr();
789 :
790 : // 1) Skip dummies
791 0 : Top();
792 :
793 0 : if( !pCurr->IsDummy() || NextLine() )
794 : {
795 : // Adjust first
796 0 : GetAdjusted();
797 :
798 0 : SwLinePortion *pPor = pCurr->GetFirstPortion();
799 :
800 : // 2) Make sure we include the ropPortion
801 : // 3) pLeft is the GluePor preceding the DropPor
802 0 : if( pPor->InGlueGrp() && pPor->GetPortion()
803 0 : && pPor->GetPortion()->IsDropPortion() )
804 : {
805 0 : const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion();
806 0 : SwGluePortion *pLeft = (SwGluePortion*) pPor;
807 :
808 : // 4) pRight: Find the GluePor coming after the DropPor
809 0 : pPor = pPor->GetPortion();
810 0 : while( pPor && !pPor->InFixMargGrp() )
811 0 : pPor = pPor->GetPortion();
812 :
813 0 : SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
814 0 : (SwGluePortion*) pPor : 0;
815 0 : if( pRight && pRight != pLeft )
816 : {
817 : // 5) Calculate nMinLeft. Who is the most to left?
818 : const KSHORT nDropLineStart =
819 0 : KSHORT(GetLineStart()) + pLeft->Width() + pDropPor->Width();
820 0 : KSHORT nMinLeft = nDropLineStart;
821 0 : for( MSHORT i = 1; i < GetDropLines(); ++i )
822 : {
823 0 : if( NextLine() )
824 : {
825 : // Adjust first
826 0 : GetAdjusted();
827 :
828 0 : pPor = pCurr->GetFirstPortion();
829 0 : const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
830 0 : (SwMarginPortion*)pPor : 0;
831 0 : if( !pMar )
832 0 : nMinLeft = 0;
833 : else
834 : {
835 : const KSHORT nLineStart =
836 0 : KSHORT(GetLineStart()) + pMar->Width();
837 0 : if( nMinLeft > nLineStart )
838 0 : nMinLeft = nLineStart;
839 : }
840 : }
841 : }
842 :
843 : // 6) Distribute the Glue anew between pLeft and pRight
844 0 : if( nMinLeft < nDropLineStart )
845 : {
846 : // The Glue is always passed from pLeft to pRight, so that
847 : // the text moves to the left.
848 0 : const short nGlue = nDropLineStart - nMinLeft;
849 0 : if( !nMinLeft )
850 0 : pLeft->MoveAllGlue( pRight );
851 : else
852 0 : pLeft->MoveGlue( pRight, nGlue );
853 : }
854 : }
855 : }
856 : }
857 :
858 0 : if( nLineNumber != GetLineNr() )
859 : {
860 0 : Top();
861 0 : while( nLineNumber != GetLineNr() && Next() )
862 : ;
863 : }
864 0 : }
865 :
866 : /*************************************************************************
867 : * SwTxtAdjuster::CalcDropRepaint()
868 : *************************************************************************/
869 :
870 0 : void SwTxtAdjuster::CalcDropRepaint()
871 : {
872 0 : Top();
873 0 : SwRepaint &rRepaint = *GetInfo().GetParaPortion()->GetRepaint();
874 0 : if( rRepaint.Top() > Y() )
875 0 : rRepaint.Top( Y() );
876 0 : for( MSHORT i = 1; i < GetDropLines(); ++i )
877 0 : NextLine();
878 0 : const SwTwips nBottom = Y() + GetLineHeight() - 1;
879 0 : if( rRepaint.Bottom() < nBottom )
880 0 : rRepaint.Bottom( nBottom );
881 99 : }
882 :
883 :
884 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|