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