19f62ea84SAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 39f62ea84SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 49f62ea84SAndrew Rist * or more contributor license agreements. See the NOTICE file 59f62ea84SAndrew Rist * distributed with this work for additional information 69f62ea84SAndrew Rist * regarding copyright ownership. The ASF licenses this file 79f62ea84SAndrew Rist * to you under the Apache License, Version 2.0 (the 89f62ea84SAndrew Rist * "License"); you may not use this file except in compliance 99f62ea84SAndrew Rist * with the License. You may obtain a copy of the License at 109f62ea84SAndrew Rist * 119f62ea84SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 129f62ea84SAndrew Rist * 139f62ea84SAndrew Rist * Unless required by applicable law or agreed to in writing, 149f62ea84SAndrew Rist * software distributed under the License is distributed on an 159f62ea84SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 169f62ea84SAndrew Rist * KIND, either express or implied. See the License for the 179f62ea84SAndrew Rist * specific language governing permissions and limitations 189f62ea84SAndrew Rist * under the License. 199f62ea84SAndrew Rist * 209f62ea84SAndrew Rist *************************************************************/ 219f62ea84SAndrew Rist 229f62ea84SAndrew 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; 4068813e934SHerbert Dürr // variation selectors 4078813e934SHerbert Dürr if( (0xFE00 <= cChar) && (cChar <= 0xFE0F) ) 4088813e934SHerbert Dürr return true; 4098813e934SHerbert Dürr if( (0xE0100 <= cChar) && (cChar <= 0xE01EF) ) 4108813e934SHerbert Dürr return true; 411cdf0e10cSrcweir return false; 412cdf0e10cSrcweir } 413cdf0e10cSrcweir 414cdf0e10cSrcweir // ======================================================================= 415cdf0e10cSrcweir 416cdf0e10cSrcweir bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL ) 417cdf0e10cSrcweir { 418cdf0e10cSrcweir // check if charpos could extend current run 419cdf0e10cSrcweir int nIndex = maRuns.size(); 420cdf0e10cSrcweir if( nIndex >= 2 ) 421cdf0e10cSrcweir { 422cdf0e10cSrcweir int nRunPos0 = maRuns[ nIndex-2 ]; 423cdf0e10cSrcweir int nRunPos1 = maRuns[ nIndex-1 ]; 424cdf0e10cSrcweir if( ((nCharPos + bRTL) == nRunPos1) 425cdf0e10cSrcweir && ((nRunPos0 > nRunPos1) == bRTL) ) 426cdf0e10cSrcweir { 427cdf0e10cSrcweir // extend current run by new charpos 428cdf0e10cSrcweir maRuns[ nIndex-1 ] = nCharPos + !bRTL; 429cdf0e10cSrcweir return false; 430cdf0e10cSrcweir } 431cdf0e10cSrcweir // ignore new charpos when it is in current run 432cdf0e10cSrcweir if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) ) 433cdf0e10cSrcweir return false; 434cdf0e10cSrcweir if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) ) 435cdf0e10cSrcweir return false; 436cdf0e10cSrcweir } 437cdf0e10cSrcweir 438cdf0e10cSrcweir // else append a new run consisting of the new charpos 439cdf0e10cSrcweir maRuns.push_back( nCharPos + (bRTL ? 1 : 0) ); 440cdf0e10cSrcweir maRuns.push_back( nCharPos + (bRTL ? 0 : 1) ); 441cdf0e10cSrcweir return true; 442cdf0e10cSrcweir } 443cdf0e10cSrcweir 444cdf0e10cSrcweir // ----------------------------------------------------------------------- 445cdf0e10cSrcweir 446cdf0e10cSrcweir bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) 447cdf0e10cSrcweir { 448cdf0e10cSrcweir if( nCharPos0 == nCharPos1 ) 449cdf0e10cSrcweir return false; 450cdf0e10cSrcweir 451cdf0e10cSrcweir // swap if needed 452cdf0e10cSrcweir if( bRTL == (nCharPos0 < nCharPos1) ) 453cdf0e10cSrcweir { 454cdf0e10cSrcweir int nTemp = nCharPos0; 455cdf0e10cSrcweir nCharPos0 = nCharPos1; 456cdf0e10cSrcweir nCharPos1 = nTemp; 457cdf0e10cSrcweir } 458cdf0e10cSrcweir 459cdf0e10cSrcweir // append new run 460cdf0e10cSrcweir maRuns.push_back( nCharPos0 ); 461cdf0e10cSrcweir maRuns.push_back( nCharPos1 ); 462cdf0e10cSrcweir return true; 463cdf0e10cSrcweir } 464cdf0e10cSrcweir 465cdf0e10cSrcweir // ----------------------------------------------------------------------- 466cdf0e10cSrcweir 467cdf0e10cSrcweir bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const 468cdf0e10cSrcweir { 469cdf0e10cSrcweir if( mnRunIndex >= (int)maRuns.size() ) 470cdf0e10cSrcweir return false; 471cdf0e10cSrcweir 472cdf0e10cSrcweir int nMinCharPos = maRuns[ mnRunIndex+0 ]; 473cdf0e10cSrcweir int nEndCharPos = maRuns[ mnRunIndex+1 ]; 474cdf0e10cSrcweir if( nMinCharPos > nEndCharPos ) // reversed in RTL case 475cdf0e10cSrcweir { 476cdf0e10cSrcweir int nTemp = nMinCharPos; 477cdf0e10cSrcweir nMinCharPos = nEndCharPos; 478cdf0e10cSrcweir nEndCharPos = nTemp; 479cdf0e10cSrcweir } 480cdf0e10cSrcweir 481cdf0e10cSrcweir if( nCharPos < nMinCharPos ) 482cdf0e10cSrcweir return false; 483cdf0e10cSrcweir if( nCharPos >= nEndCharPos ) 484cdf0e10cSrcweir return false; 485cdf0e10cSrcweir return true; 486cdf0e10cSrcweir } 487cdf0e10cSrcweir 488cdf0e10cSrcweir bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const 489cdf0e10cSrcweir { 490cdf0e10cSrcweir bool bRet = false; 491cdf0e10cSrcweir int nRunIndex = mnRunIndex; 492cdf0e10cSrcweir 493cdf0e10cSrcweir ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this); 494cdf0e10cSrcweir 495cdf0e10cSrcweir pThis->ResetPos(); 496cdf0e10cSrcweir 497cdf0e10cSrcweir for (size_t i = 0; i < maRuns.size(); i+=2) 498cdf0e10cSrcweir { 499cdf0e10cSrcweir if( (bRet = PosIsInRun( nCharPos )) == true ) 500cdf0e10cSrcweir break; 501cdf0e10cSrcweir pThis->NextRun(); 502cdf0e10cSrcweir } 503cdf0e10cSrcweir 504cdf0e10cSrcweir pThis->mnRunIndex = nRunIndex; 505cdf0e10cSrcweir return bRet; 506cdf0e10cSrcweir } 507cdf0e10cSrcweir 508cdf0e10cSrcweir 509cdf0e10cSrcweir // ----------------------------------------------------------------------- 510cdf0e10cSrcweir 511cdf0e10cSrcweir bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft ) 512cdf0e10cSrcweir { 513cdf0e10cSrcweir // negative nCharPos => reset to first run 514cdf0e10cSrcweir if( *nCharPos < 0 ) 515cdf0e10cSrcweir mnRunIndex = 0; 516cdf0e10cSrcweir 517cdf0e10cSrcweir // return false when all runs completed 518cdf0e10cSrcweir if( mnRunIndex >= (int)maRuns.size() ) 519cdf0e10cSrcweir return false; 520cdf0e10cSrcweir 521cdf0e10cSrcweir int nRunPos0 = maRuns[ mnRunIndex+0 ]; 522cdf0e10cSrcweir int nRunPos1 = maRuns[ mnRunIndex+1 ]; 523cdf0e10cSrcweir *bRightToLeft = (nRunPos0 > nRunPos1); 524cdf0e10cSrcweir 525cdf0e10cSrcweir if( *nCharPos < 0 ) 526cdf0e10cSrcweir { 527cdf0e10cSrcweir // get first valid nCharPos in run 528cdf0e10cSrcweir *nCharPos = nRunPos0; 529cdf0e10cSrcweir } 530cdf0e10cSrcweir else 531cdf0e10cSrcweir { 532cdf0e10cSrcweir // advance to next nCharPos for LTR case 533cdf0e10cSrcweir if( !*bRightToLeft ) 534cdf0e10cSrcweir ++(*nCharPos); 535cdf0e10cSrcweir 536cdf0e10cSrcweir // advance to next run if current run is completed 537cdf0e10cSrcweir if( *nCharPos == nRunPos1 ) 538cdf0e10cSrcweir { 539cdf0e10cSrcweir if( (mnRunIndex += 2) >= (int)maRuns.size() ) 540cdf0e10cSrcweir return false; 541cdf0e10cSrcweir nRunPos0 = maRuns[ mnRunIndex+0 ]; 542cdf0e10cSrcweir nRunPos1 = maRuns[ mnRunIndex+1 ]; 543cdf0e10cSrcweir *bRightToLeft = (nRunPos0 > nRunPos1); 544cdf0e10cSrcweir *nCharPos = nRunPos0; 545cdf0e10cSrcweir } 546cdf0e10cSrcweir } 547cdf0e10cSrcweir 548cdf0e10cSrcweir // advance to next nCharPos for RTL case 549cdf0e10cSrcweir if( *bRightToLeft ) 550cdf0e10cSrcweir --(*nCharPos); 551cdf0e10cSrcweir 552cdf0e10cSrcweir return true; 553cdf0e10cSrcweir } 554cdf0e10cSrcweir 555cdf0e10cSrcweir // ----------------------------------------------------------------------- 556cdf0e10cSrcweir 557cdf0e10cSrcweir bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const 558cdf0e10cSrcweir { 559cdf0e10cSrcweir if( mnRunIndex >= (int)maRuns.size() ) 560cdf0e10cSrcweir return false; 561cdf0e10cSrcweir 562cdf0e10cSrcweir int nRunPos0 = maRuns[ mnRunIndex+0 ]; 563cdf0e10cSrcweir int nRunPos1 = maRuns[ mnRunIndex+1 ]; 564cdf0e10cSrcweir *bRightToLeft = (nRunPos1 < nRunPos0) ; 565cdf0e10cSrcweir if( !*bRightToLeft ) 566cdf0e10cSrcweir { 567cdf0e10cSrcweir *nMinRunPos = nRunPos0; 568cdf0e10cSrcweir *nEndRunPos = nRunPos1; 569cdf0e10cSrcweir } 570cdf0e10cSrcweir else 571cdf0e10cSrcweir { 572cdf0e10cSrcweir *nMinRunPos = nRunPos1; 573cdf0e10cSrcweir *nEndRunPos = nRunPos0; 574cdf0e10cSrcweir } 575cdf0e10cSrcweir return true; 576cdf0e10cSrcweir } 577cdf0e10cSrcweir 578cdf0e10cSrcweir // ======================================================================= 579cdf0e10cSrcweir 580cdf0e10cSrcweir ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen, 581cdf0e10cSrcweir int nMinCharPos, int nEndCharPos, int nFlags ) 582cdf0e10cSrcweir : 583cdf0e10cSrcweir mnFlags( nFlags ), 584cdf0e10cSrcweir mnLength( nLen ), 585cdf0e10cSrcweir mnMinCharPos( nMinCharPos ), 586cdf0e10cSrcweir mnEndCharPos( nEndCharPos ), 587cdf0e10cSrcweir mpStr( pStr ), 588cdf0e10cSrcweir mpDXArray( NULL ), 589cdf0e10cSrcweir mnLayoutWidth( 0 ), 590cdf0e10cSrcweir mnOrientation( 0 ) 591cdf0e10cSrcweir { 592cdf0e10cSrcweir if( mnFlags & SAL_LAYOUT_BIDI_STRONG ) 593cdf0e10cSrcweir { 594cdf0e10cSrcweir // handle strong BiDi mode 595cdf0e10cSrcweir 596cdf0e10cSrcweir // do not bother to BiDi analyze strong LTR/RTL 597cdf0e10cSrcweir // TODO: can we assume these strings do not have unicode control chars? 598cdf0e10cSrcweir // if not remove the control characters from the runs 599cdf0e10cSrcweir bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0); 600cdf0e10cSrcweir AddRun( mnMinCharPos, mnEndCharPos, bRTL ); 601cdf0e10cSrcweir } 602cdf0e10cSrcweir else 603cdf0e10cSrcweir { 604cdf0e10cSrcweir // handle weak BiDi mode 605cdf0e10cSrcweir 606cdf0e10cSrcweir UBiDiLevel nLevel = UBIDI_DEFAULT_LTR; 607cdf0e10cSrcweir if( mnFlags & SAL_LAYOUT_BIDI_RTL ) 608cdf0e10cSrcweir nLevel = UBIDI_DEFAULT_RTL; 609cdf0e10cSrcweir 610cdf0e10cSrcweir // prepare substring for BiDi analysis 611cdf0e10cSrcweir // TODO: reuse allocated pParaBidi 612cdf0e10cSrcweir UErrorCode rcI18n = U_ZERO_ERROR; 613cdf0e10cSrcweir UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n ); 614cdf0e10cSrcweir if( !pParaBidi ) 615cdf0e10cSrcweir return; 616cdf0e10cSrcweir ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n ); // UChar != sal_Unicode in MinGW 617cdf0e10cSrcweir 618cdf0e10cSrcweir UBiDi* pLineBidi = pParaBidi; 619cdf0e10cSrcweir int nSubLength = mnEndCharPos - mnMinCharPos; 620cdf0e10cSrcweir if( nSubLength != mnLength ) 621cdf0e10cSrcweir { 622cdf0e10cSrcweir pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n ); 623cdf0e10cSrcweir ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n ); 624cdf0e10cSrcweir } 625cdf0e10cSrcweir 626cdf0e10cSrcweir // run BiDi algorithm 627cdf0e10cSrcweir const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n ); 628cdf0e10cSrcweir //maRuns.resize( 2 * nRunCount ); 629cdf0e10cSrcweir for( int i = 0; i < nRunCount; ++i ) 630cdf0e10cSrcweir { 631cdf0e10cSrcweir int32_t nMinPos, nLength; 632cdf0e10cSrcweir const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength ); 633cdf0e10cSrcweir const int nPos0 = nMinPos + mnMinCharPos; 634cdf0e10cSrcweir const int nPos1 = nPos0 + nLength; 635cdf0e10cSrcweir 636cdf0e10cSrcweir const bool bRTL = (nDir == UBIDI_RTL); 637cdf0e10cSrcweir AddRun( nPos0, nPos1, bRTL ); 638cdf0e10cSrcweir } 639cdf0e10cSrcweir 640cdf0e10cSrcweir // cleanup BiDi engine 641cdf0e10cSrcweir if( pLineBidi != pParaBidi ) 642cdf0e10cSrcweir ubidi_close( pLineBidi ); 643cdf0e10cSrcweir ubidi_close( pParaBidi ); 644cdf0e10cSrcweir } 645cdf0e10cSrcweir 646cdf0e10cSrcweir // prepare calls to GetNextPos/GetNextRun 647cdf0e10cSrcweir maRuns.ResetPos(); 648cdf0e10cSrcweir } 649cdf0e10cSrcweir 650cdf0e10cSrcweir // ----------------------------------------------------------------------- 651cdf0e10cSrcweir 652cdf0e10cSrcweir // add a run after splitting it up to get rid of control chars 653cdf0e10cSrcweir void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) 654cdf0e10cSrcweir { 655cdf0e10cSrcweir DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" ); 656cdf0e10cSrcweir 657cdf0e10cSrcweir // remove control characters from runs by splitting them up 658cdf0e10cSrcweir if( !bRTL ) 659cdf0e10cSrcweir { 660cdf0e10cSrcweir for( int i = nCharPos0; i < nCharPos1; ++i ) 661cdf0e10cSrcweir if( IsControlChar( mpStr[i] ) ) 662cdf0e10cSrcweir { 663cdf0e10cSrcweir // add run until control char 664cdf0e10cSrcweir maRuns.AddRun( nCharPos0, i, bRTL ); 665cdf0e10cSrcweir nCharPos0 = i + 1; 666cdf0e10cSrcweir } 667cdf0e10cSrcweir } 668cdf0e10cSrcweir else 669cdf0e10cSrcweir { 670cdf0e10cSrcweir for( int i = nCharPos1; --i >= nCharPos0; ) 671cdf0e10cSrcweir if( IsControlChar( mpStr[i] ) ) 672cdf0e10cSrcweir { 673cdf0e10cSrcweir // add run until control char 674cdf0e10cSrcweir maRuns.AddRun( i+1, nCharPos1, bRTL ); 675cdf0e10cSrcweir nCharPos1 = i; 676cdf0e10cSrcweir } 677cdf0e10cSrcweir } 678cdf0e10cSrcweir 679cdf0e10cSrcweir // add remainder of run 680cdf0e10cSrcweir maRuns.AddRun( nCharPos0, nCharPos1, bRTL ); 681cdf0e10cSrcweir } 682cdf0e10cSrcweir 683cdf0e10cSrcweir // ----------------------------------------------------------------------- 684cdf0e10cSrcweir 685cdf0e10cSrcweir bool ImplLayoutArgs::PrepareFallback() 686cdf0e10cSrcweir { 687cdf0e10cSrcweir // short circuit if no fallback is needed 688cdf0e10cSrcweir if( maReruns.IsEmpty() ) 689cdf0e10cSrcweir { 690cdf0e10cSrcweir maRuns.Clear(); 691cdf0e10cSrcweir return false; 692cdf0e10cSrcweir } 693cdf0e10cSrcweir 694cdf0e10cSrcweir // convert the fallback requests to layout requests 695cdf0e10cSrcweir bool bRTL; 696cdf0e10cSrcweir int nMin, nEnd; 697cdf0e10cSrcweir 698cdf0e10cSrcweir // get the individual fallback requests 699cdf0e10cSrcweir typedef std::vector<int> IntVector; 700cdf0e10cSrcweir IntVector aPosVector; 701cdf0e10cSrcweir aPosVector.reserve( mnLength ); 702cdf0e10cSrcweir maReruns.ResetPos(); 703cdf0e10cSrcweir for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() ) 704cdf0e10cSrcweir for( int i = nMin; i < nEnd; ++i ) 705cdf0e10cSrcweir aPosVector.push_back( i ); 706cdf0e10cSrcweir maReruns.Clear(); 707cdf0e10cSrcweir 708cdf0e10cSrcweir // sort the individual fallback requests 709cdf0e10cSrcweir std::sort( aPosVector.begin(), aPosVector.end() ); 710cdf0e10cSrcweir 711cdf0e10cSrcweir // adjust fallback runs to have the same order and limits of the original runs 712cdf0e10cSrcweir ImplLayoutRuns aNewRuns; 713cdf0e10cSrcweir maRuns.ResetPos(); 714cdf0e10cSrcweir for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() ) 715cdf0e10cSrcweir { 716cdf0e10cSrcweir if( !bRTL) { 717cdf0e10cSrcweir IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin ); 718cdf0e10cSrcweir for(; (it != aPosVector.end()) && (*it < nEnd); ++it ) 719cdf0e10cSrcweir aNewRuns.AddPos( *it, bRTL ); 720cdf0e10cSrcweir } else { 721cdf0e10cSrcweir IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd ); 722cdf0e10cSrcweir while( (it != aPosVector.begin()) && (*--it >= nMin) ) 723cdf0e10cSrcweir aNewRuns.AddPos( *it, bRTL ); 724cdf0e10cSrcweir } 725cdf0e10cSrcweir } 726cdf0e10cSrcweir 727cdf0e10cSrcweir maRuns = aNewRuns; // TODO: use vector<>::swap() 728cdf0e10cSrcweir maRuns.ResetPos(); 729cdf0e10cSrcweir return true; 730cdf0e10cSrcweir } 731cdf0e10cSrcweir 732cdf0e10cSrcweir // ----------------------------------------------------------------------- 733cdf0e10cSrcweir 734cdf0e10cSrcweir bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL ) 735cdf0e10cSrcweir { 736cdf0e10cSrcweir bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL ); 737cdf0e10cSrcweir maRuns.NextRun(); 738cdf0e10cSrcweir return bValid; 739cdf0e10cSrcweir } 740cdf0e10cSrcweir 741cdf0e10cSrcweir // ======================================================================= 742cdf0e10cSrcweir 743cdf0e10cSrcweir SalLayout::SalLayout() 744cdf0e10cSrcweir : mnMinCharPos( -1 ), 745cdf0e10cSrcweir mnEndCharPos( -1 ), 746cdf0e10cSrcweir mnLayoutFlags( 0 ), 747cdf0e10cSrcweir mnUnitsPerPixel( 1 ), 748cdf0e10cSrcweir mnOrientation( 0 ), 749cdf0e10cSrcweir mnRefCount( 1 ), 750cdf0e10cSrcweir maDrawOffset( 0, 0 ) 751cdf0e10cSrcweir {} 752cdf0e10cSrcweir 753cdf0e10cSrcweir // ----------------------------------------------------------------------- 754cdf0e10cSrcweir 755cdf0e10cSrcweir SalLayout::~SalLayout() 756cdf0e10cSrcweir {} 757cdf0e10cSrcweir 758cdf0e10cSrcweir // ----------------------------------------------------------------------- 759cdf0e10cSrcweir 760cdf0e10cSrcweir void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 761cdf0e10cSrcweir { 762cdf0e10cSrcweir mnMinCharPos = rArgs.mnMinCharPos; 763cdf0e10cSrcweir mnEndCharPos = rArgs.mnEndCharPos; 764cdf0e10cSrcweir mnLayoutFlags = rArgs.mnFlags; 765cdf0e10cSrcweir mnOrientation = rArgs.mnOrientation; 766cdf0e10cSrcweir } 767cdf0e10cSrcweir 768cdf0e10cSrcweir // ----------------------------------------------------------------------- 769cdf0e10cSrcweir 770cdf0e10cSrcweir void SalLayout::Reference() const 771cdf0e10cSrcweir { 772cdf0e10cSrcweir // TODO: protect when multiple threads can access this 773cdf0e10cSrcweir ++mnRefCount; 774cdf0e10cSrcweir } 775cdf0e10cSrcweir 776cdf0e10cSrcweir // ----------------------------------------------------------------------- 777cdf0e10cSrcweir 778cdf0e10cSrcweir void SalLayout::Release() const 779cdf0e10cSrcweir { 780cdf0e10cSrcweir // TODO: protect when multiple threads can access this 781cdf0e10cSrcweir if( --mnRefCount > 0 ) 782cdf0e10cSrcweir return; 783cdf0e10cSrcweir // const_cast because some compilers violate ANSI C++ spec 784cdf0e10cSrcweir delete const_cast<SalLayout*>(this); 785cdf0e10cSrcweir } 786cdf0e10cSrcweir 787cdf0e10cSrcweir // ----------------------------------------------------------------------- 788cdf0e10cSrcweir 789cdf0e10cSrcweir Point SalLayout::GetDrawPosition( const Point& rRelative ) const 790cdf0e10cSrcweir { 791cdf0e10cSrcweir Point aPos = maDrawBase; 792cdf0e10cSrcweir Point aOfs = rRelative + maDrawOffset; 793cdf0e10cSrcweir 794cdf0e10cSrcweir if( mnOrientation == 0 ) 795cdf0e10cSrcweir aPos += aOfs; 796cdf0e10cSrcweir else 797cdf0e10cSrcweir { 798cdf0e10cSrcweir // cache trigonometric results 799cdf0e10cSrcweir static int nOldOrientation = 0; 800cdf0e10cSrcweir static double fCos = 1.0, fSin = 0.0; 801cdf0e10cSrcweir if( nOldOrientation != mnOrientation ) 802cdf0e10cSrcweir { 803cdf0e10cSrcweir nOldOrientation = mnOrientation; 804cdf0e10cSrcweir double fRad = mnOrientation * (M_PI / 1800.0); 805cdf0e10cSrcweir fCos = cos( fRad ); 806cdf0e10cSrcweir fSin = sin( fRad ); 807cdf0e10cSrcweir } 808cdf0e10cSrcweir 809cdf0e10cSrcweir double fX = aOfs.X(); 810cdf0e10cSrcweir double fY = aOfs.Y(); 811cdf0e10cSrcweir long nX = static_cast<long>( +fCos * fX + fSin * fY ); 812cdf0e10cSrcweir long nY = static_cast<long>( +fCos * fY - fSin * fX ); 813cdf0e10cSrcweir aPos += Point( nX, nY ); 814cdf0e10cSrcweir } 815cdf0e10cSrcweir 816cdf0e10cSrcweir return aPos; 817cdf0e10cSrcweir } 818cdf0e10cSrcweir 819cdf0e10cSrcweir // ----------------------------------------------------------------------- 820cdf0e10cSrcweir 821cdf0e10cSrcweir // returns asian kerning values in quarter of character width units 822cdf0e10cSrcweir // to enable automatic halfwidth substitution for fullwidth punctuation 823cdf0e10cSrcweir // return value is negative for l, positive for r, zero for neutral 824cdf0e10cSrcweir 825cdf0e10cSrcweir // If the range doesn't match in 0x3000 and 0x30FB, please change 826cdf0e10cSrcweir // also ImplCalcKerning. 827cdf0e10cSrcweir 828cdf0e10cSrcweir int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ ) 829cdf0e10cSrcweir { 830cdf0e10cSrcweir // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html 831cdf0e10cSrcweir static signed char nTable[0x30] = 832cdf0e10cSrcweir { 833cdf0e10cSrcweir 0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2, 834cdf0e10cSrcweir +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2, 835cdf0e10cSrcweir 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2 836cdf0e10cSrcweir }; 837cdf0e10cSrcweir 838cdf0e10cSrcweir int nResult = 0; 839cdf0e10cSrcweir if( (c >= 0x3000) && (c < 0x3030) ) 840cdf0e10cSrcweir nResult = nTable[ c - 0x3000 ]; 841cdf0e10cSrcweir else switch( c ) 842cdf0e10cSrcweir { 843cdf0e10cSrcweir #if 0 // TODO: enable it for real-fixed-width fonts? 844cdf0e10cSrcweir case ':': case ';': case '!': 845cdf0e10cSrcweir if( !bVertical ) 846cdf0e10cSrcweir nResult = bLeft ? -1 : +1; // 25% left and right 847cdf0e10cSrcweir break; 848cdf0e10cSrcweir #endif 849cdf0e10cSrcweir case 0x30FB: 850cdf0e10cSrcweir nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom 851cdf0e10cSrcweir break; 852cdf0e10cSrcweir case 0x2019: case 0x201D: 853cdf0e10cSrcweir case 0xFF01: case 0xFF09: case 0xFF0C: 854cdf0e10cSrcweir case 0xFF1A: case 0xFF1B: 855cdf0e10cSrcweir nResult = -2; 856cdf0e10cSrcweir break; 857cdf0e10cSrcweir case 0x2018: case 0x201C: 858cdf0e10cSrcweir case 0xFF08: 859cdf0e10cSrcweir nResult = +2; 860cdf0e10cSrcweir break; 861cdf0e10cSrcweir default: 862cdf0e10cSrcweir break; 863cdf0e10cSrcweir } 864cdf0e10cSrcweir 865cdf0e10cSrcweir return nResult; 866cdf0e10cSrcweir } 867cdf0e10cSrcweir 868cdf0e10cSrcweir // ----------------------------------------------------------------------- 869cdf0e10cSrcweir 870cdf0e10cSrcweir bool SalLayout::GetOutline( SalGraphics& rSalGraphics, 871cdf0e10cSrcweir ::basegfx::B2DPolyPolygonVector& rVector ) const 872cdf0e10cSrcweir { 873cdf0e10cSrcweir bool bAllOk = true; 874cdf0e10cSrcweir bool bOneOk = false; 875cdf0e10cSrcweir 876cdf0e10cSrcweir Point aPos; 877cdf0e10cSrcweir ::basegfx::B2DPolyPolygon aGlyphOutline; 878cdf0e10cSrcweir for( int nStart = 0;;) 879cdf0e10cSrcweir { 880cdf0e10cSrcweir sal_GlyphId nLGlyph; 881cdf0e10cSrcweir if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) ) 882cdf0e10cSrcweir break; 883cdf0e10cSrcweir 884cdf0e10cSrcweir // get outline of individual glyph, ignoring "empty" glyphs 885cdf0e10cSrcweir bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline ); 886cdf0e10cSrcweir bAllOk &= bSuccess; 887cdf0e10cSrcweir bOneOk |= bSuccess; 888cdf0e10cSrcweir // only add non-empty outlines 889cdf0e10cSrcweir if( bSuccess && (aGlyphOutline.count() > 0) ) 890cdf0e10cSrcweir { 891cdf0e10cSrcweir if( aPos.X() || aPos.Y() ) 892cdf0e10cSrcweir { 893cdf0e10cSrcweir aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y())); 894cdf0e10cSrcweir } 895cdf0e10cSrcweir 896cdf0e10cSrcweir // insert outline at correct position 897cdf0e10cSrcweir rVector.push_back( aGlyphOutline ); 898cdf0e10cSrcweir } 899cdf0e10cSrcweir } 900cdf0e10cSrcweir 901cdf0e10cSrcweir return (bAllOk & bOneOk); 902cdf0e10cSrcweir } 903cdf0e10cSrcweir 904cdf0e10cSrcweir // ----------------------------------------------------------------------- 905cdf0e10cSrcweir 906cdf0e10cSrcweir bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const 907cdf0e10cSrcweir { 908cdf0e10cSrcweir bool bRet = false; 909cdf0e10cSrcweir rRect.SetEmpty(); 910cdf0e10cSrcweir 911cdf0e10cSrcweir Point aPos; 912cdf0e10cSrcweir Rectangle aRectangle; 913cdf0e10cSrcweir for( int nStart = 0;;) 914cdf0e10cSrcweir { 915cdf0e10cSrcweir sal_GlyphId nLGlyph; 916cdf0e10cSrcweir if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) ) 917cdf0e10cSrcweir break; 918cdf0e10cSrcweir 919cdf0e10cSrcweir // get bounding rectangle of individual glyph 920cdf0e10cSrcweir if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) ) 921cdf0e10cSrcweir { 922cdf0e10cSrcweir // merge rectangle 923cdf0e10cSrcweir aRectangle += aPos; 924cdf0e10cSrcweir rRect.Union( aRectangle ); 925cdf0e10cSrcweir bRet = true; 926cdf0e10cSrcweir } 927cdf0e10cSrcweir } 928cdf0e10cSrcweir 929cdf0e10cSrcweir return bRet; 930cdf0e10cSrcweir } 931cdf0e10cSrcweir 932cdf0e10cSrcweir // ----------------------------------------------------------------------- 933cdf0e10cSrcweir 934cdf0e10cSrcweir bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const 935cdf0e10cSrcweir { 936cdf0e10cSrcweir bool bRet = false; 937cdf0e10cSrcweir if( nGlyph & GF_ISCHAR ) 938cdf0e10cSrcweir { 939cdf0e10cSrcweir long nChar = nGlyph & GF_IDXMASK; 940cdf0e10cSrcweir bRet = (nChar <= 0x0020) // blank 941cdf0e10cSrcweir //|| (nChar == 0x00A0) // non breaking space 942cdf0e10cSrcweir || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace 943cdf0e10cSrcweir || (nChar == 0x3000); // ideographic space 944cdf0e10cSrcweir } 945cdf0e10cSrcweir else 946cdf0e10cSrcweir bRet = ((nGlyph & GF_IDXMASK) == 3); 947cdf0e10cSrcweir return bRet; 948cdf0e10cSrcweir } 949cdf0e10cSrcweir 950cdf0e10cSrcweir // ----------------------------------------------------------------------- 951cdf0e10cSrcweir 952*248a599fSHerbert Dürr const ImplFontData* SalLayout::GetFallbackFontData( sal_GlyphId /*aGlyphId*/ ) const 953cdf0e10cSrcweir { 954cdf0e10cSrcweir #if 0 955*248a599fSHerbert Dürr int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT 956cdf0e10cSrcweir assert( nFallbackLevel == 0 ); 957cdf0e10cSrcweir #endif 958cdf0e10cSrcweir return NULL; 959cdf0e10cSrcweir } 960cdf0e10cSrcweir 961cdf0e10cSrcweir // ======================================================================= 962cdf0e10cSrcweir 963cdf0e10cSrcweir GenericSalLayout::GenericSalLayout() 964cdf0e10cSrcweir : mpGlyphItems(0), 965cdf0e10cSrcweir mnGlyphCount(0), 966cdf0e10cSrcweir mnGlyphCapacity(0) 967cdf0e10cSrcweir {} 968cdf0e10cSrcweir 969cdf0e10cSrcweir // ----------------------------------------------------------------------- 970cdf0e10cSrcweir 971cdf0e10cSrcweir GenericSalLayout::~GenericSalLayout() 972cdf0e10cSrcweir { 973cdf0e10cSrcweir delete[] mpGlyphItems; 974cdf0e10cSrcweir } 975cdf0e10cSrcweir 976cdf0e10cSrcweir // ----------------------------------------------------------------------- 977cdf0e10cSrcweir 978cdf0e10cSrcweir void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem ) 979cdf0e10cSrcweir { 980cdf0e10cSrcweir // TODO: use std::list<GlyphItem> 981cdf0e10cSrcweir if( mnGlyphCount >= mnGlyphCapacity ) 982cdf0e10cSrcweir { 983cdf0e10cSrcweir mnGlyphCapacity += 16 + 3 * mnGlyphCount; 984cdf0e10cSrcweir GlyphItem* pNewGI = new GlyphItem[ mnGlyphCapacity ]; 985cdf0e10cSrcweir if( mpGlyphItems ) 986cdf0e10cSrcweir { 987cdf0e10cSrcweir for( int i = 0; i < mnGlyphCount; ++i ) 988cdf0e10cSrcweir pNewGI[ i ] = mpGlyphItems[ i ]; 989cdf0e10cSrcweir delete[] mpGlyphItems; 990cdf0e10cSrcweir } 991cdf0e10cSrcweir mpGlyphItems = pNewGI; 992cdf0e10cSrcweir } 993cdf0e10cSrcweir 994cdf0e10cSrcweir mpGlyphItems[ mnGlyphCount++ ] = rGlyphItem; 995cdf0e10cSrcweir } 996cdf0e10cSrcweir 997cdf0e10cSrcweir // ----------------------------------------------------------------------- 998cdf0e10cSrcweir 999cdf0e10cSrcweir bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const 1000cdf0e10cSrcweir { 1001cdf0e10cSrcweir // initialize character extents buffer 1002cdf0e10cSrcweir int nCharCount = mnEndCharPos - mnMinCharPos; 1003cdf0e10cSrcweir for( int n = 0; n < nCharCount; ++n ) 1004cdf0e10cSrcweir pCharWidths[n] = 0; 1005cdf0e10cSrcweir 1006cdf0e10cSrcweir // determine cluster extents 1007cdf0e10cSrcweir const GlyphItem* const pEnd = mpGlyphItems + mnGlyphCount; 1008cdf0e10cSrcweir for( const GlyphItem* pG = mpGlyphItems; pG < pEnd; ++pG ) 1009cdf0e10cSrcweir { 1010cdf0e10cSrcweir // use cluster start to get char index 1011cdf0e10cSrcweir if( !pG->IsClusterStart() ) 1012cdf0e10cSrcweir continue; 1013cdf0e10cSrcweir 1014cdf0e10cSrcweir int n = pG->mnCharPos; 1015cdf0e10cSrcweir if( n >= mnEndCharPos ) 1016cdf0e10cSrcweir continue; 1017cdf0e10cSrcweir n -= mnMinCharPos; 1018cdf0e10cSrcweir if( n < 0 ) 1019cdf0e10cSrcweir continue; 1020cdf0e10cSrcweir 1021cdf0e10cSrcweir // left glyph in cluster defines default extent 1022cdf0e10cSrcweir long nXPosMin = pG->maLinearPos.X(); 1023cdf0e10cSrcweir long nXPosMax = nXPosMin + pG->mnNewWidth; 1024cdf0e10cSrcweir 1025cdf0e10cSrcweir // calculate right x-position for this glyph cluster 1026cdf0e10cSrcweir // break if no more glyphs in layout 1027cdf0e10cSrcweir // break at next glyph cluster start 1028cdf0e10cSrcweir while( (pG+1 < pEnd) && !pG[1].IsClusterStart() ) 1029cdf0e10cSrcweir { 1030cdf0e10cSrcweir // advance to next glyph in cluster 1031cdf0e10cSrcweir ++pG; 1032cdf0e10cSrcweir 1033cdf0e10cSrcweir if( pG->IsDiacritic() ) 1034cdf0e10cSrcweir continue; // ignore diacritics 1035cdf0e10cSrcweir // get leftmost x-extent of this glyph 1036cdf0e10cSrcweir long nXPos = pG->maLinearPos.X(); 1037cdf0e10cSrcweir if( nXPosMin > nXPos ) 1038cdf0e10cSrcweir nXPosMin = nXPos; 1039cdf0e10cSrcweir 1040cdf0e10cSrcweir // get rightmost x-extent of this glyph 1041cdf0e10cSrcweir nXPos += pG->mnNewWidth; 1042cdf0e10cSrcweir if( nXPosMax < nXPos ) 1043cdf0e10cSrcweir nXPosMax = nXPos; 1044cdf0e10cSrcweir } 1045cdf0e10cSrcweir 1046cdf0e10cSrcweir // when the current cluster overlaps with the next one assume 1047cdf0e10cSrcweir // rightmost cluster edge is the leftmost edge of next cluster 1048cdf0e10cSrcweir // for clusters that do not have x-sorted glyphs 1049cdf0e10cSrcweir // TODO: avoid recalculation of left bound in next cluster iteration 1050cdf0e10cSrcweir for( const GlyphItem* pN = pG; ++pN < pEnd; ) 1051cdf0e10cSrcweir { 1052cdf0e10cSrcweir if( pN->IsClusterStart() ) 1053cdf0e10cSrcweir break; 1054cdf0e10cSrcweir if( pN->IsDiacritic() ) 1055cdf0e10cSrcweir continue; // ignore diacritics 1056cdf0e10cSrcweir if( nXPosMax > pN->maLinearPos.X() ) 1057cdf0e10cSrcweir nXPosMax = pN->maLinearPos.X(); 1058cdf0e10cSrcweir } 1059cdf0e10cSrcweir if( nXPosMax < nXPosMin ) 1060cdf0e10cSrcweir nXPosMin = nXPosMax = 0; 1061cdf0e10cSrcweir 1062cdf0e10cSrcweir // character width is sum of glyph cluster widths 1063cdf0e10cSrcweir pCharWidths[n] += nXPosMax - nXPosMin; 1064cdf0e10cSrcweir } 1065cdf0e10cSrcweir 1066cdf0e10cSrcweir // TODO: distribute the cluster width proportionally to the characters 1067cdf0e10cSrcweir // clusters (e.g. ligatures) correspond to more than one char index, 1068cdf0e10cSrcweir // so some character widths are still uninitialized. This is solved 1069cdf0e10cSrcweir // by setting the first charwidth of the cluster to the cluster width 1070cdf0e10cSrcweir 1071cdf0e10cSrcweir return true; 1072cdf0e10cSrcweir } 1073cdf0e10cSrcweir 1074cdf0e10cSrcweir // ----------------------------------------------------------------------- 1075cdf0e10cSrcweir 1076cdf0e10cSrcweir long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const 1077cdf0e10cSrcweir { 1078cdf0e10cSrcweir if( pCharWidths ) 1079cdf0e10cSrcweir if( !GetCharWidths( pCharWidths ) ) 1080cdf0e10cSrcweir return 0; 1081cdf0e10cSrcweir 1082cdf0e10cSrcweir long nWidth = GetTextWidth(); 1083cdf0e10cSrcweir return nWidth; 1084cdf0e10cSrcweir } 1085cdf0e10cSrcweir 1086cdf0e10cSrcweir // ----------------------------------------------------------------------- 1087cdf0e10cSrcweir 1088cdf0e10cSrcweir // the text width is the maximum logical extent of all glyphs 1089cdf0e10cSrcweir long GenericSalLayout::GetTextWidth() const 1090cdf0e10cSrcweir { 1091cdf0e10cSrcweir if( mnGlyphCount <= 0 ) 1092cdf0e10cSrcweir return 0; 1093cdf0e10cSrcweir 1094cdf0e10cSrcweir // initialize the extent 1095cdf0e10cSrcweir long nMinPos = 0; 1096cdf0e10cSrcweir long nMaxPos = 0; 1097cdf0e10cSrcweir 1098cdf0e10cSrcweir const GlyphItem* pG = mpGlyphItems; 1099cdf0e10cSrcweir for( int i = mnGlyphCount; --i >= 0; ++pG ) 1100cdf0e10cSrcweir { 1101cdf0e10cSrcweir // update the text extent with the glyph extent 1102cdf0e10cSrcweir long nXPos = pG->maLinearPos.X(); 1103cdf0e10cSrcweir if( nMinPos > nXPos ) 1104cdf0e10cSrcweir nMinPos = nXPos; 1105cdf0e10cSrcweir nXPos += pG->mnNewWidth; 1106cdf0e10cSrcweir if( nMaxPos < nXPos ) 1107cdf0e10cSrcweir nMaxPos = nXPos; 1108cdf0e10cSrcweir } 1109cdf0e10cSrcweir 1110cdf0e10cSrcweir long nWidth = nMaxPos - nMinPos; 1111cdf0e10cSrcweir return nWidth; 1112cdf0e10cSrcweir } 1113cdf0e10cSrcweir 1114cdf0e10cSrcweir // ----------------------------------------------------------------------- 1115cdf0e10cSrcweir 1116cdf0e10cSrcweir void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 1117cdf0e10cSrcweir { 1118cdf0e10cSrcweir SalLayout::AdjustLayout( rArgs ); 1119cdf0e10cSrcweir 1120cdf0e10cSrcweir if( rArgs.mpDXArray ) 1121cdf0e10cSrcweir ApplyDXArray( rArgs ); 1122cdf0e10cSrcweir else if( rArgs.mnLayoutWidth ) 1123cdf0e10cSrcweir Justify( rArgs.mnLayoutWidth ); 1124cdf0e10cSrcweir } 1125cdf0e10cSrcweir 1126cdf0e10cSrcweir // ----------------------------------------------------------------------- 1127cdf0e10cSrcweir 1128cdf0e10cSrcweir void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs ) 1129cdf0e10cSrcweir { 1130cdf0e10cSrcweir if( mnGlyphCount <= 0 ) 1131cdf0e10cSrcweir return; 1132cdf0e10cSrcweir 1133cdf0e10cSrcweir // determine cluster boundaries and x base offset 1134cdf0e10cSrcweir const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; 1135cdf0e10cSrcweir int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) ); 1136cdf0e10cSrcweir int i, n; 1137cdf0e10cSrcweir long nBasePointX = -1; 1138cdf0e10cSrcweir if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK ) 1139cdf0e10cSrcweir nBasePointX = 0; 1140cdf0e10cSrcweir for( i = 0; i < nCharCount; ++i ) 1141cdf0e10cSrcweir pLogCluster[ i ] = -1; 1142cdf0e10cSrcweir GlyphItem* pG = mpGlyphItems; 1143cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i, ++pG ) 1144cdf0e10cSrcweir { 1145cdf0e10cSrcweir n = pG->mnCharPos - rArgs.mnMinCharPos; 1146cdf0e10cSrcweir if( (n < 0) || (nCharCount <= n) ) 1147cdf0e10cSrcweir continue; 1148cdf0e10cSrcweir if( pLogCluster[ n ] < 0 ) 1149cdf0e10cSrcweir pLogCluster[ n ] = i; 1150cdf0e10cSrcweir if( nBasePointX < 0 ) 1151cdf0e10cSrcweir nBasePointX = pG->maLinearPos.X(); 1152cdf0e10cSrcweir } 1153cdf0e10cSrcweir // retarget unresolved pLogCluster[n] to a glyph inside the cluster 1154cdf0e10cSrcweir // TODO: better do it while the deleted-glyph markers are still there 1155cdf0e10cSrcweir for( n = 0; n < nCharCount; ++n ) 1156cdf0e10cSrcweir if( (i = pLogCluster[0]) >= 0 ) 1157cdf0e10cSrcweir break; 1158cdf0e10cSrcweir if( n >= nCharCount ) 1159cdf0e10cSrcweir return; 1160cdf0e10cSrcweir for( n = 0; n < nCharCount; ++n ) 1161cdf0e10cSrcweir { 1162cdf0e10cSrcweir if( pLogCluster[ n ] < 0 ) 1163cdf0e10cSrcweir pLogCluster[ n ] = i; 1164cdf0e10cSrcweir else 1165cdf0e10cSrcweir i = pLogCluster[ n ]; 1166cdf0e10cSrcweir } 1167cdf0e10cSrcweir 1168cdf0e10cSrcweir // calculate adjusted cluster widths 11699f39abbbSHerbert Dürr sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( mnGlyphCount * sizeof(sal_Int32) ); 1170cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i ) 1171cdf0e10cSrcweir pNewGlyphWidths[ i ] = 0; 1172cdf0e10cSrcweir 1173cdf0e10cSrcweir bool bRTL; 1174cdf0e10cSrcweir for( int nCharPos = i = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); ) 1175cdf0e10cSrcweir { 1176cdf0e10cSrcweir n = nCharPos - rArgs.mnMinCharPos; 1177cdf0e10cSrcweir if( (n < 0) || (nCharCount <= n) ) continue; 1178cdf0e10cSrcweir 1179cdf0e10cSrcweir if( pLogCluster[ n ] >= 0 ) 1180cdf0e10cSrcweir i = pLogCluster[ n ]; 1181cdf0e10cSrcweir if( i >= 0 ) 1182cdf0e10cSrcweir { 1183cdf0e10cSrcweir long nDelta = rArgs.mpDXArray[ n ] ; 1184cdf0e10cSrcweir if( n > 0 ) 1185cdf0e10cSrcweir nDelta -= rArgs.mpDXArray[ n-1 ]; 1186cdf0e10cSrcweir pNewGlyphWidths[ i ] += nDelta * mnUnitsPerPixel; 1187cdf0e10cSrcweir } 1188cdf0e10cSrcweir } 1189cdf0e10cSrcweir 1190cdf0e10cSrcweir // move cluster positions using the adjusted widths 1191cdf0e10cSrcweir long nDelta = 0; 1192cdf0e10cSrcweir long nNewPos = 0; 1193cdf0e10cSrcweir pG = mpGlyphItems; 1194cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i, ++pG ) 1195cdf0e10cSrcweir { 1196cdf0e10cSrcweir if( pG->IsClusterStart() ) 1197cdf0e10cSrcweir { 1198cdf0e10cSrcweir // calculate original and adjusted cluster width 1199cdf0e10cSrcweir int nOldClusterWidth = pG->mnNewWidth; 1200cdf0e10cSrcweir int nNewClusterWidth = pNewGlyphWidths[i]; 1201cdf0e10cSrcweir GlyphItem* pClusterG = pG + 1; 1202cdf0e10cSrcweir for( int j = i; ++j < mnGlyphCount; ++pClusterG ) 1203cdf0e10cSrcweir { 1204cdf0e10cSrcweir if( pClusterG->IsClusterStart() ) 1205cdf0e10cSrcweir break; 1206cdf0e10cSrcweir if( !pClusterG->IsDiacritic() ) // #i99367# ignore diacritics 1207cdf0e10cSrcweir nOldClusterWidth += pClusterG->mnNewWidth; 1208cdf0e10cSrcweir nNewClusterWidth += pNewGlyphWidths[j]; 1209cdf0e10cSrcweir } 1210cdf0e10cSrcweir const int nDiff = nNewClusterWidth - nOldClusterWidth; 1211cdf0e10cSrcweir 1212cdf0e10cSrcweir // adjust cluster glyph widths and positions 1213cdf0e10cSrcweir nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X()); 1214cdf0e10cSrcweir if( !pG->IsRTLGlyph() ) 1215cdf0e10cSrcweir { 1216cdf0e10cSrcweir // for LTR case extend rightmost glyph in cluster 1217cdf0e10cSrcweir pClusterG[-1].mnNewWidth += nDiff; 1218cdf0e10cSrcweir } 1219cdf0e10cSrcweir else 1220cdf0e10cSrcweir { 1221cdf0e10cSrcweir // right align cluster in new space for RTL case 1222cdf0e10cSrcweir pG->mnNewWidth += nDiff; 1223cdf0e10cSrcweir nDelta += nDiff; 1224cdf0e10cSrcweir } 1225cdf0e10cSrcweir 1226cdf0e10cSrcweir nNewPos += nNewClusterWidth; 1227cdf0e10cSrcweir } 1228cdf0e10cSrcweir 1229cdf0e10cSrcweir pG->maLinearPos.X() += nDelta; 1230cdf0e10cSrcweir } 1231cdf0e10cSrcweir } 1232cdf0e10cSrcweir 1233cdf0e10cSrcweir // ----------------------------------------------------------------------- 1234cdf0e10cSrcweir 1235cdf0e10cSrcweir void GenericSalLayout::Justify( long nNewWidth ) 1236cdf0e10cSrcweir { 1237cdf0e10cSrcweir nNewWidth *= mnUnitsPerPixel; 1238cdf0e10cSrcweir int nOldWidth = GetTextWidth(); 1239cdf0e10cSrcweir if( !nOldWidth || nNewWidth==nOldWidth ) 1240cdf0e10cSrcweir return; 1241cdf0e10cSrcweir 1242cdf0e10cSrcweir // find rightmost glyph, it won't get stretched 1243cdf0e10cSrcweir GlyphItem* pGRight = mpGlyphItems + mnGlyphCount - 1; 1244cdf0e10cSrcweir 1245cdf0e10cSrcweir // count stretchable glyphs 1246cdf0e10cSrcweir GlyphItem* pG; 1247cdf0e10cSrcweir int nStretchable = 0; 1248cdf0e10cSrcweir int nMaxGlyphWidth = 0; 1249cdf0e10cSrcweir for( pG = mpGlyphItems; pG < pGRight; ++pG ) 1250cdf0e10cSrcweir { 1251cdf0e10cSrcweir if( !pG->IsDiacritic() ) 1252cdf0e10cSrcweir ++nStretchable; 1253cdf0e10cSrcweir if( nMaxGlyphWidth < pG->mnOrigWidth ) 1254cdf0e10cSrcweir nMaxGlyphWidth = pG->mnOrigWidth; 1255cdf0e10cSrcweir } 1256cdf0e10cSrcweir 1257cdf0e10cSrcweir // move rightmost glyph to requested position 1258cdf0e10cSrcweir nOldWidth -= pGRight->mnOrigWidth; 1259cdf0e10cSrcweir if( nOldWidth <= 0 ) 1260cdf0e10cSrcweir return; 1261cdf0e10cSrcweir if( nNewWidth < nMaxGlyphWidth) 1262cdf0e10cSrcweir nNewWidth = nMaxGlyphWidth; 1263cdf0e10cSrcweir nNewWidth -= pGRight->mnOrigWidth; 1264cdf0e10cSrcweir pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth; 1265cdf0e10cSrcweir 1266cdf0e10cSrcweir // justify glyph widths and positions 1267cdf0e10cSrcweir int nDiffWidth = nNewWidth - nOldWidth; 1268cdf0e10cSrcweir if( nDiffWidth >= 0) // expanded case 1269cdf0e10cSrcweir { 1270cdf0e10cSrcweir // expand width by distributing space between glyphs evenly 1271cdf0e10cSrcweir int nDeltaSum = 0; 1272cdf0e10cSrcweir for( pG = mpGlyphItems; pG < pGRight; ++pG ) 1273cdf0e10cSrcweir { 1274cdf0e10cSrcweir // move glyph to justified position 1275cdf0e10cSrcweir pG->maLinearPos.X() += nDeltaSum; 1276cdf0e10cSrcweir 1277cdf0e10cSrcweir // do not stretch non-stretchable glyphs 1278cdf0e10cSrcweir if( pG->IsDiacritic() || (nStretchable <= 0) ) 1279cdf0e10cSrcweir continue; 1280cdf0e10cSrcweir 1281cdf0e10cSrcweir // distribute extra space equally to stretchable glyphs 1282cdf0e10cSrcweir int nDeltaWidth = nDiffWidth / nStretchable--; 1283cdf0e10cSrcweir nDiffWidth -= nDeltaWidth; 1284cdf0e10cSrcweir pG->mnNewWidth += nDeltaWidth; 1285cdf0e10cSrcweir nDeltaSum += nDeltaWidth; 1286cdf0e10cSrcweir } 1287cdf0e10cSrcweir } 1288cdf0e10cSrcweir else // condensed case 1289cdf0e10cSrcweir { 1290cdf0e10cSrcweir // squeeze width by moving glyphs proportionally 1291cdf0e10cSrcweir double fSqueeze = (double)nNewWidth / nOldWidth; 1292cdf0e10cSrcweir for( pG = mpGlyphItems; ++pG < pGRight;) 1293cdf0e10cSrcweir { 1294cdf0e10cSrcweir int nX = pG->maLinearPos.X() - maBasePoint.X(); 1295cdf0e10cSrcweir nX = (int)(nX * fSqueeze); 1296cdf0e10cSrcweir pG->maLinearPos.X() = nX + maBasePoint.X(); 1297cdf0e10cSrcweir } 1298cdf0e10cSrcweir // adjust glyph widths to new positions 1299cdf0e10cSrcweir for( pG = mpGlyphItems; pG < pGRight; ++pG ) 1300cdf0e10cSrcweir pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X(); 1301cdf0e10cSrcweir } 1302cdf0e10cSrcweir } 1303cdf0e10cSrcweir 1304cdf0e10cSrcweir // ----------------------------------------------------------------------- 1305cdf0e10cSrcweir 1306cdf0e10cSrcweir void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength ) 1307cdf0e10cSrcweir { 1308cdf0e10cSrcweir long nOffset = 0; 1309cdf0e10cSrcweir 1310cdf0e10cSrcweir GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount; 1311cdf0e10cSrcweir for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG ) 1312cdf0e10cSrcweir { 1313cdf0e10cSrcweir const int n = pG->mnCharPos; 1314cdf0e10cSrcweir if( n < nLength - 1) 1315cdf0e10cSrcweir { 1316cdf0e10cSrcweir // ignore code ranges that are not affected by asian punctuation compression 1317cdf0e10cSrcweir const sal_Unicode cHere = pStr[n]; 1318cdf0e10cSrcweir if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) ) 1319cdf0e10cSrcweir continue; 1320cdf0e10cSrcweir const sal_Unicode cNext = pStr[n+1]; 1321cdf0e10cSrcweir if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) ) 1322cdf0e10cSrcweir continue; 1323cdf0e10cSrcweir 1324cdf0e10cSrcweir // calculate compression values 1325cdf0e10cSrcweir const bool bVertical = false; 1326cdf0e10cSrcweir long nKernFirst = +CalcAsianKerning( cHere, true, bVertical ); 1327cdf0e10cSrcweir long nKernNext = -CalcAsianKerning( cNext, false, bVertical ); 1328cdf0e10cSrcweir 1329cdf0e10cSrcweir // apply punctuation compression to logical glyph widths 1330cdf0e10cSrcweir long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext; 1331cdf0e10cSrcweir if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 ) 1332cdf0e10cSrcweir { 1333cdf0e10cSrcweir int nGlyphWidth = pG->mnOrigWidth; 1334cdf0e10cSrcweir nDelta = (nDelta * nGlyphWidth + 2) / 4; 1335cdf0e10cSrcweir if( pG+1 == pGEnd ) 1336cdf0e10cSrcweir pG->mnNewWidth += nDelta; 1337cdf0e10cSrcweir nOffset += nDelta; 1338cdf0e10cSrcweir } 1339cdf0e10cSrcweir } 1340cdf0e10cSrcweir 1341cdf0e10cSrcweir // adjust the glyph positions to the new glyph widths 1342cdf0e10cSrcweir if( pG+1 != pGEnd ) 1343cdf0e10cSrcweir pG->maLinearPos.X() += nOffset; 1344cdf0e10cSrcweir } 1345cdf0e10cSrcweir } 1346cdf0e10cSrcweir 1347cdf0e10cSrcweir // ----------------------------------------------------------------------- 1348cdf0e10cSrcweir 1349cdf0e10cSrcweir void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth ) 1350cdf0e10cSrcweir { 1351cdf0e10cSrcweir // TODO: reimplement method when container type for GlyphItems changes 1352cdf0e10cSrcweir 1353cdf0e10cSrcweir // skip if the kashida glyph in the font looks suspicious 1354cdf0e10cSrcweir if( nKashidaWidth <= 0 ) 1355cdf0e10cSrcweir return; 1356cdf0e10cSrcweir 1357cdf0e10cSrcweir // calculate max number of needed kashidas 1358cdf0e10cSrcweir const GlyphItem* pG1 = mpGlyphItems; 1359cdf0e10cSrcweir int nKashidaCount = 0, i; 1360cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i, ++pG1 ) 1361cdf0e10cSrcweir { 1362cdf0e10cSrcweir // only inject kashidas in RTL contexts 1363cdf0e10cSrcweir if( !pG1->IsRTLGlyph() ) 1364cdf0e10cSrcweir continue; 1365cdf0e10cSrcweir // no kashida-injection for blank justified expansion either 1366*248a599fSHerbert Dürr if( IsSpacingGlyph( pG1->maGlyphId ) ) 1367cdf0e10cSrcweir continue; 1368cdf0e10cSrcweir 1369cdf0e10cSrcweir // calculate gap, ignore if too small 1370cdf0e10cSrcweir const int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth; 1371cdf0e10cSrcweir // worst case is one kashida even for mini-gaps 1372cdf0e10cSrcweir if( 3 * nGapWidth >= nKashidaWidth ) 1373cdf0e10cSrcweir nKashidaCount += 1 + (nGapWidth / nKashidaWidth); 1374cdf0e10cSrcweir } 1375cdf0e10cSrcweir 1376cdf0e10cSrcweir if( !nKashidaCount ) 1377cdf0e10cSrcweir return; 1378cdf0e10cSrcweir 1379cdf0e10cSrcweir // reallocate glyph array for additional kashidas 1380cdf0e10cSrcweir // TODO: reuse array if additional glyphs would fit 1381cdf0e10cSrcweir mnGlyphCapacity = mnGlyphCount + nKashidaCount; 1382cdf0e10cSrcweir GlyphItem* pNewGlyphItems = new GlyphItem[ mnGlyphCapacity ]; 1383cdf0e10cSrcweir GlyphItem* pG2 = pNewGlyphItems; 1384cdf0e10cSrcweir pG1 = mpGlyphItems; 1385cdf0e10cSrcweir for( i = mnGlyphCount; --i >= 0; ++pG1, ++pG2 ) 1386cdf0e10cSrcweir { 1387cdf0e10cSrcweir // default action is to copy array element 1388cdf0e10cSrcweir *pG2 = *pG1; 1389cdf0e10cSrcweir 1390cdf0e10cSrcweir // only inject kashida in RTL contexts 1391cdf0e10cSrcweir if( !pG1->IsRTLGlyph() ) 1392cdf0e10cSrcweir continue; 1393cdf0e10cSrcweir // no kashida-injection for blank justified expansion either 1394*248a599fSHerbert Dürr if( IsSpacingGlyph( pG1->maGlyphId ) ) 1395cdf0e10cSrcweir continue; 1396cdf0e10cSrcweir 1397cdf0e10cSrcweir // calculate gap, skip if too small 1398cdf0e10cSrcweir int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth; 1399cdf0e10cSrcweir if( 3*nGapWidth < nKashidaWidth ) 1400cdf0e10cSrcweir continue; 1401cdf0e10cSrcweir 1402cdf0e10cSrcweir // fill gap with kashidas 1403cdf0e10cSrcweir nKashidaCount = 0; 1404cdf0e10cSrcweir Point aPos = pG1->maLinearPos; 1405cdf0e10cSrcweir aPos.X() -= nGapWidth; // cluster is already right aligned 1406cdf0e10cSrcweir for(; nGapWidth > 0; nGapWidth -= nKashidaWidth, ++nKashidaCount ) 1407cdf0e10cSrcweir { 1408cdf0e10cSrcweir *(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos, 1409cdf0e10cSrcweir GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth ); 1410cdf0e10cSrcweir aPos.X() += nKashidaWidth; 1411cdf0e10cSrcweir } 1412cdf0e10cSrcweir 1413cdf0e10cSrcweir // fixup rightmost kashida for gap remainder 1414cdf0e10cSrcweir if( nGapWidth < 0 ) 1415cdf0e10cSrcweir { 1416cdf0e10cSrcweir aPos.X() += nGapWidth; 1417cdf0e10cSrcweir if( nKashidaCount <= 1 ) 1418cdf0e10cSrcweir nGapWidth /= 2; // for small gap move kashida to middle 1419cdf0e10cSrcweir pG2[-1].mnNewWidth += nGapWidth; // adjust kashida width to gap width 1420cdf0e10cSrcweir pG2[-1].maLinearPos.X() += nGapWidth; 1421cdf0e10cSrcweir } 1422cdf0e10cSrcweir 1423cdf0e10cSrcweir // when kashidas were inserted move the original cluster 1424cdf0e10cSrcweir // to the right and shrink it to it's original width 1425cdf0e10cSrcweir *pG2 = *pG1; 1426cdf0e10cSrcweir pG2->maLinearPos.X() = aPos.X(); 1427cdf0e10cSrcweir pG2->mnNewWidth = pG2->mnOrigWidth; 1428cdf0e10cSrcweir } 1429cdf0e10cSrcweir 1430cdf0e10cSrcweir // use the new glyph array 1431cdf0e10cSrcweir DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" ); 1432cdf0e10cSrcweir delete[] mpGlyphItems; 1433cdf0e10cSrcweir mpGlyphItems = pNewGlyphItems; 1434cdf0e10cSrcweir mnGlyphCount = pG2 - pNewGlyphItems; 1435cdf0e10cSrcweir } 1436cdf0e10cSrcweir 1437cdf0e10cSrcweir // ----------------------------------------------------------------------- 1438cdf0e10cSrcweir 1439cdf0e10cSrcweir void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const 1440cdf0e10cSrcweir { 1441cdf0e10cSrcweir // initialize result array 1442cdf0e10cSrcweir long nXPos = -1; 1443cdf0e10cSrcweir int i; 1444cdf0e10cSrcweir for( i = 0; i < nMaxIndex; ++i ) 1445cdf0e10cSrcweir pCaretXArray[ i ] = nXPos; 1446cdf0e10cSrcweir 1447cdf0e10cSrcweir // calculate caret positions using glyph array 1448cdf0e10cSrcweir const GlyphItem* pG = mpGlyphItems; 1449cdf0e10cSrcweir for( i = mnGlyphCount; --i >= 0; ++pG ) 1450cdf0e10cSrcweir { 1451cdf0e10cSrcweir nXPos = pG->maLinearPos.X(); 1452cdf0e10cSrcweir long nXRight = nXPos + pG->mnOrigWidth; 1453cdf0e10cSrcweir int n = pG->mnCharPos; 1454cdf0e10cSrcweir int nCurrIdx = 2 * (n - mnMinCharPos); 1455cdf0e10cSrcweir if( !pG->IsRTLGlyph() ) 1456cdf0e10cSrcweir { 1457cdf0e10cSrcweir // normal positions for LTR case 1458cdf0e10cSrcweir pCaretXArray[ nCurrIdx ] = nXPos; 1459cdf0e10cSrcweir pCaretXArray[ nCurrIdx+1 ] = nXRight; 1460cdf0e10cSrcweir } 1461cdf0e10cSrcweir else 1462cdf0e10cSrcweir { 1463cdf0e10cSrcweir // reverse positions for RTL case 1464cdf0e10cSrcweir pCaretXArray[ nCurrIdx ] = nXRight; 1465cdf0e10cSrcweir pCaretXArray[ nCurrIdx+1 ] = nXPos; 1466cdf0e10cSrcweir } 1467cdf0e10cSrcweir } 1468cdf0e10cSrcweir } 1469cdf0e10cSrcweir 1470cdf0e10cSrcweir // ----------------------------------------------------------------------- 1471cdf0e10cSrcweir 1472cdf0e10cSrcweir int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 1473cdf0e10cSrcweir { 1474cdf0e10cSrcweir int nCharCapacity = mnEndCharPos - mnMinCharPos; 1475cdf0e10cSrcweir sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) ); 1476cdf0e10cSrcweir if( !GetCharWidths( pCharWidths ) ) 1477cdf0e10cSrcweir return STRING_LEN; 1478cdf0e10cSrcweir 1479cdf0e10cSrcweir long nWidth = 0; 1480cdf0e10cSrcweir for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 1481cdf0e10cSrcweir { 1482cdf0e10cSrcweir nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor; 1483cdf0e10cSrcweir if( nWidth >= nMaxWidth ) 1484cdf0e10cSrcweir return i; 1485cdf0e10cSrcweir nWidth += nCharExtra; 1486cdf0e10cSrcweir } 1487cdf0e10cSrcweir 1488cdf0e10cSrcweir return STRING_LEN; 1489cdf0e10cSrcweir } 1490cdf0e10cSrcweir 1491cdf0e10cSrcweir // ----------------------------------------------------------------------- 1492cdf0e10cSrcweir 1493cdf0e10cSrcweir int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, 1494cdf0e10cSrcweir int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const 1495cdf0e10cSrcweir { 1496cdf0e10cSrcweir const GlyphItem* pG = mpGlyphItems + nStart; 1497cdf0e10cSrcweir 1498cdf0e10cSrcweir // find next glyph in substring 1499cdf0e10cSrcweir for(; nStart < mnGlyphCount; ++nStart, ++pG ) 1500cdf0e10cSrcweir { 1501cdf0e10cSrcweir int n = pG->mnCharPos; 1502cdf0e10cSrcweir if( (mnMinCharPos <= n) && (n < mnEndCharPos) ) 1503cdf0e10cSrcweir break; 1504cdf0e10cSrcweir } 1505cdf0e10cSrcweir 1506cdf0e10cSrcweir // return zero if no more glyph found 1507cdf0e10cSrcweir if( nStart >= mnGlyphCount ) 1508cdf0e10cSrcweir return 0; 1509cdf0e10cSrcweir 1510cdf0e10cSrcweir // calculate absolute position in pixel units 1511cdf0e10cSrcweir Point aRelativePos = pG->maLinearPos - maBasePoint; 1512cdf0e10cSrcweir 1513cdf0e10cSrcweir // find more glyphs which can be merged into one drawing instruction 1514cdf0e10cSrcweir int nCount = 0; 1515cdf0e10cSrcweir long nYPos = pG->maLinearPos.Y(); 1516*248a599fSHerbert Dürr long nOldFlags = pG->maGlyphId; 1517cdf0e10cSrcweir for(;;) 1518cdf0e10cSrcweir { 1519cdf0e10cSrcweir // update return data with glyph info 1520cdf0e10cSrcweir ++nCount; 1521*248a599fSHerbert Dürr *(pGlyphs++) = pG->maGlyphId; 1522cdf0e10cSrcweir if( pCharPosAry ) 1523cdf0e10cSrcweir *(pCharPosAry++) = pG->mnCharPos; 1524cdf0e10cSrcweir if( pGlyphAdvAry ) 1525cdf0e10cSrcweir *pGlyphAdvAry = pG->mnNewWidth; 1526cdf0e10cSrcweir 1527cdf0e10cSrcweir // break at end of glyph list 1528cdf0e10cSrcweir if( ++nStart >= mnGlyphCount ) 1529cdf0e10cSrcweir break; 1530cdf0e10cSrcweir // break when enough glyphs 1531cdf0e10cSrcweir if( nCount >= nLen ) 1532cdf0e10cSrcweir break; 1533cdf0e10cSrcweir 1534cdf0e10cSrcweir long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X(); 1535cdf0e10cSrcweir if( pGlyphAdvAry ) 1536cdf0e10cSrcweir { 1537cdf0e10cSrcweir // override default advance width with correct value 1538cdf0e10cSrcweir *(pGlyphAdvAry++) = nGlyphAdvance; 1539cdf0e10cSrcweir } 1540cdf0e10cSrcweir else 1541cdf0e10cSrcweir { 1542cdf0e10cSrcweir // stop when next x-position is unexpected 1543cdf0e10cSrcweir if( pG->mnOrigWidth != nGlyphAdvance ) 1544cdf0e10cSrcweir break; 1545cdf0e10cSrcweir } 1546cdf0e10cSrcweir 1547cdf0e10cSrcweir // advance to next glyph 1548cdf0e10cSrcweir ++pG; 1549cdf0e10cSrcweir 1550cdf0e10cSrcweir // stop when next y-position is unexpected 1551cdf0e10cSrcweir if( nYPos != pG->maLinearPos.Y() ) 1552cdf0e10cSrcweir break; 1553cdf0e10cSrcweir 1554cdf0e10cSrcweir // stop when no longer in string 1555cdf0e10cSrcweir int n = pG->mnCharPos; 1556cdf0e10cSrcweir if( (n < mnMinCharPos) || (mnEndCharPos <= n) ) 1557cdf0e10cSrcweir break; 1558cdf0e10cSrcweir 1559cdf0e10cSrcweir // stop when glyph flags change 1560*248a599fSHerbert Dürr if( (nOldFlags ^ pG->maGlyphId) & GF_FLAGMASK ) 1561cdf0e10cSrcweir break; 1562cdf0e10cSrcweir 1563*248a599fSHerbert Dürr nOldFlags = pG->maGlyphId; // &GF_FLAGMASK not needed for test above 1564cdf0e10cSrcweir } 1565cdf0e10cSrcweir 1566cdf0e10cSrcweir aRelativePos.X() /= mnUnitsPerPixel; 1567cdf0e10cSrcweir aRelativePos.Y() /= mnUnitsPerPixel; 1568cdf0e10cSrcweir rPos = GetDrawPosition( aRelativePos ); 1569cdf0e10cSrcweir 1570cdf0e10cSrcweir return nCount; 1571cdf0e10cSrcweir } 1572cdf0e10cSrcweir 1573cdf0e10cSrcweir // ----------------------------------------------------------------------- 1574cdf0e10cSrcweir 1575cdf0e10cSrcweir void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos ) 1576cdf0e10cSrcweir { 1577cdf0e10cSrcweir if( nStart >= mnGlyphCount ) 1578cdf0e10cSrcweir return; 1579cdf0e10cSrcweir 1580cdf0e10cSrcweir GlyphItem* pG = mpGlyphItems + nStart; 1581cdf0e10cSrcweir // the nNewXPos argument determines the new cell position 1582cdf0e10cSrcweir // as RTL-glyphs are right justified in their cell 1583cdf0e10cSrcweir // the cell position needs to be adjusted to the glyph position 1584cdf0e10cSrcweir if( pG->IsRTLGlyph() ) 1585cdf0e10cSrcweir nNewXPos += pG->mnNewWidth - pG->mnOrigWidth; 1586cdf0e10cSrcweir // calculate the x-offset to the old position 1587cdf0e10cSrcweir long nXDelta = nNewXPos - pG->maLinearPos.X(); 1588cdf0e10cSrcweir // adjust all following glyph positions if needed 1589cdf0e10cSrcweir if( nXDelta != 0 ) 1590cdf0e10cSrcweir { 1591cdf0e10cSrcweir GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount; 1592cdf0e10cSrcweir for(; pG < pGEnd; ++pG ) 1593cdf0e10cSrcweir pG->maLinearPos.X() += nXDelta; 1594cdf0e10cSrcweir } 1595cdf0e10cSrcweir } 1596cdf0e10cSrcweir 1597cdf0e10cSrcweir // ----------------------------------------------------------------------- 1598cdf0e10cSrcweir 1599cdf0e10cSrcweir void GenericSalLayout::DropGlyph( int nStart ) 1600cdf0e10cSrcweir { 1601cdf0e10cSrcweir if( nStart >= mnGlyphCount ) 1602cdf0e10cSrcweir return; 1603cdf0e10cSrcweir GlyphItem* pG = mpGlyphItems + nStart; 1604*248a599fSHerbert Dürr pG->maGlyphId = GF_DROPPED; 1605cdf0e10cSrcweir pG->mnCharPos = -1; 1606cdf0e10cSrcweir } 1607cdf0e10cSrcweir 1608cdf0e10cSrcweir // ----------------------------------------------------------------------- 1609cdf0e10cSrcweir 1610cdf0e10cSrcweir void GenericSalLayout::Simplify( bool bIsBase ) 1611cdf0e10cSrcweir { 1612cdf0e10cSrcweir const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0; 1613cdf0e10cSrcweir 1614cdf0e10cSrcweir // remove dropped glyphs inplace 1615cdf0e10cSrcweir GlyphItem* pGDst = mpGlyphItems; 1616cdf0e10cSrcweir const GlyphItem* pGSrc = mpGlyphItems; 1617cdf0e10cSrcweir const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount; 1618cdf0e10cSrcweir for(; pGSrc < pGEnd; ++pGSrc ) 1619cdf0e10cSrcweir { 1620*248a599fSHerbert Dürr if( pGSrc->maGlyphId == nDropMarker ) 1621cdf0e10cSrcweir continue; 1622cdf0e10cSrcweir if( pGDst != pGSrc ) 1623cdf0e10cSrcweir *pGDst = *pGSrc; 1624cdf0e10cSrcweir ++pGDst; 1625cdf0e10cSrcweir } 1626cdf0e10cSrcweir mnGlyphCount = pGDst - mpGlyphItems; 1627cdf0e10cSrcweir } 1628cdf0e10cSrcweir 1629cdf0e10cSrcweir // ----------------------------------------------------------------------- 1630cdf0e10cSrcweir 1631cdf0e10cSrcweir // make sure GlyphItems are sorted left to right 1632cdf0e10cSrcweir void GenericSalLayout::SortGlyphItems() 1633cdf0e10cSrcweir { 1634cdf0e10cSrcweir // move cluster components behind their cluster start (especially for RTL) 1635cdf0e10cSrcweir // using insertion sort because the glyph items are "almost sorted" 1636cdf0e10cSrcweir const GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount; 1637cdf0e10cSrcweir for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG ) 1638cdf0e10cSrcweir { 1639cdf0e10cSrcweir // find a cluster starting with a diacritic 1640cdf0e10cSrcweir if( !pG->IsDiacritic() ) 1641cdf0e10cSrcweir continue; 1642cdf0e10cSrcweir if( !pG->IsClusterStart() ) 1643cdf0e10cSrcweir continue; 1644cdf0e10cSrcweir for( GlyphItem* pBaseGlyph = pG; ++pBaseGlyph < pGEnd; ) 1645cdf0e10cSrcweir { 1646cdf0e10cSrcweir // find the base glyph matching to the misplaced diacritic 1647cdf0e10cSrcweir if( pBaseGlyph->IsClusterStart() ) 1648cdf0e10cSrcweir break; 1649cdf0e10cSrcweir if( pBaseGlyph->IsDiacritic() ) 1650cdf0e10cSrcweir continue; 1651cdf0e10cSrcweir 1652cdf0e10cSrcweir // found the matching base glyph 1653cdf0e10cSrcweir // => this base glyph becomes the new cluster start 1654cdf0e10cSrcweir const GlyphItem aDiacritic = *pG; 1655cdf0e10cSrcweir *pG = *pBaseGlyph; 1656cdf0e10cSrcweir *pBaseGlyph = aDiacritic; 1657cdf0e10cSrcweir 1658cdf0e10cSrcweir // update glyph flags of swapped glyphitems 1659cdf0e10cSrcweir pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER; 1660cdf0e10cSrcweir pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER; 1661cdf0e10cSrcweir // prepare for checking next cluster 1662cdf0e10cSrcweir pG = pBaseGlyph; 1663cdf0e10cSrcweir break; 1664cdf0e10cSrcweir } 1665cdf0e10cSrcweir } 1666cdf0e10cSrcweir } 1667cdf0e10cSrcweir 1668cdf0e10cSrcweir // ======================================================================= 1669cdf0e10cSrcweir 1670cdf0e10cSrcweir MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const ImplFontData* pBaseFont ) 1671cdf0e10cSrcweir : SalLayout() 1672cdf0e10cSrcweir , mnLevel( 1 ) 1673cdf0e10cSrcweir , mbInComplete( false ) 1674cdf0e10cSrcweir { 1675cdf0e10cSrcweir //maFallbackRuns[0].Clear(); 1676cdf0e10cSrcweir mpFallbackFonts[ 0 ] = pBaseFont; 1677cdf0e10cSrcweir mpLayouts[ 0 ] = &rBaseLayout; 1678cdf0e10cSrcweir mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel(); 1679cdf0e10cSrcweir } 1680cdf0e10cSrcweir 1681cdf0e10cSrcweir void MultiSalLayout::SetInComplete(bool bInComplete) 1682cdf0e10cSrcweir { 1683cdf0e10cSrcweir mbInComplete = bInComplete; 1684cdf0e10cSrcweir maFallbackRuns[mnLevel-1] = ImplLayoutRuns(); 1685cdf0e10cSrcweir } 1686cdf0e10cSrcweir 1687cdf0e10cSrcweir // ----------------------------------------------------------------------- 1688cdf0e10cSrcweir 1689cdf0e10cSrcweir MultiSalLayout::~MultiSalLayout() 1690cdf0e10cSrcweir { 1691cdf0e10cSrcweir for( int i = 0; i < mnLevel; ++i ) 1692cdf0e10cSrcweir mpLayouts[ i ]->Release(); 1693cdf0e10cSrcweir } 1694cdf0e10cSrcweir 1695cdf0e10cSrcweir // ----------------------------------------------------------------------- 1696cdf0e10cSrcweir 1697cdf0e10cSrcweir bool MultiSalLayout::AddFallback( SalLayout& rFallback, 1698cdf0e10cSrcweir ImplLayoutRuns& rFallbackRuns, const ImplFontData* pFallbackFont ) 1699cdf0e10cSrcweir { 1700cdf0e10cSrcweir if( mnLevel >= MAX_FALLBACK ) 1701cdf0e10cSrcweir return false; 1702cdf0e10cSrcweir 1703cdf0e10cSrcweir mpFallbackFonts[ mnLevel ] = pFallbackFont; 1704cdf0e10cSrcweir mpLayouts[ mnLevel ] = &rFallback; 1705cdf0e10cSrcweir maFallbackRuns[ mnLevel-1 ] = rFallbackRuns; 1706cdf0e10cSrcweir ++mnLevel; 1707cdf0e10cSrcweir return true; 1708cdf0e10cSrcweir } 1709cdf0e10cSrcweir 1710cdf0e10cSrcweir // ----------------------------------------------------------------------- 1711cdf0e10cSrcweir 1712cdf0e10cSrcweir bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs ) 1713cdf0e10cSrcweir { 1714cdf0e10cSrcweir if( mnLevel <= 1 ) 1715cdf0e10cSrcweir return false; 1716cdf0e10cSrcweir if (!mbInComplete) 1717cdf0e10cSrcweir maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns; 1718cdf0e10cSrcweir return true; 1719cdf0e10cSrcweir } 1720cdf0e10cSrcweir 1721cdf0e10cSrcweir // ----------------------------------------------------------------------- 1722cdf0e10cSrcweir 1723cdf0e10cSrcweir void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 1724cdf0e10cSrcweir { 1725cdf0e10cSrcweir SalLayout::AdjustLayout( rArgs ); 1726cdf0e10cSrcweir ImplLayoutArgs aMultiArgs = rArgs; 1727cdf0e10cSrcweir 1728cdf0e10cSrcweir if( !rArgs.mpDXArray && rArgs.mnLayoutWidth ) 1729cdf0e10cSrcweir { 1730cdf0e10cSrcweir // for stretched text in a MultiSalLayout the target width needs to be 1731cdf0e10cSrcweir // distributed by individually adjusting its virtual character widths 1732cdf0e10cSrcweir long nTargetWidth = aMultiArgs.mnLayoutWidth; 1733cdf0e10cSrcweir nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units 1734cdf0e10cSrcweir aMultiArgs.mnLayoutWidth = 0; 1735cdf0e10cSrcweir 1736cdf0e10cSrcweir // we need to get the original unmodified layouts ready 1737cdf0e10cSrcweir for( int n = 0; n < mnLevel; ++n ) 1738cdf0e10cSrcweir mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs ); 1739cdf0e10cSrcweir // then we can measure the unmodified metrics 1740cdf0e10cSrcweir int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; 1741cdf0e10cSrcweir sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) ); 1742cdf0e10cSrcweir FillDXArray( pJustificationArray ); 1743cdf0e10cSrcweir // #i17359# multilayout is not simplified yet, so calculating the 1744cdf0e10cSrcweir // unjustified width needs handholding; also count the number of 1745cdf0e10cSrcweir // stretchable virtual char widths 1746cdf0e10cSrcweir long nOrigWidth = 0; 1747cdf0e10cSrcweir int nStretchable = 0; 1748cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 1749cdf0e10cSrcweir { 1750cdf0e10cSrcweir // convert array from widths to sum of widths 1751cdf0e10cSrcweir nOrigWidth += pJustificationArray[i]; 1752cdf0e10cSrcweir if( pJustificationArray[i] > 0 ) 1753cdf0e10cSrcweir ++nStretchable; 1754cdf0e10cSrcweir } 1755cdf0e10cSrcweir 1756cdf0e10cSrcweir // now we are able to distribute the extra width over the virtual char widths 1757cdf0e10cSrcweir if( nOrigWidth && (nTargetWidth != nOrigWidth) ) 1758cdf0e10cSrcweir { 1759cdf0e10cSrcweir int nDiffWidth = nTargetWidth - nOrigWidth; 1760cdf0e10cSrcweir int nWidthSum = 0; 1761cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 1762cdf0e10cSrcweir { 1763cdf0e10cSrcweir int nJustWidth = pJustificationArray[i]; 1764cdf0e10cSrcweir if( (nJustWidth > 0) && (nStretchable > 0) ) 1765cdf0e10cSrcweir { 1766cdf0e10cSrcweir int nDeltaWidth = nDiffWidth / nStretchable; 1767cdf0e10cSrcweir nJustWidth += nDeltaWidth; 1768cdf0e10cSrcweir nDiffWidth -= nDeltaWidth; 1769cdf0e10cSrcweir --nStretchable; 1770cdf0e10cSrcweir } 1771cdf0e10cSrcweir nWidthSum += nJustWidth; 1772cdf0e10cSrcweir pJustificationArray[i] = nWidthSum; 1773cdf0e10cSrcweir } 1774cdf0e10cSrcweir if( nWidthSum != nTargetWidth ) 1775cdf0e10cSrcweir pJustificationArray[ nCharCount-1 ] = nTargetWidth; 1776cdf0e10cSrcweir 1777cdf0e10cSrcweir // the justification array is still in base level units 1778cdf0e10cSrcweir // => convert it to pixel units 1779cdf0e10cSrcweir if( mnUnitsPerPixel > 1 ) 1780cdf0e10cSrcweir { 1781cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 1782cdf0e10cSrcweir { 1783cdf0e10cSrcweir sal_Int32 nVal = pJustificationArray[ i ]; 1784cdf0e10cSrcweir nVal += (mnUnitsPerPixel + 1) / 2; 1785cdf0e10cSrcweir pJustificationArray[ i ] = nVal / mnUnitsPerPixel; 1786cdf0e10cSrcweir } 1787cdf0e10cSrcweir } 1788cdf0e10cSrcweir 1789cdf0e10cSrcweir // change the mpDXArray temporarilly (just for the justification) 1790cdf0e10cSrcweir aMultiArgs.mpDXArray = pJustificationArray; 1791cdf0e10cSrcweir } 1792cdf0e10cSrcweir } 1793cdf0e10cSrcweir 1794cdf0e10cSrcweir // Compute rtl flags, since in some scripts glyphs/char order can be 1795cdf0e10cSrcweir // reversed for a few character sequencies e.g. Myanmar 1796cdf0e10cSrcweir std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false); 1797cdf0e10cSrcweir rArgs.ResetPos(); 1798cdf0e10cSrcweir bool bRtl; 1799cdf0e10cSrcweir int nRunStart, nRunEnd; 1800cdf0e10cSrcweir while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl)) 1801cdf0e10cSrcweir { 1802cdf0e10cSrcweir if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos), 1803cdf0e10cSrcweir vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true); 1804cdf0e10cSrcweir } 1805cdf0e10cSrcweir rArgs.ResetPos(); 1806cdf0e10cSrcweir 1807cdf0e10cSrcweir // prepare "merge sort" 1808cdf0e10cSrcweir int nStartOld[ MAX_FALLBACK ]; 1809cdf0e10cSrcweir int nStartNew[ MAX_FALLBACK ]; 1810cdf0e10cSrcweir int nCharPos[ MAX_FALLBACK ]; 1811cdf0e10cSrcweir sal_Int32 nGlyphAdv[ MAX_FALLBACK ]; 1812cdf0e10cSrcweir int nValid[ MAX_FALLBACK ] = {0}; 1813cdf0e10cSrcweir 1814cdf0e10cSrcweir sal_GlyphId nDummy; 1815cdf0e10cSrcweir Point aPos; 1816cdf0e10cSrcweir int nLevel = 0, n; 1817cdf0e10cSrcweir for( n = 0; n < mnLevel; ++n ) 1818cdf0e10cSrcweir { 1819cdf0e10cSrcweir // now adjust the individual components 1820cdf0e10cSrcweir if( n > 0 ) 1821cdf0e10cSrcweir { 1822cdf0e10cSrcweir aMultiArgs.maRuns = maFallbackRuns[ n-1 ]; 1823cdf0e10cSrcweir aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK; 1824cdf0e10cSrcweir } 1825cdf0e10cSrcweir mpLayouts[n]->AdjustLayout( aMultiArgs ); 1826cdf0e10cSrcweir 1827cdf0e10cSrcweir // disable glyph-injection for glyph-fallback SalLayout iteration 1828cdf0e10cSrcweir mpLayouts[n]->DisableGlyphInjection( true ); 1829cdf0e10cSrcweir 1830cdf0e10cSrcweir // remove unused parts of component 1831cdf0e10cSrcweir if( n > 0 ) 1832cdf0e10cSrcweir { 1833cdf0e10cSrcweir if (mbInComplete && (n == mnLevel-1)) 1834cdf0e10cSrcweir mpLayouts[n]->Simplify( true ); 1835cdf0e10cSrcweir else 1836cdf0e10cSrcweir mpLayouts[n]->Simplify( false ); 1837cdf0e10cSrcweir } 1838cdf0e10cSrcweir 1839cdf0e10cSrcweir // prepare merging components 1840cdf0e10cSrcweir nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0; 1841cdf0e10cSrcweir nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos, 1842cdf0e10cSrcweir nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] ); 1843cdf0e10cSrcweir #ifdef MULTI_SL_DEBUG 1844cdf0e10cSrcweir 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], 1845cdf0e10cSrcweir rArgs.mpStr[nCharPos[nLevel]]); 1846cdf0e10cSrcweir #endif 1847cdf0e10cSrcweir if( (n > 0) && !nValid[ nLevel ] ) 1848cdf0e10cSrcweir { 1849cdf0e10cSrcweir // an empty fallback layout can be released 1850cdf0e10cSrcweir mpLayouts[n]->Release(); 1851cdf0e10cSrcweir } 1852cdf0e10cSrcweir else 1853cdf0e10cSrcweir { 1854cdf0e10cSrcweir // reshuffle used fallbacks if needed 1855cdf0e10cSrcweir if( nLevel != n ) 1856cdf0e10cSrcweir { 1857cdf0e10cSrcweir mpLayouts[ nLevel ] = mpLayouts[ n ]; 1858cdf0e10cSrcweir mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ]; 1859cdf0e10cSrcweir maFallbackRuns[ nLevel ] = maFallbackRuns[ n ]; 1860cdf0e10cSrcweir } 1861cdf0e10cSrcweir ++nLevel; 1862cdf0e10cSrcweir } 1863cdf0e10cSrcweir } 1864cdf0e10cSrcweir mnLevel = nLevel; 1865cdf0e10cSrcweir 1866cd276675SOliver-Rainer Wittmann // prepare merge the fallback levels 1867cdf0e10cSrcweir long nXPos = 0; 1868cdf0e10cSrcweir double fUnitMul = 1.0; 1869cdf0e10cSrcweir for( n = 0; n < nLevel; ++n ) 1870cdf0e10cSrcweir maFallbackRuns[n].ResetPos(); 1871cd276675SOliver-Rainer Wittmann // get the next codepoint index that needs fallback 1872cdf0e10cSrcweir int nActiveCharPos = nCharPos[0]; 1873cd276675SOliver-Rainer Wittmann // get the end index of the active run 1874cdf0e10cSrcweir int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])? 1875cdf0e10cSrcweir rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1; 1876cdf0e10cSrcweir int nRunVisibleEndChar = nCharPos[0]; 1877cd276675SOliver-Rainer Wittmann // merge the fallback levels 1878cdf0e10cSrcweir while( nValid[0] && (nLevel > 0)) 1879cdf0e10cSrcweir { 1880cdf0e10cSrcweir // find best fallback level 1881cdf0e10cSrcweir for( n = 0; n < nLevel; ++n ) 1882cdf0e10cSrcweir if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) ) 1883cdf0e10cSrcweir // fallback level n wins when it requested no further fallback 1884cdf0e10cSrcweir break; 1885cdf0e10cSrcweir int nFBLevel = n; 1886cdf0e10cSrcweir 1887cdf0e10cSrcweir if( n < nLevel ) 1888cdf0e10cSrcweir { 1889cdf0e10cSrcweir // use base(n==0) or fallback(n>=1) level 1890cdf0e10cSrcweir fUnitMul = mnUnitsPerPixel; 1891cdf0e10cSrcweir fUnitMul /= mpLayouts[n]->GetUnitsPerPixel(); 1892cdf0e10cSrcweir long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5); 1893cdf0e10cSrcweir mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos ); 1894cdf0e10cSrcweir } 1895cdf0e10cSrcweir else 1896cdf0e10cSrcweir { 1897cdf0e10cSrcweir n = 0; // keep NotDef in base level 1898cdf0e10cSrcweir fUnitMul = 1.0; 1899cdf0e10cSrcweir } 1900cdf0e10cSrcweir 1901cdf0e10cSrcweir if( n > 0 ) 1902cdf0e10cSrcweir { 1903cdf0e10cSrcweir // drop the NotDef glyphs in the base layout run if a fallback run exists 1904cdf0e10cSrcweir while ( 1905cdf0e10cSrcweir (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) && 1906cdf0e10cSrcweir (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) ) 1907cdf0e10cSrcweir ) 1908cdf0e10cSrcweir { 1909cdf0e10cSrcweir mpLayouts[0]->DropGlyph( nStartOld[0] ); 1910cdf0e10cSrcweir nStartOld[0] = nStartNew[0]; 1911cdf0e10cSrcweir nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos, 1912cdf0e10cSrcweir nStartNew[0], &nGlyphAdv[0], &nCharPos[0] ); 1913cdf0e10cSrcweir #ifdef MULTI_SL_DEBUG 1914cdf0e10cSrcweir 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]]); 1915cdf0e10cSrcweir #endif 1916cdf0e10cSrcweir if( !nValid[0] ) 1917cdf0e10cSrcweir break; 1918cdf0e10cSrcweir } 1919cdf0e10cSrcweir } 1920cdf0e10cSrcweir 1921cdf0e10cSrcweir // skip to end of layout run and calculate its advance width 1922cdf0e10cSrcweir int nRunAdvance = 0; 1923cdf0e10cSrcweir bool bKeepNotDef = (nFBLevel >= nLevel); 1924cdf0e10cSrcweir for(;;) 1925cdf0e10cSrcweir { 1926cdf0e10cSrcweir nRunAdvance += nGlyphAdv[n]; 1927cdf0e10cSrcweir 1928cdf0e10cSrcweir // proceed to next glyph 1929cdf0e10cSrcweir nStartOld[n] = nStartNew[n]; 1930cdf0e10cSrcweir int nOrigCharPos = nCharPos[n]; 1931cdf0e10cSrcweir nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos, 1932cdf0e10cSrcweir nStartNew[n], &nGlyphAdv[n], &nCharPos[n] ); 1933cdf0e10cSrcweir #ifdef MULTI_SL_DEBUG 1934cdf0e10cSrcweir 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]]); 1935cdf0e10cSrcweir #endif 1936cdf0e10cSrcweir // break after last glyph of active layout 1937cdf0e10cSrcweir if( !nValid[n] ) 1938cdf0e10cSrcweir { 1939cdf0e10cSrcweir // performance optimization (when a fallback layout is no longer needed) 1940cdf0e10cSrcweir if( n >= nLevel-1 ) 1941cdf0e10cSrcweir --nLevel; 1942cdf0e10cSrcweir break; 1943cdf0e10cSrcweir } 1944cdf0e10cSrcweir 1945cdf0e10cSrcweir //If the next character is one which belongs to the next level, then we 1946cdf0e10cSrcweir //are finished here for now, and we'll pick up after the next level has 1947cdf0e10cSrcweir //been processed 1948cdf0e10cSrcweir if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos)) 1949cdf0e10cSrcweir { 1950cdf0e10cSrcweir if (nOrigCharPos < nCharPos[n]) 1951cdf0e10cSrcweir { 1952cdf0e10cSrcweir if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n])) 1953cdf0e10cSrcweir break; 1954cdf0e10cSrcweir } 1955cdf0e10cSrcweir else if (nOrigCharPos > nCharPos[n]) 1956cdf0e10cSrcweir { 1957cdf0e10cSrcweir if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos)) 1958cdf0e10cSrcweir break; 1959cdf0e10cSrcweir } 1960cdf0e10cSrcweir } 1961cdf0e10cSrcweir 1962cdf0e10cSrcweir // break at end of layout run 1963cdf0e10cSrcweir if( n > 0 ) 1964cdf0e10cSrcweir { 1965cdf0e10cSrcweir // skip until end of fallback run 1966cdf0e10cSrcweir if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) ) 1967cdf0e10cSrcweir break; 1968cdf0e10cSrcweir } 1969cdf0e10cSrcweir else 1970cdf0e10cSrcweir { 1971cdf0e10cSrcweir // break when a fallback is needed and available 1972cdf0e10cSrcweir bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] ); 1973cdf0e10cSrcweir if( bNeedFallback ) 1974cdf0e10cSrcweir if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) ) 1975cdf0e10cSrcweir break; 1976cdf0e10cSrcweir // break when change from resolved to unresolved base layout run 1977cdf0e10cSrcweir if( bKeepNotDef && !bNeedFallback ) 1978cdf0e10cSrcweir { maFallbackRuns[0].NextRun(); break; } 1979cdf0e10cSrcweir bKeepNotDef = bNeedFallback; 1980cdf0e10cSrcweir } 1981cdf0e10cSrcweir // check for reordered glyphs 1982cdf0e10cSrcweir if (aMultiArgs.mpDXArray && 1983cdf0e10cSrcweir nRunVisibleEndChar < mnEndCharPos && 1984cdf0e10cSrcweir nRunVisibleEndChar >= mnMinCharPos && 1985cdf0e10cSrcweir nCharPos[n] < mnEndCharPos && 1986cdf0e10cSrcweir nCharPos[n] >= mnMinCharPos) 1987cdf0e10cSrcweir { 1988cdf0e10cSrcweir if (vRtl[nActiveCharPos - mnMinCharPos]) 1989cdf0e10cSrcweir { 1990cdf0e10cSrcweir if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos] 1991cdf0e10cSrcweir >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos]) 1992cdf0e10cSrcweir { 1993cdf0e10cSrcweir nRunVisibleEndChar = nCharPos[n]; 1994cdf0e10cSrcweir } 1995cdf0e10cSrcweir } 1996cdf0e10cSrcweir else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos] 1997cdf0e10cSrcweir <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos]) 1998cdf0e10cSrcweir { 1999cdf0e10cSrcweir nRunVisibleEndChar = nCharPos[n]; 2000cdf0e10cSrcweir } 2001cdf0e10cSrcweir } 2002cdf0e10cSrcweir } 2003cdf0e10cSrcweir 2004cdf0e10cSrcweir // if a justification array is available 2005cdf0e10cSrcweir // => use it directly to calculate the corresponding run width 2006cdf0e10cSrcweir if( aMultiArgs.mpDXArray ) 2007cdf0e10cSrcweir { 2008cdf0e10cSrcweir // the run advance is the width from the first char 2009cdf0e10cSrcweir // in the run to the first char in the next run 2010cdf0e10cSrcweir nRunAdvance = 0; 2011cdf0e10cSrcweir #ifdef MULTI_SL_DEBUG 2012cdf0e10cSrcweir const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]); 2013cdf0e10cSrcweir int nOldRunAdv = 0; 2014cdf0e10cSrcweir int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR; 2015cdf0e10cSrcweir if( nDXIndex >= 0 ) 2016cdf0e10cSrcweir nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ]; 2017cdf0e10cSrcweir nDXIndex = nActiveCharPos - mnMinCharPos - bLTR; 2018cdf0e10cSrcweir if( nDXIndex >= 0 ) 2019cdf0e10cSrcweir nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ]; 2020cdf0e10cSrcweir if( !bLTR ) 2021cdf0e10cSrcweir nOldRunAdv = -nOldRunAdv; 2022cdf0e10cSrcweir #endif 2023cdf0e10cSrcweir if (vRtl[nActiveCharPos - mnMinCharPos]) 2024cdf0e10cSrcweir { 2025cdf0e10cSrcweir if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos) 2026cdf0e10cSrcweir nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos]; 2027cdf0e10cSrcweir if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos) 2028cdf0e10cSrcweir nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos]; 2029cdf0e10cSrcweir #ifdef MULTI_SL_DEBUG 2030cdf0e10cSrcweir fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv); 2031cdf0e10cSrcweir #endif 2032cdf0e10cSrcweir } 2033cdf0e10cSrcweir else 2034cdf0e10cSrcweir { 2035cdf0e10cSrcweir if (nRunVisibleEndChar >= mnMinCharPos) 2036cdf0e10cSrcweir nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos]; 2037cdf0e10cSrcweir if (nLastRunEndChar >= mnMinCharPos) 2038cdf0e10cSrcweir nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos]; 2039cdf0e10cSrcweir #ifdef MULTI_SL_DEBUG 2040cdf0e10cSrcweir fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv); 2041cdf0e10cSrcweir #endif 2042cdf0e10cSrcweir } 2043cdf0e10cSrcweir nLastRunEndChar = nRunVisibleEndChar; 2044cdf0e10cSrcweir nRunVisibleEndChar = nCharPos[0]; 2045cdf0e10cSrcweir // the requested width is still in pixel units 2046cdf0e10cSrcweir // => convert it to base level font units 2047cdf0e10cSrcweir nRunAdvance *= mnUnitsPerPixel; 2048cdf0e10cSrcweir } 2049cdf0e10cSrcweir else 2050cdf0e10cSrcweir { 2051cdf0e10cSrcweir // the measured width is still in fallback font units 2052cdf0e10cSrcweir // => convert it to base level font units 2053cdf0e10cSrcweir if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0) 2054cdf0e10cSrcweir nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5); 2055cdf0e10cSrcweir } 2056cdf0e10cSrcweir 2057cdf0e10cSrcweir // calculate new x position (in base level units) 2058cdf0e10cSrcweir nXPos += nRunAdvance; 2059cdf0e10cSrcweir 2060cdf0e10cSrcweir // prepare for next fallback run 2061cdf0e10cSrcweir nActiveCharPos = nCharPos[0]; 2062cdf0e10cSrcweir // it essential that the runs don't get ahead of themselves and in the 2063cdf0e10cSrcweir // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may 2064cdf0e10cSrcweir // have already been reached on the base level 2065cdf0e10cSrcweir for( int i = nFBLevel; --i >= 0;) 2066cdf0e10cSrcweir { 2067cdf0e10cSrcweir if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl)) 2068cdf0e10cSrcweir { 2069cdf0e10cSrcweir if (bRtl) 2070cdf0e10cSrcweir { 2071cdf0e10cSrcweir if (nRunStart > nActiveCharPos) 2072cdf0e10cSrcweir maFallbackRuns[i].NextRun(); 2073cdf0e10cSrcweir } 2074cdf0e10cSrcweir else 2075cdf0e10cSrcweir { 2076cdf0e10cSrcweir if (nRunEnd <= nActiveCharPos) 2077cdf0e10cSrcweir maFallbackRuns[i].NextRun(); 2078cdf0e10cSrcweir } 2079cdf0e10cSrcweir } 2080cdf0e10cSrcweir } 2081cdf0e10cSrcweir } 2082cdf0e10cSrcweir 2083cdf0e10cSrcweir mpLayouts[0]->Simplify( true ); 2084cdf0e10cSrcweir 2085cdf0e10cSrcweir // reenable glyph-injection 2086cdf0e10cSrcweir for( n = 0; n < mnLevel; ++n ) 2087cdf0e10cSrcweir mpLayouts[n]->DisableGlyphInjection( false ); 2088cdf0e10cSrcweir } 2089cdf0e10cSrcweir 2090cdf0e10cSrcweir // ----------------------------------------------------------------------- 2091cdf0e10cSrcweir 2092cdf0e10cSrcweir void MultiSalLayout::InitFont() const 2093cdf0e10cSrcweir { 2094cdf0e10cSrcweir if( mnLevel > 0 ) 2095cdf0e10cSrcweir mpLayouts[0]->InitFont(); 2096cdf0e10cSrcweir } 2097cdf0e10cSrcweir 2098cdf0e10cSrcweir // ----------------------------------------------------------------------- 2099cdf0e10cSrcweir 2100*248a599fSHerbert Dürr const ImplFontData* MultiSalLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const 2101cdf0e10cSrcweir { 2102*248a599fSHerbert Dürr int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; 2103cdf0e10cSrcweir return mpFallbackFonts[ nFallbackLevel ]; 2104cdf0e10cSrcweir } 2105cdf0e10cSrcweir 2106cdf0e10cSrcweir // ----------------------------------------------------------------------- 2107cdf0e10cSrcweir 2108cdf0e10cSrcweir void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const 2109cdf0e10cSrcweir { 2110cdf0e10cSrcweir for( int i = mnLevel; --i >= 0; ) 2111cdf0e10cSrcweir { 2112cdf0e10cSrcweir SalLayout& rLayout = *mpLayouts[ i ]; 2113cdf0e10cSrcweir rLayout.DrawBase() += maDrawBase; 2114cdf0e10cSrcweir rLayout.DrawOffset() += maDrawOffset; 2115cdf0e10cSrcweir rLayout.InitFont(); 2116cdf0e10cSrcweir rLayout.DrawText( rGraphics ); 2117cdf0e10cSrcweir rLayout.DrawOffset() -= maDrawOffset; 2118cdf0e10cSrcweir rLayout.DrawBase() -= maDrawBase; 2119cdf0e10cSrcweir } 2120cdf0e10cSrcweir // NOTE: now the baselevel font is active again 2121cdf0e10cSrcweir } 2122cdf0e10cSrcweir 2123cdf0e10cSrcweir // ----------------------------------------------------------------------- 2124cdf0e10cSrcweir 2125cdf0e10cSrcweir int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2126cdf0e10cSrcweir { 2127cdf0e10cSrcweir if( mnLevel <= 0 ) 2128cdf0e10cSrcweir return STRING_LEN; 2129cdf0e10cSrcweir if( mnLevel == 1 ) 2130cdf0e10cSrcweir return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor ); 2131cdf0e10cSrcweir 2132cdf0e10cSrcweir int nCharCount = mnEndCharPos - mnMinCharPos; 2133cdf0e10cSrcweir sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) ); 2134cdf0e10cSrcweir mpLayouts[0]->FillDXArray( pCharWidths ); 2135cdf0e10cSrcweir 2136cdf0e10cSrcweir for( int n = 1; n < mnLevel; ++n ) 2137cdf0e10cSrcweir { 2138cdf0e10cSrcweir SalLayout& rLayout = *mpLayouts[ n ]; 2139cdf0e10cSrcweir rLayout.FillDXArray( pCharWidths + nCharCount ); 2140cdf0e10cSrcweir double fUnitMul = mnUnitsPerPixel; 2141cdf0e10cSrcweir fUnitMul /= rLayout.GetUnitsPerPixel(); 2142cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 2143cdf0e10cSrcweir { 2144cdf0e10cSrcweir long w = pCharWidths[ i + nCharCount ]; 2145cdf0e10cSrcweir w = static_cast<long>(w*fUnitMul + 0.5); 2146cdf0e10cSrcweir pCharWidths[ i ] += w; 2147cdf0e10cSrcweir } 2148cdf0e10cSrcweir } 2149cdf0e10cSrcweir 2150cdf0e10cSrcweir long nWidth = 0; 2151cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 2152cdf0e10cSrcweir { 2153cdf0e10cSrcweir nWidth += pCharWidths[ i ] * nFactor; 2154cdf0e10cSrcweir if( nWidth > nMaxWidth ) 2155cdf0e10cSrcweir return (i + mnMinCharPos); 2156cdf0e10cSrcweir nWidth += nCharExtra; 2157cdf0e10cSrcweir } 2158cdf0e10cSrcweir 2159cdf0e10cSrcweir return STRING_LEN; 2160cdf0e10cSrcweir } 2161cdf0e10cSrcweir 2162cdf0e10cSrcweir // ----------------------------------------------------------------------- 2163cdf0e10cSrcweir 2164cdf0e10cSrcweir long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const 2165cdf0e10cSrcweir { 2166cdf0e10cSrcweir long nMaxWidth = 0; 2167cdf0e10cSrcweir 2168cdf0e10cSrcweir // prepare merging of fallback levels 2169cdf0e10cSrcweir sal_Int32* pTempWidths = NULL; 2170cdf0e10cSrcweir const int nCharCount = mnEndCharPos - mnMinCharPos; 2171cdf0e10cSrcweir if( pCharWidths ) 2172cdf0e10cSrcweir { 2173cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 2174cdf0e10cSrcweir pCharWidths[i] = 0; 2175cdf0e10cSrcweir pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) ); 2176cdf0e10cSrcweir } 2177cdf0e10cSrcweir 2178cdf0e10cSrcweir for( int n = mnLevel; --n >= 0; ) 2179cdf0e10cSrcweir { 2180cdf0e10cSrcweir // query every fallback level 2181cdf0e10cSrcweir long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths ); 2182cdf0e10cSrcweir if( !nTextWidth ) 2183cdf0e10cSrcweir continue; 2184cdf0e10cSrcweir // merge results from current level 2185cdf0e10cSrcweir double fUnitMul = mnUnitsPerPixel; 2186cdf0e10cSrcweir fUnitMul /= mpLayouts[n]->GetUnitsPerPixel(); 2187cdf0e10cSrcweir nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5); 2188cdf0e10cSrcweir if( nMaxWidth < nTextWidth ) 2189cdf0e10cSrcweir nMaxWidth = nTextWidth; 2190cdf0e10cSrcweir if( !pCharWidths ) 2191cdf0e10cSrcweir continue; 2192cdf0e10cSrcweir // calculate virtual char widths using most probable fallback layout 2193cdf0e10cSrcweir for( int i = 0; i < nCharCount; ++i ) 2194cdf0e10cSrcweir { 2195cdf0e10cSrcweir // #i17359# restriction: 2196cdf0e10cSrcweir // one char cannot be resolved from different fallbacks 2197cdf0e10cSrcweir if( pCharWidths[i] != 0 ) 2198cdf0e10cSrcweir continue; 2199cdf0e10cSrcweir long nCharWidth = pTempWidths[i]; 2200cdf0e10cSrcweir if( !nCharWidth ) 2201cdf0e10cSrcweir continue; 2202cdf0e10cSrcweir nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5); 2203cdf0e10cSrcweir pCharWidths[i] = nCharWidth; 2204cdf0e10cSrcweir } 2205cdf0e10cSrcweir } 2206cdf0e10cSrcweir 2207cdf0e10cSrcweir return nMaxWidth; 2208cdf0e10cSrcweir } 2209cdf0e10cSrcweir 2210cdf0e10cSrcweir // ----------------------------------------------------------------------- 2211cdf0e10cSrcweir 2212cdf0e10cSrcweir void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const 2213cdf0e10cSrcweir { 2214cdf0e10cSrcweir SalLayout& rLayout = *mpLayouts[ 0 ]; 2215cdf0e10cSrcweir rLayout.GetCaretPositions( nMaxIndex, pCaretXArray ); 2216cdf0e10cSrcweir 2217cdf0e10cSrcweir if( mnLevel > 1 ) 2218cdf0e10cSrcweir { 2219cdf0e10cSrcweir sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) ); 2220cdf0e10cSrcweir for( int n = 1; n < mnLevel; ++n ) 2221cdf0e10cSrcweir { 2222cdf0e10cSrcweir mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos ); 2223cdf0e10cSrcweir double fUnitMul = mnUnitsPerPixel; 2224cdf0e10cSrcweir fUnitMul /= mpLayouts[n]->GetUnitsPerPixel(); 2225cdf0e10cSrcweir for( int i = 0; i < nMaxIndex; ++i ) 2226cdf0e10cSrcweir if( pTempPos[i] >= 0 ) 2227cdf0e10cSrcweir { 2228cdf0e10cSrcweir long w = pTempPos[i]; 2229cdf0e10cSrcweir w = static_cast<long>(w*fUnitMul + 0.5); 2230cdf0e10cSrcweir pCaretXArray[i] = w; 2231cdf0e10cSrcweir } 2232cdf0e10cSrcweir } 2233cdf0e10cSrcweir } 2234cdf0e10cSrcweir } 2235cdf0e10cSrcweir 2236cdf0e10cSrcweir // ----------------------------------------------------------------------- 2237cdf0e10cSrcweir 2238cdf0e10cSrcweir int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos, 2239cdf0e10cSrcweir int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const 2240cdf0e10cSrcweir { 2241cdf0e10cSrcweir // for multi-level fallback only single glyphs should be used 2242cdf0e10cSrcweir if( mnLevel > 1 && nLen > 1 ) 2243cdf0e10cSrcweir nLen = 1; 2244cdf0e10cSrcweir 2245cdf0e10cSrcweir // NOTE: nStart is tagged with current font index 2246cdf0e10cSrcweir int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT; 2247cdf0e10cSrcweir nStart &= ~GF_FONTMASK; 2248cdf0e10cSrcweir for(; nLevel < mnLevel; ++nLevel, nStart=0 ) 2249cdf0e10cSrcweir { 2250cdf0e10cSrcweir SalLayout& rLayout = *mpLayouts[ nLevel ]; 2251cdf0e10cSrcweir rLayout.InitFont(); 2252cdf0e10cSrcweir int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos, 2253cdf0e10cSrcweir nStart, pGlyphAdvAry, pCharPosAry ); 2254cdf0e10cSrcweir if( nRetVal ) 2255cdf0e10cSrcweir { 2256cdf0e10cSrcweir int nFontTag = nLevel << GF_FONTSHIFT; 2257cdf0e10cSrcweir nStart |= nFontTag; 2258cdf0e10cSrcweir double fUnitMul = mnUnitsPerPixel; 2259cdf0e10cSrcweir fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel(); 2260cdf0e10cSrcweir for( int i = 0; i < nRetVal; ++i ) 2261cdf0e10cSrcweir { 2262cdf0e10cSrcweir if( pGlyphAdvAry ) 2263cdf0e10cSrcweir { 2264cdf0e10cSrcweir long w = pGlyphAdvAry[i]; 2265cdf0e10cSrcweir w = static_cast<long>(w * fUnitMul + 0.5); 2266cdf0e10cSrcweir pGlyphAdvAry[i] = w; 2267cdf0e10cSrcweir } 2268cdf0e10cSrcweir pGlyphIdxAry[ i ] |= nFontTag; 2269cdf0e10cSrcweir } 2270cdf0e10cSrcweir rPos += maDrawBase; 2271cdf0e10cSrcweir rPos += maDrawOffset; 2272cdf0e10cSrcweir return nRetVal; 2273cdf0e10cSrcweir } 2274cdf0e10cSrcweir } 2275cdf0e10cSrcweir 2276cdf0e10cSrcweir // #111016# reset to base level font when done 2277cdf0e10cSrcweir mpLayouts[0]->InitFont(); 2278cdf0e10cSrcweir return 0; 2279cdf0e10cSrcweir } 2280cdf0e10cSrcweir 2281cdf0e10cSrcweir // ----------------------------------------------------------------------- 2282cdf0e10cSrcweir 2283cdf0e10cSrcweir bool MultiSalLayout::GetOutline( SalGraphics& rGraphics, 2284cdf0e10cSrcweir ::basegfx::B2DPolyPolygonVector& rPPV ) const 2285cdf0e10cSrcweir { 2286cdf0e10cSrcweir bool bRet = false; 2287cdf0e10cSrcweir 2288cdf0e10cSrcweir for( int i = mnLevel; --i >= 0; ) 2289cdf0e10cSrcweir { 2290cdf0e10cSrcweir SalLayout& rLayout = *mpLayouts[ i ]; 2291cdf0e10cSrcweir rLayout.DrawBase() = maDrawBase; 2292cdf0e10cSrcweir rLayout.DrawOffset() += maDrawOffset; 2293cdf0e10cSrcweir rLayout.InitFont(); 2294cdf0e10cSrcweir bRet |= rLayout.GetOutline( rGraphics, rPPV ); 2295cdf0e10cSrcweir rLayout.DrawOffset() -= maDrawOffset; 2296cdf0e10cSrcweir } 2297cdf0e10cSrcweir 2298cdf0e10cSrcweir return bRet; 2299cdf0e10cSrcweir } 2300cdf0e10cSrcweir 2301cdf0e10cSrcweir // ----------------------------------------------------------------------- 2302cdf0e10cSrcweir 2303cdf0e10cSrcweir bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const 2304cdf0e10cSrcweir { 2305cdf0e10cSrcweir bool bRet = false; 2306cdf0e10cSrcweir 2307cdf0e10cSrcweir Rectangle aRectangle; 2308cdf0e10cSrcweir for( int i = mnLevel; --i >= 0; ) 2309cdf0e10cSrcweir { 2310cdf0e10cSrcweir SalLayout& rLayout = *mpLayouts[ i ]; 2311cdf0e10cSrcweir rLayout.DrawBase() = maDrawBase; 2312cdf0e10cSrcweir rLayout.DrawOffset() += maDrawOffset; 2313cdf0e10cSrcweir rLayout.InitFont(); 2314cdf0e10cSrcweir if( rLayout.GetBoundRect( rGraphics, aRectangle ) ) 2315cdf0e10cSrcweir { 2316cdf0e10cSrcweir rRect.Union( aRectangle ); 2317cdf0e10cSrcweir bRet = true; 2318cdf0e10cSrcweir } 2319cdf0e10cSrcweir rLayout.DrawOffset() -= maDrawOffset; 2320cdf0e10cSrcweir } 2321cdf0e10cSrcweir 2322cdf0e10cSrcweir return bRet; 2323cdf0e10cSrcweir } 2324cdf0e10cSrcweir 2325cdf0e10cSrcweir // ======================================================================= 2326