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