LCOV - code coverage report
Current view: top level - vcl/source/gdi - sallayout.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 727 989 73.5 %
Date: 2014-04-11 Functions: 53 62 85.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10