[face] Add fallback implementation to hb_face_reference_blob

If referencing the face blob is not possible (e.g. not implemented by
the font functions), use face builder to create a blob out of
individual table blobs.

Fixes https://github.com/harfbuzz/harfbuzz/issues/5036
This commit is contained in:
Khaled Hosny 2025-02-04 00:54:58 +02:00
parent 9cf2ea3e8f
commit 4825e5e2b4
2 changed files with 108 additions and 3 deletions

View file

@ -504,7 +504,37 @@ hb_face_reference_table (const hb_face_t *face,
hb_blob_t *
hb_face_reference_blob (hb_face_t *face)
{
return face->reference_table (HB_TAG_NONE);
hb_blob_t *blob = face->reference_table (HB_TAG_NONE);
if (blob == hb_blob_get_empty ())
{
// If referencing the face blob is not possible (e.g. not implemented by the
// font functions), use face builder to create a blob out of individual
// table blobs.
unsigned total_count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
if (total_count)
{
hb_tag_t tags[10];
unsigned count = sizeof (tags) / sizeof (tags[0]);
hb_face_t* builder = hb_face_builder_create ();
for (unsigned offset = 0; offset < total_count; offset += count)
{
hb_face_get_table_tags (face, offset, &count, tags);
for (unsigned i = 0; i < count; i++)
{
hb_blob_t *table = hb_face_reference_table (face, tags[i]);
hb_face_builder_add_table (builder, tags[i], table);
hb_blob_destroy (table);
}
}
blob = hb_face_reference_blob (builder);
hb_face_destroy (builder);
}
}
return blob;
}
/**

View file

@ -82,11 +82,19 @@ free_up (void *user_data)
(*freed)++;
}
static hb_tag_t test_tags[] = {
HB_TAG ('a','b','c','d'),
HB_TAG ('e','f','g','h'),
};
static hb_blob_t *
get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data HB_UNUSED)
{
if (tag == HB_TAG ('a','b','c','d'))
return hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL);
for (unsigned i = 0; i < sizeof (test_tags) / sizeof (test_tags[0]); i++)
{
if (test_tags[i] == tag)
return hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL);
}
return hb_blob_get_empty ();
}
@ -119,6 +127,72 @@ test_face_createfortables (void)
g_assert (freed);
}
static unsigned int
get_table_tags (const hb_face_t *face HB_UNUSED,
unsigned int start_offset,
unsigned int *table_count,
hb_tag_t *table_tags,
void *user_data)
{
unsigned count = sizeof (test_tags) / sizeof (test_tags[0]);
unsigned end_offset;
if (!table_count)
return count;
if (start_offset >= count)
{
*table_count = 0;
return count;
}
end_offset = start_offset + *table_count;
if (end_offset < start_offset)
{
*table_count = 0;
return count;
}
end_offset = end_offset < count ? end_offset : count;
*table_count = end_offset - start_offset;
for (unsigned i = start_offset; i < end_offset; i++)
table_tags[i - start_offset] = test_tags[i];
return count;
}
static void
test_face_referenceblob (void)
{
hb_blob_t *blob;
hb_face_t *face;
int freed = 0;
face = hb_face_create_for_tables (get_table, &freed, free_up);
hb_face_set_get_table_tags_func (face, get_table_tags, NULL, NULL);
blob = hb_face_reference_blob (face);
hb_face_destroy (face);
g_assert (blob != hb_blob_get_empty ());
face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
g_assert (face != hb_face_get_empty ());
g_assert_cmpuint (hb_face_get_table_tags (face, 0, NULL, NULL), ==, sizeof (test_tags) / sizeof (test_tags[0]));
for (unsigned i = 0; i < sizeof (test_tags) / sizeof (test_tags[0]); i++)
{
hb_blob_t* table = hb_face_reference_table (face, test_tags[i]);
g_assert (table != hb_blob_get_empty ());
hb_blob_destroy (table);
}
hb_face_destroy (face);
}
static void
_test_font_nil_funcs (hb_font_t *font)
{
@ -606,6 +680,7 @@ main (int argc, char **argv)
hb_test_add (test_face_empty);
hb_test_add (test_face_create);
hb_test_add (test_face_createfortables);
hb_test_add (test_face_referenceblob);
hb_test_add (test_fontfuncs_empty);
hb_test_add (test_fontfuncs_nil);