blob: b135c49e994061a83ec7c9a54be9d6f2f850fa52 [file] [log] [blame]
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +04301/*
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +03302 * Copyright © 2015-2019 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 Esfahbodc77ae402018-08-25 22:36:36 -070025#include "hb.hh"
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070026#include "hb-shaper-impl.hh"
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043027
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +043028#include <DWrite_1.h>
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043029
30#include "hb-directwrite.h"
31
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043032
Ebrahim Byagowi79190332018-03-22 16:04:38 +043033/*
34 * hb-directwrite uses new/delete syntatically but as we let users
35 * to override malloc/free, we will redefine new/delete so users
36 * won't need to do that by their own.
37 */
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033038void* operator new (size_t size) { return malloc (size); }
39void* operator new [] (size_t size) { return malloc (size); }
40void operator delete (void* pointer) { free (pointer); }
Ebrahim Byagowi79190332018-03-22 16:04:38 +043041void operator delete [] (void* pointer) { free (pointer); }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043042
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043043
44/*
45 * DirectWrite font stream helpers
46 */
47
48// This is a font loader which provides only one font (unlike its original design).
49// For a better implementation which was also source of this
50// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla
51class DWriteFontFileLoader : public IDWriteFontFileLoader
52{
53private:
54 IDWriteFontFileStream *mFontFileStream;
55public:
Ebrahim Byagowi957e7752018-03-19 12:19:42 +033056 DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033057 { mFontFileStream = fontFileStream; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043058
59 // IUnknown interface
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033060 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
61 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +033062 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
63 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043064
65 // IDWriteFontFileLoader methods
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033066 virtual HRESULT STDMETHODCALLTYPE
67 CreateStreamFromKey (void const* fontFileReferenceKey,
68 uint32_t fontFileReferenceKeySize,
69 OUT IDWriteFontFileStream** fontFileStream)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043070 {
71 *fontFileStream = mFontFileStream;
72 return S_OK;
73 }
Ebrahim Byagowi0c2bd1b2019-01-19 16:30:07 +033074
75 virtual ~DWriteFontFileLoader() {}
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043076};
77
78class DWriteFontFileStream : public IDWriteFontFileStream
79{
80private:
81 uint8_t *mData;
82 uint32_t mSize;
83public:
Ebrahim Byagowi957e7752018-03-19 12:19:42 +033084 DWriteFontFileStream (uint8_t *aData, uint32_t aSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043085 {
86 mData = aData;
87 mSize = aSize;
88 }
89
90 // IUnknown interface
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033091 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
92 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +033093 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
94 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043095
96 // IDWriteFontFileStream methods
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033097 virtual HRESULT STDMETHODCALLTYPE
98 ReadFileFragment (void const** fragmentStart,
99 UINT64 fileOffset,
100 UINT64 fragmentSize,
101 OUT void** fragmentContext)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430102 {
103 // We are required to do bounds checking.
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330104 if (fileOffset + fragmentSize > mSize) return E_FAIL;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430105
106 // truncate the 64 bit fileOffset to size_t sized index into mData
107 size_t index = static_cast<size_t> (fileOffset);
108
109 // We should be alive for the duration of this.
110 *fragmentStart = &mData[index];
111 *fragmentContext = nullptr;
112 return S_OK;
113 }
114
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330115 virtual void STDMETHODCALLTYPE
116 ReleaseFileFragment (void* fragmentContext) {}
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430117
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330118 virtual HRESULT STDMETHODCALLTYPE
119 GetFileSize (OUT UINT64* fileSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430120 {
121 *fileSize = mSize;
122 return S_OK;
123 }
124
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330125 virtual HRESULT STDMETHODCALLTYPE
126 GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
Ebrahim Byagowi0c2bd1b2019-01-19 16:30:07 +0330127
128 virtual ~DWriteFontFileStream() {}
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430129};
130
131
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430132/*
133* shaper face data
134*/
135
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700136struct hb_directwrite_face_data_t
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430137{
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430138 IDWriteFactory *dwriteFactory;
139 IDWriteFontFile *fontFile;
Ebrahim Byagowi9714d3e2019-01-18 21:55:21 +0330140 DWriteFontFileStream *fontFileStream;
141 DWriteFontFileLoader *fontFileLoader;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430142 IDWriteFontFace *fontFace;
143 hb_blob_t *faceBlob;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430144};
145
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700146hb_directwrite_face_data_t *
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430147_hb_directwrite_shaper_face_data_create (hb_face_t *face)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430148{
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700149 hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430150 if (unlikely (!data))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200151 return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430152
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430153 // TODO: factory and fontFileLoader should be cached separately
154 IDWriteFactory* dwriteFactory;
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330155 DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
156 (IUnknown**) &dwriteFactory);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430157
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430158 HRESULT hr;
Ebrahim Byagowibe565d12016-06-24 11:42:01 +0430159 hb_blob_t *blob = hb_face_reference_blob (face);
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330160 DWriteFontFileStream *fontFileStream;
161 fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
162 hb_blob_get_length (blob));
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430163
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330164 DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430165 dwriteFactory->RegisterFontFileLoader (fontFileLoader);
166
167 IDWriteFontFile *fontFile;
168 uint64_t fontFileKey = 0;
169 hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330170 fontFileLoader, &fontFile);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430171
172#define FAIL(...) \
173 HB_STMT_START { \
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200174 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330175 return nullptr; \
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430176 } HB_STMT_END;
177
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430178 if (FAILED (hr))
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430179 FAIL ("Failed to load font file from data!");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430180
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430181 BOOL isSupported;
182 DWRITE_FONT_FILE_TYPE fileType;
183 DWRITE_FONT_FACE_TYPE faceType;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430184 uint32_t numberOfFaces;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430185 hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430186 if (FAILED (hr) || !isSupported)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430187 FAIL ("Font file is not supported.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430188
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430189#undef FAIL
190
191 IDWriteFontFace *fontFace;
192 dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330193 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430194
195 data->dwriteFactory = dwriteFactory;
196 data->fontFile = fontFile;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430197 data->fontFileStream = fontFileStream;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430198 data->fontFileLoader = fontFileLoader;
199 data->fontFace = fontFace;
Ebrahim Byagowibe565d12016-06-24 11:42:01 +0430200 data->faceBlob = blob;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430201
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430202 return data;
203}
204
205void
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700206_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430207{
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430208 if (data->fontFace)
209 data->fontFace->Release ();
210 if (data->fontFile)
211 data->fontFile->Release ();
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430212 if (data->dwriteFactory)
213 {
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430214 if (data->fontFileLoader)
ebraminio1e1825b2016-12-17 10:30:40 +0330215 data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
216 data->dwriteFactory->Release ();
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430217 }
218 if (data->fontFileLoader)
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330219 delete data->fontFileLoader;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430220 if (data->fontFileStream)
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330221 delete data->fontFileStream;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430222 if (data->faceBlob)
223 hb_blob_destroy (data->faceBlob);
224 if (data)
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430225 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430226}
227
228
229/*
230 * shaper font data
231 */
232
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330233struct hb_directwrite_font_data_t {};
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430234
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700235hb_directwrite_font_data_t *
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430236_hb_directwrite_shaper_font_data_create (hb_font_t *font)
237{
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700238 hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430239 if (unlikely (!data))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200240 return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430241
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430242 return data;
243}
244
245void
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700246_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430247{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430248 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430249}
250
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430251
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430252// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430253// but now is relicensed to MIT for HarfBuzz use
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330254class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430255{
256public:
257
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330258 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
259 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330260 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
261 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430262
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330263 // A single contiguous run of characters containing the same analysis
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430264 // results.
265 struct Run
266 {
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000267 uint32_t mTextStart; // starting text position of this run
268 uint32_t mTextLength; // number of contiguous code units covered
269 uint32_t mGlyphStart; // starting glyph in the glyphs array
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330270 uint32_t mGlyphCount; // number of glyphs associated with this run
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430271 // text
272 DWRITE_SCRIPT_ANALYSIS mScript;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000273 uint8_t mBidiLevel;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430274 bool mIsSideways;
275
Ebrahim Byagowib2ebaa92018-12-16 22:38:10 +0330276 bool ContainsTextPosition (uint32_t aTextPosition) const
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430277 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430278 return aTextPosition >= mTextStart &&
279 aTextPosition < mTextStart + mTextLength;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430280 }
281
282 Run *nextRun;
283 };
284
285public:
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330286 TextAnalysis (const wchar_t* text, uint32_t textLength,
287 const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
Behdad Esfahbode6527222019-01-18 12:22:07 -0500288 : mTextLength (textLength), mText (text), mLocaleName (localeName),
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330289 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330290 ~TextAnalysis ()
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430291 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430292 // delete runs, except mRunHead which is part of the TextAnalysis object
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430293 for (Run *run = mRunHead.nextRun; run;)
294 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430295 Run *origRun = run;
296 run = run->nextRun;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430297 delete origRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430298 }
299 }
300
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330301 STDMETHODIMP
302 GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430303 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430304 // Analyzes the text using the script analyzer and returns
305 // the result as a series of runs.
306
307 HRESULT hr = S_OK;
308
309 // Initially start out with one result that covers the entire range.
310 // This result will be subdivided by the analysis processes.
311 mRunHead.mTextStart = 0;
312 mRunHead.mTextLength = mTextLength;
313 mRunHead.mBidiLevel =
314 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200315 mRunHead.nextRun = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430316 mCurrentRun = &mRunHead;
317
318 // Call each of the analyzers in sequence, recording their results.
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430319 if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430320 *runHead = &mRunHead;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430321
322 return hr;
323 }
324
325 // IDWriteTextAnalysisSource implementation
326
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330327 IFACEMETHODIMP
328 GetTextAtPosition (uint32_t textPosition,
329 OUT wchar_t const** textString,
330 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430331 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430332 if (textPosition >= mTextLength)
333 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430334 // No text at this position, valid query though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200335 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430336 *textLength = 0;
337 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430338 else
339 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430340 *textString = mText + textPosition;
341 *textLength = mTextLength - textPosition;
342 }
343 return S_OK;
344 }
345
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330346 IFACEMETHODIMP
347 GetTextBeforePosition (uint32_t textPosition,
348 OUT wchar_t const** textString,
349 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430350 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430351 if (textPosition == 0 || textPosition > mTextLength)
352 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430353 // Either there is no text before here (== 0), or this
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700354 // is an invalid position. The query is considered valid though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200355 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430356 *textLength = 0;
357 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430358 else
359 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430360 *textString = mText;
361 *textLength = textPosition;
362 }
363 return S_OK;
364 }
365
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430366 IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330367 GetParagraphReadingDirection () { return mReadingDirection; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430368
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330369 IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
370 wchar_t const** localeName)
371 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430372
373 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330374 GetNumberSubstitution (uint32_t textPosition,
375 OUT uint32_t* textLength,
376 OUT IDWriteNumberSubstitution** numberSubstitution)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430377 {
378 // We do not support number substitution.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200379 *numberSubstitution = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430380 *textLength = mTextLength - textPosition;
381
382 return S_OK;
383 }
384
385 // IDWriteTextAnalysisSink implementation
386
387 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330388 SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
389 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430390 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430391 SetCurrentRun (textPosition);
392 SplitCurrentRun (textPosition);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430393 while (textLength > 0)
394 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430395 Run *run = FetchNextRun (&textLength);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430396 run->mScript = *scriptAnalysis;
397 }
398
399 return S_OK;
400 }
401
402 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330403 SetLineBreakpoints (uint32_t textPosition,
404 uint32_t textLength,
405 const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
406 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430407
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330408 IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
409 uint8_t explicitLevel, uint8_t resolvedLevel)
410 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430411
412 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330413 SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
414 IDWriteNumberSubstitution* numberSubstitution)
415 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430416
417protected:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430418 Run *FetchNextRun (IN OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430419 {
420 // Used by the sink setters, this returns a reference to the next run.
421 // Position and length are adjusted to now point after the current run
422 // being returned.
423
424 Run *origRun = mCurrentRun;
425 // Split the tail if needed (the length remaining is less than the
426 // current run's size).
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430427 if (*textLength < mCurrentRun->mTextLength)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430428 SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430429 else
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430430 // Just advance the current run.
431 mCurrentRun = mCurrentRun->nextRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430432 *textLength -= origRun->mTextLength;
433
434 // Return a reference to the run that was just current.
435 return origRun;
436 }
437
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430438 void SetCurrentRun (uint32_t textPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430439 {
440 // Move the current run to the given position.
441 // Since the analyzers generally return results in a forward manner,
442 // this will usually just return early. If not, find the
443 // corresponding run for the text position.
444
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430445 if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430446 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430447
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430448 for (Run *run = &mRunHead; run; run = run->nextRun)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430449 if (run->ContainsTextPosition (textPosition))
450 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430451 mCurrentRun = run;
452 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430453 }
Ebrahim Byagowia57f5a12018-04-28 13:58:55 +0430454 assert (0); // We should always be able to find the text position in one of our runs
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430455 }
456
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430457 void SplitCurrentRun (uint32_t splitPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430458 {
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430459 if (!mCurrentRun)
460 {
Ebrahim Byagowia57f5a12018-04-28 13:58:55 +0430461 assert (0); // SplitCurrentRun called without current run
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430462 // Shouldn't be calling this when no current run is set!
463 return;
464 }
465 // Split the current run.
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430466 if (splitPosition <= mCurrentRun->mTextStart)
467 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430468 // No need to split, already the start of a run
469 // or before it. Usually the first.
470 return;
471 }
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430472 Run *newRun = new Run;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430473
474 *newRun = *mCurrentRun;
475
476 // Insert the new run in our linked list.
477 newRun->nextRun = mCurrentRun->nextRun;
478 mCurrentRun->nextRun = newRun;
479
480 // Adjust runs' text positions and lengths.
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000481 uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430482 newRun->mTextStart += splitPoint;
483 newRun->mTextLength -= splitPoint;
484 mCurrentRun->mTextLength = splitPoint;
485 mCurrentRun = newRun;
486 }
487
488protected:
489 // Input
490 // (weak references are fine here, since this class is a transient
491 // stack-based helper that doesn't need to copy data)
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000492 uint32_t mTextLength;
493 const wchar_t* mText;
494 const wchar_t* mLocaleName;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430495 DWRITE_READING_DIRECTION mReadingDirection;
496
497 // Current processing state.
498 Run *mCurrentRun;
499
500 // Output is a list of runs starting here
501 Run mRunHead;
502};
503
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430504static inline uint16_t hb_uint16_swap (const uint16_t v)
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000505{ return (v >> 8) | (v << 8); }
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430506static inline uint32_t hb_uint32_swap (const uint32_t v)
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430507{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430508
509/*
510 * shaper
511 */
512
ebraminio1e1825b2016-12-17 10:30:40 +0330513static hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430514_hb_directwrite_shape_full (hb_shape_plan_t *shape_plan,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330515 hb_font_t *font,
516 hb_buffer_t *buffer,
517 const hb_feature_t *features,
518 unsigned int num_features,
519 float lineWidth)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430520{
521 hb_face_t *face = font->face;
Behdad Esfahbodce5da0f2018-11-16 02:29:13 -0500522 const hb_directwrite_face_data_t *face_data = face->data.directwrite;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430523 IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
524 IDWriteFontFace *fontFace = face_data->fontFace;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430525
526 IDWriteTextAnalyzer* analyzer;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430527 dwriteFactory->CreateTextAnalyzer (&analyzer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430528
529 unsigned int scratch_size;
530 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
531#define ALLOCATE_ARRAY(Type, name, len) \
532 Type *name = (Type *) scratch; \
Behdad Esfahbod68e12e62019-05-13 17:28:59 -0700533 do { \
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430534 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
535 assert (_consumed <= scratch_size); \
536 scratch += _consumed; \
537 scratch_size -= _consumed; \
Behdad Esfahbod68e12e62019-05-13 17:28:59 -0700538 } while (0)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430539
540#define utf16_index() var1.u32
541
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430542 ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430543
544 unsigned int chars_len = 0;
545 for (unsigned int i = 0; i < buffer->len; i++)
546 {
547 hb_codepoint_t c = buffer->info[i].codepoint;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430548 buffer->info[i].utf16_index () = chars_len;
549 if (likely (c <= 0xFFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000550 textString[chars_len++] = c;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430551 else if (unlikely (c > 0x10FFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000552 textString[chars_len++] = 0xFFFDu;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430553 else
554 {
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000555 textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
Behdad Esfahbod33317312016-08-08 17:24:04 -0700556 textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430557 }
558 }
559
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430560 ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430561 /* Need log_clusters to assign features. */
562 chars_len = 0;
563 for (unsigned int i = 0; i < buffer->len; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430564 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430565 hb_codepoint_t c = buffer->info[i].codepoint;
566 unsigned int cluster = buffer->info[i].cluster;
567 log_clusters[chars_len++] = cluster;
568 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
569 log_clusters[chars_len++] = cluster; /* Surrogates. */
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430570 }
571
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430572 // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
573
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330574 DWRITE_READING_DIRECTION readingDirection;
575 readingDirection = buffer->props.direction ?
576 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
577 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430578
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400579 /*
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430580 * There's an internal 16-bit limit on some things inside the analyzer,
581 * but we never attempt to shape a word longer than 64K characters
582 * in a single gfxShapedWord, so we cannot exceed that limit.
583 */
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000584 uint32_t textLength = buffer->len;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430585
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430586 TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430587 TextAnalysis::Run *runHead;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430588 HRESULT hr;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430589 hr = analysis.GenerateResults (analyzer, &runHead);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430590
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000591#define FAIL(...) \
592 HB_STMT_START { \
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200593 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000594 return false; \
595 } HB_STMT_END;
596
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000597 if (FAILED (hr))
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000598 FAIL ("Analyzer failed to generate results.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430599
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000600 uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
601 uint32_t glyphCount;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000602 bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430603
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000604 const wchar_t localeName[20] = {0};
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200605 if (buffer->props.language != nullptr)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000606 mbstowcs ((wchar_t*) localeName,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330607 hb_language_to_string (buffer->props.language), 20);
Ebrahim Byagowid691ba32016-03-30 20:21:40 +0000608
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430609 // TODO: it does work but doesn't care about ranges
610 DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
611 typographic_features.featureCount = num_features;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000612 if (num_features)
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000613 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430614 typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000615 for (unsigned int i = 0; i < num_features; ++i)
616 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430617 typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330618 hb_uint32_swap (features[i].tag);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430619 typographic_features.features[i].parameter = features[i].value;
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000620 }
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000621 }
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330622 const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
623 dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000624 const uint32_t featureRangeLengths[] = { textLength };
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430625 //
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430626
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330627 uint16_t* clusterMap;
628 clusterMap = new uint16_t[textLength];
629 DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
630 textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
Ebrahim Byagowi8179ff52016-06-27 03:54:15 +0430631retry_getglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430632 uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330633 DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
634 glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000635
Behdad Esfahbod5dfd3412017-01-22 16:55:40 -0800636 hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330637 isRightToLeft, &runHead->mScript, localeName,
638 nullptr, &dwFeatures, featureRangeLengths, 1,
639 maxGlyphCount, clusterMap, textProperties,
640 glyphIndices, glyphProperties, &glyphCount);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000641
642 if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
643 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430644 delete [] glyphIndices;
645 delete [] glyphProperties;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430646
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000647 maxGlyphCount *= 2;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430648
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000649 goto retry_getglyphs;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430650 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000651 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000652 FAIL ("Analyzer failed to get glyphs.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430653
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430654 float* glyphAdvances = new float[maxGlyphCount];
655 DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430656
657 /* The -2 in the following is to compensate for possible
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430658 * alignment needed after the WORD array. sizeof (WORD) == 2. */
659 unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330660 / (sizeof (WORD) +
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330661 sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
662 sizeof (int) +
663 sizeof (DWRITE_GLYPH_OFFSET) +
664 sizeof (uint32_t));
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000665 ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430666
667#undef ALLOCATE_ARRAY
668
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430669 int fontEmSize = font->face->get_upem ();
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330670 if (fontEmSize < 0) fontEmSize = -fontEmSize;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000671
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330672 if (fontEmSize < 0) fontEmSize = -fontEmSize;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000673 double x_mult = (double) font->x_scale / fontEmSize;
674 double y_mult = (double) font->y_scale / fontEmSize;
Ebrahim Byagowi1c00a462016-03-30 20:15:09 +0000675
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330676 hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties,
677 textLength, glyphIndices, glyphProperties,
678 glyphCount, fontFace, fontEmSize,
679 false, isRightToLeft, &runHead->mScript, localeName,
680 &dwFeatures, featureRangeLengths, 1,
681 glyphAdvances, glyphOffsets);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430682
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000683 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000684 FAIL ("Analyzer failed to get glyph placements.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430685
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430686 IDWriteTextAnalyzer1* analyzer1;
687 analyzer->QueryInterface (&analyzer1);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000688
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430689 if (analyzer1 && lineWidth)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000690 {
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430691 DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430692 new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330693 hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
694 textLength, glyphCount, textString,
695 clusterMap, glyphProperties,
696 justificationOpportunities);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000697
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430698 if (FAILED (hr))
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430699 FAIL ("Analyzer failed to get justification opportunities.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430700
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430701 float* justifiedGlyphAdvances = new float[maxGlyphCount];
702 DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430703 hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330704 glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
705 justifiedGlyphOffsets);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430706
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330707 if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430708
709 DWRITE_SCRIPT_PROPERTIES scriptProperties;
710 hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330711 if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430712 uint32_t justificationCharacter = scriptProperties.justificationCharacter;
713
714 // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
715 if (justificationCharacter != 32)
716 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430717 uint16_t* modifiedClusterMap = new uint16_t[textLength];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430718 retry_getjustifiedglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430719 uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
720 float* modifiedGlyphAdvances = new float[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330721 DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430722 uint32_t actualGlyphsCount;
723 hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330724 textLength, glyphCount, maxGlyphCount,
725 clusterMap, glyphIndices, glyphAdvances,
726 justifiedGlyphAdvances, justifiedGlyphOffsets,
727 glyphProperties, &actualGlyphsCount,
728 modifiedClusterMap, modifiedGlyphIndices,
729 modifiedGlyphAdvances, modifiedGlyphOffsets);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000730
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430731 if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
732 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430733 maxGlyphCount = actualGlyphsCount;
734 delete [] modifiedGlyphIndices;
735 delete [] modifiedGlyphAdvances;
736 delete [] modifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000737
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430738 maxGlyphCount = actualGlyphsCount;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000739
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430740 goto retry_getjustifiedglyphs;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430741 }
742 if (FAILED (hr))
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430743 FAIL ("Analyzer failed to get justified glyphs.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430744
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430745 delete [] clusterMap;
746 delete [] glyphIndices;
747 delete [] glyphAdvances;
748 delete [] glyphOffsets;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430749
750 glyphCount = actualGlyphsCount;
751 clusterMap = modifiedClusterMap;
752 glyphIndices = modifiedGlyphIndices;
753 glyphAdvances = modifiedGlyphAdvances;
754 glyphOffsets = modifiedGlyphOffsets;
755
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430756 delete [] justifiedGlyphAdvances;
757 delete [] justifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000758 }
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430759 else
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000760 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430761 delete [] glyphAdvances;
762 delete [] glyphOffsets;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430763
764 glyphAdvances = justifiedGlyphAdvances;
765 glyphOffsets = justifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000766 }
767
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430768 delete [] justificationOpportunities;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000769 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000770
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430771 /* Ok, we've got everything we need, now compose output buffer,
772 * very, *very*, carefully! */
773
774 /* Calculate visual-clusters. That's what we ship. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000775 for (unsigned int i = 0; i < glyphCount; i++)
Behdad Esfahbodc2ea7a92019-01-18 13:45:33 -0500776 vis_clusters[i] = (uint32_t) -1;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000777 for (unsigned int i = 0; i < buffer->len; i++)
778 {
779 uint32_t *p =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430780 &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700781 *p = hb_min (*p, buffer->info[i].cluster);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430782 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000783 for (unsigned int i = 1; i < glyphCount; i++)
Behdad Esfahbodc2ea7a92019-01-18 13:45:33 -0500784 if (vis_clusters[i] == (uint32_t) -1)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430785 vis_clusters[i] = vis_clusters[i - 1];
786
787#undef utf16_index
788
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000789 if (unlikely (!buffer->ensure (glyphCount)))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000790 FAIL ("Buffer in error");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430791
792#undef FAIL
793
794 /* Set glyph infos */
795 buffer->len = 0;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000796 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430797 {
798 hb_glyph_info_t *info = &buffer->info[buffer->len++];
799
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000800 info->codepoint = glyphIndices[i];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430801 info->cluster = vis_clusters[i];
802
803 /* The rest is crap. Let's store position info there for now. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000804 info->mask = glyphAdvances[i];
805 info->var1.i32 = glyphOffsets[i].advanceOffset;
806 info->var2.i32 = glyphOffsets[i].ascenderOffset;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430807 }
808
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430809 /* Set glyph positions */
810 buffer->clear_positions ();
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000811 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430812 {
813 hb_glyph_info_t *info = &buffer->info[i];
814 hb_glyph_position_t *pos = &buffer->pos[i];
815
816 /* TODO vertical */
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000817 pos->x_advance = x_mult * (int32_t) info->mask;
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330818 pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000819 pos->y_offset = y_mult * info->var2.i32;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430820 }
821
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330822 if (isRightToLeft) hb_buffer_reverse (buffer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430823
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430824 delete [] clusterMap;
825 delete [] glyphIndices;
826 delete [] textProperties;
827 delete [] glyphProperties;
828 delete [] glyphAdvances;
829 delete [] glyphOffsets;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000830
831 if (num_features)
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430832 delete [] typographic_features.features;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000833
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430834 /* Wow, done! */
835 return true;
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400836}
ebraminio1e1825b2016-12-17 10:30:40 +0330837
838hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430839_hb_directwrite_shape (hb_shape_plan_t *shape_plan,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330840 hb_font_t *font,
841 hb_buffer_t *buffer,
842 const hb_feature_t *features,
843 unsigned int num_features)
ebraminio1e1825b2016-12-17 10:30:40 +0330844{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430845 return _hb_directwrite_shape_full (shape_plan, font, buffer,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330846 features, num_features, 0);
ebraminio1e1825b2016-12-17 10:30:40 +0330847}
848
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700849/**
850 * hb_directwrite_shape_experimental_width:
851 * Experimental API to test DirectWrite's justification algorithm.
852 *
853 * It inserts Kashida at wrong order so don't use the API ever.
854 *
855 * It doesn't work with cygwin/msys due to header bugs so one
856 * should use MSVC toolchain in order to use it for now.
857 *
858 * @font:
859 * @buffer:
860 * @features:
861 * @num_features:
862 * @width:
863 *
864 * Since: 1.4.2
865 **/
ebraminio1e1825b2016-12-17 10:30:40 +0330866hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430867hb_directwrite_shape_experimental_width (hb_font_t *font,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330868 hb_buffer_t *buffer,
869 const hb_feature_t *features,
870 unsigned int num_features,
871 float width)
ebraminio1e1825b2016-12-17 10:30:40 +0330872{
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330873 static const char *shapers = "directwrite";
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330874 hb_shape_plan_t *shape_plan;
875 shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
876 features, num_features, &shapers);
ebraminio1e1825b2016-12-17 10:30:40 +0330877 hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330878 features, num_features, width);
ebraminio1e1825b2016-12-17 10:30:40 +0330879
Behdad Esfahbode4da3802017-11-10 17:14:27 -0800880 buffer->unsafe_to_break_all ();
ebraminio1e1825b2016-12-17 10:30:40 +0330881
882 return res;
883}
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330884
885struct _hb_directwrite_font_table_context {
886 IDWriteFontFace *face;
887 void *table_context;
888};
889
890static void
891_hb_directwrite_table_data_release (void *data)
892{
893 _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data;
894 context->face->ReleaseFontTable (context->table_context);
895 delete context;
896}
897
898static hb_blob_t *
899reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
900{
901 IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
902 const void *data;
903 uint32_t length;
904 void *table_context;
905 BOOL exists;
906 if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
907 &length, &table_context, &exists)))
908 return nullptr;
909
910 if (!data || !exists || !length)
911 {
912 dw_face->ReleaseFontTable (table_context);
913 return nullptr;
914 }
915
916 _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context;
917 context->face = dw_face;
918 context->table_context = table_context;
919
920 return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY,
921 context, _hb_directwrite_table_data_release);
922}
923
924static void
925_hb_directwrite_font_release (void *data)
926{
927 if (data)
928 ((IDWriteFontFace *) data)->Release ();
929}
930
931/**
932 * hb_directwrite_face_create:
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700933 * @font_face: a DirectWrite IDWriteFontFace object.
934 *
935 * Return value: #hb_face_t object corresponding to the given input
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700936 *
Behdad Esfahbod59f36f32019-03-29 10:55:12 -0700937 * Since: 2.4.0
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330938 **/
939hb_face_t *
940hb_directwrite_face_create (IDWriteFontFace *font_face)
941{
942 if (font_face)
943 font_face->AddRef ();
944 return hb_face_create_for_tables (reference_table, font_face,
945 _hb_directwrite_font_release);
946}
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700947
948/**
949* hb_directwrite_face_get_font_face:
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700950* @face: a #hb_face_t object
951*
952* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700953*
Ebrahim Byagowic91f3fa2019-06-01 10:55:37 +0430954* Since: 2.5.0
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700955**/
956IDWriteFontFace *
957hb_directwrite_face_get_font_face (hb_face_t *face)
958{
959 return face->data.directwrite->fontFace;
960}