• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DWriteFontFileLoader(IDWriteFontFileStream * fontFileStream)53   DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) {
54     mFontFileStream = fontFileStream;
55   }
56 
57   // IUnknown interface
IFACEMETHOD(QueryInterface)58   IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; }
IFACEMETHOD_(ULONG,AddRef)59   IFACEMETHOD_(ULONG, AddRef)() { return 1; }
IFACEMETHOD_(ULONG,Release)60   IFACEMETHOD_(ULONG, Release)() { return 1; }
61 
62   // IDWriteFontFileLoader methods
CreateStreamFromKey(void const * fontFileReferenceKey,UINT32 fontFileReferenceKeySize,OUT IDWriteFontFileStream ** fontFileStream)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:
DWriteFontFileStream(uint8_t * aData,uint32_t aSize)78   DWriteFontFileStream(uint8_t *aData, uint32_t aSize)
79   {
80     mData = aData;
81     mSize = aSize;
82   }
83 
84   // IUnknown interface
IFACEMETHOD(QueryInterface)85   IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; }
IFACEMETHOD_(ULONG,AddRef)86   IFACEMETHOD_(ULONG, AddRef)() { return 1; }
IFACEMETHOD_(ULONG,Release)87   IFACEMETHOD_(ULONG, Release)() { return 1; }
88 
89   // IDWriteFontFileStream methods
ReadFileFragment(void const ** fragmentStart,UINT64 fileOffset,UINT64 fragmentSize,OUT void ** fragmentContext)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 
ReleaseFileFragment(void * fragmentContext)109   virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext) { }
110 
GetFileSize(OUT UINT64 * fileSize)111   virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize)
112   {
113     *fileSize = mSize;
114     return S_OK;
115   }
116 
GetLastWriteTime(OUT UINT64 * lastWriteTime)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 *
_hb_directwrite_shaper_face_data_create(hb_face_t * face)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
_hb_directwrite_shaper_face_data_destroy(hb_directwrite_shaper_face_data_t * data)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 *
_hb_directwrite_shaper_font_data_create(hb_font_t * font)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
_hb_directwrite_shaper_font_data_destroy(hb_directwrite_shaper_font_data_t * data)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 *
_hb_directwrite_shaper_shape_plan_data_create(hb_shape_plan_t * shape_plan HB_UNUSED,const hb_feature_t * user_features HB_UNUSED,unsigned int num_user_features HB_UNUSED,const int * coords HB_UNUSED,unsigned int num_coords HB_UNUSED)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
_hb_directwrite_shaper_shape_plan_data_destroy(hb_directwrite_shaper_shape_plan_data_t * data HB_UNUSED)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 
IFACEMETHOD(QueryInterface)281   IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; }
IFACEMETHOD_(ULONG,AddRef)282   IFACEMETHOD_(ULONG, AddRef)() { return 1; }
IFACEMETHOD_(ULONG,Release)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 
ContainsTextPositionTextAnalysis::Run298     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:
TextAnalysis(const wchar_t * text,uint32_t textLength,const wchar_t * localeName,DWRITE_READING_DIRECTION readingDirection)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 
~TextAnalysis()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 
GenerateResults(IDWriteTextAnalyzer * textAnalyzer,Run ** runHead)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 
GetTextAtPosition(uint32_t textPosition,OUT wchar_t const ** textString,OUT uint32_t * textLength)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 
GetTextBeforePosition(uint32_t textPosition,OUT wchar_t const ** textString,OUT uint32_t * textLength)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)
GetParagraphReadingDirection()387     GetParagraphReadingDirection() { return mReadingDirection; }
388 
GetLocaleName(uint32_t textPosition,uint32_t * textLength,wchar_t const ** localeName)389   IFACEMETHODIMP GetLocaleName(uint32_t textPosition,
390     uint32_t* textLength,
391     wchar_t const** localeName)
392   {
393     return S_OK;
394   }
395 
396   IFACEMETHODIMP
GetNumberSubstitution(uint32_t textPosition,OUT uint32_t * textLength,OUT IDWriteNumberSubstitution ** numberSubstitution)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
SetScriptAnalysis(uint32_t textPosition,uint32_t textLength,DWRITE_SCRIPT_ANALYSIS const * scriptAnalysis)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
SetLineBreakpoints(uint32_t textPosition,uint32_t textLength,const DWRITE_LINE_BREAKPOINT * lineBreakpoints)427     SetLineBreakpoints(uint32_t textPosition,
428     uint32_t textLength,
429     const DWRITE_LINE_BREAKPOINT* lineBreakpoints) { return S_OK; }
430 
SetBidiLevel(uint32_t textPosition,uint32_t textLength,uint8_t explicitLevel,uint8_t resolvedLevel)431   IFACEMETHODIMP SetBidiLevel(uint32_t textPosition,
432     uint32_t textLength,
433     uint8_t explicitLevel,
434     uint8_t resolvedLevel) { return S_OK; }
435 
436   IFACEMETHODIMP
SetNumberSubstitution(uint32_t textPosition,uint32_t textLength,IDWriteNumberSubstitution * numberSubstitution)437     SetNumberSubstitution(uint32_t textPosition,
438     uint32_t textLength,
439     IDWriteNumberSubstitution* numberSubstitution) { return S_OK; }
440 
441 protected:
FetchNextRun(IN OUT uint32_t * textLength)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 
SetCurrentRun(uint32_t textPosition)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 
SplitCurrentRun(uint32_t splitPosition)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 
hb_uint16_swap(const uint16_t v)536 static inline uint16_t hb_uint16_swap (const uint16_t v)
537 { return (v >> 8) | (v << 8); }
hb_uint32_swap(const uint32_t v)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
_hb_directwrite_shape_full(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features,float lineWidth)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
_hb_directwrite_shape(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features)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
hb_directwrite_shape_experimental_width(hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features,float width)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