Home | History | Annotate | Download | only in src
      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 
     37 HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, face)
     38 HB_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
     48 class DWriteFontFileLoader : public IDWriteFontFileLoader
     49 {
     50 private:
     51   IDWriteFontFileStream *mFontFileStream;
     52 public:
     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 
     72 class DWriteFontFileStream : public IDWriteFontFileStream
     73 {
     74 private:
     75   uint8_t *mData;
     76   uint32_t mSize;
     77 public:
     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 
    128 struct 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 
    137 hb_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 
    203 void
    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 
    230 struct hb_directwrite_shaper_font_data_t {
    231 };
    232 
    233 hb_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 
    246 void
    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 
    257 struct hb_directwrite_shaper_shape_plan_data_t {};
    258 
    259 hb_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 
    269 void
    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
    276 class TextAnalysis
    277   : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
    278 {
    279 public:
    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 
    307 public:
    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 
    441 protected:
    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 
    520 protected:
    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 
    536 static inline uint16_t hb_uint16_swap (const uint16_t v)
    537 { return (v >> 8) | (v << 8); }
    538 static 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 
    545 static 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));
    669 retry_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 
    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   if (res)
    931     buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
    932 
    933   return res;
    934 }
    935