diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
commit | cca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch) | |
tree | 146f39ded1c938019e1ed42d30923c2ac9e86789 /src/libnrtype/FontFactory.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip |
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/libnrtype/FontFactory.cpp | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp new file mode 100644 index 0000000..88c8b55 --- /dev/null +++ b/src/libnrtype/FontFactory.cpp @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: + * fred + * bulia byak <buliabyak@users.sf.net> + * + * Copyright (C) 2018 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" // only include where actually required! +#endif + +#ifndef PANGO_ENABLE_ENGINE +#define PANGO_ENABLE_ENGINE +#endif + +#include <unordered_map> + +#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 "io/resource.h" + +#include "libnrtype/FontFactory.h" +#include "libnrtype/font-instance.h" +#include "libnrtype/OpenTypeUtil.h" + +# ifdef _WIN32 + +#include <glibmm.h> +#include <windows.h> + +#endif + +typedef std::unordered_map<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; + +// need to avoid using the size field +size_t font_descr_hash::operator()( PangoFontDescription *const &x) const { + int h = 0; + char const *theF = sp_font_description_get_family(x); + h += (theF)?g_str_hash(theF):0; + h *= 1128467; + h += (int)pango_font_description_get_style(x); + h *= 1128467; + h += (int)pango_font_description_get_variant(x); + h *= 1128467; + h += (int)pango_font_description_get_weight(x); + h *= 1128467; + h += (int)pango_font_description_get_stretch(x); + char const *theV = pango_font_description_get_variations(x); + h *= 1128467; + h += (theV)?g_str_hash(theV):0; + return h; +} + +bool font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) const { + //if ( pango_font_description_equal(a,b) ) return true; + char const *fa = sp_font_description_get_family(a); + char const *fb = sp_font_description_get_family(b); + if ( ( fa && fb == nullptr ) || ( fb && fa == nullptr ) ) return false; + if ( fa && fb && strcmp(fa,fb) != 0 ) return false; + if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false; + if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false; + if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false; + if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false; + if ( g_strcmp0( pango_font_description_get_variations(a), + pango_font_description_get_variations(b) ) != 0 ) return false; + return true; +} + +// User must free return value. +PangoFontDescription* ink_font_description_from_style(SPStyle const *style) +{ + PangoFontDescription *descr = pango_font_description_new(); + + pango_font_description_set_family(descr, style->font_family.value()); + + // This duplicates Layout::EnumConversionItem... perhaps we can share code? + switch ( style->font_style.computed ) { + case SP_CSS_FONT_STYLE_ITALIC: + pango_font_description_set_style(descr, PANGO_STYLE_ITALIC); + break; + + case SP_CSS_FONT_STYLE_OBLIQUE: + pango_font_description_set_style(descr, PANGO_STYLE_OBLIQUE); + break; + + case SP_CSS_FONT_STYLE_NORMAL: + default: + pango_font_description_set_style(descr, PANGO_STYLE_NORMAL); + break; + } + + switch( style->font_weight.computed ) { + case SP_CSS_FONT_WEIGHT_100: + pango_font_description_set_weight(descr, PANGO_WEIGHT_THIN); + break; + + case SP_CSS_FONT_WEIGHT_200: + pango_font_description_set_weight(descr, PANGO_WEIGHT_ULTRALIGHT); + break; + + case SP_CSS_FONT_WEIGHT_300: + pango_font_description_set_weight(descr, PANGO_WEIGHT_LIGHT); + break; + + case SP_CSS_FONT_WEIGHT_400: + case SP_CSS_FONT_WEIGHT_NORMAL: + pango_font_description_set_weight(descr, PANGO_WEIGHT_NORMAL); + break; + + case SP_CSS_FONT_WEIGHT_500: + pango_font_description_set_weight(descr, PANGO_WEIGHT_MEDIUM); + break; + + case SP_CSS_FONT_WEIGHT_600: + pango_font_description_set_weight(descr, PANGO_WEIGHT_SEMIBOLD); + break; + + case SP_CSS_FONT_WEIGHT_700: + case SP_CSS_FONT_WEIGHT_BOLD: + pango_font_description_set_weight(descr, PANGO_WEIGHT_BOLD); + break; + + case SP_CSS_FONT_WEIGHT_800: + pango_font_description_set_weight(descr, PANGO_WEIGHT_ULTRABOLD); + break; + + case SP_CSS_FONT_WEIGHT_900: + pango_font_description_set_weight(descr, PANGO_WEIGHT_HEAVY); + break; + + case SP_CSS_FONT_WEIGHT_LIGHTER: + case SP_CSS_FONT_WEIGHT_BOLDER: + default: + g_warning("FaceFromStyle: Unrecognized font_weight.computed value"); + pango_font_description_set_weight(descr, PANGO_WEIGHT_NORMAL); + break; + } + // PANGO_WIEGHT_ULTRAHEAVY not used (not CSS2) + + switch (style->font_stretch.computed) { + case SP_CSS_FONT_STRETCH_ULTRA_CONDENSED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_ULTRA_CONDENSED); + break; + + case SP_CSS_FONT_STRETCH_EXTRA_CONDENSED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_EXTRA_CONDENSED); + break; + + case SP_CSS_FONT_STRETCH_CONDENSED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_CONDENSED); + break; + + case SP_CSS_FONT_STRETCH_SEMI_CONDENSED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_SEMI_CONDENSED); + break; + + case SP_CSS_FONT_STRETCH_NORMAL: + pango_font_description_set_stretch(descr, PANGO_STRETCH_NORMAL); + break; + + case SP_CSS_FONT_STRETCH_SEMI_EXPANDED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_SEMI_EXPANDED); + break; + + case SP_CSS_FONT_STRETCH_EXPANDED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_EXPANDED); + break; + + case SP_CSS_FONT_STRETCH_EXTRA_EXPANDED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_EXTRA_EXPANDED); + break; + + case SP_CSS_FONT_STRETCH_ULTRA_EXPANDED: + pango_font_description_set_stretch(descr, PANGO_STRETCH_ULTRA_EXPANDED); + + case SP_CSS_FONT_STRETCH_WIDER: + case SP_CSS_FONT_STRETCH_NARROWER: + default: + g_warning("FaceFromStyle: Unrecognized font_stretch.computed value"); + pango_font_description_set_stretch(descr, PANGO_STRETCH_NORMAL); + break; + } + + switch ( style->font_variant.computed ) { + case SP_CSS_FONT_VARIANT_SMALL_CAPS: + pango_font_description_set_variant(descr, PANGO_VARIANT_SMALL_CAPS); + break; + + case SP_CSS_FONT_VARIANT_NORMAL: + default: + pango_font_description_set_variant(descr, PANGO_VARIANT_NORMAL); + break; + } + + // Check if not empty as Pango will add @ to string even if empty (bug in Pango?). + if (!style->font_variation_settings.axes.empty()) { + pango_font_description_set_variations(descr, style->font_variation_settings.toString().c_str()); + } + + return descr; +} + +/////////////////// helper functions + +static void noop(...) {} +//#define PANGO_DEBUG g_print +#define PANGO_DEBUG noop + + +///////////////////// FontFactory +#ifndef USE_PANGO_WIN32 +// the substitute function to tell fontconfig to enforce outline fonts +static void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/) +{ + FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue); + //char *fam = NULL; + //FcPatternGetString(pattern, "FC_FAMILY",0, &fam); + //printf("subst_f on %s\n",fam); +} +#endif + + +font_factory *font_factory::lUsine = nullptr; + +font_factory *font_factory::Default() +{ + if ( lUsine == nullptr ) lUsine = new font_factory; + return lUsine; +} + +font_factory::font_factory() : + nbEnt(0), // Note: this "ents" cache only keeps fonts from being unreffed, does not speed up access + maxEnt(32), + ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))), +#ifdef USE_PANGO_WIN32 + fontServer(pango_win32_font_map_for_display()), + pangoFontCache(pango_win32_font_map_get_font_cache(fontServer)), + hScreenDC(pango_win32_get_dc()), +#else + fontServer(pango_ft2_font_map_new()), +#endif + fontContext(pango_font_map_create_context(fontServer)), + fontSize(512), + loadedPtr(new FaceMapType()) +{ +#ifndef USE_PANGO_WIN32 + pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fontServer), + 72, 72); +#if PANGO_VERSION_CHECK(1,48,0) + pango_fc_font_map_set_default_substitute(PANGO_FC_FONT_MAP(fontServer), +#else + pango_ft2_font_map_set_default_substitute(PANGO_FT2_FONT_MAP(fontServer), +#endif + FactorySubstituteFunc, + this, + nullptr); +#endif + +} + +font_factory::~font_factory() +{ + for (int i = 0;i < nbEnt;i++) ents[i].f->Unref(); + if ( ents ) g_free(ents); + + g_object_unref(fontServer); +#ifdef USE_PANGO_WIN32 + pango_win32_shutdown_display(); +#else + //pango_ft2_shutdown_display(); +#endif + //g_object_unref(fontContext); + + if (loadedPtr) { + FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr); + delete tmp; + loadedPtr = nullptr; + } +} + + +Glib::ustring font_factory::ConstructFontSpecification(PangoFontDescription *font) +{ + Glib::ustring pangoString; + + g_assert(font); + + if (font) { + // Once the format for the font specification is decided, it must be + // kept.. if it is absolutely necessary to change it, the attribute + // it is written to needs to have a new version so the legacy files + // can be read. + + PangoFontDescription *copy = pango_font_description_copy(font); + + pango_font_description_unset_fields (copy, PANGO_FONT_MASK_SIZE); + char * copyAsString = pango_font_description_to_string(copy); + pangoString = copyAsString; + g_free(copyAsString); + copyAsString = nullptr; + + pango_font_description_free(copy); + + } + + return pangoString; +} + +Glib::ustring font_factory::ConstructFontSpecification(font_instance *font) +{ + Glib::ustring pangoString; + + g_assert(font); + + if (font) { + pangoString = ConstructFontSpecification(font->descr); + } + + return pangoString; +} + +/* + * Wrap calls to pango_font_description_get_family + * and replace some of the pango font names with generic css names + * http://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#generic-font-families + * + * This function should be called in place of pango_font_description_get_family() + */ +const char *sp_font_description_get_family(PangoFontDescription const *fontDescr) { + + static std::map<Glib::ustring, Glib::ustring> fontNameMap; + std::map<Glib::ustring, Glib::ustring>::iterator it; + + if (fontNameMap.empty()) { + fontNameMap.insert(std::make_pair("Sans", "sans-serif")); + fontNameMap.insert(std::make_pair("Serif", "serif")); + fontNameMap.insert(std::make_pair("Monospace", "monospace")); + } + + const char *pangoFamily = pango_font_description_get_family(fontDescr); + + if (pangoFamily && ((it = fontNameMap.find(pangoFamily)) != fontNameMap.end())) { + return (it->second).c_str(); + } + + return pangoFamily; +} + +Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr) +{ + Glib::ustring family; + + g_assert(fontDescr); + + if (fontDescr) { + // For now, keep it as family name taken from pango + const char *pangoFamily = sp_font_description_get_family(fontDescr); + + if( pangoFamily ) { + family = pangoFamily; + } + } + + return family; +} + +Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDescr) +{ + Glib::ustring style; + + g_assert(fontDescr); + + if (fontDescr) { + PangoFontDescription *fontDescrCopy = pango_font_description_copy(fontDescr); + + pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_FAMILY); + pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_SIZE); + + // For now, keep it as style name taken from pango + char *fontDescrAsString = pango_font_description_to_string(fontDescrCopy); + style = fontDescrAsString; + g_free(fontDescrAsString); + fontDescrAsString = nullptr; + pango_font_description_free(fontDescrCopy); + } + + return style; +} + + +///// + +// Calculate a Style "value" based on CSS values for ordering styles. +static int StyleNameValue( const Glib::ustring &style ) +{ + + PangoFontDescription *pfd = pango_font_description_from_string ( style.c_str() ); + int value = + pango_font_description_get_weight ( pfd ) * 1000000 + + pango_font_description_get_style ( pfd ) * 10000 + + pango_font_description_get_stretch( pfd ) * 100 + + pango_font_description_get_variant( pfd ); + pango_font_description_free ( pfd ); + return value; +} + +// Determines order in which styles are presented (sorted by CSS style values) +//static bool StyleNameCompareInternal(const StyleNames &style1, const StyleNames &style2) +//{ +// return( StyleNameValue( style1.CssName ) < StyleNameValue( style2.CssName ) ); +//} + +static gint StyleNameCompareInternalGlib(gconstpointer a, gconstpointer b) +{ + return( StyleNameValue( ((StyleNames *)a)->CssName ) < + StyleNameValue( ((StyleNames *)b)->CssName ) ? -1 : 1 ); +} + +static bool ustringPairSort(std::pair<PangoFontFamily*, Glib::ustring> const& first, std::pair<PangoFontFamily*, Glib::ustring> const& second) +{ + // well, this looks weird. + return first.second < second.second; +} + +void font_factory::GetUIFamilies(std::vector<PangoFontFamily *>& out) +{ + // Gather the family names as listed by Pango + PangoFontFamily** families = nullptr; + int numFamilies = 0; + pango_font_map_list_families(fontServer, &families, &numFamilies); + + std::vector<std::pair<PangoFontFamily *, Glib::ustring> > sorted; + + // not size_t + for (int currentFamily = 0; currentFamily < numFamilies; ++currentFamily) { + const char* displayName = pango_font_family_get_name(families[currentFamily]); + + if (displayName == nullptr || *displayName == '\0') { + std::cerr << "font_factory::GetUIFamilies: Missing displayName! " << std::endl; + continue; + } + if (!g_utf8_validate(displayName, -1, nullptr)) { + // TODO: can can do anything about this or does it always indicate broken fonts that should not be used? + std::cerr << "font_factory::GetUIFamilies: Illegal characters in displayName. "; + std::cerr << "Ignoring font '" << displayName << "'" << std::endl; + continue; + } + sorted.emplace_back(families[currentFamily], displayName); + } + + std::sort(sorted.begin(), sorted.end(), ustringPairSort); + + for (auto & i : sorted) { + out.push_back(i.first); + } +} + +GList* font_factory::GetUIStyles(PangoFontFamily * in) +{ + GList* ret = nullptr; + // Gather the styles for this family + PangoFontFace** faces = nullptr; + int numFaces = 0; + if (in == nullptr) { + 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++) { + + // If the face has a name, describe it, and then use the + // description to get the UI family and face strings + const gchar* displayName = pango_font_face_get_face_name(faces[currentFace]); + // std::cout << "Display Name: " << displayName << std::endl; + if (displayName == nullptr || *displayName == '\0') { + std::cerr << "font_factory::GetUIStyles: Missing displayName! " << std::endl; + continue; + } + + PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]); + if (faceDescr) { + 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 && + familyUIName.compare( "serif" ) != 0 && + familyUIName.compare( "monospace" ) != 0 && + familyUIName.compare( "fantasy" ) != 0 && + familyUIName.compare( "cursive" ) != 0 ) { + continue; + } + } + + // Pango breaks the 1 to 1 mapping between Pango weights and CSS weights by + // adding Semi-Light (as of 1.36.7), Book (as of 1.24), and Ultra-Heavy (as of + // 1.24). We need to map these weights to CSS weights. Book and Ultra-Heavy + // are rarely used. Semi-Light (350) is problematic as it is halfway between + // Light (300) and Normal (400) and if care is not taken it is converted to + // Normal, rather than Light. + // + // Note: The ultimate solution to handling various weight in the same + // font family is to support the @font rules from CSS. + // + // Additional notes, helpful for debugging: + // Pango's FC backend: + // Weights defined in fontconfig/fontconfig.h + // String equivalents in src/fcfreetype.c + // Weight set from os2->usWeightClass + // Use Fontforge: Element->Font Info...->OS/2->Misc->Weight Class to check font weight + size_t f = styleUIName.find( "Book" ); + if( f != Glib::ustring::npos ) { + styleUIName.replace( f, 4, "Normal" ); + } + f = styleUIName.find( "Semi-Light" ); + if( f != Glib::ustring::npos ) { + styleUIName.replace( f, 10, "Light" ); + } + f = styleUIName.find( "Ultra-Heavy" ); + if( f != Glib::ustring::npos ) { + styleUIName.replace( f, 11, "Heavy" ); + } + + bool exists = false; + for(GList *temp = ret; temp; temp = temp->next) { + if( ((StyleNames*)temp->data)->CssName.compare( styleUIName ) == 0 ) { + exists = true; + std::cerr << "Warning: Font face with same CSS values already added: " + << familyUIName.raw() << " " << styleUIName.raw() + << " (" << ((StyleNames*)temp->data)->DisplayName.raw() + << ", " << displayName << ")" << std::endl; + break; + } + } + + if (!exists && !familyUIName.empty() && !styleUIName.empty()) { + // Add the style information + ret = g_list_append(ret, new StyleNames(styleUIName, displayName)); + } + } + pango_font_description_free(faceDescr); + } + g_free(faces); + + // Sort the style lists + ret = g_list_sort( ret, StyleNameCompareInternalGlib ); + return ret; +} + + +font_instance* font_factory::FaceFromStyle(SPStyle const *style) +{ + font_instance *font = nullptr; + + g_assert(style); + + if (style) { + + // First try to use the font specification if it is set + char const *val; + if (style->font_specification.set + && (val = style->font_specification.value()) + && val[0]) { + + font = FaceFromFontSpecification(val); + } + + // If that failed, try using the CSS information in the style + if (!font) { + PangoFontDescription* temp_descr = + ink_font_description_from_style(style); + font = Face(temp_descr); + pango_font_description_free(temp_descr); + } + } + + return font; +} + +font_instance *font_factory::FaceFromDescr(char const *family, char const *style) +{ + PangoFontDescription *temp_descr = pango_font_description_from_string(style); + pango_font_description_set_family(temp_descr,family); + font_instance *res = Face(temp_descr); + pango_font_description_free(temp_descr); + return res; +} + +font_instance* font_factory::FaceFromPangoString(char const *pangoString) +{ + font_instance *fontInstance = nullptr; + + g_assert(pangoString); + + if (pangoString) { + + // Create a font description from the string - this may fail or + // produce unexpected results if the string does not have a good format + PangoFontDescription *descr = pango_font_description_from_string(pangoString); + + if (descr) { + if (sp_font_description_get_family(descr) != nullptr) { + fontInstance = Face(descr); + } + pango_font_description_free(descr); + } + } + + return fontInstance; +} + +font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification) +{ + font_instance *font = nullptr; + + g_assert(fontSpecification); + + if (fontSpecification) { + // How the string is used to reconstruct a font depends on how it + // was constructed in ConstructFontSpecification. As it stands, + // the font specification is a pango-created string + font = FaceFromPangoString(fontSpecification); + } + + return font; +} + +font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) +{ +#ifdef USE_PANGO_WIN32 + // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init() + pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround) +#else + pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround) +#endif + + font_instance *res = nullptr; + + FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr); + if ( loadedFaces.find(descr) == loadedFaces.end() ) { + // not yet loaded + PangoFont *nFace = nullptr; + + // workaround for bug #1025565. + // fonts without families blow up Pango. + if (sp_font_description_get_family(descr) != nullptr) { + nFace = pango_font_map_load_font(fontServer,fontContext,descr); + } + else { + g_warning("%s", _("Ignoring font without family that will crash Pango")); + } + + if ( nFace ) { + // duplicate FcPattern, the hard way + res = new font_instance(); + // store the descr of the font we asked for, since this is the key where we intend + // to put the font_instance at in the unordered_map. the descr of the returned + // pangofont may differ from what was asked, so we don't know (at this + // point) whether loadedFaces[that_descr] is free or not (and overwriting + // an entry will bring deallocation problems) + res->descr = pango_font_description_copy(descr); + res->parent = this; + res->InstallFace(nFace); + if ( res->pFont == nullptr ) { + // failed to install face -> bitmap font + // printf("face failed\n"); + res->parent = nullptr; + delete res; + res = nullptr; + if ( canFail ) { + char *tc = pango_font_description_to_string(descr); + PANGO_DEBUG("falling back from %s to 'sans-serif' because InstallFace failed\n",tc); + g_free(tc); + pango_font_description_set_family(descr,"sans-serif"); + res = Face(descr,false); + } + } else { + loadedFaces[res->descr]=res; + res->Ref(); + AddInCache(res); + } + } else { + // no match + if ( canFail ) { + PANGO_DEBUG("falling back to 'sans-serif'\n"); + PangoFontDescription *new_descr = pango_font_description_new(); + pango_font_description_set_family(new_descr, "sans-serif"); + res = Face(new_descr, false); + pango_font_description_free(new_descr); + } else { + g_critical("Could not load any face for font '%s'.", pango_font_description_to_string(descr)); + } + } + + } else { + // already here + res = loadedFaces[descr]; + res->Ref(); + AddInCache(res); + } + if (res) { + res->InitTheFace(); + } + return res; +} + +// Not used, need to add variations if ever used. +// font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/) +// { +// // std::cout << "font_factory::Face(family, variant, style, weight, stretch,)" << std::endl; +// PangoFontDescription *temp_descr = pango_font_description_new(); +// pango_font_description_set_family(temp_descr,family); +// pango_font_description_set_weight(temp_descr,(PangoWeight)weight); +// pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch); +// pango_font_description_set_style(temp_descr,(PangoStyle)style); +// pango_font_description_set_variant(temp_descr,(PangoVariant)variant); +// font_instance *res = Face(temp_descr); +// pango_font_description_free(temp_descr); +// return res; +// } + +void font_factory::UnrefFace(font_instance *who) +{ + if ( who ) { + FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr); + + if ( loadedFaces.find(who->descr) == loadedFaces.end() ) { + // not found + char *tc = pango_font_description_to_string(who->descr); + g_warning("unrefFace %p=%s: failed\n",who,tc); + g_free(tc); + } else { + loadedFaces.erase(loadedFaces.find(who->descr)); + // printf("unrefFace %p: success\n",who); + } + } +} + +void font_factory::AddInCache(font_instance *who) +{ + if ( who == nullptr ) return; + for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9; + for (int i = 0;i < nbEnt;i++) { + if ( ents[i].f == who ) { + // printf("present\n"); + ents[i].age += 1.0; + return; + } + } + if ( nbEnt > maxEnt ) { + printf("cache sur-plein?\n"); + return; + } + who->Ref(); + if ( nbEnt == maxEnt ) { // cache is filled, unref the oldest-accessed font in it + int bi = 0; + double ba = ents[bi].age; + for (int i = 1;i < nbEnt;i++) { + if ( ents[i].age < ba ) { + bi = i; + ba = ents[bi].age; + } + } + ents[bi].f->Unref(); + ents[bi]=ents[--nbEnt]; + } + ents[nbEnt].f = who; + ents[nbEnt].age = 1.0; + nbEnt++; +} + +# ifdef _WIN32 +void font_factory::AddFontFilesWin32(char const * directory_path ) +{ + std::vector<const char *> allowed_ext = {"ttf","otf" }; + std::vector<Glib::ustring> files = {}; + Inkscape::IO::Resource::get_filenames_from_path(files,directory_path, allowed_ext,(std::vector<const char *>) {}); + for (auto file : files) { + int result = AddFontResourceExA(file.c_str(),FR_PRIVATE,0); + if (result != 0 ) { + g_info("Font File: %s added sucessfully.",file.c_str()); + } else { + g_warning("Font File: %s wasn't added sucessfully",file.c_str()); + } + } +} +# endif + +void font_factory::AddFontsDir(char const *utf8dir) +{ +#ifdef USE_PANGO_WIN32 + g_info("Adding additional font directories only supported for fontconfig backend."); +#else + if (!Inkscape::IO::file_test(utf8dir, G_FILE_TEST_IS_DIR)) { + g_info("Fonts dir '%s' does not exist and will be ignored.", utf8dir); + return; + } + + gchar *dir; +# ifdef _WIN32 + AddFontFilesWin32(utf8dir); + dir = g_win32_locale_filename_from_utf8(utf8dir); +# else + dir = g_filename_from_utf8(utf8dir, -1, nullptr, nullptr, nullptr); +# endif + + FcConfig *conf = nullptr; + conf = pango_fc_font_map_get_config(PANGO_FC_FONT_MAP(fontServer)); + FcBool res = FcConfigAppFontAddDir(conf, (FcChar8 const *)dir); + if (res == FcTrue) { + g_info("Fonts dir '%s' added successfully.", utf8dir); + pango_fc_font_map_config_changed(PANGO_FC_FONT_MAP(fontServer)); + } else { + g_warning("Could not add fonts dir '%s'.", utf8dir); + } + + g_free(dir); +#endif +} + +void font_factory::AddFontFile(char const *utf8file) +{ +#ifdef USE_PANGO_WIN32 + g_info("Adding additional font only supported for fontconfig backend."); +#else + if (!Inkscape::IO::file_test(utf8file, G_FILE_TEST_IS_REGULAR)) { + g_warning("Font file '%s' does not exist and will be ignored.", utf8file); + return; + } + + gchar *file; +# ifdef _WIN32 + file = g_win32_locale_filename_from_utf8(utf8file); +# else + file = g_filename_from_utf8(utf8file, -1, nullptr, nullptr, nullptr); +# endif + + FcConfig *conf = nullptr; + conf = pango_fc_font_map_get_config(PANGO_FC_FONT_MAP(fontServer)); + FcBool res = FcConfigAppFontAddFile(conf, (FcChar8 const *)file); + if (res == FcTrue) { + g_info("Font file '%s' added successfully.", utf8file); + pango_fc_font_map_config_changed(PANGO_FC_FONT_MAP(fontServer)); + } else { + g_warning("Could not add font file '%s'.", utf8file); + } + + g_free(file); +#endif +} + +/* + 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 : |