diff --git a/src/libnrtype/OpenTypeUtil.cpp b/src/libnrtype/OpenTypeUtil.cpp index cfe4ce3239fcfd3d9372c4fcd735f2f4093b67e5..96ff60308afbcbe43e687ab64ae8c73515ad0549 100644 --- a/src/libnrtype/OpenTypeUtil.cpp +++ b/src/libnrtype/OpenTypeUtil.cpp @@ -14,6 +14,8 @@ #include // For debugging +#include +#include // FreeType #include FT_FREETYPE_H @@ -32,6 +34,11 @@ // Utilities used in this file +struct HbSetDeleter { + void operator()(hb_set_t* x) { hb_set_destroy(x); } +}; +using HbSet = std::unique_ptr; + void dump_tag( guint32 *tag, Glib::ustring prefix = "", bool lf=true ) { std::cout << prefix << ((char)((*tag & 0xff000000)>>24)) @@ -53,23 +60,52 @@ Glib::ustring extract_tag( guint32 *tag ) { } -#if HB_VERSION_ATLEAST(1,2,3) // Released Feb 2016 -void get_glyphs( hb_font_t* font, hb_set_t* set, Glib::ustring& characters) { +// Later (see get_glyphs) we need to lookup the Unicode codepoint for a glyph +// but there's no direct API for that. So, we need a way to iterate over all +// glyph mappings and build a reverse map. +// FIXME: we should handle UVS at some point... or better, work with glyphs directly + +// Allows looking up the lowest Unicode codepoint mapped to a given glyph. +// To do so, it lazily builds a reverse map. +class GlyphToUnicodeMap { +protected: + hb_font_t* font; + HbSet codepointSet; + + std::unordered_map mappings; + bool more = true; // false if we have finished iterating the set + hb_codepoint_t codepoint = HB_SET_VALUE_INVALID; // current iteration +public: + GlyphToUnicodeMap(hb_font_t* font): font(font), codepointSet(hb_set_create()) { + hb_face_collect_unicodes(hb_font_get_face(font), codepointSet.get()); + } - // There is a unicode to glyph mapping function but not the inverse! - hb_codepoint_t codepoint = -1; - while (hb_set_next (set, &codepoint)) { - for (hb_codepoint_t unicode_i = 0; unicode_i < 0xffff; ++unicode_i) { - hb_codepoint_t glyph = 0; - hb_font_get_nominal_glyph (font, unicode_i, &glyph); - if (glyph == codepoint) { - characters += (gunichar)unicode_i; - break; - } + hb_codepoint_t lookup(hb_codepoint_t glyph) { + // first, try to find it in the mappings we've seen so far + if (auto it = mappings.find(glyph); it != mappings.end()) + return it->second; + + // populate more mappings from the set + while (more = more && hb_set_next(codepointSet.get(), &codepoint)) { + // get the glyph that this codepoint is associated with, if any + hb_codepoint_t tGlyph; + if (!hb_font_get_nominal_glyph(font, codepoint, &tGlyph)) continue; + + // save the mapping, and return if this is the one we were looking for + mappings.emplace(tGlyph, codepoint); + if (tGlyph == glyph) return codepoint; } + return 0; + } +}; + +void get_glyphs(GlyphToUnicodeMap& glyphMap, HbSet& set, Glib::ustring& characters) { + hb_codepoint_t glyph = -1; + while (hb_set_next(set.get(), &glyph)) { + if (auto codepoint = glyphMap.lookup(glyph)) + characters += codepoint; } } -#endif // Make a list of all tables found in the GSUB // This list includes all tables regardless of script or language. @@ -133,10 +169,11 @@ void readOpenTypeGsubTable (hb_font_t* hb_font, } } -#if HB_VERSION_ATLEAST(1,2,3) // Released Feb 2016 // Find glyphs in OpenType substitution tables ('gsub'). // Note that pango's functions are just dummies. Must use harfbuzz. + GlyphToUnicodeMap glyphMap (hb_font); + // Loop over all tables for (auto table: tables) { @@ -192,17 +229,17 @@ void readOpenTypeGsubTable (hb_font_t* hb_font, // std::cout << " Lookup count: " << count << " total: " << lookup_count << std::endl; for (int i = 0; i < count; ++i) { - hb_set_t* glyphs_before = hb_set_create(); - hb_set_t* glyphs_input = hb_set_create(); - hb_set_t* glyphs_after = hb_set_create(); - hb_set_t* glyphs_output = hb_set_create(); + HbSet glyphs_before (hb_set_create()); + HbSet glyphs_input (hb_set_create()); + HbSet glyphs_after (hb_set_create()); + HbSet glyphs_output (hb_set_create()); hb_ot_layout_lookup_collect_glyphs (hb_face, HB_OT_TAG_GSUB, lookup_indexes[i], - glyphs_before, - glyphs_input, - glyphs_after, - glyphs_output ); + glyphs_before.get(), + glyphs_input.get(), + glyphs_after.get(), + glyphs_output.get() ); // std::cout << " Populations: " // << " " << hb_set_get_population (glyphs_before) @@ -211,21 +248,15 @@ void readOpenTypeGsubTable (hb_font_t* hb_font, // << " " << hb_set_get_population (glyphs_output) // << std::endl; - get_glyphs (hb_font, glyphs_before, tables[table.first].before); - get_glyphs (hb_font, glyphs_input, tables[table.first].input ); - get_glyphs (hb_font, glyphs_after, tables[table.first].after ); - get_glyphs (hb_font, glyphs_output, tables[table.first].output); + get_glyphs (glyphMap, glyphs_before, tables[table.first].before); + get_glyphs (glyphMap, glyphs_input, tables[table.first].input ); + get_glyphs (glyphMap, glyphs_after, tables[table.first].after ); + get_glyphs (glyphMap, glyphs_output, tables[table.first].output); // std::cout << " Before: " << tables[table.first].before.c_str() << std::endl; // std::cout << " Input: " << tables[table.first].input.c_str() << std::endl; // std::cout << " After: " << tables[table.first].after.c_str() << std::endl; // std::cout << " Output: " << tables[table.first].output.c_str() << std::endl; - - hb_set_destroy (glyphs_before); - hb_set_destroy (glyphs_input); - hb_set_destroy (glyphs_after); - hb_set_destroy (glyphs_output); - } // End count (lookups) } else { @@ -234,10 +265,6 @@ void readOpenTypeGsubTable (hb_font_t* hb_font, } } -#else - std::cerr << "Requires Harfbuzz 1.2.3 for visualizing alternative glyph OpenType tables. " - << "Compiled with: " << HB_VERSION_STRING << "." << std::endl; -#endif g_free(hb_scripts); }