02c9a9ea218a1469affa99e2a0d4d0c122b117d5
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;
33 static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
34 cairo_t */*cairo*/, cairo_font_extents_t *metrics){
35 cairo_font_face_t* face;
36 face = cairo_scaled_font_get_font_face(scaled_font);
37 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
38 return instance->scaled_font_init(scaled_font, metrics);
39 }
41 static cairo_status_t font_text_to_glyphs_cb (
42 cairo_scaled_font_t *scaled_font,
43 const char *utf8,
44 int utf8_len,
45 cairo_glyph_t **glyphs,
46 int *num_glyphs,
47 cairo_text_cluster_t **clusters,
48 int *num_clusters,
49 cairo_text_cluster_flags_t *flags){
50 cairo_font_face_t* face;
51 face = cairo_scaled_font_get_font_face(scaled_font);
52 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
53 return instance->scaled_font_text_to_glyphs(scaled_font, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, flags);
54 }
56 static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t *scaled_font,
57 unsigned long glyph,
58 cairo_t *cr,
59 cairo_text_extents_t *metrics){
60 cairo_font_face_t* face;
61 face = cairo_scaled_font_get_font_face(scaled_font);
62 SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
63 return instance->scaled_font_render_glyph(scaled_font, glyph, cr, metrics);
64 }
66 UserFont::UserFont(SvgFont* instance){
67 this->face = cairo_user_font_face_create ();
68 cairo_user_font_face_set_init_func (this->face, font_init_cb);
69 cairo_user_font_face_set_render_glyph_func (this->face, font_render_glyph_cb);
70 cairo_user_font_face_set_text_to_glyphs_func (this->face, font_text_to_glyphs_cb);
72 cairo_font_face_set_user_data (this->face, &key, (void*)instance, (cairo_destroy_func_t) NULL);
73 }
75 //******************************//
76 // SvgFont class Implementation //
77 //******************************//
78 SvgFont::SvgFont(SPFont* spfont){
79 this->font = spfont;
80 this->missingglyph = NULL;
81 this->userfont = NULL;
82 }
84 cairo_status_t
85 SvgFont::scaled_font_init (cairo_scaled_font_t *scaled_font,
86 cairo_font_extents_t *metrics)
87 {
88 //TODO
89 // metrics->ascent = .75;
90 // metrics->descent = .25;
91 return CAIRO_STATUS_SUCCESS;
92 }
94 unsigned int size_of_substring(gchar* substring, gchar* str){
95 const gchar* original_substring = substring;
97 while((g_utf8_get_char(substring)==g_utf8_get_char(str)) && g_utf8_get_char(substring) != 0 && g_utf8_get_char(str) != 0){
98 substring = g_utf8_next_char(substring);
99 str = g_utf8_next_char(str);
100 }
101 if (g_utf8_get_char(substring)==0)
102 return substring - original_substring;
103 else
104 return 0;
105 }
107 cairo_status_t
108 SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
109 const char *utf8,
110 int utf8_len,
111 cairo_glyph_t **glyphs,
112 int *num_glyphs,
113 cairo_text_cluster_t **clusters,
114 int *num_clusters,
115 cairo_text_cluster_flags_t *flags)
116 {
117 //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
118 // is used to properly render this string. It also defines the respective coordinates of each glyph. Thus, it
119 // has to read the attributes of the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
120 //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
122 unsigned long i;
123 int count = 0;
124 gchar* _utf8 = (gchar*) utf8;
125 unsigned int len;
127 bool missing;
128 //First we findout whats the number of glyphs needed.
129 while(g_utf8_get_char(_utf8) != 0){
130 missing = true;
131 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
132 if ( (len = size_of_substring(this->glyphs[i]->unicode, _utf8)) ){
133 //TODO: store this cluster
134 _utf8+=len;
135 count++;
136 missing=false;
137 break;
138 }
139 }
140 if (missing){
141 //TODO: store this cluster
142 _utf8++;
143 count++;
144 }
145 }
147 //g_warning("count is %d", count);
149 //We use that info to allocate memory for the glyphs
150 *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t));
152 char* previous_unicode = NULL; //This is used for kerning
153 gchar* previous_glyph_name = NULL; //This is used for kerning
155 count=0;
156 double x=0, y=0;//These vars store the position of the glyph within the rendered string
157 bool is_horizontal_text = true; //TODO
158 _utf8 = (char*) utf8;
159 while(g_utf8_get_char(_utf8) != 0){
160 len = 0;
161 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
162 if ( (len = size_of_substring(this->glyphs[i]->unicode, _utf8)) ){
163 //check whether is there a glyph declared on the SVG document
164 // that matches with the text string in its current position
165 for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
166 //apply glyph kerning if appropriate
167 if (SP_IS_HKERN(node) && is_horizontal_text){
168 if ( (((SPHkern*)node)->u1->contains(previous_unicode[0])
169 || ((SPHkern*)node)->g1->contains(previous_glyph_name)) &&
170 (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
171 || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
172 )//TODO: verify what happens when using unicode strings.
173 x -= (((SPHkern*)node)->k / this->font->horiz_adv_x);
174 }
175 if (SP_IS_VKERN(node) && !is_horizontal_text){
176 if ( (((SPVkern*)node)->u1->contains(previous_unicode[0])
177 || ((SPVkern*)node)->g1->contains(previous_glyph_name)) &&
178 (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
179 || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
180 )//TODO: idem
181 y -= (((SPVkern*)node)->k / this->font->vert_adv_y);
182 }
183 }
184 previous_unicode = this->glyphs[i]->unicode;//used for kerning checking
185 previous_glyph_name = this->glyphs[i]->glyph_name;//used for kerning checking
186 (*glyphs)[count].index = i;
187 (*glyphs)[count].x = x;
188 (*glyphs)[count++].y = y;
189 //advance glyph coordinates:
190 if (is_horizontal_text) x++;
191 else y++;
192 _utf8+=len; //advance 'len' bytes in our string pointer
193 //continue;
194 goto dirty;
195 }
196 }
197 dirty:
198 if (!len){
199 (*glyphs)[count].index = i;
200 (*glyphs)[count].x = x;
201 (*glyphs)[count++].y = y;
202 //advance glyph coordinates:
203 if (is_horizontal_text) x++;
204 else y++;
205 _utf8 = g_utf8_next_char(_utf8); //advance 1 char in our string pointer
206 }
207 }
208 *num_glyphs = count;
209 return CAIRO_STATUS_SUCCESS;
210 }
212 cairo_status_t
213 SvgFont::scaled_font_render_glyph (cairo_scaled_font_t *scaled_font,
214 unsigned long glyph,
215 cairo_t *cr,
216 cairo_text_extents_t *metrics)
217 {
218 // This method does the actual rendering of glyphs.
220 // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document
221 // The id of the missing-glyph is always equal to glyphs.size()
222 // All the other glyphs have ids ranging from 0 to glyphs.size()-1
224 if (glyph > this->glyphs.size()) return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
226 SPObject* node;
227 if (glyph == this->glyphs.size()){
228 if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
229 node = (SPObject*) this->missingglyph;
230 g_warning("RENDER MISSING-GLYPH");
231 } else {
232 node = (SPObject*) this->glyphs[glyph];
233 g_warning("RENDER %s", this->glyphs[glyph]->unicode);
234 }
236 //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
237 // or using the d attribute of a glyph node.
238 // pathv stores the path description from the d attribute:
239 Geom::PathVector pathv;
240 if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) {
241 pathv = sp_svg_read_pathv(((SPGlyph*)node)->d);
242 } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) {
243 pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d);
244 } else {
245 return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
246 }
248 if (!pathv.empty()){
249 //This glyph has a path description on its d attribute, so we render it:
250 cairo_new_path(cr);
251 Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x);
252 Geom::Rect area( Geom::Point(0,0), Geom::Point(1,1) ); //I need help here! (reaction: note that the 'area' parameter is an *optional* rect, so you can pass an empty boost::optional<Geom::Rect>() )
253 feed_pathvector_to_cairo (cr, pathv, s, area, false, 0);
254 cairo_fill(cr);
255 }
257 //TODO: render the SVG described on this glyph's child nodes.
258 return CAIRO_STATUS_SUCCESS;
259 }
261 cairo_font_face_t*
262 SvgFont::get_font_face(){
263 if (!this->userfont) {
264 for(SPObject* node = this->font->children;node;node=node->next){
265 if (SP_IS_GLYPH(node)){
266 g_warning("glyphs.push_back((SPGlyph*)node); (node->unicode='%s')", ((SPGlyph*)node)->unicode);
267 this->glyphs.push_back((SPGlyph*)node);
268 }
269 if (SP_IS_MISSING_GLYPH(node)){
270 g_warning("missingglyph=(SPMissingGlyph*)node;");
271 this->missingglyph=(SPMissingGlyph*)node;
272 }
273 }
274 this->userfont = new UserFont(this);
275 }
276 return this->userfont->face;
277 }
278 #endif //#ifdef ENABLE_SVG_FONTS