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-27 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        2705 : bool IsDiacritic( sal_UCS4 nChar )
      75             : {
      76             :     // shortcut abvious non-diacritics
      77        2705 :     if( nChar < 0x0300 )
      78           0 :         return false;
      79        2705 :      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        2705 :     const DiaRange* pRange = &aRanges[0];
      97       36021 :     for( int i = nCount; --i >= 0; ++pRange )
      98       34798 :         if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
      99        1482 :             return true;
     100             : 
     101        1223 :     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       11676 : sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
     152             : {
     153             :     // currently only conversion from ASCII digits is interesting
     154       11676 :     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       11676 :     switch( eLang & LANGUAGE_MASK_PRIMARY )
     162             :     {
     163             :         default:
     164       11676 :             nOffset = 0;
     165       11676 :             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       11676 :     nChar += nOffset;
     233       11676 :     return nChar;
     234             : }
     235             : 
     236             : // -----------------------------------------------------------------------
     237             : 
     238      220824 : inline bool IsControlChar( sal_UCS4 cChar )
     239             : {
     240             :     // C0 control characters
     241      220824 :     if( (0x0001 <= cChar) && (cChar <= 0x001F) )
     242           2 :         return true;
     243             :     // formatting characters
     244      220822 :     if( (0x200E <= cChar) && (cChar <= 0x200F) )
     245           0 :         return true;
     246      220822 :     if( (0x2028 <= cChar) && (cChar <= 0x202E) )
     247           0 :         return true;
     248             :     // deprecated formatting characters
     249      220822 :     if( (0x206A <= cChar) && (cChar <= 0x206F) )
     250           0 :         return true;
     251      220822 :     if( (0x2060 == cChar) )
     252           0 :         return true;
     253             :     // byte order markers and invalid unicode
     254      220822 :     if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
     255           0 :         return true;
     256      220822 :     return false;
     257             : }
     258             : 
     259             : // =======================================================================
     260             : 
     261        6838 : bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
     262             : {
     263             :     // check if charpos could extend current run
     264        6838 :     int nIndex = maRuns.size();
     265        6838 :     if( nIndex >= 2 )
     266             :     {
     267         310 :         int nRunPos0 = maRuns[ nIndex-2 ];
     268         310 :         int nRunPos1 = maRuns[ nIndex-1 ];
     269         310 :         if( ((nCharPos + bRTL) == nRunPos1)
     270             :     &&  ((nRunPos0 > nRunPos1) == bRTL) )
     271             :         {
     272             :             // extend current run by new charpos
     273         296 :             maRuns[ nIndex-1 ] = nCharPos + !bRTL;
     274         296 :             return false;
     275             :         }
     276             :         // ignore new charpos when it is in current run
     277          14 :         if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
     278           0 :             return false;
     279          14 :         if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
     280           0 :             return false;
     281             :     }
     282             : 
     283             :     // else append a new run consisting of the new charpos
     284        6542 :     maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
     285        6542 :     maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
     286        6542 :     return true;
     287             : }
     288             : 
     289             : // -----------------------------------------------------------------------
     290             : 
     291       52018 : bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
     292             : {
     293       52018 :     if( nCharPos0 == nCharPos1 )
     294           1 :         return false;
     295             : 
     296             :     // swap if needed
     297       52017 :     if( bRTL == (nCharPos0 < nCharPos1) )
     298             :     {
     299           8 :         int nTemp = nCharPos0;
     300           8 :         nCharPos0 = nCharPos1;
     301           8 :         nCharPos1 = nTemp;
     302             :     }
     303             : 
     304             :     // append new run
     305       52017 :     maRuns.push_back( nCharPos0 );
     306       52017 :     maRuns.push_back( nCharPos1 );
     307       52017 :     return true;
     308             : }
     309             : 
     310             : // -----------------------------------------------------------------------
     311             : 
     312        6887 : bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
     313             : {
     314        6887 :     if( mnRunIndex >= (int)maRuns.size() )
     315           7 :         return false;
     316             : 
     317        6880 :     int nMinCharPos = maRuns[ mnRunIndex+0 ];
     318        6880 :     int nEndCharPos = maRuns[ mnRunIndex+1 ];
     319        6880 :     if( nMinCharPos > nEndCharPos ) // reversed in RTL case
     320             :     {
     321         168 :         int nTemp = nMinCharPos;
     322         168 :         nMinCharPos = nEndCharPos;
     323         168 :         nEndCharPos = nTemp;
     324             :     }
     325             : 
     326        6880 :     if( nCharPos < nMinCharPos )
     327          28 :         return false;
     328        6852 :     if( nCharPos >= nEndCharPos )
     329           7 :         return false;
     330        6845 :     return true;
     331             : }
     332             : 
     333        9968 : bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
     334             : {
     335        9968 :     bool bRet = false;
     336        9968 :     int nRunIndex = mnRunIndex;
     337             : 
     338        9968 :     ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
     339             : 
     340        9968 :     pThis->ResetPos();
     341             : 
     342        9989 :     for (size_t i = 0; i < maRuns.size(); i+=2)
     343             :     {
     344        3292 :         if( (bRet = PosIsInRun( nCharPos )) == true )
     345        3271 :             break;
     346          21 :         pThis->NextRun();
     347             :     }
     348             : 
     349        9968 :     pThis->mnRunIndex = nRunIndex;
     350        9968 :     return bRet;
     351             : }
     352             : 
     353             : 
     354             : // -----------------------------------------------------------------------
     355             : 
     356       14244 : bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
     357             : {
     358             :     // negative nCharPos => reset to first run
     359       14244 :     if( *nCharPos < 0 )
     360        3848 :         mnRunIndex = 0;
     361             : 
     362             :     // return false when all runs completed
     363       14244 :     if( mnRunIndex >= (int)maRuns.size() )
     364           0 :         return false;
     365             : 
     366       14244 :     int nRunPos0 = maRuns[ mnRunIndex+0 ];
     367       14244 :     int nRunPos1 = maRuns[ mnRunIndex+1 ];
     368       14244 :     *bRightToLeft = (nRunPos0 > nRunPos1);
     369             : 
     370       14244 :     if( *nCharPos < 0 )
     371             :     {
     372             :         // get first valid nCharPos in run
     373        3848 :         *nCharPos = nRunPos0;
     374             :     }
     375             :     else
     376             :     {
     377             :         // advance to next nCharPos for LTR case
     378       10396 :         if( !*bRightToLeft )
     379       10277 :             ++(*nCharPos);
     380             : 
     381             :         // advance to next run if current run is completed
     382       10396 :         if( *nCharPos == nRunPos1 )
     383             :         {
     384        3858 :             if( (mnRunIndex += 2) >= (int)maRuns.size() )
     385        3847 :                 return false;
     386          11 :             nRunPos0 = maRuns[ mnRunIndex+0 ];
     387          11 :             nRunPos1 = maRuns[ mnRunIndex+1 ];
     388          11 :             *bRightToLeft = (nRunPos0 > nRunPos1);
     389          11 :             *nCharPos = nRunPos0;
     390             :         }
     391             :     }
     392             : 
     393             :     // advance to next nCharPos for RTL case
     394       10397 :     if( *bRightToLeft )
     395         119 :         --(*nCharPos);
     396             : 
     397       10397 :     return true;
     398             : }
     399             : 
     400             : // -----------------------------------------------------------------------
     401             : 
     402      135666 : bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
     403             : {
     404      135666 :     if( mnRunIndex >= (int)maRuns.size() )
     405       66190 :         return false;
     406             : 
     407       69476 :     int nRunPos0 = maRuns[ mnRunIndex+0 ];
     408       69476 :     int nRunPos1 = maRuns[ mnRunIndex+1 ];
     409       69476 :     *bRightToLeft = (nRunPos1 < nRunPos0) ;
     410       69476 :     if( !*bRightToLeft )
     411             :     {
     412       69412 :         *nMinRunPos = nRunPos0;
     413       69412 :         *nEndRunPos = nRunPos1;
     414             :     }
     415             :     else
     416             :     {
     417          64 :         *nMinRunPos = nRunPos1;
     418          64 :         *nEndRunPos = nRunPos0;
     419             :     }
     420       69476 :     return true;
     421             : }
     422             : 
     423             : // =======================================================================
     424             : 
     425       52016 : 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       52016 :     mnOrientation( 0 )
     436             : {
     437       52016 :     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       52015 :         bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
     445       52015 :         AddRun( mnMinCharPos, mnEndCharPos, bRTL );
     446             :     }
     447             :     else
     448             :     {
     449             :         // handle weak BiDi mode
     450             : 
     451           1 :         UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
     452           1 :         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           1 :         UErrorCode rcI18n = U_ZERO_ERROR;
     458           1 :         UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
     459           1 :         if( !pParaBidi )
     460       52016 :             return;
     461           1 :         ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n );    // UChar != sal_Unicode in MinGW
     462             : 
     463           1 :         UBiDi* pLineBidi = pParaBidi;
     464           1 :         int nSubLength = mnEndCharPos - mnMinCharPos;
     465           1 :         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           1 :         const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
     473             :         //maRuns.resize( 2 * nRunCount );
     474           2 :         for( int i = 0; i < nRunCount; ++i )
     475             :         {
     476             :             int32_t nMinPos, nLength;
     477           1 :             const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
     478           1 :             const int nPos0 = nMinPos + mnMinCharPos;
     479           1 :             const int nPos1 = nPos0 + nLength;
     480             : 
     481           1 :             const bool bRTL = (nDir == UBIDI_RTL);
     482           1 :             AddRun( nPos0, nPos1, bRTL );
     483             :         }
     484             : 
     485             :         // cleanup BiDi engine
     486           1 :         if( pLineBidi != pParaBidi )
     487           0 :             ubidi_close( pLineBidi );
     488           1 :         ubidi_close( pParaBidi );
     489             :     }
     490             : 
     491             :     // prepare calls to GetNextPos/GetNextRun
     492       52016 :     maRuns.ResetPos();
     493             : }
     494             : 
     495             : // -----------------------------------------------------------------------
     496             : 
     497             : // add a run after splitting it up to get rid of control chars
     498       52016 : 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       52016 :     if( !bRTL )
     504             :     {
     505      272758 :         for( int i = nCharPos0; i < nCharPos1; ++i )
     506      220750 :             if( IsControlChar( mpStr[i] ) )
     507             :             {
     508             :                 // add run until control char
     509           2 :                 maRuns.AddRun( nCharPos0, i, bRTL );
     510           2 :                 nCharPos0 = i + 1;
     511             :             }
     512             :     }
     513             :     else
     514             :     {
     515          90 :         for( int i = nCharPos1; --i >= nCharPos0; )
     516          74 :             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       52016 :     maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
     526       52016 : }
     527             : 
     528             : // -----------------------------------------------------------------------
     529             : 
     530        6528 : bool ImplLayoutArgs::PrepareFallback()
     531             : {
     532             :     // short circuit if no fallback is needed
     533        6528 :     if( maReruns.IsEmpty() )
     534             :     {
     535        3264 :         maRuns.Clear();
     536        3264 :         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        3264 :     IntVector aPosVector;
     546        3264 :     aPosVector.reserve( mnLength );
     547        3264 :     maReruns.ResetPos();
     548        6535 :     for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() )
     549        6690 :         for( int i = nMin; i < nEnd; ++i )
     550        3419 :             aPosVector.push_back( i );
     551        3264 :     maReruns.Clear();
     552             : 
     553             :     // sort the individual fallback requests
     554        3264 :     std::sort( aPosVector.begin(), aPosVector.end() );
     555             : 
     556             :     // adjust fallback runs to have the same order and limits of the original runs
     557        3264 :     ImplLayoutRuns aNewRuns;
     558        3264 :     maRuns.ResetPos();
     559        6528 :     for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
     560             :     {
     561        3264 :         if( !bRTL) {
     562        3257 :             IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
     563        6613 :             for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
     564        3356 :                 aNewRuns.AddPos( *it, bRTL );
     565             :         } else {
     566           7 :             IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
     567          77 :             while( (it != aPosVector.begin()) && (*--it >= nMin) )
     568          63 :                 aNewRuns.AddPos( *it, bRTL );
     569             :         }
     570             :     }
     571             : 
     572        3264 :     maRuns = aNewRuns;  // TODO: use vector<>::swap()
     573        3264 :     maRuns.ResetPos();
     574        3264 :     return true;
     575             : }
     576             : 
     577             : // -----------------------------------------------------------------------
     578             : 
     579      119332 : bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
     580             : {
     581      119332 :     bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
     582      119332 :     maRuns.NextRun();
     583      119332 :     return bValid;
     584             : }
     585             : 
     586             : // =======================================================================
     587             : 
     588       59662 : SalLayout::SalLayout()
     589             : :   mnMinCharPos( -1 ),
     590             :     mnEndCharPos( -1 ),
     591             :     mnLayoutFlags( 0 ),
     592             :     mnUnitsPerPixel( 1 ),
     593             :     mnOrientation( 0 ),
     594             :     mnRefCount( 1 ),
     595       59662 :     maDrawOffset( 0, 0 )
     596       59662 : {}
     597             : 
     598             : // -----------------------------------------------------------------------
     599             : 
     600       59662 : SalLayout::~SalLayout()
     601       59662 : {}
     602             : 
     603             : // -----------------------------------------------------------------------
     604             : 
     605       62074 : void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
     606             : {
     607       62074 :     mnMinCharPos  = rArgs.mnMinCharPos;
     608       62074 :     mnEndCharPos  = rArgs.mnEndCharPos;
     609       62074 :     mnLayoutFlags = rArgs.mnFlags;
     610       62074 :     mnOrientation = rArgs.mnOrientation;
     611       62074 : }
     612             : 
     613             : // -----------------------------------------------------------------------
     614             : 
     615       59662 : void SalLayout::Release() const
     616             : {
     617             :     // TODO: protect when multiple threads can access this
     618       59662 :     if( --mnRefCount > 0 )
     619       59662 :         return;
     620             :     // const_cast because some compilers violate ANSI C++ spec
     621       59662 :     delete const_cast<SalLayout*>(this);
     622             : }
     623             : 
     624             : // -----------------------------------------------------------------------
     625             : 
     626       67719 : Point SalLayout::GetDrawPosition( const Point& rRelative ) const
     627             : {
     628       67719 :     Point aPos = maDrawBase;
     629       67719 :     Point aOfs = rRelative + maDrawOffset;
     630             : 
     631       67719 :     if( mnOrientation == 0 )
     632       56235 :         aPos += aOfs;
     633             :     else
     634             :     {
     635             :         // cache trigonometric results
     636             :         static int nOldOrientation = 0;
     637             :         static double fCos = 1.0, fSin = 0.0;
     638       11484 :         if( nOldOrientation != mnOrientation )
     639             :         {
     640           7 :             nOldOrientation = mnOrientation;
     641           7 :             double fRad = mnOrientation * (M_PI / 1800.0);
     642           7 :             fCos = cos( fRad );
     643           7 :             fSin = sin( fRad );
     644             :         }
     645             : 
     646       11484 :         double fX = aOfs.X();
     647       11484 :         double fY = aOfs.Y();
     648       11484 :         long nX = static_cast<long>( +fCos * fX + fSin * fY );
     649       11484 :         long nY = static_cast<long>( +fCos * fY - fSin * fX );
     650       11484 :         aPos += Point( nX, nY );
     651             :     }
     652             : 
     653       67719 :     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         165 : bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
     702             :     ::basegfx::B2DPolyPolygonVector& rVector ) const
     703             : {
     704         165 :     bool bAllOk = true;
     705         165 :     bool bOneOk = false;
     706             : 
     707         165 :     Point aPos;
     708         165 :     ::basegfx::B2DPolyPolygon aGlyphOutline;
     709         284 :     for( int nStart = 0;;)
     710             :     {
     711             :         sal_GlyphId nLGlyph;
     712         284 :         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
     713             :             break;
     714             : 
     715             :         // get outline of individual glyph, ignoring "empty" glyphs
     716         119 :         bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
     717         119 :         bAllOk &= bSuccess;
     718         119 :         bOneOk |= bSuccess;
     719             :         // only add non-empty outlines
     720         119 :         if( bSuccess && (aGlyphOutline.count() > 0) )
     721             :         {
     722          73 :             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          73 :             rVector.push_back( aGlyphOutline );
     729             :         }
     730             :     }
     731             : 
     732         165 :     return (bAllOk && bOneOk);
     733             : }
     734             : 
     735             : // -----------------------------------------------------------------------
     736             : 
     737       17803 : bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
     738             : {
     739       17803 :     bool bRet = false;
     740       17803 :     rRect.SetEmpty();
     741             : 
     742       17803 :     Point aPos;
     743       17803 :     Rectangle aRectangle;
     744       41272 :     for( int nStart = 0;;)
     745             :     {
     746             :         sal_GlyphId nLGlyph;
     747       41272 :         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
     748             :             break;
     749             : 
     750             :         // get bounding rectangle of individual glyph
     751       23469 :         if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
     752             :         {
     753             :             // merge rectangle
     754       23469 :             aRectangle += aPos;
     755       23469 :             if (rRect.IsEmpty())
     756       16968 :                 rRect = aRectangle;
     757             :             else
     758        6501 :                 rRect.Union(aRectangle);
     759       23469 :             bRet = true;
     760             :         }
     761             :     }
     762             : 
     763       17803 :     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       56398 : GenericSalLayout::GenericSalLayout()
     794       56398 : {}
     795             : 
     796             : // -----------------------------------------------------------------------
     797             : 
     798       56398 : GenericSalLayout::~GenericSalLayout()
     799       56398 : {}
     800             : 
     801             : // -----------------------------------------------------------------------
     802             : 
     803      225346 : void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
     804             : {
     805      225346 :     m_GlyphItems.push_back(rGlyphItem);
     806      225346 : }
     807             : 
     808             : // -----------------------------------------------------------------------
     809             : 
     810       12993 : bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
     811             : {
     812             :     // initialize character extents buffer
     813       12993 :     int nCharCount = mnEndCharPos - mnMinCharPos;
     814      111832 :     for( int n = 0; n < nCharCount; ++n )
     815       98839 :         pCharWidths[n] = 0;
     816             : 
     817             :     // determine cluster extents
     818      111672 :     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       98679 :         if( !pG->IsClusterStart() )
     822           0 :             continue;
     823             : 
     824       98679 :         int n = pG->mnCharPos;
     825       98679 :         if( n >= mnEndCharPos )
     826           0 :             continue;
     827       98679 :         n -= mnMinCharPos;
     828       98679 :         if( n < 0 )
     829           0 :             continue;
     830             : 
     831             :         // left glyph in cluster defines default extent
     832       98679 :         long nXPosMin = pG->maLinearPos.X();
     833       98679 :         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      197358 :         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      197358 :         for( GlyphVector::const_iterator pN = pG; ++pN != end; )
     861             :         {
     862       85806 :             if( pN->IsClusterStart() )
     863       85806 :                 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       98679 :         if( nXPosMax < nXPosMin )
     870           0 :             nXPosMin = nXPosMax = 0;
     871             : 
     872             :         // character width is sum of glyph cluster widths
     873       98679 :         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       12993 :     return true;
     882             : }
     883             : 
     884             : // -----------------------------------------------------------------------
     885             : 
     886       29927 : long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
     887             : {
     888       29927 :     if( pCharWidths )
     889       12892 :         if( !GetCharWidths( pCharWidths ) )
     890           0 :             return 0;
     891             : 
     892       29927 :     long nWidth = GetTextWidth();
     893       29927 :     return nWidth;
     894             : }
     895             : 
     896             : // -----------------------------------------------------------------------
     897             : 
     898             : // the text width is the maximum logical extent of all glyphs
     899       31398 : long GenericSalLayout::GetTextWidth() const
     900             : {
     901       31398 :     if( m_GlyphItems.empty() )
     902        2170 :         return 0;
     903             : 
     904             :     // initialize the extent
     905       29228 :     long nMinPos = 0;
     906       29228 :     long nMaxPos = 0;
     907             : 
     908      195974 :     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      166746 :         long nXPos = pG->maLinearPos.X();
     912      166746 :         if( nMinPos > nXPos )
     913           0 :             nMinPos = nXPos;
     914      166746 :         nXPos += pG->mnNewWidth;
     915      166746 :         if( nMaxPos < nXPos )
     916      166294 :             nMaxPos = nXPos;
     917             :     }
     918             : 
     919       29228 :     long nWidth = nMaxPos - nMinPos;
     920       29228 :     return nWidth;
     921             : }
     922             : 
     923             : // -----------------------------------------------------------------------
     924             : 
     925       58508 : void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
     926             : {
     927       58508 :     SalLayout::AdjustLayout( rArgs );
     928             : 
     929       58508 :     if( rArgs.mpDXArray )
     930         548 :         ApplyDXArray( rArgs );
     931       57960 :     else if( rArgs.mnLayoutWidth )
     932        1432 :         Justify( rArgs.mnLayoutWidth );
     933       58508 : }
     934             : 
     935             : // -----------------------------------------------------------------------
     936             : 
     937         548 : void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
     938             : {
     939         548 :     if( m_GlyphItems.empty())
     940             :         return;
     941             : 
     942             :     // determine cluster boundaries and x base offset
     943         548 :     const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
     944         548 :     int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
     945             :     size_t i;
     946             :     int n,p;
     947         548 :     long nBasePointX = -1;
     948         548 :     if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
     949          78 :         nBasePointX = 0;
     950        7494 :     for(p = 0; p < nCharCount; ++p )
     951        6946 :         pLogCluster[ p ] = -1;
     952             : 
     953        7490 :     for( i = 0; i < m_GlyphItems.size(); ++i)
     954             :     {
     955        6942 :         n = m_GlyphItems[i].mnCharPos - rArgs.mnMinCharPos;
     956        6942 :         if( (n < 0) || (nCharCount <= n) )
     957           0 :             continue;
     958        6942 :         if( pLogCluster[ n ] < 0 )
     959        6942 :             pLogCluster[ n ] = i;
     960        6942 :         if( nBasePointX < 0 )
     961         470 :             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         548 :     for( n = 0; n < nCharCount; ++n )
     966         548 :         if( (p = pLogCluster[0]) >= 0 )
     967         548 :             break;
     968         548 :     if( n >= nCharCount )
     969             :         return;
     970        7494 :     for( n = 0; n < nCharCount; ++n )
     971             :     {
     972        6946 :         if( pLogCluster[ n ] < 0 )
     973           4 :             pLogCluster[ n ] = p;
     974             :         else
     975        6942 :             p = pLogCluster[ n ];
     976             :     }
     977             : 
     978             :     // calculate adjusted cluster widths
     979         548 :     sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( m_GlyphItems.size() * sizeof(long) );
     980        7490 :     for( i = 0; i < m_GlyphItems.size(); ++i )
     981        6942 :         pNewGlyphWidths[ i ] = 0;
     982             : 
     983             :     bool bRTL;
     984        8038 :     for( int nCharPos = p = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
     985             :     {
     986        6942 :         n = nCharPos - rArgs.mnMinCharPos;
     987        6942 :         if( (n < 0) || (nCharCount <= n) )  continue;
     988             : 
     989        6942 :         if( pLogCluster[ n ] >= 0 )
     990        6942 :             p = pLogCluster[ n ];
     991        6942 :         if( p >= 0 )
     992             :         {
     993        6942 :             long nDelta = rArgs.mpDXArray[ n ] ;
     994        6942 :             if( n > 0 )
     995        6394 :                 nDelta -= rArgs.mpDXArray[ n-1 ];
     996        6942 :             pNewGlyphWidths[ p ] += nDelta * mnUnitsPerPixel;
     997             :         }
     998             :     }
     999             : 
    1000             :     // move cluster positions using the adjusted widths
    1001         548 :     long nDelta = 0;
    1002         548 :     long nNewPos = 0;
    1003        7490 :     for( i = 0; i < m_GlyphItems.size(); ++i)
    1004             :     {
    1005        6942 :         if( m_GlyphItems[i].IsClusterStart() )
    1006             :         {
    1007             :             // calculate original and adjusted cluster width
    1008        6942 :             int nOldClusterWidth = m_GlyphItems[i].mnNewWidth;
    1009        6942 :             int nNewClusterWidth = pNewGlyphWidths[i];
    1010             :             size_t j;
    1011       13884 :             for( j = i; ++j < m_GlyphItems.size(); )
    1012             :             {
    1013        6394 :                 if( m_GlyphItems[j].IsClusterStart() )
    1014        6394 :                     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        6942 :             const int nDiff = nNewClusterWidth - nOldClusterWidth;
    1020             : 
    1021             :             // adjust cluster glyph widths and positions
    1022        6942 :             nDelta = nBasePointX + (nNewPos - m_GlyphItems[i].maLinearPos.X());
    1023        6942 :             if( !m_GlyphItems[i].IsRTLGlyph() )
    1024             :             {
    1025             :                 // for LTR case extend rightmost glyph in cluster
    1026        6886 :                 m_GlyphItems[j - 1].mnNewWidth += nDiff;
    1027             :             }
    1028             :             else
    1029             :             {
    1030             :                 // right align cluster in new space for RTL case
    1031          56 :                 m_GlyphItems[i].mnNewWidth += nDiff;
    1032          56 :                 nDelta += nDiff;
    1033             :             }
    1034             : 
    1035        6942 :             nNewPos += nNewClusterWidth;
    1036             :         }
    1037             : 
    1038        6942 :         m_GlyphItems[i].maLinearPos.X() += nDelta;
    1039             :     }
    1040             : }
    1041             : 
    1042             : // -----------------------------------------------------------------------
    1043             : 
    1044        1432 : void GenericSalLayout::Justify( long nNewWidth )
    1045             : {
    1046        1432 :     nNewWidth *= mnUnitsPerPixel;
    1047        1432 :     int nOldWidth = GetTextWidth();
    1048        1432 :     if( !nOldWidth || nNewWidth==nOldWidth )
    1049             :         return;
    1050             : 
    1051         297 :     if(m_GlyphItems.empty())
    1052             :     {
    1053             :         return;
    1054             :     }
    1055             :     // find rightmost glyph, it won't get stretched
    1056         297 :     GlyphVector::iterator pGRight = m_GlyphItems.begin();
    1057         297 :     pGRight += m_GlyphItems.size() - 1;
    1058         297 :     GlyphVector::iterator pG;
    1059             :     // count stretchable glyphs
    1060         297 :     int nStretchable = 0;
    1061         297 :     int nMaxGlyphWidth = 0;
    1062         416 :     for(pG = m_GlyphItems.begin(); pG != pGRight; ++pG)
    1063             :     {
    1064         119 :         if( !pG->IsDiacritic() )
    1065         119 :             ++nStretchable;
    1066         119 :         if( nMaxGlyphWidth < pG->mnOrigWidth )
    1067          59 :             nMaxGlyphWidth = pG->mnOrigWidth;
    1068             :     }
    1069             : 
    1070             :     // move rightmost glyph to requested position
    1071         297 :     nOldWidth -= pGRight->mnOrigWidth;
    1072         297 :     if( nOldWidth <= 0 )
    1073             :         return;
    1074          47 :     if( nNewWidth < nMaxGlyphWidth)
    1075           0 :         nNewWidth = nMaxGlyphWidth;
    1076          47 :     nNewWidth -= pGRight->mnOrigWidth;
    1077          47 :     pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
    1078             : 
    1079             :     // justify glyph widths and positions
    1080          47 :     int nDiffWidth = nNewWidth - nOldWidth;
    1081          47 :     if( nDiffWidth >= 0) // expanded case
    1082             :     {
    1083             :         // expand width by distributing space between glyphs evenly
    1084          19 :         int nDeltaSum = 0;
    1085          55 :         for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
    1086             :         {
    1087             :             // move glyph to justified position
    1088          36 :             pG->maLinearPos.X() += nDeltaSum;
    1089             : 
    1090             :             // do not stretch non-stretchable glyphs
    1091          36 :             if( pG->IsDiacritic() || (nStretchable <= 0) )
    1092           0 :                 continue;
    1093             : 
    1094             :             // distribute extra space equally to stretchable glyphs
    1095          36 :             int nDeltaWidth = nDiffWidth / nStretchable--;
    1096          36 :             nDiffWidth     -= nDeltaWidth;
    1097          36 :             pG->mnNewWidth += nDeltaWidth;
    1098          36 :             nDeltaSum      += nDeltaWidth;
    1099             :         }
    1100             :     }
    1101             :     else // condensed case
    1102             :     {
    1103             :         // squeeze width by moving glyphs proportionally
    1104          28 :         double fSqueeze = (double)nNewWidth / nOldWidth;
    1105          28 :         if(m_GlyphItems.size() > 1)
    1106             :         {
    1107         111 :             for( pG = m_GlyphItems.begin(); ++pG != pGRight;)
    1108             :             {
    1109          55 :                 int nX = pG->maLinearPos.X() - maBasePoint.X();
    1110          55 :                 nX = (int)(nX * fSqueeze);
    1111          55 :                 pG->maLinearPos.X() = nX + maBasePoint.X();
    1112             :             }
    1113             :         }
    1114             :         // adjust glyph widths to new positions
    1115         111 :         for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
    1116          83 :             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          30 : void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
    1218             : {
    1219             :     // initialize result array
    1220          30 :     long nXPos = -1;
    1221             :     int i;
    1222         620 :     for( i = 0; i < nMaxIndex; ++i )
    1223         590 :         pCaretXArray[ i ] = nXPos;
    1224             : 
    1225             :     // calculate caret positions using glyph array
    1226         325 :     for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1227             :     {
    1228         295 :         nXPos = pG->maLinearPos.X();
    1229         295 :         long nXRight = nXPos + pG->mnOrigWidth;
    1230         295 :         int n = pG->mnCharPos;
    1231         295 :         int nCurrIdx = 2 * (n - mnMinCharPos);
    1232         295 :         if( !pG->IsRTLGlyph() )
    1233             :         {
    1234             :             // normal positions for LTR case
    1235         295 :             pCaretXArray[ nCurrIdx ]   = nXPos;
    1236         295 :             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          30 : }
    1246             : 
    1247             : // -----------------------------------------------------------------------
    1248             : 
    1249         101 : int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
    1250             : {
    1251         101 :     int nCharCapacity = mnEndCharPos - mnMinCharPos;
    1252         101 :     sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
    1253         101 :     if( !GetCharWidths( pCharWidths ) )
    1254           0 :         return STRING_LEN;
    1255             : 
    1256         101 :     long nWidth = 0;
    1257        6001 :     for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
    1258             :     {
    1259        5985 :         nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
    1260        5985 :         if( nWidth > nMaxWidth )
    1261          85 :             return i;
    1262        5900 :         nWidth += nCharExtra;
    1263             :     }
    1264             : 
    1265          16 :     return STRING_LEN;
    1266             : }
    1267             : 
    1268             : // -----------------------------------------------------------------------
    1269             : 
    1270       86084 : int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
    1271             :     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
    1272             : {
    1273       86084 :     GlyphVector::const_iterator pG = m_GlyphItems.begin();
    1274       86084 :     GlyphVector::const_iterator pGEnd = m_GlyphItems.end();
    1275       86084 :     pG += nStart;
    1276             : 
    1277             :     // find next glyph in substring
    1278       86084 :     for(; pG != pGEnd; ++nStart, ++pG )
    1279             :     {
    1280       50742 :         int n = pG->mnCharPos;
    1281       50742 :         if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
    1282       50742 :             break;
    1283             :     }
    1284             : 
    1285             :     // return zero if no more glyph found
    1286       86084 :     if( nStart >= (int)m_GlyphItems.size() )
    1287       35342 :         return 0;
    1288             : 
    1289             :     // calculate absolute position in pixel units
    1290       50742 :     Point aRelativePos = pG->maLinearPos - maBasePoint;
    1291             : 
    1292             :     // find more glyphs which can be merged into one drawing instruction
    1293       50742 :     int nCount = 0;
    1294       50742 :     long nYPos = pG->maLinearPos.Y();
    1295       50742 :     long nOldFlags = pG->mnGlyphIndex;
    1296           0 :     for(;;)
    1297             :     {
    1298             :         // update return data with glyph info
    1299       50742 :         ++nCount;
    1300       50742 :         *(pGlyphs++) = pG->mnGlyphIndex;
    1301       50742 :         if( pCharPosAry )
    1302        6845 :             *(pCharPosAry++) = pG->mnCharPos;
    1303       50742 :         if( pGlyphAdvAry )
    1304        6845 :             *pGlyphAdvAry = pG->mnNewWidth;
    1305             : 
    1306             :         // break at end of glyph list
    1307       50742 :         if( ++nStart >= (int)m_GlyphItems.size() )
    1308       34220 :             break;
    1309             :         // break when enough glyphs
    1310       16522 :         if( nCount >= nLen )
    1311       16522 :             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       50742 :     aRelativePos.X() /= mnUnitsPerPixel;
    1346       50742 :     aRelativePos.Y() /= mnUnitsPerPixel;
    1347       50742 :     rPos = GetDrawPosition( aRelativePos );
    1348             : 
    1349       50742 :     return nCount;
    1350             : }
    1351             : 
    1352             : // -----------------------------------------------------------------------
    1353             : 
    1354        3278 : void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
    1355             : {
    1356        3278 :     if( nStart >= (int)m_GlyphItems.size() )
    1357        3278 :         return;
    1358             : 
    1359        3278 :     GlyphVector::iterator pG = m_GlyphItems.begin();
    1360        3278 :     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        3278 :     if( pG->IsRTLGlyph() )
    1366          21 :         nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
    1367             :     // calculate the x-offset to the old position
    1368        3278 :     long nXDelta = nNewXPos - pG->maLinearPos.X();
    1369             :     // adjust all following glyph positions if needed
    1370        3278 :     if( nXDelta != 0 )
    1371             :     {
    1372          85 :         for( GlyphVector::iterator pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1373             :         {
    1374          71 :             pG->maLinearPos.X() += nXDelta;
    1375             :         }
    1376             :     }
    1377             : }
    1378             : 
    1379             : // -----------------------------------------------------------------------
    1380             : 
    1381        3419 : void GenericSalLayout::DropGlyph( int nStart )
    1382             : {
    1383        3419 :     if( nStart >= (int)m_GlyphItems.size())
    1384        3419 :         return;
    1385             : 
    1386        3419 :     GlyphVector::iterator pG = m_GlyphItems.begin();
    1387        3419 :     pG += nStart;
    1388        3419 :     pG->mnGlyphIndex = GF_DROPPED;
    1389        3419 :     pG->mnCharPos = -1;
    1390             : }
    1391             : 
    1392             : // -----------------------------------------------------------------------
    1393             : 
    1394        6528 : void GenericSalLayout::Simplify( bool bIsBase )
    1395             : {
    1396        6528 :     const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
    1397             : 
    1398             :     // remove dropped glyphs inplace
    1399        6528 :     size_t j = 0;
    1400       13373 :     for(size_t i = 0; i < m_GlyphItems.size(); i++ )
    1401             :     {
    1402        6845 :         if( m_GlyphItems[i].mnGlyphIndex == nDropMarker )
    1403        3419 :             continue;
    1404             : 
    1405        3426 :         if( i != j )
    1406             :         {
    1407           7 :             m_GlyphItems[j] = m_GlyphItems[i];
    1408             :         }
    1409        3426 :         j += 1;
    1410             :     }
    1411        6528 :     m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
    1412        6528 : }
    1413             : 
    1414             : // -----------------------------------------------------------------------
    1415             : 
    1416             : // make sure GlyphItems are sorted left to right
    1417       56398 : 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      281744 :     for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1423             :     {
    1424             :         // find a cluster starting with a diacritic
    1425      225346 :         if( !pG->IsDiacritic() )
    1426      223864 :             continue;
    1427        1482 :         if( !pG->IsClusterStart() )
    1428           0 :             continue;
    1429        2964 :         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       56398 : }
    1450             : 
    1451             : // =======================================================================
    1452             : 
    1453        3264 : MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const PhysicalFontFace* pBaseFont )
    1454             : :   SalLayout()
    1455             : ,   mnLevel( 1 )
    1456        3264 : ,   mbInComplete( false )
    1457             : {
    1458             :     //maFallbackRuns[0].Clear();
    1459        3264 :     mpFallbackFonts[ 0 ] = pBaseFont;
    1460        3264 :     mpLayouts[ 0 ]  = &rBaseLayout;
    1461        3264 :     mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
    1462        3264 : }
    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       65280 : MultiSalLayout::~MultiSalLayout()
    1473             : {
    1474        9792 :     for( int i = 0; i < mnLevel; ++i )
    1475        6528 :         mpLayouts[ i ]->Release();
    1476       62016 : }
    1477             : 
    1478             : // -----------------------------------------------------------------------
    1479             : 
    1480        3264 : bool MultiSalLayout::AddFallback( SalLayout& rFallback,
    1481             :     ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
    1482             : {
    1483        3264 :     if( mnLevel >= MAX_FALLBACK )
    1484           0 :         return false;
    1485             : 
    1486        3264 :     mpFallbackFonts[ mnLevel ]  = pFallbackFont;
    1487        3264 :     mpLayouts[ mnLevel ]        = &rFallback;
    1488        3264 :     maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
    1489        3264 :     ++mnLevel;
    1490        3264 :     return true;
    1491             : }
    1492             : 
    1493             : // -----------------------------------------------------------------------
    1494             : 
    1495        3264 : bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
    1496             : {
    1497        3264 :     if( mnLevel <= 1 )
    1498           0 :         return false;
    1499        3264 :     if (!mbInComplete)
    1500        3264 :         maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
    1501        3264 :     return true;
    1502             : }
    1503             : 
    1504             : // -----------------------------------------------------------------------
    1505             : 
    1506        3264 : void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
    1507             : {
    1508        3264 :     SalLayout::AdjustLayout( rArgs );
    1509        3264 :     ImplLayoutArgs aMultiArgs = rArgs;
    1510             : 
    1511        3264 :     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         151 :         long nTargetWidth = aMultiArgs.mnLayoutWidth;
    1516         151 :         nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
    1517         151 :         aMultiArgs.mnLayoutWidth = 0;
    1518             : 
    1519             :         // we need to get the original unmodified layouts ready
    1520         453 :         for( int n = 0; n < mnLevel; ++n )
    1521         302 :             mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
    1522             :         // then we can measure the unmodified metrics
    1523         151 :         int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
    1524         151 :         sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
    1525         151 :         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         151 :         long nOrigWidth = 0;
    1530         151 :         int nStretchable = 0;
    1531         302 :         for( int i = 0; i < nCharCount; ++i )
    1532             :         {
    1533             :             // convert array from widths to sum of widths
    1534         151 :             nOrigWidth += pJustificationArray[i];
    1535         151 :             if( pJustificationArray[i] > 0 )
    1536         151 :                 ++nStretchable;
    1537             :         }
    1538             : 
    1539             :         // now we are able to distribute the extra width over the virtual char widths
    1540         151 :         if( nOrigWidth && (nTargetWidth != nOrigWidth) )
    1541             :         {
    1542          36 :             int nDiffWidth = nTargetWidth - nOrigWidth;
    1543          36 :             int nWidthSum = 0;
    1544          72 :             for( int i = 0; i < nCharCount; ++i )
    1545             :             {
    1546          36 :                 int nJustWidth = pJustificationArray[i];
    1547          36 :                 if( (nJustWidth > 0) && (nStretchable > 0) )
    1548             :                 {
    1549          36 :                     int nDeltaWidth = nDiffWidth / nStretchable;
    1550          36 :                     nJustWidth += nDeltaWidth;
    1551          36 :                     nDiffWidth -= nDeltaWidth;
    1552          36 :                     --nStretchable;
    1553             :                 }
    1554          36 :                 nWidthSum += nJustWidth;
    1555          36 :                 pJustificationArray[i] = nWidthSum;
    1556             :             }
    1557          36 :             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          36 :             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          36 :             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        3264 :     std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
    1580        3264 :     rArgs.ResetPos();
    1581             :     bool bRtl;
    1582             :     int nRunStart, nRunEnd;
    1583        9792 :     while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
    1584             :     {
    1585        3264 :         if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
    1586           7 :                             vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
    1587             :     }
    1588        3264 :     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        3264 :     int nValid[ MAX_FALLBACK ] = {0};
    1596             : 
    1597             :     sal_GlyphId nDummy;
    1598        3264 :     Point aPos;
    1599        3264 :     int nLevel = 0, n;
    1600        9792 :     for( n = 0; n < mnLevel; ++n )
    1601             :     {
    1602             :         // now adjust the individual components
    1603        6528 :         if( n > 0 )
    1604             :         {
    1605        3264 :             aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
    1606        3264 :             aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
    1607             :         }
    1608        6528 :         mpLayouts[n]->AdjustLayout( aMultiArgs );
    1609             : 
    1610             :         // disable glyph-injection for glyph-fallback SalLayout iteration
    1611        6528 :         mpLayouts[n]->DisableGlyphInjection( true );
    1612             : 
    1613             :         // remove unused parts of component
    1614        6528 :         if( n > 0 )
    1615             :         {
    1616        3264 :             if (mbInComplete && (n == mnLevel-1))
    1617           0 :                 mpLayouts[n]->Simplify( true );
    1618             :             else
    1619        3264 :                 mpLayouts[n]->Simplify( false );
    1620             :         }
    1621             : 
    1622             :         // prepare merging components
    1623        6528 :         nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
    1624       13056 :         nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
    1625       13056 :             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        6528 :         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        6528 :             if( nLevel != n )
    1639             :             {
    1640           0 :                 mpLayouts[ nLevel ]         = mpLayouts[ n ];
    1641           0 :                 mpFallbackFonts[ nLevel ]   = mpFallbackFonts[ n ];
    1642           0 :                 maFallbackRuns[ nLevel ]    = maFallbackRuns[ n ];
    1643             :             }
    1644        6528 :             ++nLevel;
    1645             :         }
    1646             :     }
    1647        3264 :     mnLevel = nLevel;
    1648             : 
    1649             :     // merge the fallback levels
    1650        3264 :     long nXPos = 0;
    1651        3264 :     double fUnitMul = 1.0;
    1652        9792 :     for( n = 0; n < nLevel; ++n )
    1653        6528 :         maFallbackRuns[n].ResetPos();
    1654        3264 :     int nActiveCharPos = nCharPos[0];
    1655        3264 :     int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
    1656        3264 :         rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
    1657        3264 :     int nRunVisibleEndChar = nCharPos[0];
    1658        9806 :     while( nValid[0] && (nLevel > 0))
    1659             :     {
    1660             :         // find best fallback level
    1661        6549 :         for( n = 0; n < nLevel; ++n )
    1662        6549 :             if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
    1663             :                 // fallback level n wins when it requested no further fallback
    1664        3278 :                 break;
    1665        3278 :         int nFBLevel = n;
    1666             : 
    1667        3278 :         if( n < nLevel )
    1668             :         {
    1669             :             // use base(n==0) or fallback(n>=1) level
    1670        3278 :             fUnitMul = mnUnitsPerPixel;
    1671        3278 :             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    1672        3278 :             long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
    1673        3278 :             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        3278 :         if( n > 0 )
    1682             :         {
    1683             :             // drop the NotDef glyphs in the base layout run if a fallback run exists
    1684       10116 :             while (
    1685        3426 :                     (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
    1686        3419 :                     (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
    1687             :                   )
    1688             :             {
    1689        3419 :                 mpLayouts[0]->DropGlyph( nStartOld[0] );
    1690        3419 :                 nStartOld[0] = nStartNew[0];
    1691        6838 :                 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
    1692        6838 :                     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        3419 :                 if( !nValid[0] )
    1697        3264 :                    break;
    1698             :             }
    1699             :         }
    1700             : 
    1701             :         // skip to end of layout run and calculate its advance width
    1702        3278 :         int nRunAdvance = 0;
    1703        3278 :         bool bKeepNotDef = (nFBLevel >= nLevel);
    1704         148 :         for(;;)
    1705             :         {
    1706        3426 :             nRunAdvance += nGlyphAdv[n];
    1707             : 
    1708             :             // proceed to next glyph
    1709        3426 :             nStartOld[n] = nStartNew[n];
    1710        3426 :             int nOrigCharPos = nCharPos[n];
    1711        6852 :             nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
    1712        6852 :                 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        3426 :             if( !nValid[n] )
    1718             :             {
    1719             :                 // performance optimization (when a fallback layout is no longer needed)
    1720        3264 :                 if( n >= nLevel-1 )
    1721        3264 :                     --nLevel;
    1722        3264 :                 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         162 :             if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
    1729             :             {
    1730           7 :                 if (nOrigCharPos < nCharPos[n])
    1731             :                 {
    1732           0 :                     if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
    1733           0 :                         break;
    1734             :                 }
    1735           7 :                 else if (nOrigCharPos > nCharPos[n])
    1736             :                 {
    1737           7 :                     if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
    1738           0 :                         break;
    1739             :                 }
    1740             :             }
    1741             : 
    1742             :             // break at end of layout run
    1743         162 :             if( n > 0 )
    1744             :             {
    1745             :                 // skip until end of fallback run
    1746         155 :                 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
    1747           7 :                     break;
    1748             :             }
    1749             :             else
    1750             :             {
    1751             :                 // break when a fallback is needed and available
    1752           7 :                 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
    1753           7 :                 if( bNeedFallback )
    1754           7 :                     if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
    1755           7 :                         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         176 :             if (aMultiArgs.mpDXArray &&
    1763             :                 nRunVisibleEndChar < mnEndCharPos &&
    1764             :                 nRunVisibleEndChar >= mnMinCharPos &&
    1765          14 :                 nCharPos[n] < mnEndCharPos &&
    1766          14 :                 nCharPos[n] >= mnMinCharPos)
    1767             :             {
    1768          14 :                 if (vRtl[nActiveCharPos - mnMinCharPos])
    1769             :                 {
    1770          28 :                     if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
    1771          14 :                         >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
    1772             :                     {
    1773          14 :                         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        3278 :         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          42 :             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          42 :             if (vRtl[nActiveCharPos - mnMinCharPos])
    1804             :             {
    1805           6 :               if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
    1806           4 :                   nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
    1807           6 :               if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
    1808           6 :                   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          36 :                 if (nRunVisibleEndChar >= mnMinCharPos)
    1816          36 :                   nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
    1817          36 :                 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          42 :             nLastRunEndChar = nRunVisibleEndChar;
    1824          42 :             nRunVisibleEndChar = nCharPos[0];
    1825             :             // the requested width is still in pixel units
    1826             :             // => convert it to base level font units
    1827          42 :             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        3236 :             if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
    1834        3231 :                 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
    1835             :         }
    1836             : 
    1837             :         // calculate new x position (in base level units)
    1838        3278 :         nXPos += nRunAdvance;
    1839             : 
    1840             :         // prepare for next fallback run
    1841        3278 :         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        9827 :         for( int i = nFBLevel; --i >= 0;)
    1846             :         {
    1847        3271 :             if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
    1848             :             {
    1849        3271 :                 if (bRtl)
    1850             :                 {
    1851          14 :                     if (nRunStart > nActiveCharPos)
    1852           7 :                         maFallbackRuns[i].NextRun();
    1853             :                 }
    1854             :                 else
    1855             :                 {
    1856        3257 :                     if (nRunEnd <= nActiveCharPos)
    1857           0 :                         maFallbackRuns[i].NextRun();
    1858             :                 }
    1859             :             }
    1860             :         }
    1861             : //            if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) )
    1862             : //                maFallbackRuns[i].NextRun();
    1863             :     }
    1864             : 
    1865        3264 :     mpLayouts[0]->Simplify( true );
    1866             : 
    1867             :     // reenable glyph-injection
    1868        9792 :     for( n = 0; n < mnLevel; ++n )
    1869        9792 :         mpLayouts[n]->DisableGlyphInjection( false );
    1870        3264 : }
    1871             : 
    1872             : // -----------------------------------------------------------------------
    1873             : 
    1874        3264 : void MultiSalLayout::InitFont() const
    1875             : {
    1876        3264 :     if( mnLevel > 0 )
    1877        3264 :         mpLayouts[0]->InitFont();
    1878        3264 : }
    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         243 : void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
    1891             : {
    1892         972 :     for( int i = mnLevel; --i >= 0; )
    1893             :     {
    1894         486 :         SalLayout& rLayout = *mpLayouts[ i ];
    1895         486 :         rLayout.DrawBase() += maDrawBase;
    1896         486 :         rLayout.DrawOffset() += maDrawOffset;
    1897         486 :         rLayout.InitFont();
    1898         486 :         rLayout.DrawText( rGraphics );
    1899         486 :         rLayout.DrawOffset() -= maDrawOffset;
    1900         486 :         rLayout.DrawBase() -= maDrawBase;
    1901             :     }
    1902             :     // NOTE: now the baselevel font is active again
    1903         243 : }
    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        2331 : long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
    1947             : {
    1948        2331 :     long nMaxWidth = 0;
    1949             : 
    1950             :     // prepare merging of fallback levels
    1951        2331 :     sal_Int32* pTempWidths = NULL;
    1952        2331 :     const int nCharCount = mnEndCharPos - mnMinCharPos;
    1953        2331 :     if( pCharWidths )
    1954             :     {
    1955         586 :         for( int i = 0; i < nCharCount; ++i )
    1956         311 :             pCharWidths[i] = 0;
    1957         275 :         pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
    1958             :     }
    1959             : 
    1960        9324 :     for( int n = mnLevel; --n >= 0; )
    1961             :     {
    1962             :         // query every fallback level
    1963        4662 :         long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
    1964        4662 :         if( !nTextWidth )
    1965        2622 :             continue;
    1966             :         // merge results from current level
    1967        2040 :         double fUnitMul = mnUnitsPerPixel;
    1968        2040 :         fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    1969        2040 :         nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
    1970        2040 :         if( nMaxWidth < nTextWidth )
    1971        1879 :             nMaxWidth = nTextWidth;
    1972        2040 :         if( !pCharWidths )
    1973        1632 :             continue;
    1974             :         // calculate virtual char widths using most probable fallback layout
    1975         888 :         for( int i = 0; i < nCharCount; ++i )
    1976             :         {
    1977             :             // #i17359# restriction:
    1978             :             // one char cannot be resolved from different fallbacks
    1979         480 :             if( pCharWidths[i] != 0 )
    1980         187 :                 continue;
    1981         293 :             long nCharWidth = pTempWidths[i];
    1982         293 :             if( !nCharWidth )
    1983           4 :                 continue;
    1984         289 :             nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
    1985         289 :             pCharWidths[i] = nCharWidth;
    1986             :         }
    1987             :     }
    1988             : 
    1989        2331 :     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          46 : bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
    2066             :     ::basegfx::B2DPolyPolygonVector& rPPV ) const
    2067             : {
    2068          46 :     bool bRet = false;
    2069             : 
    2070         184 :     for( int i = mnLevel; --i >= 0; )
    2071             :     {
    2072          92 :         SalLayout& rLayout = *mpLayouts[ i ];
    2073          92 :         rLayout.DrawBase() = maDrawBase;
    2074          92 :         rLayout.DrawOffset() += maDrawOffset;
    2075          92 :         rLayout.InitFont();
    2076          92 :         bRet |= rLayout.GetOutline( rGraphics, rPPV );
    2077          92 :         rLayout.DrawOffset() -= maDrawOffset;
    2078             :     }
    2079             : 
    2080          46 :     return bRet;
    2081             : }
    2082             : 
    2083             : // -----------------------------------------------------------------------
    2084             : 
    2085         835 : bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
    2086             : {
    2087         835 :     bool bRet = false;
    2088             : 
    2089         835 :     Rectangle aRectangle;
    2090        3340 :     for( int i = mnLevel; --i >= 0; )
    2091             :     {
    2092        1670 :         SalLayout& rLayout = *mpLayouts[ i ];
    2093        1670 :         rLayout.DrawBase() = maDrawBase;
    2094        1670 :         rLayout.DrawOffset() += maDrawOffset;
    2095        1670 :         rLayout.InitFont();
    2096        1670 :         if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
    2097             :         {
    2098         835 :             rRect.Union( aRectangle );
    2099         835 :             bRet = true;
    2100             :         }
    2101        1670 :         rLayout.DrawOffset() -= maDrawOffset;
    2102             :     }
    2103             : 
    2104         835 :     return bRet;
    2105             : }
    2106             : 
    2107             : // =======================================================================
    2108             : 
    2109             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10