Code

Merge from trunk.
[inkscape.git] / src / display / nr-svgfonts.cpp
1 #include "config.h"
2 #ifdef ENABLE_SVG_FONTS
3 /*
4  * SVGFonts rendering implementation
5  *
6  * Authors:
7  *    Felipe C. da S. Sanches <juca@members.fsf.org>
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 ( cairo_scaled_font_t  *scaled_font,
42                                                const char           *utf8,
43                                                int                  utf8_len,
44                                                cairo_glyph_t        **glyphs,
45                                                int                  *num_glyphs,
46                                                cairo_text_cluster_t **clusters,
47                                                int                  *num_clusters,
48                                                cairo_text_cluster_flags_t *flags){
49     cairo_font_face_t*  face;
50     face = cairo_scaled_font_get_font_face(scaled_font);
51     SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
52     return instance->scaled_font_text_to_glyphs(scaled_font, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, flags);
53 }
55 static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t  *scaled_font,
56                                             unsigned long         glyph,
57                                             cairo_t              *cr,
58                                             cairo_text_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_render_glyph(scaled_font, glyph, cr, metrics);
63 }
65 UserFont::UserFont(SvgFont* instance){
66     this->face = cairo_user_font_face_create ();
67     cairo_user_font_face_set_init_func          (this->face, font_init_cb);
68     cairo_user_font_face_set_render_glyph_func  (this->face, font_render_glyph_cb);
69     cairo_user_font_face_set_text_to_glyphs_func(this->face, font_text_to_glyphs_cb);
71     cairo_font_face_set_user_data (this->face, &key, (void*)instance, (cairo_destroy_func_t) NULL);
72 }
74 //******************************//
75 // SvgFont class Implementation //
76 //******************************//
77 SvgFont::SvgFont(SPFont* spfont){
78     this->font = spfont;
79     this->missingglyph = NULL;
80     this->userfont = NULL;
81 }
83 cairo_status_t
84 SvgFont::scaled_font_init (cairo_scaled_font_t  */*scaled_font*/,
85                            cairo_font_extents_t */*metrics*/)
86 {
87 //TODO
88 //  metrics->ascent  = .75;
89 //  metrics->descent = .25;
90   return CAIRO_STATUS_SUCCESS;
91 }
93 unsigned int size_of_substring(const char* substring, gchar* str){
94     const gchar* original_substring = substring;
96     while((g_utf8_get_char(substring)==g_utf8_get_char(str)) && g_utf8_get_char(substring) != 0 && g_utf8_get_char(str) != 0){
97         substring = g_utf8_next_char(substring);
98         str = g_utf8_next_char(str);
99     }
100     if (g_utf8_get_char(substring)==0)
101         return substring - original_substring;
102     else
103         return 0;
106 //TODO: in these macros, verify what happens when using unicode strings.
107 #define Match_VKerning_Rule (((SPVkern*)node)->u1->contains(previous_unicode[0])\
108                 || ((SPVkern*)node)->g1->contains(previous_glyph_name)) &&\
109                 (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0])\
110                 || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name.c_str()))
112 #define Match_HKerning_Rule (((SPHkern*)node)->u1->contains(previous_unicode[0])\
113                 || ((SPHkern*)node)->g1->contains(previous_glyph_name)) &&\
114                 (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0])\
115                 || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name.c_str()))
117 cairo_status_t
118 SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t  */*scaled_font*/,
119                                      const char           *utf8,
120                                      int                  /*utf8_len*/,
121                                      cairo_glyph_t        **glyphs,
122                                      int                  *num_glyphs,
123                                      cairo_text_cluster_t **/*clusters*/,
124                                      int                  */*num_clusters*/,
125                                      cairo_text_cluster_flags_t */*flags*/)
127     //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
128     // is used to properly render this string. It also defines the respective coordinates of each glyph. Thus, it
129     // has to read the attributes of the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
130     //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
132     unsigned long i;
133     int count = 0;
134     gchar* _utf8 = (gchar*) utf8;
135     unsigned int len;
137     bool missing;
138     //First we findout whats the number of glyphs needed.
139     while(g_utf8_get_char(_utf8)){
140         missing = true;
141         for (i=0; i < (unsigned long) this->glyphs.size(); i++){
142             if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){
143                 //TODO: store this cluster
144                 _utf8+=len;
145                 count++;
146                 missing=false;
147                 break;
148             }
149         }
150         if (missing){
151             //TODO: store this cluster
152             _utf8++;
153             count++;
154         }
155     }
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;
169     while(g_utf8_get_char(_utf8)){
170         len = 0;
171         for (i=0; i < (unsigned long) this->glyphs.size(); i++){
172             //check whether is there a glyph declared on the SVG document
173             // that matches with the text string in its current position
174             if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){
175                 for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
176                     //apply glyph kerning if appropriate
177                     if (SP_IS_HKERN(node) && is_horizontal_text && Match_HKerning_Rule ){
178                         x -= (((SPHkern*)node)->k / 1000.0);//TODO: use here the height of the font
179                     }
180                     if (SP_IS_VKERN(node) && !is_horizontal_text && Match_VKerning_Rule ){
181                         y -= (((SPVkern*)node)->k / 1000.0);//TODO: use here the "height" of the font
182                     }
183                 }
184                 previous_unicode = (char*) this->glyphs[i]->unicode.c_str();//used for kerning checking
185                 previous_glyph_name = (char*) this->glyphs[i]->glyph_name.c_str();//used for kerning checking
186                 (*glyphs)[count].index = i;
187                 (*glyphs)[count].x = x;
188                 (*glyphs)[count++].y = y;
190                 //advance glyph coordinates:
191                 if (is_horizontal_text) x+=(this->font->horiz_adv_x/1000.0);//TODO: use here the height of the font
192                 else y+=(this->font->vert_adv_y/1000.0);//TODO: use here the "height" of the font
193                 _utf8+=len; //advance 'len' bytes in our string pointer
194                 //continue;
195                 goto raptorz;
196             }
197         }
198     raptorz:
199         if (len==0){
200             (*glyphs)[count].index = i;
201             (*glyphs)[count].x = x;
202             (*glyphs)[count++].y = y;
204             //advance glyph coordinates:
205             if (is_horizontal_text) x+=(this->font->horiz_adv_x/1000.0);//TODO: use here the height of the font
206             else y+=(this->font->vert_adv_y/1000.0);//TODO: use here the "height" of the font
208             _utf8 = g_utf8_next_char(_utf8); //advance 1 char in our string pointer
209         }
210     }
211     *num_glyphs = count;
212     return CAIRO_STATUS_SUCCESS;
215 cairo_status_t
216 SvgFont::scaled_font_render_glyph (cairo_scaled_font_t  */*scaled_font*/,
217                                    unsigned long         glyph,
218                                    cairo_t              *cr,
219                                    cairo_text_extents_t */*metrics*/)
221     // This method does the actual rendering of glyphs.
223     // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document
224     // The id of the missing-glyph is always equal to glyphs.size()
225     // All the other glyphs have ids ranging from 0 to glyphs.size()-1
227     if (glyph > this->glyphs.size())     return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
229     SPObject* node;
230     if (glyph == this->glyphs.size()){
231         if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
232         node = (SPObject*) this->missingglyph;
233     } else {
234         node = (SPObject*) this->glyphs[glyph];
235     }
237     //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
238     // or using the d attribute of a glyph node.
239     // pathv stores the path description from the d attribute:
240     Geom::PathVector pathv;
241     if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) {
242         pathv = sp_svg_read_pathv(((SPGlyph*)node)->d);
243     } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) {
244         pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d);
245     } else {
246         return CAIRO_STATUS_SUCCESS;  // FIXME: is this the right code to return?
247     }
249     if (!pathv.empty()){
250         //This glyph has a path description on its d attribute, so we render it:
251         cairo_new_path(cr);
252         //adjust scale of the glyph
253 //        Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x);
254         Geom::Scale s(1.0/1000);//TODO: use here the units-per-em attribute?
255         //This matrix flips the glyph vertically
256         Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
257         //then we offset it
258 //      pathv += Geom::Point(Geom::Coord(0),Geom::Coord(-((SPFont*) node->parent)->horiz_adv_x));
259         pathv += Geom::Point(Geom::Coord(0),Geom::Coord(-1000));//TODO: use here the units-per-em attribute?
261         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 Geom::OptRect() )
263         feed_pathvector_to_cairo (cr, pathv, s*m, area, false, 0);
264         cairo_fill(cr);
265     }
267     //TODO: render the SVG described on this glyph's child nodes.
268     return CAIRO_STATUS_SUCCESS;
271 cairo_font_face_t*
272 SvgFont::get_font_face(){
273     if (!this->userfont) {
274         for(SPObject* node = this->font->children;node;node=node->next){
275             if (SP_IS_GLYPH(node)){
276                 this->glyphs.push_back((SPGlyph*)node);
277             }
278             if (SP_IS_MISSING_GLYPH(node)){
279                 this->missingglyph=(SPMissingGlyph*)node;
280             }
281         }
282         this->userfont = new UserFont(this);
283     }
284     return this->userfont->face;
287 void SvgFont::refresh(){
288     this->glyphs.clear();
289     delete this->userfont;
290     this->userfont = NULL;
293 #endif //#ifdef ENABLE_SVG_FONTS
295 /*
296   Local Variables:
297   mode:c++
298   c-file-style:"stroustrup"
299   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
300   indent-tabs-mode:nil
301   fill-column:99
302   End:
303 */
304 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :