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