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