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 OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) ); 1136 aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT ); 1137 if( !aUspModule ) 1138 return (bUspEnabled = false); 1139 1140 pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD)) 1141 osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" ); 1142 bUspEnabled &= (NULL != pScriptIsComplex); 1143 1144 pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int, 1145 const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*)) 1146 osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" ); 1147 bUspEnabled &= (NULL != pScriptItemize); 1148 1149 pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*, 1150 int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*)) 1151 osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" ); 1152 bUspEnabled &= (NULL != pScriptShape); 1153 1154 pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int, 1155 const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*)) 1156 osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" ); 1157 bUspEnabled &= (NULL != pScriptPlace); 1158 1159 pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*, 1160 int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*)) 1161 osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" ); 1162 bUspEnabled &= (NULL != pScriptGetLogicalWidths); 1163 1164 pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*, 1165 const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*)) 1166 osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" ); 1167 bUspEnabled &= (NULL != pScriptApplyLogicalWidth); 1168 1169 pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*, 1170 int,int,int,int*)) 1171 osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" ); 1172 bUspEnabled &= (NULL != pScriptJustify); 1173 1174 pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*)) 1175 osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" ); 1176 bUspEnabled &= (NULL != pScriptGetFontProperties); 1177 1178 pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*, 1179 int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*, 1180 int,const WORD*,int,const int*,const int*,const GOFFSET*)) 1181 osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" ); 1182 bUspEnabled &= (NULL != pScriptTextOut); 1183 1184 pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*)) 1185 osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" ); 1186 bUspEnabled &= (NULL != pScriptFreeCache); 1187 1188 if( !bUspEnabled ) 1189 { 1190 osl_unloadModule( aUspModule ); 1191 aUspModule = NULL; 1192 } 1193 1194 // get the DLL version info 1195 int nUspVersion = 0; 1196 // TODO: there must be a simpler way to get the friggin version info from OSL? 1197 rtl_uString* pModuleURL = NULL; 1198 osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL ); 1199 rtl_uString* pModuleFileName = NULL; 1200 if( pModuleURL ) 1201 osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName ); 1202 const sal_Unicode* pModuleFileCStr = NULL; 1203 if( pModuleFileName ) 1204 pModuleFileCStr = rtl_uString_getStr( pModuleFileName ); 1205 if( pModuleFileCStr ) 1206 { 1207 DWORD nHandle; 1208 DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle ); 1209 char* pBuffer = (char*)alloca( nBufSize ); 1210 BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer ); 1211 VS_FIXEDFILEINFO* pFixedFileInfo = NULL; 1212 UINT nFixedFileSize = 0; 1213 if( bRC ) 1214 ::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize ); 1215 if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD ) 1216 nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000 1217 + LOWORD(pFixedFileInfo->dwProductVersionMS); 1218 } 1219 1220 // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells 1221 if( nUspVersion >= 10600 ) 1222 bManualCellAlign = false; 1223 1224 return bUspEnabled; 1225 } 1226 1227 // ----------------------------------------------------------------------- 1228 1229 UniscribeLayout::UniscribeLayout( HDC hDC, 1230 const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry ) 1231 : WinLayout( hDC, rWinFontData, rWinFontEntry ), 1232 mnItemCount( 0 ), 1233 mpScriptItems( NULL ), 1234 mpVisualItems( NULL ), 1235 mpLogClusters( NULL ), 1236 mpCharWidths( NULL ), 1237 mnCharCapacity( 0 ), 1238 mnSubStringMin( 0 ), 1239 mnGlyphCapacity( 0 ), 1240 mnGlyphCount( 0 ), 1241 mpOutGlyphs( NULL ), 1242 mpGlyphAdvances( NULL ), 1243 mpJustifications( NULL ), 1244 mpGlyphOffsets( NULL ), 1245 mpVisualAttrs( NULL ), 1246 mpGlyphs2Chars( NULL ), 1247 mnMinKashidaGlyph( 0 ), 1248 mbDisableGlyphInjection( false ) 1249 {} 1250 1251 // ----------------------------------------------------------------------- 1252 1253 UniscribeLayout::~UniscribeLayout() 1254 { 1255 delete[] mpScriptItems; 1256 delete[] mpVisualItems; 1257 delete[] mpLogClusters; 1258 delete[] mpCharWidths; 1259 delete[] mpOutGlyphs; 1260 delete[] mpGlyphAdvances; 1261 delete[] mpJustifications; 1262 delete[] mpGlyphOffsets; 1263 delete[] mpVisualAttrs; 1264 delete[] mpGlyphs2Chars; 1265 } 1266 1267 // ----------------------------------------------------------------------- 1268 1269 bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs ) 1270 { 1271 // for a base layout only the context glyphs have to be dropped 1272 // => when the whole string is involved there is no extra context 1273 typedef std::vector<int> TIntVector; 1274 TIntVector aDropChars; 1275 if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK ) 1276 { 1277 // calculate superfluous context char positions 1278 aDropChars.push_back( 0 ); 1279 aDropChars.push_back( rArgs.mnLength ); 1280 int nMin, nEnd; 1281 bool bRTL; 1282 for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); ) 1283 { 1284 aDropChars.push_back( nMin ); 1285 aDropChars.push_back( nEnd ); 1286 } 1287 // prepare aDropChars for binary search which will allow to 1288 // not bother with visual items that will be dropped anyway 1289 std::sort( aDropChars.begin(), aDropChars.end() ); 1290 } 1291 1292 // prepare layout 1293 // TODO: fix case when recyclying old UniscribeLayout object 1294 mnMinCharPos = rArgs.mnMinCharPos; 1295 mnEndCharPos = rArgs.mnEndCharPos; 1296 1297 // determine script items from string 1298 1299 // prepare itemization 1300 // TODO: try to avoid itemization since it costs a lot of performance 1301 SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0}; 1302 aScriptState.uBidiLevel = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)); 1303 aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG)); 1304 aScriptState.fDigitSubstitute = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS)); 1305 aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel; 1306 DWORD nLangId = 0; // TODO: get language from font 1307 SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0}; 1308 aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection; 1309 aScriptControl.fContextDigits = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS)); 1310 // determine relevant substring and work only on it 1311 // when Bidi status is unknown we need to look at the whole string though 1312 mnSubStringMin = 0; 1313 int nSubStringEnd = rArgs.mnLength; 1314 if( aScriptState.fOverrideDirection ) 1315 { 1316 // TODO: limit substring to portion limits 1317 mnSubStringMin = rArgs.mnMinCharPos - 8; 1318 if( mnSubStringMin < 0 ) 1319 mnSubStringMin = 0; 1320 nSubStringEnd = rArgs.mnEndCharPos + 8; 1321 if( nSubStringEnd > rArgs.mnLength ) 1322 nSubStringEnd = rArgs.mnLength; 1323 1324 } 1325 1326 // now itemize the substring with its context 1327 for( int nItemCapacity = 16;; nItemCapacity *= 8 ) 1328 { 1329 mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ]; 1330 HRESULT nRC = (*pScriptItemize)( 1331 reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin, 1332 nItemCapacity - 1, &aScriptControl, &aScriptState, 1333 mpScriptItems, &mnItemCount ); 1334 if( !nRC ) // break loop when everything is correctly itemized 1335 break; 1336 1337 // prepare bigger buffers for another itemization round 1338 delete[] mpScriptItems; 1339 mpScriptItems = NULL; 1340 if( nRC != E_OUTOFMEMORY ) 1341 return false; 1342 if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 ) 1343 return false; 1344 } 1345 1346 // calculate the order of visual items 1347 int nItem, i; 1348 1349 // adjust char positions by substring offset 1350 for( nItem = 0; nItem <= mnItemCount; ++nItem ) 1351 mpScriptItems[ nItem ].iCharPos += mnSubStringMin; 1352 // default visual item ordering 1353 mpVisualItems = new VisualItem[ mnItemCount ]; 1354 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1355 { 1356 // initialize char specific item info 1357 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 1358 SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ]; 1359 rVisualItem.mpScriptItem = pScriptItem; 1360 rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos; 1361 rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos; 1362 } 1363 1364 // reorder visual item order if needed 1365 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG ) 1366 { 1367 // force RTL item ordering if requested 1368 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL ) 1369 { 1370 VisualItem* pVI0 = &mpVisualItems[ 0 ]; 1371 VisualItem* pVI1 = &mpVisualItems[ mnItemCount ]; 1372 while( pVI0 < --pVI1 ) 1373 { 1374 VisualItem aVtmp = *pVI0; 1375 *(pVI0++) = *pVI1; 1376 *pVI1 = aVtmp; 1377 } 1378 } 1379 } 1380 else if( mnItemCount > 1 ) 1381 { 1382 // apply bidi algorithm's rule L2 on item level 1383 // TODO: use faster L2 algorithm 1384 int nMaxBidiLevel = 0; 1385 VisualItem* pVI = &mpVisualItems[0]; 1386 VisualItem* const pVIend = pVI + mnItemCount; 1387 for(; pVI < pVIend; ++pVI ) 1388 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel ) 1389 nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel; 1390 1391 while( --nMaxBidiLevel >= 0 ) 1392 { 1393 for( pVI = &mpVisualItems[0]; pVI < pVIend; ) 1394 { 1395 // find item range that needs reordering 1396 for(; pVI < pVIend; ++pVI ) 1397 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel ) 1398 break; 1399 VisualItem* pVImin = pVI++; 1400 for(; pVI < pVIend; ++pVI ) 1401 if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel ) 1402 break; 1403 VisualItem* pVImax = pVI++; 1404 1405 // reverse order of items in this range 1406 while( pVImin < --pVImax ) 1407 { 1408 VisualItem aVtmp = *pVImin; 1409 *(pVImin++) = *pVImax; 1410 *pVImax = aVtmp; 1411 } 1412 } 1413 } 1414 } 1415 1416 // allocate arrays 1417 // TODO: when reusing object reuse old allocations or delete them 1418 // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd) 1419 mnCharCapacity = nSubStringEnd; 1420 mpLogClusters = new WORD[ mnCharCapacity ]; 1421 mpCharWidths = new int[ mnCharCapacity ]; 1422 1423 mnGlyphCount = 0; 1424 mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption 1425 mpGlyphAdvances = new int[ mnGlyphCapacity ]; 1426 mpOutGlyphs = new WORD[ mnGlyphCapacity ]; 1427 mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ]; 1428 mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ]; 1429 1430 long nXOffset = 0; 1431 for( int j = mnSubStringMin; j < nSubStringEnd; ++j ) 1432 mpCharWidths[j] = 0; 1433 1434 // layout script items 1435 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 1436 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1437 { 1438 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 1439 1440 // initialize glyph specific item info 1441 rVisualItem.mnMinGlyphPos = mnGlyphCount; 1442 rVisualItem.mnEndGlyphPos = 0; 1443 rVisualItem.mnXOffset = nXOffset; 1444 //rVisualItem.mnPixelWidth = 0; 1445 1446 // shortcut ignorable items 1447 if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos) 1448 || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) ) 1449 { 1450 for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i ) 1451 mpLogClusters[i] = sal::static_int_cast<WORD>(~0U); 1452 continue; 1453 } 1454 1455 // override bidi analysis if requested 1456 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG ) 1457 { 1458 // FIXME: is this intended ? 1459 rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1); 1460 rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel; 1461 rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection; 1462 } 1463 1464 // convert the unicodes to glyphs 1465 int nGlyphCount = 0; 1466 int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos; 1467 HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache, 1468 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos), 1469 nCharCount, 1470 mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF 1471 &rVisualItem.mpScriptItem->a, 1472 mpOutGlyphs + rVisualItem.mnMinGlyphPos, 1473 mpLogClusters + rVisualItem.mnMinCharPos, 1474 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1475 &nGlyphCount ); 1476 1477 // find and handle problems in the unicode to glyph conversion 1478 if( nRC == USP_E_SCRIPT_NOT_IN_FONT ) 1479 { 1480 // the whole visual item needs a fallback, but make sure that the next 1481 // fallback request is limited to the characters in the original request 1482 // => this is handled in ImplLayoutArgs::PrepareFallback() 1483 rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos, 1484 rVisualItem.IsRTL() ); 1485 1486 // don't bother to do a default layout in a fallback level 1487 if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) ) 1488 continue; 1489 1490 // the primitive layout engine is good enough for the default layout 1491 rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED; 1492 nRC = (*pScriptShape)( mhDC, &rScriptCache, 1493 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos), 1494 nCharCount, 1495 mnGlyphCapacity - rVisualItem.mnMinGlyphPos, 1496 &rVisualItem.mpScriptItem->a, 1497 mpOutGlyphs + rVisualItem.mnMinGlyphPos, 1498 mpLogClusters + rVisualItem.mnMinCharPos, 1499 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1500 &nGlyphCount ); 1501 1502 if( nRC != 0 ) 1503 continue; 1504 1505 #if 0 // keep the glyphs for now because they are better than nothing 1506 // mark as NotDef glyphs 1507 for( i = 0; i < nGlyphCount; ++i ) 1508 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0; 1509 #endif 1510 } 1511 else if( nRC != 0 ) 1512 // something undefined happened => give up for this visual item 1513 continue; 1514 else // if( nRC == 0 ) 1515 { 1516 // check if there are any NotDef glyphs 1517 for( i = 0; i < nGlyphCount; ++i ) 1518 if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] ) 1519 break; 1520 if( i < nGlyphCount ) 1521 { 1522 // clip charpos limits to the layout string without context 1523 int nMinCharPos = rVisualItem.mnMinCharPos; 1524 if( nMinCharPos < rArgs.mnMinCharPos ) 1525 nMinCharPos = rArgs.mnMinCharPos; 1526 int nEndCharPos = rVisualItem.mnEndCharPos; 1527 if( nEndCharPos > rArgs.mnEndCharPos ) 1528 nEndCharPos = rArgs.mnEndCharPos; 1529 // request fallback for individual NotDef glyphs 1530 do 1531 { 1532 // ignore non-NotDef glyphs 1533 if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] ) 1534 continue; 1535 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH; 1536 // request fallback for the whole cell that resulted in a NotDef glyph 1537 // TODO: optimize algorithm 1538 const bool bRTL = rVisualItem.IsRTL(); 1539 if( !bRTL ) 1540 { 1541 // request fallback for the left-to-right cell 1542 for( int c = nMinCharPos; c < nEndCharPos; ++c ) 1543 { 1544 if( mpLogClusters[ c ] == i ) 1545 { 1546 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER 1547 if( rArgs.mpStr[ c ] == 0x2060 ) 1548 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1; 1549 else 1550 // <-- 1551 rArgs.NeedFallback( c, false ); 1552 } 1553 } 1554 } 1555 else 1556 { 1557 // request fallback for the right to left cell 1558 for( int c = nEndCharPos; --c >= nMinCharPos; ) 1559 { 1560 if( mpLogClusters[ c ] == i ) 1561 { 1562 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER 1563 if( rArgs.mpStr[ c ] == 0x2060 ) 1564 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1; 1565 else 1566 // <-- 1567 rArgs.NeedFallback( c, true ); 1568 } 1569 } 1570 } 1571 } while( ++i < nGlyphCount ); 1572 } 1573 } 1574 1575 // now place the glyphs 1576 nRC = (*pScriptPlace)( mhDC, &rScriptCache, 1577 mpOutGlyphs + rVisualItem.mnMinGlyphPos, 1578 nGlyphCount, 1579 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1580 &rVisualItem.mpScriptItem->a, 1581 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 1582 mpGlyphOffsets + rVisualItem.mnMinGlyphPos, 1583 &rVisualItem.maABCWidths ); 1584 1585 if( nRC != 0 ) 1586 continue; 1587 1588 // calculate the logical char widths from the glyph layout 1589 nRC = (*pScriptGetLogicalWidths)( 1590 &rVisualItem.mpScriptItem->a, 1591 nCharCount, nGlyphCount, 1592 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 1593 mpLogClusters + rVisualItem.mnMinCharPos, 1594 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 1595 mpCharWidths + rVisualItem.mnMinCharPos ); 1596 1597 // update the glyph counters 1598 mnGlyphCount += nGlyphCount; 1599 rVisualItem.mnEndGlyphPos = mnGlyphCount; 1600 1601 // update nXOffset 1602 int nEndGlyphPos; 1603 if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) ) 1604 for(; i < nEndGlyphPos; ++i ) 1605 nXOffset += mpGlyphAdvances[ i ]; 1606 1607 // TODO: shrink glyphpos limits to match charpos/fallback limits 1608 //pVI->mnMinGlyphPos = nMinGlyphPos; 1609 //pVI->mnEndGlyphPos = nEndGlyphPos; 1610 1611 // drop the superfluous context glyphs 1612 TIntVector::const_iterator it = aDropChars.begin(); 1613 while( it != aDropChars.end() ) 1614 { 1615 // find matching "drop range" 1616 int nMinDropPos = *(it++); // begin of drop range 1617 if( nMinDropPos >= rVisualItem.mnEndCharPos ) 1618 break; 1619 int nEndDropPos = *(it++); // end of drop range 1620 if( nEndDropPos <= rVisualItem.mnMinCharPos ) 1621 continue; 1622 // clip "drop range" to visual item's char range 1623 if( nMinDropPos <= rVisualItem.mnMinCharPos ) 1624 { 1625 nMinDropPos = rVisualItem.mnMinCharPos; 1626 // drop the whole visual item if possible 1627 if( nEndDropPos >= rVisualItem.mnEndCharPos ) 1628 { 1629 rVisualItem.mnEndGlyphPos = 0; 1630 break; 1631 } 1632 } 1633 if( nEndDropPos > rVisualItem.mnEndCharPos ) 1634 nEndDropPos = rVisualItem.mnEndCharPos; 1635 1636 // drop the glyphs which correspond to the charpos range 1637 // drop the corresponding glyphs in the cluster 1638 for( int c = nMinDropPos; c < nEndDropPos; ++c ) 1639 { 1640 int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos; 1641 // no need to bother when the cluster was already dropped 1642 if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH ) 1643 { 1644 for(;;) 1645 { 1646 mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH; 1647 // until the end of visual item 1648 if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos ) 1649 break; 1650 // until the next cluster start 1651 if( mpVisualAttrs[ nGlyphPos ].fClusterStart ) 1652 break; 1653 } 1654 } 1655 } 1656 } 1657 } 1658 1659 // scale layout metrics if needed 1660 // TODO: does it make the code more simple if the metric scaling 1661 // is moved to the methods that need metric scaling (e.g. FillDXArray())? 1662 if( mfFontScale != 1.0 ) 1663 { 1664 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale); 1665 1666 for( i = 0; i < mnItemCount; ++i ) 1667 mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale); 1668 1669 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale); 1670 for( i = 0; i < mnGlyphCount; ++i ) 1671 { 1672 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale); 1673 mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale); 1674 mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale); 1675 // mpJustifications are still NULL 1676 } 1677 1678 for( i = mnSubStringMin; i < nSubStringEnd; ++i ) 1679 mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale); 1680 } 1681 1682 return true; 1683 } 1684 1685 // ----------------------------------------------------------------------- 1686 1687 // calculate the range of relevant glyphs for this visual item 1688 bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem, 1689 int& rMinGlyphPos, int& rEndGlyphPos ) const 1690 { 1691 // return early when nothing of interest in this item 1692 if( rVisualItem.IsEmpty() 1693 || (rVisualItem.mnEndCharPos <= mnMinCharPos) 1694 || (mnEndCharPos <= rVisualItem.mnMinCharPos) ) 1695 return false; 1696 1697 // default: subrange is complete range 1698 rMinGlyphPos = rVisualItem.mnMinGlyphPos; 1699 rEndGlyphPos = rVisualItem.mnEndGlyphPos; 1700 1701 // return early when the whole item is of interest 1702 if( (mnMinCharPos <= rVisualItem.mnMinCharPos) 1703 && (rVisualItem.mnEndCharPos <= mnEndCharPos ) ) 1704 return true; 1705 1706 // get glyph range from char range by looking at cluster boundries 1707 // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes 1708 rMinGlyphPos = rVisualItem.mnEndGlyphPos; 1709 int nMaxGlyphPos = 0; 1710 1711 int i = mnMinCharPos; 1712 if( i < rVisualItem.mnMinCharPos ) 1713 i = rVisualItem.mnMinCharPos; 1714 int nCharPosLimit = rVisualItem.mnEndCharPos; 1715 if( nCharPosLimit > mnEndCharPos ) 1716 nCharPosLimit = mnEndCharPos; 1717 for(; i < nCharPosLimit; ++i ) 1718 { 1719 int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos; 1720 if( rMinGlyphPos > n ) 1721 rMinGlyphPos = n; 1722 if( nMaxGlyphPos < n ) 1723 nMaxGlyphPos = n; 1724 } 1725 if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos) 1726 nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1; 1727 1728 // extend the glyph range to account for all glyphs in referenced clusters 1729 if( !rVisualItem.IsRTL() ) // LTR-item 1730 { 1731 // extend to rightmost glyph of rightmost referenced cluster 1732 for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i ) 1733 if( mpVisualAttrs[i].fClusterStart ) 1734 break; 1735 } 1736 else // RTL-item 1737 { 1738 // extend to leftmost glyph of leftmost referenced cluster 1739 for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i ) 1740 if( mpVisualAttrs[i].fClusterStart ) 1741 break; 1742 } 1743 rEndGlyphPos = nMaxGlyphPos + 1; 1744 1745 return true; 1746 } 1747 1748 // ----------------------------------------------------------------------- 1749 1750 int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, 1751 int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const 1752 { 1753 // HACK to allow fake-glyph insertion (e.g. for kashidas) 1754 // TODO: use iterator idiom instead of GetNextGlyphs(...) 1755 // TODO: else make sure that the limit for glyph injection is sufficient (currently 256) 1756 int nSubIter = nStartx8 & 0xff; 1757 int nStart = nStartx8 >> 8; 1758 1759 // check the glyph iterator 1760 if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs 1761 return 0; 1762 1763 // find the visual item for the nStart glyph position 1764 int nItem = 0; 1765 const VisualItem* pVI = mpVisualItems; 1766 if( nStart <= 0 ) // nStart<=0 requests the first visible glyph 1767 { 1768 // find first visible item 1769 for(; nItem < mnItemCount; ++nItem, ++pVI ) 1770 if( !pVI->IsEmpty() ) 1771 break; 1772 // it is possible that there are glyphs but no valid visual item 1773 // TODO: get rid of these visual items more early 1774 if( nItem < mnItemCount ) 1775 nStart = pVI->mnMinGlyphPos; 1776 } 1777 else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1 1778 { 1779 --nStart; 1780 1781 // find matching item 1782 for(; nItem < mnItemCount; ++nItem, ++pVI ) 1783 if( (nStart >= pVI->mnMinGlyphPos) 1784 && (nStart < pVI->mnEndGlyphPos) ) 1785 break; 1786 } 1787 1788 // after the last visual item there are no more glyphs 1789 if( (nItem >= mnItemCount) || (nStart < 0) ) 1790 { 1791 nStartx8 = (mnGlyphCount + 1) << 8; 1792 return 0; 1793 } 1794 1795 // calculate the first glyph in the next visual item 1796 int nNextItemStart = mnGlyphCount; 1797 while( ++nItem < mnItemCount ) 1798 { 1799 if( mpVisualItems[nItem].IsEmpty() ) 1800 continue; 1801 nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos; 1802 break; 1803 } 1804 1805 // get the range of relevant glyphs in this visual item 1806 int nMinGlyphPos, nEndGlyphPos; 1807 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); 1808 DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" ); 1809 if( !bRC ) 1810 { 1811 nStartx8 = (mnGlyphCount + 1) << 8; 1812 return 0; 1813 } 1814 1815 // make sure nStart is inside the range of relevant glyphs 1816 if( nStart < nMinGlyphPos ) 1817 nStart = nMinGlyphPos; 1818 1819 // calculate the start glyph xoffset relative to layout's base position, 1820 // advance to next visual glyph position by using adjusted glyph widths 1821 // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache 1822 long nXOffset = pVI->mnXOffset; 1823 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 1824 for( int i = nMinGlyphPos; i < nStart; ++i ) 1825 nXOffset += pGlyphWidths[ i ]; 1826 1827 // adjust the nXOffset relative to glyph cluster start 1828 int c = mnMinCharPos; 1829 if( !pVI->IsRTL() ) // LTR-case 1830 { 1831 // LTR case: subtract the remainder of the cell from xoffset 1832 int nTmpIndex = mpLogClusters[c]; 1833 while( (--c >= pVI->mnMinCharPos) 1834 && (nTmpIndex == mpLogClusters[c]) ) 1835 nXOffset -= mpCharWidths[c]; 1836 } 1837 else // RTL-case 1838 { 1839 // RTL case: add the remainder of the cell from xoffset 1840 int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ]; 1841 while( (--c >= pVI->mnMinCharPos) 1842 && (nTmpIndex == mpLogClusters[c]) ) 1843 nXOffset += mpCharWidths[c]; 1844 1845 // adjust the xoffset if justified glyphs are not positioned at their justified positions yet 1846 if( mpJustifications && !bManualCellAlign ) 1847 nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ]; 1848 } 1849 1850 // create mpGlyphs2Chars[] if it is needed later 1851 if( pCharPosAry && !mpGlyphs2Chars ) 1852 { 1853 // create and reset the new array 1854 mpGlyphs2Chars = new int[ mnGlyphCapacity ]; 1855 for( int i = 0; i < mnGlyphCount; ++i ) 1856 mpGlyphs2Chars[i] = -1; 1857 // calculate the char->glyph mapping 1858 for( nItem = 0; nItem < mnItemCount; ++nItem ) 1859 { 1860 // ignore invisible visual items 1861 const VisualItem& rVI = mpVisualItems[ nItem ]; 1862 if( rVI.IsEmpty() ) 1863 continue; 1864 // calculate the mapping by using mpLogClusters[] 1865 // mpGlyphs2Chars[] should obey the logical order 1866 // => reversing the loop does this by overwriting higher logicals 1867 for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; ) 1868 { 1869 int i = mpLogClusters[c] + rVI.mnMinGlyphPos; 1870 mpGlyphs2Chars[i] = c; 1871 } 1872 } 1873 } 1874 1875 // calculate the absolute position of the first result glyph in pixel units 1876 const GOFFSET aGOffset = mpGlyphOffsets[ nStart ]; 1877 Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv ); 1878 rPos = GetDrawPosition( aRelativePos ); 1879 1880 // fill the result arrays 1881 int nCount = 0; 1882 while( nCount < nLen ) 1883 { 1884 // prepare return values 1885 sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ]; 1886 int nGlyphWidth = pGlyphWidths[ nStart ]; 1887 int nCharPos = -1; // no need to determine charpos 1888 if( mpGlyphs2Chars ) // unless explicitly requested+provided 1889 nCharPos = mpGlyphs2Chars[ nStart ]; 1890 1891 // inject kashida glyphs if needed 1892 if( !mbDisableGlyphInjection 1893 && mpJustifications 1894 && mnMinKashidaWidth 1895 && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL ) 1896 { 1897 // prepare draw position adjustment 1898 int nExtraOfs = (nSubIter++) * mnMinKashidaWidth; 1899 // calculate space available for the injected glyphs 1900 nGlyphWidth = mpGlyphAdvances[ nStart ]; 1901 const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth; 1902 const int nToFillWidth = nExtraWidth - nExtraOfs; 1903 if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room 1904 || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others 1905 { 1906 // handle if there is not sufficient room for a full glyph 1907 if( nToFillWidth < mnMinKashidaWidth ) 1908 { 1909 // overlap it with the previously injected glyph if possible 1910 int nOverlap = mnMinKashidaWidth - nToFillWidth; 1911 // else overlap it with both neighboring glyphs 1912 if( nSubIter <= 1 ) 1913 nOverlap /= 2; 1914 nExtraOfs -= nOverlap; 1915 } 1916 nGlyphWidth = mnMinKashidaWidth; 1917 aGlyphId = mnMinKashidaGlyph; 1918 nCharPos = -1; 1919 } 1920 else 1921 { 1922 nExtraOfs += nToFillWidth; // at right of cell 1923 nSubIter = 0; // done with glyph injection 1924 } 1925 if( !bManualCellAlign ) 1926 nExtraOfs -= nExtraWidth; // adjust for right-aligned cells 1927 1928 // adjust the draw position for the injected-glyphs case 1929 if( nExtraOfs ) 1930 { 1931 aRelativePos.X() += nExtraOfs; 1932 rPos = GetDrawPosition( aRelativePos ); 1933 } 1934 } 1935 1936 // update return values 1937 *(pGlyphs++) = aGlyphId; 1938 if( pGlyphAdvances ) 1939 *(pGlyphAdvances++) = nGlyphWidth; 1940 if( pCharPosAry ) 1941 *(pCharPosAry++) = nCharPos; 1942 1943 // increment counter of returned glyphs 1944 ++nCount; 1945 1946 // reduce code complexity by returning early in glyph-injection case 1947 if( nSubIter != 0 ) 1948 break; 1949 1950 // stop after the last visible glyph in this visual item 1951 if( ++nStart >= nEndGlyphPos ) 1952 { 1953 nStart = nNextItemStart; 1954 break; 1955 } 1956 1957 // RTL-justified glyph positioning is not easy 1958 // simplify the code by just returning only one glyph at a time 1959 if( mpJustifications && pVI->IsRTL() ) 1960 break; 1961 1962 // stop when the x-position of the next glyph is unexpected 1963 if( !pGlyphAdvances ) 1964 if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) ) 1965 || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) ) 1966 break; 1967 1968 // stop when the y-position of the next glyph is unexpected 1969 if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) ) 1970 break; 1971 } 1972 1973 ++nStart; 1974 nStartx8 = (nStart << 8) + nSubIter; 1975 return nCount; 1976 } 1977 1978 // ----------------------------------------------------------------------- 1979 1980 void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos ) 1981 { 1982 DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" ); 1983 int nStart = nStartx8 >> 8; 1984 if( nStart > mnGlyphCount ) 1985 return; 1986 1987 VisualItem* pVI = mpVisualItems; 1988 int nMinGlyphPos = 0, nEndGlyphPos; 1989 if( nStart == 0 ) // nStart==0 for first visible glyph 1990 { 1991 for( int i = mnItemCount; --i >= 0; ++pVI ) 1992 if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) ) 1993 break; 1994 nStart = nMinGlyphPos; 1995 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" ); 1996 } 1997 else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1 1998 { 1999 --nStart; 2000 for( int i = mnItemCount; --i >= 0; ++pVI ) 2001 if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) ) 2002 break; 2003 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); 2004 (void)bRC; // avoid var-not-used warning 2005 DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" ); 2006 } 2007 2008 long nDelta = nNewXPos - pVI->mnXOffset; 2009 if( nStart > nMinGlyphPos ) 2010 { 2011 // move the glyph by expanding its left glyph but ignore dropped glyphs 2012 int i, nLastUndropped = nMinGlyphPos - 1; 2013 for( i = nMinGlyphPos; i < nStart; ++i ) 2014 { 2015 if (mpOutGlyphs[i] != DROPPED_OUTGLYPH) 2016 { 2017 nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ]; 2018 nLastUndropped = i; 2019 } 2020 } 2021 if (nLastUndropped >= nMinGlyphPos) 2022 { 2023 mpGlyphAdvances[ nLastUndropped ] += nDelta; 2024 if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta; 2025 } 2026 else 2027 { 2028 pVI->mnXOffset += nDelta; 2029 } 2030 } 2031 else 2032 { 2033 // move the visual item by having an offset 2034 pVI->mnXOffset += nDelta; 2035 } 2036 // move subsequent items - this often isn't necessary because subsequent 2037 // moves will correct subsequent items. However, if there is a contiguous 2038 // range not involving fallback which spans items, this will be needed 2039 while (++pVI - mpVisualItems < mnItemCount) 2040 { 2041 pVI->mnXOffset += nDelta; 2042 } 2043 } 2044 2045 // ----------------------------------------------------------------------- 2046 2047 void UniscribeLayout::DropGlyph( int nStartx8 ) 2048 { 2049 DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" ); 2050 int nStart = nStartx8 >> 8; 2051 DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" ); 2052 2053 if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1 2054 --nStart; 2055 else // nStart<=0 for first visible glyph 2056 { 2057 VisualItem* pVI = mpVisualItems; 2058 for( int i = mnItemCount, nDummy; --i >= 0; ++pVI ) 2059 if( GetItemSubrange( *pVI, nStart, nDummy ) ) 2060 break; 2061 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" ); 2062 int nOffset = 0; 2063 int j = pVI->mnMinGlyphPos; 2064 while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++; 2065 if (j == nStart) 2066 { 2067 pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]); 2068 } 2069 } 2070 2071 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH; 2072 } 2073 2074 // ----------------------------------------------------------------------- 2075 2076 void UniscribeLayout::Simplify( bool /*bIsBase*/ ) 2077 { 2078 static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH; 2079 int i; 2080 // if there are no dropped glyphs don't bother 2081 for( i = 0; i < mnGlyphCount; ++i ) 2082 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2083 break; 2084 if( i >= mnGlyphCount ) 2085 return; 2086 2087 // prepare for sparse layout 2088 // => make sure mpGlyphs2Chars[] exists 2089 if( !mpGlyphs2Chars ) 2090 { 2091 mpGlyphs2Chars = new int[ mnGlyphCapacity ]; 2092 for( i = 0; i < mnGlyphCount; ++i ) 2093 mpGlyphs2Chars[ i ] = -1; 2094 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2095 { 2096 // skip invisible items 2097 VisualItem& rVI = mpVisualItems[ nItem ]; 2098 if( rVI.IsEmpty() ) 2099 continue; 2100 for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; ) 2101 { 2102 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos; 2103 mpGlyphs2Chars[ j ] = i; 2104 } 2105 } 2106 } 2107 2108 // remove the dropped glyphs 2109 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 2110 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2111 { 2112 VisualItem& rVI = mpVisualItems[ nItem ]; 2113 if( rVI.IsEmpty() ) 2114 continue; 2115 2116 // mark replaced character widths 2117 for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i ) 2118 { 2119 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos; 2120 if( mpOutGlyphs[ j ] == cDroppedGlyph ) 2121 mpCharWidths[ i ] = 0; 2122 } 2123 2124 // handle dropped glyphs at start of visual item 2125 int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos; 2126 GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ); 2127 i = nMinGlyphPos; 2128 while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) ) 2129 { 2130 //rVI.mnXOffset += pGlyphWidths[ i ]; 2131 rVI.mnMinGlyphPos = ++i; 2132 } 2133 2134 // when all glyphs in item got dropped mark it as empty 2135 if( i >= nEndGlyphPos ) 2136 { 2137 rVI.mnEndGlyphPos = 0; 2138 continue; 2139 } 2140 // If there are still glyphs in the cluster and mnMinGlyphPos 2141 // has changed then we need to remove the dropped glyphs at start 2142 // to correct logClusters, which is unsigned and relative to the 2143 // item start. 2144 if (rVI.mnMinGlyphPos != nOrigMinGlyphPos) 2145 { 2146 // drop any glyphs in the visual item outside the range 2147 for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++) 2148 mpOutGlyphs[ i ] = cDroppedGlyph; 2149 rVI.mnMinGlyphPos = i = nOrigMinGlyphPos; 2150 } 2151 2152 // handle dropped glyphs in the middle of visual item 2153 for(; i < nEndGlyphPos; ++i ) 2154 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2155 break; 2156 int j = i; 2157 while( ++i < nEndGlyphPos ) 2158 { 2159 if( mpOutGlyphs[ i ] == cDroppedGlyph ) 2160 continue; 2161 mpOutGlyphs[ j ] = mpOutGlyphs[ i ]; 2162 mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ]; 2163 mpVisualAttrs[ j ] = mpVisualAttrs[ i ]; 2164 mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ]; 2165 if( mpJustifications ) 2166 mpJustifications[ j ] = mpJustifications[ i ]; 2167 const int k = mpGlyphs2Chars[ i ]; 2168 mpGlyphs2Chars[ j ] = k; 2169 const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos; 2170 if( k < 0) // extra glyphs are already mapped 2171 continue; 2172 mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos); 2173 } 2174 2175 rVI.mnEndGlyphPos = j; 2176 } 2177 } 2178 2179 // ----------------------------------------------------------------------- 2180 2181 void UniscribeLayout::DrawText( SalGraphics& ) const 2182 { 2183 HFONT hOrigFont = DisableFontScaling(); 2184 2185 int nBaseClusterOffset = 0; 2186 int nBaseGlyphPos = -1; 2187 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2188 { 2189 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2190 2191 // skip if there is nothing to display 2192 int nMinGlyphPos, nEndGlyphPos; 2193 if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) 2194 continue; 2195 2196 if( nBaseGlyphPos < 0 ) 2197 { 2198 // adjust draw position relative to cluster start 2199 if( rVisualItem.IsRTL() ) 2200 nBaseGlyphPos = nEndGlyphPos - 1; 2201 else 2202 nBaseGlyphPos = nMinGlyphPos; 2203 2204 const int* pGlyphWidths; 2205 if( mpJustifications ) 2206 pGlyphWidths = mpJustifications; 2207 else 2208 pGlyphWidths = mpGlyphAdvances; 2209 2210 int i = mnMinCharPos; 2211 while( (--i >= rVisualItem.mnMinCharPos) 2212 && (nBaseGlyphPos == mpLogClusters[i]) ) 2213 nBaseClusterOffset += mpCharWidths[i]; 2214 2215 if( !rVisualItem.IsRTL() ) 2216 nBaseClusterOffset = -nBaseClusterOffset; 2217 } 2218 2219 // now draw the matching glyphs in this item 2220 Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 ); 2221 Point aPos = GetDrawPosition( aRelPos ); 2222 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 2223 (*pScriptTextOut)( mhDC, &rScriptCache, 2224 aPos.X(), aPos.Y(), 0, NULL, 2225 &rVisualItem.mpScriptItem->a, NULL, 0, 2226 mpOutGlyphs + nMinGlyphPos, 2227 nEndGlyphPos - nMinGlyphPos, 2228 mpGlyphAdvances + nMinGlyphPos, 2229 mpJustifications ? mpJustifications + nMinGlyphPos : NULL, 2230 mpGlyphOffsets + nMinGlyphPos ); 2231 } 2232 2233 if( hOrigFont ) 2234 DeleteFont( SelectFont( mhDC, hOrigFont ) ); 2235 } 2236 2237 // ----------------------------------------------------------------------- 2238 2239 long UniscribeLayout::FillDXArray( long* pDXArray ) const 2240 { 2241 // calculate width of the complete layout 2242 long nWidth = mnBaseAdv; 2243 for( int nItem = mnItemCount; --nItem >= 0; ) 2244 { 2245 const VisualItem& rVI = mpVisualItems[ nItem ]; 2246 2247 // skip if there is nothing to display 2248 int nMinGlyphPos, nEndGlyphPos; 2249 if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) ) 2250 continue; 2251 2252 // width = xoffset + width of last item 2253 nWidth = rVI.mnXOffset; 2254 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances; 2255 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2256 nWidth += pGlyphWidths[i]; 2257 break; 2258 } 2259 2260 // copy the virtual char widths into pDXArray[] 2261 if( pDXArray ) 2262 for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 2263 pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ]; 2264 2265 return nWidth; 2266 } 2267 2268 // ----------------------------------------------------------------------- 2269 2270 int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2271 { 2272 long nWidth = 0; 2273 for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) 2274 { 2275 nWidth += mpCharWidths[ i ] * nFactor; 2276 2277 // check if the nMaxWidth still fits the current sub-layout 2278 if( nWidth >= nMaxWidth ) 2279 { 2280 // go back to cluster start 2281 // we have to find the visual item first since the mpLogClusters[] 2282 // needed to find the cluster start is relative to to the visual item 2283 int nMinGlyphIndex = 0; 2284 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2285 { 2286 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2287 nMinGlyphIndex = rVisualItem.mnMinGlyphPos; 2288 if( (i >= rVisualItem.mnMinCharPos) 2289 && (i < rVisualItem.mnEndCharPos) ) 2290 break; 2291 } 2292 // now go back to the matching cluster start 2293 do 2294 { 2295 int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex; 2296 if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart ) 2297 return i; 2298 } while( --i >= mnMinCharPos ); 2299 2300 // if the cluster starts before the start of the visual item 2301 // then set the visual breakpoint before this item 2302 return mnMinCharPos; 2303 } 2304 2305 // the visual break also depends on the nCharExtra between the characters 2306 nWidth += nCharExtra; 2307 } 2308 2309 // the whole layout did fit inside the nMaxWidth 2310 return STRING_LEN; 2311 } 2312 2313 // ----------------------------------------------------------------------- 2314 2315 void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const 2316 { 2317 int i; 2318 for( i = 0; i < nMaxIdx; ++i ) 2319 pCaretXArray[ i ] = -1; 2320 long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) ); 2321 for( i = 0; i <= mnGlyphCount; ++i ) 2322 pGlyphPos[ i ] = -1; 2323 2324 long nXPos = 0; 2325 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2326 { 2327 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2328 if( rVisualItem.IsEmpty() ) 2329 continue; 2330 2331 if (mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK) 2332 { 2333 nXPos = rVisualItem.mnXOffset; 2334 } 2335 // get glyph positions 2336 // TODO: handle when rVisualItem's glyph range is only partially used 2337 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2338 { 2339 pGlyphPos[ i ] = nXPos; 2340 nXPos += mpGlyphAdvances[ i ]; 2341 } 2342 // rightmost position of this visualitem 2343 pGlyphPos[ i ] = nXPos; 2344 2345 // convert glyph positions to character positions 2346 i = rVisualItem.mnMinCharPos; 2347 if( i < mnMinCharPos ) 2348 i = mnMinCharPos; 2349 for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i ) 2350 { 2351 int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos; 2352 int nCurrIdx = i * 2; 2353 if( !rVisualItem.IsRTL() ) 2354 { 2355 // normal positions for LTR case 2356 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ]; 2357 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ]; 2358 } 2359 else 2360 { 2361 // reverse positions for RTL case 2362 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ]; 2363 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ]; 2364 } 2365 } 2366 } 2367 2368 if (!(mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK)) 2369 { 2370 nXPos = 0; 2371 // fixup unknown character positions to neighbor 2372 for( i = 0; i < nMaxIdx; ++i ) 2373 { 2374 if( pCaretXArray[ i ] >= 0 ) 2375 nXPos = pCaretXArray[ i ]; 2376 else 2377 pCaretXArray[ i ] = nXPos; 2378 } 2379 } 2380 } 2381 2382 // ----------------------------------------------------------------------- 2383 2384 void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 2385 { 2386 SalLayout::AdjustLayout( rArgs ); 2387 2388 // adjust positions if requested 2389 if( rArgs.mpDXArray ) 2390 ApplyDXArray( rArgs ); 2391 else if( rArgs.mnLayoutWidth ) 2392 Justify( rArgs.mnLayoutWidth ); 2393 } 2394 2395 // ----------------------------------------------------------------------- 2396 2397 void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) 2398 { 2399 const long* pDXArray = rArgs.mpDXArray; 2400 2401 // increase char widths in string range to desired values 2402 bool bModified = false; 2403 int nOldWidth = 0; 2404 DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" ); 2405 int i,j; 2406 for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j ) 2407 { 2408 int nNewCharWidth = (pDXArray[j] - nOldWidth); 2409 // TODO: nNewCharWidth *= mnUnitsPerPixel; 2410 if( mpCharWidths[i] != nNewCharWidth ) 2411 { 2412 mpCharWidths[i] = nNewCharWidth; 2413 bModified = true; 2414 } 2415 nOldWidth = pDXArray[j]; 2416 } 2417 2418 if( !bModified ) 2419 return; 2420 2421 // initialize justifications array 2422 mpJustifications = new int[ mnGlyphCapacity ]; 2423 for( i = 0; i < mnGlyphCount; ++i ) 2424 mpJustifications[ i ] = mpGlyphAdvances[ i ]; 2425 2426 // apply new widths to script items 2427 long nXOffset = 0; 2428 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2429 { 2430 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2431 2432 // set the position of this visual item 2433 rVisualItem.mnXOffset = nXOffset; 2434 2435 // ignore empty visual items 2436 if( rVisualItem.IsEmpty() ) 2437 { 2438 for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++) 2439 nXOffset += mpCharWidths[i]; 2440 continue; 2441 } 2442 // ignore irrelevant visual items 2443 if( (rVisualItem.mnMinCharPos >= mnEndCharPos) 2444 || (rVisualItem.mnEndCharPos <= mnMinCharPos) ) 2445 continue; 2446 2447 // if needed prepare special handling for arabic justification 2448 rVisualItem.mbHasKashidas = false; 2449 if( rVisualItem.IsRTL() ) 2450 { 2451 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2452 if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification 2453 { // excluding SCRIPT_JUSTIFY_NONE 2454 // yes 2455 rVisualItem.mbHasKashidas = true; 2456 // so prepare for kashida handling 2457 InitKashidaHandling(); 2458 break; 2459 } 2460 2461 if( rVisualItem.HasKashidas() ) 2462 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) 2463 { 2464 // TODO: check if we still need this hack after correction of kashida placing? 2465 // (i87688): apparently yes, we still need it! 2466 if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE ) 2467 // usp decided that justification can't be applied here 2468 // but maybe our Kashida algorithm thinks differently. 2469 // To avoid trouble (gaps within words, last character of 2470 // a word gets a Kashida appended) override this. 2471 2472 // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE 2473 // just because this previous hack (which I haven't understand, sorry) used 2474 // the same value to replace. Don't know if this is really the best 2475 // thing to do, but it seems to fix things 2476 mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA; 2477 } 2478 } 2479 2480 // convert virtual charwidths to glyph justification values 2481 HRESULT nRC = (*pScriptApplyLogicalWidth)( 2482 mpCharWidths + rVisualItem.mnMinCharPos, 2483 rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos, 2484 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, 2485 mpLogClusters + rVisualItem.mnMinCharPos, 2486 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 2487 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 2488 &rVisualItem.mpScriptItem->a, 2489 &rVisualItem.maABCWidths, 2490 mpJustifications + rVisualItem.mnMinGlyphPos ); 2491 2492 if( nRC != 0 ) 2493 { 2494 delete[] mpJustifications; 2495 mpJustifications = NULL; 2496 break; 2497 } 2498 2499 // to prepare for the next visual item 2500 // update nXOffset to the next items position 2501 // before the mpJustifications[] array gets modified 2502 int nMinGlyphPos, nEndGlyphPos; 2503 if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) 2504 { 2505 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2506 nXOffset += mpJustifications[ i ]; 2507 2508 if( rVisualItem.mbHasKashidas ) 2509 KashidaItemFix( nMinGlyphPos, nEndGlyphPos ); 2510 } 2511 2512 // workaround needed for older USP versions: 2513 // right align the justification-adjusted glyphs in their cells for RTL-items 2514 // unless the right alignment is done by inserting kashidas 2515 if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() ) 2516 { 2517 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2518 { 2519 const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i]; 2520 // #i99862# skip diacritics, we mustn't add extra justification to diacritics 2521 int nIdxAdd = i - 1; 2522 while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] ) 2523 --nIdxAdd; 2524 if( nIdxAdd < nMinGlyphPos ) 2525 rVisualItem.mnXOffset += nXOffsetAdjust; 2526 else 2527 mpJustifications[nIdxAdd] += nXOffsetAdjust; 2528 mpJustifications[i] -= nXOffsetAdjust; 2529 } 2530 } 2531 } 2532 } 2533 2534 // ----------------------------------------------------------------------- 2535 2536 void UniscribeLayout::InitKashidaHandling() 2537 { 2538 if( mnMinKashidaGlyph != 0 ) // already initialized 2539 return; 2540 2541 mrWinFontEntry.InitKashidaHandling( mhDC ); 2542 mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth()); 2543 mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph(); 2544 } 2545 2546 // adjust the kashida placement matching to the WriterEngine 2547 void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos ) 2548 { 2549 // workaround needed for all known USP versions: 2550 // ApplyLogicalWidth does not match ScriptJustify behaviour 2551 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) 2552 { 2553 // check for vowels 2554 if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ]) 2555 && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types 2556 { // including SCRIPT_JUSTIFY_NONE 2557 // vowel, we do it like ScriptJustify does 2558 // the vowel gets the extra width 2559 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2560 mpJustifications [ i ] = mpGlyphAdvances [ i ]; 2561 mpJustifications [ i - 1 ] += nSpaceAdded; 2562 } 2563 } 2564 2565 // redistribute the widths for kashidas 2566 for( int i = nMinGlyphPos; i < nEndGlyphPos; ) 2567 KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i ); 2568 } 2569 2570 bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos ) 2571 { 2572 // doing pixel work within a word. 2573 // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth 2574 2575 // find the next kashida 2576 int nMinPos = *pnCurrentPos; 2577 int nMaxPos = *pnCurrentPos; 2578 for( int i = nMaxPos; i < nEndGlyphPos; ++i ) 2579 { 2580 if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK) 2581 && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) ) 2582 break; 2583 nMaxPos = i; 2584 } 2585 *pnCurrentPos = nMaxPos + 1; 2586 if( nMinPos == nMaxPos ) 2587 return false; 2588 2589 // calculate the available space for an extra kashida 2590 long nMaxAdded = 0; 2591 int nKashPos = -1; 2592 for( int i = nMaxPos; i >= nMinPos; --i ) 2593 { 2594 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2595 if( nSpaceAdded > nMaxAdded ) 2596 { 2597 nKashPos = i; 2598 nMaxAdded = nSpaceAdded; 2599 } 2600 } 2601 2602 // return early if there is no need for an extra kashida 2603 if ( nMaxAdded <= 0 ) 2604 return false; 2605 // return early if there is not enough space for an extra kashida 2606 if( 2*nMaxAdded < mnMinKashidaWidth ) 2607 return false; 2608 2609 // redistribute the extra spacing to the kashida position 2610 for( int i = nMinPos; i <= nMaxPos; ++i ) 2611 { 2612 if( i == nKashPos ) 2613 continue; 2614 // everything else should not have extra spacing 2615 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; 2616 if( nSpaceAdded > 0 ) 2617 { 2618 mpJustifications[ i ] -= nSpaceAdded; 2619 mpJustifications[ nKashPos ] += nSpaceAdded; 2620 } 2621 } 2622 2623 // check if we fulfill minimal kashida width 2624 long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ]; 2625 if( nSpaceAdded < mnMinKashidaWidth ) 2626 { 2627 // ugly: steal some pixels 2628 long nSteal = 1; 2629 if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos))) 2630 nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos); 2631 for( int i = nMinPos; i <= nMaxPos; ++i ) 2632 { 2633 if( i == nKashPos ) 2634 continue; 2635 nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal ); 2636 if ( nSteal > 0 ) 2637 { 2638 mpJustifications [ i ] -= nSteal; 2639 mpJustifications [ nKashPos ] += nSteal; 2640 nSpaceAdded += nSteal; 2641 } 2642 if( nSpaceAdded >= mnMinKashidaWidth ) 2643 return true; 2644 } 2645 } 2646 2647 // blank padding 2648 long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded; 2649 if( nSpaceMissing > 0 ) 2650 { 2651 // inner glyph: distribute extra space evenly 2652 if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) ) 2653 { 2654 mpJustifications [ nKashPos ] += nSpaceMissing; 2655 long nHalfSpace = nSpaceMissing / 2; 2656 mpJustifications [ nMinPos - 1 ] -= nHalfSpace; 2657 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace; 2658 } 2659 // rightmost: left glyph gets extra space 2660 else if( nMinPos > nMinGlyphPos ) 2661 { 2662 mpJustifications [ nMinPos - 1 ] -= nSpaceMissing; 2663 mpJustifications [ nKashPos ] += nSpaceMissing; 2664 } 2665 // leftmost: right glyph gets extra space 2666 else if( nMaxPos < nEndGlyphPos - 1 ) 2667 { 2668 mpJustifications [ nKashPos ] += nSpaceMissing; 2669 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing; 2670 } 2671 else 2672 return false; 2673 } 2674 2675 return true; 2676 } 2677 2678 // ----------------------------------------------------------------------- 2679 2680 void UniscribeLayout::Justify( long nNewWidth ) 2681 { 2682 long nOldWidth = 0; 2683 int i; 2684 for( i = mnMinCharPos; i < mnEndCharPos; ++i ) 2685 nOldWidth += mpCharWidths[ i ]; 2686 if( nOldWidth <= 0 ) 2687 return; 2688 2689 nNewWidth *= mnUnitsPerPixel; // convert into font units 2690 if( nNewWidth == nOldWidth ) 2691 return; 2692 // prepare to distribute the extra width evenly among the visual items 2693 const double fStretch = (double)nNewWidth / nOldWidth; 2694 2695 // initialize justifications array 2696 mpJustifications = new int[ mnGlyphCapacity ]; 2697 for( i = 0; i < mnGlyphCapacity; ++i ) 2698 mpJustifications[ i ] = mpGlyphAdvances[ i ]; 2699 2700 // justify stretched script items 2701 long nXOffset = 0; 2702 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 2703 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2704 { 2705 VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2706 if( rVisualItem.IsEmpty() ) 2707 continue; 2708 2709 if( (rVisualItem.mnMinCharPos < mnEndCharPos) 2710 && (rVisualItem.mnEndCharPos > mnMinCharPos) ) 2711 { 2712 long nItemWidth = 0; 2713 for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i ) 2714 nItemWidth += mpCharWidths[ i ]; 2715 nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5); 2716 2717 HRESULT nRC = (*pScriptJustify) ( 2718 mpVisualAttrs + rVisualItem.mnMinGlyphPos, 2719 mpGlyphAdvances + rVisualItem.mnMinGlyphPos, 2720 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, 2721 nItemWidth, 2722 mnMinKashidaWidth, 2723 mpJustifications + rVisualItem.mnMinGlyphPos ); 2724 2725 rVisualItem.mnXOffset = nXOffset; 2726 nXOffset += nItemWidth; 2727 } 2728 } 2729 } 2730 2731 // ----------------------------------------------------------------------- 2732 2733 bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const 2734 { 2735 // we have to find the visual item first since the mpLogClusters[] 2736 // needed to find the cluster start is relative to to the visual item 2737 int nMinGlyphIndex = -1; 2738 for( int nItem = 0; nItem < mnItemCount; ++nItem ) 2739 { 2740 const VisualItem& rVisualItem = mpVisualItems[ nItem ]; 2741 if( (nCharPos >= rVisualItem.mnMinCharPos) 2742 && (nCharPos < rVisualItem.mnEndCharPos) ) 2743 { 2744 nMinGlyphIndex = rVisualItem.mnMinGlyphPos; 2745 break; 2746 } 2747 } 2748 // Invalid char pos or leftmost glyph in visual item 2749 if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] ) 2750 return false; 2751 2752 // This test didn't give the expected results 2753 /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ]) 2754 // two chars, one glyph 2755 return false;*/ 2756 2757 const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex; 2758 if( nGlyphPos <= 0 ) 2759 return true; 2760 // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE 2761 // and not SCRIPT_JUSTIFY_ARABIC_BLANK 2762 // special case: glyph to the left is vowel (no advance width) 2763 if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK 2764 || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE 2765 && mpGlyphAdvances [ nGlyphPos-1 ] )) 2766 return false; 2767 return true; 2768 } 2769 2770 #endif // USE_UNISCRIBE 2771 2772 #ifdef ENABLE_GRAPHITE 2773 2774 class GraphiteLayoutWinImpl : public GraphiteLayout 2775 { 2776 public: 2777 GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont) 2778 throw() 2779 : GraphiteLayout(font), mrFont(rFont) {}; 2780 virtual ~GraphiteLayoutWinImpl() throw() {}; 2781 virtual sal_GlyphId getKashidaGlyph(int & rWidth); 2782 private: 2783 ImplWinFontEntry & mrFont; 2784 }; 2785 2786 sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth) 2787 { 2788 rWidth = mrFont.GetMinKashidaWidth(); 2789 return mrFont.GetMinKashidaGlyph(); 2790 } 2791 2792 // This class uses the SIL Graphite engine to provide complex text layout services to the VCL 2793 // @author tse 2794 // 2795 class GraphiteWinLayout : public WinLayout 2796 { 2797 private: 2798 mutable GraphiteWinFont mpFont; 2799 grutils::GrFeatureParser * mpFeatures; 2800 mutable GraphiteLayoutWinImpl maImpl; 2801 public: 2802 GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw(); 2803 2804 static bool IsGraphiteEnabledFont(HDC hDC) throw(); 2805 2806 // used by upper layers 2807 virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout 2808 virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc. 2809 // virtual void InitFont() const; 2810 virtual void DrawText( SalGraphics& ) const; 2811 2812 // methods using string indexing 2813 virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const; 2814 virtual long FillDXArray( long* pDXArray ) const; 2815 2816 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; 2817 2818 // methods using glyph indexing 2819 virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&, 2820 long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const; 2821 2822 // used by glyph+font+script fallback 2823 virtual void MoveGlyph( int nStart, long nNewXPos ); 2824 virtual void DropGlyph( int nStart ); 2825 virtual void Simplify( bool bIsBase ); 2826 ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; }; 2827 protected: 2828 virtual void ReplaceDC(gr::Segment & segment) const; 2829 virtual void RestoreDC(gr::Segment & segment) const; 2830 }; 2831 2832 bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw() 2833 { 2834 return gr::WinFont::FontHasGraphiteTables(hDC); 2835 } 2836 2837 GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw() 2838 : WinLayout(hDC, rWFD, rWFE), mpFont(hDC), 2839 maImpl(mpFont, rWFE) 2840 { 2841 const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage ); 2842 rtl::OString name = rtl::OUStringToOString( 2843 rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 2844 sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1; 2845 if (nFeat > 0) 2846 { 2847 rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat); 2848 mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr()); 2849 } 2850 else 2851 { 2852 mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr()); 2853 } 2854 maImpl.SetFeatures(mpFeatures); 2855 } 2856 2857 void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const 2858 { 2859 COLORREF color = GetTextColor(mhDC); 2860 dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC); 2861 SetTextColor(mhDC, color); 2862 } 2863 2864 void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const 2865 { 2866 dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC(); 2867 } 2868 2869 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args) 2870 { 2871 if (args.mnMinCharPos >= args.mnEndCharPos) 2872 { 2873 maImpl.clear(); 2874 return true; 2875 } 2876 HFONT hUnRotatedFont; 2877 if (args.mnOrientation) 2878 { 2879 // Graphite gets very confused if the font is rotated 2880 LOGFONTW aLogFont; 2881 ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont); 2882 aLogFont.lfEscapement = 0; 2883 aLogFont.lfOrientation = 0; 2884 hUnRotatedFont = ::CreateFontIndirectW( &aLogFont); 2885 ::SelectFont(mhDC, hUnRotatedFont); 2886 } 2887 WinLayout::AdjustLayout(args); 2888 mpFont.replaceDC(mhDC); 2889 maImpl.SetFontScale(WinLayout::mfFontScale); 2890 //bool succeeded = maImpl.LayoutText(args); 2891 #ifdef GRCACHE 2892 GrSegRecord * pSegRecord = NULL; 2893 gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord); 2894 #else 2895 gr::Segment * pSegment = maImpl.CreateSegment(args); 2896 #endif 2897 bool bSucceeded = false; 2898 if (pSegment) 2899 { 2900 // replace the DC on the font within the segment 2901 ReplaceDC(*pSegment); 2902 // create glyph vectors 2903 #ifdef GRCACHE 2904 bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord); 2905 #else 2906 bSucceeded = maImpl.LayoutGlyphs(args, pSegment); 2907 #endif 2908 // restore original DC 2909 RestoreDC(*pSegment); 2910 #ifdef GRCACHE 2911 if (pSegRecord) pSegRecord->unlock(); 2912 else delete pSegment; 2913 #else 2914 delete pSegment; 2915 #endif 2916 } 2917 mpFont.restoreDC(); 2918 if (args.mnOrientation) 2919 { 2920 // restore the rotated font 2921 ::SelectFont(mhDC, mhFont); 2922 ::DeleteObject(hUnRotatedFont); 2923 } 2924 return bSucceeded; 2925 } 2926 2927 void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs) 2928 { 2929 WinLayout::AdjustLayout(rArgs); 2930 maImpl.DrawBase() = WinLayout::maDrawBase; 2931 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2932 if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray) 2933 { 2934 mrWinFontEntry.InitKashidaHandling(mhDC); 2935 } 2936 maImpl.AdjustLayout(rArgs); 2937 } 2938 2939 void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const 2940 { 2941 HFONT hOrigFont = DisableFontScaling(); 2942 HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).mhDC; 2943 maImpl.DrawBase() = WinLayout::maDrawBase; 2944 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2945 const int MAX_GLYPHS = 2; 2946 sal_GlyphId glyphIntStr[MAX_GLYPHS]; 2947 WORD glyphWStr[MAX_GLYPHS]; 2948 int glyphIndex = 0; 2949 Point aPos(0,0); 2950 int nGlyphs = 0; 2951 do 2952 { 2953 nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex); 2954 if (nGlyphs < 1) 2955 break; 2956 std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr); 2957 ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, 2958 NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL); 2959 } while (nGlyphs); 2960 if( hOrigFont ) 2961 DeleteFont( SelectFont( mhDC, hOrigFont ) ); 2962 } 2963 2964 int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 2965 { 2966 mpFont.replaceDC(mhDC); 2967 int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor); 2968 mpFont.restoreDC(); 2969 return nBreak; 2970 } 2971 2972 long GraphiteWinLayout::FillDXArray( long* pDXArray ) const 2973 { 2974 return maImpl.FillDXArray(pDXArray); 2975 } 2976 2977 void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const 2978 { 2979 maImpl.GetCaretPositions(nArraySize, pCaretXArray); 2980 } 2981 2982 int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out, 2983 ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const 2984 { 2985 maImpl.DrawBase() = WinLayout::maDrawBase; 2986 maImpl.DrawOffset() = WinLayout::maDrawOffset; 2987 return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index); 2988 } 2989 2990 void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos ) 2991 { 2992 maImpl.MoveGlyph(glyph_idx, new_x_pos); 2993 } 2994 2995 void GraphiteWinLayout::DropGlyph( int glyph_idx ) 2996 { 2997 maImpl.DropGlyph(glyph_idx); 2998 } 2999 3000 void GraphiteWinLayout::Simplify( bool is_base ) 3001 { 3002 maImpl.Simplify(is_base); 3003 } 3004 #endif // ENABLE_GRAPHITE 3005 // ======================================================================= 3006 3007 SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) 3008 { 3009 DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL"); 3010 3011 WinLayout* pWinLayout = NULL; 3012 3013 const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ]; 3014 ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ]; 3015 3016 #if defined( USE_UNISCRIBE ) 3017 if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) 3018 && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine 3019 { 3020 #ifdef ENABLE_GRAPHITE 3021 if (rFontFace.SupportsGraphite()) 3022 pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance); 3023 else 3024 #endif // ENABLE_GRAPHITE 3025 // script complexity is determined in upper layers 3026 pWinLayout = new UniscribeLayout( mhDC, rFontFace, rFontInstance ); 3027 // NOTE: it must be guaranteed that the WinSalGraphics lives longer than 3028 // the created UniscribeLayout, otherwise the data passed into the 3029 // constructor might become invalid too early 3030 } 3031 else 3032 #endif // USE_UNISCRIBE 3033 { 3034 #ifdef GCP_KERN_HACK 3035 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() ) 3036 { 3037 // TODO: directly cache kerning info in the rFontInstance 3038 // TODO: get rid of kerning methods+data in WinSalGraphics object 3039 GetKernPairs( 0, NULL ); 3040 rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs ); 3041 } 3042 #endif // GCP_KERN_HACK 3043 3044 BYTE eCharSet = ANSI_CHARSET; 3045 if( mpLogFont ) 3046 eCharSet = mpLogFont->lfCharSet; 3047 #ifdef ENABLE_GRAPHITE 3048 if (rFontFace.SupportsGraphite()) 3049 pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance); 3050 else 3051 #endif // ENABLE_GRAPHITE 3052 pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance ); 3053 } 3054 3055 if( mfFontScale != 1.0 ) 3056 pWinLayout->SetFontScale( mfFontScale ); 3057 3058 return pWinLayout; 3059 } 3060 3061 // ----------------------------------------------------------------------- 3062 3063 int WinSalGraphics::GetMinKashidaWidth() 3064 { 3065 if( !mpWinFontEntry[0] ) 3066 return 0; 3067 mpWinFontEntry[0]->InitKashidaHandling( mhDC ); 3068 int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth()); 3069 return nMinKashida; 3070 } 3071 3072 // ======================================================================= 3073 3074 ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD ) 3075 : ImplFontEntry( rFSD ) 3076 , maWidthMap( 512 ) 3077 , mpKerningPairs( NULL ) 3078 , mnKerningPairs( -1 ) 3079 , mnMinKashidaWidth( -1 ) 3080 , mnMinKashidaGlyph( -1 ) 3081 { 3082 #ifdef USE_UNISCRIBE 3083 maScriptCache = NULL; 3084 #endif // USE_UNISCRIBE 3085 } 3086 3087 // ----------------------------------------------------------------------- 3088 3089 ImplWinFontEntry::~ImplWinFontEntry() 3090 { 3091 #ifdef USE_UNISCRIBE 3092 if( maScriptCache != NULL ) 3093 (*pScriptFreeCache)( &maScriptCache ); 3094 #endif // USE_UNISCRIBE 3095 #ifdef GCP_KERN_HACK 3096 delete[] mpKerningPairs; 3097 #endif // GCP_KERN_HACK 3098 } 3099 3100 // ----------------------------------------------------------------------- 3101 3102 bool ImplWinFontEntry::HasKernData() const 3103 { 3104 return (mnKerningPairs >= 0); 3105 } 3106 3107 // ----------------------------------------------------------------------- 3108 3109 void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData ) 3110 { 3111 mnKerningPairs = nPairCount; 3112 mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ]; 3113 ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) ); 3114 } 3115 3116 // ----------------------------------------------------------------------- 3117 3118 int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const 3119 { 3120 int nKernAmount = 0; 3121 if( mpKerningPairs ) 3122 { 3123 const KERNINGPAIR aRefPair = { cLeft, cRight, 0 }; 3124 const KERNINGPAIR* pFirstPair = mpKerningPairs; 3125 const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs; 3126 const KERNINGPAIR* pPair = std::lower_bound( pFirstPair, 3127 pEndPair, aRefPair, ImplCmpKernData ); 3128 if( (pPair != pEndPair) 3129 && (pPair->wFirst == aRefPair.wFirst) 3130 && (pPair->wSecond == aRefPair.wSecond) ) 3131 nKernAmount = pPair->iKernAmount; 3132 } 3133 3134 return nKernAmount; 3135 } 3136 3137 // ----------------------------------------------------------------------- 3138 3139 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC ) 3140 { 3141 if( mnMinKashidaWidth >= 0 ) // already cached? 3142 return mnMinKashidaWidth; 3143 3144 // initialize the kashida width 3145 mnMinKashidaWidth = 0; 3146 mnMinKashidaGlyph = 0; 3147 #ifdef USE_UNISCRIBE 3148 if (aUspModule || (bUspEnabled && InitUSP())) 3149 { 3150 SCRIPT_FONTPROPERTIES aFontProperties; 3151 aFontProperties.cBytes = sizeof (aFontProperties); 3152 SCRIPT_CACHE& rScriptCache = GetScriptCache(); 3153 HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties ); 3154 if( nRC != 0 ) 3155 return false; 3156 mnMinKashidaWidth = aFontProperties.iKashidaWidth; 3157 mnMinKashidaGlyph = aFontProperties.wgKashida; 3158 } 3159 #endif // USE_UNISCRIBE 3160 3161 return true; 3162 } 3163 3164 // ======================================================================= 3165 3166 ImplFontData* ImplWinFontData::Clone() const 3167 { 3168 if( mpUnicodeMap ) 3169 mpUnicodeMap->AddReference(); 3170 ImplFontData* pClone = new ImplWinFontData( *this ); 3171 return pClone; 3172 } 3173 3174 // ----------------------------------------------------------------------- 3175 3176 ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const 3177 { 3178 ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD ); 3179 return pEntry; 3180 } 3181 3182 // ======================================================================= 3183