Source code

Revision control

Copy as Markdown

Other Tools

/*
* 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