hb-directwrite.cc 29.0 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
#include "hb.hh"
26 27 28

#ifdef HAVE_DIRECTWRITE

29
#include "hb-shaper-impl.hh"
30

31
#include <dwrite_1.h>
32 33 34 35

#include "hb-directwrite.h"


C
Carlo Bramini 已提交
36 37 38 39 40 41 42
/* Declare object creator for dynamic support of DWRITE */
typedef HRESULT (* WINAPI t_DWriteCreateFactory)(
  DWRITE_FACTORY_TYPE factoryType,
  REFIID              iid,
  IUnknown            **factory
);

43 44 45 46 47
/*
 * 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.
 */
48 49 50
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); }
51
void operator delete [] (void* pointer) { free (pointer); }
52 53


54 55 56
/*
 * DirectWrite font stream helpers
 */
57

58 59 60 61
// 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
62
{
63 64 65
private:
  IDWriteFontFileStream *mFontFileStream;
public:
66
  DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
67
  { mFontFileStream = fontFileStream; }
68

69
  // IUnknown interface
70 71
  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
  { return S_OK; }
72 73
  IFACEMETHOD_ (ULONG, AddRef) ()  { return 1; }
  IFACEMETHOD_ (ULONG, Release) () { return 1; }
74

75
  // IDWriteFontFileLoader methods
76 77 78 79
  virtual HRESULT STDMETHODCALLTYPE
  CreateStreamFromKey (void const* fontFileReferenceKey,
		       uint32_t fontFileReferenceKeySize,
		       OUT IDWriteFontFileStream** fontFileStream)
80 81 82 83
  {
    *fontFileStream = mFontFileStream;
    return S_OK;
  }
84 85

  virtual ~DWriteFontFileLoader() {}
86
};
87

88 89 90 91 92 93
class DWriteFontFileStream : public IDWriteFontFileStream
{
private:
  uint8_t *mData;
  uint32_t mSize;
public:
94
  DWriteFontFileStream (uint8_t *aData, uint32_t aSize)
95 96 97 98
  {
    mData = aData;
    mSize = aSize;
  }
99

100
  // IUnknown interface
101 102
  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
  { return S_OK; }
103 104
  IFACEMETHOD_ (ULONG, AddRef) ()  { return 1; }
  IFACEMETHOD_ (ULONG, Release) () { return 1; }
105

106
  // IDWriteFontFileStream methods
107 108 109 110 111
  virtual HRESULT STDMETHODCALLTYPE
  ReadFileFragment (void const** fragmentStart,
		    UINT64 fileOffset,
		    UINT64 fragmentSize,
		    OUT void** fragmentContext)
112 113
  {
    // We are required to do bounds checking.
114
    if (fileOffset + fragmentSize > mSize) return E_FAIL;
115

116 117
    // truncate the 64 bit fileOffset to size_t sized index into mData
    size_t index = static_cast<size_t> (fileOffset);
118

119 120 121 122
    // We should be alive for the duration of this.
    *fragmentStart = &mData[index];
    *fragmentContext = nullptr;
    return S_OK;
123 124
  }

125 126
  virtual void STDMETHODCALLTYPE
  ReleaseFileFragment (void* fragmentContext) {}
127

128 129
  virtual HRESULT STDMETHODCALLTYPE
  GetFileSize (OUT UINT64* fileSize)
130
  {
131 132
    *fileSize = mSize;
    return S_OK;
133 134
  }

135 136
  virtual HRESULT STDMETHODCALLTYPE
  GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
137 138

  virtual ~DWriteFontFileStream() {}
139
};
140 141


142 143 144
/*
* shaper face data
*/
145

B
Rename  
Behdad Esfahbod 已提交
146
struct hb_directwrite_face_data_t
E
Ebrahim Byagowi 已提交
147
{
C
Carlo Bramini 已提交
148
  HMODULE dwrite_dll;
149 150
  IDWriteFactory *dwriteFactory;
  IDWriteFontFile *fontFile;
151 152
  DWriteFontFileStream *fontFileStream;
  DWriteFontFileLoader *fontFileLoader;
153 154
  IDWriteFontFace *fontFace;
  hb_blob_t *faceBlob;
155
};
156

B
Rename  
Behdad Esfahbod 已提交
157
hb_directwrite_face_data_t *
158
_hb_directwrite_shaper_face_data_create (hb_face_t *face)
159
{
B
Rename  
Behdad Esfahbod 已提交
160
  hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
161
  if (unlikely (!data))
B
Behdad Esfahbod 已提交
162
    return nullptr;
163

C
Carlo Bramini 已提交
164 165 166 167 168 169 170 171 172 173 174 175
#define FAIL(...) \
  HB_STMT_START { \
    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
    return nullptr; \
  } HB_STMT_END

  data->dwrite_dll = LoadLibrary(TEXT("DWRITE"));
  if (data->dwrite_dll == NULL)
    FAIL ("Cannot find DWrite.DLL");

  t_DWriteCreateFactory p_DWriteCreateFactory;

C
Carlo Bramini 已提交
176 177 178 179 180
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
#endif

C
Carlo Bramini 已提交
181 182
  p_DWriteCreateFactory = (t_DWriteCreateFactory)
	GetProcAddress(data->dwrite_dll, "DWriteCreateFactory");
C
Carlo Bramini 已提交
183 184 185 186 187

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

C
Carlo Bramini 已提交
188 189 190 191 192
  if (p_DWriteCreateFactory == NULL)
    FAIL ("Cannot find DWriteCreateFactory().");

  HRESULT hr;

193 194
  // TODO: factory and fontFileLoader should be cached separately
  IDWriteFactory* dwriteFactory;
C
Carlo Bramini 已提交
195
  hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
196
		       (IUnknown**) &dwriteFactory);
197

C
Carlo Bramini 已提交
198 199 200
  if (hr != S_OK)
    FAIL ("Failed to run DWriteCreateFactory().");

201
  hb_blob_t *blob = hb_face_reference_blob (face);
202 203 204
  DWriteFontFileStream *fontFileStream;
  fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
					     hb_blob_get_length (blob));
205

206
  DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
207 208 209 210 211
  dwriteFactory->RegisterFontFileLoader (fontFileLoader);

  IDWriteFontFile *fontFile;
  uint64_t fontFileKey = 0;
  hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
212
						     fontFileLoader, &fontFile);
213

E
Ebrahim Byagowi 已提交
214
  if (FAILED (hr))
215
    FAIL ("Failed to load font file from data!");
216

217 218 219
  BOOL isSupported;
  DWRITE_FONT_FILE_TYPE fileType;
  DWRITE_FONT_FACE_TYPE faceType;
E
Ebrahim Byagowi 已提交
220
  uint32_t numberOfFaces;
221
  hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
E
Ebrahim Byagowi 已提交
222
  if (FAILED (hr) || !isSupported)
223
    FAIL ("Font file is not supported.");
224

225 226 227 228
#undef FAIL

  IDWriteFontFace *fontFace;
  dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
229
				 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
230 231 232

  data->dwriteFactory = dwriteFactory;
  data->fontFile = fontFile;
233
  data->fontFileStream = fontFileStream;
234 235
  data->fontFileLoader = fontFileLoader;
  data->fontFace = fontFace;
236
  data->faceBlob = blob;
237

238 239 240 241
  return data;
}

void
B
Rename  
Behdad Esfahbod 已提交
242
_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
243
{
244 245 246 247
  if (data->fontFace)
    data->fontFace->Release ();
  if (data->fontFile)
    data->fontFile->Release ();
E
Ebrahim Byagowi 已提交
248 249
  if (data->dwriteFactory)
  {
250
    if (data->fontFileLoader)
251 252
      data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
    data->dwriteFactory->Release ();
253 254
  }
  if (data->fontFileLoader)
255
    delete data->fontFileLoader;
256
  if (data->fontFileStream)
257
    delete data->fontFileStream;
258 259
  if (data->faceBlob)
    hb_blob_destroy (data->faceBlob);
C
Carlo Bramini 已提交
260 261
  if (data->dwrite_dll != NULL)
    FreeLibrary(data->dwrite_dll);
262
  if (data)
263
    delete data;
264 265 266 267 268 269 270
}


/*
 * shaper font data
 */

271
struct hb_directwrite_font_data_t {};
272

B
Rename  
Behdad Esfahbod 已提交
273
hb_directwrite_font_data_t *
274 275
_hb_directwrite_shaper_font_data_create (hb_font_t *font)
{
B
Rename  
Behdad Esfahbod 已提交
276
  hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
277
  if (unlikely (!data))
B
Behdad Esfahbod 已提交
278
    return nullptr;
279 280 281 282 283

  return data;
}

void
B
Rename  
Behdad Esfahbod 已提交
284
_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
285
{
286
  delete data;
287 288 289
}


290
// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
291
// but now is relicensed to MIT for HarfBuzz use
292
class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
293 294 295
{
public:

296 297
  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
  { return S_OK; }
298 299
  IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
  IFACEMETHOD_ (ULONG, Release) () { return 1; }
300

301
  // A single contiguous run of characters containing the same analysis
302 303 304
  // results.
  struct Run
  {
305 306 307
    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
308
    uint32_t mGlyphCount;  // number of glyphs associated with this run
309 310
    // text
    DWRITE_SCRIPT_ANALYSIS mScript;
311
    uint8_t mBidiLevel;
312 313
    bool mIsSideways;

314
    bool ContainsTextPosition (uint32_t aTextPosition) const
315
    {
316 317
      return aTextPosition >= mTextStart &&
	     aTextPosition <  mTextStart + mTextLength;
318 319 320 321 322 323
    }

    Run *nextRun;
  };

public:
324 325
  TextAnalysis (const wchar_t* text, uint32_t textLength,
		const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
B
Behdad Esfahbod 已提交
326
	       : mTextLength (textLength), mText (text), mLocaleName (localeName),
327
		 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
328
  ~TextAnalysis ()
E
Ebrahim Byagowi 已提交
329
  {
330
    // delete runs, except mRunHead which is part of the TextAnalysis object
E
Ebrahim Byagowi 已提交
331 332
    for (Run *run = mRunHead.nextRun; run;)
    {
333 334
      Run *origRun = run;
      run = run->nextRun;
335
      delete origRun;
336 337 338
    }
  }

339 340
  STDMETHODIMP
  GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
E
Ebrahim Byagowi 已提交
341
  {
342 343 344 345 346 347 348 349 350 351 352
    // 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 已提交
353
    mRunHead.nextRun = nullptr;
354 355 356
    mCurrentRun = &mRunHead;

    // Call each of the analyzers in sequence, recording their results.
E
Ebrahim Byagowi 已提交
357
    if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this)))
358 359 360 361 362 363 364
      *runHead = &mRunHead;

    return hr;
  }

  // IDWriteTextAnalysisSource implementation

365 366 367 368
  IFACEMETHODIMP
  GetTextAtPosition (uint32_t textPosition,
		     OUT wchar_t const** textString,
		     OUT uint32_t* textLength)
369
  {
E
Ebrahim Byagowi 已提交
370 371
    if (textPosition >= mTextLength)
    {
372
      // No text at this position, valid query though.
B
Behdad Esfahbod 已提交
373
      *textString = nullptr;
374 375
      *textLength = 0;
    }
E
Ebrahim Byagowi 已提交
376 377
    else
    {
378 379 380 381 382 383
      *textString = mText + textPosition;
      *textLength = mTextLength - textPosition;
    }
    return S_OK;
  }

384 385 386 387
  IFACEMETHODIMP
  GetTextBeforePosition (uint32_t textPosition,
			 OUT wchar_t const** textString,
			 OUT uint32_t* textLength)
388
  {
E
Ebrahim Byagowi 已提交
389 390
    if (textPosition == 0 || textPosition > mTextLength)
    {
391
      // Either there is no text before here (== 0), or this
B
Bruce Mitchener 已提交
392
      // is an invalid position. The query is considered valid though.
B
Behdad Esfahbod 已提交
393
      *textString = nullptr;
394 395
      *textLength = 0;
    }
E
Ebrahim Byagowi 已提交
396 397
    else
    {
398 399 400 401 402 403
      *textString = mText;
      *textLength = textPosition;
    }
    return S_OK;
  }

404
  IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
405
  GetParagraphReadingDirection () { return mReadingDirection; }
406

407 408 409
  IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
				wchar_t const** localeName)
  { return S_OK; }
410 411

  IFACEMETHODIMP
412 413 414
  GetNumberSubstitution (uint32_t textPosition,
			 OUT uint32_t* textLength,
			 OUT IDWriteNumberSubstitution** numberSubstitution)
415 416
  {
    // We do not support number substitution.
B
Behdad Esfahbod 已提交
417
    *numberSubstitution = nullptr;
418 419 420 421 422 423 424 425
    *textLength = mTextLength - textPosition;

    return S_OK;
  }

  // IDWriteTextAnalysisSink implementation

  IFACEMETHODIMP
426 427
  SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
		     DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
428
  {
429 430
    SetCurrentRun (textPosition);
    SplitCurrentRun (textPosition);
431 432
    while (textLength > 0)
    {
433
      Run *run = FetchNextRun (&textLength);
434 435 436 437 438 439 440
      run->mScript = *scriptAnalysis;
    }

    return S_OK;
  }

  IFACEMETHODIMP
441 442 443 444
  SetLineBreakpoints (uint32_t textPosition,
		      uint32_t textLength,
		      const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
  { return S_OK; }
445

446 447 448
  IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
			       uint8_t explicitLevel, uint8_t resolvedLevel)
  { return S_OK; }
449 450

  IFACEMETHODIMP
451 452 453
  SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
			 IDWriteNumberSubstitution* numberSubstitution)
  { return S_OK; }
454 455

protected:
456
  Run *FetchNextRun (IN OUT uint32_t* textLength)
457 458 459 460 461 462 463 464
  {
    // 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).
465 466 467
    if (*textLength < mCurrentRun->mTextLength)
      SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
    else
468 469 470 471 472 473 474 475
      // Just advance the current run.
      mCurrentRun = mCurrentRun->nextRun;
    *textLength -= origRun->mTextLength;

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

476
  void SetCurrentRun (uint32_t textPosition)
477 478 479 480 481 482
  {
    // 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.

483
    if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
484 485
      return;

E
Ebrahim Byagowi 已提交
486
    for (Run *run = &mRunHead; run; run = run->nextRun)
487 488
      if (run->ContainsTextPosition (textPosition))
      {
489 490
	mCurrentRun = run;
	return;
491
      }
492
    assert (0); // We should always be able to find the text position in one of our runs
493 494
  }

495
  void SplitCurrentRun (uint32_t splitPosition)
496
  {
497 498
    if (!mCurrentRun)
    {
499
      assert (0); // SplitCurrentRun called without current run
500 501 502 503
      // Shouldn't be calling this when no current run is set!
      return;
    }
    // Split the current run.
504 505
    if (splitPosition <= mCurrentRun->mTextStart)
    {
506 507 508 509
      // No need to split, already the start of a run
      // or before it. Usually the first.
      return;
    }
510
    Run *newRun = new Run;
511 512 513 514 515 516 517 518

    *newRun = *mCurrentRun;

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

    // Adjust runs' text positions and lengths.
519
    uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart;
520 521 522 523 524 525 526 527 528 529
    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)
530 531 532
  uint32_t mTextLength;
  const wchar_t* mText;
  const wchar_t* mLocaleName;
533 534 535 536 537 538 539 540 541
  DWRITE_READING_DIRECTION mReadingDirection;

  // Current processing state.
  Run *mCurrentRun;

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

542
static inline uint16_t hb_uint16_swap (const uint16_t v)
543
{ return (v >> 8) | (v << 8); }
544
static inline uint32_t hb_uint32_swap (const uint32_t v)
545
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
546 547 548 549 550

/*
 * shaper
 */

551
static hb_bool_t
552
_hb_directwrite_shape_full (hb_shape_plan_t    *shape_plan,
553 554 555 556 557
			    hb_font_t          *font,
			    hb_buffer_t        *buffer,
			    const hb_feature_t *features,
			    unsigned int        num_features,
			    float               lineWidth)
558 559
{
  hb_face_t *face = font->face;
560
  const hb_directwrite_face_data_t *face_data = face->data.directwrite;
561 562
  IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
  IDWriteFontFace *fontFace = face_data->fontFace;
563 564

  IDWriteTextAnalyzer* analyzer;
565
  dwriteFactory->CreateTextAnalyzer (&analyzer);
566 567 568 569 570

  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; \
B
Behdad Esfahbod 已提交
571
  do { \
572 573 574 575
    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
    assert (_consumed <= scratch_size); \
    scratch += _consumed; \
    scratch_size -= _consumed; \
B
Behdad Esfahbod 已提交
576
  } while (0)
577 578 579

#define utf16_index() var1.u32

580
  ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2);
581 582 583 584 585

  unsigned int chars_len = 0;
  for (unsigned int i = 0; i < buffer->len; i++)
  {
    hb_codepoint_t c = buffer->info[i].codepoint;
586 587
    buffer->info[i].utf16_index () = chars_len;
    if (likely (c <= 0xFFFFu))
E
Ebrahim Byagowi 已提交
588
      textString[chars_len++] = c;
589
    else if (unlikely (c > 0x10FFFFu))
E
Ebrahim Byagowi 已提交
590
      textString[chars_len++] = 0xFFFDu;
E
Ebrahim Byagowi 已提交
591 592
    else
    {
E
Ebrahim Byagowi 已提交
593
      textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
B
Behdad Esfahbod 已提交
594
      textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
595 596 597
    }
  }

598
  ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
E
Ebrahim Byagowi 已提交
599 600 601
  /* Need log_clusters to assign features. */
  chars_len = 0;
  for (unsigned int i = 0; i < buffer->len; i++)
602
  {
E
Ebrahim Byagowi 已提交
603 604 605 606 607
    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. */
608 609 610 611
  }

  // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES

612 613 614 615
  DWRITE_READING_DIRECTION readingDirection;
  readingDirection = buffer->props.direction ?
		     DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
		     DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
616

K
Khaled Hosny 已提交
617
  /*
618 619 620 621
  * 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.
  */
622
  uint32_t textLength = buffer->len;
623

624
  TextAnalysis analysis (textString, textLength, nullptr, readingDirection);
625
  TextAnalysis::Run *runHead;
626
  HRESULT hr;
627
  hr = analysis.GenerateResults (analyzer, &runHead);
628

629 630
#define FAIL(...) \
  HB_STMT_START { \
B
Behdad Esfahbod 已提交
631
    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
632 633 634
    return false; \
  } HB_STMT_END;

E
Ebrahim Byagowi 已提交
635
  if (FAILED (hr))
636
    FAIL ("Analyzer failed to generate results.");
637

638 639
  uint32_t maxGlyphCount = 3 * textLength / 2 + 16;
  uint32_t glyphCount;
E
Ebrahim Byagowi 已提交
640
  bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
641

E
Ebrahim Byagowi 已提交
642
  const wchar_t localeName[20] = {0};
B
Behdad Esfahbod 已提交
643
  if (buffer->props.language != nullptr)
E
Ebrahim Byagowi 已提交
644
    mbstowcs ((wchar_t*) localeName,
645
	      hb_language_to_string (buffer->props.language), 20);
646

E
Ebrahim Byagowi 已提交
647 648 649
  // TODO: it does work but doesn't care about ranges
  DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
  typographic_features.featureCount = num_features;
E
Ebrahim Byagowi 已提交
650
  if (num_features)
651
  {
E
Ebrahim Byagowi 已提交
652
    typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
653 654
    for (unsigned int i = 0; i < num_features; ++i)
    {
E
Ebrahim Byagowi 已提交
655
      typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
656
						 hb_uint32_swap (features[i].tag);
E
Ebrahim Byagowi 已提交
657
      typographic_features.features[i].parameter = features[i].value;
658
    }
E
Ebrahim Byagowi 已提交
659
  }
660 661
  const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
  dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
662
  const uint32_t featureRangeLengths[] = { textLength };
E
Ebrahim Byagowi 已提交
663
  //
E
Ebrahim Byagowi 已提交
664

665 666 667 668
  uint16_t* clusterMap;
  clusterMap = new uint16_t[textLength];
  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
  textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
E
Ebrahim Byagowi 已提交
669
retry_getglyphs:
670
  uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
671 672
  DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
  glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
E
Ebrahim Byagowi 已提交
673

B
Minor  
Behdad Esfahbod 已提交
674
  hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
675 676 677 678
			    isRightToLeft, &runHead->mScript, localeName,
			    nullptr, &dwFeatures, featureRangeLengths, 1,
			    maxGlyphCount, clusterMap, textProperties,
			    glyphIndices, glyphProperties, &glyphCount);
E
Ebrahim Byagowi 已提交
679 680 681

  if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
  {
682 683
    delete [] glyphIndices;
    delete [] glyphProperties;
684

E
Ebrahim Byagowi 已提交
685
    maxGlyphCount *= 2;
686 687

    goto retry_getglyphs;
688
  }
E
Ebrahim Byagowi 已提交
689
  if (FAILED (hr))
690
    FAIL ("Analyzer failed to get glyphs.");
691

692 693
  float* glyphAdvances = new float[maxGlyphCount];
  DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
694 695

  /* The -2 in the following is to compensate for possible
696 697
   * alignment needed after the WORD array.  sizeof (WORD) == 2. */
  unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
698
			     / (sizeof (WORD) +
699 700 701 702
				sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
				sizeof (int) +
				sizeof (DWRITE_GLYPH_OFFSET) +
				sizeof (uint32_t));
E
Ebrahim Byagowi 已提交
703
  ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
704 705 706

#undef ALLOCATE_ARRAY

707
  int fontEmSize = font->face->get_upem ();
708
  if (fontEmSize < 0) fontEmSize = -fontEmSize;
E
Ebrahim Byagowi 已提交
709

710
  if (fontEmSize < 0) fontEmSize = -fontEmSize;
E
Ebrahim Byagowi 已提交
711 712 713
  double x_mult = (double) font->x_scale / fontEmSize;
  double y_mult = (double) font->y_scale / fontEmSize;

714 715 716 717 718 719
  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 已提交
720 721

  if (FAILED (hr))
722
    FAIL ("Analyzer failed to get glyph placements.");
723

724 725
  IDWriteTextAnalyzer1* analyzer1;
  analyzer->QueryInterface (&analyzer1);
E
Ebrahim Byagowi 已提交
726

727
  if (analyzer1 && lineWidth)
E
Ebrahim Byagowi 已提交
728
  {
729
    DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
730
      new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
731 732 733 734
    hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
						   textLength, glyphCount, textString,
						   clusterMap, glyphProperties,
						   justificationOpportunities);
E
Ebrahim Byagowi 已提交
735

736 737
    if (FAILED (hr))
      FAIL ("Analyzer failed to get justification opportunities.");
E
Ebrahim Byagowi 已提交
738

739 740
    float* justifiedGlyphAdvances = new float[maxGlyphCount];
    DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
741
    hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
742 743
					  glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
					  justifiedGlyphOffsets);
E
Ebrahim Byagowi 已提交
744

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

747 748
    DWRITE_SCRIPT_PROPERTIES scriptProperties;
    hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
749
    if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
750 751 752 753 754
    uint32_t justificationCharacter = scriptProperties.justificationCharacter;

    // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
    if (justificationCharacter != 32)
    {
755
      uint16_t* modifiedClusterMap = new uint16_t[textLength];
756
    retry_getjustifiedglyphs:
757 758
      uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
      float* modifiedGlyphAdvances = new float[maxGlyphCount];
759
      DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
760 761
      uint32_t actualGlyphsCount;
      hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
762 763 764 765 766 767
					  textLength, glyphCount, maxGlyphCount,
					  clusterMap, glyphIndices, glyphAdvances,
					  justifiedGlyphAdvances, justifiedGlyphOffsets,
					  glyphProperties, &actualGlyphsCount,
					  modifiedClusterMap, modifiedGlyphIndices,
					  modifiedGlyphAdvances, modifiedGlyphOffsets);
768

769 770
      if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
      {
771 772 773 774
	maxGlyphCount = actualGlyphsCount;
	delete [] modifiedGlyphIndices;
	delete [] modifiedGlyphAdvances;
	delete [] modifiedGlyphOffsets;
775

776
	maxGlyphCount = actualGlyphsCount;
777

778
	goto retry_getjustifiedglyphs;
779 780
      }
      if (FAILED (hr))
781
	FAIL ("Analyzer failed to get justified glyphs.");
782

783 784 785 786
      delete [] clusterMap;
      delete [] glyphIndices;
      delete [] glyphAdvances;
      delete [] glyphOffsets;
E
Ebrahim Byagowi 已提交
787

788 789 790 791 792
      glyphCount = actualGlyphsCount;
      clusterMap = modifiedClusterMap;
      glyphIndices = modifiedGlyphIndices;
      glyphAdvances = modifiedGlyphAdvances;
      glyphOffsets = modifiedGlyphOffsets;
E
Ebrahim Byagowi 已提交
793

794 795
      delete [] justifiedGlyphAdvances;
      delete [] justifiedGlyphOffsets;
796 797 798
    }
    else
    {
799 800
      delete [] glyphAdvances;
      delete [] glyphOffsets;
801

802 803 804
      glyphAdvances = justifiedGlyphAdvances;
      glyphOffsets = justifiedGlyphOffsets;
    }
E
Ebrahim Byagowi 已提交
805

806
    delete [] justificationOpportunities;
807
  }
E
Ebrahim Byagowi 已提交
808

809 810 811 812
  /* 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 已提交
813
  for (unsigned int i = 0; i < glyphCount; i++)
B
Behdad Esfahbod 已提交
814
    vis_clusters[i] = (uint32_t) -1;
E
Ebrahim Byagowi 已提交
815 816 817
  for (unsigned int i = 0; i < buffer->len; i++)
  {
    uint32_t *p =
818
      &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
819
    *p = hb_min (*p, buffer->info[i].cluster);
820
  }
E
Ebrahim Byagowi 已提交
821
  for (unsigned int i = 1; i < glyphCount; i++)
B
Behdad Esfahbod 已提交
822
    if (vis_clusters[i] == (uint32_t) -1)
823 824 825 826
      vis_clusters[i] = vis_clusters[i - 1];

#undef utf16_index

E
Ebrahim Byagowi 已提交
827
  if (unlikely (!buffer->ensure (glyphCount)))
828
    FAIL ("Buffer in error");
829 830 831 832 833

#undef FAIL

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

E
Ebrahim Byagowi 已提交
838
    info->codepoint = glyphIndices[i];
839 840 841
    info->cluster = vis_clusters[i];

    /* The rest is crap.  Let's store position info there for now. */
E
Ebrahim Byagowi 已提交
842 843 844
    info->mask = glyphAdvances[i];
    info->var1.i32 = glyphOffsets[i].advanceOffset;
    info->var2.i32 = glyphOffsets[i].ascenderOffset;
845 846 847 848
  }

  /* Set glyph positions */
  buffer->clear_positions ();
E
Ebrahim Byagowi 已提交
849
  for (unsigned int i = 0; i < glyphCount; i++)
850 851 852 853 854
  {
    hb_glyph_info_t *info = &buffer->info[i];
    hb_glyph_position_t *pos = &buffer->pos[i];

    /* TODO vertical */
E
Ebrahim Byagowi 已提交
855
    pos->x_advance = x_mult * (int32_t) info->mask;
856
    pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
E
Ebrahim Byagowi 已提交
857
    pos->y_offset = y_mult * info->var2.i32;
858 859
  }

860
  if (isRightToLeft) hb_buffer_reverse (buffer);
861

862 863 864 865 866 867
  delete [] clusterMap;
  delete [] glyphIndices;
  delete [] textProperties;
  delete [] glyphProperties;
  delete [] glyphAdvances;
  delete [] glyphOffsets;
E
Ebrahim Byagowi 已提交
868 869

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

872 873
  /* Wow, done! */
  return true;
K
Khaled Hosny 已提交
874
}
875 876

hb_bool_t
877
_hb_directwrite_shape (hb_shape_plan_t    *shape_plan,
878 879 880 881
		       hb_font_t          *font,
		       hb_buffer_t        *buffer,
		       const hb_feature_t *features,
		       unsigned int        num_features)
882
{
883
  return _hb_directwrite_shape_full (shape_plan, font, buffer,
884
				     features, num_features, 0);
885 886
}

887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
/**
 * 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
 **/
904
hb_bool_t
905
hb_directwrite_shape_experimental_width (hb_font_t          *font,
906 907 908 909
					 hb_buffer_t        *buffer,
					 const hb_feature_t *features,
					 unsigned int        num_features,
					 float               width)
910
{
911
  static const char *shapers = "directwrite";
912 913 914
  hb_shape_plan_t *shape_plan;
  shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
					    features, num_features, &shapers);
915
  hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
916
					      features, num_features, width);
917

918
  buffer->unsafe_to_break_all ();
919 920 921

  return res;
}
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970

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:
971 972 973
 * @font_face: a DirectWrite IDWriteFontFace object.
 *
 * Return value: #hb_face_t object corresponding to the given input
974
 *
975
 * Since: 2.4.0
976 977 978 979 980 981 982 983 984
 **/
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);
}
985 986 987

/**
* hb_directwrite_face_get_font_face:
988 989 990
* @face: a #hb_face_t object
*
* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
991
*
992
* Since: 2.5.0
993 994 995 996 997 998
**/
IDWriteFontFace *
hb_directwrite_face_get_font_face (hb_face_t *face)
{
  return face->data.directwrite->fontFace;
}
999 1000 1001


#endif