1 /* 2 * Copyright 2015-2018 Ebrahim Byagowi 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 */ 24 25 #include "hb.hh" 26 #include "hb-shaper-impl.hh" 27 28 #include <DWrite_1.h> 29 30 #include "hb-directwrite.h" 31 32 33 /* 34 * hb-directwrite uses new/delete syntatically but as we let users 35 * to override malloc/free, we will redefine new/delete so users 36 * won't need to do that by their own. 37 */ 38 void* operator new (size_t size) { return malloc (size); } 39 void* operator new [] (size_t size) { return malloc (size); } 40 void operator delete (void* pointer) { free (pointer); } 41 void operator delete [] (void* pointer) { free (pointer); } 42 43 44 /* 45 * DirectWrite font stream helpers 46 */ 47 48 // This is a font loader which provides only one font (unlike its original design). 49 // For a better implementation which was also source of this 50 // and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla 51 class DWriteFontFileLoader : public IDWriteFontFileLoader 52 { 53 private: 54 IDWriteFontFileStream *mFontFileStream; 55 public: 56 DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) 57 { mFontFileStream = fontFileStream; } 58 59 // IUnknown interface 60 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) 61 { return S_OK; } 62 IFACEMETHOD_ (ULONG, AddRef) () { return 1; } 63 IFACEMETHOD_ (ULONG, Release) () { return 1; } 64 65 // IDWriteFontFileLoader methods 66 virtual HRESULT STDMETHODCALLTYPE 67 CreateStreamFromKey (void const* fontFileReferenceKey, 68 uint32_t fontFileReferenceKeySize, 69 OUT IDWriteFontFileStream** fontFileStream) 70 { 71 *fontFileStream = mFontFileStream; 72 return S_OK; 73 } 74 }; 75 76 class DWriteFontFileStream : public IDWriteFontFileStream 77 { 78 private: 79 uint8_t *mData; 80 uint32_t mSize; 81 public: 82 DWriteFontFileStream (uint8_t *aData, uint32_t aSize) 83 { 84 mData = aData; 85 mSize = aSize; 86 } 87 88 // IUnknown interface 89 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) 90 { return S_OK; } 91 IFACEMETHOD_ (ULONG, AddRef) () { return 1; } 92 IFACEMETHOD_ (ULONG, Release) () { return 1; } 93 94 // IDWriteFontFileStream methods 95 virtual HRESULT STDMETHODCALLTYPE 96 ReadFileFragment (void const** fragmentStart, 97 UINT64 fileOffset, 98 UINT64 fragmentSize, 99 OUT void** fragmentContext) 100 { 101 // We are required to do bounds checking. 102 if (fileOffset + fragmentSize > mSize) return E_FAIL; 103 104 // truncate the 64 bit fileOffset to size_t sized index into mData 105 size_t index = static_cast<size_t> (fileOffset); 106 107 // We should be alive for the duration of this. 108 *fragmentStart = &mData[index]; 109 *fragmentContext = nullptr; 110 return S_OK; 111 } 112 113 virtual void STDMETHODCALLTYPE 114 ReleaseFileFragment (void* fragmentContext) {} 115 116 virtual HRESULT STDMETHODCALLTYPE 117 GetFileSize (OUT UINT64* fileSize) 118 { 119 *fileSize = mSize; 120 return S_OK; 121 } 122 123 virtual HRESULT STDMETHODCALLTYPE 124 GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; } 125 }; 126 127 128 /* 129 * shaper face data 130 */ 131 132 struct hb_directwrite_face_data_t 133 { 134 IDWriteFactory *dwriteFactory; 135 IDWriteFontFile *fontFile; 136 IDWriteFontFileStream *fontFileStream; 137 IDWriteFontFileLoader *fontFileLoader; 138 IDWriteFontFace *fontFace; 139 hb_blob_t *faceBlob; 140 }; 141 142 hb_directwrite_face_data_t * 143 _hb_directwrite_shaper_face_data_create (hb_face_t *face) 144 { 145 hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t; 146 if (unlikely (!data)) 147 return nullptr; 148 149 // TODO: factory and fontFileLoader should be cached separately 150 IDWriteFactory* dwriteFactory; 151 DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), 152 (IUnknown**) &dwriteFactory); 153 154 HRESULT hr; 155 hb_blob_t *blob = hb_face_reference_blob (face); 156 DWriteFontFileStream *fontFileStream; 157 fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr), 158 hb_blob_get_length (blob)); 159 160 DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); 161 dwriteFactory->RegisterFontFileLoader (fontFileLoader); 162 163 IDWriteFontFile *fontFile; 164 uint64_t fontFileKey = 0; 165 hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), 166 fontFileLoader, &fontFile); 167 168 #define FAIL(...) \ 169 HB_STMT_START { \ 170 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ 171 return nullptr; \ 172 } HB_STMT_END; 173 174 if (FAILED (hr)) 175 FAIL ("Failed to load font file from data!"); 176 177 BOOL isSupported; 178 DWRITE_FONT_FILE_TYPE fileType; 179 DWRITE_FONT_FACE_TYPE faceType; 180 uint32_t numberOfFaces; 181 hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); 182 if (FAILED (hr) || !isSupported) 183 FAIL ("Font file is not supported."); 184 185 #undef FAIL 186 187 IDWriteFontFace *fontFace; 188 dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0, 189 DWRITE_FONT_SIMULATIONS_NONE, &fontFace); 190 191 data->dwriteFactory = dwriteFactory; 192 data->fontFile = fontFile; 193 data->fontFileStream = fontFileStream; 194 data->fontFileLoader = fontFileLoader; 195 data->fontFace = fontFace; 196 data->faceBlob = blob; 197 198 return data; 199 } 200 201 void 202 _hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data) 203 { 204 if (data->fontFace) 205 data->fontFace->Release (); 206 if (data->fontFile) 207 data->fontFile->Release (); 208 if (data->dwriteFactory) 209 { 210 if (data->fontFileLoader) 211 data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader); 212 data->dwriteFactory->Release (); 213 } 214 if (data->fontFileLoader) 215 delete data->fontFileLoader; 216 if (data->fontFileStream) 217 delete data->fontFileStream; 218 if (data->faceBlob) 219 hb_blob_destroy (data->faceBlob); 220 if (data) 221 delete data; 222 } 223 224 225 /* 226 * shaper font data 227 */ 228 229 struct hb_directwrite_font_data_t {}; 230 231 hb_directwrite_font_data_t * 232 _hb_directwrite_shaper_font_data_create (hb_font_t *font) 233 { 234 hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t; 235 if (unlikely (!data)) 236 return nullptr; 237 238 return data; 239 } 240 241 void 242 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data) 243 { 244 delete data; 245 } 246 247 248 // Most of TextAnalysis is originally written by Bas Schouten for Mozilla project 249 // but now is relicensed to MIT for HarfBuzz use 250 class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink 251 { 252 public: 253 254 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) 255 { return S_OK; } 256 IFACEMETHOD_ (ULONG, AddRef) () { return 1; } 257 IFACEMETHOD_ (ULONG, Release) () { return 1; } 258 259 // A single contiguous run of characters containing the same analysis 260 // results. 261 struct Run 262 { 263 uint32_t mTextStart; // starting text position of this run 264 uint32_t mTextLength; // number of contiguous code units covered 265 uint32_t mGlyphStart; // starting glyph in the glyphs array 266 uint32_t mGlyphCount; // number of glyphs associated with this run 267 // text 268 DWRITE_SCRIPT_ANALYSIS mScript; 269 uint8_t mBidiLevel; 270 bool mIsSideways; 271 272 bool ContainsTextPosition (uint32_t aTextPosition) const 273 { 274 return aTextPosition >= mTextStart && 275 aTextPosition < mTextStart + mTextLength; 276 } 277 278 Run *nextRun; 279 }; 280 281 public: 282 TextAnalysis (const wchar_t* text, uint32_t textLength, 283 const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection) 284 : mText (text), mTextLength (textLength), mLocaleName (localeName), 285 mReadingDirection (readingDirection), mCurrentRun (nullptr) {} 286 ~TextAnalysis () 287 { 288 // delete runs, except mRunHead which is part of the TextAnalysis object 289 for (Run *run = mRunHead.nextRun; run;) 290 { 291 Run *origRun = run; 292 run = run->nextRun; 293 delete origRun; 294 } 295 } 296 297 STDMETHODIMP 298 GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead) 299 { 300 // Analyzes the text using the script analyzer and returns 301 // the result as a series of runs. 302 303 HRESULT hr = S_OK; 304 305 // Initially start out with one result that covers the entire range. 306 // This result will be subdivided by the analysis processes. 307 mRunHead.mTextStart = 0; 308 mRunHead.mTextLength = mTextLength; 309 mRunHead.mBidiLevel = 310 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); 311 mRunHead.nextRun = nullptr; 312 mCurrentRun = &mRunHead; 313 314 // Call each of the analyzers in sequence, recording their results. 315 if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) 316 *runHead = &mRunHead; 317 318 return hr; 319 } 320 321 // IDWriteTextAnalysisSource implementation 322 323 IFACEMETHODIMP 324 GetTextAtPosition (uint32_t textPosition, 325 OUT wchar_t const** textString, 326 OUT uint32_t* textLength) 327 { 328 if (textPosition >= mTextLength) 329 { 330 // No text at this position, valid query though. 331 *textString = nullptr; 332 *textLength = 0; 333 } 334 else 335 { 336 *textString = mText + textPosition; 337 *textLength = mTextLength - textPosition; 338 } 339 return S_OK; 340 } 341 342 IFACEMETHODIMP 343 GetTextBeforePosition (uint32_t textPosition, 344 OUT wchar_t const** textString, 345 OUT uint32_t* textLength) 346 { 347 if (textPosition == 0 || textPosition > mTextLength) 348 { 349 // Either there is no text before here (== 0), or this 350 // is an invalid position. The query is considered valid though. 351 *textString = nullptr; 352 *textLength = 0; 353 } 354 else 355 { 356 *textString = mText; 357 *textLength = textPosition; 358 } 359 return S_OK; 360 } 361 362 IFACEMETHODIMP_ (DWRITE_READING_DIRECTION) 363 GetParagraphReadingDirection () { return mReadingDirection; } 364 365 IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength, 366 wchar_t const** localeName) 367 { return S_OK; } 368 369 IFACEMETHODIMP 370 GetNumberSubstitution (uint32_t textPosition, 371 OUT uint32_t* textLength, 372 OUT IDWriteNumberSubstitution** numberSubstitution) 373 { 374 // We do not support number substitution. 375 *numberSubstitution = nullptr; 376 *textLength = mTextLength - textPosition; 377 378 return S_OK; 379 } 380 381 // IDWriteTextAnalysisSink implementation 382 383 IFACEMETHODIMP 384 SetScriptAnalysis (uint32_t textPosition, uint32_t textLength, 385 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) 386 { 387 SetCurrentRun (textPosition); 388 SplitCurrentRun (textPosition); 389 while (textLength > 0) 390 { 391 Run *run = FetchNextRun (&textLength); 392 run->mScript = *scriptAnalysis; 393 } 394 395 return S_OK; 396 } 397 398 IFACEMETHODIMP 399 SetLineBreakpoints (uint32_t textPosition, 400 uint32_t textLength, 401 const DWRITE_LINE_BREAKPOINT* lineBreakpoints) 402 { return S_OK; } 403 404 IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength, 405 uint8_t explicitLevel, uint8_t resolvedLevel) 406 { return S_OK; } 407 408 IFACEMETHODIMP 409 SetNumberSubstitution (uint32_t textPosition, uint32_t textLength, 410 IDWriteNumberSubstitution* numberSubstitution) 411 { return S_OK; } 412 413 protected: 414 Run *FetchNextRun (IN OUT uint32_t* textLength) 415 { 416 // Used by the sink setters, this returns a reference to the next run. 417 // Position and length are adjusted to now point after the current run 418 // being returned. 419 420 Run *origRun = mCurrentRun; 421 // Split the tail if needed (the length remaining is less than the 422 // current run's size). 423 if (*textLength < mCurrentRun->mTextLength) 424 SplitCurrentRun (mCurrentRun->mTextStart + *textLength); 425 else 426 // Just advance the current run. 427 mCurrentRun = mCurrentRun->nextRun; 428 *textLength -= origRun->mTextLength; 429 430 // Return a reference to the run that was just current. 431 return origRun; 432 } 433 434 void SetCurrentRun (uint32_t textPosition) 435 { 436 // Move the current run to the given position. 437 // Since the analyzers generally return results in a forward manner, 438 // this will usually just return early. If not, find the 439 // corresponding run for the text position. 440 441 if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) 442 return; 443 444 for (Run *run = &mRunHead; run; run = run->nextRun) 445 if (run->ContainsTextPosition (textPosition)) 446 { 447 mCurrentRun = run; 448 return; 449 } 450 assert (0); // We should always be able to find the text position in one of our runs 451 } 452 453 void SplitCurrentRun (uint32_t splitPosition) 454 { 455 if (!mCurrentRun) 456 { 457 assert (0); // SplitCurrentRun called without current run 458 // Shouldn't be calling this when no current run is set! 459 return; 460 } 461 // Split the current run. 462 if (splitPosition <= mCurrentRun->mTextStart) 463 { 464 // No need to split, already the start of a run 465 // or before it. Usually the first. 466 return; 467 } 468 Run *newRun = new Run; 469 470 *newRun = *mCurrentRun; 471 472 // Insert the new run in our linked list. 473 newRun->nextRun = mCurrentRun->nextRun; 474 mCurrentRun->nextRun = newRun; 475 476 // Adjust runs' text positions and lengths. 477 uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; 478 newRun->mTextStart += splitPoint; 479 newRun->mTextLength -= splitPoint; 480 mCurrentRun->mTextLength = splitPoint; 481 mCurrentRun = newRun; 482 } 483 484 protected: 485 // Input 486 // (weak references are fine here, since this class is a transient 487 // stack-based helper that doesn't need to copy data) 488 uint32_t mTextLength; 489 const wchar_t* mText; 490 const wchar_t* mLocaleName; 491 DWRITE_READING_DIRECTION mReadingDirection; 492 493 // Current processing state. 494 Run *mCurrentRun; 495 496 // Output is a list of runs starting here 497 Run mRunHead; 498 }; 499 500 static inline uint16_t hb_uint16_swap (const uint16_t v) 501 { return (v >> 8) | (v << 8); } 502 static inline uint32_t hb_uint32_swap (const uint32_t v) 503 { return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } 504 505 /* 506 * shaper 507 */ 508 509 static hb_bool_t 510 _hb_directwrite_shape_full (hb_shape_plan_t *shape_plan, 511 hb_font_t *font, 512 hb_buffer_t *buffer, 513 const hb_feature_t *features, 514 unsigned int num_features, 515 float lineWidth) 516 { 517 hb_face_t *face = font->face; 518 const hb_directwrite_face_data_t *face_data = face->data.directwrite; 519 const hb_directwrite_font_data_t *font_data = font->data.directwrite; 520 IDWriteFactory *dwriteFactory = face_data->dwriteFactory; 521 IDWriteFontFace *fontFace = face_data->fontFace; 522 523 IDWriteTextAnalyzer* analyzer; 524 dwriteFactory->CreateTextAnalyzer (&analyzer); 525 526 unsigned int scratch_size; 527 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); 528 #define ALLOCATE_ARRAY(Type, name, len) \ 529 Type *name = (Type *) scratch; \ 530 { \ 531 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ 532 assert (_consumed <= scratch_size); \ 533 scratch += _consumed; \ 534 scratch_size -= _consumed; \ 535 } 536 537 #define utf16_index() var1.u32 538 539 ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2); 540 541 unsigned int chars_len = 0; 542 for (unsigned int i = 0; i < buffer->len; i++) 543 { 544 hb_codepoint_t c = buffer->info[i].codepoint; 545 buffer->info[i].utf16_index () = chars_len; 546 if (likely (c <= 0xFFFFu)) 547 textString[chars_len++] = c; 548 else if (unlikely (c > 0x10FFFFu)) 549 textString[chars_len++] = 0xFFFDu; 550 else 551 { 552 textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); 553 textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); 554 } 555 } 556 557 ALLOCATE_ARRAY (WORD, log_clusters, chars_len); 558 /* Need log_clusters to assign features. */ 559 chars_len = 0; 560 for (unsigned int i = 0; i < buffer->len; i++) 561 { 562 hb_codepoint_t c = buffer->info[i].codepoint; 563 unsigned int cluster = buffer->info[i].cluster; 564 log_clusters[chars_len++] = cluster; 565 if (hb_in_range (c, 0x10000u, 0x10FFFFu)) 566 log_clusters[chars_len++] = cluster; /* Surrogates. */ 567 } 568 569 // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES 570 571 DWRITE_READING_DIRECTION readingDirection; 572 readingDirection = buffer->props.direction ? 573 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : 574 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; 575 576 /* 577 * There's an internal 16-bit limit on some things inside the analyzer, 578 * but we never attempt to shape a word longer than 64K characters 579 * in a single gfxShapedWord, so we cannot exceed that limit. 580 */ 581 uint32_t textLength = buffer->len; 582 583 TextAnalysis analysis (textString, textLength, nullptr, readingDirection); 584 TextAnalysis::Run *runHead; 585 HRESULT hr; 586 hr = analysis.GenerateResults (analyzer, &runHead); 587 588 #define FAIL(...) \ 589 HB_STMT_START { \ 590 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ 591 return false; \ 592 } HB_STMT_END; 593 594 if (FAILED (hr)) 595 FAIL ("Analyzer failed to generate results."); 596 597 uint32_t maxGlyphCount = 3 * textLength / 2 + 16; 598 uint32_t glyphCount; 599 bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); 600 601 const wchar_t localeName[20] = {0}; 602 if (buffer->props.language != nullptr) 603 mbstowcs ((wchar_t*) localeName, 604 hb_language_to_string (buffer->props.language), 20); 605 606 // TODO: it does work but doesn't care about ranges 607 DWRITE_TYPOGRAPHIC_FEATURES typographic_features; 608 typographic_features.featureCount = num_features; 609 if (num_features) 610 { 611 typographic_features.features = new DWRITE_FONT_FEATURE[num_features]; 612 for (unsigned int i = 0; i < num_features; ++i) 613 { 614 typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG) 615 hb_uint32_swap (features[i].tag); 616 typographic_features.features[i].parameter = features[i].value; 617 } 618 } 619 const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures; 620 dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features; 621 const uint32_t featureRangeLengths[] = { textLength }; 622 // 623 624 uint16_t* clusterMap; 625 clusterMap = new uint16_t[textLength]; 626 DWRITE_SHAPING_TEXT_PROPERTIES* textProperties; 627 textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength]; 628 retry_getglyphs: 629 uint16_t* glyphIndices = new uint16_t[maxGlyphCount]; 630 DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties; 631 glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount]; 632 633 hr = analyzer->GetGlyphs (textString, textLength, fontFace, false, 634 isRightToLeft, &runHead->mScript, localeName, 635 nullptr, &dwFeatures, featureRangeLengths, 1, 636 maxGlyphCount, clusterMap, textProperties, 637 glyphIndices, glyphProperties, &glyphCount); 638 639 if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) 640 { 641 delete [] glyphIndices; 642 delete [] glyphProperties; 643 644 maxGlyphCount *= 2; 645 646 goto retry_getglyphs; 647 } 648 if (FAILED (hr)) 649 FAIL ("Analyzer failed to get glyphs."); 650 651 float* glyphAdvances = new float[maxGlyphCount]; 652 DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; 653 654 /* The -2 in the following is to compensate for possible 655 * alignment needed after the WORD array. sizeof (WORD) == 2. */ 656 unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) 657 / (sizeof (WORD) + 658 sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + 659 sizeof (int) + 660 sizeof (DWRITE_GLYPH_OFFSET) + 661 sizeof (uint32_t)); 662 ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); 663 664 #undef ALLOCATE_ARRAY 665 666 int fontEmSize = font->face->get_upem (); 667 if (fontEmSize < 0) fontEmSize = -fontEmSize; 668 669 if (fontEmSize < 0) fontEmSize = -fontEmSize; 670 double x_mult = (double) font->x_scale / fontEmSize; 671 double y_mult = (double) font->y_scale / fontEmSize; 672 673 hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties, 674 textLength, glyphIndices, glyphProperties, 675 glyphCount, fontFace, fontEmSize, 676 false, isRightToLeft, &runHead->mScript, localeName, 677 &dwFeatures, featureRangeLengths, 1, 678 glyphAdvances, glyphOffsets); 679 680 if (FAILED (hr)) 681 FAIL ("Analyzer failed to get glyph placements."); 682 683 IDWriteTextAnalyzer1* analyzer1; 684 analyzer->QueryInterface (&analyzer1); 685 686 if (analyzer1 && lineWidth) 687 { 688 DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities = 689 new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount]; 690 hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript, 691 textLength, glyphCount, textString, 692 clusterMap, glyphProperties, 693 justificationOpportunities); 694 695 if (FAILED (hr)) 696 FAIL ("Analyzer failed to get justification opportunities."); 697 698 float* justifiedGlyphAdvances = new float[maxGlyphCount]; 699 DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount]; 700 hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities, 701 glyphAdvances, glyphOffsets, justifiedGlyphAdvances, 702 justifiedGlyphOffsets); 703 704 if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances."); 705 706 DWRITE_SCRIPT_PROPERTIES scriptProperties; 707 hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties); 708 if (FAILED (hr)) FAIL ("Analyzer failed to get script properties."); 709 uint32_t justificationCharacter = scriptProperties.justificationCharacter; 710 711 // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs 712 if (justificationCharacter != 32) 713 { 714 uint16_t* modifiedClusterMap = new uint16_t[textLength]; 715 retry_getjustifiedglyphs: 716 uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount]; 717 float* modifiedGlyphAdvances = new float[maxGlyphCount]; 718 DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; 719 uint32_t actualGlyphsCount; 720 hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript, 721 textLength, glyphCount, maxGlyphCount, 722 clusterMap, glyphIndices, glyphAdvances, 723 justifiedGlyphAdvances, justifiedGlyphOffsets, 724 glyphProperties, &actualGlyphsCount, 725 modifiedClusterMap, modifiedGlyphIndices, 726 modifiedGlyphAdvances, modifiedGlyphOffsets); 727 728 if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)) 729 { 730 maxGlyphCount = actualGlyphsCount; 731 delete [] modifiedGlyphIndices; 732 delete [] modifiedGlyphAdvances; 733 delete [] modifiedGlyphOffsets; 734 735 maxGlyphCount = actualGlyphsCount; 736 737 goto retry_getjustifiedglyphs; 738 } 739 if (FAILED (hr)) 740 FAIL ("Analyzer failed to get justified glyphs."); 741 742 delete [] clusterMap; 743 delete [] glyphIndices; 744 delete [] glyphAdvances; 745 delete [] glyphOffsets; 746 747 glyphCount = actualGlyphsCount; 748 clusterMap = modifiedClusterMap; 749 glyphIndices = modifiedGlyphIndices; 750 glyphAdvances = modifiedGlyphAdvances; 751 glyphOffsets = modifiedGlyphOffsets; 752 753 delete [] justifiedGlyphAdvances; 754 delete [] justifiedGlyphOffsets; 755 } 756 else 757 { 758 delete [] glyphAdvances; 759 delete [] glyphOffsets; 760 761 glyphAdvances = justifiedGlyphAdvances; 762 glyphOffsets = justifiedGlyphOffsets; 763 } 764 765 delete [] justificationOpportunities; 766 } 767 768 /* Ok, we've got everything we need, now compose output buffer, 769 * very, *very*, carefully! */ 770 771 /* Calculate visual-clusters. That's what we ship. */ 772 for (unsigned int i = 0; i < glyphCount; i++) 773 vis_clusters[i] = -1; 774 for (unsigned int i = 0; i < buffer->len; i++) 775 { 776 uint32_t *p = 777 &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]]; 778 *p = MIN (*p, buffer->info[i].cluster); 779 } 780 for (unsigned int i = 1; i < glyphCount; i++) 781 if (vis_clusters[i] == -1) 782 vis_clusters[i] = vis_clusters[i - 1]; 783 784 #undef utf16_index 785 786 if (unlikely (!buffer->ensure (glyphCount))) 787 FAIL ("Buffer in error"); 788 789 #undef FAIL 790 791 /* Set glyph infos */ 792 buffer->len = 0; 793 for (unsigned int i = 0; i < glyphCount; i++) 794 { 795 hb_glyph_info_t *info = &buffer->info[buffer->len++]; 796 797 info->codepoint = glyphIndices[i]; 798 info->cluster = vis_clusters[i]; 799 800 /* The rest is crap. Let's store position info there for now. */ 801 info->mask = glyphAdvances[i]; 802 info->var1.i32 = glyphOffsets[i].advanceOffset; 803 info->var2.i32 = glyphOffsets[i].ascenderOffset; 804 } 805 806 /* Set glyph positions */ 807 buffer->clear_positions (); 808 for (unsigned int i = 0; i < glyphCount; i++) 809 { 810 hb_glyph_info_t *info = &buffer->info[i]; 811 hb_glyph_position_t *pos = &buffer->pos[i]; 812 813 /* TODO vertical */ 814 pos->x_advance = x_mult * (int32_t) info->mask; 815 pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); 816 pos->y_offset = y_mult * info->var2.i32; 817 } 818 819 if (isRightToLeft) hb_buffer_reverse (buffer); 820 821 delete [] clusterMap; 822 delete [] glyphIndices; 823 delete [] textProperties; 824 delete [] glyphProperties; 825 delete [] glyphAdvances; 826 delete [] glyphOffsets; 827 828 if (num_features) 829 delete [] typographic_features.features; 830 831 /* Wow, done! */ 832 return true; 833 } 834 835 hb_bool_t 836 _hb_directwrite_shape (hb_shape_plan_t *shape_plan, 837 hb_font_t *font, 838 hb_buffer_t *buffer, 839 const hb_feature_t *features, 840 unsigned int num_features) 841 { 842 return _hb_directwrite_shape_full (shape_plan, font, buffer, 843 features, num_features, 0); 844 } 845 846 /* 847 * Public [experimental] API 848 */ 849 850 hb_bool_t 851 hb_directwrite_shape_experimental_width (hb_font_t *font, 852 hb_buffer_t *buffer, 853 const hb_feature_t *features, 854 unsigned int num_features, 855 float width) 856 { 857 static const char *shapers = "directwrite"; 858 hb_shape_plan_t *shape_plan; 859 shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, 860 features, num_features, &shapers); 861 hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer, 862 features, num_features, width); 863 864 buffer->unsafe_to_break_all (); 865 866 return res; 867 } 868