LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/sw/source/core/text - porlay.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 534 970 55.1 %
Date: 2013-07-09 Functions: 41 59 69.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10