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 <calbck.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 27 : static bool lcl_HasStrongLTR ( const OUString& rText, sal_Int32 nStart, sal_Int32 nEnd )
108 : {
109 51 : for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
110 : {
111 45 : const UCharDirection nCharDir = u_charDirection ( rText[ nCharIdx ] );
112 45 : if ( nCharDir == U_LEFT_TO_RIGHT ||
113 24 : nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
114 : nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
115 21 : return true;
116 : }
117 6 : 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 132696 : SwLineLayout::~SwLineLayout()
125 : {
126 58662 : Truncate();
127 58662 : delete pNext;
128 58662 : if( pBlink )
129 109 : pBlink->Delete( this );
130 58662 : delete pLLSpaceAdd;
131 58662 : delete pKanaComp;
132 74034 : }
133 :
134 25450 : SwLinePortion *SwLineLayout::Insert( SwLinePortion *pIns )
135 : {
136 : // First attribute change: copy mass and length from *pIns into the first
137 : // text portion
138 25450 : if( !pPortion )
139 : {
140 25450 : if( GetLen() )
141 : {
142 12802 : pPortion = SwTextPortion::CopyLinePortion(*this);
143 12802 : if( IsBlinking() && pBlink )
144 : {
145 0 : SetBlinking( false );
146 0 : pBlink->Replace( this, pPortion );
147 : }
148 : }
149 : else
150 : {
151 12648 : SetPortion( pIns );
152 12648 : return pIns;
153 : }
154 : }
155 : // Call with scope or we'll end up with recursion!
156 12802 : return pPortion->SwLinePortion::Insert( pIns );
157 : }
158 :
159 3 : SwLinePortion *SwLineLayout::Append( SwLinePortion *pIns )
160 : {
161 : // First attribute change: copy mass and length from *pIns into the first
162 : // text portion
163 3 : if( !pPortion )
164 3 : pPortion = SwTextPortion::CopyLinePortion(*this);
165 : // Call with scope or we'll end up with recursion!
166 3 : return pPortion->SwLinePortion::Append( pIns );
167 : }
168 :
169 : // For special treatment of empty lines
170 :
171 60916 : bool SwLineLayout::Format( SwTextFormatInfo &rInf )
172 : {
173 60916 : if( GetLen() )
174 60916 : return SwTextPortion::Format( rInf );
175 :
176 0 : Height( rInf.GetTextHeight() );
177 0 : return true;
178 : }
179 :
180 : // We collect all FlyPortions at the beginning of the line and make that a
181 : // MarginPortion.
182 1117 : SwMarginPortion *SwLineLayout::CalcLeftMargin()
183 : {
184 1392 : SwMarginPortion *pLeft = (GetPortion() && GetPortion()->IsMarginPortion()) ?
185 1117 : static_cast<SwMarginPortion *>(GetPortion()) : 0;
186 1117 : if( !GetPortion() )
187 842 : SetPortion(SwTextPortion::CopyLinePortion(*this));
188 1117 : if( !pLeft )
189 : {
190 1117 : pLeft = new SwMarginPortion( 0 );
191 1117 : pLeft->SetPortion( GetPortion() );
192 1117 : 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 1117 : SwLinePortion *pPos = pLeft->GetPortion();
205 3351 : while( pPos )
206 : {
207 1117 : if( pPos->IsFlyPortion() )
208 : {
209 : // The FlyPortion get's sucked out ...
210 2 : pLeft->Join( static_cast<SwGluePortion*>(pPos) );
211 2 : pPos = pLeft->GetPortion();
212 2 : if( GetpKanaComp() && !GetKanaComp().empty() )
213 0 : GetKanaComp().pop_front();
214 : }
215 : else
216 1115 : pPos = 0;
217 : }
218 1117 : return pLeft;
219 : }
220 :
221 294 : void SwLineLayout::InitSpaceAdd()
222 : {
223 294 : if ( !pLLSpaceAdd )
224 294 : CreateSpaceAdd();
225 : else
226 0 : SetLLSpaceAdd( 0, 0 );
227 294 : }
228 :
229 294 : void SwLineLayout::CreateSpaceAdd( const long nInit )
230 : {
231 294 : pLLSpaceAdd = new std::vector<long>;
232 294 : SetLLSpaceAdd( nInit, 0 );
233 294 : }
234 :
235 : // Returns true if there are only blanks in [nStt, nEnd[
236 60374 : static bool lcl_HasOnlyBlanks( const OUString& rText, sal_Int32 nStt, sal_Int32 nEnd )
237 : {
238 60374 : bool bBlankOnly = true;
239 127120 : while ( nStt < nEnd )
240 : {
241 55618 : const sal_Unicode cChar = rText[ nStt++ ];
242 55618 : if ( ' ' != cChar && 0x3000 != cChar )
243 : {
244 49246 : bBlankOnly = false;
245 49246 : break;
246 : }
247 : }
248 60374 : return bBlankOnly;
249 : }
250 :
251 : // Swapped out from FormatLine()
252 86792 : void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
253 : {
254 86792 : const sal_uInt16 nLineWidth = rInf.RealWidth();
255 :
256 86792 : sal_uInt16 nFlyAscent = 0;
257 86792 : sal_uInt16 nFlyHeight = 0;
258 86792 : sal_uInt16 nFlyDescent = 0;
259 86792 : bool bOnlyPostIts = true;
260 86792 : SetHanging( false );
261 :
262 86792 : bool bTmpDummy = !GetLen();
263 86792 : SwFlyCntPortion* pFlyCnt = 0;
264 86792 : if( bTmpDummy )
265 : {
266 24031 : nFlyAscent = 0;
267 24031 : nFlyHeight = 0;
268 24031 : nFlyDescent = 0;
269 : }
270 :
271 : // #i3952#
272 : const bool bIgnoreBlanksAndTabsForLineHeightCalculation =
273 86792 : rInf.GetTextFrm()->GetNode()->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION);
274 :
275 86792 : bool bHasBlankPortion = false;
276 86792 : bool bHasOnlyBlankPortions = true;
277 :
278 86792 : if( pPortion )
279 : {
280 27457 : SetContent( false );
281 27457 : if( pPortion->IsBreakPortion() )
282 : {
283 100 : SetLen( pPortion->GetLen() );
284 100 : if( GetLen() )
285 100 : bTmpDummy = false;
286 : }
287 : else
288 : {
289 27357 : const sal_uInt16 nLineHeight = Height();
290 27357 : Init( GetPortion() );
291 27357 : SwLinePortion *pPos = pPortion;
292 27357 : SwLinePortion *pLast = this;
293 27357 : 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 143929 : 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 89215 : if( !pPos->Compress() )
305 : {
306 : // Only take over Height and Ascent if the rest of the line
307 : // is empty.
308 8298 : if( !pPos->GetPortion() )
309 : {
310 2225 : if( !Height() )
311 163 : Height( pPos->Height() );
312 2225 : if( !GetAscent() )
313 217 : SetAscent( pPos->GetAscent() );
314 : }
315 8298 : delete pLast->Cut( pPos );
316 8298 : pPos = pLast->GetPortion();
317 8298 : continue;
318 : }
319 :
320 80917 : const sal_Int32 nPorSttIdx = rInf.GetLineStart() + nLineLength;
321 80917 : nLineLength += pPos->GetLen();
322 80917 : AddPrtWidth( pPos->Width() );
323 :
324 : // #i3952#
325 80917 : if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
326 : {
327 120194 : if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
328 73767 : ( pPos->IsTextPortion() &&
329 30925 : lcl_HasOnlyBlanks( rInf.GetText(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
330 : {
331 17971 : pLast = pPos;
332 17971 : pPos = pPos->GetPortion();
333 17971 : bHasBlankPortion = true;
334 17971 : continue;
335 : }
336 : }
337 :
338 : // Ignore drop portion height
339 62946 : if( pPos->IsDropPortion() && static_cast<SwDropPortion*>(pPos)->GetLines() > 1)
340 : {
341 6 : pLast = pPos;
342 6 : pPos = pPos->GetPortion();
343 6 : continue;
344 : }
345 :
346 62940 : bHasOnlyBlankPortions = false;
347 :
348 : // We had an attribute change: Sum up/build maxima of length and mass
349 :
350 62940 : sal_uInt16 nPosHeight = pPos->Height();
351 62940 : sal_uInt16 nPosAscent = pPos->GetAscent();
352 :
353 : SAL_WARN_IF( nPosHeight < nPosAscent,
354 : "sw.core", "SwLineLayout::CalcLine: bad ascent or height" );
355 :
356 62940 : 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 62940 : if ( !pPos->IsBreakPortion() || !Height() )
366 : {
367 62482 : if (!pPos->IsPostItsPortion()) bOnlyPostIts = false;
368 :
369 62482 : if( bTmpDummy && !nLineLength )
370 : {
371 7551 : if( pPos->IsFlyPortion() )
372 : {
373 365 : if( nFlyHeight < nPosHeight )
374 365 : nFlyHeight = nPosHeight;
375 365 : if( nFlyAscent < nPosAscent )
376 365 : nFlyAscent = nPosAscent;
377 365 : if( nFlyDescent < nPosHeight - nPosAscent )
378 344 : nFlyDescent = nPosHeight - nPosAscent;
379 : }
380 : else
381 : {
382 7186 : if( pPos->InNumberGrp() )
383 : {
384 : sal_uInt16 nTmp = rInf.GetFont()->GetAscent(
385 6859 : rInf.GetVsh(), *rInf.GetOut() );
386 6859 : if( nTmp > nPosAscent )
387 : {
388 4118 : nPosHeight += nTmp - nPosAscent;
389 4118 : nPosAscent = nTmp;
390 : }
391 : nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
392 6859 : *rInf.GetOut() );
393 6859 : if( nTmp > nPosHeight )
394 2097 : nPosHeight = nTmp;
395 : }
396 7186 : Height( nPosHeight );
397 7186 : nAscent = nPosAscent;
398 7186 : nMaxDescent = nPosHeight - nPosAscent;
399 7551 : }
400 : }
401 54931 : else if( !pPos->IsFlyPortion() )
402 : {
403 54819 : if( Height() < nPosHeight )
404 : {
405 : // Height is set to 0 when Init() is called.
406 20066 : if (bIgnoreBlanksAndTabsForLineHeightCalculation && pPos->GetWhichPor() == POR_FLYCNT)
407 : // Compat flag set: take the line height, if it's larger.
408 2655 : Height(std::max(nPosHeight, nLineHeight));
409 : else
410 : // Just care about the portion height.
411 17411 : Height(nPosHeight);
412 : }
413 58933 : if( pPos->IsFlyCntPortion() || ( pPos->IsMultiPortion()
414 245 : && static_cast<SwMultiPortion*>(pPos)->HasFlyInContent() ) )
415 4114 : rLine.SetFlyInCntBase();
416 58933 : if( pPos->IsFlyCntPortion() &&
417 4114 : static_cast<SwFlyCntPortion*>(pPos)->GetAlign() )
418 : {
419 126 : static_cast<SwFlyCntPortion*>(pPos)->SetMax( false );
420 126 : if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() )
421 94 : pFlyCnt = static_cast<SwFlyCntPortion*>(pPos);
422 : }
423 : else
424 : {
425 54693 : if( nAscent < nPosAscent )
426 18774 : nAscent = nPosAscent;
427 54693 : if( nMaxDescent < nPosHeight - nPosAscent )
428 18590 : nMaxDescent = nPosHeight - nPosAscent;
429 : }
430 : }
431 : }
432 458 : else if( pPos->GetLen() )
433 458 : bTmpDummy = false;
434 :
435 62940 : if( !HasContent() && !pPos->InNumberGrp() )
436 : {
437 29231 : if ( pPos->InExpGrp() )
438 : {
439 2339 : OUString aText;
440 2339 : if( pPos->GetExpText( rInf, aText ) && !aText.isEmpty() )
441 2242 : SetContent();
442 : }
443 46547 : else if( ( pPos->InTextGrp() || pPos->IsMultiPortion() ) &&
444 19655 : pPos->GetLen() )
445 19655 : SetContent();
446 : }
447 :
448 62940 : bTmpDummy &= !HasContent() && ( !pPos->Width() || pPos->IsFlyPortion() );
449 :
450 62940 : pLast = pPos;
451 62940 : pPos = pPos->GetPortion();
452 : }
453 :
454 27357 : if( pFlyCnt )
455 : {
456 94 : if( pFlyCnt->Height() == Height() )
457 : {
458 75 : pFlyCnt->SetMax( true );
459 75 : if( Height() > nMaxDescent + nAscent )
460 : {
461 75 : if( 3 == pFlyCnt->GetAlign() ) // Bottom
462 0 : nAscent = Height() - nMaxDescent;
463 75 : else if( 2 == pFlyCnt->GetAlign() ) // Center
464 36 : nAscent = ( Height() + nAscent - nMaxDescent ) / 2;
465 : }
466 75 : pFlyCnt->SetAscent( nAscent );
467 : }
468 : }
469 :
470 27357 : if( bTmpDummy && nFlyHeight )
471 : {
472 227 : nAscent = nFlyAscent;
473 454 : if( nFlyDescent > nFlyHeight - nFlyAscent )
474 0 : Height( nFlyHeight + nFlyDescent );
475 : else
476 227 : Height( nFlyHeight );
477 : }
478 27130 : else if( nMaxDescent > Height() - nAscent )
479 877 : Height( nMaxDescent + nAscent );
480 :
481 27357 : if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) )
482 : {
483 184 : Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
484 184 : nAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() );
485 : }
486 : }
487 : }
488 : else
489 : {
490 59335 : SetContent( !bTmpDummy );
491 :
492 : // #i3952#
493 88784 : if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
494 29449 : lcl_HasOnlyBlanks( rInf.GetText(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
495 : {
496 9696 : bHasBlankPortion = true;
497 : }
498 : }
499 :
500 : // #i3952#
501 86792 : if ( bHasBlankPortion && bHasOnlyBlankPortions )
502 : {
503 10275 : sal_uInt16 nTmpAscent = GetAscent();
504 10275 : sal_uInt16 nTmpHeight = Height();
505 10275 : rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
506 10275 : SetAscent( nTmpAscent );
507 10275 : Height( nTmpHeight );
508 : }
509 :
510 : // Robust:
511 86792 : if( nLineWidth < Width() )
512 268 : Width( nLineWidth );
513 : SAL_WARN_IF( nLineWidth < Width(), "sw.core", "SwLineLayout::CalcLine: line is bursting" );
514 86792 : SetDummy( bTmpDummy );
515 87346 : SetRedline( rLine.GetRedln() &&
516 87346 : rLine.GetRedln()->CheckLine( rLine.GetStart(), rLine.GetEnd() ) );
517 86792 : }
518 :
519 : // #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
520 : // to control, if the fly content portions and line portion are considered.
521 102651 : 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 102651 : _orAscent = 0;
529 102651 : _orDescent = 0;
530 102651 : _orObjAscent = 0;
531 102651 : _orObjDescent = 0;
532 :
533 102651 : const SwLinePortion* pTmpPortion = this;
534 102651 : if ( !pTmpPortion->GetLen() && pTmpPortion->GetPortion() )
535 : {
536 1137 : pTmpPortion = pTmpPortion->GetPortion();
537 : }
538 :
539 424799 : while ( pTmpPortion )
540 : {
541 639418 : if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() &&
542 308223 : ( !_bNoFlyCntPorAndLinePor ||
543 176492 : ( !pTmpPortion->IsFlyCntPortion() &&
544 127965 : !(pTmpPortion == this && pTmpPortion->GetPortion() ) ) ) )
545 : {
546 201330 : SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent());
547 201330 : SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) -
548 201330 : nPortionAsc;
549 :
550 201330 : const bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
551 : static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
552 201330 : !( pTmpPortion == _pDontConsiderPortion );
553 :
554 201330 : if ( bFlyCmp )
555 : {
556 193208 : _orObjAscent = std::max( _orObjAscent, nPortionAsc );
557 193208 : _orObjDescent = std::max( _orObjDescent, nPortionDesc );
558 : }
559 :
560 201330 : if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() )
561 : {
562 192990 : _orAscent = std::max( _orAscent, nPortionAsc );
563 192990 : _orDescent = std::max( _orDescent, nPortionDesc );
564 : }
565 : }
566 219497 : pTmpPortion = pTmpPortion->GetPortion();
567 : }
568 102651 : }
569 :
570 9929 : SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange)
571 : {
572 9929 : if(0 != rRange.nLen ) {
573 9929 : if(0 == nLen) {
574 8514 : nStart = rRange.nStart;
575 8514 : nLen = rRange.nLen ;
576 : }
577 : else {
578 1415 : if(rRange.nStart + rRange.nLen > nStart + nLen) {
579 1163 : nLen = rRange.nStart + rRange.nLen - nStart;
580 : }
581 1415 : if(rRange.nStart < nStart) {
582 257 : nLen += nStart - rRange.nStart;
583 257 : nStart = rRange.nStart;
584 : }
585 : }
586 : }
587 9929 : return *this;
588 : }
589 :
590 43344 : SwScriptInfo::SwScriptInfo()
591 : : nInvalidityPos(0)
592 43344 : , nDefaultDir(0)
593 : {
594 43344 : };
595 :
596 43344 : SwScriptInfo::~SwScriptInfo()
597 : {
598 43344 : }
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 482137 : sal_uInt8 SwScriptInfo::WhichFont( sal_Int32 nIdx, const OUString* pText, const SwScriptInfo* pSI )
603 : {
604 : assert((pSI || pText) && "How should I determine the script type?");
605 : const sal_uInt16 nScript = pSI
606 482031 : ? pSI->ScriptType( nIdx ) // use our SwScriptInfo if available
607 964168 : : g_pBreakIt->GetRealScriptOfText( *pText, nIdx ); // else ask the break iterator
608 :
609 482137 : switch ( nScript ) {
610 481471 : case i18n::ScriptType::LATIN : return SW_LATIN;
611 486 : case i18n::ScriptType::ASIAN : return SW_CJK;
612 180 : 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 rText and stores them
620 8 : void SwScriptInfo::InitScriptInfo( const SwTextNode& rNode )
621 : {
622 8 : InitScriptInfo( rNode, nDefaultDir == UBIDI_RTL );
623 8 : }
624 :
625 47507 : void SwScriptInfo::InitScriptInfo( const SwTextNode& rNode, bool bRTL )
626 : {
627 47507 : if( !g_pBreakIt->GetBreakIter().is() )
628 47507 : return;
629 :
630 47507 : const OUString& rText = rNode.GetText();
631 :
632 : // HIDDEN TEXT INFORMATION
633 :
634 47507 : Range aRange( 0, !rText.isEmpty() ? rText.getLength() - 1 : 0 );
635 47507 : MultiSelection aHiddenMulti( aRange );
636 47507 : CalcHiddenRanges( rNode, aHiddenMulti );
637 :
638 47507 : aHiddenChg.clear();
639 47510 : for( size_t i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
640 : {
641 3 : const Range& rRange = aHiddenMulti.GetRange( i );
642 3 : const sal_Int32 nStart = rRange.Min();
643 3 : const sal_Int32 nEnd = rRange.Max() + 1;
644 :
645 3 : aHiddenChg.push_back( nStart );
646 3 : aHiddenChg.push_back( nEnd );
647 : }
648 :
649 : // SCRIPT AND SCRIPT RELATED INFORMATION
650 :
651 47507 : sal_Int32 nChg = nInvalidityPos;
652 :
653 : // COMPLETE_STRING means the data structure is up to date
654 47507 : nInvalidityPos = COMPLETE_STRING;
655 :
656 : // this is the default direction
657 47507 : nDefaultDir = static_cast<sal_uInt8>(bRTL ? UBIDI_RTL : UBIDI_LTR);
658 :
659 : // counter for script info arrays
660 47507 : size_t nCnt = 0;
661 : // counter for compression information arrays
662 47507 : size_t nCntComp = 0;
663 : // counter for kashida array
664 47507 : size_t nCntKash = 0;
665 :
666 47507 : sal_Int16 nScript = i18n::ScriptType::LATIN;
667 :
668 : // compression type
669 47507 : const SwCharCompressType aCompEnum = rNode.getIDocumentSettingAccess()->getCharacterCompressionType();
670 :
671 : // justification type
672 : const bool bAdjustBlock = SVX_ADJUST_BLOCK ==
673 47507 : rNode.GetSwAttrSet().GetAdjust().GetAdjust();
674 :
675 : // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
676 :
677 47507 : 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 3993 : while( nCnt < CountScriptChg() )
684 : {
685 1996 : if ( nChg > GetScriptChg( nCnt ) )
686 1 : nCnt++;
687 : else
688 : {
689 1995 : nScript = GetScriptType( nCnt );
690 1995 : break;
691 : }
692 : }
693 1996 : 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 1996 : 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 47507 : if ( nChg )
718 1996 : --nChg;
719 :
720 47507 : 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 194082 : while ( nChg > nGrpStart &&
725 55293 : nScript != g_pBreakIt->GetBreakIter()->getScriptType( rText, nChg ) )
726 547 : --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 47507 : if ( nChg == nGrpStart )
731 45641 : nScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rText, nChg );
732 :
733 : // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
734 :
735 : // remove invalid entries from script information arrays
736 47507 : aScriptChanges.erase( aScriptChanges.begin() + nCnt, aScriptChanges.end() );
737 :
738 : // get the start of the last compression group
739 47507 : sal_Int32 nLastCompression = nChg;
740 47507 : 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 47507 : aCompressionChanges.erase(aCompressionChanges.begin() + nCntComp, aCompressionChanges.end() );
753 :
754 : // get the start of the last kashida group
755 47507 : sal_Int32 nLastKashida = nChg;
756 47507 : 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 47507 : 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 47507 : if( WEAK == g_pBreakIt->GetBreakIter()->getScriptType( rText, 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 16556 : g_pBreakIt->GetBreakIter()->endOfScript( rText, nChg, WEAK );
776 :
777 16556 : if (nEnd > rText.getLength() || nEnd < 0)
778 11543 : nEnd = rText.getLength();
779 :
780 16556 : nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( 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 16556 : nChg = nEnd;
787 :
788 : // Get next script type or set to weak in order to exit
789 16556 : sal_uInt8 nNextScript = ( nEnd < rText.getLength() ) ?
790 22754 : (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rText, nEnd ) :
791 35178 : (sal_uInt8)WEAK;
792 :
793 16556 : if ( nScript != nNextScript )
794 : {
795 14490 : aScriptChanges.push_back( ScriptChangeInfo(nEnd, nScript) );
796 14490 : nCnt++;
797 14490 : nScript = nNextScript;
798 : }
799 : }
800 :
801 : // UPDATE THE SCRIPT INFO ARRAYS:
802 :
803 128134 : while ( nChg < rText.getLength() || ( aScriptChanges.empty() && rText.isEmpty() ) )
804 : {
805 : SAL_WARN_IF( i18n::ScriptType::WEAK == nScript,
806 : "sw.core", "Inserting WEAK into SwScriptInfo structure" );
807 :
808 33120 : sal_Int32 nSearchStt = nChg;
809 33120 : nChg = g_pBreakIt->GetBreakIter()->endOfScript( rText, nSearchStt, nScript );
810 :
811 33120 : if (nChg > rText.getLength() || nChg < 0)
812 0 : nChg = rText.getLength();
813 :
814 : // #i28203#
815 : // for 'complex' portions, we make sure that a portion does not contain more
816 : // than one script:
817 33120 : if( i18n::ScriptType::COMPLEX == nScript )
818 : {
819 24 : const short nScriptType = ScriptTypeDetector::getCTLScriptType( rText, nSearchStt );
820 24 : sal_Int32 nNextCTLScriptStart = nSearchStt;
821 24 : short nCurrentScriptType = nScriptType;
822 51 : while( com::sun::star::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
823 : {
824 27 : nNextCTLScriptStart = ScriptTypeDetector::endOfCTLScriptType( rText, nNextCTLScriptStart );
825 27 : if( nNextCTLScriptStart >= rText.getLength() || nNextCTLScriptStart >= nChg )
826 24 : break;
827 3 : nCurrentScriptType = ScriptTypeDetector::getCTLScriptType( rText, nNextCTLScriptStart );
828 : }
829 24 : 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 99463 : if (nChg < rText.getLength() && nChg > 0 && (i18n::ScriptType::WEAK ==
835 33429 : g_pBreakIt->GetBreakIter()->getScriptType(rText,nChg - 1)))
836 : {
837 40 : int8_t nType = u_charType(rText[nChg] );
838 40 : 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 40 : aScriptChanges.push_back( ScriptChangeInfo(nChg, nScript) );
846 : }
847 : }
848 : else
849 : {
850 33080 : aScriptChanges.push_back( ScriptChangeInfo(nChg, nScript) );
851 : }
852 33120 : ++nCnt;
853 :
854 : // if current script is asian, we search for compressable characters
855 : // in this range
856 33120 : 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 = rText[ 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 33120 : else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
921 : {
922 0 : SwScanner aScanner( rNode, rNode.GetText(), 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 33120 : if ( nChg < rText.getLength() )
1088 103 : nScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rText, nChg );
1089 :
1090 33120 : nLastCompression = nChg;
1091 33120 : 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 47507 : aDirectionChanges.clear();
1113 :
1114 : // Perform Unicode Bidi Algorithm for text direction information
1115 47507 : bool bPerformUBA = UBIDI_LTR != nDefaultDir;
1116 47507 : nCnt = 0;
1117 142550 : while( !bPerformUBA && nCnt < CountScriptChg() )
1118 : {
1119 47536 : if ( i18n::ScriptType::COMPLEX == GetScriptType( nCnt++ ) )
1120 12 : bPerformUBA = true;
1121 : }
1122 :
1123 : // do not call the unicode bidi algorithm if not required
1124 47507 : if ( bPerformUBA )
1125 : {
1126 49 : UpdateBidiInfo( rText );
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 113 : for ( size_t nDirIdx = 0; nDirIdx < aDirectionChanges.size(); ++nDirIdx )
1133 : {
1134 64 : const sal_uInt8 nCurrDirType = GetDirType( nDirIdx );
1135 : // nStart ist start of RTL run:
1136 64 : const sal_Int32 nStart = nDirIdx > 0 ? GetDirChg( nDirIdx - 1 ) : 0;
1137 : // nEnd is end of RTL run:
1138 64 : const sal_Int32 nEnd = GetDirChg( nDirIdx );
1139 :
1140 92 : if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
1141 27 : ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( rText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
1142 : {
1143 : // nScriptIdx points into the ScriptArrays:
1144 28 : 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 64 : while ( GetScriptChg( nScriptIdx ) <= nStart )
1151 8 : ++nScriptIdx;
1152 :
1153 28 : const sal_Int32 nStartPosOfGroup = nScriptIdx ? GetScriptChg( nScriptIdx - 1 ) : 0;
1154 28 : 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 28 : if ( nStart > 0 && nStartPosOfGroup < nStart )
1163 : {
1164 2 : aScriptChanges.insert(aScriptChanges.begin() + nScriptIdx,
1165 4 : ScriptChangeInfo(nStart, nScriptTypeOfGroup) );
1166 2 : ++nScriptIdx;
1167 : }
1168 :
1169 : // Remove entries in ScriptArray which end inside the RTL run:
1170 82 : while ( nScriptIdx < aScriptChanges.size() && GetScriptChg( nScriptIdx ) <= nEnd )
1171 : {
1172 26 : aScriptChanges.erase(aScriptChanges.begin() + nScriptIdx);
1173 : }
1174 :
1175 : // Insert a new entry in ScriptArray for the end of the RTL run:
1176 28 : aScriptChanges.insert(aScriptChanges.begin() + nScriptIdx,
1177 56 : 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 47507 : }
1196 : }
1197 :
1198 49 : void SwScriptInfo::UpdateBidiInfo( const OUString& rText )
1199 : {
1200 : // remove invalid entries from direction information arrays
1201 49 : aDirectionChanges.clear();
1202 :
1203 : // Bidi functions from icu 2.0
1204 :
1205 49 : UErrorCode nError = U_ZERO_ERROR;
1206 49 : UBiDi* pBidi = ubidi_openSized( rText.getLength(), 0, &nError );
1207 49 : nError = U_ZERO_ERROR;
1208 :
1209 49 : ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength(), // UChar != sal_Unicode in MinGW
1210 98 : nDefaultDir, NULL, &nError );
1211 49 : nError = U_ZERO_ERROR;
1212 49 : int nCount = ubidi_countRuns( pBidi, &nError );
1213 49 : int32_t nStart = 0;
1214 : int32_t nEnd;
1215 : UBiDiLevel nCurrDir;
1216 113 : for ( int nIdx = 0; nIdx < nCount; ++nIdx )
1217 : {
1218 64 : ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
1219 64 : aDirectionChanges.push_back( DirectionChangeInfo(nEnd, nCurrDir) );
1220 64 : nStart = nEnd;
1221 : }
1222 :
1223 49 : ubidi_close( pBidi );
1224 49 : }
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 142094 : sal_Int32 SwScriptInfo::NextScriptChg(const sal_Int32 nPos) const
1234 : {
1235 142094 : const size_t nEnd = CountScriptChg();
1236 145019 : for( size_t nX = 0; nX < nEnd; ++nX )
1237 : {
1238 145019 : if( nPos < GetScriptChg( nX ) )
1239 142094 : return GetScriptChg( nX );
1240 : }
1241 :
1242 0 : return COMPLETE_STRING;
1243 : }
1244 :
1245 : // returns the script of the character at the input position
1246 491716 : sal_Int16 SwScriptInfo::ScriptType(const sal_Int32 nPos) const
1247 : {
1248 491716 : const size_t nEnd = CountScriptChg();
1249 537118 : for( size_t nX = 0; nX < nEnd; ++nX )
1250 : {
1251 499099 : if( nPos < GetScriptChg( nX ) )
1252 453697 : return GetScriptType( nX );
1253 : }
1254 :
1255 : // the default is the application language script
1256 38019 : return SvtLanguageOptions::GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() );
1257 : }
1258 :
1259 88009 : sal_Int32 SwScriptInfo::NextDirChg( const sal_Int32 nPos,
1260 : const sal_uInt8* pLevel ) const
1261 : {
1262 88009 : const sal_uInt8 nCurrDir = pLevel ? *pLevel : 62;
1263 88009 : const size_t nEnd = CountDirChg();
1264 88216 : for( size_t nX = 0; nX < nEnd; ++nX )
1265 : {
1266 1266 : if( nPos < GetDirChg( nX ) &&
1267 407 : ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) )
1268 353 : return GetDirChg( nX );
1269 : }
1270 :
1271 87656 : return COMPLETE_STRING;
1272 : }
1273 :
1274 155620 : sal_uInt8 SwScriptInfo::DirType(const sal_Int32 nPos) const
1275 : {
1276 155620 : const size_t nEnd = CountDirChg();
1277 155948 : for( size_t nX = 0; nX < nEnd; ++nX )
1278 : {
1279 705 : if( nPos < GetDirChg( nX ) )
1280 377 : return GetDirType( nX );
1281 : }
1282 :
1283 155243 : return 0;
1284 : }
1285 :
1286 : // Takes a string and replaced the hidden ranges with cChar.
1287 48517700 : sal_Int32 SwScriptInfo::MaskHiddenRanges( const SwTextNode& rNode, OUStringBuffer & rText,
1288 : const sal_Int32 nStt, const sal_Int32 nEnd,
1289 : const sal_Unicode cChar )
1290 : {
1291 : assert(rNode.GetText().getLength() == rText.getLength());
1292 :
1293 48517700 : PositionList aList;
1294 : sal_Int32 nHiddenStart;
1295 : sal_Int32 nHiddenEnd;
1296 48517700 : sal_Int32 nNumOfHiddenChars = 0;
1297 48517700 : GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1298 48517700 : PositionList::const_reverse_iterator rFirst( aList.end() );
1299 48517700 : PositionList::const_reverse_iterator rLast( aList.begin() );
1300 97035414 : while ( rFirst != rLast )
1301 : {
1302 14 : nHiddenEnd = *(rFirst++);
1303 14 : nHiddenStart = *(rFirst++);
1304 :
1305 14 : if ( nHiddenEnd < nStt || nHiddenStart > nEnd )
1306 0 : continue;
1307 :
1308 206 : while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
1309 : {
1310 178 : if ( nHiddenStart >= nStt && nHiddenStart < nEnd )
1311 : {
1312 178 : rText[nHiddenStart] = cChar;
1313 178 : ++nNumOfHiddenChars;
1314 : }
1315 178 : ++nHiddenStart;
1316 : }
1317 : }
1318 :
1319 48517700 : return nNumOfHiddenChars;
1320 : }
1321 :
1322 : // Takes a SwTextNode and deletes the hidden ranges from the node.
1323 0 : void SwScriptInfo::DeleteHiddenRanges( SwTextNode& 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 48557031 : bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTextNode& rNode, sal_Int32 nPos,
1342 : sal_Int32& rnStartPos, sal_Int32& rnEndPos,
1343 : PositionList* pList )
1344 : {
1345 48557031 : rnStartPos = COMPLETE_STRING;
1346 48557031 : rnEndPos = 0;
1347 :
1348 48557031 : bool bNewContainsHiddenChars = false;
1349 :
1350 : // Optimization: First examine the flags at the text node:
1351 :
1352 48557031 : if ( !rNode.IsCalcHiddenCharFlags() )
1353 : {
1354 48522674 : bool bWholePara = rNode.HasHiddenCharAttribute( true );
1355 48522674 : bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false );
1356 48522674 : if ( !bContainsHiddenChars )
1357 48522540 : return false;
1358 :
1359 134 : if ( bWholePara )
1360 : {
1361 134 : if ( pList )
1362 : {
1363 14 : pList->push_back( 0 );
1364 14 : pList->push_back(rNode.GetText().getLength());
1365 : }
1366 :
1367 134 : rnStartPos = 0;
1368 134 : rnEndPos = rNode.GetText().getLength();
1369 134 : return true;
1370 : }
1371 : }
1372 :
1373 34357 : const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
1374 34357 : if ( pSI )
1375 : {
1376 :
1377 : // Check first, if we have a valid SwScriptInfo object for this text node:
1378 :
1379 38 : bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList );
1380 : const bool bNewHiddenCharsHidePara =
1381 38 : rnStartPos == 0 && rnEndPos >= rNode.GetText().getLength();
1382 38 : rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
1383 : }
1384 : else
1385 : {
1386 :
1387 : // No valid SwScriptInfo Object, we have to do it the hard way:
1388 :
1389 34319 : Range aRange(0, (!rNode.GetText().isEmpty())
1390 22284 : ? rNode.GetText().getLength() - 1
1391 56603 : : 0);
1392 34319 : MultiSelection aHiddenMulti( aRange );
1393 34319 : SwScriptInfo::CalcHiddenRanges( rNode, aHiddenMulti );
1394 68638 : for( size_t i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1395 : {
1396 103 : const Range& rRange = aHiddenMulti.GetRange( i );
1397 103 : const sal_Int32 nHiddenStart = rRange.Min();
1398 103 : const sal_Int32 nHiddenEnd = rRange.Max() + 1;
1399 :
1400 103 : if ( nHiddenStart > nPos )
1401 103 : break;
1402 103 : if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1403 : {
1404 103 : rnStartPos = nHiddenStart;
1405 : rnEndPos = std::min<sal_Int32>(nHiddenEnd,
1406 103 : rNode.GetText().getLength());
1407 103 : break;
1408 : }
1409 : }
1410 :
1411 34319 : if ( pList )
1412 : {
1413 149 : 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 34319 : bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0;
1422 : }
1423 :
1424 34357 : return bNewContainsHiddenChars;
1425 : }
1426 :
1427 162539 : bool SwScriptInfo::GetBoundsOfHiddenRange( sal_Int32 nPos, sal_Int32& rnStartPos,
1428 : sal_Int32& rnEndPos, PositionList* pList ) const
1429 : {
1430 162539 : rnStartPos = COMPLETE_STRING;
1431 162539 : rnEndPos = 0;
1432 :
1433 162539 : const size_t nEnd = CountHiddenChg();
1434 162551 : for( size_t nX = 0; nX < nEnd; ++nX )
1435 : {
1436 16 : const sal_Int32 nHiddenStart = GetHiddenChg( nX++ );
1437 16 : const sal_Int32 nHiddenEnd = GetHiddenChg( nX );
1438 :
1439 16 : if ( nHiddenStart > nPos )
1440 0 : break;
1441 16 : if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1442 : {
1443 4 : rnStartPos = nHiddenStart;
1444 4 : rnEndPos = nHiddenEnd;
1445 4 : break;
1446 : }
1447 : }
1448 :
1449 162539 : if ( pList )
1450 : {
1451 33 : 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 162539 : return CountHiddenChg() > 0;
1459 : }
1460 :
1461 884 : bool SwScriptInfo::IsInHiddenRange( const SwTextNode& rNode, sal_Int32 nPos )
1462 : {
1463 : sal_Int32 nStartPos;
1464 : sal_Int32 nEndPos;
1465 884 : SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos );
1466 884 : 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 NewTextPortion
1708 3 : bool SwScriptInfo::IsArabicText( const OUString& rText, 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 3 : const CharClass& rCC = GetAppCharClass();
1718 3 : sal_Int32 nIdx = nStt;
1719 3 : const sal_Int32 nEnd = nStt + nLen;
1720 6 : while ( nIdx < nEnd && !rCC.isLetterNumeric( rText, nIdx ) )
1721 : {
1722 0 : ++nIdx;
1723 : }
1724 :
1725 3 : if( nIdx == nEnd )
1726 : {
1727 : // no regular character found in this portion. Go backward:
1728 0 : --nIdx;
1729 0 : while ( nIdx >= 0 && !rCC.isLetterNumeric( rText, nIdx ) )
1730 : {
1731 0 : --nIdx;
1732 : }
1733 : }
1734 :
1735 3 : if( nIdx >= 0 )
1736 : {
1737 3 : const sal_Unicode cCh = rText[nIdx];
1738 3 : const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, UnicodeScript_kScriptCount );
1739 3 : 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& rText, 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 > rText.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 = rText[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 41596 : SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTextNode& rTNd,
1932 : bool bAllowInvalid )
1933 : {
1934 41596 : SwIterator<SwTextFrm,SwTextNode> aIter( rTNd );
1935 41596 : SwScriptInfo* pScriptInfo = 0;
1936 :
1937 82106 : for( SwTextFrm* pLast = aIter.First(); pLast; pLast = aIter.Next() )
1938 : {
1939 42498 : pScriptInfo = const_cast<SwScriptInfo*>(pLast->GetScriptInfo());
1940 42498 : if ( pScriptInfo )
1941 : {
1942 7013 : if ( bAllowInvalid || COMPLETE_STRING == pScriptInfo->GetInvalidityA() )
1943 1988 : break;
1944 5025 : pScriptInfo = 0;
1945 : }
1946 : }
1947 :
1948 41596 : return pScriptInfo;
1949 : }
1950 :
1951 43044 : SwParaPortion::SwParaPortion()
1952 : : bFlag00(false)
1953 : , bFlag11(false)
1954 : , bFlag12(false)
1955 : , bFlag13(false)
1956 : , bFlag14(false)
1957 : , bFlag15(false)
1958 43044 : , bFlag16(false)
1959 : {
1960 43044 : FormatReset();
1961 43044 : bFlys = bFootnoteNum = bMargin = false;
1962 43044 : SetWhichPor( POR_PARA );
1963 43044 : }
1964 :
1965 86088 : SwParaPortion::~SwParaPortion()
1966 : {
1967 86088 : }
1968 :
1969 27 : sal_Int32 SwParaPortion::GetParLen() const
1970 : {
1971 27 : sal_Int32 nLen = 0;
1972 27 : const SwLineLayout *pLay = this;
1973 81 : while( pLay )
1974 : {
1975 27 : nLen += pLay->GetLen();
1976 27 : pLay = pLay->GetNext();
1977 : }
1978 27 : return nLen;
1979 : }
1980 :
1981 184327 : const SwDropPortion *SwParaPortion::FindDropPortion() const
1982 : {
1983 184327 : const SwLineLayout *pLay = this;
1984 381962 : while( pLay && pLay->IsDummy() )
1985 13308 : pLay = pLay->GetNext();
1986 540656 : while( pLay )
1987 : {
1988 172080 : const SwLinePortion *pPos = pLay->GetPortion();
1989 363027 : while ( pPos && !pPos->GetLen() )
1990 18867 : pPos = pPos->GetPortion();
1991 172080 : if( pPos && pPos->IsDropPortion() )
1992 78 : return static_cast<const SwDropPortion *>(pPos);
1993 172002 : pLay = pLay->GetLen() ? NULL : pLay->GetNext();
1994 : }
1995 184249 : return NULL;
1996 : }
1997 :
1998 111873 : void SwLineLayout::Init( SwLinePortion* pNextPortion )
1999 : {
2000 111873 : Height( 0 );
2001 111873 : Width( 0 );
2002 111873 : SetLen( 0 );
2003 111873 : SetAscent( 0 );
2004 111873 : SetRealHeight( 0 );
2005 111873 : SetPortion( pNextPortion );
2006 111873 : }
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 159673 : SwTwips SwLineLayout::_GetHangingMargin() const
2012 : {
2013 159673 : SwLinePortion* pPor = GetPortion();
2014 159673 : bool bFound = false;
2015 159673 : SwTwips nDiff = 0;
2016 392187 : while( pPor)
2017 : {
2018 72841 : if( pPor->IsHangingPortion() )
2019 : {
2020 0 : nDiff = static_cast<SwHangingPortion*>(pPor)->GetInnerWidth() - pPor->Width();
2021 0 : if( nDiff )
2022 0 : bFound = true;
2023 : }
2024 : // the last post its portion
2025 72841 : else if ( pPor->IsPostItsPortion() && ! pPor->GetPortion() )
2026 26 : nDiff = nAscent;
2027 :
2028 72841 : pPor = pPor->GetPortion();
2029 : }
2030 159673 : if( !bFound ) // update the hanging-flag
2031 159673 : const_cast<SwLineLayout*>(this)->SetHanging( false );
2032 159673 : return nDiff;
2033 : }
2034 :
2035 29884 : SwTwips SwTextFrm::HangingMargin() const
2036 : {
2037 : SAL_WARN_IF( !HasPara(), "sw.core", "Don't call me without a paraportion" );
2038 29884 : if( !GetPara()->IsMargin() )
2039 29884 : 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 : const_cast<SwParaPortion*>(GetPara())->SetMargin( false );
2051 0 : return nRet;
2052 : }
2053 :
2054 87268 : void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode, MultiSelection &rHiddenMulti)
2055 : {
2056 : assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
2057 : || (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
2058 :
2059 87268 : const SfxPoolItem* pItem = 0;
2060 87376 : if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) &&
2061 108 : static_cast<const SvxCharHiddenItem*>(pItem)->GetValue() )
2062 : {
2063 90 : rHiddenMulti.SelectAll();
2064 : }
2065 :
2066 87268 : const SwpHints* pHints = rNode.GetpSwpHints();
2067 :
2068 87268 : if( pHints )
2069 : {
2070 114497 : for( size_t nTmp = 0; nTmp < pHints->GetStartCount(); ++nTmp )
2071 : {
2072 79138 : const SwTextAttr* pTextAttr = pHints->GetStart( nTmp );
2073 : const SvxCharHiddenItem* pHiddenItem =
2074 79138 : static_cast<const SvxCharHiddenItem*>( CharFormat::GetItem( *pTextAttr, RES_CHRATR_HIDDEN ) );
2075 79138 : if( pHiddenItem )
2076 : {
2077 862 : const sal_Int32 nSt = pTextAttr->GetStart();
2078 862 : const sal_Int32 nEnd = *pTextAttr->End();
2079 862 : if( nEnd > nSt )
2080 : {
2081 689 : Range aTmp( nSt, nEnd - 1 );
2082 689 : rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() );
2083 : }
2084 : }
2085 : }
2086 : }
2087 87268 : }
2088 :
2089 87268 : void SwScriptInfo::selectRedLineDeleted(const SwTextNode& rNode, MultiSelection &rHiddenMulti, bool bSelect)
2090 : {
2091 : assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
2092 : || (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
2093 :
2094 87268 : const IDocumentRedlineAccess& rIDRA = *rNode.getIDocumentRedlineAccess();
2095 87268 : if ( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineMode() ) )
2096 : {
2097 86128 : sal_uInt16 nAct = rIDRA.GetRedlinePos( rNode, USHRT_MAX );
2098 :
2099 90035 : for ( ; nAct < rIDRA.GetRedlineTable().size(); nAct++ )
2100 : {
2101 4345 : const SwRangeRedline* pRed = rIDRA.GetRedlineTable()[ nAct ];
2102 :
2103 4345 : if (pRed->Start()->nNode > rNode.GetIndex())
2104 438 : break;
2105 :
2106 3907 : if (pRed->GetType() != nsRedlineType_t::REDLINE_DELETE)
2107 3453 : continue;
2108 :
2109 : sal_Int32 nRedlStart;
2110 : sal_Int32 nRedlnEnd;
2111 454 : pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd );
2112 : //clip it if the redline extends past the end of the nodes text
2113 454 : nRedlnEnd = std::min<sal_Int32>(nRedlnEnd, rNode.GetText().getLength());
2114 454 : if ( nRedlnEnd > nRedlStart )
2115 : {
2116 297 : Range aTmp( nRedlStart, nRedlnEnd - 1 );
2117 297 : rHiddenMulti.Select( aTmp, bSelect );
2118 : }
2119 : }
2120 : }
2121 87268 : }
2122 :
2123 : // Returns a MultiSection indicating the hidden ranges.
2124 81826 : void SwScriptInfo::CalcHiddenRanges( const SwTextNode& rNode, MultiSelection& rHiddenMulti )
2125 : {
2126 81826 : selectHiddenTextProperty(rNode, rHiddenMulti);
2127 :
2128 : // If there are any hidden ranges in the current text node, we have
2129 : // to unhide the redlining ranges:
2130 81826 : selectRedLineDeleted(rNode, rHiddenMulti, false);
2131 :
2132 : // We calculated a lot of stuff. Finally we can update the flags at the text node.
2133 :
2134 81826 : const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0;
2135 81826 : bool bNewHiddenCharsHidePara = false;
2136 81826 : if ( bNewContainsHiddenChars )
2137 : {
2138 106 : const Range& rRange = rHiddenMulti.GetRange( 0 );
2139 106 : const sal_Int32 nHiddenStart = rRange.Min();
2140 106 : const sal_Int32 nHiddenEnd = rRange.Max() + 1;
2141 : bNewHiddenCharsHidePara =
2142 106 : (nHiddenStart == 0 && nHiddenEnd >= rNode.GetText().getLength());
2143 : }
2144 81826 : rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
2145 82003 : }
2146 :
2147 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|