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