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