update dev300-m58
[ooovba.git] / sw / source / core / text / porlay.cxx
blobda275d1ebd08e5793e4e535f2f4e6c70a014bef3
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
34 #include "txtcfg.hxx"
35 #include "porlay.hxx"
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>
49 #include <paratr.hxx>
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>
65 // <--
67 #include <IDocumentRedlineAccess.hxx>
68 #include <IDocumentSettingAccess.hxx>
69 #include <IDocumentContentOperations.hxx>
71 using namespace ::com::sun::star;
72 using namespace i18n::ScriptType;
74 //#ifdef BIDI
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 )
125 || cCh == 0x6BF );
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 ) ||
166 ( cCh == 0x670 ) ||
167 ( cCh >= 0x6D6 && cCh <= 0x6DC ) ||
168 ( cCh >= 0x6DF && cCh <= 0x6E4 ) ||
169 ( cCh >= 0x6E7 && cCh <= 0x6E8 ) ||
170 ( cCh >= 0x6EA && cCh <= 0x6ED ));
173 /*************************************************************************
174 * lcl_IsLigature
176 * Checks if cCh + cNectCh builds a ligature (used for Kashidas)
177 *************************************************************************/
179 sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh )
181 // Lam + Alef
182 return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
185 /*************************************************************************
186 * lcl_ConnectToPrev
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:
198 // (hennerdrewes):
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 !!!
206 0x64A == cPrevCh ||
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
213 if( bRet )
214 bRet = !lcl_IsLigature( cPrevCh, cCh );
215 return bRet;
218 /*************************************************************************
219 * lcl_HasStrongLTR
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 )
229 return true;
231 return false;
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
243 * fragmentieren).
244 *************************************************************************/
246 SwLineLayout::~SwLineLayout()
248 Truncate();
249 if( GetNext() )
250 delete GetNext();
251 if( pBlink )
252 pBlink->Delete( this );
253 delete pLLSpaceAdd;
254 if ( pKanaComp )
255 delete pKanaComp;
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.
266 if( !pPortion )
268 if( GetLen() )
270 pPortion = new SwTxtPortion( *(SwLinePortion*)this );
271 if( IsBlinking() && pBlink )
273 SetBlinking( sal_False );
274 pBlink->Replace( this, pPortion );
277 else
279 SetPortion( pIns );
280 return pIns;
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.
295 if( !pPortion )
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 )
309 if( GetLen() )
310 return SwTxtPortion::Format( rInf );
311 else
313 Height( rInf.GetTxtHeight() );
314 return sal_True;
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;
328 if( !GetPortion() )
329 SetPortion( new SwTxtPortion( *(SwLinePortion*)this ) );
330 if( !pLeft )
332 pLeft = new SwMarginPortion( 0 );
333 pLeft->SetPortion( GetPortion() );
334 SetPortion( pLeft );
336 else
338 pLeft->Height( 0 );
339 pLeft->Width( 0 );
340 pLeft->SetLen( 0 );
341 pLeft->SetAscent( 0 );
342 pLeft->SetPortion( NULL );
343 pLeft->SetFixWidth(0);
346 SwLinePortion *pPos = pLeft->GetPortion();
347 while( pPos )
349 DBG_LOOP;
350 if( pPos->IsFlyPortion() )
352 // Die FlyPortion wird ausgesogen ...
353 pLeft->Join( (SwGluePortion*)pPos );
354 pPos = pLeft->GetPortion();
355 if( GetpKanaComp() )
356 GetKanaComp().Remove( 0, 1 );
358 else
359 pPos = 0;
361 return pLeft;
364 /*************************************************************************
365 * SwLineLayout::InitSpaceAdd()
366 *************************************************************************/
368 void SwLineLayout::InitSpaceAdd()
370 if ( !pLLSpaceAdd )
371 CreateSpaceAdd();
372 else
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
388 * in [nStt, nEnd[
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 )
399 bBlankOnly = false;
400 break;
403 return bBlankOnly;
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;
424 if( bTmpDummy )
426 nFlyAscent = 0;
427 nFlyHeight = 0;
428 nFlyDescent = 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;
437 // <--
439 if( pPortion )
441 SetCntnt( sal_False );
442 if( pPortion->IsBreakPortion() )
444 SetLen( pPortion->GetLen() );
445 if( GetLen() )
446 bTmpDummy = sal_False;
448 else
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
457 // Fix-Portion.
458 while( pPos )
460 DBG_LOOP;
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() )
472 if( !Height() )
473 Height( pPos->Height() );
474 if( !GetAscent() )
475 SetAscent( pPos->GetAscent() );
477 delete pLast->Cut( pPos );
478 pPos = pLast->GetPortion();
479 continue;
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() ) ) )
493 pLast = pPos;
494 pPos = pPos->GetPortion();
495 bHasBlankPortion = true;
496 continue;
499 // <--
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;
535 else
537 if( pPos->InNumberGrp() )
539 KSHORT nTmp = rInf.GetFont()->GetAscent(
540 rInf.GetVsh(), *rInf.GetOut() );
541 if( nTmp > nPosAscent )
543 nPosHeight += nTmp - nPosAscent;
544 nPosAscent = nTmp;
546 nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
547 *rInf.GetOut() );
548 if( nTmp > nPosHeight )
549 nPosHeight = nTmp;
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;
570 else
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() )
586 XubString aTxt;
587 if( pPos->GetExpTxt( rInf, aTxt ) && aTxt.Len() )
588 SetCntnt( sal_True );
590 else if( ( pPos->InTxtGrp() || pPos->IsMultiPortion() ) &&
591 pPos->GetLen() )
592 SetCntnt( sal_True );
595 bTmpDummy = bTmpDummy && !HasCntnt() &&
596 ( !pPos->Width() || pPos->IsFlyPortion() );
598 pLast = pPos;
599 pPos = pPos->GetPortion();
602 if( pFlyCnt )
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 );
623 else
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() );
636 else
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;
646 // <--
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 );
658 // <--
660 // Robust:
661 if( nLineWidth < Width() )
662 Width( nLineWidth );
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,
672 SwTwips& _orDescent,
673 SwTwips& _orObjAscent,
674 SwTwips& _orObjDescent,
675 const SwLinePortion* _pDontConsiderPortion,
676 const bool _bNoFlyCntPorAndLinePor ) const
678 _orAscent = 0;
679 _orDescent = 0;
680 _orObjAscent = 0;
681 _orObjDescent = 0;
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()) -
698 nPortionAsc;
700 const sal_Bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
701 static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
702 !( pTmpPortion == _pDontConsiderPortion );
704 if ( bFlyCmp )
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 /*************************************************************************
721 * class SwCharRange
722 *************************************************************************/
724 SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange)
726 if(0 != rRange.nLen ) {
727 if(0 == nLen) {
728 nStart = rRange.nStart;
729 nLen = rRange.nLen ;
731 else {
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;
741 return *this;
744 /*************************************************************************
745 * SwScriptInfo::SwScriptInfo()
746 *************************************************************************/
747 SwScriptInfo::SwScriptInfo() :
748 nInvalidityPos( 0 ),
749 nDefaultDir( 0 )
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?" );
769 USHORT nScript;
771 // First we try to use our SwScriptInfo
772 if ( pSI )
773 nScript = pSI->ScriptType( nIdx );
774 else
775 // Ok, we have to ask the break iterator
776 nScript = pBreakIt->GetRealScriptOfText( *pTxt, nIdx );
778 switch ( nScript ) {
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!" );
785 return SW_LATIN;
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() )
802 return;
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;
815 USHORT i = 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
839 USHORT nCnt = 0;
840 // counter for compression information arrays
841 USHORT nCntComp = 0;
842 // counter for kashida array
843 USHORT nCntKash = 0;
845 BYTE nScript = i18n::ScriptType::LATIN;
847 // compression type
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:
858 if( nChg )
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 ) )
867 nCnt++;
868 else
870 nScript = GetScriptType( nCnt );
871 break;
874 if( CHARCOMPRESS_NONE != aCompEnum )
876 while( nCntComp < CountCompChg() )
878 if ( nChg > GetCompStart( nCntComp ) )
879 nCntComp++;
880 else
881 break;
884 if ( bAdjustBlock )
886 while( nCntKash < CountKashida() )
888 if ( nChg > GetKashida( nCntKash ) )
889 nCntKash++;
890 else
891 break;
897 // ADJUST nChg VALUE:
900 // by stepping back one position we know that we are inside a group
901 // declared as an nScript group
902 if ( nChg )
903 --nChg;
905 const xub_StrLen nGrpStart = nCnt ? GetScriptChg( nCnt - 1 ) : 0;
907 // we go back in our group until we reach the first character of
908 // type nScript
909 while ( nChg > nGrpStart &&
910 nScript != pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
911 --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;
929 if( nCntComp )
931 --nCntComp;
932 nLastCompression = GetCompStart( nCntComp );
933 if( nChg >= nLastCompression + GetCompLen( nCntComp ) )
935 nLastCompression = nChg;
936 ++nCntComp;
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 )
950 --nCntKash;
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.
968 xub_StrLen nEnd =
969 (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nChg, WEAK );
971 if( nEnd > rTxt.Len() )
972 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" );
980 nChg = nEnd;
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 ) :
985 (BYTE)WEAK;
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() )
1009 nChg = rTxt.Len();
1011 // --> FME 2008-09-17 #i28203#
1012 // for 'complex' portions, we make sure that a portion does not contain more
1013 // than one script:
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 );
1024 else
1025 break;
1027 nChg = Min( nChg, nNextCTLScriptStart );
1029 // <--
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 );
1042 else
1044 aScriptChg.Insert( nChg, nCnt );
1047 else
1049 aScriptChg.Insert( nChg, nCnt );
1051 aScriptType.Insert( nScript, nCnt++ );
1053 // if current script is asian, we search for compressable characters
1054 // in this range
1055 if ( CHARCOMPRESS_NONE != aCompEnum &&
1056 i18n::ScriptType::ASIAN == nScript )
1058 BYTE ePrevState = NONE;
1059 BYTE eState;
1060 USHORT nPrevChg = nLastCompression;
1062 while ( nLastCompression < nChg )
1064 xub_Unicode cChar = rTxt.GetChar( nLastCompression );
1066 // examine current character
1067 switch ( cChar )
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;
1074 break;
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:
1079 case 0x301F:
1080 eState = SPECIAL_RIGHT;
1081 break;
1082 default:
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;
1106 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;
1138 xub_Unicode cCh;
1139 xub_Unicode cPrevCh = 0;
1141 USHORT nPriorityLevel = 7; // 0..6 = level found
1142 // 7 not found
1144 xub_StrLen nWordLen = rWord.Len();
1146 // ignore trailing vowel chars
1147 while( nWordLen && isTransparentChar( rWord.GetChar( nWordLen - 1 )))
1148 --nWordLen;
1150 while (nIdx < nWordLen)
1152 cCh = rWord.GetChar( nIdx );
1154 // 1. Priority:
1155 // after user inserted kashida
1156 if ( 0x640 == cCh )
1158 nKashidaPos = aScanner.GetBegin() + nIdx;
1159 nPriorityLevel = 0;
1162 // 2. Priority:
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;
1170 nPriorityLevel = 1;
1174 // 3. Priority:
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;
1188 nPriorityLevel = 2;
1193 // 4. Priority:
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)
1200 isGafChar ( cCh ) )
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;
1208 nPriorityLevel = 3;
1213 // 5. Priority:
1214 // before media Bah
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;
1228 nPriorityLevel = 4;
1234 // 6. Priority:
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;
1250 nPriorityLevel = 5;
1255 // other connecting possibilities
1256 if ( nPriorityLevel >= 6 && nIdx > 0 )
1258 // remaining right joiners
1259 // Reh, Zain, Thal,
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;
1270 nPriorityLevel = 6;
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) )
1279 cPrevCh = cCh;
1281 ++nIdx;
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;
1296 #ifndef PRODUCT
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;
1306 break;
1308 nTmpKashidaPos = nCurrKashidaPos;
1310 ASSERT( ! bWrongKash, "Kashida array contains wrong data" )
1311 #endif
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;
1320 nCnt = 0;
1321 while( !bPerformUBA && nCnt < CountScriptChg() )
1323 if ( i18n::ScriptType::COMPLEX == GetScriptType( nCnt++ ) )
1324 bPerformUBA = true;
1327 // do not call the unicode bidi algorithm if not required
1328 if ( bPerformUBA )
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 )
1355 ++nScriptIdx;
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 );
1370 ++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
1385 BYTE nScriptType;
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" )
1400 #endif
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 );
1424 int32_t nStart = 0;
1425 int32_t nEnd;
1426 UBiDiLevel nCurrDir;
1427 // counter for direction information arrays
1428 USHORT nCntDir = 0;
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++ );
1435 nStart = nEnd;
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
1447 * STRING_LEN.
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 );
1462 return STRING_LEN;
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 );
1495 return STRING_LEN;
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 );
1507 return 0;
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" )
1521 PositionList aList;
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 )
1534 continue;
1536 while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
1538 if ( nHiddenStart >= nStt && nHiddenStart < nEnd )
1540 rText.SetChar( nHiddenStart, cChar );
1541 ++nNumOfHiddenChars;
1543 ++nHiddenStart;
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 )
1557 PositionList aList;
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(..)
1575 * static version
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;
1583 rnEndPos = 0;
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 )
1595 return false;
1597 if ( bWholePara )
1599 if ( pList )
1601 pList->push_back( 0 );
1602 pList->push_back( rNode.GetTxt().Len() );
1605 rnStartPos = 0;
1606 rnEndPos = rNode.GetTxt().Len();
1607 return true;
1611 const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
1612 if ( pSI )
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 );
1621 else
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 )
1636 break;
1637 else if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1639 rnStartPos = nHiddenStart;
1640 rnEndPos = Min( nHiddenEnd, rNode.GetTxt().Len() );
1641 break;
1645 if ( pList )
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;
1670 rnEndPos = 0;
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 )
1679 break;
1680 else if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1682 rnStartPos = nHiddenStart;
1683 rnEndPos = nHiddenEnd;
1684 break;
1688 if ( pList )
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;
1707 xub_StrLen nEndPos;
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 );
1726 if ( nPos < nChg )
1727 return NONE;
1729 if( nPos < nChg + GetCompLen( nX ) )
1730 return GetCompType( nX );
1732 return NONE;
1734 #endif
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 )
1753 return USHRT_MAX;
1755 if ( nStart < nKanaEnd )
1756 return nX;
1759 return USHRT_MAX;
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 )
1782 return 0;
1784 xub_StrLen nChg = GetCompStart( nCompIdx );
1785 xub_StrLen nCompLen = GetCompLen( nCompIdx );
1786 USHORT nI = 0;
1787 nLen = nLen + nIdx;
1789 if( nChg > nIdx )
1791 nI = nChg - nIdx;
1792 nIdx = nChg;
1794 else if( nIdx < nChg + nCompLen )
1795 nCompLen -= nIdx - nChg;
1797 if( nIdx > nLen || nCompIdx >= nCompCount )
1798 return 0;
1800 long nSub = 0;
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!" );
1807 #endif
1808 nCompLen = nCompLen + nIdx;
1809 if( nCompLen > nLen )
1810 nCompLen = nLen;
1812 // are we allowed to compress the character?
1813 if ( pKernArray[ nI ] - nLast < nMinWidth )
1815 nIdx++; nI++;
1817 else
1819 while( nIdx < nCompLen )
1821 ASSERT( SwScriptInfo::NONE != nType, "None compression?!" );
1823 // nLast is width of current character
1824 nLast -= pKernArray[ nI ];
1826 nLast *= nCompress;
1827 long nMove = 0;
1828 if( SwScriptInfo::KANA != nType )
1830 nLast /= 20000;
1831 if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType )
1833 if( nI )
1834 nMove = nLast;
1835 else
1837 pPoint->X() += nLast;
1838 nLast = 0;
1842 else
1843 nLast /= 100000;
1844 nSub -= nLast;
1845 nLast = pKernArray[ nI ];
1846 if( nMove )
1847 pKernArray[ nI - 1 ] += nMove;
1848 pKernArray[ nI++ ] -= nSub;
1849 ++nIdx;
1853 if( nIdx < nLen )
1855 xub_StrLen nTmpChg;
1856 if( ++nCompIdx < nCompCount )
1858 nTmpChg = GetCompStart( nCompIdx );
1859 if( nTmpChg > nLen )
1860 nTmpChg = nLen;
1861 nCompLen = GetCompLen( nCompIdx );
1863 else
1864 nTmpChg = nLen;
1865 while( nIdx < nTmpChg )
1867 nLast = pKernArray[ nI ];
1868 pKernArray[ nI++ ] -= nSub;
1869 ++nIdx;
1872 else
1873 break;
1874 } while( nIdx < nLen );
1875 return nSub;
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,
1889 xub_StrLen nStt,
1890 xub_StrLen nLen,
1891 long nSpaceAdd ) const
1893 ASSERT( nLen, "Kashida justification without text?!" )
1895 if( !IsKashidaLine(nStt))
1896 return STRING_LEN;
1898 // evaluate kashida informatin in collected in SwScriptInfo
1900 USHORT nCntKash = 0;
1901 while( nCntKash < CountKashida() )
1903 if ( nStt <= GetKashida( nCntKash ) )
1904 break;
1905 else
1906 nCntKash++;
1909 const xub_StrLen nEnd = nStt + nLen;
1911 USHORT nCntKashEnd = nCntKash;
1912 while ( nCntKashEnd < CountKashida() )
1914 if ( nEnd <= GetKashida( nCntKashEnd ) )
1915 break;
1916 else
1917 nCntKashEnd++;
1920 USHORT nActualKashCount = nCntKashEnd - nCntKash;
1921 for ( USHORT i = nCntKash; i < nCntKashEnd; ++i )
1923 if ( nActualKashCount && !IsKashidaValid ( i ) )
1924 --nActualKashCount;
1927 if ( !pKernArray )
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 )
1935 ++nCntKash;
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
1946 ++nCntKash;
1947 while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
1948 ++nCntKash;
1950 nIdx = nCntKash < CountKashida() && IsKashidaValid ( nCntKash ) ? GetKashida( nCntKash ) : nEnd;
1951 if ( nIdx > nEnd )
1952 nIdx = nEnd;
1954 const USHORT nArrayEnd = nIdx - nStt;
1956 while ( nArrayPos < nArrayEnd )
1958 pKernArray[ nArrayPos ] += nKashAdd;
1959 if ( pScrArray )
1960 pScrArray[ nArrayPos ] += nKashAdd;
1961 ++nArrayPos;
1963 nKashAdd += nSpaceAdd;
1967 return 0;
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 ) )
1991 ++nIdx;
1994 if( nIdx == nEnd )
1996 // no regular character found in this portion. Go backward:
1997 --nIdx;
1998 while ( nIdx >= 0 && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) )
2000 --nIdx;
2004 if( nIdx >= 0 )
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;
2010 return sal_False;
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 )
2022 return false;
2024 return true;
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);
2038 return;
2043 /*************************************************************************
2044 * SwScriptInfo::MarkOrClearKashidaInvalid()
2045 *************************************************************************/
2046 // bMark == true:
2047 // marks the first valid kashida in the given text range as invalid
2049 // bMark == false:
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 ) )
2058 break;
2059 else
2060 nCntKash++;
2063 const xub_StrLen nEnd = nStt + nLen;
2065 while ( nCntKash < CountKashida() )
2067 if ( nEnd <= GetKashida( nCntKash ) )
2068 break;
2069 else
2071 if(bMark)
2073 if ( IsKashidaValid ( nCntKash ) )
2075 MarkKashidaInvalid ( nCntKash );
2076 --nMarkCount;
2077 if(!nMarkCount)
2078 return true;
2081 else
2083 ClearKashidaInvalid ( nCntKash );
2085 nCntKash++;
2088 return false;
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 ) )
2107 break;
2108 else
2109 nCntKash++;
2112 const xub_StrLen nEnd = nStt + nLen;
2114 USHORT nCntKashEnd = nCntKash;
2115 while ( nCntKashEnd < CountKashida() )
2117 if ( nEnd <= GetKashida( nCntKashEnd ) )
2118 break;
2119 else
2121 pKashidaPosition [ nCntKashEnd - nCntKash ] = GetKashida ( nCntKashEnd );
2122 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 ])
2144 return false;
2146 return true;
2148 /*************************************************************************
2149 * SwScriptInfo::ClearKashidaLine()
2150 *************************************************************************/
2152 void SwScriptInfo::ClearNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen )
2154 xub_StrLen i = 0;
2155 while( i < aNoKashidaLine.Count())
2157 if( nStt + nLen >= aNoKashidaLine[ i ] && nStt < aNoKashidaLineEnd [ i ] )
2159 aNoKashidaLine.Remove(i, 1);
2160 aNoKashidaLineEnd.Remove(i, 1);
2162 else
2163 ++i;
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 ) )
2182 nCntKash++;
2183 continue;
2186 if ( pKashidaPositions [nKashidaPosIdx] == GetKashida( nCntKash ) && IsKashidaValid ( nCntKash ) )
2188 MarkKashidaInvalid ( nCntKash );
2190 else
2191 return false; // something is wrong
2192 nKashidaPosIdx++;
2194 return true;
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,
2204 long nSpaceAdd )
2206 ASSERT( nStt + nLen <= rTxt.Len(), "String in ThaiJustify too small" )
2208 SwTwips nNumOfTwipsToDistribute = nSpaceAdd * nNumberOfBlanks /
2209 SPACING_PRECISION_FACTOR;
2211 long nSpaceSum = 0;
2212 USHORT nCnt = 0;
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;
2225 --nNumberOfBlanks;
2226 nNumOfTwipsToDistribute -= nSpaceAdd;
2228 nSpaceSum += nSpaceAdd;
2229 ++nCnt;
2232 if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
2233 if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
2236 return nCnt;
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;
2250 while( pLast )
2252 if ( pLast->ISA( SwTxtFrm ) )
2254 pScriptInfo = (SwScriptInfo*)((SwTxtFrm*)pLast)->GetScriptInfo();
2255 if ( pScriptInfo )
2257 if ( !bAllowInvalid && STRING_LEN != pScriptInfo->GetInvalidity() )
2258 pScriptInfo = 0;
2259 else break;
2262 pLast = ++aClientIter;
2265 return pScriptInfo;
2268 /*************************************************************************
2269 * SwParaPortion::SwParaPortion()
2270 *************************************************************************/
2271 SwParaPortion::SwParaPortion()
2273 FormatReset();
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;
2292 while( pLay )
2294 DBG_LOOP;
2295 nLen = nLen + pLay->GetLen();
2296 pLay = pLay->GetNext();
2298 return nLen;
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();
2310 while( pLay )
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();
2319 return NULL;
2322 /*************************************************************************
2323 * SwLineLayout::Init()
2324 *************************************************************************/
2326 void SwLineLayout::Init( SwLinePortion* pNextPortion )
2328 Height( 0 );
2329 Width( 0 );
2330 SetLen( 0 );
2331 SetAscent( 0 );
2332 SetRealHeight( 0 );
2333 SetPortion( pNextPortion );
2336 /*-----------------16.11.00 11:04-------------------
2337 * HangingMargin()
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;
2347 SwTwips nDiff = 0;
2348 while( pPor)
2350 if( pPor->IsHangingPortion() )
2352 nDiff = ((SwHangingPortion*)pPor)->GetInnerWidth() - pPor->Width();
2353 if( nDiff )
2354 bFound = sal_True;
2356 // the last post its portion
2357 else if ( pPor->IsPostItsPortion() && ! pPor->GetPortion() )
2358 nDiff = nAscent;
2360 pPor = pPor->GetPortion();
2362 if( !bFound ) // actualize the hanging-flag
2363 ((SwLineLayout*)this)->SetHanging( sal_False );
2364 return nDiff;
2367 SwTwips SwTxtFrm::HangingMargin() const
2369 ASSERT( HasPara(), "Don't call me without a paraportion" );
2370 if( !GetPara()->IsMargin() )
2371 return 0;
2372 const SwLineLayout* pLine = GetPara();
2373 SwTwips nRet = 0;
2376 SwTwips nDiff = pLine->GetHangingMargin();
2377 if( nDiff > nRet )
2378 nRet = nDiff;
2379 pLine = pLine->GetNext();
2380 } while ( pLine );
2381 if( !nRet ) // actualize the margin-flag
2382 ((SwParaPortion*)GetPara())->SetMargin( sal_False );
2383 return nRet;
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;
2405 if( pHints )
2407 MSHORT nTmp = 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 ) );
2413 if( pHiddenItem )
2415 xub_StrLen nSt = *pTxtAttr->GetStart();
2416 xub_StrLen nEnd = *pTxtAttr->GetEnd();
2417 if( nEnd > nSt )
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() )
2438 break;
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 );