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 "porlay.hxx"
21 : #include "itrform2.hxx"
22 : #include "porglue.hxx"
23 : #include "porexp.hxx"
24 : #include "blink.hxx"
25 : #include "redlnitr.hxx"
26 : #include "porfly.hxx"
27 : #include <porrst.hxx>
28 : #include <pormulti.hxx>
29 : #include <pordrop.hxx>
30 : #include <breakit.hxx>
31 : #include <unicode/uchar.h>
32 : #include <com/sun/star/i18n/ScriptType.hpp>
33 : #include <com/sun/star/i18n/CTLScriptType.hpp>
34 : #include <com/sun/star/i18n/WordType.hpp>
35 : #include <paratr.hxx>
36 : #include <editeng/adjustitem.hxx>
37 : #include <editeng/scripttypeitem.hxx>
38 : #include <editeng/charhiddenitem.hxx>
39 : #include <vcl/outdev.hxx>
40 : #include <editeng/blinkitem.hxx>
41 : #include <tools/multisel.hxx>
42 : #include <unotools/charclass.hxx>
43 : #include <i18nlangtag/mslangid.hxx>
44 : #include <charfmt.hxx>
45 : #include <fchrfmt.hxx>
46 : #include <docary.hxx>
47 : #include <redline.hxx>
48 : #include <section.hxx>
49 : #include <switerator.hxx>
50 : #include <IDocumentRedlineAccess.hxx>
51 : #include <IDocumentSettingAccess.hxx>
52 : #include <IDocumentContentOperations.hxx>
53 :
54 : using namespace ::com::sun::star;
55 : using namespace i18n::ScriptType;
56 :
57 : #include <unicode/ubidi.h>
58 : #include <i18nutil/scripttypedetector.hxx>
59 : #include <i18nutil/unicode.hxx>
60 :
61 : #define IS_JOINING_GROUP(c, g) ( u_getIntPropertyValue( (c), UCHAR_JOINING_GROUP ) == U_JG_##g )
62 : #define isAinChar(c) IS_JOINING_GROUP((c), AIN)
63 : #define isAlefChar(c) IS_JOINING_GROUP((c), ALEF)
64 : #define isBaaChar(c) IS_JOINING_GROUP((c), BEH)
65 : #define isDalChar(c) IS_JOINING_GROUP((c), DAL)
66 : #define isFehChar(c) IS_JOINING_GROUP((c), FEH)
67 : #define isGafChar(c) IS_JOINING_GROUP((c), GAF)
68 : #define isHahChar(c) IS_JOINING_GROUP((c), HAH)
69 : #define isKafChar(c) IS_JOINING_GROUP((c), KAF)
70 : #define isLamChar(c) IS_JOINING_GROUP((c), LAM)
71 : #define isQafChar(c) IS_JOINING_GROUP((c), QAF)
72 : #define isRehChar(c) IS_JOINING_GROUP((c), REH)
73 : #define isTehMarbutaChar(c) IS_JOINING_GROUP((c), TEH_MARBUTA)
74 : #define isWawChar(c) IS_JOINING_GROUP((c), WAW)
75 : #if (U_ICU_VERSION_MAJOR_NUM > 4) || (U_ICU_VERSION_MAJOR_NUM == 4 && U_ICU_VERSION_MINOR_NUM >= 4)
76 : #define isYehChar(c) (IS_JOINING_GROUP((c), YEH) || IS_JOINING_GROUP((c), FARSI_YEH))
77 : #else
78 : #define isYehChar(c) IS_JOINING_GROUP((c), YEH)
79 : #endif
80 : #define isSeenOrSadChar(c) (IS_JOINING_GROUP((c), SAD) || IS_JOINING_GROUP((c), SEEN))
81 :
82 0 : bool isTransparentChar ( sal_Unicode cCh )
83 : {
84 0 : return u_getIntPropertyValue( cCh, UCHAR_JOINING_TYPE ) == U_JT_TRANSPARENT;
85 : }
86 :
87 : /*************************************************************************
88 : * lcl_IsLigature
89 : *
90 : * Checks if cCh + cNectCh builds a ligature (used for Kashidas)
91 : *************************************************************************/
92 :
93 0 : static bool lcl_IsLigature( sal_Unicode cCh, sal_Unicode cNextCh )
94 : {
95 : // Lam + Alef
96 0 : return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
97 : }
98 :
99 : /*************************************************************************
100 : * lcl_ConnectToPrev
101 : *
102 : * Checks if cCh is connectable to cPrevCh (used for Kashidas)
103 : *************************************************************************/
104 :
105 0 : static bool lcl_ConnectToPrev( sal_Unicode cCh, sal_Unicode cPrevCh )
106 : {
107 0 : const int32_t nJoiningType = u_getIntPropertyValue( cPrevCh, UCHAR_JOINING_TYPE );
108 0 : bool bRet = nJoiningType != U_JT_RIGHT_JOINING && nJoiningType != U_JT_NON_JOINING;
109 :
110 : // check for ligatures cPrevChar + cChar
111 0 : if( bRet )
112 0 : bRet = !lcl_IsLigature( cPrevCh, cCh );
113 :
114 0 : return bRet;
115 : }
116 :
117 : /*************************************************************************
118 : * lcl_HasStrongLTR
119 : *************************************************************************/
120 0 : static bool lcl_HasStrongLTR ( const OUString& rTxt, sal_Int32 nStart, sal_Int32 nEnd )
121 : {
122 0 : for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
123 : {
124 0 : const UCharDirection nCharDir = u_charDirection ( rTxt[ nCharIdx ] );
125 0 : if ( nCharDir == U_LEFT_TO_RIGHT ||
126 0 : nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
127 : nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
128 0 : return true;
129 : }
130 0 : return false;
131 : }
132 :
133 : /*************************************************************************
134 : * SwLineLayout::~SwLineLayout()
135 : *
136 : * class SwLineLayout: This is the layout of a single line, which is made
137 : * up of it's dimension, the character count and the word spacing in the
138 : * line.
139 : * Line objects are managed in an own pool, in order to store them continuously
140 : * in memory so that they are paged out together and don't fragment memory.
141 : *************************************************************************/
142 :
143 0 : SwLineLayout::~SwLineLayout()
144 : {
145 0 : Truncate();
146 0 : delete pNext;
147 0 : if( pBlink )
148 0 : pBlink->Delete( this );
149 0 : delete pLLSpaceAdd;
150 0 : delete pKanaComp;
151 0 : }
152 :
153 0 : SwLinePortion *SwLineLayout::Insert( SwLinePortion *pIns )
154 : {
155 : // First attribute change: copy mass and length from *pIns into the first
156 : // text portion
157 0 : if( !pPortion )
158 : {
159 0 : if( GetLen() )
160 : {
161 0 : pPortion = new SwTxtPortion( *(SwLinePortion*)this );
162 0 : if( IsBlinking() && pBlink )
163 : {
164 0 : SetBlinking( false );
165 0 : pBlink->Replace( this, pPortion );
166 : }
167 : }
168 : else
169 : {
170 0 : SetPortion( pIns );
171 0 : return pIns;
172 : }
173 : }
174 : // Call with scope or we'll end up with recursion!
175 0 : return pPortion->SwLinePortion::Insert( pIns );
176 : }
177 :
178 0 : SwLinePortion *SwLineLayout::Append( SwLinePortion *pIns )
179 : {
180 : // First attribute change: copy mass and length from *pIns into the first
181 : // text portion
182 0 : if( !pPortion )
183 0 : pPortion = new SwTxtPortion( *(SwLinePortion*)this );
184 : // Call with scope or we'll end up with recursion!
185 0 : return pPortion->SwLinePortion::Append( pIns );
186 : }
187 :
188 : // For special treatment of empty lines
189 :
190 0 : bool SwLineLayout::Format( SwTxtFormatInfo &rInf )
191 : {
192 0 : if( GetLen() )
193 0 : return SwTxtPortion::Format( rInf );
194 :
195 0 : Height( rInf.GetTxtHeight() );
196 0 : return true;
197 : }
198 :
199 : /*************************************************************************
200 : * SwLineLayout::CalcLeftMargin()
201 : *
202 : * We collect all FlyPortions at the beginning of the line and make that a
203 : * MarginPortion.
204 : *************************************************************************/
205 :
206 0 : SwMarginPortion *SwLineLayout::CalcLeftMargin()
207 : {
208 0 : SwMarginPortion *pLeft = (GetPortion() && GetPortion()->IsMarginPortion()) ?
209 0 : (SwMarginPortion *)GetPortion() : 0;
210 0 : if( !GetPortion() )
211 0 : SetPortion( new SwTxtPortion( *(SwLinePortion*)this ) );
212 0 : if( !pLeft )
213 : {
214 0 : pLeft = new SwMarginPortion( 0 );
215 0 : pLeft->SetPortion( GetPortion() );
216 0 : SetPortion( pLeft );
217 : }
218 : else
219 : {
220 0 : pLeft->Height( 0 );
221 0 : pLeft->Width( 0 );
222 0 : pLeft->SetLen( 0 );
223 0 : pLeft->SetAscent( 0 );
224 0 : pLeft->SetPortion( NULL );
225 0 : pLeft->SetFixWidth(0);
226 : }
227 :
228 0 : SwLinePortion *pPos = pLeft->GetPortion();
229 0 : while( pPos )
230 : {
231 0 : if( pPos->IsFlyPortion() )
232 : {
233 : // The FlyPortion get's sucked out ...
234 0 : pLeft->Join( (SwGluePortion*)pPos );
235 0 : pPos = pLeft->GetPortion();
236 0 : if( GetpKanaComp() && !GetKanaComp().empty() )
237 0 : GetKanaComp().pop_front();
238 : }
239 : else
240 0 : pPos = 0;
241 : }
242 0 : return pLeft;
243 : }
244 :
245 0 : void SwLineLayout::InitSpaceAdd()
246 : {
247 0 : if ( !pLLSpaceAdd )
248 0 : CreateSpaceAdd();
249 : else
250 0 : SetLLSpaceAdd( 0, 0 );
251 0 : }
252 :
253 0 : void SwLineLayout::CreateSpaceAdd( const long nInit )
254 : {
255 0 : pLLSpaceAdd = new std::vector<long>;
256 0 : SetLLSpaceAdd( nInit, 0 );
257 0 : }
258 :
259 : /*************************************************************************
260 : * Local helper function. Returns true if there are only blanks
261 : * in [nStt, nEnd[
262 : *************************************************************************/
263 :
264 0 : static bool lcl_HasOnlyBlanks( const OUString& rTxt, sal_Int32 nStt, sal_Int32 nEnd )
265 : {
266 0 : bool bBlankOnly = true;
267 0 : while ( nStt < nEnd )
268 : {
269 0 : const sal_Unicode cChar = rTxt[ nStt++ ];
270 0 : if ( ' ' != cChar && 0x3000 != cChar )
271 : {
272 0 : bBlankOnly = false;
273 0 : break;
274 : }
275 : }
276 0 : return bBlankOnly;
277 : }
278 :
279 : /*************************************************************************
280 : * SwLineLayout::CalcLine()
281 : *
282 : * Swapped out from FormatLine()
283 : *************************************************************************/
284 :
285 0 : void SwLineLayout::CalcLine( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf )
286 : {
287 0 : const KSHORT nLineWidth = rInf.RealWidth();
288 :
289 0 : KSHORT nFlyAscent = 0;
290 0 : KSHORT nFlyHeight = 0;
291 0 : KSHORT nFlyDescent = 0;
292 0 : bool bOnlyPostIts = true;
293 0 : SetHanging( false );
294 :
295 0 : bool bTmpDummy = !GetLen();
296 0 : SwFlyCntPortion* pFlyCnt = 0;
297 0 : if( bTmpDummy )
298 : {
299 0 : nFlyAscent = 0;
300 0 : nFlyHeight = 0;
301 0 : nFlyDescent = 0;
302 : }
303 :
304 : // #i3952#
305 : const bool bIgnoreBlanksAndTabsForLineHeightCalculation =
306 0 : rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION);
307 :
308 0 : bool bHasBlankPortion = false;
309 0 : bool bHasOnlyBlankPortions = true;
310 :
311 0 : if( pPortion )
312 : {
313 0 : SetCntnt( false );
314 0 : if( pPortion->IsBreakPortion() )
315 : {
316 0 : SetLen( pPortion->GetLen() );
317 0 : if( GetLen() )
318 0 : bTmpDummy = false;
319 : }
320 : else
321 : {
322 0 : KSHORT nLineHeight = Height();
323 0 : Init( GetPortion() );
324 0 : SwLinePortion *pPos = pPortion;
325 0 : SwLinePortion *pLast = this;
326 0 : KSHORT nMaxDescent = 0;
327 :
328 : // A group is a segment in the portion chain of pCurr or a fixed
329 : // portion spanning to the end or the next fixed portion
330 0 : while( pPos )
331 : {
332 : SAL_WARN_IF( POR_LIN == pPos->GetWhichPor(),
333 : "sw.core", "SwLineLayout::CalcLine: don't use SwLinePortions !" );
334 :
335 : // Null portions are eliminated. They can form if two FlyFrms
336 : // overlap.
337 0 : if( !pPos->Compress() )
338 : {
339 : // Only take over Height and Ascent if the rest of the line
340 : // is empty.
341 0 : if( !pPos->GetPortion() )
342 : {
343 0 : if( !Height() )
344 0 : Height( pPos->Height() );
345 0 : if( !GetAscent() )
346 0 : SetAscent( pPos->GetAscent() );
347 : }
348 0 : delete pLast->Cut( pPos );
349 0 : pPos = pLast->GetPortion();
350 0 : continue;
351 : }
352 :
353 0 : const sal_Int32 nPorSttIdx = rInf.GetLineStart() + nLineLength;
354 0 : nLineLength += pPos->GetLen();
355 0 : AddPrtWidth( pPos->Width() );
356 :
357 : // #i3952#
358 0 : if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
359 : {
360 0 : if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
361 0 : ( pPos->IsTextPortion() &&
362 0 : lcl_HasOnlyBlanks( rInf.GetTxt(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
363 : {
364 0 : pLast = pPos;
365 0 : pPos = pPos->GetPortion();
366 0 : bHasBlankPortion = true;
367 0 : continue;
368 : }
369 : }
370 :
371 : // Ignore drop portion height
372 0 : if( pPos->IsDropPortion() && static_cast<SwDropPortion*>(pPos)->GetLines() > 1)
373 : {
374 0 : pLast = pPos;
375 0 : pPos = pPos->GetPortion();
376 0 : continue;
377 : }
378 :
379 0 : bHasOnlyBlankPortions = false;
380 :
381 : // We had an attribute change: Sum up/build maxima of length and mass
382 :
383 0 : KSHORT nPosHeight = pPos->Height();
384 0 : KSHORT nPosAscent = pPos->GetAscent();
385 :
386 : SAL_WARN_IF( nPosHeight < nPosAscent,
387 : "sw.core", "SwLineLayout::CalcLine: bad ascent or height" );
388 :
389 0 : if( pPos->IsHangingPortion() )
390 : {
391 0 : SetHanging();
392 0 : rInf.GetParaPortion()->SetMargin();
393 : }
394 :
395 : // To prevent that a paragraph-end-character does not change
396 : // the line height through a Descent and thus causing the line
397 : // to reformat.
398 0 : if ( !pPos->IsBreakPortion() || !Height() )
399 : {
400 0 : if (!pPos->IsPostItsPortion()) bOnlyPostIts = false;
401 :
402 0 : if( bTmpDummy && !nLineLength )
403 : {
404 0 : if( pPos->IsFlyPortion() )
405 : {
406 0 : if( nFlyHeight < nPosHeight )
407 0 : nFlyHeight = nPosHeight;
408 0 : if( nFlyAscent < nPosAscent )
409 0 : nFlyAscent = nPosAscent;
410 0 : if( nFlyDescent < nPosHeight - nPosAscent )
411 0 : nFlyDescent = nPosHeight - nPosAscent;
412 : }
413 : else
414 : {
415 0 : if( pPos->InNumberGrp() )
416 : {
417 : KSHORT nTmp = rInf.GetFont()->GetAscent(
418 0 : rInf.GetVsh(), *rInf.GetOut() );
419 0 : if( nTmp > nPosAscent )
420 : {
421 0 : nPosHeight += nTmp - nPosAscent;
422 0 : nPosAscent = nTmp;
423 : }
424 : nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
425 0 : *rInf.GetOut() );
426 0 : if( nTmp > nPosHeight )
427 0 : nPosHeight = nTmp;
428 : }
429 0 : Height( nPosHeight );
430 0 : nAscent = nPosAscent;
431 0 : nMaxDescent = nPosHeight - nPosAscent;
432 0 : }
433 : }
434 0 : else if( !pPos->IsFlyPortion() )
435 : {
436 0 : if( Height() < nPosHeight )
437 : {
438 : // Height is set to 0 when Init() is called.
439 0 : if (bIgnoreBlanksAndTabsForLineHeightCalculation && pPos->GetWhichPor() == POR_FLYCNT)
440 : // Compat flag set: take the line height, if it's larger.
441 0 : Height(std::max(nPosHeight, nLineHeight));
442 : else
443 : // Just care about the portion height.
444 0 : Height(nPosHeight);
445 : }
446 0 : if( pPos->IsFlyCntPortion() || ( pPos->IsMultiPortion()
447 0 : && ((SwMultiPortion*)pPos)->HasFlyInCntnt() ) )
448 0 : rLine.SetFlyInCntBase();
449 0 : if( pPos->IsFlyCntPortion() &&
450 0 : ((SwFlyCntPortion*)pPos)->GetAlign() )
451 : {
452 0 : ((SwFlyCntPortion*)pPos)->SetMax( false );
453 0 : if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() )
454 0 : pFlyCnt = (SwFlyCntPortion*)pPos;
455 : }
456 : else
457 : {
458 0 : if( nAscent < nPosAscent )
459 0 : nAscent = nPosAscent;
460 0 : if( nMaxDescent < nPosHeight - nPosAscent )
461 0 : nMaxDescent = nPosHeight - nPosAscent;
462 : }
463 : }
464 : }
465 0 : else if( pPos->GetLen() )
466 0 : bTmpDummy = false;
467 :
468 0 : if( !HasCntnt() && !pPos->InNumberGrp() )
469 : {
470 0 : if ( pPos->InExpGrp() )
471 : {
472 0 : OUString aTxt;
473 0 : if( pPos->GetExpTxt( rInf, aTxt ) && !aTxt.isEmpty() )
474 0 : SetCntnt();
475 : }
476 0 : else if( ( pPos->InTxtGrp() || pPos->IsMultiPortion() ) &&
477 0 : pPos->GetLen() )
478 0 : SetCntnt();
479 : }
480 :
481 0 : bTmpDummy &= !HasCntnt() && ( !pPos->Width() || pPos->IsFlyPortion() );
482 :
483 0 : pLast = pPos;
484 0 : pPos = pPos->GetPortion();
485 : }
486 :
487 0 : if( pFlyCnt )
488 : {
489 0 : if( pFlyCnt->Height() == Height() )
490 : {
491 0 : pFlyCnt->SetMax( true );
492 0 : if( Height() > nMaxDescent + nAscent )
493 : {
494 0 : if( 3 == pFlyCnt->GetAlign() ) // Bottom
495 0 : nAscent = Height() - nMaxDescent;
496 0 : else if( 2 == pFlyCnt->GetAlign() ) // Center
497 0 : nAscent = ( Height() + nAscent - nMaxDescent ) / 2;
498 : }
499 0 : pFlyCnt->SetAscent( nAscent );
500 : }
501 : }
502 :
503 0 : if( bTmpDummy && nFlyHeight )
504 : {
505 0 : nAscent = nFlyAscent;
506 0 : if( nFlyDescent > nFlyHeight - nFlyAscent )
507 0 : Height( nFlyHeight + nFlyDescent );
508 : else
509 0 : Height( nFlyHeight );
510 : }
511 0 : else if( nMaxDescent > Height() - nAscent )
512 0 : Height( nMaxDescent + nAscent );
513 :
514 0 : if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) )
515 : {
516 0 : Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
517 0 : nAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() );
518 : }
519 : }
520 : }
521 : else
522 : {
523 0 : SetCntnt( !bTmpDummy );
524 :
525 : // #i3952#
526 0 : if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
527 0 : lcl_HasOnlyBlanks( rInf.GetTxt(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
528 : {
529 0 : bHasBlankPortion = true;
530 : }
531 : }
532 :
533 : // #i3952#
534 0 : if ( bHasBlankPortion && bHasOnlyBlankPortions )
535 : {
536 0 : sal_uInt16 nTmpAscent = GetAscent();
537 0 : sal_uInt16 nTmpHeight = Height();
538 0 : rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
539 0 : SetAscent( nTmpAscent );
540 0 : Height( nTmpHeight );
541 : }
542 :
543 : // Robust:
544 0 : if( nLineWidth < Width() )
545 0 : Width( nLineWidth );
546 : SAL_WARN_IF( nLineWidth < Width(), "sw.core", "SwLineLayout::CalcLine: line is bursting" );
547 0 : SetDummy( bTmpDummy );
548 0 : SetRedline( rLine.GetRedln() &&
549 0 : rLine.GetRedln()->CheckLine( rLine.GetStart(), rLine.GetEnd() ) );
550 0 : }
551 :
552 : // #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
553 : // to control, if the fly content portions and line portion are considered.
554 0 : void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent,
555 : SwTwips& _orDescent,
556 : SwTwips& _orObjAscent,
557 : SwTwips& _orObjDescent,
558 : const SwLinePortion* _pDontConsiderPortion,
559 : const bool _bNoFlyCntPorAndLinePor ) const
560 : {
561 0 : _orAscent = 0;
562 0 : _orDescent = 0;
563 0 : _orObjAscent = 0;
564 0 : _orObjDescent = 0;
565 :
566 0 : const SwLinePortion* pTmpPortion = this;
567 0 : if ( !pTmpPortion->GetLen() && pTmpPortion->GetPortion() )
568 : {
569 0 : pTmpPortion = pTmpPortion->GetPortion();
570 : }
571 :
572 0 : while ( pTmpPortion )
573 : {
574 0 : if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() &&
575 0 : ( !_bNoFlyCntPorAndLinePor ||
576 0 : ( !pTmpPortion->IsFlyCntPortion() &&
577 0 : !(pTmpPortion == this && pTmpPortion->GetPortion() ) ) ) )
578 : {
579 0 : SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent());
580 0 : SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) -
581 0 : nPortionAsc;
582 :
583 0 : const bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
584 : static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
585 0 : !( pTmpPortion == _pDontConsiderPortion );
586 :
587 0 : if ( bFlyCmp )
588 : {
589 0 : _orObjAscent = std::max( _orObjAscent, nPortionAsc );
590 0 : _orObjDescent = std::max( _orObjDescent, nPortionDesc );
591 : }
592 :
593 0 : if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() )
594 : {
595 0 : _orAscent = std::max( _orAscent, nPortionAsc );
596 0 : _orDescent = std::max( _orDescent, nPortionDesc );
597 : }
598 : }
599 0 : pTmpPortion = pTmpPortion->GetPortion();
600 : }
601 0 : }
602 :
603 0 : SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange)
604 : {
605 0 : if(0 != rRange.nLen ) {
606 0 : if(0 == nLen) {
607 0 : nStart = rRange.nStart;
608 0 : nLen = rRange.nLen ;
609 : }
610 : else {
611 0 : if(rRange.nStart + rRange.nLen > nStart + nLen) {
612 0 : nLen = rRange.nStart + rRange.nLen - nStart;
613 : }
614 0 : if(rRange.nStart < nStart) {
615 0 : nLen += nStart - rRange.nStart;
616 0 : nStart = rRange.nStart;
617 : }
618 : }
619 : }
620 0 : return *this;
621 : }
622 :
623 0 : SwScriptInfo::SwScriptInfo()
624 : : nInvalidityPos(0)
625 0 : , nDefaultDir(0)
626 : {
627 0 : };
628 :
629 0 : SwScriptInfo::~SwScriptInfo()
630 : {
631 0 : }
632 :
633 : /*************************************************************************
634 : * SwScriptInfo::WhichFont()
635 : *
636 : * Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to
637 : * Sw Script Types (SW_LATIN, SW_CJK, SW_CTL), used to identify the font
638 : *************************************************************************/
639 :
640 0 : sal_uInt8 SwScriptInfo::WhichFont( sal_Int32 nIdx, const OUString* pTxt, const SwScriptInfo* pSI )
641 : {
642 : SAL_WARN_IF( !pTxt && !pSI, "sw.core", "How should I determine the script type?" );
643 : sal_uInt16 nScript;
644 :
645 : // First we try to use our SwScriptInfo
646 0 : if ( pSI )
647 0 : nScript = pSI->ScriptType( nIdx );
648 : else
649 : // Ok, we have to ask the break iterator
650 0 : nScript = g_pBreakIt->GetRealScriptOfText( *pTxt, nIdx );
651 :
652 0 : switch ( nScript ) {
653 0 : case i18n::ScriptType::LATIN : return SW_LATIN;
654 0 : case i18n::ScriptType::ASIAN : return SW_CJK;
655 0 : case i18n::ScriptType::COMPLEX : return SW_CTL;
656 : }
657 :
658 : OSL_FAIL( "Somebody tells lies about the script type!" );
659 0 : return SW_LATIN;
660 : }
661 :
662 : /*************************************************************************
663 : * SwScriptInfo::InitScriptInfo()
664 : *
665 : * searches for script changes in rTxt and stores them
666 : *************************************************************************/
667 :
668 0 : void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode )
669 : {
670 0 : InitScriptInfo( rNode, nDefaultDir == UBIDI_RTL );
671 0 : }
672 :
673 0 : void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode, bool bRTL )
674 : {
675 0 : if( !g_pBreakIt->GetBreakIter().is() )
676 0 : return;
677 :
678 0 : const OUString& rTxt = rNode.GetTxt();
679 :
680 : // HIDDEN TEXT INFORMATION
681 :
682 0 : Range aRange( 0, !rTxt.isEmpty() ? rTxt.getLength() - 1 : 0 );
683 0 : MultiSelection aHiddenMulti( aRange );
684 0 : CalcHiddenRanges( rNode, aHiddenMulti );
685 :
686 0 : aHiddenChg.clear();
687 0 : for( size_t i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
688 : {
689 0 : const Range& rRange = aHiddenMulti.GetRange( i );
690 0 : const sal_Int32 nStart = rRange.Min();
691 0 : const sal_Int32 nEnd = rRange.Max() + 1;
692 :
693 0 : aHiddenChg.push_back( nStart );
694 0 : aHiddenChg.push_back( nEnd );
695 : }
696 :
697 : // SCRIPT AND SCRIPT RELATED INFORMATION
698 :
699 0 : sal_Int32 nChg = nInvalidityPos;
700 :
701 : // COMPLETE_STRING means the data structure is up to date
702 0 : nInvalidityPos = COMPLETE_STRING;
703 :
704 : // this is the default direction
705 0 : nDefaultDir = static_cast<sal_uInt8>(bRTL ? UBIDI_RTL : UBIDI_LTR);
706 :
707 : // counter for script info arrays
708 0 : sal_uInt16 nCnt = 0;
709 : // counter for compression information arrays
710 0 : sal_uInt16 nCntComp = 0;
711 : // counter for kashida array
712 0 : sal_uInt16 nCntKash = 0;
713 :
714 0 : sal_uInt8 nScript = i18n::ScriptType::LATIN;
715 :
716 : // compression type
717 0 : const SwCharCompressType aCompEnum = rNode.getIDocumentSettingAccess()->getCharacterCompressionType();
718 :
719 : // justification type
720 : const bool bAdjustBlock = SVX_ADJUST_BLOCK ==
721 0 : rNode.GetSwAttrSet().GetAdjust().GetAdjust();
722 :
723 : // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
724 :
725 0 : if( nChg )
726 : {
727 : // if change position = 0 we do not use any data from the arrays
728 : // because by deleting all characters of the first group at the beginning
729 : // of a paragraph nScript is set to a wrong value
730 : SAL_WARN_IF( !CountScriptChg(), "sw.core", "Where're my changes of script?" );
731 0 : while( nCnt < CountScriptChg() )
732 : {
733 0 : if ( nChg > GetScriptChg( nCnt ) )
734 0 : nCnt++;
735 : else
736 : {
737 0 : nScript = GetScriptType( nCnt );
738 0 : break;
739 : }
740 : }
741 0 : if( CHARCOMPRESS_NONE != aCompEnum )
742 : {
743 0 : while( nCntComp < CountCompChg() )
744 : {
745 0 : if ( nChg <= GetCompStart( nCntComp ) )
746 0 : break;
747 0 : nCntComp++;
748 : }
749 : }
750 0 : if ( bAdjustBlock )
751 : {
752 0 : while( nCntKash < CountKashida() )
753 : {
754 0 : if ( nChg <= GetKashida( nCntKash ) )
755 0 : break;
756 0 : nCntKash++;
757 : }
758 : }
759 : }
760 :
761 : // ADJUST nChg VALUE:
762 :
763 : // by stepping back one position we know that we are inside a group
764 : // declared as an nScript group
765 0 : if ( nChg )
766 0 : --nChg;
767 :
768 0 : const sal_Int32 nGrpStart = nCnt ? GetScriptChg( nCnt - 1 ) : 0;
769 :
770 : // we go back in our group until we reach the first character of
771 : // type nScript
772 0 : while ( nChg > nGrpStart &&
773 0 : nScript != g_pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
774 0 : --nChg;
775 :
776 : // If we are at the start of a group, we do not trust nScript,
777 : // we better get nScript from the breakiterator:
778 0 : if ( nChg == nGrpStart )
779 0 : nScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg );
780 :
781 : // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
782 :
783 : // remove invalid entries from script information arrays
784 0 : aScriptChanges.erase( aScriptChanges.begin() + nCnt, aScriptChanges.end() );
785 :
786 : // get the start of the last compression group
787 0 : sal_Int32 nLastCompression = nChg;
788 0 : if( nCntComp )
789 : {
790 0 : --nCntComp;
791 0 : nLastCompression = GetCompStart( nCntComp );
792 0 : if( nChg >= nLastCompression + GetCompLen( nCntComp ) )
793 : {
794 0 : nLastCompression = nChg;
795 0 : ++nCntComp;
796 : }
797 : }
798 :
799 : // remove invalid entries from compression information arrays
800 0 : aCompressionChanges.erase(aCompressionChanges.begin() + nCntComp, aCompressionChanges.end() );
801 :
802 : // get the start of the last kashida group
803 0 : sal_Int32 nLastKashida = nChg;
804 0 : if( nCntKash && i18n::ScriptType::COMPLEX == nScript )
805 : {
806 0 : --nCntKash;
807 0 : nLastKashida = GetKashida( nCntKash );
808 : }
809 :
810 : // remove invalid entries from kashida array
811 0 : aKashida.erase( aKashida.begin() + nCntKash, aKashida.end() );
812 :
813 : // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
814 : // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
815 :
816 0 : if( WEAK == g_pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
817 : {
818 : // If the beginning of the current group is weak, this means that
819 : // all of the characters in this grounp are weak. We have to assign
820 : // the scripts to these characters depending on the fonts which are
821 : // set for these characters to display them.
822 : sal_Int32 nEnd =
823 0 : g_pBreakIt->GetBreakIter()->endOfScript( rTxt, nChg, WEAK );
824 :
825 0 : if (nEnd > rTxt.getLength() || nEnd < 0)
826 0 : nEnd = rTxt.getLength();
827 :
828 0 : nScript = (sal_uInt8)GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() );
829 :
830 : SAL_WARN_IF( i18n::ScriptType::LATIN != nScript &&
831 : i18n::ScriptType::ASIAN != nScript &&
832 : i18n::ScriptType::COMPLEX != nScript, "sw.core", "Wrong default language" );
833 :
834 0 : nChg = nEnd;
835 :
836 : // Get next script type or set to weak in order to exit
837 0 : sal_uInt8 nNextScript = ( nEnd < rTxt.getLength() ) ?
838 0 : (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rTxt, nEnd ) :
839 0 : (sal_uInt8)WEAK;
840 :
841 0 : if ( nScript != nNextScript )
842 : {
843 0 : aScriptChanges.push_back( ScriptChangeInfo(nEnd, nScript) );
844 0 : nCnt++;
845 0 : nScript = nNextScript;
846 : }
847 : }
848 :
849 : // UPDATE THE SCRIPT INFO ARRAYS:
850 :
851 0 : while ( nChg < rTxt.getLength() || ( aScriptChanges.empty() && rTxt.isEmpty() ) )
852 : {
853 : SAL_WARN_IF( i18n::ScriptType::WEAK == nScript,
854 : "sw.core", "Inserting WEAK into SwScriptInfo structure" );
855 :
856 0 : sal_Int32 nSearchStt = nChg;
857 0 : nChg = g_pBreakIt->GetBreakIter()->endOfScript( rTxt, nSearchStt, nScript );
858 :
859 0 : if (nChg > rTxt.getLength() || nChg < 0)
860 0 : nChg = rTxt.getLength();
861 :
862 : // #i28203#
863 : // for 'complex' portions, we make sure that a portion does not contain more
864 : // than one script:
865 0 : if( i18n::ScriptType::COMPLEX == nScript )
866 : {
867 0 : const short nScriptType = ScriptTypeDetector::getCTLScriptType( rTxt, nSearchStt );
868 0 : sal_Int32 nNextCTLScriptStart = nSearchStt;
869 0 : short nCurrentScriptType = nScriptType;
870 0 : while( com::sun::star::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
871 : {
872 0 : nNextCTLScriptStart = ScriptTypeDetector::endOfCTLScriptType( rTxt, nNextCTLScriptStart );
873 0 : if( nNextCTLScriptStart >= rTxt.getLength() || nNextCTLScriptStart >= nChg )
874 0 : break;
875 0 : nCurrentScriptType = ScriptTypeDetector::getCTLScriptType( rTxt, nNextCTLScriptStart );
876 : }
877 0 : nChg = std::min( nChg, nNextCTLScriptStart );
878 : }
879 :
880 : // special case for dotted circle since it can be used with complex
881 : // before a mark, so we want it associated with the mark's script
882 0 : if (nChg < rTxt.getLength() && nChg > 0 && (i18n::ScriptType::WEAK ==
883 0 : g_pBreakIt->GetBreakIter()->getScriptType(rTxt,nChg - 1)))
884 : {
885 0 : int8_t nType = u_charType(rTxt[nChg] );
886 0 : if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
887 : nType == U_COMBINING_SPACING_MARK )
888 : {
889 0 : aScriptChanges.push_back( ScriptChangeInfo(nChg-1, nScript) );
890 : }
891 : else
892 : {
893 0 : aScriptChanges.push_back( ScriptChangeInfo(nChg, nScript) );
894 : }
895 : }
896 : else
897 : {
898 0 : aScriptChanges.push_back( ScriptChangeInfo(nChg, nScript) );
899 : }
900 0 : ++nCnt;
901 :
902 : // if current script is asian, we search for compressable characters
903 : // in this range
904 0 : if ( CHARCOMPRESS_NONE != aCompEnum &&
905 : i18n::ScriptType::ASIAN == nScript )
906 : {
907 0 : sal_uInt8 ePrevState = NONE;
908 : sal_uInt8 eState;
909 0 : sal_Int32 nPrevChg = nLastCompression;
910 :
911 0 : while ( nLastCompression < nChg )
912 : {
913 0 : sal_Unicode cChar = rTxt[ nLastCompression ];
914 :
915 : // examine current character
916 0 : switch ( cChar )
917 : {
918 : // Left punctuation found
919 : case 0x3008: case 0x300A: case 0x300C: case 0x300E:
920 : case 0x3010: case 0x3014: case 0x3016: case 0x3018:
921 : case 0x301A: case 0x301D:
922 0 : eState = SPECIAL_LEFT;
923 0 : break;
924 : // Right punctuation found
925 : case 0x3001: case 0x3002: case 0x3009: case 0x300B:
926 : case 0x300D: case 0x300F: case 0x3011: case 0x3015:
927 : case 0x3017: case 0x3019: case 0x301B: case 0x301E:
928 : case 0x301F:
929 0 : eState = SPECIAL_RIGHT;
930 0 : break;
931 : default:
932 0 : eState = static_cast<sal_uInt8>( ( 0x3040 <= cChar && 0x3100 > cChar ) ? KANA : NONE );
933 : }
934 :
935 : // insert range of compressable characters
936 0 : if( ePrevState != eState )
937 : {
938 0 : if ( ePrevState != NONE )
939 : {
940 : // insert start and type
941 0 : if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum ||
942 : ePrevState != KANA )
943 : {
944 0 : aCompressionChanges.push_back( CompressionChangeInfo(nPrevChg, nLastCompression - nPrevChg, ePrevState) );
945 : }
946 : }
947 :
948 0 : ePrevState = eState;
949 0 : nPrevChg = nLastCompression;
950 : }
951 :
952 0 : nLastCompression++;
953 : }
954 :
955 : // we still have to examine last entry
956 0 : if ( ePrevState != NONE )
957 : {
958 : // insert start and type
959 0 : if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum ||
960 : ePrevState != KANA )
961 : {
962 0 : aCompressionChanges.push_back( CompressionChangeInfo(nPrevChg, nLastCompression - nPrevChg, ePrevState) );
963 : }
964 0 : }
965 : }
966 :
967 : // we search for connecting opportunities (kashida)
968 0 : else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
969 : {
970 0 : SwScanner aScanner( rNode, rNode.GetTxt(), 0, ModelToViewHelper(),
971 : i18n::WordType::DICTIONARY_WORD,
972 0 : nLastKashida, nChg );
973 :
974 : // the search has to be performed on a per word base
975 0 : while ( aScanner.NextWord() )
976 : {
977 0 : const OUString& rWord = aScanner.GetWord();
978 :
979 0 : sal_Int32 nIdx = 0;
980 0 : sal_Int32 nKashidaPos = -1;
981 : sal_Unicode cCh;
982 0 : sal_Unicode cPrevCh = 0;
983 :
984 0 : sal_uInt16 nPriorityLevel = 7; // 0..6 = level found
985 : // 7 not found
986 :
987 0 : sal_Int32 nWordLen = rWord.getLength();
988 :
989 : // ignore trailing vowel chars
990 0 : while( nWordLen && isTransparentChar( rWord[ nWordLen - 1 ] ))
991 0 : --nWordLen;
992 :
993 0 : while (nIdx < nWordLen)
994 : {
995 0 : cCh = rWord[ nIdx ];
996 :
997 : // 1. Priority:
998 : // after user inserted kashida
999 0 : if ( 0x640 == cCh )
1000 : {
1001 0 : nKashidaPos = aScanner.GetBegin() + nIdx;
1002 0 : nPriorityLevel = 0;
1003 : }
1004 :
1005 : // 2. Priority:
1006 : // after a Seen or Sad
1007 0 : if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
1008 : {
1009 0 : if( isSeenOrSadChar( cCh )
1010 0 : && (rWord[ nIdx+1 ] != 0x200C) ) // #i98410#: prevent ZWNJ expansion
1011 : {
1012 0 : nKashidaPos = aScanner.GetBegin() + nIdx;
1013 0 : nPriorityLevel = 1;
1014 : }
1015 : }
1016 :
1017 : // 3. Priority:
1018 : // before final form of Teh Marbuta, Hah, Dal
1019 0 : if ( nPriorityLevel >= 2 && nIdx > 0 )
1020 : {
1021 0 : if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
1022 0 : isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word
1023 0 : ( isHahChar ( cCh ) && nIdx == nWordLen - 1)) // Hah (dual joining) only at end of word
1024 : {
1025 :
1026 : SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1027 : // check if character is connectable to previous character,
1028 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1029 : {
1030 0 : nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1031 0 : nPriorityLevel = 2;
1032 : }
1033 : }
1034 : }
1035 :
1036 : // 4. Priority:
1037 : // before final form of Alef, Lam or Kaf
1038 0 : if ( nPriorityLevel >= 3 && nIdx > 0 )
1039 : {
1040 0 : if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word
1041 0 : (( isLamChar ( cCh ) || // Lam
1042 0 : isKafChar ( cCh ) || // Kaf (both dual joining)
1043 0 : isGafChar ( cCh ) )
1044 0 : && nIdx == nWordLen - 1)) // only at end of word
1045 : {
1046 : SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1047 : // check if character is connectable to previous character,
1048 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1049 : {
1050 0 : nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1051 0 : nPriorityLevel = 3;
1052 : }
1053 : }
1054 : }
1055 :
1056 : // 5. Priority:
1057 : // before media Bah
1058 0 : if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
1059 : {
1060 0 : if ( isBaaChar ( cCh )) // Bah
1061 : {
1062 : // check if next character is Reh, Yeh or Alef Maksura
1063 0 : sal_Unicode cNextCh = rWord[ nIdx + 1 ];
1064 0 : if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
1065 : {
1066 : SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1067 : // check if character is connectable to previous character,
1068 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1069 : {
1070 0 : nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1071 0 : nPriorityLevel = 4;
1072 : }
1073 : }
1074 : }
1075 : }
1076 :
1077 : // 6. Priority:
1078 : // before the final form of Waw, Ain, Qaf and Fa
1079 0 : if ( nPriorityLevel >= 5 && nIdx > 0 )
1080 : {
1081 0 : if ( isWawChar ( cCh ) || // Wav (right joining)
1082 : // final form may appear in the middle of word
1083 0 : (( isAinChar ( cCh ) || // Ain (dual joining)
1084 0 : isQafChar ( cCh ) || // Qaf (dual joining)
1085 0 : isFehChar ( cCh ) ) // Feh (dual joining)
1086 0 : && nIdx == nWordLen - 1)) // only at end of word
1087 : {
1088 : SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1089 : // check if character is connectable to previous character,
1090 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1091 : {
1092 0 : nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1093 0 : nPriorityLevel = 5;
1094 : }
1095 : }
1096 : }
1097 :
1098 : // other connecting possibilities
1099 0 : if ( nPriorityLevel >= 6 && nIdx > 0 )
1100 : {
1101 : // remaining right joiners
1102 : // Reh, Zain, Thal,
1103 0 : if ( isRehChar ( cCh ) || // Reh Zain (right joining)
1104 : // final form may appear in the middle of word
1105 0 : ( 0x60C <= cCh && 0x6FE >= cCh // all others
1106 0 : && nIdx == nWordLen - 1)) // only at end of word
1107 : {
1108 : SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1109 : // check if character is connectable to previous character,
1110 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1111 : {
1112 0 : nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1113 0 : nPriorityLevel = 6;
1114 : }
1115 : }
1116 : }
1117 :
1118 : // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
1119 : // Damma, Kasra, Shadda and Sukun when checking if
1120 : // a character can be connected to previous character.
1121 0 : if ( !isTransparentChar ( cCh) )
1122 0 : cPrevCh = cCh;
1123 :
1124 0 : ++nIdx;
1125 : } // end of current word
1126 :
1127 0 : if ( -1 != nKashidaPos )
1128 : {
1129 0 : aKashida.insert( aKashida.begin() + nCntKash, nKashidaPos);
1130 0 : nCntKash++;
1131 : }
1132 0 : } // end of kashida search
1133 : }
1134 :
1135 0 : if ( nChg < rTxt.getLength() )
1136 0 : nScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg );
1137 :
1138 0 : nLastCompression = nChg;
1139 0 : nLastKashida = nChg;
1140 : }
1141 :
1142 : #if OSL_DEBUG_LEVEL > 0
1143 : // check kashida data
1144 : long nTmpKashidaPos = -1;
1145 : bool bWrongKash = false;
1146 : for (size_t i = 0; i < aKashida.size(); ++i )
1147 : {
1148 : long nCurrKashidaPos = GetKashida( i );
1149 : if ( nCurrKashidaPos <= nTmpKashidaPos )
1150 : {
1151 : bWrongKash = true;
1152 : break;
1153 : }
1154 : nTmpKashidaPos = nCurrKashidaPos;
1155 : }
1156 : SAL_WARN_IF( bWrongKash, "sw.core", "Kashida array contains wrong data" );
1157 : #endif
1158 :
1159 : // remove invalid entries from direction information arrays
1160 0 : aDirectionChanges.clear();
1161 :
1162 : // Perform Unicode Bidi Algorithm for text direction information
1163 0 : bool bPerformUBA = UBIDI_LTR != nDefaultDir;
1164 0 : nCnt = 0;
1165 0 : while( !bPerformUBA && nCnt < CountScriptChg() )
1166 : {
1167 0 : if ( i18n::ScriptType::COMPLEX == GetScriptType( nCnt++ ) )
1168 0 : bPerformUBA = true;
1169 : }
1170 :
1171 : // do not call the unicode bidi algorithm if not required
1172 0 : if ( bPerformUBA )
1173 : {
1174 0 : UpdateBidiInfo( rTxt );
1175 :
1176 : // #i16354# Change script type for RTL text to CTL:
1177 : // 1. All text in RTL runs will use the CTL font
1178 : // #i89825# change the script type also to CTL (hennerdrewes)
1179 : // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
1180 0 : for ( sal_uInt32 nDirIdx = 0; nDirIdx < aDirectionChanges.size(); ++nDirIdx )
1181 : {
1182 0 : const sal_uInt8 nCurrDirType = GetDirType( nDirIdx );
1183 : // nStart ist start of RTL run:
1184 0 : const sal_Int32 nStart = nDirIdx > 0 ? GetDirChg( nDirIdx - 1 ) : 0;
1185 : // nEnd is end of RTL run:
1186 0 : const sal_Int32 nEnd = GetDirChg( nDirIdx );
1187 :
1188 0 : if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
1189 0 : ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( rTxt, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
1190 : {
1191 : // nScriptIdx points into the ScriptArrays:
1192 0 : size_t nScriptIdx = 0;
1193 :
1194 : // Skip entries in ScriptArray which are not inside the RTL run:
1195 : // Make nScriptIdx become the index of the script group with
1196 : // 1. nStartPosOfGroup <= nStart and
1197 : // 2. nEndPosOfGroup > nStart
1198 0 : while ( GetScriptChg( nScriptIdx ) <= nStart )
1199 0 : ++nScriptIdx;
1200 :
1201 0 : const sal_Int32 nStartPosOfGroup = nScriptIdx ? GetScriptChg( nScriptIdx - 1 ) : 0;
1202 0 : const sal_uInt8 nScriptTypeOfGroup = GetScriptType( nScriptIdx );
1203 :
1204 : SAL_WARN_IF( nStartPosOfGroup > nStart || GetScriptChg( nScriptIdx ) <= nStart,
1205 : "sw.core", "Script override with CTL font trouble" );
1206 :
1207 : // Check if we have to insert a new script change at
1208 : // position nStart. If nStartPosOfGroup < nStart,
1209 : // we have to insert a new script change:
1210 0 : if ( nStart > 0 && nStartPosOfGroup < nStart )
1211 : {
1212 0 : aScriptChanges.insert(aScriptChanges.begin() + nScriptIdx,
1213 0 : ScriptChangeInfo(nStart, nScriptTypeOfGroup) );
1214 0 : ++nScriptIdx;
1215 : }
1216 :
1217 : // Remove entries in ScriptArray which end inside the RTL run:
1218 0 : while ( nScriptIdx < aScriptChanges.size() && GetScriptChg( nScriptIdx ) <= nEnd )
1219 : {
1220 0 : aScriptChanges.erase(aScriptChanges.begin() + nScriptIdx);
1221 : }
1222 :
1223 : // Insert a new entry in ScriptArray for the end of the RTL run:
1224 0 : aScriptChanges.insert(aScriptChanges.begin() + nScriptIdx,
1225 0 : ScriptChangeInfo(nEnd, i18n::ScriptType::COMPLEX) );
1226 :
1227 : #if OSL_DEBUG_LEVEL > 1
1228 : // Check that ScriptChangeInfos are in increasing order of
1229 : // position and that we don't have "empty" changes.
1230 : sal_uInt8 nLastTyp = i18n::ScriptType::WEAK;
1231 : sal_Int32 nLastPos = 0;
1232 : for (std::vector<ScriptChangeInfo>::const_iterator i2 = aScriptChanges.begin(); i2 != aScriptChanges.end(); ++i2)
1233 : {
1234 : SAL_WARN_IF( nLastTyp == i2->type ||
1235 : nLastPos >= i2->position,
1236 : "sw.core", "Heavy InitScriptType() confusion" );
1237 : nLastPos = i2->position;
1238 : nLastTyp = i2->type;
1239 : }
1240 : #endif
1241 : }
1242 : }
1243 0 : }
1244 : }
1245 :
1246 0 : void SwScriptInfo::UpdateBidiInfo( const OUString& rTxt )
1247 : {
1248 : // remove invalid entries from direction information arrays
1249 0 : aDirectionChanges.clear();
1250 :
1251 : // Bidi functions from icu 2.0
1252 :
1253 0 : UErrorCode nError = U_ZERO_ERROR;
1254 0 : UBiDi* pBidi = ubidi_openSized( rTxt.getLength(), 0, &nError );
1255 0 : nError = U_ZERO_ERROR;
1256 :
1257 0 : ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rTxt.getStr()), rTxt.getLength(), // UChar != sal_Unicode in MinGW
1258 0 : nDefaultDir, NULL, &nError );
1259 0 : nError = U_ZERO_ERROR;
1260 0 : int nCount = ubidi_countRuns( pBidi, &nError );
1261 0 : int32_t nStart = 0;
1262 : int32_t nEnd;
1263 : UBiDiLevel nCurrDir;
1264 0 : for ( int nIdx = 0; nIdx < nCount; ++nIdx )
1265 : {
1266 0 : ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
1267 0 : aDirectionChanges.push_back( DirectionChangeInfo(nEnd, nCurrDir) );
1268 0 : nStart = nEnd;
1269 : }
1270 :
1271 0 : ubidi_close( pBidi );
1272 0 : }
1273 :
1274 : /*************************************************************************
1275 : * SwScriptInfo::NextScriptChg(..)
1276 : * returns the position of the next character which belongs to another script
1277 : * than the character of the actual (input) position.
1278 : * If there's no script change until the end of the paragraph, it will return
1279 : * COMPLETE_STRING.
1280 : * Scripts are Asian (Chinese, Japanese, Korean),
1281 : * Latin ( English etc.)
1282 : * and Complex ( Hebrew, Arabian )
1283 : *************************************************************************/
1284 :
1285 0 : sal_Int32 SwScriptInfo::NextScriptChg(const sal_Int32 nPos) const
1286 : {
1287 0 : sal_uInt16 nEnd = CountScriptChg();
1288 0 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1289 : {
1290 0 : if( nPos < GetScriptChg( nX ) )
1291 0 : return GetScriptChg( nX );
1292 : }
1293 :
1294 0 : return COMPLETE_STRING;
1295 : }
1296 :
1297 : /*************************************************************************
1298 : * SwScriptInfo::ScriptType(..)
1299 : * returns the script of the character at the input position
1300 : *************************************************************************/
1301 :
1302 0 : sal_uInt8 SwScriptInfo::ScriptType(const sal_Int32 nPos) const
1303 : {
1304 0 : sal_uInt16 nEnd = CountScriptChg();
1305 0 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1306 : {
1307 0 : if( nPos < GetScriptChg( nX ) )
1308 0 : return GetScriptType( nX );
1309 : }
1310 :
1311 : // the default is the application language script
1312 0 : return (sal_uInt8)GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() );
1313 : }
1314 :
1315 0 : sal_Int32 SwScriptInfo::NextDirChg( const sal_Int32 nPos,
1316 : const sal_uInt8* pLevel ) const
1317 : {
1318 0 : sal_uInt8 nCurrDir = pLevel ? *pLevel : 62;
1319 0 : sal_uInt16 nEnd = CountDirChg();
1320 0 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1321 : {
1322 0 : if( nPos < GetDirChg( nX ) &&
1323 0 : ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) )
1324 0 : return GetDirChg( nX );
1325 : }
1326 :
1327 0 : return COMPLETE_STRING;
1328 : }
1329 :
1330 0 : sal_uInt8 SwScriptInfo::DirType(const sal_Int32 nPos) const
1331 : {
1332 0 : sal_uInt16 nEnd = CountDirChg();
1333 0 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1334 : {
1335 0 : if( nPos < GetDirChg( nX ) )
1336 0 : return GetDirType( nX );
1337 : }
1338 :
1339 0 : return 0;
1340 : }
1341 :
1342 : /*************************************************************************
1343 : * SwScriptInfo::MaskHiddenRanges(..)
1344 : * Takes a string and replaced the hidden ranges with cChar.
1345 : **************************************************************************/
1346 :
1347 0 : sal_Int32 SwScriptInfo::MaskHiddenRanges( const SwTxtNode& rNode, OUStringBuffer & rText,
1348 : const sal_Int32 nStt, const sal_Int32 nEnd,
1349 : const sal_Unicode cChar )
1350 : {
1351 : assert(rNode.GetTxt().getLength() == rText.getLength());
1352 :
1353 0 : PositionList aList;
1354 : sal_Int32 nHiddenStart;
1355 : sal_Int32 nHiddenEnd;
1356 0 : sal_uInt16 nNumOfHiddenChars = 0;
1357 0 : GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1358 0 : PositionList::const_reverse_iterator rFirst( aList.end() );
1359 0 : PositionList::const_reverse_iterator rLast( aList.begin() );
1360 0 : while ( rFirst != rLast )
1361 : {
1362 0 : nHiddenEnd = *(rFirst++);
1363 0 : nHiddenStart = *(rFirst++);
1364 :
1365 0 : if ( nHiddenEnd < nStt || nHiddenStart > nEnd )
1366 0 : continue;
1367 :
1368 0 : while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
1369 : {
1370 0 : if ( nHiddenStart >= nStt && nHiddenStart < nEnd )
1371 : {
1372 0 : rText[nHiddenStart] = cChar;
1373 0 : ++nNumOfHiddenChars;
1374 : }
1375 0 : ++nHiddenStart;
1376 : }
1377 : }
1378 :
1379 0 : return nNumOfHiddenChars;
1380 : }
1381 :
1382 : /*************************************************************************
1383 : * SwScriptInfo::DeleteHiddenRanges(..)
1384 : * Takes a SwTxtNode and deletes the hidden ranges from the node.
1385 : **************************************************************************/
1386 :
1387 0 : void SwScriptInfo::DeleteHiddenRanges( SwTxtNode& rNode )
1388 : {
1389 0 : PositionList aList;
1390 : sal_Int32 nHiddenStart;
1391 : sal_Int32 nHiddenEnd;
1392 0 : GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1393 0 : PositionList::const_reverse_iterator rFirst( aList.end() );
1394 0 : PositionList::const_reverse_iterator rLast( aList.begin() );
1395 0 : while ( rFirst != rLast )
1396 : {
1397 0 : nHiddenEnd = *(rFirst++);
1398 0 : nHiddenStart = *(rFirst++);
1399 :
1400 0 : SwPaM aPam( rNode, nHiddenStart, rNode, nHiddenEnd );
1401 0 : rNode.getIDocumentContentOperations()->DeleteRange( aPam );
1402 0 : }
1403 0 : }
1404 :
1405 : /*************************************************************************
1406 : * SwScriptInfo::GetBoundsOfHiddenRange(..)
1407 : * static version
1408 : **************************************************************************/
1409 :
1410 0 : bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTxtNode& rNode, sal_Int32 nPos,
1411 : sal_Int32& rnStartPos, sal_Int32& rnEndPos,
1412 : PositionList* pList )
1413 : {
1414 0 : rnStartPos = COMPLETE_STRING;
1415 0 : rnEndPos = 0;
1416 :
1417 0 : bool bNewContainsHiddenChars = false;
1418 :
1419 : // Optimization: First examine the flags at the text node:
1420 :
1421 0 : if ( !rNode.IsCalcHiddenCharFlags() )
1422 : {
1423 0 : bool bWholePara = rNode.HasHiddenCharAttribute( true );
1424 0 : bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false );
1425 0 : if ( !bContainsHiddenChars )
1426 0 : return false;
1427 :
1428 0 : if ( bWholePara )
1429 : {
1430 0 : if ( pList )
1431 : {
1432 0 : pList->push_back( 0 );
1433 0 : pList->push_back(rNode.GetTxt().getLength());
1434 : }
1435 :
1436 0 : rnStartPos = 0;
1437 0 : rnEndPos = rNode.GetTxt().getLength();
1438 0 : return true;
1439 : }
1440 : }
1441 :
1442 0 : const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
1443 0 : if ( pSI )
1444 : {
1445 :
1446 : // Check first, if we have a valid SwScriptInfo object for this text node:
1447 :
1448 0 : bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList );
1449 : const bool bNewHiddenCharsHidePara =
1450 0 : rnStartPos == 0 && rnEndPos >= rNode.GetTxt().getLength();
1451 0 : rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
1452 : }
1453 : else
1454 : {
1455 :
1456 : // No valid SwScriptInfo Object, we have to do it the hard way:
1457 :
1458 0 : Range aRange(0, (!rNode.GetTxt().isEmpty())
1459 0 : ? rNode.GetTxt().getLength() - 1
1460 0 : : 0);
1461 0 : MultiSelection aHiddenMulti( aRange );
1462 0 : SwScriptInfo::CalcHiddenRanges( rNode, aHiddenMulti );
1463 0 : for( size_t i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1464 : {
1465 0 : const Range& rRange = aHiddenMulti.GetRange( i );
1466 0 : const sal_Int32 nHiddenStart = rRange.Min();
1467 0 : const sal_Int32 nHiddenEnd = rRange.Max() + 1;
1468 :
1469 0 : if ( nHiddenStart > nPos )
1470 0 : break;
1471 0 : if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1472 : {
1473 0 : rnStartPos = nHiddenStart;
1474 : rnEndPos = std::min<sal_Int32>(nHiddenEnd,
1475 0 : rNode.GetTxt().getLength());
1476 0 : break;
1477 : }
1478 : }
1479 :
1480 0 : if ( pList )
1481 : {
1482 0 : for( size_t i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1483 : {
1484 0 : const Range& rRange = aHiddenMulti.GetRange( i );
1485 0 : pList->push_back( rRange.Min() );
1486 0 : pList->push_back( rRange.Max() + 1 );
1487 : }
1488 : }
1489 :
1490 0 : bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0;
1491 : }
1492 :
1493 0 : return bNewContainsHiddenChars;
1494 : }
1495 :
1496 : /*************************************************************************
1497 : * SwScriptInfo::GetBoundsOfHiddenRange(..)
1498 : * non-static version
1499 : **************************************************************************/
1500 :
1501 0 : bool SwScriptInfo::GetBoundsOfHiddenRange( sal_Int32 nPos, sal_Int32& rnStartPos,
1502 : sal_Int32& rnEndPos, PositionList* pList ) const
1503 : {
1504 0 : rnStartPos = COMPLETE_STRING;
1505 0 : rnEndPos = 0;
1506 :
1507 0 : sal_uInt16 nEnd = CountHiddenChg();
1508 0 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1509 : {
1510 0 : const sal_Int32 nHiddenStart = GetHiddenChg( nX++ );
1511 0 : const sal_Int32 nHiddenEnd = GetHiddenChg( nX );
1512 :
1513 0 : if ( nHiddenStart > nPos )
1514 0 : break;
1515 0 : if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1516 : {
1517 0 : rnStartPos = nHiddenStart;
1518 0 : rnEndPos = nHiddenEnd;
1519 0 : break;
1520 : }
1521 : }
1522 :
1523 0 : if ( pList )
1524 : {
1525 0 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1526 : {
1527 0 : pList->push_back( GetHiddenChg( nX++ ) );
1528 0 : pList->push_back( GetHiddenChg( nX ) );
1529 : }
1530 : }
1531 :
1532 0 : return CountHiddenChg() > 0;
1533 : }
1534 :
1535 0 : bool SwScriptInfo::IsInHiddenRange( const SwTxtNode& rNode, sal_Int32 nPos )
1536 : {
1537 : sal_Int32 nStartPos;
1538 : sal_Int32 nEndPos;
1539 0 : SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos );
1540 0 : return nStartPos != COMPLETE_STRING;
1541 : }
1542 :
1543 : #ifdef DBG_UTIL
1544 : /*************************************************************************
1545 : * SwScriptInfo::CompType(..)
1546 : * returns the type of the compressed character
1547 : *************************************************************************/
1548 :
1549 : sal_uInt8 SwScriptInfo::CompType( const sal_Int32 nPos ) const
1550 : {
1551 : sal_uInt16 nEnd = CountCompChg();
1552 : for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1553 : {
1554 : sal_Int32 nChg = GetCompStart( nX );
1555 :
1556 : if ( nPos < nChg )
1557 : return NONE;
1558 :
1559 : if( nPos < nChg + GetCompLen( nX ) )
1560 : return GetCompType( nX );
1561 : }
1562 : return NONE;
1563 : }
1564 : #endif
1565 :
1566 : /*************************************************************************
1567 : * SwScriptInfo::HasKana()
1568 : * returns, if there are compressable kanas or specials
1569 : * between nStart and nEnd
1570 : *************************************************************************/
1571 :
1572 0 : sal_uInt16 SwScriptInfo::HasKana( sal_Int32 nStart, const sal_Int32 nLen ) const
1573 : {
1574 0 : sal_uInt16 nCnt = CountCompChg();
1575 0 : sal_Int32 nEnd = nStart + nLen;
1576 :
1577 0 : for( sal_uInt16 nX = 0; nX < nCnt; ++nX )
1578 : {
1579 0 : sal_Int32 nKanaStart = GetCompStart( nX );
1580 0 : sal_Int32 nKanaEnd = nKanaStart + GetCompLen( nX );
1581 :
1582 0 : if ( nKanaStart >= nEnd )
1583 0 : return USHRT_MAX;
1584 :
1585 0 : if ( nStart < nKanaEnd )
1586 0 : return nX;
1587 : }
1588 :
1589 0 : return USHRT_MAX;
1590 : }
1591 :
1592 0 : long SwScriptInfo::Compress( sal_Int32* pKernArray, sal_Int32 nIdx, sal_Int32 nLen,
1593 : const sal_uInt16 nCompress, const sal_uInt16 nFontHeight,
1594 : Point* pPoint ) const
1595 : {
1596 : SAL_WARN_IF( !nCompress, "sw.core", "Compression without compression?!" );
1597 : SAL_WARN_IF( !nLen, "sw.core", "Compression without text?!" );
1598 0 : sal_uInt16 nCompCount = CountCompChg();
1599 :
1600 : // In asian typography, there are full width and half width characters.
1601 : // Full width punctuation characters can be compressed by 50 %
1602 : // to determine this, we compare the font width with 75 % of its height
1603 0 : sal_uInt16 nMinWidth = ( 3 * nFontHeight ) / 4;
1604 :
1605 0 : sal_uInt16 nCompIdx = HasKana( nIdx, nLen );
1606 :
1607 0 : if ( USHRT_MAX == nCompIdx )
1608 0 : return 0;
1609 :
1610 0 : sal_Int32 nChg = GetCompStart( nCompIdx );
1611 0 : sal_Int32 nCompLen = GetCompLen( nCompIdx );
1612 0 : sal_uInt16 nI = 0;
1613 0 : nLen += nIdx;
1614 :
1615 0 : if( nChg > nIdx )
1616 : {
1617 0 : nI = nChg - nIdx;
1618 0 : nIdx = nChg;
1619 : }
1620 0 : else if( nIdx < nChg + nCompLen )
1621 0 : nCompLen -= nIdx - nChg;
1622 :
1623 0 : if( nIdx > nLen || nCompIdx >= nCompCount )
1624 0 : return 0;
1625 :
1626 0 : long nSub = 0;
1627 0 : long nLast = nI ? pKernArray[ nI - 1 ] : 0;
1628 0 : do
1629 : {
1630 0 : sal_uInt16 nType = GetCompType( nCompIdx );
1631 : #ifdef DBG_UTIL
1632 : SAL_WARN_IF( nType != CompType( nIdx ), "sw.core", "Gimme the right type!" );
1633 : #endif
1634 0 : nCompLen += nIdx;
1635 0 : if( nCompLen > nLen )
1636 0 : nCompLen = nLen;
1637 :
1638 : // are we allowed to compress the character?
1639 0 : if ( pKernArray[ nI ] - nLast < nMinWidth )
1640 : {
1641 0 : nIdx++; nI++;
1642 : }
1643 : else
1644 : {
1645 0 : while( nIdx < nCompLen )
1646 : {
1647 : SAL_WARN_IF( SwScriptInfo::NONE == nType, "sw.core", "None compression?!" );
1648 :
1649 : // nLast is width of current character
1650 0 : nLast -= pKernArray[ nI ];
1651 :
1652 0 : nLast *= nCompress;
1653 0 : long nMove = 0;
1654 0 : if( SwScriptInfo::KANA != nType )
1655 : {
1656 0 : nLast /= 20000;
1657 0 : if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType )
1658 : {
1659 0 : if( nI )
1660 0 : nMove = nLast;
1661 : else
1662 : {
1663 0 : pPoint->X() += nLast;
1664 0 : nLast = 0;
1665 : }
1666 : }
1667 : }
1668 : else
1669 0 : nLast /= 100000;
1670 0 : nSub -= nLast;
1671 0 : nLast = pKernArray[ nI ];
1672 0 : if( nMove )
1673 0 : pKernArray[ nI - 1 ] += nMove;
1674 0 : pKernArray[ nI++ ] -= nSub;
1675 0 : ++nIdx;
1676 : }
1677 : }
1678 :
1679 0 : if( nIdx >= nLen )
1680 0 : break;
1681 :
1682 0 : sal_Int32 nTmpChg = nLen;
1683 0 : if( ++nCompIdx < nCompCount )
1684 : {
1685 0 : nTmpChg = GetCompStart( nCompIdx );
1686 0 : if( nTmpChg > nLen )
1687 0 : nTmpChg = nLen;
1688 0 : nCompLen = GetCompLen( nCompIdx );
1689 : }
1690 :
1691 0 : while( nIdx < nTmpChg )
1692 : {
1693 0 : nLast = pKernArray[ nI ];
1694 0 : pKernArray[ nI++ ] -= nSub;
1695 0 : ++nIdx;
1696 : }
1697 : } while( nIdx < nLen );
1698 0 : return nSub;
1699 : }
1700 :
1701 : /*************************************************************************
1702 : * SwScriptInfo::KashidaJustify()
1703 : *************************************************************************/
1704 :
1705 : // Note on calling KashidaJustify():
1706 : // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
1707 : // total number of kashida positions, or the number of kashida positions after some positions
1708 : // have been dropped, depending on the state of the aKashidaInvalid array.
1709 :
1710 0 : sal_Int32 SwScriptInfo::KashidaJustify( sal_Int32* pKernArray,
1711 : sal_Int32* pScrArray,
1712 : sal_Int32 nStt,
1713 : sal_Int32 nLen,
1714 : long nSpaceAdd ) const
1715 : {
1716 : SAL_WARN_IF( !nLen, "sw.core", "Kashida justification without text?!" );
1717 :
1718 0 : if( !IsKashidaLine(nStt))
1719 0 : return -1;
1720 :
1721 : // evaluate kashida information in collected in SwScriptInfo
1722 :
1723 0 : size_t nCntKash = 0;
1724 0 : while( nCntKash < CountKashida() )
1725 : {
1726 0 : if ( nStt <= GetKashida( nCntKash ) )
1727 0 : break;
1728 0 : ++nCntKash;
1729 : }
1730 :
1731 0 : const sal_Int32 nEnd = nStt + nLen;
1732 :
1733 0 : size_t nCntKashEnd = nCntKash;
1734 0 : while ( nCntKashEnd < CountKashida() )
1735 : {
1736 0 : if ( nEnd <= GetKashida( nCntKashEnd ) )
1737 0 : break;
1738 0 : ++nCntKashEnd;
1739 : }
1740 :
1741 0 : size_t nActualKashCount = nCntKashEnd - nCntKash;
1742 0 : for (size_t i = nCntKash; i < nCntKashEnd; ++i)
1743 : {
1744 0 : if ( nActualKashCount && !IsKashidaValid ( i ) )
1745 0 : --nActualKashCount;
1746 : }
1747 :
1748 0 : if ( !pKernArray )
1749 0 : return nActualKashCount;
1750 :
1751 : // do nothing if there is no more kashida
1752 0 : if ( nCntKash < CountKashida() )
1753 : {
1754 : // skip any invalid kashidas
1755 0 : while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
1756 0 : ++nCntKash;
1757 :
1758 0 : sal_Int32 nKashidaPos = GetKashida( nCntKash );
1759 0 : sal_Int32 nIdx = nKashidaPos;
1760 0 : long nKashAdd = nSpaceAdd;
1761 :
1762 0 : while ( nIdx < nEnd )
1763 : {
1764 0 : sal_Int32 nArrayPos = nIdx - nStt;
1765 :
1766 : // next kashida position
1767 0 : ++nCntKash;
1768 0 : while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
1769 0 : ++nCntKash;
1770 :
1771 0 : nIdx = nCntKash < CountKashida() && IsKashidaValid ( nCntKash ) ? GetKashida( nCntKash ) : nEnd;
1772 0 : if ( nIdx > nEnd )
1773 0 : nIdx = nEnd;
1774 :
1775 0 : const sal_Int32 nArrayEnd = nIdx - nStt;
1776 :
1777 0 : while ( nArrayPos < nArrayEnd )
1778 : {
1779 0 : pKernArray[ nArrayPos ] += nKashAdd;
1780 0 : if ( pScrArray )
1781 0 : pScrArray[ nArrayPos ] += nKashAdd;
1782 0 : ++nArrayPos;
1783 : }
1784 0 : nKashAdd += nSpaceAdd;
1785 : }
1786 : }
1787 :
1788 0 : return 0;
1789 : }
1790 :
1791 : /*************************************************************************
1792 : * SwScriptInfo::IsArabicText()
1793 : *
1794 : * Checks if the current text is 'Arabic' text. Note that only the first
1795 : * character has to be checked because a ctl portion only contains one
1796 : * script, see NewTxtPortion
1797 : *************************************************************************/
1798 0 : bool SwScriptInfo::IsArabicText( const OUString& rTxt, sal_Int32 nStt, sal_Int32 nLen )
1799 : {
1800 : using namespace ::com::sun::star::i18n;
1801 : static const ScriptTypeList typeList[] = {
1802 : { UnicodeScript_kArabic, UnicodeScript_kArabic, UnicodeScript_kArabic }, // 11,
1803 : { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, UnicodeScript_kScriptCount } // 88
1804 : };
1805 :
1806 : // go forward if current position does not hold a regular character:
1807 0 : const CharClass& rCC = GetAppCharClass();
1808 0 : sal_Int32 nIdx = nStt;
1809 0 : const sal_Int32 nEnd = nStt + nLen;
1810 0 : while ( nIdx < nEnd && !rCC.isLetterNumeric( rTxt, nIdx ) )
1811 : {
1812 0 : ++nIdx;
1813 : }
1814 :
1815 0 : if( nIdx == nEnd )
1816 : {
1817 : // no regular character found in this portion. Go backward:
1818 0 : --nIdx;
1819 0 : while ( nIdx >= 0 && !rCC.isLetterNumeric( rTxt, nIdx ) )
1820 : {
1821 0 : --nIdx;
1822 : }
1823 : }
1824 :
1825 0 : if( nIdx >= 0 )
1826 : {
1827 0 : const sal_Unicode cCh = rTxt[nIdx];
1828 0 : const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, UnicodeScript_kScriptCount );
1829 0 : return type == UnicodeScript_kArabic;
1830 : }
1831 0 : return false;
1832 : }
1833 :
1834 0 : bool SwScriptInfo::IsKashidaValid(sal_Int32 nKashPos) const
1835 : {
1836 0 : for ( size_t i = 0; i < aKashidaInvalid.size(); ++i )
1837 : {
1838 0 : if ( aKashidaInvalid [ i ] == nKashPos )
1839 0 : return false;
1840 : }
1841 0 : return true;
1842 : }
1843 :
1844 0 : void SwScriptInfo::ClearKashidaInvalid(sal_Int32 nKashPos)
1845 : {
1846 0 : for ( size_t i = 0; i < aKashidaInvalid.size(); ++i )
1847 : {
1848 0 : if ( aKashidaInvalid [ i ] == nKashPos )
1849 : {
1850 0 : aKashidaInvalid.erase ( aKashidaInvalid.begin() + i );
1851 0 : return;
1852 : }
1853 : }
1854 : }
1855 :
1856 : /*************************************************************************
1857 : * SwScriptInfo::MarkOrClearKashidaInvalid()
1858 : *
1859 : * bMark == true:
1860 : * marks the first valid kashida in the given text range as invalid
1861 : *
1862 : * bMark == false:
1863 : * clears all kashida invalid flags in the given text range
1864 : *************************************************************************/
1865 :
1866 0 : bool SwScriptInfo::MarkOrClearKashidaInvalid(sal_Int32 nStt, sal_Int32 nLen,
1867 : bool bMark, sal_Int32 nMarkCount)
1868 : {
1869 0 : size_t nCntKash = 0;
1870 0 : while( nCntKash < CountKashida() )
1871 : {
1872 0 : if ( nStt <= GetKashida( nCntKash ) )
1873 0 : break;
1874 0 : nCntKash++;
1875 : }
1876 :
1877 0 : const sal_Int32 nEnd = nStt + nLen;
1878 :
1879 0 : while ( nCntKash < CountKashida() )
1880 : {
1881 0 : if ( nEnd <= GetKashida( nCntKash ) )
1882 0 : break;
1883 0 : if(bMark)
1884 : {
1885 0 : if ( IsKashidaValid ( nCntKash ) )
1886 : {
1887 0 : MarkKashidaInvalid ( nCntKash );
1888 0 : --nMarkCount;
1889 0 : if (!nMarkCount)
1890 0 : return true;
1891 : }
1892 : }
1893 : else
1894 : {
1895 0 : ClearKashidaInvalid ( nCntKash );
1896 : }
1897 0 : nCntKash++;
1898 : }
1899 0 : return false;
1900 : }
1901 :
1902 0 : void SwScriptInfo::MarkKashidaInvalid(sal_Int32 nKashPos)
1903 : {
1904 0 : aKashidaInvalid.push_back(nKashPos);
1905 0 : }
1906 :
1907 : /*************************************************************************
1908 : * SwScriptInfo::GetKashidaPositions()
1909 : * retrieve the kashida positions in the given text range
1910 : *************************************************************************/
1911 :
1912 0 : sal_Int32 SwScriptInfo::GetKashidaPositions(sal_Int32 nStt, sal_Int32 nLen,
1913 : sal_Int32* pKashidaPosition)
1914 : {
1915 0 : size_t nCntKash = 0;
1916 0 : while( nCntKash < CountKashida() )
1917 : {
1918 0 : if ( nStt <= GetKashida( nCntKash ) )
1919 0 : break;
1920 0 : nCntKash++;
1921 : }
1922 :
1923 0 : const sal_Int32 nEnd = nStt + nLen;
1924 :
1925 0 : size_t nCntKashEnd = nCntKash;
1926 0 : while ( nCntKashEnd < CountKashida() )
1927 : {
1928 0 : if ( nEnd <= GetKashida( nCntKashEnd ) )
1929 0 : break;
1930 0 : pKashidaPosition [ nCntKashEnd - nCntKash ] = GetKashida ( nCntKashEnd );
1931 0 : nCntKashEnd++;
1932 : }
1933 0 : return nCntKashEnd - nCntKash;
1934 : }
1935 :
1936 0 : void SwScriptInfo::SetNoKashidaLine(sal_Int32 nStt, sal_Int32 nLen)
1937 : {
1938 0 : aNoKashidaLine.push_back( nStt );
1939 0 : aNoKashidaLineEnd.push_back( nStt+nLen );
1940 0 : }
1941 :
1942 : /*************************************************************************
1943 : * SwScriptInfo::IsKashidaLine()
1944 : * determines if the line uses kashida justification
1945 : *************************************************************************/
1946 :
1947 0 : bool SwScriptInfo::IsKashidaLine(sal_Int32 nCharIdx) const
1948 : {
1949 0 : for (size_t i = 0; i < aNoKashidaLine.size(); ++i)
1950 : {
1951 0 : if (nCharIdx >= aNoKashidaLine[ i ] && nCharIdx < aNoKashidaLineEnd[ i ])
1952 0 : return false;
1953 : }
1954 0 : return true;
1955 : }
1956 :
1957 0 : void SwScriptInfo::ClearNoKashidaLine(sal_Int32 nStt, sal_Int32 nLen)
1958 : {
1959 0 : size_t i = 0;
1960 0 : while( i < aNoKashidaLine.size())
1961 : {
1962 0 : if( nStt + nLen >= aNoKashidaLine[ i ] && nStt < aNoKashidaLineEnd [ i ] )
1963 : {
1964 0 : aNoKashidaLine.erase(aNoKashidaLine.begin() + i);
1965 0 : aNoKashidaLineEnd.erase(aNoKashidaLineEnd.begin() + i);
1966 : }
1967 : else
1968 0 : ++i;
1969 : }
1970 0 : }
1971 :
1972 : /*************************************************************************
1973 : * SwScriptInfo::MarkKashidasInvalid()
1974 : *
1975 : * mark the given character indices as invalid kashida positions
1976 : ************************************************************************/
1977 :
1978 0 : bool SwScriptInfo::MarkKashidasInvalid(sal_Int32 nCnt, sal_Int32* pKashidaPositions)
1979 : {
1980 : SAL_WARN_IF( !pKashidaPositions || nCnt == 0, "sw.core", "Where are kashidas?" );
1981 :
1982 0 : size_t nCntKash = 0;
1983 0 : sal_Int32 nKashidaPosIdx = 0;
1984 :
1985 0 : while (nCntKash < CountKashida() && nKashidaPosIdx < nCnt)
1986 : {
1987 0 : if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) )
1988 : {
1989 0 : ++nCntKash;
1990 0 : continue;
1991 : }
1992 :
1993 0 : if ( pKashidaPositions [nKashidaPosIdx] != GetKashida( nCntKash ) || !IsKashidaValid ( nCntKash ) )
1994 0 : return false; // something is wrong
1995 :
1996 0 : MarkKashidaInvalid ( nCntKash );
1997 0 : nKashidaPosIdx++;
1998 : }
1999 0 : return true;
2000 : }
2001 :
2002 0 : sal_Int32 SwScriptInfo::ThaiJustify( const OUString& rTxt, sal_Int32* pKernArray,
2003 : sal_Int32* pScrArray, sal_Int32 nStt,
2004 : sal_Int32 nLen, sal_Int32 nNumberOfBlanks,
2005 : long nSpaceAdd )
2006 : {
2007 : SAL_WARN_IF( nStt + nLen > rTxt.getLength(), "sw.core", "String in ThaiJustify too small" );
2008 :
2009 0 : SwTwips nNumOfTwipsToDistribute = nSpaceAdd * nNumberOfBlanks /
2010 0 : SPACING_PRECISION_FACTOR;
2011 :
2012 0 : long nSpaceSum = 0;
2013 0 : sal_Int32 nCnt = 0;
2014 :
2015 0 : for (sal_Int32 nI = 0; nI < nLen; ++nI)
2016 : {
2017 0 : const sal_Unicode cCh = rTxt[nStt + nI];
2018 :
2019 : // check if character is not above or below base
2020 0 : if ( ( 0xE34 > cCh || cCh > 0xE3A ) &&
2021 0 : ( 0xE47 > cCh || cCh > 0xE4E ) && cCh != 0xE31 )
2022 : {
2023 0 : if ( nNumberOfBlanks > 0 )
2024 : {
2025 0 : nSpaceAdd = nNumOfTwipsToDistribute / nNumberOfBlanks;
2026 0 : --nNumberOfBlanks;
2027 0 : nNumOfTwipsToDistribute -= nSpaceAdd;
2028 : }
2029 0 : nSpaceSum += nSpaceAdd;
2030 0 : ++nCnt;
2031 : }
2032 :
2033 0 : if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
2034 0 : if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
2035 : }
2036 :
2037 0 : return nCnt;
2038 : }
2039 :
2040 0 : SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTxtNode& rTNd,
2041 : bool bAllowInvalid )
2042 : {
2043 0 : SwIterator<SwTxtFrm,SwTxtNode> aIter( rTNd );
2044 0 : SwScriptInfo* pScriptInfo = 0;
2045 :
2046 0 : for( SwTxtFrm* pLast = aIter.First(); pLast; pLast = aIter.Next() )
2047 : {
2048 0 : pScriptInfo = (SwScriptInfo*)pLast->GetScriptInfo();
2049 0 : if ( pScriptInfo )
2050 : {
2051 0 : if ( bAllowInvalid || COMPLETE_STRING == pScriptInfo->GetInvalidityA() )
2052 0 : break;
2053 0 : pScriptInfo = 0;
2054 : }
2055 : }
2056 :
2057 0 : return pScriptInfo;
2058 : }
2059 :
2060 0 : SwParaPortion::SwParaPortion()
2061 : : bFlag00(false)
2062 : , bFlag11(false)
2063 : , bFlag12(false)
2064 : , bFlag13(false)
2065 : , bFlag14(false)
2066 : , bFlag15(false)
2067 0 : , bFlag16(false)
2068 : {
2069 0 : FormatReset();
2070 0 : bFlys = bFtnNum = bMargin = false;
2071 0 : SetWhichPor( POR_PARA );
2072 0 : }
2073 :
2074 0 : SwParaPortion::~SwParaPortion()
2075 : {
2076 0 : }
2077 :
2078 0 : sal_Int32 SwParaPortion::GetParLen() const
2079 : {
2080 0 : sal_Int32 nLen = 0;
2081 0 : const SwLineLayout *pLay = this;
2082 0 : while( pLay )
2083 : {
2084 0 : nLen += pLay->GetLen();
2085 0 : pLay = pLay->GetNext();
2086 : }
2087 0 : return nLen;
2088 : }
2089 :
2090 0 : const SwDropPortion *SwParaPortion::FindDropPortion() const
2091 : {
2092 0 : const SwLineLayout *pLay = this;
2093 0 : while( pLay && pLay->IsDummy() )
2094 0 : pLay = pLay->GetNext();
2095 0 : while( pLay )
2096 : {
2097 0 : const SwLinePortion *pPos = pLay->GetPortion();
2098 0 : while ( pPos && !pPos->GetLen() )
2099 0 : pPos = pPos->GetPortion();
2100 0 : if( pPos && pPos->IsDropPortion() )
2101 0 : return (SwDropPortion *)pPos;
2102 0 : pLay = pLay->GetLen() ? NULL : pLay->GetNext();
2103 : }
2104 0 : return NULL;
2105 : }
2106 :
2107 0 : void SwLineLayout::Init( SwLinePortion* pNextPortion )
2108 : {
2109 0 : Height( 0 );
2110 0 : Width( 0 );
2111 0 : SetLen( 0 );
2112 0 : SetAscent( 0 );
2113 0 : SetRealHeight( 0 );
2114 0 : SetPortion( pNextPortion );
2115 0 : }
2116 :
2117 : /*--------------------------------------------------
2118 : * HangingMargin()
2119 : * looks for hanging punctuation portions in the paragraph
2120 : * and return the maximum right offset of them.
2121 : * If no such portion is found, the Margin/Hanging-flags will be updated.
2122 : * --------------------------------------------------*/
2123 :
2124 0 : SwTwips SwLineLayout::_GetHangingMargin() const
2125 : {
2126 0 : SwLinePortion* pPor = GetPortion();
2127 0 : bool bFound = false;
2128 0 : SwTwips nDiff = 0;
2129 0 : while( pPor)
2130 : {
2131 0 : if( pPor->IsHangingPortion() )
2132 : {
2133 0 : nDiff = ((SwHangingPortion*)pPor)->GetInnerWidth() - pPor->Width();
2134 0 : if( nDiff )
2135 0 : bFound = true;
2136 : }
2137 : // the last post its portion
2138 0 : else if ( pPor->IsPostItsPortion() && ! pPor->GetPortion() )
2139 0 : nDiff = nAscent;
2140 :
2141 0 : pPor = pPor->GetPortion();
2142 : }
2143 0 : if( !bFound ) // update the hanging-flag
2144 0 : ((SwLineLayout*)this)->SetHanging( false );
2145 0 : return nDiff;
2146 : }
2147 :
2148 0 : SwTwips SwTxtFrm::HangingMargin() const
2149 : {
2150 : SAL_WARN_IF( !HasPara(), "sw.core", "Don't call me without a paraportion" );
2151 0 : if( !GetPara()->IsMargin() )
2152 0 : return 0;
2153 0 : const SwLineLayout* pLine = GetPara();
2154 0 : SwTwips nRet = 0;
2155 0 : do
2156 : {
2157 0 : SwTwips nDiff = pLine->GetHangingMargin();
2158 0 : if( nDiff > nRet )
2159 0 : nRet = nDiff;
2160 0 : pLine = pLine->GetNext();
2161 : } while ( pLine );
2162 0 : if( !nRet ) // update the margin-flag
2163 0 : ((SwParaPortion*)GetPara())->SetMargin( false );
2164 0 : return nRet;
2165 : }
2166 :
2167 0 : void SwScriptInfo::selectHiddenTextProperty(const SwTxtNode& rNode, MultiSelection &rHiddenMulti)
2168 : {
2169 : assert((rNode.GetTxt().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
2170 : || (rNode.GetTxt().getLength() == rHiddenMulti.GetTotalRange().Len()));
2171 :
2172 0 : const SfxPoolItem* pItem = 0;
2173 0 : if( SFX_ITEM_SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) &&
2174 0 : ((SvxCharHiddenItem*)pItem)->GetValue() )
2175 : {
2176 0 : rHiddenMulti.SelectAll();
2177 : }
2178 :
2179 0 : const SwpHints* pHints = rNode.GetpSwpHints();
2180 :
2181 0 : if( pHints )
2182 : {
2183 0 : MSHORT nTmp = 0;
2184 :
2185 0 : while( nTmp < pHints->GetStartCount() )
2186 : {
2187 0 : const SwTxtAttr* pTxtAttr = pHints->GetStart( nTmp++ );
2188 : const SvxCharHiddenItem* pHiddenItem =
2189 0 : static_cast<const SvxCharHiddenItem*>( CharFmt::GetItem( *pTxtAttr, RES_CHRATR_HIDDEN ) );
2190 0 : if( pHiddenItem )
2191 : {
2192 0 : const sal_Int32 nSt = *pTxtAttr->GetStart();
2193 0 : const sal_Int32 nEnd = *pTxtAttr->End();
2194 0 : if( nEnd > nSt )
2195 : {
2196 0 : Range aTmp( nSt, nEnd - 1 );
2197 0 : rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() );
2198 : }
2199 : }
2200 : }
2201 : }
2202 0 : }
2203 :
2204 0 : void SwScriptInfo::selectRedLineDeleted(const SwTxtNode& rNode, MultiSelection &rHiddenMulti, bool bSelect)
2205 : {
2206 : assert((rNode.GetTxt().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
2207 : || (rNode.GetTxt().getLength() == rHiddenMulti.GetTotalRange().Len()));
2208 :
2209 0 : const IDocumentRedlineAccess& rIDRA = *rNode.getIDocumentRedlineAccess();
2210 0 : if ( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineMode() ) )
2211 : {
2212 0 : sal_uInt16 nAct = rIDRA.GetRedlinePos( rNode, USHRT_MAX );
2213 :
2214 0 : for ( ; nAct < rIDRA.GetRedlineTbl().size(); nAct++ )
2215 : {
2216 0 : const SwRangeRedline* pRed = rIDRA.GetRedlineTbl()[ nAct ];
2217 :
2218 0 : if ( pRed->Start()->nNode > rNode.GetIndex() )
2219 0 : break;
2220 :
2221 : sal_Int32 nRedlStart;
2222 : sal_Int32 nRedlnEnd;
2223 0 : pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd );
2224 : //clip it if the redline extends past the end of the nodes text
2225 0 : nRedlnEnd = std::min<sal_Int32>(nRedlnEnd, rNode.GetTxt().getLength());
2226 0 : if ( nRedlnEnd > nRedlStart )
2227 : {
2228 0 : Range aTmp( nRedlStart, nRedlnEnd - 1 );
2229 0 : rHiddenMulti.Select( aTmp, bSelect );
2230 : }
2231 : }
2232 : }
2233 0 : }
2234 :
2235 : /*************************************************************************
2236 : * SwScriptInfo::CalcHiddenRanges()
2237 : *
2238 : * Returns a MultiSection indicating the hidden ranges.
2239 : *************************************************************************/
2240 :
2241 0 : void SwScriptInfo::CalcHiddenRanges( const SwTxtNode& rNode, MultiSelection& rHiddenMulti )
2242 : {
2243 0 : selectHiddenTextProperty(rNode, rHiddenMulti);
2244 :
2245 : // If there are any hidden ranges in the current text node, we have
2246 : // to unhide the redlining ranges:
2247 0 : selectRedLineDeleted(rNode, rHiddenMulti, false);
2248 :
2249 : // We calculated a lot of stuff. Finally we can update the flags at the text node.
2250 :
2251 0 : const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0;
2252 0 : bool bNewHiddenCharsHidePara = false;
2253 0 : if ( bNewContainsHiddenChars )
2254 : {
2255 0 : const Range& rRange = rHiddenMulti.GetRange( 0 );
2256 0 : const sal_Int32 nHiddenStart = rRange.Min();
2257 0 : const sal_Int32 nHiddenEnd = rRange.Max() + 1;
2258 : bNewHiddenCharsHidePara =
2259 0 : (nHiddenStart == 0 && nHiddenEnd >= rNode.GetTxt().getLength());
2260 : }
2261 0 : rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
2262 0 : }
2263 :
2264 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|