LCOV - code coverage report
Current view: top level - vcl/source/gdi - sallayout.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 1 989 0.1 %
Date: 2014-04-14 Functions: 2 62 3.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <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           0 : int GetVerticalFlags( sal_UCS4 nChar )
     189             : {
     190           0 :     if( (nChar >= 0x1100 && nChar <= 0x11f9)    // Hangul Jamo
     191           0 :      || (nChar == 0x2030 || nChar == 0x2031)    // per mille sign
     192           0 :      || (nChar >= 0x3000 && nChar <= 0xfaff)    // unified CJK
     193           0 :      || (nChar >= 0xfe20 && nChar <= 0xfe6f)    // CJK compatibility
     194           0 :      || (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           0 :     else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs
     211           0 :         return GF_ROTL; // left
     212             : 
     213           0 :     return GF_NONE; // not rotated as default
     214             : }
     215             : 
     216           0 : sal_UCS4 GetVerticalChar( sal_UCS4 )
     217             : {
     218           0 :     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           0 : VCL_DLLPUBLIC sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
     228             : {
     229             :     // currently only conversion from ASCII digits is interesting
     230           0 :     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           0 :     switch( eLang & LANGUAGE_MASK_PRIMARY )
     238             :     {
     239             :         default:
     240           0 :             nOffset = 0;
     241           0 :             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           0 :     nChar += nOffset;
     316           0 :     return nChar;
     317             : }
     318             : 
     319           0 : inline bool IsControlChar( sal_UCS4 cChar )
     320             : {
     321             :     // C0 control characters
     322           0 :     if( (0x0001 <= cChar) && (cChar <= 0x001F) )
     323           0 :         return true;
     324             :     // formatting characters
     325           0 :     if( (0x200E <= cChar) && (cChar <= 0x200F) )
     326           0 :         return true;
     327           0 :     if( (0x2028 <= cChar) && (cChar <= 0x202E) )
     328           0 :         return true;
     329             :     // deprecated formatting characters
     330           0 :     if( (0x206A <= cChar) && (cChar <= 0x206F) )
     331           0 :         return true;
     332           0 :     if( (0x2060 == cChar) )
     333           0 :         return true;
     334             :     // byte order markers and invalid unicode
     335           0 :     if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
     336           0 :         return true;
     337           0 :     return false;
     338             : }
     339             : 
     340           0 : bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
     341             : {
     342             :     // check if charpos could extend current run
     343           0 :     int nIndex = maRuns.size();
     344           0 :     if( nIndex >= 2 )
     345             :     {
     346           0 :         int nRunPos0 = maRuns[ nIndex-2 ];
     347           0 :         int nRunPos1 = maRuns[ nIndex-1 ];
     348           0 :         if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) )
     349             :         {
     350             :             // extend current run by new charpos
     351           0 :             maRuns[ nIndex-1 ] = nCharPos + int(!bRTL);
     352           0 :             return false;
     353             :         }
     354             :         // ignore new charpos when it is in current run
     355           0 :         if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
     356           0 :             return false;
     357           0 :         if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
     358           0 :             return false;
     359             :     }
     360             : 
     361             :     // else append a new run consisting of the new charpos
     362           0 :     maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
     363           0 :     maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
     364           0 :     return true;
     365             : }
     366             : 
     367           0 : bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
     368             : {
     369           0 :     if( nCharPos0 == nCharPos1 )
     370           0 :         return false;
     371             : 
     372             :     // swap if needed
     373           0 :     if( bRTL == (nCharPos0 < nCharPos1) )
     374             :     {
     375           0 :         int nTemp = nCharPos0;
     376           0 :         nCharPos0 = nCharPos1;
     377           0 :         nCharPos1 = nTemp;
     378             :     }
     379             : 
     380             :     // append new run
     381           0 :     maRuns.push_back( nCharPos0 );
     382           0 :     maRuns.push_back( nCharPos1 );
     383           0 :     return true;
     384             : }
     385             : 
     386           0 : bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
     387             : {
     388           0 :     if( mnRunIndex >= (int)maRuns.size() )
     389           0 :         return false;
     390             : 
     391           0 :     int nMinCharPos = maRuns[ mnRunIndex+0 ];
     392           0 :     int nEndCharPos = maRuns[ mnRunIndex+1 ];
     393           0 :     if( nMinCharPos > nEndCharPos ) // reversed in RTL case
     394             :     {
     395           0 :         int nTemp = nMinCharPos;
     396           0 :         nMinCharPos = nEndCharPos;
     397           0 :         nEndCharPos = nTemp;
     398             :     }
     399             : 
     400           0 :     if( nCharPos < nMinCharPos )
     401           0 :         return false;
     402           0 :     if( nCharPos >= nEndCharPos )
     403           0 :         return false;
     404           0 :     return true;
     405             : }
     406             : 
     407           0 : bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
     408             : {
     409           0 :     bool bRet = false;
     410           0 :     int nRunIndex = mnRunIndex;
     411             : 
     412           0 :     ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
     413             : 
     414           0 :     pThis->ResetPos();
     415             : 
     416           0 :     for (size_t i = 0; i < maRuns.size(); i+=2)
     417             :     {
     418           0 :         if( (bRet = PosIsInRun( nCharPos )) == true )
     419           0 :             break;
     420           0 :         pThis->NextRun();
     421             :     }
     422             : 
     423           0 :     pThis->mnRunIndex = nRunIndex;
     424           0 :     return bRet;
     425             : }
     426             : 
     427           0 : bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
     428             : {
     429             :     // negative nCharPos => reset to first run
     430           0 :     if( *nCharPos < 0 )
     431           0 :         mnRunIndex = 0;
     432             : 
     433             :     // return false when all runs completed
     434           0 :     if( mnRunIndex >= (int)maRuns.size() )
     435           0 :         return false;
     436             : 
     437           0 :     int nRunPos0 = maRuns[ mnRunIndex+0 ];
     438           0 :     int nRunPos1 = maRuns[ mnRunIndex+1 ];
     439           0 :     *bRightToLeft = (nRunPos0 > nRunPos1);
     440             : 
     441           0 :     if( *nCharPos < 0 )
     442             :     {
     443             :         // get first valid nCharPos in run
     444           0 :         *nCharPos = nRunPos0;
     445             :     }
     446             :     else
     447             :     {
     448             :         // advance to next nCharPos for LTR case
     449           0 :         if( !*bRightToLeft )
     450           0 :             ++(*nCharPos);
     451             : 
     452             :         // advance to next run if current run is completed
     453           0 :         if( *nCharPos == nRunPos1 )
     454             :         {
     455           0 :             if( (mnRunIndex += 2) >= (int)maRuns.size() )
     456           0 :                 return false;
     457           0 :             nRunPos0 = maRuns[ mnRunIndex+0 ];
     458           0 :             nRunPos1 = maRuns[ mnRunIndex+1 ];
     459           0 :             *bRightToLeft = (nRunPos0 > nRunPos1);
     460           0 :             *nCharPos = nRunPos0;
     461             :         }
     462             :     }
     463             : 
     464             :     // advance to next nCharPos for RTL case
     465           0 :     if( *bRightToLeft )
     466           0 :         --(*nCharPos);
     467             : 
     468           0 :     return true;
     469             : }
     470             : 
     471           0 : bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
     472             : {
     473           0 :     if( mnRunIndex >= (int)maRuns.size() )
     474           0 :         return false;
     475             : 
     476           0 :     int nRunPos0 = maRuns[ mnRunIndex+0 ];
     477           0 :     int nRunPos1 = maRuns[ mnRunIndex+1 ];
     478           0 :     *bRightToLeft = (nRunPos1 < nRunPos0) ;
     479           0 :     if( !*bRightToLeft )
     480             :     {
     481           0 :         *nMinRunPos = nRunPos0;
     482           0 :         *nEndRunPos = nRunPos1;
     483             :     }
     484             :     else
     485             :     {
     486           0 :         *nMinRunPos = nRunPos1;
     487           0 :         *nEndRunPos = nRunPos0;
     488             :     }
     489           0 :     return true;
     490             : }
     491             : 
     492           0 : 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           0 :     mnOrientation( 0 )
     504             : {
     505           0 :     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           0 :         bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
     513           0 :         AddRun( mnMinCharPos, mnEndCharPos, bRTL );
     514             :     }
     515             :     else
     516             :     {
     517             :         // handle weak BiDi mode
     518             : 
     519           0 :         UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
     520           0 :         if( mnFlags & SAL_LAYOUT_BIDI_RTL )
     521           0 :             nLevel = UBIDI_DEFAULT_RTL;
     522             : 
     523             :         // prepare substring for BiDi analysis
     524             :         // TODO: reuse allocated pParaBidi
     525           0 :         UErrorCode rcI18n = U_ZERO_ERROR;
     526           0 :         UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
     527           0 :         if( !pParaBidi )
     528           0 :             return;
     529           0 :         ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n );    // UChar != sal_Unicode in MinGW
     530             : 
     531           0 :         UBiDi* pLineBidi = pParaBidi;
     532           0 :         int nSubLength = mnEndCharPos - mnMinCharPos;
     533           0 :         if( nSubLength != mnLength )
     534             :         {
     535           0 :             pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
     536           0 :             ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
     537             :         }
     538             : 
     539             :         // run BiDi algorithm
     540           0 :         const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
     541             :         //maRuns.resize( 2 * nRunCount );
     542           0 :         for( int i = 0; i < nRunCount; ++i )
     543             :         {
     544             :             int32_t nMinPos, nLength;
     545           0 :             const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
     546           0 :             const int nPos0 = nMinPos + mnMinCharPos;
     547           0 :             const int nPos1 = nPos0 + nLength;
     548             : 
     549           0 :             const bool bRTL = (nDir == UBIDI_RTL);
     550           0 :             AddRun( nPos0, nPos1, bRTL );
     551             :         }
     552             : 
     553             :         // cleanup BiDi engine
     554           0 :         if( pLineBidi != pParaBidi )
     555           0 :             ubidi_close( pLineBidi );
     556           0 :         ubidi_close( pParaBidi );
     557             :     }
     558             : 
     559             :     // prepare calls to GetNextPos/GetNextRun
     560           0 :     maRuns.ResetPos();
     561             : }
     562             : 
     563             : // add a run after splitting it up to get rid of control chars
     564           0 : 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           0 :     if( !bRTL )
     570             :     {
     571           0 :         for( int i = nCharPos0; i < nCharPos1; ++i )
     572           0 :             if( IsControlChar( mpStr[i] ) )
     573             :             {
     574             :                 // add run until control char
     575           0 :                 maRuns.AddRun( nCharPos0, i, bRTL );
     576           0 :                 nCharPos0 = i + 1;
     577             :             }
     578             :     }
     579             :     else
     580             :     {
     581           0 :         for( int i = nCharPos1; --i >= nCharPos0; )
     582           0 :             if( IsControlChar( mpStr[i] ) )
     583             :             {
     584             :                 // add run until control char
     585           0 :                 maRuns.AddRun( i+1, nCharPos1, bRTL );
     586           0 :                 nCharPos1 = i;
     587             :             }
     588             :     }
     589             : 
     590             :     // add remainder of run
     591           0 :     maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
     592           0 : }
     593             : 
     594           0 : bool ImplLayoutArgs::PrepareFallback()
     595             : {
     596             :     // short circuit if no fallback is needed
     597           0 :     if( maFallbackRuns.IsEmpty() )
     598             :     {
     599           0 :         maRuns.Clear();
     600           0 :         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           0 :     IntVector aPosVector;
     610           0 :     aPosVector.reserve( mnLength );
     611           0 :     maFallbackRuns.ResetPos();
     612           0 :     for(; maFallbackRuns.GetRun( &nMin, &nEnd, &bRTL ); maFallbackRuns.NextRun() )
     613           0 :         for( int i = nMin; i < nEnd; ++i )
     614           0 :             aPosVector.push_back( i );
     615           0 :     maFallbackRuns.Clear();
     616             : 
     617             :     // sort the individual fallback requests
     618           0 :     std::sort( aPosVector.begin(), aPosVector.end() );
     619             : 
     620             :     // adjust fallback runs to have the same order and limits of the original runs
     621           0 :     ImplLayoutRuns aNewRuns;
     622           0 :     maRuns.ResetPos();
     623           0 :     for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
     624             :     {
     625           0 :         if( !bRTL) {
     626           0 :             IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
     627           0 :             for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
     628           0 :                 aNewRuns.AddPos( *it, bRTL );
     629             :         } else {
     630           0 :             IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
     631           0 :             while( (it != aPosVector.begin()) && (*--it >= nMin) )
     632           0 :                 aNewRuns.AddPos( *it, bRTL );
     633             :         }
     634             :     }
     635             : 
     636           0 :     maRuns = aNewRuns;  // TODO: use vector<>::swap()
     637           0 :     maRuns.ResetPos();
     638           0 :     return true;
     639             : }
     640             : 
     641           0 : bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
     642             : {
     643           0 :     bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
     644           0 :     maRuns.NextRun();
     645           0 :     return bValid;
     646             : }
     647             : 
     648           0 : SalLayout::SalLayout()
     649             : :   mnMinCharPos( -1 ),
     650             :     mnEndCharPos( -1 ),
     651             :     mnLayoutFlags( 0 ),
     652             :     mnUnitsPerPixel( 1 ),
     653             :     mnOrientation( 0 ),
     654             :     mnRefCount( 1 ),
     655           0 :     maDrawOffset( 0, 0 )
     656           0 : {}
     657             : 
     658           0 : SalLayout::~SalLayout()
     659           0 : {}
     660             : 
     661           0 : void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
     662             : {
     663           0 :     mnMinCharPos  = rArgs.mnMinCharPos;
     664           0 :     mnEndCharPos  = rArgs.mnEndCharPos;
     665           0 :     mnLayoutFlags = rArgs.mnFlags;
     666           0 :     mnOrientation = rArgs.mnOrientation;
     667           0 : }
     668             : 
     669           0 : void SalLayout::Release() const
     670             : {
     671             :     // TODO: protect when multiple threads can access this
     672           0 :     if( --mnRefCount > 0 )
     673           0 :         return;
     674             :     // const_cast because some compilers violate ANSI C++ spec
     675           0 :     delete const_cast<SalLayout*>(this);
     676             : }
     677             : 
     678           0 : Point SalLayout::GetDrawPosition( const Point& rRelative ) const
     679             : {
     680           0 :     Point aPos = maDrawBase;
     681           0 :     Point aOfs = rRelative + maDrawOffset;
     682             : 
     683           0 :     if( mnOrientation == 0 )
     684           0 :         aPos += aOfs;
     685             :     else
     686             :     {
     687             :         // cache trigonometric results
     688             :         static int nOldOrientation = 0;
     689             :         static double fCos = 1.0, fSin = 0.0;
     690           0 :         if( nOldOrientation != mnOrientation )
     691             :         {
     692           0 :             nOldOrientation = mnOrientation;
     693           0 :             double fRad = mnOrientation * (M_PI / 1800.0);
     694           0 :             fCos = cos( fRad );
     695           0 :             fSin = sin( fRad );
     696             :         }
     697             : 
     698           0 :         double fX = aOfs.X();
     699           0 :         double fY = aOfs.Y();
     700           0 :         long nX = static_cast<long>( +fCos * fX + fSin * fY );
     701           0 :         long nY = static_cast<long>( +fCos * fY - fSin * fX );
     702           0 :         aPos += Point( nX, nY );
     703             :     }
     704             : 
     705           0 :     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           0 : bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
     750             :     ::basegfx::B2DPolyPolygonVector& rVector ) const
     751             : {
     752           0 :     bool bAllOk = true;
     753           0 :     bool bOneOk = false;
     754             : 
     755           0 :     Point aPos;
     756           0 :     ::basegfx::B2DPolyPolygon aGlyphOutline;
     757           0 :     for( int nStart = 0;;)
     758             :     {
     759             :         sal_GlyphId nLGlyph;
     760           0 :         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
     761           0 :             break;
     762             : 
     763             :         // get outline of individual glyph, ignoring "empty" glyphs
     764           0 :         bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
     765           0 :         bAllOk &= bSuccess;
     766           0 :         bOneOk |= bSuccess;
     767             :         // only add non-empty outlines
     768           0 :         if( bSuccess && (aGlyphOutline.count() > 0) )
     769             :         {
     770           0 :             if( aPos.X() || aPos.Y() )
     771             :             {
     772           0 :                 aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
     773             :             }
     774             : 
     775             :             // insert outline at correct position
     776           0 :             rVector.push_back( aGlyphOutline );
     777             :         }
     778           0 :     }
     779             : 
     780           0 :     return (bAllOk && bOneOk);
     781             : }
     782             : 
     783           0 : bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
     784             : {
     785           0 :     bool bRet = false;
     786           0 :     rRect.SetEmpty();
     787             : 
     788           0 :     Point aPos;
     789           0 :     Rectangle aRectangle;
     790           0 :     for( int nStart = 0;;)
     791             :     {
     792             :         sal_GlyphId nLGlyph;
     793           0 :         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
     794           0 :             break;
     795             : 
     796             :         // get bounding rectangle of individual glyph
     797           0 :         if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
     798             :         {
     799             :             // merge rectangle
     800           0 :             aRectangle += aPos;
     801           0 :             if (rRect.IsEmpty())
     802           0 :                 rRect = aRectangle;
     803             :             else
     804           0 :                 rRect.Union(aRectangle);
     805           0 :             bRet = true;
     806             :         }
     807           0 :     }
     808             : 
     809           0 :     return bRet;
     810             : }
     811             : 
     812           0 : bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const
     813             : {
     814           0 :     bool bRet = false;
     815           0 :     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           0 :         bRet = ((nGlyph & GF_IDXMASK) == 3);
     825           0 :     return bRet;
     826             : }
     827             : 
     828           0 : GenericSalLayout::GenericSalLayout()
     829           0 : {}
     830             : 
     831           0 : GenericSalLayout::~GenericSalLayout()
     832           0 : {}
     833             : 
     834           0 : void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
     835             : {
     836           0 :     m_GlyphItems.push_back(rGlyphItem);
     837           0 : }
     838             : 
     839           0 : bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
     840             : {
     841             :     // initialize character extents buffer
     842           0 :     int nCharCount = mnEndCharPos - mnMinCharPos;
     843           0 :     for( int n = 0; n < nCharCount; ++n )
     844           0 :         pCharWidths[n] = 0;
     845             : 
     846             :     // determine cluster extents
     847           0 :     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           0 :         if( !pG->IsClusterStart() )
     851           0 :             continue;
     852             : 
     853           0 :         int n = pG->mnCharPos;
     854           0 :         if( n >= mnEndCharPos )
     855           0 :             continue;
     856           0 :         n -= mnMinCharPos;
     857           0 :         if( n < 0 )
     858           0 :             continue;
     859             : 
     860             :         // left glyph in cluster defines default extent
     861           0 :         long nXPosMin = pG->maLinearPos.X();
     862           0 :         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           0 :         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           0 :         for( GlyphVector::const_iterator pN = pG; ++pN != end; )
     890             :         {
     891           0 :             if( pN->IsClusterStart() )
     892           0 :                 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           0 :         if( nXPosMax < nXPosMin )
     899           0 :             nXPosMin = nXPosMax = 0;
     900             : 
     901             :         // character width is sum of glyph cluster widths
     902           0 :         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           0 :     return true;
     911             : }
     912             : 
     913           0 : long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
     914             : {
     915           0 :     if( pCharWidths )
     916           0 :         if( !GetCharWidths( pCharWidths ) )
     917           0 :             return 0;
     918             : 
     919           0 :     long nWidth = GetTextWidth();
     920           0 :     return nWidth;
     921             : }
     922             : 
     923             : // the text width is the maximum logical extent of all glyphs
     924           0 : long GenericSalLayout::GetTextWidth() const
     925             : {
     926           0 :     if( m_GlyphItems.empty() )
     927           0 :         return 0;
     928             : 
     929             :     // initialize the extent
     930           0 :     long nMinPos = 0;
     931           0 :     long nMaxPos = 0;
     932             : 
     933           0 :     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           0 :         long nXPos = pG->maLinearPos.X();
     937           0 :         if( nMinPos > nXPos )
     938           0 :             nMinPos = nXPos;
     939           0 :         nXPos += pG->mnNewWidth - pG->mnXOffset;
     940           0 :         if( nMaxPos < nXPos )
     941           0 :             nMaxPos = nXPos;
     942             :     }
     943             : 
     944           0 :     long nWidth = nMaxPos - nMinPos;
     945           0 :     return nWidth;
     946             : }
     947             : 
     948           0 : void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
     949             : {
     950           0 :     SalLayout::AdjustLayout( rArgs );
     951             : 
     952           0 :     if( rArgs.mpDXArray )
     953           0 :         ApplyDXArray( rArgs );
     954           0 :     else if( rArgs.mnLayoutWidth )
     955           0 :         Justify( rArgs.mnLayoutWidth );
     956           0 : }
     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           0 : void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
     980             : {
     981           0 :     if( m_GlyphItems.empty())
     982           0 :         return;
     983             : 
     984             :     // determine cluster boundaries and x base offset
     985           0 :     const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
     986           0 :     int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
     987             :     size_t i;
     988             :     int n,p;
     989           0 :     long nBasePointX = -1;
     990           0 :     if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
     991           0 :         nBasePointX = 0;
     992           0 :     for(p = 0; p < nCharCount; ++p )
     993           0 :         pLogCluster[ p ] = -1;
     994             : 
     995           0 :     for( i = 0; i < m_GlyphItems.size(); ++i)
     996             :     {
     997           0 :         n = m_GlyphItems[i].mnCharPos - rArgs.mnMinCharPos;
     998           0 :         if( (n < 0) || (nCharCount <= n) )
     999           0 :             continue;
    1000           0 :         if( pLogCluster[ n ] < 0 )
    1001           0 :             pLogCluster[ n ] = i;
    1002           0 :         if( nBasePointX < 0 )
    1003           0 :             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           0 :     for( n = 0; n < nCharCount; ++n )
    1008           0 :         if( (p = pLogCluster[0]) >= 0 )
    1009           0 :             break;
    1010           0 :     if( n >= nCharCount )
    1011           0 :         return;
    1012           0 :     for( n = 0; n < nCharCount; ++n )
    1013             :     {
    1014           0 :         if( pLogCluster[ n ] < 0 )
    1015           0 :             pLogCluster[ n ] = p;
    1016             :         else
    1017           0 :             p = pLogCluster[ n ];
    1018             :     }
    1019             : 
    1020             :     // calculate adjusted cluster widths
    1021           0 :     sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( m_GlyphItems.size() * sizeof(sal_Int32) );
    1022           0 :     for( i = 0; i < m_GlyphItems.size(); ++i )
    1023           0 :         pNewGlyphWidths[ i ] = 0;
    1024             : 
    1025             :     bool bRTL;
    1026           0 :     for( int nCharPos = p = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
    1027             :     {
    1028           0 :         n = nCharPos - rArgs.mnMinCharPos;
    1029           0 :         if( (n < 0) || (nCharCount <= n) )  continue;
    1030             : 
    1031           0 :         if( pLogCluster[ n ] >= 0 )
    1032           0 :             p = pLogCluster[ n ];
    1033           0 :         if( p >= 0 )
    1034             :         {
    1035           0 :             long nDelta = rArgs.mpDXArray[ n ] ;
    1036           0 :             if( n > 0 )
    1037           0 :                 nDelta -= rArgs.mpDXArray[ n-1 ];
    1038           0 :             pNewGlyphWidths[ p ] += nDelta * mnUnitsPerPixel;
    1039             :         }
    1040             :     }
    1041             : 
    1042             :     // move cluster positions using the adjusted widths
    1043           0 :     long nDelta = 0;
    1044           0 :     long nNewPos = 0;
    1045           0 :     for( i = 0; i < m_GlyphItems.size(); ++i)
    1046             :     {
    1047           0 :         if( m_GlyphItems[i].IsClusterStart() )
    1048             :         {
    1049             :             // calculate original and adjusted cluster width
    1050           0 :             int nOldClusterWidth = m_GlyphItems[i].mnNewWidth - m_GlyphItems[i].mnXOffset;
    1051           0 :             int nNewClusterWidth = pNewGlyphWidths[i];
    1052             :             size_t j;
    1053           0 :             for( j = i; ++j < m_GlyphItems.size(); )
    1054             :             {
    1055           0 :                 if( m_GlyphItems[j].IsClusterStart() )
    1056           0 :                     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           0 :             const int nDiff = nNewClusterWidth - nOldClusterWidth;
    1062             : 
    1063             :             // adjust cluster glyph widths and positions
    1064           0 :             nDelta = nBasePointX + (nNewPos - m_GlyphItems[i].maLinearPos.X());
    1065           0 :             if( !m_GlyphItems[i].IsRTLGlyph() )
    1066             :             {
    1067             :                 // for LTR case extend rightmost glyph in cluster
    1068           0 :                 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           0 :             nNewPos += nNewClusterWidth;
    1078             :         }
    1079             : 
    1080           0 :         m_GlyphItems[i].maLinearPos.X() += nDelta;
    1081             :     }
    1082             : }
    1083             : 
    1084           0 : void GenericSalLayout::Justify( long nNewWidth )
    1085             : {
    1086           0 :     nNewWidth *= mnUnitsPerPixel;
    1087           0 :     int nOldWidth = GetTextWidth();
    1088           0 :     if( !nOldWidth || nNewWidth==nOldWidth )
    1089           0 :         return;
    1090             : 
    1091           0 :     if(m_GlyphItems.empty())
    1092             :     {
    1093           0 :         return;
    1094             :     }
    1095             :     // find rightmost glyph, it won't get stretched
    1096           0 :     GlyphVector::iterator pGRight = m_GlyphItems.begin();
    1097           0 :     pGRight += m_GlyphItems.size() - 1;
    1098           0 :     GlyphVector::iterator pG;
    1099             :     // count stretchable glyphs
    1100           0 :     int nStretchable = 0;
    1101           0 :     int nMaxGlyphWidth = 0;
    1102           0 :     for(pG = m_GlyphItems.begin(); pG != pGRight; ++pG)
    1103             :     {
    1104           0 :         if( !pG->IsDiacritic() )
    1105           0 :             ++nStretchable;
    1106           0 :         if( nMaxGlyphWidth < pG->mnOrigWidth )
    1107           0 :             nMaxGlyphWidth = pG->mnOrigWidth;
    1108             :     }
    1109             : 
    1110             :     // move rightmost glyph to requested position
    1111           0 :     nOldWidth -= pGRight->mnOrigWidth;
    1112           0 :     if( nOldWidth <= 0 )
    1113           0 :         return;
    1114           0 :     if( nNewWidth < nMaxGlyphWidth)
    1115           0 :         nNewWidth = nMaxGlyphWidth;
    1116           0 :     nNewWidth -= pGRight->mnOrigWidth;
    1117           0 :     pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
    1118             : 
    1119             :     // justify glyph widths and positions
    1120           0 :     int nDiffWidth = nNewWidth - nOldWidth;
    1121           0 :     if( nDiffWidth >= 0) // expanded case
    1122             :     {
    1123             :         // expand width by distributing space between glyphs evenly
    1124           0 :         int nDeltaSum = 0;
    1125           0 :         for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
    1126             :         {
    1127             :             // move glyph to justified position
    1128           0 :             pG->maLinearPos.X() += nDeltaSum;
    1129             : 
    1130             :             // do not stretch non-stretchable glyphs
    1131           0 :             if( pG->IsDiacritic() || (nStretchable <= 0) )
    1132           0 :                 continue;
    1133             : 
    1134             :             // distribute extra space equally to stretchable glyphs
    1135           0 :             int nDeltaWidth = nDiffWidth / nStretchable--;
    1136           0 :             nDiffWidth     -= nDeltaWidth;
    1137           0 :             pG->mnNewWidth += nDeltaWidth;
    1138           0 :             nDeltaSum      += nDeltaWidth;
    1139             :         }
    1140             :     }
    1141             :     else // condensed case
    1142             :     {
    1143             :         // squeeze width by moving glyphs proportionally
    1144           0 :         double fSqueeze = (double)nNewWidth / nOldWidth;
    1145           0 :         if(m_GlyphItems.size() > 1)
    1146             :         {
    1147           0 :             for( pG = m_GlyphItems.begin(); ++pG != pGRight;)
    1148             :             {
    1149           0 :                 int nX = pG->maLinearPos.X() - maBasePoint.X();
    1150           0 :                 nX = (int)(nX * fSqueeze);
    1151           0 :                 pG->maLinearPos.X() = nX + maBasePoint.X();
    1152             :             }
    1153             :         }
    1154             :         // adjust glyph widths to new positions
    1155           0 :         for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
    1156           0 :             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           0 : void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
    1252             : {
    1253             :     // initialize result array
    1254           0 :     long nXPos = -1;
    1255             :     int i;
    1256           0 :     for( i = 0; i < nMaxIndex; ++i )
    1257           0 :         pCaretXArray[ i ] = nXPos;
    1258             : 
    1259             :     // calculate caret positions using glyph array
    1260           0 :     for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1261             :     {
    1262           0 :         nXPos = pG->maLinearPos.X();
    1263           0 :         long nXRight = nXPos + pG->mnOrigWidth;
    1264           0 :         int n = pG->mnCharPos;
    1265           0 :         int nCurrIdx = 2 * (n - mnMinCharPos);
    1266           0 :         if( !pG->IsRTLGlyph() )
    1267             :         {
    1268             :             // normal positions for LTR case
    1269           0 :             pCaretXArray[ nCurrIdx ]   = nXPos;
    1270           0 :             pCaretXArray[ nCurrIdx+1 ] = nXRight;
    1271             :         }
    1272             :         else
    1273             :         {
    1274             :             // reverse positions for RTL case
    1275           0 :             pCaretXArray[ nCurrIdx ]   = nXRight;
    1276           0 :             pCaretXArray[ nCurrIdx+1 ] = nXPos;
    1277             :         }
    1278             :     }
    1279           0 : }
    1280             : 
    1281           0 : sal_Int32 GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
    1282             : {
    1283           0 :     int nCharCapacity = mnEndCharPos - mnMinCharPos;
    1284           0 :     sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
    1285           0 :     if( !GetCharWidths( pCharWidths ) )
    1286           0 :         return -1;
    1287             : 
    1288           0 :     long nWidth = 0;
    1289           0 :     for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
    1290             :     {
    1291           0 :         nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
    1292           0 :         if( nWidth > nMaxWidth )
    1293           0 :             return i;
    1294           0 :         nWidth += nCharExtra;
    1295             :     }
    1296             : 
    1297           0 :     return -1;
    1298             : }
    1299             : 
    1300           0 : 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           0 :     GlyphVector::const_iterator pG = m_GlyphItems.begin();
    1305           0 :     GlyphVector::const_iterator pGEnd = m_GlyphItems.end();
    1306           0 :     pG += nStart;
    1307             : 
    1308             :     // find next glyph in substring
    1309           0 :     for(; pG != pGEnd; ++nStart, ++pG )
    1310             :     {
    1311           0 :         int n = pG->mnCharPos;
    1312           0 :         if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
    1313           0 :             break;
    1314             :     }
    1315             : 
    1316             :     // return zero if no more glyph found
    1317           0 :     if( nStart >= (int)m_GlyphItems.size() )
    1318           0 :         return 0;
    1319             : 
    1320           0 :     if( pG == pGEnd )
    1321           0 :         return 0;
    1322             : 
    1323             :     // calculate absolute position in pixel units
    1324           0 :     Point aRelativePos = pG->maLinearPos - maBasePoint;
    1325             : 
    1326             :     // find more glyphs which can be merged into one drawing instruction
    1327           0 :     int nCount = 0;
    1328           0 :     long nYPos = pG->maLinearPos.Y();
    1329           0 :     long nOldFlags = pG->maGlyphId;
    1330             :     for(;;)
    1331             :     {
    1332             :         // update return data with glyph info
    1333           0 :         ++nCount;
    1334           0 :         *(pGlyphs++) = pG->maGlyphId;
    1335           0 :         if( pCharPosAry )
    1336           0 :             *(pCharPosAry++) = pG->mnCharPos;
    1337           0 :         if( pGlyphAdvAry )
    1338           0 :             *pGlyphAdvAry = pG->mnNewWidth;
    1339             : 
    1340             :         // break at end of glyph list
    1341           0 :         if( ++nStart >= (int)m_GlyphItems.size() )
    1342           0 :             break;
    1343             :         // break when enough glyphs
    1344           0 :         if( nCount >= nLen )
    1345           0 :             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           0 :     aRelativePos.X() /= mnUnitsPerPixel;
    1380           0 :     aRelativePos.Y() /= mnUnitsPerPixel;
    1381           0 :     rPos = GetDrawPosition( aRelativePos );
    1382             : 
    1383           0 :     return nCount;
    1384             : }
    1385             : 
    1386           0 : void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
    1387             : {
    1388           0 :     if( nStart >= (int)m_GlyphItems.size() )
    1389           0 :         return;
    1390             : 
    1391           0 :     GlyphVector::iterator pG = m_GlyphItems.begin();
    1392           0 :     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           0 :     if( pG->IsRTLGlyph() )
    1398           0 :         nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
    1399             :     // calculate the x-offset to the old position
    1400           0 :     long nXDelta = nNewXPos - pG->maLinearPos.X();
    1401             :     // adjust all following glyph positions if needed
    1402           0 :     if( nXDelta != 0 )
    1403             :     {
    1404           0 :         for( GlyphVector::iterator pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1405             :         {
    1406           0 :             pG->maLinearPos.X() += nXDelta;
    1407             :         }
    1408             :     }
    1409             : }
    1410             : 
    1411           0 : void GenericSalLayout::DropGlyph( int nStart )
    1412             : {
    1413           0 :     if( nStart >= (int)m_GlyphItems.size())
    1414           0 :         return;
    1415             : 
    1416           0 :     GlyphVector::iterator pG = m_GlyphItems.begin();
    1417           0 :     pG += nStart;
    1418           0 :     pG->maGlyphId = GF_DROPPED;
    1419           0 :     pG->mnCharPos = -1;
    1420             : }
    1421             : 
    1422           0 : void GenericSalLayout::Simplify( bool bIsBase )
    1423             : {
    1424           0 :     const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
    1425             : 
    1426             :     // remove dropped glyphs inplace
    1427           0 :     size_t j = 0;
    1428           0 :     for(size_t i = 0; i < m_GlyphItems.size(); i++ )
    1429             :     {
    1430           0 :         if( m_GlyphItems[i].maGlyphId == nDropMarker )
    1431           0 :             continue;
    1432             : 
    1433           0 :         if( i != j )
    1434             :         {
    1435           0 :             m_GlyphItems[j] = m_GlyphItems[i];
    1436             :         }
    1437           0 :         j += 1;
    1438             :     }
    1439           0 :     m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
    1440           0 : }
    1441             : 
    1442             : // make sure GlyphItems are sorted left to right
    1443           0 : 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           0 :     for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
    1449             :     {
    1450             :         // find a cluster starting with a diacritic
    1451           0 :         if( !pG->IsDiacritic() )
    1452           0 :             continue;
    1453           0 :         if( !pG->IsClusterStart() )
    1454           0 :             continue;
    1455           0 :         for( GlyphVector::iterator pBaseGlyph = pG; ++pBaseGlyph != pGEnd; )
    1456             :         {
    1457             :             // find the base glyph matching to the misplaced diacritic
    1458           0 :             if( pBaseGlyph->IsClusterStart() )
    1459           0 :                 break;
    1460           0 :             if( pBaseGlyph->IsDiacritic() )
    1461           0 :                 continue;
    1462             : 
    1463             :             // found the matching base glyph
    1464             :             // => this base glyph becomes the new cluster start
    1465           0 :             iter_swap(pG, pBaseGlyph);
    1466             : 
    1467             :             // update glyph flags of swapped glyphitems
    1468           0 :             pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
    1469           0 :             pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
    1470             :             // prepare for checking next cluster
    1471           0 :             pG = pBaseGlyph;
    1472           0 :             break;
    1473             :         }
    1474             :     }
    1475           0 : }
    1476             : 
    1477           0 : MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const PhysicalFontFace* pBaseFont )
    1478             : :   SalLayout()
    1479             : ,   mnLevel( 1 )
    1480           0 : ,   mbInComplete( false )
    1481             : {
    1482             :     //maFallbackRuns[0].Clear();
    1483           0 :     mpFallbackFonts[ 0 ] = pBaseFont;
    1484           0 :     mpLayouts[ 0 ]  = &rBaseLayout;
    1485           0 :     mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
    1486           0 : }
    1487             : 
    1488           0 : void MultiSalLayout::SetInComplete(bool bInComplete)
    1489             : {
    1490           0 :     mbInComplete = bInComplete;
    1491           0 :     maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
    1492           0 : }
    1493             : 
    1494           0 : MultiSalLayout::~MultiSalLayout()
    1495             : {
    1496           0 :     for( int i = 0; i < mnLevel; ++i )
    1497           0 :         mpLayouts[ i ]->Release();
    1498           0 : }
    1499             : 
    1500           0 : bool MultiSalLayout::AddFallback( SalLayout& rFallback,
    1501             :     ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
    1502             : {
    1503           0 :     if( mnLevel >= MAX_FALLBACK )
    1504           0 :         return false;
    1505             : 
    1506           0 :     mpFallbackFonts[ mnLevel ]  = pFallbackFont;
    1507           0 :     mpLayouts[ mnLevel ]        = &rFallback;
    1508           0 :     maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
    1509           0 :     ++mnLevel;
    1510           0 :     return true;
    1511             : }
    1512             : 
    1513           0 : bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
    1514             : {
    1515           0 :     if( mnLevel <= 1 )
    1516           0 :         return false;
    1517           0 :     if (!mbInComplete)
    1518           0 :         maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
    1519           0 :     return true;
    1520             : }
    1521             : 
    1522           0 : void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
    1523             : {
    1524           0 :     SalLayout::AdjustLayout( rArgs );
    1525           0 :     ImplLayoutArgs aMultiArgs = rArgs;
    1526             : 
    1527           0 :     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           0 :         long nTargetWidth = aMultiArgs.mnLayoutWidth;
    1532           0 :         nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
    1533           0 :         aMultiArgs.mnLayoutWidth = 0;
    1534             : 
    1535             :         // we need to get the original unmodified layouts ready
    1536           0 :         for( int n = 0; n < mnLevel; ++n )
    1537           0 :             mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
    1538             :         // then we can measure the unmodified metrics
    1539           0 :         int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
    1540           0 :         sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
    1541           0 :         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           0 :         long nOrigWidth = 0;
    1546           0 :         int nStretchable = 0;
    1547           0 :         for( int i = 0; i < nCharCount; ++i )
    1548             :         {
    1549             :             // convert array from widths to sum of widths
    1550           0 :             nOrigWidth += pJustificationArray[i];
    1551           0 :             if( pJustificationArray[i] > 0 )
    1552           0 :                 ++nStretchable;
    1553             :         }
    1554             : 
    1555             :         // now we are able to distribute the extra width over the virtual char widths
    1556           0 :         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           0 :     std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
    1596           0 :     rArgs.ResetPos();
    1597             :     bool bRtl;
    1598             :     int nRunStart, nRunEnd;
    1599           0 :     while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
    1600             :     {
    1601           0 :         if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
    1602           0 :                             vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
    1603             :     }
    1604           0 :     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           0 :     int nValid[ MAX_FALLBACK ] = {0};
    1612             : 
    1613             :     sal_GlyphId nDummy;
    1614           0 :     Point aPos;
    1615           0 :     int nLevel = 0, n;
    1616           0 :     for( n = 0; n < mnLevel; ++n )
    1617             :     {
    1618             :         // now adjust the individual components
    1619           0 :         if( n > 0 )
    1620             :         {
    1621           0 :             aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
    1622           0 :             aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
    1623             :         }
    1624           0 :         mpLayouts[n]->AdjustLayout( aMultiArgs );
    1625             : 
    1626             :         // disable glyph-injection for glyph-fallback SalLayout iteration
    1627           0 :         mpLayouts[n]->DisableGlyphInjection( true );
    1628             : 
    1629             :         // remove unused parts of component
    1630           0 :         if( n > 0 )
    1631             :         {
    1632           0 :             if (mbInComplete && (n == mnLevel-1))
    1633           0 :                 mpLayouts[n]->Simplify( true );
    1634             :             else
    1635           0 :                 mpLayouts[n]->Simplify( false );
    1636             :         }
    1637             : 
    1638             :         // prepare merging components
    1639           0 :         nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
    1640           0 :         nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
    1641           0 :             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           0 :         if( (n > 0) && !nValid[ nLevel ] )
    1647             :         {
    1648             :             // an empty fallback layout can be released
    1649           0 :             mpLayouts[n]->Release();
    1650             :         }
    1651             :         else
    1652             :         {
    1653             :             // reshuffle used fallbacks if needed
    1654           0 :             if( nLevel != n )
    1655             :             {
    1656           0 :                 mpLayouts[ nLevel ]         = mpLayouts[ n ];
    1657           0 :                 mpFallbackFonts[ nLevel ]   = mpFallbackFonts[ n ];
    1658           0 :                 maFallbackRuns[ nLevel ]    = maFallbackRuns[ n ];
    1659             :             }
    1660           0 :             ++nLevel;
    1661             :         }
    1662             :     }
    1663           0 :     mnLevel = nLevel;
    1664             : 
    1665             :     // prepare merge the fallback levels
    1666           0 :     long nXPos = 0;
    1667           0 :     double fUnitMul = 1.0;
    1668           0 :     for( n = 0; n < nLevel; ++n )
    1669           0 :         maFallbackRuns[n].ResetPos();
    1670             :     // get the next codepoint index that needs fallback
    1671           0 :     int nActiveCharPos = nCharPos[0];
    1672             :     // get the end index of the active run
    1673           0 :     int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
    1674           0 :         rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
    1675           0 :     int nRunVisibleEndChar = nCharPos[0];
    1676             :     // merge the fallback levels
    1677           0 :     while( nValid[0] && (nLevel > 0))
    1678             :     {
    1679             :         // find best fallback level
    1680           0 :         for( n = 0; n < nLevel; ++n )
    1681           0 :             if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
    1682             :                 // fallback level n wins when it requested no further fallback
    1683           0 :                 break;
    1684           0 :         int nFBLevel = n;
    1685             : 
    1686           0 :         if( n < nLevel )
    1687             :         {
    1688             :             // use base(n==0) or fallback(n>=1) level
    1689           0 :             fUnitMul = mnUnitsPerPixel;
    1690           0 :             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    1691           0 :             long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
    1692           0 :             mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
    1693             :         }
    1694             :         else
    1695             :         {
    1696           0 :             n = 0;  // keep NotDef in base level
    1697           0 :             fUnitMul = 1.0;
    1698             :         }
    1699             : 
    1700           0 :         if( n > 0 )
    1701             :         {
    1702             :             // drop the NotDef glyphs in the base layout run if a fallback run exists
    1703           0 :             while (
    1704           0 :                     (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
    1705           0 :                     (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
    1706             :                   )
    1707             :             {
    1708           0 :                 mpLayouts[0]->DropGlyph( nStartOld[0] );
    1709           0 :                 nStartOld[0] = nStartNew[0];
    1710           0 :                 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
    1711           0 :                     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           0 :                 if( !nValid[0] )
    1716           0 :                    break;
    1717             :             }
    1718             :         }
    1719             : 
    1720             :         // skip to end of layout run and calculate its advance width
    1721           0 :         int nRunAdvance = 0;
    1722           0 :         bool bKeepNotDef = (nFBLevel >= nLevel);
    1723             :         for(;;)
    1724             :         {
    1725           0 :             nRunAdvance += nGlyphAdv[n];
    1726             : 
    1727             :             // proceed to next glyph
    1728           0 :             nStartOld[n] = nStartNew[n];
    1729           0 :             int nOrigCharPos = nCharPos[n];
    1730           0 :             nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
    1731           0 :                 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           0 :             if( !nValid[n] )
    1737             :             {
    1738             :                 // performance optimization (when a fallback layout is no longer needed)
    1739           0 :                 if( n >= nLevel-1 )
    1740           0 :                     --nLevel;
    1741           0 :                 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           0 :             if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
    1748             :             {
    1749           0 :                 if (nOrigCharPos < nCharPos[n])
    1750             :                 {
    1751           0 :                     if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
    1752           0 :                         break;
    1753             :                 }
    1754           0 :                 else if (nOrigCharPos > nCharPos[n])
    1755             :                 {
    1756           0 :                     if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
    1757           0 :                         break;
    1758             :                 }
    1759             :             }
    1760             : 
    1761             :             // break at end of layout run
    1762           0 :             if( n > 0 )
    1763             :             {
    1764             :                 // skip until end of fallback run
    1765           0 :                 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
    1766           0 :                     break;
    1767             :             }
    1768             :             else
    1769             :             {
    1770             :                 // break when a fallback is needed and available
    1771           0 :                 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
    1772           0 :                 if( bNeedFallback )
    1773           0 :                     if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
    1774           0 :                         break;
    1775             :                 // break when change from resolved to unresolved base layout run
    1776           0 :                 if( bKeepNotDef && !bNeedFallback )
    1777           0 :                     { maFallbackRuns[0].NextRun(); break; }
    1778           0 :                 bKeepNotDef = bNeedFallback;
    1779             :             }
    1780             :             // check for reordered glyphs
    1781           0 :             if (aMultiArgs.mpDXArray &&
    1782           0 :                 nRunVisibleEndChar < mnEndCharPos &&
    1783           0 :                 nRunVisibleEndChar >= mnMinCharPos &&
    1784           0 :                 nCharPos[n] < mnEndCharPos &&
    1785           0 :                 nCharPos[n] >= mnMinCharPos)
    1786             :             {
    1787           0 :                 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           0 :                 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
    1796           0 :                          <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
    1797             :                 {
    1798           0 :                     nRunVisibleEndChar = nCharPos[n];
    1799             :                 }
    1800             :             }
    1801           0 :         }
    1802             : 
    1803             :         // if a justification array is available
    1804             :         // => use it directly to calculate the corresponding run width
    1805           0 :         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           0 :             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           0 :             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           0 :                 if (nRunVisibleEndChar >= mnMinCharPos)
    1835           0 :                   nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
    1836           0 :                 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           0 :             nLastRunEndChar = nRunVisibleEndChar;
    1843           0 :             nRunVisibleEndChar = nCharPos[0];
    1844             :             // the requested width is still in pixel units
    1845             :             // => convert it to base level font units
    1846           0 :             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           0 :             if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
    1853           0 :                 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
    1854             :         }
    1855             : 
    1856             :         // calculate new x position (in base level units)
    1857           0 :         nXPos += nRunAdvance;
    1858             : 
    1859             :         // prepare for next fallback run
    1860           0 :         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           0 :         for( int i = nFBLevel; --i >= 0;)
    1865             :         {
    1866           0 :             if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
    1867             :             {
    1868           0 :                 if (bRtl)
    1869             :                 {
    1870           0 :                     if (nRunStart > nActiveCharPos)
    1871           0 :                         maFallbackRuns[i].NextRun();
    1872             :                 }
    1873             :                 else
    1874             :                 {
    1875           0 :                     if (nRunEnd <= nActiveCharPos)
    1876           0 :                         maFallbackRuns[i].NextRun();
    1877             :                 }
    1878             :             }
    1879             :         }
    1880             :     }
    1881             : 
    1882           0 :     mpLayouts[0]->Simplify( true );
    1883             : 
    1884             :     // reenable glyph-injection
    1885           0 :     for( n = 0; n < mnLevel; ++n )
    1886           0 :         mpLayouts[n]->DisableGlyphInjection( false );
    1887           0 : }
    1888             : 
    1889           0 : void MultiSalLayout::InitFont() const
    1890             : {
    1891           0 :     if( mnLevel > 0 )
    1892           0 :         mpLayouts[0]->InitFont();
    1893           0 : }
    1894             : 
    1895           0 : void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
    1896             : {
    1897           0 :     for( int i = mnLevel; --i >= 0; )
    1898             :     {
    1899           0 :         SalLayout& rLayout = *mpLayouts[ i ];
    1900           0 :         rLayout.DrawBase() += maDrawBase;
    1901           0 :         rLayout.DrawOffset() += maDrawOffset;
    1902           0 :         rLayout.InitFont();
    1903           0 :         rLayout.DrawText( rGraphics );
    1904           0 :         rLayout.DrawOffset() -= maDrawOffset;
    1905           0 :         rLayout.DrawBase() -= maDrawBase;
    1906             :     }
    1907             :     // NOTE: now the baselevel font is active again
    1908           0 : }
    1909             : 
    1910           0 : sal_Int32 MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
    1911             : {
    1912           0 :     if( mnLevel <= 0 )
    1913           0 :         return -1;
    1914           0 :     if( mnLevel == 1 )
    1915           0 :         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           0 : long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
    1948             : {
    1949           0 :     long nMaxWidth = 0;
    1950             : 
    1951             :     // prepare merging of fallback levels
    1952           0 :     sal_Int32* pTempWidths = NULL;
    1953           0 :     const int nCharCount = mnEndCharPos - mnMinCharPos;
    1954           0 :     if( pCharWidths )
    1955             :     {
    1956           0 :         for( int i = 0; i < nCharCount; ++i )
    1957           0 :             pCharWidths[i] = 0;
    1958           0 :         pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
    1959             :     }
    1960             : 
    1961           0 :     for( int n = mnLevel; --n >= 0; )
    1962             :     {
    1963             :         // query every fallback level
    1964           0 :         long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
    1965           0 :         if( !nTextWidth )
    1966           0 :             continue;
    1967             :         // merge results from current level
    1968           0 :         double fUnitMul = mnUnitsPerPixel;
    1969           0 :         fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
    1970           0 :         nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
    1971           0 :         if( nMaxWidth < nTextWidth )
    1972           0 :             nMaxWidth = nTextWidth;
    1973           0 :         if( !pCharWidths )
    1974           0 :             continue;
    1975             :         // calculate virtual char widths using most probable fallback layout
    1976           0 :         for( int i = 0; i < nCharCount; ++i )
    1977             :         {
    1978             :             // #i17359# restriction:
    1979             :             // one char cannot be resolved from different fallbacks
    1980           0 :             if( pCharWidths[i] != 0 )
    1981           0 :                 continue;
    1982           0 :             long nCharWidth = pTempWidths[i];
    1983           0 :             if( !nCharWidth )
    1984           0 :                 continue;
    1985           0 :             nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
    1986           0 :             pCharWidths[i] = nCharWidth;
    1987             :         }
    1988             :     }
    1989             : 
    1990           0 :     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           0 : 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           0 :     if( mnLevel > 1 && nLen > 1 )
    2023           0 :         nLen = 1;
    2024             : 
    2025             :     // NOTE: nStart is tagged with current font index
    2026           0 :     int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
    2027           0 :     nStart &= ~GF_FONTMASK;
    2028           0 :     for(; nLevel < mnLevel; ++nLevel, nStart=0 )
    2029             :     {
    2030           0 :         SalLayout& rLayout = *mpLayouts[ nLevel ];
    2031           0 :         rLayout.InitFont();
    2032             :         int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
    2033           0 :             nStart, pGlyphAdvAry, pCharPosAry );
    2034           0 :         if( nRetVal )
    2035             :         {
    2036           0 :             int nFontTag = nLevel << GF_FONTSHIFT;
    2037           0 :             nStart |= nFontTag;
    2038           0 :             double fUnitMul = mnUnitsPerPixel;
    2039           0 :             fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
    2040           0 :             for( int i = 0; i < nRetVal; ++i )
    2041             :             {
    2042           0 :                 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           0 :                 pGlyphIdxAry[ i ] |= nFontTag;
    2049           0 :                 if( pFallbackFonts )
    2050             :                 {
    2051           0 :                     pFallbackFonts[ i ] =  mpFallbackFonts[ nLevel ];
    2052             :                 }
    2053             :             }
    2054           0 :             rPos += maDrawBase;
    2055           0 :             rPos += maDrawOffset;
    2056           0 :             return nRetVal;
    2057             :         }
    2058             :     }
    2059             : 
    2060             :     // #111016# reset to base level font when done
    2061           0 :     mpLayouts[0]->InitFont();
    2062           0 :     return 0;
    2063             : }
    2064             : 
    2065           0 : bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
    2066             :     ::basegfx::B2DPolyPolygonVector& rPPV ) const
    2067             : {
    2068           0 :     bool bRet = false;
    2069             : 
    2070           0 :     for( int i = mnLevel; --i >= 0; )
    2071             :     {
    2072           0 :         SalLayout& rLayout = *mpLayouts[ i ];
    2073           0 :         rLayout.DrawBase() = maDrawBase;
    2074           0 :         rLayout.DrawOffset() += maDrawOffset;
    2075           0 :         rLayout.InitFont();
    2076           0 :         bRet |= rLayout.GetOutline( rGraphics, rPPV );
    2077           0 :         rLayout.DrawOffset() -= maDrawOffset;
    2078             :     }
    2079             : 
    2080           0 :     return bRet;
    2081           3 : }
    2082             : 
    2083             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10