blob: 3175fe0550bbb4b5589b3b3ee1f391fe214dcaa7 [file] [log] [blame]
Ebrahim Byagowife18c472018-03-25 18:19:23 +04301/*
Ebrahim Byagowid4907e82018-03-14 11:04:28 +03302 * Copyright © 2015-2018 Ebrahim Byagowi
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +04303 *
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
Behdad Esfahbod40ec3bb2017-11-03 16:57:30 -040025#include "hb-private.hh"
26#include "hb-debug.hh"
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043027#define HB_SHAPER directwrite
28#include "hb-shaper-impl-private.hh"
29
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +043030#include <DWrite_1.h>
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043031
32#include "hb-directwrite.h"
33
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043034
Ebrahim Byagowi79190332018-03-22 16:04:38 +043035HB_SHAPER_DATA_ENSURE_DEFINE (directwrite, face)
36HB_SHAPER_DATA_ENSURE_DEFINE (directwrite, font)
37
38
39/*
40 * hb-directwrite uses new/delete syntatically but as we let users
41 * to override malloc/free, we will redefine new/delete so users
42 * won't need to do that by their own.
43 */
44void* operator new (size_t size) { return malloc (size); }
45void* operator new [] (size_t size) { return malloc (size); }
46void operator delete (void* pointer) { free (pointer); }
47void operator delete [] (void* pointer) { free (pointer); }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043048
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043049
50/*
51 * DirectWrite font stream helpers
52 */
53
54// This is a font loader which provides only one font (unlike its original design).
55// For a better implementation which was also source of this
56// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla
57class DWriteFontFileLoader : public IDWriteFontFileLoader
58{
59private:
60 IDWriteFontFileStream *mFontFileStream;
61public:
Ebrahim Byagowi957e7752018-03-19 12:19:42 +033062 DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
Ebrahim Byagowid4907e82018-03-14 11:04:28 +033063 {
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043064 mFontFileStream = fontFileStream;
65 }
66
67 // IUnknown interface
Ebrahim Byagowi79190332018-03-22 16:04:38 +043068 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) { return S_OK; }
69 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
70 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043071
72 // IDWriteFontFileLoader methods
Ebrahim Byagowi79190332018-03-22 16:04:38 +043073 virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey (void const* fontFileReferenceKey,
Ebrahim Byagowife18c472018-03-25 18:19:23 +043074 uint32_t fontFileReferenceKeySize,
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043075 OUT IDWriteFontFileStream** fontFileStream)
76 {
77 *fontFileStream = mFontFileStream;
78 return S_OK;
79 }
80};
81
82class DWriteFontFileStream : public IDWriteFontFileStream
83{
84private:
85 uint8_t *mData;
86 uint32_t mSize;
87public:
Ebrahim Byagowi957e7752018-03-19 12:19:42 +033088 DWriteFontFileStream (uint8_t *aData, uint32_t aSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043089 {
90 mData = aData;
91 mSize = aSize;
92 }
93
94 // IUnknown interface
Ebrahim Byagowi79190332018-03-22 16:04:38 +043095 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) { return S_OK; }
96 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
97 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043098
99 // IDWriteFontFileStream methods
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430100 virtual HRESULT STDMETHODCALLTYPE ReadFileFragment (void const** fragmentStart,
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430101 UINT64 fileOffset,
102 UINT64 fragmentSize,
103 OUT void** fragmentContext)
104 {
105 // We are required to do bounds checking.
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430106 if (fileOffset + fragmentSize > mSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430107 return E_FAIL;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430108
109 // truncate the 64 bit fileOffset to size_t sized index into mData
110 size_t index = static_cast<size_t> (fileOffset);
111
112 // We should be alive for the duration of this.
113 *fragmentStart = &mData[index];
114 *fragmentContext = nullptr;
115 return S_OK;
116 }
117
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430118 virtual void STDMETHODCALLTYPE ReleaseFileFragment (void* fragmentContext) { }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430119
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430120 virtual HRESULT STDMETHODCALLTYPE GetFileSize (OUT UINT64* fileSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430121 {
122 *fileSize = mSize;
123 return S_OK;
124 }
125
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430126 virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime (OUT UINT64* lastWriteTime)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430127 {
128 return E_NOTIMPL;
129 }
130};
131
132
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430133/*
134* shaper face data
135*/
136
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430137struct hb_directwrite_shaper_face_data_t
138{
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430139 IDWriteFactory *dwriteFactory;
140 IDWriteFontFile *fontFile;
141 IDWriteFontFileStream *fontFileStream;
142 IDWriteFontFileLoader *fontFileLoader;
143 IDWriteFontFace *fontFace;
144 hb_blob_t *faceBlob;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430145};
146
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430147hb_directwrite_shaper_face_data_t *
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430148_hb_directwrite_shaper_face_data_create (hb_face_t *face)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430149{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430150 hb_directwrite_shaper_face_data_t *data = new hb_directwrite_shaper_face_data_t;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430151 if (unlikely (!data))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200152 return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430153
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430154 // TODO: factory and fontFileLoader should be cached separately
155 IDWriteFactory* dwriteFactory;
156 DWriteCreateFactory (
157 DWRITE_FACTORY_TYPE_SHARED,
158 __uuidof (IDWriteFactory),
159 (IUnknown**) &dwriteFactory
160 );
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430161
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430162 HRESULT hr;
Ebrahim Byagowibe565d12016-06-24 11:42:01 +0430163 hb_blob_t *blob = hb_face_reference_blob (face);
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330164 DWriteFontFileStream *fontFileStream = new DWriteFontFileStream (
165 (uint8_t *) hb_blob_get_data (blob, nullptr),
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330166 hb_blob_get_length (blob));
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430167
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330168 DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430169 dwriteFactory->RegisterFontFileLoader (fontFileLoader);
170
171 IDWriteFontFile *fontFile;
172 uint64_t fontFileKey = 0;
173 hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
174 fontFileLoader, &fontFile);
175
176#define FAIL(...) \
177 HB_STMT_START { \
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200178 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330179 return nullptr; \
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430180 } HB_STMT_END;
181
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430182 if (FAILED (hr))
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430183 FAIL ("Failed to load font file from data!");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430184
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430185 BOOL isSupported;
186 DWRITE_FONT_FILE_TYPE fileType;
187 DWRITE_FONT_FACE_TYPE faceType;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430188 uint32_t numberOfFaces;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430189 hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430190 if (FAILED (hr) || !isSupported)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430191 FAIL ("Font file is not supported.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430192
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430193#undef FAIL
194
195 IDWriteFontFace *fontFace;
196 dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
197 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
198
199 data->dwriteFactory = dwriteFactory;
200 data->fontFile = fontFile;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430201 data->fontFileStream = fontFileStream;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430202 data->fontFileLoader = fontFileLoader;
203 data->fontFace = fontFace;
Ebrahim Byagowibe565d12016-06-24 11:42:01 +0430204 data->faceBlob = blob;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430205
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430206 return data;
207}
208
209void
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430210_hb_directwrite_shaper_face_data_destroy (hb_directwrite_shaper_face_data_t *data)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430211{
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430212 if (data->fontFace)
213 data->fontFace->Release ();
214 if (data->fontFile)
215 data->fontFile->Release ();
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430216 if (data->dwriteFactory)
217 {
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430218 if (data->fontFileLoader)
ebraminio1e1825b2016-12-17 10:30:40 +0330219 data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
220 data->dwriteFactory->Release ();
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430221 }
222 if (data->fontFileLoader)
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330223 delete data->fontFileLoader;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430224 if (data->fontFileStream)
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330225 delete data->fontFileStream;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430226 if (data->faceBlob)
227 hb_blob_destroy (data->faceBlob);
228 if (data)
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430229 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430230}
231
232
233/*
234 * shaper font data
235 */
236
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430237struct hb_directwrite_shaper_font_data_t
238{
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430239};
240
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430241hb_directwrite_shaper_font_data_t *
242_hb_directwrite_shaper_font_data_create (hb_font_t *font)
243{
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200244 if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430245
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430246 hb_directwrite_shaper_font_data_t *data = new hb_directwrite_shaper_font_data_t;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430247 if (unlikely (!data))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200248 return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430249
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430250 return data;
251}
252
253void
254_hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data)
255{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430256 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430257}
258
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430259
260/*
261 * shaper shape_plan data
262 */
263
264struct hb_directwrite_shaper_shape_plan_data_t {};
265
266hb_directwrite_shaper_shape_plan_data_t *
267_hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED,
Behdad Esfahbod72ada4f2016-09-10 03:57:24 -0700268 const hb_feature_t *user_features HB_UNUSED,
269 unsigned int num_user_features HB_UNUSED,
270 const int *coords HB_UNUSED,
271 unsigned int num_coords HB_UNUSED)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430272{
273 return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
274}
275
276void
277_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan_data_t *data HB_UNUSED)
278{
279}
280
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430281// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430282// but now is relicensed to MIT for HarfBuzz use
283class TextAnalysis
284 : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
285{
286public:
287
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430288 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) { return S_OK; }
289 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
290 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430291
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330292 // A single contiguous run of characters containing the same analysis
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430293 // results.
294 struct Run
295 {
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000296 uint32_t mTextStart; // starting text position of this run
297 uint32_t mTextLength; // number of contiguous code units covered
298 uint32_t mGlyphStart; // starting glyph in the glyphs array
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330299 uint32_t mGlyphCount; // number of glyphs associated with this run
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430300 // text
301 DWRITE_SCRIPT_ANALYSIS mScript;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000302 uint8_t mBidiLevel;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430303 bool mIsSideways;
304
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430305 inline bool ContainsTextPosition (uint32_t aTextPosition) const
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430306 {
307 return aTextPosition >= mTextStart
308 && aTextPosition < mTextStart + mTextLength;
309 }
310
311 Run *nextRun;
312 };
313
314public:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430315 TextAnalysis (const wchar_t* text,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000316 uint32_t textLength,
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430317 const wchar_t* localeName,
318 DWRITE_READING_DIRECTION readingDirection)
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430319 : mText (text)
320 , mTextLength (textLength)
321 , mLocaleName (localeName)
322 , mReadingDirection (readingDirection)
323 , mCurrentRun (nullptr) { };
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430324
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430325 ~TextAnalysis ()
326 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430327 // delete runs, except mRunHead which is part of the TextAnalysis object
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430328 for (Run *run = mRunHead.nextRun; run;)
329 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430330 Run *origRun = run;
331 run = run->nextRun;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430332 delete origRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430333 }
334 }
335
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430336 STDMETHODIMP GenerateResults (IDWriteTextAnalyzer* textAnalyzer,
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430337 Run **runHead)
338 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430339 // Analyzes the text using the script analyzer and returns
340 // the result as a series of runs.
341
342 HRESULT hr = S_OK;
343
344 // Initially start out with one result that covers the entire range.
345 // This result will be subdivided by the analysis processes.
346 mRunHead.mTextStart = 0;
347 mRunHead.mTextLength = mTextLength;
348 mRunHead.mBidiLevel =
349 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200350 mRunHead.nextRun = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430351 mCurrentRun = &mRunHead;
352
353 // Call each of the analyzers in sequence, recording their results.
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430354 if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430355 *runHead = &mRunHead;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430356
357 return hr;
358 }
359
360 // IDWriteTextAnalysisSource implementation
361
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430362 IFACEMETHODIMP GetTextAtPosition (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000363 OUT wchar_t const** textString,
364 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430365 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430366 if (textPosition >= mTextLength)
367 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430368 // No text at this position, valid query though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200369 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430370 *textLength = 0;
371 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430372 else
373 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430374 *textString = mText + textPosition;
375 *textLength = mTextLength - textPosition;
376 }
377 return S_OK;
378 }
379
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430380 IFACEMETHODIMP GetTextBeforePosition (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000381 OUT wchar_t const** textString,
382 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430383 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430384 if (textPosition == 0 || textPosition > mTextLength)
385 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430386 // Either there is no text before here (== 0), or this
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700387 // is an invalid position. The query is considered valid though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200388 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430389 *textLength = 0;
390 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430391 else
392 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430393 *textString = mText;
394 *textLength = textPosition;
395 }
396 return S_OK;
397 }
398
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430399 IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
400 GetParagraphReadingDirection () { return mReadingDirection; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430401
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430402 IFACEMETHODIMP GetLocaleName (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000403 uint32_t* textLength,
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430404 wchar_t const** localeName)
405 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430406 return S_OK;
407 }
408
409 IFACEMETHODIMP
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430410 GetNumberSubstitution (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000411 OUT uint32_t* textLength,
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430412 OUT IDWriteNumberSubstitution** numberSubstitution)
413 {
414 // We do not support number substitution.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200415 *numberSubstitution = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430416 *textLength = mTextLength - textPosition;
417
418 return S_OK;
419 }
420
421 // IDWriteTextAnalysisSink implementation
422
423 IFACEMETHODIMP
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430424 SetScriptAnalysis (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000425 uint32_t textLength,
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430426 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
427 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430428 SetCurrentRun (textPosition);
429 SplitCurrentRun (textPosition);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430430 while (textLength > 0)
431 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430432 Run *run = FetchNextRun (&textLength);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430433 run->mScript = *scriptAnalysis;
434 }
435
436 return S_OK;
437 }
438
439 IFACEMETHODIMP
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430440 SetLineBreakpoints (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000441 uint32_t textLength,
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430442 const DWRITE_LINE_BREAKPOINT* lineBreakpoints) { return S_OK; }
443
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430444 IFACEMETHODIMP SetBidiLevel (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000445 uint32_t textLength,
446 uint8_t explicitLevel,
447 uint8_t resolvedLevel) { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430448
449 IFACEMETHODIMP
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430450 SetNumberSubstitution (uint32_t textPosition,
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000451 uint32_t textLength,
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430452 IDWriteNumberSubstitution* numberSubstitution) { return S_OK; }
453
454protected:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430455 Run *FetchNextRun (IN OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430456 {
457 // Used by the sink setters, this returns a reference to the next run.
458 // Position and length are adjusted to now point after the current run
459 // being returned.
460
461 Run *origRun = mCurrentRun;
462 // Split the tail if needed (the length remaining is less than the
463 // current run's size).
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430464 if (*textLength < mCurrentRun->mTextLength)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430465 SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430466 else
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430467 // Just advance the current run.
468 mCurrentRun = mCurrentRun->nextRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430469 *textLength -= origRun->mTextLength;
470
471 // Return a reference to the run that was just current.
472 return origRun;
473 }
474
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430475 void SetCurrentRun (uint32_t textPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430476 {
477 // Move the current run to the given position.
478 // Since the analyzers generally return results in a forward manner,
479 // this will usually just return early. If not, find the
480 // corresponding run for the text position.
481
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430482 if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430483 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430484
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430485 for (Run *run = &mRunHead; run; run = run->nextRun)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430486 if (run->ContainsTextPosition (textPosition))
487 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430488 mCurrentRun = run;
489 return;
490 }
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430491 //NS_NOTREACHED ("We should always be able to find the text position in one \
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430492 // of our runs");
493 }
494
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430495 void SplitCurrentRun (uint32_t splitPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430496 {
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430497 if (!mCurrentRun)
498 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430499 //NS_ASSERTION (false, "SplitCurrentRun called without current run.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430500 // Shouldn't be calling this when no current run is set!
501 return;
502 }
503 // Split the current run.
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430504 if (splitPosition <= mCurrentRun->mTextStart)
505 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430506 // No need to split, already the start of a run
507 // or before it. Usually the first.
508 return;
509 }
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430510 Run *newRun = new Run;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430511
512 *newRun = *mCurrentRun;
513
514 // Insert the new run in our linked list.
515 newRun->nextRun = mCurrentRun->nextRun;
516 mCurrentRun->nextRun = newRun;
517
518 // Adjust runs' text positions and lengths.
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000519 uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430520 newRun->mTextStart += splitPoint;
521 newRun->mTextLength -= splitPoint;
522 mCurrentRun->mTextLength = splitPoint;
523 mCurrentRun = newRun;
524 }
525
526protected:
527 // Input
528 // (weak references are fine here, since this class is a transient
529 // stack-based helper that doesn't need to copy data)
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000530 uint32_t mTextLength;
531 const wchar_t* mText;
532 const wchar_t* mLocaleName;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430533 DWRITE_READING_DIRECTION mReadingDirection;
534
535 // Current processing state.
536 Run *mCurrentRun;
537
538 // Output is a list of runs starting here
539 Run mRunHead;
540};
541
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430542static inline uint16_t hb_uint16_swap (const uint16_t v)
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000543{ return (v >> 8) | (v << 8); }
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430544static inline uint32_t hb_uint32_swap (const uint32_t v)
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430545{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430546
547/*
548 * shaper
549 */
550
ebraminio1e1825b2016-12-17 10:30:40 +0330551static hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430552_hb_directwrite_shape_full (hb_shape_plan_t *shape_plan,
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430553 hb_font_t *font,
554 hb_buffer_t *buffer,
555 const hb_feature_t *features,
ebraminio1e1825b2016-12-17 10:30:40 +0330556 unsigned int num_features,
557 float lineWidth)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430558{
559 hb_face_t *face = font->face;
560 hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
561 hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430562 IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
563 IDWriteFontFace *fontFace = face_data->fontFace;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430564
565 IDWriteTextAnalyzer* analyzer;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430566 dwriteFactory->CreateTextAnalyzer (&analyzer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430567
568 unsigned int scratch_size;
569 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
570#define ALLOCATE_ARRAY(Type, name, len) \
571 Type *name = (Type *) scratch; \
572 { \
573 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
574 assert (_consumed <= scratch_size); \
575 scratch += _consumed; \
576 scratch_size -= _consumed; \
577 }
578
579#define utf16_index() var1.u32
580
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430581 ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430582
583 unsigned int chars_len = 0;
584 for (unsigned int i = 0; i < buffer->len; i++)
585 {
586 hb_codepoint_t c = buffer->info[i].codepoint;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430587 buffer->info[i].utf16_index () = chars_len;
588 if (likely (c <= 0xFFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000589 textString[chars_len++] = c;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430590 else if (unlikely (c > 0x10FFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000591 textString[chars_len++] = 0xFFFDu;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430592 else
593 {
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000594 textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
Behdad Esfahbod33317312016-08-08 17:24:04 -0700595 textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430596 }
597 }
598
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430599 ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430600 /* Need log_clusters to assign features. */
601 chars_len = 0;
602 for (unsigned int i = 0; i < buffer->len; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430603 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430604 hb_codepoint_t c = buffer->info[i].codepoint;
605 unsigned int cluster = buffer->info[i].cluster;
606 log_clusters[chars_len++] = cluster;
607 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
608 log_clusters[chars_len++] = cluster; /* Surrogates. */
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430609 }
610
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430611 // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
612
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330613 DWRITE_READING_DIRECTION readingDirection = buffer->props.direction ?
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430614 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
615 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
616
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400617 /*
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430618 * There's an internal 16-bit limit on some things inside the analyzer,
619 * but we never attempt to shape a word longer than 64K characters
620 * in a single gfxShapedWord, so we cannot exceed that limit.
621 */
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000622 uint32_t textLength = buffer->len;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430623
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430624 TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430625 TextAnalysis::Run *runHead;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430626 HRESULT hr;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430627 hr = analysis.GenerateResults (analyzer, &runHead);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430628
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000629#define FAIL(...) \
630 HB_STMT_START { \
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200631 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000632 return false; \
633 } HB_STMT_END;
634
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000635 if (FAILED (hr))
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000636 FAIL ("Analyzer failed to generate results.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430637
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000638 uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
639 uint32_t glyphCount;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000640 bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430641
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000642 const wchar_t localeName[20] = {0};
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200643 if (buffer->props.language != nullptr)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000644 {
645 mbstowcs ((wchar_t*) localeName,
646 hb_language_to_string (buffer->props.language), 20);
Ebrahim Byagowid691ba32016-03-30 20:21:40 +0000647 }
648
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430649 // TODO: it does work but doesn't care about ranges
650 DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
651 typographic_features.featureCount = num_features;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000652 if (num_features)
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000653 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430654 typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000655 for (unsigned int i = 0; i < num_features; ++i)
656 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430657 typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000658 hb_uint32_swap (features[i].tag);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430659 typographic_features.features[i].parameter = features[i].value;
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000660 }
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000661 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000662 const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures =
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430663 (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000664 const uint32_t featureRangeLengths[] = { textLength };
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430665 //
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430666
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430667 uint16_t* clusterMap = new uint16_t[textLength];
668 DWRITE_SHAPING_TEXT_PROPERTIES* textProperties =
669 new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
Ebrahim Byagowi8179ff52016-06-27 03:54:15 +0430670retry_getglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430671 uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
672 DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties =
673 new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000674
Behdad Esfahbod5dfd3412017-01-22 16:55:40 -0800675 hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200676 isRightToLeft, &runHead->mScript, localeName, nullptr, &dwFeatures,
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000677 featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
678 glyphProperties, &glyphCount);
679
680 if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
681 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430682 delete [] glyphIndices;
683 delete [] glyphProperties;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430684
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000685 maxGlyphCount *= 2;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430686
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000687 goto retry_getglyphs;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430688 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000689 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000690 FAIL ("Analyzer failed to get glyphs.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430691
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430692 float* glyphAdvances = new float[maxGlyphCount];
693 DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430694
695 /* The -2 in the following is to compensate for possible
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430696 * alignment needed after the WORD array. sizeof (WORD) == 2. */
697 unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
698 / (sizeof (WORD) +
699 sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
700 sizeof (int) +
701 sizeof (DWRITE_GLYPH_OFFSET) +
702 sizeof (uint32_t));
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000703 ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430704
705#undef ALLOCATE_ARRAY
706
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430707 int fontEmSize = font->face->get_upem ();
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000708 if (fontEmSize < 0)
709 fontEmSize = -fontEmSize;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000710
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000711 if (fontEmSize < 0)
712 fontEmSize = -fontEmSize;
713 double x_mult = (double) font->x_scale / fontEmSize;
714 double y_mult = (double) font->y_scale / fontEmSize;
Ebrahim Byagowi1c00a462016-03-30 20:15:09 +0000715
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000716 hr = analyzer->GetGlyphPlacements (textString,
717 clusterMap, textProperties, textLength, glyphIndices,
718 glyphProperties, glyphCount, fontFace, fontEmSize,
Behdad Esfahbod5dfd3412017-01-22 16:55:40 -0800719 false, isRightToLeft, &runHead->mScript, localeName,
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000720 &dwFeatures, featureRangeLengths, 1,
721 glyphAdvances, glyphOffsets);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430722
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000723 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000724 FAIL ("Analyzer failed to get glyph placements.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430725
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430726 IDWriteTextAnalyzer1* analyzer1;
727 analyzer->QueryInterface (&analyzer1);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000728
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430729 if (analyzer1 && lineWidth)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000730 {
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000731
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430732 DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430733 new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430734 hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize,
735 runHead->mScript, textLength, glyphCount, textString, clusterMap,
736 glyphProperties, justificationOpportunities);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000737
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430738 if (FAILED (hr))
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430739 FAIL ("Analyzer failed to get justification opportunities.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430740
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430741 float* justifiedGlyphAdvances = new float[maxGlyphCount];
742 DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430743 hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
744 glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets);
745
746 if (FAILED (hr))
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430747 FAIL ("Analyzer failed to get justified glyph advances.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430748
749 DWRITE_SCRIPT_PROPERTIES scriptProperties;
750 hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
751 if (FAILED (hr))
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430752 FAIL ("Analyzer failed to get script properties.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430753 uint32_t justificationCharacter = scriptProperties.justificationCharacter;
754
755 // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
756 if (justificationCharacter != 32)
757 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430758 uint16_t* modifiedClusterMap = new uint16_t[textLength];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430759 retry_getjustifiedglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430760 uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
761 float* modifiedGlyphAdvances = new float[maxGlyphCount];
762 DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets =
763 new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430764 uint32_t actualGlyphsCount;
765 hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000766 textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices,
767 glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets,
768 glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices,
769 modifiedGlyphAdvances, modifiedGlyphOffsets);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000770
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430771 if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
772 {
773 maxGlyphCount = actualGlyphsCount;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430774 delete [] modifiedGlyphIndices;
775 delete [] modifiedGlyphAdvances;
776 delete [] modifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000777
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430778 maxGlyphCount = actualGlyphsCount;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000779
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430780 goto retry_getjustifiedglyphs;
781 }
782 if (FAILED (hr))
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430783 FAIL ("Analyzer failed to get justified glyphs.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430784
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430785 delete [] clusterMap;
786 delete [] glyphIndices;
787 delete [] glyphAdvances;
788 delete [] glyphOffsets;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430789
790 glyphCount = actualGlyphsCount;
791 clusterMap = modifiedClusterMap;
792 glyphIndices = modifiedGlyphIndices;
793 glyphAdvances = modifiedGlyphAdvances;
794 glyphOffsets = modifiedGlyphOffsets;
795
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430796 delete [] justifiedGlyphAdvances;
797 delete [] justifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000798 }
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430799 else
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000800 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430801 delete [] glyphAdvances;
802 delete [] glyphOffsets;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430803
804 glyphAdvances = justifiedGlyphAdvances;
805 glyphOffsets = justifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000806 }
807
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430808 delete [] justificationOpportunities;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000809
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000810 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000811
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430812 /* Ok, we've got everything we need, now compose output buffer,
813 * very, *very*, carefully! */
814
815 /* Calculate visual-clusters. That's what we ship. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000816 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430817 vis_clusters[i] = -1;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000818 for (unsigned int i = 0; i < buffer->len; i++)
819 {
820 uint32_t *p =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430821 &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000822 *p = MIN (*p, buffer->info[i].cluster);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430823 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000824 for (unsigned int i = 1; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430825 if (vis_clusters[i] == -1)
826 vis_clusters[i] = vis_clusters[i - 1];
827
828#undef utf16_index
829
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000830 if (unlikely (!buffer->ensure (glyphCount)))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000831 FAIL ("Buffer in error");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430832
833#undef FAIL
834
835 /* Set glyph infos */
836 buffer->len = 0;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000837 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430838 {
839 hb_glyph_info_t *info = &buffer->info[buffer->len++];
840
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000841 info->codepoint = glyphIndices[i];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430842 info->cluster = vis_clusters[i];
843
844 /* The rest is crap. Let's store position info there for now. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000845 info->mask = glyphAdvances[i];
846 info->var1.i32 = glyphOffsets[i].advanceOffset;
847 info->var2.i32 = glyphOffsets[i].ascenderOffset;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430848 }
849
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430850 /* Set glyph positions */
851 buffer->clear_positions ();
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000852 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430853 {
854 hb_glyph_info_t *info = &buffer->info[i];
855 hb_glyph_position_t *pos = &buffer->pos[i];
856
857 /* TODO vertical */
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000858 pos->x_advance = x_mult * (int32_t) info->mask;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000859 pos->x_offset =
860 x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000861 pos->y_offset = y_mult * info->var2.i32;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430862 }
863
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000864 if (isRightToLeft)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430865 hb_buffer_reverse (buffer);
866
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430867 delete [] clusterMap;
868 delete [] glyphIndices;
869 delete [] textProperties;
870 delete [] glyphProperties;
871 delete [] glyphAdvances;
872 delete [] glyphOffsets;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000873
874 if (num_features)
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430875 delete [] typographic_features.features;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000876
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430877 /* Wow, done! */
878 return true;
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400879}
ebraminio1e1825b2016-12-17 10:30:40 +0330880
881hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430882_hb_directwrite_shape (hb_shape_plan_t *shape_plan,
ebraminio1e1825b2016-12-17 10:30:40 +0330883 hb_font_t *font,
884 hb_buffer_t *buffer,
885 const hb_feature_t *features,
886 unsigned int num_features)
887{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430888 return _hb_directwrite_shape_full (shape_plan, font, buffer,
ebraminio1e1825b2016-12-17 10:30:40 +0330889 features, num_features, 0);
890}
891
892/*
893 * Public [experimental] API
894 */
895
896hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430897hb_directwrite_shape_experimental_width (hb_font_t *font,
ebraminio1e1825b2016-12-17 10:30:40 +0330898 hb_buffer_t *buffer,
899 const hb_feature_t *features,
900 unsigned int num_features,
901 float width)
902{
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330903 static const char *shapers = "directwrite";
ebraminio1e1825b2016-12-17 10:30:40 +0330904 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face,
905 &buffer->props, features, num_features, &shapers);
906 hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
907 features, num_features, width);
908
Behdad Esfahbode4da3802017-11-10 17:14:27 -0800909 buffer->unsafe_to_break_all ();
ebraminio1e1825b2016-12-17 10:30:40 +0330910
911 return res;
912}