summaryrefslogtreecommitdiff
path: root/sw/source/core/text/porlay.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text/porlay.cxx')
-rw-r--r--sw/source/core/text/porlay.cxx2461
1 files changed, 2461 insertions, 0 deletions
diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx
new file mode 100644
index 000000000000..057d0a2a668d
--- /dev/null
+++ b/sw/source/core/text/porlay.cxx
@@ -0,0 +1,2461 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sw.hxx"
+
+
+#include "errhdl.hxx" // ASSERT
+
+#include "txtcfg.hxx"
+#include "porlay.hxx"
+#include "itrform2.hxx"
+#include "porglue.hxx"
+#include "porexp.hxx" // SwQuoVadisPortion
+#include "blink.hxx" // pBlink
+#include "redlnitr.hxx" // SwRedlineItr
+#include "porfly.hxx" // SwFlyCntPortion
+#include <porrst.hxx> // SwHangingPortion
+#include <pormulti.hxx> // SwMultiPortion
+#include <breakit.hxx>
+#include <unicode/uchar.h>
+#include <com/sun/star/i18n/ScriptType.hdl>
+#include <com/sun/star/i18n/CTLScriptType.hdl>
+#include <com/sun/star/i18n/WordType.hdl>
+#include <paratr.hxx>
+#include <editeng/adjitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <vcl/outdev.hxx>
+#include <editeng/blnkitem.hxx>
+#include <tools/multisel.hxx>
+#include <unotools/charclass.hxx>
+#include <i18npool/mslangid.hxx>
+#include <charfmt.hxx>
+#include <fchrfmt.hxx>
+#include <docary.hxx> // SwRedlineTbl
+#include <redline.hxx> // SwRedline
+
+// --> FME 2004-06-08 #i12836# enhanced pdf export
+#include <section.hxx>
+// <--
+
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentContentOperations.hxx>
+
+using namespace ::com::sun::star;
+using namespace i18n::ScriptType;
+
+#include <unicode/ubidi.h>
+#include <i18nutil/unicode.hxx> //unicode::getUnicodeScriptType
+
+sal_Bool isAlefChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x622 || cCh == 0x623 || cCh == 0x625 || cCh == 0x627 ||
+ cCh == 0x622 || cCh == 0x671 || cCh == 0x672 || cCh == 0x673 || cCh == 0x675 );
+}
+
+sal_Bool isWawChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x624 || cCh == 0x648 || cCh == 0x676 || cCh == 0x677 ||
+ ( cCh >= 0x6C4 && cCh <= 0x6CB ) || cCh == 0x6CF );
+}
+
+sal_Bool isDalChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x62F || cCh == 0x630 || cCh == 0x688 || cCh == 0x689 || cCh == 0x690 );
+}
+
+sal_Bool isRehChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x631 || cCh == 0x632 || ( cCh >= 0x691 && cCh <= 0x699 ));
+}
+
+sal_Bool isTehMarbutaChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x629 || cCh == 0x6C0 );
+}
+
+sal_Bool isBaaChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x628 || cCh == 0x62A || cCh == 0x62B || cCh == 0x679 || cCh == 0x680 );
+}
+
+sal_Bool isYehChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x626 || cCh == 0x649 || cCh == 0x64A || cCh == 0x678 || cCh == 0x6CC ||
+ cCh == 0x6CE || cCh == 0x6D0 || cCh == 0x6D1 );
+}
+
+sal_Bool isSeenOrSadChar ( xub_Unicode cCh )
+{
+ return ( ( cCh >= 0x633 && cCh <= 0x636 ) || ( cCh >= 0x69A && cCh <= 0x69E )
+ || cCh == 0x6FA || cCh == 0x6FB );
+}
+
+sal_Bool isHahChar ( xub_Unicode cCh )
+{
+ return ( ( cCh >= 0x62C && cCh <= 0x62E ) || ( cCh >= 0x681 && cCh <= 0x687 )
+ || cCh == 0x6BF );
+}
+
+sal_Bool isAinChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x639 || cCh == 0x63A || cCh == 0x6A0 || cCh == 0x6FC );
+}
+
+sal_Bool isKafChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x643 || ( cCh >= 0x6AC && cCh <= 0x6AE ) );
+}
+
+sal_Bool isLamChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x644 || ( cCh >= 0x6B5 && cCh <= 0x6B8 ) );
+}
+
+sal_Bool isGafChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x6A9 || cCh == 0x6AB ||( cCh >= 0x6AF && cCh <= 0x6B4 ) );
+}
+
+sal_Bool isQafChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x642 || cCh == 0x6A7 || cCh == 0x6A8 );
+}
+
+sal_Bool isFeChar ( xub_Unicode cCh )
+{
+ return ( cCh == 0x641 || ( cCh >= 0x6A1 && cCh <= 0x6A6 ) );
+}
+sal_Bool isTransparentChar ( xub_Unicode cCh )
+{
+ return ( ( cCh >= 0x610 && cCh <= 0x61A ) ||
+ ( cCh >= 0x64B && cCh <= 0x65E ) ||
+ ( cCh == 0x670 ) ||
+ ( cCh >= 0x6D6 && cCh <= 0x6DC ) ||
+ ( cCh >= 0x6DF && cCh <= 0x6E4 ) ||
+ ( cCh >= 0x6E7 && cCh <= 0x6E8 ) ||
+ ( cCh >= 0x6EA && cCh <= 0x6ED ));
+}
+
+/*************************************************************************
+ * lcl_IsLigature
+ *
+ * Checks if cCh + cNectCh builds a ligature (used for Kashidas)
+ *************************************************************************/
+
+sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh )
+{
+ // Lam + Alef
+ return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
+}
+
+/*************************************************************************
+ * lcl_ConnectToPrev
+ *
+ * Checks if cCh is connectable to cPrevCh (used for Kashidas)
+ *************************************************************************/
+
+sal_Bool lcl_ConnectToPrev( xub_Unicode cCh, xub_Unicode cPrevCh )
+{
+ // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
+ // Uh, there seem to be some more characters that are not connectable
+ // to the left. So we look for the characters that are actually connectable
+ // to the left. Here is the complete list of WH:
+
+ // (hennerdrewes):
+ // added lam forms 0x06B5..0x06B8
+ // added 0x6FA..0x6FC, according to unicode documentation, although not present in my fonts
+ // added heh goal 0x6C1
+ sal_Bool bRet = 0x628 == cPrevCh ||
+ ( 0x62A <= cPrevCh && cPrevCh <= 0x62E ) ||
+ ( 0x633 <= cPrevCh && cPrevCh <= 0x647 ) ||
+ 0x649 == cPrevCh || // Alef Maksura does connect !!!
+ 0x64A == cPrevCh ||
+ ( 0x678 <= cPrevCh && cPrevCh <= 0x687 ) ||
+ ( 0x69A <= cPrevCh && cPrevCh <= 0x6C1 ) ||
+ ( 0x6C3 <= cPrevCh && cPrevCh <= 0x6D3 ) ||
+ ( 0x6FA <= cPrevCh && cPrevCh <= 0x6FC ) ;
+
+ // check for ligatures cPrevChar + cChar
+ if( bRet )
+ bRet = !lcl_IsLigature( cPrevCh, cCh );
+ return bRet;
+}
+
+/*************************************************************************
+ * lcl_HasStrongLTR
+ *************************************************************************/
+ bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd )
+ {
+ for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
+ {
+ const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx ));
+ if ( nCharDir == U_LEFT_TO_RIGHT ||
+ nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
+ nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
+ return true;
+ }
+ return false;
+ }
+
+/*************************************************************************
+ * SwLineLayout::~SwLineLayout()
+ *
+ * class SwLineLayout: Das Layout einer einzelnen Zeile. Dazu
+ * gehoeren vor allen Dingen die Dimension, die Anzahl der
+ * Character und der Wortzwischenraeume in der Zeile.
+ * Zeilenobjekte werden in einem eigenen Pool verwaltet, um zu
+ * erreichen, dass sie im Speicher moeglichst beeinander liegen
+ * (d.h. zusammen gepaged werden und den Speicher nicht
+ * fragmentieren).
+ *************************************************************************/
+
+SwLineLayout::~SwLineLayout()
+{
+ Truncate();
+ if( GetNext() )
+ delete GetNext();
+ if( pBlink )
+ pBlink->Delete( this );
+ delete pLLSpaceAdd;
+ if ( pKanaComp )
+ delete pKanaComp;
+}
+
+/*************************************************************************
+ * virtual SwLineLayout::Insert()
+ *************************************************************************/
+
+SwLinePortion *SwLineLayout::Insert( SwLinePortion *pIns )
+{
+ // Erster Attributwechsel, Masse und Laengen
+ // aus *pCurr in die erste Textportion kopieren.
+ if( !pPortion )
+ {
+ if( GetLen() )
+ {
+ pPortion = new SwTxtPortion( *(SwLinePortion*)this );
+ if( IsBlinking() && pBlink )
+ {
+ SetBlinking( sal_False );
+ pBlink->Replace( this, pPortion );
+ }
+ }
+ else
+ {
+ SetPortion( pIns );
+ return pIns;
+ }
+ }
+ // mit Skope aufrufen, sonst Rekursion !
+ return pPortion->SwLinePortion::Insert( pIns );
+}
+
+/*************************************************************************
+ * virtual SwLineLayout::Append()
+ *************************************************************************/
+
+SwLinePortion *SwLineLayout::Append( SwLinePortion *pIns )
+{
+ // Erster Attributwechsel, Masse und Laengen
+ // aus *pCurr in die erste Textportion kopieren.
+ if( !pPortion )
+ pPortion = new SwTxtPortion( *(SwLinePortion*)this );
+ // mit Skope aufrufen, sonst Rekursion !
+ return pPortion->SwLinePortion::Append( pIns );
+}
+
+/*************************************************************************
+ * virtual SwLineLayout::Format()
+ *************************************************************************/
+
+// fuer die Sonderbehandlung bei leeren Zeilen
+
+sal_Bool SwLineLayout::Format( SwTxtFormatInfo &rInf )
+{
+ if( GetLen() )
+ return SwTxtPortion::Format( rInf );
+ else
+ {
+ Height( rInf.GetTxtHeight() );
+ return sal_True;
+ }
+}
+
+/*************************************************************************
+ * SwLineLayout::CalcLeftMargin()
+ *
+ * Wir sammeln alle FlyPortions am Anfang der Zeile zu einer MarginPortion.
+ *************************************************************************/
+
+SwMarginPortion *SwLineLayout::CalcLeftMargin()
+{
+ SwMarginPortion *pLeft = (GetPortion() && GetPortion()->IsMarginPortion()) ?
+ (SwMarginPortion *)GetPortion() : 0;
+ if( !GetPortion() )
+ SetPortion( new SwTxtPortion( *(SwLinePortion*)this ) );
+ if( !pLeft )
+ {
+ pLeft = new SwMarginPortion( 0 );
+ pLeft->SetPortion( GetPortion() );
+ SetPortion( pLeft );
+ }
+ else
+ {
+ pLeft->Height( 0 );
+ pLeft->Width( 0 );
+ pLeft->SetLen( 0 );
+ pLeft->SetAscent( 0 );
+ pLeft->SetPortion( NULL );
+ pLeft->SetFixWidth(0);
+ }
+
+ SwLinePortion *pPos = pLeft->GetPortion();
+ while( pPos )
+ {
+ DBG_LOOP;
+ if( pPos->IsFlyPortion() )
+ {
+ // Die FlyPortion wird ausgesogen ...
+ pLeft->Join( (SwGluePortion*)pPos );
+ pPos = pLeft->GetPortion();
+ if( GetpKanaComp() )
+ GetKanaComp().Remove( 0, 1 );
+ }
+ else
+ pPos = 0;
+ }
+ return pLeft;
+}
+
+/*************************************************************************
+ * SwLineLayout::InitSpaceAdd()
+ *************************************************************************/
+
+void SwLineLayout::InitSpaceAdd()
+{
+ if ( !pLLSpaceAdd )
+ CreateSpaceAdd();
+ else
+ SetLLSpaceAdd( 0, 0 );
+}
+
+/*************************************************************************
+ * SwLineLayout::CreateSpaceAdd()
+ *************************************************************************/
+
+void SwLineLayout::CreateSpaceAdd( const long nInit )
+{
+ pLLSpaceAdd = new std::vector<long>;
+ SetLLSpaceAdd( nInit, 0 );
+}
+
+/*************************************************************************
+ * Local helper function. Returns true if there are only blanks
+ * in [nStt, nEnd[
+ *************************************************************************/
+
+bool lcl_HasOnlyBlanks( const XubString& rTxt, xub_StrLen nStt, xub_StrLen nEnd )
+{
+ bool bBlankOnly = true;
+ while ( nStt < nEnd )
+ {
+ const xub_Unicode cChar = rTxt.GetChar( nStt++ );
+ if ( ' ' != cChar && 0x3000 != cChar )
+ {
+ bBlankOnly = false;
+ break;
+ }
+ }
+ return bBlankOnly;
+}
+
+/*************************************************************************
+ * SwLineLayout::CalcLine()
+ *
+ * Aus FormatLine() ausgelagert.
+ *************************************************************************/
+
+void SwLineLayout::CalcLine( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf )
+{
+ const KSHORT nLineWidth = rInf.RealWidth();
+
+ KSHORT nFlyAscent = 0;
+ KSHORT nFlyHeight = 0;
+ KSHORT nFlyDescent = 0;
+ sal_Bool bOnlyPostIts = sal_True;
+ SetHanging( sal_False );
+
+ sal_Bool bTmpDummy = ( 0 == GetLen() );
+ SwFlyCntPortion* pFlyCnt = 0;
+ if( bTmpDummy )
+ {
+ nFlyAscent = 0;
+ nFlyHeight = 0;
+ nFlyDescent = 0;
+ }
+
+ // --> FME 2006-03-01 #i3952#
+ const bool bIgnoreBlanksAndTabsForLineHeightCalculation =
+ rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION);
+
+ bool bHasBlankPortion = false;
+ bool bHasOnlyBlankPortions = true;
+ // <--
+
+ if( pPortion )
+ {
+ SetCntnt( sal_False );
+ if( pPortion->IsBreakPortion() )
+ {
+ SetLen( pPortion->GetLen() );
+ if( GetLen() )
+ bTmpDummy = sal_False;
+ }
+ else
+ {
+ Init( GetPortion() );
+ SwLinePortion *pPos = pPortion;
+ SwLinePortion *pLast = this;
+ KSHORT nMaxDescent = 0;
+
+ // Eine Gruppe ist ein Abschnitt in der Portion-Kette von
+ // pCurr oder einer Fix-Portion bis zum Ende bzw. zur naechsten
+ // Fix-Portion.
+ while( pPos )
+ {
+ DBG_LOOP;
+ OSL_ENSURE( POR_LIN != pPos->GetWhichPor(),
+ "SwLineLayout::CalcLine: don't use SwLinePortions !" );
+
+ // Null-Portions werden eliminiert. Sie koennen entstehen,
+ // wenn zwei FlyFrms ueberlappen.
+ if( !pPos->Compress() )
+ {
+ // 8110: Hoehe und Ascent nur uebernehmen, wenn sonst in der
+ // Zeile nichts mehr los ist.
+ if( !pPos->GetPortion() )
+ {
+ if( !Height() )
+ Height( pPos->Height() );
+ if( !GetAscent() )
+ SetAscent( pPos->GetAscent() );
+ }
+ delete pLast->Cut( pPos );
+ pPos = pLast->GetPortion();
+ continue;
+ }
+
+ const xub_StrLen nPorSttIdx = rInf.GetLineStart() + nLineLength;
+ nLineLength = nLineLength + pPos->GetLen();
+ AddPrtWidth( pPos->Width() );
+
+ // --> FME 2006-03-01 #i3952#
+ if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
+ {
+ if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
+ ( pPos->IsTextPortion() &&
+ lcl_HasOnlyBlanks( rInf.GetTxt(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
+ {
+ pLast = pPos;
+ pPos = pPos->GetPortion();
+ bHasBlankPortion = true;
+ continue;
+ }
+ }
+ // <--
+
+ bHasOnlyBlankPortions = false;
+
+ // Es gab Attributwechsel: Laengen und Masse aufaddieren;
+ // bzw.Maxima bilden.
+
+ KSHORT nPosHeight = pPos->Height();
+ KSHORT nPosAscent = pPos->GetAscent();
+
+ OSL_ENSURE( nPosHeight >= nPosAscent,
+ "SwLineLayout::CalcLine: bad ascent or height" );
+
+ if( pPos->IsHangingPortion() )
+ {
+ SetHanging( sal_True );
+ rInf.GetParaPortion()->SetMargin( sal_True );
+ }
+
+ // Damit ein Paragraphende-Zeichen nicht durch ein Descent zu einer
+ // geaenderten Zeilenhoehe und zum Umformatieren fuehrt.
+ if ( !pPos->IsBreakPortion() || !Height() )
+ {
+ bOnlyPostIts &= pPos->IsPostItsPortion();
+
+ if( bTmpDummy && !nLineLength )
+ {
+ if( pPos->IsFlyPortion() )
+ {
+ if( nFlyHeight < nPosHeight )
+ nFlyHeight = nPosHeight;
+ if( nFlyAscent < nPosAscent )
+ nFlyAscent = nPosAscent;
+ if( nFlyDescent < nPosHeight - nPosAscent )
+ nFlyDescent = nPosHeight - nPosAscent;
+ }
+ else
+ {
+ if( pPos->InNumberGrp() )
+ {
+ KSHORT nTmp = rInf.GetFont()->GetAscent(
+ rInf.GetVsh(), *rInf.GetOut() );
+ if( nTmp > nPosAscent )
+ {
+ nPosHeight += nTmp - nPosAscent;
+ nPosAscent = nTmp;
+ }
+ nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
+ *rInf.GetOut() );
+ if( nTmp > nPosHeight )
+ nPosHeight = nTmp;
+ }
+ Height( nPosHeight );
+ nAscent = nPosAscent;
+ nMaxDescent = nPosHeight - nPosAscent;
+ }
+ }
+ else if( !pPos->IsFlyPortion() )
+ {
+ if( Height() < nPosHeight )
+ Height( nPosHeight );
+ if( pPos->IsFlyCntPortion() || ( pPos->IsMultiPortion()
+ && ((SwMultiPortion*)pPos)->HasFlyInCntnt() ) )
+ rLine.SetFlyInCntBase();
+ if( pPos->IsFlyCntPortion() &&
+ ((SwFlyCntPortion*)pPos)->GetAlign() )
+ {
+ ((SwFlyCntPortion*)pPos)->SetMax( sal_False );
+ if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() )
+ pFlyCnt = (SwFlyCntPortion*)pPos;
+ }
+ else
+ {
+ if( nAscent < nPosAscent )
+ nAscent = nPosAscent;
+ if( nMaxDescent < nPosHeight - nPosAscent )
+ nMaxDescent = nPosHeight - nPosAscent;
+ }
+ }
+ }
+ else if( pPos->GetLen() )
+ bTmpDummy = sal_False;
+
+ if( !HasCntnt() && !pPos->InNumberGrp() )
+ {
+ if ( pPos->InExpGrp() )
+ {
+ XubString aTxt;
+ if( pPos->GetExpTxt( rInf, aTxt ) && aTxt.Len() )
+ SetCntnt( sal_True );
+ }
+ else if( ( pPos->InTxtGrp() || pPos->IsMultiPortion() ) &&
+ pPos->GetLen() )
+ SetCntnt( sal_True );
+ }
+
+ bTmpDummy = bTmpDummy && !HasCntnt() &&
+ ( !pPos->Width() || pPos->IsFlyPortion() );
+
+ pLast = pPos;
+ pPos = pPos->GetPortion();
+ }
+
+ if( pFlyCnt )
+ {
+ if( pFlyCnt->Height() == Height() )
+ {
+ pFlyCnt->SetMax( sal_True );
+ if( Height() > nMaxDescent + nAscent )
+ {
+ if( 3 == pFlyCnt->GetAlign() ) // Bottom
+ nAscent = Height() - nMaxDescent;
+ else if( 2 == pFlyCnt->GetAlign() ) // Center
+ nAscent = ( Height() + nAscent - nMaxDescent ) / 2;
+ }
+ pFlyCnt->SetAscent( nAscent );
+ }
+ }
+
+ if( bTmpDummy && nFlyHeight )
+ {
+ nAscent = nFlyAscent;
+ if( nFlyDescent > nFlyHeight - nFlyAscent )
+ Height( nFlyHeight + nFlyDescent );
+ else
+ Height( nFlyHeight );
+ }
+ else if( nMaxDescent > Height() - nAscent )
+ Height( nMaxDescent + nAscent );
+
+ if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) )
+ {
+ Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
+ nAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() );
+ }
+ }
+ }
+ else
+ {
+ SetCntnt( !bTmpDummy );
+
+ // --> FME 2006-03-01 #i3952#
+ if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
+ lcl_HasOnlyBlanks( rInf.GetTxt(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
+ {
+ bHasBlankPortion = true;
+ }
+ // <--
+ }
+
+ // --> FME 2006-03-01 #i3952#
+ if ( bHasBlankPortion && bHasOnlyBlankPortions )
+ {
+ USHORT nTmpAscent = GetAscent();
+ USHORT nTmpHeight = Height();
+ rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
+ SetAscent( nTmpAscent );
+ Height( nTmpHeight );
+ }
+ // <--
+
+ // Robust:
+ if( nLineWidth < Width() )
+ Width( nLineWidth );
+ OSL_ENSURE( nLineWidth >= Width(), "SwLineLayout::CalcLine: line is bursting" );
+ SetDummy( bTmpDummy );
+ SetRedline( rLine.GetRedln() &&
+ rLine.GetRedln()->CheckLine( rLine.GetStart(), rLine.GetEnd() ) );
+}
+
+// --> OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
+// to control, if the fly content portions and line portion are considered.
+void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent,
+ SwTwips& _orDescent,
+ SwTwips& _orObjAscent,
+ SwTwips& _orObjDescent,
+ const SwLinePortion* _pDontConsiderPortion,
+ const bool _bNoFlyCntPorAndLinePor ) const
+{
+ _orAscent = 0;
+ _orDescent = 0;
+ _orObjAscent = 0;
+ _orObjDescent = 0;
+
+ const SwLinePortion* pTmpPortion = this;
+ if ( !pTmpPortion->GetLen() && pTmpPortion->GetPortion() )
+ {
+ pTmpPortion = pTmpPortion->GetPortion();
+ }
+
+ while ( pTmpPortion )
+ {
+ if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() &&
+ ( !_bNoFlyCntPorAndLinePor ||
+ ( !pTmpPortion->IsFlyCntPortion() &&
+ !(pTmpPortion == this && pTmpPortion->GetPortion() ) ) ) )
+ {
+ SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent());
+ SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) -
+ nPortionAsc;
+
+ const sal_Bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
+ static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
+ !( pTmpPortion == _pDontConsiderPortion );
+
+ if ( bFlyCmp )
+ {
+ _orObjAscent = Max( _orObjAscent, nPortionAsc );
+ _orObjDescent = Max( _orObjDescent, nPortionDesc );
+ }
+
+ if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() )
+ {
+ _orAscent = Max( _orAscent, nPortionAsc );
+ _orDescent = Max( _orDescent, nPortionDesc );
+ }
+ }
+ pTmpPortion = pTmpPortion->GetPortion();
+ }
+}
+
+/*************************************************************************
+ * class SwCharRange
+ *************************************************************************/
+
+SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange)
+{
+ if(0 != rRange.nLen ) {
+ if(0 == nLen) {
+ nStart = rRange.nStart;
+ nLen = rRange.nLen ;
+ }
+ else {
+ if(rRange.nStart + rRange.nLen > nStart + nLen) {
+ nLen = rRange.nStart + rRange.nLen - nStart;
+ }
+ if(rRange.nStart < nStart) {
+ nLen += nStart - rRange.nStart;
+ nStart = rRange.nStart;
+ }
+ }
+ }
+ return *this;
+}
+
+/*************************************************************************
+ * SwScriptInfo::SwScriptInfo()
+ *************************************************************************/
+SwScriptInfo::SwScriptInfo() :
+ nInvalidityPos( 0 ),
+ nDefaultDir( 0 )
+{
+};
+
+/*************************************************************************
+ * SwScriptInfo::~SwScriptInfo()
+ *************************************************************************/
+SwScriptInfo::~SwScriptInfo()
+{
+}
+
+/*************************************************************************
+ * SwScriptInfo::WhichFont()
+ *
+ * Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to
+ * Sw Script Types (SW_LATIN, SW_CJK, SW_CTL), used to identify the font
+ *************************************************************************/
+BYTE SwScriptInfo::WhichFont( xub_StrLen nIdx, const String* pTxt, const SwScriptInfo* pSI )
+{
+ OSL_ENSURE( pTxt || pSI,"How should I determine the script type?" );
+ USHORT nScript;
+
+ // First we try to use our SwScriptInfo
+ if ( pSI )
+ nScript = pSI->ScriptType( nIdx );
+ else
+ // Ok, we have to ask the break iterator
+ nScript = pBreakIt->GetRealScriptOfText( *pTxt, nIdx );
+
+ switch ( nScript ) {
+ case i18n::ScriptType::LATIN : return SW_LATIN;
+ case i18n::ScriptType::ASIAN : return SW_CJK;
+ case i18n::ScriptType::COMPLEX : return SW_CTL;
+ }
+
+ OSL_ENSURE( sal_False, "Somebody tells lies about the script type!" );
+ return SW_LATIN;
+}
+
+/*************************************************************************
+ * SwScriptInfo::InitScriptInfo()
+ *
+ * searches for script changes in rTxt and stores them
+ *************************************************************************/
+
+void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode )
+{
+ InitScriptInfo( rNode, nDefaultDir == UBIDI_RTL );
+}
+
+void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode, sal_Bool bRTL )
+{
+ if( !pBreakIt->GetBreakIter().is() )
+ return;
+
+ const String& rTxt = rNode.GetTxt();
+
+ //
+ // HIDDEN TEXT INFORMATION
+ //
+ Range aRange( 0, rTxt.Len() ? rTxt.Len() - 1 : 0 );
+ MultiSelection aHiddenMulti( aRange );
+ CalcHiddenRanges( rNode, aHiddenMulti );
+
+ aHiddenChg.Remove( 0, aHiddenChg.Count() );
+ USHORT nHiddenIdx = 0;
+ USHORT i = 0;
+ for( i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ const xub_StrLen nStart = (xub_StrLen)rRange.Min();
+ const xub_StrLen nEnd = (xub_StrLen)rRange.Max() + 1;
+
+ aHiddenChg.Insert( nStart, nHiddenIdx++ );
+ aHiddenChg.Insert( nEnd, nHiddenIdx++ );
+ }
+
+ //
+ // SCRIPT AND SCRIPT RELATED INFORMATION
+ //
+
+ xub_StrLen nChg = nInvalidityPos;
+
+ // STRING_LEN means the data structure is up to date
+ nInvalidityPos = STRING_LEN;
+
+ // this is the default direction
+ nDefaultDir = static_cast<BYTE>(bRTL ? UBIDI_RTL : UBIDI_LTR);
+
+ // counter for script info arrays
+ USHORT nCnt = 0;
+ // counter for compression information arrays
+ USHORT nCntComp = 0;
+ // counter for kashida array
+ USHORT nCntKash = 0;
+
+ BYTE nScript = i18n::ScriptType::LATIN;
+
+ // compression type
+ const SwCharCompressType aCompEnum = rNode.getIDocumentSettingAccess()->getCharacterCompressionType();
+
+ // justification type
+ const sal_Bool bAdjustBlock = SVX_ADJUST_BLOCK ==
+ rNode.GetSwAttrSet().GetAdjust().GetAdjust();
+
+ //
+ // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
+ //
+
+ if( nChg )
+ {
+ // if change position = 0 we do not use any data from the arrays
+ // because by deleting all characters of the first group at the beginning
+ // of a paragraph nScript is set to a wrong value
+ OSL_ENSURE( CountScriptChg(), "Where're my changes of script?" );
+ while( nCnt < CountScriptChg() )
+ {
+ if ( nChg > GetScriptChg( nCnt ) )
+ nCnt++;
+ else
+ {
+ nScript = GetScriptType( nCnt );
+ break;
+ }
+ }
+ if( CHARCOMPRESS_NONE != aCompEnum )
+ {
+ while( nCntComp < CountCompChg() )
+ {
+ if ( nChg > GetCompStart( nCntComp ) )
+ nCntComp++;
+ else
+ break;
+ }
+ }
+ if ( bAdjustBlock )
+ {
+ while( nCntKash < CountKashida() )
+ {
+ if ( nChg > GetKashida( nCntKash ) )
+ nCntKash++;
+ else
+ break;
+ }
+ }
+ }
+
+ //
+ // ADJUST nChg VALUE:
+ //
+
+ // by stepping back one position we know that we are inside a group
+ // declared as an nScript group
+ if ( nChg )
+ --nChg;
+
+ const xub_StrLen nGrpStart = nCnt ? GetScriptChg( nCnt - 1 ) : 0;
+
+ // we go back in our group until we reach the first character of
+ // type nScript
+ while ( nChg > nGrpStart &&
+ nScript != pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
+ --nChg;
+
+ // If we are at the start of a group, we do not trust nScript,
+ // we better get nScript from the breakiterator:
+ if ( nChg == nGrpStart )
+ nScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg );
+
+ //
+ // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
+ //
+
+ // remove invalid entries from script information arrays
+ const USHORT nScriptRemove = aScriptChg.Count() - nCnt;
+ aScriptChg.Remove( nCnt, nScriptRemove );
+ aScriptType.Remove( nCnt, nScriptRemove );
+
+ // get the start of the last compression group
+ USHORT nLastCompression = nChg;
+ if( nCntComp )
+ {
+ --nCntComp;
+ nLastCompression = GetCompStart( nCntComp );
+ if( nChg >= nLastCompression + GetCompLen( nCntComp ) )
+ {
+ nLastCompression = nChg;
+ ++nCntComp;
+ }
+ }
+
+ // remove invalid entries from compression information arrays
+ const USHORT nCompRemove = aCompChg.Count() - nCntComp;
+ aCompChg.Remove( nCntComp, nCompRemove );
+ aCompLen.Remove( nCntComp, nCompRemove );
+ aCompType.Remove( nCntComp, nCompRemove );
+
+ // get the start of the last kashida group
+ USHORT nLastKashida = nChg;
+ if( nCntKash && i18n::ScriptType::COMPLEX == nScript )
+ {
+ --nCntKash;
+ nLastKashida = GetKashida( nCntKash );
+ }
+
+ // remove invalid entries from kashida array
+ aKashida.Remove( nCntKash, aKashida.Count() - nCntKash );
+
+ //
+ // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
+ // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
+ //
+
+ if( WEAK == pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
+ {
+ // If the beginning of the current group is weak, this means that
+ // all of the characters in this grounp are weak. We have to assign
+ // the scripts to these characters depending on the fonts which are
+ // set for these characters to display them.
+ xub_StrLen nEnd =
+ (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nChg, WEAK );
+
+ if( nEnd > rTxt.Len() )
+ nEnd = rTxt.Len();
+
+ nScript = (BYTE)GetI18NScriptTypeOfLanguage( (USHORT)GetAppLanguage() );
+
+ OSL_ENSURE( i18n::ScriptType::LATIN == nScript ||
+ i18n::ScriptType::ASIAN == nScript ||
+ i18n::ScriptType::COMPLEX == nScript, "Wrong default language" );
+
+ nChg = nEnd;
+
+ // Get next script type or set to weak in order to exit
+ BYTE nNextScript = ( nEnd < rTxt.Len() ) ?
+ (BYTE)pBreakIt->GetBreakIter()->getScriptType( rTxt, nEnd ) :
+ (BYTE)WEAK;
+
+ if ( nScript != nNextScript )
+ {
+ aScriptChg.Insert( nEnd, nCnt );
+ aScriptType.Insert( nScript, nCnt++ );
+ nScript = nNextScript;
+ }
+ }
+
+ //
+ // UPDATE THE SCRIPT INFO ARRAYS:
+ //
+
+ while ( nChg < rTxt.Len() || ( !aScriptChg.Count() && !rTxt.Len() ) )
+ {
+ OSL_ENSURE( i18n::ScriptType::WEAK != nScript,
+ "Inserting WEAK into SwScriptInfo structure" );
+ OSL_ENSURE( STRING_LEN != nChg, "65K? Strange length of script section" );
+
+ xub_StrLen nSearchStt = nChg;
+ nChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nSearchStt, nScript );
+
+ if ( nChg > rTxt.Len() )
+ nChg = rTxt.Len();
+
+ // --> FME 2008-09-17 #i28203#
+ // for 'complex' portions, we make sure that a portion does not contain more
+ // than one script:
+ if( i18n::ScriptType::COMPLEX == nScript && pBreakIt->GetScriptTypeDetector().is() )
+ {
+ const short nScriptType = pBreakIt->GetScriptTypeDetector()->getCTLScriptType( rTxt, nSearchStt );
+ xub_StrLen nNextCTLScriptStart = nSearchStt;
+ short nCurrentScriptType = nScriptType;
+ while( com::sun::star::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
+ {
+ nNextCTLScriptStart = (xub_StrLen)pBreakIt->GetScriptTypeDetector()->endOfCTLScriptType( rTxt, nNextCTLScriptStart );
+ if( nNextCTLScriptStart < rTxt.Len() && nNextCTLScriptStart < nChg )
+ nCurrentScriptType = pBreakIt->GetScriptTypeDetector()->getCTLScriptType( rTxt, nNextCTLScriptStart );
+ else
+ break;
+ }
+ nChg = Min( nChg, nNextCTLScriptStart );
+ }
+ // <--
+
+ // special case for dotted circle since it can be used with complex
+ // before a mark, so we want it associated with the mark's script
+ if (nChg < rTxt.Len() && nChg > 0 && (i18n::ScriptType::WEAK ==
+ pBreakIt->GetBreakIter()->getScriptType(rTxt,nChg - 1)))
+ {
+ int8_t nType = u_charType(rTxt.GetChar(nChg) );
+ if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
+ nType == U_COMBINING_SPACING_MARK )
+ {
+ aScriptChg.Insert( nChg - 1, nCnt );
+ }
+ else
+ {
+ aScriptChg.Insert( nChg, nCnt );
+ }
+ }
+ else
+ {
+ aScriptChg.Insert( nChg, nCnt );
+ }
+ aScriptType.Insert( nScript, nCnt++ );
+
+ // if current script is asian, we search for compressable characters
+ // in this range
+ if ( CHARCOMPRESS_NONE != aCompEnum &&
+ i18n::ScriptType::ASIAN == nScript )
+ {
+ BYTE ePrevState = NONE;
+ BYTE eState;
+ USHORT nPrevChg = nLastCompression;
+
+ while ( nLastCompression < nChg )
+ {
+ xub_Unicode cChar = rTxt.GetChar( nLastCompression );
+
+ // examine current character
+ switch ( cChar )
+ {
+ // Left punctuation found
+ case 0x3008: case 0x300A: case 0x300C: case 0x300E:
+ case 0x3010: case 0x3014: case 0x3016: case 0x3018:
+ case 0x301A: case 0x301D:
+ eState = SPECIAL_LEFT;
+ break;
+ // Right punctuation found
+ case 0x3001: case 0x3002: case 0x3009: case 0x300B:
+ case 0x300D: case 0x300F: case 0x3011: case 0x3015:
+ case 0x3017: case 0x3019: case 0x301B: case 0x301E:
+ case 0x301F:
+ eState = SPECIAL_RIGHT;
+ break;
+ default:
+ eState = static_cast<BYTE>( ( 0x3040 <= cChar && 0x3100 > cChar ) ? KANA : NONE );
+ }
+
+ // insert range of compressable characters
+ if( ePrevState != eState )
+ {
+ if ( ePrevState != NONE )
+ {
+ // insert start and type
+ if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum ||
+ ePrevState != KANA )
+ {
+ aCompChg.Insert( nPrevChg, nCntComp );
+ BYTE nTmpType = ePrevState;
+ aCompType.Insert( nTmpType, nCntComp );
+ aCompLen.Insert( nLastCompression - nPrevChg, nCntComp++ );
+ }
+ }
+
+ ePrevState = eState;
+ nPrevChg = nLastCompression;
+ }
+
+ nLastCompression++;
+ }
+
+ // we still have to examine last entry
+ if ( ePrevState != NONE )
+ {
+ // insert start and type
+ if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum ||
+ ePrevState != KANA )
+ {
+ aCompChg.Insert( nPrevChg, nCntComp );
+ BYTE nTmpType = ePrevState;
+ aCompType.Insert( nTmpType, nCntComp );
+ aCompLen.Insert( nLastCompression - nPrevChg, nCntComp++ );
+ }
+ }
+ }
+
+ // we search for connecting opportunities (kashida)
+ else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
+ {
+ SwScanner aScanner( rNode, rNode.GetTxt(), 0, 0,
+ i18n::WordType::DICTIONARY_WORD,
+ nLastKashida, nChg );
+
+ // the search has to be performed on a per word base
+ while ( aScanner.NextWord() )
+ {
+ const XubString& rWord = aScanner.GetWord();
+
+ xub_StrLen nIdx = 0;
+ xub_StrLen nKashidaPos = STRING_LEN;
+ xub_Unicode cCh;
+ xub_Unicode cPrevCh = 0;
+
+ USHORT nPriorityLevel = 7; // 0..6 = level found
+ // 7 not found
+
+ xub_StrLen nWordLen = rWord.Len();
+
+ // ignore trailing vowel chars
+ while( nWordLen && isTransparentChar( rWord.GetChar( nWordLen - 1 )))
+ --nWordLen;
+
+ while (nIdx < nWordLen)
+ {
+ cCh = rWord.GetChar( nIdx );
+
+ // 1. Priority:
+ // after user inserted kashida
+ if ( 0x640 == cCh )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx;
+ nPriorityLevel = 0;
+ }
+
+ // 2. Priority:
+ // after a Seen or Sad
+ if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
+ {
+ if( isSeenOrSadChar( cCh )
+ && (rWord.GetChar( nIdx+1 ) != 0x200C) ) // #i98410#: prevent ZWNJ expansion
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx;
+ nPriorityLevel = 1;
+ }
+ }
+
+ // 3. Priority:
+ // before final form of Teh Marbuta, Hah, Dal
+ if ( nPriorityLevel >= 2 && nIdx > 0 )
+ {
+ if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
+ isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word
+ ( isHahChar ( cCh ) && nIdx == nWordLen - 1)) // Hah (dual joining) only at end of word
+ {
+
+ OSL_ENSURE( 0 != cPrevCh, "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 2;
+ }
+ }
+ }
+
+ // 4. Priority:
+ // before final form of Alef, Lam or Kaf
+ if ( nPriorityLevel >= 3 && nIdx > 0 )
+ {
+ if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word
+ (( isLamChar ( cCh ) || // Lam
+ isKafChar ( cCh ) || // Kaf (both dual joining)
+ isGafChar ( cCh ) )
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ OSL_ENSURE( 0 != cPrevCh, "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 3;
+ }
+ }
+ }
+
+ // 5. Priority:
+ // before media Bah
+ if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
+ {
+ if ( isBaaChar ( cCh )) // Bah
+ {
+ // check if next character is Reh, Yeh or Alef Maksura
+ xub_Unicode cNextCh = rWord.GetChar( nIdx + 1 );
+ if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
+ {
+ OSL_ENSURE( 0 != cPrevCh, "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 4;
+ }
+ }
+ }
+ }
+
+ // 6. Priority:
+ // before the final form of Waw, Ain, Qaf and Fa
+ if ( nPriorityLevel >= 5 && nIdx > 0 )
+ {
+ if ( isWawChar ( cCh ) || // Wav (right joining)
+ // final form may appear in the middle of word
+ (( isAinChar ( cCh ) || // Ain (dual joining)
+ isQafChar ( cCh ) || // Qaf (dual joining)
+ isFeChar ( cCh ) ) // Feh (dual joining)
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ OSL_ENSURE( 0 != cPrevCh, "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 5;
+ }
+ }
+ }
+
+ // other connecting possibilities
+ if ( nPriorityLevel >= 6 && nIdx > 0 )
+ {
+ // remaining right joiners
+ // Reh, Zain, Thal,
+ if ( isRehChar ( cCh ) || // Reh Zain (right joining)
+ // final form may appear in the middle of word
+ ( 0x60C <= cCh && 0x6FE >= cCh // all others
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ OSL_ENSURE( 0 != cPrevCh, "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 6;
+ }
+ }
+ }
+
+ // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
+ // Damma, Kasra, Shadda and Sukun when checking if
+ // a character can be connected to previous character.
+ if ( !isTransparentChar ( cCh) )
+ cPrevCh = cCh;
+
+ ++nIdx;
+ } // end of current word
+
+ if ( STRING_LEN != nKashidaPos )
+ aKashida.Insert( nKashidaPos, nCntKash++ );
+ } // end of kashida search
+ }
+
+ if ( nChg < rTxt.Len() )
+ nScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg );
+
+ nLastCompression = nChg;
+ nLastKashida = nChg;
+ };
+
+#if OSL_DEBUG_LEVEL > 1
+ // check kashida data
+ long nTmpKashidaPos = -1;
+ sal_Bool bWrongKash = sal_False;
+ for (i = 0; i < aKashida.Count(); ++i )
+ {
+ long nCurrKashidaPos = GetKashida( i );
+ if ( nCurrKashidaPos <= nTmpKashidaPos )
+ {
+ bWrongKash = sal_True;
+ break;
+ }
+ nTmpKashidaPos = nCurrKashidaPos;
+ }
+ OSL_ENSURE( ! bWrongKash, "Kashida array contains wrong data" );
+#endif
+
+ // remove invalid entries from direction information arrays
+ const USHORT nDirRemove = aDirChg.Count();
+ aDirChg.Remove( 0, nDirRemove );
+ aDirType.Remove( 0, nDirRemove );
+
+ // Perform Unicode Bidi Algorithm for text direction information
+ bool bPerformUBA = UBIDI_LTR != nDefaultDir;
+ nCnt = 0;
+ while( !bPerformUBA && nCnt < CountScriptChg() )
+ {
+ if ( i18n::ScriptType::COMPLEX == GetScriptType( nCnt++ ) )
+ bPerformUBA = true;
+ }
+
+ // do not call the unicode bidi algorithm if not required
+ if ( bPerformUBA )
+ {
+ UpdateBidiInfo( rTxt );
+
+ // #i16354# Change script type for RTL text to CTL:
+ // 1. All text in RTL runs will use the CTL font
+ // #i89825# change the script type also to CTL (hennerdrewes)
+ // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
+ for ( USHORT nDirIdx = 0; nDirIdx < aDirChg.Count(); ++nDirIdx )
+ {
+ const BYTE nCurrDirType = GetDirType( nDirIdx );
+ // nStart ist start of RTL run:
+ const xub_StrLen nStart = nDirIdx > 0 ? GetDirChg( nDirIdx - 1 ) : 0;
+ // nEnd is end of RTL run:
+ const xub_StrLen nEnd = GetDirChg( nDirIdx );
+
+ if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
+ ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( rTxt, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
+ {
+ // nScriptIdx points into the ScriptArrays:
+ USHORT nScriptIdx = 0;
+
+ // Skip entries in ScriptArray which are not inside the RTL run:
+ // Make nScriptIdx become the index of the script group with
+ // 1. nStartPosOfGroup <= nStart and
+ // 2. nEndPosOfGroup > nStart
+ while ( GetScriptChg( nScriptIdx ) <= nStart )
+ ++nScriptIdx;
+
+ const xub_StrLen nStartPosOfGroup = nScriptIdx ? GetScriptChg( nScriptIdx - 1 ) : 0;
+ const BYTE nScriptTypeOfGroup = GetScriptType( nScriptIdx );
+
+ OSL_ENSURE( nStartPosOfGroup <= nStart && GetScriptChg( nScriptIdx ) > nStart,
+ "Script override with CTL font trouble" );
+
+ // Check if we have to insert a new script change at
+ // position nStart. If nStartPosOfGroup < nStart,
+ // we have to insert a new script change:
+ if ( nStart > 0 && nStartPosOfGroup < nStart )
+ {
+ aScriptChg.Insert( nStart, nScriptIdx );
+ aScriptType.Insert( nScriptTypeOfGroup, nScriptIdx );
+ ++nScriptIdx;
+ }
+
+ // Remove entries in ScriptArray which end inside the RTL run:
+ while ( nScriptIdx < aScriptChg.Count() && GetScriptChg( nScriptIdx ) <= nEnd )
+ {
+ aScriptChg.Remove( nScriptIdx, 1 );
+ aScriptType.Remove( nScriptIdx, 1 );
+ }
+
+ // Insert a new entry in ScriptArray for the end of the RTL run:
+ aScriptChg.Insert( nEnd, nScriptIdx );
+ aScriptType.Insert( i18n::ScriptType::COMPLEX, nScriptIdx );
+
+#if OSL_DEBUG_LEVEL > 1
+ BYTE nScriptType;
+ BYTE nLastScriptType = i18n::ScriptType::WEAK;
+ xub_StrLen nScriptChg;
+ xub_StrLen nLastScriptChg = 0;
+ (void) nLastScriptChg;
+ (void) nLastScriptType;
+
+ for ( USHORT i2 = 0; i2 < aScriptChg.Count(); ++i2 )
+ {
+ nScriptChg = GetScriptChg( i2 );
+ nScriptType = GetScriptType( i2 );
+ OSL_ENSURE( nLastScriptType != nScriptType &&
+ nLastScriptChg < nScriptChg,
+ "Heavy InitScriptType() confusion" );
+ }
+#endif
+ }
+ }
+ }
+}
+
+void SwScriptInfo::UpdateBidiInfo( const String& rTxt )
+{
+ // remove invalid entries from direction information arrays
+ const USHORT nDirRemove = aDirChg.Count();
+ aDirChg.Remove( 0, nDirRemove );
+ aDirType.Remove( 0, nDirRemove );
+
+ //
+ // Bidi functions from icu 2.0
+ //
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( rTxt.Len(), 0, &nError );
+ nError = U_ZERO_ERROR;
+
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rTxt.GetBuffer()), rTxt.Len(), // UChar != sal_Unicode in MinGW
+ nDefaultDir, NULL, &nError );
+ nError = U_ZERO_ERROR;
+ long nCount = ubidi_countRuns( pBidi, &nError );
+ int32_t nStart = 0;
+ int32_t nEnd;
+ UBiDiLevel nCurrDir;
+ // counter for direction information arrays
+ USHORT nCntDir = 0;
+
+ for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
+ aDirChg.Insert( (USHORT)nEnd, nCntDir );
+ aDirType.Insert( (BYTE)nCurrDir, nCntDir++ );
+ nStart = nEnd;
+ }
+
+ ubidi_close( pBidi );
+}
+
+
+/*************************************************************************
+ * SwScriptInfo::NextScriptChg(..)
+ * returns the position of the next character which belongs to another script
+ * than the character of the actual (input) position.
+ * If there's no script change until the end of the paragraph, it will return
+ * STRING_LEN.
+ * Scripts are Asian (Chinese, Japanese, Korean),
+ * Latin ( English etc.)
+ * and Complex ( Hebrew, Arabian )
+ *************************************************************************/
+
+xub_StrLen SwScriptInfo::NextScriptChg( const xub_StrLen nPos ) const
+{
+ USHORT nEnd = CountScriptChg();
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetScriptChg( nX ) )
+ return GetScriptChg( nX );
+ }
+
+ return STRING_LEN;
+}
+
+/*************************************************************************
+ * SwScriptInfo::ScriptType(..)
+ * returns the script of the character at the input position
+ *************************************************************************/
+
+BYTE SwScriptInfo::ScriptType( const xub_StrLen nPos ) const
+{
+ USHORT nEnd = CountScriptChg();
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetScriptChg( nX ) )
+ return GetScriptType( nX );
+ }
+
+ // the default is the application language script
+ return (BYTE)GetI18NScriptTypeOfLanguage( (USHORT)GetAppLanguage() );
+}
+
+xub_StrLen SwScriptInfo::NextDirChg( const xub_StrLen nPos,
+ const BYTE* pLevel ) const
+{
+ BYTE nCurrDir = pLevel ? *pLevel : 62;
+ USHORT nEnd = CountDirChg();
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetDirChg( nX ) &&
+ ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) )
+ return GetDirChg( nX );
+ }
+
+ return STRING_LEN;
+}
+
+BYTE SwScriptInfo::DirType( const xub_StrLen nPos ) const
+{
+ USHORT nEnd = CountDirChg();
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetDirChg( nX ) )
+ return GetDirType( nX );
+ }
+
+ return 0;
+}
+
+/*************************************************************************
+ * SwScriptInfo::MaskHiddenRanges(..)
+ * Takes a string and replaced the hidden ranges with cChar.
+ **************************************************************************/
+
+USHORT SwScriptInfo::MaskHiddenRanges( const SwTxtNode& rNode, XubString& rText,
+ const xub_StrLen nStt, const xub_StrLen nEnd,
+ const xub_Unicode cChar )
+{
+ OSL_ENSURE( rNode.GetTxt().Len() == rText.Len(), "MaskHiddenRanges, string len mismatch" );
+
+ PositionList aList;
+ xub_StrLen nHiddenStart;
+ xub_StrLen nHiddenEnd;
+ USHORT nNumOfHiddenChars = 0;
+ GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
+ PositionList::const_reverse_iterator rFirst( aList.end() );
+ PositionList::const_reverse_iterator rLast( aList.begin() );
+ while ( rFirst != rLast )
+ {
+ nHiddenEnd = *(rFirst++);
+ nHiddenStart = *(rFirst++);
+
+ if ( nHiddenEnd < nStt || nHiddenStart > nEnd )
+ continue;
+
+ while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
+ {
+ if ( nHiddenStart >= nStt && nHiddenStart < nEnd )
+ {
+ rText.SetChar( nHiddenStart, cChar );
+ ++nNumOfHiddenChars;
+ }
+ ++nHiddenStart;
+ }
+ }
+
+ return nNumOfHiddenChars;
+}
+
+/*************************************************************************
+ * SwScriptInfo::DeleteHiddenRanges(..)
+ * Takes a SwTxtNode and deletes the hidden ranges from the node.
+ **************************************************************************/
+
+void SwScriptInfo::DeleteHiddenRanges( SwTxtNode& rNode )
+{
+ PositionList aList;
+ xub_StrLen nHiddenStart;
+ xub_StrLen nHiddenEnd;
+ GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
+ PositionList::const_reverse_iterator rFirst( aList.end() );
+ PositionList::const_reverse_iterator rLast( aList.begin() );
+ while ( rFirst != rLast )
+ {
+ nHiddenEnd = *(rFirst++);
+ nHiddenStart = *(rFirst++);
+
+ SwPaM aPam( rNode, nHiddenStart, rNode, nHiddenEnd );
+ rNode.getIDocumentContentOperations()->DeleteRange( aPam );
+ }
+}
+
+/*************************************************************************
+ * SwScriptInfo::GetBoundsOfHiddenRange(..)
+ * static version
+ **************************************************************************/
+
+bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTxtNode& rNode, xub_StrLen nPos,
+ xub_StrLen& rnStartPos, xub_StrLen& rnEndPos,
+ PositionList* pList )
+{
+ rnStartPos = STRING_LEN;
+ rnEndPos = 0;
+
+ bool bNewContainsHiddenChars = false;
+
+ //
+ // Optimization: First examine the flags at the text node:
+ //
+ if ( !rNode.IsCalcHiddenCharFlags() )
+ {
+ bool bWholePara = rNode.HasHiddenCharAttribute( true );
+ bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false );
+ if ( !bContainsHiddenChars )
+ return false;
+
+ if ( bWholePara )
+ {
+ if ( pList )
+ {
+ pList->push_back( 0 );
+ pList->push_back( rNode.GetTxt().Len() );
+ }
+
+ rnStartPos = 0;
+ rnEndPos = rNode.GetTxt().Len();
+ return true;
+ }
+ }
+
+ const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
+ if ( pSI )
+ {
+ //
+ // Check first, if we have a valid SwScriptInfo object for this text node:
+ //
+ bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList );
+ const bool bNewHiddenCharsHidePara = ( rnStartPos == 0 && rnEndPos >= rNode.GetTxt().Len() );
+ rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
+ }
+ else
+ {
+ //
+ // No valid SwScriptInfo Object, we have to do it the hard way:
+ //
+ Range aRange( 0, rNode.GetTxt().Len() ? rNode.GetTxt().Len() - 1 : 0 );
+ MultiSelection aHiddenMulti( aRange );
+ SwScriptInfo::CalcHiddenRanges( rNode, aHiddenMulti );
+ for( USHORT i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ const xub_StrLen nHiddenStart = (xub_StrLen)rRange.Min();
+ const xub_StrLen nHiddenEnd = (xub_StrLen)rRange.Max() + 1;
+
+ if ( nHiddenStart > nPos )
+ break;
+ else if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
+ {
+ rnStartPos = nHiddenStart;
+ rnEndPos = Min( nHiddenEnd, rNode.GetTxt().Len() );
+ break;
+ }
+ }
+
+ if ( pList )
+ {
+ for( USHORT i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ pList->push_back( (xub_StrLen)rRange.Min() );
+ pList->push_back( (xub_StrLen)rRange.Max() + 1 );
+ }
+ }
+
+ bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0;
+ }
+
+ return bNewContainsHiddenChars;
+}
+
+/*************************************************************************
+ * SwScriptInfo::GetBoundsOfHiddenRange(..)
+ * non-static version
+ **************************************************************************/
+
+bool SwScriptInfo::GetBoundsOfHiddenRange( xub_StrLen nPos, xub_StrLen& rnStartPos,
+ xub_StrLen& rnEndPos, PositionList* pList ) const
+{
+ rnStartPos = STRING_LEN;
+ rnEndPos = 0;
+
+ USHORT nEnd = CountHiddenChg();
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ const xub_StrLen nHiddenStart = GetHiddenChg( nX++ );
+ const xub_StrLen nHiddenEnd = GetHiddenChg( nX );
+
+ if ( nHiddenStart > nPos )
+ break;
+ else if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
+ {
+ rnStartPos = nHiddenStart;
+ rnEndPos = nHiddenEnd;
+ break;
+ }
+ }
+
+ if ( pList )
+ {
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ pList->push_back( GetHiddenChg( nX++ ) );
+ pList->push_back( GetHiddenChg( nX ) );
+ }
+ }
+
+ return CountHiddenChg() > 0;
+}
+
+/*************************************************************************
+ * SwScriptInfo::IsInHiddenRange()
+ **************************************************************************/
+
+bool SwScriptInfo::IsInHiddenRange( const SwTxtNode& rNode, xub_StrLen nPos )
+{
+ xub_StrLen nStartPos;
+ xub_StrLen nEndPos;
+ SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos );
+ return nStartPos != STRING_LEN;
+}
+
+
+#if OSL_DEBUG_LEVEL > 1
+/*************************************************************************
+ * SwScriptInfo::CompType(..)
+ * returns the type of the compressed character
+ *************************************************************************/
+
+BYTE SwScriptInfo::CompType( const xub_StrLen nPos ) const
+{
+ USHORT nEnd = CountCompChg();
+ for( USHORT nX = 0; nX < nEnd; ++nX )
+ {
+ xub_StrLen nChg = GetCompStart( nX );
+
+ if ( nPos < nChg )
+ return NONE;
+
+ if( nPos < nChg + GetCompLen( nX ) )
+ return GetCompType( nX );
+ }
+ return NONE;
+}
+#endif
+
+/*************************************************************************
+ * SwScriptInfo::HasKana()
+ * returns, if there are compressable kanas or specials
+ * betwenn nStart and nEnd
+ *************************************************************************/
+
+USHORT SwScriptInfo::HasKana( xub_StrLen nStart, const xub_StrLen nLen ) const
+{
+ USHORT nCnt = CountCompChg();
+ xub_StrLen nEnd = nStart + nLen;
+
+ for( USHORT nX = 0; nX < nCnt; ++nX )
+ {
+ xub_StrLen nKanaStart = GetCompStart( nX );
+ xub_StrLen nKanaEnd = nKanaStart + GetCompLen( nX );
+
+ if ( nKanaStart >= nEnd )
+ return USHRT_MAX;
+
+ if ( nStart < nKanaEnd )
+ return nX;
+ }
+
+ return USHRT_MAX;
+}
+
+/*************************************************************************
+ * SwScriptInfo::Compress()
+ *************************************************************************/
+
+long SwScriptInfo::Compress( sal_Int32* pKernArray, xub_StrLen nIdx, xub_StrLen nLen,
+ const USHORT nCompress, const USHORT nFontHeight,
+ Point* pPoint ) const
+{
+ OSL_ENSURE( nCompress, "Compression without compression?!" );
+ OSL_ENSURE( nLen, "Compression without text?!" );
+ USHORT nCompCount = CountCompChg();
+
+ // In asian typography, there are full width and half width characters.
+ // Full width punctuation characters can be compressed by 50 %
+ // to determine this, we compare the font width with 75 % of its height
+ USHORT nMinWidth = ( 3 * nFontHeight ) / 4;
+
+ USHORT nCompIdx = HasKana( nIdx, nLen );
+
+ if ( USHRT_MAX == nCompIdx )
+ return 0;
+
+ xub_StrLen nChg = GetCompStart( nCompIdx );
+ xub_StrLen nCompLen = GetCompLen( nCompIdx );
+ USHORT nI = 0;
+ nLen = nLen + nIdx;
+
+ if( nChg > nIdx )
+ {
+ nI = nChg - nIdx;
+ nIdx = nChg;
+ }
+ else if( nIdx < nChg + nCompLen )
+ nCompLen -= nIdx - nChg;
+
+ if( nIdx > nLen || nCompIdx >= nCompCount )
+ return 0;
+
+ long nSub = 0;
+ long nLast = nI ? pKernArray[ nI - 1 ] : 0;
+ do
+ {
+ USHORT nType = GetCompType( nCompIdx );
+#if OSL_DEBUG_LEVEL > 1
+ OSL_ENSURE( nType == CompType( nIdx ), "Gimme the right type!" );
+#endif
+ nCompLen = nCompLen + nIdx;
+ if( nCompLen > nLen )
+ nCompLen = nLen;
+
+ // are we allowed to compress the character?
+ if ( pKernArray[ nI ] - nLast < nMinWidth )
+ {
+ nIdx++; nI++;
+ }
+ else
+ {
+ while( nIdx < nCompLen )
+ {
+ OSL_ENSURE( SwScriptInfo::NONE != nType, "None compression?!" );
+
+ // nLast is width of current character
+ nLast -= pKernArray[ nI ];
+
+ nLast *= nCompress;
+ long nMove = 0;
+ if( SwScriptInfo::KANA != nType )
+ {
+ nLast /= 20000;
+ if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType )
+ {
+ if( nI )
+ nMove = nLast;
+ else
+ {
+ pPoint->X() += nLast;
+ nLast = 0;
+ }
+ }
+ }
+ else
+ nLast /= 100000;
+ nSub -= nLast;
+ nLast = pKernArray[ nI ];
+ if( nMove )
+ pKernArray[ nI - 1 ] += nMove;
+ pKernArray[ nI++ ] -= nSub;
+ ++nIdx;
+ }
+ }
+
+ if( nIdx < nLen )
+ {
+ xub_StrLen nTmpChg;
+ if( ++nCompIdx < nCompCount )
+ {
+ nTmpChg = GetCompStart( nCompIdx );
+ if( nTmpChg > nLen )
+ nTmpChg = nLen;
+ nCompLen = GetCompLen( nCompIdx );
+ }
+ else
+ nTmpChg = nLen;
+ while( nIdx < nTmpChg )
+ {
+ nLast = pKernArray[ nI ];
+ pKernArray[ nI++ ] -= nSub;
+ ++nIdx;
+ }
+ }
+ else
+ break;
+ } while( nIdx < nLen );
+ return nSub;
+}
+
+/*************************************************************************
+ * SwScriptInfo::KashidaJustify()
+ *************************************************************************/
+
+// Note on calling KashidaJustify():
+// Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
+// total number of kashida positions, or the number of kashida positions after some positions
+// have been dropped, depending on the state of the aKashidaInvalid array.
+
+USHORT SwScriptInfo::KashidaJustify( sal_Int32* pKernArray,
+ sal_Int32* pScrArray,
+ xub_StrLen nStt,
+ xub_StrLen nLen,
+ long nSpaceAdd ) const
+{
+ OSL_ENSURE( nLen, "Kashida justification without text?!" );
+
+ if( !IsKashidaLine(nStt))
+ return STRING_LEN;
+
+ // evaluate kashida informatin in collected in SwScriptInfo
+
+ USHORT nCntKash = 0;
+ while( nCntKash < CountKashida() )
+ {
+ if ( nStt <= GetKashida( nCntKash ) )
+ break;
+ else
+ nCntKash++;
+ }
+
+ const xub_StrLen nEnd = nStt + nLen;
+
+ USHORT nCntKashEnd = nCntKash;
+ while ( nCntKashEnd < CountKashida() )
+ {
+ if ( nEnd <= GetKashida( nCntKashEnd ) )
+ break;
+ else
+ nCntKashEnd++;
+ }
+
+ USHORT nActualKashCount = nCntKashEnd - nCntKash;
+ for ( USHORT i = nCntKash; i < nCntKashEnd; ++i )
+ {
+ if ( nActualKashCount && !IsKashidaValid ( i ) )
+ --nActualKashCount;
+ }
+
+ if ( !pKernArray )
+ return nActualKashCount;
+
+ // do nothing if there is no more kashida
+ if ( nCntKash < CountKashida() )
+ {
+ // skip any invalid kashidas
+ while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
+ ++nCntKash;
+
+ xub_StrLen nKashidaPos = GetKashida( nCntKash );
+ xub_StrLen nIdx = nKashidaPos;
+ long nKashAdd = nSpaceAdd;
+
+ while ( nIdx < nEnd )
+ {
+ USHORT nArrayPos = nIdx - nStt;
+
+ // next kashida position
+ ++nCntKash;
+ while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
+ ++nCntKash;
+
+ nIdx = nCntKash < CountKashida() && IsKashidaValid ( nCntKash ) ? GetKashida( nCntKash ) : nEnd;
+ if ( nIdx > nEnd )
+ nIdx = nEnd;
+
+ const USHORT nArrayEnd = nIdx - nStt;
+
+ while ( nArrayPos < nArrayEnd )
+ {
+ pKernArray[ nArrayPos ] += nKashAdd;
+ if ( pScrArray )
+ pScrArray[ nArrayPos ] += nKashAdd;
+ ++nArrayPos;
+ }
+ nKashAdd += nSpaceAdd;
+ }
+ }
+
+ return 0;
+}
+
+/*************************************************************************
+ * SwScriptInfo::IsArabicText()
+ *
+ * Checks if the current text is 'Arabic' text. Note that only the first
+ * character has to be checked because a ctl portion only contains one
+ * script, see NewTxtPortion
+ *************************************************************************/
+sal_Bool SwScriptInfo::IsArabicText( const XubString& rTxt, xub_StrLen nStt, xub_StrLen nLen )
+{
+ using namespace ::com::sun::star::i18n;
+ static ScriptTypeList typeList[] = {
+ { UnicodeScript_kArabic, UnicodeScript_kArabic, UnicodeScript_kArabic }, // 11,
+ { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, UnicodeScript_kScriptCount } // 88
+ };
+
+ // go forward if current position does not hold a regular character:
+ const CharClass& rCC = GetAppCharClass();
+ sal_Int32 nIdx = nStt;
+ const xub_StrLen nEnd = nStt + nLen;
+ while ( nIdx < nEnd && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) )
+ {
+ ++nIdx;
+ }
+
+ if( nIdx == nEnd )
+ {
+ // no regular character found in this portion. Go backward:
+ --nIdx;
+ while ( nIdx >= 0 && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) )
+ {
+ --nIdx;
+ }
+ }
+
+ if( nIdx >= 0 )
+ {
+ const xub_Unicode cCh = rTxt.GetChar( (xub_StrLen)nIdx );
+ const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, UnicodeScript_kScriptCount );
+ return type == UnicodeScript_kArabic;
+ }
+ return sal_False;
+}
+
+/*************************************************************************
+ * SwScriptInfo::IsKashidaValid()
+ *************************************************************************/
+
+sal_Bool SwScriptInfo::IsKashidaValid ( xub_StrLen nKashPos ) const
+{
+ for ( xub_StrLen i = 0; i < aKashidaInvalid.Count(); ++i )
+ {
+ if ( aKashidaInvalid [ i ] == nKashPos )
+ return false;
+ }
+ return true;
+}
+
+/*************************************************************************
+ * SwScriptInfo::ClearKashidaInvalid()
+ *************************************************************************/
+
+void SwScriptInfo::ClearKashidaInvalid ( xub_StrLen nKashPos )
+{
+ for ( xub_StrLen i = 0; i < aKashidaInvalid.Count(); ++i )
+ {
+ if ( aKashidaInvalid [ i ] == nKashPos )
+ {
+ aKashidaInvalid.Remove (i, 1);
+ return;
+ }
+ }
+}
+
+/*************************************************************************
+ * SwScriptInfo::MarkOrClearKashidaInvalid()
+ *************************************************************************/
+// bMark == true:
+// marks the first valid kashida in the given text range as invalid
+
+// bMark == false:
+// clears all kashida invalid flags in the given text range
+
+bool SwScriptInfo::MarkOrClearKashidaInvalid ( xub_StrLen nStt, xub_StrLen nLen, bool bMark, xub_StrLen nMarkCount )
+{
+ USHORT nCntKash = 0;
+ while( nCntKash < CountKashida() )
+ {
+ if ( nStt <= GetKashida( nCntKash ) )
+ break;
+ else
+ nCntKash++;
+ }
+
+ const xub_StrLen nEnd = nStt + nLen;
+
+ while ( nCntKash < CountKashida() )
+ {
+ if ( nEnd <= GetKashida( nCntKash ) )
+ break;
+ else
+ {
+ if(bMark)
+ {
+ if ( IsKashidaValid ( nCntKash ) )
+ {
+ MarkKashidaInvalid ( nCntKash );
+ --nMarkCount;
+ if(!nMarkCount)
+ return true;
+ }
+ }
+ else
+ {
+ ClearKashidaInvalid ( nCntKash );
+ }
+ nCntKash++;
+ }
+ }
+ return false;
+}
+
+void SwScriptInfo::MarkKashidaInvalid ( xub_StrLen nKashPos )
+{
+ aKashidaInvalid.Insert( nKashPos, aKashidaInvalid.Count() );
+}
+
+/*************************************************************************
+ * SwScriptInfo::GetKashidaPositions()
+ *************************************************************************/
+// retrieve the kashida positions in the given text range
+USHORT SwScriptInfo::GetKashidaPositions ( xub_StrLen nStt, xub_StrLen nLen,
+ xub_StrLen* pKashidaPosition )
+{
+ USHORT nCntKash = 0;
+ while( nCntKash < CountKashida() )
+ {
+ if ( nStt <= GetKashida( nCntKash ) )
+ break;
+ else
+ nCntKash++;
+ }
+
+ const xub_StrLen nEnd = nStt + nLen;
+
+ USHORT nCntKashEnd = nCntKash;
+ while ( nCntKashEnd < CountKashida() )
+ {
+ if ( nEnd <= GetKashida( nCntKashEnd ) )
+ break;
+ else
+ {
+ pKashidaPosition [ nCntKashEnd - nCntKash ] = GetKashida ( nCntKashEnd );
+ nCntKashEnd++;
+ }
+ }
+ return nCntKashEnd - nCntKash;
+}
+
+void SwScriptInfo::SetNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen )
+{
+ aNoKashidaLine.Insert( nStt, aNoKashidaLine.Count());
+ aNoKashidaLineEnd.Insert( nStt+nLen, aNoKashidaLineEnd.Count());
+}
+
+/*************************************************************************
+ * SwScriptInfo::IsKashidaLine()
+ *************************************************************************/
+// determines if the line uses kashida justification
+
+bool SwScriptInfo::IsKashidaLine ( xub_StrLen nCharIdx ) const
+{
+ for( xub_StrLen i = 0; i < aNoKashidaLine.Count(); ++i )
+ {
+ if( nCharIdx >= aNoKashidaLine[ i ] && nCharIdx < aNoKashidaLineEnd[ i ])
+ return false;
+ }
+ return true;
+}
+/*************************************************************************
+ * SwScriptInfo::ClearKashidaLine()
+ *************************************************************************/
+
+void SwScriptInfo::ClearNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen )
+{
+ xub_StrLen i = 0;
+ while( i < aNoKashidaLine.Count())
+ {
+ if( nStt + nLen >= aNoKashidaLine[ i ] && nStt < aNoKashidaLineEnd [ i ] )
+ {
+ aNoKashidaLine.Remove(i, 1);
+ aNoKashidaLineEnd.Remove(i, 1);
+ }
+ else
+ ++i;
+ }
+}
+
+/*************************************************************************
+ * SwScriptInfo::MarkKashidasInvalid()
+ *************************************************************************/
+// mark the given character indices as invalid kashida positions
+bool SwScriptInfo::MarkKashidasInvalid ( xub_StrLen nCnt, xub_StrLen* pKashidaPositions )
+{
+ OSL_ENSURE( pKashidaPositions && nCnt > 0, "Where are kashidas?" );
+
+ USHORT nCntKash = 0;
+ xub_StrLen nKashidaPosIdx = 0;
+
+ while ( nCntKash < CountKashida() && nKashidaPosIdx < nCnt )
+ {
+ if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) )
+ {
+ nCntKash++;
+ continue;
+ }
+
+ if ( pKashidaPositions [nKashidaPosIdx] == GetKashida( nCntKash ) && IsKashidaValid ( nCntKash ) )
+ {
+ MarkKashidaInvalid ( nCntKash );
+ }
+ else
+ return false; // something is wrong
+ nKashidaPosIdx++;
+ }
+ return true;
+}
+
+/*************************************************************************
+ * SwScriptInfo::ThaiJustify()
+ *************************************************************************/
+
+USHORT SwScriptInfo::ThaiJustify( const XubString& rTxt, sal_Int32* pKernArray,
+ sal_Int32* pScrArray, xub_StrLen nStt,
+ xub_StrLen nLen, xub_StrLen nNumberOfBlanks,
+ long nSpaceAdd )
+{
+ OSL_ENSURE( nStt + nLen <= rTxt.Len(), "String in ThaiJustify too small" );
+
+ SwTwips nNumOfTwipsToDistribute = nSpaceAdd * nNumberOfBlanks /
+ SPACING_PRECISION_FACTOR;
+
+ long nSpaceSum = 0;
+ USHORT nCnt = 0;
+
+ for ( USHORT nI = 0; nI < nLen; ++nI )
+ {
+ const xub_Unicode cCh = rTxt.GetChar( nStt + nI );
+
+ // check if character is not above or below base
+ if ( ( 0xE34 > cCh || cCh > 0xE3A ) &&
+ ( 0xE47 > cCh || cCh > 0xE4E ) && cCh != 0xE31 )
+ {
+ if ( nNumberOfBlanks > 0 )
+ {
+ nSpaceAdd = nNumOfTwipsToDistribute / nNumberOfBlanks;
+ --nNumberOfBlanks;
+ nNumOfTwipsToDistribute -= nSpaceAdd;
+ }
+ nSpaceSum += nSpaceAdd;
+ ++nCnt;
+ }
+
+ if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
+ if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
+ }
+
+ return nCnt;
+}
+
+/*************************************************************************
+ * SwScriptInfo::GetScriptInfo()
+ *************************************************************************/
+
+SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTxtNode& rTNd,
+ sal_Bool bAllowInvalid )
+{
+ SwClientIter aClientIter( (SwTxtNode&)rTNd );
+ SwClient* pLast = aClientIter.GoStart();
+ SwScriptInfo* pScriptInfo = 0;
+
+ while( pLast )
+ {
+ if ( pLast->ISA( SwTxtFrm ) )
+ {
+ pScriptInfo = (SwScriptInfo*)((SwTxtFrm*)pLast)->GetScriptInfo();
+ if ( pScriptInfo )
+ {
+ if ( !bAllowInvalid && STRING_LEN != pScriptInfo->GetInvalidity() )
+ pScriptInfo = 0;
+ else break;
+ }
+ }
+ pLast = ++aClientIter;
+ }
+
+ return pScriptInfo;
+}
+
+/*************************************************************************
+ * SwParaPortion::SwParaPortion()
+ *************************************************************************/
+SwParaPortion::SwParaPortion()
+{
+ FormatReset();
+ bFlys = bFtnNum = bMargin = sal_False;
+ SetWhichPor( POR_PARA );
+}
+
+/*************************************************************************
+ * SwParaPortion::~SwParaPortion()
+ *************************************************************************/
+SwParaPortion::~SwParaPortion()
+{
+}
+
+/*************************************************************************
+ * SwParaPortion::GetParLen()
+ *************************************************************************/
+xub_StrLen SwParaPortion::GetParLen() const
+{
+ xub_StrLen nLen = 0;
+ const SwLineLayout *pLay = this;
+ while( pLay )
+ {
+ DBG_LOOP;
+ nLen = nLen + pLay->GetLen();
+ pLay = pLay->GetNext();
+ }
+ return nLen;
+}
+
+/*************************************************************************
+ * SwParaPortion::FindDropPortion()
+ *************************************************************************/
+
+const SwDropPortion *SwParaPortion::FindDropPortion() const
+{
+ const SwLineLayout *pLay = this;
+ while( pLay && pLay->IsDummy() )
+ pLay = pLay->GetNext();
+ while( pLay )
+ {
+ const SwLinePortion *pPos = pLay->GetPortion();
+ while ( pPos && !pPos->GetLen() )
+ pPos = pPos->GetPortion();
+ if( pPos && pPos->IsDropPortion() )
+ return (SwDropPortion *)pPos;
+ pLay = pLay->GetLen() ? NULL : pLay->GetNext();
+ }
+ return NULL;
+}
+
+/*************************************************************************
+ * SwLineLayout::Init()
+ *************************************************************************/
+
+void SwLineLayout::Init( SwLinePortion* pNextPortion )
+{
+ Height( 0 );
+ Width( 0 );
+ SetLen( 0 );
+ SetAscent( 0 );
+ SetRealHeight( 0 );
+ SetPortion( pNextPortion );
+}
+
+/*-----------------16.11.00 11:04-------------------
+ * HangingMargin()
+ * looks for hanging punctuation portions in the paragraph
+ * and return the maximum right offset of them.
+ * If no such portion is found, the Margin/Hanging-flags will be atualized.
+ * --------------------------------------------------*/
+
+SwTwips SwLineLayout::_GetHangingMargin() const
+{
+ SwLinePortion* pPor = GetPortion();
+ BOOL bFound = sal_False;
+ SwTwips nDiff = 0;
+ while( pPor)
+ {
+ if( pPor->IsHangingPortion() )
+ {
+ nDiff = ((SwHangingPortion*)pPor)->GetInnerWidth() - pPor->Width();
+ if( nDiff )
+ bFound = sal_True;
+ }
+ // the last post its portion
+ else if ( pPor->IsPostItsPortion() && ! pPor->GetPortion() )
+ nDiff = nAscent;
+
+ pPor = pPor->GetPortion();
+ }
+ if( !bFound ) // actualize the hanging-flag
+ ((SwLineLayout*)this)->SetHanging( sal_False );
+ return nDiff;
+}
+
+SwTwips SwTxtFrm::HangingMargin() const
+{
+ OSL_ENSURE( HasPara(), "Don't call me without a paraportion" );
+ if( !GetPara()->IsMargin() )
+ return 0;
+ const SwLineLayout* pLine = GetPara();
+ SwTwips nRet = 0;
+ do
+ {
+ SwTwips nDiff = pLine->GetHangingMargin();
+ if( nDiff > nRet )
+ nRet = nDiff;
+ pLine = pLine->GetNext();
+ } while ( pLine );
+ if( !nRet ) // actualize the margin-flag
+ ((SwParaPortion*)GetPara())->SetMargin( sal_False );
+ return nRet;
+}
+
+
+/*************************************************************************
+ * SwScriptInfo::CalcHiddenRanges()
+ *
+ * Returns a MultiSection indicating the hidden ranges.
+ *************************************************************************/
+
+void SwScriptInfo::CalcHiddenRanges( const SwTxtNode& rNode, MultiSelection& rHiddenMulti )
+{
+ const SfxPoolItem* pItem = 0;
+ if( SFX_ITEM_SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, TRUE, &pItem ) &&
+ ((SvxCharHiddenItem*)pItem)->GetValue() )
+ {
+ rHiddenMulti.SelectAll();
+ }
+
+ const SwpHints* pHints = rNode.GetpSwpHints();
+ const SwTxtAttr* pTxtAttr = 0;
+
+ if( pHints )
+ {
+ MSHORT nTmp = 0;
+
+ while( nTmp < pHints->GetStartCount() )
+ {
+ pTxtAttr = pHints->GetStart( nTmp++ );
+ const SvxCharHiddenItem* pHiddenItem = static_cast<const SvxCharHiddenItem*>( CharFmt::GetItem( *pTxtAttr, RES_CHRATR_HIDDEN ) );
+ if( pHiddenItem )
+ {
+ xub_StrLen nSt = *pTxtAttr->GetStart();
+ xub_StrLen nEnd = *pTxtAttr->GetEnd();
+ if( nEnd > nSt )
+ {
+ Range aTmp( nSt, nEnd - 1 );
+ rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() );
+ }
+ }
+ }
+ }
+
+ // If there are any hidden ranges in the current text node, we have
+ // to unhide the redlining ranges:
+ const IDocumentRedlineAccess& rIDRA = *rNode.getIDocumentRedlineAccess();
+ if ( rHiddenMulti.GetRangeCount() && IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineMode() ) )
+ {
+ USHORT nAct = rIDRA.GetRedlinePos( rNode, USHRT_MAX );
+
+ for ( ; nAct < rIDRA.GetRedlineTbl().Count(); nAct++ )
+ {
+ const SwRedline* pRed = rIDRA.GetRedlineTbl()[ nAct ];
+
+ if ( pRed->Start()->nNode > rNode.GetIndex() )
+ break;
+
+ xub_StrLen nRedlStart;
+ xub_StrLen nRedlnEnd;
+ pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd );
+ if ( nRedlnEnd > nRedlStart )
+ {
+ Range aTmp( nRedlStart, nRedlnEnd - 1 );
+ rHiddenMulti.Select( aTmp, false );
+ }
+ }
+ }
+
+ //
+ // We calculated a lot of stuff. Finally we can update the flags at the text node.
+ //
+ const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0;
+ bool bNewHiddenCharsHidePara = false;
+ if ( bNewContainsHiddenChars )
+ {
+ const Range& rRange = rHiddenMulti.GetRange( 0 );
+ const xub_StrLen nHiddenStart = (xub_StrLen)rRange.Min();
+ const xub_StrLen nHiddenEnd = (xub_StrLen)rRange.Max() + 1;
+ bNewHiddenCharsHidePara = ( nHiddenStart == 0 && nHiddenEnd >= rNode.GetTxt().Len() );
+ }
+ rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */