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#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
29  #include <DWrite.h>
30#else
31  #include <DWrite_1.h>
32#endif
33
34#include "hb-directwrite.h"
35
36#include "hb-open-file-private.hh"
37#include "hb-ot-name-table.hh"
38#include "hb-ot-tag.h"
39
40
41#ifndef HB_DEBUG_DIRECTWRITE
42#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
43#endif
44
45HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, face)
46HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, font)
47
48/*
49* shaper face data
50*/
51
52struct hb_directwrite_shaper_face_data_t {
53  HANDLE fh;
54  wchar_t face_name[LF_FACESIZE];
55};
56
57/* face_name should point to a wchar_t[LF_FACESIZE] object. */
58static void
59_hb_generate_unique_face_name(wchar_t *face_name, unsigned int *plen)
60{
61  /* We'll create a private name for the font from a UUID using a simple,
62  * somewhat base64-like encoding scheme */
63  const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
64  UUID id;
65  UuidCreate ((UUID*)&id);
66  ASSERT_STATIC (2 + 3 * (16 / 2) < LF_FACESIZE);
67  unsigned int name_str_len = 0;
68  face_name[name_str_len++] = 'F';
69  face_name[name_str_len++] = '_';
70  unsigned char *p = (unsigned char *)&id;
71  for (unsigned int i = 0; i < 16; i += 2)
72  {
73    /* Spread the 16 bits from two bytes of the UUID across three chars of face_name,
74    * using the bits in groups of 5,5,6 to select chars from enc.
75    * This will generate 24 characters; with the 'F_' prefix we already provided,
76    * the name will be 26 chars (plus the NUL terminator), so will always fit within
77    * face_name (LF_FACESIZE = 32). */
78    face_name[name_str_len++] = enc[p[i] >> 3];
79    face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f];
80    face_name[name_str_len++] = enc[p[i + 1] & 0x3f];
81  }
82  face_name[name_str_len] = 0;
83  if (plen)
84    *plen = name_str_len;
85}
86
87/* Destroys blob. */
88static hb_blob_t *
89_hb_rename_font(hb_blob_t *blob, wchar_t *new_name)
90{
91  /* Create a copy of the font data, with the 'name' table replaced by a
92   * table that names the font with our private F_* name created above.
93   * For simplicity, we just append a new 'name' table and update the
94   * sfnt directory; the original table is left in place, but unused.
95   *
96   * The new table will contain just 5 name IDs: family, style, unique,
97   * full, PS. All of them point to the same name data with our unique name.
98   */
99
100  blob = OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (blob);
101
102  unsigned int length, new_length, name_str_len;
103  const char *orig_sfnt_data = hb_blob_get_data (blob, &length);
104
105  _hb_generate_unique_face_name (new_name, &name_str_len);
106
107  static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 };
108
109  unsigned int name_table_length = OT::name::min_size +
110    ARRAY_LENGTH(name_IDs) * OT::NameRecord::static_size +
111    name_str_len * 2; /* for name data in UTF16BE form */
112  unsigned int name_table_offset = (length + 3) & ~3;
113
114  new_length = name_table_offset + ((name_table_length + 3) & ~3);
115  void *new_sfnt_data = calloc(1, new_length);
116  if (!new_sfnt_data)
117  {
118    hb_blob_destroy (blob);
119    return NULL;
120  }
121
122  memcpy(new_sfnt_data, orig_sfnt_data, length);
123
124  OT::name &name = OT::StructAtOffset<OT::name> (new_sfnt_data, name_table_offset);
125  name.format.set (0);
126  name.count.set (ARRAY_LENGTH (name_IDs));
127  name.stringOffset.set (name.get_size());
128  for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++)
129  {
130    OT::NameRecord &record = name.nameRecord[i];
131    record.platformID.set(3);
132    record.encodingID.set(1);
133    record.languageID.set(0x0409u); /* English */
134    record.nameID.set(name_IDs[i]);
135    record.length.set(name_str_len * 2);
136    record.offset.set(0);
137  }
138
139  /* Copy string data from new_name, converting wchar_t to UTF16BE. */
140  unsigned char *p = &OT::StructAfter<unsigned char>(name);
141  for (unsigned int i = 0; i < name_str_len; i++)
142  {
143    *p++ = new_name[i] >> 8;
144    *p++ = new_name[i] & 0xff;
145  }
146
147  /* Adjust name table entry to point to new name table */
148  const OT::OpenTypeFontFile &file = *(OT::OpenTypeFontFile *) (new_sfnt_data);
149  unsigned int face_count = file.get_face_count ();
150  for (unsigned int face_index = 0; face_index < face_count; face_index++)
151  {
152    /* Note: doing multiple edits (ie. TTC) can be unsafe.  There may be
153    * toe-stepping.  But we don't really care. */
154    const OT::OpenTypeFontFace &face = file.get_face (face_index);
155    unsigned int index;
156    if (face.find_table_index (HB_OT_TAG_name, &index))
157    {
158      OT::TableRecord &record = const_cast<OT::TableRecord &> (face.get_table (index));
159      record.checkSum.set_for_data (&name, name_table_length);
160      record.offset.set (name_table_offset);
161      record.length.set (name_table_length);
162    }
163    else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */
164    {
165      free (new_sfnt_data);
166      hb_blob_destroy (blob);
167      return NULL;
168    }
169  }
170
171  /* The checkSumAdjustment field in the 'head' table is now wrong,
172  * but that doesn't actually seem to cause any problems so we don't
173  * bother. */
174
175  hb_blob_destroy (blob);
176  return hb_blob_create ((const char *)new_sfnt_data, new_length,
177    HB_MEMORY_MODE_WRITABLE, NULL, free);
178}
179
180hb_directwrite_shaper_face_data_t *
181_hb_directwrite_shaper_face_data_create(hb_face_t *face)
182{
183  hb_directwrite_shaper_face_data_t *data =
184    (hb_directwrite_shaper_face_data_t *) calloc (1, sizeof (hb_directwrite_shaper_face_data_t));
185  if (unlikely (!data))
186    return NULL;
187
188  hb_blob_t *blob = hb_face_reference_blob (face);
189  if (unlikely (!hb_blob_get_length (blob)))
190    DEBUG_MSG(DIRECTWRITE, face, "Face has empty blob");
191
192  blob = _hb_rename_font (blob, data->face_name);
193  if (unlikely (!blob))
194  {
195    free(data);
196    return NULL;
197  }
198
199  DWORD num_fonts_installed;
200  data->fh = AddFontMemResourceEx ((void *)hb_blob_get_data(blob, NULL),
201    hb_blob_get_length (blob),
202    0, &num_fonts_installed);
203  if (unlikely (!data->fh))
204  {
205    DEBUG_MSG (DIRECTWRITE, face, "Face AddFontMemResourceEx() failed");
206    free (data);
207    return NULL;
208  }
209
210  return data;
211}
212
213void
214_hb_directwrite_shaper_face_data_destroy(hb_directwrite_shaper_face_data_t *data)
215{
216  RemoveFontMemResourceEx(data->fh);
217  free(data);
218}
219
220
221/*
222 * shaper font data
223 */
224
225struct hb_directwrite_shaper_font_data_t {
226  HDC hdc;
227  LOGFONTW log_font;
228  HFONT hfont;
229};
230
231static bool
232populate_log_font (LOGFONTW  *lf,
233       hb_font_t *font)
234{
235  memset (lf, 0, sizeof (*lf));
236  lf->lfHeight = -font->y_scale;
237  lf->lfCharSet = DEFAULT_CHARSET;
238
239  hb_face_t *face = font->face;
240  hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
241
242  memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName));
243
244  return true;
245}
246
247hb_directwrite_shaper_font_data_t *
248_hb_directwrite_shaper_font_data_create (hb_font_t *font)
249{
250  if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return NULL;
251
252  hb_directwrite_shaper_font_data_t *data =
253    (hb_directwrite_shaper_font_data_t *) calloc (1, sizeof (hb_directwrite_shaper_font_data_t));
254  if (unlikely (!data))
255    return NULL;
256
257  data->hdc = GetDC (NULL);
258
259  if (unlikely (!populate_log_font (&data->log_font, font)))
260  {
261    DEBUG_MSG (DIRECTWRITE, font, "Font populate_log_font() failed");
262    _hb_directwrite_shaper_font_data_destroy (data);
263    return NULL;
264  }
265
266  data->hfont = CreateFontIndirectW (&data->log_font);
267  if (unlikely (!data->hfont))
268  {
269    DEBUG_MSG (DIRECTWRITE, font, "Font CreateFontIndirectW() failed");
270    _hb_directwrite_shaper_font_data_destroy (data);
271     return NULL;
272  }
273
274  if (!SelectObject (data->hdc, data->hfont))
275  {
276    DEBUG_MSG (DIRECTWRITE, font, "Font SelectObject() failed");
277    _hb_directwrite_shaper_font_data_destroy (data);
278     return NULL;
279  }
280
281  return data;
282}
283
284void
285_hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data)
286{
287  if (data->hdc)
288    ReleaseDC (NULL, data->hdc);
289  if (data->hfont)
290    DeleteObject (data->hfont);
291  free (data);
292}
293
294LOGFONTW *
295hb_directwrite_font_get_logfontw (hb_font_t *font)
296{
297  if (unlikely (!hb_directwrite_shaper_font_data_ensure (font))) return NULL;
298  hb_directwrite_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
299  return &font_data->log_font;
300}
301
302HFONT
303hb_directwrite_font_get_hfont (hb_font_t *font)
304{
305  if (unlikely (!hb_directwrite_shaper_font_data_ensure (font))) return NULL;
306  hb_directwrite_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
307  return font_data->hfont;
308}
309
310
311/*
312 * shaper shape_plan data
313 */
314
315struct hb_directwrite_shaper_shape_plan_data_t {};
316
317hb_directwrite_shaper_shape_plan_data_t *
318_hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
319               const hb_feature_t *user_features HB_UNUSED,
320               unsigned int        num_user_features HB_UNUSED)
321{
322  return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
323}
324
325void
326_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan_data_t *data HB_UNUSED)
327{
328}
329
330// Most of here TextAnalysis is originally written by Bas Schouten for Mozilla project
331// but now is relicensed to MIT for HarfBuzz use
332class TextAnalysis
333  : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
334{
335public:
336
337  IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; }
338  IFACEMETHOD_(ULONG, AddRef)() { return 1; }
339  IFACEMETHOD_(ULONG, Release)() { return 1; }
340
341  // A single contiguous run of characters containing the same analysis
342  // results.
343  struct Run
344  {
345    uint32_t mTextStart;   // starting text position of this run
346    uint32_t mTextLength;  // number of contiguous code units covered
347    uint32_t mGlyphStart;  // starting glyph in the glyphs array
348    uint32_t mGlyphCount;  // number of glyphs associated with this run of
349    // text
350    DWRITE_SCRIPT_ANALYSIS mScript;
351    uint8_t mBidiLevel;
352    bool mIsSideways;
353
354    inline bool ContainsTextPosition(uint32_t aTextPosition) const
355    {
356      return aTextPosition >= mTextStart
357        && aTextPosition <  mTextStart + mTextLength;
358    }
359
360    Run *nextRun;
361  };
362
363public:
364  TextAnalysis(const wchar_t* text,
365    uint32_t textLength,
366    const wchar_t* localeName,
367    DWRITE_READING_DIRECTION readingDirection)
368    : mText(text)
369    , mTextLength(textLength)
370    , mLocaleName(localeName)
371    , mReadingDirection(readingDirection)
372    , mCurrentRun(NULL) { };
373
374  ~TextAnalysis() {
375    // delete runs, except mRunHead which is part of the TextAnalysis object
376    for (Run *run = mRunHead.nextRun; run;) {
377      Run *origRun = run;
378      run = run->nextRun;
379      free (origRun);
380    }
381  }
382
383  STDMETHODIMP GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
384    Run **runHead) {
385    // Analyzes the text using the script analyzer and returns
386    // the result as a series of runs.
387
388    HRESULT hr = S_OK;
389
390    // Initially start out with one result that covers the entire range.
391    // This result will be subdivided by the analysis processes.
392    mRunHead.mTextStart = 0;
393    mRunHead.mTextLength = mTextLength;
394    mRunHead.mBidiLevel =
395      (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
396    mRunHead.nextRun = NULL;
397    mCurrentRun = &mRunHead;
398
399    // Call each of the analyzers in sequence, recording their results.
400    if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) {
401      *runHead = &mRunHead;
402    }
403
404    return hr;
405  }
406
407  // IDWriteTextAnalysisSource implementation
408
409  IFACEMETHODIMP GetTextAtPosition(uint32_t textPosition,
410    OUT wchar_t const** textString,
411    OUT uint32_t* textLength)
412  {
413    if (textPosition >= mTextLength) {
414      // No text at this position, valid query though.
415      *textString = NULL;
416      *textLength = 0;
417    }
418    else {
419      *textString = mText + textPosition;
420      *textLength = mTextLength - textPosition;
421    }
422    return S_OK;
423  }
424
425  IFACEMETHODIMP GetTextBeforePosition(uint32_t textPosition,
426    OUT wchar_t const** textString,
427    OUT uint32_t* textLength)
428  {
429    if (textPosition == 0 || textPosition > mTextLength) {
430      // Either there is no text before here (== 0), or this
431      // is an invalid position. The query is considered valid thouh.
432      *textString = NULL;
433      *textLength = 0;
434    }
435    else {
436      *textString = mText;
437      *textLength = textPosition;
438    }
439    return S_OK;
440  }
441
442  IFACEMETHODIMP_(DWRITE_READING_DIRECTION)
443    GetParagraphReadingDirection() { return mReadingDirection; }
444
445  IFACEMETHODIMP GetLocaleName(uint32_t textPosition,
446    uint32_t* textLength,
447    wchar_t const** localeName) {
448    return S_OK;
449  }
450
451  IFACEMETHODIMP
452    GetNumberSubstitution(uint32_t textPosition,
453    OUT uint32_t* textLength,
454    OUT IDWriteNumberSubstitution** numberSubstitution)
455  {
456    // We do not support number substitution.
457    *numberSubstitution = NULL;
458    *textLength = mTextLength - textPosition;
459
460    return S_OK;
461  }
462
463  // IDWriteTextAnalysisSink implementation
464
465  IFACEMETHODIMP
466    SetScriptAnalysis(uint32_t textPosition,
467    uint32_t textLength,
468    DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
469  {
470    SetCurrentRun(textPosition);
471    SplitCurrentRun(textPosition);
472    while (textLength > 0) {
473      Run *run = FetchNextRun(&textLength);
474      run->mScript = *scriptAnalysis;
475    }
476
477    return S_OK;
478  }
479
480  IFACEMETHODIMP
481    SetLineBreakpoints(uint32_t textPosition,
482    uint32_t textLength,
483    const DWRITE_LINE_BREAKPOINT* lineBreakpoints) { return S_OK; }
484
485  IFACEMETHODIMP SetBidiLevel(uint32_t textPosition,
486    uint32_t textLength,
487    uint8_t explicitLevel,
488    uint8_t resolvedLevel) { return S_OK; }
489
490  IFACEMETHODIMP
491    SetNumberSubstitution(uint32_t textPosition,
492    uint32_t textLength,
493    IDWriteNumberSubstitution* numberSubstitution) { return S_OK; }
494
495protected:
496  Run *FetchNextRun(IN OUT uint32_t* textLength)
497  {
498    // Used by the sink setters, this returns a reference to the next run.
499    // Position and length are adjusted to now point after the current run
500    // being returned.
501
502    Run *origRun = mCurrentRun;
503    // Split the tail if needed (the length remaining is less than the
504    // current run's size).
505    if (*textLength < mCurrentRun->mTextLength) {
506      SplitCurrentRun(mCurrentRun->mTextStart + *textLength);
507    }
508    else {
509      // Just advance the current run.
510      mCurrentRun = mCurrentRun->nextRun;
511    }
512    *textLength -= origRun->mTextLength;
513
514    // Return a reference to the run that was just current.
515    return origRun;
516  }
517
518  void SetCurrentRun(uint32_t textPosition)
519  {
520    // Move the current run to the given position.
521    // Since the analyzers generally return results in a forward manner,
522    // this will usually just return early. If not, find the
523    // corresponding run for the text position.
524
525    if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) {
526      return;
527    }
528
529    for (Run *run = &mRunHead; run; run = run->nextRun) {
530      if (run->ContainsTextPosition(textPosition)) {
531        mCurrentRun = run;
532        return;
533      }
534    }
535    //NS_NOTREACHED("We should always be able to find the text position in one \
536            //                of our runs");
537  }
538
539  void SplitCurrentRun(uint32_t splitPosition)
540  {
541    if (!mCurrentRun) {
542      //NS_ASSERTION(false, "SplitCurrentRun called without current run.");
543      // Shouldn't be calling this when no current run is set!
544      return;
545    }
546    // Split the current run.
547    if (splitPosition <= mCurrentRun->mTextStart) {
548      // No need to split, already the start of a run
549      // or before it. Usually the first.
550      return;
551    }
552    Run *newRun = (Run*) malloc (sizeof (Run));
553
554    *newRun = *mCurrentRun;
555
556    // Insert the new run in our linked list.
557    newRun->nextRun = mCurrentRun->nextRun;
558    mCurrentRun->nextRun = newRun;
559
560    // Adjust runs' text positions and lengths.
561    uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
562    newRun->mTextStart += splitPoint;
563    newRun->mTextLength -= splitPoint;
564    mCurrentRun->mTextLength = splitPoint;
565    mCurrentRun = newRun;
566  }
567
568protected:
569  // Input
570  // (weak references are fine here, since this class is a transient
571  //  stack-based helper that doesn't need to copy data)
572  uint32_t mTextLength;
573  const wchar_t* mText;
574  const wchar_t* mLocaleName;
575  DWRITE_READING_DIRECTION mReadingDirection;
576
577  // Current processing state.
578  Run *mCurrentRun;
579
580  // Output is a list of runs starting here
581  Run  mRunHead;
582};
583
584static inline uint16_t hb_uint16_swap (const uint16_t v)
585{ return (v >> 8) | (v << 8); }
586static inline uint32_t hb_uint32_swap (const uint32_t v)
587{ return (hb_uint16_swap(v) << 16) | hb_uint16_swap(v >> 16); }
588
589/*
590 * shaper
591 */
592
593hb_bool_t
594_hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
595  hb_font_t          *font,
596  hb_buffer_t        *buffer,
597  const hb_feature_t *features,
598  unsigned int        num_features)
599{
600  hb_face_t *face = font->face;
601  hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
602  hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
603
604  // factory probably should be cached
605#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
606  IDWriteFactory* dwriteFactory;
607#else
608  IDWriteFactory1* dwriteFactory;
609#endif
610  DWriteCreateFactory (
611    DWRITE_FACTORY_TYPE_SHARED,
612    __uuidof (IDWriteFactory),
613    (IUnknown**) &dwriteFactory
614  );
615
616  IDWriteGdiInterop *gdiInterop;
617  dwriteFactory->GetGdiInterop (&gdiInterop);
618  IDWriteFontFace* fontFace;
619  gdiInterop->CreateFontFaceFromHdc (font_data->hdc, &fontFace);
620
621#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
622  IDWriteTextAnalyzer* analyzer;
623  dwriteFactory->CreateTextAnalyzer(&analyzer);
624#else
625  IDWriteTextAnalyzer* analyzer0;
626  dwriteFactory->CreateTextAnalyzer (&analyzer0);
627  IDWriteTextAnalyzer1* analyzer = (IDWriteTextAnalyzer1*) analyzer0;
628#endif
629
630  unsigned int scratch_size;
631  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
632#define ALLOCATE_ARRAY(Type, name, len) \
633  Type *name = (Type *) scratch; \
634  { \
635    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
636    assert (_consumed <= scratch_size); \
637    scratch += _consumed; \
638    scratch_size -= _consumed; \
639  }
640
641#define utf16_index() var1.u32
642
643  ALLOCATE_ARRAY(wchar_t, textString, buffer->len * 2);
644
645  unsigned int chars_len = 0;
646  for (unsigned int i = 0; i < buffer->len; i++)
647  {
648    hb_codepoint_t c = buffer->info[i].codepoint;
649    buffer->info[i].utf16_index() = chars_len;
650    if (likely(c <= 0xFFFFu))
651      textString[chars_len++] = c;
652    else if (unlikely(c > 0x10FFFFu))
653      textString[chars_len++] = 0xFFFDu;
654    else {
655      textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
656      textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1));
657    }
658  }
659
660  ALLOCATE_ARRAY(WORD, log_clusters, chars_len);
661  // if (num_features)
662  {
663    /* Need log_clusters to assign features. */
664    chars_len = 0;
665    for (unsigned int i = 0; i < buffer->len; i++)
666    {
667      hb_codepoint_t c = buffer->info[i].codepoint;
668      unsigned int cluster = buffer->info[i].cluster;
669      log_clusters[chars_len++] = cluster;
670      if (hb_in_range(c, 0x10000u, 0x10FFFFu))
671        log_clusters[chars_len++] = cluster; /* Surrogates. */
672    }
673  }
674
675  HRESULT hr;
676  // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
677
678  DWRITE_READING_DIRECTION readingDirection = buffer->props.direction ?
679    DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
680    DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
681
682  /*
683  * There's an internal 16-bit limit on some things inside the analyzer,
684  * but we never attempt to shape a word longer than 64K characters
685  * in a single gfxShapedWord, so we cannot exceed that limit.
686  */
687  uint32_t textLength = buffer->len;
688
689  TextAnalysis analysis(textString, textLength, NULL, readingDirection);
690  TextAnalysis::Run *runHead;
691  hr = analysis.GenerateResults(analyzer, &runHead);
692
693#define FAIL(...) \
694  HB_STMT_START { \
695    DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \
696    return false; \
697  } HB_STMT_END;
698
699  if (FAILED (hr))
700  {
701    FAIL ("Analyzer failed to generate results.");
702    return false;
703  }
704
705  uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
706  uint32_t glyphCount;
707  bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
708
709  const wchar_t localeName[20] = {0};
710  if (buffer->props.language != NULL)
711  {
712    mbstowcs ((wchar_t*) localeName,
713      hb_language_to_string (buffer->props.language), 20);
714  }
715
716  DWRITE_TYPOGRAPHIC_FEATURES singleFeatures;
717  singleFeatures.featureCount = num_features;
718  if (num_features)
719  {
720    DWRITE_FONT_FEATURE* dwfeatureArray = (DWRITE_FONT_FEATURE*)
721      malloc (sizeof (DWRITE_FONT_FEATURE) * num_features);
722    for (unsigned int i = 0; i < num_features; ++i)
723    {
724      dwfeatureArray[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
725        hb_uint32_swap (features[i].tag);
726      dwfeatureArray[i].parameter = features[i].value;
727    }
728    singleFeatures.features = dwfeatureArray;
729  }
730  const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures =
731    (const DWRITE_TYPOGRAPHIC_FEATURES*) &singleFeatures;
732  const uint32_t featureRangeLengths[] = { textLength };
733
734retry_getglyphs:
735  uint16_t* clusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
736  uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
737  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*)
738    malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES));
739  DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*)
740    malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES));
741
742  hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE,
743    isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures,
744    featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
745    glyphProperties, &glyphCount);
746
747  if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
748  {
749    free (clusterMap);
750    free (glyphIndices);
751    free (textProperties);
752    free (glyphProperties);
753
754    maxGlyphCount *= 2;
755
756    goto retry_getglyphs;
757  }
758  if (FAILED (hr))
759  {
760    FAIL ("Analyzer failed to get glyphs.");
761    return false;
762  }
763
764  float* glyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float));
765  DWRITE_GLYPH_OFFSET* glyphOffsets = (DWRITE_GLYPH_OFFSET*)
766    malloc(maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET));
767
768  /* The -2 in the following is to compensate for possible
769   * alignment needed after the WORD array.  sizeof(WORD) == 2. */
770  unsigned int glyphs_size = (scratch_size * sizeof(int) - 2)
771         / (sizeof(WORD) +
772            sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES) +
773            sizeof(int) +
774            sizeof(DWRITE_GLYPH_OFFSET) +
775            sizeof(uint32_t));
776  ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
777
778#undef ALLOCATE_ARRAY
779
780  int fontEmSize = font->face->get_upem();
781  if (fontEmSize < 0)
782    fontEmSize = -fontEmSize;
783
784  if (fontEmSize < 0)
785    fontEmSize = -fontEmSize;
786  double x_mult = (double) font->x_scale / fontEmSize;
787  double y_mult = (double) font->y_scale / fontEmSize;
788
789  hr = analyzer->GetGlyphPlacements (textString,
790    clusterMap, textProperties, textLength, glyphIndices,
791    glyphProperties, glyphCount, fontFace, fontEmSize,
792    FALSE, isRightToLeft, &runHead->mScript, localeName,
793    &dwFeatures, featureRangeLengths, 1,
794    glyphAdvances, glyphOffsets);
795
796  if (FAILED (hr))
797  {
798    FAIL ("Analyzer failed to get glyph placements.");
799    return false;
800  }
801
802#ifdef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
803
804  DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
805    (DWRITE_JUSTIFICATION_OPPORTUNITY*)
806    malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY));
807  hr = analyzer->GetJustificationOpportunities (fontFace, fontEmSize,
808    runHead->mScript, textLength, glyphCount, textString, clusterMap,
809    glyphProperties, justificationOpportunities);
810
811  if (FAILED (hr))
812  {
813    FAIL ("Analyzer failed to get justification opportunities.");
814    return false;
815  }
816
817  // TODO: get lineWith from somewhere
818  float lineWidth = 60000;
819
820  float* justifiedGlyphAdvances =
821    (float*) malloc (maxGlyphCount * sizeof (float));
822  DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*)
823    malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET));
824  hr = analyzer->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
825    glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets);
826
827  if (FAILED (hr))
828  {
829    FAIL ("Analyzer failed to get justified glyph advances.");
830    return false;
831  }
832
833  DWRITE_SCRIPT_PROPERTIES scriptProperties;
834  hr = analyzer->GetScriptProperties (runHead->mScript, &scriptProperties);
835  if (FAILED (hr))
836  {
837    FAIL ("Analyzer failed to get script properties.");
838    return false;
839  }
840  uint32_t justificationCharacter = scriptProperties.justificationCharacter;
841
842  // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
843  if (justificationCharacter != 32)
844  {
845retry_getjustifiedglyphs:
846    uint16_t* modifiedClusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
847    uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
848    float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float));
849    DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*)
850      malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET));
851    uint32_t actualGlyphsCount;
852    hr = analyzer->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
853        textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices,
854        glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets,
855        glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices,
856        modifiedGlyphAdvances, modifiedGlyphOffsets);
857
858    if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
859    {
860      maxGlyphCount = actualGlyphsCount;
861      free (modifiedClusterMap);
862      free (modifiedGlyphIndices);
863      free (modifiedGlyphAdvances);
864      free (modifiedGlyphOffsets);
865
866      maxGlyphCount = actualGlyphsCount;
867
868      goto retry_getjustifiedglyphs;
869    }
870    if (FAILED (hr))
871    {
872      FAIL ("Analyzer failed to get justified glyphs.");
873      return false;
874    }
875
876    free (clusterMap);
877    free (glyphIndices);
878    free (glyphAdvances);
879    free (glyphOffsets);
880
881    glyphCount = actualGlyphsCount;
882    clusterMap = modifiedClusterMap;
883    glyphIndices = modifiedGlyphIndices;
884    glyphAdvances = modifiedGlyphAdvances;
885    glyphOffsets = modifiedGlyphOffsets;
886
887    free(justifiedGlyphAdvances);
888    free(justifiedGlyphOffsets);
889  }
890  else
891  {
892    free(glyphAdvances);
893    free(glyphOffsets);
894
895    glyphAdvances = justifiedGlyphAdvances;
896    glyphOffsets = justifiedGlyphOffsets;
897  }
898
899  free(justificationOpportunities);
900
901#endif
902
903  /* Ok, we've got everything we need, now compose output buffer,
904   * very, *very*, carefully! */
905
906  /* Calculate visual-clusters.  That's what we ship. */
907  for (unsigned int i = 0; i < glyphCount; i++)
908    vis_clusters[i] = -1;
909  for (unsigned int i = 0; i < buffer->len; i++)
910  {
911    uint32_t *p =
912      &vis_clusters[log_clusters[buffer->info[i].utf16_index()]];
913    *p = MIN (*p, buffer->info[i].cluster);
914  }
915  for (unsigned int i = 1; i < glyphCount; i++)
916    if (vis_clusters[i] == -1)
917      vis_clusters[i] = vis_clusters[i - 1];
918
919#undef utf16_index
920
921  if (unlikely (!buffer->ensure (glyphCount)))
922    FAIL ("Buffer in error");
923
924#undef FAIL
925
926  /* Set glyph infos */
927  buffer->len = 0;
928  for (unsigned int i = 0; i < glyphCount; i++)
929  {
930    hb_glyph_info_t *info = &buffer->info[buffer->len++];
931
932    info->codepoint = glyphIndices[i];
933    info->cluster = vis_clusters[i];
934
935    /* The rest is crap.  Let's store position info there for now. */
936    info->mask = glyphAdvances[i];
937    info->var1.i32 = glyphOffsets[i].advanceOffset;
938    info->var2.i32 = glyphOffsets[i].ascenderOffset;
939  }
940
941  /* Set glyph positions */
942  buffer->clear_positions ();
943  for (unsigned int i = 0; i < glyphCount; i++)
944  {
945    hb_glyph_info_t *info = &buffer->info[i];
946    hb_glyph_position_t *pos = &buffer->pos[i];
947
948    /* TODO vertical */
949    pos->x_advance = x_mult * (int32_t) info->mask;
950    pos->x_offset =
951      x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
952    pos->y_offset = y_mult * info->var2.i32;
953  }
954
955  if (isRightToLeft)
956    hb_buffer_reverse (buffer);
957
958  free (clusterMap);
959  free (glyphIndices);
960  free (textProperties);
961  free (glyphProperties);
962  free (glyphAdvances);
963  free (glyphOffsets);
964
965  if (num_features)
966    free (singleFeatures.features);
967
968  /* Wow, done! */
969  return true;
970}
971