1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
32 #include "errhdl.hxx" // ASSERT
36 #include "itrform2.hxx"
37 #include "porglue.hxx"
38 #include "porexp.hxx" // SwQuoVadisPortion
39 #include "blink.hxx" // pBlink
40 #include "redlnitr.hxx" // SwRedlineItr
41 #include "porfly.hxx" // SwFlyCntPortion
42 #include <porrst.hxx> // SwHangingPortion
43 #include <pormulti.hxx> // SwMultiPortion
44 #include <breakit.hxx>
45 #include <unicode/uchar.h>
46 #include <com/sun/star/i18n/ScriptType.hdl>
47 #include <com/sun/star/i18n/CTLScriptType.hdl>
48 #include <com/sun/star/i18n/WordType.hdl>
50 #include <svx/adjitem.hxx>
51 #include <svx/scripttypeitem.hxx>
52 #include <svx/charhiddenitem.hxx>
53 #include <vcl/outdev.hxx>
54 #include <svx/blnkitem.hxx>
55 #include <tools/multisel.hxx>
56 #include <unotools/charclass.hxx>
57 #include <i18npool/mslangid.hxx>
58 #include <charfmt.hxx>
59 #include <fchrfmt.hxx>
60 #include <docary.hxx> // SwRedlineTbl
61 #include <redline.hxx> // SwRedline
63 // --> FME 2004-06-08 #i12836# enhanced pdf export
64 #include <section.hxx>
67 #include <IDocumentRedlineAccess.hxx>
68 #include <IDocumentSettingAccess.hxx>
69 #include <IDocumentContentOperations.hxx>
71 using namespace ::com::sun::star
;
72 using namespace i18n::ScriptType
;
75 #include <unicode/ubidi.h>
76 #include <i18nutil/unicode.hxx> //unicode::getUnicodeScriptType
78 sal_Bool
isAlefChar ( xub_Unicode cCh
)
80 return ( cCh
== 0x622 || cCh
== 0x623 || cCh
== 0x625 || cCh
== 0x627 ||
81 cCh
== 0x622 || cCh
== 0x671 || cCh
== 0x672 || cCh
== 0x673 || cCh
== 0x675 );
84 sal_Bool
isWawChar ( xub_Unicode cCh
)
86 return ( cCh
== 0x624 || cCh
== 0x648 || cCh
== 0x676 || cCh
== 0x677 ||
87 ( cCh
>= 0x6C4 && cCh
<= 0x6CB ) || cCh
== 0x6CF );
90 sal_Bool
isDalChar ( xub_Unicode cCh
)
92 return ( cCh
== 0x62F || cCh
== 0x630 || cCh
== 0x688 || cCh
== 0x689 || cCh
== 0x690 );
95 sal_Bool
isRehChar ( xub_Unicode cCh
)
97 return ( cCh
== 0x631 || cCh
== 0x632 || ( cCh
>= 0x691 && cCh
<= 0x699 ));
100 sal_Bool
isTehMarbutaChar ( xub_Unicode cCh
)
102 return ( cCh
== 0x629 || cCh
== 0x6C0 );
105 sal_Bool
isBaaChar ( xub_Unicode cCh
)
107 return ( cCh
== 0x628 || cCh
== 0x62A || cCh
== 0x62B || cCh
== 0x679 || cCh
== 0x680 );
110 sal_Bool
isYehChar ( xub_Unicode cCh
)
112 return ( cCh
== 0x626 || cCh
== 0x649 || cCh
== 0x64A || cCh
== 0x678 || cCh
== 0x6CC ||
113 cCh
== 0x6CE || cCh
== 0x6D0 || cCh
== 0x6D1 );
116 sal_Bool
isSeenOrSadChar ( xub_Unicode cCh
)
118 return ( ( cCh
>= 0x633 && cCh
<= 0x636 ) || ( cCh
>= 0x69A && cCh
<= 0x69E )
119 || cCh
== 0x6FA || cCh
== 0x6FB );
122 sal_Bool
isHahChar ( xub_Unicode cCh
)
124 return ( ( cCh
>= 0x62C && cCh
<= 0x62E ) || ( cCh
>= 0x681 && cCh
<= 0x687 )
128 sal_Bool
isTahChar ( xub_Unicode cCh
)
130 return ( cCh
== 0x637 || cCh
== 0x638 || cCh
== 0x69F );
133 sal_Bool
isAinChar ( xub_Unicode cCh
)
135 return ( cCh
== 0x639 || cCh
== 0x63A || cCh
== 0x6A0 || cCh
== 0x6FC );
138 sal_Bool
isKafChar ( xub_Unicode cCh
)
140 return ( cCh
== 0x643 || ( cCh
>= 0x6AC && cCh
<= 0x6AE ) );
143 sal_Bool
isLamChar ( xub_Unicode cCh
)
145 return ( cCh
== 0x644 || ( cCh
>= 0x6B5 && cCh
<= 0x6B8 ) );
148 sal_Bool
isGafChar ( xub_Unicode cCh
)
150 return ( cCh
== 0x6A9 || cCh
== 0x6AB ||( cCh
>= 0x6AF && cCh
<= 0x6B4 ) );
153 sal_Bool
isQafChar ( xub_Unicode cCh
)
155 return ( cCh
== 0x642 || cCh
== 0x6A7 || cCh
== 0x6A8 );
158 sal_Bool
isFeChar ( xub_Unicode cCh
)
160 return ( cCh
== 0x641 || ( cCh
>= 0x6A1 && cCh
<= 0x6A6 ) );
162 sal_Bool
isTransparentChar ( xub_Unicode cCh
)
164 return ( ( cCh
>= 0x610 && cCh
<= 0x61A ) ||
165 ( cCh
>= 0x64B && cCh
<= 0x65E ) ||
167 ( cCh
>= 0x6D6 && cCh
<= 0x6DC ) ||
168 ( cCh
>= 0x6DF && cCh
<= 0x6E4 ) ||
169 ( cCh
>= 0x6E7 && cCh
<= 0x6E8 ) ||
170 ( cCh
>= 0x6EA && cCh
<= 0x6ED ));
173 /*************************************************************************
176 * Checks if cCh + cNectCh builds a ligature (used for Kashidas)
177 *************************************************************************/
179 sal_Bool
lcl_IsLigature( xub_Unicode cCh
, xub_Unicode cNextCh
)
182 return ( isLamChar ( cCh
) && isAlefChar ( cNextCh
));
185 /*************************************************************************
188 * Checks if cCh is connectable to cPrevCh (used for Kashidas)
189 *************************************************************************/
191 sal_Bool
lcl_ConnectToPrev( xub_Unicode cCh
, xub_Unicode cPrevCh
)
193 // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
194 // Uh, there seem to be some more characters that are not connectable
195 // to the left. So we look for the characters that are actually connectable
196 // to the left. Here is the complete list of WH:
199 // added lam forms 0x06B5..0x06B8
200 // added 0x6FA..0x6FC, according to unicode documentation, although not present in my fonts
201 // added heh goal 0x6C1
202 sal_Bool bRet
= 0x628 == cPrevCh
||
203 ( 0x62A <= cPrevCh
&& cPrevCh
<= 0x62E ) ||
204 ( 0x633 <= cPrevCh
&& cPrevCh
<= 0x647 ) ||
205 0x649 == cPrevCh
|| // Alef Maksura does connect !!!
207 ( 0x678 <= cPrevCh
&& cPrevCh
<= 0x687 ) ||
208 ( 0x69A <= cPrevCh
&& cPrevCh
<= 0x6C1 ) ||
209 ( 0x6C3 <= cPrevCh
&& cPrevCh
<= 0x6D3 ) ||
210 ( 0x6FA <= cPrevCh
&& cPrevCh
<= 0x6FC ) ;
212 // check for ligatures cPrevChar + cChar
214 bRet
= !lcl_IsLigature( cPrevCh
, cCh
);
218 /*************************************************************************
220 *************************************************************************/
221 bool lcl_HasStrongLTR ( const String
& rTxt
, xub_StrLen nStart
, xub_StrLen nEnd
)
223 for ( xub_StrLen nCharIdx
= nStart
; nCharIdx
< nEnd
; ++nCharIdx
)
225 const UCharDirection nCharDir
= u_charDirection ( rTxt
.GetChar ( nCharIdx
));
226 if ( nCharDir
== U_LEFT_TO_RIGHT
||
227 nCharDir
== U_LEFT_TO_RIGHT_EMBEDDING
||
228 nCharDir
== U_LEFT_TO_RIGHT_OVERRIDE
)
234 /*************************************************************************
235 * SwLineLayout::~SwLineLayout()
237 * class SwLineLayout: Das Layout einer einzelnen Zeile. Dazu
238 * gehoeren vor allen Dingen die Dimension, die Anzahl der
239 * Character und der Wortzwischenraeume in der Zeile.
240 * Zeilenobjekte werden in einem eigenen Pool verwaltet, um zu
241 * erreichen, dass sie im Speicher moeglichst beeinander liegen
242 * (d.h. zusammen gepaged werden und den Speicher nicht
244 *************************************************************************/
246 SwLineLayout::~SwLineLayout()
252 pBlink
->Delete( this );
258 /*************************************************************************
259 * virtual SwLineLayout::Insert()
260 *************************************************************************/
262 SwLinePortion
*SwLineLayout::Insert( SwLinePortion
*pIns
)
264 // Erster Attributwechsel, Masse und Laengen
265 // aus *pCurr in die erste Textportion kopieren.
270 pPortion
= new SwTxtPortion( *(SwLinePortion
*)this );
271 if( IsBlinking() && pBlink
)
273 SetBlinking( sal_False
);
274 pBlink
->Replace( this, pPortion
);
283 // mit Skope aufrufen, sonst Rekursion !
284 return pPortion
->SwLinePortion::Insert( pIns
);
287 /*************************************************************************
288 * virtual SwLineLayout::Append()
289 *************************************************************************/
291 SwLinePortion
*SwLineLayout::Append( SwLinePortion
*pIns
)
293 // Erster Attributwechsel, Masse und Laengen
294 // aus *pCurr in die erste Textportion kopieren.
296 pPortion
= new SwTxtPortion( *(SwLinePortion
*)this );
297 // mit Skope aufrufen, sonst Rekursion !
298 return pPortion
->SwLinePortion::Append( pIns
);
301 /*************************************************************************
302 * virtual SwLineLayout::Format()
303 *************************************************************************/
305 // fuer die Sonderbehandlung bei leeren Zeilen
307 sal_Bool
SwLineLayout::Format( SwTxtFormatInfo
&rInf
)
310 return SwTxtPortion::Format( rInf
);
313 Height( rInf
.GetTxtHeight() );
318 /*************************************************************************
319 * SwLineLayout::CalcLeftMargin()
321 * Wir sammeln alle FlyPortions am Anfang der Zeile zu einer MarginPortion.
322 *************************************************************************/
324 SwMarginPortion
*SwLineLayout::CalcLeftMargin()
326 SwMarginPortion
*pLeft
= (GetPortion() && GetPortion()->IsMarginPortion()) ?
327 (SwMarginPortion
*)GetPortion() : 0;
329 SetPortion( new SwTxtPortion( *(SwLinePortion
*)this ) );
332 pLeft
= new SwMarginPortion( 0 );
333 pLeft
->SetPortion( GetPortion() );
341 pLeft
->SetAscent( 0 );
342 pLeft
->SetPortion( NULL
);
343 pLeft
->SetFixWidth(0);
346 SwLinePortion
*pPos
= pLeft
->GetPortion();
350 if( pPos
->IsFlyPortion() )
352 // Die FlyPortion wird ausgesogen ...
353 pLeft
->Join( (SwGluePortion
*)pPos
);
354 pPos
= pLeft
->GetPortion();
356 GetKanaComp().Remove( 0, 1 );
364 /*************************************************************************
365 * SwLineLayout::InitSpaceAdd()
366 *************************************************************************/
368 void SwLineLayout::InitSpaceAdd()
373 SetLLSpaceAdd( 0, 0 );
376 /*************************************************************************
377 * SwLineLayout::CreateSpaceAdd()
378 *************************************************************************/
380 void SwLineLayout::CreateSpaceAdd( const long nInit
)
382 pLLSpaceAdd
= new std::vector
<long>;
383 SetLLSpaceAdd( nInit
, 0 );
386 /*************************************************************************
387 * Local helper function. Returns true if there are only blanks
389 *************************************************************************/
391 bool lcl_HasOnlyBlanks( const XubString
& rTxt
, xub_StrLen nStt
, xub_StrLen nEnd
)
393 bool bBlankOnly
= true;
394 while ( nStt
< nEnd
)
396 const xub_Unicode cChar
= rTxt
.GetChar( nStt
++ );
397 if ( ' ' != cChar
&& 0x3000 != cChar
)
406 /*************************************************************************
407 * SwLineLayout::CalcLine()
409 * Aus FormatLine() ausgelagert.
410 *************************************************************************/
412 void SwLineLayout::CalcLine( SwTxtFormatter
&rLine
, SwTxtFormatInfo
&rInf
)
414 const KSHORT nLineWidth
= rInf
.RealWidth();
416 KSHORT nFlyAscent
= 0;
417 KSHORT nFlyHeight
= 0;
418 KSHORT nFlyDescent
= 0;
419 sal_Bool bOnlyPostIts
= sal_True
;
420 SetHanging( sal_False
);
422 sal_Bool bTmpDummy
= ( 0 == GetLen() );
423 SwFlyCntPortion
* pFlyCnt
= 0;
431 // --> FME 2006-03-01 #i3952#
432 const bool bIgnoreBlanksAndTabsForLineHeightCalculation
=
433 rInf
.GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION
);
435 bool bHasBlankPortion
= false;
436 bool bHasOnlyBlankPortions
= true;
441 SetCntnt( sal_False
);
442 if( pPortion
->IsBreakPortion() )
444 SetLen( pPortion
->GetLen() );
446 bTmpDummy
= sal_False
;
450 Init( GetPortion() );
451 SwLinePortion
*pPos
= pPortion
;
452 SwLinePortion
*pLast
= this;
453 KSHORT nMaxDescent
= 0;
455 // Eine Gruppe ist ein Abschnitt in der Portion-Kette von
456 // pCurr oder einer Fix-Portion bis zum Ende bzw. zur naechsten
461 ASSERT( POR_LIN
!= pPos
->GetWhichPor(),
462 "SwLineLayout::CalcLine: don't use SwLinePortions !" );
464 // Null-Portions werden eliminiert. Sie koennen entstehen,
465 // wenn zwei FlyFrms ueberlappen.
466 if( !pPos
->Compress() )
468 // 8110: Hoehe und Ascent nur uebernehmen, wenn sonst in der
469 // Zeile nichts mehr los ist.
470 if( !pPos
->GetPortion() )
473 Height( pPos
->Height() );
475 SetAscent( pPos
->GetAscent() );
477 delete pLast
->Cut( pPos
);
478 pPos
= pLast
->GetPortion();
482 const xub_StrLen nPorSttIdx
= rInf
.GetLineStart() + nLineLength
;
483 nLineLength
= nLineLength
+ pPos
->GetLen();
484 AddPrtWidth( pPos
->Width() );
486 // --> FME 2006-03-01 #i3952#
487 if ( bIgnoreBlanksAndTabsForLineHeightCalculation
)
489 if ( pPos
->InTabGrp() || pPos
->IsHolePortion() ||
490 ( pPos
->IsTextPortion() &&
491 lcl_HasOnlyBlanks( rInf
.GetTxt(), nPorSttIdx
, nPorSttIdx
+ pPos
->GetLen() ) ) )
494 pPos
= pPos
->GetPortion();
495 bHasBlankPortion
= true;
501 bHasOnlyBlankPortions
= false;
503 // Es gab Attributwechsel: Laengen und Masse aufaddieren;
504 // bzw.Maxima bilden.
506 KSHORT nPosHeight
= pPos
->Height();
507 KSHORT nPosAscent
= pPos
->GetAscent();
509 ASSERT( nPosHeight
>= nPosAscent
,
510 "SwLineLayout::CalcLine: bad ascent or height" );
512 if( pPos
->IsHangingPortion() )
514 SetHanging( sal_True
);
515 rInf
.GetParaPortion()->SetMargin( sal_True
);
518 // Damit ein Paragraphende-Zeichen nicht durch ein Descent zu einer
519 // geaenderten Zeilenhoehe und zum Umformatieren fuehrt.
520 if ( !pPos
->IsBreakPortion() || !Height() )
522 bOnlyPostIts
&= pPos
->IsPostItsPortion();
524 if( bTmpDummy
&& !nLineLength
)
526 if( pPos
->IsFlyPortion() )
528 if( nFlyHeight
< nPosHeight
)
529 nFlyHeight
= nPosHeight
;
530 if( nFlyAscent
< nPosAscent
)
531 nFlyAscent
= nPosAscent
;
532 if( nFlyDescent
< nPosHeight
- nPosAscent
)
533 nFlyDescent
= nPosHeight
- nPosAscent
;
537 if( pPos
->InNumberGrp() )
539 KSHORT nTmp
= rInf
.GetFont()->GetAscent(
540 rInf
.GetVsh(), *rInf
.GetOut() );
541 if( nTmp
> nPosAscent
)
543 nPosHeight
+= nTmp
- nPosAscent
;
546 nTmp
= rInf
.GetFont()->GetHeight( rInf
.GetVsh(),
548 if( nTmp
> nPosHeight
)
551 Height( nPosHeight
);
552 nAscent
= nPosAscent
;
553 nMaxDescent
= nPosHeight
- nPosAscent
;
556 else if( !pPos
->IsFlyPortion() )
558 if( Height() < nPosHeight
)
559 Height( nPosHeight
);
560 if( pPos
->IsFlyCntPortion() || ( pPos
->IsMultiPortion()
561 && ((SwMultiPortion
*)pPos
)->HasFlyInCntnt() ) )
562 rLine
.SetFlyInCntBase();
563 if( pPos
->IsFlyCntPortion() &&
564 ((SwFlyCntPortion
*)pPos
)->GetAlign() )
566 ((SwFlyCntPortion
*)pPos
)->SetMax( sal_False
);
567 if( !pFlyCnt
|| pPos
->Height() > pFlyCnt
->Height() )
568 pFlyCnt
= (SwFlyCntPortion
*)pPos
;
572 if( nAscent
< nPosAscent
)
573 nAscent
= nPosAscent
;
574 if( nMaxDescent
< nPosHeight
- nPosAscent
)
575 nMaxDescent
= nPosHeight
- nPosAscent
;
579 else if( pPos
->GetLen() )
580 bTmpDummy
= sal_False
;
582 if( !HasCntnt() && !pPos
->InNumberGrp() )
584 if ( pPos
->InExpGrp() )
587 if( pPos
->GetExpTxt( rInf
, aTxt
) && aTxt
.Len() )
588 SetCntnt( sal_True
);
590 else if( ( pPos
->InTxtGrp() || pPos
->IsMultiPortion() ) &&
592 SetCntnt( sal_True
);
595 bTmpDummy
= bTmpDummy
&& !HasCntnt() &&
596 ( !pPos
->Width() || pPos
->IsFlyPortion() );
599 pPos
= pPos
->GetPortion();
604 if( pFlyCnt
->Height() == Height() )
606 pFlyCnt
->SetMax( sal_True
);
607 if( Height() > nMaxDescent
+ nAscent
)
609 if( 3 == pFlyCnt
->GetAlign() ) // Bottom
610 nAscent
= Height() - nMaxDescent
;
611 else if( 2 == pFlyCnt
->GetAlign() ) // Center
612 nAscent
= ( Height() + nAscent
- nMaxDescent
) / 2;
614 pFlyCnt
->SetAscent( nAscent
);
618 if( bTmpDummy
&& nFlyHeight
)
620 nAscent
= nFlyAscent
;
621 if( nFlyDescent
> nFlyHeight
- nFlyAscent
)
622 Height( nFlyHeight
+ nFlyDescent
);
624 Height( nFlyHeight
);
626 else if( nMaxDescent
> Height() - nAscent
)
627 Height( nMaxDescent
+ nAscent
);
629 if( bOnlyPostIts
&& !( bHasBlankPortion
&& bHasOnlyBlankPortions
) )
631 Height( rInf
.GetFont()->GetHeight( rInf
.GetVsh(), *rInf
.GetOut() ) );
632 nAscent
= rInf
.GetFont()->GetAscent( rInf
.GetVsh(), *rInf
.GetOut() );
638 SetCntnt( !bTmpDummy
);
640 // --> FME 2006-03-01 #i3952#
641 if ( bIgnoreBlanksAndTabsForLineHeightCalculation
&&
642 lcl_HasOnlyBlanks( rInf
.GetTxt(), rInf
.GetLineStart(), rInf
.GetLineStart() + GetLen() ) )
644 bHasBlankPortion
= true;
649 // --> FME 2006-03-01 #i3952#
650 if ( bHasBlankPortion
&& bHasOnlyBlankPortions
)
652 USHORT nTmpAscent
= GetAscent();
653 USHORT nTmpHeight
= Height();
654 rLine
.GetAttrHandler().GetDefaultAscentAndHeight( rInf
.GetVsh(), *rInf
.GetOut(), nTmpAscent
, nTmpHeight
);
655 SetAscent( nTmpAscent
);
656 Height( nTmpHeight
);
661 if( nLineWidth
< Width() )
663 ASSERT( nLineWidth
>= Width(), "SwLineLayout::CalcLine: line is bursting" );
664 SetDummy( bTmpDummy
);
665 SetRedline( rLine
.GetRedln() &&
666 rLine
.GetRedln()->CheckLine( rLine
.GetStart(), rLine
.GetEnd() ) );
669 // --> OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
670 // to control, if the fly content portions and line portion are considered.
671 void SwLineLayout::MaxAscentDescent( SwTwips
& _orAscent
,
673 SwTwips
& _orObjAscent
,
674 SwTwips
& _orObjDescent
,
675 const SwLinePortion
* _pDontConsiderPortion
,
676 const bool _bNoFlyCntPorAndLinePor
) const
683 const SwLinePortion
* pTmpPortion
= this;
684 if ( !pTmpPortion
->GetLen() && pTmpPortion
->GetPortion() )
686 pTmpPortion
= pTmpPortion
->GetPortion();
689 while ( pTmpPortion
)
691 if ( !pTmpPortion
->IsBreakPortion() && !pTmpPortion
->IsFlyPortion() &&
692 ( !_bNoFlyCntPorAndLinePor
||
693 ( !pTmpPortion
->IsFlyCntPortion() &&
694 !(pTmpPortion
== this && pTmpPortion
->GetPortion() ) ) ) )
696 SwTwips nPortionAsc
= static_cast<SwTwips
>(pTmpPortion
->GetAscent());
697 SwTwips nPortionDesc
= static_cast<SwTwips
>(pTmpPortion
->Height()) -
700 const sal_Bool bFlyCmp
= pTmpPortion
->IsFlyCntPortion() ?
701 static_cast<const SwFlyCntPortion
*>(pTmpPortion
)->IsMax() :
702 !( pTmpPortion
== _pDontConsiderPortion
);
706 _orObjAscent
= Max( _orObjAscent
, nPortionAsc
);
707 _orObjDescent
= Max( _orObjDescent
, nPortionDesc
);
710 if ( !pTmpPortion
->IsFlyCntPortion() && !pTmpPortion
->IsGrfNumPortion() )
712 _orAscent
= Max( _orAscent
, nPortionAsc
);
713 _orDescent
= Max( _orDescent
, nPortionDesc
);
716 pTmpPortion
= pTmpPortion
->GetPortion();
720 /*************************************************************************
722 *************************************************************************/
724 SwCharRange
&SwCharRange::operator+=(const SwCharRange
&rRange
)
726 if(0 != rRange
.nLen
) {
728 nStart
= rRange
.nStart
;
732 if(rRange
.nStart
+ rRange
.nLen
> nStart
+ nLen
) {
733 nLen
= rRange
.nStart
+ rRange
.nLen
- nStart
;
735 if(rRange
.nStart
< nStart
) {
736 nLen
+= nStart
- rRange
.nStart
;
737 nStart
= rRange
.nStart
;
744 /*************************************************************************
745 * SwScriptInfo::SwScriptInfo()
746 *************************************************************************/
747 SwScriptInfo::SwScriptInfo() :
753 /*************************************************************************
754 * SwScriptInfo::~SwScriptInfo()
755 *************************************************************************/
756 SwScriptInfo::~SwScriptInfo()
760 /*************************************************************************
761 * SwScriptInfo::WhichFont()
763 * Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to
764 * Sw Script Types (SW_LATIN, SW_CJK, SW_CTL), used to identify the font
765 *************************************************************************/
766 BYTE
SwScriptInfo::WhichFont( xub_StrLen nIdx
, const String
* pTxt
, const SwScriptInfo
* pSI
)
768 ASSERT( pTxt
|| pSI
,"How should I determine the script type?" );
771 // First we try to use our SwScriptInfo
773 nScript
= pSI
->ScriptType( nIdx
);
775 // Ok, we have to ask the break iterator
776 nScript
= pBreakIt
->GetRealScriptOfText( *pTxt
, nIdx
);
779 case i18n::ScriptType::LATIN
: return SW_LATIN
;
780 case i18n::ScriptType::ASIAN
: return SW_CJK
;
781 case i18n::ScriptType::COMPLEX
: return SW_CTL
;
784 ASSERT( sal_False
, "Somebody tells lies about the script type!" );
788 /*************************************************************************
789 * SwScriptInfo::InitScriptInfo()
791 * searches for script changes in rTxt and stores them
792 *************************************************************************/
794 void SwScriptInfo::InitScriptInfo( const SwTxtNode
& rNode
)
796 InitScriptInfo( rNode
, nDefaultDir
== UBIDI_RTL
);
799 void SwScriptInfo::InitScriptInfo( const SwTxtNode
& rNode
, sal_Bool bRTL
)
801 if( !pBreakIt
->GetBreakIter().is() )
804 const String
& rTxt
= rNode
.GetTxt();
807 // HIDDEN TEXT INFORMATION
809 Range
aRange( 0, rTxt
.Len() ? rTxt
.Len() - 1 : 0 );
810 MultiSelection
aHiddenMulti( aRange
);
811 CalcHiddenRanges( rNode
, aHiddenMulti
);
813 aHiddenChg
.Remove( 0, aHiddenChg
.Count() );
814 USHORT nHiddenIdx
= 0;
816 for( i
= 0; i
< aHiddenMulti
.GetRangeCount(); ++i
)
818 const Range
& rRange
= aHiddenMulti
.GetRange( i
);
819 const xub_StrLen nStart
= (xub_StrLen
)rRange
.Min();
820 const xub_StrLen nEnd
= (xub_StrLen
)rRange
.Max() + 1;
822 aHiddenChg
.Insert( nStart
, nHiddenIdx
++ );
823 aHiddenChg
.Insert( nEnd
, nHiddenIdx
++ );
827 // SCRIPT AND SCRIPT RELATED INFORMATION
830 xub_StrLen nChg
= nInvalidityPos
;
832 // STRING_LEN means the data structure is up to date
833 nInvalidityPos
= STRING_LEN
;
835 // this is the default direction
836 nDefaultDir
= static_cast<BYTE
>(bRTL
? UBIDI_RTL
: UBIDI_LTR
);
838 // counter for script info arrays
840 // counter for compression information arrays
842 // counter for kashida array
845 BYTE nScript
= i18n::ScriptType::LATIN
;
848 const SwCharCompressType aCompEnum
= rNode
.getIDocumentSettingAccess()->getCharacterCompressionType();
850 // justification type
851 const sal_Bool bAdjustBlock
= SVX_ADJUST_BLOCK
==
852 rNode
.GetSwAttrSet().GetAdjust().GetAdjust();
855 // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
860 // if change position = 0 we do not use any data from the arrays
861 // because by deleting all characters of the first group at the beginning
862 // of a paragraph nScript is set to a wrong value
863 ASSERT( CountScriptChg(), "Where're my changes of script?" );
864 while( nCnt
< CountScriptChg() )
866 if ( nChg
> GetScriptChg( nCnt
) )
870 nScript
= GetScriptType( nCnt
);
874 if( CHARCOMPRESS_NONE
!= aCompEnum
)
876 while( nCntComp
< CountCompChg() )
878 if ( nChg
> GetCompStart( nCntComp
) )
886 while( nCntKash
< CountKashida() )
888 if ( nChg
> GetKashida( nCntKash
) )
897 // ADJUST nChg VALUE:
900 // by stepping back one position we know that we are inside a group
901 // declared as an nScript group
905 const xub_StrLen nGrpStart
= nCnt
? GetScriptChg( nCnt
- 1 ) : 0;
907 // we go back in our group until we reach the first character of
909 while ( nChg
> nGrpStart
&&
910 nScript
!= pBreakIt
->GetBreakIter()->getScriptType( rTxt
, nChg
) )
913 // If we are at the start of a group, we do not trust nScript,
914 // we better get nScript from the breakiterator:
915 if ( nChg
== nGrpStart
)
916 nScript
= (BYTE
)pBreakIt
->GetBreakIter()->getScriptType( rTxt
, nChg
);
919 // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
922 // remove invalid entries from script information arrays
923 const USHORT nScriptRemove
= aScriptChg
.Count() - nCnt
;
924 aScriptChg
.Remove( nCnt
, nScriptRemove
);
925 aScriptType
.Remove( nCnt
, nScriptRemove
);
927 // get the start of the last compression group
928 USHORT nLastCompression
= nChg
;
932 nLastCompression
= GetCompStart( nCntComp
);
933 if( nChg
>= nLastCompression
+ GetCompLen( nCntComp
) )
935 nLastCompression
= nChg
;
940 // remove invalid entries from compression information arrays
941 const USHORT nCompRemove
= aCompChg
.Count() - nCntComp
;
942 aCompChg
.Remove( nCntComp
, nCompRemove
);
943 aCompLen
.Remove( nCntComp
, nCompRemove
);
944 aCompType
.Remove( nCntComp
, nCompRemove
);
946 // get the start of the last kashida group
947 USHORT nLastKashida
= nChg
;
948 if( nCntKash
&& i18n::ScriptType::COMPLEX
== nScript
)
951 nLastKashida
= GetKashida( nCntKash
);
954 // remove invalid entries from kashida array
955 aKashida
.Remove( nCntKash
, aKashida
.Count() - nCntKash
);
958 // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
959 // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
962 if( WEAK
== pBreakIt
->GetBreakIter()->getScriptType( rTxt
, nChg
) )
964 // If the beginning of the current group is weak, this means that
965 // all of the characters in this grounp are weak. We have to assign
966 // the scripts to these characters depending on the fonts which are
967 // set for these characters to display them.
969 (xub_StrLen
)pBreakIt
->GetBreakIter()->endOfScript( rTxt
, nChg
, WEAK
);
971 if( nEnd
> rTxt
.Len() )
974 nScript
= (BYTE
)GetI18NScriptTypeOfLanguage( (USHORT
)GetAppLanguage() );
976 ASSERT( i18n::ScriptType::LATIN
== nScript
||
977 i18n::ScriptType::ASIAN
== nScript
||
978 i18n::ScriptType::COMPLEX
== nScript
, "Wrong default language" );
982 // Get next script type or set to weak in order to exit
983 BYTE nNextScript
= ( nEnd
< rTxt
.Len() ) ?
984 (BYTE
)pBreakIt
->GetBreakIter()->getScriptType( rTxt
, nEnd
) :
987 if ( nScript
!= nNextScript
)
989 aScriptChg
.Insert( nEnd
, nCnt
);
990 aScriptType
.Insert( nScript
, nCnt
++ );
991 nScript
= nNextScript
;
996 // UPDATE THE SCRIPT INFO ARRAYS:
999 while ( nChg
< rTxt
.Len() || ( !aScriptChg
.Count() && !rTxt
.Len() ) )
1001 ASSERT( i18n::ScriptType::WEAK
!= nScript
,
1002 "Inserting WEAK into SwScriptInfo structure" );
1003 ASSERT( STRING_LEN
!= nChg
, "65K? Strange length of script section" );
1005 xub_StrLen nSearchStt
= nChg
;
1006 nChg
= (xub_StrLen
)pBreakIt
->GetBreakIter()->endOfScript( rTxt
, nSearchStt
, nScript
);
1008 if ( nChg
> rTxt
.Len() )
1011 // --> FME 2008-09-17 #i28203#
1012 // for 'complex' portions, we make sure that a portion does not contain more
1014 if( i18n::ScriptType::COMPLEX
== nScript
&& pBreakIt
->GetScriptTypeDetector().is() )
1016 const short nScriptType
= pBreakIt
->GetScriptTypeDetector()->getCTLScriptType( rTxt
, nSearchStt
);
1017 xub_StrLen nNextCTLScriptStart
= nSearchStt
;
1018 short nCurrentScriptType
= nScriptType
;
1019 while( com::sun::star::i18n::CTLScriptType::CTL_UNKNOWN
== nCurrentScriptType
|| nScriptType
== nCurrentScriptType
)
1021 nNextCTLScriptStart
= (xub_StrLen
)pBreakIt
->GetScriptTypeDetector()->endOfCTLScriptType( rTxt
, nNextCTLScriptStart
);
1022 if( nNextCTLScriptStart
< rTxt
.Len() && nNextCTLScriptStart
< nChg
)
1023 nCurrentScriptType
= pBreakIt
->GetScriptTypeDetector()->getCTLScriptType( rTxt
, nNextCTLScriptStart
);
1027 nChg
= Min( nChg
, nNextCTLScriptStart
);
1031 // special case for dotted circle since it can be used with complex
1032 // before a mark, so we want it associated with the mark's script
1033 if (nChg
< rTxt
.Len() && nChg
> 0 && (i18n::ScriptType::WEAK
==
1034 pBreakIt
->GetBreakIter()->getScriptType(rTxt
,nChg
- 1)))
1036 int8_t nType
= u_charType(rTxt
.GetChar(nChg
) );
1037 if (nType
== U_NON_SPACING_MARK
|| nType
== U_ENCLOSING_MARK
||
1038 nType
== U_COMBINING_SPACING_MARK
)
1040 aScriptChg
.Insert( nChg
- 1, nCnt
);
1044 aScriptChg
.Insert( nChg
, nCnt
);
1049 aScriptChg
.Insert( nChg
, nCnt
);
1051 aScriptType
.Insert( nScript
, nCnt
++ );
1053 // if current script is asian, we search for compressable characters
1055 if ( CHARCOMPRESS_NONE
!= aCompEnum
&&
1056 i18n::ScriptType::ASIAN
== nScript
)
1058 BYTE ePrevState
= NONE
;
1060 USHORT nPrevChg
= nLastCompression
;
1062 while ( nLastCompression
< nChg
)
1064 xub_Unicode cChar
= rTxt
.GetChar( nLastCompression
);
1066 // examine current character
1069 // Left punctuation found
1070 case 0x3008: case 0x300A: case 0x300C: case 0x300E:
1071 case 0x3010: case 0x3014: case 0x3016: case 0x3018:
1072 case 0x301A: case 0x301D:
1073 eState
= SPECIAL_LEFT
;
1075 // Right punctuation found
1076 case 0x3001: case 0x3002: case 0x3009: case 0x300B:
1077 case 0x300D: case 0x300F: case 0x3011: case 0x3015:
1078 case 0x3017: case 0x3019: case 0x301B: case 0x301E:
1080 eState
= SPECIAL_RIGHT
;
1083 eState
= static_cast<BYTE
>( ( 0x3040 <= cChar
&& 0x3100 > cChar
) ? KANA
: NONE
);
1086 // insert range of compressable characters
1087 if( ePrevState
!= eState
)
1089 if ( ePrevState
!= NONE
)
1091 // insert start and type
1092 if ( CHARCOMPRESS_PUNCTUATION_KANA
== aCompEnum
||
1093 ePrevState
!= KANA
)
1095 aCompChg
.Insert( nPrevChg
, nCntComp
);
1096 BYTE nTmpType
= ePrevState
;
1097 aCompType
.Insert( nTmpType
, nCntComp
);
1098 aCompLen
.Insert( nLastCompression
- nPrevChg
, nCntComp
++ );
1102 ePrevState
= eState
;
1103 nPrevChg
= nLastCompression
;
1109 // we still have to examine last entry
1110 if ( ePrevState
!= NONE
)
1112 // insert start and type
1113 if ( CHARCOMPRESS_PUNCTUATION_KANA
== aCompEnum
||
1114 ePrevState
!= KANA
)
1116 aCompChg
.Insert( nPrevChg
, nCntComp
);
1117 BYTE nTmpType
= ePrevState
;
1118 aCompType
.Insert( nTmpType
, nCntComp
);
1119 aCompLen
.Insert( nLastCompression
- nPrevChg
, nCntComp
++ );
1124 // we search for connecting opportunities (kashida)
1125 else if ( bAdjustBlock
&& i18n::ScriptType::COMPLEX
== nScript
)
1127 SwScanner
aScanner( rNode
, rNode
.GetTxt(), 0, 0,
1128 i18n::WordType::DICTIONARY_WORD
,
1129 nLastKashida
, nChg
);
1131 // the search has to be performed on a per word base
1132 while ( aScanner
.NextWord() )
1134 const XubString
& rWord
= aScanner
.GetWord();
1136 xub_StrLen nIdx
= 0;
1137 xub_StrLen nKashidaPos
= STRING_LEN
;
1139 xub_Unicode cPrevCh
= 0;
1141 USHORT nPriorityLevel
= 7; // 0..6 = level found
1144 xub_StrLen nWordLen
= rWord
.Len();
1146 // ignore trailing vowel chars
1147 while( nWordLen
&& isTransparentChar( rWord
.GetChar( nWordLen
- 1 )))
1150 while (nIdx
< nWordLen
)
1152 cCh
= rWord
.GetChar( nIdx
);
1155 // after user inserted kashida
1158 nKashidaPos
= aScanner
.GetBegin() + nIdx
;
1163 // after a Seen or Sad
1164 if (nPriorityLevel
>= 1 && nIdx
< nWordLen
- 1)
1166 if( isSeenOrSadChar( cCh
)
1167 && (rWord
.GetChar( nIdx
+1 ) != 0x200C) ) // #i98410#: prevent ZWNJ expansion
1169 nKashidaPos
= aScanner
.GetBegin() + nIdx
;
1175 // before final form of Teh Marbuta, Hah, Dal
1176 if ( nPriorityLevel
>= 2 && nIdx
> 0 )
1178 if ( isTehMarbutaChar ( cCh
) || // Teh Marbuta (right joining)
1179 isDalChar ( cCh
) || // Dal (right joining) final form may appear in the middle of word
1180 ( isHahChar ( cCh
) && nIdx
== nWordLen
- 1)) // Hah (dual joining) only at end of word
1183 ASSERT( 0 != cPrevCh
, "No previous character" )
1184 // check if character is connectable to previous character,
1185 if ( lcl_ConnectToPrev( cCh
, cPrevCh
) )
1187 nKashidaPos
= aScanner
.GetBegin() + nIdx
- 1;
1194 // before final form of Alef, Lam or Kaf
1195 if ( nPriorityLevel
>= 3 && nIdx
> 0 )
1197 if ( isAlefChar ( cCh
) || // Alef (right joining) final form may appear in the middle of word
1198 (( isLamChar ( cCh
) || // Lam
1199 isKafChar ( cCh
) || // Kaf (both dual joining)
1201 && nIdx
== nWordLen
- 1)) // only at end of word
1203 ASSERT( 0 != cPrevCh
, "No previous character" )
1204 // check if character is connectable to previous character,
1205 if ( lcl_ConnectToPrev( cCh
, cPrevCh
) )
1207 nKashidaPos
= aScanner
.GetBegin() + nIdx
- 1;
1215 if ( nPriorityLevel
>= 4 && nIdx
> 0 && nIdx
< nWordLen
- 1 )
1217 if ( isBaaChar ( cCh
)) // Bah
1219 // check if next character is Reh, Yeh or Alef Maksura
1220 xub_Unicode cNextCh
= rWord
.GetChar( nIdx
+ 1 );
1221 if ( isRehChar ( cNextCh
) || isYehChar ( cNextCh
))
1223 ASSERT( 0 != cPrevCh
, "No previous character" )
1224 // check if character is connectable to previous character,
1225 if ( lcl_ConnectToPrev( cCh
, cPrevCh
) )
1227 nKashidaPos
= aScanner
.GetBegin() + nIdx
- 1;
1235 // before the final form of Waw, Ain, Qaf and Fa
1236 if ( nPriorityLevel
>= 5 && nIdx
> 0 )
1238 if ( isWawChar ( cCh
) || // Wav (right joining)
1239 // final form may appear in the middle of word
1240 (( isAinChar ( cCh
) || // Ain (dual joining)
1241 isQafChar ( cCh
) || // Qaf (dual joining)
1242 isFeChar ( cCh
) ) // Feh (dual joining)
1243 && nIdx
== nWordLen
- 1)) // only at end of word
1245 ASSERT( 0 != cPrevCh
, "No previous character" )
1246 // check if character is connectable to previous character,
1247 if ( lcl_ConnectToPrev( cCh
, cPrevCh
) )
1249 nKashidaPos
= aScanner
.GetBegin() + nIdx
- 1;
1255 // other connecting possibilities
1256 if ( nPriorityLevel
>= 6 && nIdx
> 0 )
1258 // remaining right joiners
1260 if ( isRehChar ( cCh
) || // Reh Zain (right joining)
1261 // final form may appear in the middle of word
1262 ( 0x60C <= cCh
&& 0x6FE >= cCh
// all others
1263 && nIdx
== nWordLen
- 1)) // only at end of word
1265 ASSERT( 0 != cPrevCh
, "No previous character" )
1266 // check if character is connectable to previous character,
1267 if ( lcl_ConnectToPrev( cCh
, cPrevCh
) )
1269 nKashidaPos
= aScanner
.GetBegin() + nIdx
- 1;
1275 // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
1276 // Damma, Kasra, Shadda and Sukun when checking if
1277 // a character can be connected to previous character.
1278 if ( !isTransparentChar ( cCh
) )
1282 } // end of current word
1284 if ( STRING_LEN
!= nKashidaPos
)
1285 aKashida
.Insert( nKashidaPos
, nCntKash
++ );
1286 } // end of kashida search
1289 if ( nChg
< rTxt
.Len() )
1290 nScript
= (BYTE
)pBreakIt
->GetBreakIter()->getScriptType( rTxt
, nChg
);
1292 nLastCompression
= nChg
;
1293 nLastKashida
= nChg
;
1297 // check kashida data
1298 long nTmpKashidaPos
= -1;
1299 sal_Bool bWrongKash
= sal_False
;
1300 for (i
= 0; i
< aKashida
.Count(); ++i
)
1302 long nCurrKashidaPos
= GetKashida( i
);
1303 if ( nCurrKashidaPos
<= nTmpKashidaPos
)
1305 bWrongKash
= sal_True
;
1308 nTmpKashidaPos
= nCurrKashidaPos
;
1310 ASSERT( ! bWrongKash
, "Kashida array contains wrong data" )
1313 // remove invalid entries from direction information arrays
1314 const USHORT nDirRemove
= aDirChg
.Count();
1315 aDirChg
.Remove( 0, nDirRemove
);
1316 aDirType
.Remove( 0, nDirRemove
);
1318 // Perform Unicode Bidi Algorithm for text direction information
1319 bool bPerformUBA
= UBIDI_LTR
!= nDefaultDir
;
1321 while( !bPerformUBA
&& nCnt
< CountScriptChg() )
1323 if ( i18n::ScriptType::COMPLEX
== GetScriptType( nCnt
++ ) )
1327 // do not call the unicode bidi algorithm if not required
1330 UpdateBidiInfo( rTxt
);
1332 // #i16354# Change script type for RTL text to CTL:
1333 // 1. All text in RTL runs will use the CTL font
1334 // #i89825# change the script type also to CTL (hennerdrewes)
1335 // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
1336 for ( USHORT nDirIdx
= 0; nDirIdx
< aDirChg
.Count(); ++nDirIdx
)
1338 const BYTE nCurrDirType
= GetDirType( nDirIdx
);
1339 // nStart ist start of RTL run:
1340 const xub_StrLen nStart
= nDirIdx
> 0 ? GetDirChg( nDirIdx
- 1 ) : 0;
1341 // nEnd is end of RTL run:
1342 const xub_StrLen nEnd
= GetDirChg( nDirIdx
);
1344 if ( nCurrDirType
% 2 == UBIDI_RTL
|| // text in RTL run
1345 ( nCurrDirType
> UBIDI_LTR
&& !lcl_HasStrongLTR( rTxt
, nStart
, nEnd
) ) ) // non-strong text in embedded LTR run
1347 // nScriptIdx points into the ScriptArrays:
1348 USHORT nScriptIdx
= 0;
1350 // Skip entries in ScriptArray which are not inside the RTL run:
1351 // Make nScriptIdx become the index of the script group with
1352 // 1. nStartPosOfGroup <= nStart and
1353 // 2. nEndPosOfGroup > nStart
1354 while ( GetScriptChg( nScriptIdx
) <= nStart
)
1357 const xub_StrLen nStartPosOfGroup
= nScriptIdx
? GetScriptChg( nScriptIdx
- 1 ) : 0;
1358 const BYTE nScriptTypeOfGroup
= GetScriptType( nScriptIdx
);
1360 ASSERT( nStartPosOfGroup
<= nStart
&& GetScriptChg( nScriptIdx
) > nStart
,
1361 "Script override with CTL font trouble" )
1363 // Check if we have to insert a new script change at
1364 // position nStart. If nStartPosOfGroup < nStart,
1365 // we have to insert a new script change:
1366 if ( nStart
> 0 && nStartPosOfGroup
< nStart
)
1368 aScriptChg
.Insert( nStart
, nScriptIdx
);
1369 aScriptType
.Insert( nScriptTypeOfGroup
, nScriptIdx
);
1373 // Remove entries in ScriptArray which end inside the RTL run:
1374 while ( nScriptIdx
< aScriptChg
.Count() && GetScriptChg( nScriptIdx
) <= nEnd
)
1376 aScriptChg
.Remove( nScriptIdx
, 1 );
1377 aScriptType
.Remove( nScriptIdx
, 1 );
1380 // Insert a new entry in ScriptArray for the end of the RTL run:
1381 aScriptChg
.Insert( nEnd
, nScriptIdx
);
1382 aScriptType
.Insert( i18n::ScriptType::COMPLEX
, nScriptIdx
);
1384 #if OSL_DEBUG_LEVEL > 1
1386 BYTE nLastScriptType
= i18n::ScriptType::WEAK
;
1387 xub_StrLen nScriptChg
;
1388 xub_StrLen nLastScriptChg
= 0;
1389 (void) nLastScriptChg
;
1390 (void) nLastScriptType
;
1392 for ( USHORT i2
= 0; i2
< aScriptChg
.Count(); ++i2
)
1394 nScriptChg
= GetScriptChg( i2
);
1395 nScriptType
= GetScriptType( i2
);
1396 ASSERT( nLastScriptType
!= nScriptType
&&
1397 nLastScriptChg
< nScriptChg
,
1398 "Heavy InitScriptType() confusion" )
1406 void SwScriptInfo::UpdateBidiInfo( const String
& rTxt
)
1408 // remove invalid entries from direction information arrays
1409 const USHORT nDirRemove
= aDirChg
.Count();
1410 aDirChg
.Remove( 0, nDirRemove
);
1411 aDirType
.Remove( 0, nDirRemove
);
1414 // Bidi functions from icu 2.0
1416 UErrorCode nError
= U_ZERO_ERROR
;
1417 UBiDi
* pBidi
= ubidi_openSized( rTxt
.Len(), 0, &nError
);
1418 nError
= U_ZERO_ERROR
;
1420 ubidi_setPara( pBidi
, reinterpret_cast<const UChar
*>(rTxt
.GetBuffer()), rTxt
.Len(), // UChar != sal_Unicode in MinGW
1421 nDefaultDir
, NULL
, &nError
);
1422 nError
= U_ZERO_ERROR
;
1423 long nCount
= ubidi_countRuns( pBidi
, &nError
);
1426 UBiDiLevel nCurrDir
;
1427 // counter for direction information arrays
1430 for ( USHORT nIdx
= 0; nIdx
< nCount
; ++nIdx
)
1432 ubidi_getLogicalRun( pBidi
, nStart
, &nEnd
, &nCurrDir
);
1433 aDirChg
.Insert( (USHORT
)nEnd
, nCntDir
);
1434 aDirType
.Insert( (BYTE
)nCurrDir
, nCntDir
++ );
1438 ubidi_close( pBidi
);
1442 /*************************************************************************
1443 * SwScriptInfo::NextScriptChg(..)
1444 * returns the position of the next character which belongs to another script
1445 * than the character of the actual (input) position.
1446 * If there's no script change until the end of the paragraph, it will return
1448 * Scripts are Asian (Chinese, Japanese, Korean),
1449 * Latin ( English etc.)
1450 * and Complex ( Hebrew, Arabian )
1451 *************************************************************************/
1453 xub_StrLen
SwScriptInfo::NextScriptChg( const xub_StrLen nPos
) const
1455 USHORT nEnd
= CountScriptChg();
1456 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1458 if( nPos
< GetScriptChg( nX
) )
1459 return GetScriptChg( nX
);
1465 /*************************************************************************
1466 * SwScriptInfo::ScriptType(..)
1467 * returns the script of the character at the input position
1468 *************************************************************************/
1470 BYTE
SwScriptInfo::ScriptType( const xub_StrLen nPos
) const
1472 USHORT nEnd
= CountScriptChg();
1473 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1475 if( nPos
< GetScriptChg( nX
) )
1476 return GetScriptType( nX
);
1479 // the default is the application language script
1480 return (BYTE
)GetI18NScriptTypeOfLanguage( (USHORT
)GetAppLanguage() );
1483 xub_StrLen
SwScriptInfo::NextDirChg( const xub_StrLen nPos
,
1484 const BYTE
* pLevel
) const
1486 BYTE nCurrDir
= pLevel
? *pLevel
: 62;
1487 USHORT nEnd
= CountDirChg();
1488 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1490 if( nPos
< GetDirChg( nX
) &&
1491 ( nX
+ 1 == nEnd
|| GetDirType( nX
+ 1 ) <= nCurrDir
) )
1492 return GetDirChg( nX
);
1498 BYTE
SwScriptInfo::DirType( const xub_StrLen nPos
) const
1500 USHORT nEnd
= CountDirChg();
1501 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1503 if( nPos
< GetDirChg( nX
) )
1504 return GetDirType( nX
);
1510 /*************************************************************************
1511 * SwScriptInfo::MaskHiddenRanges(..)
1512 * Takes a string and replaced the hidden ranges with cChar.
1513 **************************************************************************/
1515 USHORT
SwScriptInfo::MaskHiddenRanges( const SwTxtNode
& rNode
, XubString
& rText
,
1516 const xub_StrLen nStt
, const xub_StrLen nEnd
,
1517 const xub_Unicode cChar
)
1519 ASSERT( rNode
.GetTxt().Len() == rText
.Len(), "MaskHiddenRanges, string len mismatch" )
1522 xub_StrLen nHiddenStart
;
1523 xub_StrLen nHiddenEnd
;
1524 USHORT nNumOfHiddenChars
= 0;
1525 GetBoundsOfHiddenRange( rNode
, 0, nHiddenStart
, nHiddenEnd
, &aList
);
1526 PositionList::const_reverse_iterator
rFirst( aList
.end() );
1527 PositionList::const_reverse_iterator
rLast( aList
.begin() );
1528 while ( rFirst
!= rLast
)
1530 nHiddenEnd
= *(rFirst
++);
1531 nHiddenStart
= *(rFirst
++);
1533 if ( nHiddenEnd
< nStt
|| nHiddenStart
> nEnd
)
1536 while ( nHiddenStart
< nHiddenEnd
&& nHiddenStart
< nEnd
)
1538 if ( nHiddenStart
>= nStt
&& nHiddenStart
< nEnd
)
1540 rText
.SetChar( nHiddenStart
, cChar
);
1541 ++nNumOfHiddenChars
;
1547 return nNumOfHiddenChars
;
1550 /*************************************************************************
1551 * SwScriptInfo::DeleteHiddenRanges(..)
1552 * Takes a SwTxtNode and deletes the hidden ranges from the node.
1553 **************************************************************************/
1555 void SwScriptInfo::DeleteHiddenRanges( SwTxtNode
& rNode
)
1558 xub_StrLen nHiddenStart
;
1559 xub_StrLen nHiddenEnd
;
1560 GetBoundsOfHiddenRange( rNode
, 0, nHiddenStart
, nHiddenEnd
, &aList
);
1561 PositionList::const_reverse_iterator
rFirst( aList
.end() );
1562 PositionList::const_reverse_iterator
rLast( aList
.begin() );
1563 while ( rFirst
!= rLast
)
1565 nHiddenEnd
= *(rFirst
++);
1566 nHiddenStart
= *(rFirst
++);
1568 SwPaM
aPam( rNode
, nHiddenStart
, rNode
, nHiddenEnd
);
1569 rNode
.getIDocumentContentOperations()->Delete( aPam
);
1573 /*************************************************************************
1574 * SwScriptInfo::GetBoundsOfHiddenRange(..)
1576 **************************************************************************/
1578 bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTxtNode
& rNode
, xub_StrLen nPos
,
1579 xub_StrLen
& rnStartPos
, xub_StrLen
& rnEndPos
,
1580 PositionList
* pList
)
1582 rnStartPos
= STRING_LEN
;
1585 bool bNewContainsHiddenChars
= false;
1588 // Optimization: First examine the flags at the text node:
1590 if ( !rNode
.IsCalcHiddenCharFlags() )
1592 bool bWholePara
= rNode
.HasHiddenCharAttribute( true );
1593 bool bContainsHiddenChars
= rNode
.HasHiddenCharAttribute( false );
1594 if ( !bContainsHiddenChars
)
1601 pList
->push_back( 0 );
1602 pList
->push_back( rNode
.GetTxt().Len() );
1606 rnEndPos
= rNode
.GetTxt().Len();
1611 const SwScriptInfo
* pSI
= SwScriptInfo::GetScriptInfo( rNode
);
1615 // Check first, if we have a valid SwScriptInfo object for this text node:
1617 bNewContainsHiddenChars
= pSI
->GetBoundsOfHiddenRange( nPos
, rnStartPos
, rnEndPos
, pList
);
1618 const bool bNewHiddenCharsHidePara
= ( rnStartPos
== 0 && rnEndPos
>= rNode
.GetTxt().Len() );
1619 rNode
.SetHiddenCharAttribute( bNewHiddenCharsHidePara
, bNewContainsHiddenChars
);
1624 // No valid SwScriptInfo Object, we have to do it the hard way:
1626 Range
aRange( 0, rNode
.GetTxt().Len() ? rNode
.GetTxt().Len() - 1 : 0 );
1627 MultiSelection
aHiddenMulti( aRange
);
1628 SwScriptInfo::CalcHiddenRanges( rNode
, aHiddenMulti
);
1629 for( USHORT i
= 0; i
< aHiddenMulti
.GetRangeCount(); ++i
)
1631 const Range
& rRange
= aHiddenMulti
.GetRange( i
);
1632 const xub_StrLen nHiddenStart
= (xub_StrLen
)rRange
.Min();
1633 const xub_StrLen nHiddenEnd
= (xub_StrLen
)rRange
.Max() + 1;
1635 if ( nHiddenStart
> nPos
)
1637 else if ( nHiddenStart
<= nPos
&& nPos
< nHiddenEnd
)
1639 rnStartPos
= nHiddenStart
;
1640 rnEndPos
= Min( nHiddenEnd
, rNode
.GetTxt().Len() );
1647 for( USHORT i
= 0; i
< aHiddenMulti
.GetRangeCount(); ++i
)
1649 const Range
& rRange
= aHiddenMulti
.GetRange( i
);
1650 pList
->push_back( (xub_StrLen
)rRange
.Min() );
1651 pList
->push_back( (xub_StrLen
)rRange
.Max() + 1 );
1655 bNewContainsHiddenChars
= aHiddenMulti
.GetRangeCount() > 0;
1658 return bNewContainsHiddenChars
;
1661 /*************************************************************************
1662 * SwScriptInfo::GetBoundsOfHiddenRange(..)
1663 * non-static version
1664 **************************************************************************/
1666 bool SwScriptInfo::GetBoundsOfHiddenRange( xub_StrLen nPos
, xub_StrLen
& rnStartPos
,
1667 xub_StrLen
& rnEndPos
, PositionList
* pList
) const
1669 rnStartPos
= STRING_LEN
;
1672 USHORT nEnd
= CountHiddenChg();
1673 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1675 const xub_StrLen nHiddenStart
= GetHiddenChg( nX
++ );
1676 const xub_StrLen nHiddenEnd
= GetHiddenChg( nX
);
1678 if ( nHiddenStart
> nPos
)
1680 else if ( nHiddenStart
<= nPos
&& nPos
< nHiddenEnd
)
1682 rnStartPos
= nHiddenStart
;
1683 rnEndPos
= nHiddenEnd
;
1690 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1692 pList
->push_back( GetHiddenChg( nX
++ ) );
1693 pList
->push_back( GetHiddenChg( nX
) );
1697 return CountHiddenChg() > 0;
1700 /*************************************************************************
1701 * SwScriptInfo::IsInHiddenRange()
1702 **************************************************************************/
1704 bool SwScriptInfo::IsInHiddenRange( const SwTxtNode
& rNode
, xub_StrLen nPos
)
1706 xub_StrLen nStartPos
;
1708 SwScriptInfo::GetBoundsOfHiddenRange( rNode
, nPos
, nStartPos
, nEndPos
);
1709 return nStartPos
!= STRING_LEN
;
1713 #if OSL_DEBUG_LEVEL > 1
1714 /*************************************************************************
1715 * SwScriptInfo::CompType(..)
1716 * returns the type of the compressed character
1717 *************************************************************************/
1719 BYTE
SwScriptInfo::CompType( const xub_StrLen nPos
) const
1721 USHORT nEnd
= CountCompChg();
1722 for( USHORT nX
= 0; nX
< nEnd
; ++nX
)
1724 xub_StrLen nChg
= GetCompStart( nX
);
1729 if( nPos
< nChg
+ GetCompLen( nX
) )
1730 return GetCompType( nX
);
1736 /*************************************************************************
1737 * SwScriptInfo::HasKana()
1738 * returns, if there are compressable kanas or specials
1739 * betwenn nStart and nEnd
1740 *************************************************************************/
1742 USHORT
SwScriptInfo::HasKana( xub_StrLen nStart
, const xub_StrLen nLen
) const
1744 USHORT nCnt
= CountCompChg();
1745 xub_StrLen nEnd
= nStart
+ nLen
;
1747 for( USHORT nX
= 0; nX
< nCnt
; ++nX
)
1749 xub_StrLen nKanaStart
= GetCompStart( nX
);
1750 xub_StrLen nKanaEnd
= nKanaStart
+ GetCompLen( nX
);
1752 if ( nKanaStart
>= nEnd
)
1755 if ( nStart
< nKanaEnd
)
1762 /*************************************************************************
1763 * SwScriptInfo::Compress()
1764 *************************************************************************/
1766 long SwScriptInfo::Compress( sal_Int32
* pKernArray
, xub_StrLen nIdx
, xub_StrLen nLen
,
1767 const USHORT nCompress
, const USHORT nFontHeight
,
1768 Point
* pPoint
) const
1770 ASSERT( nCompress
, "Compression without compression?!" );
1771 ASSERT( nLen
, "Compression without text?!" );
1772 USHORT nCompCount
= CountCompChg();
1774 // In asian typography, there are full width and half width characters.
1775 // Full width punctuation characters can be compressed by 50 %
1776 // to determine this, we compare the font width with 75 % of its height
1777 USHORT nMinWidth
= ( 3 * nFontHeight
) / 4;
1779 USHORT nCompIdx
= HasKana( nIdx
, nLen
);
1781 if ( USHRT_MAX
== nCompIdx
)
1784 xub_StrLen nChg
= GetCompStart( nCompIdx
);
1785 xub_StrLen nCompLen
= GetCompLen( nCompIdx
);
1794 else if( nIdx
< nChg
+ nCompLen
)
1795 nCompLen
-= nIdx
- nChg
;
1797 if( nIdx
> nLen
|| nCompIdx
>= nCompCount
)
1801 long nLast
= nI
? pKernArray
[ nI
- 1 ] : 0;
1804 USHORT nType
= GetCompType( nCompIdx
);
1805 #if OSL_DEBUG_LEVEL > 1
1806 ASSERT( nType
== CompType( nIdx
), "Gimme the right type!" );
1808 nCompLen
= nCompLen
+ nIdx
;
1809 if( nCompLen
> nLen
)
1812 // are we allowed to compress the character?
1813 if ( pKernArray
[ nI
] - nLast
< nMinWidth
)
1819 while( nIdx
< nCompLen
)
1821 ASSERT( SwScriptInfo::NONE
!= nType
, "None compression?!" );
1823 // nLast is width of current character
1824 nLast
-= pKernArray
[ nI
];
1828 if( SwScriptInfo::KANA
!= nType
)
1831 if( pPoint
&& SwScriptInfo::SPECIAL_LEFT
== nType
)
1837 pPoint
->X() += nLast
;
1845 nLast
= pKernArray
[ nI
];
1847 pKernArray
[ nI
- 1 ] += nMove
;
1848 pKernArray
[ nI
++ ] -= nSub
;
1856 if( ++nCompIdx
< nCompCount
)
1858 nTmpChg
= GetCompStart( nCompIdx
);
1859 if( nTmpChg
> nLen
)
1861 nCompLen
= GetCompLen( nCompIdx
);
1865 while( nIdx
< nTmpChg
)
1867 nLast
= pKernArray
[ nI
];
1868 pKernArray
[ nI
++ ] -= nSub
;
1874 } while( nIdx
< nLen
);
1878 /*************************************************************************
1879 * SwScriptInfo::KashidaJustify()
1880 *************************************************************************/
1882 // Note on calling KashidaJustify():
1883 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
1884 // total number of kashida positions, or the number of kashida positions after some positions
1885 // have been dropped, depending on the state of the aKashidaInvalid array.
1887 USHORT
SwScriptInfo::KashidaJustify( sal_Int32
* pKernArray
,
1888 sal_Int32
* pScrArray
,
1891 long nSpaceAdd
) const
1893 ASSERT( nLen
, "Kashida justification without text?!" )
1895 if( !IsKashidaLine(nStt
))
1898 // evaluate kashida informatin in collected in SwScriptInfo
1900 USHORT nCntKash
= 0;
1901 while( nCntKash
< CountKashida() )
1903 if ( nStt
<= GetKashida( nCntKash
) )
1909 const xub_StrLen nEnd
= nStt
+ nLen
;
1911 USHORT nCntKashEnd
= nCntKash
;
1912 while ( nCntKashEnd
< CountKashida() )
1914 if ( nEnd
<= GetKashida( nCntKashEnd
) )
1920 USHORT nActualKashCount
= nCntKashEnd
- nCntKash
;
1921 for ( USHORT i
= nCntKash
; i
< nCntKashEnd
; ++i
)
1923 if ( nActualKashCount
&& !IsKashidaValid ( i
) )
1928 return nActualKashCount
;
1930 // do nothing if there is no more kashida
1931 if ( nCntKash
< CountKashida() )
1933 // skip any invalid kashidas
1934 while ( ! IsKashidaValid ( nCntKash
) && nCntKash
< nCntKashEnd
)
1937 xub_StrLen nKashidaPos
= GetKashida( nCntKash
);
1938 xub_StrLen nIdx
= nKashidaPos
;
1939 long nKashAdd
= nSpaceAdd
;
1941 while ( nIdx
< nEnd
)
1943 USHORT nArrayPos
= nIdx
- nStt
;
1945 // next kashida position
1947 while ( ! IsKashidaValid ( nCntKash
) && nCntKash
< nCntKashEnd
)
1950 nIdx
= nCntKash
< CountKashida() && IsKashidaValid ( nCntKash
) ? GetKashida( nCntKash
) : nEnd
;
1954 const USHORT nArrayEnd
= nIdx
- nStt
;
1956 while ( nArrayPos
< nArrayEnd
)
1958 pKernArray
[ nArrayPos
] += nKashAdd
;
1960 pScrArray
[ nArrayPos
] += nKashAdd
;
1963 nKashAdd
+= nSpaceAdd
;
1970 /*************************************************************************
1971 * SwScriptInfo::IsArabicText()
1973 * Checks if the current text is 'Arabic' text. Note that only the first
1974 * character has to be checked because a ctl portion only contains one
1975 * script, see NewTxtPortion
1976 *************************************************************************/
1977 sal_Bool
SwScriptInfo::IsArabicText( const XubString
& rTxt
, xub_StrLen nStt
, xub_StrLen nLen
)
1979 using namespace ::com::sun::star::i18n
;
1980 static ScriptTypeList typeList
[] = {
1981 { UnicodeScript_kArabic
, UnicodeScript_kArabic
, UnicodeScript_kArabic
}, // 11,
1982 { UnicodeScript_kScriptCount
, UnicodeScript_kScriptCount
, UnicodeScript_kScriptCount
} // 88
1985 // go forward if current position does not hold a regular character:
1986 const CharClass
& rCC
= GetAppCharClass();
1987 sal_Int32 nIdx
= nStt
;
1988 const xub_StrLen nEnd
= nStt
+ nLen
;
1989 while ( nIdx
< nEnd
&& !rCC
.isLetterNumeric( rTxt
, (xub_StrLen
)nIdx
) )
1996 // no regular character found in this portion. Go backward:
1998 while ( nIdx
>= 0 && !rCC
.isLetterNumeric( rTxt
, (xub_StrLen
)nIdx
) )
2006 const xub_Unicode cCh
= rTxt
.GetChar( (xub_StrLen
)nIdx
);
2007 const sal_Int16 type
= unicode::getUnicodeScriptType( cCh
, typeList
, UnicodeScript_kScriptCount
);
2008 return type
== UnicodeScript_kArabic
;
2013 /*************************************************************************
2014 * SwScriptInfo::IsKashidaValid()
2015 *************************************************************************/
2017 sal_Bool
SwScriptInfo::IsKashidaValid ( xub_StrLen nKashPos
) const
2019 for ( xub_StrLen i
= 0; i
< aKashidaInvalid
.Count(); ++i
)
2021 if ( aKashidaInvalid
[ i
] == nKashPos
)
2027 /*************************************************************************
2028 * SwScriptInfo::ClearKashidaInvalid()
2029 *************************************************************************/
2031 void SwScriptInfo::ClearKashidaInvalid ( xub_StrLen nKashPos
)
2033 for ( xub_StrLen i
= 0; i
< aKashidaInvalid
.Count(); ++i
)
2035 if ( aKashidaInvalid
[ i
] == nKashPos
)
2037 aKashidaInvalid
.Remove (i
, 1);
2043 /*************************************************************************
2044 * SwScriptInfo::MarkOrClearKashidaInvalid()
2045 *************************************************************************/
2047 // marks the first valid kashida in the given text range as invalid
2050 // clears all kashida invalid flags in the given text range
2052 bool SwScriptInfo::MarkOrClearKashidaInvalid ( xub_StrLen nStt
, xub_StrLen nLen
, bool bMark
, xub_StrLen nMarkCount
)
2054 USHORT nCntKash
= 0;
2055 while( nCntKash
< CountKashida() )
2057 if ( nStt
<= GetKashida( nCntKash
) )
2063 const xub_StrLen nEnd
= nStt
+ nLen
;
2065 while ( nCntKash
< CountKashida() )
2067 if ( nEnd
<= GetKashida( nCntKash
) )
2073 if ( IsKashidaValid ( nCntKash
) )
2075 MarkKashidaInvalid ( nCntKash
);
2083 ClearKashidaInvalid ( nCntKash
);
2091 void SwScriptInfo::MarkKashidaInvalid ( xub_StrLen nKashPos
)
2093 aKashidaInvalid
.Insert( nKashPos
, aKashidaInvalid
.Count() );
2096 /*************************************************************************
2097 * SwScriptInfo::GetKashidaPositions()
2098 *************************************************************************/
2099 // retrieve the kashida positions in the given text range
2100 USHORT
SwScriptInfo::GetKashidaPositions ( xub_StrLen nStt
, xub_StrLen nLen
,
2101 xub_StrLen
* pKashidaPosition
)
2103 USHORT nCntKash
= 0;
2104 while( nCntKash
< CountKashida() )
2106 if ( nStt
<= GetKashida( nCntKash
) )
2112 const xub_StrLen nEnd
= nStt
+ nLen
;
2114 USHORT nCntKashEnd
= nCntKash
;
2115 while ( nCntKashEnd
< CountKashida() )
2117 if ( nEnd
<= GetKashida( nCntKashEnd
) )
2121 pKashidaPosition
[ nCntKashEnd
- nCntKash
] = GetKashida ( nCntKashEnd
);
2125 return nCntKashEnd
- nCntKash
;
2128 void SwScriptInfo::SetNoKashidaLine ( xub_StrLen nStt
, xub_StrLen nLen
)
2130 aNoKashidaLine
.Insert( nStt
, aNoKashidaLine
.Count());
2131 aNoKashidaLineEnd
.Insert( nStt
+nLen
, aNoKashidaLineEnd
.Count());
2134 /*************************************************************************
2135 * SwScriptInfo::IsKashidaLine()
2136 *************************************************************************/
2137 // determines if the line uses kashida justification
2139 bool SwScriptInfo::IsKashidaLine ( xub_StrLen nCharIdx
) const
2141 for( xub_StrLen i
= 0; i
< aNoKashidaLine
.Count(); ++i
)
2143 if( nCharIdx
>= aNoKashidaLine
[ i
] && nCharIdx
< aNoKashidaLineEnd
[ i
])
2148 /*************************************************************************
2149 * SwScriptInfo::ClearKashidaLine()
2150 *************************************************************************/
2152 void SwScriptInfo::ClearNoKashidaLine ( xub_StrLen nStt
, xub_StrLen nLen
)
2155 while( i
< aNoKashidaLine
.Count())
2157 if( nStt
+ nLen
>= aNoKashidaLine
[ i
] && nStt
< aNoKashidaLineEnd
[ i
] )
2159 aNoKashidaLine
.Remove(i
, 1);
2160 aNoKashidaLineEnd
.Remove(i
, 1);
2167 /*************************************************************************
2168 * SwScriptInfo::MarkKashidasInvalid()
2169 *************************************************************************/
2170 // mark the given character indices as invalid kashida positions
2171 bool SwScriptInfo::MarkKashidasInvalid ( xub_StrLen nCnt
, xub_StrLen
* pKashidaPositions
)
2173 ASSERT( pKashidaPositions
&& nCnt
> 0, "Where are kashidas?" )
2175 USHORT nCntKash
= 0;
2176 xub_StrLen nKashidaPosIdx
= 0;
2178 while ( nCntKash
< CountKashida() && nKashidaPosIdx
< nCnt
)
2180 if ( pKashidaPositions
[nKashidaPosIdx
] > GetKashida( nCntKash
) )
2186 if ( pKashidaPositions
[nKashidaPosIdx
] == GetKashida( nCntKash
) && IsKashidaValid ( nCntKash
) )
2188 MarkKashidaInvalid ( nCntKash
);
2191 return false; // something is wrong
2197 /*************************************************************************
2198 * SwScriptInfo::ThaiJustify()
2199 *************************************************************************/
2201 USHORT
SwScriptInfo::ThaiJustify( const XubString
& rTxt
, sal_Int32
* pKernArray
,
2202 sal_Int32
* pScrArray
, xub_StrLen nStt
,
2203 xub_StrLen nLen
, xub_StrLen nNumberOfBlanks
,
2206 ASSERT( nStt
+ nLen
<= rTxt
.Len(), "String in ThaiJustify too small" )
2208 SwTwips nNumOfTwipsToDistribute
= nSpaceAdd
* nNumberOfBlanks
/
2209 SPACING_PRECISION_FACTOR
;
2214 for ( USHORT nI
= 0; nI
< nLen
; ++nI
)
2216 const xub_Unicode cCh
= rTxt
.GetChar( nStt
+ nI
);
2218 // check if character is not above or below base
2219 if ( ( 0xE34 > cCh
|| cCh
> 0xE3A ) &&
2220 ( 0xE47 > cCh
|| cCh
> 0xE4E ) && cCh
!= 0xE31 )
2222 if ( nNumberOfBlanks
> 0 )
2224 nSpaceAdd
= nNumOfTwipsToDistribute
/ nNumberOfBlanks
;
2226 nNumOfTwipsToDistribute
-= nSpaceAdd
;
2228 nSpaceSum
+= nSpaceAdd
;
2232 if ( pKernArray
) pKernArray
[ nI
] += nSpaceSum
;
2233 if ( pScrArray
) pScrArray
[ nI
] += nSpaceSum
;
2239 /*************************************************************************
2240 * SwScriptInfo::GetScriptInfo()
2241 *************************************************************************/
2243 SwScriptInfo
* SwScriptInfo::GetScriptInfo( const SwTxtNode
& rTNd
,
2244 sal_Bool bAllowInvalid
)
2246 SwClientIter
aClientIter( (SwTxtNode
&)rTNd
);
2247 SwClient
* pLast
= aClientIter
.GoStart();
2248 SwScriptInfo
* pScriptInfo
= 0;
2252 if ( pLast
->ISA( SwTxtFrm
) )
2254 pScriptInfo
= (SwScriptInfo
*)((SwTxtFrm
*)pLast
)->GetScriptInfo();
2257 if ( !bAllowInvalid
&& STRING_LEN
!= pScriptInfo
->GetInvalidity() )
2262 pLast
= ++aClientIter
;
2268 /*************************************************************************
2269 * SwParaPortion::SwParaPortion()
2270 *************************************************************************/
2271 SwParaPortion::SwParaPortion()
2274 bFlys
= bFtnNum
= bMargin
= sal_False
;
2275 SetWhichPor( POR_PARA
);
2278 /*************************************************************************
2279 * SwParaPortion::~SwParaPortion()
2280 *************************************************************************/
2281 SwParaPortion::~SwParaPortion()
2285 /*************************************************************************
2286 * SwParaPortion::GetParLen()
2287 *************************************************************************/
2288 xub_StrLen
SwParaPortion::GetParLen() const
2290 xub_StrLen nLen
= 0;
2291 const SwLineLayout
*pLay
= this;
2295 nLen
= nLen
+ pLay
->GetLen();
2296 pLay
= pLay
->GetNext();
2301 /*************************************************************************
2302 * SwParaPortion::FindDropPortion()
2303 *************************************************************************/
2305 const SwDropPortion
*SwParaPortion::FindDropPortion() const
2307 const SwLineLayout
*pLay
= this;
2308 while( pLay
&& pLay
->IsDummy() )
2309 pLay
= pLay
->GetNext();
2312 const SwLinePortion
*pPos
= pLay
->GetPortion();
2313 while ( pPos
&& !pPos
->GetLen() )
2314 pPos
= pPos
->GetPortion();
2315 if( pPos
&& pPos
->IsDropPortion() )
2316 return (SwDropPortion
*)pPos
;
2317 pLay
= pLay
->GetLen() ? NULL
: pLay
->GetNext();
2322 /*************************************************************************
2323 * SwLineLayout::Init()
2324 *************************************************************************/
2326 void SwLineLayout::Init( SwLinePortion
* pNextPortion
)
2333 SetPortion( pNextPortion
);
2336 /*-----------------16.11.00 11:04-------------------
2338 * looks for hanging punctuation portions in the paragraph
2339 * and return the maximum right offset of them.
2340 * If no such portion is found, the Margin/Hanging-flags will be atualized.
2341 * --------------------------------------------------*/
2343 SwTwips
SwLineLayout::_GetHangingMargin() const
2345 SwLinePortion
* pPor
= GetPortion();
2346 BOOL bFound
= sal_False
;
2350 if( pPor
->IsHangingPortion() )
2352 nDiff
= ((SwHangingPortion
*)pPor
)->GetInnerWidth() - pPor
->Width();
2356 // the last post its portion
2357 else if ( pPor
->IsPostItsPortion() && ! pPor
->GetPortion() )
2360 pPor
= pPor
->GetPortion();
2362 if( !bFound
) // actualize the hanging-flag
2363 ((SwLineLayout
*)this)->SetHanging( sal_False
);
2367 SwTwips
SwTxtFrm::HangingMargin() const
2369 ASSERT( HasPara(), "Don't call me without a paraportion" );
2370 if( !GetPara()->IsMargin() )
2372 const SwLineLayout
* pLine
= GetPara();
2376 SwTwips nDiff
= pLine
->GetHangingMargin();
2379 pLine
= pLine
->GetNext();
2381 if( !nRet
) // actualize the margin-flag
2382 ((SwParaPortion
*)GetPara())->SetMargin( sal_False
);
2387 /*************************************************************************
2388 * SwScriptInfo::CalcHiddenRanges()
2390 * Returns a MultiSection indicating the hidden ranges.
2391 *************************************************************************/
2393 void SwScriptInfo::CalcHiddenRanges( const SwTxtNode
& rNode
, MultiSelection
& rHiddenMulti
)
2395 const SfxPoolItem
* pItem
= 0;
2396 if( SFX_ITEM_SET
== rNode
.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN
, TRUE
, &pItem
) &&
2397 ((SvxCharHiddenItem
*)pItem
)->GetValue() )
2399 rHiddenMulti
.SelectAll();
2402 const SwpHints
* pHints
= rNode
.GetpSwpHints();
2403 const SwTxtAttr
* pTxtAttr
= 0;
2409 while( nTmp
< pHints
->GetStartCount() )
2411 pTxtAttr
= pHints
->GetStart( nTmp
++ );
2412 const SvxCharHiddenItem
* pHiddenItem
= static_cast<const SvxCharHiddenItem
*>( CharFmt::GetItem( *pTxtAttr
, RES_CHRATR_HIDDEN
) );
2415 xub_StrLen nSt
= *pTxtAttr
->GetStart();
2416 xub_StrLen nEnd
= *pTxtAttr
->GetEnd();
2419 Range
aTmp( nSt
, nEnd
- 1 );
2420 rHiddenMulti
.Select( aTmp
, pHiddenItem
->GetValue() );
2426 // If there are any hidden ranges in the current text node, we have
2427 // to unhide the redlining ranges:
2428 const IDocumentRedlineAccess
& rIDRA
= *rNode
.getIDocumentRedlineAccess();
2429 if ( rHiddenMulti
.GetRangeCount() && IDocumentRedlineAccess::IsShowChanges( rIDRA
.GetRedlineMode() ) )
2431 USHORT nAct
= rIDRA
.GetRedlinePos( rNode
, USHRT_MAX
);
2433 for ( ; nAct
< rIDRA
.GetRedlineTbl().Count(); nAct
++ )
2435 const SwRedline
* pRed
= rIDRA
.GetRedlineTbl()[ nAct
];
2437 if ( pRed
->Start()->nNode
> rNode
.GetIndex() )
2440 xub_StrLen nRedlStart
;
2441 xub_StrLen nRedlnEnd
;
2442 pRed
->CalcStartEnd( rNode
.GetIndex(), nRedlStart
, nRedlnEnd
);
2443 if ( nRedlnEnd
> nRedlStart
)
2445 Range
aTmp( nRedlStart
, nRedlnEnd
- 1 );
2446 rHiddenMulti
.Select( aTmp
, false );
2452 // We calculated a lot of stuff. Finally we can update the flags at the text node.
2454 const bool bNewContainsHiddenChars
= rHiddenMulti
.GetRangeCount() > 0;
2455 bool bNewHiddenCharsHidePara
= false;
2456 if ( bNewContainsHiddenChars
)
2458 const Range
& rRange
= rHiddenMulti
.GetRange( 0 );
2459 const xub_StrLen nHiddenStart
= (xub_StrLen
)rRange
.Min();
2460 const xub_StrLen nHiddenEnd
= (xub_StrLen
)rRange
.Max() + 1;
2461 bNewHiddenCharsHidePara
= ( nHiddenStart
== 0 && nHiddenEnd
>= rNode
.GetTxt().Len() );
2463 rNode
.SetHiddenCharAttribute( bNewHiddenCharsHidePara
, bNewContainsHiddenChars
);