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