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