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.getHDC(); 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 static const int CHARPOS_NONE = -1; 1854 for( int i = 0; i < mnGlyphCount; ++i ) 1855 mpGlyphs2Chars[i] = CHARPOS_NONE; 1856 // calculate the char->glyph mapping 1857 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1858 { 1859 // ignore invisible visual items 1860 const VisualItem& rVI = mpVisualItems[ nItem ]; 1861 if( rVI.IsEmpty() ) 1862 continue; 1863 // calculate the mapping by using mpLogClusters[] 1864 // mpGlyphs2Chars[] should obey the logical order 1865 // => reversing the loop does this by overwriting higher logicals 1866 for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; ) 1867 { 1868 int i = mpLogClusters[c] + rVI.mnMinGlyphPos; 1869 mpGlyphs2Chars[i] = c; 1870 } 1871 // use a heuristic to fill the gaps in the glyphs2chars array 1872 c = !rVI.IsRTL() ? rVI.mnMinCharPos : rVI.mnEndCharPos - 1; 1873 for( int i = rVI.mnMinGlyphPos; i < rVI.mnEndGlyphPos; ++i ) { 1874 if( mpGlyphs2Chars[i] == CHARPOS_NONE ) 1875 mpGlyphs2Chars[i] = c; 1876 else 1877 c = mpGlyphs2Chars[i]; 1878 } 1879 } 1880 } 1881 1882 // calculate the absolute position of the first result glyph in pixel units 1883 const GOFFSET aGOffset = mpGlyphOffsets[ nStart ]; 1884 Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv ); 1885 rPos = GetDrawPosition( aRelativePos ); 1886 1887 // fill the result arrays 1888 int nCount = 0; 1889 while( nCount < nLen ) 1890 { 1891 // prepare return values 1892 sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ]; 1893 int nGlyphWidth = pGlyphWidths[ nStart ]; 1894 int nCharPos = -1; // no need to determine charpos 1895 if( mpGlyphs2Chars ) // unless explicitly requested+provided 1896 nCharPos = mpGlyphs2Chars[ nStart ]; 1897 1898 // inject kashida glyphs if needed 1899 if( !mbDisableGlyphInjection 1900 && mpJustifications 1901 && mnMinKashidaWidth 1902 && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL ) 1903 { 1904 // prepare draw position adjustment 1905 int nExtraOfs = (nSubIter++) * mnMinKashidaWidth; 1906 // calculate space available for the injected glyphs 1907 nGlyphWidth = mpGlyphAdvances[ nStart ]; 1908 const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth; 1909 const int nToFillWidth = nExtraWidth - nExtraOfs; 1910 if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room 1911 || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others 1912 { 1913 // handle if there is not sufficient room for a full glyph 1914 if( nToFillWidth < mnMinKashidaWidth ) 1915 { 1916 // overlap it with the previously injected glyph if possible 1917 int nOverlap = mnMinKashidaWidth - nToFillWidth; 1918 // else overlap it with both neighboring glyphs 1919 if( nSubIter <= 1 ) 1920 nOverlap /= 2; 1921 nExtraOfs -= nOverlap; 1922 } 1923 nGlyphWidth = mnMinKashidaWidth; 1924 aGlyphId = mnMinKashidaGlyph; 1925 nCharPos = -1; 1926 } 1927 else 1928 { 1929 nExtraOfs += nToFillWidth; // at right of cell 1930 nSubIter = 0; // done with glyph injection 1931 } 1932 if( !bManualCellAlign ) 1933 nExtraOfs -= nExtraWidth; // adjust for right-aligned cells 1934 1935 // adjust the draw position for the injected-glyphs case 1936 if( nExtraOfs ) 1937 { 1938 aRelativePos.X() += nExtraOfs; 1939 rPos = GetDrawPosition( aRelativePos ); 1940 } 1941 } 1942 1943 // update return values 1944 *(pGlyphs++) = aGlyphId; 1945 if( pGlyphAdvances ) 1946 *(pGlyphAdvances++) = nGlyphWidth; 1947 if( pCharPosAry ) 1948 *(pCharPosAry++) = nCharPos; 1949 1950 // increment counter of returned glyphs 1951 ++nCount; 1952 1953 // reduce code complexity by returning early in glyph-injection case 1954 if( nSubIter != 0 ) 1955 break; 1956 1957 // stop after the last visible glyph in this visual item 1958 if( ++nStart >= nEndGlyphPos ) 1959 { 1960 nStart = nNextItemStart; 1961 break; 1962 } 1963 1964 // RTL-justified glyph positioning is not easy 1965 // simplify the code by just returning only one glyph at a time 1966 if( mpJustifications && pVI->IsRTL() ) 1967 break; 1968 1969 // stop when the x-position of the next glyph is unexpected 1970 if( !pGlyphAdvances ) 1971 if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) ) 1972 || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) ) 1973 break; 1974 1975 // stop when the y-position of the next glyph is unexpected 1976 if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) ) 1977 break; 1978 } 1979 1980 ++nStart; 1981 nStartx8 = (nStart << 8) + nSubIter; 1982 return nCount; 1983 } 1984 1985 // ----------------------------------------------------------------------- 1986 1987 void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos ) 1988 { 1989 DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" ); 1990 int nStart = nStartx8 >> 8; 1991 if( nStart > mnGlyphCount ) 1992 return; 1993 1994 VisualItem* pVI = mpVisualItems; 1995 int nMinGlyphPos = 0, nEndGlyphPos; 1996 if( nStart == 0 ) // nStart==0 for first visible glyph 1997 { 1998 for( int i = mnItemCount; --i >= 0; ++pVI ) 1999 if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) ) 2000 break; 2001 nStart = nMinGlyphPos; 2002 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" ); 2003 } 2004 else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1 2005 { 2006 --nStart; 2007 for( int i = mnItemCount; --i >= 0; ++pVI ) 2008 if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) ) 2009 break; 2010 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); 2011 (void)bRC; // avoid var-not-used warning 2012 DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" ); 2013 } 2014 2015 long nDelta = nNewXPos - pVI->mnXOffset; 2016 if( nStart > nMinGlyphPos ) 2017 { 2018 // move the glyph by expanding its left glyph but ignore dropped glyphs 2019 int i, nLastUndropped = nMinGlyphPos - 1; 2020 for( i = nMinGlyphPos; i < nStart; ++i ) 2021 { 2022 if (mpOutGlyphs[i] != DROPPED_OUTGLYPH) 2023 { 2024 nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ]; 2025 nLastUndropped = i; 2026 } 2027 } 2028 if (nLastUndropped >= nMinGlyphPos) 2029 { 2030 mpGlyphAdvances[ nLastUndropped ] += nDelta; 2031 if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta; 2032 } 2033 else 2034 { 2035 pVI->mnXOffset += nDelta; 2036 } 2037 } 2038 else 2039 { 2040 // move the visual item by having an offset 2041 pVI->mnXOffset += nDelta; 2042 } 2043 // move subsequent items - this often isn't necessary because subsequent 2044 // moves will correct subsequent items. However, if there is a contiguous 2045 // range not involving fallback which spans items, this will be needed 2046 while (++pVI - mpVisualItems < mnItemCount) 2047 { 2048 pVI->mnXOffset += nDelta; 2049 } 2050 } 2051 2052 // ----------------------------------------------------------------------- 2053 2054 void UniscribeLayout::DropGlyph( int nStartx8 ) 2055 { 2056 DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" ); 2057 int nStart = nStartx8 >> 8; 2058 DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" ); 2059 2060 if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1 2061 --nStart; 2062 else // nStart<=0 for first visible glyph 2063 { 2064 VisualItem* pVI = mpVisualItems; 2065 for( int i = mnItemCount, nDummy; --i >= 0; ++pVI ) 2066 if( GetItemSubrange( *pVI, nStart, nDummy ) ) 2067 break; 2068 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" ); 2069 int nOffset = 0; 2070 int j = pVI->mnMinGlyphPos; 2071 while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++; 2072 if (j == nStart) 2073 { 2074 pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]); 2075 } 2076 } 2077 2078 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH; 2079 } 2080 2081 // ----------------------------------------------------------------------- 2082 2083 void UniscribeLayout::Simplify( bool /*bIsBase*/ ) 2084 { 2085 static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH; 2086 int i; 2087 // if there are no dropped glyphs don't bother 2088 for( i = 0; i < mnGlyphCount; ++i ) 2089 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2090 break; 2091 if( i >= mnGlyphCount ) 2092 return; 2093 2094 // prepare for sparse layout 2095 // => make sure mpGlyphs2Chars[] exists 2096 if( !mpGlyphs2Chars ) 2097 { 2098 mpGlyphs2Chars = new int[ mnGlyphCapacity ]; 2099 for( i = 0; i < mnGlyphCount; ++i ) 2100 mpGlyphs2Chars[ i ] = -1; 2101 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2102 { 2103 // skip invisible items 2104 VisualItem& rVI = mpVisualItems[ nItem ]; 2105 if( rVI.IsEmpty() ) 2106 continue; 2107 for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; ) 2108 { 2109 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos; 2110 mpGlyphs2Chars[ j ] = i; 2111 } 2112 } 2113 } 2114 2115 // remove the dropped glyphs 2116 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 2117 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2118 { 2119 VisualItem& rVI = mpVisualItems[ nItem ]; 2120 if( rVI.IsEmpty() ) 2121 continue; 2122 2123 // mark replaced character widths 2124 for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i ) 2125 { 2126 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos; 2127 if( mpOutGlyphs[ j ] == cDroppedGlyph ) 2128 mpCharWidths[ i ] = 0; 2129 } 2130 2131 // handle dropped glyphs at start of visual item 2132 int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos; 2133 GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ); 2134 i = nMinGlyphPos; 2135 while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) ) 2136 { 2137 //rVI.mnXOffset += pGlyphWidths[ i ]; 2138 rVI.mnMinGlyphPos = ++i; 2139 } 2140 2141 // when all glyphs in item got dropped mark it as empty 2142 if( i >= nEndGlyphPos ) 2143 { 2144 rVI.mnEndGlyphPos = 0; 2145 continue; 2146 } 2147 // If there are still glyphs in the cluster and mnMinGlyphPos 2148 // has changed then we need to remove the dropped glyphs at start 2149 // to correct logClusters, which is unsigned and relative to the 2150 // item start. 2151 if (rVI.mnMinGlyphPos != nOrigMinGlyphPos) 2152 { 2153 // drop any glyphs in the visual item outside the range 2154 for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++) 2155 mpOutGlyphs[ i ] = cDroppedGlyph; 2156 rVI.mnMinGlyphPos = i = nOrigMinGlyphPos; 2157 } 2158 2159 // handle dropped glyphs in the middle of visual item 2160 for(; i < nEndGlyphPos; ++i ) 2161 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2162 break; 2163 int j = i; 2164 while( ++i < nEndGlyphPos ) 2165 { 2166 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2167 continue; 2168 mpOutGlyphs[ j ] = mpOutGlyphs[ i ]; 2169 mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ]; 2170 mpVisualAttrs[ j ] = mpVisualAttrs[ i ]; 2171 mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ]; 2172 if( mpJustifications ) 2173 mpJustifications[ j ] = mpJustifications[ i ]; 2174 const int k = mpGlyphs2Chars[ i ]; 2175 mpGlyphs2Chars[ j ] = k; 2176 const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos; 2177 if( k < 0) // extra glyphs are already mapped 2178 continue; 2179 mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos); 2180 } 2181 2182 rVI.mnEndGlyphPos = j; 2183 } 2184 } 2185 2186 // ----------------------------------------------------------------------- 2187 2188 void UniscribeLayout::DrawText( SalGraphics& ) const 2189 { 2190 HFONT hOrigFont = DisableFontScaling(); 2191 2192 int nBaseClusterOffset = 0; 2193 int nBaseGlyphPos = -1; 2194 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2195 { 2196 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2197 2198 // skip if there is nothing to display 2199 int nMinGlyphPos, nEndGlyphPos; 2200 if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) 2201 continue; 2202 2203 if( nBaseGlyphPos < 0 ) 2204 { 2205 // adjust draw position relative to cluster start 2206 if( rVisualItem.IsRTL() ) 2207 nBaseGlyphPos = nEndGlyphPos - 1; 2208 else 2209 nBaseGlyphPos = nMinGlyphPos; 2210 2211 const int* pGlyphWidths; 2212 if( mpJustifications ) 2213 pGlyphWidths = mpJustifications; 2214 else 2215 pGlyphWidths = mpGlyphAdvances; 2216 2217 int i = mnMinCharPos; 2218 while( (--i >= rVisualItem.mnMinCharPos) 2219 && (nBaseGlyphPos == mpLogClusters[i]) ) 2220 nBaseClusterOffset += mpCharWidths[i]; 2221 2222 if( !rVisualItem.IsRTL() ) 2223 nBaseClusterOffset = -nBaseClusterOffset; 2224 } 2225 2226 // now draw the matching glyphs in this item 2227 Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 ); 2228 Point aPos = GetDrawPosition( aRelPos ); 2229 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 2230 (*pScriptTextOut)( mhDC, &rScriptCache, 2231 aPos.X(), aPos.Y(), 0, NULL, 2232 &rVisualItem.mpScriptItem->a, NULL, 0, 2233 mpOutGlyphs + nMinGlyphPos, 2234 nEndGlyphPos - nMinGlyphPos, 2235 mpGlyphAdvances + nMinGlyphPos, 2236 mpJustifications ? mpJustifications + nMinGlyphPos : NULL, 2237 mpGlyphOffsets + nMinGlyphPos ); 2238 } 2239 2240 if( hOrigFont ) 2241 DeleteFont( SelectFont( mhDC, hOrigFont ) ); 2242 } 2243 2244 // ----------------------------------------------------------------------- 2245 2246 long UniscribeLayout::FillDXArray( long* pDXArray ) const 2247 { 2248 // calculate width of the complete layout 2249 long nWidth = mnBaseAdv; 2250 for( int nItem = mnItemCount; --nItem >= 0; ) 2251 { 2252 const VisualItem& rVI = mpVisualItems[ nItem ]; 2253 2254 // skip if there is nothing to display 2255 int nMinGlyphPos, nEndGlyphPos; 2256 if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) ) 2257 continue; 2258 2259 // width = xoffset + width of last item 2260 nWidth = rVI.mnXOffset; 2261 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 2262 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2263 nWidth += pGlyphWidths[i]; 2264 break; 2265 } 2266 2267 // copy the virtual char widths into pDXArray[] 2268 if( pDXArray ) 2269 for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 2270 pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ]; 2271 2272 return nWidth; 2273 } 2274 2275 // ----------------------------------------------------------------------- 2276 2277 int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2278 { 2279 long nWidth = 0; 2280 for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 2281 { 2282 nWidth += mpCharWidths[ i ] * nFactor; 2283 2284 // check if the nMaxWidth still fits the current sub-layout 2285 if( nWidth >= nMaxWidth ) 2286 { 2287 // go back to cluster start 2288 // we have to find the visual item first since the mpLogClusters[] 2289 // needed to find the cluster start is relative to to the visual item 2290 int nMinGlyphIndex = 0; 2291 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2292 { 2293 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2294 nMinGlyphIndex = rVisualItem.mnMinGlyphPos; 2295 if( (i >= rVisualItem.mnMinCharPos) 2296 && (i < rVisualItem.mnEndCharPos) ) 2297 break; 2298 } 2299 // now go back to the matching cluster start 2300 do 2301 { 2302 int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex; 2303 if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart ) 2304 return i; 2305 } while( --i >= mnMinCharPos ); 2306 2307 // if the cluster starts before the start of the visual item 2308 // then set the visual breakpoint before this item 2309 return mnMinCharPos; 2310 } 2311 2312 // the visual break also depends on the nCharExtra between the characters 2313 nWidth += nCharExtra; 2314 } 2315 2316 // the whole layout did fit inside the nMaxWidth 2317 return STRING_LEN; 2318 } 2319 2320 // ----------------------------------------------------------------------- 2321 2322 void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const 2323 { 2324 int i; 2325 for( i = 0; i < nMaxIdx; ++i ) 2326 pCaretXArray[ i ] = -1; 2327 long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) ); 2328 for( i = 0; i <= mnGlyphCount; ++i ) 2329 pGlyphPos[ i ] = -1; 2330 2331 long nXPos = 0; 2332 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2333 { 2334 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2335 if( rVisualItem.IsEmpty() ) 2336 continue; 2337 2338 if (mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK) 2339 { 2340 nXPos = rVisualItem.mnXOffset; 2341 } 2342 // get glyph positions 2343 // TODO: handle when rVisualItem's glyph range is only partially used 2344 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2345 { 2346 pGlyphPos[ i ] = nXPos; 2347 nXPos += mpGlyphAdvances[ i ]; 2348 } 2349 // rightmost position of this visualitem 2350 pGlyphPos[ i ] = nXPos; 2351 2352 // convert glyph positions to character positions 2353 i = rVisualItem.mnMinCharPos; 2354 if( i < mnMinCharPos ) 2355 i = mnMinCharPos; 2356 for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i ) 2357 { 2358 int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos; 2359 int nCurrIdx = i * 2; 2360 if( !rVisualItem.IsRTL() ) 2361 { 2362 // normal positions for LTR case 2363 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ]; 2364 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ]; 2365 } 2366 else 2367 { 2368 // reverse positions for RTL case 2369 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ]; 2370 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ]; 2371 } 2372 } 2373 } 2374 2375 if (!(mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK)) 2376 { 2377 nXPos = 0; 2378 // fixup unknown character positions to neighbor 2379 for( i = 0; i < nMaxIdx; ++i ) 2380 { 2381 if( pCaretXArray[ i ] >= 0 ) 2382 nXPos = pCaretXArray[ i ]; 2383 else 2384 pCaretXArray[ i ] = nXPos; 2385 } 2386 } 2387 } 2388 2389 // ----------------------------------------------------------------------- 2390 2391 void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 2392 { 2393 SalLayout::AdjustLayout( rArgs ); 2394 2395 // adjust positions if requested 2396 if( rArgs.mpDXArray ) 2397 ApplyDXArray( rArgs ); 2398 else if( rArgs.mnLayoutWidth ) 2399 Justify( rArgs.mnLayoutWidth ); 2400 } 2401 2402 // ----------------------------------------------------------------------- 2403 2404 void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) 2405 { 2406 const long* pDXArray = rArgs.mpDXArray; 2407 2408 // increase char widths in string range to desired values 2409 bool bModified = false; 2410 int nOldWidth = 0; 2411 DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" ); 2412 int i,j; 2413 for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j ) 2414 { 2415 int nNewCharWidth = (pDXArray[j] - nOldWidth); 2416 // TODO: nNewCharWidth *= mnUnitsPerPixel; 2417 if( mpCharWidths[i] != nNewCharWidth ) 2418 { 2419 mpCharWidths[i] = nNewCharWidth; 2420 bModified = true; 2421 } 2422 nOldWidth = pDXArray[j]; 2423 } 2424 2425 if( !bModified ) 2426 return; 2427 2428 // initialize justifications array 2429 mpJustifications = new int[ mnGlyphCapacity ]; 2430 for( i = 0; i < mnGlyphCount; ++i ) 2431 mpJustifications[ i ] = mpGlyphAdvances[ i ]; 2432 2433 // apply new widths to script items 2434 long nXOffset = 0; 2435 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2436 { 2437 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2438 2439 // set the position of this visual item 2440 rVisualItem.mnXOffset = nXOffset; 2441 2442 // ignore empty visual items 2443 if( rVisualItem.IsEmpty() ) 2444 { 2445 for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++) 2446 nXOffset += mpCharWidths[i]; 2447 continue; 2448 } 2449 // ignore irrelevant visual items 2450 if( (rVisualItem.mnMinCharPos >= mnEndCharPos) 2451 || (rVisualItem.mnEndCharPos <= mnMinCharPos) ) 2452 continue; 2453 2454 // if needed prepare special handling for arabic justification 2455 rVisualItem.mbHasKashidas = false; 2456 if( rVisualItem.IsRTL() ) 2457 { 2458 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2459 if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification 2460 { // excluding SCRIPT_JUSTIFY_NONE 2461 // yes 2462 rVisualItem.mbHasKashidas = true; 2463 // so prepare for kashida handling 2464 InitKashidaHandling(); 2465 break; 2466 } 2467 2468 if( rVisualItem.HasKashidas() ) 2469 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2470 { 2471 // TODO: check if we still need this hack after correction of kashida placing? 2472 // (i87688): apparently yes, we still need it! 2473 if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE ) 2474 // usp decided that justification can't be applied here 2475 // but maybe our Kashida algorithm thinks differently. 2476 // To avoid trouble (gaps within words, last character of 2477 // a word gets a Kashida appended) override this. 2478 2479 // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE 2480 // just because this previous hack (which I haven't understand, sorry) used 2481 // the same value to replace. Don't know if this is really the best 2482 // thing to do, but it seems to fix things 2483 mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA; 2484 } 2485 } 2486 2487 // convert virtual charwidths to glyph justification values 2488 HRESULT nRC = (*pScriptApplyLogicalWidth)( 2489 mpCharWidths + rVisualItem.mnMinCharPos, 2490 rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos, 2491 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, 2492 mpLogClusters + rVisualItem.mnMinCharPos, 2493 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 2494 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 2495 &rVisualItem.mpScriptItem->a, 2496 &rVisualItem.maABCWidths, 2497 mpJustifications + rVisualItem.mnMinGlyphPos ); 2498 2499 if( nRC != 0 ) 2500 { 2501 delete[] mpJustifications; 2502 mpJustifications = NULL; 2503 break; 2504 } 2505 2506 // to prepare for the next visual item 2507 // update nXOffset to the next items position 2508 // before the mpJustifications[] array gets modified 2509 int nMinGlyphPos, nEndGlyphPos; 2510 if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) 2511 { 2512 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2513 nXOffset += mpJustifications[ i ]; 2514 2515 if( rVisualItem.mbHasKashidas ) 2516 KashidaItemFix( nMinGlyphPos, nEndGlyphPos ); 2517 } 2518 2519 // workaround needed for older USP versions: 2520 // right align the justification-adjusted glyphs in their cells for RTL-items 2521 // unless the right alignment is done by inserting kashidas 2522 if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() ) 2523 { 2524 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2525 { 2526 const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i]; 2527 // #i99862# skip diacritics, we mustn't add extra justification to diacritics 2528 int nIdxAdd = i - 1; 2529 while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] ) 2530 --nIdxAdd; 2531 if( nIdxAdd < nMinGlyphPos ) 2532 rVisualItem.mnXOffset += nXOffsetAdjust; 2533 else 2534 mpJustifications[nIdxAdd] += nXOffsetAdjust; 2535 mpJustifications[i] -= nXOffsetAdjust; 2536 } 2537 } 2538 } 2539 } 2540 2541 // ----------------------------------------------------------------------- 2542 2543 void UniscribeLayout::InitKashidaHandling() 2544 { 2545 if( mnMinKashidaGlyph != 0 ) // already initialized 2546 return; 2547 2548 mrWinFontEntry.InitKashidaHandling( mhDC ); 2549 mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth()); 2550 mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph(); 2551 } 2552 2553 // adjust the kashida placement matching to the WriterEngine 2554 void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos ) 2555 { 2556 // workaround needed for all known USP versions: 2557 // ApplyLogicalWidth does not match ScriptJustify behaviour 2558 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2559 { 2560 // check for vowels 2561 if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ]) 2562 && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types 2563 { // including SCRIPT_JUSTIFY_NONE 2564 // vowel, we do it like ScriptJustify does 2565 // the vowel gets the extra width 2566 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2567 mpJustifications [ i ] = mpGlyphAdvances [ i ]; 2568 mpJustifications [ i - 1 ] += nSpaceAdded; 2569 } 2570 } 2571 2572 // redistribute the widths for kashidas 2573 for( int i = nMinGlyphPos; i < nEndGlyphPos; ) 2574 KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i ); 2575 } 2576 2577 bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos ) 2578 { 2579 // doing pixel work within a word. 2580 // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth 2581 2582 // find the next kashida 2583 int nMinPos = *pnCurrentPos; 2584 int nMaxPos = *pnCurrentPos; 2585 for( int i = nMaxPos; i < nEndGlyphPos; ++i ) 2586 { 2587 if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK) 2588 && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) ) 2589 break; 2590 nMaxPos = i; 2591 } 2592 *pnCurrentPos = nMaxPos + 1; 2593 if( nMinPos == nMaxPos ) 2594 return false; 2595 2596 // calculate the available space for an extra kashida 2597 long nMaxAdded = 0; 2598 int nKashPos = -1; 2599 for( int i = nMaxPos; i >= nMinPos; --i ) 2600 { 2601 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2602 if( nSpaceAdded > nMaxAdded ) 2603 { 2604 nKashPos = i; 2605 nMaxAdded = nSpaceAdded; 2606 } 2607 } 2608 2609 // return early if there is no need for an extra kashida 2610 if ( nMaxAdded <= 0 ) 2611 return false; 2612 // return early if there is not enough space for an extra kashida 2613 if( 2*nMaxAdded < mnMinKashidaWidth ) 2614 return false; 2615 2616 // redistribute the extra spacing to the kashida position 2617 for( int i = nMinPos; i <= nMaxPos; ++i ) 2618 { 2619 if( i == nKashPos ) 2620 continue; 2621 // everything else should not have extra spacing 2622 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2623 if( nSpaceAdded > 0 ) 2624 { 2625 mpJustifications[ i ] -= nSpaceAdded; 2626 mpJustifications[ nKashPos ] += nSpaceAdded; 2627 } 2628 } 2629 2630 // check if we fulfill minimal kashida width 2631 long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ]; 2632 if( nSpaceAdded < mnMinKashidaWidth ) 2633 { 2634 // ugly: steal some pixels 2635 long nSteal = 1; 2636 if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos))) 2637 nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos); 2638 for( int i = nMinPos; i <= nMaxPos; ++i ) 2639 { 2640 if( i == nKashPos ) 2641 continue; 2642 nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal ); 2643 if ( nSteal > 0 ) 2644 { 2645 mpJustifications [ i ] -= nSteal; 2646 mpJustifications [ nKashPos ] += nSteal; 2647 nSpaceAdded += nSteal; 2648 } 2649 if( nSpaceAdded >= mnMinKashidaWidth ) 2650 return true; 2651 } 2652 } 2653 2654 // blank padding 2655 long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded; 2656 if( nSpaceMissing > 0 ) 2657 { 2658 // inner glyph: distribute extra space evenly 2659 if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) ) 2660 { 2661 mpJustifications [ nKashPos ] += nSpaceMissing; 2662 long nHalfSpace = nSpaceMissing / 2; 2663 mpJustifications [ nMinPos - 1 ] -= nHalfSpace; 2664 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace; 2665 } 2666 // rightmost: left glyph gets extra space 2667 else if( nMinPos > nMinGlyphPos ) 2668 { 2669 mpJustifications [ nMinPos - 1 ] -= nSpaceMissing; 2670 mpJustifications [ nKashPos ] += nSpaceMissing; 2671 } 2672 // leftmost: right glyph gets extra space 2673 else if( nMaxPos < nEndGlyphPos - 1 ) 2674 { 2675 mpJustifications [ nKashPos ] += nSpaceMissing; 2676 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing; 2677 } 2678 else 2679 return false; 2680 } 2681 2682 return true; 2683 } 2684 2685 // ----------------------------------------------------------------------- 2686 2687 void UniscribeLayout::Justify( long nNewWidth ) 2688 { 2689 long nOldWidth = 0; 2690 int i; 2691 for( i = mnMinCharPos; i < mnEndCharPos; ++i ) 2692 nOldWidth += mpCharWidths[ i ]; 2693 if( nOldWidth <= 0 ) 2694 return; 2695 2696 nNewWidth *= mnUnitsPerPixel; // convert into font units 2697 if( nNewWidth == nOldWidth ) 2698 return; 2699 // prepare to distribute the extra width evenly among the visual items 2700 const double fStretch = (double)nNewWidth / nOldWidth; 2701 2702 // initialize justifications array 2703 mpJustifications = new int[ mnGlyphCapacity ]; 2704 for( i = 0; i < mnGlyphCapacity; ++i ) 2705 mpJustifications[ i ] = mpGlyphAdvances[ i ]; 2706 2707 // justify stretched script items 2708 long nXOffset = 0; 2709 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 2710 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2711 { 2712 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2713 if( rVisualItem.IsEmpty() ) 2714 continue; 2715 2716 if( (rVisualItem.mnMinCharPos < mnEndCharPos) 2717 && (rVisualItem.mnEndCharPos > mnMinCharPos) ) 2718 { 2719 long nItemWidth = 0; 2720 for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i ) 2721 nItemWidth += mpCharWidths[ i ]; 2722 nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5); 2723 2724 HRESULT nRC = (*pScriptJustify) ( 2725 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 2726 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 2727 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, 2728 nItemWidth, 2729 mnMinKashidaWidth, 2730 mpJustifications + rVisualItem.mnMinGlyphPos ); 2731 2732 rVisualItem.mnXOffset = nXOffset; 2733 nXOffset += nItemWidth; 2734 } 2735 } 2736 } 2737 2738 // ----------------------------------------------------------------------- 2739 2740 bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const 2741 { 2742 // we have to find the visual item first since the mpLogClusters[] 2743 // needed to find the cluster start is relative to to the visual item 2744 int nMinGlyphIndex = -1; 2745 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2746 { 2747 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2748 if( (nCharPos >= rVisualItem.mnMinCharPos) 2749 && (nCharPos < rVisualItem.mnEndCharPos) ) 2750 { 2751 nMinGlyphIndex = rVisualItem.mnMinGlyphPos; 2752 break; 2753 } 2754 } 2755 // Invalid char pos or leftmost glyph in visual item 2756 if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] ) 2757 return false; 2758 2759 // This test didn't give the expected results 2760 /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ]) 2761 // two chars, one glyph 2762 return false;*/ 2763 2764 const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex; 2765 if( nGlyphPos <= 0 ) 2766 return true; 2767 // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE 2768 // and not SCRIPT_JUSTIFY_ARABIC_BLANK 2769 // special case: glyph to the left is vowel (no advance width) 2770 if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK 2771 || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE 2772 && mpGlyphAdvances [ nGlyphPos-1 ] )) 2773 return false; 2774 return true; 2775 } 2776 2777 #endif // USE_UNISCRIBE 2778 2779 #ifdef ENABLE_GRAPHITE 2780 2781 class GraphiteLayoutWinImpl : public GraphiteLayout 2782 { 2783 public: 2784 GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont) 2785 throw() 2786 : GraphiteLayout(font), mrFont(rFont) {}; 2787 virtual ~GraphiteLayoutWinImpl() throw() {}; 2788 virtual sal_GlyphId getKashidaGlyph(int & rWidth); 2789 private: 2790 ImplWinFontEntry & mrFont; 2791 }; 2792 2793 sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth) 2794 { 2795 rWidth = mrFont.GetMinKashidaWidth(); 2796 return mrFont.GetMinKashidaGlyph(); 2797 } 2798 2799 // This class uses the SIL Graphite engine to provide complex text layout services to the VCL 2800 // @author tse 2801 // 2802 class GraphiteWinLayout : public WinLayout 2803 { 2804 private: 2805 mutable GraphiteWinFont mpFont; 2806 grutils::GrFeatureParser * mpFeatures; 2807 mutable GraphiteLayoutWinImpl maImpl; 2808 public: 2809 GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw(); 2810 2811 static bool IsGraphiteEnabledFont(HDC hDC) throw(); 2812 2813 // used by upper layers 2814 virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout 2815 virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc. 2816 // virtual void InitFont() const; 2817 virtual void DrawText( SalGraphics& ) const; 2818 2819 // methods using string indexing 2820 virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const; 2821 virtual long FillDXArray( long* pDXArray ) const; 2822 2823 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; 2824 2825 // methods using glyph indexing 2826 virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&, 2827 long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const; 2828 2829 // used by glyph+font+script fallback 2830 virtual void MoveGlyph( int nStart, long nNewXPos ); 2831 virtual void DropGlyph( int nStart ); 2832 virtual void Simplify( bool bIsBase ); 2833 ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; }; 2834 protected: 2835 virtual void ReplaceDC(gr::Segment & segment) const; 2836 virtual void RestoreDC(gr::Segment & segment) const; 2837 }; 2838 2839 bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw() 2840 { 2841 return gr::WinFont::FontHasGraphiteTables(hDC); 2842 } 2843 2844 GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw() 2845 : WinLayout(hDC, rWFD, rWFE), mpFont(hDC), 2846 maImpl(mpFont, rWFE) 2847 { 2848 const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage ); 2849 rtl::OString name = rtl::OUStringToOString( 2850 rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 2851 sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1; 2852 if (nFeat > 0) 2853 { 2854 rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat); 2855 mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr()); 2856 } 2857 else 2858 { 2859 mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr()); 2860 } 2861 maImpl.SetFeatures(mpFeatures); 2862 } 2863 2864 void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const 2865 { 2866 COLORREF color = GetTextColor(mhDC); 2867 dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC); 2868 SetTextColor(mhDC, color); 2869 } 2870 2871 void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const 2872 { 2873 dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC(); 2874 } 2875 2876 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args) 2877 { 2878 if (args.mnMinCharPos >= args.mnEndCharPos) 2879 { 2880 maImpl.clear(); 2881 return true; 2882 } 2883 HFONT hUnRotatedFont; 2884 if (args.mnOrientation) 2885 { 2886 // Graphite gets very confused if the font is rotated 2887 LOGFONTW aLogFont; 2888 ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont); 2889 aLogFont.lfEscapement = 0; 2890 aLogFont.lfOrientation = 0; 2891 hUnRotatedFont = ::CreateFontIndirectW( &aLogFont); 2892 ::SelectFont(mhDC, hUnRotatedFont); 2893 } 2894 WinLayout::AdjustLayout(args); 2895 mpFont.replaceDC(mhDC); 2896 maImpl.SetFontScale(WinLayout::mfFontScale); 2897 //bool succeeded = maImpl.LayoutText(args); 2898 #ifdef GRCACHE 2899 GrSegRecord * pSegRecord = NULL; 2900 gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord); 2901 #else 2902 gr::Segment * pSegment = maImpl.CreateSegment(args); 2903 #endif 2904 bool bSucceeded = false; 2905 if (pSegment) 2906 { 2907 // replace the DC on the font within the segment 2908 ReplaceDC(*pSegment); 2909 // create glyph vectors 2910 #ifdef GRCACHE 2911 bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord); 2912 #else 2913 bSucceeded = maImpl.LayoutGlyphs(args, pSegment); 2914 #endif 2915 // restore original DC 2916 RestoreDC(*pSegment); 2917 #ifdef GRCACHE 2918 if (pSegRecord) pSegRecord->unlock(); 2919 else delete pSegment; 2920 #else 2921 delete pSegment; 2922 #endif 2923 } 2924 mpFont.restoreDC(); 2925 if (args.mnOrientation) 2926 { 2927 // restore the rotated font 2928 ::SelectFont(mhDC, mhFont); 2929 ::DeleteObject(hUnRotatedFont); 2930 } 2931 return bSucceeded; 2932 } 2933 2934 void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs) 2935 { 2936 WinLayout::AdjustLayout(rArgs); 2937 maImpl.DrawBase() = WinLayout::maDrawBase; 2938 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2939 if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray) 2940 { 2941 mrWinFontEntry.InitKashidaHandling(mhDC); 2942 } 2943 maImpl.AdjustLayout(rArgs); 2944 } 2945 2946 void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const 2947 { 2948 HFONT hOrigFont = DisableFontScaling(); 2949 const HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).getHDC(); 2950 maImpl.DrawBase() = WinLayout::maDrawBase; 2951 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2952 const int MAX_GLYPHS = 2; 2953 sal_GlyphId glyphIntStr[MAX_GLYPHS]; 2954 WORD glyphWStr[MAX_GLYPHS]; 2955 int glyphIndex = 0; 2956 Point aPos(0,0); 2957 int nGlyphs = 0; 2958 do 2959 { 2960 nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex); 2961 if (nGlyphs < 1) 2962 break; 2963 std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr); 2964 ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, 2965 NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL); 2966 } while (nGlyphs); 2967 if( hOrigFont ) 2968 DeleteFont( SelectFont( aHDC, hOrigFont ) ); 2969 } 2970 2971 int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2972 { 2973 mpFont.replaceDC(mhDC); 2974 int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor); 2975 mpFont.restoreDC(); 2976 return nBreak; 2977 } 2978 2979 long GraphiteWinLayout::FillDXArray( long* pDXArray ) const 2980 { 2981 return maImpl.FillDXArray(pDXArray); 2982 } 2983 2984 void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const 2985 { 2986 maImpl.GetCaretPositions(nArraySize, pCaretXArray); 2987 } 2988 2989 int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out, 2990 ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const 2991 { 2992 maImpl.DrawBase() = WinLayout::maDrawBase; 2993 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2994 return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index); 2995 } 2996 2997 void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos ) 2998 { 2999 maImpl.MoveGlyph(glyph_idx, new_x_pos); 3000 } 3001 3002 void GraphiteWinLayout::DropGlyph( int glyph_idx ) 3003 { 3004 maImpl.DropGlyph(glyph_idx); 3005 } 3006 3007 void GraphiteWinLayout::Simplify( bool is_base ) 3008 { 3009 maImpl.Simplify(is_base); 3010 } 3011 #endif // ENABLE_GRAPHITE 3012 // ======================================================================= 3013 3014 SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) 3015 { 3016 DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL"); 3017 3018 WinLayout* pWinLayout = NULL; 3019 3020 const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ]; 3021 ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ]; 3022 3023 #if defined( USE_UNISCRIBE ) 3024 if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) 3025 && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine 3026 { 3027 #ifdef ENABLE_GRAPHITE 3028 if (rFontFace.SupportsGraphite()) 3029 pWinLayout = new GraphiteWinLayout( getHDC(), rFontFace, rFontInstance); 3030 else 3031 #endif // ENABLE_GRAPHITE 3032 // script complexity is determined in upper layers 3033 pWinLayout = new UniscribeLayout( getHDC(), rFontFace, rFontInstance ); 3034 // NOTE: it must be guaranteed that the WinSalGraphics lives longer than 3035 // the created UniscribeLayout, otherwise the data passed into the 3036 // constructor might become invalid too early 3037 } 3038 else 3039 #endif // USE_UNISCRIBE 3040 { 3041 #ifdef GCP_KERN_HACK 3042 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() ) 3043 { 3044 // TODO: directly cache kerning info in the rFontInstance 3045 // TODO: get rid of kerning methods+data in WinSalGraphics object 3046 GetKernPairs( 0, NULL ); 3047 rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs ); 3048 } 3049 #endif // GCP_KERN_HACK 3050 3051 BYTE eCharSet = ANSI_CHARSET; 3052 if( mpLogFont ) 3053 eCharSet = mpLogFont->lfCharSet; 3054 #ifdef ENABLE_GRAPHITE 3055 if (rFontFace.SupportsGraphite()) 3056 pWinLayout = new GraphiteWinLayout( getHDC(), rFontFace, rFontInstance); 3057 else 3058 #endif // ENABLE_GRAPHITE 3059 pWinLayout = new SimpleWinLayout( getHDC(), eCharSet, rFontFace, rFontInstance ); 3060 } 3061 3062 if( mfFontScale != 1.0 ) 3063 pWinLayout->SetFontScale( mfFontScale ); 3064 3065 return pWinLayout; 3066 } 3067 3068 // ----------------------------------------------------------------------- 3069 3070 int WinSalGraphics::GetMinKashidaWidth() 3071 { 3072 if( !mpWinFontEntry[0] ) 3073 return 0; 3074 mpWinFontEntry[0]->InitKashidaHandling( getHDC() ); 3075 int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth()); 3076 return nMinKashida; 3077 } 3078 3079 // ======================================================================= 3080 3081 ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD ) 3082 : ImplFontEntry( rFSD ) 3083 , maWidthMap( 512 ) 3084 , mpKerningPairs( NULL ) 3085 , mnKerningPairs( -1 ) 3086 , mnMinKashidaWidth( -1 ) 3087 , mnMinKashidaGlyph( -1 ) 3088 { 3089 #ifdef USE_UNISCRIBE 3090 maScriptCache = NULL; 3091 #endif // USE_UNISCRIBE 3092 } 3093 3094 // ----------------------------------------------------------------------- 3095 3096 ImplWinFontEntry::~ImplWinFontEntry() 3097 { 3098 #ifdef USE_UNISCRIBE 3099 if( maScriptCache != NULL ) 3100 (*pScriptFreeCache)( &maScriptCache ); 3101 #endif // USE_UNISCRIBE 3102 #ifdef GCP_KERN_HACK 3103 delete[] mpKerningPairs; 3104 #endif // GCP_KERN_HACK 3105 } 3106 3107 // ----------------------------------------------------------------------- 3108 3109 bool ImplWinFontEntry::HasKernData() const 3110 { 3111 return (mnKerningPairs >= 0); 3112 } 3113 3114 // ----------------------------------------------------------------------- 3115 3116 void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData ) 3117 { 3118 mnKerningPairs = nPairCount; 3119 mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ]; 3120 ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) ); 3121 } 3122 3123 // ----------------------------------------------------------------------- 3124 3125 int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const 3126 { 3127 int nKernAmount = 0; 3128 if( mpKerningPairs ) 3129 { 3130 const KERNINGPAIR aRefPair = { cLeft, cRight, 0 }; 3131 const KERNINGPAIR* pFirstPair = mpKerningPairs; 3132 const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs; 3133 const KERNINGPAIR* pPair = std::lower_bound( pFirstPair, 3134 pEndPair, aRefPair, ImplCmpKernData ); 3135 if( (pPair != pEndPair) 3136 && (pPair->wFirst == aRefPair.wFirst) 3137 && (pPair->wSecond == aRefPair.wSecond) ) 3138 nKernAmount = pPair->iKernAmount; 3139 } 3140 3141 return nKernAmount; 3142 } 3143 3144 // ----------------------------------------------------------------------- 3145 3146 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC ) 3147 { 3148 if( mnMinKashidaWidth >= 0 ) // already cached? 3149 return mnMinKashidaWidth; 3150 3151 // initialize the kashida width 3152 mnMinKashidaWidth = 0; 3153 mnMinKashidaGlyph = 0; 3154 #ifdef USE_UNISCRIBE 3155 if (aUspModule || (bUspEnabled && InitUSP())) 3156 { 3157 SCRIPT_FONTPROPERTIES aFontProperties; 3158 aFontProperties.cBytes = sizeof (aFontProperties); 3159 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 3160 HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties ); 3161 if( nRC != 0 ) 3162 return false; 3163 mnMinKashidaWidth = aFontProperties.iKashidaWidth; 3164 mnMinKashidaGlyph = aFontProperties.wgKashida; 3165 } 3166 #endif // USE_UNISCRIBE 3167 3168 return true; 3169 } 3170 3171 // ======================================================================= 3172 3173 ImplFontData* ImplWinFontData::Clone() const 3174 { 3175 if( mpUnicodeMap ) 3176 mpUnicodeMap->AddReference(); 3177 ImplFontData* pClone = new ImplWinFontData( *this ); 3178 return pClone; 3179 } 3180 3181 // ----------------------------------------------------------------------- 3182 3183 ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const 3184 { 3185 ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD ); 3186 return pEntry; 3187 } 3188 3189 // ======================================================================= 3190