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 "rtl/ustring.hxx" 32 33 #include "osl/module.h" 34 #include "osl/file.h" 35 36 #include "tools/svwin.h" 37 38 #include "vcl/svapp.hxx" 39 40 #include "win/salgdi.h" 41 #include "win/saldata.hxx" 42 43 // for GetMirroredChar 44 #include "sft.hxx" 45 #include "sallayout.hxx" 46 47 #include <cstdio> 48 #include <malloc.h> 49 #ifndef __MINGW32__ 50 #define alloca _alloca 51 #endif 52 53 #ifdef GCP_KERN_HACK 54 #include <algorithm> 55 #endif // GCP_KERN_HACK 56 57 58 #define USE_UNISCRIBE 59 #ifdef USE_UNISCRIBE 60 #include <Usp10.h> 61 #include <ShLwApi.h> 62 #include <winver.h> 63 #endif // USE_UNISCRIBE 64 65 #include <hash_map> 66 #include <set> 67 68 typedef std::hash_map<int,int> IntMap; 69 typedef std::set<int> IntSet; 70 71 // Graphite headers 72 #ifdef ENABLE_GRAPHITE 73 #include <i18npool/mslangid.hxx> 74 #include <graphite/GrClient.h> 75 #include <graphite/WinFont.h> 76 #include <graphite/Segment.h> 77 #include <graphite_layout.hxx> 78 #include <graphite_cache.hxx> 79 #include <graphite_features.hxx> 80 #endif 81 82 #define DROPPED_OUTGLYPH 0xFFFF 83 84 using namespace rtl; 85 86 // ======================================================================= 87 88 // win32 specific physical font instance 89 class ImplWinFontEntry : public ImplFontEntry 90 { 91 public: 92 ImplWinFontEntry( ImplFontSelectData& ); 93 ~ImplWinFontEntry(); 94 95 private: 96 // TODO: also add HFONT??? Watch out for issues with too many active fonts... 97 98 #ifdef GCP_KERN_HACK 99 public: 100 bool HasKernData() const; 101 void SetKernData( int, const KERNINGPAIR* ); 102 int GetKerning( sal_Unicode, sal_Unicode ) const; 103 private: 104 KERNINGPAIR* mpKerningPairs; 105 int mnKerningPairs; 106 #endif // GCP_KERN_HACK 107 108 #ifdef USE_UNISCRIBE 109 public: 110 SCRIPT_CACHE& GetScriptCache() const 111 { return maScriptCache; } 112 private: 113 mutable SCRIPT_CACHE maScriptCache; 114 #endif // USE_UNISCRIBE 115 116 public: 117 int GetCachedGlyphWidth( int nCharCode ) const; 118 void CacheGlyphWidth( int nCharCode, int nCharWidth ); 119 120 bool InitKashidaHandling( HDC ); 121 int GetMinKashidaWidth() const { return mnMinKashidaWidth; } 122 int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; } 123 124 private: 125 IntMap maWidthMap; 126 mutable int mnMinKashidaWidth; 127 mutable int mnMinKashidaGlyph; 128 }; 129 130 // ----------------------------------------------------------------------- 131 132 inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth ) 133 { 134 maWidthMap[ nCharCode ] = nCharWidth; 135 } 136 137 inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const 138 { 139 IntMap::const_iterator it = maWidthMap.find( nCharCode ); 140 if( it == maWidthMap.end() ) 141 return -1; 142 return it->second; 143 } 144 145 // ======================================================================= 146 147 class WinLayout : public SalLayout 148 { 149 public: 150 WinLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& ); 151 virtual void InitFont() const; 152 void SetFontScale( float f ) { mfFontScale = f; } 153 float GetFontScale() const { return mfFontScale; } 154 HFONT DisableFontScaling( void) const; 155 156 #ifdef USE_UNISCRIBE 157 SCRIPT_CACHE& GetScriptCache() const 158 { return mrWinFontEntry.GetScriptCache(); } 159 #endif // USE_UNISCRIBE 160 161 protected: 162 HDC mhDC; // WIN32 device handle 163 HFONT mhFont; // WIN32 font handle 164 int mnBaseAdv; // x-offset relative to Layout origin 165 float mfFontScale; // allows metrics emulation of huge font sizes 166 167 const ImplWinFontData& mrWinFontData; 168 ImplWinFontEntry& mrWinFontEntry; 169 }; 170 171 // ======================================================================= 172 173 class SimpleWinLayout : public WinLayout 174 { 175 public: 176 SimpleWinLayout( HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry& ); 177 virtual ~SimpleWinLayout(); 178 179 virtual bool LayoutText( ImplLayoutArgs& ); 180 virtual void AdjustLayout( ImplLayoutArgs& ); 181 virtual void DrawText( SalGraphics& ) const; 182 183 virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, 184 sal_Int32* pGlyphAdvances, int* pCharIndexes ) const; 185 186 virtual long FillDXArray( long* pDXArray ) const; 187 virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; 188 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; 189 190 // for glyph+font+script fallback 191 virtual void MoveGlyph( int nStart, long nNewXPos ); 192 virtual void DropGlyph( int nStart ); 193 virtual void Simplify( bool bIsBase ); 194 195 protected: 196 void Justify( long nNewWidth ); 197 void ApplyDXArray( const ImplLayoutArgs& ); 198 199 private: 200 int mnGlyphCount; 201 int mnCharCount; 202 WCHAR* mpOutGlyphs; 203 int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[] 204 int* mpGlyphOrigAdvs; 205 int* mpCharWidths; // map rel char pos to char width 206 int* mpChars2Glyphs; // map rel char pos to abs glyph pos 207 int* mpGlyphs2Chars; // map abs glyph pos to abs char pos 208 bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL 209 mutable long mnWidth; 210 bool mbDisableGlyphs; 211 212 int mnNotdefWidth; 213 BYTE mnCharSet; 214 }; 215 216 // ======================================================================= 217 218 WinLayout::WinLayout( HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE ) 219 : mhDC( hDC ), 220 mhFont( (HFONT)::GetCurrentObject(hDC,OBJ_FONT) ), 221 mnBaseAdv( 0 ), 222 mfFontScale( 1.0 ), 223 mrWinFontData( rWFD ), 224 mrWinFontEntry( rWFE ) 225 {} 226 227 // ----------------------------------------------------------------------- 228 229 void WinLayout::InitFont() const 230 { 231 ::SelectObject( mhDC, mhFont ); 232 } 233 234 // ----------------------------------------------------------------------- 235 236 // Using reasonably sized fonts to emulate huge fonts works around 237 // a lot of problems in printer and display drivers. Huge fonts are 238 // mostly used by high resolution reference devices which are never 239 // painted to anyway. In the rare case that a huge font needs to be 240 // displayed somewhere then the workaround doesn't help anymore. 241 // If the drivers fail silently for huge fonts, so be it... 242 HFONT WinLayout::DisableFontScaling() const 243 { 244 if( mfFontScale == 1.0 ) 245 return 0; 246 247 LOGFONTW aLogFont; 248 ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont); 249 aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight); 250 aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth); 251 HFONT hHugeFont = ::CreateFontIndirectW( &aLogFont); 252 if( !hHugeFont ) 253 return 0; 254 255 return SelectFont( mhDC, hHugeFont ); 256 } 257 258 // ======================================================================= 259 260 SimpleWinLayout::SimpleWinLayout( HDC hDC, BYTE nCharSet, 261 const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry ) 262 : WinLayout( hDC, rWinFontData, rWinFontEntry ), 263 mnGlyphCount( 0 ), 264 mnCharCount( 0 ), 265 mpOutGlyphs( NULL ), 266 mpGlyphAdvances( NULL ), 267 mpGlyphOrigAdvs( NULL ), 268 mpCharWidths( NULL ), 269 mpChars2Glyphs( NULL ), 270 mpGlyphs2Chars( NULL ), 271 mpGlyphRTLFlags( NULL ), 272 mnWidth( 0 ), 273 mnNotdefWidth( -1 ), 274 mnCharSet( nCharSet ), 275 mbDisableGlyphs( false ) 276 { 277 mbDisableGlyphs = true; 278 } 279 280 // ----------------------------------------------------------------------- 281 282 SimpleWinLayout::~SimpleWinLayout() 283 { 284 delete[] mpGlyphRTLFlags; 285 delete[] mpGlyphs2Chars; 286 delete[] mpChars2Glyphs; 287 if( mpCharWidths != mpGlyphAdvances ) 288 delete[] mpCharWidths; 289 delete[] mpGlyphOrigAdvs; 290 delete[] mpGlyphAdvances; 291 delete[] mpOutGlyphs; 292 } 293 294 // ----------------------------------------------------------------------- 295 296 bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs ) 297 { 298 // prepare layout 299 // TODO: fix case when recyclying old SimpleWinLayout object 300 mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0); 301 mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; 302 303 if( !mbDisableGlyphs ) 304 { 305 // Win32 glyph APIs have serious problems with vertical layout 306 // => workaround is to use the unicode methods then 307 if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL ) 308 mbDisableGlyphs = true; 309 else 310 // use cached value from font face 311 mbDisableGlyphs = mrWinFontData.IsGlyphApiDisabled(); 312 } 313 314 // TODO: use a cached value for bDisableAsianKern from upper layers 315 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN ) 316 { 317 TEXTMETRICA aTextMetricA; 318 if( ::GetTextMetricsA( mhDC, &aTextMetricA ) 319 && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) ) 320 rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN; 321 } 322 323 // layout text 324 int i, j; 325 326 mnGlyphCount = 0; 327 bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0; 328 329 // count the number of chars to process if no RTL run 330 rArgs.ResetPos(); 331 bool bHasRTL = false; 332 while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL ) 333 mnGlyphCount += j - i; 334 335 // if there are RTL runs we need room to remember individual BiDi flags 336 if( bHasRTL ) 337 { 338 mpGlyphRTLFlags = new bool[ mnCharCount ]; 339 for( i = 0; i < mnCharCount; ++i ) 340 mpGlyphRTLFlags[i] = false; 341 } 342 343 // rewrite the logical string if needed to prepare for the API calls 344 const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos; 345 if( (mnGlyphCount != mnCharCount) || bVertical ) 346 { 347 // we need to rewrite the pBidiStr when any of 348 // - BiDirectional layout 349 // - vertical layout 350 // - partial runs (e.g. with control chars or for glyph fallback) 351 // are involved 352 sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) ); 353 pBidiStr = pRewrittenStr; 354 355 // note: glyph to char mapping is relative to first character 356 mpChars2Glyphs = new int[ mnCharCount ]; 357 mpGlyphs2Chars = new int[ mnCharCount ]; 358 for( i = 0; i < mnCharCount; ++i ) 359 mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1; 360 361 mnGlyphCount = 0; 362 rArgs.ResetPos(); 363 bool bIsRTL = false; 364 while( rArgs.GetNextRun( &i, &j, &bIsRTL ) ) 365 { 366 do 367 { 368 // get the next leftmost character in this run 369 int nCharPos = bIsRTL ? --j : i++; 370 sal_UCS4 cChar = rArgs.mpStr[ nCharPos ]; 371 372 // in the RTL case mirror the character and remember its RTL status 373 if( bIsRTL ) 374 { 375 cChar = ::GetMirroredChar( cChar ); 376 mpGlyphRTLFlags[ mnGlyphCount ] = true; 377 } 378 379 // for vertical writing use vertical alternatives 380 if( bVertical ) 381 { 382 sal_UCS4 cVert = ::GetVerticalChar( cChar ); 383 if( cVert ) 384 cChar = cVert; 385 } 386 387 // rewrite the original string 388 // update the mappings between original and rewritten string 389 // TODO: support surrogates in rewritten strings 390 pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar); 391 mpGlyphs2Chars[ mnGlyphCount ] = nCharPos; 392 mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount; 393 ++mnGlyphCount; 394 } while( i < j ); 395 } 396 } 397 398 mpOutGlyphs = new WCHAR[ mnGlyphCount ]; 399 mpGlyphAdvances = new int[ mnGlyphCount ]; 400 401 if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) ) 402 mpGlyphOrigAdvs = new int[ mnGlyphCount ]; 403 404 #ifndef GCP_KERN_HACK 405 DWORD nGcpOption = 0; 406 // enable kerning if requested 407 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS ) 408 nGcpOption |= GCP_USEKERNING; 409 #endif // GCP_KERN_HACK 410 411 for( i = 0; i < mnGlyphCount; ++i ) 412 mpOutGlyphs[i] = pBidiStr[ i ]; 413 mnWidth = 0; 414 for( i = 0; i < mnGlyphCount; ++i ) 415 { 416 // get the current UCS-4 code point, check for surrogate pairs 417 const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]); 418 unsigned nCharCode = pCodes[0]; 419 bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF)); 420 if( bSurrogate ) 421 { 422 if( nCharCode >= 0xDC00 ) // this part of a surrogate pair was already processed 423 continue; 424 nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00); 425 } 426 427 // get the advance width for the current UCS-4 code point 428 int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode ); 429 if( nGlyphWidth == -1 ) 430 { 431 ABC aABC; 432 SIZE aExtent; 433 if( ::GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) ) 434 nGlyphWidth = aExtent.cx; 435 else if( ::GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) ) 436 nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC; 437 else if( !::GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth ) 438 && !::GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) ) 439 nGlyphWidth = 0; 440 mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth ); 441 } 442 mpGlyphAdvances[ i ] = nGlyphWidth; 443 mnWidth += nGlyphWidth; 444 445 // remaining codes of surrogate pair get a zero width 446 if( bSurrogate && ((i+1) < mnGlyphCount) ) 447 mpGlyphAdvances[ i+1 ] = 0; 448 449 // check with the font face if glyph fallback is needed 450 if( mrWinFontData.HasChar( nCharCode ) ) 451 continue; 452 453 // request glyph fallback at this position in the string 454 bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false; 455 int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos; 456 rArgs.NeedFallback( nCharPos, bRTL ); 457 if( bSurrogate && ((nCharPos+1) < rArgs.mnLength) ) 458 rArgs.NeedFallback( nCharPos+1, bRTL ); 459 460 // replace the current glyph shape with the NotDef glyph shape 461 if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK ) 462 { 463 // when we already are layouting for glyph fallback 464 // then a new unresolved glyph is not interesting 465 mnNotdefWidth = 0; 466 mpOutGlyphs[i] = DROPPED_OUTGLYPH; 467 } 468 else 469 { 470 if( mnNotdefWidth < 0 ) 471 { 472 // get the width of the NotDef glyph 473 SIZE aExtent; 474 WCHAR cNotDef = rArgs.mpStr[ nCharPos ]; 475 mnNotdefWidth = 0; 476 if( ::GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) ) 477 mnNotdefWidth = aExtent.cx; 478 } 479 // use a better NotDef glyph 480 if( !mbDisableGlyphs && !bSurrogate ) 481 mpOutGlyphs[i] = 0; 482 } 483 if( bSurrogate && ((i+1) < mnGlyphCount) ) 484 mpOutGlyphs[i+1] = DROPPED_OUTGLYPH; 485 486 // adjust the current glyph width to the NotDef glyph width 487 mnWidth += mnNotdefWidth - mpGlyphAdvances[i]; 488 mpGlyphAdvances[i] = mnNotdefWidth; 489 if( mpGlyphOrigAdvs ) 490 mpGlyphOrigAdvs[i] = mnNotdefWidth; 491 } 492 493 #ifdef GCP_KERN_HACK 494 // apply kerning if the layout engine has not yet done it 495 if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) ) 496 { 497 #else // GCP_KERN_HACK 498 // apply just asian kerning 499 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN ) 500 { 501 if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) ) 502 #endif // GCP_KERN_HACK 503 for( i = 0; i < mnGlyphCount; ++i ) 504 mpGlyphOrigAdvs[i] = mpGlyphAdvances[i]; 505 506 // #99658# also apply asian kerning on the substring border 507 int nLen = mnGlyphCount; 508 if( rArgs.mnMinCharPos + nLen < rArgs.mnLength ) 509 ++nLen; 510 for( i = 1; i < nLen; ++i ) 511 { 512 #ifdef GCP_KERN_HACK 513 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS ) 514 { 515 int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] ); 516 mpGlyphAdvances[ i-1 ] += nKernAmount; 517 mnWidth += nKernAmount; 518 } 519 else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN ) 520 #endif // GCP_KERN_HACK 521 522 if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1]))) 523 && ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) ) 524 { 525 long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical ); 526 long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical ); 527 528 long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext; 529 if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 ) 530 { 531 nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4; 532 mpGlyphAdvances[i-1] += nDelta; 533 mnWidth += nDelta; 534 } 535 } 536 } 537 } 538 539 // calculate virtual char widths 540 if( !mpGlyphs2Chars ) 541 mpCharWidths = mpGlyphAdvances; 542 else 543 { 544 mpCharWidths = new int[ mnCharCount ]; 545 for( i = 0; i < mnCharCount; ++i ) 546 mpCharWidths[ i ] = 0; 547 for( i = 0; i < mnGlyphCount; ++i ) 548 { 549 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos; 550 if( j >= 0 ) 551 mpCharWidths[ j ] += mpGlyphAdvances[ i ]; 552 } 553 } 554 555 // scale layout metrics if needed 556 // TODO: does it make the code more simple if the metric scaling 557 // is moved to the methods that need metric scaling (e.g. FillDXArray())? 558 if( mfFontScale != 1.0 ) 559 { 560 mnWidth = (long)(mnWidth * mfFontScale); 561 mnBaseAdv = (int)(mnBaseAdv * mfFontScale); 562 for( i = 0; i < mnCharCount; ++i ) 563 mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale); 564 if( mpGlyphAdvances != mpCharWidths ) 565 for( i = 0; i < mnGlyphCount; ++i ) 566 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale); 567 if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) ) 568 for( i = 0; i < mnGlyphCount; ++i ) 569 mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale); 570 } 571 572 return true; 573 } 574 575 // ----------------------------------------------------------------------- 576 577 int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int& nStart, 578 long* pGlyphAdvances, int* pCharIndexes ) const 579 { 580 // return zero if no more glyph found 581 if( nStart >= mnGlyphCount ) 582 return 0; 583 584 // calculate glyph position relative to layout base 585 // TODO: avoid for nStart!=0 case by reusing rPos 586 long nXOffset = mnBaseAdv; 587 for( int i = 0; i < nStart; ++i ) 588 nXOffset += mpGlyphAdvances[ i ]; 589 590 // calculate absolute position in pixel units 591 Point aRelativePos( nXOffset, 0 ); 592 rPos = GetDrawPosition( aRelativePos ); 593 594 int nCount = 0; 595 while( nCount < nLen ) 596 { 597 // update return values {nGlyphIndex,nCharPos,nGlyphAdvance} 598 sal_GlyphId nGlyphIndex = mpOutGlyphs[ nStart ]; 599 if( mbDisableGlyphs ) 600 { 601 if( mnLayoutFlags & SAL_LAYOUT_VERTICAL ) 602 { 603 const sal_UCS4 cChar = static_cast<sal_UCS4>(nGlyphIndex & GF_IDXMASK); 604 if( mrWinFontData.HasGSUBstitutions( mhDC ) 605 && mrWinFontData.IsGSUBstituted( cChar ) ) 606 nGlyphIndex |= GF_GSUB | GF_ROTL; 607 else 608 { 609 nGlyphIndex |= GetVerticalFlags( cChar ); 610 if( (nGlyphIndex & GF_ROTMASK) == 0 ) 611 nGlyphIndex |= GF_VERT; 612 } 613 } 614 nGlyphIndex |= GF_ISCHAR; 615 } 616 ++nCount; 617 *(pGlyphs++) = nGlyphIndex; 618 if( pGlyphAdvances ) 619 *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ]; 620 if( pCharIndexes ) 621 { 622 int nCharPos; 623 if( !mpGlyphs2Chars ) 624 nCharPos = nStart + mnMinCharPos; 625 else 626 nCharPos = mpGlyphs2Chars[nStart]; 627 *(pCharIndexes++) = nCharPos; 628 } 629 630 // stop at last glyph 631 if( ++nStart >= mnGlyphCount ) 632 break; 633 634 // stop when next x-position is unexpected 635 if( !pGlyphAdvances && mpGlyphOrigAdvs ) 636 if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] ) 637 break; 638 } 639 640 return nCount; 641 } 642 643 // ----------------------------------------------------------------------- 644 645 void SimpleWinLayout::DrawText( SalGraphics& rGraphics ) const 646 { 647 if( mnGlyphCount <= 0 ) 648 return; 649 650 WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics); 651 HDC aHDC = rWinGraphics.mhDC; 652 653 HFONT hOrigFont = DisableFontScaling(); 654 655 UINT mnDrawOptions = ETO_GLYPH_INDEX; 656 if( mbDisableGlyphs ) 657 mnDrawOptions = 0; 658 659 Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) ); 660 661 // #108267#, break up into glyph portions of a limited size required by Win32 API 662 const unsigned int maxGlyphCount = 8192; 663 UINT numGlyphPortions = mnGlyphCount / maxGlyphCount; 664 UINT remainingGlyphs = mnGlyphCount % maxGlyphCount; 665 666 if( numGlyphPortions ) 667 { 668 // #108267#,#109387# break up string into smaller chunks 669 // the output positions will be updated by windows (SetTextAlign) 670 POINT oldPos; 671 UINT oldTa = ::GetTextAlign( aHDC ); 672 ::SetTextAlign( aHDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP ); 673 ::MoveToEx( aHDC, aPos.X(), aPos.Y(), &oldPos ); 674 unsigned int i = 0; 675 for( unsigned int n = 0; n < numGlyphPortions; ++n, i+=maxGlyphCount ) 676 ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL, 677 mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i ); 678 ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL, 679 mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i ); 680 ::MoveToEx( aHDC, oldPos.x, oldPos.y, (LPPOINT) NULL); 681 ::SetTextAlign( aHDC, oldTa ); 682 } 683 else 684 ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), mnDrawOptions, NULL, 685 mpOutGlyphs, mnGlyphCount, mpGlyphAdvances ); 686 687 if( hOrigFont ) 688 DeleteFont( SelectFont( aHDC, hOrigFont ) ); 689 } 690 691 // ----------------------------------------------------------------------- 692 693 long SimpleWinLayout::FillDXArray( long* pDXArray ) const 694 { 695 if( !mnWidth ) 696 { 697 long mnWidth = mnBaseAdv; 698 for( int i = 0; i < mnGlyphCount; ++i ) 699 mnWidth += mpGlyphAdvances[ i ]; 700 } 701 702 if( pDXArray != NULL ) 703 { 704 for( int i = 0; i < mnCharCount; ++i ) 705 pDXArray[ i ] = mpCharWidths[ i ]; 706 } 707 708 return mnWidth; 709 } 710 711 // ----------------------------------------------------------------------- 712 713 int SimpleWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 714 // NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values 715 { 716 if( mnWidth ) 717 if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth ) 718 return STRING_LEN; 719 720 long nExtraWidth = mnBaseAdv * nFactor; 721 for( int n = 0; n < mnCharCount; ++n ) 722 { 723 // skip unused characters 724 if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) ) 725 continue; 726 // add char widths until max 727 nExtraWidth += mpCharWidths[ n ] * nFactor; 728 if( nExtraWidth >= nMaxWidth ) 729 return (mnMinCharPos + n); 730 nExtraWidth += nCharExtra; 731 } 732 733 return STRING_LEN; 734 } 735 736 // ----------------------------------------------------------------------- 737 738 void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const 739 { 740 long nXPos = mnBaseAdv; 741 742 if( !mpGlyphs2Chars ) 743 { 744 for( int i = 0; i < nMaxIdx; i += 2 ) 745 { 746 pCaretXArray[ i ] = nXPos; 747 nXPos += mpGlyphAdvances[ i>>1 ]; 748 pCaretXArray[ i+1 ] = nXPos; 749 } 750 } 751 else 752 { 753 int i; 754 for( i = 0; i < nMaxIdx; ++i ) 755 pCaretXArray[ i ] = -1; 756 757 // assign glyph positions to character positions 758 for( i = 0; i < mnGlyphCount; ++i ) 759 { 760 int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos; 761 long nXRight = nXPos + mpCharWidths[ nCurrIdx ]; 762 nCurrIdx *= 2; 763 if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) ) 764 { 765 // normal positions for LTR case 766 pCaretXArray[ nCurrIdx ] = nXPos; 767 pCaretXArray[ nCurrIdx+1 ] = nXRight; 768 } 769 else 770 { 771 // reverse positions for RTL case 772 pCaretXArray[ nCurrIdx ] = nXRight; 773 pCaretXArray[ nCurrIdx+1 ] = nXPos; 774 } 775 nXPos += mpGlyphAdvances[ i ]; 776 } 777 } 778 } 779 780 // ----------------------------------------------------------------------- 781 782 void SimpleWinLayout::Justify( long nNewWidth ) 783 { 784 long nOldWidth = mnWidth; 785 mnWidth = nNewWidth; 786 787 if( mnGlyphCount <= 0 ) 788 return; 789 790 if( nNewWidth == nOldWidth ) 791 return; 792 793 // the rightmost glyph cannot be stretched 794 const int nRight = mnGlyphCount - 1; 795 nOldWidth -= mpGlyphAdvances[ nRight ]; 796 nNewWidth -= mpGlyphAdvances[ nRight ]; 797 798 // count stretchable glyphs 799 int nStretchable = 0, i; 800 for( i = 0; i < nRight; ++i ) 801 if( mpGlyphAdvances[i] >= 0 ) 802 ++nStretchable; 803 804 // stretch these glyphs 805 int nDiffWidth = nNewWidth - nOldWidth; 806 for( i = 0; (i < nRight) && (nStretchable > 0); ++i ) 807 { 808 if( mpGlyphAdvances[i] <= 0 ) 809 continue; 810 int nDeltaWidth = nDiffWidth / nStretchable; 811 mpGlyphAdvances[i] += nDeltaWidth; 812 --nStretchable; 813 nDiffWidth -= nDeltaWidth; 814 } 815 } 816 817 // ----------------------------------------------------------------------- 818 819 void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 820 { 821 SalLayout::AdjustLayout( rArgs ); 822 823 // adjust positions if requested 824 if( rArgs.mpDXArray ) 825 ApplyDXArray( rArgs ); 826 else if( rArgs.mnLayoutWidth ) 827 Justify( rArgs.mnLayoutWidth ); 828 else 829 return; 830 831 // recalculate virtual char widths if they were changed 832 if( mpCharWidths != mpGlyphAdvances ) 833 { 834 int i; 835 if( !mpGlyphs2Chars ) 836 { 837 // standard LTR case 838 for( i = 0; i < mnGlyphCount; ++i ) 839 mpCharWidths[ i ] = mpGlyphAdvances[ i ]; 840 } 841 else 842 { 843 // BiDi or complex case 844 for( i = 0; i < mnCharCount; ++i ) 845 mpCharWidths[ i ] = 0; 846 for( i = 0; i < mnGlyphCount; ++i ) 847 { 848 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos; 849 if( j >= 0 ) 850 mpCharWidths[ j ] += mpGlyphAdvances[ i ]; 851 } 852 } 853 } 854 } 855 856 // ----------------------------------------------------------------------- 857 858 void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) 859 { 860 // try to avoid disturbance of text flow for LSB rounding case; 861 const long* pDXArray = rArgs.mpDXArray; 862 863 int i = 0; 864 long nOldWidth = mnBaseAdv; 865 for(; i < mnCharCount; ++i ) 866 { 867 int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i]; 868 if( j >= 0 ) 869 { 870 nOldWidth += mpGlyphAdvances[ j ]; 871 int nDiff = nOldWidth - pDXArray[ i ]; 872 873 // disabled because of #104768# 874 // works great for static text, but problems when typing 875 // if( nDiff>+1 || nDiff<-1 ) 876 // only bother with changing anything when something moved 877 if( nDiff != 0 ) 878 break; 879 } 880 } 881 if( i >= mnCharCount ) 882 return; 883 884 if( !mpGlyphOrigAdvs ) 885 { 886 mpGlyphOrigAdvs = new int[ mnGlyphCount ]; 887 for( i = 0; i < mnGlyphCount; ++i ) 888 mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ]; 889 } 890 891 mnWidth = mnBaseAdv; 892 for( i = 0; i < mnCharCount; ++i ) 893 { 894 int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i]; 895 if( j >= 0 ) 896 mpGlyphAdvances[j] = pDXArray[i] - mnWidth; 897 mnWidth = pDXArray[i]; 898 } 899 } 900 901 // ----------------------------------------------------------------------- 902 903 void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos ) 904 { 905 if( nStart > mnGlyphCount ) 906 return; 907 908 // calculate the current x-position of the requested glyph 909 // TODO: cache absolute positions 910 int nXPos = mnBaseAdv; 911 for( int i = 0; i < nStart; ++i ) 912 nXPos += mpGlyphAdvances[i]; 913 914 // calculate the difference to the current glyph position 915 int nDelta = nNewXPos - nXPos; 916 917 // adjust the width of the layout if it was already cached 918 if( mnWidth ) 919 mnWidth += nDelta; 920 921 // depending on whether the requested glyph is leftmost in the layout 922 // adjust either the layout's or the requested glyph's relative position 923 if( nStart > 0 ) 924 mpGlyphAdvances[ nStart-1 ] += nDelta; 925 else 926 mnBaseAdv += nDelta; 927 } 928 929 // ----------------------------------------------------------------------- 930 931 void SimpleWinLayout::DropGlyph( int nStart ) 932 { 933 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH; 934 } 935 936 // ----------------------------------------------------------------------- 937 938 void SimpleWinLayout::Simplify( bool /*bIsBase*/ ) 939 { 940 // return early if no glyph has been dropped 941 int i = mnGlyphCount; 942 while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) ); 943 if( i < 0 ) 944 return; 945 946 // convert the layout to a sparse layout if it is not already 947 if( !mpGlyphs2Chars ) 948 { 949 mpGlyphs2Chars = new int[ mnGlyphCount ]; 950 mpCharWidths = new int[ mnCharCount ]; 951 // assertion: mnGlyphCount == mnCharCount 952 for( int k = 0; k < mnGlyphCount; ++k ) 953 { 954 mpGlyphs2Chars[ k ] = mnMinCharPos + k; 955 mpCharWidths[ k ] = mpGlyphAdvances[ k ]; 956 } 957 } 958 959 // remove dropped glyphs that are rightmost in the layout 960 for( i = mnGlyphCount; --i >= 0; ) 961 { 962 if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH ) 963 break; 964 if( mnWidth ) 965 mnWidth -= mpGlyphAdvances[ i ]; 966 int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos; 967 if( nRelCharPos >= 0 ) 968 mpCharWidths[ nRelCharPos ] = 0; 969 } 970 mnGlyphCount = i + 1; 971 972 // keep original glyph widths around 973 if( !mpGlyphOrigAdvs ) 974 { 975 mpGlyphOrigAdvs = new int[ mnGlyphCount ]; 976 for( int k = 0; k < mnGlyphCount; ++k ) 977 mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ]; 978 } 979 980 // remove dropped glyphs inside the layout 981 int nNewGC = 0; 982 for( i = 0; i < mnGlyphCount; ++i ) 983 { 984 if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH ) 985 { 986 // adjust relative position to last valid glyph 987 int nDroppedWidth = mpGlyphAdvances[ i ]; 988 mpGlyphAdvances[ i ] = 0; 989 if( nNewGC > 0 ) 990 mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth; 991 else 992 mnBaseAdv += nDroppedWidth; 993 994 // zero the virtual char width for the char that has a fallback 995 int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos; 996 if( nRelCharPos >= 0 ) 997 mpCharWidths[ nRelCharPos ] = 0; 998 } 999 else 1000 { 1001 if( nNewGC != i ) 1002 { 1003 // rearrange the glyph array to get rid of the dropped glyph 1004 mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ]; 1005 mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ]; 1006 mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ]; 1007 mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ]; 1008 } 1009 ++nNewGC; 1010 } 1011 } 1012 1013 mnGlyphCount = nNewGC; 1014 if( mnGlyphCount <= 0 ) 1015 mnWidth = mnBaseAdv = 0; 1016 } 1017 1018 // ======================================================================= 1019 1020 #ifdef USE_UNISCRIBE 1021 1022 struct VisualItem 1023 { 1024 public: 1025 SCRIPT_ITEM* mpScriptItem; 1026 int mnMinGlyphPos; 1027 int mnEndGlyphPos; 1028 int mnMinCharPos; 1029 int mnEndCharPos; 1030 //long mnPixelWidth; 1031 int mnXOffset; 1032 ABC maABCWidths; 1033 bool mbHasKashidas; 1034 1035 public: 1036 bool IsEmpty() const { return (mnEndGlyphPos <= 0); } 1037 bool IsRTL() const { return mpScriptItem->a.fRTL; } 1038 bool HasKashidas() const { return mbHasKashidas; } 1039 }; 1040 1041 // ----------------------------------------------------------------------- 1042 1043 class UniscribeLayout : public WinLayout 1044 { 1045 public: 1046 UniscribeLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& ); 1047 1048 virtual bool LayoutText( ImplLayoutArgs& ); 1049 virtual void AdjustLayout( ImplLayoutArgs& ); 1050 virtual void DrawText( SalGraphics& ) const; 1051 virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, 1052 sal_Int32* pGlyphAdvances, int* pCharPosAry ) const; 1053 1054 virtual long FillDXArray( long* pDXArray ) const; 1055 virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; 1056 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; 1057 virtual bool IsKashidaPosValid ( int nCharPos ) const; 1058 1059 // for glyph+font+script fallback 1060 virtual void MoveGlyph( int nStart, long nNewXPos ); 1061 virtual void DropGlyph( int nStart ); 1062 virtual void Simplify( bool bIsBase ); 1063 virtual void DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; } 1064 1065 protected: 1066 virtual ~UniscribeLayout(); 1067 1068 void Justify( long nNewWidth ); 1069 void ApplyDXArray( const ImplLayoutArgs& ); 1070 1071 bool GetItemSubrange( const VisualItem&, 1072 int& rMinIndex, int& rEndIndex ) const; 1073 1074 private: 1075 // item specific info 1076 SCRIPT_ITEM* mpScriptItems; // in logical order 1077 VisualItem* mpVisualItems; // in visual order 1078 int mnItemCount; // number of visual items 1079 1080 // string specific info 1081 // everything is in logical order 1082 int mnCharCapacity; 1083 WORD* mpLogClusters; // map from absolute_char_pos to relative_glyph_pos 1084 int* mpCharWidths; // map from absolute_char_pos to char_width 1085 int mnSubStringMin; // char_pos of first char in context 1086 1087 // glyph specific info 1088 // everything is in visual order 1089 int mnGlyphCount; 1090 int mnGlyphCapacity; 1091 int* mpGlyphAdvances; // glyph advance width before justification 1092 int* mpJustifications; // glyph advance width after justification 1093 WORD* mpOutGlyphs; // glyphids in visual order 1094 GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout 1095 SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes 1096 mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos 1097 1098 // kashida stuff 1099 void InitKashidaHandling(); 1100 void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos ); 1101 bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos ); 1102 1103 int mnMinKashidaWidth; 1104 int mnMinKashidaGlyph; 1105 bool mbDisableGlyphInjection; 1106 }; 1107 1108 // ----------------------------------------------------------------------- 1109 // dynamic loading of usp library 1110 1111 static oslModule aUspModule = NULL; 1112 static bool bUspEnabled = true; 1113 1114 static HRESULT ((WINAPI *pScriptIsComplex)( const WCHAR*, int, DWORD )); 1115 static HRESULT ((WINAPI *pScriptItemize)( const WCHAR*, int, int, 1116 const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int* )); 1117 static HRESULT ((WINAPI *pScriptShape)( HDC, SCRIPT_CACHE*, const WCHAR*, 1118 int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int* )); 1119 static HRESULT ((WINAPI *pScriptPlace)( HDC, SCRIPT_CACHE*, const WORD*, int, 1120 const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC* )); 1121 static HRESULT ((WINAPI *pScriptGetLogicalWidths)( const SCRIPT_ANALYSIS*, 1122 int, int, const int*, const WORD*, const SCRIPT_VISATTR*, int* )); 1123 static HRESULT ((WINAPI *pScriptApplyLogicalWidth)( const int*, int, int, const WORD*, 1124 const SCRIPT_VISATTR*, const int*, const SCRIPT_ANALYSIS*, ABC*, int* )); 1125 static HRESULT ((WINAPI *pScriptJustify)( const SCRIPT_VISATTR*, 1126 const int*, int, int, int, int* )); 1127 static HRESULT ((WINAPI *pScriptTextOut)( const HDC, SCRIPT_CACHE*, 1128 int, int, UINT, const RECT*, const SCRIPT_ANALYSIS*, const WCHAR*, 1129 int, const WORD*, int, const int*, const int*, const GOFFSET* )); 1130 static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* )); 1131 static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* )); 1132 1133 static bool bManualCellAlign = true; 1134 1135 // ----------------------------------------------------------------------- 1136 1137 static bool InitUSP() 1138 { 1139 OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) ); 1140 aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT ); 1141 if( !aUspModule ) 1142 return (bUspEnabled = false); 1143 1144 pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD)) 1145 osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" ); 1146 bUspEnabled &= (NULL != pScriptIsComplex); 1147 1148 pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int, 1149 const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*)) 1150 osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" ); 1151 bUspEnabled &= (NULL != pScriptItemize); 1152 1153 pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*, 1154 int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*)) 1155 osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" ); 1156 bUspEnabled &= (NULL != pScriptShape); 1157 1158 pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int, 1159 const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*)) 1160 osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" ); 1161 bUspEnabled &= (NULL != pScriptPlace); 1162 1163 pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*, 1164 int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*)) 1165 osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" ); 1166 bUspEnabled &= (NULL != pScriptGetLogicalWidths); 1167 1168 pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*, 1169 const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*)) 1170 osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" ); 1171 bUspEnabled &= (NULL != pScriptApplyLogicalWidth); 1172 1173 pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*, 1174 int,int,int,int*)) 1175 osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" ); 1176 bUspEnabled &= (NULL != pScriptJustify); 1177 1178 pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*)) 1179 osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" ); 1180 bUspEnabled &= (NULL != pScriptGetFontProperties); 1181 1182 pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*, 1183 int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*, 1184 int,const WORD*,int,const int*,const int*,const GOFFSET*)) 1185 osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" ); 1186 bUspEnabled &= (NULL != pScriptTextOut); 1187 1188 pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*)) 1189 osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" ); 1190 bUspEnabled &= (NULL != pScriptFreeCache); 1191 1192 if( !bUspEnabled ) 1193 { 1194 osl_unloadModule( aUspModule ); 1195 aUspModule = NULL; 1196 } 1197 1198 // get the DLL version info 1199 int nUspVersion = 0; 1200 // TODO: there must be a simpler way to get the friggin version info from OSL? 1201 rtl_uString* pModuleURL = NULL; 1202 osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL ); 1203 rtl_uString* pModuleFileName = NULL; 1204 if( pModuleURL ) 1205 osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName ); 1206 const sal_Unicode* pModuleFileCStr = NULL; 1207 if( pModuleFileName ) 1208 pModuleFileCStr = rtl_uString_getStr( pModuleFileName ); 1209 if( pModuleFileCStr ) 1210 { 1211 DWORD nHandle; 1212 DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle ); 1213 char* pBuffer = (char*)alloca( nBufSize ); 1214 BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer ); 1215 VS_FIXEDFILEINFO* pFixedFileInfo = NULL; 1216 UINT nFixedFileSize = 0; 1217 if( bRC ) 1218 ::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize ); 1219 if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD ) 1220 nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000 1221 + LOWORD(pFixedFileInfo->dwProductVersionMS); 1222 } 1223 1224 // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells 1225 if( nUspVersion >= 10600 ) 1226 bManualCellAlign = false; 1227 1228 return bUspEnabled; 1229 } 1230 1231 // ----------------------------------------------------------------------- 1232 1233 UniscribeLayout::UniscribeLayout( HDC hDC, 1234 const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry ) 1235 : WinLayout( hDC, rWinFontData, rWinFontEntry ), 1236 mnItemCount( 0 ), 1237 mpScriptItems( NULL ), 1238 mpVisualItems( NULL ), 1239 mpLogClusters( NULL ), 1240 mpCharWidths( NULL ), 1241 mnCharCapacity( 0 ), 1242 mnSubStringMin( 0 ), 1243 mnGlyphCapacity( 0 ), 1244 mnGlyphCount( 0 ), 1245 mpOutGlyphs( NULL ), 1246 mpGlyphAdvances( NULL ), 1247 mpJustifications( NULL ), 1248 mpGlyphOffsets( NULL ), 1249 mpVisualAttrs( NULL ), 1250 mpGlyphs2Chars( NULL ), 1251 mnMinKashidaGlyph( 0 ), 1252 mbDisableGlyphInjection( false ) 1253 {} 1254 1255 // ----------------------------------------------------------------------- 1256 1257 UniscribeLayout::~UniscribeLayout() 1258 { 1259 delete[] mpScriptItems; 1260 delete[] mpVisualItems; 1261 delete[] mpLogClusters; 1262 delete[] mpCharWidths; 1263 delete[] mpOutGlyphs; 1264 delete[] mpGlyphAdvances; 1265 delete[] mpJustifications; 1266 delete[] mpGlyphOffsets; 1267 delete[] mpVisualAttrs; 1268 delete[] mpGlyphs2Chars; 1269 } 1270 1271 // ----------------------------------------------------------------------- 1272 1273 bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs ) 1274 { 1275 // for a base layout only the context glyphs have to be dropped 1276 // => when the whole string is involved there is no extra context 1277 typedef std::vector<int> TIntVector; 1278 TIntVector aDropChars; 1279 if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK ) 1280 { 1281 // calculate superfluous context char positions 1282 aDropChars.push_back( 0 ); 1283 aDropChars.push_back( rArgs.mnLength ); 1284 int nMin, nEnd; 1285 bool bRTL; 1286 for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); ) 1287 { 1288 aDropChars.push_back( nMin ); 1289 aDropChars.push_back( nEnd ); 1290 } 1291 // prepare aDropChars for binary search which will allow to 1292 // not bother with visual items that will be dropped anyway 1293 std::sort( aDropChars.begin(), aDropChars.end() ); 1294 } 1295 1296 // prepare layout 1297 // TODO: fix case when recyclying old UniscribeLayout object 1298 mnMinCharPos = rArgs.mnMinCharPos; 1299 mnEndCharPos = rArgs.mnEndCharPos; 1300 1301 // determine script items from string 1302 1303 // prepare itemization 1304 // TODO: try to avoid itemization since it costs a lot of performance 1305 SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0}; 1306 aScriptState.uBidiLevel = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)); 1307 aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG)); 1308 aScriptState.fDigitSubstitute = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS)); 1309 aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel; 1310 DWORD nLangId = 0; // TODO: get language from font 1311 SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0}; 1312 aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection; 1313 aScriptControl.fContextDigits = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS)); 1314 // determine relevant substring and work only on it 1315 // when Bidi status is unknown we need to look at the whole string though 1316 mnSubStringMin = 0; 1317 int nSubStringEnd = rArgs.mnLength; 1318 if( aScriptState.fOverrideDirection ) 1319 { 1320 // TODO: limit substring to portion limits 1321 mnSubStringMin = rArgs.mnMinCharPos - 8; 1322 if( mnSubStringMin < 0 ) 1323 mnSubStringMin = 0; 1324 nSubStringEnd = rArgs.mnEndCharPos + 8; 1325 if( nSubStringEnd > rArgs.mnLength ) 1326 nSubStringEnd = rArgs.mnLength; 1327 1328 } 1329 1330 // now itemize the substring with its context 1331 for( int nItemCapacity = 16;; nItemCapacity *= 8 ) 1332 { 1333 mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ]; 1334 HRESULT nRC = (*pScriptItemize)( 1335 reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin, 1336 nItemCapacity - 1, &aScriptControl, &aScriptState, 1337 mpScriptItems, &mnItemCount ); 1338 if( !nRC ) // break loop when everything is correctly itemized 1339 break; 1340 1341 // prepare bigger buffers for another itemization round 1342 delete[] mpScriptItems; 1343 mpScriptItems = NULL; 1344 if( nRC != E_OUTOFMEMORY ) 1345 return false; 1346 if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 ) 1347 return false; 1348 } 1349 1350 // calculate the order of visual items 1351 int nItem, i; 1352 1353 // adjust char positions by substring offset 1354 for( nItem = 0; nItem <= mnItemCount; ++nItem ) 1355 mpScriptItems[ nItem ].iCharPos += mnSubStringMin; 1356 // default visual item ordering 1357 mpVisualItems = new VisualItem[ mnItemCount ]; 1358 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1359 { 1360 // initialize char specific item info 1361 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 1362 SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ]; 1363 rVisualItem.mpScriptItem = pScriptItem; 1364 rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos; 1365 rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos; 1366 } 1367 1368 // reorder visual item order if needed 1369 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG ) 1370 { 1371 // force RTL item ordering if requested 1372 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL ) 1373 { 1374 VisualItem* pVI0 = &mpVisualItems[ 0 ]; 1375 VisualItem* pVI1 = &mpVisualItems[ mnItemCount ]; 1376 while( pVI0 < --pVI1 ) 1377 { 1378 VisualItem aVtmp = *pVI0; 1379 *(pVI0++) = *pVI1; 1380 *pVI1 = aVtmp; 1381 } 1382 } 1383 } 1384 else if( mnItemCount > 1 ) 1385 { 1386 // apply bidi algorithm's rule L2 on item level 1387 // TODO: use faster L2 algorithm 1388 int nMaxBidiLevel = 0; 1389 VisualItem* pVI = &mpVisualItems[0]; 1390 VisualItem* const pVIend = pVI + mnItemCount; 1391 for(; pVI < pVIend; ++pVI ) 1392 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel ) 1393 nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel; 1394 1395 while( --nMaxBidiLevel >= 0 ) 1396 { 1397 for( pVI = &mpVisualItems[0]; pVI < pVIend; ) 1398 { 1399 // find item range that needs reordering 1400 for(; pVI < pVIend; ++pVI ) 1401 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel ) 1402 break; 1403 VisualItem* pVImin = pVI++; 1404 for(; pVI < pVIend; ++pVI ) 1405 if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel ) 1406 break; 1407 VisualItem* pVImax = pVI++; 1408 1409 // reverse order of items in this range 1410 while( pVImin < --pVImax ) 1411 { 1412 VisualItem aVtmp = *pVImin; 1413 *(pVImin++) = *pVImax; 1414 *pVImax = aVtmp; 1415 } 1416 } 1417 } 1418 } 1419 1420 // allocate arrays 1421 // TODO: when reusing object reuse old allocations or delete them 1422 // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd) 1423 mnCharCapacity = nSubStringEnd; 1424 mpLogClusters = new WORD[ mnCharCapacity ]; 1425 mpCharWidths = new int[ mnCharCapacity ]; 1426 1427 mnGlyphCount = 0; 1428 mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption 1429 mpGlyphAdvances = new int[ mnGlyphCapacity ]; 1430 mpOutGlyphs = new WORD[ mnGlyphCapacity ]; 1431 mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ]; 1432 mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ]; 1433 1434 long nXOffset = 0; 1435 for( int j = mnSubStringMin; j < nSubStringEnd; ++j ) 1436 mpCharWidths[j] = 0; 1437 1438 // layout script items 1439 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 1440 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1441 { 1442 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 1443 1444 // initialize glyph specific item info 1445 rVisualItem.mnMinGlyphPos = mnGlyphCount; 1446 rVisualItem.mnEndGlyphPos = 0; 1447 rVisualItem.mnXOffset = nXOffset; 1448 //rVisualItem.mnPixelWidth = 0; 1449 1450 // shortcut ignorable items 1451 if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos) 1452 || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) ) 1453 { 1454 for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i ) 1455 mpLogClusters[i] = sal::static_int_cast<WORD>(~0U); 1456 continue; 1457 } 1458 1459 // override bidi analysis if requested 1460 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG ) 1461 { 1462 // FIXME: is this intended ? 1463 rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1); 1464 rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel; 1465 rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection; 1466 } 1467 1468 // convert the unicodes to glyphs 1469 int nGlyphCount = 0; 1470 int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos; 1471 HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache, 1472 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos), 1473 nCharCount, 1474 mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF 1475 &rVisualItem.mpScriptItem->a, 1476 mpOutGlyphs + rVisualItem.mnMinGlyphPos, 1477 mpLogClusters + rVisualItem.mnMinCharPos, 1478 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1479 &nGlyphCount ); 1480 1481 // find and handle problems in the unicode to glyph conversion 1482 if( nRC == USP_E_SCRIPT_NOT_IN_FONT ) 1483 { 1484 // the whole visual item needs a fallback, but make sure that the next 1485 // fallback request is limited to the characters in the original request 1486 // => this is handled in ImplLayoutArgs::PrepareFallback() 1487 rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos, 1488 rVisualItem.IsRTL() ); 1489 1490 // don't bother to do a default layout in a fallback level 1491 if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) ) 1492 continue; 1493 1494 // the primitive layout engine is good enough for the default layout 1495 rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED; 1496 nRC = (*pScriptShape)( mhDC, &rScriptCache, 1497 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos), 1498 nCharCount, 1499 mnGlyphCapacity - rVisualItem.mnMinGlyphPos, 1500 &rVisualItem.mpScriptItem->a, 1501 mpOutGlyphs + rVisualItem.mnMinGlyphPos, 1502 mpLogClusters + rVisualItem.mnMinCharPos, 1503 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1504 &nGlyphCount ); 1505 1506 if( nRC != 0 ) 1507 continue; 1508 1509 #if 0 // keep the glyphs for now because they are better than nothing 1510 // mark as NotDef glyphs 1511 for( i = 0; i < nGlyphCount; ++i ) 1512 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0; 1513 #endif 1514 } 1515 else if( nRC != 0 ) 1516 // something undefined happened => give up for this visual item 1517 continue; 1518 else // if( nRC == 0 ) 1519 { 1520 // check if there are any NotDef glyphs 1521 for( i = 0; i < nGlyphCount; ++i ) 1522 if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] ) 1523 break; 1524 if( i < nGlyphCount ) 1525 { 1526 // clip charpos limits to the layout string without context 1527 int nMinCharPos = rVisualItem.mnMinCharPos; 1528 if( nMinCharPos < rArgs.mnMinCharPos ) 1529 nMinCharPos = rArgs.mnMinCharPos; 1530 int nEndCharPos = rVisualItem.mnEndCharPos; 1531 if( nEndCharPos > rArgs.mnEndCharPos ) 1532 nEndCharPos = rArgs.mnEndCharPos; 1533 // request fallback for individual NotDef glyphs 1534 do 1535 { 1536 // ignore non-NotDef glyphs 1537 if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] ) 1538 continue; 1539 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH; 1540 // request fallback for the whole cell that resulted in a NotDef glyph 1541 // TODO: optimize algorithm 1542 const bool bRTL = rVisualItem.IsRTL(); 1543 if( !bRTL ) 1544 { 1545 // request fallback for the left-to-right cell 1546 for( int c = nMinCharPos; c < nEndCharPos; ++c ) 1547 { 1548 if( mpLogClusters[ c ] == i ) 1549 { 1550 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER 1551 if( rArgs.mpStr[ c ] == 0x2060 ) 1552 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1; 1553 else 1554 // <-- 1555 rArgs.NeedFallback( c, false ); 1556 } 1557 } 1558 } 1559 else 1560 { 1561 // request fallback for the right to left cell 1562 for( int c = nEndCharPos; --c >= nMinCharPos; ) 1563 { 1564 if( mpLogClusters[ c ] == i ) 1565 { 1566 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER 1567 if( rArgs.mpStr[ c ] == 0x2060 ) 1568 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1; 1569 else 1570 // <-- 1571 rArgs.NeedFallback( c, true ); 1572 } 1573 } 1574 } 1575 } while( ++i < nGlyphCount ); 1576 } 1577 } 1578 1579 // now place the glyphs 1580 nRC = (*pScriptPlace)( mhDC, &rScriptCache, 1581 mpOutGlyphs + rVisualItem.mnMinGlyphPos, 1582 nGlyphCount, 1583 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1584 &rVisualItem.mpScriptItem->a, 1585 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 1586 mpGlyphOffsets + rVisualItem.mnMinGlyphPos, 1587 &rVisualItem.maABCWidths ); 1588 1589 if( nRC != 0 ) 1590 continue; 1591 1592 // calculate the logical char widths from the glyph layout 1593 nRC = (*pScriptGetLogicalWidths)( 1594 &rVisualItem.mpScriptItem->a, 1595 nCharCount, nGlyphCount, 1596 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 1597 mpLogClusters + rVisualItem.mnMinCharPos, 1598 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1599 mpCharWidths + rVisualItem.mnMinCharPos ); 1600 1601 // update the glyph counters 1602 mnGlyphCount += nGlyphCount; 1603 rVisualItem.mnEndGlyphPos = mnGlyphCount; 1604 1605 // update nXOffset 1606 int nEndGlyphPos; 1607 if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) ) 1608 for(; i < nEndGlyphPos; ++i ) 1609 nXOffset += mpGlyphAdvances[ i ]; 1610 1611 // TODO: shrink glyphpos limits to match charpos/fallback limits 1612 //pVI->mnMinGlyphPos = nMinGlyphPos; 1613 //pVI->mnEndGlyphPos = nEndGlyphPos; 1614 1615 // drop the superfluous context glyphs 1616 TIntVector::const_iterator it = aDropChars.begin(); 1617 while( it != aDropChars.end() ) 1618 { 1619 // find matching "drop range" 1620 int nMinDropPos = *(it++); // begin of drop range 1621 if( nMinDropPos >= rVisualItem.mnEndCharPos ) 1622 break; 1623 int nEndDropPos = *(it++); // end of drop range 1624 if( nEndDropPos <= rVisualItem.mnMinCharPos ) 1625 continue; 1626 // clip "drop range" to visual item's char range 1627 if( nMinDropPos <= rVisualItem.mnMinCharPos ) 1628 { 1629 nMinDropPos = rVisualItem.mnMinCharPos; 1630 // drop the whole visual item if possible 1631 if( nEndDropPos >= rVisualItem.mnEndCharPos ) 1632 { 1633 rVisualItem.mnEndGlyphPos = 0; 1634 break; 1635 } 1636 } 1637 if( nEndDropPos > rVisualItem.mnEndCharPos ) 1638 nEndDropPos = rVisualItem.mnEndCharPos; 1639 1640 // drop the glyphs which correspond to the charpos range 1641 // drop the corresponding glyphs in the cluster 1642 for( int c = nMinDropPos; c < nEndDropPos; ++c ) 1643 { 1644 int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos; 1645 // no need to bother when the cluster was already dropped 1646 if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH ) 1647 { 1648 for(;;) 1649 { 1650 mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH; 1651 // until the end of visual item 1652 if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos ) 1653 break; 1654 // until the next cluster start 1655 if( mpVisualAttrs[ nGlyphPos ].fClusterStart ) 1656 break; 1657 } 1658 } 1659 } 1660 } 1661 } 1662 1663 // scale layout metrics if needed 1664 // TODO: does it make the code more simple if the metric scaling 1665 // is moved to the methods that need metric scaling (e.g. FillDXArray())? 1666 if( mfFontScale != 1.0 ) 1667 { 1668 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale); 1669 1670 for( i = 0; i < mnItemCount; ++i ) 1671 mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale); 1672 1673 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale); 1674 for( i = 0; i < mnGlyphCount; ++i ) 1675 { 1676 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale); 1677 mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale); 1678 mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale); 1679 // mpJustifications are still NULL 1680 } 1681 1682 for( i = mnSubStringMin; i < nSubStringEnd; ++i ) 1683 mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale); 1684 } 1685 1686 return true; 1687 } 1688 1689 // ----------------------------------------------------------------------- 1690 1691 // calculate the range of relevant glyphs for this visual item 1692 bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem, 1693 int& rMinGlyphPos, int& rEndGlyphPos ) const 1694 { 1695 // return early when nothing of interest in this item 1696 if( rVisualItem.IsEmpty() 1697 || (rVisualItem.mnEndCharPos <= mnMinCharPos) 1698 || (mnEndCharPos <= rVisualItem.mnMinCharPos) ) 1699 return false; 1700 1701 // default: subrange is complete range 1702 rMinGlyphPos = rVisualItem.mnMinGlyphPos; 1703 rEndGlyphPos = rVisualItem.mnEndGlyphPos; 1704 1705 // return early when the whole item is of interest 1706 if( (mnMinCharPos <= rVisualItem.mnMinCharPos) 1707 && (rVisualItem.mnEndCharPos <= mnEndCharPos ) ) 1708 return true; 1709 1710 // get glyph range from char range by looking at cluster boundries 1711 // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes 1712 rMinGlyphPos = rVisualItem.mnEndGlyphPos; 1713 int nMaxGlyphPos = 0; 1714 1715 int i = mnMinCharPos; 1716 if( i < rVisualItem.mnMinCharPos ) 1717 i = rVisualItem.mnMinCharPos; 1718 int nCharPosLimit = rVisualItem.mnEndCharPos; 1719 if( nCharPosLimit > mnEndCharPos ) 1720 nCharPosLimit = mnEndCharPos; 1721 for(; i < nCharPosLimit; ++i ) 1722 { 1723 int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos; 1724 if( rMinGlyphPos > n ) 1725 rMinGlyphPos = n; 1726 if( nMaxGlyphPos < n ) 1727 nMaxGlyphPos = n; 1728 } 1729 if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos) 1730 nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1; 1731 1732 // extend the glyph range to account for all glyphs in referenced clusters 1733 if( !rVisualItem.IsRTL() ) // LTR-item 1734 { 1735 // extend to rightmost glyph of rightmost referenced cluster 1736 for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i ) 1737 if( mpVisualAttrs[i].fClusterStart ) 1738 break; 1739 } 1740 else // RTL-item 1741 { 1742 // extend to leftmost glyph of leftmost referenced cluster 1743 for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i ) 1744 if( mpVisualAttrs[i].fClusterStart ) 1745 break; 1746 } 1747 rEndGlyphPos = nMaxGlyphPos + 1; 1748 1749 return true; 1750 } 1751 1752 // ----------------------------------------------------------------------- 1753 1754 int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, 1755 int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const 1756 { 1757 // HACK to allow fake-glyph insertion (e.g. for kashidas) 1758 // TODO: use iterator idiom instead of GetNextGlyphs(...) 1759 // TODO: else make sure that the limit for glyph injection is sufficient (currently 256) 1760 int nSubIter = nStartx8 & 0xff; 1761 int nStart = nStartx8 >> 8; 1762 1763 // check the glyph iterator 1764 if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs 1765 return 0; 1766 1767 // find the visual item for the nStart glyph position 1768 int nItem = 0; 1769 const VisualItem* pVI = mpVisualItems; 1770 if( nStart <= 0 ) // nStart<=0 requests the first visible glyph 1771 { 1772 // find first visible item 1773 for(; nItem < mnItemCount; ++nItem, ++pVI ) 1774 if( !pVI->IsEmpty() ) 1775 break; 1776 // it is possible that there are glyphs but no valid visual item 1777 // TODO: get rid of these visual items more early 1778 if( nItem < mnItemCount ) 1779 nStart = pVI->mnMinGlyphPos; 1780 } 1781 else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1 1782 { 1783 --nStart; 1784 1785 // find matching item 1786 for(; nItem < mnItemCount; ++nItem, ++pVI ) 1787 if( (nStart >= pVI->mnMinGlyphPos) 1788 && (nStart < pVI->mnEndGlyphPos) ) 1789 break; 1790 } 1791 1792 // after the last visual item there are no more glyphs 1793 if( (nItem >= mnItemCount) || (nStart < 0) ) 1794 { 1795 nStartx8 = (mnGlyphCount + 1) << 8; 1796 return 0; 1797 } 1798 1799 // calculate the first glyph in the next visual item 1800 int nNextItemStart = mnGlyphCount; 1801 while( ++nItem < mnItemCount ) 1802 { 1803 if( mpVisualItems[nItem].IsEmpty() ) 1804 continue; 1805 nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos; 1806 break; 1807 } 1808 1809 // get the range of relevant glyphs in this visual item 1810 int nMinGlyphPos, nEndGlyphPos; 1811 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); 1812 DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" ); 1813 if( !bRC ) 1814 { 1815 nStartx8 = (mnGlyphCount + 1) << 8; 1816 return 0; 1817 } 1818 1819 // make sure nStart is inside the range of relevant glyphs 1820 if( nStart < nMinGlyphPos ) 1821 nStart = nMinGlyphPos; 1822 1823 // calculate the start glyph xoffset relative to layout's base position, 1824 // advance to next visual glyph position by using adjusted glyph widths 1825 // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache 1826 long nXOffset = pVI->mnXOffset; 1827 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 1828 for( int i = nMinGlyphPos; i < nStart; ++i ) 1829 nXOffset += pGlyphWidths[ i ]; 1830 1831 // adjust the nXOffset relative to glyph cluster start 1832 int c = mnMinCharPos; 1833 if( !pVI->IsRTL() ) // LTR-case 1834 { 1835 // LTR case: subtract the remainder of the cell from xoffset 1836 int nTmpIndex = mpLogClusters[c]; 1837 while( (--c >= pVI->mnMinCharPos) 1838 && (nTmpIndex == mpLogClusters[c]) ) 1839 nXOffset -= mpCharWidths[c]; 1840 } 1841 else // RTL-case 1842 { 1843 // RTL case: add the remainder of the cell from xoffset 1844 int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ]; 1845 while( (--c >= pVI->mnMinCharPos) 1846 && (nTmpIndex == mpLogClusters[c]) ) 1847 nXOffset += mpCharWidths[c]; 1848 1849 // adjust the xoffset if justified glyphs are not positioned at their justified positions yet 1850 if( mpJustifications && !bManualCellAlign ) 1851 nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ]; 1852 } 1853 1854 // create mpGlyphs2Chars[] if it is needed later 1855 if( pCharPosAry && !mpGlyphs2Chars ) 1856 { 1857 // create and reset the new array 1858 mpGlyphs2Chars = new int[ mnGlyphCapacity ]; 1859 for( int i = 0; i < mnGlyphCount; ++i ) 1860 mpGlyphs2Chars[i] = -1; 1861 // calculate the char->glyph mapping 1862 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1863 { 1864 // ignore invisible visual items 1865 const VisualItem& rVI = mpVisualItems[ nItem ]; 1866 if( rVI.IsEmpty() ) 1867 continue; 1868 // calculate the mapping by using mpLogClusters[] 1869 // mpGlyphs2Chars[] should obey the logical order 1870 // => reversing the loop does this by overwriting higher logicals 1871 for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; ) 1872 { 1873 int i = mpLogClusters[c] + rVI.mnMinGlyphPos; 1874 mpGlyphs2Chars[i] = c; 1875 } 1876 } 1877 } 1878 1879 // calculate the absolute position of the first result glyph in pixel units 1880 const GOFFSET aGOffset = mpGlyphOffsets[ nStart ]; 1881 Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv ); 1882 rPos = GetDrawPosition( aRelativePos ); 1883 1884 // fill the result arrays 1885 int nCount = 0; 1886 while( nCount < nLen ) 1887 { 1888 // prepare return values 1889 sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ]; 1890 int nGlyphWidth = pGlyphWidths[ nStart ]; 1891 int nCharPos = -1; // no need to determine charpos 1892 if( mpGlyphs2Chars ) // unless explicitly requested+provided 1893 nCharPos = mpGlyphs2Chars[ nStart ]; 1894 1895 // inject kashida glyphs if needed 1896 if( !mbDisableGlyphInjection 1897 && mpJustifications 1898 && mnMinKashidaWidth 1899 && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL ) 1900 { 1901 // prepare draw position adjustment 1902 int nExtraOfs = (nSubIter++) * mnMinKashidaWidth; 1903 // calculate space available for the injected glyphs 1904 nGlyphWidth = mpGlyphAdvances[ nStart ]; 1905 const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth; 1906 const int nToFillWidth = nExtraWidth - nExtraOfs; 1907 if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room 1908 || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others 1909 { 1910 // handle if there is not sufficient room for a full glyph 1911 if( nToFillWidth < mnMinKashidaWidth ) 1912 { 1913 // overlap it with the previously injected glyph if possible 1914 int nOverlap = mnMinKashidaWidth - nToFillWidth; 1915 // else overlap it with both neighboring glyphs 1916 if( nSubIter <= 1 ) 1917 nOverlap /= 2; 1918 nExtraOfs -= nOverlap; 1919 } 1920 nGlyphWidth = mnMinKashidaWidth; 1921 aGlyphId = mnMinKashidaGlyph; 1922 nCharPos = -1; 1923 } 1924 else 1925 { 1926 nExtraOfs += nToFillWidth; // at right of cell 1927 nSubIter = 0; // done with glyph injection 1928 } 1929 if( !bManualCellAlign ) 1930 nExtraOfs -= nExtraWidth; // adjust for right-aligned cells 1931 1932 // adjust the draw position for the injected-glyphs case 1933 if( nExtraOfs ) 1934 { 1935 aRelativePos.X() += nExtraOfs; 1936 rPos = GetDrawPosition( aRelativePos ); 1937 } 1938 } 1939 1940 // update return values 1941 *(pGlyphs++) = aGlyphId; 1942 if( pGlyphAdvances ) 1943 *(pGlyphAdvances++) = nGlyphWidth; 1944 if( pCharPosAry ) 1945 *(pCharPosAry++) = nCharPos; 1946 1947 // increment counter of returned glyphs 1948 ++nCount; 1949 1950 // reduce code complexity by returning early in glyph-injection case 1951 if( nSubIter != 0 ) 1952 break; 1953 1954 // stop after the last visible glyph in this visual item 1955 if( ++nStart >= nEndGlyphPos ) 1956 { 1957 nStart = nNextItemStart; 1958 break; 1959 } 1960 1961 // RTL-justified glyph positioning is not easy 1962 // simplify the code by just returning only one glyph at a time 1963 if( mpJustifications && pVI->IsRTL() ) 1964 break; 1965 1966 // stop when the x-position of the next glyph is unexpected 1967 if( !pGlyphAdvances ) 1968 if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) ) 1969 || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) ) 1970 break; 1971 1972 // stop when the y-position of the next glyph is unexpected 1973 if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) ) 1974 break; 1975 } 1976 1977 ++nStart; 1978 nStartx8 = (nStart << 8) + nSubIter; 1979 return nCount; 1980 } 1981 1982 // ----------------------------------------------------------------------- 1983 1984 void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos ) 1985 { 1986 DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" ); 1987 int nStart = nStartx8 >> 8; 1988 if( nStart > mnGlyphCount ) 1989 return; 1990 1991 VisualItem* pVI = mpVisualItems; 1992 int nMinGlyphPos = 0, nEndGlyphPos; 1993 if( nStart == 0 ) // nStart==0 for first visible glyph 1994 { 1995 for( int i = mnItemCount; --i >= 0; ++pVI ) 1996 if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) ) 1997 break; 1998 nStart = nMinGlyphPos; 1999 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" ); 2000 } 2001 else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1 2002 { 2003 --nStart; 2004 for( int i = mnItemCount; --i >= 0; ++pVI ) 2005 if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) ) 2006 break; 2007 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); 2008 (void)bRC; // avoid var-not-used warning 2009 DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" ); 2010 } 2011 2012 long nDelta = nNewXPos - pVI->mnXOffset; 2013 if( nStart > nMinGlyphPos ) 2014 { 2015 // move the glyph by expanding its left glyph but ignore dropped glyphs 2016 int i, nLastUndropped = nMinGlyphPos - 1; 2017 for( i = nMinGlyphPos; i < nStart; ++i ) 2018 { 2019 if (mpOutGlyphs[i] != DROPPED_OUTGLYPH) 2020 { 2021 nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ]; 2022 nLastUndropped = i; 2023 } 2024 } 2025 if (nLastUndropped >= nMinGlyphPos) 2026 { 2027 mpGlyphAdvances[ nLastUndropped ] += nDelta; 2028 if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta; 2029 } 2030 else 2031 { 2032 pVI->mnXOffset += nDelta; 2033 } 2034 } 2035 else 2036 { 2037 // move the visual item by having an offset 2038 pVI->mnXOffset += nDelta; 2039 } 2040 // move subsequent items - this often isn't necessary because subsequent 2041 // moves will correct subsequent items. However, if there is a contiguous 2042 // range not involving fallback which spans items, this will be needed 2043 while (++pVI - mpVisualItems < mnItemCount) 2044 { 2045 pVI->mnXOffset += nDelta; 2046 } 2047 } 2048 2049 // ----------------------------------------------------------------------- 2050 2051 void UniscribeLayout::DropGlyph( int nStartx8 ) 2052 { 2053 DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" ); 2054 int nStart = nStartx8 >> 8; 2055 DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" ); 2056 2057 if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1 2058 --nStart; 2059 else // nStart<=0 for first visible glyph 2060 { 2061 VisualItem* pVI = mpVisualItems; 2062 for( int i = mnItemCount, nDummy; --i >= 0; ++pVI ) 2063 if( GetItemSubrange( *pVI, nStart, nDummy ) ) 2064 break; 2065 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" ); 2066 int nOffset = 0; 2067 int j = pVI->mnMinGlyphPos; 2068 while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++; 2069 if (j == nStart) 2070 { 2071 pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]); 2072 } 2073 } 2074 2075 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH; 2076 } 2077 2078 // ----------------------------------------------------------------------- 2079 2080 void UniscribeLayout::Simplify( bool /*bIsBase*/ ) 2081 { 2082 static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH; 2083 int i; 2084 // if there are no dropped glyphs don't bother 2085 for( i = 0; i < mnGlyphCount; ++i ) 2086 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2087 break; 2088 if( i >= mnGlyphCount ) 2089 return; 2090 2091 // prepare for sparse layout 2092 // => make sure mpGlyphs2Chars[] exists 2093 if( !mpGlyphs2Chars ) 2094 { 2095 mpGlyphs2Chars = new int[ mnGlyphCapacity ]; 2096 for( i = 0; i < mnGlyphCount; ++i ) 2097 mpGlyphs2Chars[ i ] = -1; 2098 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2099 { 2100 // skip invisible items 2101 VisualItem& rVI = mpVisualItems[ nItem ]; 2102 if( rVI.IsEmpty() ) 2103 continue; 2104 for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; ) 2105 { 2106 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos; 2107 mpGlyphs2Chars[ j ] = i; 2108 } 2109 } 2110 } 2111 2112 // remove the dropped glyphs 2113 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 2114 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2115 { 2116 VisualItem& rVI = mpVisualItems[ nItem ]; 2117 if( rVI.IsEmpty() ) 2118 continue; 2119 2120 // mark replaced character widths 2121 for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i ) 2122 { 2123 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos; 2124 if( mpOutGlyphs[ j ] == cDroppedGlyph ) 2125 mpCharWidths[ i ] = 0; 2126 } 2127 2128 // handle dropped glyphs at start of visual item 2129 int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos; 2130 GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ); 2131 i = nMinGlyphPos; 2132 while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) ) 2133 { 2134 //rVI.mnXOffset += pGlyphWidths[ i ]; 2135 rVI.mnMinGlyphPos = ++i; 2136 } 2137 2138 // when all glyphs in item got dropped mark it as empty 2139 if( i >= nEndGlyphPos ) 2140 { 2141 rVI.mnEndGlyphPos = 0; 2142 continue; 2143 } 2144 // If there are still glyphs in the cluster and mnMinGlyphPos 2145 // has changed then we need to remove the dropped glyphs at start 2146 // to correct logClusters, which is unsigned and relative to the 2147 // item start. 2148 if (rVI.mnMinGlyphPos != nOrigMinGlyphPos) 2149 { 2150 // drop any glyphs in the visual item outside the range 2151 for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++) 2152 mpOutGlyphs[ i ] = cDroppedGlyph; 2153 rVI.mnMinGlyphPos = i = nOrigMinGlyphPos; 2154 } 2155 2156 // handle dropped glyphs in the middle of visual item 2157 for(; i < nEndGlyphPos; ++i ) 2158 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2159 break; 2160 int j = i; 2161 while( ++i < nEndGlyphPos ) 2162 { 2163 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2164 continue; 2165 mpOutGlyphs[ j ] = mpOutGlyphs[ i ]; 2166 mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ]; 2167 mpVisualAttrs[ j ] = mpVisualAttrs[ i ]; 2168 mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ]; 2169 if( mpJustifications ) 2170 mpJustifications[ j ] = mpJustifications[ i ]; 2171 const int k = mpGlyphs2Chars[ i ]; 2172 mpGlyphs2Chars[ j ] = k; 2173 const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos; 2174 if( k < 0) // extra glyphs are already mapped 2175 continue; 2176 mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos); 2177 } 2178 2179 rVI.mnEndGlyphPos = j; 2180 } 2181 } 2182 2183 // ----------------------------------------------------------------------- 2184 2185 void UniscribeLayout::DrawText( SalGraphics& ) const 2186 { 2187 HFONT hOrigFont = DisableFontScaling(); 2188 2189 int nBaseClusterOffset = 0; 2190 int nBaseGlyphPos = -1; 2191 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2192 { 2193 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2194 2195 // skip if there is nothing to display 2196 int nMinGlyphPos, nEndGlyphPos; 2197 if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) 2198 continue; 2199 2200 if( nBaseGlyphPos < 0 ) 2201 { 2202 // adjust draw position relative to cluster start 2203 if( rVisualItem.IsRTL() ) 2204 nBaseGlyphPos = nEndGlyphPos - 1; 2205 else 2206 nBaseGlyphPos = nMinGlyphPos; 2207 2208 const int* pGlyphWidths; 2209 if( mpJustifications ) 2210 pGlyphWidths = mpJustifications; 2211 else 2212 pGlyphWidths = mpGlyphAdvances; 2213 2214 int i = mnMinCharPos; 2215 while( (--i >= rVisualItem.mnMinCharPos) 2216 && (nBaseGlyphPos == mpLogClusters[i]) ) 2217 nBaseClusterOffset += mpCharWidths[i]; 2218 2219 if( !rVisualItem.IsRTL() ) 2220 nBaseClusterOffset = -nBaseClusterOffset; 2221 } 2222 2223 // now draw the matching glyphs in this item 2224 Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 ); 2225 Point aPos = GetDrawPosition( aRelPos ); 2226 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 2227 (*pScriptTextOut)( mhDC, &rScriptCache, 2228 aPos.X(), aPos.Y(), 0, NULL, 2229 &rVisualItem.mpScriptItem->a, NULL, 0, 2230 mpOutGlyphs + nMinGlyphPos, 2231 nEndGlyphPos - nMinGlyphPos, 2232 mpGlyphAdvances + nMinGlyphPos, 2233 mpJustifications ? mpJustifications + nMinGlyphPos : NULL, 2234 mpGlyphOffsets + nMinGlyphPos ); 2235 } 2236 2237 if( hOrigFont ) 2238 DeleteFont( SelectFont( mhDC, hOrigFont ) ); 2239 } 2240 2241 // ----------------------------------------------------------------------- 2242 2243 long UniscribeLayout::FillDXArray( long* pDXArray ) const 2244 { 2245 // calculate width of the complete layout 2246 long nWidth = mnBaseAdv; 2247 for( int nItem = mnItemCount; --nItem >= 0; ) 2248 { 2249 const VisualItem& rVI = mpVisualItems[ nItem ]; 2250 2251 // skip if there is nothing to display 2252 int nMinGlyphPos, nEndGlyphPos; 2253 if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) ) 2254 continue; 2255 2256 // width = xoffset + width of last item 2257 nWidth = rVI.mnXOffset; 2258 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 2259 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2260 nWidth += pGlyphWidths[i]; 2261 break; 2262 } 2263 2264 // copy the virtual char widths into pDXArray[] 2265 if( pDXArray ) 2266 for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 2267 pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ]; 2268 2269 return nWidth; 2270 } 2271 2272 // ----------------------------------------------------------------------- 2273 2274 int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2275 { 2276 long nWidth = 0; 2277 for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 2278 { 2279 nWidth += mpCharWidths[ i ] * nFactor; 2280 2281 // check if the nMaxWidth still fits the current sub-layout 2282 if( nWidth >= nMaxWidth ) 2283 { 2284 // go back to cluster start 2285 // we have to find the visual item first since the mpLogClusters[] 2286 // needed to find the cluster start is relative to to the visual item 2287 int nMinGlyphIndex = 0; 2288 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2289 { 2290 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2291 nMinGlyphIndex = rVisualItem.mnMinGlyphPos; 2292 if( (i >= rVisualItem.mnMinCharPos) 2293 && (i < rVisualItem.mnEndCharPos) ) 2294 break; 2295 } 2296 // now go back to the matching cluster start 2297 do 2298 { 2299 int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex; 2300 if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart ) 2301 return i; 2302 } while( --i >= mnMinCharPos ); 2303 2304 // if the cluster starts before the start of the visual item 2305 // then set the visual breakpoint before this item 2306 return mnMinCharPos; 2307 } 2308 2309 // the visual break also depends on the nCharExtra between the characters 2310 nWidth += nCharExtra; 2311 } 2312 2313 // the whole layout did fit inside the nMaxWidth 2314 return STRING_LEN; 2315 } 2316 2317 // ----------------------------------------------------------------------- 2318 2319 void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const 2320 { 2321 int i; 2322 for( i = 0; i < nMaxIdx; ++i ) 2323 pCaretXArray[ i ] = -1; 2324 long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) ); 2325 for( i = 0; i <= mnGlyphCount; ++i ) 2326 pGlyphPos[ i ] = -1; 2327 2328 long nXPos = 0; 2329 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2330 { 2331 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2332 if( rVisualItem.IsEmpty() ) 2333 continue; 2334 2335 if (mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK) 2336 { 2337 nXPos = rVisualItem.mnXOffset; 2338 } 2339 // get glyph positions 2340 // TODO: handle when rVisualItem's glyph range is only partially used 2341 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2342 { 2343 pGlyphPos[ i ] = nXPos; 2344 nXPos += mpGlyphAdvances[ i ]; 2345 } 2346 // rightmost position of this visualitem 2347 pGlyphPos[ i ] = nXPos; 2348 2349 // convert glyph positions to character positions 2350 i = rVisualItem.mnMinCharPos; 2351 if( i < mnMinCharPos ) 2352 i = mnMinCharPos; 2353 for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i ) 2354 { 2355 int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos; 2356 int nCurrIdx = i * 2; 2357 if( !rVisualItem.IsRTL() ) 2358 { 2359 // normal positions for LTR case 2360 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ]; 2361 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ]; 2362 } 2363 else 2364 { 2365 // reverse positions for RTL case 2366 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ]; 2367 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ]; 2368 } 2369 } 2370 } 2371 2372 if (!(mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK)) 2373 { 2374 nXPos = 0; 2375 // fixup unknown character positions to neighbor 2376 for( i = 0; i < nMaxIdx; ++i ) 2377 { 2378 if( pCaretXArray[ i ] >= 0 ) 2379 nXPos = pCaretXArray[ i ]; 2380 else 2381 pCaretXArray[ i ] = nXPos; 2382 } 2383 } 2384 } 2385 2386 // ----------------------------------------------------------------------- 2387 2388 void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 2389 { 2390 SalLayout::AdjustLayout( rArgs ); 2391 2392 // adjust positions if requested 2393 if( rArgs.mpDXArray ) 2394 ApplyDXArray( rArgs ); 2395 else if( rArgs.mnLayoutWidth ) 2396 Justify( rArgs.mnLayoutWidth ); 2397 } 2398 2399 // ----------------------------------------------------------------------- 2400 2401 void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) 2402 { 2403 const long* pDXArray = rArgs.mpDXArray; 2404 2405 // increase char widths in string range to desired values 2406 bool bModified = false; 2407 int nOldWidth = 0; 2408 DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" ); 2409 int i,j; 2410 for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j ) 2411 { 2412 int nNewCharWidth = (pDXArray[j] - nOldWidth); 2413 // TODO: nNewCharWidth *= mnUnitsPerPixel; 2414 if( mpCharWidths[i] != nNewCharWidth ) 2415 { 2416 mpCharWidths[i] = nNewCharWidth; 2417 bModified = true; 2418 } 2419 nOldWidth = pDXArray[j]; 2420 } 2421 2422 if( !bModified ) 2423 return; 2424 2425 // initialize justifications array 2426 mpJustifications = new int[ mnGlyphCapacity ]; 2427 for( i = 0; i < mnGlyphCount; ++i ) 2428 mpJustifications[ i ] = mpGlyphAdvances[ i ]; 2429 2430 // apply new widths to script items 2431 long nXOffset = 0; 2432 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2433 { 2434 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2435 2436 // set the position of this visual item 2437 rVisualItem.mnXOffset = nXOffset; 2438 2439 // ignore empty visual items 2440 if( rVisualItem.IsEmpty() ) 2441 { 2442 for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++) 2443 nXOffset += mpCharWidths[i]; 2444 continue; 2445 } 2446 // ignore irrelevant visual items 2447 if( (rVisualItem.mnMinCharPos >= mnEndCharPos) 2448 || (rVisualItem.mnEndCharPos <= mnMinCharPos) ) 2449 continue; 2450 2451 // if needed prepare special handling for arabic justification 2452 rVisualItem.mbHasKashidas = false; 2453 if( rVisualItem.IsRTL() ) 2454 { 2455 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2456 if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification 2457 { // excluding SCRIPT_JUSTIFY_NONE 2458 // yes 2459 rVisualItem.mbHasKashidas = true; 2460 // so prepare for kashida handling 2461 InitKashidaHandling(); 2462 break; 2463 } 2464 2465 if( rVisualItem.HasKashidas() ) 2466 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2467 { 2468 // TODO: check if we still need this hack after correction of kashida placing? 2469 // (i87688): apparently yes, we still need it! 2470 if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE ) 2471 // usp decided that justification can't be applied here 2472 // but maybe our Kashida algorithm thinks differently. 2473 // To avoid trouble (gaps within words, last character of 2474 // a word gets a Kashida appended) override this. 2475 2476 // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE 2477 // just because this previous hack (which I haven't understand, sorry) used 2478 // the same value to replace. Don't know if this is really the best 2479 // thing to do, but it seems to fix things 2480 mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA; 2481 } 2482 } 2483 2484 // convert virtual charwidths to glyph justification values 2485 HRESULT nRC = (*pScriptApplyLogicalWidth)( 2486 mpCharWidths + rVisualItem.mnMinCharPos, 2487 rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos, 2488 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, 2489 mpLogClusters + rVisualItem.mnMinCharPos, 2490 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 2491 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 2492 &rVisualItem.mpScriptItem->a, 2493 &rVisualItem.maABCWidths, 2494 mpJustifications + rVisualItem.mnMinGlyphPos ); 2495 2496 if( nRC != 0 ) 2497 { 2498 delete[] mpJustifications; 2499 mpJustifications = NULL; 2500 break; 2501 } 2502 2503 // to prepare for the next visual item 2504 // update nXOffset to the next items position 2505 // before the mpJustifications[] array gets modified 2506 int nMinGlyphPos, nEndGlyphPos; 2507 if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) 2508 { 2509 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2510 nXOffset += mpJustifications[ i ]; 2511 2512 if( rVisualItem.mbHasKashidas ) 2513 KashidaItemFix( nMinGlyphPos, nEndGlyphPos ); 2514 } 2515 2516 // workaround needed for older USP versions: 2517 // right align the justification-adjusted glyphs in their cells for RTL-items 2518 // unless the right alignment is done by inserting kashidas 2519 if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() ) 2520 { 2521 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2522 { 2523 const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i]; 2524 // #i99862# skip diacritics, we mustn't add extra justification to diacritics 2525 int nIdxAdd = i - 1; 2526 while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] ) 2527 --nIdxAdd; 2528 if( nIdxAdd < nMinGlyphPos ) 2529 rVisualItem.mnXOffset += nXOffsetAdjust; 2530 else 2531 mpJustifications[nIdxAdd] += nXOffsetAdjust; 2532 mpJustifications[i] -= nXOffsetAdjust; 2533 } 2534 } 2535 } 2536 } 2537 2538 // ----------------------------------------------------------------------- 2539 2540 void UniscribeLayout::InitKashidaHandling() 2541 { 2542 if( mnMinKashidaGlyph != 0 ) // already initialized 2543 return; 2544 2545 mrWinFontEntry.InitKashidaHandling( mhDC ); 2546 mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth()); 2547 mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph(); 2548 } 2549 2550 // adjust the kashida placement matching to the WriterEngine 2551 void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos ) 2552 { 2553 // workaround needed for all known USP versions: 2554 // ApplyLogicalWidth does not match ScriptJustify behaviour 2555 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2556 { 2557 // check for vowels 2558 if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ]) 2559 && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types 2560 { // including SCRIPT_JUSTIFY_NONE 2561 // vowel, we do it like ScriptJustify does 2562 // the vowel gets the extra width 2563 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2564 mpJustifications [ i ] = mpGlyphAdvances [ i ]; 2565 mpJustifications [ i - 1 ] += nSpaceAdded; 2566 } 2567 } 2568 2569 // redistribute the widths for kashidas 2570 for( int i = nMinGlyphPos; i < nEndGlyphPos; ) 2571 KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i ); 2572 } 2573 2574 bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos ) 2575 { 2576 // doing pixel work within a word. 2577 // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth 2578 2579 // find the next kashida 2580 int nMinPos = *pnCurrentPos; 2581 int nMaxPos = *pnCurrentPos; 2582 for( int i = nMaxPos; i < nEndGlyphPos; ++i ) 2583 { 2584 if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK) 2585 && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) ) 2586 break; 2587 nMaxPos = i; 2588 } 2589 *pnCurrentPos = nMaxPos + 1; 2590 if( nMinPos == nMaxPos ) 2591 return false; 2592 2593 // calculate the available space for an extra kashida 2594 long nMaxAdded = 0; 2595 int nKashPos = -1; 2596 for( int i = nMaxPos; i >= nMinPos; --i ) 2597 { 2598 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2599 if( nSpaceAdded > nMaxAdded ) 2600 { 2601 nKashPos = i; 2602 nMaxAdded = nSpaceAdded; 2603 } 2604 } 2605 2606 // return early if there is no need for an extra kashida 2607 if ( nMaxAdded <= 0 ) 2608 return false; 2609 // return early if there is not enough space for an extra kashida 2610 if( 2*nMaxAdded < mnMinKashidaWidth ) 2611 return false; 2612 2613 // redistribute the extra spacing to the kashida position 2614 for( int i = nMinPos; i <= nMaxPos; ++i ) 2615 { 2616 if( i == nKashPos ) 2617 continue; 2618 // everything else should not have extra spacing 2619 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2620 if( nSpaceAdded > 0 ) 2621 { 2622 mpJustifications[ i ] -= nSpaceAdded; 2623 mpJustifications[ nKashPos ] += nSpaceAdded; 2624 } 2625 } 2626 2627 // check if we fulfill minimal kashida width 2628 long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ]; 2629 if( nSpaceAdded < mnMinKashidaWidth ) 2630 { 2631 // ugly: steal some pixels 2632 long nSteal = 1; 2633 if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos))) 2634 nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos); 2635 for( int i = nMinPos; i <= nMaxPos; ++i ) 2636 { 2637 if( i == nKashPos ) 2638 continue; 2639 nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal ); 2640 if ( nSteal > 0 ) 2641 { 2642 mpJustifications [ i ] -= nSteal; 2643 mpJustifications [ nKashPos ] += nSteal; 2644 nSpaceAdded += nSteal; 2645 } 2646 if( nSpaceAdded >= mnMinKashidaWidth ) 2647 return true; 2648 } 2649 } 2650 2651 // blank padding 2652 long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded; 2653 if( nSpaceMissing > 0 ) 2654 { 2655 // inner glyph: distribute extra space evenly 2656 if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) ) 2657 { 2658 mpJustifications [ nKashPos ] += nSpaceMissing; 2659 long nHalfSpace = nSpaceMissing / 2; 2660 mpJustifications [ nMinPos - 1 ] -= nHalfSpace; 2661 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace; 2662 } 2663 // rightmost: left glyph gets extra space 2664 else if( nMinPos > nMinGlyphPos ) 2665 { 2666 mpJustifications [ nMinPos - 1 ] -= nSpaceMissing; 2667 mpJustifications [ nKashPos ] += nSpaceMissing; 2668 } 2669 // leftmost: right glyph gets extra space 2670 else if( nMaxPos < nEndGlyphPos - 1 ) 2671 { 2672 mpJustifications [ nKashPos ] += nSpaceMissing; 2673 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing; 2674 } 2675 else 2676 return false; 2677 } 2678 2679 return true; 2680 } 2681 2682 // ----------------------------------------------------------------------- 2683 2684 void UniscribeLayout::Justify( long nNewWidth ) 2685 { 2686 long nOldWidth = 0; 2687 int i; 2688 for( i = mnMinCharPos; i < mnEndCharPos; ++i ) 2689 nOldWidth += mpCharWidths[ i ]; 2690 if( nOldWidth <= 0 ) 2691 return; 2692 2693 nNewWidth *= mnUnitsPerPixel; // convert into font units 2694 if( nNewWidth == nOldWidth ) 2695 return; 2696 // prepare to distribute the extra width evenly among the visual items 2697 const double fStretch = (double)nNewWidth / nOldWidth; 2698 2699 // initialize justifications array 2700 mpJustifications = new int[ mnGlyphCapacity ]; 2701 for( i = 0; i < mnGlyphCapacity; ++i ) 2702 mpJustifications[ i ] = mpGlyphAdvances[ i ]; 2703 2704 // justify stretched script items 2705 long nXOffset = 0; 2706 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 2707 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2708 { 2709 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2710 if( rVisualItem.IsEmpty() ) 2711 continue; 2712 2713 if( (rVisualItem.mnMinCharPos < mnEndCharPos) 2714 && (rVisualItem.mnEndCharPos > mnMinCharPos) ) 2715 { 2716 long nItemWidth = 0; 2717 for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i ) 2718 nItemWidth += mpCharWidths[ i ]; 2719 nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5); 2720 2721 HRESULT nRC = (*pScriptJustify) ( 2722 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 2723 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 2724 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, 2725 nItemWidth, 2726 mnMinKashidaWidth, 2727 mpJustifications + rVisualItem.mnMinGlyphPos ); 2728 2729 rVisualItem.mnXOffset = nXOffset; 2730 nXOffset += nItemWidth; 2731 } 2732 } 2733 } 2734 2735 // ----------------------------------------------------------------------- 2736 2737 bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const 2738 { 2739 // we have to find the visual item first since the mpLogClusters[] 2740 // needed to find the cluster start is relative to to the visual item 2741 int nMinGlyphIndex = -1; 2742 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2743 { 2744 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2745 if( (nCharPos >= rVisualItem.mnMinCharPos) 2746 && (nCharPos < rVisualItem.mnEndCharPos) ) 2747 { 2748 nMinGlyphIndex = rVisualItem.mnMinGlyphPos; 2749 break; 2750 } 2751 } 2752 // Invalid char pos or leftmost glyph in visual item 2753 if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] ) 2754 return false; 2755 2756 // This test didn't give the expected results 2757 /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ]) 2758 // two chars, one glyph 2759 return false;*/ 2760 2761 const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex; 2762 if( nGlyphPos <= 0 ) 2763 return true; 2764 // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE 2765 // and not SCRIPT_JUSTIFY_ARABIC_BLANK 2766 // special case: glyph to the left is vowel (no advance width) 2767 if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK 2768 || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE 2769 && mpGlyphAdvances [ nGlyphPos-1 ] )) 2770 return false; 2771 return true; 2772 } 2773 2774 #endif // USE_UNISCRIBE 2775 2776 #ifdef ENABLE_GRAPHITE 2777 2778 class GraphiteLayoutWinImpl : public GraphiteLayout 2779 { 2780 public: 2781 GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont) 2782 throw() 2783 : GraphiteLayout(font), mrFont(rFont) {}; 2784 virtual ~GraphiteLayoutWinImpl() throw() {}; 2785 virtual sal_GlyphId getKashidaGlyph(int & rWidth); 2786 private: 2787 ImplWinFontEntry & mrFont; 2788 }; 2789 2790 sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth) 2791 { 2792 rWidth = mrFont.GetMinKashidaWidth(); 2793 return mrFont.GetMinKashidaGlyph(); 2794 } 2795 2796 // This class uses the SIL Graphite engine to provide complex text layout services to the VCL 2797 // @author tse 2798 // 2799 class GraphiteWinLayout : public WinLayout 2800 { 2801 private: 2802 mutable GraphiteWinFont mpFont; 2803 grutils::GrFeatureParser * mpFeatures; 2804 mutable GraphiteLayoutWinImpl maImpl; 2805 public: 2806 GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw(); 2807 2808 static bool IsGraphiteEnabledFont(HDC hDC) throw(); 2809 2810 // used by upper layers 2811 virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout 2812 virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc. 2813 // virtual void InitFont() const; 2814 virtual void DrawText( SalGraphics& ) const; 2815 2816 // methods using string indexing 2817 virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const; 2818 virtual long FillDXArray( long* pDXArray ) const; 2819 2820 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; 2821 2822 // methods using glyph indexing 2823 virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&, 2824 long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const; 2825 2826 // used by glyph+font+script fallback 2827 virtual void MoveGlyph( int nStart, long nNewXPos ); 2828 virtual void DropGlyph( int nStart ); 2829 virtual void Simplify( bool bIsBase ); 2830 ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; }; 2831 protected: 2832 virtual void ReplaceDC(gr::Segment & segment) const; 2833 virtual void RestoreDC(gr::Segment & segment) const; 2834 }; 2835 2836 bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw() 2837 { 2838 return gr::WinFont::FontHasGraphiteTables(hDC); 2839 } 2840 2841 GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw() 2842 : WinLayout(hDC, rWFD, rWFE), mpFont(hDC), 2843 maImpl(mpFont, rWFE) 2844 { 2845 const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage ); 2846 rtl::OString name = rtl::OUStringToOString( 2847 rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 2848 sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1; 2849 if (nFeat > 0) 2850 { 2851 rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat); 2852 mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr()); 2853 } 2854 else 2855 { 2856 mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr()); 2857 } 2858 maImpl.SetFeatures(mpFeatures); 2859 } 2860 2861 void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const 2862 { 2863 COLORREF color = GetTextColor(mhDC); 2864 dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC); 2865 SetTextColor(mhDC, color); 2866 } 2867 2868 void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const 2869 { 2870 dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC(); 2871 } 2872 2873 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args) 2874 { 2875 if (args.mnMinCharPos >= args.mnEndCharPos) 2876 { 2877 maImpl.clear(); 2878 return true; 2879 } 2880 HFONT hUnRotatedFont; 2881 if (args.mnOrientation) 2882 { 2883 // Graphite gets very confused if the font is rotated 2884 LOGFONTW aLogFont; 2885 ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont); 2886 aLogFont.lfEscapement = 0; 2887 aLogFont.lfOrientation = 0; 2888 hUnRotatedFont = ::CreateFontIndirectW( &aLogFont); 2889 ::SelectFont(mhDC, hUnRotatedFont); 2890 } 2891 WinLayout::AdjustLayout(args); 2892 mpFont.replaceDC(mhDC); 2893 maImpl.SetFontScale(WinLayout::mfFontScale); 2894 //bool succeeded = maImpl.LayoutText(args); 2895 #ifdef GRCACHE 2896 GrSegRecord * pSegRecord = NULL; 2897 gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord); 2898 #else 2899 gr::Segment * pSegment = maImpl.CreateSegment(args); 2900 #endif 2901 bool bSucceeded = false; 2902 if (pSegment) 2903 { 2904 // replace the DC on the font within the segment 2905 ReplaceDC(*pSegment); 2906 // create glyph vectors 2907 #ifdef GRCACHE 2908 bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord); 2909 #else 2910 bSucceeded = maImpl.LayoutGlyphs(args, pSegment); 2911 #endif 2912 // restore original DC 2913 RestoreDC(*pSegment); 2914 #ifdef GRCACHE 2915 if (pSegRecord) pSegRecord->unlock(); 2916 else delete pSegment; 2917 #else 2918 delete pSegment; 2919 #endif 2920 } 2921 mpFont.restoreDC(); 2922 if (args.mnOrientation) 2923 { 2924 // restore the rotated font 2925 ::SelectFont(mhDC, mhFont); 2926 ::DeleteObject(hUnRotatedFont); 2927 } 2928 return bSucceeded; 2929 } 2930 2931 void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs) 2932 { 2933 WinLayout::AdjustLayout(rArgs); 2934 maImpl.DrawBase() = WinLayout::maDrawBase; 2935 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2936 if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray) 2937 { 2938 mrWinFontEntry.InitKashidaHandling(mhDC); 2939 } 2940 maImpl.AdjustLayout(rArgs); 2941 } 2942 2943 void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const 2944 { 2945 HFONT hOrigFont = DisableFontScaling(); 2946 HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).mhDC; 2947 maImpl.DrawBase() = WinLayout::maDrawBase; 2948 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2949 const int MAX_GLYPHS = 2; 2950 sal_GlyphId glyphIntStr[MAX_GLYPHS]; 2951 WORD glyphWStr[MAX_GLYPHS]; 2952 int glyphIndex = 0; 2953 Point aPos(0,0); 2954 int nGlyphs = 0; 2955 do 2956 { 2957 nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex); 2958 if (nGlyphs < 1) 2959 break; 2960 std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr); 2961 ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, 2962 NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL); 2963 } while (nGlyphs); 2964 if( hOrigFont ) 2965 DeleteFont( SelectFont( mhDC, hOrigFont ) ); 2966 } 2967 2968 int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2969 { 2970 mpFont.replaceDC(mhDC); 2971 int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor); 2972 mpFont.restoreDC(); 2973 return nBreak; 2974 } 2975 2976 long GraphiteWinLayout::FillDXArray( long* pDXArray ) const 2977 { 2978 return maImpl.FillDXArray(pDXArray); 2979 } 2980 2981 void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const 2982 { 2983 maImpl.GetCaretPositions(nArraySize, pCaretXArray); 2984 } 2985 2986 int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out, 2987 ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const 2988 { 2989 maImpl.DrawBase() = WinLayout::maDrawBase; 2990 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2991 return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index); 2992 } 2993 2994 void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos ) 2995 { 2996 maImpl.MoveGlyph(glyph_idx, new_x_pos); 2997 } 2998 2999 void GraphiteWinLayout::DropGlyph( int glyph_idx ) 3000 { 3001 maImpl.DropGlyph(glyph_idx); 3002 } 3003 3004 void GraphiteWinLayout::Simplify( bool is_base ) 3005 { 3006 maImpl.Simplify(is_base); 3007 } 3008 #endif // ENABLE_GRAPHITE 3009 // ======================================================================= 3010 3011 SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) 3012 { 3013 DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL"); 3014 3015 WinLayout* pWinLayout = NULL; 3016 3017 const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ]; 3018 ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ]; 3019 3020 #if defined( USE_UNISCRIBE ) 3021 if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) 3022 && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine 3023 { 3024 #ifdef ENABLE_GRAPHITE 3025 if (rFontFace.SupportsGraphite()) 3026 pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance); 3027 else 3028 #endif // ENABLE_GRAPHITE 3029 // script complexity is determined in upper layers 3030 pWinLayout = new UniscribeLayout( mhDC, rFontFace, rFontInstance ); 3031 // NOTE: it must be guaranteed that the WinSalGraphics lives longer than 3032 // the created UniscribeLayout, otherwise the data passed into the 3033 // constructor might become invalid too early 3034 } 3035 else 3036 #endif // USE_UNISCRIBE 3037 { 3038 #ifdef GCP_KERN_HACK 3039 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() ) 3040 { 3041 // TODO: directly cache kerning info in the rFontInstance 3042 // TODO: get rid of kerning methods+data in WinSalGraphics object 3043 GetKernPairs( 0, NULL ); 3044 rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs ); 3045 } 3046 #endif // GCP_KERN_HACK 3047 3048 BYTE eCharSet = ANSI_CHARSET; 3049 if( mpLogFont ) 3050 eCharSet = mpLogFont->lfCharSet; 3051 #ifdef ENABLE_GRAPHITE 3052 if (rFontFace.SupportsGraphite()) 3053 pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance); 3054 else 3055 #endif // ENABLE_GRAPHITE 3056 pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance ); 3057 } 3058 3059 if( mfFontScale != 1.0 ) 3060 pWinLayout->SetFontScale( mfFontScale ); 3061 3062 return pWinLayout; 3063 } 3064 3065 // ----------------------------------------------------------------------- 3066 3067 int WinSalGraphics::GetMinKashidaWidth() 3068 { 3069 if( !mpWinFontEntry[0] ) 3070 return 0; 3071 mpWinFontEntry[0]->InitKashidaHandling( mhDC ); 3072 int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth()); 3073 return nMinKashida; 3074 } 3075 3076 // ======================================================================= 3077 3078 ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD ) 3079 : ImplFontEntry( rFSD ) 3080 , maWidthMap( 512 ) 3081 , mpKerningPairs( NULL ) 3082 , mnKerningPairs( -1 ) 3083 , mnMinKashidaWidth( -1 ) 3084 , mnMinKashidaGlyph( -1 ) 3085 { 3086 #ifdef USE_UNISCRIBE 3087 maScriptCache = NULL; 3088 #endif // USE_UNISCRIBE 3089 } 3090 3091 // ----------------------------------------------------------------------- 3092 3093 ImplWinFontEntry::~ImplWinFontEntry() 3094 { 3095 #ifdef USE_UNISCRIBE 3096 if( maScriptCache != NULL ) 3097 (*pScriptFreeCache)( &maScriptCache ); 3098 #endif // USE_UNISCRIBE 3099 #ifdef GCP_KERN_HACK 3100 delete[] mpKerningPairs; 3101 #endif // GCP_KERN_HACK 3102 } 3103 3104 // ----------------------------------------------------------------------- 3105 3106 bool ImplWinFontEntry::HasKernData() const 3107 { 3108 return (mnKerningPairs >= 0); 3109 } 3110 3111 // ----------------------------------------------------------------------- 3112 3113 void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData ) 3114 { 3115 mnKerningPairs = nPairCount; 3116 mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ]; 3117 ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) ); 3118 } 3119 3120 // ----------------------------------------------------------------------- 3121 3122 int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const 3123 { 3124 int nKernAmount = 0; 3125 if( mpKerningPairs ) 3126 { 3127 const KERNINGPAIR aRefPair = { cLeft, cRight, 0 }; 3128 const KERNINGPAIR* pFirstPair = mpKerningPairs; 3129 const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs; 3130 const KERNINGPAIR* pPair = std::lower_bound( pFirstPair, 3131 pEndPair, aRefPair, ImplCmpKernData ); 3132 if( (pPair != pEndPair) 3133 && (pPair->wFirst == aRefPair.wFirst) 3134 && (pPair->wSecond == aRefPair.wSecond) ) 3135 nKernAmount = pPair->iKernAmount; 3136 } 3137 3138 return nKernAmount; 3139 } 3140 3141 // ----------------------------------------------------------------------- 3142 3143 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC ) 3144 { 3145 if( mnMinKashidaWidth >= 0 ) // already cached? 3146 return mnMinKashidaWidth; 3147 3148 // initialize the kashida width 3149 mnMinKashidaWidth = 0; 3150 mnMinKashidaGlyph = 0; 3151 #ifdef USE_UNISCRIBE 3152 if (aUspModule || (bUspEnabled && InitUSP())) 3153 { 3154 SCRIPT_FONTPROPERTIES aFontProperties; 3155 aFontProperties.cBytes = sizeof (aFontProperties); 3156 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 3157 HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties ); 3158 if( nRC != 0 ) 3159 return false; 3160 mnMinKashidaWidth = aFontProperties.iKashidaWidth; 3161 mnMinKashidaGlyph = aFontProperties.wgKashida; 3162 } 3163 #endif // USE_UNISCRIBE 3164 3165 return true; 3166 } 3167 3168 // ======================================================================= 3169 3170 ImplFontData* ImplWinFontData::Clone() const 3171 { 3172 if( mpUnicodeMap ) 3173 mpUnicodeMap->AddReference(); 3174 ImplFontData* pClone = new ImplWinFontData( *this ); 3175 return pClone; 3176 } 3177 3178 // ----------------------------------------------------------------------- 3179 3180 ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const 3181 { 3182 ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD ); 3183 return pEntry; 3184 } 3185 3186 // ======================================================================= 3187