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

Generated by: LCOV version 1.11