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