| /* |
| * Copyright © 2015-2019 Ebrahim Byagowi |
| * |
| * 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. |
| */ |
| |
| #include "hb.hh" |
| |
| #ifdef HAVE_DIRECTWRITE |
| |
| #include "hb-directwrite.hh" |
| |
| #include "hb-font.hh" |
| |
| |
| /** |
| * SECTION:hb-directwrite |
| * @title: hb-directwrite |
| * @short_description: DirectWrite integration |
| * @include: hb-directwrite.h |
| * |
| * Functions for using HarfBuzz with DirectWrite fonts. |
| **/ |
| |
| static inline void free_static_directwrite_global (); |
| |
| static struct hb_directwrite_global_lazy_loader_t : hb_lazy_loader_t<hb_directwrite_global_t, |
| hb_directwrite_global_lazy_loader_t> |
| { |
| static hb_directwrite_global_t * create () |
| { |
| hb_directwrite_global_t *global = new hb_directwrite_global_t; |
| |
| if (unlikely (!global)) |
| return nullptr; |
| if (unlikely (!global->success)) |
| { |
| delete global; |
| return nullptr; |
| } |
| |
| hb_atexit (free_static_directwrite_global); |
| |
| return global; |
| } |
| static void destroy (hb_directwrite_global_t *l) |
| { |
| delete l; |
| } |
| static hb_directwrite_global_t * get_null () |
| { |
| return nullptr; |
| } |
| } static_directwrite_global; |
| |
| |
| static inline |
| void free_static_directwrite_global () |
| { |
| static_directwrite_global.free_instance (); |
| } |
| |
| hb_directwrite_global_t * |
| get_directwrite_global () |
| { |
| return static_directwrite_global.get_unconst (); |
| } |
| |
| DWriteFontFileStream::DWriteFontFileStream (hb_blob_t *blob) |
| { |
| auto *global = get_directwrite_global (); |
| mLoader = global->fontFileLoader; |
| mRefCount.init (); |
| mLoader->AddRef (); |
| hb_blob_make_immutable (blob); |
| mBlob = hb_blob_reference (blob); |
| mData = (uint8_t *) hb_blob_get_data (blob, &mSize); |
| fontFileKey = mLoader->RegisterFontFileStream (this); |
| } |
| |
| DWriteFontFileStream::~DWriteFontFileStream() |
| { |
| mLoader->UnregisterFontFileStream (fontFileKey); |
| mLoader->Release (); |
| hb_blob_destroy (mBlob); |
| } |
| |
| IDWriteFontFace * |
| dw_face_create (hb_blob_t *blob, unsigned index) |
| { |
| #define FAIL(...) \ |
| HB_STMT_START { \ |
| DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ |
| return nullptr; \ |
| } HB_STMT_END |
| |
| auto *global = get_directwrite_global (); |
| if (unlikely (!global)) |
| FAIL ("Couldn't load DirectWrite!"); |
| |
| DWriteFontFileStream *fontFileStream = new DWriteFontFileStream (blob); |
| |
| IDWriteFontFile *fontFile; |
| auto hr = global->dwriteFactory->CreateCustomFontFileReference (&fontFileStream->fontFileKey, sizeof (fontFileStream->fontFileKey), |
| global->fontFileLoader, &fontFile); |
| |
| fontFileStream->Release (); |
| |
| if (FAILED (hr)) |
| FAIL ("Failed to load font file from data!"); |
| |
| BOOL isSupported; |
| DWRITE_FONT_FILE_TYPE fileType; |
| DWRITE_FONT_FACE_TYPE faceType; |
| uint32_t numberOfFaces; |
| hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); |
| if (FAILED (hr) || !isSupported) |
| { |
| fontFile->Release (); |
| FAIL ("Font file is not supported."); |
| } |
| |
| #undef FAIL |
| |
| IDWriteFontFace *fontFace = nullptr; |
| global->dwriteFactory->CreateFontFace (faceType, 1, &fontFile, index, |
| DWRITE_FONT_SIMULATIONS_NONE, &fontFace); |
| fontFile->Release (); |
| |
| return fontFace; |
| } |
| |
| 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); |
| hb_free (context); |
| } |
| |
| static hb_blob_t * |
| _hb_directwrite_get_file_blob (IDWriteFontFace *dw_face) |
| { |
| UINT32 file_count; |
| if (FAILED (dw_face->GetFiles(&file_count, NULL))) |
| return nullptr; |
| |
| IDWriteFontFile **files = new IDWriteFontFile*[file_count]; |
| if (FAILED (dw_face->GetFiles(&file_count, files))) |
| { |
| delete [] files; |
| return nullptr; |
| } |
| |
| hb_blob_t *blob = nullptr; |
| for (UINT32 i = 0; i < file_count; i++) |
| { |
| LPCVOID reference_key; |
| UINT32 reference_key_size; |
| if (FAILED (files[i]->GetReferenceKey(&reference_key, &reference_key_size))) |
| continue; |
| |
| IDWriteFontFileLoader *loader; |
| if (FAILED (files[i]->GetLoader(&loader))) |
| continue; |
| |
| IDWriteFontFileStream *stream; |
| if (FAILED (loader->CreateStreamFromKey (reference_key, reference_key_size, &stream))) |
| { |
| loader->Release (); |
| continue; |
| } |
| |
| UINT64 file_size; |
| const void *fragment; |
| void *context; |
| if (FAILED (stream->GetFileSize(&file_size)) || |
| FAILED (stream->ReadFileFragment (&fragment, 0, file_size, &context))) |
| { |
| loader->Release (); |
| continue; |
| } |
| blob = hb_blob_create ((const char *) fragment, file_size, HB_MEMORY_MODE_DUPLICATE, NULL, NULL); |
| stream->ReleaseFileFragment (context); |
| loader->Release (); |
| break; |
| } |
| |
| delete [] files; |
| return blob; |
| } |
| |
| static hb_blob_t * |
| _hb_directwrite_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 (tag == HB_TAG_NONE) |
| return _hb_directwrite_get_file_blob (dw_face); |
| |
| if (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 = (_hb_directwrite_font_table_context *) hb_malloc (sizeof (_hb_directwrite_font_table_context)); |
| if (unlikely (!context)) |
| { |
| dw_face->ReleaseFontTable (table_context); |
| return nullptr; |
| } |
| 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_face_release (void *data) |
| { |
| ((IDWriteFontFace *) data)->Release (); |
| } |
| |
| /** |
| * hb_directwrite_face_create: |
| * @dw_face: a DirectWrite IDWriteFontFace object. |
| * |
| * Constructs a new face object from the specified DirectWrite IDWriteFontFace. |
| * |
| * Return value: #hb_face_t object corresponding to the given input |
| * |
| * Since: 2.4.0 |
| **/ |
| hb_face_t * |
| hb_directwrite_face_create (IDWriteFontFace *dw_face) |
| { |
| if (!dw_face) |
| return hb_face_get_empty (); |
| |
| dw_face->AddRef (); |
| hb_face_t *face = hb_face_create_for_tables (_hb_directwrite_reference_table, |
| dw_face, |
| _hb_directwrite_face_release); |
| |
| hb_face_set_index (face, dw_face->GetIndex ()); |
| hb_face_set_glyph_count (face, dw_face->GetGlyphCount ()); |
| |
| return face; |
| } |
| |
| /** |
| * hb_directwrite_face_create_from_file_or_fail: |
| * @file_name: A font filename |
| * @index: The index of the face within the file |
| * |
| * Creates an #hb_face_t face object from the specified |
| * font file and face index. |
| * |
| * This is similar in functionality to hb_face_create_from_file_or_fail(), |
| * but uses the DirectWrite library for loading the font file. |
| * |
| * Return value: (transfer full): The new face object, or `NULL` if |
| * no face is found at the specified index or the file cannot be read. |
| * |
| * Since: 11.0.0 |
| */ |
| hb_face_t * |
| hb_directwrite_face_create_from_file_or_fail (const char *file_name, |
| unsigned int index) |
| { |
| auto *blob = hb_blob_create_from_file_or_fail (file_name); |
| if (unlikely (!blob)) |
| return nullptr; |
| |
| return hb_directwrite_face_create_from_blob_or_fail (blob, index); |
| } |
| |
| /** |
| * hb_directwrite_face_create_from_blob_or_fail: |
| * @blob: A blob containing the font data |
| * @index: The index of the face within the blob |
| * |
| * Creates an #hb_face_t face object from the specified |
| * blob and face index. |
| * |
| * This is similar in functionality to hb_face_create_from_blob_or_fail(), |
| * but uses the DirectWrite library for loading the font data. |
| * |
| * Return value: (transfer full): The new face object, or `NULL` if |
| * no face is found at the specified index or the blob cannot be read. |
| * |
| * Since: 11.0.0 |
| */ |
| HB_EXTERN hb_face_t * |
| hb_directwrite_face_create_from_blob_or_fail (hb_blob_t *blob, |
| unsigned int index) |
| { |
| IDWriteFontFace *dw_face = dw_face_create (blob, index); |
| if (unlikely (!dw_face)) |
| return nullptr; |
| |
| hb_face_t *face = hb_directwrite_face_create (dw_face); |
| if (unlikely (hb_object_is_immutable (face))) |
| { |
| dw_face->Release (); |
| return face; |
| } |
| |
| /* Let there be dragons here... */ |
| face->data.directwrite.cmpexch (nullptr, (hb_directwrite_face_data_t *) dw_face); |
| |
| return face; |
| } |
| |
| /** |
| * hb_directwrite_face_get_dw_font_face: |
| * @face: a #hb_face_t object |
| * |
| * Gets the DirectWrite IDWriteFontFace associated with @face. |
| * |
| * Return value: DirectWrite IDWriteFontFace object corresponding to the given input |
| * |
| * Since: 10.4.0 |
| **/ |
| IDWriteFontFace * |
| hb_directwrite_face_get_dw_font_face (hb_face_t *face) |
| { |
| return (IDWriteFontFace *) (const void *) face->data.directwrite; |
| } |
| |
| #ifndef HB_DISABLE_DEPRECATED |
| |
| /** |
| * hb_directwrite_face_get_font_face: |
| * @face: a #hb_face_t object |
| * |
| * Gets the DirectWrite IDWriteFontFace associated with @face. |
| * |
| * Return value: DirectWrite IDWriteFontFace object corresponding to the given input |
| * |
| * Since: 2.5.0 |
| * Deprecated: 10.4.0: Use hb_directwrite_face_get_dw_font_face() instead |
| **/ |
| IDWriteFontFace * |
| hb_directwrite_face_get_font_face (hb_face_t *face) |
| { |
| return hb_directwrite_face_get_dw_font_face (face); |
| } |
| |
| #endif |
| |
| /** |
| * hb_directwrite_font_create: |
| * @dw_face: a DirectWrite IDWriteFontFace object. |
| * |
| * Constructs a new font object from the specified DirectWrite IDWriteFontFace. |
| * |
| * Return value: #hb_font_t object corresponding to the given input |
| * |
| * Since: 11.0.0 |
| **/ |
| hb_font_t * |
| hb_directwrite_font_create (IDWriteFontFace *dw_face) |
| { |
| IDWriteFontFace5 *dw_face5 = nullptr; |
| |
| hb_face_t *face = hb_directwrite_face_create (dw_face); |
| hb_font_t *font = hb_font_create (face); |
| hb_face_destroy (face); |
| |
| if (unlikely (hb_object_is_immutable (font))) |
| return font; |
| |
| /* Copy font variations */ |
| if (SUCCEEDED (dw_face->QueryInterface (__uuidof (IDWriteFontFace5), (void**) &dw_face5))) |
| { |
| if (dw_face5->HasVariations ()) |
| { |
| hb_vector_t<DWRITE_FONT_AXIS_VALUE> values; |
| uint32_t count = dw_face5->GetFontAxisValueCount (); |
| if (likely (values.resize_exact (count)) && |
| SUCCEEDED (dw_face5->GetFontAxisValues (values.arrayZ, count))) |
| { |
| hb_vector_t<hb_variation_t> vars; |
| if (likely (vars.resize_exact (count))) |
| { |
| for (uint32_t i = 0; i < count; ++i) |
| { |
| hb_tag_t tag = hb_uint32_swap (values[i].axisTag); |
| float value = values[i].value; |
| vars[i] = {tag, value}; |
| } |
| hb_font_set_variations (font, vars.arrayZ, vars.length); |
| } |
| } |
| } |
| dw_face5->Release (); |
| } |
| |
| /* Let there be dragons here... */ |
| dw_face->AddRef (); |
| font->data.directwrite.cmpexch (nullptr, (hb_directwrite_font_data_t *) dw_face); |
| |
| return font; |
| } |
| |
| /** |
| * hb_directwrite_font_get_dw_font_face: |
| * @font: a #hb_font_t object |
| * |
| * Gets the DirectWrite IDWriteFontFace associated with @font. |
| * |
| * Return value: DirectWrite IDWriteFontFace object corresponding to the given input |
| * |
| * Since: 11.0.0 |
| **/ |
| IDWriteFontFace * |
| hb_directwrite_font_get_dw_font_face (hb_font_t *font) |
| { |
| return (IDWriteFontFace *) (const void *) font->data.directwrite; |
| } |
| |
| |
| /** |
| * hb_directwrite_font_get_dw_font: |
| * @font: a #hb_font_t object |
| * |
| * Deprecated. |
| * |
| * Return value: Returns `NULL`. |
| * |
| * Since: 10.3.0 |
| * Deprecated: 11.0.0: |
| **/ |
| IDWriteFont * |
| hb_directwrite_font_get_dw_font (hb_font_t *font) |
| { |
| return nullptr; |
| } |
| |
| #endif |