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