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

Generated by: LCOV version 1.10