0dd5d996ae8dc5cefd6c451c60973fef88d88654
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 <2geom/pathvector.h>
16 #include <2geom/transforms.h>
17 #include "../style.h"
18 #include <cairo.h>
19 #include <vector>
20 #include "svg/svg.h"
21 #include "inkscape-cairo.h"
22 #include "nr-svgfonts.h"
24 //*************************//
25 // UserFont Implementation //
26 //*************************//
28 // I wrote this binding code because Cairomm does not yet support userfonts. I have moved this code to cairomm and sent them a patch.
29 // Once Cairomm incorporate the UserFonts binding, this code should be removed from inkscape and Cairomm API should be used.
31 static cairo_user_data_key_t key;
34 /**
35 * Felipe,
36 * Cairo is changing its userfont api a little bit for 1.7+, 1.8,
37 * etc. This switch makes your code compile both on 1.6 and 1.7. Is
38 * this ok?
39 *
40 * Bob
41 */
42 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 7, 0))
44 static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
45 cairo_t */*cairo*/, cairo_font_extents_t *metrics){
46 cairo_font_face_t* face;
47 face = cairo_scaled_font_get_font_face(scaled_font);
48 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
49 return instance->scaled_font_init(scaled_font, metrics);
50 }
52 static cairo_status_t font_text_to_glyphs_cb (
53 cairo_scaled_font_t *scaled_font,
54 const char *utf8,
55 int /*utf8_len*/,
56 cairo_glyph_t **glyphs,
57 int *num_glyphs,
58 cairo_text_cluster_t **/*clusters*/,
59 int */*num_clusters*/,
60 cairo_bool_t */*backward*/){
61 cairo_font_face_t* face;
62 face = cairo_scaled_font_get_font_face(scaled_font);
63 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
64 return instance->scaled_font_text_to_glyphs(scaled_font, utf8, glyphs, num_glyphs);
65 }
68 #else
70 static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
71 cairo_font_extents_t *metrics){
72 cairo_font_face_t* face;
73 face = cairo_scaled_font_get_font_face(scaled_font);
74 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
75 return instance->scaled_font_init(scaled_font, metrics);
76 }
78 static cairo_status_t font_text_to_glyphs_cb (cairo_scaled_font_t *scaled_font,
79 const char *utf8,
80 cairo_glyph_t **glyphs,
81 int *num_glyphs){
82 cairo_font_face_t* face;
83 face = cairo_scaled_font_get_font_face(scaled_font);
84 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
85 return instance->scaled_font_text_to_glyphs(scaled_font, utf8, glyphs, num_glyphs);
86 }
88 #endif
91 static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t *scaled_font,
92 unsigned long glyph,
93 cairo_t *cr,
94 cairo_text_extents_t *metrics){
95 cairo_font_face_t* face;
96 face = cairo_scaled_font_get_font_face(scaled_font);
97 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
98 return instance->scaled_font_render_glyph(scaled_font, glyph, cr, metrics);
99 }
101 UserFont::UserFont(SvgFont* instance){
102 this->face = cairo_user_font_face_create ();
103 cairo_user_font_face_set_init_func (this->face, font_init_cb);
104 cairo_user_font_face_set_render_glyph_func (this->face, font_render_glyph_cb);
105 cairo_user_font_face_set_text_to_glyphs_func (this->face, font_text_to_glyphs_cb);
107 cairo_font_face_set_user_data (this->face, &key, (void*)instance, (cairo_destroy_func_t) NULL);
108 }
110 //******************************//
111 // SvgFont class Implementation //
112 //******************************//
113 SvgFont::SvgFont(SPFont* spfont){
114 this->font = spfont;
115 this->missingglyph = NULL;
116 this->userfont = NULL;
117 }
119 cairo_status_t
120 SvgFont::scaled_font_init (cairo_scaled_font_t *scaled_font,
121 cairo_font_extents_t *metrics)
122 {
123 //TODO
124 // metrics->ascent = .75;
125 // metrics->descent = .25;
126 return CAIRO_STATUS_SUCCESS;
127 }
129 unsigned int compare_them(char* s1, char* s2){
130 unsigned int p=0;
131 while((s1[p] == s2[p]) && s1[p] != '\0' && s2[p] != '\0') p++;
132 if (s1[p]=='\0') return p;
133 else return 0;
134 }
136 cairo_status_t
137 SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
138 const char *utf8,
139 cairo_glyph_t **glyphs,
140 int *num_glyphs)
141 {
142 //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
143 // is used to properly render this string. It alse defines the respective coordinates of each glyph. Thus, it
144 // has to read the attributes od the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
145 //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
147 unsigned long i;
148 int count = 0;
149 char* _utf8 = (char*) utf8;
150 unsigned int len;
152 //First we findout whats the worst case number of glyphs.
153 while(_utf8[0] != '\0'){
154 _utf8++;
155 count++;
156 }
158 //We use that info to allocate memory for the glyphs
159 *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t));
161 char* previous_unicode = NULL; //This is used for kerning
162 gchar* previous_glyph_name = NULL; //This is used for kerning
164 count=0;
165 double x=0, y=0;//These vars store the position of the glyph within the rendered string
166 bool is_horizontal_text = true; //TODO
167 _utf8 = (char*) utf8;
168 while(_utf8[0] != '\0'){
169 len = 0;
170 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
171 if ( (len = compare_them(this->glyphs[i]->unicode, _utf8)) ){
172 //check whether is there a glyph declared on the SVG document
173 // that matches with the text string in its current position
174 for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
175 //apply glyph kerning if appropriate
176 if (SP_IS_HKERN(node) && is_horizontal_text){
177 if ( (((SPHkern*)node)->u1->contains(previous_unicode[0])
178 || ((SPHkern*)node)->g1->contains(previous_glyph_name)) &&
179 (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
180 || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
181 )//TODO: verify what happens when using unicode strings.
182 x -= (((SPHkern*)node)->k / this->font->horiz_adv_x);
183 }
184 if (SP_IS_VKERN(node) && !is_horizontal_text){
185 if ( (((SPVkern*)node)->u1->contains(previous_unicode[0])
186 || ((SPVkern*)node)->g1->contains(previous_glyph_name)) &&
187 (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
188 || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
189 )//TODO: idem
190 y -= (((SPVkern*)node)->k / this->font->vert_adv_y);
191 }
192 }
193 previous_unicode = this->glyphs[i]->unicode;//used for kerning checking
194 previous_glyph_name = this->glyphs[i]->glyph_name;//used for kerning checking
195 (*glyphs)[count].index = i;
196 (*glyphs)[count].x = x;
197 (*glyphs)[count++].y = y;
198 //advance glyph coordinates:
199 if (is_horizontal_text) x++;
200 else y++;
201 _utf8+=len; //advance 'len' chars in our string pointer
202 //continue;
203 goto dirty;
204 }
205 }
206 dirty:
207 if (!len){
208 (*glyphs)[count].index = i;
209 (*glyphs)[count].x = x;
210 (*glyphs)[count++].y = y;
211 //advance glyph coordinates:
212 if (is_horizontal_text) x++;
213 else y++;
214 _utf8++; //advance 1 char in our string pointer
215 }
216 }
217 *num_glyphs = count;
218 return CAIRO_STATUS_SUCCESS;
219 }
221 cairo_status_t
222 SvgFont::scaled_font_render_glyph (cairo_scaled_font_t *scaled_font,
223 unsigned long glyph,
224 cairo_t *cr,
225 cairo_text_extents_t *metrics)
226 {
227 // This method does the actual rendering of glyphs.
229 // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document
230 // The id of the missing-glyph is always equal to glyphs.size()
231 // All the other glyphs have ids ranging from 0 to glyphs.size()-1
233 if (glyph > this->glyphs.size()) return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
235 SPObject* node;
236 if (glyph == this->glyphs.size()){
237 if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
238 node = (SPObject*) this->missingglyph;
239 g_warning("RENDER MISSING-GLYPH");
240 } else {
241 node = (SPObject*) this->glyphs[glyph];
242 g_warning("RENDER %s", this->glyphs[glyph]->unicode);
243 }
245 //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
246 // or using the d attribute of a glyph node.
247 // pathv stores the path description from the d attribute:
248 Geom::PathVector pathv;
249 if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) {
250 pathv = sp_svg_read_pathv(((SPGlyph*)node)->d);
251 } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) {
252 pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d);
253 } else {
254 return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
255 }
257 if (!pathv.empty()){
258 //This glyph has a path description on its d attribute, so we render it:
259 cairo_new_path(cr);
260 Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x);
261 NRRect area(0,0,1,1); //I need help here!
262 feed_pathvector_to_cairo (cr, pathv, s, area.upgrade(), false, 0);
263 cairo_fill(cr);
264 }
266 //TODO: render the SVG described on this glyph's child nodes.
267 return CAIRO_STATUS_SUCCESS;
268 }
270 cairo_font_face_t*
271 SvgFont::get_font_face(){
272 if (!this->userfont) {
273 for(SPObject* node = this->font->children;node;node=node->next){
274 if (SP_IS_GLYPH(node)){
275 g_warning("glyphs.push_back((SPGlyph*)node); (node->unicode='%s')", ((SPGlyph*)node)->unicode);
276 this->glyphs.push_back((SPGlyph*)node);
277 }
278 if (SP_IS_MISSING_GLYPH(node)){
279 g_warning("missingglyph=(SPMissingGlyph*)node;");
280 this->missingglyph=(SPMissingGlyph*)node;
281 }
282 }
283 this->userfont = new UserFont(this);
284 }
285 return this->userfont->face;
286 }
287 #endif //#ifdef ENABLE_SVG_FONTS