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