LCOV - code coverage report
Current view: top level - libreoffice/vcl/source/gdi - sallayout.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 689 980 70.3 %
Date: 2012-12-17 Functions: 47 62 75.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : 
      21             : #include <cstdio>
      22             : 
      23             : #define _USE_MATH_DEFINES
      24             : #include <math.h>
      25             : #include <sal/alloca.h>
      26             : 
      27             : #include <salgdi.hxx>
      28             : #include <sallayout.hxx>
      29             : #include <basegfx/polygon/b2dpolypolygon.hxx>
      30             : #include <basegfx/matrix/b2dhommatrix.hxx>
      31             : #include <basegfx/matrix/b2dhommatrixtools.hxx>
      32             : 
      33             : #include <i18npool/lang.h>
      34             : 
      35             : #include <tools/debug.hxx>
      36             : 
      37             : #include <limits.h>
      38             : 
      39             : #if defined _MSC_VER
      40             : #pragma warning(push, 1)
      41             : #endif
      42             : #include <unicode/ubidi.h>
      43             : #include <unicode/uchar.h>
      44             : #if defined _MSC_VER
      45             : #pragma warning(pop)
      46             : #endif
      47             : 
      48             : #include <algorithm>
      49             : 
      50             : #ifdef DEBUG
      51             : //#define MULTI_SL_DEBUG
      52             : #endif
      53             : 
      54             : #ifdef MULTI_SL_DEBUG
      55             : #include <string>
      56             : FILE * mslLogFile = NULL;
      57             : FILE * mslLog()
      58             : {
      59             : #ifdef MSC
      60             :     std::string logFileName(getenv("TEMP"));
      61             :     logFileName.append("\\msllayout.log");
      62             :     if (mslLogFile == NULL) mslLogFile = fopen(logFileName.c_str(),"w");
      63             :     else fflush(mslLogFile);
      64             :     return mslLogFile;
      65             : #else
      66             :     return stdout;
      67             : #endif
      68             : }
      69             : #endif
      70             : // =======================================================================
      71             : 
      72             : // TODO: ask the glyph directly, for now we need this method because of #i99367#
      73             : // true if a codepoint doesn't influence the logical text width
      74        5842 : bool IsDiacritic( sal_UCS4 nChar )
      75             : {
      76             :     // shortcut abvious non-diacritics
      77        5842 :     if( nChar < 0x0300 )
      78           0 :         return false;
      79        5842 :      if( nChar >= 0x2100 )
      80           0 :         return false;
      81             : 
      82             :     // TODO: #i105058# use icu uchar.h's character classification instead of the handcrafted table
      83             :     struct DiaRange { sal_UCS4 mnMin, mnEnd;};
      84             :     static const DiaRange aRanges[] = {
      85             :         {0x0300, 0x0370},
      86             :         {0x0590, 0x05BE}, {0x05BF, 0x05C0}, {0x05C1, 0x05C3}, {0x05C4, 0x05C6}, {0x05C7, 0x05C8},
      87             :         {0x0610, 0x061B}, {0x064B, 0x0660}, {0x0670, 0x0671}, {0x06D6, 0x06DD}, {0x06DF, 0x06E5}, {0x06E7, 0x06E9}, {0x06EA,0x06EF},
      88             :         {0x0730, 0x074D}, {0x07A6, 0x07B1}, {0x07EB, 0x07F4},
      89             :         {0x1DC0, 0x1E00},
      90             :         {0x205F, 0x2070}, {0x20D0, 0x2100},
      91             :         {0xFB1E, 0xFB1F}
      92             :     };
      93             : 
      94             :     // TODO: almost anything is faster than an O(n) search
      95             :     static const int nCount = SAL_N_ELEMENTS(aRanges);
      96        5842 :     const DiaRange* pRange = &aRanges[0];
      97       79786 :     for( int i = nCount; --i >= 0; ++pRange )
      98       76996 :         if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
      99        3052 :             return true;
     100             : 
     101        2790 :     return false;
     102             : }
     103             : 
     104             : // =======================================================================
     105             : 
     106           0 : int GetVerticalFlags( sal_UCS4 nChar )
     107             : {
     108           0 :     if( (nChar >= 0x1100 && nChar <= 0x11f9)    // Hangul Jamo
     109             :      || (nChar == 0x2030 || nChar == 0x2031)    // per mille sign
     110             :      || (nChar >= 0x3000 && nChar <= 0xfaff)    // unified CJK
     111             :      || (nChar >= 0xfe20 && nChar <= 0xfe6f)    // CJK compatibility
     112             :      || (nChar >= 0xff00 && nChar <= 0xfffd) )  // other CJK
     113             :     {
     114             :         /* #i52932# remember:
     115             :          nChar == 0x2010 || nChar == 0x2015
     116             :          nChar == 0x2016 || nChar == 0x2026
     117             :          are GF_NONE also, but already handled in the outer if condition
     118             :         */
     119           0 :         if((nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012)
     120             :         || (nChar == 0xFF3B || nChar == 0xFF3D)
     121             :         || (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms
     122             :         || (nChar == 0xFFE3) )
     123           0 :             return GF_NONE; // not rotated
     124           0 :         else if( nChar == 0x30fc )
     125           0 :             return GF_ROTR; // right
     126           0 :         return GF_ROTL;     // left
     127             :     }
     128           0 :     else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs
     129           0 :         return GF_ROTL; // left
     130             : 
     131           0 :     return GF_NONE; // not rotated as default
     132             : }
     133             : 
     134             : // -----------------------------------------------------------------------
     135             : 
     136           0 : sal_UCS4 GetVerticalChar( sal_UCS4 )
     137             : {
     138           0 :     return 0; // #i14788# input method is responsible vertical char changes
     139             : }
     140             : 
     141             : // -----------------------------------------------------------------------
     142             : 
     143           0 : VCL_DLLPUBLIC sal_UCS4 GetMirroredChar( sal_UCS4 nChar )
     144             : {
     145           0 :     nChar = u_charMirror( nChar );
     146           0 :     return nChar;
     147             : }
     148             : 
     149             : // -----------------------------------------------------------------------
     150             : 
     151       23639 : sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
     152             : {
     153             :     // currently only conversion from ASCII digits is interesting
     154       23639 :     if( (nChar < '0') || ('9' < nChar) )
     155           0 :         return nChar;
     156             : 
     157             :     int nOffset;
     158             :     // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
     159             :     // CAVEAT! To some like Mongolian MS assigned the same primary language
     160             :     // although the script type is different!
     161       23639 :     switch( eLang & LANGUAGE_MASK_PRIMARY )
     162             :     {
     163             :         default:
     164       23639 :             nOffset = 0;
     165       23639 :             break;
     166             :         case LANGUAGE_ARABIC_SAUDI_ARABIA  & LANGUAGE_MASK_PRIMARY:
     167           0 :             nOffset = 0x0660 - '0';  // arabic-indic digits
     168           0 :             break;
     169             :         case LANGUAGE_FARSI         & LANGUAGE_MASK_PRIMARY:
     170             :         case LANGUAGE_URDU          & LANGUAGE_MASK_PRIMARY:
     171             :         case LANGUAGE_PUNJABI       & LANGUAGE_MASK_PRIMARY: //???
     172             :         case LANGUAGE_SINDHI        & LANGUAGE_MASK_PRIMARY:
     173           0 :             nOffset = 0x06F0 - '0';  // eastern arabic-indic digits
     174           0 :             break;
     175             :         case LANGUAGE_BENGALI       & LANGUAGE_MASK_PRIMARY:
     176           0 :             nOffset = 0x09E6 - '0';  // bengali
     177           0 :             break;
     178             :         case LANGUAGE_HINDI         & LANGUAGE_MASK_PRIMARY:
     179           0 :             nOffset = 0x0966 - '0';  // devanagari
     180           0 :             break;
     181             :         case LANGUAGE_AMHARIC_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
     182             :         case LANGUAGE_TIGRIGNA_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
     183             :         // TODO case:
     184           0 :             nOffset = 0x1369 - '0';  // ethiopic
     185           0 :             break;
     186             :         case LANGUAGE_GUJARATI      & LANGUAGE_MASK_PRIMARY:
     187           0 :             nOffset = 0x0AE6 - '0';  // gujarati
     188           0 :             break;
     189             : #ifdef LANGUAGE_GURMUKHI // TODO case:
     190             :         case LANGUAGE_GURMUKHI      & LANGUAGE_MASK_PRIMARY:
     191             :             nOffset = 0x0A66 - '0';  // gurmukhi
     192             :             break;
     193             : #endif
     194             :         case LANGUAGE_KANNADA       & LANGUAGE_MASK_PRIMARY:
     195           0 :             nOffset = 0x0CE6 - '0';  // kannada
     196           0 :             break;
     197             :         case LANGUAGE_KHMER         & LANGUAGE_MASK_PRIMARY:
     198           0 :             nOffset = 0x17E0 - '0';  // khmer
     199           0 :             break;
     200             :         case LANGUAGE_LAO           & LANGUAGE_MASK_PRIMARY:
     201           0 :             nOffset = 0x0ED0 - '0';  // lao
     202           0 :             break;
     203             :         case LANGUAGE_MALAYALAM     & LANGUAGE_MASK_PRIMARY:
     204           0 :             nOffset = 0x0D66 - '0';  // malayalam
     205           0 :             break;
     206             :         case LANGUAGE_MONGOLIAN     & LANGUAGE_MASK_PRIMARY:
     207           0 :             if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
     208           0 :                 nOffset = 0x1810 - '0';   // mongolian
     209             :             else
     210           0 :                 nOffset = 0;              // mongolian cyrillic
     211           0 :             break;
     212             :         case LANGUAGE_BURMESE       & LANGUAGE_MASK_PRIMARY:
     213           0 :             nOffset = 0x1040 - '0';  // myanmar
     214           0 :             break;
     215             :         case LANGUAGE_ORIYA         & LANGUAGE_MASK_PRIMARY:
     216           0 :             nOffset = 0x0B66 - '0';  // oriya
     217           0 :             break;
     218             :         case LANGUAGE_TAMIL         & LANGUAGE_MASK_PRIMARY:
     219           0 :             nOffset = 0x0BE7 - '0';  // tamil
     220           0 :             break;
     221             :         case LANGUAGE_TELUGU        & LANGUAGE_MASK_PRIMARY:
     222           0 :             nOffset = 0x0C66 - '0';  // telugu
     223           0 :             break;
     224             :         case LANGUAGE_THAI          & LANGUAGE_MASK_PRIMARY:
     225           0 :             nOffset = 0x0E50 - '0';  // thai
     226           0 :             break;
     227             :         case LANGUAGE_TIBETAN       & LANGUAGE_MASK_PRIMARY:
     228           0 :             nOffset = 0x0F20 - '0';  // tibetan
     229           0 :             break;
     230             :     }
     231             : 
     232       23639 :     nChar += nOffset;
     233       23639 :     return nChar;
     234             : }
     235             : 
     236             : // -----------------------------------------------------------------------
     237             : 
     238      506760 : inline bool IsControlChar( sal_UCS4 cChar )
     239             : {
     240             :     // C0 control characters
     241      506760 :     if( (0x0001 <= cChar) && (cChar <= 0x001F) )
     242           4 :         return true;
     243             :     // formatting characters
     244      506756 :     if( (0x200E <= cChar) && (cChar <= 0x200F) )
     245           0 :         return true;
     246      506756 :     if( (0x2028 <= cChar) && (cChar <= 0x202E) )
     247           0 :         return true;
     248             :     // deprecated formatting characters
     249      506756 :     if( (0x206A <= cChar) && (cChar <= 0x206F) )
     250           0 :         return true;
     251      506756 :     if( (0x2060 == cChar) )
     252           0 :         return true;
     253             :     // byte order markers and invalid unicode
     254      506756 :     if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
     255           0 :         return true;
     256      506756 :     return false;
     257             : }
     258             : 
     259             : // =======================================================================
     260             : 
     261       14000 : bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
     262             : {
     263             :     // check if charpos could extend current run
     264       14000 :     int nIndex = maRuns.size();
     265       14000 :     if( nIndex >= 2 )
     266             :     {
     267         620 :         int nRunPos0 = maRuns[ nIndex-2 ];
     268         620 :         int nRunPos1 = maRuns[ nIndex-1 ];
     269         620 :         if( ((nCharPos + bRTL) == nRunPos1)
     270             :     &&  ((nRunPos0 > nRunPos1) == bRTL) )
     271             :         {
     272             :             // extend current run by new charpos
     273         592 :             maRuns[ nIndex-1 ] = nCharPos + !bRTL;
     274         592 :             return false;
     275             :         }
     276             :         // ignore new charpos when it is in current run
     277          28 :         if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
     278           0 :             return false;
     279          28 :         if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
     280           0 :             return false;
     281             :     }
     282             : 
     283             :     // else append a new run consisting of the new charpos
     284       13408 :     maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
     285       13408 :     maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
     286       13408 :     return true;
     287             : }
     288             : 
     289             : // -----------------------------------------------------------------------
     290             : 
     291      114095 : bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
     292             : {
     293      114095 :     if( nCharPos0 == nCharPos1 )
     294           2 :         return false;
     295             : 
     296             :     // swap if needed
     297      114093 :     if( bRTL == (nCharPos0 < nCharPos1) )
     298             :     {
     299          16 :         int nTemp = nCharPos0;
     300          16 :         nCharPos0 = nCharPos1;
     301          16 :         nCharPos1 = nTemp;
     302             :     }
     303             : 
     304             :     // append new run
     305      114093 :     maRuns.push_back( nCharPos0 );
     306      114093 :     maRuns.push_back( nCharPos1 );
     307      114093 :     return true;
     308             : }
     309             : 
     310             : // -----------------------------------------------------------------------
     311             : 
     312       14098 : bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
     313             : {
     314       14098 :     if( mnRunIndex >= (int)maRuns.size() )
     315          14 :         return false;
     316             : 
     317       14084 :     int nMinCharPos = maRuns[ mnRunIndex+0 ];
     318       14084 :     int nEndCharPos = maRuns[ mnRunIndex+1 ];
     319       14084 :     if( nMinCharPos > nEndCharPos ) // reversed in RTL case
     320             :     {
     321         336 :         int nTemp = nMinCharPos;
     322         336 :         nMinCharPos = nEndCharPos;
     323         336 :         nEndCharPos = nTemp;
     324             :     }
     325             : 
     326       14084 :     if( nCharPos < nMinCharPos )
     327          56 :         return false;
     328       14028 :     if( nCharPos >= nEndCharPos )
     329          14 :         return false;
     330       14014 :     return true;
     331             : }
     332             : 
     333       20422 : bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
     334             : {
     335       20422 :     bool bRet = false;
     336       20422 :     int nRunIndex = mnRunIndex;
     337             : 
     338       20422 :     ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
     339             : 
     340       20422 :     pThis->ResetPos();
     341             : 
     342       20464 :     for (size_t i = 0; i < maRuns.size(); i+=2)
     343             :     {
     344        6746 :         if( (bRet = PosIsInRun( nCharPos )) == true )
     345        6704 :             break;
     346          42 :         pThis->NextRun();
     347             :     }
     348             : 
     349       20422 :     pThis->mnRunIndex = nRunIndex;
     350       20422 :     return bRet;
     351             : }
     352             : 
     353             : 
     354             : // -----------------------------------------------------------------------
     355             : 
     356       34078 : bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
     357             : {
     358             :     // negative nCharPos => reset to first run
     359       34078 :     if( *nCharPos < 0 )
     360        8169 :         mnRunIndex = 0;
     361             : 
     362             :     // return false when all runs completed
     363       34078 :     if( mnRunIndex >= (int)maRuns.size() )
     364           0 :         return false;
     365             : 
     366       34078 :     int nRunPos0 = maRuns[ mnRunIndex+0 ];
     367       34078 :     int nRunPos1 = maRuns[ mnRunIndex+1 ];
     368       34078 :     *bRightToLeft = (nRunPos0 > nRunPos1);
     369             : 
     370       34078 :     if( *nCharPos < 0 )
     371             :     {
     372             :         // get first valid nCharPos in run
     373        8169 :         *nCharPos = nRunPos0;
     374             :     }
     375             :     else
     376             :     {
     377             :         // advance to next nCharPos for LTR case
     378       25909 :         if( !*bRightToLeft )
     379       25671 :             ++(*nCharPos);
     380             : 
     381             :         // advance to next run if current run is completed
     382       25909 :         if( *nCharPos == nRunPos1 )
     383             :         {
     384        8189 :             if( (mnRunIndex += 2) >= (int)maRuns.size() )
     385        8167 :                 return false;
     386          22 :             nRunPos0 = maRuns[ mnRunIndex+0 ];
     387          22 :             nRunPos1 = maRuns[ mnRunIndex+1 ];
     388          22 :             *bRightToLeft = (nRunPos0 > nRunPos1);
     389          22 :             *nCharPos = nRunPos0;
     390             :         }
     391             :     }
     392             : 
     393             :     // advance to next nCharPos for RTL case
     394       25911 :     if( *bRightToLeft )
     395         238 :         --(*nCharPos);
     396             : 
     397       25911 :     return true;
     398             : }
     399             : 
     400             : // -----------------------------------------------------------------------
     401             : 
     402      292928 : bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
     403             : {
     404      292928 :     if( mnRunIndex >= (int)maRuns.size() )
     405      143097 :         return false;
     406             : 
     407      149831 :     int nRunPos0 = maRuns[ mnRunIndex+0 ];
     408      149831 :     int nRunPos1 = maRuns[ mnRunIndex+1 ];
     409      149831 :     *bRightToLeft = (nRunPos1 < nRunPos0) ;
     410      149831 :     if( !*bRightToLeft )
     411             :     {
     412      149703 :         *nMinRunPos = nRunPos0;
     413      149703 :         *nEndRunPos = nRunPos1;
     414             :     }
     415             :     else
     416             :     {
     417         128 :         *nMinRunPos = nRunPos1;
     418         128 :         *nEndRunPos = nRunPos0;
     419             :     }
     420      149831 :     return true;
     421             : }
     422             : 
     423             : // =======================================================================
     424             : 
     425      114091 : ImplLayoutArgs::ImplLayoutArgs( const sal_Unicode* pStr, int nLen,
     426             :     int nMinCharPos, int nEndCharPos, int nFlags )
     427             : :
     428             :     mnFlags( nFlags ),
     429             :     mnLength( nLen ),
     430             :     mnMinCharPos( nMinCharPos ),
     431             :     mnEndCharPos( nEndCharPos ),
     432             :     mpStr( pStr ),
     433             :     mpDXArray( NULL ),
     434             :     mnLayoutWidth( 0 ),
     435      114091 :     mnOrientation( 0 )
     436             : {
     437      114091 :     if( mnFlags & SAL_LAYOUT_BIDI_STRONG )
     438             :     {
     439             :         // handle strong BiDi mode
     440             : 
     441             :         // do not bother to BiDi analyze strong LTR/RTL
     442             :         // TODO: can we assume these strings do not have unicode control chars?
     443             :         //       if not remove the control characters from the runs
     444      114089 :         bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
     445      114089 :         AddRun( mnMinCharPos, mnEndCharPos, bRTL );
     446             :     }
     447             :     else
     448             :     {
     449             :         // handle weak BiDi mode
     450             : 
     451           2 :         UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
     452           2 :         if( mnFlags & SAL_LAYOUT_BIDI_RTL )
     453           0 :             nLevel = UBIDI_DEFAULT_RTL;
     454             : 
     455             :         // prepare substring for BiDi analysis
     456             :         // TODO: reuse allocated pParaBidi
     457           2 :         UErrorCode rcI18n = U_ZERO_ERROR;
     458           2 :         UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
     459           2 :         if( !pParaBidi )
     460      114091 :             return;
     461           2 :         ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n );    // UChar != sal_Unicode in MinGW
     462             : 
     463           2 :         UBiDi* pLineBidi = pParaBidi;
     464           2 :         int nSubLength = mnEndCharPos - mnMinCharPos;
     465           2 :         if( nSubLength != mnLength )
     466             :         {
     467           0 :             pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
     468           0 :             ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
     469             :         }
     470             : 
     471             :         // run BiDi algorithm
     472           2 :         const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
     473             :         //maRuns.resize( 2 * nRunCount );
     474           4 :         for( int i = 0; i < nRunCount; ++i )
     475             :         {
     476             :             int32_t nMinPos, nLength;
     477           2 :             const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
     478           2 :             const int nPos0 = nMinPos + mnMinCharPos;
     479           2 :             const int nPos1 = nPos0 + nLength;
     480             : 
     481           2 :             const bool bRTL = (nDir == UBIDI_RTL);
     482           2 :             AddRun( nPos0, nPos1, bRTL );
     483             :         }
     484             : 
     485             :         // cleanup BiDi engine
     486           2 :         if( pLineBidi != pParaBidi )
     487           0 :             ubidi_close( pLineBidi );
     488           2 :         ubidi_close( pParaBidi );
     489             :     }
     490             : 
     491             :     // prepare calls to GetNextPos/GetNextRun
     492      114091 :     maRuns.ResetPos();
     493             : }
     494             : 
     495             : // -----------------------------------------------------------------------
     496             : 
     497             : // add a run after splitting it up to get rid of control chars
     498      114091 : void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
     499             : {
     500             :     DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
     501             : 
     502             :     // remove control characters from runs by splitting them up
     503      114091 :     if( !bRTL )
     504             :     {
     505      620687 :         for( int i = nCharPos0; i < nCharPos1; ++i )
     506      506612 :             if( IsControlChar( mpStr[i] ) )
     507             :             {
     508             :                 // add run until control char
     509           4 :                 maRuns.AddRun( nCharPos0, i, bRTL );
     510           4 :                 nCharPos0 = i + 1;
     511             :             }
     512             :     }
     513             :     else
     514             :     {
     515         180 :         for( int i = nCharPos1; --i >= nCharPos0; )
     516         148 :             if( IsControlChar( mpStr[i] ) )
     517             :             {
     518             :                 // add run until control char
     519           0 :                 maRuns.AddRun( i+1, nCharPos1, bRTL );
     520           0 :                 nCharPos1 = i;
     521             :             }
     522             :     }
     523             : 
     524             :     // add remainder of run
     525      114091 :     maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
     526      114091 : }
     527             : 
     528             : // -----------------------------------------------------------------------
     529             : 
     530       13380 : bool ImplLayoutArgs::PrepareFallback()
     531             : {
     532             :     // short circuit if no fallback is needed
     533       13380 :     if( maReruns.IsEmpty() )
     534             :     {
     535        6690 :         maRuns.Clear();
     536        6690 :         return false;
     537             :     }
     538             : 
     539             :     // convert the fallback requests to layout requests
     540             :     bool bRTL;
     541             :     int nMin, nEnd;
     542             : 
     543             :     // get the individual fallback requests
     544             :     typedef std::vector<int> IntVector;
     545        6690 :     IntVector aPosVector;
     546        6690 :     aPosVector.reserve( mnLength );
     547        6690 :     maReruns.ResetPos();
     548       13394 :     for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() )
     549       13704 :         for( int i = nMin; i < nEnd; ++i )
     550        7000 :             aPosVector.push_back( i );
     551        6690 :     maReruns.Clear();
     552             : 
     553             :     // sort the individual fallback requests
     554        6690 :     std::sort( aPosVector.begin(), aPosVector.end() );
     555             : 
     556             :     // adjust fallback runs to have the same order and limits of the original runs
     557        6690 :     ImplLayoutRuns aNewRuns;
     558        6690 :     maRuns.ResetPos();
     559       13380 :     for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
     560             :     {
     561        6690 :         if( !bRTL) {
     562        6676 :             IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
     563       13550 :             for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
     564        6874 :                 aNewRuns.AddPos( *it, bRTL );
     565             :         } else {
     566          14 :             IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
     567         154 :             while( (it != aPosVector.begin()) && (*--it >= nMin) )
     568         126 :                 aNewRuns.AddPos( *it, bRTL );
     569             :         }
     570             :     }
     571             : 
     572        6690 :     maRuns = aNewRuns;  // TODO: use vector<>::swap()
     573        6690 :     maRuns.ResetPos();
     574        6690 :     return true;
     575             : }
     576             : 
     577             : // -----------------------------------------------------------------------
     578             : 
     579      259450 : bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
     580             : {
     581      259450 :     bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
     582      259450 :     maRuns.NextRun();
     583      259450 :     return bValid;
     584             : }
     585             : 
     586             : // =======================================================================
     587             : 
     588      129717 : SalLayout::SalLayout()
     589             : :   mnMinCharPos( -1 ),
     590             :     mnEndCharPos( -1 ),
     591             :     mnLayoutFlags( 0 ),
     592             :     mnUnitsPerPixel( 1 ),
     593             :     mnOrientation( 0 ),
     594             :     mnRefCount( 1 ),
     595      129717 :     maDrawOffset( 0, 0 )
     596      129717 : {}
     597             : 
     598             : // -----------------------------------------------------------------------
     599             : 
     600      129717 : SalLayout::~SalLayout()
     601      129717 : {}
     602             : 
     603             : // -----------------------------------------------------------------------
     604             : 
     605      134965 : void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
     606             : {
     607      134965 :     mnMinCharPos  = rArgs.mnMinCharPos;
     608      134965 :     mnEndCharPos  = rArgs.mnEndCharPos;
     609      134965 :     mnLayoutFlags = rArgs.mnFlags;
     610      134965 :     mnOrientation = rArgs.mnOrientation;
     611      134965 : }
     612             : 
     613             : // -----------------------------------------------------------------------
     614             : 
     615      129717 : void SalLayout::Release() const
     616             : {
     617             :     // TODO: protect when multiple threads can access this
     618      129717 :     if( --mnRefCount > 0 )
     619      129717 :         return;
     620             :     // const_cast because some compilers violate ANSI C++ spec
     621      129717 :     delete const_cast<SalLayout*>(this);
     622             : }
     623             : 
     624             : // -----------------------------------------------------------------------
     625             : 
     626      150739 : Point SalLayout::GetDrawPosition( const Point& rRelative ) const
     627             : {
     628      150739 :     Point aPos = maDrawBase;
     629      150739 :     Point aOfs = rRelative + maDrawOffset;
     630             : 
     631      150739 :     if( mnOrientation == 0 )
     632      124504 :         aPos += aOfs;
     633             :     else
     634             :     {
     635             :         // cache trigonometric results
     636             :         static int nOldOrientation = 0;
     637             :         static double fCos = 1.0, fSin = 0.0;
     638       26235 :         if( nOldOrientation != mnOrientation )
     639             :         {
     640          18 :             nOldOrientation = mnOrientation;
     641          18 :             double fRad = mnOrientation * (M_PI / 1800.0);
     642          18 :             fCos = cos( fRad );
     643          18 :             fSin = sin( fRad );
     644             :         }
     645             : 
     646       26235 :         double fX = aOfs.X();
     647       26235 :         double fY = aOfs.Y();
     648       26235 :         long nX = static_cast<long>( +fCos * fX + fSin * fY );
     649       26235 :         long nY = static_cast<long>( +fCos * fY - fSin * fX );
     650       26235 :         aPos += Point( nX, nY );
     651             :     }
     652             : 
     653      150739 :     return aPos;
     654             : }
     655             : 
     656             : // -----------------------------------------------------------------------
     657             : 
     658             : // returns asian kerning values in quarter of character width units
     659             : // to enable automatic halfwidth substitution for fullwidth punctuation
     660             : // return value is negative for l, positive for r, zero for neutral
     661             : 
     662             : // If the range doesn't match in 0x3000 and 0x30FB, please change
     663             : // also ImplCalcKerning.
     664             : 
     665           0 : int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
     666             : {
     667             :     // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
     668             :     static signed char nTable[0x30] =
     669             :     {
     670             :          0, -2, -2,  0,   0,  0,  0,  0,  +2, -2, +2, -2,  +2, -2, +2, -2,
     671             :         +2, -2,  0,  0,  +2, -2, +2, -2,   0,  0,  0,  0,   0, +2, -2, -2,
     672             :          0,  0,  0,  0,   0,  0,  0,  0,   0,  0, -2, -2,  +2, +2, -2, -2
     673             :     };
     674             : 
     675           0 :     int nResult = 0;
     676           0 :     if( (c >= 0x3000) && (c < 0x3030) )
     677           0 :         nResult = nTable[ c - 0x3000 ];
     678           0 :     else switch( c )
     679             :     {
     680             :         case 0x30FB:
     681           0 :             nResult = bLeft ? -1 : +1;      // 25% left/right/top/bottom
     682           0 :             break;
     683             :         case 0x2019: case 0x201D:
     684             :         case 0xFF01: case 0xFF09: case 0xFF0C:
     685             :         case 0xFF1A: case 0xFF1B:
     686           0 :             nResult = -2;
     687           0 :             break;
     688             :         case 0x2018: case 0x201C:
     689             :         case 0xFF08:
     690           0 :             nResult = +2;
     691           0 :             break;
     692             :         default:
     693           0 :             break;
     694             :     }
     695             : 
     696           0 :     return nResult;
     697             : }
     698             : 
     699             : // -----------------------------------------------------------------------
     700             : 
     701         330 : bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
     702             :     ::basegfx::B2DPolyPolygonVector& rVector ) const
     703             : {
     704         330 :     bool bAllOk = true;
     705         330 :     bool bOneOk = false;
     706             : 
     707         330 :     Point aPos;
     708         330 :     ::basegfx::B2DPolyPolygon aGlyphOutline;
     709         568 :     for( int nStart = 0;;)
     710             :     {
     711             :         sal_GlyphId nLGlyph;
     712         568 :         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
     713             :             break;
     714             : 
     715             :         // get outline of individual glyph, ignoring "empty" glyphs
     716         238 :         bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
     717         238 :         bAllOk &= bSuccess;
     718         238 :         bOneOk |= bSuccess;
     719             :         // only add non-empty outlines
     720         238 :         if( bSuccess && (aGlyphOutline.count() > 0) )
     721             :         {
     722         146 :             if( aPos.X() || aPos.Y() )
     723             :             {
     724           0 :                 aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
     725             :             }
     726             : 
     727             :             // insert outline at correct position
     728         146 :             rVector.push_back( aGlyphOutline );
     729             :         }
     730             :     }
     731             : 
     732         330 :     return (bAllOk && bOneOk);
     733             : }
     734             : 
     735             : // -----------------------------------------------------------------------
     736             : 
     737       38226 : bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
     738             : {
     739       38226 :     bool bRet = false;
     740       38226 :     rRect.SetEmpty();
     741             : 
     742       38226 :     Point aPos;
     743       38226 :     Rectangle aRectangle;
     744       87774 :     for( int nStart = 0;;)
     745             :     {
     746             :         sal_GlyphId nLGlyph;
     747       87774 :         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
     748             :             break;
     749             : 
     750             :         // get bounding rectangle of individual glyph
     751       49548 :         if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
     752             :         {
     753             :             // merge rectangle
     754       49548 :             aRectangle += aPos;
     755       49548 :             if (rRect.IsEmpty())
     756       36562 :                 rRect = aRectangle;
     757             :             else
     758       12986 :                 rRect.Union(aRectangle);
     759       49548 :             bRet = true;
     760             :         }
     761             :     }
     762             : 
     763       38226 :     return bRet;
     764             : }
     765             : 
     766             : // -----------------------------------------------------------------------
     767             : 
     768           0 : bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const
     769             : {
     770           0 :     bool bRet = false;
     771           0 :     if( nGlyph & GF_ISCHAR )
     772             :     {
     773           0 :         long nChar = nGlyph & GF_IDXMASK;
     774             :         bRet = (nChar <= 0x0020)                    // blank
     775             :             //|| (nChar == 0x00A0)                  // non breaking space
     776             :             || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
     777           0 :             || (nChar == 0x3000);                   // ideographic space
     778             :     }
     779             :     else
     780           0 :         bRet = ((nGlyph & GF_IDXMASK) == 3);
     781           0 :     return bRet;
     782             : }
     783             : 
     784             : // -----------------------------------------------------------------------
     785             : 
     786           0 : const PhysicalFontFace* SalLayout::GetFallbackFontData( sal_GlyphId /*nGlyphId*/ ) const
     787             : {
     788           0 :     return NULL;
     789             : }
     790             : 
     791             : // =======================================================================
     792             : 
     793      123027 : GenericSalLayout::GenericSalLayout()
     794      123027 : {}
     795             : 
     796             : // -----------------------------------------------------------------------
     797             : 
     798      123027 : GenericSalLayout::~GenericSalLayout()
     799      123027 : {}
     800             : 
     801             : // -----------------------------------------------------------------------
     802             : 
     803      515976 : void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
     804             : {
     805      515976 :     m_GlyphItems.push_back(rGlyphItem);
     806      515976 : }
     807             : 
     808             : // -----------------------------------------------------------------------
     809             : 
     810       27111 : bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
     811             : {
     812             :     // initialize character extents buffer
     813       27111 :     int nCharCount = mnEndCharPos - mnMinCharPos;
     814      239200 :     for( int n = 0; n < nCharCount; ++n )
     815      212089 :         pCharWidths[n] = 0;
     816             : 
     817             :     // determine cluster extents
     818      238880 :     for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), end = m_GlyphItems.end(); pG != end ; ++pG)
     819             :     {
     820             :         // use cluster start to get char index
     821      211769 :         if( !pG->IsClusterStart() )
     822           0 :             continue;
     823             : 
     824      211769 :         int n = pG->mnCharPos;
     825      211769 :         if( n >= mnEndCharPos )
     826           0 :             continue;
     827      211769 :         n -= mnMinCharPos;
     828      211769 :         if( n < 0 )
     829           0 :             continue;
     830             : 
     831             :         // left glyph in cluster defines default extent
     832      211769 :         long nXPosMin = pG->maLinearPos.X();
     833      211769 :         long nXPosMax = nXPosMin + pG->mnNewWidth;
     834             : 
     835             :         // calculate right x-position for this glyph cluster
     836             :         // break if no more glyphs in layout
     837             :         // break at next glyph cluster start
     838      423538 :         while( (pG+1 != end) && !pG[1].IsClusterStart() )
     839             :         {
     840             :             // advance to next glyph in cluster
     841           0 :             ++pG;
     842             : 
     843           0 :             if( pG->IsDiacritic() )
     844           0 :                 continue; // ignore diacritics
     845             :             // get leftmost x-extent of this glyph
     846           0 :             long nXPos = pG->maLinearPos.X();
     847           0 :             if( nXPosMin > nXPos )
     848           0 :                 nXPosMin = nXPos;
     849             : 
     850             :             // get rightmost x-extent of this glyph
     851           0 :             nXPos += pG->mnNewWidth;
     852           0 :             if( nXPosMax < nXPos )
     853           0 :                 nXPosMax = nXPos;
     854             :         }
     855             : 
     856             :         // when the current cluster overlaps with the next one assume
     857             :         // rightmost cluster edge is the leftmost edge of next cluster
     858             :         // for clusters that do not have x-sorted glyphs
     859             :         // TODO: avoid recalculation of left bound in next cluster iteration
     860      423538 :         for( GlyphVector::const_iterator pN = pG; ++pN != end; )
     861             :         {
     862      184898 :             if( pN->IsClusterStart() )
     863      184898 :                 break;
     864           0 :             if( pN->IsDiacritic() )
     865           0 :                 continue;   // ignore diacritics
     866           0 :             if( nXPosMax > pN->maLinearPos.X() )
     867           0 :                 nXPosMax = pN->maLinearPos.X();
     868             :         }
     869      211769 :         if( nXPosMax < nXPosMin )
     870           0 :             nXPosMin = nXPosMax = 0;
     871             : 
     872             :         // character width is sum of glyph cluster widths
     873      211769 :         pCharWidths[n] += nXPosMax - nXPosMin;
     874             :     }
     875             : 
     876             :     // TODO: distribute the cluster width proportionally to the characters
     877             :     // clusters (e.g. ligatures) correspond to more than one char index,
     878             :     // so some character widths are still uninitialized. This is solved
     879             :     // by setting the first charwidth of the cluster to the cluster width
     880             : 
     881       27111 :     return true;
     882             : }
     883             : 
     884             : // -----------------------------------------------------------------------
     885             : 
     886       64009 : long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
     887             : {
     888       64009 :     if( pCharWidths )
     889       26746 :         if( !GetCharWidths( pCharWidths ) )
     890           0 :             return 0;
     891             : 
     892       64009 :     long nWidth = GetTextWidth();
     893       64009 :     return nWidth;
     894             : }
     895             : 
     896             : // -----------------------------------------------------------------------
     897             : 
     898             : // the text width is the maximum logical extent of all glyphs
     899       68342 : long GenericSalLayout::GetTextWidth() const
     900             : {
     901       68342 :     if( m_GlyphItems.empty() )
     902        4328 :         return 0;
     903             : 
     904             :     // initialize the extent
     905       64014 :     long nMinPos = 0;
     906       64014 :     long nMaxPos = 0;
     907             : 
     908      448473 :     for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), end = m_GlyphItems.end(); pG != end ; ++pG )
     909             :     {
     910             :         // update the text extent with the glyph extent
     911      384459 :         long nXPos = pG->maLinearPos.X();
     912      384459 :         if( nMinPos > nXPos )
     913           0 :             nMinPos = nXPos;
     914      384459 :         nXPos += pG->mnNewWidth;
     915      384459 :         if( nMaxPos < nXPos )
     916      383555 :             nMaxPos = nXPos;
     917             :     }
     918             : 
     919       64014 :     long nWidth = nMaxPos - nMinPos;
     920       64014 :     return nWidth;
     921             : }
     922             : 
     923             : // -----------------------------------------------------------------------
     924             : 
     925      127399 : void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
     926             : {
     927      127399 :     SalLayout::AdjustLayout( rArgs );
     928             : 
     929      127399 :     if( rArgs.mpDXArray )
     930        1407 :         ApplyDXArray( rArgs );
     931      125992 :     else if( rArgs.mnLayoutWidth )
     932        4128 :         Justify( rArgs.mnLayoutWidth );
     933      127399 : }
     934             : 
     935             : // -----------------------------------------------------------------------
     936             : 
     937        1407 : void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
     938             : {
     939        1407 :     if( m_GlyphItems.empty())
     940             :         return;
     941             : 
     942             :     // determine cluster boundaries and x base offset
     943        1407 :     const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
     944        1407 :     int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
     945             :     size_t i;
     946             :     int n,p;
     947        1407 :     long nBasePointX = -1;
     948        1407 :     if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
     949         196 :         nBasePointX = 0;
     950       20254 :     for(p = 0; p < nCharCount; ++p )
     951       18847 :         pLogCluster[ p ] = -1;
     952             : 
     953       20246 :     for( i = 0; i < m_GlyphItems.size(); ++i)
     954             :     {
     955       18839 :         n = m_GlyphItems[i].mnCharPos - rArgs.mnMinCharPos;
     956       18839 :         if( (n < 0) || (nCharCount <= n) )
     957           0 :             continue;
     958       18839 :         if( pLogCluster[ n ] < 0 )
     959       18839 :             pLogCluster[ n ] = i;
     960       18839 :         if( nBasePointX < 0 )
     961        1211 :             nBasePointX = m_GlyphItems[i].maLinearPos.X();
     962             :     }
     963             :     // retarget unresolved pLogCluster[n] to a glyph inside the cluster
     964             :     // TODO: better do it while the deleted-glyph markers are still there
     965        1407 :     for( n = 0; n < nCharCount; ++n )
     966        1407 :         if( (p = pLogCluster[0]) >= 0 )
     967        1407 :             break;
     968        1407 :     if( n >= nCharCount )
     969             :         return;
     970       20254 :     for( n = 0; n < nCharCount; ++n )
     971             :     {
     972       18847 :         if( pLogCluster[ n ] < 0 )
     973           8 :             pLogCluster[ n ] = p;
     974             :         else
     975       18839 :             p = pLogCluster[ n ];
     976             :     }
     977             : 
     978             :     // calculate adjusted cluster widths
     979        1407 :     sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( m_GlyphItems.size() * sizeof(long) );
     980       20246 :     for( i = 0; i < m_GlyphItems.size(); ++i )
     981       18839 :         pNewGlyphWidths[ i ] = 0;
     982             : 
     983             :     bool bRTL;
     984       21653 :     for( int nCharPos = p = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
     985             :     {
     986       18839 :         n = nCharPos - rArgs.mnMinCharPos;
     987       18839 :         if( (n < 0) || (nCharCount <= n) )  continue;
     988             : 
     989       18839 :         if( pLogCluster[ n ] >= 0 )
     990       18839 :             p = pLogCluster[ n ];
     991       18839 :         if( p >= 0 )
     992             :         {
     993       18839 :             long nDelta = rArgs.mpDXArray[ n ] ;
     994       18839 :             if( n > 0 )
     995       17432 :                 nDelta -= rArgs.mpDXArray[ n-1 ];
     996       18839 :             pNewGlyphWidths[ p ] += nDelta * mnUnitsPerPixel;
     997             :         }
     998             :     }
     999             : 
    1000             :     // move cluster positions using the adjusted widths
    1001        1407 :     long nDelta = 0;
    1002        1407 :     long nNewPos = 0;
    1003       20246 :     for( i = 0; i < m_GlyphItems.size(); ++i)
    1004             :     {
    1005       18839 :         if( m_GlyphItems[i].IsClusterStart() )
    1006             :         {
    1007             :             // calculate original and adjusted cluster width
    1008       18839 :             int nOldClusterWidth = m_GlyphItems[i].mnNewWidth;
    1009       18839 :             int nNewClusterWidth = pNewGlyphWidths[i];
    1010             :             size_t j;
    1011       37678 :             for( j = i; ++j < m_GlyphItems.size(); )
    1012             :             {
    1013       17432 :                 if( m_GlyphItems[j].IsClusterStart() )
    1014       17432 :                     break;
    1015           0 :                 if( !m_GlyphItems[j].IsDiacritic() ) // #i99367# ignore diacritics
    1016           0 :                     nOldClusterWidth += m_GlyphItems[j].mnNewWidth;
    1017           0 :                 nNewClusterWidth += pNewGlyphWidths[j];
    1018             :             }
    1019       18839 :             const int nDiff = nNewClusterWidth - nOldClusterWidth;
    1020             : 
    1021             :             // adjust cluster glyph widths and positions
    1022       18839 :             nDelta = nBasePointX + (nNewPos - m_GlyphItems[i].maLinearPos.X());
    1023       18839 :             if( !m_GlyphItems[i].IsRTLGlyph() )
    1024             :             {
    1025             :                 // for LTR case extend rightmost glyph in cluster
    1026       18727 :                 m_GlyphItems[j - 1].mnNewWidth += nDiff;
    1027             :             }
    1028             :             else
    1029             :             {
    1030             :                 // right align cluster in new space for RTL case
    1031         112 :                 m_GlyphItems[i].mnNewWidth += nDiff;
    1032         112 :                 nDelta += nDiff;
    1033             :             }
    1034             : 
    1035       18839 :             nNewPos += nNewClusterWidth;
    1036             :         }
    1037             : 
    1038       18839 :         m_GlyphItems[i].maLinearPos.X() += nDelta;
    1039             :     }
    1040             : }
    1041             : 
    1042             : // -----------------------------------------------------------------------
    1043             : 
    1044        4128 : void GenericSalLayout::Justify( long nNewWidth )
    1045             : {
    1046        4128 :     nNewWidth *= mnUnitsPerPixel;
    1047        4128 :     int nOldWidth = GetTextWidth();
    1048        4128 :     if( !nOldWidth || nNewWidth==nOldWidth )
    1049             :         return;
    1050             : 
    1051         826 :     if(m_GlyphItems.empty())
    1052             :     {
    1053             :         return;
    1054             :     }
    1055             :     // find rightmost glyph, it won't get stretched
    1056         826 :     GlyphVector::iterator pGRight = m_GlyphItems.begin();
    1057         826 :     pGRight += m_GlyphItems.size() - 1;
    1058         826 :     GlyphVector::iterator pG;
    1059             :     // count stretchable glyphs
    1060         826 :     int nStretchable = 0;
    1061         826 :     int nMaxGlyphWidth = 0;
    1062        1060 :     for(pG = m_GlyphItems.begin(); pG != pGRight; ++pG)
    1063             :     {
    1064         234 :         if( !pG->IsDiacritic() )
    1065         234 :             ++nStretchable;
    1066         234 :         if( nMaxGlyphWidth < pG->mnOrigWidth )
    1067         126 :             nMaxGlyphWidth = pG->mnOrigWidth;
    1068             :     }
    1069             : 
    1070             :     // move rightmost glyph to requested position
    1071         826 :     nOldWidth -= pGRight->mnOrigWidth;
    1072         826 :     if( nOldWidth <= 0 )
    1073             :         return;
    1074         122 :     if( nNewWidth < nMaxGlyphWidth)
    1075           0 :         nNewWidth = nMaxGlyphWidth;
    1076         122 :     nNewWidth -= pGRight->mnOrigWidth;
    1077         122 :     pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
    1078             : 
    1079             :     // justify glyph widths and positions
    1080         122 :     int nDiffWidth = nNewWidth - nOldWidth;
    1081         122 :     if( nDiffWidth >= 0) // expanded case
    1082             :     {
    1083             :         // expand width by distributing space between glyphs evenly
    1084          58 :         int nDeltaSum = 0;
    1085         170 :         for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
    1086             :         {
    1087             :             // move glyph to justified position
    1088         112 :             pG->maLinearPos.X() += nDeltaSum;
    1089             : 
    1090             :             // do not stretch non-stretchable glyphs
    1091         112 :             if( pG->IsDiacritic() || (nStretchable <= 0) )
    1092           0 :                 continue;
    1093             : 
    1094             :             // distribute extra space equally to stretchable glyphs
    1095         112 :             int nDeltaWidth = nDiffWidth / nStretchable--;
    1096         112 :             nDiffWidth     -= nDeltaWidth;
    1097         112 :             pG->mnNewWidth += nDeltaWidth;
    1098         112 :             nDeltaSum      += nDeltaWidth;
    1099             :         }
    1100             :     }
    1101             :     else // condensed case
    1102             :     {
    1103             :         // squeeze width by moving glyphs proportionally
    1104          64 :         double fSqueeze = (double)nNewWidth / nOldWidth;
    1105          64 :         if(m_GlyphItems.size() > 1)
    1106             :         {
    1107         186 :             for( pG = m_GlyphItems.begin(); ++pG != pGRight;)
    1108             :             {
    1109          58 :                 int nX = pG->maLinearPos.X() - maBasePoint.X();
    1110          58 :                 nX = (int)(nX * fSqueeze);
    1111          58 :                 pG->maLinearPos.X() = nX + maBasePoint.X();
    1112             :             }
    1113             :         }
    1114             :         // adjust glyph widths to new positions
    1115         186 :         for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
    1116         122 :             pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X();
    1117             :     }
    1118             : }
    1119             : 
    1120             : // -----------------------------------------------------------------------
    1121             : 
    1122           0 : void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
    1123             : {
    1124           0 :     long nOffset = 0;
    1125             : 
    1126           0 :     for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1127             :     {
    1128           0 :         const int n = pG->mnCharPos;
    1129           0 :         if( n < nLength - 1)
    1130             :         {
    1131             :             // ignore code ranges that are not affected by asian punctuation compression
    1132           0 :             const sal_Unicode cHere = pStr[n];
    1133           0 :             if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
    1134           0 :                 continue;
    1135           0 :             const sal_Unicode cNext = pStr[n+1];
    1136           0 :             if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
    1137           0 :                 continue;
    1138             : 
    1139             :             // calculate compression values
    1140           0 :             const bool bVertical = false;
    1141           0 :             long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
    1142           0 :             long nKernNext  = -CalcAsianKerning( cNext, false, bVertical );
    1143             : 
    1144             :             // apply punctuation compression to logical glyph widths
    1145           0 :             long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
    1146           0 :             if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
    1147             :             {
    1148           0 :                 int nGlyphWidth = pG->mnOrigWidth;
    1149           0 :                 nDelta = (nDelta * nGlyphWidth + 2) / 4;
    1150           0 :                 if( pG+1 == pGEnd )
    1151           0 :                     pG->mnNewWidth += nDelta;
    1152           0 :                 nOffset += nDelta;
    1153             :             }
    1154             :         }
    1155             : 
    1156             :         // adjust the glyph positions to the new glyph widths
    1157           0 :         if( pG+1 != pGEnd )
    1158           0 :             pG->maLinearPos.X() += nOffset;
    1159             :     }
    1160           0 : }
    1161             : 
    1162             : // -----------------------------------------------------------------------
    1163             : 
    1164           0 : void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
    1165             : {
    1166             :     // TODO: reimplement method when container type for GlyphItems changes
    1167             : 
    1168             :     // skip if the kashida glyph in the font looks suspicious
    1169           0 :     if( nKashidaWidth <= 0 )
    1170           0 :         return;
    1171             : 
    1172             :     // calculate max number of needed kashidas
    1173           0 :     int nKashidaCount = 0;
    1174           0 :     for (GlyphVector::iterator pG = m_GlyphItems.begin();
    1175           0 :             pG != m_GlyphItems.end(); ++pG)
    1176             :     {
    1177             :         // only inject kashidas in RTL contexts
    1178           0 :         if( !pG->IsRTLGlyph() )
    1179           0 :             continue;
    1180             :         // no kashida-injection for blank justified expansion either
    1181           0 :         if( IsSpacingGlyph( pG->mnGlyphIndex ) )
    1182           0 :             continue;
    1183             : 
    1184             :         // calculate gap, ignore if too small
    1185           0 :         int nGapWidth = pG->mnNewWidth - pG->mnOrigWidth;
    1186             :         // worst case is one kashida even for mini-gaps
    1187           0 :         if( 3 * nGapWidth < nKashidaWidth )
    1188           0 :             continue;
    1189             : 
    1190           0 :         nKashidaCount = 0;
    1191           0 :         Point aPos = pG->maLinearPos;
    1192           0 :         aPos.X() -= nGapWidth; // cluster is already right aligned
    1193           0 :         int const nCharPos = pG->mnCharPos;
    1194           0 :         GlyphVector::iterator pG2 = pG;
    1195           0 :         for(; nGapWidth > nKashidaWidth; nGapWidth -= nKashidaWidth, ++nKashidaCount )
    1196             :         {
    1197             :             pG2 = m_GlyphItems.insert(pG2, GlyphItem(nCharPos, nKashidaIndex, aPos,
    1198           0 :                                                       GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth ));
    1199           0 :             ++pG2;
    1200           0 :             aPos.X() += nKashidaWidth;
    1201             :         }
    1202             : 
    1203             :         // fixup rightmost kashida for gap remainder
    1204           0 :         if( nGapWidth > 0 )
    1205             :         {
    1206             :             pG2 = m_GlyphItems.insert(pG2, GlyphItem(nCharPos, nKashidaIndex, aPos,
    1207           0 :                                                       GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaCount ? nGapWidth : nGapWidth/2 ));
    1208           0 :             ++pG2;
    1209           0 :             aPos.X() += nGapWidth;
    1210             :         }
    1211           0 :         pG = pG2;
    1212             :     }
    1213             : }
    1214             : 
    1215             : // -----------------------------------------------------------------------
    1216             : 
    1217         161 : void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
    1218             : {
    1219             :     // initialize result array
    1220         161 :     long nXPos = -1;
    1221             :     int i;
    1222        2077 :     for( i = 0; i < nMaxIndex; ++i )
    1223        1916 :         pCaretXArray[ i ] = nXPos;
    1224             : 
    1225             :     // calculate caret positions using glyph array
    1226        1119 :     for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1227             :     {
    1228         958 :         nXPos = pG->maLinearPos.X();
    1229         958 :         long nXRight = nXPos + pG->mnOrigWidth;
    1230         958 :         int n = pG->mnCharPos;
    1231         958 :         int nCurrIdx = 2 * (n - mnMinCharPos);
    1232         958 :         if( !pG->IsRTLGlyph() )
    1233             :         {
    1234             :             // normal positions for LTR case
    1235         958 :             pCaretXArray[ nCurrIdx ]   = nXPos;
    1236         958 :             pCaretXArray[ nCurrIdx+1 ] = nXRight;
    1237             :         }
    1238             :         else
    1239             :         {
    1240             :             // reverse positions for RTL case
    1241           0 :             pCaretXArray[ nCurrIdx ]   = nXRight;
    1242           0 :             pCaretXArray[ nCurrIdx+1 ] = nXPos;
    1243             :         }
    1244             :     }
    1245         161 : }
    1246             : 
    1247             : // -----------------------------------------------------------------------
    1248             : 
    1249         365 : int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
    1250             : {
    1251         365 :     int nCharCapacity = mnEndCharPos - mnMinCharPos;
    1252         365 :     sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
    1253         365 :     if( !GetCharWidths( pCharWidths ) )
    1254           0 :         return STRING_LEN;
    1255             : 
    1256         365 :     long nWidth = 0;
    1257       15493 :     for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
    1258             :     {
    1259       15382 :         nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
    1260       15382 :         if( nWidth > nMaxWidth )
    1261         254 :             return i;
    1262       15128 :         nWidth += nCharExtra;
    1263             :     }
    1264             : 
    1265         111 :     return STRING_LEN;
    1266             : }
    1267             : 
    1268             : // -----------------------------------------------------------------------
    1269             : 
    1270      191403 : int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
    1271             :     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
    1272             : {
    1273      191403 :     GlyphVector::const_iterator pG = m_GlyphItems.begin();
    1274      191403 :     GlyphVector::const_iterator pGEnd = m_GlyphItems.end();
    1275      191403 :     pG += nStart;
    1276             : 
    1277             :     // find next glyph in substring
    1278      191403 :     for(; pG != pGEnd; ++nStart, ++pG )
    1279             :     {
    1280      114159 :         int n = pG->mnCharPos;
    1281      114159 :         if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
    1282      114159 :             break;
    1283             :     }
    1284             : 
    1285             :     // return zero if no more glyph found
    1286      191403 :     if( nStart >= (int)m_GlyphItems.size() )
    1287       77244 :         return 0;
    1288             : 
    1289             :     // calculate absolute position in pixel units
    1290      114159 :     Point aRelativePos = pG->maLinearPos - maBasePoint;
    1291             : 
    1292             :     // find more glyphs which can be merged into one drawing instruction
    1293      114159 :     int nCount = 0;
    1294      114159 :     long nYPos = pG->maLinearPos.Y();
    1295      114159 :     long nOldFlags = pG->mnGlyphIndex;
    1296           0 :     for(;;)
    1297             :     {
    1298             :         // update return data with glyph info
    1299      114159 :         ++nCount;
    1300      114159 :         *(pGlyphs++) = pG->mnGlyphIndex;
    1301      114159 :         if( pCharPosAry )
    1302       14014 :             *(pCharPosAry++) = pG->mnCharPos;
    1303      114159 :         if( pGlyphAdvAry )
    1304       14014 :             *pGlyphAdvAry = pG->mnNewWidth;
    1305             : 
    1306             :         // break at end of glyph list
    1307      114159 :         if( ++nStart >= (int)m_GlyphItems.size() )
    1308       74826 :             break;
    1309             :         // break when enough glyphs
    1310       39333 :         if( nCount >= nLen )
    1311       39333 :             break;
    1312             : 
    1313           0 :         long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X();
    1314           0 :         if( pGlyphAdvAry )
    1315             :         {
    1316             :             // override default advance width with correct value
    1317           0 :             *(pGlyphAdvAry++) = nGlyphAdvance;
    1318             :         }
    1319             :         else
    1320             :         {
    1321             :             // stop when next x-position is unexpected
    1322           0 :             if( pG->mnOrigWidth != nGlyphAdvance )
    1323           0 :                 break;
    1324             :         }
    1325             : 
    1326             :         // advance to next glyph
    1327           0 :         ++pG;
    1328             : 
    1329             :         // stop when next y-position is unexpected
    1330           0 :         if( nYPos != pG->maLinearPos.Y() )
    1331           0 :             break;
    1332             : 
    1333             :         // stop when no longer in string
    1334           0 :         int n = pG->mnCharPos;
    1335           0 :         if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
    1336           0 :             break;
    1337             : 
    1338             :         // stop when glyph flags change
    1339           0 :         if( (nOldFlags ^ pG->mnGlyphIndex) & GF_FLAGMASK )
    1340           0 :             break;
    1341             : 
    1342           0 :         nOldFlags = pG->mnGlyphIndex; // &GF_FLAGMASK not needed for test above
    1343             :     }
    1344             : 
    1345      114159 :     aRelativePos.X() /= mnUnitsPerPixel;
    1346      114159 :     aRelativePos.Y() /= mnUnitsPerPixel;
    1347      114159 :     rPos = GetDrawPosition( aRelativePos );
    1348             : 
    1349      114159 :     return nCount;
    1350             : }
    1351             : 
    1352             : // -----------------------------------------------------------------------
    1353             : 
    1354        6718 : void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
    1355             : {
    1356        6718 :     if( nStart >= (int)m_GlyphItems.size() )
    1357        6718 :         return;
    1358             : 
    1359        6718 :     GlyphVector::iterator pG = m_GlyphItems.begin();
    1360        6718 :     pG += nStart;
    1361             : 
    1362             :     // the nNewXPos argument determines the new cell position
    1363             :     // as RTL-glyphs are right justified in their cell
    1364             :     // the cell position needs to be adjusted to the glyph position
    1365        6718 :     if( pG->IsRTLGlyph() )
    1366          42 :         nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
    1367             :     // calculate the x-offset to the old position
    1368        6718 :     long nXDelta = nNewXPos - pG->maLinearPos.X();
    1369             :     // adjust all following glyph positions if needed
    1370        6718 :     if( nXDelta != 0 )
    1371             :     {
    1372         170 :         for( GlyphVector::iterator pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1373             :         {
    1374         142 :             pG->maLinearPos.X() += nXDelta;
    1375             :         }
    1376             :     }
    1377             : }
    1378             : 
    1379             : // -----------------------------------------------------------------------
    1380             : 
    1381        7000 : void GenericSalLayout::DropGlyph( int nStart )
    1382             : {
    1383        7000 :     if( nStart >= (int)m_GlyphItems.size())
    1384        7000 :         return;
    1385             : 
    1386        7000 :     GlyphVector::iterator pG = m_GlyphItems.begin();
    1387        7000 :     pG += nStart;
    1388        7000 :     pG->mnGlyphIndex = GF_DROPPED;
    1389        7000 :     pG->mnCharPos = -1;
    1390             : }
    1391             : 
    1392             : // -----------------------------------------------------------------------
    1393             : 
    1394       13380 : void GenericSalLayout::Simplify( bool bIsBase )
    1395             : {
    1396       13380 :     const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
    1397             : 
    1398             :     // remove dropped glyphs inplace
    1399       13380 :     size_t j = 0;
    1400       27394 :     for(size_t i = 0; i < m_GlyphItems.size(); i++ )
    1401             :     {
    1402       14014 :         if( m_GlyphItems[i].mnGlyphIndex == nDropMarker )
    1403        7000 :             continue;
    1404             : 
    1405        7014 :         if( i != j )
    1406             :         {
    1407          14 :             m_GlyphItems[j] = m_GlyphItems[i];
    1408             :         }
    1409        7014 :         j += 1;
    1410             :     }
    1411       13380 :     m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
    1412       13380 : }
    1413             : 
    1414             : // -----------------------------------------------------------------------
    1415             : 
    1416             : // make sure GlyphItems are sorted left to right
    1417      123027 : void GenericSalLayout::SortGlyphItems()
    1418             : {
    1419             :     // move cluster components behind their cluster start (especially for RTL)
    1420             :     // using insertion sort because the glyph items are "almost sorted"
    1421             : 
    1422      639003 :     for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1423             :     {
    1424             :         // find a cluster starting with a diacritic
    1425      515976 :         if( !pG->IsDiacritic() )
    1426      512924 :             continue;
    1427        3052 :         if( !pG->IsClusterStart() )
    1428           0 :             continue;
    1429        6104 :         for( GlyphVector::iterator pBaseGlyph = pG; ++pBaseGlyph != pGEnd; )
    1430             :         {
    1431             :             // find the base glyph matching to the misplaced diacritic
    1432           0 :             if( pBaseGlyph->IsClusterStart() )
    1433           0 :                 break;
    1434           0 :             if( pBaseGlyph->IsDiacritic() )
    1435           0 :                 continue;
    1436             : 
    1437             :             // found the matching base glyph
    1438             :             // => this base glyph becomes the new cluster start
    1439           0 :             iter_swap(pG, pBaseGlyph);
    1440             : 
    1441             :             // update glyph flags of swapped glyphitems
    1442           0 :             pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
    1443           0 :             pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
    1444             :             // prepare for checking next cluster
    1445           0 :             pG = pBaseGlyph;
    1446           0 :             break;
    1447             :         }
    1448             :     }
    1449      123027 : }
    1450             : 
    1451             : // =======================================================================
    1452             : 
    1453        6690 : MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const PhysicalFontFace* pBaseFont )
    1454             : :   SalLayout()
    1455             : ,   mnLevel( 1 )
    1456        6690 : ,   mbInComplete( false )
    1457             : {
    1458             :     //maFallbackRuns[0].Clear();
    1459        6690 :     mpFallbackFonts[ 0 ] = pBaseFont;
    1460        6690 :     mpLayouts[ 0 ]  = &rBaseLayout;
    1461        6690 :     mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
    1462        6690 : }
    1463             : 
    1464           0 : void MultiSalLayout::SetInComplete(bool bInComplete)
    1465             : {
    1466           0 :     mbInComplete = bInComplete;
    1467           0 :     maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
    1468           0 : }
    1469             : 
    1470             : // -----------------------------------------------------------------------
    1471             : 
    1472      133800 : MultiSalLayout::~MultiSalLayout()
    1473             : {
    1474       20070 :     for( int i = 0; i < mnLevel; ++i )
    1475       13380 :         mpLayouts[ i ]->Release();
    1476      127110 : }
    1477             : 
    1478             : // -----------------------------------------------------------------------
    1479             : 
    1480        6690 : bool MultiSalLayout::AddFallback( SalLayout& rFallback,
    1481             :     ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
    1482             : {
    1483        6690 :     if( mnLevel >= MAX_FALLBACK )
    1484           0 :         return false;
    1485             : 
    1486        6690 :     mpFallbackFonts[ mnLevel ]  = pFallbackFont;
    1487        6690 :     mpLayouts[ mnLevel ]        = &rFallback;
    1488        6690 :     maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
    1489        6690 :     ++mnLevel;
    1490        6690 :     return true;
    1491             : }
    1492             : 
    1493             : // -----------------------------------------------------------------------
    1494             : 
    1495        6690 : bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
    1496             : {
    1497        6690 :     if( mnLevel <= 1 )
    1498           0 :         return false;
    1499        6690 :     if (!mbInComplete)
    1500        6690 :         maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
    1501        6690 :     return true;
    1502             : }
    1503             : 
    1504             : // -----------------------------------------------------------------------
    1505             : 
    1506        6690 : void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
    1507             : {
    1508        6690 :     SalLayout::AdjustLayout( rArgs );
    1509        6690 :     ImplLayoutArgs aMultiArgs = rArgs;
    1510             : 
    1511        6690 :     if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
    1512             :     {
    1513             :         // for stretched text in a MultiSalLayout the target width needs to be
    1514             :         // distributed by individually adjusting its virtual character widths
    1515         438 :         long nTargetWidth = aMultiArgs.mnLayoutWidth;
    1516         438 :         nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
    1517         438 :         aMultiArgs.mnLayoutWidth = 0;
    1518             : 
    1519             :         // we need to get the original unmodified layouts ready
    1520        1314 :         for( int n = 0; n < mnLevel; ++n )
    1521         876 :             mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
    1522             :         // then we can measure the unmodified metrics
    1523         438 :         int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
    1524         438 :         sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
    1525         438 :         FillDXArray( pJustificationArray );
    1526             :         // #i17359# multilayout is not simplified yet, so calculating the
    1527             :         // unjustified width needs handholding; also count the number of
    1528             :         // stretchable virtual char widths
    1529         438 :         long nOrigWidth = 0;
    1530         438 :         int nStretchable = 0;
    1531         876 :         for( int i = 0; i < nCharCount; ++i )
    1532             :         {
    1533             :             // convert array from widths to sum of widths
    1534         438 :             nOrigWidth += pJustificationArray[i];
    1535         438 :             if( pJustificationArray[i] > 0 )
    1536         438 :                 ++nStretchable;
    1537             :         }
    1538             : 
    1539             :         // now we are able to distribute the extra width over the virtual char widths
    1540         438 :         if( nOrigWidth && (nTargetWidth != nOrigWidth) )
    1541             :         {
    1542          92 :             int nDiffWidth = nTargetWidth - nOrigWidth;
    1543          92 :             int nWidthSum = 0;
    1544         184 :             for( int i = 0; i < nCharCount; ++i )
    1545             :             {
    1546          92 :                 int nJustWidth = pJustificationArray[i];
    1547          92 :                 if( (nJustWidth > 0) && (nStretchable > 0) )
    1548             :                 {
    1549          92 :                     int nDeltaWidth = nDiffWidth / nStretchable;
    1550          92 :                     nJustWidth += nDeltaWidth;
    1551          92 :                     nDiffWidth -= nDeltaWidth;
    1552          92 :                     --nStretchable;
    1553             :                 }
    1554          92 :                 nWidthSum += nJustWidth;
    1555          92 :                 pJustificationArray[i] = nWidthSum;
    1556             :             }
    1557          92 :             if( nWidthSum != nTargetWidth )
    1558           0 :                 pJustificationArray[ nCharCount-1 ] = nTargetWidth;
    1559             : 
    1560             :             // the justification array is still in base level units
    1561             :             // => convert it to pixel units
    1562          92 :             if( mnUnitsPerPixel > 1 )
    1563             :             {
    1564           0 :                 for( int i = 0; i < nCharCount; ++i )
    1565             :                 {
    1566           0 :                     sal_Int32 nVal = pJustificationArray[ i ];
    1567           0 :                     nVal += (mnUnitsPerPixel + 1) / 2;
    1568           0 :                     pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
    1569             :                 }
    1570             :             }
    1571             : 
    1572             :             // change the mpDXArray temporarilly (just for the justification)
    1573          92 :             aMultiArgs.mpDXArray = pJustificationArray;
    1574             :         }
    1575             :     }
    1576             : 
    1577             :     // Compute rtl flags, since in some scripts glyphs/char order can be
    1578             :     // reversed for a few character sequencies e.g. Myanmar
    1579        6690 :     std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
    1580        6690 :     rArgs.ResetPos();
    1581             :     bool bRtl;
    1582             :     int nRunStart, nRunEnd;
    1583       20070 :     while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
    1584             :     {
    1585        6690 :         if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
    1586          14 :                             vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
    1587             :     }
    1588        6690 :     rArgs.ResetPos();
    1589             : 
    1590             :     // prepare "merge sort"
    1591             :     int nStartOld[ MAX_FALLBACK ];
    1592             :     int nStartNew[ MAX_FALLBACK ];
    1593             :     int nCharPos[ MAX_FALLBACK ];
    1594             :     sal_Int32 nGlyphAdv[ MAX_FALLBACK ];
    1595        6690 :     int nValid[ MAX_FALLBACK ] = {0};
    1596             : 
    1597             :     sal_GlyphId nDummy;
    1598        6690 :     Point aPos;
    1599        6690 :     int nLevel = 0, n;
    1600       20070 :     for( n = 0; n < mnLevel; ++n )
    1601             :     {
    1602             :         // now adjust the individual components
    1603       13380 :         if( n > 0 )
    1604             :         {
    1605        6690 :             aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
    1606        6690 :             aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
    1607             :         }
    1608       13380 :         mpLayouts[n]->AdjustLayout( aMultiArgs );
    1609             : 
    1610             :         // disable glyph-injection for glyph-fallback SalLayout iteration
    1611       13380 :         mpLayouts[n]->DisableGlyphInjection( true );
    1612             : 
    1613             :         // remove unused parts of component
    1614       13380 :         if( n > 0 )
    1615             :         {
    1616        6690 :             if (mbInComplete && (n == mnLevel-1))
    1617           0 :                 mpLayouts[n]->Simplify( true );
    1618             :             else
    1619        6690 :                 mpLayouts[n]->Simplify( false );
    1620             :         }
    1621             : 
    1622             :         // prepare merging components
    1623       13380 :         nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
    1624       26760 :         nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
    1625       26760 :             nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] );
    1626             : #ifdef MULTI_SL_DEBUG
    1627             :         if (nValid[nLevel]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", n, nStartOld[nLevel], nStartNew[nLevel], aPos.X(), nGlyphAdv[nLevel], nCharPos[nLevel],
    1628             :             rArgs.mpStr[nCharPos[nLevel]]);
    1629             : #endif
    1630       13380 :         if( (n > 0) && !nValid[ nLevel ] )
    1631             :         {
    1632             :             // an empty fallback layout can be released
    1633           0 :             mpLayouts[n]->Release();
    1634             :         }
    1635             :         else
    1636             :         {
    1637             :             // reshuffle used fallbacks if needed
    1638       13380 :             if( nLevel != n )
    1639             :             {
    1640           0 :                 mpLayouts[ nLevel ]         = mpLayouts[ n ];
    1641           0 :                 mpFallbackFonts[ nLevel ]   = mpFallbackFonts[ n ];
    1642           0 :                 maFallbackRuns[ nLevel ]    = maFallbackRuns[ n ];
    1643             :             }
    1644       13380 :             ++nLevel;
    1645             :         }
    1646             :     }
    1647        6690 :     mnLevel = nLevel;
    1648             : 
    1649             :     // merge the fallback levels
    1650        6690 :     long nXPos = 0;
    1651        6690 :     double fUnitMul = 1.0;
    1652       20070 :     for( n = 0; n < nLevel; ++n )
    1653       13380 :         maFallbackRuns[n].ResetPos();
    1654        6690 :     int nActiveCharPos = nCharPos[0];
    1655        6690 :     int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
    1656        6690 :         rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
    1657        6690 :     int nRunVisibleEndChar = nCharPos[0];
    1658       20098 :     while( nValid[0] && (nLevel > 0))
    1659             :     {
    1660             :         // find best fallback level
    1661       13422 :         for( n = 0; n < nLevel; ++n )
    1662       13422 :             if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
    1663             :                 // fallback level n wins when it requested no further fallback
    1664        6718 :                 break;
    1665        6718 :         int nFBLevel = n;
    1666             : 
    1667        6718 :         if( n < nLevel )
    1668             :         {
    1669             :             // use base(n==0) or fallback(n>=1) level
    1670        6718 :             fUnitMul = mnUnitsPerPixel;
    1671        6718 :             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    1672        6718 :             long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
    1673        6718 :             mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
    1674             :         }
    1675             :         else
    1676             :         {
    1677           0 :             n = 0;  // keep NotDef in base level
    1678           0 :             fUnitMul = 1.0;
    1679             :         }
    1680             : 
    1681        6718 :         if( n > 0 )
    1682             :         {
    1683             :             // drop the NotDef glyphs in the base layout run if a fallback run exists
    1684       20718 :             while (
    1685        7014 :                     (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
    1686        7000 :                     (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
    1687             :                   )
    1688             :             {
    1689        7000 :                 mpLayouts[0]->DropGlyph( nStartOld[0] );
    1690        7000 :                 nStartOld[0] = nStartNew[0];
    1691       14000 :                 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
    1692       14000 :                     nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
    1693             : #ifdef MULTI_SL_DEBUG
    1694             :                 if (nValid[0]) fprintf(mslLog(), "layout[0]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", nStartOld[0], nStartNew[0], aPos.X(), nGlyphAdv[0], nCharPos[0], rArgs.mpStr[nCharPos[0]]);
    1695             : #endif
    1696        7000 :                 if( !nValid[0] )
    1697        6690 :                    break;
    1698             :             }
    1699             :         }
    1700             : 
    1701             :         // skip to end of layout run and calculate its advance width
    1702        6718 :         int nRunAdvance = 0;
    1703        6718 :         bool bKeepNotDef = (nFBLevel >= nLevel);
    1704         296 :         for(;;)
    1705             :         {
    1706        7014 :             nRunAdvance += nGlyphAdv[n];
    1707             : 
    1708             :             // proceed to next glyph
    1709        7014 :             nStartOld[n] = nStartNew[n];
    1710        7014 :             int nOrigCharPos = nCharPos[n];
    1711       14028 :             nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
    1712       14028 :                 nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
    1713             : #ifdef MULTI_SL_DEBUG
    1714             :             if (nValid[n]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d a%d c%d %x\n", n, nStartOld[n], nStartNew[n], nGlyphAdv[n], nCharPos[n], rArgs.mpStr[nCharPos[n]]);
    1715             : #endif
    1716             :             // break after last glyph of active layout
    1717        7014 :             if( !nValid[n] )
    1718             :             {
    1719             :                 // performance optimization (when a fallback layout is no longer needed)
    1720        6690 :                 if( n >= nLevel-1 )
    1721        6690 :                     --nLevel;
    1722        6690 :                 break;
    1723             :             }
    1724             : 
    1725             :             //If the next character is one which belongs to the next level, then we
    1726             :             //are finished here for now, and we'll pick up after the next level has
    1727             :             //been processed
    1728         324 :             if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
    1729             :             {
    1730          14 :                 if (nOrigCharPos < nCharPos[n])
    1731             :                 {
    1732           0 :                     if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
    1733           0 :                         break;
    1734             :                 }
    1735          14 :                 else if (nOrigCharPos > nCharPos[n])
    1736             :                 {
    1737          14 :                     if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
    1738           0 :                         break;
    1739             :                 }
    1740             :             }
    1741             : 
    1742             :             // break at end of layout run
    1743         324 :             if( n > 0 )
    1744             :             {
    1745             :                 // skip until end of fallback run
    1746         310 :                 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
    1747          14 :                     break;
    1748             :             }
    1749             :             else
    1750             :             {
    1751             :                 // break when a fallback is needed and available
    1752          14 :                 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
    1753          14 :                 if( bNeedFallback )
    1754          14 :                     if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
    1755          14 :                         break;
    1756             :                 // break when change from resolved to unresolved base layout run
    1757           0 :                 if( bKeepNotDef && !bNeedFallback )
    1758           0 :                     { maFallbackRuns[0].NextRun(); break; }
    1759           0 :                 bKeepNotDef = bNeedFallback;
    1760             :             }
    1761             :             // check for reordered glyphs
    1762         352 :             if (aMultiArgs.mpDXArray &&
    1763             :                 nRunVisibleEndChar < mnEndCharPos &&
    1764             :                 nRunVisibleEndChar >= mnMinCharPos &&
    1765          28 :                 nCharPos[n] < mnEndCharPos &&
    1766          28 :                 nCharPos[n] >= mnMinCharPos)
    1767             :             {
    1768          28 :                 if (vRtl[nActiveCharPos - mnMinCharPos])
    1769             :                 {
    1770          56 :                     if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
    1771          28 :                         >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
    1772             :                     {
    1773          28 :                         nRunVisibleEndChar = nCharPos[n];
    1774             :                     }
    1775             :                 }
    1776           0 :                 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
    1777           0 :                          <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
    1778             :                 {
    1779           0 :                     nRunVisibleEndChar = nCharPos[n];
    1780             :                 }
    1781             :             }
    1782             :         }
    1783             : 
    1784             :         // if a justification array is available
    1785             :         // => use it directly to calculate the corresponding run width
    1786        6718 :         if( aMultiArgs.mpDXArray )
    1787             :         {
    1788             :             // the run advance is the width from the first char
    1789             :             // in the run to the first char in the next run
    1790         104 :             nRunAdvance = 0;
    1791             : #ifdef MULTI_SL_DEBUG
    1792             :             const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
    1793             :             int nOldRunAdv = 0;
    1794             :             int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
    1795             :             if( nDXIndex >= 0 )
    1796             :                 nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
    1797             :             nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
    1798             :             if( nDXIndex >= 0 )
    1799             :                 nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
    1800             :             if( !bLTR )
    1801             :                 nOldRunAdv = -nOldRunAdv;
    1802             : #endif
    1803         104 :             if (vRtl[nActiveCharPos - mnMinCharPos])
    1804             :             {
    1805          12 :               if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
    1806           8 :                   nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
    1807          12 :               if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
    1808          12 :                   nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
    1809             : #ifdef MULTI_SL_DEBUG
    1810             :               fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
    1811             : #endif
    1812             :             }
    1813             :             else
    1814             :             {
    1815          92 :                 if (nRunVisibleEndChar >= mnMinCharPos)
    1816          92 :                   nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
    1817          92 :                 if (nLastRunEndChar >= mnMinCharPos)
    1818           0 :                   nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
    1819             : #ifdef MULTI_SL_DEBUG
    1820             :                 fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
    1821             : #endif
    1822             :             }
    1823         104 :             nLastRunEndChar = nRunVisibleEndChar;
    1824         104 :             nRunVisibleEndChar = nCharPos[0];
    1825             :             // the requested width is still in pixel units
    1826             :             // => convert it to base level font units
    1827         104 :             nRunAdvance *= mnUnitsPerPixel;
    1828             :         }
    1829             :         else
    1830             :         {
    1831             :             // the measured width is still in fallback font units
    1832             :             // => convert it to base level font units
    1833        6614 :             if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
    1834        6604 :                 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
    1835             :         }
    1836             : 
    1837             :         // calculate new x position (in base level units)
    1838        6718 :         nXPos += nRunAdvance;
    1839             : 
    1840             :         // prepare for next fallback run
    1841        6718 :         nActiveCharPos = nCharPos[0];
    1842             :         // it essential that the runs don't get ahead of themselves and in the
    1843             :         // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
    1844             :         // have already been reached on the base level
    1845       20140 :         for( int i = nFBLevel; --i >= 0;)
    1846             :         {
    1847        6704 :             if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
    1848             :             {
    1849        6704 :                 if (bRtl)
    1850             :                 {
    1851          28 :                     if (nRunStart > nActiveCharPos)
    1852          14 :                         maFallbackRuns[i].NextRun();
    1853             :                 }
    1854             :                 else
    1855             :                 {
    1856        6676 :                     if (nRunEnd <= nActiveCharPos)
    1857           0 :                         maFallbackRuns[i].NextRun();
    1858             :                 }
    1859             :             }
    1860             :         }
    1861             : //            if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) )
    1862             : //                maFallbackRuns[i].NextRun();
    1863             :     }
    1864             : 
    1865        6690 :     mpLayouts[0]->Simplify( true );
    1866             : 
    1867             :     // reenable glyph-injection
    1868       20070 :     for( n = 0; n < mnLevel; ++n )
    1869       20070 :         mpLayouts[n]->DisableGlyphInjection( false );
    1870        6690 : }
    1871             : 
    1872             : // -----------------------------------------------------------------------
    1873             : 
    1874        6690 : void MultiSalLayout::InitFont() const
    1875             : {
    1876        6690 :     if( mnLevel > 0 )
    1877        6690 :         mpLayouts[0]->InitFont();
    1878        6690 : }
    1879             : 
    1880             : // -----------------------------------------------------------------------
    1881             : 
    1882           0 : const PhysicalFontFace* MultiSalLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const
    1883             : {
    1884           0 :     int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
    1885           0 :     return mpFallbackFonts[ nFallbackLevel ];
    1886             : }
    1887             : 
    1888             : // -----------------------------------------------------------------------
    1889             : 
    1890         666 : void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
    1891             : {
    1892        2664 :     for( int i = mnLevel; --i >= 0; )
    1893             :     {
    1894        1332 :         SalLayout& rLayout = *mpLayouts[ i ];
    1895        1332 :         rLayout.DrawBase() += maDrawBase;
    1896        1332 :         rLayout.DrawOffset() += maDrawOffset;
    1897        1332 :         rLayout.InitFont();
    1898        1332 :         rLayout.DrawText( rGraphics );
    1899        1332 :         rLayout.DrawOffset() -= maDrawOffset;
    1900        1332 :         rLayout.DrawBase() -= maDrawBase;
    1901             :     }
    1902             :     // NOTE: now the baselevel font is active again
    1903         666 : }
    1904             : 
    1905             :  // -----------------------------------------------------------------------
    1906             : 
    1907           0 : int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
    1908             : {
    1909           0 :     if( mnLevel <= 0 )
    1910           0 :         return STRING_LEN;
    1911           0 :     if( mnLevel == 1 )
    1912           0 :         return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
    1913             : 
    1914           0 :     int nCharCount = mnEndCharPos - mnMinCharPos;
    1915           0 :     sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
    1916           0 :     mpLayouts[0]->FillDXArray( pCharWidths );
    1917             : 
    1918           0 :     for( int n = 1; n < mnLevel; ++n )
    1919             :     {
    1920           0 :         SalLayout& rLayout = *mpLayouts[ n ];
    1921           0 :         rLayout.FillDXArray( pCharWidths + nCharCount );
    1922           0 :         double fUnitMul = mnUnitsPerPixel;
    1923           0 :         fUnitMul /= rLayout.GetUnitsPerPixel();
    1924           0 :         for( int i = 0; i < nCharCount; ++i )
    1925             :         {
    1926           0 :             long w = pCharWidths[ i + nCharCount ];
    1927           0 :             w = static_cast<long>(w*fUnitMul + 0.5);
    1928           0 :             pCharWidths[ i ] += w;
    1929             :         }
    1930             :     }
    1931             : 
    1932           0 :     long nWidth = 0;
    1933           0 :     for( int i = 0; i < nCharCount; ++i )
    1934             :     {
    1935           0 :         nWidth += pCharWidths[ i ] * nFactor;
    1936           0 :         if( nWidth > nMaxWidth )
    1937           0 :             return (i + mnMinCharPos);
    1938           0 :         nWidth += nCharExtra;
    1939             :     }
    1940             : 
    1941           0 :     return STRING_LEN;
    1942             : }
    1943             : 
    1944             : // -----------------------------------------------------------------------
    1945             : 
    1946        4786 : long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
    1947             : {
    1948        4786 :     long nMaxWidth = 0;
    1949             : 
    1950             :     // prepare merging of fallback levels
    1951        4786 :     sal_Int32* pTempWidths = NULL;
    1952        4786 :     const int nCharCount = mnEndCharPos - mnMinCharPos;
    1953        4786 :     if( pCharWidths )
    1954             :     {
    1955        1444 :         for( int i = 0; i < nCharCount; ++i )
    1956         758 :             pCharWidths[i] = 0;
    1957         686 :         pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
    1958             :     }
    1959             : 
    1960       19144 :     for( int n = mnLevel; --n >= 0; )
    1961             :     {
    1962             :         // query every fallback level
    1963        9572 :         long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
    1964        9572 :         if( !nTextWidth )
    1965        5232 :             continue;
    1966             :         // merge results from current level
    1967        4340 :         double fUnitMul = mnUnitsPerPixel;
    1968        4340 :         fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    1969        4340 :         nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
    1970        4340 :         if( nMaxWidth < nTextWidth )
    1971        3882 :             nMaxWidth = nTextWidth;
    1972        4340 :         if( !pCharWidths )
    1973        3252 :             continue;
    1974             :         // calculate virtual char widths using most probable fallback layout
    1975        2320 :         for( int i = 0; i < nCharCount; ++i )
    1976             :         {
    1977             :             // #i17359# restriction:
    1978             :             // one char cannot be resolved from different fallbacks
    1979        1232 :             if( pCharWidths[i] != 0 )
    1980         510 :                 continue;
    1981         722 :             long nCharWidth = pTempWidths[i];
    1982         722 :             if( !nCharWidth )
    1983           8 :                 continue;
    1984         714 :             nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
    1985         714 :             pCharWidths[i] = nCharWidth;
    1986             :         }
    1987             :     }
    1988             : 
    1989        4786 :     return nMaxWidth;
    1990             : }
    1991             : 
    1992             : // -----------------------------------------------------------------------
    1993             : 
    1994           0 : void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
    1995             : {
    1996           0 :     SalLayout& rLayout = *mpLayouts[ 0 ];
    1997           0 :     rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
    1998             : 
    1999           0 :     if( mnLevel > 1 )
    2000             :     {
    2001           0 :         sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
    2002           0 :         for( int n = 1; n < mnLevel; ++n )
    2003             :         {
    2004           0 :             mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
    2005           0 :             double fUnitMul = mnUnitsPerPixel;
    2006           0 :             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    2007           0 :             for( int i = 0; i < nMaxIndex; ++i )
    2008           0 :                 if( pTempPos[i] >= 0 )
    2009             :                 {
    2010           0 :                     long w = pTempPos[i];
    2011           0 :                     w = static_cast<long>(w*fUnitMul + 0.5);
    2012           0 :                     pCaretXArray[i] = w;
    2013             :                 }
    2014             :         }
    2015             :     }
    2016           0 : }
    2017             : 
    2018             : // -----------------------------------------------------------------------
    2019             : 
    2020           0 : int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
    2021             :     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
    2022             : {
    2023             :     // for multi-level fallback only single glyphs should be used
    2024           0 :     if( mnLevel > 1 && nLen > 1 )
    2025           0 :         nLen = 1;
    2026             : 
    2027             :     // NOTE: nStart is tagged with current font index
    2028           0 :     int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
    2029           0 :     nStart &= ~GF_FONTMASK;
    2030           0 :     for(; nLevel < mnLevel; ++nLevel, nStart=0 )
    2031             :     {
    2032           0 :         SalLayout& rLayout = *mpLayouts[ nLevel ];
    2033           0 :         rLayout.InitFont();
    2034             :         int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
    2035           0 :             nStart, pGlyphAdvAry, pCharPosAry );
    2036           0 :         if( nRetVal )
    2037             :         {
    2038           0 :             int nFontTag = nLevel << GF_FONTSHIFT;
    2039           0 :             nStart |= nFontTag;
    2040           0 :             double fUnitMul = mnUnitsPerPixel;
    2041           0 :             fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
    2042           0 :             for( int i = 0; i < nRetVal; ++i )
    2043             :             {
    2044           0 :                 if( pGlyphAdvAry )
    2045             :                 {
    2046           0 :                     long w = pGlyphAdvAry[i];
    2047           0 :                     w = static_cast<long>(w * fUnitMul + 0.5);
    2048           0 :                     pGlyphAdvAry[i] = w;
    2049             :                 }
    2050           0 :                 pGlyphIdxAry[ i ] |= nFontTag;
    2051             :             }
    2052           0 :             rPos += maDrawBase;
    2053           0 :             rPos += maDrawOffset;
    2054           0 :             return nRetVal;
    2055             :         }
    2056             :     }
    2057             : 
    2058             :     // #111016# reset to base level font when done
    2059           0 :     mpLayouts[0]->InitFont();
    2060           0 :     return 0;
    2061             : }
    2062             : 
    2063             : // -----------------------------------------------------------------------
    2064             : 
    2065          92 : bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
    2066             :     ::basegfx::B2DPolyPolygonVector& rPPV ) const
    2067             : {
    2068          92 :     bool bRet = false;
    2069             : 
    2070         368 :     for( int i = mnLevel; --i >= 0; )
    2071             :     {
    2072         184 :         SalLayout& rLayout = *mpLayouts[ i ];
    2073         184 :         rLayout.DrawBase() = maDrawBase;
    2074         184 :         rLayout.DrawOffset() += maDrawOffset;
    2075         184 :         rLayout.InitFont();
    2076         184 :         bRet |= rLayout.GetOutline( rGraphics, rPPV );
    2077         184 :         rLayout.DrawOffset() -= maDrawOffset;
    2078             :     }
    2079             : 
    2080          92 :     return bRet;
    2081             : }
    2082             : 
    2083             : // -----------------------------------------------------------------------
    2084             : 
    2085        1664 : bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
    2086             : {
    2087        1664 :     bool bRet = false;
    2088             : 
    2089        1664 :     Rectangle aRectangle;
    2090        6656 :     for( int i = mnLevel; --i >= 0; )
    2091             :     {
    2092        3328 :         SalLayout& rLayout = *mpLayouts[ i ];
    2093        3328 :         rLayout.DrawBase() = maDrawBase;
    2094        3328 :         rLayout.DrawOffset() += maDrawOffset;
    2095        3328 :         rLayout.InitFont();
    2096        3328 :         if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
    2097             :         {
    2098        1664 :             rRect.Union( aRectangle );
    2099        1664 :             bRet = true;
    2100             :         }
    2101        3328 :         rLayout.DrawOffset() -= maDrawOffset;
    2102             :     }
    2103             : 
    2104        1664 :     return bRet;
    2105             : }
    2106             : 
    2107             : // =======================================================================
    2108             : 
    2109             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10