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