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

Generated by: LCOV version 1.11