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