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