1 /*
2 * Copyright © 2015-2019 Ebrahim Byagowi
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 */
24
25 #include "hb.hh"
26
27 #ifdef HAVE_DIRECTWRITE
28
29 #include "hb-shaper-impl.hh"
30
31 #include <dwrite_1.h>
32
33 #include "hb-directwrite.h"
34
35
36 /**
37 * SECTION:hb-directwrite
38 * @title: hb-directwrite
39 * @short_description: DirectWrite integration
40 * @include: hb-directwrite.h
41 *
42 * Functions for using HarfBuzz with DirectWrite fonts.
43 **/
44
45 /* Declare object creator for dynamic support of DWRITE */
46 typedef HRESULT (* WINAPI t_DWriteCreateFactory)(
47 DWRITE_FACTORY_TYPE factoryType,
48 REFIID iid,
49 IUnknown **factory
50 );
51
52 /*
53 * hb-directwrite uses new/delete syntatically but as we let users
54 * to override malloc/free, we will redefine new/delete so users
55 * won't need to do that by their own.
56 */
operator new(size_t size)57 void* operator new (size_t size) { return malloc (size); }
operator new[](size_t size)58 void* operator new [] (size_t size) { return malloc (size); }
operator delete(void * pointer)59 void operator delete (void* pointer) { free (pointer); }
operator delete[](void * pointer)60 void operator delete [] (void* pointer) { free (pointer); }
61
62
63 /*
64 * DirectWrite font stream helpers
65 */
66
67 // This is a font loader which provides only one font (unlike its original design).
68 // For a better implementation which was also source of this
69 // and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla
70 class DWriteFontFileLoader : public IDWriteFontFileLoader
71 {
72 private:
73 IDWriteFontFileStream *mFontFileStream;
74 public:
DWriteFontFileLoader(IDWriteFontFileStream * fontFileStream)75 DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
76 { mFontFileStream = fontFileStream; }
77
78 // IUnknown interface
IFACEMETHOD(QueryInterface)79 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
80 { return S_OK; }
IFACEMETHOD_(ULONG,AddRef)81 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
IFACEMETHOD_(ULONG,Release)82 IFACEMETHOD_ (ULONG, Release) () { return 1; }
83
84 // IDWriteFontFileLoader methods
85 virtual HRESULT STDMETHODCALLTYPE
CreateStreamFromKey(void const * fontFileReferenceKey,uint32_t fontFileReferenceKeySize,OUT IDWriteFontFileStream ** fontFileStream)86 CreateStreamFromKey (void const* fontFileReferenceKey,
87 uint32_t fontFileReferenceKeySize,
88 OUT IDWriteFontFileStream** fontFileStream)
89 {
90 *fontFileStream = mFontFileStream;
91 return S_OK;
92 }
93
~DWriteFontFileLoader()94 virtual ~DWriteFontFileLoader() {}
95 };
96
97 class DWriteFontFileStream : public IDWriteFontFileStream
98 {
99 private:
100 uint8_t *mData;
101 uint32_t mSize;
102 public:
DWriteFontFileStream(uint8_t * aData,uint32_t aSize)103 DWriteFontFileStream (uint8_t *aData, uint32_t aSize)
104 {
105 mData = aData;
106 mSize = aSize;
107 }
108
109 // IUnknown interface
IFACEMETHOD(QueryInterface)110 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
111 { return S_OK; }
IFACEMETHOD_(ULONG,AddRef)112 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
IFACEMETHOD_(ULONG,Release)113 IFACEMETHOD_ (ULONG, Release) () { return 1; }
114
115 // IDWriteFontFileStream methods
116 virtual HRESULT STDMETHODCALLTYPE
ReadFileFragment(void const ** fragmentStart,UINT64 fileOffset,UINT64 fragmentSize,OUT void ** fragmentContext)117 ReadFileFragment (void const** fragmentStart,
118 UINT64 fileOffset,
119 UINT64 fragmentSize,
120 OUT void** fragmentContext)
121 {
122 // We are required to do bounds checking.
123 if (fileOffset + fragmentSize > mSize) return E_FAIL;
124
125 // truncate the 64 bit fileOffset to size_t sized index into mData
126 size_t index = static_cast<size_t> (fileOffset);
127
128 // We should be alive for the duration of this.
129 *fragmentStart = &mData[index];
130 *fragmentContext = nullptr;
131 return S_OK;
132 }
133
134 virtual void STDMETHODCALLTYPE
ReleaseFileFragment(void * fragmentContext)135 ReleaseFileFragment (void* fragmentContext) {}
136
137 virtual HRESULT STDMETHODCALLTYPE
GetFileSize(OUT UINT64 * fileSize)138 GetFileSize (OUT UINT64* fileSize)
139 {
140 *fileSize = mSize;
141 return S_OK;
142 }
143
144 virtual HRESULT STDMETHODCALLTYPE
GetLastWriteTime(OUT UINT64 * lastWriteTime)145 GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
146
~DWriteFontFileStream()147 virtual ~DWriteFontFileStream() {}
148 };
149
150
151 /*
152 * shaper face data
153 */
154
155 struct hb_directwrite_face_data_t
156 {
157 HMODULE dwrite_dll;
158 IDWriteFactory *dwriteFactory;
159 IDWriteFontFile *fontFile;
160 DWriteFontFileStream *fontFileStream;
161 DWriteFontFileLoader *fontFileLoader;
162 IDWriteFontFace *fontFace;
163 hb_blob_t *faceBlob;
164 };
165
166 hb_directwrite_face_data_t *
_hb_directwrite_shaper_face_data_create(hb_face_t * face)167 _hb_directwrite_shaper_face_data_create (hb_face_t *face)
168 {
169 hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
170 if (unlikely (!data))
171 return nullptr;
172
173 #define FAIL(...) \
174 HB_STMT_START { \
175 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
176 return nullptr; \
177 } HB_STMT_END
178
179 data->dwrite_dll = LoadLibrary (TEXT ("DWRITE"));
180 if (unlikely (!data->dwrite_dll))
181 FAIL ("Cannot find DWrite.DLL");
182
183 t_DWriteCreateFactory p_DWriteCreateFactory;
184
185 #if defined(__GNUC__)
186 #pragma GCC diagnostic push
187 #pragma GCC diagnostic ignored "-Wcast-function-type"
188 #endif
189
190 p_DWriteCreateFactory = (t_DWriteCreateFactory)
191 GetProcAddress (data->dwrite_dll, "DWriteCreateFactory");
192
193 #if defined(__GNUC__)
194 #pragma GCC diagnostic pop
195 #endif
196
197 if (unlikely (!p_DWriteCreateFactory))
198 FAIL ("Cannot find DWriteCreateFactory().");
199
200 HRESULT hr;
201
202 // TODO: factory and fontFileLoader should be cached separately
203 IDWriteFactory* dwriteFactory;
204 hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
205 (IUnknown**) &dwriteFactory);
206
207 if (unlikely (hr != S_OK))
208 FAIL ("Failed to run DWriteCreateFactory().");
209
210 hb_blob_t *blob = hb_face_reference_blob (face);
211 DWriteFontFileStream *fontFileStream;
212 fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
213 hb_blob_get_length (blob));
214
215 DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
216 dwriteFactory->RegisterFontFileLoader (fontFileLoader);
217
218 IDWriteFontFile *fontFile;
219 uint64_t fontFileKey = 0;
220 hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
221 fontFileLoader, &fontFile);
222
223 if (FAILED (hr))
224 FAIL ("Failed to load font file from data!");
225
226 BOOL isSupported;
227 DWRITE_FONT_FILE_TYPE fileType;
228 DWRITE_FONT_FACE_TYPE faceType;
229 uint32_t numberOfFaces;
230 hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
231 if (FAILED (hr) || !isSupported)
232 FAIL ("Font file is not supported.");
233
234 #undef FAIL
235
236 IDWriteFontFace *fontFace;
237 dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
238 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
239
240 data->dwriteFactory = dwriteFactory;
241 data->fontFile = fontFile;
242 data->fontFileStream = fontFileStream;
243 data->fontFileLoader = fontFileLoader;
244 data->fontFace = fontFace;
245 data->faceBlob = blob;
246
247 return data;
248 }
249
250 void
_hb_directwrite_shaper_face_data_destroy(hb_directwrite_face_data_t * data)251 _hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
252 {
253 if (data->fontFace)
254 data->fontFace->Release ();
255 if (data->fontFile)
256 data->fontFile->Release ();
257 if (data->dwriteFactory)
258 {
259 if (data->fontFileLoader)
260 data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
261 data->dwriteFactory->Release ();
262 }
263 if (data->fontFileLoader)
264 delete data->fontFileLoader;
265 if (data->fontFileStream)
266 delete data->fontFileStream;
267 if (data->faceBlob)
268 hb_blob_destroy (data->faceBlob);
269 if (data->dwrite_dll)
270 FreeLibrary (data->dwrite_dll);
271 if (data)
272 delete data;
273 }
274
275
276 /*
277 * shaper font data
278 */
279
280 struct hb_directwrite_font_data_t {};
281
282 hb_directwrite_font_data_t *
_hb_directwrite_shaper_font_data_create(hb_font_t * font)283 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
284 {
285 hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
286 if (unlikely (!data))
287 return nullptr;
288
289 return data;
290 }
291
292 void
_hb_directwrite_shaper_font_data_destroy(hb_directwrite_font_data_t * data)293 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
294 {
295 delete data;
296 }
297
298
299 // Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
300 // but now is relicensed to MIT for HarfBuzz use
301 class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
302 {
303 public:
304
IFACEMETHOD(QueryInterface)305 IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
306 { return S_OK; }
IFACEMETHOD_(ULONG,AddRef)307 IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
IFACEMETHOD_(ULONG,Release)308 IFACEMETHOD_ (ULONG, Release) () { return 1; }
309
310 // A single contiguous run of characters containing the same analysis
311 // results.
312 struct Run
313 {
314 uint32_t mTextStart; // starting text position of this run
315 uint32_t mTextLength; // number of contiguous code units covered
316 uint32_t mGlyphStart; // starting glyph in the glyphs array
317 uint32_t mGlyphCount; // number of glyphs associated with this run
318 // text
319 DWRITE_SCRIPT_ANALYSIS mScript;
320 uint8_t mBidiLevel;
321 bool mIsSideways;
322
ContainsTextPositionTextAnalysis::Run323 bool ContainsTextPosition (uint32_t aTextPosition) const
324 {
325 return aTextPosition >= mTextStart &&
326 aTextPosition < mTextStart + mTextLength;
327 }
328
329 Run *nextRun;
330 };
331
332 public:
TextAnalysis(const wchar_t * text,uint32_t textLength,const wchar_t * localeName,DWRITE_READING_DIRECTION readingDirection)333 TextAnalysis (const wchar_t* text, uint32_t textLength,
334 const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
335 : mTextLength (textLength), mText (text), mLocaleName (localeName),
336 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
~TextAnalysis()337 ~TextAnalysis ()
338 {
339 // delete runs, except mRunHead which is part of the TextAnalysis object
340 for (Run *run = mRunHead.nextRun; run;)
341 {
342 Run *origRun = run;
343 run = run->nextRun;
344 delete origRun;
345 }
346 }
347
348 STDMETHODIMP
GenerateResults(IDWriteTextAnalyzer * textAnalyzer,Run ** runHead)349 GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
350 {
351 // Analyzes the text using the script analyzer and returns
352 // the result as a series of runs.
353
354 HRESULT hr = S_OK;
355
356 // Initially start out with one result that covers the entire range.
357 // This result will be subdivided by the analysis processes.
358 mRunHead.mTextStart = 0;
359 mRunHead.mTextLength = mTextLength;
360 mRunHead.mBidiLevel =
361 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
362 mRunHead.nextRun = nullptr;
363 mCurrentRun = &mRunHead;
364
365 // Call each of the analyzers in sequence, recording their results.
366 if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
367 *runHead = &mRunHead;
368
369 return hr;
370 }
371
372 // IDWriteTextAnalysisSource implementation
373
374 IFACEMETHODIMP
GetTextAtPosition(uint32_t textPosition,OUT wchar_t const ** textString,OUT uint32_t * textLength)375 GetTextAtPosition (uint32_t textPosition,
376 OUT wchar_t const** textString,
377 OUT uint32_t* textLength)
378 {
379 if (textPosition >= mTextLength)
380 {
381 // No text at this position, valid query though.
382 *textString = nullptr;
383 *textLength = 0;
384 }
385 else
386 {
387 *textString = mText + textPosition;
388 *textLength = mTextLength - textPosition;
389 }
390 return S_OK;
391 }
392
393 IFACEMETHODIMP
GetTextBeforePosition(uint32_t textPosition,OUT wchar_t const ** textString,OUT uint32_t * textLength)394 GetTextBeforePosition (uint32_t textPosition,
395 OUT wchar_t const** textString,
396 OUT uint32_t* textLength)
397 {
398 if (textPosition == 0 || textPosition > mTextLength)
399 {
400 // Either there is no text before here (== 0), or this
401 // is an invalid position. The query is considered valid though.
402 *textString = nullptr;
403 *textLength = 0;
404 }
405 else
406 {
407 *textString = mText;
408 *textLength = textPosition;
409 }
410 return S_OK;
411 }
412
413 IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
GetParagraphReadingDirection()414 GetParagraphReadingDirection () { return mReadingDirection; }
415
GetLocaleName(uint32_t textPosition,uint32_t * textLength,wchar_t const ** localeName)416 IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
417 wchar_t const** localeName)
418 { return S_OK; }
419
420 IFACEMETHODIMP
GetNumberSubstitution(uint32_t textPosition,OUT uint32_t * textLength,OUT IDWriteNumberSubstitution ** numberSubstitution)421 GetNumberSubstitution (uint32_t textPosition,
422 OUT uint32_t* textLength,
423 OUT IDWriteNumberSubstitution** numberSubstitution)
424 {
425 // We do not support number substitution.
426 *numberSubstitution = nullptr;
427 *textLength = mTextLength - textPosition;
428
429 return S_OK;
430 }
431
432 // IDWriteTextAnalysisSink implementation
433
434 IFACEMETHODIMP
SetScriptAnalysis(uint32_t textPosition,uint32_t textLength,DWRITE_SCRIPT_ANALYSIS const * scriptAnalysis)435 SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
436 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
437 {
438 SetCurrentRun (textPosition);
439 SplitCurrentRun (textPosition);
440 while (textLength > 0)
441 {
442 Run *run = FetchNextRun (&textLength);
443 run->mScript = *scriptAnalysis;
444 }
445
446 return S_OK;
447 }
448
449 IFACEMETHODIMP
SetLineBreakpoints(uint32_t textPosition,uint32_t textLength,const DWRITE_LINE_BREAKPOINT * lineBreakpoints)450 SetLineBreakpoints (uint32_t textPosition,
451 uint32_t textLength,
452 const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
453 { return S_OK; }
454
SetBidiLevel(uint32_t textPosition,uint32_t textLength,uint8_t explicitLevel,uint8_t resolvedLevel)455 IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
456 uint8_t explicitLevel, uint8_t resolvedLevel)
457 { return S_OK; }
458
459 IFACEMETHODIMP
SetNumberSubstitution(uint32_t textPosition,uint32_t textLength,IDWriteNumberSubstitution * numberSubstitution)460 SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
461 IDWriteNumberSubstitution* numberSubstitution)
462 { return S_OK; }
463
464 protected:
FetchNextRun(IN OUT uint32_t * textLength)465 Run *FetchNextRun (IN OUT uint32_t* textLength)
466 {
467 // Used by the sink setters, this returns a reference to the next run.
468 // Position and length are adjusted to now point after the current run
469 // being returned.
470
471 Run *origRun = mCurrentRun;
472 // Split the tail if needed (the length remaining is less than the
473 // current run's size).
474 if (*textLength < mCurrentRun->mTextLength)
475 SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
476 else
477 // Just advance the current run.
478 mCurrentRun = mCurrentRun->nextRun;
479 *textLength -= origRun->mTextLength;
480
481 // Return a reference to the run that was just current.
482 return origRun;
483 }
484
SetCurrentRun(uint32_t textPosition)485 void SetCurrentRun (uint32_t textPosition)
486 {
487 // Move the current run to the given position.
488 // Since the analyzers generally return results in a forward manner,
489 // this will usually just return early. If not, find the
490 // corresponding run for the text position.
491
492 if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
493 return;
494
495 for (Run *run = &mRunHead; run; run = run->nextRun)
496 if (run->ContainsTextPosition (textPosition))
497 {
498 mCurrentRun = run;
499 return;
500 }
501 assert (0); // We should always be able to find the text position in one of our runs
502 }
503
SplitCurrentRun(uint32_t splitPosition)504 void SplitCurrentRun (uint32_t splitPosition)
505 {
506 if (!mCurrentRun)
507 {
508 assert (0); // SplitCurrentRun called without current run
509 // Shouldn't be calling this when no current run is set!
510 return;
511 }
512 // Split the current run.
513 if (splitPosition <= mCurrentRun->mTextStart)
514 {
515 // No need to split, already the start of a run
516 // or before it. Usually the first.
517 return;
518 }
519 Run *newRun = new Run;
520
521 *newRun = *mCurrentRun;
522
523 // Insert the new run in our linked list.
524 newRun->nextRun = mCurrentRun->nextRun;
525 mCurrentRun->nextRun = newRun;
526
527 // Adjust runs' text positions and lengths.
528 uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
529 newRun->mTextStart += splitPoint;
530 newRun->mTextLength -= splitPoint;
531 mCurrentRun->mTextLength = splitPoint;
532 mCurrentRun = newRun;
533 }
534
535 protected:
536 // Input
537 // (weak references are fine here, since this class is a transient
538 // stack-based helper that doesn't need to copy data)
539 uint32_t mTextLength;
540 const wchar_t* mText;
541 const wchar_t* mLocaleName;
542 DWRITE_READING_DIRECTION mReadingDirection;
543
544 // Current processing state.
545 Run *mCurrentRun;
546
547 // Output is a list of runs starting here
548 Run mRunHead;
549 };
550
551 /*
552 * shaper
553 */
554
555 static hb_bool_t
_hb_directwrite_shape_full(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features,float lineWidth)556 _hb_directwrite_shape_full (hb_shape_plan_t *shape_plan,
557 hb_font_t *font,
558 hb_buffer_t *buffer,
559 const hb_feature_t *features,
560 unsigned int num_features,
561 float lineWidth)
562 {
563 hb_face_t *face = font->face;
564 const hb_directwrite_face_data_t *face_data = face->data.directwrite;
565 IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
566 IDWriteFontFace *fontFace = face_data->fontFace;
567
568 IDWriteTextAnalyzer* analyzer;
569 dwriteFactory->CreateTextAnalyzer (&analyzer);
570
571 unsigned int scratch_size;
572 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
573 #define ALLOCATE_ARRAY(Type, name, len) \
574 Type *name = (Type *) scratch; \
575 do { \
576 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
577 assert (_consumed <= scratch_size); \
578 scratch += _consumed; \
579 scratch_size -= _consumed; \
580 } while (0)
581
582 #define utf16_index() var1.u32
583
584 ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
585
586 unsigned int chars_len = 0;
587 for (unsigned int i = 0; i < buffer->len; i++)
588 {
589 hb_codepoint_t c = buffer->info[i].codepoint;
590 buffer->info[i].utf16_index () = chars_len;
591 if (likely (c <= 0xFFFFu))
592 textString[chars_len++] = c;
593 else if (unlikely (c > 0x10FFFFu))
594 textString[chars_len++] = 0xFFFDu;
595 else
596 {
597 textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
598 textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
599 }
600 }
601
602 ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
603 /* Need log_clusters to assign features. */
604 chars_len = 0;
605 for (unsigned int i = 0; i < buffer->len; i++)
606 {
607 hb_codepoint_t c = buffer->info[i].codepoint;
608 unsigned int cluster = buffer->info[i].cluster;
609 log_clusters[chars_len++] = cluster;
610 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
611 log_clusters[chars_len++] = cluster; /* Surrogates. */
612 }
613
614 // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
615
616 DWRITE_READING_DIRECTION readingDirection;
617 readingDirection = buffer->props.direction ?
618 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
619 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
620
621 /*
622 * There's an internal 16-bit limit on some things inside the analyzer,
623 * but we never attempt to shape a word longer than 64K characters
624 * in a single gfxShapedWord, so we cannot exceed that limit.
625 */
626 uint32_t textLength = chars_len;
627
628 TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
629 TextAnalysis::Run *runHead;
630 HRESULT hr;
631 hr = analysis.GenerateResults (analyzer, &runHead);
632
633 #define FAIL(...) \
634 HB_STMT_START { \
635 DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
636 return false; \
637 } HB_STMT_END
638
639 if (FAILED (hr))
640 FAIL ("Analyzer failed to generate results.");
641
642 uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
643 uint32_t glyphCount;
644 bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
645
646 const wchar_t localeName[20] = {0};
647 if (buffer->props.language)
648 mbstowcs ((wchar_t*) localeName,
649 hb_language_to_string (buffer->props.language), 20);
650
651 // TODO: it does work but doesn't care about ranges
652 DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
653 typographic_features.featureCount = num_features;
654 if (num_features)
655 {
656 typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
657 for (unsigned int i = 0; i < num_features; ++i)
658 {
659 typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
660 hb_uint32_swap (features[i].tag);
661 typographic_features.features[i].parameter = features[i].value;
662 }
663 }
664 const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
665 dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
666 const uint32_t featureRangeLengths[] = { textLength };
667 //
668
669 uint16_t* clusterMap;
670 clusterMap = new uint16_t[textLength];
671 DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
672 textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
673 retry_getglyphs:
674 uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
675 DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
676 glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
677
678 hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
679 isRightToLeft, &runHead->mScript, localeName,
680 nullptr, &dwFeatures, featureRangeLengths, 1,
681 maxGlyphCount, clusterMap, textProperties,
682 glyphIndices, glyphProperties, &glyphCount);
683
684 if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
685 {
686 delete [] glyphIndices;
687 delete [] glyphProperties;
688
689 maxGlyphCount *= 2;
690
691 goto retry_getglyphs;
692 }
693 if (FAILED (hr))
694 FAIL ("Analyzer failed to get glyphs.");
695
696 float* glyphAdvances = new float[maxGlyphCount];
697 DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
698
699 /* The -2 in the following is to compensate for possible
700 * alignment needed after the WORD array. sizeof (WORD) == 2. */
701 unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
702 / (sizeof (WORD) +
703 sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
704 sizeof (int) +
705 sizeof (DWRITE_GLYPH_OFFSET) +
706 sizeof (uint32_t));
707 ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
708
709 #undef ALLOCATE_ARRAY
710
711 int fontEmSize = font->face->get_upem ();
712 if (fontEmSize < 0) fontEmSize = -fontEmSize;
713
714 if (fontEmSize < 0) fontEmSize = -fontEmSize;
715 double x_mult = (double) font->x_scale / fontEmSize;
716 double y_mult = (double) font->y_scale / fontEmSize;
717
718 hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties,
719 textLength, glyphIndices, glyphProperties,
720 glyphCount, fontFace, fontEmSize,
721 false, isRightToLeft, &runHead->mScript, localeName,
722 &dwFeatures, featureRangeLengths, 1,
723 glyphAdvances, glyphOffsets);
724
725 if (FAILED (hr))
726 FAIL ("Analyzer failed to get glyph placements.");
727
728 IDWriteTextAnalyzer1* analyzer1;
729 analyzer->QueryInterface (&analyzer1);
730
731 if (analyzer1 && lineWidth)
732 {
733 DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
734 new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
735 hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
736 textLength, glyphCount, textString,
737 clusterMap, glyphProperties,
738 justificationOpportunities);
739
740 if (FAILED (hr))
741 FAIL ("Analyzer failed to get justification opportunities.");
742
743 float* justifiedGlyphAdvances = new float[maxGlyphCount];
744 DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
745 hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
746 glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
747 justifiedGlyphOffsets);
748
749 if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances.");
750
751 DWRITE_SCRIPT_PROPERTIES scriptProperties;
752 hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
753 if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
754 uint32_t justificationCharacter = scriptProperties.justificationCharacter;
755
756 // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
757 if (justificationCharacter != 32)
758 {
759 uint16_t* modifiedClusterMap = new uint16_t[textLength];
760 retry_getjustifiedglyphs:
761 uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
762 float* modifiedGlyphAdvances = new float[maxGlyphCount];
763 DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
764 uint32_t actualGlyphsCount;
765 hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
766 textLength, glyphCount, maxGlyphCount,
767 clusterMap, glyphIndices, glyphAdvances,
768 justifiedGlyphAdvances, justifiedGlyphOffsets,
769 glyphProperties, &actualGlyphsCount,
770 modifiedClusterMap, modifiedGlyphIndices,
771 modifiedGlyphAdvances, modifiedGlyphOffsets);
772
773 if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
774 {
775 maxGlyphCount = actualGlyphsCount;
776 delete [] modifiedGlyphIndices;
777 delete [] modifiedGlyphAdvances;
778 delete [] modifiedGlyphOffsets;
779
780 maxGlyphCount = actualGlyphsCount;
781
782 goto retry_getjustifiedglyphs;
783 }
784 if (FAILED (hr))
785 FAIL ("Analyzer failed to get justified glyphs.");
786
787 delete [] clusterMap;
788 delete [] glyphIndices;
789 delete [] glyphAdvances;
790 delete [] glyphOffsets;
791
792 glyphCount = actualGlyphsCount;
793 clusterMap = modifiedClusterMap;
794 glyphIndices = modifiedGlyphIndices;
795 glyphAdvances = modifiedGlyphAdvances;
796 glyphOffsets = modifiedGlyphOffsets;
797
798 delete [] justifiedGlyphAdvances;
799 delete [] justifiedGlyphOffsets;
800 }
801 else
802 {
803 delete [] glyphAdvances;
804 delete [] glyphOffsets;
805
806 glyphAdvances = justifiedGlyphAdvances;
807 glyphOffsets = justifiedGlyphOffsets;
808 }
809
810 delete [] justificationOpportunities;
811 }
812
813 /* Ok, we've got everything we need, now compose output buffer,
814 * very, *very*, carefully! */
815
816 /* Calculate visual-clusters. That's what we ship. */
817 for (unsigned int i = 0; i < glyphCount; i++)
818 vis_clusters[i] = (uint32_t) -1;
819 for (unsigned int i = 0; i < buffer->len; i++)
820 {
821 uint32_t *p =
822 &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
823 *p = hb_min (*p, buffer->info[i].cluster);
824 }
825 for (unsigned int i = 1; i < glyphCount; i++)
826 if (vis_clusters[i] == (uint32_t) -1)
827 vis_clusters[i] = vis_clusters[i - 1];
828
829 #undef utf16_index
830
831 if (unlikely (!buffer->ensure (glyphCount)))
832 FAIL ("Buffer in error");
833
834 #undef FAIL
835
836 /* Set glyph infos */
837 buffer->len = 0;
838 for (unsigned int i = 0; i < glyphCount; i++)
839 {
840 hb_glyph_info_t *info = &buffer->info[buffer->len++];
841
842 info->codepoint = glyphIndices[i];
843 info->cluster = vis_clusters[i];
844
845 /* The rest is crap. Let's store position info there for now. */
846 info->mask = glyphAdvances[i];
847 info->var1.i32 = glyphOffsets[i].advanceOffset;
848 info->var2.i32 = glyphOffsets[i].ascenderOffset;
849 }
850
851 /* Set glyph positions */
852 buffer->clear_positions ();
853 for (unsigned int i = 0; i < glyphCount; i++)
854 {
855 hb_glyph_info_t *info = &buffer->info[i];
856 hb_glyph_position_t *pos = &buffer->pos[i];
857
858 /* TODO vertical */
859 pos->x_advance = x_mult * (int32_t) info->mask;
860 pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
861 pos->y_offset = y_mult * info->var2.i32;
862 }
863
864 if (isRightToLeft) hb_buffer_reverse (buffer);
865
866 delete [] clusterMap;
867 delete [] glyphIndices;
868 delete [] textProperties;
869 delete [] glyphProperties;
870 delete [] glyphAdvances;
871 delete [] glyphOffsets;
872
873 if (num_features)
874 delete [] typographic_features.features;
875
876 /* Wow, done! */
877 return true;
878 }
879
880 hb_bool_t
_hb_directwrite_shape(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features)881 _hb_directwrite_shape (hb_shape_plan_t *shape_plan,
882 hb_font_t *font,
883 hb_buffer_t *buffer,
884 const hb_feature_t *features,
885 unsigned int num_features)
886 {
887 return _hb_directwrite_shape_full (shape_plan, font, buffer,
888 features, num_features, 0);
889 }
890
891 HB_UNUSED static bool
_hb_directwrite_shape_experimental_width(hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features,float width)892 _hb_directwrite_shape_experimental_width (hb_font_t *font,
893 hb_buffer_t *buffer,
894 const hb_feature_t *features,
895 unsigned int num_features,
896 float width)
897 {
898 static const char *shapers = "directwrite";
899 hb_shape_plan_t *shape_plan;
900 shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
901 features, num_features, &shapers);
902 hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
903 features, num_features, width);
904
905 buffer->unsafe_to_break_all ();
906
907 return res;
908 }
909
910 struct _hb_directwrite_font_table_context {
911 IDWriteFontFace *face;
912 void *table_context;
913 };
914
915 static void
_hb_directwrite_table_data_release(void * data)916 _hb_directwrite_table_data_release (void *data)
917 {
918 _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data;
919 context->face->ReleaseFontTable (context->table_context);
920 delete context;
921 }
922
923 static hb_blob_t *
_hb_directwrite_reference_table(hb_face_t * face HB_UNUSED,hb_tag_t tag,void * user_data)924 _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
925 {
926 IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
927 const void *data;
928 uint32_t length;
929 void *table_context;
930 BOOL exists;
931 if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
932 &length, &table_context, &exists)))
933 return nullptr;
934
935 if (!data || !exists || !length)
936 {
937 dw_face->ReleaseFontTable (table_context);
938 return nullptr;
939 }
940
941 _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context;
942 context->face = dw_face;
943 context->table_context = table_context;
944
945 return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY,
946 context, _hb_directwrite_table_data_release);
947 }
948
949 static void
_hb_directwrite_font_release(void * data)950 _hb_directwrite_font_release (void *data)
951 {
952 if (data)
953 ((IDWriteFontFace *) data)->Release ();
954 }
955
956 /**
957 * hb_directwrite_face_create:
958 * @font_face: a DirectWrite IDWriteFontFace object.
959 *
960 * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
961 *
962 * Return value: #hb_face_t object corresponding to the given input
963 *
964 * Since: 2.4.0
965 **/
966 hb_face_t *
hb_directwrite_face_create(IDWriteFontFace * font_face)967 hb_directwrite_face_create (IDWriteFontFace *font_face)
968 {
969 if (font_face)
970 font_face->AddRef ();
971 return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
972 _hb_directwrite_font_release);
973 }
974
975 /**
976 * hb_directwrite_face_get_font_face:
977 * @face: a #hb_face_t object
978 *
979 * Gets the DirectWrite IDWriteFontFace associated with @face.
980 *
981 * Return value: DirectWrite IDWriteFontFace object corresponding to the given input
982 *
983 * Since: 2.5.0
984 **/
985 IDWriteFontFace *
hb_directwrite_face_get_font_face(hb_face_t * face)986 hb_directwrite_face_get_font_face (hb_face_t *face)
987 {
988 return face->data.directwrite->fontFace;
989 }
990
991
992 #endif
993