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