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