1 /** @file
2 * @brief SVG Fonts dialog - implementation
3 */
4 /* Authors:
5 * Felipe C. da S. Sanches <juca@members.fsf.org>
6 * Jon A. Cruz <jon@joncruz.org>
7 * Abhishek Sharma
8 *
9 * Copyright (C) 2008 Authors
10 * Released under GNU GPLv2 (or later). Read the file 'COPYING' for more information.
11 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #ifdef ENABLE_SVG_FONTS
19 #include <2geom/pathvector.h>
20 #include "document-private.h"
21 #include <gtkmm/notebook.h>
22 #include <glibmm/i18n.h>
23 #include <message-stack.h>
24 #include "selection.h"
25 #include <string.h>
26 #include "svg/svg.h"
27 #include "svg-fonts-dialog.h"
28 #include "xml/node.h"
29 #include "xml/repr.h"
31 SvgFontDrawingArea::SvgFontDrawingArea(){
32 this->text = "";
33 this->svgfont = NULL;
34 }
36 void SvgFontDrawingArea::set_svgfont(SvgFont* svgfont){
37 this->svgfont = svgfont;
38 }
40 void SvgFontDrawingArea::set_text(Glib::ustring text){
41 this->text = text;
42 redraw();
43 }
45 void SvgFontDrawingArea::set_size(int x, int y){
46 this->x = x;
47 this->y = y;
48 ((Gtk::Widget*) this)->set_size_request(x, y);
49 }
51 void SvgFontDrawingArea::redraw(){
52 ((Gtk::Widget*) this)->queue_draw();
53 }
55 bool SvgFontDrawingArea::on_expose_event (GdkEventExpose */*event*/){
56 if (this->svgfont){
57 Glib::RefPtr<Gdk::Window> window = get_window();
58 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
59 cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(this->svgfont->get_font_face(), false /* does not have reference */)) );
60 cr->set_font_size (this->y-20);
61 cr->move_to (10, 10);
62 cr->show_text (this->text.c_str());
63 }
64 return TRUE;
65 }
67 namespace Inkscape {
68 namespace UI {
69 namespace Dialog {
71 /*
72 Gtk::HBox* SvgFontsDialog::AttrEntry(gchar* lbl, const SPAttributeEnum attr){
73 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
74 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
75 Gtk::Entry* entry = Gtk::manage(new Gtk::Entry());
76 hbox->add(* entry );
77 hbox->show_all();
79 entry->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_attr_changed));
80 return hbox;
81 }
82 */
84 SvgFontsDialog::AttrEntry::AttrEntry(SvgFontsDialog* d, gchar* lbl, const SPAttributeEnum attr){
85 this->dialog = d;
86 this->attr = attr;
87 this->add(* Gtk::manage(new Gtk::Label(lbl)) );
88 this->add(entry);
89 this->show_all();
91 entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::AttrEntry::on_attr_changed));
92 }
94 void SvgFontsDialog::AttrEntry::set_text(char* t){
95 if (!t) return;
96 entry.set_text(t);
97 }
99 void SvgFontsDialog::AttrEntry::on_attr_changed(){
101 SPObject* o = NULL;
102 for(SPObject* node = this->dialog->get_selected_spfont()->children; node; node=node->next){
103 switch(this->attr){
104 case SP_PROP_FONT_FAMILY:
105 if (SP_IS_FONTFACE(node)){
106 o = node;
107 continue;
108 }
109 break;
110 default:
111 o = NULL;
112 }
113 }
115 const gchar* name = (const gchar*)sp_attribute_name(this->attr);
116 if(name && o) {
117 SP_OBJECT_REPR(o)->setAttribute((const gchar*) name, this->entry.get_text().c_str());
118 o->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
120 Glib::ustring undokey = "svgfonts:";
121 undokey += name;
122 DocumentUndo::maybeDone(o->document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS,
123 _("Set SVG Font attribute"));
124 }
126 }
128 Gtk::HBox* SvgFontsDialog::AttrCombo(gchar* lbl, const SPAttributeEnum /*attr*/){
129 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
130 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
131 hbox->add(* Gtk::manage(new Gtk::ComboBox()) );
132 hbox->show_all();
133 return hbox;
134 }
136 /*
137 Gtk::HBox* SvgFontsDialog::AttrSpin(gchar* lbl){
138 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
139 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
140 hbox->add(* Gtk::manage(new Gtk::SpinBox()) );
141 hbox->show_all();
142 return hbox;
143 }*/
145 /*** SvgFontsDialog ***/
147 GlyphComboBox::GlyphComboBox(){
148 }
150 void GlyphComboBox::update(SPFont* spfont){
151 if (!spfont) return
152 //TODO: figure out why do we need to append_text("") before clearing items properly...
154 this->append_text(""); //Gtk is refusing to clear the combobox when I comment out this line
155 this->clear_items();
157 for(SPObject* node = spfont->children; node; node=node->next){
158 if (SP_IS_GLYPH(node)){
159 this->append_text(((SPGlyph*)node)->unicode);
160 }
161 }
162 }
164 void SvgFontsDialog::on_kerning_value_changed(){
165 if (!this->kerning_pair) return;
166 SPDocument* document = sp_desktop_document(this->getDesktop());
168 //TODO: I am unsure whether this is the correct way of calling SPDocumentUndo::maybe_done
169 Glib::ustring undokey = "svgfonts:hkern:k:";
170 undokey += this->kerning_pair->u1->attribute_string();
171 undokey += ":";
172 undokey += this->kerning_pair->u2->attribute_string();
174 //slider values increase from right to left so that they match the kerning pair preview
176 //XML Tree being directly used here while it shouldn't be.
177 this->kerning_pair->getRepr()->setAttribute("k", Glib::Ascii::dtostr(get_selected_spfont()->horiz_adv_x - kerning_slider.get_value()).c_str());
178 DocumentUndo::maybeDone(document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS, _("Adjust kerning value"));
180 //populate_kerning_pairs_box();
181 kerning_preview.redraw();
182 _font_da.redraw();
183 }
185 void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event)
186 {
187 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
188 _GlyphsContextMenu.popup(event->button, event->time);
189 }
190 }
192 void SvgFontsDialog::kerning_pairs_list_button_release(GdkEventButton* event)
193 {
194 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
195 _KerningPairsContextMenu.popup(event->button, event->time);
196 }
197 }
199 void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event)
200 {
201 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
202 _FontsContextMenu.popup(event->button, event->time);
203 }
204 }
206 void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
207 {
208 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
209 _GlyphsContextMenu.append(*mi);
210 mi->signal_activate().connect(rem);
211 mi->show();
212 _GlyphsContextMenu.accelerate(parent);
213 }
215 void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
216 {
217 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
218 _KerningPairsContextMenu.append(*mi);
219 mi->signal_activate().connect(rem);
220 mi->show();
221 _KerningPairsContextMenu.accelerate(parent);
222 }
224 void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
225 {
226 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
227 _FontsContextMenu.append(*mi);
228 mi->signal_activate().connect(rem);
229 mi->show();
230 _FontsContextMenu.accelerate(parent);
231 }
233 void SvgFontsDialog::update_sensitiveness(){
234 if (get_selected_spfont()){
235 global_vbox.set_sensitive(true);
236 glyphs_vbox.set_sensitive(true);
237 kerning_vbox.set_sensitive(true);
238 } else {
239 global_vbox.set_sensitive(false);
240 glyphs_vbox.set_sensitive(false);
241 kerning_vbox.set_sensitive(false);
242 }
243 }
245 /* Add all fonts in the document to the combobox. */
246 void SvgFontsDialog::update_fonts()
247 {
248 SPDesktop* desktop = this->getDesktop();
249 SPDocument* document = sp_desktop_document(desktop);
250 const GSList* fonts = document->getResourceList("font");
252 _model->clear();
253 for(const GSList *l = fonts; l; l = l->next) {
254 Gtk::TreeModel::Row row = *_model->append();
255 SPFont* f = (SPFont*)l->data;
256 row[_columns.spfont] = f;
257 row[_columns.svgfont] = new SvgFont(f);
258 const gchar* lbl = f->label();
259 const gchar* id = f->getId();
260 row[_columns.label] = lbl ? lbl : (id ? id : "font");
261 }
263 update_sensitiveness();
264 }
266 void SvgFontsDialog::on_preview_text_changed(){
267 _font_da.set_text((gchar*) _preview_entry.get_text().c_str());
268 _font_da.set_text(_preview_entry.get_text());
269 }
271 void SvgFontsDialog::on_kerning_pair_selection_changed(){
272 SPGlyphKerning* kern = get_selected_kerning_pair();
273 if (!kern) {
274 kerning_preview.set_text("");
275 return;
276 }
277 Glib::ustring str;
278 str += kern->u1->sample_glyph();
279 str += kern->u2->sample_glyph();
281 kerning_preview.set_text(str);
282 this->kerning_pair = kern;
284 //slider values increase from right to left so that they match the kerning pair preview
285 kerning_slider.set_value(get_selected_spfont()->horiz_adv_x - kern->k);
286 }
288 void SvgFontsDialog::update_global_settings_tab(){
289 SPFont* font = get_selected_spfont();
290 if (!font) return;
292 SPObject* obj;
293 for (obj=font->children; obj; obj=obj->next){
294 if (SP_IS_FONTFACE(obj)){
295 _familyname_entry->set_text(((SPFontFace*) obj)->font_family);
296 }
297 }
298 }
300 void SvgFontsDialog::on_font_selection_changed(){
301 SPFont* spfont = this->get_selected_spfont();
302 if (!spfont) return;
304 SvgFont* svgfont = this->get_selected_svgfont();
305 first_glyph.update(spfont);
306 second_glyph.update(spfont);
307 kerning_preview.set_svgfont(svgfont);
308 _font_da.set_svgfont(svgfont);
309 _font_da.redraw();
311 double set_width = spfont->horiz_adv_x;
312 setwidth_spin.set_value(set_width);
314 kerning_slider.set_range(0, set_width);
315 kerning_slider.set_draw_value(false);
316 kerning_slider.set_value(0);
318 update_global_settings_tab();
319 populate_glyphs_box();
320 populate_kerning_pairs_box();
321 update_sensitiveness();
322 }
324 void SvgFontsDialog::on_setwidth_changed(){
325 SPFont* spfont = this->get_selected_spfont();
326 if (spfont){
327 spfont->horiz_adv_x = setwidth_spin.get_value();
328 //TODO: tell cairo that the glyphs cache has to be invalidated
329 // The current solution is to recreate the whole cairo svgfont.
330 // This is not a good solution to the issue because big fonts will result in poor performance.
331 update_glyphs();
332 }
333 }
335 SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair()
336 {
337 Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
338 if(i)
339 return (*i)[_KerningPairsListColumns.spnode];
340 return NULL;
341 }
343 SvgFont* SvgFontsDialog::get_selected_svgfont()
344 {
345 Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
346 if(i)
347 return (*i)[_columns.svgfont];
348 return NULL;
349 }
351 SPFont* SvgFontsDialog::get_selected_spfont()
352 {
353 Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
354 if(i)
355 return (*i)[_columns.spfont];
356 return NULL;
357 }
359 SPGlyph* SvgFontsDialog::get_selected_glyph()
360 {
361 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
362 if(i)
363 return (*i)[_GlyphsListColumns.glyph_node];
364 return NULL;
365 }
367 Gtk::VBox* SvgFontsDialog::global_settings_tab(){
368 _familyname_entry = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY);
370 global_vbox.pack_start(*_familyname_entry, false, false);
371 /* global_vbox->add(*AttrCombo((gchar*) _("Style:"), SP_PROP_FONT_STYLE));
372 global_vbox->add(*AttrCombo((gchar*) _("Variant:"), SP_PROP_FONT_VARIANT));
373 global_vbox->add(*AttrCombo((gchar*) _("Weight:"), SP_PROP_FONT_WEIGHT));
374 */
376 //Set Width (horiz_adv_x):
377 Gtk::HBox* setwidth_hbox = Gtk::manage(new Gtk::HBox());
378 setwidth_hbox->add(*Gtk::manage(new Gtk::Label(_("Set width:"))));
379 setwidth_hbox->add(setwidth_spin);
381 setwidth_spin.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_setwidth_changed));
382 setwidth_spin.set_range(0, 4096);
383 setwidth_spin.set_increments(10, 0);
384 global_vbox.pack_start(*setwidth_hbox, false, false);
386 return &global_vbox;
387 }
389 void
390 SvgFontsDialog::populate_glyphs_box()
391 {
392 if (!_GlyphsListStore) return;
393 _GlyphsListStore->clear();
395 SPFont* spfont = this->get_selected_spfont();
396 _glyphs_observer.set(spfont);
398 for(SPObject* node = spfont->children; node; node=node->next){
399 if (SP_IS_GLYPH(node)){
400 Gtk::TreeModel::Row row = *(_GlyphsListStore->append());
401 row[_GlyphsListColumns.glyph_node] = (SPGlyph*) node;
402 row[_GlyphsListColumns.glyph_name] = ((SPGlyph*) node)->glyph_name;
403 row[_GlyphsListColumns.unicode] = ((SPGlyph*) node)->unicode;
404 }
405 }
406 }
408 void
409 SvgFontsDialog::populate_kerning_pairs_box()
410 {
411 if (!_KerningPairsListStore) return;
412 _KerningPairsListStore->clear();
414 SPFont* spfont = this->get_selected_spfont();
416 for(SPObject* node = spfont->children; node; node=node->next){
417 if (SP_IS_HKERN(node)){
418 Gtk::TreeModel::Row row = *(_KerningPairsListStore->append());
419 row[_KerningPairsListColumns.first_glyph] = ((SPGlyphKerning*) node)->u1->attribute_string().c_str();
420 row[_KerningPairsListColumns.second_glyph] = ((SPGlyphKerning*) node)->u2->attribute_string().c_str();
421 row[_KerningPairsListColumns.kerning_value] = ((SPGlyphKerning*) node)->k;
422 row[_KerningPairsListColumns.spnode] = (SPGlyphKerning*) node;
423 }
424 }
425 }
427 SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count)
428 {
429 g_return_val_if_fail(font != NULL, NULL);
430 Inkscape::XML::Document *xml_doc = document->getReprDoc();
432 // create a new glyph
433 Inkscape::XML::Node *repr;
434 repr = xml_doc->createElement("svg:glyph");
436 std::ostringstream os;
437 os << _("glyph") << " " << count;
438 repr->setAttribute("glyph-name", os.str().c_str());
440 // Append the new glyph node to the current font
441 SP_OBJECT_REPR(font)->appendChild(repr);
442 Inkscape::GC::release(repr);
444 // get corresponding object
445 SPGlyph *g = SP_GLYPH( document->getObjectByRepr(repr) );
447 g_assert(g != NULL);
448 g_assert(SP_IS_GLYPH(g));
450 return g;
451 }
453 void SvgFontsDialog::update_glyphs(){
454 SPFont* font = get_selected_spfont();
455 if (!font) return;
456 populate_glyphs_box();
457 populate_kerning_pairs_box();
458 first_glyph.update(font);
459 second_glyph.update(font);
460 get_selected_svgfont()->refresh();
461 _font_da.redraw();
462 }
464 void SvgFontsDialog::add_glyph(){
465 const int count = _GlyphsListStore->children().size();
466 SPDocument* doc = sp_desktop_document(this->getDesktop());
467 /* SPGlyph* glyph =*/ new_glyph(doc, get_selected_spfont(), count+1);
469 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add glyph"));
471 update_glyphs();
472 }
474 void SvgFontsDialog::set_glyph_description_from_selected_path(){
475 SPDesktop* desktop = this->getDesktop();
476 if (!desktop) {
477 g_warning("SvgFontsDialog: No active desktop");
478 return;
479 }
481 Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
482 SPDocument* doc = sp_desktop_document(desktop);
483 Inkscape::Selection* sel = sp_desktop_selection(desktop);
484 if (sel->isEmpty()){
485 char *msg = _("Select a <b>path</b> to define the curves of a glyph");
486 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
487 return;
488 }
490 Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
491 if (!node) return;//TODO: should this be an assert?
492 if (!node->matchAttributeName("d") || !node->attribute("d")){
493 char *msg = _("The selected object does not have a <b>path</b> description.");
494 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
495 return;
496 } //TODO: //Is there a better way to tell it to to the user?
498 Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
500 //This matrix flips the glyph vertically
501 Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
502 pathv*=m;
503 //then we offset it
504 pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
506 SPGlyph* glyph = get_selected_glyph();
507 if (!glyph){
508 char *msg = _("No glyph selected in the SVGFonts dialog.");
509 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
510 return;
511 }
512 //XML Tree being directly used here while it shouldn't be.
513 glyph->getRepr()->setAttribute("d", (char*) sp_svg_write_path (pathv));
514 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
516 update_glyphs();
517 }
519 void SvgFontsDialog::missing_glyph_description_from_selected_path(){
520 SPDesktop* desktop = this->getDesktop();
521 if (!desktop) {
522 g_warning("SvgFontsDialog: No active desktop");
523 return;
524 }
526 Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
527 SPDocument* doc = sp_desktop_document(desktop);
528 Inkscape::Selection* sel = sp_desktop_selection(desktop);
529 if (sel->isEmpty()){
530 char *msg = _("Select a <b>path</b> to define the curves of a glyph");
531 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
532 return;
533 }
535 Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
536 if (!node) return;//TODO: should this be an assert?
537 if (!node->matchAttributeName("d") || !node->attribute("d")){
538 char *msg = _("The selected object does not have a <b>path</b> description.");
539 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
540 return;
541 } //TODO: //Is there a better way to tell it to to the user?
543 Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
545 //This matrix flips the glyph vertically
546 Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
547 pathv*=m;
548 //then we offset it
549 // pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
550 pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(1000));//TODO: use here the units-per-em attribute?
552 SPObject* obj;
553 for (obj = get_selected_spfont()->children; obj; obj=obj->next){
554 if (SP_IS_MISSING_GLYPH(obj)){
556 //XML Tree being directly used here while it shouldn't be.
557 obj->getRepr()->setAttribute("d", (char*) sp_svg_write_path (pathv));
558 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
559 }
560 }
562 update_glyphs();
563 }
565 void SvgFontsDialog::reset_missing_glyph_description(){
566 SPDesktop* desktop = this->getDesktop();
567 if (!desktop) {
568 g_warning("SvgFontsDialog: No active desktop");
569 return;
570 }
572 SPDocument* doc = sp_desktop_document(desktop);
573 SPObject* obj;
574 for (obj = get_selected_spfont()->children; obj; obj=obj->next){
575 if (SP_IS_MISSING_GLYPH(obj)){
576 //XML Tree being directly used here while it shouldn't be.
577 obj->getRepr()->setAttribute("d", (char*) "M0,0h1000v1024h-1000z");
578 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Reset missing-glyph"));
579 }
580 }
582 update_glyphs();
583 }
585 void SvgFontsDialog::glyph_name_edit(const Glib::ustring&, const Glib::ustring& str){
586 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
587 if (!i) return;
589 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
590 //XML Tree being directly used here while it shouldn't be.
591 glyph->getRepr()->setAttribute("glyph-name", str.c_str());
593 SPDocument* doc = sp_desktop_document(this->getDesktop());
594 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Edit glyph name"));
596 update_glyphs();
597 }
599 void SvgFontsDialog::glyph_unicode_edit(const Glib::ustring&, const Glib::ustring& str){
600 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
601 if (!i) return;
603 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
604 //XML Tree being directly used here while it shouldn't be.
605 glyph->getRepr()->setAttribute("unicode", str.c_str());
607 SPDocument* doc = sp_desktop_document(this->getDesktop());
608 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph unicode"));
610 update_glyphs();
611 }
613 void SvgFontsDialog::remove_selected_font(){
614 SPFont* font = get_selected_spfont();
615 if (!font) return;
617 //XML Tree being directly used here while it shouldn't be.
618 sp_repr_unparent(font->getRepr());
619 SPDocument* doc = sp_desktop_document(this->getDesktop());
620 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove font"));
622 update_fonts();
623 }
625 void SvgFontsDialog::remove_selected_glyph(){
626 if(!_GlyphsList.get_selection()) return;
628 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
629 if(!i) return;
631 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
633 //XML Tree being directly used here while it shouldn't be.
634 sp_repr_unparent(glyph->getRepr());
636 SPDocument* doc = sp_desktop_document(this->getDesktop());
637 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove glyph"));
639 update_glyphs();
640 }
642 void SvgFontsDialog::remove_selected_kerning_pair(){
643 if(!_KerningPairsList.get_selection()) return;
645 Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
646 if(!i) return;
648 SPGlyphKerning* pair = (*i)[_KerningPairsListColumns.spnode];
650 //XML Tree being directly used here while it shouldn't be.
651 sp_repr_unparent(pair->getRepr());
653 SPDocument* doc = sp_desktop_document(this->getDesktop());
654 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove kerning pair"));
656 update_glyphs();
657 }
659 Gtk::VBox* SvgFontsDialog::glyphs_tab(){
660 _GlyphsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::glyphs_list_button_release));
661 create_glyphs_popup_menu(_GlyphsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_glyph));
663 Gtk::HBox* missing_glyph_hbox = Gtk::manage(new Gtk::HBox());
664 Gtk::Label* missing_glyph_label = Gtk::manage(new Gtk::Label(_("Missing Glyph:")));
665 missing_glyph_hbox->pack_start(*missing_glyph_label, false,false);
666 missing_glyph_hbox->pack_start(missing_glyph_button, false,false);
667 missing_glyph_hbox->pack_start(missing_glyph_reset_button, false,false);
668 missing_glyph_button.set_label(_("From selection..."));
669 missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path));
670 missing_glyph_reset_button.set_label(_("Reset"));
671 missing_glyph_reset_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::reset_missing_glyph_description));
673 glyphs_vbox.pack_start(*missing_glyph_hbox, false,false);
675 glyphs_vbox.add(_GlyphsListScroller);
676 _GlyphsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
677 _GlyphsListScroller.set_size_request(-1, 290);//It seems that is does not work. Why? I want a box with larger height
678 _GlyphsListScroller.add(_GlyphsList);
679 _GlyphsListStore = Gtk::ListStore::create(_GlyphsListColumns);
680 _GlyphsList.set_model(_GlyphsListStore);
681 _GlyphsList.append_column_editable(_("Glyph name"), _GlyphsListColumns.glyph_name);
682 _GlyphsList.append_column_editable(_("Matching string"), _GlyphsListColumns.unicode);
684 Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
685 add_glyph_button.set_label(_("Add Glyph"));
686 add_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_glyph));
688 hb->pack_start(add_glyph_button, false,false);
689 hb->pack_start(glyph_from_path_button, false,false);
691 glyphs_vbox.pack_start(*hb, false, false);
692 glyph_from_path_button.set_label(_("Get curves from selection..."));
693 glyph_from_path_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::set_glyph_description_from_selected_path));
695 dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect(
696 sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit));
698 dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(1))->signal_edited().connect(
699 sigc::mem_fun(*this, &SvgFontsDialog::glyph_unicode_edit));
701 _glyphs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_glyphs));
703 return &glyphs_vbox;
704 }
706 void SvgFontsDialog::add_kerning_pair(){
707 if (first_glyph.get_active_text() == "" ||
708 second_glyph.get_active_text() == "") return;
710 //look for this kerning pair on the currently selected font
711 this->kerning_pair = NULL;
712 for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){
713 //TODO: It is not really correct to get only the first byte of each string.
714 //TODO: We should also support vertical kerning
715 if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0])
716 && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){
717 this->kerning_pair = (SPGlyphKerning*)node;
718 continue;
719 }
720 }
722 if (this->kerning_pair) return; //We already have this kerning pair
724 SPDocument* document = sp_desktop_document(this->getDesktop());
725 Inkscape::XML::Document *xml_doc = document->getReprDoc();
727 // create a new hkern node
728 Inkscape::XML::Node *repr = xml_doc->createElement("svg:hkern");
730 repr->setAttribute("u1", first_glyph.get_active_text().c_str());
731 repr->setAttribute("u2", second_glyph.get_active_text().c_str());
732 repr->setAttribute("k", "0");
734 // Append the new hkern node to the current font
735 SP_OBJECT_REPR(get_selected_spfont())->appendChild(repr);
736 Inkscape::GC::release(repr);
738 // get corresponding object
739 this->kerning_pair = SP_HKERN( document->getObjectByRepr(repr) );
741 DocumentUndo::done(document, SP_VERB_DIALOG_SVG_FONTS, _("Add kerning pair"));
742 }
744 Gtk::VBox* SvgFontsDialog::kerning_tab(){
745 _KerningPairsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::kerning_pairs_list_button_release));
746 create_kerning_pairs_popup_menu(_KerningPairsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_kerning_pair));
748 //Kerning Setup:
749 kerning_vbox.add(*Gtk::manage(new Gtk::Label(_("Kerning Setup"))));
750 Gtk::HBox* kerning_selector = Gtk::manage(new Gtk::HBox());
751 kerning_selector->add(*Gtk::manage(new Gtk::Label(_("1st Glyph:"))));
752 kerning_selector->add(first_glyph);
753 kerning_selector->add(*Gtk::manage(new Gtk::Label(_("2nd Glyph:"))));
754 kerning_selector->add(second_glyph);
755 kerning_selector->add(add_kernpair_button);
756 add_kernpair_button.set_label(_("Add pair"));
757 add_kernpair_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_kerning_pair));
758 _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_pair_selection_changed));
759 kerning_slider.signal_value_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_value_changed));
761 kerning_vbox.pack_start(*kerning_selector, false,false);
763 kerning_vbox.add(_KerningPairsListScroller);
764 _KerningPairsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
765 _KerningPairsListScroller.add(_KerningPairsList);
766 _KerningPairsListStore = Gtk::ListStore::create(_KerningPairsListColumns);
767 _KerningPairsList.set_model(_KerningPairsListStore);
768 _KerningPairsList.append_column(_("First Unicode range"), _KerningPairsListColumns.first_glyph);
769 _KerningPairsList.append_column(_("Second Unicode range"), _KerningPairsListColumns.second_glyph);
770 // _KerningPairsList.append_column_numeric_editable(_("Kerning Value"), _KerningPairsListColumns.kerning_value, "%f");
772 kerning_vbox.add((Gtk::Widget&) kerning_preview);
774 Gtk::HBox* kerning_amount_hbox = Gtk::manage(new Gtk::HBox());
775 kerning_vbox.pack_start(*kerning_amount_hbox, false,false);
776 kerning_amount_hbox->add(*Gtk::manage(new Gtk::Label(_("Kerning value:"))));
777 kerning_amount_hbox->add(kerning_slider);
779 kerning_preview.set_size(300 + 20, 150 + 20);
780 _font_da.set_size(150 + 20, 50 + 20);
782 return &kerning_vbox;
783 }
785 SPFont *new_font(SPDocument *document)
786 {
787 g_return_val_if_fail(document != NULL, NULL);
789 SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
791 Inkscape::XML::Document *xml_doc = document->getReprDoc();
793 // create a new font
794 Inkscape::XML::Node *repr = xml_doc->createElement("svg:font");
796 //By default, set the horizontal advance to 1024 units
797 repr->setAttribute("horiz-adv-x", "1024");
799 // Append the new font node to defs
800 SP_OBJECT_REPR(defs)->appendChild(repr);
802 //create a missing glyph
803 Inkscape::XML::Node *fontface;
804 fontface = xml_doc->createElement("svg:font-face");
805 fontface->setAttribute("units-per-em", "1024");
806 repr->appendChild(fontface);
808 //create a missing glyph
809 Inkscape::XML::Node *mg;
810 mg = xml_doc->createElement("svg:missing-glyph");
811 mg->setAttribute("d", "M0,0h1000v1024h-1000z");
812 repr->appendChild(mg);
814 // get corresponding object
815 SPFont *f = SP_FONT( document->getObjectByRepr(repr) );
817 g_assert(f != NULL);
818 g_assert(SP_IS_FONT(f));
819 Inkscape::GC::release(mg);
820 Inkscape::GC::release(repr);
821 return f;
822 }
824 void set_font_family(SPFont* font, char* str){
825 if (!font) return;
826 SPObject* obj;
827 for (obj=font->children; obj; obj=obj->next){
828 if (SP_IS_FONTFACE(obj)){
829 //XML Tree being directly used here while it shouldn't be.
830 obj->getRepr()->setAttribute("font-family", str);
831 }
832 }
834 DocumentUndo::done(font->document, SP_VERB_DIALOG_SVG_FONTS, _("Set font family"));
835 }
837 void SvgFontsDialog::add_font(){
838 SPDocument* doc = sp_desktop_document(this->getDesktop());
839 SPFont* font = new_font(doc);
841 const int count = _model->children().size();
842 std::ostringstream os, os2;
843 os << _("font") << " " << count;
844 font->setLabel(os.str().c_str());
846 os2 << "SVGFont " << count;
847 SPObject* obj;
848 for (obj=font->children; obj; obj=obj->next){
849 if (SP_IS_FONTFACE(obj)){
850 //XML Tree being directly used here while it shouldn't be.
851 obj->getRepr()->setAttribute("font-family", os2.str().c_str());
852 }
853 }
855 update_fonts();
856 // select_font(font);
858 DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add font"));
859 }
861 SvgFontsDialog::SvgFontsDialog()
862 : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW)
863 {
864 _add.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_font));
866 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
867 Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
869 vbox->pack_start(_FontsList);
870 vbox->pack_start(_add, false, false);
871 hbox->add(*vbox);
872 hbox->add(_font_settings);
873 _getContents()->add(*hbox);
875 //List of SVGFonts declared in a document:
876 _model = Gtk::ListStore::create(_columns);
877 _FontsList.set_model(_model);
878 _FontsList.append_column_editable(_("_Font"), _columns.label);
879 _FontsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed));
881 this->update_fonts();
883 Gtk::Notebook *tabs = Gtk::manage(new Gtk::Notebook());
884 tabs->set_scrollable();
886 tabs->append_page(*global_settings_tab(), _("_Global Settings"), true);
887 tabs->append_page(*glyphs_tab(), _("_Glyphs"), true);
888 tabs->append_page(*kerning_tab(), _("_Kerning"), true);
890 _font_settings.add(*tabs);
892 //Text Preview:
893 _preview_entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_preview_text_changed));
894 _getContents()->add((Gtk::Widget&) _font_da);
895 _preview_entry.set_text(_("Sample Text"));
896 _font_da.set_text(_("Sample Text"));
898 Gtk::HBox* preview_entry_hbox = Gtk::manage(new Gtk::HBox());
899 _getContents()->add(*preview_entry_hbox);
900 preview_entry_hbox->add(*Gtk::manage(new Gtk::Label(_("Preview Text:"))));
901 preview_entry_hbox->add(_preview_entry);
903 _FontsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::fonts_list_button_release));
904 create_fonts_popup_menu(_FontsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_font));
906 _defs_observer.set(SP_DOCUMENT_DEFS(sp_desktop_document(this->getDesktop())));
907 _defs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_fonts));
909 _getContents()->show_all();
910 }
912 SvgFontsDialog::~SvgFontsDialog(){}
914 } // namespace Dialog
915 } // namespace UI
916 } // namespace Inkscape
918 #endif //#ifdef ENABLE_SVG_FONTS
920 /*
921 Local Variables:
922 mode:c++
923 c-file-style:"stroustrup"
924 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
925 indent-tabs-mode:nil
926 fill-column:99
927 End:
928 */
929 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :