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