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