diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/libnrtype/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/libnrtype/FontFactory.cpp | 184 | ||||
| -rw-r--r-- | src/libnrtype/OpenTypeUtil.cpp | 207 | ||||
| -rw-r--r-- | src/libnrtype/OpenTypeUtil.h | 36 |
4 files changed, 255 insertions, 174 deletions
diff --git a/src/libnrtype/CMakeLists.txt b/src/libnrtype/CMakeLists.txt index c39eecbbc..3061e9304 100644 --- a/src/libnrtype/CMakeLists.txt +++ b/src/libnrtype/CMakeLists.txt @@ -9,6 +9,7 @@ set(nrtype_SRC Layout-TNG-OutIter.cpp Layout-TNG-Output.cpp Layout-TNG-Scanline-Makers.cpp + OpenTypeUtil.cpp # ------- # Headers @@ -19,6 +20,7 @@ set(nrtype_SRC FontFactory.h Layout-TNG-Scanline-Maker.h Layout-TNG.h + OpenTypeUtil.cpp ) add_inkscape_lib(nrtype_LIB "${nrtype_SRC}") diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index 9b39f9e9b..d17a8a310 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -17,18 +17,19 @@ #endif #include <glibmm/i18n.h> + #include <fontconfig/fontconfig.h> + #include <pango/pangofc-fontmap.h> #include <pango/pangoft2.h> #include <pango/pango-ot.h> + #include "io/sys.h" -#include "libnrtype/FontFactory.h" -#include "libnrtype/font-instance.h" #include "util/unordered-containers.h" -#include <map> -#include <harfbuzz/hb-ft.h> -#include <harfbuzz/hb-ot.h> +#include "libnrtype/FontFactory.h" +#include "libnrtype/font-instance.h" +#include "libnrtype/OpenTypeUtil.h" typedef INK_UNORDERED_MAP<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; @@ -472,6 +473,7 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) std::cerr << "font_factory::GetUIStyles(): PangoFontFamily is NULL" << std::endl; return ret; } + pango_font_family_list_faces(in, &faces, &numFaces); for (int currentFace = 0; currentFace < numFaces; currentFace++) { @@ -490,6 +492,7 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) Glib::ustring familyUIName = GetUIFamilyString(faceDescr); Glib::ustring styleUIName = GetUIStyleString(faceDescr); // std::cout << " " << familyUIName << " styleUIName: " << styleUIName << " displayName: " << displayName << std::endl; + // Disable synthesized (faux) font faces except for CSS generic faces if (pango_font_face_is_synthesized(faces[currentFace]) ) { if (familyUIName.compare( "sans-serif" ) != 0 && @@ -633,174 +636,6 @@ font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecifica return font; } -void dump_tag( guint32 *tag, Glib::ustring prefix = "", bool lf=true ) { - std::cout << prefix - << ((char)((*tag & 0xff000000)>>24)) - << ((char)((*tag & 0x00ff0000)>>16)) - << ((char)((*tag & 0x0000ff00)>>8)) - << ((char)((*tag & 0x000000ff)>>0)); - if( lf ) { - std::cout << std::endl; - } -} - -Glib::ustring extract_tag( guint32 *tag ) { - Glib::ustring tag_name; - tag_name += ((char)((*tag & 0xff000000)>>24)); - tag_name += ((char)((*tag & 0x00ff0000)>>16)); - tag_name += ((char)((*tag & 0x0000ff00)>>8)); - tag_name += ((char)((*tag & 0x000000ff)>>0)); - return tag_name; -} - -// Extract which OpenType tables are in the font. A list of all tables (regardless of which script and langauge -// they are in) will be stored as a std::map in the openTypeTables field of the font_instance -// This Harfbuzz code replaces an earlier Pango version as the Pango functions are deprecated. -void extract_openTypeTables(font_instance *res) { - // Empty map... bitmap fonts seem to be loaded multiple times. - res->openTypeTables.clear(); - -#ifndef USE_PANGO_WIN32 - auto const face = hb_ft_face_create(res->theFace, NULL); - - // First time to get size of array - auto script_count = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL); - auto const scripts_hb = g_new(hb_tag_t, script_count + 1); - - // Second time to fill array (this two step process was not necessary with Pango). - hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &script_count, scripts_hb); - - for(unsigned int i = 0; i < script_count; ++i) { - auto language_count = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); - - if(language_count > 0) { - auto const languages_hb = g_new(hb_tag_t, language_count + 1); - hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, &language_count, languages_hb); - - for(unsigned int j = 0; j < language_count; ++j) { - auto feature_count = hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, j, 0, NULL, NULL); - auto const features_hb = g_new(hb_tag_t, feature_count + 1); - hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, j, 0, &feature_count, features_hb); - - for(unsigned int k = 0; k < feature_count; ++k) { - ++(res->openTypeTables[ extract_tag(&features_hb[k])]); - } - - g_free(features_hb); - } - - g_free(languages_hb); - } - else { - // Even if no languages are present there is still the default. - auto feature_count = hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, - 0, NULL, NULL); - auto const features_hb = g_new(hb_tag_t, feature_count + 1); - hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, - 0, &feature_count, features_hb); - - for(unsigned int k = 0; k < feature_count; ++k) { - ++(res->openTypeTables[ extract_tag(&features_hb[k])]); - } - - g_free(features_hb); - } - } - -// TODO: Ideally, we should use the HB_VERSION_ATLEAST macro here, -// but this was only released in harfbuzz >= 0.9.30 -// #if HB_VERSION_ATLEAST(1,2,3) -#if HB_VERSION_MAJOR*10000 + HB_VERSION_MINOR*100 + HB_VERSION_MICRO >= 10203 - // Find glyphs in OpenType substitution tables ('gsub'). - // Note that pango's functions are just dummies. Must use harfbuzz. - - // Loop over all tables - for (auto table: res->openTypeTables) { - - // Only look at style substitution tables ('salt', 'ss01', etc. but not 'ssty'). - if (table.first == "salt" || - (table.first[0] == 's' && table.first[1] == 's' && !(table.first[2] == 't') ) ) { - // std::cout << " Table: " << table.first << std::endl; - - Glib::ustring unicode_characters; - - unsigned int feature_index; - if ( hb_ot_layout_language_find_feature (face, HB_OT_TAG_GSUB, - 0, // Assume one script exists with index 0 - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, - HB_TAG(table.first[0], - table.first[1], - table.first[2], - table.first[3]), - &feature_index ) ) { - - // std::cout << " Found feature, number: " << feature_index << std::endl; - unsigned int lookup_indexes[32]; - unsigned int lookup_count = 32; - int count = hb_ot_layout_feature_get_lookups (face, HB_OT_TAG_GSUB, - feature_index, - 0, // Start - &lookup_count, - lookup_indexes ); - // std::cout << " Lookup count: " << count << " total: " << lookup_count << std::endl; - - if (count > 0) { - hb_set_t* glyphs_before = NULL; // hb_set_create(); - hb_set_t* glyphs_input = hb_set_create(); - hb_set_t* glyphs_after = NULL; // hb_set_create(); - hb_set_t* glyphs_output = NULL; // hb_set_create(); - - // For now, just look at first index - hb_ot_layout_lookup_collect_glyphs (face, HB_OT_TAG_GSUB, - lookup_indexes[0], - glyphs_before, - glyphs_input, - glyphs_after, - glyphs_output ); - - hb_font_t *font = hb_font_create (face); - - // Without this, all functions return 0, etc. - hb_ft_font_set_funcs (font); - - hb_codepoint_t codepoint = -1; - while (hb_set_next (glyphs_input, &codepoint)) { - - // There is a unicode to glyph mapping function but not the inverse! - 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) { - unicode_characters += (gunichar)unicode_i; - continue; - } - } - } - res->openTypeSubstitutions[table.first] = unicode_characters; - - hb_set_destroy (glyphs_input); - hb_font_destroy (font); - } - } else { - // std::cout << " Did not find '" << table.first << "'!" << std::endl; - } - } - } - // for (auto table: res->openTypeSubstitutions) { - // std::cout << table.first << ": " << table.second << std::endl; - // } -#else - std::cerr << "Requires Harfbuzz 1.2.3 for visualizing alternative glyph OpenType tables. " - << "Compiled with: " << HB_VERSION_STRING << "." << std::endl; -#endif - - hb_face_destroy (face); - g_free(scripts_hb); -#endif // USE_PANGO_WIN32 -} - font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) { #ifdef USE_PANGO_WIN32 @@ -869,8 +704,9 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) } if (res) { - extract_openTypeTables(res); + readOpenTypeGsubTable( res->theFace, res->openTypeTables, res->openTypeSubstitutions ); } + } else { // already here res = loadedFaces[descr]; diff --git a/src/libnrtype/OpenTypeUtil.cpp b/src/libnrtype/OpenTypeUtil.cpp new file mode 100644 index 000000000..d1d1dea82 --- /dev/null +++ b/src/libnrtype/OpenTypeUtil.cpp @@ -0,0 +1,207 @@ + +#include "OpenTypeUtil.h" + +#include <iostream> // For debugging + +// FreeType +#include FT_FREETYPE_H +#include FT_MULTIPLE_MASTERS_H +#include FT_SFNT_NAMES_H + +// Harfbuzz +#include <harfbuzz/hb-ft.h> +#include <harfbuzz/hb-ot.h> + + +// Utilities used in this file + +void dump_tag( guint32 *tag, Glib::ustring prefix = "", bool lf=true ) { + std::cout << prefix + << ((char)((*tag & 0xff000000)>>24)) + << ((char)((*tag & 0x00ff0000)>>16)) + << ((char)((*tag & 0x0000ff00)>> 8)) + << ((char)((*tag & 0x000000ff) )); + if( lf ) { + std::cout << std::endl; + } +} + +Glib::ustring extract_tag( guint32 *tag ) { + Glib::ustring tag_name; + tag_name += ((char)((*tag & 0xff000000)>>24)); + tag_name += ((char)((*tag & 0x00ff0000)>>16)); + tag_name += ((char)((*tag & 0x0000ff00)>> 8)); + tag_name += ((char)((*tag & 0x000000ff) )); + return tag_name; +} + +inline double FTFixedToDouble (FT_Fixed value) { + return static_cast<FT_Int32>(value) / 65536.0; +} + +inline FT_Fixed FTDoubleToFixed (double value) { + return static_cast<FT_Fixed>(value * 65536); +} + + +// Make a list of all tables fount in the GSUB +// This list includes all tables regardless of script or language. +void readOpenTypeGsubTable (const FT_Face ft_face, + std::map<Glib::ustring, int>& tables, + std::map<Glib::ustring, Glib::ustring>& substitutions) { + + tables.clear(); + substitutions.clear(); + + // Use Harfbuzz, Pango's equivalent calls are deprecated. + auto const hb_face = hb_ft_face_create(ft_face, NULL); + + // First time to get size of array + auto script_count = hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, NULL, NULL); + auto const hb_scripts = g_new(hb_tag_t, script_count + 1); + + // Second time to fill array (this two step process was not necessary with Pango). + hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, &script_count, hb_scripts); + + for(unsigned int i = 0; i < script_count; ++i) { + auto language_count = hb_ot_layout_script_get_language_tags(hb_face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); + + if(language_count > 0) { + auto const hb_languages = g_new(hb_tag_t, language_count + 1); + hb_ot_layout_script_get_language_tags(hb_face, HB_OT_TAG_GSUB, i, 0, &language_count, hb_languages); + + for(unsigned int j = 0; j < language_count; ++j) { + auto feature_count = hb_ot_layout_language_get_feature_tags(hb_face, HB_OT_TAG_GSUB, i, j, 0, NULL, NULL); + auto const hb_features = g_new(hb_tag_t, feature_count + 1); + hb_ot_layout_language_get_feature_tags(hb_face, HB_OT_TAG_GSUB, i, j, 0, &feature_count, hb_features); + + for(unsigned int k = 0; k < feature_count; ++k) { + ++(tables[ extract_tag(&hb_features[k])]); + } + + g_free(hb_features); + } + + g_free(hb_languages); + + } else { + + // Even if no languages are present there is still the default. + auto feature_count = hb_ot_layout_language_get_feature_tags(hb_face, HB_OT_TAG_GSUB, i, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + 0, NULL, NULL); + auto const hb_features = g_new(hb_tag_t, feature_count + 1); + hb_ot_layout_language_get_feature_tags(hb_face, HB_OT_TAG_GSUB, i, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + 0, &feature_count, hb_features); + + for(unsigned int k = 0; k < feature_count; ++k) { + ++(tables[ extract_tag(&hb_features[k])]); + } + + g_free(hb_features); + } + } + +// TODO: Ideally, we should use the HB_VERSION_ATLEAST macro here, +// but this was only released in harfbuzz >= 0.9.30 +// #if HB_VERSION_ATLEAST(1,2,3) +#if HB_VERSION_MAJOR*10000 + HB_VERSION_MINOR*100 + HB_VERSION_MICRO >= 10203 + // Find glyphs in OpenType substitution tables ('gsub'). + // Note that pango's functions are just dummies. Must use harfbuzz. + + // Loop over all tables + for (auto table: tables) { + + // Only look at style substitution tables ('salt', 'ss01', etc. but not 'ssty'). + if (table.first == "salt" || + (table.first[0] == 's' && table.first[1] == 's' && !(table.first[2] == 't') ) ) { + // std::cout << " Table: " << table.first << std::endl; + + Glib::ustring unicode_characters; + + unsigned int feature_index; + if ( hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, + 0, // Assume one script exists with index 0 + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + HB_TAG(table.first[0], + table.first[1], + table.first[2], + table.first[3]), + &feature_index ) ) { + + // std::cout << " Found feature, number: " << feature_index << std::endl; + unsigned int lookup_indexes[32]; + unsigned int lookup_count = 32; + int count = hb_ot_layout_feature_get_lookups (hb_face, HB_OT_TAG_GSUB, + feature_index, + 0, // Start + &lookup_count, + lookup_indexes ); + // std::cout << " Lookup count: " << count << " total: " << lookup_count << std::endl; + + if (count > 0) { + hb_set_t* glyphs_before = NULL; // hb_set_create(); + hb_set_t* glyphs_input = hb_set_create(); + hb_set_t* glyphs_after = NULL; // hb_set_create(); + hb_set_t* glyphs_output = NULL; // hb_set_create(); + + // For now, just look at first index + hb_ot_layout_lookup_collect_glyphs (hb_face, HB_OT_TAG_GSUB, + lookup_indexes[0], + glyphs_before, + glyphs_input, + glyphs_after, + glyphs_output ); + + hb_font_t *hb_font = hb_font_create (hb_face); + + // Without this, all functions return 0, etc. + hb_ft_font_set_funcs (hb_font); + + hb_codepoint_t codepoint = -1; + while (hb_set_next (glyphs_input, &codepoint)) { + + // There is a unicode to glyph mapping function but not the inverse! + for (hb_codepoint_t unicode_i = 0; unicode_i < 0xffff; ++unicode_i) { + hb_codepoint_t glyph = 0; + hb_font_get_nominal_glyph (hb_font, unicode_i, &glyph); + if ( glyph == codepoint) { + unicode_characters += (gunichar)unicode_i; + continue; + } + } + } + substitutions[table.first] = unicode_characters; + + hb_set_destroy (glyphs_input); + hb_font_destroy (hb_font); + } + } else { + // std::cout << " Did not find '" << table.first << "'!" << std::endl; + } + } + } + // for (auto table: res->openTypeSubstitutions) { + // std::cout << table.first << ": " << table.second << std::endl; + // } +#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); + hb_face_destroy (hb_face); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/libnrtype/OpenTypeUtil.h b/src/libnrtype/OpenTypeUtil.h new file mode 100644 index 000000000..6d9ac9b3b --- /dev/null +++ b/src/libnrtype/OpenTypeUtil.h @@ -0,0 +1,36 @@ + +#ifndef SEEN_OPENTYPEUTIL_H +#define SEEN_OPENTYPEUTIL_H + +#include <map> + +#include <ft2build.h> +#include FT_FREETYPE_H + +#include <glibmm/ustring.h> + +/* + * A set of utilities to extract data from OpenType fonts. + * + * Isolates dependencies on FreeType, Harfbuzz, and Pango. + * All three provide variable amounts of access to data. + */ + + +void readOpenTypeGsubTable (const FT_Face ft_face, + std::map<Glib::ustring, int>& tables, + std::map<Glib::ustring, Glib::ustring>& substitutions); + + +#endif /* !SEEN_OPENTYPEUTIL_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
