diff options
| author | Felipe Corr??a da Silva Sanches <juca@members.fsf.org> | 2008-12-28 18:35:14 +0000 |
|---|---|---|
| committer | JucaBlues <JucaBlues@users.sourceforge.net> | 2008-12-28 18:35:14 +0000 |
| commit | c3e30311de25dc8b874ce99ccdb864e0a18cf7d6 (patch) | |
| tree | 1a9d0a2de159bd94fab214717136e1474d4db08c /src/ui | |
| parent | Makes sure a Gaussian filter is applied to premultiplied data. (diff) | |
| download | inkscape-c3e30311de25dc8b874ce99ccdb864e0a18cf7d6.tar.gz inkscape-c3e30311de25dc8b874ce99ccdb864e0a18cf7d6.zip | |
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
(bzr r7029)
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/dialog/document-properties.cpp | 36 | ||||
| -rw-r--r-- | src/ui/dialog/document-properties.h | 28 | ||||
| -rw-r--r-- | src/ui/dialog/filter-effects-dialog.cpp | 46 | ||||
| -rw-r--r-- | src/ui/dialog/filter-effects-dialog.h | 6 | ||||
| -rw-r--r-- | src/ui/dialog/svg-fonts-dialog.cpp | 530 | ||||
| -rw-r--r-- | src/ui/dialog/svg-fonts-dialog.h | 98 |
6 files changed, 558 insertions, 186 deletions
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<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) 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 <list> -#include <sigc++/sigc++.h> +#include <sigc++/sigc++.h>// #include <gtkmm/notebook.h> #include <glibmm/i18n.h> @@ -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<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; 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<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)); 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<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(); 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 <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(); } 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<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 { |
