diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 01e8fd209..4a5d1d87d 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -956,6 +956,7 @@ hb_subset_serialize_or_fail hb_subset_input_override_name_table hb_subset_cff_get_charstring_data +hb_subset_cff2_get_charstring_data
diff --git a/src/gen-def.py b/src/gen-def.py index ac98ffc2d..063f859ad 100755 --- a/src/gen-def.py +++ b/src/gen-def.py @@ -22,6 +22,7 @@ if '--experimental-api' not in sys.argv: """hb_shape_justify hb_subset_input_override_name_table hb_subset_cff_get_charstring_data +hb_subset_cff2_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 aacaa5814..b1e4d82aa 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -714,6 +714,29 @@ end: #include "hb-ot-cff1-table.hh" +template +static hb_blob_t* get_charstrings_data(accel_t& accel, hb_codepoint_t glyph_index) { + if (!accel.is_valid()) { + return hb_blob_get_empty (); + } + + hb_ubytes_t bytes = (*accel.charStrings)[glyph_index]; + if (!bytes) { + return hb_blob_get_empty (); + } + + hb_blob_t* cff_blob = accel.get_blob(); + uint32_t length; + const char* cff_data = hb_blob_get_data(cff_blob, &length) ; + + long int offset = (const char*) bytes.arrayZ - cff_data; + if (offset < 0 || offset > UINT32_MAX) { + return hb_blob_get_empty (); + } + + return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, bytes.length); +} + /** * hb_subset_cff_get_charstring_data: * @face: A face object @@ -725,17 +748,28 @@ end: **/ 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) { + if (!_is_table_present(face, HB_TAG('C', 'F', 'F', ' '))) { return hb_blob_get_empty (); } - return hb_blob_create((const char*) bytes.arrayZ, bytes.length, HB_MEMORY_MODE_READONLY, nullptr, nullptr); + return get_charstrings_data(*face->table.cff1, glyph_index); + } + + /** + * hb_subset_cff2_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_cff2_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { + if (!_is_table_present(face, HB_TAG('C', 'F', 'F', '2'))) { + return hb_blob_get_empty (); + } + + return get_charstrings_data(*face->table.cff2, glyph_index); } #endif \ No newline at end of file diff --git a/src/hb-subset.h b/src/hb-subset.h index 360c34f39..3e1405a3f 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -230,8 +230,12 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, /* * Raw outline data access */ + HB_EXTERN hb_blob_t* hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index); + +HB_EXTERN hb_blob_t* +hb_subset_cff2_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 82faed40e..b22c76a24 100644 --- a/test/api/test-subset.c +++ b/test/api/test-subset.c @@ -229,52 +229,60 @@ test_subset_create_for_tables_face (void) } #ifdef HB_EXPERIMENTAL_API +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 +}; + static void -test_subset_cff_get_charstring_data (void) +test_subset_cff2_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 + const uint8_t maxp_data[6] = { + 0x00, 0x00, 0x50, 0x00, + 0x00, 0x02 // numGlyphs }; hb_blob_t* cff2 = hb_blob_create ((const char*) CFF2, 226, HB_MEMORY_MODE_READONLY, 0, 0); + hb_blob_t* maxp = hb_blob_create ((const char*) maxp_data, 6, 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_face_builder_add_table (builder, HB_TAG('m', 'a', 'x', 'p'), maxp); 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); + hb_blob_t* cs0 = hb_subset_cff2_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); + hb_blob_t* cs1 = hb_subset_cff2_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); + hb_blob_t* cs2 = hb_subset_cff2_get_charstring_data (face, 2); data = (const uint8_t*) hb_blob_get_data (cs2, &length); g_assert (length == 0); hb_blob_destroy (cff2); + hb_blob_destroy (maxp); hb_face_destroy (builder); hb_blob_destroy (face_blob); hb_face_destroy (face); @@ -284,22 +292,78 @@ test_subset_cff_get_charstring_data (void) } static void -test_subset_cff_get_charstring_data_no_cff (void) +test_subset_cff2_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_blob_t* cs0 = hb_subset_cff2_get_charstring_data (face, 0); + g_assert (hb_blob_get_length (cs0) == 0); + hb_face_destroy (builder); + hb_blob_destroy (face_blob); + hb_face_destroy (face); + hb_blob_destroy + (cs0); +} + +static void +test_subset_cff2_get_charstring_data_invalid_cff2 (void) +{ + // cff2 will parse as invalid since charstrings count doesn't not match num glyphs + // (because there is no maxp). + 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_cff2_get_charstring_data (face, 0); + g_assert (hb_blob_get_length (cs0) == 0); + + hb_blob_destroy (cff2); hb_face_destroy (builder); hb_blob_destroy (face_blob); hb_face_destroy (face); hb_blob_destroy (cs0); } + +static void +test_subset_cff2_get_charstring_data_lifetime (void) +{ + const uint8_t maxp_data[6] = { + 0x00, 0x00, 0x50, 0x00, + 0x00, 0x02 // numGlyphs + }; + + hb_blob_t* cff2 = hb_blob_create ((const char*) CFF2, 226, HB_MEMORY_MODE_READONLY, 0, 0); + hb_blob_t* maxp = hb_blob_create ((const char*) maxp_data, 6, 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_face_builder_add_table (builder, HB_TAG('m', 'a', 'x', 'p'), maxp); + 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_cff2_get_charstring_data (face, 0); + + // Destroy the blob that cs0 is referencing via subblob to ensure lifetimes are being + // handled correctly. + hb_blob_destroy (cff2); + hb_blob_destroy (maxp); + hb_face_destroy (builder); + hb_blob_destroy (face_blob); + hb_face_destroy (face); + + 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_destroy (cs0); +} + #endif int @@ -316,8 +380,10 @@ main (int argc, char **argv) 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); + hb_test_add (test_subset_cff2_get_charstring_data); + hb_test_add (test_subset_cff2_get_charstring_data_no_cff); + hb_test_add (test_subset_cff2_get_charstring_data_invalid_cff2); + hb_test_add (test_subset_cff2_get_charstring_data_lifetime); #endif return hb_test_run();