hb-directwrite.cc 28.1 KB
Newer Older
1
/*
2
 * Copyright © 2015-2019  Ebrahim Byagowi
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

25 26
#include "hb.hh"
#include "hb-shaper-impl.hh"
27

28
#include <DWrite_1.h>
29 30 31 32

#include "hb-directwrite.h"


33 34 35 36 37
/*
 * hb-directwrite uses new/delete syntatically but as we let users
 * to override malloc/free, we will redefine new/delete so users
 * won't need to do that by their own.
 */
38 39 40
void* operator new (size_t size)        { return malloc (size); }
void* operator new [] (size_t size)     { return malloc (size); }
void operator delete (void* pointer)    { free (pointer); }
41
void operator delete [] (void* pointer) { free (pointer); }
42 43


44 45 46
/*
 * DirectWrite font stream helpers
 */
47

48 49 50 51
// This is a font loader which provides only one font (unlike its original design).
// For a better implementation which was also source of this
// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla
class DWriteFontFileLoader : public IDWriteFontFileLoader
52
{
53 54 55
private:
  IDWriteFontFileStream *mFontFileStream;
public:
56
  DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
57
  { mFontFileStream = fontFileStream; }
58

59
  // IUnknown interface
60 61
  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
  { return S_OK; }
62 63
  IFACEMETHOD_ (ULONG, AddRef) ()  { return 1; }
  IFACEMETHOD_ (ULONG, Release) () { return 1; }
64

65
  // IDWriteFontFileLoader methods
66 67 68 69
  virtual HRESULT STDMETHODCALLTYPE
  CreateStreamFromKey (void const* fontFileReferenceKey,
		       uint32_t fontFileReferenceKeySize,
		       OUT IDWriteFontFileStream** fontFileStream)
70 71 72 73
  {
    *fontFileStream = mFontFileStream;
    return S_OK;
  }
74 75

  virtual ~DWriteFontFileLoader() {}
76
};
77

78 79 80 81 82 83
class DWriteFontFileStream : public IDWriteFontFileStream
{
private:
  uint8_t *mData;
  uint32_t mSize;
public:
84
  DWriteFontFileStream (uint8_t *aData, uint32_t aSize)
85 86 87 88
  {
    mData = aData;
    mSize = aSize;
  }
89

90
  // IUnknown interface
91 92
  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
  { return S_OK; }
93 94
  IFACEMETHOD_ (ULONG, AddRef) ()  { return 1; }
  IFACEMETHOD_ (ULONG, Release) () { return 1; }
95

96
  // IDWriteFontFileStream methods
97 98 99 100 101
  virtual HRESULT STDMETHODCALLTYPE
  ReadFileFragment (void const** fragmentStart,
		    UINT64 fileOffset,
		    UINT64 fragmentSize,
		    OUT void** fragmentContext)
102 103
  {
    // We are required to do bounds checking.
104
    if (fileOffset + fragmentSize > mSize) return E_FAIL;
105

106 107
    // truncate the 64 bit fileOffset to size_t sized index into mData
    size_t index = static_cast<size_t> (fileOffset);
108

109 110 111 112
    // We should be alive for the duration of this.
    *fragmentStart = &mData[index];
    *fragmentContext = nullptr;
    return S_OK;
113 114
  }

115 116
  virtual void STDMETHODCALLTYPE
  ReleaseFileFragment (void* fragmentContext) {}
117

118 119
  virtual HRESULT STDMETHODCALLTYPE
  GetFileSize (OUT UINT64* fileSize)
120
  {
121 122
    *fileSize = mSize;
    return S_OK;
123 124
  }

125 126
  virtual HRESULT STDMETHODCALLTYPE
  GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
127 128

  virtual ~DWriteFontFileStream() {}
129
};
130 131


132 133 134
/*
* shaper face data
*/
135

B
Rename  
Behdad Esfahbod 已提交
136
struct hb_directwrite_face_data_t
E
Ebrahim Byagowi 已提交
137
{
138 139
  IDWriteFactory *dwriteFactory;
  IDWriteFontFile *fontFile;
140 141
  DWriteFontFileStream *fontFileStream;
  DWriteFontFileLoader *fontFileLoader;
142 143
  IDWriteFontFace *fontFace;
  hb_blob_t *faceBlob;
144
};
145

B
Rename  
Behdad Esfahbod 已提交
146
hb_directwrite_face_data_t *
147
_hb_directwrite_shaper_face_data_create (hb_face_t *face)
148
{
B
Rename  
Behdad Esfahbod 已提交
149
  hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
150
  if (unlikely (!data))
B
Behdad Esfahbod 已提交
151
    return nullptr;
152

153 154
  // TODO: factory and fontFileLoader should be cached separately
  IDWriteFactory* dwriteFactory;
155 156
  DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
		       (IUnknown**) &dwriteFactory);
157 158

  HRESULT hr;
159
  hb_blob_t *blob = hb_face_reference_blob (face);
160 161 162
  DWriteFontFileStream *fontFileStream;
  fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
					     hb_blob_get_length (blob));
163

164
  DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
165 166 167 168 169
  dwriteFactory->RegisterFontFileLoader (fontFileLoader);

  IDWriteFontFile *fontFile;
  uint64_t fontFileKey = 0;
  hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
170
						     fontFileLoader, &fontFile);
171 172 173

#define FAIL(...) \
  HB_STMT_START { \
B
Behdad Esfahbod 已提交
174
    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
175
    return nullptr; \
176 177
  } HB_STMT_END;

E
Ebrahim Byagowi 已提交
178
  if (FAILED (hr))
179
    FAIL ("Failed to load font file from data!");
180

181 182 183
  BOOL isSupported;
  DWRITE_FONT_FILE_TYPE fileType;
  DWRITE_FONT_FACE_TYPE faceType;
E
Ebrahim Byagowi 已提交
184
  uint32_t numberOfFaces;
185
  hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
E
Ebrahim Byagowi 已提交
186
  if (FAILED (hr) || !isSupported)
187
    FAIL ("Font file is not supported.");
188

189 190 191 192
#undef FAIL

  IDWriteFontFace *fontFace;
  dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
193
				 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
194 195 196

  data->dwriteFactory = dwriteFactory;
  data->fontFile = fontFile;
197
  data->fontFileStream = fontFileStream;
198 199
  data->fontFileLoader = fontFileLoader;
  data->fontFace = fontFace;
200
  data->faceBlob = blob;
201

202 203 204 205
  return data;
}

void
B
Rename  
Behdad Esfahbod 已提交
206
_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
207
{
208 209 210 211
  if (data->fontFace)
    data->fontFace->Release ();
  if (data->fontFile)
    data->fontFile->Release ();
E
Ebrahim Byagowi 已提交
212 213
  if (data->dwriteFactory)
  {
214
    if (data->fontFileLoader)
215 216
      data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
    data->dwriteFactory->Release ();
217 218
  }
  if (data->fontFileLoader)
219
    delete data->fontFileLoader;
220
  if (data->fontFileStream)
221
    delete data->fontFileStream;
222 223 224
  if (data->faceBlob)
    hb_blob_destroy (data->faceBlob);
  if (data)
225
    delete data;
226 227 228 229 230 231 232
}


/*
 * shaper font data
 */

233
struct hb_directwrite_font_data_t {};
234

B
Rename  
Behdad Esfahbod 已提交
235
hb_directwrite_font_data_t *
236 237
_hb_directwrite_shaper_font_data_create (hb_font_t *font)
{
B
Rename  
Behdad Esfahbod 已提交
238
  hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
239
  if (unlikely (!data))
B
Behdad Esfahbod 已提交
240
    return nullptr;
241 242 243 244 245

  return data;
}

void
B
Rename  
Behdad Esfahbod 已提交
246
_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
247
{
248
  delete data;
249 250 251
}


252
// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
253
// but now is relicensed to MIT for HarfBuzz use
254
class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
255 256 257
{
public:

258 259
  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
  { return S_OK; }
260 261
  IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
  IFACEMETHOD_ (ULONG, Release) () { return 1; }
262

263
  // A single contiguous run of characters containing the same analysis
264 265 266
  // results.
  struct Run
  {
267 268 269
    uint32_t mTextStart;   // starting text position of this run
    uint32_t mTextLength;  // number of contiguous code units covered
    uint32_t mGlyphStart;  // starting glyph in the glyphs array
270
    uint32_t mGlyphCount;  // number of glyphs associated with this run
271 272
    // text
    DWRITE_SCRIPT_ANALYSIS mScript;
273
    uint8_t mBidiLevel;
274 275
    bool mIsSideways;

276
    bool ContainsTextPosition (uint32_t aTextPosition) const
277
    {
278 279
      return aTextPosition >= mTextStart &&
	     aTextPosition <  mTextStart + mTextLength;
280 281 282 283 284 285
    }

    Run *nextRun;
  };

public:
286 287
  TextAnalysis (const wchar_t* text, uint32_t textLength,
		const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
B
Behdad Esfahbod 已提交
288
	       : mTextLength (textLength), mText (text), mLocaleName (localeName),
289
		 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
290
  ~TextAnalysis ()
E
Ebrahim Byagowi 已提交
291
  {
292
    // delete runs, except mRunHead which is part of the TextAnalysis object
E
Ebrahim Byagowi 已提交
293 294
    for (Run *run = mRunHead.nextRun; run;)
    {
295 296
      Run *origRun = run;
      run = run->nextRun;
297
      delete origRun;
298 299 300
    }
  }

301 302
  STDMETHODIMP
  GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
E
Ebrahim Byagowi 已提交
303
  {
304 305 306 307 308 309 310 311 312 313 314
    // Analyzes the text using the script analyzer and returns
    // the result as a series of runs.

    HRESULT hr = S_OK;

    // Initially start out with one result that covers the entire range.
    // This result will be subdivided by the analysis processes.
    mRunHead.mTextStart = 0;
    mRunHead.mTextLength = mTextLength;
    mRunHead.mBidiLevel =
      (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
B
Behdad Esfahbod 已提交
315
    mRunHead.nextRun = nullptr;
316 317 318
    mCurrentRun = &mRunHead;

    // Call each of the analyzers in sequence, recording their results.
E
Ebrahim Byagowi 已提交
319
    if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
320 321 322 323 324 325 326
      *runHead = &mRunHead;

    return hr;
  }

  // IDWriteTextAnalysisSource implementation

327 328 329 330
  IFACEMETHODIMP
  GetTextAtPosition (uint32_t textPosition,
		     OUT wchar_t const** textString,
		     OUT uint32_t* textLength)
331
  {
E
Ebrahim Byagowi 已提交
332 333
    if (textPosition >= mTextLength)
    {
334
      // No text at this position, valid query though.
B
Behdad Esfahbod 已提交
335
      *textString = nullptr;
336 337
      *textLength = 0;
    }
E
Ebrahim Byagowi 已提交
338 339
    else
    {
340 341 342 343 344 345
      *textString = mText + textPosition;
      *textLength = mTextLength - textPosition;
    }
    return S_OK;
  }

346 347 348 349
  IFACEMETHODIMP
  GetTextBeforePosition (uint32_t textPosition,
			 OUT wchar_t const** textString,
			 OUT uint32_t* textLength)
350
  {
E
Ebrahim Byagowi 已提交
351 352
    if (textPosition == 0 || textPosition > mTextLength)
    {
353
      // Either there is no text before here (== 0), or this
B
Bruce Mitchener 已提交
354
      // is an invalid position. The query is considered valid though.
B
Behdad Esfahbod 已提交
355
      *textString = nullptr;
356 357
      *textLength = 0;
    }
E
Ebrahim Byagowi 已提交
358 359
    else
    {
360 361 362 363 364 365
      *textString = mText;
      *textLength = textPosition;
    }
    return S_OK;
  }

366
  IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
367
  GetParagraphReadingDirection () { return mReadingDirection; }
368

369 370 371
  IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
				wchar_t const** localeName)
  { return S_OK; }
372 373

  IFACEMETHODIMP
374 375 376
  GetNumberSubstitution (uint32_t textPosition,
			 OUT uint32_t* textLength,
			 OUT IDWriteNumberSubstitution** numberSubstitution)
377 378
  {
    // We do not support number substitution.
B
Behdad Esfahbod 已提交
379
    *numberSubstitution = nullptr;
380 381 382 383 384 385 386 387
    *textLength = mTextLength - textPosition;

    return S_OK;
  }

  // IDWriteTextAnalysisSink implementation

  IFACEMETHODIMP
388 389
  SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
		     DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
390
  {
391 392
    SetCurrentRun (textPosition);
    SplitCurrentRun (textPosition);
393 394
    while (textLength > 0)
    {
395
      Run *run = FetchNextRun (&textLength);
396 397 398 399 400 401 402
      run->mScript = *scriptAnalysis;
    }

    return S_OK;
  }

  IFACEMETHODIMP
403 404 405 406
  SetLineBreakpoints (uint32_t textPosition,
		      uint32_t textLength,
		      const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
  { return S_OK; }
407

408 409 410
  IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
			       uint8_t explicitLevel, uint8_t resolvedLevel)
  { return S_OK; }
411 412

  IFACEMETHODIMP
413 414 415
  SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
			 IDWriteNumberSubstitution* numberSubstitution)
  { return S_OK; }
416 417

protected:
418
  Run *FetchNextRun (IN OUT uint32_t* textLength)
419 420 421 422 423 424 425 426
  {
    // Used by the sink setters, this returns a reference to the next run.
    // Position and length are adjusted to now point after the current run
    // being returned.

    Run *origRun = mCurrentRun;
    // Split the tail if needed (the length remaining is less than the
    // current run's size).
427 428 429
    if (*textLength < mCurrentRun->mTextLength)
      SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
    else
430 431 432 433 434 435 436 437
      // Just advance the current run.
      mCurrentRun = mCurrentRun->nextRun;
    *textLength -= origRun->mTextLength;

    // Return a reference to the run that was just current.
    return origRun;
  }

438
  void SetCurrentRun (uint32_t textPosition)
439 440 441 442 443 444
  {
    // Move the current run to the given position.
    // Since the analyzers generally return results in a forward manner,
    // this will usually just return early. If not, find the
    // corresponding run for the text position.

445
    if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
446 447
      return;

E
Ebrahim Byagowi 已提交
448
    for (Run *run = &mRunHead; run; run = run->nextRun)
449 450
      if (run->ContainsTextPosition (textPosition))
      {
451 452
	mCurrentRun = run;
	return;
453
      }
454
    assert (0); // We should always be able to find the text position in one of our runs
455 456
  }

457
  void SplitCurrentRun (uint32_t splitPosition)
458
  {
459 460
    if (!mCurrentRun)
    {
461
      assert (0); // SplitCurrentRun called without current run
462 463 464 465
      // Shouldn't be calling this when no current run is set!
      return;
    }
    // Split the current run.
466 467
    if (splitPosition <= mCurrentRun->mTextStart)
    {
468 469 470 471
      // No need to split, already the start of a run
      // or before it. Usually the first.
      return;
    }
472
    Run *newRun = new Run;
473 474 475 476 477 478 479 480

    *newRun = *mCurrentRun;

    // Insert the new run in our linked list.
    newRun->nextRun = mCurrentRun->nextRun;
    mCurrentRun->nextRun = newRun;

    // Adjust runs' text positions and lengths.
481
    uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
482 483 484 485 486 487 488 489 490 491
    newRun->mTextStart += splitPoint;
    newRun->mTextLength -= splitPoint;
    mCurrentRun->mTextLength = splitPoint;
    mCurrentRun = newRun;
  }

protected:
  // Input
  // (weak references are fine here, since this class is a transient
  //  stack-based helper that doesn't need to copy data)
492 493 494
  uint32_t mTextLength;
  const wchar_t* mText;
  const wchar_t* mLocaleName;
495 496 497 498 499 500 501 502 503
  DWRITE_READING_DIRECTION mReadingDirection;

  // Current processing state.
  Run *mCurrentRun;

  // Output is a list of runs starting here
  Run  mRunHead;
};

504
static inline uint16_t hb_uint16_swap (const uint16_t v)
505
{ return (v >> 8) | (v << 8); }
506
static inline uint32_t hb_uint32_swap (const uint32_t v)
507
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
508 509 510 511 512

/*
 * shaper
 */

513
static hb_bool_t
514
_hb_directwrite_shape_full (hb_shape_plan_t    *shape_plan,
515 516 517 518 519
			    hb_font_t          *font,
			    hb_buffer_t        *buffer,
			    const hb_feature_t *features,
			    unsigned int        num_features,
			    float               lineWidth)
520 521
{
  hb_face_t *face = font->face;
522
  const hb_directwrite_face_data_t *face_data = face->data.directwrite;
523 524
  IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
  IDWriteFontFace *fontFace = face_data->fontFace;
525 526

  IDWriteTextAnalyzer* analyzer;
527
  dwriteFactory->CreateTextAnalyzer (&analyzer);
528 529 530 531 532 533 534 535 536 537 538 539 540 541

  unsigned int scratch_size;
  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
#define ALLOCATE_ARRAY(Type, name, len) \
  Type *name = (Type *) scratch; \
  { \
    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
    assert (_consumed <= scratch_size); \
    scratch += _consumed; \
    scratch_size -= _consumed; \
  }

#define utf16_index() var1.u32

542
  ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
543 544 545 546 547

  unsigned int chars_len = 0;
  for (unsigned int i = 0; i < buffer->len; i++)
  {
    hb_codepoint_t c = buffer->info[i].codepoint;
548 549
    buffer->info[i].utf16_index () = chars_len;
    if (likely (c <= 0xFFFFu))
E
Ebrahim Byagowi 已提交
550
      textString[chars_len++] = c;
551
    else if (unlikely (c > 0x10FFFFu))
E
Ebrahim Byagowi 已提交
552
      textString[chars_len++] = 0xFFFDu;
E
Ebrahim Byagowi 已提交
553 554
    else
    {
E
Ebrahim Byagowi 已提交
555
      textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
B
Behdad Esfahbod 已提交
556
      textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
557 558 559
    }
  }

560
  ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
E
Ebrahim Byagowi 已提交
561 562 563
  /* Need log_clusters to assign features. */
  chars_len = 0;
  for (unsigned int i = 0; i < buffer->len; i++)
564
  {
E
Ebrahim Byagowi 已提交
565 566 567 568 569
    hb_codepoint_t c = buffer->info[i].codepoint;
    unsigned int cluster = buffer->info[i].cluster;
    log_clusters[chars_len++] = cluster;
    if (hb_in_range (c, 0x10000u, 0x10FFFFu))
      log_clusters[chars_len++] = cluster; /* Surrogates. */
570 571 572 573
  }

  // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES

574 575 576 577
  DWRITE_READING_DIRECTION readingDirection;
  readingDirection = buffer->props.direction ?
		     DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
		     DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
578

K
Khaled Hosny 已提交
579
  /*
580 581 582 583
  * There's an internal 16-bit limit on some things inside the analyzer,
  * but we never attempt to shape a word longer than 64K characters
  * in a single gfxShapedWord, so we cannot exceed that limit.
  */
584
  uint32_t textLength = buffer->len;
585

586
  TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
587
  TextAnalysis::Run *runHead;
588
  HRESULT hr;
589
  hr = analysis.GenerateResults (analyzer, &runHead);
590

591 592
#define FAIL(...) \
  HB_STMT_START { \
B
Behdad Esfahbod 已提交
593
    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
594 595 596
    return false; \
  } HB_STMT_END;

E
Ebrahim Byagowi 已提交
597
  if (FAILED (hr))
598
    FAIL ("Analyzer failed to generate results.");
599

600 601
  uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
  uint32_t glyphCount;
E
Ebrahim Byagowi 已提交
602
  bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
603

E
Ebrahim Byagowi 已提交
604
  const wchar_t localeName[20] = {0};
B
Behdad Esfahbod 已提交
605
  if (buffer->props.language != nullptr)
E
Ebrahim Byagowi 已提交
606
    mbstowcs ((wchar_t*) localeName,
607
	      hb_language_to_string (buffer->props.language), 20);
608

E
Ebrahim Byagowi 已提交
609 610 611
  // TODO: it does work but doesn't care about ranges
  DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
  typographic_features.featureCount = num_features;
E
Ebrahim Byagowi 已提交
612
  if (num_features)
613
  {
E
Ebrahim Byagowi 已提交
614
    typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
615 616
    for (unsigned int i = 0; i < num_features; ++i)
    {
E
Ebrahim Byagowi 已提交
617
      typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
618
						 hb_uint32_swap (features[i].tag);
E
Ebrahim Byagowi 已提交
619
      typographic_features.features[i].parameter = features[i].value;
620
    }
E
Ebrahim Byagowi 已提交
621
  }
622 623
  const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
  dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
624
  const uint32_t featureRangeLengths[] = { textLength };
E
Ebrahim Byagowi 已提交
625
  //
E
Ebrahim Byagowi 已提交
626

627 628 629 630
  uint16_t* clusterMap;
  clusterMap = new uint16_t[textLength];
  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
  textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
E
Ebrahim Byagowi 已提交
631
retry_getglyphs:
632
  uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
633 634
  DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
  glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
E
Ebrahim Byagowi 已提交
635

B
Minor  
Behdad Esfahbod 已提交
636
  hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
637 638 639 640
			    isRightToLeft, &runHead->mScript, localeName,
			    nullptr, &dwFeatures, featureRangeLengths, 1,
			    maxGlyphCount, clusterMap, textProperties,
			    glyphIndices, glyphProperties, &glyphCount);
E
Ebrahim Byagowi 已提交
641 642 643

  if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
  {
644 645
    delete [] glyphIndices;
    delete [] glyphProperties;
646

E
Ebrahim Byagowi 已提交
647
    maxGlyphCount *= 2;
648 649

    goto retry_getglyphs;
650
  }
E
Ebrahim Byagowi 已提交
651
  if (FAILED (hr))
652
    FAIL ("Analyzer failed to get glyphs.");
653

654 655
  float* glyphAdvances = new float[maxGlyphCount];
  DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
656 657

  /* The -2 in the following is to compensate for possible
658 659
   * alignment needed after the WORD array.  sizeof (WORD) == 2. */
  unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
660
			     / (sizeof (WORD) +
661 662 663 664
				sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
				sizeof (int) +
				sizeof (DWRITE_GLYPH_OFFSET) +
				sizeof (uint32_t));
E
Ebrahim Byagowi 已提交
665
  ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
666 667 668

#undef ALLOCATE_ARRAY

669
  int fontEmSize = font->face->get_upem ();
670
  if (fontEmSize < 0) fontEmSize = -fontEmSize;
E
Ebrahim Byagowi 已提交
671

672
  if (fontEmSize < 0) fontEmSize = -fontEmSize;
E
Ebrahim Byagowi 已提交
673 674 675
  double x_mult = (double) font->x_scale / fontEmSize;
  double y_mult = (double) font->y_scale / fontEmSize;

676 677 678 679 680 681
  hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties,
				     textLength, glyphIndices, glyphProperties,
				     glyphCount, fontFace, fontEmSize,
				     false, isRightToLeft, &runHead->mScript, localeName,
				     &dwFeatures, featureRangeLengths, 1,
				     glyphAdvances, glyphOffsets);
E
Ebrahim Byagowi 已提交
682 683

  if (FAILED (hr))
684
    FAIL ("Analyzer failed to get glyph placements.");
685

686 687
  IDWriteTextAnalyzer1* analyzer1;
  analyzer->QueryInterface (&analyzer1);
E
Ebrahim Byagowi 已提交
688

689
  if (analyzer1 && lineWidth)
E
Ebrahim Byagowi 已提交
690
  {
691
    DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
692
      new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
693 694 695 696
    hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
						   textLength, glyphCount, textString,
						   clusterMap, glyphProperties,
						   justificationOpportunities);
E
Ebrahim Byagowi 已提交
697

698 699
    if (FAILED (hr))
      FAIL ("Analyzer failed to get justification opportunities.");
E
Ebrahim Byagowi 已提交
700

701 702
    float* justifiedGlyphAdvances = new float[maxGlyphCount];
    DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
703
    hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
704 705
					  glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
					  justifiedGlyphOffsets);
E
Ebrahim Byagowi 已提交
706

707
    if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances.");
708

709 710
    DWRITE_SCRIPT_PROPERTIES scriptProperties;
    hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
711
    if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
712 713 714 715 716
    uint32_t justificationCharacter = scriptProperties.justificationCharacter;

    // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
    if (justificationCharacter != 32)
    {
717
      uint16_t* modifiedClusterMap = new uint16_t[textLength];
718
    retry_getjustifiedglyphs:
719 720
      uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
      float* modifiedGlyphAdvances = new float[maxGlyphCount];
721
      DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
722 723
      uint32_t actualGlyphsCount;
      hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
724 725 726 727 728 729
					  textLength, glyphCount, maxGlyphCount,
					  clusterMap, glyphIndices, glyphAdvances,
					  justifiedGlyphAdvances, justifiedGlyphOffsets,
					  glyphProperties, &actualGlyphsCount,
					  modifiedClusterMap, modifiedGlyphIndices,
					  modifiedGlyphAdvances, modifiedGlyphOffsets);
730

731 732
      if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
      {
733 734 735 736
	maxGlyphCount = actualGlyphsCount;
	delete [] modifiedGlyphIndices;
	delete [] modifiedGlyphAdvances;
	delete [] modifiedGlyphOffsets;
737

738
	maxGlyphCount = actualGlyphsCount;
739

740
	goto retry_getjustifiedglyphs;
741 742
      }
      if (FAILED (hr))
743
	FAIL ("Analyzer failed to get justified glyphs.");
744

745 746 747 748
      delete [] clusterMap;
      delete [] glyphIndices;
      delete [] glyphAdvances;
      delete [] glyphOffsets;
E
Ebrahim Byagowi 已提交
749

750 751 752 753 754
      glyphCount = actualGlyphsCount;
      clusterMap = modifiedClusterMap;
      glyphIndices = modifiedGlyphIndices;
      glyphAdvances = modifiedGlyphAdvances;
      glyphOffsets = modifiedGlyphOffsets;
E
Ebrahim Byagowi 已提交
755

756 757
      delete [] justifiedGlyphAdvances;
      delete [] justifiedGlyphOffsets;
758 759 760
    }
    else
    {
761 762
      delete [] glyphAdvances;
      delete [] glyphOffsets;
763

764 765 766
      glyphAdvances = justifiedGlyphAdvances;
      glyphOffsets = justifiedGlyphOffsets;
    }
E
Ebrahim Byagowi 已提交
767

768
    delete [] justificationOpportunities;
769
  }
E
Ebrahim Byagowi 已提交
770

771 772 773 774
  /* Ok, we've got everything we need, now compose output buffer,
   * very, *very*, carefully! */

  /* Calculate visual-clusters.  That's what we ship. */
E
Ebrahim Byagowi 已提交
775
  for (unsigned int i = 0; i < glyphCount; i++)
B
Behdad Esfahbod 已提交
776
    vis_clusters[i] = (uint32_t) -1;
E
Ebrahim Byagowi 已提交
777 778 779
  for (unsigned int i = 0; i < buffer->len; i++)
  {
    uint32_t *p =
780
      &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
781
    *p = MIN (*p, buffer->info[i].cluster);
782
  }
E
Ebrahim Byagowi 已提交
783
  for (unsigned int i = 1; i < glyphCount; i++)
B
Behdad Esfahbod 已提交
784
    if (vis_clusters[i] == (uint32_t) -1)
785 786 787 788
      vis_clusters[i] = vis_clusters[i - 1];

#undef utf16_index

E
Ebrahim Byagowi 已提交
789
  if (unlikely (!buffer->ensure (glyphCount)))
790
    FAIL ("Buffer in error");
791 792 793 794 795

#undef FAIL

  /* Set glyph infos */
  buffer->len = 0;
E
Ebrahim Byagowi 已提交
796
  for (unsigned int i = 0; i < glyphCount; i++)
797 798 799
  {
    hb_glyph_info_t *info = &buffer->info[buffer->len++];

E
Ebrahim Byagowi 已提交
800
    info->codepoint = glyphIndices[i];
801 802 803
    info->cluster = vis_clusters[i];

    /* The rest is crap.  Let's store position info there for now. */
E
Ebrahim Byagowi 已提交
804 805 806
    info->mask = glyphAdvances[i];
    info->var1.i32 = glyphOffsets[i].advanceOffset;
    info->var2.i32 = glyphOffsets[i].ascenderOffset;
807 808 809 810
  }

  /* Set glyph positions */
  buffer->clear_positions ();
E
Ebrahim Byagowi 已提交
811
  for (unsigned int i = 0; i < glyphCount; i++)
812 813 814 815 816
  {
    hb_glyph_info_t *info = &buffer->info[i];
    hb_glyph_position_t *pos = &buffer->pos[i];

    /* TODO vertical */
E
Ebrahim Byagowi 已提交
817
    pos->x_advance = x_mult * (int32_t) info->mask;
818
    pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
E
Ebrahim Byagowi 已提交
819
    pos->y_offset = y_mult * info->var2.i32;
820 821
  }

822
  if (isRightToLeft) hb_buffer_reverse (buffer);
823

824 825 826 827 828 829
  delete [] clusterMap;
  delete [] glyphIndices;
  delete [] textProperties;
  delete [] glyphProperties;
  delete [] glyphAdvances;
  delete [] glyphOffsets;
E
Ebrahim Byagowi 已提交
830 831

  if (num_features)
E
Ebrahim Byagowi 已提交
832
    delete [] typographic_features.features;
E
Ebrahim Byagowi 已提交
833

834 835
  /* Wow, done! */
  return true;
K
Khaled Hosny 已提交
836
}
837 838

hb_bool_t
839
_hb_directwrite_shape (hb_shape_plan_t    *shape_plan,
840 841 842 843
		       hb_font_t          *font,
		       hb_buffer_t        *buffer,
		       const hb_feature_t *features,
		       unsigned int        num_features)
844
{
845
  return _hb_directwrite_shape_full (shape_plan, font, buffer,
846
				     features, num_features, 0);
847 848
}

849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
/**
 * hb_directwrite_shape_experimental_width:
 * Experimental API to test DirectWrite's justification algorithm.
 *
 * It inserts Kashida at wrong order so don't use the API ever.
 *
 * It doesn't work with cygwin/msys due to header bugs so one
 * should use MSVC toolchain in order to use it for now.
 *
 * @font:
 * @buffer:
 * @features:
 * @num_features:
 * @width:
 *
 * Since: 1.4.2
 **/
866
hb_bool_t
867
hb_directwrite_shape_experimental_width (hb_font_t          *font,
868 869 870 871
					 hb_buffer_t        *buffer,
					 const hb_feature_t *features,
					 unsigned int        num_features,
					 float               width)
872
{
873
  static const char *shapers = "directwrite";
874 875 876
  hb_shape_plan_t *shape_plan;
  shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
					    features, num_features, &shapers);
877
  hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
878
					      features, num_features, width);
879

880
  buffer->unsafe_to_break_all ();
881 882 883

  return res;
}
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932

struct _hb_directwrite_font_table_context {
  IDWriteFontFace *face;
  void *table_context;
};

static void
_hb_directwrite_table_data_release (void *data)
{
  _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data;
  context->face->ReleaseFontTable (context->table_context);
  delete context;
}

static hb_blob_t *
reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
{
  IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
  const void *data;
  uint32_t length;
  void *table_context;
  BOOL exists;
  if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
						    &length, &table_context, &exists)))
    return nullptr;

  if (!data || !exists || !length)
  {
    dw_face->ReleaseFontTable (table_context);
    return nullptr;
  }

  _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context;
  context->face = dw_face;
  context->table_context = table_context;

  return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY,
			 context, _hb_directwrite_table_data_release);
}

static void
_hb_directwrite_font_release (void *data)
{
  if (data)
    ((IDWriteFontFace *) data)->Release ();
}

/**
 * hb_directwrite_face_create:
933 934 935
 * @font_face: a DirectWrite IDWriteFontFace object.
 *
 * Return value: #hb_face_t object corresponding to the given input
936
 *
937
 * Since: 2.4.0
938 939 940 941 942 943 944 945 946
 **/
hb_face_t *
hb_directwrite_face_create (IDWriteFontFace *font_face)
{
  if (font_face)
    font_face->AddRef ();
  return hb_face_create_for_tables (reference_table, font_face,
				    _hb_directwrite_font_release);
}
947 948 949

/**
* hb_directwrite_face_get_font_face:
950 951 952
* @face: a #hb_face_t object
*
* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
953 954 955 956 957 958 959 960
*
* Since: REPLACEME
**/
IDWriteFontFace *
hb_directwrite_face_get_font_face (hb_face_t *face)
{
  return face->data.directwrite->fontFace;
}