474 lines
12 KiB
C++
474 lines
12 KiB
C++
/*
|
|
* 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
|