Code

Merging 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 <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 compare_them(char* s1, char* s2){
95         unsigned int p=0;
96         while((s1[p] == s2[p]) && s1[p] != '\0' && s2[p] != '\0') p++;
97         if (s1[p]=='\0') return p;
98         else return 0;
99 }
101 cairo_status_t
102 SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
103                                 const char      *utf8,
104                                 int             utf8_len,
105                                 cairo_glyph_t   **glyphs,
106                                 int             *num_glyphs,
107                                 cairo_text_cluster_t **clusters,
108                                 int                  *num_clusters,
109                                 cairo_text_cluster_flags_t         *flags)
111         //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
112         // is used to properly render this string. It also defines the respective coordinates of each glyph. Thus, it
113         // has to read the attributes of the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
114         //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
116     unsigned long i;
117     int count = 0;
118     char* _utf8 = (char*) utf8;
119     unsigned int len;
121     //First we findout whats the worst case number of glyphs.
122     while(_utf8[0] != '\0'){
123         _utf8++;
124         count++;
125     }
127     //We use that info to allocate memory for the glyphs
128     *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t));
130     char* previous_unicode = NULL; //This is used for kerning
131     gchar* previous_glyph_name = NULL; //This is used for kerning
133     count=0;
134     double x=0, y=0;//These vars store the position of the glyph within the rendered string
135     bool is_horizontal_text = true; //TODO
136     _utf8 = (char*) utf8;
137     while(_utf8[0] != '\0'){
138         len = 0;
139         for (i=0; i < (unsigned long) this->glyphs.size(); i++){
140             if ( (len = compare_them(this->glyphs[i]->unicode, _utf8)) ){
141                 //check whether is there a glyph declared on the SVG document
142                 // that matches with the text string in its current position
143                 for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
144                     //apply glyph kerning if appropriate
145                     if (SP_IS_HKERN(node) && is_horizontal_text){
146                         if (    (((SPHkern*)node)->u1->contains(previous_unicode[0])
147                                  || ((SPHkern*)node)->g1->contains(previous_glyph_name)) &&
148                                 (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
149                                  || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
150                         )//TODO: verify what happens when using unicode strings.
151                                 x -= (((SPHkern*)node)->k / this->font->horiz_adv_x);
152                     }
153                     if (SP_IS_VKERN(node) && !is_horizontal_text){
154                         if (    (((SPVkern*)node)->u1->contains(previous_unicode[0])
155                                  || ((SPVkern*)node)->g1->contains(previous_glyph_name)) &&
156                                 (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0])
157                                  || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name))
158                         )//TODO: idem
159                                 y -= (((SPVkern*)node)->k / this->font->vert_adv_y);
160                     }
161                 }
162                 previous_unicode = this->glyphs[i]->unicode;//used for kerning checking
163                 previous_glyph_name = this->glyphs[i]->glyph_name;//used for kerning checking
164                 (*glyphs)[count].index = i;
165                 (*glyphs)[count].x = x;
166                 (*glyphs)[count++].y = y;
167                 //advance glyph coordinates:
168                 if (is_horizontal_text) x++;
169                 else y++;
170                 _utf8+=len; //advance 'len' chars in our string pointer
171                 //continue;
172                 goto dirty;
173             }
174         }
175 dirty:
176         if (!len){
177                 (*glyphs)[count].index = i;
178                 (*glyphs)[count].x = x;
179                 (*glyphs)[count++].y = y;
180                 //advance glyph coordinates:
181                 if (is_horizontal_text) x++;
182                 else y++;
183                 _utf8++; //advance 1 char in our string pointer
184         }
185     }
186     *num_glyphs = count;
187     return CAIRO_STATUS_SUCCESS;
190 cairo_status_t
191 SvgFont::scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
192                                unsigned long         glyph,
193                                cairo_t              *cr,
194                                cairo_text_extents_t *metrics)
196     // This method does the actual rendering of glyphs.
198     // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document 
199     // The id of the missing-glyph is always equal to glyphs.size()
200     // All the other glyphs have ids ranging from 0 to glyphs.size()-1
202     if (glyph > this->glyphs.size())     return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
204     SPObject* node;
205     if (glyph == this->glyphs.size()){
206         if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
207         node = (SPObject*) this->missingglyph;
208         g_warning("RENDER MISSING-GLYPH");
209     } else {
210         node = (SPObject*) this->glyphs[glyph];
211         g_warning("RENDER %s", this->glyphs[glyph]->unicode);
212     }
214     //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
215     // or using the d attribute of a glyph node.
216     // pathv stores the path description from the d attribute:
217     Geom::PathVector pathv;
218     if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) {
219         pathv = sp_svg_read_pathv(((SPGlyph*)node)->d);
220     } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) {
221         pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d);
222     } else {
223         return CAIRO_STATUS_SUCCESS;  // FIXME: is this the right code to return?
224     }
226     if (!pathv.empty()){
227         //This glyph has a path description on its d attribute, so we render it:
228         cairo_new_path(cr);
229         Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x);
230         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>() )
231         feed_pathvector_to_cairo (cr, pathv, s, area, false, 0);
232         cairo_fill(cr);
233     }
235     //TODO: render the SVG described on this glyph's child nodes.
236     return CAIRO_STATUS_SUCCESS;
239 cairo_font_face_t*
240 SvgFont::get_font_face(){
241     if (!this->userfont) {
242         for(SPObject* node = this->font->children;node;node=node->next){
243             if (SP_IS_GLYPH(node)){
244                 g_warning("glyphs.push_back((SPGlyph*)node); (node->unicode='%s')", ((SPGlyph*)node)->unicode);
245                 this->glyphs.push_back((SPGlyph*)node);
246             }
247             if (SP_IS_MISSING_GLYPH(node)){
248                 g_warning("missingglyph=(SPMissingGlyph*)node;");
249                 this->missingglyph=(SPMissingGlyph*)node;
250             }
251         }
252         this->userfont = new UserFont(this);
253     }
254     return this->userfont->face;
256 #endif //#ifdef ENABLE_SVG_FONTS