blob: efb2029ec0cd81dd859ff2c3b0ac05fb8ea12a58 [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 Esfahbodaa3450c2019-06-17 22:41:49 -070026
27#ifdef HAVE_DIRECTWRITE
28
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070029#include "hb-shaper-impl.hh"
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043030
Carlo Bramini693dacb2019-07-01 13:31:26 +020031#include <dwrite_1.h>
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043032
33#include "hb-directwrite.h"
34
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043035
Carlo Bramini601b6822019-06-30 15:03:44 +020036/* Declare object creator for dynamic support of DWRITE */
37typedef HRESULT (* WINAPI t_DWriteCreateFactory)(
38 DWRITE_FACTORY_TYPE factoryType,
39 REFIID iid,
40 IUnknown **factory
41);
42
Ebrahim Byagowi79190332018-03-22 16:04:38 +043043/*
44 * hb-directwrite uses new/delete syntatically but as we let users
45 * to override malloc/free, we will redefine new/delete so users
46 * won't need to do that by their own.
47 */
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033048void* operator new (size_t size) { return malloc (size); }
49void* operator new [] (size_t size) { return malloc (size); }
50void operator delete (void* pointer) { free (pointer); }
Ebrahim Byagowi79190332018-03-22 16:04:38 +043051void operator delete [] (void* pointer) { free (pointer); }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043052
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043053
54/*
55 * DirectWrite font stream helpers
56 */
57
58// This is a font loader which provides only one font (unlike its original design).
59// For a better implementation which was also source of this
60// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla
61class DWriteFontFileLoader : public IDWriteFontFileLoader
62{
63private:
64 IDWriteFontFileStream *mFontFileStream;
65public:
Ebrahim Byagowi957e7752018-03-19 12:19:42 +033066 DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033067 { mFontFileStream = fontFileStream; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043068
69 // IUnknown interface
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033070 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
71 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +033072 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
73 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043074
75 // IDWriteFontFileLoader methods
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +033076 virtual HRESULT STDMETHODCALLTYPE
77 CreateStreamFromKey (void const* fontFileReferenceKey,
78 uint32_t fontFileReferenceKeySize,
79 OUT IDWriteFontFileStream** fontFileStream)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043080 {
81 *fontFileStream = mFontFileStream;
82 return S_OK;
83 }
Ebrahim Byagowi0c2bd1b2019-01-19 16:30:07 +033084
85 virtual ~DWriteFontFileLoader() {}
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043086};
87
88class DWriteFontFileStream : public IDWriteFontFileStream
89{
90private:
91 uint8_t *mData;
92 uint32_t mSize;
93public:
Ebrahim Byagowi957e7752018-03-19 12:19:42 +033094 DWriteFontFileStream (uint8_t *aData, uint32_t aSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043095 {
96 mData = aData;
97 mSize = aSize;
98 }
99
100 // IUnknown interface
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330101 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
102 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330103 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
104 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430105
106 // IDWriteFontFileStream methods
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330107 virtual HRESULT STDMETHODCALLTYPE
108 ReadFileFragment (void const** fragmentStart,
109 UINT64 fileOffset,
110 UINT64 fragmentSize,
111 OUT void** fragmentContext)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430112 {
113 // We are required to do bounds checking.
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330114 if (fileOffset + fragmentSize > mSize) return E_FAIL;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430115
116 // truncate the 64 bit fileOffset to size_t sized index into mData
117 size_t index = static_cast<size_t> (fileOffset);
118
119 // We should be alive for the duration of this.
120 *fragmentStart = &mData[index];
121 *fragmentContext = nullptr;
122 return S_OK;
123 }
124
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330125 virtual void STDMETHODCALLTYPE
126 ReleaseFileFragment (void* fragmentContext) {}
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430127
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330128 virtual HRESULT STDMETHODCALLTYPE
129 GetFileSize (OUT UINT64* fileSize)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430130 {
131 *fileSize = mSize;
132 return S_OK;
133 }
134
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330135 virtual HRESULT STDMETHODCALLTYPE
136 GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
Ebrahim Byagowi0c2bd1b2019-01-19 16:30:07 +0330137
138 virtual ~DWriteFontFileStream() {}
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430139};
140
141
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430142/*
143* shaper face data
144*/
145
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700146struct hb_directwrite_face_data_t
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430147{
Carlo Bramini601b6822019-06-30 15:03:44 +0200148 HMODULE dwrite_dll;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430149 IDWriteFactory *dwriteFactory;
150 IDWriteFontFile *fontFile;
Ebrahim Byagowi9714d3e2019-01-18 21:55:21 +0330151 DWriteFontFileStream *fontFileStream;
152 DWriteFontFileLoader *fontFileLoader;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430153 IDWriteFontFace *fontFace;
154 hb_blob_t *faceBlob;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430155};
156
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700157hb_directwrite_face_data_t *
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430158_hb_directwrite_shaper_face_data_create (hb_face_t *face)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430159{
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700160 hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430161 if (unlikely (!data))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200162 return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430163
Carlo Bramini601b6822019-06-30 15:03:44 +0200164#define FAIL(...) \
165 HB_STMT_START { \
166 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
167 return nullptr; \
168 } HB_STMT_END
169
Ebrahim Byagowi4ab2d1d2019-07-01 19:30:21 +0430170 data->dwrite_dll = LoadLibrary (TEXT ("DWRITE"));
171 if (unlikely (!data->dwrite_dll))
Carlo Bramini601b6822019-06-30 15:03:44 +0200172 FAIL ("Cannot find DWrite.DLL");
173
174 t_DWriteCreateFactory p_DWriteCreateFactory;
175
Carlo Bramini5ebd2652019-07-01 16:06:43 +0200176#if defined(__GNUC__)
177#pragma GCC diagnostic push
178#pragma GCC diagnostic ignored "-Wcast-function-type"
179#endif
180
Carlo Bramini601b6822019-06-30 15:03:44 +0200181 p_DWriteCreateFactory = (t_DWriteCreateFactory)
Ebrahim Byagowi4ab2d1d2019-07-01 19:30:21 +0430182 GetProcAddress (data->dwrite_dll, "DWriteCreateFactory");
Carlo Bramini5ebd2652019-07-01 16:06:43 +0200183
184#if defined(__GNUC__)
185#pragma GCC diagnostic pop
186#endif
187
Ebrahim Byagowi4ab2d1d2019-07-01 19:30:21 +0430188 if (unlikely (!p_DWriteCreateFactory))
Carlo Bramini601b6822019-06-30 15:03:44 +0200189 FAIL ("Cannot find DWriteCreateFactory().");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430190
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430191 HRESULT hr;
Carlo Bramini601b6822019-06-30 15:03:44 +0200192
193 // TODO: factory and fontFileLoader should be cached separately
194 IDWriteFactory* dwriteFactory;
195 hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
Ebrahim Byagowi4ab2d1d2019-07-01 19:30:21 +0430196 (IUnknown**) &dwriteFactory);
Carlo Bramini601b6822019-06-30 15:03:44 +0200197
Ebrahim Byagowi4ab2d1d2019-07-01 19:30:21 +0430198 if (unlikely (hr != S_OK))
Carlo Bramini601b6822019-06-30 15:03:44 +0200199 FAIL ("Failed to run DWriteCreateFactory().");
200
Ebrahim Byagowibe565d12016-06-24 11:42:01 +0430201 hb_blob_t *blob = hb_face_reference_blob (face);
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330202 DWriteFontFileStream *fontFileStream;
203 fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
204 hb_blob_get_length (blob));
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430205
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330206 DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430207 dwriteFactory->RegisterFontFileLoader (fontFileLoader);
208
209 IDWriteFontFile *fontFile;
210 uint64_t fontFileKey = 0;
211 hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330212 fontFileLoader, &fontFile);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430213
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430214 if (FAILED (hr))
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430215 FAIL ("Failed to load font file from data!");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430216
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430217 BOOL isSupported;
218 DWRITE_FONT_FILE_TYPE fileType;
219 DWRITE_FONT_FACE_TYPE faceType;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430220 uint32_t numberOfFaces;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430221 hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430222 if (FAILED (hr) || !isSupported)
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430223 FAIL ("Font file is not supported.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430224
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430225#undef FAIL
226
227 IDWriteFontFace *fontFace;
228 dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330229 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430230
231 data->dwriteFactory = dwriteFactory;
232 data->fontFile = fontFile;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430233 data->fontFileStream = fontFileStream;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430234 data->fontFileLoader = fontFileLoader;
235 data->fontFace = fontFace;
Ebrahim Byagowibe565d12016-06-24 11:42:01 +0430236 data->faceBlob = blob;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430237
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430238 return data;
239}
240
241void
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700242_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430243{
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430244 if (data->fontFace)
245 data->fontFace->Release ();
246 if (data->fontFile)
247 data->fontFile->Release ();
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430248 if (data->dwriteFactory)
249 {
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430250 if (data->fontFileLoader)
ebraminio1e1825b2016-12-17 10:30:40 +0330251 data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
252 data->dwriteFactory->Release ();
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430253 }
254 if (data->fontFileLoader)
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330255 delete data->fontFileLoader;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430256 if (data->fontFileStream)
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330257 delete data->fontFileStream;
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430258 if (data->faceBlob)
259 hb_blob_destroy (data->faceBlob);
Ebrahim Byagowi4ab2d1d2019-07-01 19:30:21 +0430260 if (data->dwrite_dll)
261 FreeLibrary (data->dwrite_dll);
Ebrahim Byagowi07b724f2016-06-24 12:23:25 +0430262 if (data)
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430263 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430264}
265
266
267/*
268 * shaper font data
269 */
270
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330271struct hb_directwrite_font_data_t {};
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430272
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700273hb_directwrite_font_data_t *
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430274_hb_directwrite_shaper_font_data_create (hb_font_t *font)
275{
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700276 hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430277 if (unlikely (!data))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200278 return nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430279
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430280 return data;
281}
282
283void
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700284_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430285{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430286 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430287}
288
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430289
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430290// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430291// but now is relicensed to MIT for HarfBuzz use
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330292class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430293{
294public:
295
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330296 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
297 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330298 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
299 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430300
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330301 // A single contiguous run of characters containing the same analysis
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430302 // results.
303 struct Run
304 {
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000305 uint32_t mTextStart; // starting text position of this run
306 uint32_t mTextLength; // number of contiguous code units covered
307 uint32_t mGlyphStart; // starting glyph in the glyphs array
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330308 uint32_t mGlyphCount; // number of glyphs associated with this run
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430309 // text
310 DWRITE_SCRIPT_ANALYSIS mScript;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000311 uint8_t mBidiLevel;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430312 bool mIsSideways;
313
Ebrahim Byagowib2ebaa92018-12-16 22:38:10 +0330314 bool ContainsTextPosition (uint32_t aTextPosition) const
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430315 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430316 return aTextPosition >= mTextStart &&
317 aTextPosition < mTextStart + mTextLength;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430318 }
319
320 Run *nextRun;
321 };
322
323public:
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330324 TextAnalysis (const wchar_t* text, uint32_t textLength,
325 const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
Behdad Esfahbode6527222019-01-18 12:22:07 -0500326 : mTextLength (textLength), mText (text), mLocaleName (localeName),
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330327 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330328 ~TextAnalysis ()
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430329 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430330 // delete runs, except mRunHead which is part of the TextAnalysis object
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430331 for (Run *run = mRunHead.nextRun; run;)
332 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430333 Run *origRun = run;
334 run = run->nextRun;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430335 delete origRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430336 }
337 }
338
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330339 STDMETHODIMP
340 GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430341 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430342 // Analyzes the text using the script analyzer and returns
343 // the result as a series of runs.
344
345 HRESULT hr = S_OK;
346
347 // Initially start out with one result that covers the entire range.
348 // This result will be subdivided by the analysis processes.
349 mRunHead.mTextStart = 0;
350 mRunHead.mTextLength = mTextLength;
351 mRunHead.mBidiLevel =
352 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200353 mRunHead.nextRun = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430354 mCurrentRun = &mRunHead;
355
356 // Call each of the analyzers in sequence, recording their results.
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430357 if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430358 *runHead = &mRunHead;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430359
360 return hr;
361 }
362
363 // IDWriteTextAnalysisSource implementation
364
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330365 IFACEMETHODIMP
366 GetTextAtPosition (uint32_t textPosition,
367 OUT wchar_t const** textString,
368 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430369 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430370 if (textPosition >= mTextLength)
371 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430372 // No text at this position, valid query though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200373 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430374 *textLength = 0;
375 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430376 else
377 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430378 *textString = mText + textPosition;
379 *textLength = mTextLength - textPosition;
380 }
381 return S_OK;
382 }
383
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330384 IFACEMETHODIMP
385 GetTextBeforePosition (uint32_t textPosition,
386 OUT wchar_t const** textString,
387 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430388 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430389 if (textPosition == 0 || textPosition > mTextLength)
390 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430391 // Either there is no text before here (== 0), or this
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700392 // is an invalid position. The query is considered valid though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200393 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430394 *textLength = 0;
395 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430396 else
397 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430398 *textString = mText;
399 *textLength = textPosition;
400 }
401 return S_OK;
402 }
403
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430404 IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330405 GetParagraphReadingDirection () { return mReadingDirection; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430406
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330407 IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
408 wchar_t const** localeName)
409 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430410
411 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330412 GetNumberSubstitution (uint32_t textPosition,
413 OUT uint32_t* textLength,
414 OUT IDWriteNumberSubstitution** numberSubstitution)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430415 {
416 // We do not support number substitution.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200417 *numberSubstitution = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430418 *textLength = mTextLength - textPosition;
419
420 return S_OK;
421 }
422
423 // IDWriteTextAnalysisSink implementation
424
425 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330426 SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
427 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430428 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430429 SetCurrentRun (textPosition);
430 SplitCurrentRun (textPosition);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430431 while (textLength > 0)
432 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430433 Run *run = FetchNextRun (&textLength);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430434 run->mScript = *scriptAnalysis;
435 }
436
437 return S_OK;
438 }
439
440 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330441 SetLineBreakpoints (uint32_t textPosition,
442 uint32_t textLength,
443 const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
444 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430445
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330446 IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
447 uint8_t explicitLevel, uint8_t resolvedLevel)
448 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430449
450 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330451 SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
452 IDWriteNumberSubstitution* numberSubstitution)
453 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430454
455protected:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430456 Run *FetchNextRun (IN OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430457 {
458 // Used by the sink setters, this returns a reference to the next run.
459 // Position and length are adjusted to now point after the current run
460 // being returned.
461
462 Run *origRun = mCurrentRun;
463 // Split the tail if needed (the length remaining is less than the
464 // current run's size).
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430465 if (*textLength < mCurrentRun->mTextLength)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430466 SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430467 else
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430468 // Just advance the current run.
469 mCurrentRun = mCurrentRun->nextRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430470 *textLength -= origRun->mTextLength;
471
472 // Return a reference to the run that was just current.
473 return origRun;
474 }
475
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430476 void SetCurrentRun (uint32_t textPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430477 {
478 // Move the current run to the given position.
479 // Since the analyzers generally return results in a forward manner,
480 // this will usually just return early. If not, find the
481 // corresponding run for the text position.
482
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430483 if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430484 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430485
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430486 for (Run *run = &mRunHead; run; run = run->nextRun)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430487 if (run->ContainsTextPosition (textPosition))
488 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430489 mCurrentRun = run;
490 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430491 }
Ebrahim Byagowia57f5a12018-04-28 13:58:55 +0430492 assert (0); // We should always be able to find the text position in one of our runs
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430493 }
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 Byagowia57f5a12018-04-28 13:58:55 +0430499 assert (0); // 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 Byagowif35b3e92015-09-11 09:48:12 +0430542/*
543 * shaper
544 */
545
ebraminio1e1825b2016-12-17 10:30:40 +0330546static hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430547_hb_directwrite_shape_full (hb_shape_plan_t *shape_plan,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330548 hb_font_t *font,
549 hb_buffer_t *buffer,
550 const hb_feature_t *features,
551 unsigned int num_features,
552 float lineWidth)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430553{
554 hb_face_t *face = font->face;
Behdad Esfahbodce5da0f2018-11-16 02:29:13 -0500555 const hb_directwrite_face_data_t *face_data = face->data.directwrite;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430556 IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
557 IDWriteFontFace *fontFace = face_data->fontFace;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430558
559 IDWriteTextAnalyzer* analyzer;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430560 dwriteFactory->CreateTextAnalyzer (&analyzer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430561
562 unsigned int scratch_size;
563 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
564#define ALLOCATE_ARRAY(Type, name, len) \
565 Type *name = (Type *) scratch; \
Behdad Esfahbod68e12e62019-05-13 17:28:59 -0700566 do { \
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430567 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
568 assert (_consumed <= scratch_size); \
569 scratch += _consumed; \
570 scratch_size -= _consumed; \
Behdad Esfahbod68e12e62019-05-13 17:28:59 -0700571 } while (0)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430572
573#define utf16_index() var1.u32
574
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430575 ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430576
577 unsigned int chars_len = 0;
578 for (unsigned int i = 0; i < buffer->len; i++)
579 {
580 hb_codepoint_t c = buffer->info[i].codepoint;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430581 buffer->info[i].utf16_index () = chars_len;
582 if (likely (c <= 0xFFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000583 textString[chars_len++] = c;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430584 else if (unlikely (c > 0x10FFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000585 textString[chars_len++] = 0xFFFDu;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430586 else
587 {
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000588 textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
Behdad Esfahbod33317312016-08-08 17:24:04 -0700589 textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430590 }
591 }
592
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430593 ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430594 /* Need log_clusters to assign features. */
595 chars_len = 0;
596 for (unsigned int i = 0; i < buffer->len; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430597 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430598 hb_codepoint_t c = buffer->info[i].codepoint;
599 unsigned int cluster = buffer->info[i].cluster;
600 log_clusters[chars_len++] = cluster;
601 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
602 log_clusters[chars_len++] = cluster; /* Surrogates. */
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430603 }
604
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430605 // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
606
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330607 DWRITE_READING_DIRECTION readingDirection;
608 readingDirection = buffer->props.direction ?
609 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
610 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430611
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400612 /*
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430613 * There's an internal 16-bit limit on some things inside the analyzer,
614 * but we never attempt to shape a word longer than 64K characters
615 * in a single gfxShapedWord, so we cannot exceed that limit.
616 */
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000617 uint32_t textLength = buffer->len;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430618
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430619 TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430620 TextAnalysis::Run *runHead;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430621 HRESULT hr;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430622 hr = analysis.GenerateResults (analyzer, &runHead);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430623
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000624#define FAIL(...) \
625 HB_STMT_START { \
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200626 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000627 return false; \
Iceflowerc4567962019-09-26 11:35:27 +0200628 } HB_STMT_END
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000629
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000630 if (FAILED (hr))
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000631 FAIL ("Analyzer failed to generate results.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430632
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000633 uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
634 uint32_t glyphCount;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000635 bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430636
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000637 const wchar_t localeName[20] = {0};
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200638 if (buffer->props.language != nullptr)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000639 mbstowcs ((wchar_t*) localeName,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330640 hb_language_to_string (buffer->props.language), 20);
Ebrahim Byagowid691ba32016-03-30 20:21:40 +0000641
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430642 // TODO: it does work but doesn't care about ranges
643 DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
644 typographic_features.featureCount = num_features;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000645 if (num_features)
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000646 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430647 typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000648 for (unsigned int i = 0; i < num_features; ++i)
649 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430650 typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
Ebrahim Byagowieb8bd2f2019-07-16 22:27:01 +0430651 hb_uint32_swap (features[i].tag);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430652 typographic_features.features[i].parameter = features[i].value;
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000653 }
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000654 }
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330655 const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
656 dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000657 const uint32_t featureRangeLengths[] = { textLength };
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430658 //
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430659
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330660 uint16_t* clusterMap;
661 clusterMap = new uint16_t[textLength];
662 DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
663 textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
Ebrahim Byagowi8179ff52016-06-27 03:54:15 +0430664retry_getglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430665 uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330666 DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
667 glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000668
Behdad Esfahbod5dfd3412017-01-22 16:55:40 -0800669 hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330670 isRightToLeft, &runHead->mScript, localeName,
671 nullptr, &dwFeatures, featureRangeLengths, 1,
672 maxGlyphCount, clusterMap, textProperties,
673 glyphIndices, glyphProperties, &glyphCount);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000674
675 if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
676 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430677 delete [] glyphIndices;
678 delete [] glyphProperties;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430679
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000680 maxGlyphCount *= 2;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430681
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000682 goto retry_getglyphs;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430683 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000684 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000685 FAIL ("Analyzer failed to get glyphs.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430686
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430687 float* glyphAdvances = new float[maxGlyphCount];
688 DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430689
690 /* The -2 in the following is to compensate for possible
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430691 * alignment needed after the WORD array. sizeof (WORD) == 2. */
692 unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330693 / (sizeof (WORD) +
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330694 sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
695 sizeof (int) +
696 sizeof (DWRITE_GLYPH_OFFSET) +
697 sizeof (uint32_t));
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000698 ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430699
700#undef ALLOCATE_ARRAY
701
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430702 int fontEmSize = font->face->get_upem ();
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330703 if (fontEmSize < 0) fontEmSize = -fontEmSize;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000704
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330705 if (fontEmSize < 0) fontEmSize = -fontEmSize;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000706 double x_mult = (double) font->x_scale / fontEmSize;
707 double y_mult = (double) font->y_scale / fontEmSize;
Ebrahim Byagowi1c00a462016-03-30 20:15:09 +0000708
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330709 hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties,
710 textLength, glyphIndices, glyphProperties,
711 glyphCount, fontFace, fontEmSize,
712 false, isRightToLeft, &runHead->mScript, localeName,
713 &dwFeatures, featureRangeLengths, 1,
714 glyphAdvances, glyphOffsets);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430715
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000716 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000717 FAIL ("Analyzer failed to get glyph placements.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430718
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430719 IDWriteTextAnalyzer1* analyzer1;
720 analyzer->QueryInterface (&analyzer1);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000721
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430722 if (analyzer1 && lineWidth)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000723 {
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430724 DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430725 new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330726 hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
727 textLength, glyphCount, textString,
728 clusterMap, glyphProperties,
729 justificationOpportunities);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000730
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430731 if (FAILED (hr))
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430732 FAIL ("Analyzer failed to get justification opportunities.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430733
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430734 float* justifiedGlyphAdvances = new float[maxGlyphCount];
735 DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430736 hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330737 glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
738 justifiedGlyphOffsets);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430739
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330740 if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430741
742 DWRITE_SCRIPT_PROPERTIES scriptProperties;
743 hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330744 if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430745 uint32_t justificationCharacter = scriptProperties.justificationCharacter;
746
747 // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
748 if (justificationCharacter != 32)
749 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430750 uint16_t* modifiedClusterMap = new uint16_t[textLength];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430751 retry_getjustifiedglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430752 uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
753 float* modifiedGlyphAdvances = new float[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330754 DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430755 uint32_t actualGlyphsCount;
756 hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330757 textLength, glyphCount, maxGlyphCount,
758 clusterMap, glyphIndices, glyphAdvances,
759 justifiedGlyphAdvances, justifiedGlyphOffsets,
760 glyphProperties, &actualGlyphsCount,
761 modifiedClusterMap, modifiedGlyphIndices,
762 modifiedGlyphAdvances, modifiedGlyphOffsets);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000763
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430764 if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
765 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430766 maxGlyphCount = actualGlyphsCount;
767 delete [] modifiedGlyphIndices;
768 delete [] modifiedGlyphAdvances;
769 delete [] modifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000770
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430771 maxGlyphCount = actualGlyphsCount;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000772
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430773 goto retry_getjustifiedglyphs;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430774 }
775 if (FAILED (hr))
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430776 FAIL ("Analyzer failed to get justified glyphs.");
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430777
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430778 delete [] clusterMap;
779 delete [] glyphIndices;
780 delete [] glyphAdvances;
781 delete [] glyphOffsets;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430782
783 glyphCount = actualGlyphsCount;
784 clusterMap = modifiedClusterMap;
785 glyphIndices = modifiedGlyphIndices;
786 glyphAdvances = modifiedGlyphAdvances;
787 glyphOffsets = modifiedGlyphOffsets;
788
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430789 delete [] justifiedGlyphAdvances;
790 delete [] justifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000791 }
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430792 else
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000793 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430794 delete [] glyphAdvances;
795 delete [] glyphOffsets;
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430796
797 glyphAdvances = justifiedGlyphAdvances;
798 glyphOffsets = justifiedGlyphOffsets;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000799 }
800
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430801 delete [] justificationOpportunities;
Ebrahim Byagowiadf20ba2016-04-01 15:36:40 +0000802 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000803
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430804 /* Ok, we've got everything we need, now compose output buffer,
805 * very, *very*, carefully! */
806
807 /* Calculate visual-clusters. That's what we ship. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000808 for (unsigned int i = 0; i < glyphCount; i++)
Behdad Esfahbodc2ea7a92019-01-18 13:45:33 -0500809 vis_clusters[i] = (uint32_t) -1;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000810 for (unsigned int i = 0; i < buffer->len; i++)
811 {
812 uint32_t *p =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430813 &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700814 *p = hb_min (*p, buffer->info[i].cluster);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430815 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000816 for (unsigned int i = 1; i < glyphCount; i++)
Behdad Esfahbodc2ea7a92019-01-18 13:45:33 -0500817 if (vis_clusters[i] == (uint32_t) -1)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430818 vis_clusters[i] = vis_clusters[i - 1];
819
820#undef utf16_index
821
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000822 if (unlikely (!buffer->ensure (glyphCount)))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000823 FAIL ("Buffer in error");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430824
825#undef FAIL
826
827 /* Set glyph infos */
828 buffer->len = 0;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000829 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430830 {
831 hb_glyph_info_t *info = &buffer->info[buffer->len++];
832
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000833 info->codepoint = glyphIndices[i];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430834 info->cluster = vis_clusters[i];
835
836 /* The rest is crap. Let's store position info there for now. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000837 info->mask = glyphAdvances[i];
838 info->var1.i32 = glyphOffsets[i].advanceOffset;
839 info->var2.i32 = glyphOffsets[i].ascenderOffset;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430840 }
841
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430842 /* Set glyph positions */
843 buffer->clear_positions ();
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000844 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430845 {
846 hb_glyph_info_t *info = &buffer->info[i];
847 hb_glyph_position_t *pos = &buffer->pos[i];
848
849 /* TODO vertical */
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000850 pos->x_advance = x_mult * (int32_t) info->mask;
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330851 pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000852 pos->y_offset = y_mult * info->var2.i32;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430853 }
854
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330855 if (isRightToLeft) hb_buffer_reverse (buffer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430856
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430857 delete [] clusterMap;
858 delete [] glyphIndices;
859 delete [] textProperties;
860 delete [] glyphProperties;
861 delete [] glyphAdvances;
862 delete [] glyphOffsets;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000863
864 if (num_features)
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430865 delete [] typographic_features.features;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000866
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430867 /* Wow, done! */
868 return true;
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400869}
ebraminio1e1825b2016-12-17 10:30:40 +0330870
871hb_bool_t
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430872_hb_directwrite_shape (hb_shape_plan_t *shape_plan,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330873 hb_font_t *font,
874 hb_buffer_t *buffer,
875 const hb_feature_t *features,
876 unsigned int num_features)
ebraminio1e1825b2016-12-17 10:30:40 +0330877{
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430878 return _hb_directwrite_shape_full (shape_plan, font, buffer,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330879 features, num_features, 0);
ebraminio1e1825b2016-12-17 10:30:40 +0330880}
881
Ebrahim Byagowi2a3d4982019-08-21 03:02:01 +0430882HB_UNUSED static bool
883_hb_directwrite_shape_experimental_width (hb_font_t *font,
884 hb_buffer_t *buffer,
885 const hb_feature_t *features,
886 unsigned int num_features,
887 float width)
ebraminio1e1825b2016-12-17 10:30:40 +0330888{
Ebrahim Byagowi957e7752018-03-19 12:19:42 +0330889 static const char *shapers = "directwrite";
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330890 hb_shape_plan_t *shape_plan;
891 shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
892 features, num_features, &shapers);
ebraminio1e1825b2016-12-17 10:30:40 +0330893 hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330894 features, num_features, width);
ebraminio1e1825b2016-12-17 10:30:40 +0330895
Behdad Esfahbode4da3802017-11-10 17:14:27 -0800896 buffer->unsafe_to_break_all ();
ebraminio1e1825b2016-12-17 10:30:40 +0330897
898 return res;
899}
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330900
901struct _hb_directwrite_font_table_context {
902 IDWriteFontFace *face;
903 void *table_context;
904};
905
906static void
907_hb_directwrite_table_data_release (void *data)
908{
909 _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data;
910 context->face->ReleaseFontTable (context->table_context);
911 delete context;
912}
913
914static hb_blob_t *
Ebrahim Byagowi9fea6b42019-07-05 18:46:41 +0430915_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330916{
917 IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
918 const void *data;
919 uint32_t length;
920 void *table_context;
921 BOOL exists;
Ebrahim Byagowieb8bd2f2019-07-16 22:27:01 +0430922 if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330923 &length, &table_context, &exists)))
924 return nullptr;
925
926 if (!data || !exists || !length)
927 {
928 dw_face->ReleaseFontTable (table_context);
929 return nullptr;
930 }
931
932 _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context;
933 context->face = dw_face;
934 context->table_context = table_context;
935
936 return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY,
937 context, _hb_directwrite_table_data_release);
938}
939
940static void
941_hb_directwrite_font_release (void *data)
942{
943 if (data)
944 ((IDWriteFontFace *) data)->Release ();
945}
946
947/**
948 * hb_directwrite_face_create:
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700949 * @font_face: a DirectWrite IDWriteFontFace object.
950 *
951 * Return value: #hb_face_t object corresponding to the given input
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700952 *
Behdad Esfahbod59f36f32019-03-29 10:55:12 -0700953 * Since: 2.4.0
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330954 **/
955hb_face_t *
956hb_directwrite_face_create (IDWriteFontFace *font_face)
957{
958 if (font_face)
959 font_face->AddRef ();
Ebrahim Byagowi9fea6b42019-07-05 18:46:41 +0430960 return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330961 _hb_directwrite_font_release);
962}
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700963
964/**
965* hb_directwrite_face_get_font_face:
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700966* @face: a #hb_face_t object
967*
968* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700969*
Ebrahim Byagowic91f3fa2019-06-01 10:55:37 +0430970* Since: 2.5.0
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700971**/
972IDWriteFontFace *
973hb_directwrite_face_get_font_face (hb_face_t *face)
974{
975 return face->data.directwrite->fontFace;
976}
Behdad Esfahbodaa3450c2019-06-17 22:41:49 -0700977
978
979#endif