blob: 42764a244b14bcca93ad2320d1ccf26852676bac [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
Khaled Hosny69199212021-07-27 19:51:23 +020035#include "hb-ms-feature-ranges.hh"
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +043036
Khaled Hosny08269402020-12-23 00:45:39 +020037/**
38 * SECTION:hb-directwrite
39 * @title: hb-directwrite
40 * @short_description: DirectWrite integration
41 * @include: hb-directwrite.h
42 *
43 * Functions for using HarfBuzz with DirectWrite fonts.
44 **/
45
Luca Bacci3e881ef2022-06-27 14:34:18 +020046/* Declare object creator for dynamic support of DWRITE */
Luca Baccic22acfa2022-07-06 13:50:47 +020047typedef HRESULT (WINAPI *t_DWriteCreateFactory)(
Luca Bacci3e881ef2022-06-27 14:34:18 +020048 DWRITE_FACTORY_TYPE factoryType,
49 REFIID iid,
50 IUnknown **factory
51);
52
53
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +043054/*
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{
Luca Bacci3e881ef2022-06-27 14:34:18 +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
Luca Bacci3e881ef2022-06-27 14:34:18 +0200170 data->dwrite_dll = LoadLibrary (TEXT ("DWRITE"));
171 if (unlikely (!data->dwrite_dll))
172 FAIL ("Cannot find DWrite.DLL");
173
174 t_DWriteCreateFactory p_DWriteCreateFactory;
175
176#if defined(__GNUC__)
177#pragma GCC diagnostic push
178#pragma GCC diagnostic ignored "-Wcast-function-type"
179#endif
180
181 p_DWriteCreateFactory = (t_DWriteCreateFactory)
182 GetProcAddress (data->dwrite_dll, "DWriteCreateFactory");
183
184#if defined(__GNUC__)
185#pragma GCC diagnostic pop
186#endif
187
188 if (unlikely (!p_DWriteCreateFactory))
189 FAIL ("Cannot find DWriteCreateFactory().");
190
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;
Luca Bacci3e881ef2022-06-27 14:34:18 +0200195 hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
196 (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 }
Behdad Esfahbod326db322022-12-27 14:38:17 -0700254 delete data->fontFileLoader;
255 delete data->fontFileStream;
256 hb_blob_destroy (data->faceBlob);
Luca Bacci3e881ef2022-06-27 14:34:18 +0200257 if (data->dwrite_dll)
258 FreeLibrary (data->dwrite_dll);
Behdad Esfahbod326db322022-12-27 14:38:17 -0700259 delete data;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430260}
261
262
263/*
264 * shaper font data
265 */
266
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330267struct hb_directwrite_font_data_t {};
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430268
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700269hb_directwrite_font_data_t *
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430270_hb_directwrite_shaper_font_data_create (hb_font_t *font)
271{
Behdad Esfahbodeaba5e72022-06-28 13:47:49 -0600272 return (hb_directwrite_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430273}
274
275void
Behdad Esfahbod3d22aef2018-08-01 18:03:32 -0700276_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430277{
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430278}
279
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430280
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430281// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430282// but now is relicensed to MIT for HarfBuzz use
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330283class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430284{
285public:
286
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330287 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
288 { return S_OK; }
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330289 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
290 IFACEMETHOD_ (ULONG, Release) () { return 1; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430291
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330292 // A single contiguous run of characters containing the same analysis
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430293 // results.
294 struct Run
295 {
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000296 uint32_t mTextStart; // starting text position of this run
297 uint32_t mTextLength; // number of contiguous code units covered
298 uint32_t mGlyphStart; // starting glyph in the glyphs array
Ebrahim Byagowid4907e82018-03-14 11:04:28 +0330299 uint32_t mGlyphCount; // number of glyphs associated with this run
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430300 // text
301 DWRITE_SCRIPT_ANALYSIS mScript;
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000302 uint8_t mBidiLevel;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430303 bool mIsSideways;
304
Ebrahim Byagowib2ebaa92018-12-16 22:38:10 +0330305 bool ContainsTextPosition (uint32_t aTextPosition) const
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430306 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430307 return aTextPosition >= mTextStart &&
308 aTextPosition < mTextStart + mTextLength;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430309 }
310
311 Run *nextRun;
312 };
313
314public:
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330315 TextAnalysis (const wchar_t* text, uint32_t textLength,
316 const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
Behdad Esfahbode6527222019-01-18 12:22:07 -0500317 : mTextLength (textLength), mText (text), mLocaleName (localeName),
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330318 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330319 ~TextAnalysis ()
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430320 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430321 // delete runs, except mRunHead which is part of the TextAnalysis object
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430322 for (Run *run = mRunHead.nextRun; run;)
323 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430324 Run *origRun = run;
325 run = run->nextRun;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430326 delete origRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430327 }
328 }
329
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330330 STDMETHODIMP
331 GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430332 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430333 // Analyzes the text using the script analyzer and returns
334 // the result as a series of runs.
335
336 HRESULT hr = S_OK;
337
338 // Initially start out with one result that covers the entire range.
339 // This result will be subdivided by the analysis processes.
340 mRunHead.mTextStart = 0;
341 mRunHead.mTextLength = mTextLength;
342 mRunHead.mBidiLevel =
343 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200344 mRunHead.nextRun = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430345 mCurrentRun = &mRunHead;
346
347 // Call each of the analyzers in sequence, recording their results.
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430348 if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430349 *runHead = &mRunHead;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430350
351 return hr;
352 }
353
354 // IDWriteTextAnalysisSource implementation
355
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330356 IFACEMETHODIMP
357 GetTextAtPosition (uint32_t textPosition,
358 OUT wchar_t const** textString,
359 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430360 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430361 if (textPosition >= mTextLength)
362 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430363 // No text at this position, valid query though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200364 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430365 *textLength = 0;
366 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430367 else
368 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430369 *textString = mText + textPosition;
370 *textLength = mTextLength - textPosition;
371 }
372 return S_OK;
373 }
374
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330375 IFACEMETHODIMP
376 GetTextBeforePosition (uint32_t textPosition,
377 OUT wchar_t const** textString,
378 OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430379 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430380 if (textPosition == 0 || textPosition > mTextLength)
381 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430382 // Either there is no text before here (== 0), or this
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700383 // is an invalid position. The query is considered valid though.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200384 *textString = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430385 *textLength = 0;
386 }
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430387 else
388 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430389 *textString = mText;
390 *textLength = textPosition;
391 }
392 return S_OK;
393 }
394
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430395 IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330396 GetParagraphReadingDirection () { return mReadingDirection; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430397
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330398 IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
399 wchar_t const** localeName)
400 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430401
402 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330403 GetNumberSubstitution (uint32_t textPosition,
404 OUT uint32_t* textLength,
405 OUT IDWriteNumberSubstitution** numberSubstitution)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430406 {
407 // We do not support number substitution.
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200408 *numberSubstitution = nullptr;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430409 *textLength = mTextLength - textPosition;
410
411 return S_OK;
412 }
413
414 // IDWriteTextAnalysisSink implementation
415
416 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330417 SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
418 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430419 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430420 SetCurrentRun (textPosition);
421 SplitCurrentRun (textPosition);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430422 while (textLength > 0)
423 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430424 Run *run = FetchNextRun (&textLength);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430425 run->mScript = *scriptAnalysis;
426 }
427
428 return S_OK;
429 }
430
431 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330432 SetLineBreakpoints (uint32_t textPosition,
433 uint32_t textLength,
434 const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
435 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430436
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330437 IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
438 uint8_t explicitLevel, uint8_t resolvedLevel)
439 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430440
441 IFACEMETHODIMP
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330442 SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
443 IDWriteNumberSubstitution* numberSubstitution)
444 { return S_OK; }
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430445
446protected:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430447 Run *FetchNextRun (IN OUT uint32_t* textLength)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430448 {
449 // Used by the sink setters, this returns a reference to the next run.
450 // Position and length are adjusted to now point after the current run
451 // being returned.
452
453 Run *origRun = mCurrentRun;
454 // Split the tail if needed (the length remaining is less than the
455 // current run's size).
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430456 if (*textLength < mCurrentRun->mTextLength)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430457 SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430458 else
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430459 // Just advance the current run.
460 mCurrentRun = mCurrentRun->nextRun;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430461 *textLength -= origRun->mTextLength;
462
463 // Return a reference to the run that was just current.
464 return origRun;
465 }
466
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430467 void SetCurrentRun (uint32_t textPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430468 {
469 // Move the current run to the given position.
470 // Since the analyzers generally return results in a forward manner,
471 // this will usually just return early. If not, find the
472 // corresponding run for the text position.
473
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430474 if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430475 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430476
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430477 for (Run *run = &mRunHead; run; run = run->nextRun)
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430478 if (run->ContainsTextPosition (textPosition))
479 {
Ebrahim Byagowicb3fa702018-04-11 18:00:13 +0430480 mCurrentRun = run;
481 return;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430482 }
Ebrahim Byagowia57f5a12018-04-28 13:58:55 +0430483 assert (0); // We should always be able to find the text position in one of our runs
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430484 }
485
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430486 void SplitCurrentRun (uint32_t splitPosition)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430487 {
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430488 if (!mCurrentRun)
489 {
Ebrahim Byagowia57f5a12018-04-28 13:58:55 +0430490 assert (0); // SplitCurrentRun called without current run
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430491 // Shouldn't be calling this when no current run is set!
492 return;
493 }
494 // Split the current run.
Ebrahim Byagowif3f0ea92016-06-23 16:41:37 +0430495 if (splitPosition <= mCurrentRun->mTextStart)
496 {
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430497 // No need to split, already the start of a run
498 // or before it. Usually the first.
499 return;
500 }
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430501 Run *newRun = new Run;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430502
503 *newRun = *mCurrentRun;
504
505 // Insert the new run in our linked list.
506 newRun->nextRun = mCurrentRun->nextRun;
507 mCurrentRun->nextRun = newRun;
508
509 // Adjust runs' text positions and lengths.
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000510 uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430511 newRun->mTextStart += splitPoint;
512 newRun->mTextLength -= splitPoint;
513 mCurrentRun->mTextLength = splitPoint;
514 mCurrentRun = newRun;
515 }
516
517protected:
518 // Input
519 // (weak references are fine here, since this class is a transient
520 // stack-based helper that doesn't need to copy data)
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000521 uint32_t mTextLength;
522 const wchar_t* mText;
523 const wchar_t* mLocaleName;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430524 DWRITE_READING_DIRECTION mReadingDirection;
525
526 // Current processing state.
527 Run *mCurrentRun;
528
529 // Output is a list of runs starting here
530 Run mRunHead;
531};
532
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430533/*
534 * shaper
535 */
536
Khaled Hosny221d6422021-06-03 10:45:23 +0200537hb_bool_t
538_hb_directwrite_shape (hb_shape_plan_t *shape_plan,
539 hb_font_t *font,
540 hb_buffer_t *buffer,
541 const hb_feature_t *features,
542 unsigned int num_features)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430543{
544 hb_face_t *face = font->face;
Behdad Esfahbodce5da0f2018-11-16 02:29:13 -0500545 const hb_directwrite_face_data_t *face_data = face->data.directwrite;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430546 IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
547 IDWriteFontFace *fontFace = face_data->fontFace;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430548
549 IDWriteTextAnalyzer* analyzer;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430550 dwriteFactory->CreateTextAnalyzer (&analyzer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430551
552 unsigned int scratch_size;
553 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
554#define ALLOCATE_ARRAY(Type, name, len) \
555 Type *name = (Type *) scratch; \
Behdad Esfahbod68e12e62019-05-13 17:28:59 -0700556 do { \
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430557 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
558 assert (_consumed <= scratch_size); \
559 scratch += _consumed; \
560 scratch_size -= _consumed; \
Behdad Esfahbod68e12e62019-05-13 17:28:59 -0700561 } while (0)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430562
563#define utf16_index() var1.u32
564
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430565 ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430566
567 unsigned int chars_len = 0;
568 for (unsigned int i = 0; i < buffer->len; i++)
569 {
570 hb_codepoint_t c = buffer->info[i].codepoint;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430571 buffer->info[i].utf16_index () = chars_len;
572 if (likely (c <= 0xFFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000573 textString[chars_len++] = c;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430574 else if (unlikely (c > 0x10FFFFu))
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000575 textString[chars_len++] = 0xFFFDu;
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430576 else
577 {
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000578 textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
Behdad Esfahbod33317312016-08-08 17:24:04 -0700579 textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430580 }
581 }
582
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430583 ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430584 /* Need log_clusters to assign features. */
585 chars_len = 0;
586 for (unsigned int i = 0; i < buffer->len; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430587 {
Ebrahim Byagowife18c472018-03-25 18:19:23 +0430588 hb_codepoint_t c = buffer->info[i].codepoint;
589 unsigned int cluster = buffer->info[i].cluster;
590 log_clusters[chars_len++] = cluster;
591 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
592 log_clusters[chars_len++] = cluster; /* Surrogates. */
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430593 }
594
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330595 DWRITE_READING_DIRECTION readingDirection;
596 readingDirection = buffer->props.direction ?
597 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
598 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430599
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400600 /*
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430601 * There's an internal 16-bit limit on some things inside the analyzer,
602 * but we never attempt to shape a word longer than 64K characters
603 * in a single gfxShapedWord, so we cannot exceed that limit.
604 */
Khaled Hosnye1160582021-04-15 13:52:19 +0200605 uint32_t textLength = chars_len;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430606
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430607 TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430608 TextAnalysis::Run *runHead;
Ebrahim Byagowi6b861db2016-06-21 13:57:26 +0430609 HRESULT hr;
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430610 hr = analysis.GenerateResults (analyzer, &runHead);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430611
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000612#define FAIL(...) \
613 HB_STMT_START { \
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200614 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000615 return false; \
Iceflowerc4567962019-09-26 11:35:27 +0200616 } HB_STMT_END
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000617
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000618 if (FAILED (hr))
Ebrahim Byagowi10c3d9e2016-03-31 18:19:44 +0000619 FAIL ("Analyzer failed to generate results.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430620
Ebrahim Byagowi63ee9ca2016-04-01 15:47:07 +0000621 uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
622 uint32_t glyphCount;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000623 bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430624
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000625 const wchar_t localeName[20] = {0};
Ebrahim Byagowi2be859d2020-04-20 23:48:23 +0430626 if (buffer->props.language)
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000627 mbstowcs ((wchar_t*) localeName,
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330628 hb_language_to_string (buffer->props.language), 20);
Ebrahim Byagowid691ba32016-03-30 20:21:40 +0000629
Khaled Hosny14f220b2021-06-20 18:59:13 +0200630 /*
631 * Set up features.
632 */
Khaled Hosny69199212021-07-27 19:51:23 +0200633 static_assert ((sizeof (DWRITE_TYPOGRAPHIC_FEATURES) == sizeof (hb_ms_features_t)), "");
634 static_assert ((sizeof (DWRITE_FONT_FEATURE) == sizeof (hb_ms_feature_t)), "");
635 hb_vector_t<hb_ms_features_t *> range_features;
Khaled Hosny14f220b2021-06-20 18:59:13 +0200636 hb_vector_t<uint32_t> range_char_counts;
637 if (num_features)
638 {
Khaled Hosny69199212021-07-27 19:51:23 +0200639 hb_vector_t<hb_ms_feature_t> feature_records;
Khaled Hosny990c15d2021-07-27 20:43:53 +0200640 hb_vector_t<hb_ms_range_record_t> range_records;
Khaled Hosny69199212021-07-27 19:51:23 +0200641 if (hb_ms_setup_features (features, num_features, feature_records, range_records))
642 hb_ms_make_feature_ranges (feature_records,
643 range_records,
644 0,
645 chars_len,
646 log_clusters,
647 range_features,
648 range_char_counts);
Khaled Hosny14f220b2021-06-20 18:59:13 +0200649 }
650
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330651 uint16_t* clusterMap;
652 clusterMap = new uint16_t[textLength];
653 DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
654 textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
Khaled Hosny14f220b2021-06-20 18:59:13 +0200655
Ebrahim Byagowi8179ff52016-06-27 03:54:15 +0430656retry_getglyphs:
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430657 uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330658 DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
659 glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000660
Khaled Hosny69199212021-07-27 19:51:23 +0200661 hr = analyzer->GetGlyphs (textString,
662 chars_len,
663 fontFace,
664 false,
665 isRightToLeft,
666 &runHead->mScript,
667 localeName,
668 nullptr,
669 (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ,
670 range_char_counts.arrayZ,
671 range_features.length,
672 maxGlyphCount,
673 clusterMap,
674 textProperties,
675 glyphIndices,
676 glyphProperties,
677 &glyphCount);
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000678
679 if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
680 {
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430681 delete [] glyphIndices;
682 delete [] glyphProperties;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430683
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000684 maxGlyphCount *= 2;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430685
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000686 goto retry_getglyphs;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430687 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000688 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000689 FAIL ("Analyzer failed to get glyphs.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430690
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430691 float* glyphAdvances = new float[maxGlyphCount];
692 DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430693
694 /* The -2 in the following is to compensate for possible
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430695 * alignment needed after the WORD array. sizeof (WORD) == 2. */
696 unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330697 / (sizeof (WORD) +
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330698 sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
699 sizeof (int) +
700 sizeof (DWRITE_GLYPH_OFFSET) +
701 sizeof (uint32_t));
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000702 ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430703
704#undef ALLOCATE_ARRAY
705
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430706 int fontEmSize = font->face->get_upem ();
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330707 if (fontEmSize < 0) fontEmSize = -fontEmSize;
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000708
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330709 if (fontEmSize < 0) fontEmSize = -fontEmSize;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000710 double x_mult = (double) font->x_scale / fontEmSize;
711 double y_mult = (double) font->y_scale / fontEmSize;
Ebrahim Byagowi1c00a462016-03-30 20:15:09 +0000712
Khaled Hosny69199212021-07-27 19:51:23 +0200713 hr = analyzer->GetGlyphPlacements (textString,
714 clusterMap,
715 textProperties,
716 chars_len,
717 glyphIndices,
718 glyphProperties,
719 glyphCount,
720 fontFace,
721 fontEmSize,
722 false,
723 isRightToLeft,
724 &runHead->mScript,
725 localeName,
726 (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ,
727 range_char_counts.arrayZ,
728 range_features.length,
729 glyphAdvances,
730 glyphOffsets);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430731
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000732 if (FAILED (hr))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000733 FAIL ("Analyzer failed to get glyph placements.");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430734
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430735 /* Ok, we've got everything we need, now compose output buffer,
736 * very, *very*, carefully! */
737
738 /* Calculate visual-clusters. That's what we ship. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000739 for (unsigned int i = 0; i < glyphCount; i++)
Behdad Esfahbodc2ea7a92019-01-18 13:45:33 -0500740 vis_clusters[i] = (uint32_t) -1;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000741 for (unsigned int i = 0; i < buffer->len; i++)
742 {
743 uint32_t *p =
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430744 &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700745 *p = hb_min (*p, buffer->info[i].cluster);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430746 }
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000747 for (unsigned int i = 1; i < glyphCount; i++)
Behdad Esfahbodc2ea7a92019-01-18 13:45:33 -0500748 if (vis_clusters[i] == (uint32_t) -1)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430749 vis_clusters[i] = vis_clusters[i - 1];
750
751#undef utf16_index
752
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000753 if (unlikely (!buffer->ensure (glyphCount)))
Ebrahim Byagowid1298972016-03-31 13:45:37 +0000754 FAIL ("Buffer in error");
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430755
756#undef FAIL
757
758 /* Set glyph infos */
759 buffer->len = 0;
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000760 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430761 {
762 hb_glyph_info_t *info = &buffer->info[buffer->len++];
763
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000764 info->codepoint = glyphIndices[i];
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430765 info->cluster = vis_clusters[i];
766
767 /* The rest is crap. Let's store position info there for now. */
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000768 info->mask = glyphAdvances[i];
769 info->var1.i32 = glyphOffsets[i].advanceOffset;
770 info->var2.i32 = glyphOffsets[i].ascenderOffset;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430771 }
772
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430773 /* Set glyph positions */
774 buffer->clear_positions ();
Ebrahim Byagowi32ae9d12016-04-01 06:39:57 +0000775 for (unsigned int i = 0; i < glyphCount; i++)
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430776 {
777 hb_glyph_info_t *info = &buffer->info[i];
778 hb_glyph_position_t *pos = &buffer->pos[i];
779
780 /* TODO vertical */
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000781 pos->x_advance = x_mult * (int32_t) info->mask;
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330782 pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
Ebrahim Byagowi5f1a8962016-03-31 12:26:16 +0000783 pos->y_offset = y_mult * info->var2.i32;
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430784 }
785
Ebrahim Byagowi7ee5c522018-12-12 15:14:37 +0330786 if (isRightToLeft) hb_buffer_reverse (buffer);
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430787
Behdad Esfahbod20031dd2022-01-22 11:41:30 -0700788 buffer->clear_glyph_flags ();
789 buffer->unsafe_to_break ();
Khaled Hosny7608b192021-12-01 18:57:21 +0200790
Ebrahim Byagowi79190332018-03-22 16:04:38 +0430791 delete [] clusterMap;
792 delete [] glyphIndices;
793 delete [] textProperties;
794 delete [] glyphProperties;
795 delete [] glyphAdvances;
796 delete [] glyphOffsets;
Ebrahim Byagowid3134a62016-04-05 21:01:05 +0000797
Ebrahim Byagowif35b3e92015-09-11 09:48:12 +0430798 /* Wow, done! */
799 return true;
Khaled Hosnyd7bf9d02015-12-29 02:23:24 +0400800}
ebraminio1e1825b2016-12-17 10:30:40 +0330801
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330802struct _hb_directwrite_font_table_context {
803 IDWriteFontFace *face;
804 void *table_context;
805};
806
807static void
808_hb_directwrite_table_data_release (void *data)
809{
810 _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data;
811 context->face->ReleaseFontTable (context->table_context);
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600812 hb_free (context);
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330813}
814
815static hb_blob_t *
Ebrahim Byagowi9fea6b42019-07-05 18:46:41 +0430816_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330817{
818 IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
819 const void *data;
820 uint32_t length;
821 void *table_context;
822 BOOL exists;
Ebrahim Byagowieb8bd2f2019-07-16 22:27:01 +0430823 if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330824 &length, &table_context, &exists)))
825 return nullptr;
826
827 if (!data || !exists || !length)
828 {
829 dw_face->ReleaseFontTable (table_context);
830 return nullptr;
831 }
832
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600833 _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) hb_malloc (sizeof (_hb_directwrite_font_table_context));
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330834 context->face = dw_face;
835 context->table_context = table_context;
836
837 return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY,
838 context, _hb_directwrite_table_data_release);
839}
840
841static void
842_hb_directwrite_font_release (void *data)
843{
844 if (data)
845 ((IDWriteFontFace *) data)->Release ();
846}
847
848/**
849 * hb_directwrite_face_create:
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700850 * @font_face: a DirectWrite IDWriteFontFace object.
851 *
Khaled Hosny3d7a3612020-12-30 23:58:37 +0200852 * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
853 *
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700854 * Return value: #hb_face_t object corresponding to the given input
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700855 *
Behdad Esfahbod59f36f32019-03-29 10:55:12 -0700856 * Since: 2.4.0
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330857 **/
858hb_face_t *
859hb_directwrite_face_create (IDWriteFontFace *font_face)
860{
861 if (font_face)
862 font_face->AddRef ();
Ebrahim Byagowi9fea6b42019-07-05 18:46:41 +0430863 return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
Ebrahim Byagowi45149eb2019-02-22 13:13:42 +0330864 _hb_directwrite_font_release);
865}
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700866
867/**
868* hb_directwrite_face_get_font_face:
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700869* @face: a #hb_face_t object
870*
Khaled Hosny3d7a3612020-12-30 23:58:37 +0200871* Gets the DirectWrite IDWriteFontFace associated with @face.
872*
Ebrahim Byagowif27fdca2019-04-30 13:01:04 -0700873* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700874*
Ebrahim Byagowic91f3fa2019-06-01 10:55:37 +0430875* Since: 2.5.0
Ebrahim Byagowib2927722019-03-29 13:00:56 -0700876**/
877IDWriteFontFace *
878hb_directwrite_face_get_font_face (hb_face_t *face)
879{
880 return face->data.directwrite->fontFace;
881}
Behdad Esfahbodaa3450c2019-06-17 22:41:49 -0700882
883
884#endif