/* * Authors: * * Copyright (C) 2012 Authors * * Released under GNU GPL. Read the file 'COPYING' for more information. */ #ifdef HAVE_CONFIG_H # include #endif #include #include "font-substitution.h" #include "inkscape.h" #include "desktop.h" #include "document.h" #include "selection.h" #include "dialogs/dialog-events.h" #include "desktop-handles.h" #include "selection-chemistry.h" #include "preferences.h" #include "xml/repr.h" #include "sp-defs.h" #include "sp-text.h" #include "sp-textpath.h" #include "sp-flowtext.h" #include "sp-flowdiv.h" #include "sp-tspan.h" #include "sp-tref.h" #include "style.h" #include "text-editing.h" #include "libnrtype/FontFactory.h" #include "libnrtype/font-instance.h" namespace Inkscape { namespace UI { namespace Dialog { FontSubstitution::FontSubstitution() { } FontSubstitution::~FontSubstitution() { } void FontSubstitution::checkFontSubstitutions(SPDocument* doc) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int show_dlg = prefs->getInt("/options/font/substitutedlg", 0); if (show_dlg) { Glib::ustring out; GSList *l = getFontReplacedItems(doc, &out); if (out.length() > 0) { show(out, l); } if (l) { g_slist_free(l); l = NULL; } } } void FontSubstitution::show(Glib::ustring out, GSList *l) { Gtk::MessageDialog warning(_("\nSome fonts are not available and have been substituted."), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); warning.set_resizable(true); warning.set_title(_("Font substitution")); GtkWidget *dlg = GTK_WIDGET(warning.gobj()); sp_transientize(dlg); Gtk::TextView * textview = new Gtk::TextView(); textview->set_editable(false); textview->set_wrap_mode(Gtk::WRAP_WORD); textview->show(); textview->get_buffer()->set_text(_(out.c_str())); Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow(); scrollwindow->add(*textview); scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scrollwindow->set_shadow_type(Gtk::SHADOW_IN); scrollwindow->set_size_request(0, 100); scrollwindow->show(); Gtk::CheckButton *cbSelect = new Gtk::CheckButton(); cbSelect->set_label(_("Select all the affected items")); cbSelect->set_active(true); cbSelect->show(); Gtk::CheckButton *cbWarning = new Gtk::CheckButton(); cbWarning->set_label(_("Don't show this warning again")); cbWarning->show(); #if GTK_CHECK_VERSION(3,0,0) Gtk::Box * box = warning.get_content_area(); #else Gtk::Box * box = warning.get_vbox(); #endif box->set_spacing(2); box->pack_start(*scrollwindow, true, true, 4); box->pack_start(*cbSelect, false, false, 0); box->pack_start(*cbWarning, false, false, 0); warning.run(); if (cbWarning->get_active()) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt("/options/font/substitutedlg", 0); } if (cbSelect->get_active()) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::Selection *selection = sp_desktop_selection (desktop); selection->clear(); selection->setList(l); } } /* * Find all the fonts that are in the document but not available on the users system * and have been substituted for other fonts * * Return a list of SPItems where fonts have been substituted. * * Walk thru all the objects ... * a. Build up a list of the objects with fonts defined in the style attribute * b. Build up a list of the objects rendered fonts - taken for the objects layout/spans * If there are fonts in a. that are not in b. then those fonts have been substituted. */ GSList * FontSubstitution::getFontReplacedItems(SPDocument* doc, Glib::ustring *out) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; GSList *allList = NULL; GSList *outList = NULL; std::set setErrors; std::set setFontSpans; std::map mapFontStyles; allList = get_all_items(NULL, doc->getRoot(), desktop, false, false, true, NULL); for (GSList *i = allList; i != NULL; i = i->next) { SPItem *item = SP_ITEM(i->data); SPStyle *style = item->style; Glib::ustring family = ""; if (is_top_level_text_object (item)) { // Should only need to check the first span, since the others should be covered by TSPAN's etc family = te_get_layout(item)->getFontFamily(0); setFontSpans.insert(family); } else if (SP_IS_TEXTPATH(item)) { SPTextPath const *textpath = SP_TEXTPATH(item); if (textpath->originalPath != NULL) { family = SP_TEXT(item->parent)->layout.getFontFamily(0); setFontSpans.insert(family); } } else if (SP_IS_TSPAN(item) || SP_IS_FLOWTSPAN(item)) { // is_part_of_text_subtree (item) // TSPAN layout comes from the parent->layout->_spans SPObject *parent_text = item; while (parent_text && !SP_IS_TEXT(parent_text)) { parent_text = parent_text->parent; } if (parent_text != NULL) { family = SP_TEXT(parent_text)->layout.getFontFamily(0); // Add all the spans fonts to the set gint ii = 0; for (SPObject *child = parent_text->firstChild() ; child ; child = child->getNext() ) { family = SP_TEXT(parent_text)->layout.getFontFamily(ii); setFontSpans.insert(family); ii++; } } } if (style && style->text) { gchar const *style_font = NULL; if (style->text->font_family.set) style_font = style->text->font_family.value; else if (style->text->font_specification.set) style_font = style->text->font_specification.value; else if (style->text->font_family.value) style_font = style->text->font_family.value; else if (style->text->font_specification.value) style_font = style->text->font_specification.value; if (style_font) { if (has_visible_text(item)) { mapFontStyles.insert(std::make_pair (item, style_font)); } } } } // Check if any document styles are not in the actual layout std::map::const_iterator mapIter; for (mapIter = mapFontStyles.begin(); mapIter != mapFontStyles.end(); ++mapIter) { SPItem *item = mapIter->first; Glib::ustring fonts = mapIter->second; // CSS font fallbacks can have more that one font listed, split the font list std::vector vFonts = Glib::Regex::split_simple("," , fonts); bool fontFound = false; for(size_t i=0; i::const_iterator iter = setFontSpans.find(font); if (iter != setFontSpans.end() || font == Glib::ustring("sans-serif") || font == Glib::ustring("Sans")) { fontFound = true; break; } } if (fontFound == false) { Glib::ustring subName = getSubstituteFontName(fonts); Glib::ustring err = Glib::ustring::compose( "Font '%1' substituted with '%2'", fonts.c_str(), subName.c_str()); setErrors.insert(err); outList = g_slist_prepend (outList, item); } } std::set::const_iterator setIter; for (setIter = setErrors.begin(); setIter != setErrors.end(); ++setIter) { Glib::ustring err = (*setIter); out->append(err + "\n"); g_warning("%s", err.c_str()); } return outList; } Glib::ustring FontSubstitution::getSubstituteFontName (Glib::ustring font) { Glib::ustring out = font; PangoFontDescription *descr = pango_font_description_new(); pango_font_description_set_family(descr,font.c_str()); font_instance *res = (font_factory::Default())->Face(descr); if (res->pFont) { PangoFontDescription *nFaceDesc = pango_font_describe(res->pFont); out = pango_font_description_get_family(nFaceDesc); } pango_font_description_free(descr); return out; } } // namespace Dialog } // namespace UI } // namespace Inkscape /* 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 :