From 0d4053b11d347fe500a3bde9e2690a9486045921 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 27 Mar 2025 18:45:12 +0000 Subject: [PATCH] [subset] Add hb-subset.h API for retrieving the raw per glyph outline data from CFF and CFF2. --- docs/harfbuzz-sections.txt | 1 + src/gen-def.py | 1 + src/hb-subset.cc | 31 +++++++++++++++ src/hb-subset.h | 8 ++++ test/api/test-subset.c | 79 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+) diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 650c07a98..01e8fd209 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -955,6 +955,7 @@ hb_subset_serialize_object_t hb_subset_serialize_or_fail hb_subset_input_override_name_table +hb_subset_cff_get_charstring_data
diff --git a/src/gen-def.py b/src/gen-def.py index bc5a40137..ac98ffc2d 100755 --- a/src/gen-def.py +++ b/src/gen-def.py @@ -21,6 +21,7 @@ if '--experimental-api' not in sys.argv: experimental_symbols = \ """hb_shape_justify hb_subset_input_override_name_table +hb_subset_cff_get_charstring_data """.splitlines () symbols = [x for x in symbols if x not in experimental_symbols] symbols = "\n".join (symbols) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index fbdf1b4f9..aacaa5814 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -708,3 +708,34 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) end: return success ? hb_face_reference (plan->dest) : nullptr; } + + +#ifdef HB_EXPERIMENTAL_API + +#include "hb-ot-cff1-table.hh" + +/** + * hb_subset_cff_get_charstring_data: + * @face: A face object + * @glyph_index: Glyph index to get data for. + * + * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. + * + * XSince: EXPERIMENTAL + **/ + HB_EXTERN hb_blob_t* + hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { + hb_ubytes_t bytes; + if (_is_table_present(face, HB_TAG('C', 'F', 'F', ' '))) { + bytes = (*face->table.cff1->charStrings)[glyph_index]; + } else if (_is_table_present(face, HB_TAG('C', 'F', 'F', '2'))) { + bytes = (*face->table.cff2->charStrings)[glyph_index]; + } + + if (!bytes) { + return hb_blob_get_empty (); + } + + return hb_blob_create((const char*) bytes.arrayZ, bytes.length, HB_MEMORY_MODE_READONLY, nullptr, nullptr); + } + #endif \ No newline at end of file diff --git a/src/hb-subset.h b/src/hb-subset.h index 71276c7a6..360c34f39 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -224,6 +224,14 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, unsigned language_id, const char *name_str, int str_len); + + + +/* +* Raw outline data access +*/ +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index); #endif HB_EXTERN hb_face_t * diff --git a/test/api/test-subset.c b/test/api/test-subset.c index 312c15663..82faed40e 100644 --- a/test/api/test-subset.c +++ b/test/api/test-subset.c @@ -228,6 +228,80 @@ test_subset_create_for_tables_face (void) hb_face_destroy (face_ac); } +#ifdef HB_EXPERIMENTAL_API +static void +test_subset_cff_get_charstring_data (void) +{ + const uint8_t CFF2[226] = { + // From https://learn.microsoft.com/en-us/typography/opentype/spec/cff2 + 0x02, 0x00, 0x05, 0x00, 0x07, 0xCF, 0x0C, 0x24, 0xC3, 0x11, 0x9B, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x26, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, + 0x00, 0x02, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x03, 0x05, + 0x20, 0x0A, 0x20, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x05, 0xF7, 0x06, 0xDA, 0x12, 0x77, + 0x9F, 0xF8, 0x6C, 0x9D, 0xAE, 0x9A, 0xF4, 0x9A, 0x95, 0x9F, 0xB3, 0x9F, 0x8B, 0x8B, 0x8B, 0x8B, + 0x85, 0x9A, 0x8B, 0x8B, 0x97, 0x73, 0x8B, 0x8B, 0x8C, 0x80, 0x8B, 0x8B, 0x8B, 0x8D, 0x8B, 0x8B, + 0x8C, 0x8A, 0x8B, 0x8B, 0x97, 0x17, 0x06, 0xFB, 0x8E, 0x95, 0x86, 0x9D, 0x8B, 0x8B, 0x8D, 0x17, + 0x07, 0x77, 0x9F, 0xF8, 0x6D, 0x9D, 0xAD, 0x9A, 0xF3, 0x9A, 0x95, 0x9F, 0xB3, 0x9F, 0x08, 0xFB, + 0x8D, 0x95, 0x09, 0x1E, 0xA0, 0x37, 0x5F, 0x0C, 0x09, 0x8B, 0x0C, 0x0B, 0xC2, 0x6E, 0x9E, 0x8C, + 0x17, 0x0A, 0xDB, 0x57, 0xF7, 0x02, 0x8C, 0x17, 0x0B, 0xB3, 0x9A, 0x77, 0x9F, 0x82, 0x8A, 0x8D, + 0x17, 0x0C, 0x0C, 0xDB, 0x95, 0x57, 0xF7, 0x02, 0x85, 0x8B, 0x8D, 0x17, 0x0C, 0x0D, 0xF7, 0x06, + 0x13, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1B, 0xBD, 0xBD, 0xEF, 0x8C, 0x10, 0x8B, 0x15, 0xF8, + 0x88, 0x27, 0xFB, 0x5C, 0x8C, 0x10, 0x06, 0xF8, 0x88, 0x07, 0xFC, 0x88, 0xEF, 0xF7, 0x5C, 0x8C, + 0x10, 0x06 + }; + + hb_blob_t* cff2 = hb_blob_create ((const char*) CFF2, 226, HB_MEMORY_MODE_READONLY, 0, 0); + hb_face_t* builder = hb_face_builder_create (); + hb_face_builder_add_table (builder, HB_TAG('C', 'F', 'F', '2'), cff2); + hb_blob_t* face_blob = hb_face_reference_blob (builder); + hb_face_t* face = hb_face_create (face_blob, 0); + + hb_blob_t* cs0 = hb_subset_cff_get_charstring_data (face, 0); + unsigned int length; + const uint8_t* data = (const uint8_t*) hb_blob_get_data (cs0, &length); + g_assert (length == 2); + g_assert (data[0] == 0x20); + g_assert (data[1] == 0x0A); + + hb_blob_t* cs1 = hb_subset_cff_get_charstring_data (face, 1); + data = (const uint8_t*) hb_blob_get_data (cs1, &length); + g_assert (length == 2); + g_assert (data[0] == 0x20); + g_assert (data[1] == 0x0A); + + hb_blob_t* cs2 = hb_subset_cff_get_charstring_data (face, 2); + data = (const uint8_t*) hb_blob_get_data (cs2, &length); + g_assert (length == 0); + + hb_blob_destroy (cff2); + hb_face_destroy (builder); + hb_blob_destroy (face_blob); + hb_face_destroy (face); + hb_blob_destroy (cs0); + hb_blob_destroy (cs1); + hb_blob_destroy (cs2); +} + +static void +test_subset_cff_get_charstring_data_no_cff (void) +{ + hb_face_t* builder = hb_face_builder_create (); + hb_blob_t* face_blob = hb_face_reference_blob (builder); + hb_face_t* face = hb_face_create (face_blob, 0); + + hb_blob_t* cs0 = hb_subset_cff_get_charstring_data (face, 0); + unsigned int length; + const uint8_t* data = (const uint8_t*) hb_blob_get_data (cs0, &length); + g_assert (length == 0); + + hb_face_destroy (builder); + hb_blob_destroy (face_blob); + hb_face_destroy (face); + hb_blob_destroy (cs0); +} +#endif + int main (int argc, char **argv) { @@ -241,5 +315,10 @@ main (int argc, char **argv) hb_test_add (test_subset_plan); hb_test_add (test_subset_create_for_tables_face); + #ifdef HB_EXPERIMENTAL_API + hb_test_add (test_subset_cff_get_charstring_data); + hb_test_add (test_subset_cff_get_charstring_data_no_cff); + #endif + return hb_test_run(); }