22#include <glibmm/i18n.h>
23#include <glibmm/markup.h>
24#include <glibmm/stringutils.h>
25#include <giomm/themedicon.h>
26#include <gtkmm/expander.h>
27#include <gtkmm/image.h>
28#include <gtkmm/liststore.h>
29#include <gtkmm/notebook.h>
30#include <gtkmm/scale.h>
31#include <gtkmm/snapshot.h>
32#include <gtkmm/togglebutton.h>
33#include <sigc++/functors/mem_fun.h>
61 set_name(
"SVGFontDrawingArea");
70 _text = std::move(text);
77 set_size_request(
_x,
_y);
89 cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(
new Cairo::FontFace(
_svgfont->
get_font_face(),
false )) );
90 cr->set_font_size (
_y-20);
92 auto const fg = get_color();
93 cr->set_source_rgb(fg.get_red(), fg.get_green(), fg.get_blue());
96 cr->show_text(
_text.c_str());
97 }
catch (std::exception
const &ex) {
98 g_warning(
"Error drawing custom SVG font text: %s", ex.what());
105 Glib::RefPtr<Gtk::Snapshot>
const &snapshot, Gtk::Widget& widget,
106 const Gdk::Rectangle& background_area,
const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags) {
110 auto const cr = snapshot->append_cairo(background_area);
112 cr->set_font_face(Cairo::RefPtr<Cairo::FontFace>(
new Cairo::FontFace(
_font->
get_font_face(),
false )));
115 Cairo::TextExtents ext;
116 cr->get_text_extents(glyph, ext);
117 cr->move_to(cell_area.get_x() + (cell_area.get_width() - ext.width) / 2, cell_area.get_y() + (cell_area.get_height() + ext.height) / 2);
119 auto const selected = (flags & Gtk::CellRendererState::SELECTED) != Gtk::CellRendererState{};
121 if (!
_tree->get_style_context()->lookup_color(selected ?
"theme_selected_fg_color" :
"theme_fg_color", fg)) {
123 fg = Gdk::RGBA{
"#000"};
125 cr->set_source_rgb(fg.get_red(), fg.get_green(), fg.get_blue());
129 cr->show_text(glyph);
130 }
catch (std::exception
const &ex) {
131 g_warning(
"Error drawing custom SVG font glyphs: %s", ex.what());
136 Glib::RefPtr<Gdk::Event const>
const &event,
137 Gtk::Widget &widget, Glib::ustring
const &path, Gdk::Rectangle
const &background_area,
138 const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags) {
149 entry.set_tooltip_text(tooltip);
150 _label = Gtk::make_managed<Gtk::Label>(lbl);
151 _label->set_visible(
true);
152 _label->set_halign(Gtk::Align::START);
163 if (dialog->_update.pending())
return;
166 for (
auto&
node: dialog->get_selected_spfont()->children) {
169 if (is<SPFontFace>(&
node)){
184 Glib::ustring undokey =
"svgfonts:";
195 spin.set_tooltip_text(tooltip);
196 spin.set_visible(
true);
197 _label = Gtk::make_managed<Gtk::Label>(lbl);
198 _label->set_visible(
true);
199 _label->set_halign(Gtk::Align::START);
200 spin.set_range(0, 4096);
201 spin.set_increments(10, 0);
206 spin.set_range(low, high);
214 if (dialog->_update.pending())
return;
217 switch (this->attr) {
226 o = this->dialog->get_selected_spfont();
235 for (
auto&
node: dialog->get_selected_spfont()->children){
236 if (is<SPFontFace>(&
node)){
249 std::ostringstream temp;
250 temp << this->spin.get_value();
254 Glib::ustring undokey =
"svgfonts:";
262 auto const hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
263 hbox->append(*Gtk::make_managed<Gtk::Label>(lbl));
264 hbox->append(*Gtk::make_managed<Gtk::ComboBox>());
271 _label.set_width_chars(2);
273 auto const arrow = Gtk::make_managed<Gtk::Image>(Gio::ThemedIcon::create(
"pan-down-symbolic"));
274 arrow->add_css_class(
"arrow");
276 auto const box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 2);
294 auto const &font_nodes = spfont->
children;
295 if (font_nodes.empty())
return;
299 Glib::ustring active_label;
301 for (
auto const &
node: font_nodes) {
302 if (!is<SPGlyph>(&
node))
continue;
304 Glib::ustring text =
static_cast<SPGlyph const *
>(&
node)->unicode;
305 if (text.empty())
continue;
307 builder.add_item(text, {}, {},
true,
false,
308 [=,
this]{
_label.set_label(text); });
309 if (active_label.empty()) active_label = std::move(text);
313 _label.set_label(active_label);
318 return _label.get_label();
327 Glib::ustring undokey =
"svgfonts:hkern:k:";
355 std::ostringstream ost;
356 if (unicode.empty()) {
360 auto it = unicode.begin();
361 for (
int i = 0; i < max_chars && it != unicode.end(); ++i) {
365 unsigned int code = *it++;
366 ost <<
"U+" << std::hex << std::uppercase << std::setw(6) << std::setfill(
'0') << code;
368 if (it != unicode.end()) {
379 return unicode_name +
" " + glyph.
unicode;
399 auto root = root_layer ==
nullptr ? layers.currentRoot() : root_layer;
400 if (!
root)
return nullptr;
403 auto it = std::find_if(
root->children.begin(),
root->children.end(), [&](
SPObject& obj) {
404 return layers.isLayer(&obj) && obj.label() && strcmp(obj.label(), name.c_str()) == 0;
406 if (it !=
root->children.end()) {
407 return static_cast<SPItem*
>(&*it);
414 std::vector<SPGroup*> layers;
415 if (!layer)
return layers;
427 if (!
desktop || !layer || font.empty() ||
name.empty())
return;
430 if (!parent_layer)
return;
435 auto it = std::lower_bound(glyph_layers.rbegin(), glyph_layers.rend(),
name, [&](
auto&& layer,
const Glib::ustring n) {
436 auto label = layer->label();
437 if (!label) return false;
439 Glib::ustring temp(label);
440 return std::lexicographical_compare(temp.begin(), temp.end(), n.begin(), n.end());
443 if (it != glyph_layers.rend()) {
448 if (layer != after && parent_layer->
getRepr() && layer->
getRepr()) {
456 if (!
desktop ||
name.empty() || font.empty())
return nullptr;
459 if (!parent_layer)
return nullptr;
465 if (!
desktop ||
name.empty() || font.empty())
return nullptr;
472 if (!parent_layer)
return nullptr;
474 layers.renameLayer(parent_layer, font.c_str(),
false);
484 auto it = std::lower_bound(glyph_layers.rbegin(), glyph_layers.rend(),
name, [&](
auto&& layer,
const Glib::ustring n) {
485 auto label = layer->label();
486 if (!label) return false;
488 Glib::ustring temp(label);
489 return std::lexicographical_compare(temp.begin(), temp.end(), n.begin(), n.end());
493 if (it != glyph_layers.rend()) {
500 if (!glyph_layers.empty()) {
501 insert = glyph_layers.front();
508 if (!layer)
return nullptr;
510 layers.renameLayer(layer,
name.c_str(),
false);
513 return cast<SPItem>(layer);
518 _grid.set_sensitive(
true);
522 _grid.set_sensitive(
false);
529 if (!font)
return Glib::ustring();
532 const gchar*
id = font->
getId();
533 return Glib::ustring(
label ?
label : (
id ? id :
"font"));
542 std::vector<SPObject*> fonts;
547 auto children =
_model->children();
549 bool selected_font =
false;
552 if (!document_replaced && children.size() == fonts.size()) {
554 auto it = fonts.begin();
555 for (
auto&&
node : children) {
557 if (it == fonts.end() || *it != sp_font) {
569 for (
auto font : fonts) {
570 Gtk::TreeModel::Row row = *
_model->append();
571 auto f = cast<SPFont>(font);
576 if (!fonts.empty()) {
581 selected_font =
true;
587 auto it = fonts.begin();
588 for (
auto&&
node : children) {
589 if (
auto font = cast<SPFont>(*it++)) {
595 if (document_replaced && !selected_font) {
638 if (is<SPFontFace>(&obj)){
641 _ascent_spin->set_value((cast<SPFontFace>(&obj))->ascent);
687 Gtk::TreeModel::iterator i =
_FontsList.get_selection()->get_selected();
695 Gtk::TreeModel::iterator i =
_FontsList.get_selection()->get_selected();
704 Gtk::TreeModel::iterator it =
selection->get_selected();
709 std::vector<Gtk::TreePath> selected =
_glyphs_grid.get_selected_items();
710 if (selected.size() == 1) {
715 return Gtk::TreeModel::iterator();
729 _GlyphsListStore->foreach_iter([=,
this](Gtk::TreeModel::iterator
const &it) {
731 if (auto selection = _GlyphsList.get_selection()) {
732 selection->select(it);
744 if (!
object)
return nullptr;
747 if (
auto guide = cast<SPGuide>(
object)) {
761 if (!document || em <= 0)
return;
765 bool change_size =
false;
771 double ascPos = base + asc;
772 double capPos = base + cap;
773 double xPos = base + xheight;
774 double desPos = base - des;
777 base =
size.quantity - des;
780 xPos = base - xheight;
785 struct {
double pos;
const char*
name;
const char* id; } guides[5] = {
786 {ascPos, _(
"ascender"),
"ink-font-guide-ascender"},
787 {capPos, _(
"caps"),
"ink-font-guide-caps"},
788 {xPos, _(
"x-height"),
"ink-font-guide-x-height"},
789 {base, _(
"baseline"),
"ink-font-guide-baseline"},
790 {desPos, _(
"descender"),
"ink-font-guide-descender"},
796 for (
auto&& g : guides) {
797 double y = em - g.pos;
800 guide->set_locked(
false,
true);
805 guide->getRepr()->setAttributeOrRemoveIfEmpty(
"id", g.id);
807 guide->set_label(g.name,
true);
808 guide->set_locked(
true,
true);
816Gtk::Box* SvgFontsDialog::global_settings_tab(){
817 _fonts_scroller.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
818 _fonts_scroller.set_child(_FontsList);
819 _fonts_scroller.set_hexpand();
820 _fonts_scroller.set_visible(
true);
824 _header_box.attach(_fonts_scroller, 0, 0, 1, 3);
825 _header_box.attach(*Gtk::make_managed<Gtk::Label>(), 1, 0);
826 _header_box.attach(_font_add, 1, 1);
827 _header_box.attach(_font_remove, 1, 2);
831 _font_add.set_valign(Gtk::Align::CENTER);
832 _font_add.set_image_from_icon_name(
"list-add", Gtk::IconSize::NORMAL);
833 _font_add.signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::add_font));
835 _font_remove.set_valign(Gtk::Align::CENTER);
836 _font_remove.set_halign(Gtk::Align::CENTER);
837 _font_remove.set_image_from_icon_name(
"list-remove", Gtk::IconSize::NORMAL);
838 _font_remove.signal_clicked().connect([
this]{ remove_selected_font(); });
840 global_vbox.set_name(
"SVGFontsGlobalSettingsTab");
843 _font_label = Gtk::make_managed<Gtk::Label>(Glib::ustring::compose(
"<b>%1</b>", _(
"Font Attributes")), Gtk::Align::START, Gtk::Align::CENTER);
844 _horiz_adv_x_spin = std::make_unique <AttrSpin >(
this, _(
"Horizontal advance X:"), _(
"Default glyph width for horizontal text" ),
SPAttr::HORIZ_ADV_X );
845 _horiz_origin_x_spin = std::make_unique <AttrSpin >(
this, _(
"Horizontal origin X:" ), _(
"Default X-coordinate of the origin of a glyph (for horizontal text)"),
SPAttr::HORIZ_ORIGIN_X);
846 _horiz_origin_y_spin = std::make_unique <AttrSpin >(
this, _(
"Horizontal origin Y:" ), _(
"Default Y-coordinate of the origin of a glyph (for horizontal text)"),
SPAttr::HORIZ_ORIGIN_Y);
847 _font_face_label = Gtk::make_managed<Gtk::Label>(Glib::ustring::compose(
"<b>%1</b>", _(
"Font face attributes")), Gtk::Align::START, Gtk::Align::CENTER);
848 _familyname_entry = std::make_unique <AttrEntry >(
this, _(
"Family name:" ), _(
"Name of the font as it appears in font selectors and css font-family properties"),
SPAttr::FONT_FAMILY );
849 _units_per_em_spin = std::make_unique <AttrSpin >(
this, _(
"Em-size:" ), _(
"Display units per <italic>em</italic> (nominally width of 'M' character)" ),
SPAttr::UNITS_PER_EM);
850 _ascent_spin = std::make_unique <AttrSpin >(
this, _(
"Ascender:" ), _(
"Amount of space taken up by ascenders like the tall line on the letter 'h'" ),
SPAttr::ASCENT );
851 _cap_height_spin = std::make_unique <AttrSpin >(
this, _(
"Caps height:" ), _(
"The height of a capital letter above the baseline like the letter 'H' or 'I'" ),
SPAttr::CAP_HEIGHT );
852 _x_height_spin = std::make_unique <AttrSpin >(
this, _(
"x-height:" ), _(
"The height of a lower-case letter above the baseline like the letter 'x'" ),
SPAttr::X_HEIGHT );
853 _descent_spin = std::make_unique <AttrSpin >(
this, _(
"Descender:" ), _(
"Amount of space taken up by descenders like the tail on the letter 'g'" ),
SPAttr::DESCENT );
855 _font_label->set_use_markup();
856 _font_face_label->set_use_markup();
865 _grid.attach(*_font_label, 0, row++, 2);
866 for (
auto const spin: {_horiz_adv_x_spin.get(),
867 _horiz_origin_x_spin.get(), _horiz_origin_y_spin.get()})
869 spin->get_label()->set_margin_start(indent);
870 _grid.attach(*spin->get_label(), 0, row);
871 _grid.attach(*spin->getSpin(), 1, row++);
874 _grid.attach(*_font_face_label, 0, row++, 2);
875 _familyname_entry->get_label()->set_margin_start(indent);
876 _familyname_entry->get_entry()->set_margin_end(
MARGIN_SPACE);
877 _grid.attach(*_familyname_entry->get_label(), 0, row);
878 _grid.attach(*_familyname_entry->get_entry(), 1, row++, 2);
880 for (
auto const &spin : {_units_per_em_spin.get(), _ascent_spin.get(), _cap_height_spin.get(),
881 _x_height_spin.get(), _descent_spin.get()}) {
882 spin->get_label()->set_margin_start(indent);
883 _grid.attach(*spin->get_label(), 0, row);
884 _grid.attach(*spin->getSpin(), 1, row++);
887 auto const setup = Gtk::make_managed<Gtk::Button>(_(
"Set up canvas"));
888 _grid.attach(*setup, 0, row++, 2);
889 setup->set_halign(Gtk::Align::START);
890 setup->signal_clicked().connect([
this]{
894 _units_per_em_spin->getSpin()->get_value(),
895 _ascent_spin->getSpin()->get_value(),
896 _cap_height_spin->getSpin()->get_value(),
897 _x_height_spin->getSpin()->get_value(),
898 _descent_spin->getSpin()->get_value()
902 global_vbox.set_margin(2);
907void SvgFontsDialog::set_glyph_row(Gtk::TreeRow &row,
SPGlyph &glyph)
910 row[_GlyphsListColumns.glyph_node] = &glyph;
911 row[_GlyphsListColumns.glyph_name] = glyph.
glyph_name;
912 row[_GlyphsListColumns.unicode] = glyph.
unicode;
913 row[_GlyphsListColumns.UplusCode] = unicode_name;
914 row[_GlyphsListColumns.advance] = glyph.
horiz_adv_x;
915 row[_GlyphsListColumns.name_markup] =
"<small>" + Glib::Markup::escape_text(
get_glyph_synthetic_name(glyph)) +
"</small>";
919SvgFontsDialog::populate_glyphs_box()
921 if (!_GlyphsListStore)
return;
923 _GlyphsListStore->freeze_notify();
926 Gtk::TreeModel::Path selected_item;
927 if (
auto selected = get_selected_glyph_iter()) {
928 selected_item = _GlyphsListStore->get_path(selected);
930 _GlyphsListStore->clear();
932 SPFont* spfont = get_selected_spfont();
933 _glyphs_observer.set(spfont);
937 if (is<SPGlyph>(&
node)) {
939 auto row = *_GlyphsListStore->append();
940 set_glyph_row(row, glyph);
944 if (!selected_item.empty()) {
945 if (
auto selection = _GlyphsList.get_selection()) {
946 selection->select(selected_item);
947 _GlyphsList.scroll_to_row(selected_item);
949 _glyphs_grid.select_path(selected_item);
953 _GlyphsListStore->thaw_notify();
957SvgFontsDialog::populate_kerning_pairs_box()
959 if (!_KerningPairsListStore)
return;
961 _KerningPairsListStore->clear();
963 if (
SPFont* spfont = get_selected_spfont()) {
964 for (
auto&
node: spfont->children) {
965 if (is<SPHkern>(&
node)){
966 Gtk::TreeModel::Row row = *(_KerningPairsListStore->append());
967 row[_KerningPairsListColumns.first_glyph] = (
static_cast<SPGlyphKerning*
>(&
node))->u1->attribute_string().c_str();
968 row[_KerningPairsListColumns.second_glyph] = (
static_cast<SPGlyphKerning*
>(&
node))->u2->attribute_string().c_str();
969 row[_KerningPairsListColumns.kerning_value] = (
static_cast<SPGlyphKerning*
>(&
node))->k;
977void SvgFontsDialog::update_glyph(
SPGlyph* glyph) {
978 if (_update.pending() || !glyph)
return;
980 _GlyphsListStore->foreach_iter([&](
const Gtk::TreeModel::iterator& it) {
981 if (it->get_value(_GlyphsListColumns.glyph_node) == glyph) {
982 Gtk::TreeRow row = *it;
983 set_glyph_row(row, *glyph);
990void SvgFontsDialog::update_glyphs(
SPGlyph* changed_glyph) {
991 if (_update.pending())
return;
993 SPFont* font = get_selected_spfont();
997 update_glyph(changed_glyph);
1000 populate_glyphs_box();
1003 populate_kerning_pairs_box();
1007void SvgFontsDialog::refresh_svgfont() {
1008 if (
auto font = get_selected_svgfont()) {
1014void SvgFontsDialog::add_glyph(){
1015 auto document = getDocument();
1016 if (!document)
return;
1017 auto font = get_selected_spfont();
1020 auto glyphs = _GlyphsListStore->children();
1023 if (!glyphs.empty()) {
1024 const auto& last = glyphs[glyphs.size() - 1];
1025 if (
SPGlyph* last_glyph = last[_GlyphsListColumns.glyph_node]) {
1026 const Glib::ustring& code = last_glyph->unicode;
1027 if (!code.empty()) {
1028 auto value = code[0];
1030 if (value == 0x7e) value = 0x9f;
1032 if (value == 0x10ffff) value = 0x1f;
1033 unicode = value + 1;
1037 auto str = Glib::ustring(1, unicode);
1040 SPGlyph* glyph = font->create_new_glyph(
"", str.c_str());
1044 set_selected_glyph(glyph);
1048 double units_per_em = 0.0;
1051 if (is<SPFontFace>(&obj)){
1053 units_per_em = obj.getRepr()->getAttributeDouble(
"units-per-em", units_per_em);
1058 return units_per_em;
1062 if (!font)
return pathv;
1064 if (units_per_em <= 0) {
1065 g_warning(
"Units per em not defined, path will be misplaced.");
1074void SvgFontsDialog::set_glyph_description_from_selected_path() {
1075 auto font = get_selected_spfont();
1078 auto selection = getSelection();
1083 if (selection->isEmpty()){
1084 char *
msg = _(
"Select a <b>path</b> to define the curves of a glyph");
1092 char *
msg = _(
"The selected object does not have a <b>path</b> description.");
1097 SPGlyph* glyph = get_selected_glyph();
1099 char *
msg = _(
"No glyph selected in the SVGFonts dialog.");
1111 update_glyphs(glyph);
1114void SvgFontsDialog::missing_glyph_description_from_selected_path(){
1115 auto font = get_selected_spfont();
1118 auto selection = getSelection();
1123 if (selection->isEmpty()){
1124 char *
msg = _(
"Select a <b>path</b> to define the curves of a glyph");
1132 char *
msg = _(
"The selected object does not have a <b>path</b> description.");
1140 for (
auto& obj: font->children) {
1141 if (is<SPMissingGlyph>(&obj)){
1151void SvgFontsDialog::reset_missing_glyph_description(){
1152 for (
auto& obj: get_selected_spfont()->children) {
1153 if (is<SPMissingGlyph>(&obj)){
1155 obj.setAttribute(
"d",
"M0,0h1000v1024h-1000z");
1178void SvgFontsDialog::glyph_name_edit(
const Glib::ustring&,
const Glib::ustring& str){
1179 SPGlyph* glyph = get_selected_glyph();
1189 update_glyphs(glyph);
1193void SvgFontsDialog::glyph_unicode_edit(
const Glib::ustring&,
const Glib::ustring& str){
1194 SPGlyph* glyph = get_selected_glyph();
1197 if (glyph->
unicode == str)
return;
1204 update_glyphs(glyph);
1208void SvgFontsDialog::glyph_advance_edit(
const Glib::ustring&,
const Glib::ustring& str){
1209 SPGlyph* glyph = get_selected_glyph();
1213 if (str == val)
return;
1217 std::istringstream
is(str.raw());
1220 if ((
is >> value)) {
1224 update_glyphs(glyph);
1226 std::cerr <<
"SvgFontDialog::glyph_advance_edit: Error in input: " << str.raw() << std::endl;
1230void SvgFontsDialog::remove_selected_font(){
1231 SPFont* font = get_selected_spfont();
1238 update_fonts(
false);
1241void SvgFontsDialog::remove_selected_glyph(){
1242 SPGlyph* glyph = get_selected_glyph();
1252void SvgFontsDialog::remove_selected_kerning_pair() {
1267 auto font = cast<SPFont>(glyph.
parent);
1275 if (!glyph || !glyph->
parent)
return;
1279 auto document = getDocument();
1280 if (!document)
return;
1284 if (
name.empty())
return;
1287 if (font_label.empty())
return;
1293 if (!layer->hasChildren()) {
1298 layer->addChild(path);
1304 if (layers.isLayer(layer) && layer != layers.currentRoot()) {
1306 layers.toggleLayerSolo(layer,
true);
1307 layers.toggleLockOtherLayers(layer,
true);
1312void SvgFontsDialog::set_glyphs_view_mode(
bool list) {
1314 _glyphs_icon_scroller.set_visible(
false);
1315 _GlyphsListScroller.set_visible(
true);
1318 _GlyphsListScroller.set_visible(
false);
1319 _glyphs_icon_scroller.set_visible(
true);
1323Gtk::Box* SvgFontsDialog::glyphs_tab() {
1325 glyphs_vbox.set_name(
"SVGFontsGlyphsTab");
1326 glyphs_vbox.set_margin(4);
1327 glyphs_vbox.set_spacing(4);
1329 auto const missing_glyph_button = Gtk::make_managed<Gtk::Button>(_(
"From selection"));
1331 missing_glyph_button->signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::missing_glyph_description_from_selected_path));
1333 auto const missing_glyph_reset_button = Gtk::make_managed<Gtk::Button>(_(
"Reset"));
1334 missing_glyph_reset_button->set_margin_top(
MARGIN_SPACE);
1335 missing_glyph_reset_button->signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::reset_missing_glyph_description));
1337 auto const missing_glyph_hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
1338 missing_glyph_hbox->set_hexpand(
false);
1339 UI::pack_start(*missing_glyph_hbox, *missing_glyph_button,
false,
false);
1340 UI::pack_start(*missing_glyph_hbox, *missing_glyph_reset_button,
false,
false);
1342 auto const missing_glyph = Gtk::make_managed<Gtk::Expander>();
1343 missing_glyph->set_label(_(
"Missing glyph"));
1344 missing_glyph->set_child(*missing_glyph_hbox);
1345 missing_glyph->set_valign(Gtk::Align::CENTER);
1347 _GlyphsListScroller.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::ALWAYS);
1348 _GlyphsListScroller.set_child(_GlyphsList);
1349 fix_inner_scroll(_GlyphsListScroller);
1350 _GlyphsList.set_model(_GlyphsListStore);
1351 _GlyphsList.set_enable_search(
false);
1353 _glyph_renderer = Gtk::make_managed<SvgGlyphRenderer>();
1354 const int size = 20;
1355 _glyph_renderer->set_font_size(
size * 9 / 10);
1356 _glyph_renderer->set_cell_size(
size * 3 / 2,
size);
1357 _glyph_renderer->set_tree(&_GlyphsList);
1358 _glyph_renderer->signal_clicked().connect([
this](Glib::RefPtr<Gdk::Event const>
const &,
1359 Glib::ustring
const &unicodes)
1362 _preview_entry.set_text(unicodes);
1364 auto col_index = _GlyphsList.append_column(_(
"Glyph"), *_glyph_renderer) - 1;
1365 if (
auto column = _GlyphsList.get_column(col_index)) {
1366 column->add_attribute(_glyph_renderer->property_glyph(), _GlyphsListColumns.unicode);
1368 _GlyphsList.append_column_editable(_(
"Name"), _GlyphsListColumns.glyph_name);
1369 _GlyphsList.append_column_editable(_(
"Characters"), _GlyphsListColumns.unicode);
1370 _GlyphsList.append_column(_(
"Unicode"), _GlyphsListColumns.UplusCode);
1371 _GlyphsList.append_column_numeric_editable(_(
"Advance"), _GlyphsListColumns.advance,
"%.2f");
1372 _GlyphsList.set_visible(
true);
1373 _GlyphsList.signal_row_activated().connect([
this](Gtk::TreeModel::Path
const &path, Gtk::TreeViewColumn*) {
1374 edit_glyph(get_selected_glyph());
1377 auto const glyph_from_path_button = Gtk::make_managed<Gtk::Button>();
1378 auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 2);
1379 box->append(*Gtk::make_managed<Gtk::Image>(Gio::ThemedIcon::create(
"glyph-copy-from")));
1380 box->append(*Gtk::make_managed<Gtk::Label>(_(
"Get curves")));
1381 glyph_from_path_button->set_child(*box);
1382 glyph_from_path_button->set_tooltip_text(_(
"Get curves from selection to replace current glyph"));
1383 glyph_from_path_button->signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::set_glyph_description_from_selected_path));
1385 auto const edit = Gtk::make_managed<Gtk::Button>();
1386 box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 2);
1387 box->append(*Gtk::make_managed<Gtk::Image>(Gio::ThemedIcon::create(
"edit")));
1388 box->append(*Gtk::make_managed<Gtk::Label>(_(
"Edit")));
1389 edit->set_child(*box);
1390 edit->set_tooltip_text(_(
"Switch to a layer with the same name as current glyph"));
1391 edit->signal_clicked().connect([
this]{ edit_glyph(get_selected_glyph()); });
1393 auto const sort_glyphs_button = Gtk::make_managed<Gtk::Button>(_(
"Sort glyphs"));
1394 sort_glyphs_button->set_tooltip_text(_(
"Sort glyphs in Unicode order"));
1395 sort_glyphs_button->signal_clicked().connect([
this]{ sort_glyphs(get_selected_spfont()); });
1397 auto const add_glyph_button = Gtk::make_managed<Gtk::Button>();
1398 add_glyph_button->set_image_from_icon_name(
"list-add");
1399 add_glyph_button->set_tooltip_text(_(
"Add new glyph"));
1400 add_glyph_button->signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::add_glyph));
1402 auto const remove_glyph_button = Gtk::make_managed<Gtk::Button>();
1403 remove_glyph_button->set_image_from_icon_name(
"list-remove");
1404 remove_glyph_button->set_tooltip_text(_(
"Delete current glyph"));
1405 remove_glyph_button->signal_clicked().connect([
this]{ remove_selected_glyph(); });
1407 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
1414 _glyph_cell_renderer = Gtk::make_managed<SvgGlyphRenderer>();
1415 _glyph_cell_renderer->set_tree(&_glyphs_grid);
1416 const int cell_width = 70;
1417 const int cell_height = 50;
1418 _glyph_cell_renderer->set_cell_size(cell_width, cell_height);
1419 _glyph_cell_renderer->set_font_size(cell_height * 8 / 10);
1420 _glyphs_icon_scroller.set_child(_glyphs_grid);
1421 _glyphs_icon_scroller.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
1422 _glyphs_grid.set_name(
"GlyphsGrid");
1423 _glyphs_grid.set_model(_GlyphsListStore);
1424 _glyphs_grid.set_item_width(cell_width);
1425 _glyphs_grid.set_selection_mode(Gtk::SelectionMode::SINGLE);
1426 _glyphs_grid.set_margin(0);
1427 _glyphs_grid.set_item_padding(0);
1428 _glyphs_grid.set_row_spacing(0);
1429 _glyphs_grid.set_column_spacing(0);
1430 _glyphs_grid.set_columns(-1);
1431 _glyphs_grid.set_markup_column(_GlyphsListColumns.name_markup);
1432 _glyphs_grid.pack_start(*_glyph_cell_renderer);
1433 _glyphs_grid.add_attribute(*_glyph_cell_renderer,
"glyph", _GlyphsListColumns.unicode);
1434 _glyphs_grid.set_visible(
true);
1435 _glyphs_grid.signal_item_activated().connect([
this](Gtk::TreeModel::Path
const &path) {
1436 edit_glyph(get_selected_glyph());
1440 _glyphs_grid.signal_selection_changed().connect([
this]{
1441 if (_glyphs_icon_scroller.get_visible()) {
1442 if (
auto selected = get_selected_glyph_iter()) {
1443 if (
auto selection = _GlyphsList.get_selection()) {
1444 selection->select(selected);
1449 if (
auto selection = _GlyphsList.get_selection()) {
1450 selection->signal_changed().connect([
this]{
1451 if (_GlyphsListScroller.get_visible()) {
1452 if (
auto selected = get_selected_glyph_iter()) {
1453 auto selected_item = _GlyphsListStore->get_path(selected);
1454 _glyphs_grid.select_path(selected_item);
1461 auto const hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
1462 auto const list = Gtk::make_managed<Gtk::ToggleButton>();
1463 list->set_icon_name(
"layout-list");
1464 list->set_tooltip_text(_(
"Glyph list view"));
1465 list->set_valign(Gtk::Align::START);
1466 list->signal_toggled().connect([
this]{ set_glyphs_view_mode(
true); });
1467 auto const grid = Gtk::make_managed<Gtk::ToggleButton>();
1468 grid->set_group(*list);
1469 grid->set_icon_name(
"layout-grid");
1470 grid->set_tooltip_text(_(
"Glyph grid view"));
1471 grid->set_valign(Gtk::Align::START);
1472 grid->signal_toggled().connect([
this] { set_glyphs_view_mode(
false); });
1482 _GlyphsListScroller.set_visible(
false);
1483 _glyphs_icon_scroller.set_visible(
false);
1484 (_show_glyph_list ? list : grid)->set_active();
1485 set_glyphs_view_mode(_show_glyph_list);
1487 for (
auto&& col : _GlyphsList.get_columns()) {
1488 col->set_resizable();
1491 static_cast<Gtk::CellRendererText*
>(_GlyphsList.get_column_cell_renderer(
ColName))->signal_edited().connect(
1492 sigc::mem_fun(*
this, &SvgFontsDialog::glyph_name_edit));
1494 static_cast<Gtk::CellRendererText*
>(_GlyphsList.get_column_cell_renderer(ColString))->signal_edited().connect(
1495 sigc::mem_fun(*
this, &SvgFontsDialog::glyph_unicode_edit));
1497 static_cast<Gtk::CellRendererText*
>(_GlyphsList.get_column_cell_renderer(ColAdvance))->signal_edited().connect(
1498 sigc::mem_fun(*
this, &SvgFontsDialog::glyph_advance_edit));
1500 _glyphs_observer.signal_changed().connect([
this]{ update_glyphs(); });
1502 return &glyphs_vbox;
1505void SvgFontsDialog::add_kerning_pair() {
1506 if (first_glyph.get_active_text() ==
"" ||
1507 second_glyph.get_active_text() ==
"")
return;
1509 this->kerning_pair =
nullptr;
1510 for (
auto&
node: get_selected_spfont()->children) {
1513 if(is<SPHkern>(&
node) &&
1514 (
static_cast<SPGlyphKerning*
>(&
node))->u1->contains((gchar) first_glyph.get_active_text().c_str()[0]) &&
1515 (
static_cast<SPGlyphKerning*
>(&
node))->u2->contains((gchar) second_glyph.get_active_text().c_str()[0])) {
1527 repr->
setAttribute(
"u1", first_glyph.get_active_text());
1528 repr->
setAttribute(
"u2", second_glyph.get_active_text());
1532 get_selected_spfont()->getRepr()->appendChild(repr);
1536 kerning_pair = cast<SPHkern>(getDocument()->getObjectByRepr(repr));
1539 if (
auto selection = _KerningPairsList.get_selection()) {
1540 _KerningPairsListStore->foreach_iter([=,
this](Gtk::TreeModel::iterator
const &it) {
1541 if (it->get_value(_KerningPairsListColumns.spnode) == kerning_pair) {
1542 selection->select(it);
1552Gtk::Box* SvgFontsDialog::kerning_tab(){
1553 auto const add_kernpair_button = Gtk::make_managed<Gtk::Button>(_(
"Add pair"));
1554 add_kernpair_button->signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::add_kerning_pair));
1556 auto const remove_kernpair_button = Gtk::make_managed<Gtk::Button>(_(
"Remove pair"));
1557 remove_kernpair_button->signal_clicked().connect(sigc::mem_fun(*
this, &SvgFontsDialog::remove_selected_kerning_pair));
1559 auto const kerning_selector = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,
MARGIN_SPACE);
1560 kerning_selector->append(*Gtk::make_managed<Gtk::Label>(_(
"Select glyphs:")));
1561 kerning_selector->append(first_glyph);
1562 kerning_selector->append(second_glyph);
1563 kerning_selector->append(*add_kernpair_button);
1564 kerning_selector->append(*remove_kernpair_button);
1566 _KerningPairsList.set_model(_KerningPairsListStore);
1567 _KerningPairsList.append_column(_(
"First glyph"), _KerningPairsListColumns.first_glyph);
1568 _KerningPairsList.append_column(_(
"Second glyph"), _KerningPairsListColumns.second_glyph);
1569 _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*
this, &SvgFontsDialog::on_kerning_pair_selection_changed));
1571 _KerningPairsListScroller.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::ALWAYS);
1572 _KerningPairsListScroller.set_child(_KerningPairsList);
1574 kerning_slider->signal_value_changed().connect(sigc::mem_fun(*
this, &SvgFontsDialog::on_kerning_value_changed));
1577 auto const kerning_amount_hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
1578 UI::pack_start(*kerning_amount_hbox, *Gtk::make_managed<Gtk::Label>(_(
"Kerning value:")),
false,
false);
1579 UI::pack_start(*kerning_amount_hbox, *kerning_slider,
true,
true);
1581 kerning_preview.set_size(-1, 150 + 20);
1582 _font_da.set_size(-1, 60 + 20);
1584 kerning_vbox.set_name(
"SVGFontsKerningTab");
1585 kerning_vbox.set_margin(4);
1586 kerning_vbox.set_spacing(4);
1588 UI::pack_start(kerning_vbox, _KerningPairsListScroller,
true,
true);
1589 UI::pack_start(kerning_vbox, (Gtk::Widget&) kerning_preview,
false,
false);
1591 return &kerning_vbox;
1596 g_return_val_if_fail(document !=
nullptr, NULL);
1630 g_assert(f !=
nullptr);
1639 if (is<SPFontFace>(&obj)){
1641 obj.setAttribute(
"font-family", str);
1648void SvgFontsDialog::add_font(){
1649 SPDocument* doc = this->getDesktop()->getDocument();
1652 const int count = _model->children().size();
1653 std::ostringstream os, os2;
1654 os << _(
"font") <<
" " << count;
1657 os2 <<
"SVGFont " << count;
1659 if (is<SPFontFace>(&obj)){
1661 obj.setAttribute(
"font-family", os2.str());
1665 update_fonts(
false);
1666 on_font_selection_changed();
1671SvgFontsDialog::SvgFontsDialog()
1672 :
DialogBase(
"/dialogs/svgfonts",
"SVGFonts")
1677 kerning_slider = Gtk::make_managed<Gtk::Scale>(Gtk::Orientation::HORIZONTAL);
1692 if (
auto renderer =
dynamic_cast<Gtk::CellRendererText*
>(
_FontsList.get_column_cell_renderer(0))) {
1694 renderer->signal_edited().connect([
this](Glib::ustring
const &path, Glib::ustring
const &new_name) {
1695 if (
auto it =
_model->get_iter(path)) {
1696 auto font = it->get_value(_columns.spfont);
1697 font->setLabel(new_name.c_str());
1698 Glib::ustring undokey =
"svgfonts:fontName";
1699 DocumentUndo::maybeDone(font->document, undokey.c_str(), _(
"Set SVG font name"),
"");
1704 auto const tabs = Gtk::make_managed<Gtk::Notebook>();
1705 tabs->set_scrollable();
1708 tabs->append_page(*
glyphs_tab(), _(
"_Glyphs"),
true);
1709 tabs->append_page(*
kerning_tab(), _(
"_Kerning"),
true);
1710 tabs->signal_switch_page().connect([
this](Gtk::Widget*, guint
page) {
1728 auto const preview_entry_hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,
MARGIN_SPACE);
1730 UI::pack_start(*preview_entry_hbox, *Gtk::make_managed<Gtk::Label>(_(
"Preview text:")),
false,
false);
gchar const * sp_attribute_name(SPAttr id)
Get attribute name by id.
bool is(S const *s)
Equivalent to the boolean value of dynamic_cast<T const*>(...).
3x3 matrix representing an affine transformation.
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
Two-dimensional point that doubles as a vector.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void maybeDone(SPDocument *document, const gchar *keyconst, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
void renameLayer(SPObject *obj, char const *label, bool uniquify)
void setCurrentLayer(SPObject *object, bool clear=false)
Sets the current layer of the desktop.
static SPGroup * asLayer(SPObject *object)
Return the SPGroup if we have a layer object.
SPGroup * currentRoot() const
Returns current root (=bottom) layer.
A class which holds a stack of displayed messages.
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
DialogBase is the base class for the dialog system.
SPDocument * getDocument() const
AttrEntry(SvgFontsDialog *d, gchar *lbl, Glib::ustring tooltip, const SPAttr attr)
void set_text(const char *)
AttrSpin(SvgFontsDialog *d, gchar *lbl, Glib::ustring tooltip, const SPAttr attr)
void set_range(double low, double high)
Gtk::TreeModelColumn< SPFont * > spfont
Gtk::TreeModelColumn< Glib::ustring > label
Gtk::TreeModelColumn< SvgFont * > svgfont
Gtk::TreeModelColumn< SPGlyph * > glyph_node
Gtk::TreeModelColumn< SPGlyphKerning * > spnode
std::unique_ptr< AttrEntry > _familyname_entry
SPFont * get_selected_spfont()
SvgFont * get_selected_svgfont()
Glib::RefPtr< Gtk::ListStore > _GlyphsListStore
SPGlyphKerning * kerning_pair
GlyphMenuButton second_glyph
void update_fonts(bool document_replaced)
Add all fonts in the getDocument() to the combobox.
Gtk::Box * AttrCombo(gchar *lbl, const SPAttr attr)
KerningPairColumns _KerningPairsListColumns
SPGlyphKerning * get_selected_kerning_pair()
GlyphsColumns _GlyphsListColumns
std::unique_ptr< AttrSpin > _horiz_adv_x_spin
std::unique_ptr< AttrSpin > _ascent_spin
Gtk::Box * global_settings_tab()
void on_font_selection_changed()
Gtk::TreeView _KerningPairsList
std::unique_ptr< AttrSpin > _x_height_spin
void populate_glyphs_box()
Gtk::Scale * kerning_slider
Glib::RefPtr< Gtk::ListStore > _model
SvgGlyphRenderer * _glyph_cell_renderer
void on_preview_text_changed()
void update_sensitiveness()
Gtk::TreeView _GlyphsList
GlyphMenuButton first_glyph
void update_global_settings_tab()
void update_glyphs(SPGlyph *changed_glyph=nullptr)
SvgFontDrawingArea _font_da
void on_kerning_value_changed()
void font_selected(SvgFont *svgfont, SPFont *spfont)
void populate_kerning_pairs_box()
std::unique_ptr< AttrSpin > _descent_spin
void set_selected_glyph(SPGlyph *glyph)
SPGlyph * get_selected_glyph()
sigc::scoped_connection _defs_observer_connection
void on_kerning_pair_selection_changed()
Inkscape::XML::SignalObserver _defs_observer
SvgGlyphRenderer * _glyph_renderer
Gtk::TreeModel::iterator get_selected_glyph_iter()
Glib::RefPtr< Gtk::ListStore > _KerningPairsListStore
Gtk::Entry _preview_entry
Gtk::IconView _glyphs_grid
std::unique_ptr< AttrSpin > _units_per_em_spin
std::unique_ptr< AttrSpin > _horiz_origin_x_spin
std::unique_ptr< AttrSpin > _cap_height_spin
Gtk::ScrolledWindow _GlyphsListScroller
void documentReplaced() final
void sort_glyphs(SPFont *font)
SvgFontDrawingArea kerning_preview
std::unique_ptr< AttrSpin > _horiz_origin_y_spin
bool activate_vfunc(Glib::RefPtr< Gdk::Event const > const &event, Gtk::Widget &widget, Glib::ustring const &path, Gdk::Rectangle const &background_area, Gdk::Rectangle const &cell_area, Gtk::CellRendererState flags) final
void set_svg_font(SvgFont *font)
sigc::signal< void(Glib::RefPtr< Gdk::Event const > const &, Glib::ustring const &)> _signal_clicked
void snapshot_vfunc(Glib::RefPtr< Gtk::Snapshot > const &snapshot, Gtk::Widget &widget, Gdk::Rectangle const &background_area, Gdk::Rectangle const &cell_area, Gtk::CellRendererState flags) final
Glib::Property< Glib::ustring > _property_glyph
Interface for refcounted XML nodes.
virtual void changeOrder(Node *child, Node *after)=0
Move a given node in this node's child order.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual bool matchAttributeName(char const *partial_name) const =0
Check whether this node has any attribute that matches a string.
sigc::signal< void()> & signal_changed()
To do: update description of desktop.
SPDocument * getDocument() const
Inkscape::LayerManager & layerManager()
Typed SVG document implementation.
void setWidthAndHeight(const Inkscape::Util::Quantity &width, const Inkscape::Util::Quantity &height, bool changeSize=true)
SPObject * getObjectById(std::string const &id) const
std::vector< SPObject * > const getResourceList(char const *key)
SPDefs * getDefs()
Return the main defs object for the document.
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
bool is_yaxisdown() const
True if the desktop Y-axis points down, false if it points up.
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
void setViewBox()
Set default viewbox calculated from document properties.
static SPGuide * createSPGuide(SPDocument *doc, Geom::Point const &pt1, Geom::Point const &pt2)
Base class for visual SVG elements.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
void requestModified(unsigned int flags)
Requests that a modification notification signal be emitted later (e.g.
char const * getId() const
Returns the objects current ID string.
void setLabel(char const *label)
Sets the author-visible label for this object.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
void set_svgfont(SvgFont *)
void set_text(Glib::ustring)
void draw_func(Cairo::RefPtr< Cairo::Context > const &cr, int width, int height)
void set_size(int x, int y)
cairo_font_face_t * get_font_face()
Glib::ustring attribute_string()
Editable view implementation.
TODO: insert short description here.
Inkscape::XML::Node * node
Raw stack of active status messages.
static R & release(R &r)
Decrements the reference count of a anchored object.
SPItem * find_layer(SPDesktop *desktop, SPObject *root_layer, const Glib::ustring &name)
Glib::ustring create_unicode_name(const Glib::ustring &unicode, int max_chars)
SPGuide * create_guide(SPDocument &doc, double x0, double y0, double x1, double y1)
Glib::ustring get_glyph_full_name(const SPGlyph &glyph)
static void set_sensitive(Gtk::SearchEntry2 &entry, bool const sensitive)
void rename_glyph_layer(SPDesktop *desktop, SPItem *layer, const Glib::ustring &font, const Glib::ustring &name)
SPFont * new_font(SPDocument *document)
std::vector< SPGroup * > get_direct_sublayers(SPObject *layer)
Glib::ustring get_font_label(SPFont *font)
Glib::ustring get_glyph_synthetic_name(const SPGlyph &glyph)
Geom::PathVector flip_coordinate_system(Geom::PathVector pathv, const SPFont *font, double units_per_em)
void change_glyph_attribute(SPDesktop *desktop, SPGlyph &glyph, std::function< void()> change)
SPItem * get_layer_for_glyph(SPDesktop *desktop, const Glib::ustring &font, const Glib::ustring &name)
SPGuide * get_guide(SPDocument &doc, const Glib::ustring &id)
void set_font_family(SPFont *font, char *str)
void set_up_typography_canvas(SPDocument *document, double em, double asc, double cap, double xheight, double des)
Inkscape::XML::Node * create_path_from_glyph(const SPGlyph &glyph)
SPItem * get_or_create_layer_for_glyph(SPDesktop *desktop, const Glib::ustring &font, const Glib::ustring &name)
double get_font_units_per_em(const SPFont *font)
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
SPObject * create_layer(SPObject *root, SPObject *layer, LayerRelativePosition position)
Creates a new layer.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
C facade to Inkscape::XML::Node.
void sp_repr_unparent(Inkscape::XML::Node *repr)
Remove repr from children of its parent node.
Interface for XML documents.
virtual Node * createElement(char const *name)=0
Geom::PathVector sp_svg_read_pathv(char const *str)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Glib::RefPtr< Gtk::Builder > builder