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.h>
16 #include "../style.h"
17 #include <cairo.h>
18 #include <vector>
19 #include "svg/svg.h"
20 #include "inkscape-cairo.h"
21 #include "nr-svgfonts.h"
23 //***********************************//
24 // SvgFontDrawingArea Implementation //
25 //***********************************//
26 class SvgFontDrawingArea : Gtk::DrawingArea{
27 public:
28 SvgFontDrawingArea(SvgFont* svgfont){
29 this->svgfont = svgfont;
30 }
31 private:
32 SvgFont* svgfont;
34 bool on_expose_event (GdkEventExpose *event){
35 Glib::RefPtr<Gdk::Window> window = get_window();
36 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
37 cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(this->svgfont->get_font_face(), false /* does not have reference */)) );
38 cr->set_font_size (100);
39 cr->move_to (100, 100);
40 cr->show_text ("A@!A!@A");
42 return TRUE;
43 }
44 };//class SvgFontDrawingArea
46 //*************************//
47 // UserFont Implementation //
48 //*************************//
50 // I wrote this binding code because Cairomm does not yet support userfonts. I have moved this code to cairomm and sent them a patch.
51 // Once Cairomm incorporate the UserFonts binding, this code should be removed from inkscape and Cairomm API should be used.
53 static cairo_user_data_key_t key;
55 static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
56 cairo_font_extents_t *metrics){
57 cairo_font_face_t* face;
58 face = cairo_scaled_font_get_font_face(scaled_font);
59 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
60 return instance->scaled_font_init(scaled_font, metrics);
61 }
63 static cairo_status_t font_text_to_glyphs_cb (cairo_scaled_font_t *scaled_font,
64 const char *utf8,
65 cairo_glyph_t **glyphs,
66 int *num_glyphs){
67 cairo_font_face_t* face;
68 face = cairo_scaled_font_get_font_face(scaled_font);
69 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
70 return instance->scaled_font_text_to_glyphs(scaled_font, utf8, glyphs, num_glyphs);
71 }
73 static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t *scaled_font,
74 unsigned long glyph,
75 cairo_t *cr,
76 cairo_text_extents_t *metrics){
77 cairo_font_face_t* face;
78 face = cairo_scaled_font_get_font_face(scaled_font);
79 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
80 return instance->scaled_font_render_glyph(scaled_font, glyph, cr, metrics);
81 }
83 UserFont::UserFont(SvgFont* instance){
84 this->face = cairo_user_font_face_create ();
85 cairo_user_font_face_set_init_func (this->face, font_init_cb);
86 cairo_user_font_face_set_render_glyph_func (this->face, font_render_glyph_cb);
87 cairo_user_font_face_set_text_to_glyphs_func (this->face, font_text_to_glyphs_cb);
89 cairo_font_face_set_user_data (this->face, &key, (void*)instance, (cairo_destroy_func_t) NULL);
90 }
92 //******************************//
93 // SvgFont class Implementation //
94 //******************************//
95 SvgFont::SvgFont(SPFont* spfont){
96 this->font = spfont;
97 this->missingglyph = NULL;
98 this->userfont = NULL;
100 //This is an auxiliary gtkWindow used only while we do not have proper Pango integration with cairo-user-fonts.
101 Gtk::Window* window;
102 SvgFontDrawingArea* font_da;
104 window = new Gtk::Window();
105 window->set_default_size (1200, 850);
106 font_da = new SvgFontDrawingArea(this);
107 window->add((Gtk::Widget&) *font_da);
108 window->show_all();
109 }
111 cairo_status_t
112 SvgFont::scaled_font_init (cairo_scaled_font_t *scaled_font,
113 cairo_font_extents_t *metrics)
114 {
115 //TODO
116 // metrics->ascent = .75;
117 // metrics->descent = .25;
118 return CAIRO_STATUS_SUCCESS;
119 }
121 unsigned int compare_them(char* s1, char* s2){
122 unsigned int p=0;
123 while((s1[p] == s2[p]) && s1[p] != '\0' && s2[p] != '\0') p++;
124 if (s1[p]=='\0') return p;
125 else return 0;
126 }
128 cairo_status_t
129 SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
130 const char *utf8,
131 cairo_glyph_t **glyphs,
132 int *num_glyphs)
133 {
134 //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
135 // is used to properly render this string. It alse defines the respective coordinates of each glyph. Thus, it
136 // has to read the attributes od the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
137 //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
139 unsigned long i;
140 int count = 0;
141 char* _utf8 = (char*) utf8;
142 unsigned int len;
144 //First we findout whats the worst case number of glyphs.
145 while(_utf8[0] != '\0'){
146 _utf8++;
147 count++;
148 }
150 //We use that info to allocate memory for the glyphs
151 *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t));
153 char* previous_unicode = NULL; //This is used for kerning
154 count=0;
155 double x=0, y=0;//These vars store the position of the glyph within the rendered string
156 bool is_horizontal_text = true; //TODO
157 _utf8 = (char*) utf8;
158 while(_utf8[0] != '\0'){
159 len = 0;
160 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
161 if ( len = compare_them(this->glyphs[i]->unicode, _utf8) ){
162 //check whether is there a glyph declared on the SVG document
163 // that matches with the text string in its current position
164 for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
165 //apply glyph kerning if appropriate
166 if (SP_IS_HKERN(node) && is_horizontal_text){
167 if ( (((SPHkern*)node)->u1[0] == previous_unicode[0]) && (((SPHkern*)node)->u2[0] == this->glyphs[i]->unicode[0]))//TODO: strings
168 x -= (((SPHkern*)node)->k / this->font->horiz_adv_x);
169 }
170 if (SP_IS_VKERN(node) && !is_horizontal_text){
171 if ( (((SPVkern*)node)->u1[0] == previous_unicode[0]) && (((SPVkern*)node)->u2[0] == this->glyphs[i]->unicode[0]))//TODO: strings
172 y -= (((SPVkern*)node)->k / this->font->vert_adv_y);
173 }
174 }
175 previous_unicode = this->glyphs[i]->unicode;//used for kerning checking
176 (*glyphs)[count].index = i;
177 (*glyphs)[count].x = x;
178 (*glyphs)[count++].y = y;
179 //advance glyph coordinates:
180 if (is_horizontal_text) x++;
181 else y++;
182 _utf8+=len; //advance 'len' chars in our string pointer
183 continue;
184 }
185 }
186 if (!len){
187 (*glyphs)[count].index = i;
188 (*glyphs)[count].x = x;
189 (*glyphs)[count++].y = y;
190 //advance glyph coordinates:
191 if (is_horizontal_text) x++;
192 else y++;
193 _utf8++; //advance 1 char in our string pointer
194 }
195 }
196 *num_glyphs = count;
197 return CAIRO_STATUS_SUCCESS;
198 }
200 cairo_status_t
201 SvgFont::scaled_font_render_glyph (cairo_scaled_font_t *scaled_font,
202 unsigned long glyph,
203 cairo_t *cr,
204 cairo_text_extents_t *metrics)
205 {
206 // This method does the actual rendering of glyphs.
208 // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document
209 // The id of the missing-glyph is always equal to glyphs.size()
210 // All the other glyphs have ids ranging from 0 to glyphs.size()-1
212 if (glyph > this->glyphs.size()) return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
214 SPObject* node;
215 if (glyph == this->glyphs.size()){
216 if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
217 node = (SPObject*) this->missingglyph;
218 g_warning("RENDER MISSING-GLYPH");
219 } else {
220 node = (SPObject*) this->glyphs[glyph];
221 g_warning("RENDER %s", this->glyphs[glyph]->unicode);
222 }
224 //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
225 // or using the d attribute of a glyph node.
226 // bpath stores the path description from the d attribute:
227 NArtBpath *bpath = NULL;
228 if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) bpath = sp_svg_read_path(((SPGlyph*)node)->d);
229 if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) bpath = sp_svg_read_path(((SPMissingGlyph*)node)->d);
231 if (bpath){
232 //This glyph have a path description on its d attribute, so we render it:
233 cairo_new_path(cr);
234 NR::scale s(1.0/((SPFont*) node->parent)->horiz_adv_x);
235 NR::Matrix t(s);
236 NRRect area(0,0,1,1); //I need help here!
237 feed_curve_to_cairo (cr, bpath, t, area.upgrade(), false, 0);
238 cairo_fill(cr);
239 }
241 //TODO: render the SVG described on this glyph's child nodes.
242 return CAIRO_STATUS_SUCCESS;
243 }
245 cairo_font_face_t*
246 SvgFont::get_font_face(){
247 if (!this->userfont) {
248 for(SPObject* node = this->font->children;node;node=node->next){
249 if (SP_IS_GLYPH(node)){
250 g_warning("glyphs.push_back((SPGlyph*)node); (node->unicode='%s')", ((SPGlyph*)node)->unicode);
251 this->glyphs.push_back((SPGlyph*)node);
252 }
253 if (SP_IS_MISSING_GLYPH(node)){
254 g_warning("missingglyph=(SPMissingGlyph*)node;");
255 this->missingglyph=(SPMissingGlyph*)node;
256 }
257 }
258 this->userfont = new UserFont(this);
259 }
260 return this->userfont->face;
261 }
262 #endif //#ifdef ENABLE_SVG_FONTS