1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Author: 4 * Tavmjong Bah <tavmjong@free.fr> 5 * 6 * Copyright (C) 2015, 2018 Tavmong Bah 7 * 8 * Released under GNU GPL v2+, read the file 'COPYING' for more information. 9 */ 10 11 #include <gtkmm.h> 12 #include <glibmm/i18n.h> 13 14 #include <libnrtype/font-instance.h> 15 16 #include "font-variants.h" 17 18 // For updating from selection 19 #include "desktop.h" 20 #include "object/sp-text.h" 21 22 namespace Inkscape { 23 namespace UI { 24 namespace Widget { 25 26 // A simple class to handle UI for one feature. We could of derived this from Gtk::HBox but by 27 // attaching widgets directly to Gtk::Grid, we keep columns lined up (which may or may not be a 28 // good thing). 29 class Feature 30 { 31 public: Feature(const Glib::ustring & name,OTSubstitution & glyphs,int options,Glib::ustring family,Gtk::Grid & grid,int & row,FontVariants * parent)32 Feature( const Glib::ustring& name, OTSubstitution& glyphs, int options, Glib::ustring family, Gtk::Grid& grid, int &row, FontVariants* parent) 33 : _name (name) 34 { 35 Gtk::Label* table_name = Gtk::manage (new Gtk::Label()); 36 table_name->set_markup ("\"" + name + "\" "); 37 38 grid.attach (*table_name, 0, row, 1, 1); 39 40 Gtk::FlowBox* flow_box = nullptr; 41 Gtk::ScrolledWindow* scrolled_window = nullptr; 42 if (options > 2) { 43 // If there are more than 2 option, pack them into a flowbox instead of directly putting them in the grid. 44 // Some fonts might have a table with many options (Bungee Hairline table 'ornm' has 113 entries). 45 flow_box = Gtk::manage (new Gtk::FlowBox()); 46 flow_box->set_selection_mode(); // Turn off selection 47 flow_box->set_homogeneous(); 48 flow_box->set_max_children_per_line (100); // Override default value 49 flow_box->set_min_children_per_line (10); // Override default value 50 51 // We pack this into a scrollbar... otherwise the minimum height is set to what is required to fit all 52 // flow box children into the flow box when the flow box has minimum width. (Crazy if you ask me!) 53 scrolled_window = Gtk::manage (new Gtk::ScrolledWindow()); 54 scrolled_window->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); 55 scrolled_window->add(*flow_box); 56 } 57 58 Gtk::RadioButton::Group group; 59 for (int i = 0; i < options; ++i) { 60 61 // Create radio button and create or add to button group. 62 Gtk::RadioButton* button = Gtk::manage (new Gtk::RadioButton()); 63 if (i == 0) { 64 group = button->get_group(); 65 } else { 66 button->set_group (group); 67 } 68 button->signal_clicked().connect ( sigc::mem_fun(*parent, &FontVariants::feature_callback) ); 69 buttons.push_back (button); 70 71 // Create label. 72 Gtk::Label* label = Gtk::manage (new Gtk::Label()); 73 74 // Restrict label width (some fonts have lots of alternatives). 75 label->set_line_wrap( true ); 76 label->set_line_wrap_mode( Pango::WRAP_WORD_CHAR ); 77 label->set_ellipsize( Pango::ELLIPSIZE_END ); 78 label->set_lines(3); 79 label->set_hexpand(); 80 81 Glib::ustring markup; 82 markup += "<span font_family='"; 83 markup += family; 84 markup += "' font_features='"; 85 markup += name; 86 markup += " "; 87 markup += std::to_string (i); 88 markup += "'>"; 89 markup += Glib::Markup::escape_text (glyphs.input); 90 markup += "</span>"; 91 label->set_markup (markup); 92 93 // Add button and label to widget 94 if (!flow_box) { 95 // Attach directly to grid (keeps things aligned row-to-row). 96 grid.attach (*button, 2*i+1, row, 1, 1); 97 grid.attach (*label, 2*i+2, row, 1, 1); 98 } else { 99 // Pack into FlowBox 100 101 // Pack button and label into a box so they stay together. 102 Gtk::Box* box = Gtk::manage (new Gtk::Box()); 103 box->add(*button); 104 box->add(*label); 105 106 flow_box->add(*box); 107 } 108 } 109 110 if (scrolled_window) { 111 grid.attach (*scrolled_window, 1, row, 4, 1); 112 } 113 } 114 115 Glib::ustring get_css()116 get_css() 117 { 118 int i = 0; 119 for (auto b: buttons) { 120 if (b->get_active()) { 121 if (i == 0) { 122 // Features are always off by default (for those handled here). 123 return ""; 124 } else if (i == 1) { 125 // Feature without value has implied value of 1. 126 return ("\"" + _name + "\", "); 127 } else { 128 // Feature with value greater than 1 must be explicitly set. 129 return ("\"" + _name + "\" " + std::to_string (i) + ", "); 130 } 131 } 132 ++i; 133 } 134 return ""; 135 } 136 137 void set_active(int i)138 set_active(int i) 139 { 140 if (i < buttons.size()) { 141 buttons[i]->set_active(); 142 } 143 } 144 145 private: 146 Glib::ustring _name; 147 std::vector <Gtk::RadioButton*> buttons; 148 }; 149 FontVariants()150 FontVariants::FontVariants () : 151 Gtk::Box (Gtk::ORIENTATION_VERTICAL), 152 _ligatures_frame ( Glib::ustring(C_("Font feature", "Ligatures" )) ), 153 _ligatures_common ( Glib::ustring(C_("Font feature", "Common" )) ), 154 _ligatures_discretionary ( Glib::ustring(C_("Font feature", "Discretionary")) ), 155 _ligatures_historical ( Glib::ustring(C_("Font feature", "Historical" )) ), 156 _ligatures_contextual ( Glib::ustring(C_("Font feature", "Contextual" )) ), 157 158 _position_frame ( Glib::ustring(C_("Font feature", "Position" )) ), 159 _position_normal ( Glib::ustring(C_("Font feature", "Normal" )) ), 160 _position_sub ( Glib::ustring(C_("Font feature", "Subscript" )) ), 161 _position_super ( Glib::ustring(C_("Font feature", "Superscript" )) ), 162 163 _caps_frame ( Glib::ustring(C_("Font feature", "Capitals" )) ), 164 _caps_normal ( Glib::ustring(C_("Font feature", "Normal" )) ), 165 _caps_small ( Glib::ustring(C_("Font feature", "Small" )) ), 166 _caps_all_small ( Glib::ustring(C_("Font feature", "All small" )) ), 167 _caps_petite ( Glib::ustring(C_("Font feature", "Petite" )) ), 168 _caps_all_petite ( Glib::ustring(C_("Font feature", "All petite" )) ), 169 _caps_unicase ( Glib::ustring(C_("Font feature", "Unicase" )) ), 170 _caps_titling ( Glib::ustring(C_("Font feature", "Titling" )) ), 171 172 _numeric_frame ( Glib::ustring(C_("Font feature", "Numeric" )) ), 173 _numeric_lining ( Glib::ustring(C_("Font feature", "Lining" )) ), 174 _numeric_old_style ( Glib::ustring(C_("Font feature", "Old Style" )) ), 175 _numeric_default_style ( Glib::ustring(C_("Font feature", "Default Style")) ), 176 _numeric_proportional ( Glib::ustring(C_("Font feature", "Proportional" )) ), 177 _numeric_tabular ( Glib::ustring(C_("Font feature", "Tabular" )) ), 178 _numeric_default_width ( Glib::ustring(C_("Font feature", "Default Width")) ), 179 _numeric_diagonal ( Glib::ustring(C_("Font feature", "Diagonal" )) ), 180 _numeric_stacked ( Glib::ustring(C_("Font feature", "Stacked" )) ), 181 _numeric_default_fractions( Glib::ustring(C_("Font feature", "Default Fractions")) ), 182 _numeric_ordinal ( Glib::ustring(C_("Font feature", "Ordinal" )) ), 183 _numeric_slashed_zero ( Glib::ustring(C_("Font feature", "Slashed Zero" )) ), 184 185 _asian_frame ( Glib::ustring(C_("Font feature", "East Asian" )) ), 186 _asian_default_variant ( Glib::ustring(C_("Font feature", "Default" )) ), 187 _asian_jis78 ( Glib::ustring(C_("Font feature", "JIS78" )) ), 188 _asian_jis83 ( Glib::ustring(C_("Font feature", "JIS83" )) ), 189 _asian_jis90 ( Glib::ustring(C_("Font feature", "JIS90" )) ), 190 _asian_jis04 ( Glib::ustring(C_("Font feature", "JIS04" )) ), 191 _asian_simplified ( Glib::ustring(C_("Font feature", "Simplified" )) ), 192 _asian_traditional ( Glib::ustring(C_("Font feature", "Traditional" )) ), 193 _asian_default_width ( Glib::ustring(C_("Font feature", "Default" )) ), 194 _asian_full_width ( Glib::ustring(C_("Font feature", "Full Width" )) ), 195 _asian_proportional_width ( Glib::ustring(C_("Font feature", "Proportional" )) ), 196 _asian_ruby ( Glib::ustring(C_("Font feature", "Ruby" )) ), 197 198 _feature_frame ( Glib::ustring(C_("Font feature", "Feature Settings")) ), 199 _feature_label ( Glib::ustring(C_("Font feature", "Selection has different Feature Settings!")) ), 200 201 _ligatures_changed( false ), 202 _position_changed( false ), 203 _caps_changed( false ), 204 _numeric_changed( false ), 205 _asian_changed( false ), 206 _feature_vbox(Gtk::ORIENTATION_VERTICAL) 207 208 { 209 210 set_name ( "FontVariants" ); 211 212 // Ligatures -------------------------- 213 214 // Add tooltips 215 _ligatures_common.set_tooltip_text( 216 _("Common ligatures. On by default. OpenType tables: 'liga', 'clig'")); 217 _ligatures_discretionary.set_tooltip_text( 218 _("Discretionary ligatures. Off by default. OpenType table: 'dlig'")); 219 _ligatures_historical.set_tooltip_text( 220 _("Historical ligatures. Off by default. OpenType table: 'hlig'")); 221 _ligatures_contextual.set_tooltip_text( 222 _("Contextual forms. On by default. OpenType table: 'calt'")); 223 224 // Add signals 225 _ligatures_common.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); 226 _ligatures_discretionary.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); 227 _ligatures_historical.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); 228 _ligatures_contextual.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); 229 230 // Restrict label widths (some fonts have lots of ligatures). Must also set ellipsize mode. 231 _ligatures_label_common.set_max_width_chars( 60 ); 232 _ligatures_label_discretionary.set_max_width_chars( 60 ); 233 _ligatures_label_historical.set_max_width_chars( 60 ); 234 _ligatures_label_contextual.set_max_width_chars( 60 ); 235 236 _ligatures_label_common.set_ellipsize( Pango::ELLIPSIZE_END ); 237 _ligatures_label_discretionary.set_ellipsize( Pango::ELLIPSIZE_END ); 238 _ligatures_label_historical.set_ellipsize( Pango::ELLIPSIZE_END ); 239 _ligatures_label_contextual.set_ellipsize( Pango::ELLIPSIZE_END ); 240 241 _ligatures_label_common.set_lines( 5 ); 242 _ligatures_label_discretionary.set_lines( 5 ); 243 _ligatures_label_historical.set_lines( 5 ); 244 _ligatures_label_contextual.set_lines( 5 ); 245 246 // Allow user to select characters. Not useful as this selects the ligatures. 247 // _ligatures_label_common.set_selectable( true ); 248 // _ligatures_label_discretionary.set_selectable( true ); 249 // _ligatures_label_historical.set_selectable( true ); 250 // _ligatures_label_contextual.set_selectable( true ); 251 252 // Add to frame 253 _ligatures_grid.attach( _ligatures_common, 0, 0, 1, 1); 254 _ligatures_grid.attach( _ligatures_discretionary, 0, 1, 1, 1); 255 _ligatures_grid.attach( _ligatures_historical, 0, 2, 1, 1); 256 _ligatures_grid.attach( _ligatures_contextual, 0, 3, 1, 1); 257 _ligatures_grid.attach( _ligatures_label_common, 1, 0, 1, 1); 258 _ligatures_grid.attach( _ligatures_label_discretionary, 1, 1, 1, 1); 259 _ligatures_grid.attach( _ligatures_label_historical, 1, 2, 1, 1); 260 _ligatures_grid.attach( _ligatures_label_contextual, 1, 3, 1, 1); 261 262 _ligatures_grid.set_margin_start(15); 263 _ligatures_grid.set_margin_end(15); 264 265 _ligatures_frame.add( _ligatures_grid ); 266 pack_start( _ligatures_frame, Gtk::PACK_SHRINK ); 267 268 ligatures_init(); 269 270 // Position ---------------------------------- 271 272 // Add tooltips 273 _position_normal.set_tooltip_text( _("Normal position.")); 274 _position_sub.set_tooltip_text( _("Subscript. OpenType table: 'subs'") ); 275 _position_super.set_tooltip_text( _("Superscript. OpenType table: 'sups'") ); 276 277 // Group buttons 278 Gtk::RadioButton::Group position_group = _position_normal.get_group(); 279 _position_sub.set_group(position_group); 280 _position_super.set_group(position_group); 281 282 // Add signals 283 _position_normal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); 284 _position_sub.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); 285 _position_super.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); 286 287 // Add to frame 288 _position_grid.attach( _position_normal, 0, 0, 1, 1); 289 _position_grid.attach( _position_sub, 1, 0, 1, 1); 290 _position_grid.attach( _position_super, 2, 0, 1, 1); 291 292 _position_grid.set_margin_start(15); 293 _position_grid.set_margin_end(15); 294 295 _position_frame.add( _position_grid ); 296 pack_start( _position_frame, Gtk::PACK_SHRINK ); 297 298 position_init(); 299 300 // Caps ---------------------------------- 301 302 // Add tooltips 303 _caps_normal.set_tooltip_text( _("Normal capitalization.")); 304 _caps_small.set_tooltip_text( _("Small-caps (lowercase). OpenType table: 'smcp'")); 305 _caps_all_small.set_tooltip_text( _("All small-caps (uppercase and lowercase). OpenType tables: 'c2sc' and 'smcp'")); 306 _caps_petite.set_tooltip_text( _("Petite-caps (lowercase). OpenType table: 'pcap'")); 307 _caps_all_petite.set_tooltip_text( _("All petite-caps (uppercase and lowercase). OpenType tables: 'c2sc' and 'pcap'")); 308 _caps_unicase.set_tooltip_text( _("Unicase (small caps for uppercase, normal for lowercase). OpenType table: 'unic'")); 309 _caps_titling.set_tooltip_text( _("Titling caps (lighter-weight uppercase for use in titles). OpenType table: 'titl'")); 310 311 // Group buttons 312 Gtk::RadioButton::Group caps_group = _caps_normal.get_group(); 313 _caps_small.set_group(caps_group); 314 _caps_all_small.set_group(caps_group); 315 _caps_petite.set_group(caps_group); 316 _caps_all_petite.set_group(caps_group); 317 _caps_unicase.set_group(caps_group); 318 _caps_titling.set_group(caps_group); 319 320 // Add signals 321 _caps_normal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 322 _caps_small.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 323 _caps_all_small.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 324 _caps_petite.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 325 _caps_all_petite.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 326 _caps_unicase.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 327 _caps_titling.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); 328 329 // Add to frame 330 _caps_grid.attach( _caps_normal, 0, 0, 1, 1); 331 _caps_grid.attach( _caps_unicase, 1, 0, 1, 1); 332 _caps_grid.attach( _caps_titling, 2, 0, 1, 1); 333 _caps_grid.attach( _caps_small, 0, 1, 1, 1); 334 _caps_grid.attach( _caps_all_small, 1, 1, 1, 1); 335 _caps_grid.attach( _caps_petite, 2, 1, 1, 1); 336 _caps_grid.attach( _caps_all_petite, 3, 1, 1, 1); 337 338 _caps_grid.set_margin_start(15); 339 _caps_grid.set_margin_end(15); 340 341 _caps_frame.add( _caps_grid ); 342 pack_start( _caps_frame, Gtk::PACK_SHRINK ); 343 344 caps_init(); 345 346 // Numeric ------------------------------ 347 348 // Add tooltips 349 _numeric_default_style.set_tooltip_text( _("Normal style.")); 350 _numeric_lining.set_tooltip_text( _("Lining numerals. OpenType table: 'lnum'")); 351 _numeric_old_style.set_tooltip_text( _("Old style numerals. OpenType table: 'onum'")); 352 _numeric_default_width.set_tooltip_text( _("Normal widths.")); 353 _numeric_proportional.set_tooltip_text( _("Proportional width numerals. OpenType table: 'pnum'")); 354 _numeric_tabular.set_tooltip_text( _("Same width numerals. OpenType table: 'tnum'")); 355 _numeric_default_fractions.set_tooltip_text( _("Normal fractions.")); 356 _numeric_diagonal.set_tooltip_text( _("Diagonal fractions. OpenType table: 'frac'")); 357 _numeric_stacked.set_tooltip_text( _("Stacked fractions. OpenType table: 'afrc'")); 358 _numeric_ordinal.set_tooltip_text( _("Ordinals (raised 'th', etc.). OpenType table: 'ordn'")); 359 _numeric_slashed_zero.set_tooltip_text( _("Slashed zeros. OpenType table: 'zero'")); 360 361 // Group buttons 362 Gtk::RadioButton::Group style_group = _numeric_default_style.get_group(); 363 _numeric_lining.set_group(style_group); 364 _numeric_old_style.set_group(style_group); 365 366 Gtk::RadioButton::Group width_group = _numeric_default_width.get_group(); 367 _numeric_proportional.set_group(width_group); 368 _numeric_tabular.set_group(width_group); 369 370 Gtk::RadioButton::Group fraction_group = _numeric_default_fractions.get_group(); 371 _numeric_diagonal.set_group(fraction_group); 372 _numeric_stacked.set_group(fraction_group); 373 374 // Add signals 375 _numeric_default_style.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 376 _numeric_lining.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 377 _numeric_old_style.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 378 _numeric_default_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 379 _numeric_proportional.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 380 _numeric_tabular.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 381 _numeric_default_fractions.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 382 _numeric_diagonal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 383 _numeric_stacked.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 384 _numeric_ordinal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 385 _numeric_slashed_zero.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); 386 387 // Add to frame 388 _numeric_grid.attach (_numeric_default_style, 0, 0, 1, 1); 389 _numeric_grid.attach (_numeric_lining, 1, 0, 1, 1); 390 _numeric_grid.attach (_numeric_lining_label, 2, 0, 1, 1); 391 _numeric_grid.attach (_numeric_old_style, 3, 0, 1, 1); 392 _numeric_grid.attach (_numeric_old_style_label, 4, 0, 1, 1); 393 394 _numeric_grid.attach (_numeric_default_width, 0, 1, 1, 1); 395 _numeric_grid.attach (_numeric_proportional, 1, 1, 1, 1); 396 _numeric_grid.attach (_numeric_proportional_label, 2, 1, 1, 1); 397 _numeric_grid.attach (_numeric_tabular, 3, 1, 1, 1); 398 _numeric_grid.attach (_numeric_tabular_label, 4, 1, 1, 1); 399 400 _numeric_grid.attach (_numeric_default_fractions, 0, 2, 1, 1); 401 _numeric_grid.attach (_numeric_diagonal, 1, 2, 1, 1); 402 _numeric_grid.attach (_numeric_diagonal_label, 2, 2, 1, 1); 403 _numeric_grid.attach (_numeric_stacked, 3, 2, 1, 1); 404 _numeric_grid.attach (_numeric_stacked_label, 4, 2, 1, 1); 405 406 _numeric_grid.attach (_numeric_ordinal, 0, 3, 1, 1); 407 _numeric_grid.attach (_numeric_ordinal_label, 1, 3, 4, 1); 408 409 _numeric_grid.attach (_numeric_slashed_zero, 0, 4, 1, 1); 410 _numeric_grid.attach (_numeric_slashed_zero_label, 1, 4, 1, 1); 411 412 _numeric_grid.set_margin_start(15); 413 _numeric_grid.set_margin_end(15); 414 415 _numeric_frame.add( _numeric_grid ); 416 pack_start( _numeric_frame, Gtk::PACK_SHRINK ); 417 418 // East Asian 419 420 // Add tooltips 421 _asian_default_variant.set_tooltip_text ( _("Default variant.")); 422 _asian_jis78.set_tooltip_text( _("JIS78 forms. OpenType table: 'jp78'.")); 423 _asian_jis83.set_tooltip_text( _("JIS83 forms. OpenType table: 'jp83'.")); 424 _asian_jis90.set_tooltip_text( _("JIS90 forms. OpenType table: 'jp90'.")); 425 _asian_jis04.set_tooltip_text( _("JIS2004 forms. OpenType table: 'jp04'.")); 426 _asian_simplified.set_tooltip_text( _("Simplified forms. OpenType table: 'smpl'.")); 427 _asian_traditional.set_tooltip_text( _("Traditional forms. OpenType table: 'trad'.")); 428 _asian_default_width.set_tooltip_text ( _("Default width.")); 429 _asian_full_width.set_tooltip_text( _("Full width variants. OpenType table: 'fwid'.")); 430 _asian_proportional_width.set_tooltip_text(_("Proportional width variants. OpenType table: 'pwid'.")); 431 _asian_ruby.set_tooltip_text( _("Ruby variants. OpenType table: 'ruby'.")); 432 433 // Add signals 434 _asian_default_variant.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 435 _asian_jis78.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 436 _asian_jis83.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 437 _asian_jis90.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 438 _asian_jis04.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 439 _asian_simplified.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 440 _asian_traditional.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 441 _asian_default_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 442 _asian_full_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 443 _asian_proportional_width.signal_clicked().connect (sigc::mem_fun(*this, &FontVariants::asian_callback) ); 444 _asian_ruby.signal_clicked().connect( sigc::mem_fun(*this, &FontVariants::asian_callback) ); 445 446 // Add to frame 447 _asian_grid.attach (_asian_default_variant, 0, 0, 1, 1); 448 _asian_grid.attach (_asian_jis78, 1, 0, 1, 1); 449 _asian_grid.attach (_asian_jis83, 2, 0, 1, 1); 450 _asian_grid.attach (_asian_jis90, 1, 1, 1, 1); 451 _asian_grid.attach (_asian_jis04, 2, 1, 1, 1); 452 _asian_grid.attach (_asian_simplified, 1, 2, 1, 1); 453 _asian_grid.attach (_asian_traditional, 2, 2, 1, 1); 454 _asian_grid.attach (_asian_default_width, 0, 3, 1, 1); 455 _asian_grid.attach (_asian_full_width, 1, 3, 1, 1); 456 _asian_grid.attach (_asian_proportional_width, 2, 3, 1, 1); 457 _asian_grid.attach (_asian_ruby, 0, 4, 1, 1); 458 459 _asian_grid.set_margin_start(15); 460 _asian_grid.set_margin_end(15); 461 462 _asian_frame.add( _asian_grid ); 463 pack_start( _asian_frame, Gtk::PACK_SHRINK ); 464 465 // Group Buttons 466 Gtk::RadioButton::Group asian_variant_group = _asian_default_variant.get_group(); 467 _asian_jis78.set_group(asian_variant_group); 468 _asian_jis83.set_group(asian_variant_group); 469 _asian_jis90.set_group(asian_variant_group); 470 _asian_jis04.set_group(asian_variant_group); 471 _asian_simplified.set_group(asian_variant_group); 472 _asian_traditional.set_group(asian_variant_group); 473 474 Gtk::RadioButton::Group asian_width_group = _asian_default_width.get_group(); 475 _asian_full_width.set_group (asian_width_group); 476 _asian_proportional_width.set_group (asian_width_group); 477 478 // Feature settings --------------------- 479 480 // Add tooltips 481 _feature_entry.set_tooltip_text( _("Feature settings in CSS form (e.g. \"wxyz\" or \"wxyz\" 3).")); 482 483 _feature_substitutions.set_justify( Gtk::JUSTIFY_LEFT ); 484 _feature_substitutions.set_line_wrap( true ); 485 _feature_substitutions.set_line_wrap_mode( Pango::WRAP_WORD_CHAR ); 486 487 _feature_list.set_justify( Gtk::JUSTIFY_LEFT ); 488 _feature_list.set_line_wrap( true ); 489 490 // Add to frame 491 _feature_vbox.pack_start( _feature_grid, Gtk::PACK_SHRINK ); 492 _feature_vbox.pack_start( _feature_entry, Gtk::PACK_SHRINK ); 493 _feature_vbox.pack_start( _feature_label, Gtk::PACK_SHRINK ); 494 _feature_vbox.pack_start( _feature_substitutions, Gtk::PACK_SHRINK ); 495 _feature_vbox.pack_start( _feature_list, Gtk::PACK_SHRINK ); 496 497 _feature_vbox.set_margin_start(15); 498 _feature_vbox.set_margin_end(15); 499 500 _feature_frame.add( _feature_vbox ); 501 pack_start( _feature_frame, Gtk::PACK_SHRINK ); 502 503 // Add signals 504 //_feature_entry.signal_key_press_event().connect ( sigc::mem_fun(*this, &FontVariants::feature_callback) ); 505 _feature_entry.signal_changed().connect( sigc::mem_fun(*this, &FontVariants::feature_callback) ); 506 507 show_all_children(); 508 509 } 510 511 void ligatures_init()512 FontVariants::ligatures_init() { 513 // std::cout << "FontVariants::ligatures_init()" << std::endl; 514 } 515 516 void ligatures_callback()517 FontVariants::ligatures_callback() { 518 // std::cout << "FontVariants::ligatures_callback()" << std::endl; 519 _ligatures_changed = true; 520 _changed_signal.emit(); 521 } 522 523 void position_init()524 FontVariants::position_init() { 525 // std::cout << "FontVariants::position_init()" << std::endl; 526 } 527 528 void position_callback()529 FontVariants::position_callback() { 530 // std::cout << "FontVariants::position_callback()" << std::endl; 531 _position_changed = true; 532 _changed_signal.emit(); 533 } 534 535 void caps_init()536 FontVariants::caps_init() { 537 // std::cout << "FontVariants::caps_init()" << std::endl; 538 } 539 540 void caps_callback()541 FontVariants::caps_callback() { 542 // std::cout << "FontVariants::caps_callback()" << std::endl; 543 _caps_changed = true; 544 _changed_signal.emit(); 545 } 546 547 void numeric_init()548 FontVariants::numeric_init() { 549 // std::cout << "FontVariants::numeric_init()" << std::endl; 550 } 551 552 void numeric_callback()553 FontVariants::numeric_callback() { 554 // std::cout << "FontVariants::numeric_callback()" << std::endl; 555 _numeric_changed = true; 556 _changed_signal.emit(); 557 } 558 559 void asian_init()560 FontVariants::asian_init() { 561 // std::cout << "FontVariants::asian_init()" << std::endl; 562 } 563 564 void asian_callback()565 FontVariants::asian_callback() { 566 // std::cout << "FontVariants::asian_callback()" << std::endl; 567 _asian_changed = true; 568 _changed_signal.emit(); 569 } 570 571 void feature_init()572 FontVariants::feature_init() { 573 // std::cout << "FontVariants::feature_init()" << std::endl; 574 } 575 576 void feature_callback()577 FontVariants::feature_callback() { 578 // std::cout << "FontVariants::feature_callback()" << std::endl; 579 _feature_changed = true; 580 _changed_signal.emit(); 581 } 582 583 // Update GUI based on query. 584 void update(SPStyle const * query,bool different_features,Glib::ustring & font_spec)585 FontVariants::update( SPStyle const *query, bool different_features, Glib::ustring& font_spec ) { 586 587 update_opentype( font_spec ); 588 589 _ligatures_all = query->font_variant_ligatures.computed; 590 _ligatures_mix = query->font_variant_ligatures.value; 591 592 _ligatures_common.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_COMMON ); 593 _ligatures_discretionary.set_active(_ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ); 594 _ligatures_historical.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ); 595 _ligatures_contextual.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL ); 596 597 _ligatures_common.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_COMMON ); 598 _ligatures_discretionary.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ); 599 _ligatures_historical.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ); 600 _ligatures_contextual.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL ); 601 602 _position_all = query->font_variant_position.computed; 603 _position_mix = query->font_variant_position.value; 604 605 _position_normal.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_NORMAL ); 606 _position_sub.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_SUB ); 607 _position_super.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_SUPER ); 608 609 _position_normal.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_NORMAL ); 610 _position_sub.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_SUB ); 611 _position_super.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_SUPER ); 612 613 _caps_all = query->font_variant_caps.computed; 614 _caps_mix = query->font_variant_caps.value; 615 616 _caps_normal.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_NORMAL ); 617 _caps_small.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_SMALL ); 618 _caps_all_small.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ); 619 _caps_petite.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_PETITE ); 620 _caps_all_petite.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ); 621 _caps_unicase.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_UNICASE ); 622 _caps_titling.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_TITLING ); 623 624 _caps_normal.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_NORMAL ); 625 _caps_small.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_SMALL ); 626 _caps_all_small.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ); 627 _caps_petite.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_PETITE ); 628 _caps_all_petite.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ); 629 _caps_unicase.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_UNICASE ); 630 _caps_titling.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_TITLING ); 631 632 _numeric_all = query->font_variant_numeric.computed; 633 _numeric_mix = query->font_variant_numeric.value; 634 635 if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS) { 636 _numeric_lining.set_active(); 637 } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS) { 638 _numeric_old_style.set_active(); 639 } else { 640 _numeric_default_style.set_active(); 641 } 642 643 if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS) { 644 _numeric_proportional.set_active(); 645 } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS) { 646 _numeric_tabular.set_active(); 647 } else { 648 _numeric_default_width.set_active(); 649 } 650 651 if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS) { 652 _numeric_diagonal.set_active(); 653 } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS) { 654 _numeric_stacked.set_active(); 655 } else { 656 _numeric_default_fractions.set_active(); 657 } 658 659 _numeric_ordinal.set_active( _numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ); 660 _numeric_slashed_zero.set_active( _numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ); 661 662 663 _numeric_lining.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS ); 664 _numeric_old_style.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS ); 665 _numeric_proportional.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS ); 666 _numeric_tabular.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS ); 667 _numeric_diagonal.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS ); 668 _numeric_stacked.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS ); 669 _numeric_ordinal.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ); 670 _numeric_slashed_zero.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ); 671 672 _asian_all = query->font_variant_east_asian.computed; 673 _asian_mix = query->font_variant_east_asian.value; 674 675 if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78) { 676 _asian_jis78.set_active(); 677 } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83) { 678 _asian_jis83.set_active(); 679 } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90) { 680 _asian_jis90.set_active(); 681 } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04) { 682 _asian_jis04.set_active(); 683 } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED) { 684 _asian_simplified.set_active(); 685 } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL) { 686 _asian_traditional.set_active(); 687 } else { 688 _asian_default_variant.set_active(); 689 } 690 691 if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH) { 692 _asian_full_width.set_active(); 693 } else if (_asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH) { 694 _asian_proportional_width.set_active(); 695 } else { 696 _asian_default_width.set_active(); 697 } 698 699 _asian_ruby.set_active ( _asian_all & SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY ); 700 701 _asian_jis78.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78); 702 _asian_jis83.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83); 703 _asian_jis90.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90); 704 _asian_jis04.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04); 705 _asian_simplified.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED); 706 _asian_traditional.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL); 707 _asian_full_width.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH); 708 _asian_proportional_width.set_inconsistent(_asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH); 709 _asian_ruby.set_inconsistent( _asian_mix & SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY); 710 711 // Fix me: Should match a space if second part matches. ---, 712 // : Add boundary to 'on' and 'off'. v 713 Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("\"(\\w{4})\"\\s*([0-9]+|on|off|)"); 714 Glib::MatchInfo matchInfo; 715 std::string setting; 716 717 // Set feature radiobutton (if it exists) or add to _feature_entry string. 718 char const *val = query->font_feature_settings.value(); 719 if (val) { 720 721 std::vector<Glib::ustring> tokens = 722 Glib::Regex::split_simple("\\s*,\\s*", val); 723 724 for (auto token: tokens) { 725 regex->match(token, matchInfo); 726 if (matchInfo.matches()) { 727 Glib::ustring table = matchInfo.fetch(1); 728 Glib::ustring value = matchInfo.fetch(2); 729 730 if (_features.find(table) != _features.end()) { 731 int v = 0; 732 if (value == "0" || value == "off") v = 0; 733 else if (value == "1" || value == "on" || value.empty() ) v = 1; 734 else v = std::stoi(value); 735 _features[table]->set_active(v); 736 } else { 737 setting += token + ", "; 738 } 739 } 740 } 741 } 742 743 // Remove final ", " 744 if (setting.length() > 1) { 745 setting.pop_back(); 746 setting.pop_back(); 747 } 748 749 // Tables without radiobuttons. 750 _feature_entry.set_text( setting ); 751 752 if( different_features ) { 753 _feature_label.show(); 754 } else { 755 _feature_label.hide(); 756 } 757 } 758 759 // Update GUI based on OpenType tables of selected font (which may be changed in font selector tab). 760 void update_opentype(Glib::ustring & font_spec)761 FontVariants::update_opentype (Glib::ustring& font_spec) { 762 763 // Disable/Enable based on available OpenType tables. 764 font_instance* res = font_factory::Default()->FaceFromFontSpecification( font_spec.c_str() ); 765 if( res ) { 766 767 std::map<Glib::ustring, OTSubstitution>::iterator it; 768 769 if((it = res->openTypeTables.find("liga"))!= res->openTypeTables.end() || 770 (it = res->openTypeTables.find("clig"))!= res->openTypeTables.end()) { 771 _ligatures_common.set_sensitive(); 772 } else { 773 _ligatures_common.set_sensitive( false ); 774 } 775 776 if((it = res->openTypeTables.find("dlig"))!= res->openTypeTables.end()) { 777 _ligatures_discretionary.set_sensitive(); 778 } else { 779 _ligatures_discretionary.set_sensitive( false ); 780 } 781 782 if((it = res->openTypeTables.find("hlig"))!= res->openTypeTables.end()) { 783 _ligatures_historical.set_sensitive(); 784 } else { 785 _ligatures_historical.set_sensitive( false ); 786 } 787 788 if((it = res->openTypeTables.find("calt"))!= res->openTypeTables.end()) { 789 _ligatures_contextual.set_sensitive(); 790 } else { 791 _ligatures_contextual.set_sensitive( false ); 792 } 793 794 if((it = res->openTypeTables.find("subs"))!= res->openTypeTables.end()) { 795 _position_sub.set_sensitive(); 796 } else { 797 _position_sub.set_sensitive( false ); 798 } 799 800 if((it = res->openTypeTables.find("sups"))!= res->openTypeTables.end()) { 801 _position_super.set_sensitive(); 802 } else { 803 _position_super.set_sensitive( false ); 804 } 805 806 if((it = res->openTypeTables.find("smcp"))!= res->openTypeTables.end()) { 807 _caps_small.set_sensitive(); 808 } else { 809 _caps_small.set_sensitive( false ); 810 } 811 812 if((it = res->openTypeTables.find("c2sc"))!= res->openTypeTables.end() && 813 (it = res->openTypeTables.find("smcp"))!= res->openTypeTables.end()) { 814 _caps_all_small.set_sensitive(); 815 } else { 816 _caps_all_small.set_sensitive( false ); 817 } 818 819 if((it = res->openTypeTables.find("pcap"))!= res->openTypeTables.end()) { 820 _caps_petite.set_sensitive(); 821 } else { 822 _caps_petite.set_sensitive( false ); 823 } 824 825 if((it = res->openTypeTables.find("c2sc"))!= res->openTypeTables.end() && 826 (it = res->openTypeTables.find("pcap"))!= res->openTypeTables.end()) { 827 _caps_all_petite.set_sensitive(); 828 } else { 829 _caps_all_petite.set_sensitive( false ); 830 } 831 832 if((it = res->openTypeTables.find("unic"))!= res->openTypeTables.end()) { 833 _caps_unicase.set_sensitive(); 834 } else { 835 _caps_unicase.set_sensitive( false ); 836 } 837 838 if((it = res->openTypeTables.find("titl"))!= res->openTypeTables.end()) { 839 _caps_titling.set_sensitive(); 840 } else { 841 _caps_titling.set_sensitive( false ); 842 } 843 844 if((it = res->openTypeTables.find("lnum"))!= res->openTypeTables.end()) { 845 _numeric_lining.set_sensitive(); 846 } else { 847 _numeric_lining.set_sensitive( false ); 848 } 849 850 if((it = res->openTypeTables.find("onum"))!= res->openTypeTables.end()) { 851 _numeric_old_style.set_sensitive(); 852 } else { 853 _numeric_old_style.set_sensitive( false ); 854 } 855 856 if((it = res->openTypeTables.find("pnum"))!= res->openTypeTables.end()) { 857 _numeric_proportional.set_sensitive(); 858 } else { 859 _numeric_proportional.set_sensitive( false ); 860 } 861 862 if((it = res->openTypeTables.find("tnum"))!= res->openTypeTables.end()) { 863 _numeric_tabular.set_sensitive(); 864 } else { 865 _numeric_tabular.set_sensitive( false ); 866 } 867 868 if((it = res->openTypeTables.find("frac"))!= res->openTypeTables.end()) { 869 _numeric_diagonal.set_sensitive(); 870 } else { 871 _numeric_diagonal.set_sensitive( false ); 872 } 873 874 if((it = res->openTypeTables.find("afrac"))!= res->openTypeTables.end()) { 875 _numeric_stacked.set_sensitive(); 876 } else { 877 _numeric_stacked.set_sensitive( false ); 878 } 879 880 if((it = res->openTypeTables.find("ordn"))!= res->openTypeTables.end()) { 881 _numeric_ordinal.set_sensitive(); 882 } else { 883 _numeric_ordinal.set_sensitive( false ); 884 } 885 886 if((it = res->openTypeTables.find("zero"))!= res->openTypeTables.end()) { 887 _numeric_slashed_zero.set_sensitive(); 888 } else { 889 _numeric_slashed_zero.set_sensitive( false ); 890 } 891 892 // East-Asian 893 if((it = res->openTypeTables.find("jp78"))!= res->openTypeTables.end()) { 894 _asian_jis78.set_sensitive(); 895 } else { 896 _asian_jis78.set_sensitive( false ); 897 } 898 899 if((it = res->openTypeTables.find("jp83"))!= res->openTypeTables.end()) { 900 _asian_jis83.set_sensitive(); 901 } else { 902 _asian_jis83.set_sensitive( false ); 903 } 904 905 if((it = res->openTypeTables.find("jp90"))!= res->openTypeTables.end()) { 906 _asian_jis90.set_sensitive(); 907 } else { 908 _asian_jis90.set_sensitive( false ); 909 } 910 911 if((it = res->openTypeTables.find("jp04"))!= res->openTypeTables.end()) { 912 _asian_jis04.set_sensitive(); 913 } else { 914 _asian_jis04.set_sensitive( false ); 915 } 916 917 if((it = res->openTypeTables.find("smpl"))!= res->openTypeTables.end()) { 918 _asian_simplified.set_sensitive(); 919 } else { 920 _asian_simplified.set_sensitive( false ); 921 } 922 923 if((it = res->openTypeTables.find("trad"))!= res->openTypeTables.end()) { 924 _asian_traditional.set_sensitive(); 925 } else { 926 _asian_traditional.set_sensitive( false ); 927 } 928 929 if((it = res->openTypeTables.find("fwid"))!= res->openTypeTables.end()) { 930 _asian_full_width.set_sensitive(); 931 } else { 932 _asian_full_width.set_sensitive( false ); 933 } 934 935 if((it = res->openTypeTables.find("pwid"))!= res->openTypeTables.end()) { 936 _asian_proportional_width.set_sensitive(); 937 } else { 938 _asian_proportional_width.set_sensitive( false ); 939 } 940 941 if((it = res->openTypeTables.find("ruby"))!= res->openTypeTables.end()) { 942 _asian_ruby.set_sensitive(); 943 } else { 944 _asian_ruby.set_sensitive( false ); 945 } 946 947 // List available ligatures 948 Glib::ustring markup_liga; 949 Glib::ustring markup_dlig; 950 Glib::ustring markup_hlig; 951 Glib::ustring markup_calt; 952 953 for (auto table: res->openTypeTables) { 954 955 if (table.first == "liga" || 956 table.first == "clig" || 957 table.first == "dlig" || 958 table.first == "hgli" || 959 table.first == "calt") { 960 961 Glib::ustring markup; 962 markup += "<span font_family='"; 963 markup += sp_font_description_get_family(res->descr); 964 markup += "'>"; 965 markup += Glib::Markup::escape_text(table.second.output); 966 markup += "</span>"; 967 968 if (table.first == "liga") markup_liga += markup; 969 if (table.first == "clig") markup_liga += markup; 970 if (table.first == "dlig") markup_dlig += markup; 971 if (table.first == "hlig") markup_hlig += markup; 972 if (table.first == "calt") markup_calt += markup; 973 } 974 } 975 976 _ligatures_label_common.set_markup ( markup_liga.c_str() ); 977 _ligatures_label_discretionary.set_markup ( markup_dlig.c_str() ); 978 _ligatures_label_historical.set_markup ( markup_hlig.c_str() ); 979 _ligatures_label_contextual.set_markup ( markup_calt.c_str() ); 980 981 // List available numeric variants 982 Glib::ustring markup_lnum; 983 Glib::ustring markup_onum; 984 Glib::ustring markup_pnum; 985 Glib::ustring markup_tnum; 986 Glib::ustring markup_frac; 987 Glib::ustring markup_afrc; 988 Glib::ustring markup_ordn; 989 Glib::ustring markup_zero; 990 991 for (auto table: res->openTypeTables) { 992 993 Glib::ustring markup; 994 markup += "<span font_family='"; 995 markup += sp_font_description_get_family(res->descr); 996 markup += "' font_features='"; 997 markup += table.first; 998 markup += "'>"; 999 if (table.first == "lnum" || 1000 table.first == "onum" || 1001 table.first == "pnum" || 1002 table.first == "tnum") markup += "0123456789"; 1003 if (table.first == "zero") markup += "0"; 1004 if (table.first == "ordn") markup += "[" + table.second.before + "]" + table.second.output; 1005 if (table.first == "frac" || 1006 table.first == "afrc" ) markup += "1/2 2/3 3/4 4/5 5/6"; // Can we do better? 1007 markup += "</span>"; 1008 1009 if (table.first == "lnum") markup_lnum += markup; 1010 if (table.first == "onum") markup_onum += markup; 1011 if (table.first == "pnum") markup_pnum += markup; 1012 if (table.first == "tnum") markup_tnum += markup; 1013 if (table.first == "frac") markup_frac += markup; 1014 if (table.first == "afrc") markup_afrc += markup; 1015 if (table.first == "ordn") markup_ordn += markup; 1016 if (table.first == "zero") markup_zero += markup; 1017 } 1018 1019 _numeric_lining_label.set_markup ( markup_lnum.c_str() ); 1020 _numeric_old_style_label.set_markup ( markup_onum.c_str() ); 1021 _numeric_proportional_label.set_markup ( markup_pnum.c_str() ); 1022 _numeric_tabular_label.set_markup ( markup_tnum.c_str() ); 1023 _numeric_diagonal_label.set_markup ( markup_frac.c_str() ); 1024 _numeric_stacked_label.set_markup ( markup_afrc.c_str() ); 1025 _numeric_ordinal_label.set_markup ( markup_ordn.c_str() ); 1026 _numeric_slashed_zero_label.set_markup ( markup_zero.c_str() ); 1027 1028 // Make list of tables not handled above. 1029 std::map<Glib::ustring, OTSubstitution> table_copy = res->openTypeTables; 1030 if( (it = table_copy.find("liga")) != table_copy.end() ) table_copy.erase( it ); 1031 if( (it = table_copy.find("clig")) != table_copy.end() ) table_copy.erase( it ); 1032 if( (it = table_copy.find("dlig")) != table_copy.end() ) table_copy.erase( it ); 1033 if( (it = table_copy.find("hlig")) != table_copy.end() ) table_copy.erase( it ); 1034 if( (it = table_copy.find("calt")) != table_copy.end() ) table_copy.erase( it ); 1035 1036 if( (it = table_copy.find("subs")) != table_copy.end() ) table_copy.erase( it ); 1037 if( (it = table_copy.find("sups")) != table_copy.end() ) table_copy.erase( it ); 1038 1039 if( (it = table_copy.find("smcp")) != table_copy.end() ) table_copy.erase( it ); 1040 if( (it = table_copy.find("c2sc")) != table_copy.end() ) table_copy.erase( it ); 1041 if( (it = table_copy.find("pcap")) != table_copy.end() ) table_copy.erase( it ); 1042 if( (it = table_copy.find("c2pc")) != table_copy.end() ) table_copy.erase( it ); 1043 if( (it = table_copy.find("unic")) != table_copy.end() ) table_copy.erase( it ); 1044 if( (it = table_copy.find("titl")) != table_copy.end() ) table_copy.erase( it ); 1045 1046 if( (it = table_copy.find("lnum")) != table_copy.end() ) table_copy.erase( it ); 1047 if( (it = table_copy.find("onum")) != table_copy.end() ) table_copy.erase( it ); 1048 if( (it = table_copy.find("pnum")) != table_copy.end() ) table_copy.erase( it ); 1049 if( (it = table_copy.find("tnum")) != table_copy.end() ) table_copy.erase( it ); 1050 if( (it = table_copy.find("frac")) != table_copy.end() ) table_copy.erase( it ); 1051 if( (it = table_copy.find("afrc")) != table_copy.end() ) table_copy.erase( it ); 1052 if( (it = table_copy.find("ordn")) != table_copy.end() ) table_copy.erase( it ); 1053 if( (it = table_copy.find("zero")) != table_copy.end() ) table_copy.erase( it ); 1054 1055 if( (it = table_copy.find("jp78")) != table_copy.end() ) table_copy.erase( it ); 1056 if( (it = table_copy.find("jp83")) != table_copy.end() ) table_copy.erase( it ); 1057 if( (it = table_copy.find("jp90")) != table_copy.end() ) table_copy.erase( it ); 1058 if( (it = table_copy.find("jp04")) != table_copy.end() ) table_copy.erase( it ); 1059 if( (it = table_copy.find("smpl")) != table_copy.end() ) table_copy.erase( it ); 1060 if( (it = table_copy.find("trad")) != table_copy.end() ) table_copy.erase( it ); 1061 if( (it = table_copy.find("fwid")) != table_copy.end() ) table_copy.erase( it ); 1062 if( (it = table_copy.find("pwid")) != table_copy.end() ) table_copy.erase( it ); 1063 if( (it = table_copy.find("ruby")) != table_copy.end() ) table_copy.erase( it ); 1064 1065 // An incomplete list of tables that should not be exposed to the user: 1066 if( (it = table_copy.find("abvf")) != table_copy.end() ) table_copy.erase( it ); 1067 if( (it = table_copy.find("abvs")) != table_copy.end() ) table_copy.erase( it ); 1068 if( (it = table_copy.find("akhn")) != table_copy.end() ) table_copy.erase( it ); 1069 if( (it = table_copy.find("blwf")) != table_copy.end() ) table_copy.erase( it ); 1070 if( (it = table_copy.find("blws")) != table_copy.end() ) table_copy.erase( it ); 1071 if( (it = table_copy.find("ccmp")) != table_copy.end() ) table_copy.erase( it ); 1072 if( (it = table_copy.find("cjct")) != table_copy.end() ) table_copy.erase( it ); 1073 if( (it = table_copy.find("dnom")) != table_copy.end() ) table_copy.erase( it ); 1074 if( (it = table_copy.find("dtls")) != table_copy.end() ) table_copy.erase( it ); 1075 if( (it = table_copy.find("fina")) != table_copy.end() ) table_copy.erase( it ); 1076 if( (it = table_copy.find("half")) != table_copy.end() ) table_copy.erase( it ); 1077 if( (it = table_copy.find("haln")) != table_copy.end() ) table_copy.erase( it ); 1078 if( (it = table_copy.find("init")) != table_copy.end() ) table_copy.erase( it ); 1079 if( (it = table_copy.find("isol")) != table_copy.end() ) table_copy.erase( it ); 1080 if( (it = table_copy.find("locl")) != table_copy.end() ) table_copy.erase( it ); 1081 if( (it = table_copy.find("medi")) != table_copy.end() ) table_copy.erase( it ); 1082 if( (it = table_copy.find("nukt")) != table_copy.end() ) table_copy.erase( it ); 1083 if( (it = table_copy.find("numr")) != table_copy.end() ) table_copy.erase( it ); 1084 if( (it = table_copy.find("pref")) != table_copy.end() ) table_copy.erase( it ); 1085 if( (it = table_copy.find("pres")) != table_copy.end() ) table_copy.erase( it ); 1086 if( (it = table_copy.find("pstf")) != table_copy.end() ) table_copy.erase( it ); 1087 if( (it = table_copy.find("psts")) != table_copy.end() ) table_copy.erase( it ); 1088 if( (it = table_copy.find("rlig")) != table_copy.end() ) table_copy.erase( it ); 1089 if( (it = table_copy.find("rkrf")) != table_copy.end() ) table_copy.erase( it ); 1090 if( (it = table_copy.find("rphf")) != table_copy.end() ) table_copy.erase( it ); 1091 if( (it = table_copy.find("rtlm")) != table_copy.end() ) table_copy.erase( it ); 1092 if( (it = table_copy.find("ssty")) != table_copy.end() ) table_copy.erase( it ); 1093 if( (it = table_copy.find("vatu")) != table_copy.end() ) table_copy.erase( it ); 1094 1095 // Clear out old features 1096 auto children = _feature_grid.get_children(); 1097 for (auto child: children) { 1098 _feature_grid.remove (*child); 1099 } 1100 _features.clear(); 1101 1102 std::string markup; 1103 int grid_row = 0; 1104 1105 // GSUB lookup type 1 (1 to 1 mapping). 1106 for (auto table: res->openTypeTables) { 1107 if (table.first == "case" || 1108 table.first == "hist" || 1109 (table.first[0] == 's' && table.first[1] == 's' && !(table.first[2] == 't'))) { 1110 1111 if( (it = table_copy.find(table.first)) != table_copy.end() ) table_copy.erase( it ); 1112 1113 _features[table.first] = new Feature (table.first, table.second, 2, 1114 sp_font_description_get_family(res->descr), 1115 _feature_grid, grid_row, this); 1116 grid_row++; 1117 } 1118 } 1119 1120 // GSUB lookup type 3 (1 to many mapping). Optionally type 1. 1121 for (auto table: res->openTypeTables) { 1122 if (table.first == "salt" || 1123 table.first == "swsh" || 1124 table.first == "cwsh" || 1125 table.first == "ornm" || 1126 table.first == "nalt" || 1127 (table.first[0] == 'c' && table.first[1] == 'v')) { 1128 1129 if (table.second.input.length() == 0) { 1130 // This can happen if a table is not in the 'DFLT' script and 'dflt' language. 1131 // We should be using the 'lang' attribute to find the correct tables. 1132 // std::cerr << "FontVariants::open_type_update: " 1133 // << table.first << " has no entries!" << std::endl; 1134 continue; 1135 } 1136 1137 if( (it = table_copy.find(table.first)) != table_copy.end() ) table_copy.erase( it ); 1138 1139 // Our lame attempt at determining number of alternative glyphs for one glyph: 1140 int number = table.second.output.length() / table.second.input.length(); 1141 if (number < 1) { 1142 number = 1; // Must have at least on/off, see comment above about 'lang' attribute. 1143 // std::cout << table.first << " " 1144 // << table.second.output.length() << "/" 1145 // << table.second.input.length() << "=" 1146 // << number << std::endl; 1147 } 1148 1149 _features[table.first] = new Feature (table.first, table.second, number+1, 1150 sp_font_description_get_family(res->descr), 1151 _feature_grid, grid_row, this); 1152 grid_row++; 1153 } 1154 } 1155 1156 _feature_grid.show_all(); 1157 1158 _feature_substitutions.set_markup ( markup.c_str() ); 1159 1160 std::string ott_list = "OpenType tables not included above: "; 1161 for(it = table_copy.begin(); it != table_copy.end(); ++it) { 1162 ott_list += it->first; 1163 ott_list += ", "; 1164 } 1165 1166 if (table_copy.size() > 0) { 1167 ott_list.pop_back(); 1168 ott_list.pop_back(); 1169 _feature_list.set_text( ott_list.c_str() ); 1170 } else { 1171 _feature_list.set_text( "" ); 1172 } 1173 1174 } else { 1175 std::cerr << "FontVariants::update(): Couldn't find font_instance for: " 1176 << font_spec << std::endl; 1177 } 1178 1179 _ligatures_changed = false; 1180 _position_changed = false; 1181 _caps_changed = false; 1182 _numeric_changed = false; 1183 _feature_changed = false; 1184 } 1185 1186 void fill_css(SPCSSAttr * css)1187 FontVariants::fill_css( SPCSSAttr *css ) { 1188 1189 // Ligatures 1190 bool common = _ligatures_common.get_active(); 1191 bool discretionary = _ligatures_discretionary.get_active(); 1192 bool historical = _ligatures_historical.get_active(); 1193 bool contextual = _ligatures_contextual.get_active(); 1194 1195 if( !common && !discretionary && !historical && !contextual ) { 1196 sp_repr_css_set_property(css, "font-variant-ligatures", "none" ); 1197 } else if ( common && !discretionary && !historical && contextual ) { 1198 sp_repr_css_set_property(css, "font-variant-ligatures", "normal" ); 1199 } else { 1200 Glib::ustring css_string; 1201 if ( !common ) 1202 css_string += "no-common-ligatures "; 1203 if ( discretionary ) 1204 css_string += "discretionary-ligatures "; 1205 if ( historical ) 1206 css_string += "historical-ligatures "; 1207 if ( !contextual ) 1208 css_string += "no-contextual "; 1209 sp_repr_css_set_property(css, "font-variant-ligatures", css_string.c_str() ); 1210 } 1211 1212 // Position 1213 { 1214 unsigned position_new = SP_CSS_FONT_VARIANT_POSITION_NORMAL; 1215 Glib::ustring css_string; 1216 if( _position_normal.get_active() ) { 1217 css_string = "normal"; 1218 } else if( _position_sub.get_active() ) { 1219 css_string = "sub"; 1220 position_new = SP_CSS_FONT_VARIANT_POSITION_SUB; 1221 } else if( _position_super.get_active() ) { 1222 css_string = "super"; 1223 position_new = SP_CSS_FONT_VARIANT_POSITION_SUPER; 1224 } 1225 1226 // 'if' may not be necessary... need to test. 1227 if( (_position_all != position_new) || ((_position_mix != 0) && _position_changed) ) { 1228 sp_repr_css_set_property(css, "font-variant-position", css_string.c_str() ); 1229 } 1230 } 1231 1232 // Caps 1233 { 1234 //unsigned caps_new; 1235 Glib::ustring css_string; 1236 if( _caps_normal.get_active() ) { 1237 css_string = "normal"; 1238 // caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; 1239 } else if( _caps_small.get_active() ) { 1240 css_string = "small-caps"; 1241 // caps_new = SP_CSS_FONT_VARIANT_CAPS_SMALL; 1242 } else if( _caps_all_small.get_active() ) { 1243 css_string = "all-small-caps"; 1244 // caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL; 1245 } else if( _caps_petite.get_active() ) { 1246 css_string = "petite"; 1247 // caps_new = SP_CSS_FONT_VARIANT_CAPS_PETITE; 1248 } else if( _caps_all_petite.get_active() ) { 1249 css_string = "all-petite"; 1250 // caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE; 1251 } else if( _caps_unicase.get_active() ) { 1252 css_string = "unicase"; 1253 // caps_new = SP_CSS_FONT_VARIANT_CAPS_UNICASE; 1254 } else if( _caps_titling.get_active() ) { 1255 css_string = "titling"; 1256 // caps_new = SP_CSS_FONT_VARIANT_CAPS_TITLING; 1257 //} else { 1258 // caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; 1259 } 1260 1261 // May not be necessary... need to test. 1262 //if( (_caps_all != caps_new) || ((_caps_mix != 0) && _caps_changed) ) { 1263 sp_repr_css_set_property(css, "font-variant-caps", css_string.c_str() ); 1264 //} 1265 } 1266 1267 // Numeric 1268 bool default_style = _numeric_default_style.get_active(); 1269 bool lining = _numeric_lining.get_active(); 1270 bool old_style = _numeric_old_style.get_active(); 1271 1272 bool default_width = _numeric_default_width.get_active(); 1273 bool proportional = _numeric_proportional.get_active(); 1274 bool tabular = _numeric_tabular.get_active(); 1275 1276 bool default_fractions = _numeric_default_fractions.get_active(); 1277 bool diagonal = _numeric_diagonal.get_active(); 1278 bool stacked = _numeric_stacked.get_active(); 1279 1280 bool ordinal = _numeric_ordinal.get_active(); 1281 bool slashed_zero = _numeric_slashed_zero.get_active(); 1282 1283 if (default_style & default_width & default_fractions & !ordinal & !slashed_zero) { 1284 sp_repr_css_set_property(css, "font-variant-numeric", "normal"); 1285 } else { 1286 Glib::ustring css_string; 1287 if ( lining ) 1288 css_string += "lining-nums "; 1289 if ( old_style ) 1290 css_string += "oldstyle-nums "; 1291 if ( proportional ) 1292 css_string += "proportional-nums "; 1293 if ( tabular ) 1294 css_string += "tabular-nums "; 1295 if ( diagonal ) 1296 css_string += "diagonal-fractions "; 1297 if ( stacked ) 1298 css_string += "stacked-fractions "; 1299 if ( ordinal ) 1300 css_string += "ordinal "; 1301 if ( slashed_zero ) 1302 css_string += "slashed-zero "; 1303 sp_repr_css_set_property(css, "font-variant-numeric", css_string.c_str() ); 1304 } 1305 1306 // East Asian 1307 bool jis78 = _asian_jis78.get_active(); 1308 bool jis83 = _asian_jis83.get_active(); 1309 bool jis90 = _asian_jis90.get_active(); 1310 bool jis04 = _asian_jis04.get_active(); 1311 bool simplified = _asian_simplified.get_active(); 1312 bool traditional = _asian_traditional.get_active(); 1313 bool asian_width = _asian_default_width.get_active(); 1314 bool fwid = _asian_full_width.get_active(); 1315 bool pwid = _asian_proportional_width.get_active(); 1316 bool ruby = _asian_ruby.get_active(); 1317 1318 if (default_style & asian_width & !ruby) { 1319 sp_repr_css_set_property(css, "font-variant-east-asian", "normal"); 1320 } else { 1321 Glib::ustring css_string; 1322 if (jis78) css_string += "jis78 "; 1323 if (jis83) css_string += "jis83 "; 1324 if (jis90) css_string += "jis90 "; 1325 if (jis04) css_string += "jis04 "; 1326 if (simplified) css_string += "simplfied "; 1327 if (traditional) css_string += "traditional "; 1328 1329 if (fwid) css_string += "fwid "; 1330 if (pwid) css_string += "pwid "; 1331 1332 if (ruby) css_string += "ruby "; 1333 1334 sp_repr_css_set_property(css, "font-variant-east-asian", css_string.c_str() ); 1335 } 1336 1337 // Feature settings 1338 Glib::ustring feature_string; 1339 for (auto i: _features) { 1340 feature_string += i.second->get_css(); 1341 } 1342 1343 feature_string += _feature_entry.get_text(); 1344 // std::cout << "feature_string: " << feature_string << std::endl; 1345 1346 if (!feature_string.empty()) { 1347 sp_repr_css_set_property(css, "font-feature-settings", feature_string.c_str()); 1348 } else { 1349 sp_repr_css_unset_property(css, "font-feature-settings"); 1350 } 1351 } 1352 1353 Glib::ustring get_markup()1354 FontVariants::get_markup() { 1355 1356 Glib::ustring markup; 1357 1358 // Ligatures 1359 bool common = _ligatures_common.get_active(); 1360 bool discretionary = _ligatures_discretionary.get_active(); 1361 bool historical = _ligatures_historical.get_active(); 1362 bool contextual = _ligatures_contextual.get_active(); 1363 1364 if (!common) markup += "liga=0,clig=0,"; // On by default. 1365 if (discretionary) markup += "dlig=1,"; 1366 if (historical) markup += "hlig=1,"; 1367 if (contextual) markup += "calt=1,"; 1368 1369 // Position 1370 if ( _position_sub.get_active() ) markup += "subs=1,"; 1371 else if ( _position_super.get_active() ) markup += "sups=1,"; 1372 1373 // Caps 1374 if ( _caps_small.get_active() ) markup += "smcp=1,"; 1375 else if ( _caps_all_small.get_active() ) markup += "c2sc=1,smcp=1,"; 1376 else if ( _caps_petite.get_active() ) markup += "pcap=1,"; 1377 else if ( _caps_all_petite.get_active() ) markup += "c2pc=1,pcap=1,"; 1378 else if ( _caps_unicase.get_active() ) markup += "unic=1,"; 1379 else if ( _caps_titling.get_active() ) markup += "titl=1,"; 1380 1381 // Numeric 1382 bool lining = _numeric_lining.get_active(); 1383 bool old_style = _numeric_old_style.get_active(); 1384 1385 bool proportional = _numeric_proportional.get_active(); 1386 bool tabular = _numeric_tabular.get_active(); 1387 1388 bool diagonal = _numeric_diagonal.get_active(); 1389 bool stacked = _numeric_stacked.get_active(); 1390 1391 bool ordinal = _numeric_ordinal.get_active(); 1392 bool slashed_zero = _numeric_slashed_zero.get_active(); 1393 1394 if (lining) markup += "lnum=1,"; 1395 if (old_style) markup += "onum=1,"; 1396 if (proportional) markup += "pnum=1,"; 1397 if (tabular) markup += "tnum=1,"; 1398 if (diagonal) markup += "frac=1,"; 1399 if (stacked) markup += "afrc=1,"; 1400 if (ordinal) markup += "ordn=1,"; 1401 if (slashed_zero) markup += "zero=1,"; 1402 1403 // East Asian 1404 bool jis78 = _asian_jis78.get_active(); 1405 bool jis83 = _asian_jis83.get_active(); 1406 bool jis90 = _asian_jis90.get_active(); 1407 bool jis04 = _asian_jis04.get_active(); 1408 bool simplified = _asian_simplified.get_active(); 1409 bool traditional = _asian_traditional.get_active(); 1410 //bool asian_width = _asian_default_width.get_active(); 1411 bool fwid = _asian_full_width.get_active(); 1412 bool pwid = _asian_proportional_width.get_active(); 1413 bool ruby = _asian_ruby.get_active(); 1414 1415 if (jis78 ) markup += "jp78=1,"; 1416 if (jis83 ) markup += "jp83=1,"; 1417 if (jis90 ) markup += "jp90=1,"; 1418 if (jis04 ) markup += "jp04=1,"; 1419 if (simplified ) markup += "smpl=1,"; 1420 if (traditional ) markup += "trad=1,"; 1421 1422 if (fwid ) markup += "fwid=1,"; 1423 if (pwid ) markup += "pwid=1,"; 1424 1425 if (ruby ) markup += "ruby=1,"; 1426 1427 // Feature settings 1428 Glib::ustring feature_string; 1429 for (auto i: _features) { 1430 feature_string += i.second->get_css(); 1431 } 1432 1433 feature_string += _feature_entry.get_text(); 1434 if (!feature_string.empty()) { 1435 markup += feature_string; 1436 } 1437 1438 // std::cout << "|" << markup << "|" << std::endl; 1439 return markup; 1440 } 1441 1442 } // namespace Widget 1443 } // namespace UI 1444 } // namespace Inkscape 1445 1446 /* 1447 Local Variables: 1448 mode:c++ 1449 c-file-style:"stroustrup" 1450 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) 1451 indent-tabs-mode:nil 1452 fill-column:99 1453 End: 1454 */ 1455 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : 1456