diff options
Diffstat (limited to '')
-rw-r--r-- | src/ui/widget/font-variants.cpp | 1459 |
1 files changed, 1459 insertions, 0 deletions
diff --git a/src/ui/widget/font-variants.cpp b/src/ui/widget/font-variants.cpp new file mode 100644 index 0000000..3ec77c8 --- /dev/null +++ b/src/ui/widget/font-variants.cpp @@ -0,0 +1,1459 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2015, 2018 Tavmong Bah + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <gtkmm.h> +#include <glibmm/i18n.h> + +#include <libnrtype/font-instance.h> + +#include "font-variants.h" + +// For updating from selection +#include "desktop.h" +#include "object/sp-text.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + + // A simple class to handle UI for one feature. We could of derived this from Gtk::HBox but by + // attaching widgets directly to Gtk::Grid, we keep columns lined up (which may or may not be a + // good thing). + class Feature + { + public: + Feature( const Glib::ustring& name, OTSubstitution& glyphs, int options, Glib::ustring family, Gtk::Grid& grid, int &row, FontVariants* parent) + : _name (name) + { + Gtk::Label* table_name = Gtk::manage (new Gtk::Label()); + table_name->set_markup ("\"" + name + "\" "); + + grid.attach (*table_name, 0, row, 1, 1); + + Gtk::FlowBox* flow_box = nullptr; + Gtk::ScrolledWindow* scrolled_window = nullptr; + if (options > 2) { + // If there are more than 2 option, pack them into a flowbox instead of directly putting them in the grid. + // Some fonts might have a table with many options (Bungee Hairline table 'ornm' has 113 entries). + flow_box = Gtk::manage (new Gtk::FlowBox()); + flow_box->set_selection_mode(); // Turn off selection + flow_box->set_homogeneous(); + flow_box->set_max_children_per_line (100); // Override default value + flow_box->set_min_children_per_line (10); // Override default value + + // We pack this into a scrollbar... otherwise the minimum height is set to what is required to fit all + // flow box children into the flow box when the flow box has minimum width. (Crazy if you ask me!) + scrolled_window = Gtk::manage (new Gtk::ScrolledWindow()); + scrolled_window->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + scrolled_window->add(*flow_box); + } + + Gtk::RadioButton::Group group; + for (int i = 0; i < options; ++i) { + + // Create radio button and create or add to button group. + Gtk::RadioButton* button = Gtk::manage (new Gtk::RadioButton()); + if (i == 0) { + group = button->get_group(); + } else { + button->set_group (group); + } + button->signal_clicked().connect ( sigc::mem_fun(*parent, &FontVariants::feature_callback) ); + buttons.push_back (button); + + // Create label. + Gtk::Label* label = Gtk::manage (new Gtk::Label()); + + // Restrict label width (some fonts have lots of alternatives). + label->set_line_wrap( true ); + label->set_line_wrap_mode( Pango::WRAP_WORD_CHAR ); + label->set_ellipsize( Pango::ELLIPSIZE_END ); + label->set_lines(3); + label->set_hexpand(); + + Glib::ustring markup; + markup += "<span font_family='"; + markup += family; + markup += "' font_features='"; + markup += name; + markup += " "; + markup += std::to_string (i); + markup += "'>"; + markup += Glib::Markup::escape_text (glyphs.input); + markup += "</span>"; + label->set_markup (markup); + + // Add button and label to widget + if (!flow_box) { + // Attach directly to grid (keeps things aligned row-to-row). + grid.attach (*button, 2*i+1, row, 1, 1); + grid.attach (*label, 2*i+2, row, 1, 1); + } else { + // Pack into FlowBox + + // Pack button and label into a box so they stay together. + Gtk::Box* box = Gtk::manage (new Gtk::Box()); + box->add(*button); + box->add(*label); + + flow_box->add(*box); + } + } + + if (scrolled_window) { + grid.attach (*scrolled_window, 1, row, 4, 1); + } + } + + Glib::ustring + get_css() + { + int i = 0; + for (auto b: buttons) { + if (b->get_active()) { + if (i == 0) { + // Features are always off by default (for those handled here). + return ""; + } else if (i == 1) { + // Feature without value has implied value of 1. + return ("\"" + _name + "\", "); + } else { + // Feature with value greater than 1 must be explicitly set. + return ("\"" + _name + "\" " + std::to_string (i) + ", "); + } + } + ++i; + } + return ""; + } + + void + set_active(int i) + { + if (i < buttons.size()) { + buttons[i]->set_active(); + } + } + + private: + Glib::ustring _name; + std::vector <Gtk::RadioButton*> buttons; + }; + + FontVariants::FontVariants () : + Gtk::Box (Gtk::ORIENTATION_VERTICAL), + _ligatures_frame ( Glib::ustring(C_("Font feature", "Ligatures" )) ), + _ligatures_common ( Glib::ustring(C_("Font feature", "Common" )) ), + _ligatures_discretionary ( Glib::ustring(C_("Font feature", "Discretionary")) ), + _ligatures_historical ( Glib::ustring(C_("Font feature", "Historical" )) ), + _ligatures_contextual ( Glib::ustring(C_("Font feature", "Contextual" )) ), + + _position_frame ( Glib::ustring(C_("Font feature", "Position" )) ), + _position_normal ( Glib::ustring(C_("Font feature", "Normal" )) ), + _position_sub ( Glib::ustring(C_("Font feature", "Subscript" )) ), + _position_super ( Glib::ustring(C_("Font feature", "Superscript" )) ), + + _caps_frame ( Glib::ustring(C_("Font feature", "Capitals" )) ), + _caps_normal ( Glib::ustring(C_("Font feature", "Normal" )) ), + _caps_small ( Glib::ustring(C_("Font feature", "Small" )) ), + _caps_all_small ( Glib::ustring(C_("Font feature", "All small" )) ), + _caps_petite ( Glib::ustring(C_("Font feature", "Petite" )) ), + _caps_all_petite ( Glib::ustring(C_("Font feature", "All petite" )) ), + _caps_unicase ( Glib::ustring(C_("Font feature", "Unicase" )) ), + _caps_titling ( Glib::ustring(C_("Font feature", "Titling" )) ), + + _numeric_frame ( Glib::ustring(C_("Font feature", "Numeric" )) ), + _numeric_lining ( Glib::ustring(C_("Font feature", "Lining" )) ), + _numeric_old_style ( Glib::ustring(C_("Font feature", "Old Style" )) ), + _numeric_default_style ( Glib::ustring(C_("Font feature", "Default Style")) ), + _numeric_proportional ( Glib::ustring(C_("Font feature", "Proportional" )) ), + _numeric_tabular ( Glib::ustring(C_("Font feature", "Tabular" )) ), + _numeric_default_width ( Glib::ustring(C_("Font feature", "Default Width")) ), + _numeric_diagonal ( Glib::ustring(C_("Font feature", "Diagonal" )) ), + _numeric_stacked ( Glib::ustring(C_("Font feature", "Stacked" )) ), + _numeric_default_fractions( Glib::ustring(C_("Font feature", "Default Fractions")) ), + _numeric_ordinal ( Glib::ustring(C_("Font feature", "Ordinal" )) ), + _numeric_slashed_zero ( Glib::ustring(C_("Font feature", "Slashed Zero" )) ), + + _asian_frame ( Glib::ustring(C_("Font feature", "East Asian" )) ), + _asian_default_variant ( Glib::ustring(C_("Font feature", "Default" )) ), + _asian_jis78 ( Glib::ustring(C_("Font feature", "JIS78" )) ), + _asian_jis83 ( Glib::ustring(C_("Font feature", "JIS83" )) ), + _asian_jis90 ( Glib::ustring(C_("Font feature", "JIS90" )) ), + _asian_jis04 ( Glib::ustring(C_("Font feature", "JIS04" )) ), + _asian_simplified ( Glib::ustring(C_("Font feature", "Simplified" )) ), + _asian_traditional ( Glib::ustring(C_("Font feature", "Traditional" )) ), + _asian_default_width ( Glib::ustring(C_("Font feature", "Default" )) ), + _asian_full_width ( Glib::ustring(C_("Font feature", "Full Width" )) ), + _asian_proportional_width ( Glib::ustring(C_("Font feature", "Proportional" )) ), + _asian_ruby ( Glib::ustring(C_("Font feature", "Ruby" )) ), + + _feature_frame ( Glib::ustring(C_("Font feature", "Feature Settings")) ), + _feature_label ( Glib::ustring(C_("Font feature", "Selection has different Feature Settings!")) ), + + _ligatures_changed( false ), + _position_changed( false ), + _caps_changed( false ), + _numeric_changed( false ), + _asian_changed( false ), + _feature_vbox(Gtk::ORIENTATION_VERTICAL) + + { + + set_name ( "FontVariants" ); + + // Ligatures -------------------------- + + // Add tooltips + _ligatures_common.set_tooltip_text( + _("Common ligatures. On by default. OpenType tables: 'liga', 'clig'")); + _ligatures_discretionary.set_tooltip_text( + _("Discretionary ligatures. Off by default. OpenType table: 'dlig'")); + _ligatures_historical.set_tooltip_text( + _("Historical ligatures. Off by default. OpenType table: 'hlig'")); + _ligatures_contextual.set_tooltip_text( + _("Contextual forms. On by default. OpenType table: 'calt'")); + + // Add signals + _ligatures_common.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + _ligatures_discretionary.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + _ligatures_historical.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + _ligatures_contextual.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + + // Restrict label widths (some fonts have lots of ligatures). Must also set ellipsize mode. + Gtk::Label* labels[] = { + &_ligatures_label_common, + &_ligatures_label_discretionary, + &_ligatures_label_historical, + &_ligatures_label_contextual + }; + for (auto label : labels) { + // char limit - not really needed, since number of lines is restricted + label->set_max_width_chars(999); + // show ellipsis when text overflows + label->set_ellipsize(Pango::ELLIPSIZE_END); + // up to 5 lines + label->set_lines(5); + // multiline + label->set_line_wrap(); + // break it as needed + label->set_line_wrap_mode(Pango::WRAP_WORD_CHAR); + } + + // Allow user to select characters. Not useful as this selects the ligatures. + // _ligatures_label_common.set_selectable( true ); + // _ligatures_label_discretionary.set_selectable( true ); + // _ligatures_label_historical.set_selectable( true ); + // _ligatures_label_contextual.set_selectable( true ); + + // Add to frame + _ligatures_grid.attach( _ligatures_common, 0, 0, 1, 1); + _ligatures_grid.attach( _ligatures_discretionary, 0, 1, 1, 1); + _ligatures_grid.attach( _ligatures_historical, 0, 2, 1, 1); + _ligatures_grid.attach( _ligatures_contextual, 0, 3, 1, 1); + _ligatures_grid.attach( _ligatures_label_common, 1, 0, 1, 1); + _ligatures_grid.attach( _ligatures_label_discretionary, 1, 1, 1, 1); + _ligatures_grid.attach( _ligatures_label_historical, 1, 2, 1, 1); + _ligatures_grid.attach( _ligatures_label_contextual, 1, 3, 1, 1); + + _ligatures_grid.set_margin_start(15); + _ligatures_grid.set_margin_end(15); + + _ligatures_frame.add( _ligatures_grid ); + pack_start( _ligatures_frame, Gtk::PACK_SHRINK ); + + ligatures_init(); + + // Position ---------------------------------- + + // Add tooltips + _position_normal.set_tooltip_text( _("Normal position.")); + _position_sub.set_tooltip_text( _("Subscript. OpenType table: 'subs'") ); + _position_super.set_tooltip_text( _("Superscript. OpenType table: 'sups'") ); + + // Group buttons + Gtk::RadioButton::Group position_group = _position_normal.get_group(); + _position_sub.set_group(position_group); + _position_super.set_group(position_group); + + // Add signals + _position_normal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); + _position_sub.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); + _position_super.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); + + // Add to frame + _position_grid.attach( _position_normal, 0, 0, 1, 1); + _position_grid.attach( _position_sub, 1, 0, 1, 1); + _position_grid.attach( _position_super, 2, 0, 1, 1); + + _position_grid.set_margin_start(15); + _position_grid.set_margin_end(15); + + _position_frame.add( _position_grid ); + pack_start( _position_frame, Gtk::PACK_SHRINK ); + + position_init(); + + // Caps ---------------------------------- + + // Add tooltips + _caps_normal.set_tooltip_text( _("Normal capitalization.")); + _caps_small.set_tooltip_text( _("Small-caps (lowercase). OpenType table: 'smcp'")); + _caps_all_small.set_tooltip_text( _("All small-caps (uppercase and lowercase). OpenType tables: 'c2sc' and 'smcp'")); + _caps_petite.set_tooltip_text( _("Petite-caps (lowercase). OpenType table: 'pcap'")); + _caps_all_petite.set_tooltip_text( _("All petite-caps (uppercase and lowercase). OpenType tables: 'c2sc' and 'pcap'")); + _caps_unicase.set_tooltip_text( _("Unicase (small caps for uppercase, normal for lowercase). OpenType table: 'unic'")); + _caps_titling.set_tooltip_text( _("Titling caps (lighter-weight uppercase for use in titles). OpenType table: 'titl'")); + + // Group buttons + Gtk::RadioButton::Group caps_group = _caps_normal.get_group(); + _caps_small.set_group(caps_group); + _caps_all_small.set_group(caps_group); + _caps_petite.set_group(caps_group); + _caps_all_petite.set_group(caps_group); + _caps_unicase.set_group(caps_group); + _caps_titling.set_group(caps_group); + + // Add signals + _caps_normal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_small.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_all_small.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_petite.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_all_petite.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_unicase.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_titling.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + + // Add to frame + _caps_grid.attach( _caps_normal, 0, 0, 1, 1); + _caps_grid.attach( _caps_unicase, 1, 0, 1, 1); + _caps_grid.attach( _caps_titling, 2, 0, 1, 1); + _caps_grid.attach( _caps_small, 0, 1, 1, 1); + _caps_grid.attach( _caps_all_small, 1, 1, 1, 1); + _caps_grid.attach( _caps_petite, 2, 1, 1, 1); + _caps_grid.attach( _caps_all_petite, 3, 1, 1, 1); + + _caps_grid.set_margin_start(15); + _caps_grid.set_margin_end(15); + + _caps_frame.add( _caps_grid ); + pack_start( _caps_frame, Gtk::PACK_SHRINK ); + + caps_init(); + + // Numeric ------------------------------ + + // Add tooltips + _numeric_default_style.set_tooltip_text( _("Normal style.")); + _numeric_lining.set_tooltip_text( _("Lining numerals. OpenType table: 'lnum'")); + _numeric_old_style.set_tooltip_text( _("Old style numerals. OpenType table: 'onum'")); + _numeric_default_width.set_tooltip_text( _("Normal widths.")); + _numeric_proportional.set_tooltip_text( _("Proportional width numerals. OpenType table: 'pnum'")); + _numeric_tabular.set_tooltip_text( _("Same width numerals. OpenType table: 'tnum'")); + _numeric_default_fractions.set_tooltip_text( _("Normal fractions.")); + _numeric_diagonal.set_tooltip_text( _("Diagonal fractions. OpenType table: 'frac'")); + _numeric_stacked.set_tooltip_text( _("Stacked fractions. OpenType table: 'afrc'")); + _numeric_ordinal.set_tooltip_text( _("Ordinals (raised 'th', etc.). OpenType table: 'ordn'")); + _numeric_slashed_zero.set_tooltip_text( _("Slashed zeros. OpenType table: 'zero'")); + + // Group buttons + Gtk::RadioButton::Group style_group = _numeric_default_style.get_group(); + _numeric_lining.set_group(style_group); + _numeric_old_style.set_group(style_group); + + Gtk::RadioButton::Group width_group = _numeric_default_width.get_group(); + _numeric_proportional.set_group(width_group); + _numeric_tabular.set_group(width_group); + + Gtk::RadioButton::Group fraction_group = _numeric_default_fractions.get_group(); + _numeric_diagonal.set_group(fraction_group); + _numeric_stacked.set_group(fraction_group); + + // Add signals + _numeric_default_style.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_lining.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_old_style.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_default_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_proportional.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_tabular.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_default_fractions.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_diagonal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_stacked.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_ordinal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_slashed_zero.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + + // Add to frame + _numeric_grid.attach (_numeric_default_style, 0, 0, 1, 1); + _numeric_grid.attach (_numeric_lining, 1, 0, 1, 1); + _numeric_grid.attach (_numeric_lining_label, 2, 0, 1, 1); + _numeric_grid.attach (_numeric_old_style, 3, 0, 1, 1); + _numeric_grid.attach (_numeric_old_style_label, 4, 0, 1, 1); + + _numeric_grid.attach (_numeric_default_width, 0, 1, 1, 1); + _numeric_grid.attach (_numeric_proportional, 1, 1, 1, 1); + _numeric_grid.attach (_numeric_proportional_label, 2, 1, 1, 1); + _numeric_grid.attach (_numeric_tabular, 3, 1, 1, 1); + _numeric_grid.attach (_numeric_tabular_label, 4, 1, 1, 1); + + _numeric_grid.attach (_numeric_default_fractions, 0, 2, 1, 1); + _numeric_grid.attach (_numeric_diagonal, 1, 2, 1, 1); + _numeric_grid.attach (_numeric_diagonal_label, 2, 2, 1, 1); + _numeric_grid.attach (_numeric_stacked, 3, 2, 1, 1); + _numeric_grid.attach (_numeric_stacked_label, 4, 2, 1, 1); + + _numeric_grid.attach (_numeric_ordinal, 0, 3, 1, 1); + _numeric_grid.attach (_numeric_ordinal_label, 1, 3, 4, 1); + + _numeric_grid.attach (_numeric_slashed_zero, 0, 4, 1, 1); + _numeric_grid.attach (_numeric_slashed_zero_label, 1, 4, 1, 1); + + _numeric_grid.set_margin_start(15); + _numeric_grid.set_margin_end(15); + + _numeric_frame.add( _numeric_grid ); + pack_start( _numeric_frame, Gtk::PACK_SHRINK ); + + // East Asian + + // Add tooltips + _asian_default_variant.set_tooltip_text ( _("Default variant.")); + _asian_jis78.set_tooltip_text( _("JIS78 forms. OpenType table: 'jp78'.")); + _asian_jis83.set_tooltip_text( _("JIS83 forms. OpenType table: 'jp83'.")); + _asian_jis90.set_tooltip_text( _("JIS90 forms. OpenType table: 'jp90'.")); + _asian_jis04.set_tooltip_text( _("JIS2004 forms. OpenType table: 'jp04'.")); + _asian_simplified.set_tooltip_text( _("Simplified forms. OpenType table: 'smpl'.")); + _asian_traditional.set_tooltip_text( _("Traditional forms. OpenType table: 'trad'.")); + _asian_default_width.set_tooltip_text ( _("Default width.")); + _asian_full_width.set_tooltip_text( _("Full width variants. OpenType table: 'fwid'.")); + _asian_proportional_width.set_tooltip_text(_("Proportional width variants. OpenType table: 'pwid'.")); + _asian_ruby.set_tooltip_text( _("Ruby variants. OpenType table: 'ruby'.")); + + // Add signals + _asian_default_variant.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_jis78.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_jis83.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_jis90.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_jis04.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_simplified.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_traditional.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_default_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_full_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_proportional_width.signal_clicked().connect (sigc::mem_fun(*this, &FontVariants::asian_callback) ); + _asian_ruby.signal_clicked().connect( sigc::mem_fun(*this, &FontVariants::asian_callback) ); + + // Add to frame + _asian_grid.attach (_asian_default_variant, 0, 0, 1, 1); + _asian_grid.attach (_asian_jis78, 1, 0, 1, 1); + _asian_grid.attach (_asian_jis83, 2, 0, 1, 1); + _asian_grid.attach (_asian_jis90, 1, 1, 1, 1); + _asian_grid.attach (_asian_jis04, 2, 1, 1, 1); + _asian_grid.attach (_asian_simplified, 1, 2, 1, 1); + _asian_grid.attach (_asian_traditional, 2, 2, 1, 1); + _asian_grid.attach (_asian_default_width, 0, 3, 1, 1); + _asian_grid.attach (_asian_full_width, 1, 3, 1, 1); + _asian_grid.attach (_asian_proportional_width, 2, 3, 1, 1); + _asian_grid.attach (_asian_ruby, 0, 4, 1, 1); + + _asian_grid.set_margin_start(15); + _asian_grid.set_margin_end(15); + + _asian_frame.add( _asian_grid ); + pack_start( _asian_frame, Gtk::PACK_SHRINK ); + + // Group Buttons + Gtk::RadioButton::Group asian_variant_group = _asian_default_variant.get_group(); + _asian_jis78.set_group(asian_variant_group); + _asian_jis83.set_group(asian_variant_group); + _asian_jis90.set_group(asian_variant_group); + _asian_jis04.set_group(asian_variant_group); + _asian_simplified.set_group(asian_variant_group); + _asian_traditional.set_group(asian_variant_group); + + Gtk::RadioButton::Group asian_width_group = _asian_default_width.get_group(); + _asian_full_width.set_group (asian_width_group); + _asian_proportional_width.set_group (asian_width_group); + + // Feature settings --------------------- + + // Add tooltips + _feature_entry.set_tooltip_text( _("Feature settings in CSS form (e.g. \"wxyz\" or \"wxyz\" 3).")); + + _feature_substitutions.set_justify( Gtk::JUSTIFY_LEFT ); + _feature_substitutions.set_line_wrap( true ); + _feature_substitutions.set_line_wrap_mode( Pango::WRAP_WORD_CHAR ); + + _feature_list.set_justify( Gtk::JUSTIFY_LEFT ); + _feature_list.set_line_wrap( true ); + + // Add to frame + _feature_vbox.pack_start( _feature_grid, Gtk::PACK_SHRINK ); + _feature_vbox.pack_start( _feature_entry, Gtk::PACK_SHRINK ); + _feature_vbox.pack_start( _feature_label, Gtk::PACK_SHRINK ); + _feature_vbox.pack_start( _feature_substitutions, Gtk::PACK_SHRINK ); + _feature_vbox.pack_start( _feature_list, Gtk::PACK_SHRINK ); + + _feature_vbox.set_margin_start(15); + _feature_vbox.set_margin_end(15); + + _feature_frame.add( _feature_vbox ); + pack_start( _feature_frame, Gtk::PACK_SHRINK ); + + // Add signals + //_feature_entry.signal_key_press_event().connect ( sigc::mem_fun(*this, &FontVariants::feature_callback) ); + _feature_entry.signal_changed().connect( sigc::mem_fun(*this, &FontVariants::feature_callback) ); + + show_all_children(); + + } + + void + FontVariants::ligatures_init() { + // std::cout << "FontVariants::ligatures_init()" << std::endl; + } + + void + FontVariants::ligatures_callback() { + // std::cout << "FontVariants::ligatures_callback()" << std::endl; + _ligatures_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::position_init() { + // std::cout << "FontVariants::position_init()" << std::endl; + } + + void + FontVariants::position_callback() { + // std::cout << "FontVariants::position_callback()" << std::endl; + _position_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::caps_init() { + // std::cout << "FontVariants::caps_init()" << std::endl; + } + + void + FontVariants::caps_callback() { + // std::cout << "FontVariants::caps_callback()" << std::endl; + _caps_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::numeric_init() { + // std::cout << "FontVariants::numeric_init()" << std::endl; + } + + void + FontVariants::numeric_callback() { + // std::cout << "FontVariants::numeric_callback()" << std::endl; + _numeric_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::asian_init() { + // std::cout << "FontVariants::asian_init()" << std::endl; + } + + void + FontVariants::asian_callback() { + // std::cout << "FontVariants::asian_callback()" << std::endl; + _asian_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::feature_init() { + // std::cout << "FontVariants::feature_init()" << std::endl; + } + + void + FontVariants::feature_callback() { + // std::cout << "FontVariants::feature_callback()" << std::endl; + _feature_changed = true; + _changed_signal.emit(); + } + + // Update GUI based on query. + void + FontVariants::update( SPStyle const *query, bool different_features, Glib::ustring& font_spec ) { + + update_opentype( font_spec ); + + _ligatures_all = query->font_variant_ligatures.computed; + _ligatures_mix = query->font_variant_ligatures.value; + + _ligatures_common.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_COMMON ); + _ligatures_discretionary.set_active(_ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ); + _ligatures_historical.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ); + _ligatures_contextual.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL ); + + _ligatures_common.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_COMMON ); + _ligatures_discretionary.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ); + _ligatures_historical.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ); + _ligatures_contextual.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL ); + + _position_all = query->font_variant_position.computed; + _position_mix = query->font_variant_position.value; + + _position_normal.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_NORMAL ); + _position_sub.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_SUB ); + _position_super.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_SUPER ); + + _position_normal.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_NORMAL ); + _position_sub.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_SUB ); + _position_super.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_SUPER ); + + _caps_all = query->font_variant_caps.computed; + _caps_mix = query->font_variant_caps.value; + + _caps_normal.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_NORMAL ); + _caps_small.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_SMALL ); + _caps_all_small.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ); + _caps_petite.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_PETITE ); + _caps_all_petite.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ); + _caps_unicase.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_UNICASE ); + _caps_titling.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_TITLING ); + + _caps_normal.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_NORMAL ); + _caps_small.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_SMALL ); + _caps_all_small.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ); + _caps_petite.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_PETITE ); + _caps_all_petite.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ); + _caps_unicase.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_UNICASE ); + _caps_titling.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_TITLING ); + + _numeric_all = query->font_variant_numeric.computed; + _numeric_mix = query->font_variant_numeric.value; + + if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS) { + _numeric_lining.set_active(); + } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS) { + _numeric_old_style.set_active(); + } else { + _numeric_default_style.set_active(); + } + + if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS) { + _numeric_proportional.set_active(); + } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS) { + _numeric_tabular.set_active(); + } else { + _numeric_default_width.set_active(); + } + + if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS) { + _numeric_diagonal.set_active(); + } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS) { + _numeric_stacked.set_active(); + } else { + _numeric_default_fractions.set_active(); + } + + _numeric_ordinal.set_active( _numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ); + _numeric_slashed_zero.set_active( _numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ); + + + _numeric_lining.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS ); + _numeric_old_style.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS ); + _numeric_proportional.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS ); + _numeric_tabular.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS ); + _numeric_diagonal.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS ); + _numeric_stacked.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS ); + _numeric_ordinal.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ); + _numeric_slashed_zero.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ); + + _asian_all = query->font_variant_east_asian.computed; + _asian_mix = query->font_variant_east_asian.value; + + if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78) { + _asian_jis78.set_active(); + } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83) { + _asian_jis83.set_active(); + } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90) { + _asian_jis90.set_active(); + } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04) { + _asian_jis04.set_active(); + } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED) { + _asian_simplified.set_active(); + } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL) { + _asian_traditional.set_active(); + } else { + _asian_default_variant.set_active(); + } + + if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH) { + _asian_full_width.set_active(); + } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH) { + _asian_proportional_width.set_active(); + } else { + _asian_default_width.set_active(); + } + + _asian_ruby.set_active ( _asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY ); + + _asian_jis78.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78); + _asian_jis83.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83); + _asian_jis90.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90); + _asian_jis04.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04); + _asian_simplified.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED); + _asian_traditional.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL); + _asian_full_width.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH); + _asian_proportional_width.set_inconsistent(_asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH); + _asian_ruby.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY); + + // Fix me: Should match a space if second part matches. ---, + // : Add boundary to 'on' and 'off'. v + Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("\"(\\w{4})\"\\s*([0-9]+|on|off|)"); + Glib::MatchInfo matchInfo; + std::string setting; + + // Set feature radiobutton (if it exists) or add to _feature_entry string. + char const *val = query->font_feature_settings.value(); + if (val) { + + std::vector<Glib::ustring> tokens = + Glib::Regex::split_simple("\\s*,\\s*", val); + + for (auto token: tokens) { + regex->match(token, matchInfo); + if (matchInfo.matches()) { + Glib::ustring table = matchInfo.fetch(1); + Glib::ustring value = matchInfo.fetch(2); + + if (_features.find(table) != _features.end()) { + int v = 0; + if (value == "0" || value == "off") v = 0; + else if (value == "1" || value == "on" || value.empty() ) v = 1; + else v = std::stoi(value); + _features[table]->set_active(v); + } else { + setting += token + ", "; + } + } + } + } + + // Remove final ", " + if (setting.length() > 1) { + setting.pop_back(); + setting.pop_back(); + } + + // Tables without radiobuttons. + _feature_entry.set_text( setting ); + + if( different_features ) { + _feature_label.show(); + } else { + _feature_label.hide(); + } + } + + // Update GUI based on OpenType tables of selected font (which may be changed in font selector tab). + void + FontVariants::update_opentype (Glib::ustring& font_spec) { + + // Disable/Enable based on available OpenType tables. + font_instance* res = font_factory::Default()->FaceFromFontSpecification( font_spec.c_str() ); + if( res ) { + + std::map<Glib::ustring, OTSubstitution>::iterator it; + + if((it = res->openTypeTables.find("liga"))!= res->openTypeTables.end() || + (it = res->openTypeTables.find("clig"))!= res->openTypeTables.end()) { + _ligatures_common.set_sensitive(); + } else { + _ligatures_common.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("dlig"))!= res->openTypeTables.end()) { + _ligatures_discretionary.set_sensitive(); + } else { + _ligatures_discretionary.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("hlig"))!= res->openTypeTables.end()) { + _ligatures_historical.set_sensitive(); + } else { + _ligatures_historical.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("calt"))!= res->openTypeTables.end()) { + _ligatures_contextual.set_sensitive(); + } else { + _ligatures_contextual.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("subs"))!= res->openTypeTables.end()) { + _position_sub.set_sensitive(); + } else { + _position_sub.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("sups"))!= res->openTypeTables.end()) { + _position_super.set_sensitive(); + } else { + _position_super.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("smcp"))!= res->openTypeTables.end()) { + _caps_small.set_sensitive(); + } else { + _caps_small.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("c2sc"))!= res->openTypeTables.end() && + (it = res->openTypeTables.find("smcp"))!= res->openTypeTables.end()) { + _caps_all_small.set_sensitive(); + } else { + _caps_all_small.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("pcap"))!= res->openTypeTables.end()) { + _caps_petite.set_sensitive(); + } else { + _caps_petite.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("c2sc"))!= res->openTypeTables.end() && + (it = res->openTypeTables.find("pcap"))!= res->openTypeTables.end()) { + _caps_all_petite.set_sensitive(); + } else { + _caps_all_petite.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("unic"))!= res->openTypeTables.end()) { + _caps_unicase.set_sensitive(); + } else { + _caps_unicase.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("titl"))!= res->openTypeTables.end()) { + _caps_titling.set_sensitive(); + } else { + _caps_titling.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("lnum"))!= res->openTypeTables.end()) { + _numeric_lining.set_sensitive(); + } else { + _numeric_lining.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("onum"))!= res->openTypeTables.end()) { + _numeric_old_style.set_sensitive(); + } else { + _numeric_old_style.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("pnum"))!= res->openTypeTables.end()) { + _numeric_proportional.set_sensitive(); + } else { + _numeric_proportional.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("tnum"))!= res->openTypeTables.end()) { + _numeric_tabular.set_sensitive(); + } else { + _numeric_tabular.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("frac"))!= res->openTypeTables.end()) { + _numeric_diagonal.set_sensitive(); + } else { + _numeric_diagonal.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("afrac"))!= res->openTypeTables.end()) { + _numeric_stacked.set_sensitive(); + } else { + _numeric_stacked.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("ordn"))!= res->openTypeTables.end()) { + _numeric_ordinal.set_sensitive(); + } else { + _numeric_ordinal.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("zero"))!= res->openTypeTables.end()) { + _numeric_slashed_zero.set_sensitive(); + } else { + _numeric_slashed_zero.set_sensitive( false ); + } + + // East-Asian + if((it = res->openTypeTables.find("jp78"))!= res->openTypeTables.end()) { + _asian_jis78.set_sensitive(); + } else { + _asian_jis78.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("jp83"))!= res->openTypeTables.end()) { + _asian_jis83.set_sensitive(); + } else { + _asian_jis83.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("jp90"))!= res->openTypeTables.end()) { + _asian_jis90.set_sensitive(); + } else { + _asian_jis90.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("jp04"))!= res->openTypeTables.end()) { + _asian_jis04.set_sensitive(); + } else { + _asian_jis04.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("smpl"))!= res->openTypeTables.end()) { + _asian_simplified.set_sensitive(); + } else { + _asian_simplified.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("trad"))!= res->openTypeTables.end()) { + _asian_traditional.set_sensitive(); + } else { + _asian_traditional.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("fwid"))!= res->openTypeTables.end()) { + _asian_full_width.set_sensitive(); + } else { + _asian_full_width.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("pwid"))!= res->openTypeTables.end()) { + _asian_proportional_width.set_sensitive(); + } else { + _asian_proportional_width.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("ruby"))!= res->openTypeTables.end()) { + _asian_ruby.set_sensitive(); + } else { + _asian_ruby.set_sensitive( false ); + } + + // List available ligatures + Glib::ustring markup_liga; + Glib::ustring markup_dlig; + Glib::ustring markup_hlig; + Glib::ustring markup_calt; + + for (auto table: res->openTypeTables) { + + if (table.first == "liga" || + table.first == "clig" || + table.first == "dlig" || + table.first == "hgli" || + table.first == "calt") { + + Glib::ustring markup; + markup += "<span font_family='"; + markup += sp_font_description_get_family(res->descr); + markup += "'>"; + markup += Glib::Markup::escape_text(table.second.output); + markup += "</span>"; + + if (table.first == "liga") markup_liga += markup; + if (table.first == "clig") markup_liga += markup; + if (table.first == "dlig") markup_dlig += markup; + if (table.first == "hlig") markup_hlig += markup; + if (table.first == "calt") markup_calt += markup; + } + } + + _ligatures_label_common.set_markup ( markup_liga.c_str() ); + _ligatures_label_discretionary.set_markup ( markup_dlig.c_str() ); + _ligatures_label_historical.set_markup ( markup_hlig.c_str() ); + _ligatures_label_contextual.set_markup ( markup_calt.c_str() ); + + // List available numeric variants + Glib::ustring markup_lnum; + Glib::ustring markup_onum; + Glib::ustring markup_pnum; + Glib::ustring markup_tnum; + Glib::ustring markup_frac; + Glib::ustring markup_afrc; + Glib::ustring markup_ordn; + Glib::ustring markup_zero; + + for (auto table: res->openTypeTables) { + + Glib::ustring markup; + markup += "<span font_family='"; + markup += sp_font_description_get_family(res->descr); + markup += "' font_features='"; + markup += table.first; + markup += "'>"; + if (table.first == "lnum" || + table.first == "onum" || + table.first == "pnum" || + table.first == "tnum") markup += "0123456789"; + if (table.first == "zero") markup += "0"; + if (table.first == "ordn") markup += "[" + table.second.before + "]" + table.second.output; + if (table.first == "frac" || + table.first == "afrc" ) markup += "1/2 2/3 3/4 4/5 5/6"; // Can we do better? + markup += "</span>"; + + if (table.first == "lnum") markup_lnum += markup; + if (table.first == "onum") markup_onum += markup; + if (table.first == "pnum") markup_pnum += markup; + if (table.first == "tnum") markup_tnum += markup; + if (table.first == "frac") markup_frac += markup; + if (table.first == "afrc") markup_afrc += markup; + if (table.first == "ordn") markup_ordn += markup; + if (table.first == "zero") markup_zero += markup; + } + + _numeric_lining_label.set_markup ( markup_lnum.c_str() ); + _numeric_old_style_label.set_markup ( markup_onum.c_str() ); + _numeric_proportional_label.set_markup ( markup_pnum.c_str() ); + _numeric_tabular_label.set_markup ( markup_tnum.c_str() ); + _numeric_diagonal_label.set_markup ( markup_frac.c_str() ); + _numeric_stacked_label.set_markup ( markup_afrc.c_str() ); + _numeric_ordinal_label.set_markup ( markup_ordn.c_str() ); + _numeric_slashed_zero_label.set_markup ( markup_zero.c_str() ); + + // Make list of tables not handled above. + std::map<Glib::ustring, OTSubstitution> table_copy = res->openTypeTables; + if( (it = table_copy.find("liga")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("clig")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("dlig")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("hlig")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("calt")) != table_copy.end() ) table_copy.erase( it ); + + if( (it = table_copy.find("subs")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("sups")) != table_copy.end() ) table_copy.erase( it ); + + if( (it = table_copy.find("smcp")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("c2sc")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("pcap")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("c2pc")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("unic")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("titl")) != table_copy.end() ) table_copy.erase( it ); + + if( (it = table_copy.find("lnum")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("onum")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("pnum")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("tnum")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("frac")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("afrc")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("ordn")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("zero")) != table_copy.end() ) table_copy.erase( it ); + + if( (it = table_copy.find("jp78")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("jp83")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("jp90")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("jp04")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("smpl")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("trad")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("fwid")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("pwid")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("ruby")) != table_copy.end() ) table_copy.erase( it ); + + // An incomplete list of tables that should not be exposed to the user: + if( (it = table_copy.find("abvf")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("abvs")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("akhn")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("blwf")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("blws")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("ccmp")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("cjct")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("dnom")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("dtls")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("fina")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("half")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("haln")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("init")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("isol")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("locl")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("medi")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("nukt")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("numr")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("pref")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("pres")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("pstf")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("psts")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("rlig")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("rkrf")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("rphf")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("rtlm")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("ssty")) != table_copy.end() ) table_copy.erase( it ); + if( (it = table_copy.find("vatu")) != table_copy.end() ) table_copy.erase( it ); + + // Clear out old features + auto children = _feature_grid.get_children(); + for (auto child: children) { + _feature_grid.remove (*child); + } + _features.clear(); + + std::string markup; + int grid_row = 0; + + // GSUB lookup type 1 (1 to 1 mapping). + for (auto table: res->openTypeTables) { + if (table.first == "case" || + table.first == "hist" || + (table.first[0] == 's' && table.first[1] == 's' && !(table.first[2] == 't'))) { + + if( (it = table_copy.find(table.first)) != table_copy.end() ) table_copy.erase( it ); + + _features[table.first] = new Feature (table.first, table.second, 2, + sp_font_description_get_family(res->descr), + _feature_grid, grid_row, this); + grid_row++; + } + } + + // GSUB lookup type 3 (1 to many mapping). Optionally type 1. + for (auto table: res->openTypeTables) { + if (table.first == "salt" || + table.first == "swsh" || + table.first == "cwsh" || + table.first == "ornm" || + table.first == "nalt" || + (table.first[0] == 'c' && table.first[1] == 'v')) { + + if (table.second.input.length() == 0) { + // This can happen if a table is not in the 'DFLT' script and 'dflt' language. + // We should be using the 'lang' attribute to find the correct tables. + // std::cerr << "FontVariants::open_type_update: " + // << table.first << " has no entries!" << std::endl; + continue; + } + + if( (it = table_copy.find(table.first)) != table_copy.end() ) table_copy.erase( it ); + + // Our lame attempt at determining number of alternative glyphs for one glyph: + int number = table.second.output.length() / table.second.input.length(); + if (number < 1) { + number = 1; // Must have at least on/off, see comment above about 'lang' attribute. + // std::cout << table.first << " " + // << table.second.output.length() << "/" + // << table.second.input.length() << "=" + // << number << std::endl; + } + + _features[table.first] = new Feature (table.first, table.second, number+1, + sp_font_description_get_family(res->descr), + _feature_grid, grid_row, this); + grid_row++; + } + } + + _feature_grid.show_all(); + + _feature_substitutions.set_markup ( markup.c_str() ); + + std::string ott_list = "OpenType tables not included above: "; + for(it = table_copy.begin(); it != table_copy.end(); ++it) { + ott_list += it->first; + ott_list += ", "; + } + + if (table_copy.size() > 0) { + ott_list.pop_back(); + ott_list.pop_back(); + _feature_list.set_text( ott_list.c_str() ); + } else { + _feature_list.set_text( "" ); + } + + } else { + std::cerr << "FontVariants::update(): Couldn't find font_instance for: " + << font_spec << std::endl; + } + + _ligatures_changed = false; + _position_changed = false; + _caps_changed = false; + _numeric_changed = false; + _feature_changed = false; + } + + void + FontVariants::fill_css( SPCSSAttr *css ) { + + // Ligatures + bool common = _ligatures_common.get_active(); + bool discretionary = _ligatures_discretionary.get_active(); + bool historical = _ligatures_historical.get_active(); + bool contextual = _ligatures_contextual.get_active(); + + if( !common && !discretionary && !historical && !contextual ) { + sp_repr_css_set_property(css, "font-variant-ligatures", "none" ); + } else if ( common && !discretionary && !historical && contextual ) { + sp_repr_css_set_property(css, "font-variant-ligatures", "normal" ); + } else { + Glib::ustring css_string; + if ( !common ) + css_string += "no-common-ligatures "; + if ( discretionary ) + css_string += "discretionary-ligatures "; + if ( historical ) + css_string += "historical-ligatures "; + if ( !contextual ) + css_string += "no-contextual "; + sp_repr_css_set_property(css, "font-variant-ligatures", css_string.c_str() ); + } + + // Position + { + unsigned position_new = SP_CSS_FONT_VARIANT_POSITION_NORMAL; + Glib::ustring css_string; + if( _position_normal.get_active() ) { + css_string = "normal"; + } else if( _position_sub.get_active() ) { + css_string = "sub"; + position_new = SP_CSS_FONT_VARIANT_POSITION_SUB; + } else if( _position_super.get_active() ) { + css_string = "super"; + position_new = SP_CSS_FONT_VARIANT_POSITION_SUPER; + } + + // 'if' may not be necessary... need to test. + if( (_position_all != position_new) || ((_position_mix != 0) && _position_changed) ) { + sp_repr_css_set_property(css, "font-variant-position", css_string.c_str() ); + } + } + + // Caps + { + //unsigned caps_new; + Glib::ustring css_string; + if( _caps_normal.get_active() ) { + css_string = "normal"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; + } else if( _caps_small.get_active() ) { + css_string = "small-caps"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_SMALL; + } else if( _caps_all_small.get_active() ) { + css_string = "all-small-caps"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL; + } else if( _caps_petite.get_active() ) { + css_string = "petite"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_PETITE; + } else if( _caps_all_petite.get_active() ) { + css_string = "all-petite"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE; + } else if( _caps_unicase.get_active() ) { + css_string = "unicase"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_UNICASE; + } else if( _caps_titling.get_active() ) { + css_string = "titling"; + // caps_new = SP_CSS_FONT_VARIANT_CAPS_TITLING; + //} else { + // caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; + } + + // May not be necessary... need to test. + //if( (_caps_all != caps_new) || ((_caps_mix != 0) && _caps_changed) ) { + sp_repr_css_set_property(css, "font-variant-caps", css_string.c_str() ); + //} + } + + // Numeric + bool default_style = _numeric_default_style.get_active(); + bool lining = _numeric_lining.get_active(); + bool old_style = _numeric_old_style.get_active(); + + bool default_width = _numeric_default_width.get_active(); + bool proportional = _numeric_proportional.get_active(); + bool tabular = _numeric_tabular.get_active(); + + bool default_fractions = _numeric_default_fractions.get_active(); + bool diagonal = _numeric_diagonal.get_active(); + bool stacked = _numeric_stacked.get_active(); + + bool ordinal = _numeric_ordinal.get_active(); + bool slashed_zero = _numeric_slashed_zero.get_active(); + + if (default_style & default_width & default_fractions & !ordinal & !slashed_zero) { + sp_repr_css_set_property(css, "font-variant-numeric", "normal"); + } else { + Glib::ustring css_string; + if ( lining ) + css_string += "lining-nums "; + if ( old_style ) + css_string += "oldstyle-nums "; + if ( proportional ) + css_string += "proportional-nums "; + if ( tabular ) + css_string += "tabular-nums "; + if ( diagonal ) + css_string += "diagonal-fractions "; + if ( stacked ) + css_string += "stacked-fractions "; + if ( ordinal ) + css_string += "ordinal "; + if ( slashed_zero ) + css_string += "slashed-zero "; + sp_repr_css_set_property(css, "font-variant-numeric", css_string.c_str() ); + } + + // East Asian + bool jis78 = _asian_jis78.get_active(); + bool jis83 = _asian_jis83.get_active(); + bool jis90 = _asian_jis90.get_active(); + bool jis04 = _asian_jis04.get_active(); + bool simplified = _asian_simplified.get_active(); + bool traditional = _asian_traditional.get_active(); + bool asian_width = _asian_default_width.get_active(); + bool fwid = _asian_full_width.get_active(); + bool pwid = _asian_proportional_width.get_active(); + bool ruby = _asian_ruby.get_active(); + + if (default_style & asian_width & !ruby) { + sp_repr_css_set_property(css, "font-variant-east-asian", "normal"); + } else { + Glib::ustring css_string; + if (jis78) css_string += "jis78 "; + if (jis83) css_string += "jis83 "; + if (jis90) css_string += "jis90 "; + if (jis04) css_string += "jis04 "; + if (simplified) css_string += "simplfied "; + if (traditional) css_string += "traditional "; + + if (fwid) css_string += "fwid "; + if (pwid) css_string += "pwid "; + + if (ruby) css_string += "ruby "; + + sp_repr_css_set_property(css, "font-variant-east-asian", css_string.c_str() ); + } + + // Feature settings + Glib::ustring feature_string; + for (auto i: _features) { + feature_string += i.second->get_css(); + } + + feature_string += _feature_entry.get_text(); + // std::cout << "feature_string: " << feature_string << std::endl; + + if (!feature_string.empty()) { + sp_repr_css_set_property(css, "font-feature-settings", feature_string.c_str()); + } else { + sp_repr_css_unset_property(css, "font-feature-settings"); + } + } + + Glib::ustring + FontVariants::get_markup() { + + Glib::ustring markup; + + // Ligatures + bool common = _ligatures_common.get_active(); + bool discretionary = _ligatures_discretionary.get_active(); + bool historical = _ligatures_historical.get_active(); + bool contextual = _ligatures_contextual.get_active(); + + if (!common) markup += "liga=0,clig=0,"; // On by default. + if (discretionary) markup += "dlig=1,"; + if (historical) markup += "hlig=1,"; + if (contextual) markup += "calt=1,"; + + // Position + if ( _position_sub.get_active() ) markup += "subs=1,"; + else if ( _position_super.get_active() ) markup += "sups=1,"; + + // Caps + if ( _caps_small.get_active() ) markup += "smcp=1,"; + else if ( _caps_all_small.get_active() ) markup += "c2sc=1,smcp=1,"; + else if ( _caps_petite.get_active() ) markup += "pcap=1,"; + else if ( _caps_all_petite.get_active() ) markup += "c2pc=1,pcap=1,"; + else if ( _caps_unicase.get_active() ) markup += "unic=1,"; + else if ( _caps_titling.get_active() ) markup += "titl=1,"; + + // Numeric + bool lining = _numeric_lining.get_active(); + bool old_style = _numeric_old_style.get_active(); + + bool proportional = _numeric_proportional.get_active(); + bool tabular = _numeric_tabular.get_active(); + + bool diagonal = _numeric_diagonal.get_active(); + bool stacked = _numeric_stacked.get_active(); + + bool ordinal = _numeric_ordinal.get_active(); + bool slashed_zero = _numeric_slashed_zero.get_active(); + + if (lining) markup += "lnum=1,"; + if (old_style) markup += "onum=1,"; + if (proportional) markup += "pnum=1,"; + if (tabular) markup += "tnum=1,"; + if (diagonal) markup += "frac=1,"; + if (stacked) markup += "afrc=1,"; + if (ordinal) markup += "ordn=1,"; + if (slashed_zero) markup += "zero=1,"; + + // East Asian + bool jis78 = _asian_jis78.get_active(); + bool jis83 = _asian_jis83.get_active(); + bool jis90 = _asian_jis90.get_active(); + bool jis04 = _asian_jis04.get_active(); + bool simplified = _asian_simplified.get_active(); + bool traditional = _asian_traditional.get_active(); + //bool asian_width = _asian_default_width.get_active(); + bool fwid = _asian_full_width.get_active(); + bool pwid = _asian_proportional_width.get_active(); + bool ruby = _asian_ruby.get_active(); + + if (jis78 ) markup += "jp78=1,"; + if (jis83 ) markup += "jp83=1,"; + if (jis90 ) markup += "jp90=1,"; + if (jis04 ) markup += "jp04=1,"; + if (simplified ) markup += "smpl=1,"; + if (traditional ) markup += "trad=1,"; + + if (fwid ) markup += "fwid=1,"; + if (pwid ) markup += "pwid=1,"; + + if (ruby ) markup += "ruby=1,"; + + // Feature settings + Glib::ustring feature_string; + for (auto i: _features) { + feature_string += i.second->get_css(); + } + + feature_string += _feature_entry.get_text(); + if (!feature_string.empty()) { + markup += feature_string; + } + + // std::cout << "|" << markup << "|" << std::endl; + return markup; + } + +} // namespace Widget +} // 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 : |