From 280e31bdf7f5ffd28f8b14565c1d93de4070bd0c Mon Sep 17 00:00:00 2001 From: JucaBlues Date: Sun, 28 Dec 2008 18:35:14 +0000 Subject: [PATCH] Now users can design a font within inkscape, save it and then open the SVG file in Fontforge in order to export a truetype font (or other system font formarts fontforge supports). This improves previous workflow of font design using Inkscape which involved creating one SVG for each glyph. Now user only needs to create a single SVG file containing an SVGFont. Glyph kerning settings for the font can also be defined withing Inkscape itself with live preview. The kerning management still needs some improvements but is currently functional at least. Improvements in the SVGFonts dialog: * In Global Settings tab you can define the font family name. Other attributes should be added to this tab in the future. * Glyphs tab allows the user to: ** see a list (combobox) of glyphs available in the currently selected font. ** add/remove glyphs ** edit glyph name and unicode ** set the glyph curves based on a given path (selected from canvas). Same feature for the missing glyph. * Kerning tab allows user to: ** add new kerning pairs ** adjust kerning values of selected kerning pair ** live preview while adjusting the kerning values Code refactoring: * Inner classes DocumentProperties::SignalObserver and FilterEffectsDialog::SignalObserver were duplicated code and another instance would be needed in SVGFonts dialog. So, I moved it to Inkscape::XML::SignalObserver (in helper-observer.{cpp,h}) * changed SPGlyph->glyph_name and SPGlyph->unicode from char* to Glib::ustring * added sp_remove_resource to the release method in sp-font.cpp * glyph curves used to be stored (in d attribute) and rendered upside-down. Now that bug is fixed. Sorry about this huge commit. I got one week away from the Internet during a xmas travel. The lack of 'net connection made me work more intensely in Inkscape :-D Felipe Sanches --- src/display/nr-svgfonts.cpp | 35 +- src/display/nr-svgfonts.h | 1 + src/sp-font-face.cpp | 8 +- src/sp-font.cpp | 3 +- src/sp-glyph-kerning.cpp | 2 +- src/sp-glyph-kerning.h | 2 +- src/sp-glyph.cpp | 13 +- src/sp-glyph.h | 4 +- src/ui/dialog/document-properties.cpp | 36 -- src/ui/dialog/document-properties.h | 28 +- src/ui/dialog/filter-effects-dialog.cpp | 46 +- src/ui/dialog/filter-effects-dialog.h | 6 +- src/ui/dialog/svg-fonts-dialog.cpp | 530 ++++++++++++++++++++---- src/ui/dialog/svg-fonts-dialog.h | 98 ++++- src/unicoderange.cpp | 26 ++ src/unicoderange.h | 3 + src/xml/Makefile_insert | 2 + src/xml/helper-observer.cpp | 43 ++ src/xml/helper-observer.h | 35 ++ 19 files changed, 710 insertions(+), 211 deletions(-) create mode 100644 src/xml/helper-observer.cpp create mode 100644 src/xml/helper-observer.h diff --git a/src/display/nr-svgfonts.cpp b/src/display/nr-svgfonts.cpp index 723929ea7..633f4cddd 100644 --- a/src/display/nr-svgfonts.cpp +++ b/src/display/nr-svgfonts.cpp @@ -91,7 +91,7 @@ SvgFont::scaled_font_init (cairo_scaled_font_t *scaled_font, return CAIRO_STATUS_SUCCESS; } -unsigned int size_of_substring(gchar* substring, gchar* str){ +unsigned int size_of_substring(const char* substring, gchar* str){ const gchar* original_substring = substring; while((g_utf8_get_char(substring)==g_utf8_get_char(str)) && g_utf8_get_char(substring) != 0 && g_utf8_get_char(str) != 0){ @@ -129,7 +129,7 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, while(g_utf8_get_char(_utf8) != 0){ missing = true; for (i=0; i < (unsigned long) this->glyphs.size(); i++){ - if ( (len = size_of_substring(this->glyphs[i]->unicode, _utf8)) ){ + if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){ //TODO: store this cluster _utf8+=len; count++; @@ -144,7 +144,6 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, } } -//g_warning("count is %d", count); //We use that info to allocate memory for the glyphs *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t)); @@ -159,7 +158,7 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, while(g_utf8_get_char(_utf8) != 0){ len = 0; for (i=0; i < (unsigned long) this->glyphs.size(); i++){ - if ( (len = size_of_substring(this->glyphs[i]->unicode, _utf8)) ){ + if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){ //check whether is there a glyph declared on the SVG document // that matches with the text string in its current position for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){ @@ -168,7 +167,7 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, if ( (((SPHkern*)node)->u1->contains(previous_unicode[0]) || ((SPHkern*)node)->g1->contains(previous_glyph_name)) && (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0]) - || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name)) + || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name.c_str())) )//TODO: verify what happens when using unicode strings. x -= (((SPHkern*)node)->k / this->font->horiz_adv_x); } @@ -176,13 +175,13 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, if ( (((SPVkern*)node)->u1->contains(previous_unicode[0]) || ((SPVkern*)node)->g1->contains(previous_glyph_name)) && (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0]) - || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name)) + || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name.c_str())) )//TODO: idem y -= (((SPVkern*)node)->k / this->font->vert_adv_y); } } - previous_unicode = this->glyphs[i]->unicode;//used for kerning checking - previous_glyph_name = this->glyphs[i]->glyph_name;//used for kerning checking + previous_unicode = (char*) this->glyphs[i]->unicode.c_str();//used for kerning checking + previous_glyph_name = (char*) this->glyphs[i]->glyph_name.c_str();//used for kerning checking (*glyphs)[count].index = i; (*glyphs)[count].x = x; (*glyphs)[count++].y = y; @@ -227,10 +226,8 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, if (glyph == this->glyphs.size()){ if (!this->missingglyph) return CAIRO_STATUS_SUCCESS; node = (SPObject*) this->missingglyph; - g_warning("RENDER MISSING-GLYPH"); } else { node = (SPObject*) this->glyphs[glyph]; - g_warning("RENDER %s", this->glyphs[glyph]->unicode); } //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node @@ -248,9 +245,16 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, if (!pathv.empty()){ //This glyph has a path description on its d attribute, so we render it: cairo_new_path(cr); + //adjust scale of the glyph Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x); + //This matrix flips the glyph vertically + Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0)); + //then we offset it + pathv += Geom::Point(Geom::Coord(0),Geom::Coord(-((SPFont*) node->parent)->horiz_adv_x)); + Geom::Rect area( Geom::Point(0,0), Geom::Point(1,1) ); //I need help here! (reaction: note that the 'area' parameter is an *optional* rect, so you can pass an empty Geom::OptRect() ) - feed_pathvector_to_cairo (cr, pathv, s, area, false, 0); + + feed_pathvector_to_cairo (cr, pathv, s*m, area, false, 0); cairo_fill(cr); } @@ -263,11 +267,9 @@ SvgFont::get_font_face(){ if (!this->userfont) { for(SPObject* node = this->font->children;node;node=node->next){ if (SP_IS_GLYPH(node)){ - g_warning("glyphs.push_back((SPGlyph*)node); (node->unicode='%s')", ((SPGlyph*)node)->unicode); this->glyphs.push_back((SPGlyph*)node); } if (SP_IS_MISSING_GLYPH(node)){ - g_warning("missingglyph=(SPMissingGlyph*)node;"); this->missingglyph=(SPMissingGlyph*)node; } } @@ -275,4 +277,11 @@ SvgFont::get_font_face(){ } return this->userfont->face; } + +void SvgFont::refresh(){ + this->glyphs.clear(); + delete this->userfont; + this->userfont = NULL; +} + #endif //#ifdef ENABLE_SVG_FONTS diff --git a/src/display/nr-svgfonts.h b/src/display/nr-svgfonts.h index a41627f86..ebf5ad08b 100644 --- a/src/display/nr-svgfonts.h +++ b/src/display/nr-svgfonts.h @@ -32,6 +32,7 @@ cairo_font_face_t* face; class SvgFont{ public: SvgFont(SPFont* spfont); +void refresh(); cairo_font_face_t* get_font_face(); cairo_status_t scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *metrics); cairo_status_t scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *flags); diff --git a/src/sp-font-face.cpp b/src/sp-font-face.cpp index c4197406b..811d9a14f 100644 --- a/src/sp-font-face.cpp +++ b/src/sp-font-face.cpp @@ -26,6 +26,7 @@ #include "document.h" #include "helper-fns.h" +//TODO: apparently unused. Maybe should be removed. class ObjectContainer { @@ -360,8 +361,8 @@ static void sp_fontface_init(SPFontFace *face) std::vector stretch; stretch.push_back(SP_FONTFACE_STRETCH_NORMAL); face->font_stretch = stretch; -/* face->font_family = NULL; + /* //face->font_style = ; //face->font_variant = ; //face->font_weight = ; @@ -492,6 +493,11 @@ static void sp_fontface_set(SPObject *object, unsigned int key, const gchar *val std::vector stretch; switch (key) { + case SP_PROP_FONT_FAMILY: + if (face->font_family) g_free(face->font_family); + face->font_family = g_strdup(value); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SP_PROP_FONT_STYLE: style = sp_read_fontFaceStyleType(value); if (face->font_style.size() != style.size()){ diff --git a/src/sp-font.cpp b/src/sp-font.cpp index f586e6c62..f2d37f3bf 100644 --- a/src/sp-font.cpp +++ b/src/sp-font.cpp @@ -144,7 +144,8 @@ sp_font_remove_child(SPObject *object, Inkscape::XML::Node *child) static void sp_font_release(SPObject *object) { - //SPFont *font = SP_FONT(object); + SPFont *font = SP_FONT(object); + sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "font", object); if (((SPObjectClass *) parent_class)->release) { ((SPObjectClass *) parent_class)->release(object); diff --git a/src/sp-glyph-kerning.cpp b/src/sp-glyph-kerning.cpp index bd5340b6b..6d08f212c 100644 --- a/src/sp-glyph-kerning.cpp +++ b/src/sp-glyph-kerning.cpp @@ -135,7 +135,7 @@ GlyphNames::~GlyphNames(){ if (this->names) g_free(this->names); } -bool GlyphNames::contains(gchar* name){ +bool GlyphNames::contains(const char* name){ if (!(this->names) || !name) return false; std::istringstream is(this->names); std::string str; diff --git a/src/sp-glyph-kerning.h b/src/sp-glyph-kerning.h index 8da752e49..ec0866c2c 100644 --- a/src/sp-glyph-kerning.h +++ b/src/sp-glyph-kerning.h @@ -36,7 +36,7 @@ class GlyphNames{ public: GlyphNames(const gchar* value); ~GlyphNames(); -bool contains(gchar* name); +bool contains(const char* name); private: gchar* names; }; diff --git a/src/sp-glyph.cpp b/src/sp-glyph.cpp index fb7cea15d..8af78a0aa 100644 --- a/src/sp-glyph.cpp +++ b/src/sp-glyph.cpp @@ -72,8 +72,9 @@ static void sp_glyph_class_init(SPGlyphClass *gc) static void sp_glyph_init(SPGlyph *glyph) { //TODO: correct these values: - glyph->unicode = NULL; - glyph->glyph_name = NULL; + + new (&glyph->unicode) Glib::ustring(); + new (&glyph->glyph_name) Glib::ustring(); glyph->d = NULL; glyph->orientation = GLYPH_ORIENTATION_BOTH; glyph->arabic_form = GLYPH_ARABIC_FORM_INITIAL; @@ -151,13 +152,13 @@ static void sp_glyph_set(SPObject *object, unsigned int key, const gchar *value) switch (key) { case SP_ATTR_UNICODE: - if (glyph->unicode) g_free(glyph->unicode); - glyph->unicode = g_strdup(value); + glyph->unicode.clear(); + if (value) glyph->unicode.append(value); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_GLYPH_NAME: - if (glyph->glyph_name) g_free(glyph->glyph_name); - glyph->glyph_name = g_strdup(value); + glyph->glyph_name.clear(); + if (value) glyph->glyph_name.append(value); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_D: diff --git a/src/sp-glyph.h b/src/sp-glyph.h index b7a7c316f..8c35a3a83 100644 --- a/src/sp-glyph.h +++ b/src/sp-glyph.h @@ -39,8 +39,8 @@ enum glyphOrientation { }; struct SPGlyph : public SPObject { - char* unicode; - char* glyph_name; + Glib::ustring unicode; + Glib::ustring glyph_name; char* d; glyphOrientation orientation; glyphArabicForm arabic_form; diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 713acc23e..277652596 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -388,42 +388,6 @@ DocumentProperties::build_snap_dtls() attach_all(_page_snap_dtls.table(), array, G_N_ELEMENTS(array)); } -// Very simple observer that just emits a signal if anything happens to a node -DocumentProperties::SignalObserver::SignalObserver() - : _oldsel(0) -{} - -// Add this observer to the SPObject and remove it from any previous object -void -DocumentProperties::SignalObserver::set(SPObject* o) -{ - if(_oldsel && _oldsel->repr) - _oldsel->repr->removeObserver(*this); - if(o && o->repr) - o->repr->addObserver(*this); - _oldsel = o; -} - -void DocumentProperties::SignalObserver::notifyChildAdded(XML::Node&, XML::Node&, XML::Node*) -{ signal_changed()(); } - -void DocumentProperties::SignalObserver::notifyChildRemoved(XML::Node&, XML::Node&, XML::Node*) -{ signal_changed()(); } - -void DocumentProperties::SignalObserver::notifyChildOrderChanged(XML::Node&, XML::Node&, XML::Node*, XML::Node*) -{ signal_changed()(); } - -void DocumentProperties::SignalObserver::notifyContentChanged(XML::Node&, Util::ptr_shared, Util::ptr_shared) -{} - -void DocumentProperties::SignalObserver::notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared, Util::ptr_shared) -{ signal_changed()(); } - -sigc::signal& DocumentProperties::SignalObserver::signal_changed() -{ - return _signal_changed; -} - #if ENABLE_LCMS static void lcms_profile_get_name (cmsHPROFILE profile, const gchar **name) diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index 36a6c739b..f4d5724be 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -15,7 +15,7 @@ #define INKSCAPE_UI_DIALOG_DOCUMENT_PREFERENCES_H #include -#include +#include // #include #include @@ -26,14 +26,11 @@ #include "ui/widget/tolerance-slider.h" #include "ui/widget/panel.h" -#include "xml/node-observer.h" +#include "xml/helper-observer.h" using namespace Inkscape::UI::Widget; namespace Inkscape { - namespace XML { - class Node; - } namespace UI { namespace Dialog { @@ -71,26 +68,7 @@ protected: void _handleActivateDesktop(Inkscape::Application *application, SPDesktop *desktop); void _handleDeactivateDesktop(Inkscape::Application *application, SPDesktop *desktop); - // Very simple observer that just emits a signal if anything happens to a node - class SignalObserver : public XML::NodeObserver - { - public: - SignalObserver(); - - // Add this observer to the SPObject and remove it from any previous object - void set(SPObject* o); - void notifyChildAdded(XML::Node&, XML::Node&, XML::Node*); - void notifyChildRemoved(XML::Node&, XML::Node&, XML::Node*); - void notifyChildOrderChanged(XML::Node&, XML::Node&, XML::Node*, XML::Node*); - void notifyContentChanged(XML::Node&, Util::ptr_shared, Util::ptr_shared); - void notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared, Util::ptr_shared); - sigc::signal& signal_changed(); - private: - sigc::signal _signal_changed; - SPObject* _oldsel; - }; - - SignalObserver _emb_profiles_observer; + Inkscape::XML::SignalObserver _emb_profiles_observer; Gtk::Tooltips _tt; Gtk::Notebook _notebook; diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 08df7fd05..7f92f5f01 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -91,48 +91,6 @@ int input_count(const SPFilterPrimitive* prim) return 1; } -// Very simple observer that just emits a signal if anything happens to a node -class FilterEffectsDialog::SignalObserver : public XML::NodeObserver -{ -public: - SignalObserver() - : _oldsel(0) - {} - - // Add this observer to the SPObject and remove it from any previous object - void set(SPObject* o) - { - if(_oldsel && _oldsel->repr) - _oldsel->repr->removeObserver(*this); - if(o && o->repr) - o->repr->addObserver(*this); - _oldsel = o; - } - - void notifyChildAdded(XML::Node&, XML::Node&, XML::Node*) - { signal_changed()(); } - - void notifyChildRemoved(XML::Node&, XML::Node&, XML::Node*) - { signal_changed()(); } - - void notifyChildOrderChanged(XML::Node&, XML::Node&, XML::Node*, XML::Node*) - { signal_changed()(); } - - void notifyContentChanged(XML::Node&, Util::ptr_shared, Util::ptr_shared) - {} - - void notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared, Util::ptr_shared) - { signal_changed()(); } - - sigc::signal& signal_changed() - { - return _signal_changed; - } -private: - sigc::signal _signal_changed; - SPObject* _oldsel; -}; - class CheckButtonAttr : public Gtk::CheckButton, public AttrWidget { public: @@ -1119,7 +1077,7 @@ Glib::RefPtr create_popup_menu(Gtk::Widget& parent, sigc::slot /*** FilterModifier ***/ FilterEffectsDialog::FilterModifier::FilterModifier(FilterEffectsDialog& d) - : _dialog(d), _add(Gtk::Stock::NEW), _observer(new SignalObserver) + : _dialog(d), _add(Gtk::Stock::NEW), _observer(new Inkscape::XML::SignalObserver) { Gtk::ScrolledWindow* sw = Gtk::manage(new Gtk::ScrolledWindow); pack_start(*sw); @@ -1443,7 +1401,7 @@ void FilterEffectsDialog::CellRendererConnection::get_size_vfunc( FilterEffectsDialog::PrimitiveList::PrimitiveList(FilterEffectsDialog& d) : _dialog(d), _in_drag(0), - _observer(new SignalObserver) + _observer(new Inkscape::XML::SignalObserver) { d.signal_expose_event().connect(sigc::mem_fun(*this, &PrimitiveList::on_expose_signal)); diff --git a/src/ui/dialog/filter-effects-dialog.h b/src/ui/dialog/filter-effects-dialog.h index ccffb2a67..99b411441 100644 --- a/src/ui/dialog/filter-effects-dialog.h +++ b/src/ui/dialog/filter-effects-dialog.h @@ -33,6 +33,7 @@ #include "sp-filter.h" #include "ui/widget/combo-enums.h" #include "ui/widget/spin-slider.h" +#include "xml/helper-observer.h" using namespace Inkscape::UI::Widget; @@ -55,7 +56,6 @@ public: protected: virtual void show_all_vfunc(); private: - class SignalObserver; class FilterModifier : public Gtk::VBox { @@ -119,7 +119,7 @@ private: Gtk::Button _add; Glib::RefPtr _menu; sigc::signal _signal_filter_changed; - std::auto_ptr _observer; + std::auto_ptr _observer; }; class PrimitiveColumns : public Gtk::TreeModel::ColumnRecord @@ -203,7 +203,7 @@ private: sigc::signal _signal_primitive_changed; sigc::connection _scroll_connection; int _autoscroll; - std::auto_ptr _observer; + std::auto_ptr _observer; }; void init_settings_widgets(); diff --git a/src/ui/dialog/svg-fonts-dialog.cpp b/src/ui/dialog/svg-fonts-dialog.cpp index 4034cb10d..6ef8fa729 100644 --- a/src/ui/dialog/svg-fonts-dialog.cpp +++ b/src/ui/dialog/svg-fonts-dialog.cpp @@ -14,13 +14,16 @@ #ifdef ENABLE_SVG_FONTS +#include "document-private.h" #include -#include "svg-fonts-dialog.h" #include +#include "selection.h" #include -#include "document-private.h" +#include "svg-fonts-dialog.h" #include "xml/node.h" #include "xml/repr.h" +#include "svg/svg.h" +#include <2geom/pathvector.h> SvgFontDrawingArea::SvgFontDrawingArea(){ this->text = ""; @@ -33,6 +36,7 @@ void SvgFontDrawingArea::set_svgfont(SvgFont* svgfont){ void SvgFontDrawingArea::set_text(Glib::ustring text){ this->text = text; + redraw(); } void SvgFontDrawingArea::set_size(int x, int y){ @@ -84,8 +88,12 @@ SvgFontsDialog::AttrEntry::AttrEntry(SvgFontsDialog* d, gchar* lbl, const SPAttr entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::AttrEntry::on_attr_changed)); } +void SvgFontsDialog::AttrEntry::set_text(char* t){ + if (!t) return; + entry.set_text(t); +} + void SvgFontsDialog::AttrEntry::on_attr_changed(){ - g_warning("attr entry changed: %s", this->entry.get_text().c_str()); SPObject* o = NULL; for(SPObject* node = this->dialog->get_selected_spfont()->children; node; node=node->next){ @@ -137,46 +145,81 @@ GlyphComboBox::GlyphComboBox(){ } void GlyphComboBox::update(SPFont* spfont){ - if (spfont) { - this->clear(); - for(SPObject* node = spfont->children; node; node=node->next){ - if (SP_IS_GLYPH(node)){ - this->append_text(((SPGlyph*)node)->unicode); - } + if (!spfont) return +//TODO: figure out why do we need to append_text("") before clearing items properly... + + this->append_text(""); //Gtk is refusing to clear the combobox when I comment out this line + this->clear_items(); + + for(SPObject* node = spfont->children; node; node=node->next){ + if (SP_IS_GLYPH(node)){ + this->append_text(((SPGlyph*)node)->unicode); } } } -void SvgFontsDialog::on_kerning_changed(){ - if (this->kerning_pair){ - this->kerning_pair->k = kerning_spin.get_value(); - kerning_preview.redraw(); - _font_da.redraw(); +void SvgFontsDialog::on_kerning_value_changed(){ + if (!this->kerning_pair) return; + SPDocument* document = sp_desktop_document(this->getDesktop()); + + //TODO: I am unsure whether this is the correct way of calling sp_document_maybe_done + Glib::ustring undokey = "svgfonts:hkern:k:"; + undokey += this->kerning_pair->u1->attribute_string(); + undokey += ":"; + undokey += this->kerning_pair->u2->attribute_string(); + + //slider values increase from right to left so that they match the kerning pair preview + this->kerning_pair->repr->setAttribute("k", Glib::Ascii::dtostr(get_selected_spfont()->horiz_adv_x - kerning_slider.get_value()).c_str()); + sp_document_maybe_done(document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS, _("Adjust kerning value")); + + //populate_kerning_pairs_box(); + kerning_preview.redraw(); + _font_da.redraw(); +} + +void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event) +{ + if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) { + _GlyphsContextMenu.popup(event->button, event->time); } } -void SvgFontsDialog::on_glyphs_changed(){ - std::string str1(first_glyph.get_active_text()); - std::string str2(second_glyph.get_active_text()); - kerning_preview.set_text((gchar*) (str1+str2).c_str()); - kerning_preview.redraw(); +void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot rem) +{ + Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + _GlyphsContextMenu.append(*mi); + mi->signal_activate().connect(rem); + mi->show(); + _GlyphsContextMenu.accelerate(parent); +} - //look for this kerning pair on the currently selected font - this->kerning_pair = NULL; - for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){ - if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0]) - && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){ - this->kerning_pair = (SPGlyphKerning*)node; - continue; - } +void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event) +{ + if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) { + _FontsContextMenu.popup(event->button, event->time); } +} + +void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot rem) +{ + Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + _FontsContextMenu.append(*mi); + mi->signal_activate().connect(rem); + mi->show(); + _FontsContextMenu.accelerate(parent); +} -//TODO: - //if not found, - //create new kern node - if (this->kerning_pair) - kerning_spin.set_value(this->kerning_pair->k); +void SvgFontsDialog::update_sensitiveness(){ + if (get_selected_spfont()){ + global_vbox.set_sensitive(true); + glyphs_vbox.set_sensitive(true); + kerning_vbox.set_sensitive(true); + } else { + global_vbox.set_sensitive(false); + glyphs_vbox.set_sensitive(false); + kerning_vbox.set_sensitive(false); + } } /* Add all fonts in the document to the combobox. */ @@ -196,16 +239,48 @@ void SvgFontsDialog::update_fonts() const gchar* id = SP_OBJECT_ID(f); row[_columns.label] = lbl ? lbl : (id ? id : "font"); } + + update_sensitiveness(); } void SvgFontsDialog::on_preview_text_changed(){ _font_da.set_text((gchar*) _preview_entry.get_text().c_str()); _font_da.set_text(_preview_entry.get_text()); - _font_da.redraw(); +} + +void SvgFontsDialog::on_kerning_pair_selection_changed(){ + SPGlyphKerning* kern = get_selected_kerning_pair(); + if (!kern) { + kerning_preview.set_text(""); + return; + } + Glib::ustring str; + str += kern->u1->sample_glyph(); + str += kern->u2->sample_glyph(); + + kerning_preview.set_text(str); + this->kerning_pair = kern; + + //slider values increase from right to left so that they match the kerning pair preview + kerning_slider.set_value(get_selected_spfont()->horiz_adv_x - kern->k); +} + +void SvgFontsDialog::update_global_settings_tab(){ + SPFont* font = get_selected_spfont(); + if (!font) return; + + SPObject* obj; + for (obj=font->children; obj; obj=obj->next){ + if (SP_IS_FONTFACE(obj)){ + _familyname_entry->set_text(((SPFontFace*) obj)->font_family); + } + } } void SvgFontsDialog::on_font_selection_changed(){ SPFont* spfont = this->get_selected_spfont(); + if (!spfont) return; + SvgFont* svgfont = this->get_selected_svgfont(); first_glyph.update(spfont); second_glyph.update(spfont); @@ -213,13 +288,17 @@ void SvgFontsDialog::on_font_selection_changed(){ _font_da.set_svgfont(svgfont); _font_da.redraw(); - int steps = 50; double set_width = spfont->horiz_adv_x; setwidth_spin.set_value(set_width); - kerning_spin.set_range(0,set_width); - kerning_spin.set_increments(int(set_width/steps),2*int(set_width/steps)); - kerning_spin.set_value(0); + kerning_slider.set_range(0, set_width); + kerning_slider.set_draw_value(false); + kerning_slider.set_value(0); + + update_global_settings_tab(); + populate_glyphs_box(); + populate_kerning_pairs_box(); + update_sensitiveness(); } void SvgFontsDialog::on_setwidth_changed(){ @@ -230,9 +309,17 @@ void SvgFontsDialog::on_setwidth_changed(){ } } +SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair() +{ + Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected(); + if(i) + return (*i)[_KerningPairsListColumns.spnode]; + return NULL; +} + SvgFont* SvgFontsDialog::get_selected_svgfont() { - Gtk::TreeModel::iterator i = _font_list.get_selection()->get_selected(); + Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected(); if(i) return (*i)[_columns.svgfont]; return NULL; @@ -240,19 +327,24 @@ SvgFont* SvgFontsDialog::get_selected_svgfont() SPFont* SvgFontsDialog::get_selected_spfont() { - Gtk::TreeModel::iterator i = _font_list.get_selection()->get_selected(); + Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected(); if(i) return (*i)[_columns.spfont]; return NULL; } -Gtk::VBox* SvgFontsDialog::global_settings_tab(){ - Gtk::VBox* global_vbox = Gtk::manage(new Gtk::VBox()); +SPGlyph* SvgFontsDialog::get_selected_glyph() +{ + Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected(); + if(i) + return (*i)[_GlyphsListColumns.glyph_node]; + return NULL; +} - AttrEntry* familyname; - familyname = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY); +Gtk::VBox* SvgFontsDialog::global_settings_tab(){ + _familyname_entry = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY); - global_vbox->add(*familyname); + global_vbox.pack_start(*_familyname_entry, false, false); /* global_vbox->add(*AttrCombo((gchar*) _("Style:"), SP_PROP_FONT_STYLE)); global_vbox->add(*AttrCombo((gchar*) _("Variant:"), SP_PROP_FONT_VARIANT)); global_vbox->add(*AttrCombo((gchar*) _("Weight:"), SP_PROP_FONT_WEIGHT)); @@ -267,46 +359,301 @@ Gtk::VBox* SvgFontsDialog::global_settings_tab(){ setwidth_spin.set_increments(10, 100); global_vbox->add(*setwidth_hbox); */ - return global_vbox; + return &global_vbox; +} + +void +SvgFontsDialog::populate_glyphs_box() +{ + if (!_GlyphsListStore) return; + _GlyphsListStore->clear(); + + SPFont* spfont = this->get_selected_spfont(); + _glyphs_observer.set(spfont); + + for(SPObject* node = spfont->children; node; node=node->next){ + if (SP_IS_GLYPH(node)){ + Gtk::TreeModel::Row row = *(_GlyphsListStore->append()); + row[_GlyphsListColumns.glyph_node] = (SPGlyph*) node; + row[_GlyphsListColumns.glyph_name] = ((SPGlyph*) node)->glyph_name; + row[_GlyphsListColumns.unicode] = ((SPGlyph*) node)->unicode; + } + } +} + +void +SvgFontsDialog::populate_kerning_pairs_box() +{ + if (!_KerningPairsListStore) return; + _KerningPairsListStore->clear(); + + SPFont* spfont = this->get_selected_spfont(); + + for(SPObject* node = spfont->children; node; node=node->next){ + if (SP_IS_HKERN(node)){ + Gtk::TreeModel::Row row = *(_KerningPairsListStore->append()); + row[_KerningPairsListColumns.first_glyph] = ((SPGlyphKerning*) node)->u1->attribute_string().c_str(); + row[_KerningPairsListColumns.second_glyph] = ((SPGlyphKerning*) node)->u2->attribute_string().c_str(); + row[_KerningPairsListColumns.kerning_value] = ((SPGlyphKerning*) node)->k; + row[_KerningPairsListColumns.spnode] = (SPGlyphKerning*) node; + } + } +} + +SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count) +{ + g_return_val_if_fail(font != NULL, NULL); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); + + // create a new glyph + Inkscape::XML::Node *repr; + repr = xml_doc->createElement("svg:glyph"); + + std::ostringstream os; + os << _("glyph") << " " << count; + repr->setAttribute("glyph-name", os.str().c_str()); + + // Append the new glyph node to the current font + SP_OBJECT_REPR(font)->appendChild(repr); + Inkscape::GC::release(repr); + + // get corresponding object + SPGlyph *g = SP_GLYPH( document->getObjectByRepr(repr) ); + + g_assert(g != NULL); + g_assert(SP_IS_GLYPH(g)); + + return g; +} + +void SvgFontsDialog::update_glyphs(){ + SPFont* font = get_selected_spfont(); + if (!font) return; + populate_glyphs_box(); + populate_kerning_pairs_box(); + first_glyph.update(font); + second_glyph.update(font); + get_selected_svgfont()->refresh(); + _font_da.redraw(); +} + +void SvgFontsDialog::add_glyph(){ + const int count = _GlyphsListStore->children().size(); + SPDocument* doc = sp_desktop_document(this->getDesktop()); + /* SPGlyph* glyph =*/ new_glyph(doc, get_selected_spfont(), count+1); + + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add glyph")); + + update_glyphs(); +} + +void SvgFontsDialog::set_glyph_description_from_selected_path(){ + SPDocument* doc = sp_desktop_document(this->getDesktop()); + Inkscape::Selection* sel = sp_desktop_selection(this->getDesktop()); + if (sel->isEmpty()) return; + Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0); + if (!node || !node->matchAttributeName("d")) return; + + Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d")); + //This matrix flips the glyph vertically + Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0)); + pathv*=m; + //then we offset it + pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x)); + + get_selected_glyph()->repr->setAttribute("d", (char*) sp_svg_write_path (pathv)); + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves")); + + update_glyphs(); +} + +void SvgFontsDialog::missing_glyph_description_from_selected_path(){ + SPDocument* doc = sp_desktop_document(this->getDesktop()); + Inkscape::Selection* sel = sp_desktop_selection(this->getDesktop()); + if (sel->isEmpty()) return; + Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0); + if (!node || !node->matchAttributeName("d")) return; + + SPObject* obj; + for (obj = get_selected_spfont()->children; obj; obj=obj->next){ + if (SP_IS_MISSING_GLYPH(obj)){ + obj->repr->setAttribute("d", (char*) node->attribute("d")); + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves")); + } + } + + update_glyphs(); +} + +void SvgFontsDialog::glyph_name_edit(const Glib::ustring&, const Glib::ustring& str){ + Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected(); + if (!i) return; + + SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node]; + glyph->repr->setAttribute("glyph-name", str.c_str()); + + SPDocument* doc = sp_desktop_document(this->getDesktop()); + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Edit glyph name")); + + update_glyphs(); +} + +void SvgFontsDialog::glyph_unicode_edit(const Glib::ustring&, const Glib::ustring& str){ + Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected(); + if (!i) return; + + SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node]; + glyph->repr->setAttribute("unicode", str.c_str()); + + SPDocument* doc = sp_desktop_document(this->getDesktop()); + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph unicode")); + + update_glyphs(); +} + +void SvgFontsDialog::remove_selected_font(){ + SPFont* font = get_selected_spfont(); + + sp_repr_unparent(font->repr); + SPDocument* doc = sp_desktop_document(this->getDesktop()); + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove font")); + + update_fonts(); +} + +void SvgFontsDialog::remove_selected_glyph(){ + if(!_GlyphsList.get_selection()) return; + + Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected(); + if(!i) return; + + SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node]; + sp_repr_unparent(glyph->repr); + + SPDocument* doc = sp_desktop_document(this->getDesktop()); + sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove glyph")); + + update_glyphs(); } Gtk::VBox* SvgFontsDialog::glyphs_tab(){ - Gtk::VBox* glyphs_vbox = Gtk::manage(new Gtk::VBox()); - glyphs_vbox->add(*new SvgFontsDialog::AttrEntry(this, (gchar*) _("Glyph Name:"), SP_ATTR_GLYPH_NAME)); - glyphs_vbox->add(*new SvgFontsDialog::AttrEntry(this, (gchar*) _("Unicode:"), SP_ATTR_UNICODE)); - //glyphs_vbox->add(*AttrSpin((gchar*) "Horizontal Advance"), SP_ATTR_HORIZ_ADV_X); - //glyphs_vbox->add(*AttrCombo((gchar*) "Missing Glyph"), SP_ATTR_); ? - return glyphs_vbox; + _GlyphsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::glyphs_list_button_release)); + create_glyphs_popup_menu(_GlyphsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_glyph)); + + Gtk::HBox* missing_glyph_hbox = Gtk::manage(new Gtk::HBox()); + Gtk::Label* missing_glyph_label = Gtk::manage(new Gtk::Label(_("Missing Glyph:"))); + missing_glyph_hbox->pack_start(*missing_glyph_label, false,false); + missing_glyph_hbox->pack_start(missing_glyph_button, false,false); + missing_glyph_button.set_label(_("From selection...")); + missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path)); + glyphs_vbox.pack_start(*missing_glyph_hbox, false,false); + + glyphs_vbox.add(_GlyphsListScroller); + _GlyphsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS); + _GlyphsListScroller.set_size_request(-1, 290);//It seems that is does not work. Why? I want a box with larger height + _GlyphsListScroller.add(_GlyphsList); + _GlyphsListStore = Gtk::ListStore::create(_GlyphsListColumns); + _GlyphsList.set_model(_GlyphsListStore); + _GlyphsList.append_column_editable(_("Glyph Name"), _GlyphsListColumns.glyph_name); + _GlyphsList.append_column_editable(_("Unicode"), _GlyphsListColumns.unicode); + + Gtk::HBox* hb = Gtk::manage(new Gtk::HBox()); + add_glyph_button.set_label(_("Add Glyph")); + add_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_glyph)); + + hb->pack_start(add_glyph_button, false,false); + hb->pack_start(glyph_from_path_button, false,false); + + glyphs_vbox.pack_start(*hb, false, false); + glyph_from_path_button.set_label(_("Get curves from selection...")); + glyph_from_path_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::set_glyph_description_from_selected_path)); + + dynamic_cast( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect( + sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit)); + + dynamic_cast( _GlyphsList.get_column_cell_renderer(1))->signal_edited().connect( + sigc::mem_fun(*this, &SvgFontsDialog::glyph_unicode_edit)); + + _glyphs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_glyphs)); + + return &glyphs_vbox; } -Gtk::VBox* SvgFontsDialog::kerning_tab(){ +void SvgFontsDialog::add_kerning_pair(){ + if (first_glyph.get_active_text() == "" || + second_glyph.get_active_text() == "") return; + + //look for this kerning pair on the currently selected font + this->kerning_pair = NULL; + for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){ + //TODO: It is not really correct to get only the first byte of each string. + //TODO: We should also support vertical kerning + if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0]) + && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){ + this->kerning_pair = (SPGlyphKerning*)node; + continue; + } + } + + if (this->kerning_pair) return; //We already have this kerning pair + + SPDocument* document = sp_desktop_document(this->getDesktop()); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); -//kerning setup: - Gtk::VBox* kernvbox = Gtk::manage(new Gtk::VBox()); + // create a new hkern node + Inkscape::XML::Node *repr; + repr = xml_doc->createElement("svg:hkern"); + repr->setAttribute("u1", first_glyph.get_active_text().c_str()); + repr->setAttribute("u2", second_glyph.get_active_text().c_str()); + repr->setAttribute("k", "0"); + + // Append the new hkern node to the current font + SP_OBJECT_REPR(get_selected_spfont())->appendChild(repr); + Inkscape::GC::release(repr); + + // get corresponding object + this->kerning_pair = SP_HKERN( document->getObjectByRepr(repr) ); + + sp_document_done(document, SP_VERB_DIALOG_SVG_FONTS, _("Add kerning pair")); +} + +Gtk::VBox* SvgFontsDialog::kerning_tab(){ //Kerning Setup: - kernvbox->add(*Gtk::manage(new Gtk::Label(_("Kerning Setup:")))); + kerning_vbox.add(*Gtk::manage(new Gtk::Label(_("Kerning Setup:")))); Gtk::HBox* kerning_selector = Gtk::manage(new Gtk::HBox()); kerning_selector->add(*Gtk::manage(new Gtk::Label(_("1st Glyph:")))); kerning_selector->add(first_glyph); kerning_selector->add(*Gtk::manage(new Gtk::Label(_("2nd Glyph:")))); kerning_selector->add(second_glyph); - first_glyph.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_glyphs_changed)); - second_glyph.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_glyphs_changed)); - kerning_spin.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_changed)); + kerning_selector->add(add_kernpair_button); + add_kernpair_button.set_label(_("Add pair")); + add_kernpair_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_kerning_pair)); + _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_pair_selection_changed)); + kerning_slider.signal_value_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_value_changed)); + + kerning_vbox.pack_start(*kerning_selector, false,false); - kernvbox->add(*kerning_selector); - kernvbox->add((Gtk::Widget&) kerning_preview); + kerning_vbox.add(_KerningPairsListScroller); + _KerningPairsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS); + _KerningPairsListScroller.add(_KerningPairsList); + _KerningPairsListStore = Gtk::ListStore::create(_KerningPairsListColumns); + _KerningPairsList.set_model(_KerningPairsListStore); + _KerningPairsList.append_column(_("First Unicode range"), _KerningPairsListColumns.first_glyph); + _KerningPairsList.append_column(_("Second Unicode range"), _KerningPairsListColumns.second_glyph); +// _KerningPairsList.append_column_numeric_editable(_("Kerning Value"), _KerningPairsListColumns.kerning_value, "%f"); + + kerning_vbox.add((Gtk::Widget&) kerning_preview); Gtk::HBox* kerning_amount_hbox = Gtk::manage(new Gtk::HBox()); - kernvbox->add(*kerning_amount_hbox); + kerning_vbox.pack_start(*kerning_amount_hbox, false,false); kerning_amount_hbox->add(*Gtk::manage(new Gtk::Label(_("Kerning value:")))); - kerning_amount_hbox->add(kerning_spin); + kerning_amount_hbox->add(kerning_slider); kerning_preview.set_size(300 + 20, 150 + 20); _font_da.set_size(150 + 20, 50 + 20); - return kernvbox; + return &kerning_vbox; } SPFont *new_font(SPDocument *document) @@ -320,33 +667,66 @@ SPFont *new_font(SPDocument *document) // create a new font Inkscape::XML::Node *repr; repr = xml_doc->createElement("svg:font"); + + //By default, set the horizontal advance to 1024 units + repr->setAttribute("horiz-adv-x", "1024"); // Append the new font node to defs SP_OBJECT_REPR(defs)->appendChild(repr); - Inkscape::GC::release(repr); + + //create a missing glyph + Inkscape::XML::Node *fontface; + fontface = xml_doc->createElement("svg:font-face"); + fontface->setAttribute("units-per-em", "1024"); + repr->appendChild(fontface); + + //create a missing glyph + Inkscape::XML::Node *mg; + mg = xml_doc->createElement("svg:missing-glyph"); + mg->setAttribute("d", "M0,0h1020v1024h-1020z"); + repr->appendChild(mg); // get corresponding object SPFont *f = SP_FONT( document->getObjectByRepr(repr) ); g_assert(f != NULL); g_assert(SP_IS_FONT(f)); - + Inkscape::GC::release(mg); + Inkscape::GC::release(repr); return f; } +void set_font_family(SPFont* font, char* str){ + if (!font) return; + SPObject* obj; + for (obj=font->children; obj; obj=obj->next){ + if (SP_IS_FONTFACE(obj)){ + obj->repr->setAttribute("font-family", str); + } + } + + sp_document_done(font->document, SP_VERB_DIALOG_SVG_FONTS, _("Set font family")); +} void SvgFontsDialog::add_font(){ SPDocument* doc = sp_desktop_document(this->getDesktop()); SPFont* font = new_font(doc); const int count = _model->children().size(); - std::ostringstream os; - os << "font" << count; + std::ostringstream os, os2; + os << _("font") << " " << count; font->setLabel(os.str().c_str()); + os2 << "SVGFont " << count; + SPObject* obj; + for (obj=font->children; obj; obj=obj->next){ + if (SP_IS_FONTFACE(obj)){ + obj->repr->setAttribute("font-family", os2.str().c_str()); + } + } + update_fonts(); // select_font(font); -// on_font_selection_changed(); sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add font")); } @@ -359,7 +739,7 @@ SvgFontsDialog::SvgFontsDialog() Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox()); Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox()); - vbox->pack_start(_font_list); + vbox->pack_start(_FontsList); vbox->pack_start(_add, false, false); hbox->add(*vbox); hbox->add(_font_settings); @@ -367,9 +747,9 @@ SvgFontsDialog::SvgFontsDialog() //List of SVGFonts declared in a document: _model = Gtk::ListStore::create(_columns); - _font_list.set_model(_model); - _font_list.append_column_editable(_("_Font"), _columns.label); - _font_list.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed)); + _FontsList.set_model(_model); + _FontsList.append_column_editable(_("_Font"), _columns.label); + _FontsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed)); this->update_fonts(); @@ -393,6 +773,12 @@ SvgFontsDialog::SvgFontsDialog() preview_entry_hbox->add(*Gtk::manage(new Gtk::Label(_("Preview Text:")))); preview_entry_hbox->add(_preview_entry); + _FontsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::fonts_list_button_release)); + create_fonts_popup_menu(_FontsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_font)); + + _defs_observer.set(SP_DOCUMENT_DEFS(sp_desktop_document(this->getDesktop()))); + _defs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_fonts)); + _getContents()->show_all(); } diff --git a/src/ui/dialog/svg-fonts-dialog.h b/src/ui/dialog/svg-fonts-dialog.h index 9eab3b2f8..d1d6b440d 100644 --- a/src/ui/dialog/svg-fonts-dialog.h +++ b/src/ui/dialog/svg-fonts-dialog.h @@ -27,7 +27,7 @@ #include "display/nr-svgfonts.h" #include "attributes.h" - +#include "xml/helper-observer.h" class SvgFontDrawingArea : Gtk::DrawingArea{ public: @@ -66,17 +66,24 @@ public: void update_fonts(); SvgFont* get_selected_svgfont(); SPFont* get_selected_spfont(); + SPGlyph* get_selected_glyph(); + SPGlyphKerning* get_selected_kerning_pair(); + + //TODO: these methods should be private, right?! void on_font_selection_changed(); + void on_kerning_pair_selection_changed(); void on_preview_text_changed(); - void on_glyphs_changed(); - void on_kerning_changed(); + void on_kerning_pair_changed(); + void on_kerning_value_changed(); void on_setwidth_changed(); void add_font(); + //TODO: AttrEntry is currently unused. Should we remove it? class AttrEntry : public Gtk::HBox { public: AttrEntry(SvgFontsDialog* d, gchar* lbl, const SPAttributeEnum attr); + void set_text(char*); private: SvgFontsDialog* dialog; void on_attr_changed(); @@ -85,11 +92,41 @@ public: }; private: + void update_glyphs(); + void update_sensitiveness(); + void update_global_settings_tab(); + void populate_glyphs_box(); + void populate_kerning_pairs_box(); + void set_glyph_description_from_selected_path(); + void missing_glyph_description_from_selected_path(); + void add_glyph(); + void glyph_unicode_edit(const Glib::ustring&, const Glib::ustring&); + void glyph_name_edit(const Glib::ustring&, const Glib::ustring&); + void remove_selected_glyph(); + void remove_selected_font(); + + void add_kerning_pair(); + + void create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot rem); + void glyphs_list_button_release(GdkEventButton* event); + + void create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot rem); + void fonts_list_button_release(GdkEventButton* event); + + Inkscape::XML::SignalObserver _defs_observer; //in order to update fonts + Inkscape::XML::SignalObserver _glyphs_observer; + Gtk::HBox* AttrCombo(gchar* lbl, const SPAttributeEnum attr); // Gtk::HBox* AttrSpin(gchar* lbl, const SPAttributeEnum attr); Gtk::VBox* global_settings_tab(); + AttrEntry* _familyname_entry; + Gtk::VBox* kerning_tab(); Gtk::VBox* glyphs_tab(); + Gtk::Button _add; + Gtk::Button add_glyph_button; + Gtk::Button glyph_from_path_button; + Gtk::Button missing_glyph_button; class Columns : public Gtk::TreeModel::ColumnRecord { @@ -107,14 +144,63 @@ private: }; Glib::RefPtr _model; Columns _columns; - Gtk::Button _add; - Gtk::TreeView _font_list; + Gtk::TreeView _FontsList; + + class GlyphsColumns : public Gtk::TreeModel::ColumnRecord + { + public: + GlyphsColumns() + { + add(glyph_node); + add(glyph_name); + add(unicode); + } + + Gtk::TreeModelColumn glyph_node; + Gtk::TreeModelColumn glyph_name; + Gtk::TreeModelColumn unicode; + }; + GlyphsColumns _GlyphsListColumns; + Glib::RefPtr _GlyphsListStore; + Gtk::TreeView _GlyphsList; + Gtk::ScrolledWindow _GlyphsListScroller; + + class KerningPairColumns : public Gtk::TreeModel::ColumnRecord + { + public: + KerningPairColumns() + { + add(first_glyph); + add(second_glyph); + add(kerning_value); + add(spnode); + } + + Gtk::TreeModelColumn first_glyph; + Gtk::TreeModelColumn second_glyph; + Gtk::TreeModelColumn kerning_value; + Gtk::TreeModelColumn spnode; + }; + KerningPairColumns _KerningPairsListColumns; + Glib::RefPtr _KerningPairsListStore; + Gtk::TreeView _KerningPairsList; + Gtk::ScrolledWindow _KerningPairsListScroller; + Gtk::Button add_kernpair_button; + Gtk::VBox _font_settings; + Gtk::VBox global_vbox; + Gtk::VBox glyphs_vbox; + Gtk::VBox kerning_vbox; Gtk::Entry _preview_entry; + + Gtk::Menu _FontsContextMenu; + Gtk::Menu _GlyphsContextMenu; + SvgFontDrawingArea _font_da, kerning_preview; GlyphComboBox first_glyph, second_glyph; SPGlyphKerning* kerning_pair; - Gtk::SpinButton kerning_spin, setwidth_spin; + Gtk::SpinButton setwidth_spin; + Gtk::HScale kerning_slider; class EntryWidget : public Gtk::HBox { diff --git a/src/unicoderange.cpp b/src/unicoderange.cpp index a4dcc8a0b..72df25b5c 100644 --- a/src/unicoderange.cpp +++ b/src/unicoderange.cpp @@ -98,3 +98,29 @@ bool UnicodeRange::contains(gchar unicode){ } return false; } + +Glib::ustring UnicodeRange::attribute_string(){ + Glib::ustring result; + unsigned int i; + for(i=0; iunichars.size(); i++){ + result += this->unichars[i]; + if (i!=this->unichars.size()-1) result += ","; + } + + for(i=0; irange.size(); i++){ + result += "U+" + Glib::ustring(this->range[i].start) + "-" + Glib::ustring(this->range[i].end); + if (i!=this->range.size()-1) result += ","; + } + + return result; +} + +gunichar UnicodeRange::sample_glyph(){ + //This could be better + if (unichars.size()) + return unichars[0]; + if (range.size()) + return hex2int(range[0].start); + return (gunichar) ' '; +} + diff --git a/src/unicoderange.h b/src/unicoderange.h index 656f53c50..b0c7f34a9 100644 --- a/src/unicoderange.h +++ b/src/unicoderange.h @@ -1,5 +1,6 @@ #include #include +#include struct Urange{ gchar* start; @@ -11,6 +12,8 @@ public: UnicodeRange(const gchar* val); int add_range(gchar* val); bool contains(gchar unicode); +Glib::ustring attribute_string(); +gunichar sample_glyph(); private: std::vector range; diff --git a/src/xml/Makefile_insert b/src/xml/Makefile_insert index 9ca81b231..3321031c9 100644 --- a/src/xml/Makefile_insert +++ b/src/xml/Makefile_insert @@ -18,6 +18,8 @@ xml_libspxml_a_SOURCES = \ xml/comment-node.h \ xml/composite-node-observer.cpp xml/composite-node-observer.h \ xml/element-node.h \ + xml/helper-observer.cpp \ + xml/helper-observer.h \ xml/node-observer.h \ xml/quote.cpp \ xml/quote.h \ diff --git a/src/xml/helper-observer.cpp b/src/xml/helper-observer.cpp new file mode 100644 index 000000000..620a88d8c --- /dev/null +++ b/src/xml/helper-observer.cpp @@ -0,0 +1,43 @@ +#include "helper-observer.h" + +namespace Inkscape { +namespace XML { + +// Very simple observer that just emits a signal if anything happens to a node +SignalObserver::SignalObserver() + : _oldsel(0) +{} + +// Add this observer to the SPObject and remove it from any previous object +void SignalObserver::set(SPObject* o) +{ + if(_oldsel && _oldsel->repr) + _oldsel->repr->removeObserver(*this); + if(o && o->repr) + o->repr->addObserver(*this); + _oldsel = o; +} + +void SignalObserver::notifyChildAdded(XML::Node&, XML::Node&, XML::Node*) +{ signal_changed()(); } + +void SignalObserver::notifyChildRemoved(XML::Node&, XML::Node&, XML::Node*) +{ signal_changed()(); } + +void SignalObserver::notifyChildOrderChanged(XML::Node&, XML::Node&, XML::Node*, XML::Node*) +{ signal_changed()(); } + +void SignalObserver::notifyContentChanged(XML::Node&, Util::ptr_shared, Util::ptr_shared) +{} + +void SignalObserver::notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared, Util::ptr_shared) +{ signal_changed()(); } + +sigc::signal& SignalObserver::signal_changed() +{ + return _signal_changed; +} + +} //namespace XML +} //namespace Inkscape + diff --git a/src/xml/helper-observer.h b/src/xml/helper-observer.h new file mode 100644 index 000000000..d028d390b --- /dev/null +++ b/src/xml/helper-observer.h @@ -0,0 +1,35 @@ +#ifndef __XML_HELPER_OBSERVER__ +#define __XML_HELPER_OBSERVER__ + +#include "node-observer.h" +#include "node.h" +#include "../sp-object.h" +//#include "../sp-object-repr.h" +#include + +namespace Inkscape { + namespace XML { + class Node; + + // Very simple observer that just emits a signal if anything happens to a node + class SignalObserver : public NodeObserver + { + public: + SignalObserver(); + + // Add this observer to the SPObject and remove it from any previous object + void set(SPObject* o); + void notifyChildAdded(Node&, Node&, Node*); + void notifyChildRemoved(Node&, Node&, Node*); + void notifyChildOrderChanged(Node&, Node&, Node*, Node*); + void notifyContentChanged(Node&, Util::ptr_shared, Util::ptr_shared); + void notifyAttributeChanged(Node&, GQuark, Util::ptr_shared, Util::ptr_shared); + sigc::signal& signal_changed(); + private: + sigc::signal _signal_changed; + SPObject* _oldsel; + }; + } +} + +#endif //#ifndef __XML_HELPER_OBSERVER__ -- 2.30.2