1 #include "config.h"
2 #ifdef ENABLE_SVG_FONTS
3 /*
4 * SVGFonts rendering implementation
5 *
6 * Authors:
7 * Felipe C. da S. Sanches <felipe.sanches@gmail.com>
8 *
9 * Copyright (C) 2008 Felipe C. da S. Sanches
10 *
11 * Released under GNU GPL version 2 or later.
12 * Read the file 'COPYING' for more information.
13 */
15 #include <libnr/n-art-bpath-2geom.h>
16 #include <2geom/pathvector.h>
17 #include <2geom/transforms.h>
18 #include "../style.h"
19 #include <cairo.h>
20 #include <vector>
21 #include "svg/svg.h"
22 #include "inkscape-cairo.h"
23 #include "nr-svgfonts.h"
25 //***********************************//
26 // SvgFontDrawingArea Implementation //
27 //***********************************//
28 class SvgFontDrawingArea : Gtk::DrawingArea{
29 public:
30 SvgFontDrawingArea(SvgFont* svgfont){
31 this->svgfont = svgfont;
32 }
33 private:
34 SvgFont* svgfont;
36 bool on_expose_event (GdkEventExpose *event){
37 Glib::RefPtr<Gdk::Window> window = get_window();
38 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
39 cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(this->svgfont->get_font_face(), false /* does not have reference */)) );
40 cr->set_font_size (100);
41 cr->move_to (100, 100);
42 cr->show_text ("A@!A!@A");
44 return TRUE;
45 }
46 };//class SvgFontDrawingArea
48 //*************************//
49 // UserFont Implementation //
50 //*************************//
52 // I wrote this binding code because Cairomm does not yet support userfonts. I have moved this code to cairomm and sent them a patch.
53 // Once Cairomm incorporate the UserFonts binding, this code should be removed from inkscape and Cairomm API should be used.
55 static cairo_user_data_key_t key;
57 static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
58 cairo_font_extents_t *metrics){
59 cairo_font_face_t* face;
60 face = cairo_scaled_font_get_font_face(scaled_font);
61 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
62 return instance->scaled_font_init(scaled_font, metrics);
63 }
65 static cairo_status_t font_text_to_glyphs_cb (cairo_scaled_font_t *scaled_font,
66 const char *utf8,
67 cairo_glyph_t **glyphs,
68 int *num_glyphs){
69 cairo_font_face_t* face;
70 face = cairo_scaled_font_get_font_face(scaled_font);
71 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
72 return instance->scaled_font_text_to_glyphs(scaled_font, utf8, glyphs, num_glyphs);
73 }
75 static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t *scaled_font,
76 unsigned long glyph,
77 cairo_t *cr,
78 cairo_text_extents_t *metrics){
79 cairo_font_face_t* face;
80 face = cairo_scaled_font_get_font_face(scaled_font);
81 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
82 return instance->scaled_font_render_glyph(scaled_font, glyph, cr, metrics);
83 }
85 UserFont::UserFont(SvgFont* instance){
86 this->face = cairo_user_font_face_create ();
87 cairo_user_font_face_set_init_func (this->face, font_init_cb);
88 cairo_user_font_face_set_render_glyph_func (this->face, font_render_glyph_cb);
89 cairo_user_font_face_set_text_to_glyphs_func (this->face, font_text_to_glyphs_cb);
91 cairo_font_face_set_user_data (this->face, &key, (void*)instance, (cairo_destroy_func_t) NULL);
92 }
94 //******************************//
95 // SvgFont class Implementation //
96 //******************************//
97 SvgFont::SvgFont(SPFont* spfont){
98 this->font = spfont;
99 this->missingglyph = NULL;
100 this->userfont = NULL;
102 //This is an auxiliary gtkWindow used only while we do not have proper Pango integration with cairo-user-fonts.
103 Gtk::Window* window;
104 SvgFontDrawingArea* font_da;
106 window = new Gtk::Window();
107 window->set_default_size (1200, 850);
108 font_da = new SvgFontDrawingArea(this);
109 window->add((Gtk::Widget&) *font_da);
110 window->show_all();
111 }
113 cairo_status_t
114 SvgFont::scaled_font_init (cairo_scaled_font_t *scaled_font,
115 cairo_font_extents_t *metrics)
116 {
117 //TODO
118 // metrics->ascent = .75;
119 // metrics->descent = .25;
120 return CAIRO_STATUS_SUCCESS;
121 }
123 unsigned int compare_them(char* s1, char* s2){
124 unsigned int p=0;
125 while((s1[p] == s2[p]) && s1[p] != '\0' && s2[p] != '\0') p++;
126 if (s1[p]=='\0') return p;
127 else return 0;
128 }
130 cairo_status_t
131 SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
132 const char *utf8,
133 cairo_glyph_t **glyphs,
134 int *num_glyphs)
135 {
136 //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
137 // is used to properly render this string. It alse defines the respective coordinates of each glyph. Thus, it
138 // has to read the attributes od the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
139 //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
141 unsigned long i;
142 int count = 0;
143 char* _utf8 = (char*) utf8;
144 unsigned int len;
146 //First we findout whats the worst case number of glyphs.
147 while(_utf8[0] != '\0'){
148 _utf8++;
149 count++;
150 }
152 //We use that info to allocate memory for the glyphs
153 *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t));
155 char* previous_unicode = NULL; //This is used for kerning
156 gchar* previous_glyph_name = NULL; //This is used for kerning
158 count=0;
159 double x=0, y=0;//These vars store the position of the glyph within the rendered string
160 bool is_horizontal_text = true; //TODO
161 _utf8 = (char*) utf8;
162 while(_utf8[0] != '\0'){
163 len = 0;
164 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
165 if ( (len = compare_them(this->glyphs[i]->unicode, _utf8)) ){
166 //check whether is there a glyph declared on the SVG document
167 // that matches with the text string in its current position
168 for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
169 //apply glyph kerning if appropriate
170 if (SP_IS_HKERN(node) && is_horizontal_text){
171 if ( (((SPHkern*)node)->u1->contains(previous_unicode[0])
172 || ((SPHkern*)node)->g1->contains(previous_glyph_name)) &&
173 (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
174 || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
175 )//TODO: verify what happens when using unicode strings.
176 x -= (((SPHkern*)node)->k / this->font->horiz_adv_x);
177 }
178 if (SP_IS_VKERN(node) && !is_horizontal_text){
179 if ( (((SPVkern*)node)->u1->contains(previous_unicode[0])
180 || ((SPVkern*)node)->g1->contains(previous_glyph_name)) &&
181 (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
182 || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
183 )//TODO: idem
184 y -= (((SPVkern*)node)->k / this->font->vert_adv_y);
185 }
186 }
187 previous_unicode = this->glyphs[i]->unicode;//used for kerning checking
188 previous_glyph_name = this->glyphs[i]->glyph_name;//used for kerning checking
190 (*glyphs)[count].index = i;
191 (*glyphs)[count].x = x;
192 (*glyphs)[count++].y = y;
193 //advance glyph coordinates:
194 if (is_horizontal_text) x++;
195 else y++;
196 _utf8+=len; //advance 'len' chars in our string pointer
197 continue;
198 }
199 }
200 if (!len){
201 (*glyphs)[count].index = i;
202 (*glyphs)[count].x = x;
203 (*glyphs)[count++].y = y;
204 //advance glyph coordinates:
205 if (is_horizontal_text) x++;
206 else y++;
207 _utf8++; //advance 1 char in our string pointer
208 }
209 }
210 *num_glyphs = count;
211 return CAIRO_STATUS_SUCCESS;
212 }
214 cairo_status_t
215 SvgFont::scaled_font_render_glyph (cairo_scaled_font_t *scaled_font,
216 unsigned long glyph,
217 cairo_t *cr,
218 cairo_text_extents_t *metrics)
219 {
220 // This method does the actual rendering of glyphs.
222 // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document
223 // The id of the missing-glyph is always equal to glyphs.size()
224 // All the other glyphs have ids ranging from 0 to glyphs.size()-1
226 if (glyph > this->glyphs.size()) return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
228 SPObject* node;
229 if (glyph == this->glyphs.size()){
230 if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
231 node = (SPObject*) this->missingglyph;
232 g_warning("RENDER MISSING-GLYPH");
233 } else {
234 node = (SPObject*) this->glyphs[glyph];
235 g_warning("RENDER %s", this->glyphs[glyph]->unicode);
236 }
238 //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
239 // or using the d attribute of a glyph node.
240 // pathv stores the path description from the d attribute:
241 Geom::PathVector pathv;
242 if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) {
243 pathv = sp_svg_read_pathv(((SPGlyph*)node)->d);
244 } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) {
245 pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d);
246 } else {
247 return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
248 }
250 if (!pathv.empty()){
251 //This glyph has a path description on its d attribute, so we render it:
252 cairo_new_path(cr);
253 Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x);
254 NRRect area(0,0,1,1); //I need help here!
255 feed_pathvector_to_cairo (cr, pathv, s, area.upgrade(), false, 0);
256 cairo_fill(cr);
257 }
259 //TODO: render the SVG described on this glyph's child nodes.
260 return CAIRO_STATUS_SUCCESS;
261 }
263 cairo_font_face_t*
264 SvgFont::get_font_face(){
265 if (!this->userfont) {
266 for(SPObject* node = this->font->children;node;node=node->next){
267 if (SP_IS_GLYPH(node)){
268 g_warning("glyphs.push_back((SPGlyph*)node); (node->unicode='%s')", ((SPGlyph*)node)->unicode);
269 this->glyphs.push_back((SPGlyph*)node);
270 }
271 if (SP_IS_MISSING_GLYPH(node)){
272 g_warning("missingglyph=(SPMissingGlyph*)node;");
273 this->missingglyph=(SPMissingGlyph*)node;
274 }
275 }
276 this->userfont = new UserFont(this);
277 }
278 return this->userfont->face;
279 }
280 #endif //#ifdef ENABLE_SVG_FONTS