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

Generated by: LCOV version 1.10