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