summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/font-substitution.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/font-substitution.cpp')
-rw-r--r--src/ui/dialog/font-substitution.cpp241
1 files changed, 241 insertions, 0 deletions
diff --git a/src/ui/dialog/font-substitution.cpp b/src/ui/dialog/font-substitution.cpp
new file mode 100644
index 0000000..0e34b84
--- /dev/null
+++ b/src/ui/dialog/font-substitution.cpp
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <set>
+#include <vector>
+
+#include <glibmm/i18n.h>
+#include <glibmm/regex.h>
+#include <glibmm/ustring.h>
+
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/checkbutton.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/textview.h>
+
+#include "font-substitution.h"
+
+#include "desktop.h"
+#include "document.h"
+#include "inkscape.h"
+#include "selection-chemistry.h"
+#include "text-editing.h"
+
+#include "object/sp-item.h"
+#include "object/sp-root.h"
+#include "object/sp-text.h"
+#include "object/sp-textpath.h"
+#include "object/sp-flowdiv.h"
+#include "object/sp-tspan.h"
+
+#include "libnrtype/font-factory.h"
+#include "libnrtype/font-instance.h"
+
+#include "ui/dialog-events.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+namespace {
+
+void show(std::vector<SPItem*> const &list, Glib::ustring const &out)
+{
+ Gtk::MessageDialog warning(_("Some 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"));
+
+ sp_transientize(GTK_WIDGET(warning.gobj()));
+
+ Gtk::TextView 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;
+ 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;
+ cbSelect.set_label(_("Select all the affected items"));
+ cbSelect.set_active(true);
+ cbSelect.show();
+
+ Gtk::CheckButton cbWarning;
+ cbWarning.set_label(_("Don't show this warning again"));
+ cbWarning.show();
+
+ auto box = warning.get_content_area();
+ box->set_border_width(5);
+ 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::get()->setBool("/options/font/substitutedlg", false);
+ }
+
+ if (cbSelect.get_active()) {
+ auto desktop = SP_ACTIVE_DESKTOP;
+ auto selection = desktop->getSelection();
+ selection->clear();
+ selection->setList(list);
+ }
+}
+
+/*
+ * Find all the fonts that are in the document but not available on the user's system
+ * and have been substituted for other fonts.
+ *
+ * Return a list of SPItems where fonts have been substituted.
+ *
+ * Walk through 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.
+ */
+std::pair<std::vector<SPItem*>, Glib::ustring> getFontReplacedItems(SPDocument *doc)
+{
+ std::vector<SPItem*> outList;
+ std::set<Glib::ustring> setErrors;
+ std::set<Glib::ustring> setFontSpans;
+ std::map<SPItem*, Glib::ustring> mapFontStyles;
+ Glib::ustring out;
+
+ auto const allList = get_all_items(doc->getRoot(), SP_ACTIVE_DESKTOP, false, false, true);
+ for (auto item : allList) {
+ auto 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 (auto textpath = cast<SPTextPath>(item)) {
+ if (textpath->originalPath) {
+ family = cast<SPText>(item->parent)->layout.getFontFamily(0);
+ setFontSpans.insert(family);
+ }
+ }
+ else if (is<SPTSpan>(item) || is<SPFlowtspan>(item)) {
+ // is_part_of_text_subtree (item)
+ // TSPAN layout comes from the parent->layout->_spans
+ SPObject *parent_text = item;
+ while (parent_text && !is<SPText>(parent_text)) {
+ parent_text = parent_text->parent;
+ }
+ if (parent_text) {
+ family = cast<SPText>(parent_text)->layout.getFontFamily(0);
+ // Add all the spans fonts to the set
+ for (unsigned int f = 0; f < parent_text->children.size(); f++) {
+ family = cast<SPText>(parent_text)->layout.getFontFamily(f);
+ setFontSpans.insert(family);
+ }
+ }
+ }
+
+ if (style) {
+ char const *style_font = nullptr;
+ if (style->font_family.set) {
+ style_font = style->font_family.value();
+ } else if (style->font_specification.set) {
+ style_font = style->font_specification.value();
+ } else {
+ style_font = style->font_family.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
+ for (auto mapIter = mapFontStyles.rbegin(); mapIter != mapFontStyles.rend(); ++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<Glib::ustring> vFonts = Glib::Regex::split_simple("," , fonts);
+ bool fontFound = false;
+ for (auto const &font : vFonts) {
+ // trim whitespace
+ size_t startpos = font.find_first_not_of(" \n\r\t");
+ size_t endpos = font.find_last_not_of(" \n\r\t");
+ if (startpos == std::string::npos || endpos == std::string::npos) {
+ continue; // empty font name
+ }
+ auto const trimmed = font.substr(startpos, endpos - startpos + 1);
+ if (setFontSpans.find(trimmed) != setFontSpans.end() ||
+ trimmed == Glib::ustring("sans-serif") ||
+ trimmed == Glib::ustring("Sans") ||
+ trimmed == Glib::ustring("serif") ||
+ trimmed == Glib::ustring("Serif") ||
+ trimmed == Glib::ustring("monospace") ||
+ trimmed == Glib::ustring("Monospace"))
+ {
+ fontFound = true;
+ break;
+ }
+ }
+ if (!fontFound) {
+ 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.emplace_back(item);
+ }
+ }
+
+ for (auto const &err : setErrors) {
+ out.append(err + "\n");
+ g_warning("%s", err.c_str());
+ }
+
+ return std::make_pair(std::move(outList), std::move(out));
+}
+
+} // namespace
+
+void checkFontSubstitutions(SPDocument *doc)
+{
+ bool show_dlg = Inkscape::Preferences::get()->getBool("/options/font/substitutedlg");
+ if (!show_dlg) {
+ return;
+ }
+
+ auto [list, msg] = getFontReplacedItems(doc);
+ if (!msg.empty()) {
+ show(list, msg);
+ }
+}
+
+} // 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 :