Code

Now users can design a font within inkscape, save it and then open the
authorJucaBlues <JucaBlues@users.sourceforge.net>
Sun, 28 Dec 2008 18:35:14 +0000 (18:35 +0000)
committerJucaBlues <JucaBlues@users.sourceforge.net>
Sun, 28 Dec 2008 18:35:14 +0000 (18:35 +0000)
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

19 files changed:
src/display/nr-svgfonts.cpp
src/display/nr-svgfonts.h
src/sp-font-face.cpp
src/sp-font.cpp
src/sp-glyph-kerning.cpp
src/sp-glyph-kerning.h
src/sp-glyph.cpp
src/sp-glyph.h
src/ui/dialog/document-properties.cpp
src/ui/dialog/document-properties.h
src/ui/dialog/filter-effects-dialog.cpp
src/ui/dialog/filter-effects-dialog.h
src/ui/dialog/svg-fonts-dialog.cpp
src/ui/dialog/svg-fonts-dialog.h
src/unicoderange.cpp
src/unicoderange.h
src/xml/Makefile_insert
src/xml/helper-observer.cpp [new file with mode: 0644]
src/xml/helper-observer.h [new file with mode: 0644]

index 723929ea77ec33bb1ead76d031179d91d3c3fd75..633f4cddd3ec197fe0c092291d15bc2fef4ebaa2 100644 (file)
@@ -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
index a41627f86fab13a5f8a3f37d3367f3c1c6064c88..ebf5ad08b059e42d2eb9bb9b4b471c764d02d088 100644 (file)
@@ -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);
index c4197406b3d2fa7d76f89b83c8d9636d68ff702f..811d9a14f0af50f5852da274e1d047fec82756a7 100644 (file)
@@ -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<FontFaceStretchType> 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<FontFaceStretchType> 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()){
index f586e6c62362e2e649263ed760125439876f47a2..f2d37f3bfa111ddb687c5645e67e6d1310bdbd54 100644 (file)
@@ -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);
index bd5340b6b25b7d7bcf08e9a8e86a1008bae4e49c..6d08f212cea1318cb9ad2a6037dc06aadca59072 100644 (file)
@@ -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;
index 8da752e496e66be845d68ba9e53e22c3d367542d..ec0866c2c2b57db9be988b3b8e1c32063623befb 100644 (file)
@@ -36,7 +36,7 @@ class GlyphNames{
 public: 
 GlyphNames(const gchar* value);
 ~GlyphNames();
-bool contains(gchar* name);
+bool contains(const char* name);
 private:
 gchar* names;
 };
index fb7cea15deb275ea2676b03bc57b6bc2c29f7f5e..8af78a0aaf55111113cd1f056e2a98ac5036b454 100644 (file)
@@ -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:
index b7a7c316fedde0fc847939b6042e4854083e6491..8c35a3a8342531de4a18e42a6c5644e2c7f48a26 100644 (file)
@@ -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;
index 713acc23e049592beb554f3a00abddac79a21eab..277652596aa95ac0e0ad36f349cd8b51fe104f2f 100644 (file)
@@ -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<char>, Util::ptr_shared<char>)
-{}
-
-void DocumentProperties::SignalObserver::notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared<char>, Util::ptr_shared<char>)
-{ signal_changed()(); }
-
-sigc::signal<void>& DocumentProperties::SignalObserver::signal_changed()
-{
-    return _signal_changed;
-}
-
 #if ENABLE_LCMS
 static void
 lcms_profile_get_name (cmsHPROFILE   profile, const gchar **name)
index 36a6c739b61ae929b9cf89e77766701a7df2ff7d..f4d5724bec922fdded0e8e43b6c89b5db102dff2 100644 (file)
@@ -15,7 +15,7 @@
 #define INKSCAPE_UI_DIALOG_DOCUMENT_PREFERENCES_H
 
 #include <list>
-#include <sigc++/sigc++.h>
+#include <sigc++/sigc++.h>//
 #include <gtkmm/notebook.h>
 #include <glibmm/i18n.h>
 
 #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<char>, Util::ptr_shared<char>);
-        void notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared<char>, Util::ptr_shared<char>);
-        sigc::signal<void>& signal_changed();
-    private:
-        sigc::signal<void> _signal_changed;
-        SPObject* _oldsel;
-    };
-
-    SignalObserver _emb_profiles_observer;
+    Inkscape::XML::SignalObserver _emb_profiles_observer;
     Gtk::Tooltips _tt;
     Gtk::Notebook  _notebook;
 
index 08df7fd05d5f38111da6dd6539280bcb32e643dd..7f92f5f01ee3d1881794277b4b008a3c55123be6 100644 (file)
@@ -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<char>, Util::ptr_shared<char>)
-    {}
-
-    void notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared<char>, Util::ptr_shared<char>)
-    { signal_changed()(); }
-
-    sigc::signal<void>& signal_changed()
-    {
-        return _signal_changed;
-    }
-private:
-    sigc::signal<void> _signal_changed;
-    SPObject* _oldsel;
-};
-
 class CheckButtonAttr : public Gtk::CheckButton, public AttrWidget
 {
 public:
@@ -1119,7 +1077,7 @@ Glib::RefPtr<Gtk::Menu> create_popup_menu(Gtk::Widget& parent, sigc::slot<void>
 
 /*** 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));
 
index ccffb2a67d78be6356540bf913328b11d05dcca2..99b411441c028fe5c59b574506c4b11b42c8854a 100644 (file)
@@ -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<Gtk::Menu> _menu;
         sigc::signal<void> _signal_filter_changed;
-        std::auto_ptr<SignalObserver> _observer;
+        std::auto_ptr<Inkscape::XML::SignalObserver> _observer;
     };
 
     class PrimitiveColumns : public Gtk::TreeModel::ColumnRecord
@@ -203,7 +203,7 @@ private:
         sigc::signal<void> _signal_primitive_changed;
         sigc::connection _scroll_connection;
         int _autoscroll;
-        std::auto_ptr<SignalObserver> _observer;
+        std::auto_ptr<Inkscape::XML::SignalObserver> _observer;
     };
 
     void init_settings_widgets();
index 4034cb10d272dfbd84213f8beb46b7a7959035a6..6ef8fa7293491d71d0a9526f4d5b53f640782530 100644 (file)
 
 #ifdef ENABLE_SVG_FONTS
 
+#include "document-private.h"
 #include <gtkmm/notebook.h>
-#include "svg-fonts-dialog.h"
 #include <glibmm/i18n.h>
+#include "selection.h"
 #include <string.h>
-#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<void> 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<void> 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<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect(
+                        sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit));
+
+       dynamic_cast<Gtk::CellRendererText*>( _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();
 }
 
index 9eab3b2f83f922c87765220dd2bc001600108896..d1d6b440dee475b1155b43c6ff44f239ba7b5370 100644 (file)
@@ -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<void> rem);
+       void glyphs_list_button_release(GdkEventButton* event);
+
+       void create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> 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<Gtk::ListStore> _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<SPGlyph*> glyph_node;
+            Gtk::TreeModelColumn<Glib::ustring> glyph_name;
+            Gtk::TreeModelColumn<Glib::ustring> unicode;
+    };
+    GlyphsColumns _GlyphsListColumns;
+    Glib::RefPtr<Gtk::ListStore> _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<Glib::ustring> first_glyph;
+            Gtk::TreeModelColumn<Glib::ustring> second_glyph;
+            Gtk::TreeModelColumn<double> kerning_value;
+            Gtk::TreeModelColumn<SPGlyphKerning*> spnode;
+    };
+    KerningPairColumns _KerningPairsListColumns;
+    Glib::RefPtr<Gtk::ListStore> _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
         {
index a4dcc8a0b41518ae18813bdbd492f41de34e0970..72df25b5cf6aefa23fe3715f596c1518f9f03a0c 100644 (file)
@@ -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; i<this->unichars.size(); i++){
+               result += this->unichars[i];
+               if (i!=this->unichars.size()-1) result += ",";
+       }
+
+       for(i=0; i<this->range.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) ' ';
+}
+
index 656f53c50e92df2d19439aef7a3ef8ec108a328d..b0c7f34a92fea5ae5dd41bbadaab01ab3f031fe5 100644 (file)
@@ -1,5 +1,6 @@
 #include <glib-object.h>
 #include<vector>
+#include <glibmm.h>
 
 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<Urange> range;
index 9ca81b23145572689fc5447e68759e7db5815e54..3321031c90ec4777f0807fdf54db6e03def4ae88 100644 (file)
@@ -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 (file)
index 0000000..620a88d
--- /dev/null
@@ -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<char>, Util::ptr_shared<char>)
+{}
+
+void SignalObserver::notifyAttributeChanged(XML::Node&, GQuark, Util::ptr_shared<char>, Util::ptr_shared<char>)
+{ signal_changed()(); }
+
+sigc::signal<void>& 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 (file)
index 0000000..d028d39
--- /dev/null
@@ -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 <sigc++/sigc++.h>
+
+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<char>, Util::ptr_shared<char>);
+                       void notifyAttributeChanged(Node&, GQuark, Util::ptr_shared<char>, Util::ptr_shared<char>);
+                       sigc::signal<void>& signal_changed();
+               private:
+                       sigc::signal<void> _signal_changed;
+                       SPObject* _oldsel;
+               };
+       }
+}
+
+#endif //#ifndef __XML_HELPER_OBSERVER__