/* * 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 { 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 values; uint32_t count = dw_face5->GetFontAxisValueCount (); if (likely (values.resize_exact (count)) && SUCCEEDED (dw_face5->GetFontAxisValues (values.arrayZ, count))) { hb_vector_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; } #ifndef HB_DISABLE_DEPRECATED /** * 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 #endif