mirror of
https://github.com/harfbuzz/harfbuzz.git
synced 2025-04-10 15:23:35 +00:00
Merge pull request #5131 from harfbuzz/using2
Add hb_ft_face_create_from_blob_or_fail() et al
This commit is contained in:
commit
d014efd03d
11 changed files with 411 additions and 73 deletions
|
@ -347,6 +347,7 @@ HB_CORETEXT_TAG_MORT
|
|||
HB_CORETEXT_TAG_MORX
|
||||
hb_coretext_face_create
|
||||
hb_coretext_face_create_from_file_or_fail
|
||||
hb_coretext_face_create_from_blob_or_fail
|
||||
hb_coretext_font_create
|
||||
hb_coretext_face_get_cg_font
|
||||
hb_coretext_font_get_ct_font
|
||||
|
@ -367,6 +368,7 @@ hb_face_count
|
|||
hb_face_t
|
||||
hb_face_create
|
||||
hb_face_create_or_fail
|
||||
hb_face_create_or_fail_using
|
||||
hb_face_create_from_file_or_fail
|
||||
hb_face_create_from_file_or_fail_using
|
||||
hb_face_list_loaders
|
||||
|
@ -536,6 +538,7 @@ hb_ft_face_create
|
|||
hb_ft_face_create_cached
|
||||
hb_ft_face_create_referenced
|
||||
hb_ft_face_create_from_file_or_fail
|
||||
hb_ft_face_create_from_blob_or_fail
|
||||
hb_ft_font_create
|
||||
hb_ft_font_create_referenced
|
||||
hb_ft_font_changed
|
||||
|
|
|
@ -160,30 +160,68 @@ release_data (void *info, const void *data, size_t size)
|
|||
hb_blob_destroy ((hb_blob_t *) info);
|
||||
}
|
||||
|
||||
static CGFontRef
|
||||
create_cg_font (CFArrayRef ct_font_desc_array, unsigned int index)
|
||||
{
|
||||
auto ct_font_desc = (CFArrayGetCount (ct_font_desc_array) > index) ?
|
||||
(CTFontDescriptorRef) CFArrayGetValueAtIndex (ct_font_desc_array, index) : nullptr;
|
||||
if (unlikely (!ct_font_desc))
|
||||
{
|
||||
CFRelease (ct_font_desc_array);
|
||||
return nullptr;
|
||||
}
|
||||
auto ct_font = ct_font_desc ? CTFontCreateWithFontDescriptor (ct_font_desc, 0, nullptr) : nullptr;
|
||||
CFRelease (ct_font_desc_array);
|
||||
if (unlikely (!ct_font))
|
||||
return nullptr;
|
||||
|
||||
auto cg_font = ct_font ? CTFontCopyGraphicsFont (ct_font, nullptr) : nullptr;
|
||||
CFRelease (ct_font);
|
||||
|
||||
return cg_font;
|
||||
}
|
||||
|
||||
static CGFontRef
|
||||
create_cg_font (hb_blob_t *blob, unsigned int index)
|
||||
{
|
||||
hb_blob_make_immutable (blob);
|
||||
unsigned int blob_length;
|
||||
const char *blob_data = hb_blob_get_data (blob, &blob_length);
|
||||
if (unlikely (!blob_length))
|
||||
DEBUG_MSG (CORETEXT, blob, "Empty blob");
|
||||
|
||||
if (unlikely (index != 0))
|
||||
{
|
||||
auto ct_font_desc_array = CTFontManagerCreateFontDescriptorsFromData (CFDataCreate (kCFAllocatorDefault, (const UInt8 *) blob_data, blob_length));
|
||||
if (unlikely (!ct_font_desc_array))
|
||||
return nullptr;
|
||||
return create_cg_font (ct_font_desc_array, index);
|
||||
}
|
||||
|
||||
hb_blob_reference (blob);
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
|
||||
CGFontRef cg_font = nullptr;
|
||||
if (likely (provider))
|
||||
{
|
||||
cg_font = CGFontCreateWithDataProvider (provider);
|
||||
if (unlikely (!cg_font))
|
||||
DEBUG_MSG (CORETEXT, blob, "CGFontCreateWithDataProvider() failed");
|
||||
CGDataProviderRelease (provider);
|
||||
}
|
||||
return cg_font;
|
||||
}
|
||||
|
||||
static CGFontRef
|
||||
create_cg_font (hb_face_t *face)
|
||||
{
|
||||
CGFontRef cg_font = nullptr;
|
||||
if (face->destroy == _hb_cg_font_release)
|
||||
{
|
||||
cg_font = CGFontRetain ((CGFontRef) face->user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
hb_blob_t *blob = hb_face_reference_blob (face);
|
||||
unsigned int blob_length;
|
||||
const char *blob_data = hb_blob_get_data (blob, &blob_length);
|
||||
if (unlikely (!blob_length))
|
||||
DEBUG_MSG (CORETEXT, face, "Face has empty blob");
|
||||
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
|
||||
if (likely (provider))
|
||||
{
|
||||
cg_font = CGFontCreateWithDataProvider (provider);
|
||||
if (unlikely (!cg_font))
|
||||
DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
|
||||
CGDataProviderRelease (provider);
|
||||
}
|
||||
cg_font = create_cg_font (blob, face->index);
|
||||
hb_blob_destroy (blob);
|
||||
}
|
||||
return cg_font;
|
||||
}
|
||||
|
@ -377,22 +415,39 @@ hb_coretext_face_create_from_file_or_fail (const char *file_name,
|
|||
CFRelease (url);
|
||||
return nullptr;
|
||||
}
|
||||
auto ct_font_desc = (CFArrayGetCount (ct_font_desc_array) > index) ?
|
||||
(CTFontDescriptorRef) CFArrayGetValueAtIndex (ct_font_desc_array, index) : nullptr;
|
||||
if (unlikely (!ct_font_desc))
|
||||
{
|
||||
CFRelease (ct_font_desc_array);
|
||||
CFRelease (url);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto cg_font = create_cg_font (ct_font_desc_array, index);
|
||||
CFRelease (url);
|
||||
auto ct_font = ct_font_desc ? CTFontCreateWithFontDescriptor (ct_font_desc, 0, nullptr) : nullptr;
|
||||
CFRelease (ct_font_desc_array);
|
||||
if (unlikely (!ct_font))
|
||||
|
||||
hb_face_t *face = hb_coretext_face_create (cg_font);
|
||||
CFRelease (cg_font);
|
||||
if (unlikely (hb_face_is_immutable (face)))
|
||||
return nullptr;
|
||||
|
||||
auto cg_font = ct_font ? CTFontCopyGraphicsFont (ct_font, nullptr) : nullptr;
|
||||
CFRelease (ct_font);
|
||||
return face;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_coretext_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 CoreText 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.
|
||||
*
|
||||
* XSince: REPLACEME
|
||||
*/
|
||||
hb_face_t *
|
||||
hb_coretext_face_create_from_blob_or_fail (hb_blob_t *blob,
|
||||
unsigned int index)
|
||||
{
|
||||
auto cg_font = create_cg_font (blob, index);
|
||||
if (unlikely (!cg_font))
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -84,6 +84,10 @@ HB_EXTERN hb_face_t *
|
|||
hb_coretext_face_create_from_file_or_fail (const char *file_name,
|
||||
unsigned int index);
|
||||
|
||||
HB_EXTERN hb_face_t *
|
||||
hb_coretext_face_create_from_blob_or_fail (hb_blob_t *blob,
|
||||
unsigned int index);
|
||||
|
||||
HB_EXTERN hb_font_t *
|
||||
hb_coretext_font_create (CTFontRef ct_font);
|
||||
|
||||
|
|
118
src/hb-face.cc
118
src/hb-face.cc
|
@ -79,14 +79,14 @@ hb_face_count (hb_blob_t *blob)
|
|||
if (unlikely (!blob))
|
||||
return 0;
|
||||
|
||||
/* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */
|
||||
/* Make API signature const after. */
|
||||
hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob));
|
||||
const OT::OpenTypeFontFile& ot = *sanitized->as<OT::OpenTypeFontFile> ();
|
||||
unsigned int ret = ot.get_face_count ();
|
||||
hb_blob_destroy (sanitized);
|
||||
hb_sanitize_context_t c (blob);
|
||||
|
||||
return ret;
|
||||
const char *start = hb_blob_get_data (blob, nullptr);
|
||||
auto *ot = reinterpret_cast<OT::OpenTypeFontFile *> (const_cast<char *> (start));
|
||||
if (unlikely (!ot->sanitize (&c)))
|
||||
return 0;
|
||||
|
||||
return ot->get_face_count ();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -328,20 +328,47 @@ hb_face_create_from_file_or_fail (const char *file_name,
|
|||
|
||||
static struct supported_face_loaders_t {
|
||||
char name[9];
|
||||
hb_face_t * (*func) (const char *font_file, unsigned face_index);
|
||||
hb_face_t * (*from_file) (const char *font_file, unsigned face_index);
|
||||
hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index);
|
||||
} supported_face_loaders[] =
|
||||
{
|
||||
{"ot",
|
||||
#ifndef HB_NO_OPEN
|
||||
{"ot", hb_face_create_from_file_or_fail},
|
||||
hb_face_create_from_file_or_fail,
|
||||
#else
|
||||
nullptr,
|
||||
#endif
|
||||
hb_face_create_or_fail
|
||||
},
|
||||
#ifdef HAVE_FREETYPE
|
||||
{"ft", hb_ft_face_create_from_file_or_fail},
|
||||
{"ft",
|
||||
hb_ft_face_create_from_file_or_fail,
|
||||
hb_ft_face_create_from_blob_or_fail
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_CORETEXT
|
||||
{"coretext", hb_coretext_face_create_from_file_or_fail},
|
||||
{"coretext",
|
||||
hb_coretext_face_create_from_file_or_fail,
|
||||
hb_coretext_face_create_from_blob_or_fail
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char *get_default_loader_name ()
|
||||
{
|
||||
static hb_atomic_ptr_t<const char> static_loader_name;
|
||||
const char *loader_name = static_loader_name.get_acquire ();
|
||||
if (!loader_name)
|
||||
{
|
||||
loader_name = getenv ("HB_FACE_LOADER");
|
||||
if (!loader_name)
|
||||
loader_name = "";
|
||||
if (!static_loader_name.cmpexch (nullptr, loader_name))
|
||||
loader_name = static_loader_name.get_acquire ();
|
||||
}
|
||||
return loader_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_face_create_from_file_or_fail_using:
|
||||
* @file_name: A font filename
|
||||
|
@ -353,7 +380,7 @@ static struct supported_face_loaders_t {
|
|||
* is used.
|
||||
*
|
||||
* For example, the FreeType ("ft") loader might be able to load
|
||||
* .woff and .woff2 files if FreeType is built with those features,
|
||||
* WOFF and WOFF2 files if FreeType is built with those features,
|
||||
* whereas the OpenType ("ot") loader will not.
|
||||
*
|
||||
* Return value: (transfer full): The new face object, or `NULL` if
|
||||
|
@ -366,20 +393,11 @@ hb_face_create_from_file_or_fail_using (const char *file_name,
|
|||
unsigned int index,
|
||||
const char *loader_name)
|
||||
{
|
||||
// Duplicated in hb_face_create_or_fail_using
|
||||
bool retry = false;
|
||||
|
||||
if (!loader_name || !*loader_name)
|
||||
{
|
||||
static hb_atomic_ptr_t<const char> static_funcs_name;
|
||||
loader_name = static_funcs_name.get_acquire ();
|
||||
if (!loader_name)
|
||||
{
|
||||
loader_name = getenv ("HB_FACE_LOADER");
|
||||
if (!loader_name)
|
||||
loader_name = "";
|
||||
if (!static_funcs_name.cmpexch (nullptr, loader_name))
|
||||
loader_name = static_funcs_name.get_acquire ();
|
||||
}
|
||||
loader_name = get_default_loader_name ();
|
||||
retry = true;
|
||||
}
|
||||
if (loader_name && !*loader_name) loader_name = nullptr;
|
||||
|
@ -387,8 +405,58 @@ hb_face_create_from_file_or_fail_using (const char *file_name,
|
|||
retry:
|
||||
for (unsigned i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++)
|
||||
{
|
||||
if (!loader_name || !strcmp (supported_face_loaders[i].name, loader_name))
|
||||
return supported_face_loaders[i].func (file_name, index);
|
||||
if (!loader_name || (supported_face_loaders[i].from_file && !strcmp (supported_face_loaders[i].name, loader_name)))
|
||||
return supported_face_loaders[i].from_file (file_name, index);
|
||||
}
|
||||
|
||||
if (retry)
|
||||
{
|
||||
retry = false;
|
||||
loader_name = nullptr;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_face_create_or_fail_using:
|
||||
* @blob: #hb_blob_t to work upon
|
||||
* @index: The index of the face within @blob
|
||||
* @loader_name: (nullable): The name of the loader to use, or `NULL`
|
||||
*
|
||||
* A thin wrapper around the face loader functions registered with HarfBuzz.
|
||||
* If @loader_name is `NULL` or the empty string, the first available loader
|
||||
* is used.
|
||||
*
|
||||
* For example, the FreeType ("ft") loader might be able to load
|
||||
* WOFF and WOFF2 files if FreeType is built with those features,
|
||||
* whereas the OpenType ("ot") loader will not.
|
||||
*
|
||||
* Return value: (transfer full): The new face object, or `NULL` if
|
||||
* the loader fails to load the face.
|
||||
*
|
||||
* XSince: REPLACEME
|
||||
**/
|
||||
hb_face_t *
|
||||
hb_face_create_or_fail_using (hb_blob_t *blob,
|
||||
unsigned int index,
|
||||
const char *loader_name)
|
||||
{
|
||||
// Duplicated in hb_face_create_from_file_or_fail_using
|
||||
bool retry = false;
|
||||
if (!loader_name || !*loader_name)
|
||||
{
|
||||
loader_name = get_default_loader_name ();
|
||||
retry = true;
|
||||
}
|
||||
if (loader_name && !*loader_name) loader_name = nullptr;
|
||||
|
||||
retry:
|
||||
for (unsigned i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++)
|
||||
{
|
||||
if (!loader_name || (supported_face_loaders[i].from_blob && !strcmp (supported_face_loaders[i].name, loader_name)))
|
||||
return supported_face_loaders[i].from_blob (blob, index);
|
||||
}
|
||||
|
||||
if (retry)
|
||||
|
|
|
@ -63,6 +63,11 @@ HB_EXTERN hb_face_t *
|
|||
hb_face_create_or_fail (hb_blob_t *blob,
|
||||
unsigned int index);
|
||||
|
||||
HB_EXTERN hb_face_t *
|
||||
hb_face_create_or_fail_using (hb_blob_t *blob,
|
||||
unsigned int index,
|
||||
const char *loader_name);
|
||||
|
||||
HB_EXTERN hb_face_t *
|
||||
hb_face_create_from_file_or_fail (const char *file_name,
|
||||
unsigned int index);
|
||||
|
|
|
@ -2321,6 +2321,21 @@ static struct supported_font_funcs_t {
|
|||
#endif
|
||||
};
|
||||
|
||||
static const char *get_default_funcs_name ()
|
||||
{
|
||||
static hb_atomic_ptr_t<const char> static_funcs_name;
|
||||
const char *name = static_funcs_name.get_acquire ();
|
||||
if (!name)
|
||||
{
|
||||
name = getenv ("HB_FONT_FUNCS");
|
||||
if (!name)
|
||||
name = "";
|
||||
if (!static_funcs_name.cmpexch (nullptr, name))
|
||||
name = static_funcs_name.get_acquire ();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_font_set_funcs_using:
|
||||
* @font: #hb_font_t to work upon
|
||||
|
@ -2345,16 +2360,7 @@ hb_font_set_funcs_using (hb_font_t *font,
|
|||
|
||||
if (!name || !*name)
|
||||
{
|
||||
static hb_atomic_ptr_t<const char> static_funcs_name;
|
||||
name = static_funcs_name.get_acquire ();
|
||||
if (!name)
|
||||
{
|
||||
name = getenv ("HB_FONT_FUNCS");
|
||||
if (!name)
|
||||
name = "";
|
||||
if (!static_funcs_name.cmpexch (nullptr, name))
|
||||
name = static_funcs_name.get_acquire ();
|
||||
}
|
||||
name = get_default_funcs_name ();
|
||||
retry = true;
|
||||
}
|
||||
if (name && !*name) name = nullptr;
|
||||
|
|
72
src/hb-ft.cc
72
src/hb-ft.cc
|
@ -1610,7 +1610,8 @@ destroy_ft_library (void *arg)
|
|||
* font file and face index.
|
||||
*
|
||||
* This is similar in functionality to hb_face_create_from_file_or_fail(),
|
||||
* but uses the FreeType library for loading the font file.
|
||||
* but uses the FreeType library for loading the font file. This can
|
||||
* be useful, for example, to load WOFF and WOFF2 font data.
|
||||
*
|
||||
* 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.
|
||||
|
@ -1647,6 +1648,75 @@ hb_ft_face_create_from_file_or_fail (const char *file_name,
|
|||
return face;
|
||||
}
|
||||
|
||||
static hb_user_data_key_t ft_blob_key = {0};
|
||||
|
||||
static void
|
||||
_destroy_blob (void *p)
|
||||
{
|
||||
hb_blob_destroy ((hb_blob_t *) p);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_ft_face_create_from_blob_or_fail:
|
||||
* @blob: A blob
|
||||
* @index: The index of the face within the blob
|
||||
*
|
||||
* Creates an #hb_face_t face object from the specified
|
||||
* font blob and face index.
|
||||
*
|
||||
* This is similar in functionality to hb_face_create_from_blob_or_fail(),
|
||||
* but uses the FreeType library for loading the font blob. This can
|
||||
* be useful, for example, to load WOFF and WOFF2 font data.
|
||||
*
|
||||
* Return value: (transfer full): The new face object, or `NULL` if
|
||||
* loading fails (eg. blob does not contain valid font data).
|
||||
*
|
||||
* XSince: REPLACEME
|
||||
*/
|
||||
hb_face_t *
|
||||
hb_ft_face_create_from_blob_or_fail (hb_blob_t *blob,
|
||||
unsigned int index)
|
||||
{
|
||||
FT_Library ft_library = reference_ft_library ();
|
||||
if (unlikely (!ft_library))
|
||||
{
|
||||
DEBUG_MSG (FT, ft_library, "reference_ft_library failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hb_blob_make_immutable (blob);
|
||||
unsigned blob_size;
|
||||
const char *blob_data = hb_blob_get_data (blob, &blob_size);
|
||||
|
||||
FT_Face ft_face;
|
||||
if (unlikely (FT_New_Memory_Face (ft_library,
|
||||
(const FT_Byte *) blob_data,
|
||||
blob_size,
|
||||
index,
|
||||
&ft_face)))
|
||||
return nullptr;
|
||||
|
||||
hb_face_t *face = hb_ft_face_create_referenced (ft_face);
|
||||
FT_Done_Face (ft_face);
|
||||
|
||||
ft_face->generic.data = ft_library;
|
||||
ft_face->generic.finalizer = finalize_ft_library;
|
||||
|
||||
if (hb_face_is_immutable (face))
|
||||
return nullptr;
|
||||
|
||||
// Hook the blob to the hb_face_t, since FT_Face still needs it.
|
||||
hb_blob_reference (blob);
|
||||
if (!hb_face_set_user_data (face, &ft_blob_key, blob, _destroy_blob, true))
|
||||
{
|
||||
hb_blob_destroy (blob);
|
||||
hb_face_destroy (face);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
static void
|
||||
_release_blob (void *arg)
|
||||
{
|
||||
|
|
|
@ -88,6 +88,10 @@ HB_EXTERN hb_face_t *
|
|||
hb_ft_face_create_from_file_or_fail (const char *file_name,
|
||||
unsigned int index);
|
||||
|
||||
HB_EXTERN hb_face_t *
|
||||
hb_ft_face_create_from_blob_or_fail (hb_blob_t *blob,
|
||||
unsigned int index);
|
||||
|
||||
/*
|
||||
* hb-font from ft-face.
|
||||
*/
|
||||
|
|
|
@ -241,9 +241,14 @@ hb_test_add_data_func_flavor (const char *test_path,
|
|||
gconstpointer test_data,
|
||||
hb_test_data_func_t test_func)
|
||||
{
|
||||
char *path = g_strdup_printf ("%s/%s", test_path, flavor);
|
||||
hb_test_add_data_func (path, test_data, test_func);
|
||||
g_free (path);
|
||||
if (flavor && *flavor)
|
||||
{
|
||||
char *path = g_strdup_printf ("%s/%s", test_path, flavor);
|
||||
hb_test_add_data_func (path, test_data, test_func);
|
||||
g_free (path);
|
||||
}
|
||||
else
|
||||
hb_test_add_data_func (test_path, test_data, test_func);
|
||||
}
|
||||
#define hb_test_add_data_flavor(UserData, Flavor, Func) hb_test_add_data_func_flavor (#Func, Flavor, UserData, Func)
|
||||
|
||||
|
@ -305,27 +310,38 @@ G_STMT_START { \
|
|||
} G_STMT_END
|
||||
|
||||
|
||||
static inline hb_face_t *
|
||||
hb_test_open_font_file (const char *font_path)
|
||||
static inline char *
|
||||
hb_test_resolve_path (const char *path)
|
||||
{
|
||||
#if GLIB_CHECK_VERSION(2,37,2)
|
||||
char *path = g_test_build_filename (G_TEST_DIST, font_path, NULL);
|
||||
#else
|
||||
char *path = g_strdup (font_path);
|
||||
if (path[0] != '/')
|
||||
return g_test_build_filename (G_TEST_DIST, path, NULL);
|
||||
#endif
|
||||
return g_strdup (path);
|
||||
}
|
||||
|
||||
static inline hb_face_t *
|
||||
hb_test_open_font_file_with_index (const char *font_path, unsigned face_index)
|
||||
{
|
||||
char *path = hb_test_resolve_path (font_path);
|
||||
|
||||
hb_blob_t *blob = hb_blob_create_from_file_or_fail (path);
|
||||
hb_face_t *face;
|
||||
if (!blob)
|
||||
g_error ("Font %s not found.", path);
|
||||
|
||||
face = hb_face_create (blob, 0);
|
||||
face = hb_face_create (blob, face_index);
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
g_free (path);
|
||||
|
||||
return face;
|
||||
}
|
||||
static inline hb_face_t *
|
||||
hb_test_open_font_file (const char *font_path)
|
||||
{
|
||||
return hb_test_open_font_file_with_index (font_path, 0);
|
||||
}
|
||||
|
||||
HB_END_DECLS
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ tests = [
|
|||
'test-draw.c',
|
||||
'test-draw-varc.c',
|
||||
'test-extents.c',
|
||||
'test-face.c',
|
||||
'test-font.c',
|
||||
'test-font-scale.c',
|
||||
'test-get-table-tags.c',
|
||||
|
|
106
test/api/test-face.c
Normal file
106
test/api/test-face.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "hb-test.h"
|
||||
|
||||
/* Unit tests for hb-face.h */
|
||||
|
||||
#define FONT_FILE "fonts/Roboto-Regular.ac.ttf"
|
||||
static const char *font_file = NULL;
|
||||
static unsigned int face_index = 0;
|
||||
|
||||
hb_face_t *master_face = NULL;
|
||||
|
||||
#define HEAD_TAG HB_TAG ('h', 'e', 'a', 'd')
|
||||
hb_blob_t *master_head = NULL;
|
||||
|
||||
static void
|
||||
test_face (hb_face_t *face)
|
||||
{
|
||||
g_assert_nonnull (face);
|
||||
|
||||
hb_blob_t *head = hb_face_reference_table (face, HEAD_TAG);
|
||||
|
||||
unsigned int length;
|
||||
unsigned int master_length;
|
||||
const char *data = hb_blob_get_data (head, &length);
|
||||
const char *master_data = hb_blob_get_data (master_head, &master_length);
|
||||
g_assert_cmpmem (data, length, master_data, master_length);
|
||||
|
||||
hb_blob_destroy (head);
|
||||
}
|
||||
|
||||
static void
|
||||
test_create_from_file_using (const void *user_data)
|
||||
{
|
||||
const char *loader = user_data;
|
||||
hb_face_t *face = hb_face_create_from_file_or_fail_using (font_file, face_index, loader);
|
||||
test_face (face);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_create_from_blob_using (const void *user_data)
|
||||
{
|
||||
const char *loader = user_data;
|
||||
hb_blob_t *blob = hb_blob_create_from_file_or_fail (font_file);
|
||||
hb_face_t *face = hb_face_create_or_fail_using (blob, face_index, loader);
|
||||
hb_blob_destroy (blob);
|
||||
test_face (face);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
hb_test_init (&argc, &argv);
|
||||
|
||||
font_file = FONT_FILE;
|
||||
if (argc > 1)
|
||||
font_file = argv[1];
|
||||
if (argc > 2)
|
||||
face_index = atoi (argv[2]);
|
||||
|
||||
master_face = hb_test_open_font_file_with_index (font_file, face_index);
|
||||
master_head = hb_face_reference_table (master_face, HEAD_TAG);
|
||||
g_assert (hb_blob_get_length (master_head) > 0);
|
||||
|
||||
font_file = hb_test_resolve_path (font_file);
|
||||
|
||||
hb_test_add_flavor ("", test_create_from_file_using);
|
||||
hb_test_add_flavor ("", test_create_from_blob_using);
|
||||
for (const char **loaders = hb_face_list_loaders (); *loaders; loaders++)
|
||||
{
|
||||
hb_test_add_flavor (*loaders, test_create_from_file_using);
|
||||
hb_test_add_flavor (*loaders, test_create_from_blob_using);
|
||||
}
|
||||
|
||||
int ret = hb_test_run();
|
||||
|
||||
hb_blob_destroy (master_head);
|
||||
hb_face_destroy (master_face);
|
||||
g_free ((char *) font_file);
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Add table
Reference in a new issue