Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
nr-svgfonts.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SVGFonts rendering implementation
4 *
5 * Authors:
6 * Felipe C. da S. Sanches <juca@members.fsf.org>
7 * Jon A. Cruz <jon@joncruz.org>
8 *
9 * Copyright (C) 2008 Felipe C. da S. Sanches
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 * Read the file 'COPYING' for more information.
13 */
14
15#include "nr-svgfonts.h"
16
17#include <vector>
18#include <cairo.h>
19#include <2geom/pathvector.h>
20#include <2geom/transforms.h>
21
22#include "display/cairo-utils.h"
23#include "display/nr-svgfonts.h"
24#include "display/nr-svgfonts.h"
25#include "display/curve.h"
26
27#include "object/sp-path.h"
29#include "object/sp-use.h"
31#include "object/sp-font-face.h"
32#include "object/sp-glyph.h"
34#include "object/sp-font.h"
36
37#include "svg/svg.h"
38
39// ************************//
40// UserFont Implementation //
41// ************************//
42
43// I wrote this binding code because Cairomm does not yet support userfonts. I have moved this code to cairomm and sent them a patch.
44// Once Cairomm incorporate the UserFonts binding, this code should be removed from inkscape and Cairomm API should be used.
45
46static cairo_user_data_key_t key;
47
48static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
49 cairo_t * /*cairo*/, cairo_font_extents_t *metrics){
50 cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font);
51 SvgFont* instance = static_cast<SvgFont*>(cairo_font_face_get_user_data(face, &key));
52 return instance->scaled_font_init(scaled_font, metrics);
53}
54
55static cairo_status_t font_text_to_glyphs_cb ( cairo_scaled_font_t *scaled_font,
56 const char *utf8,
57 int utf8_len,
58 cairo_glyph_t **glyphs,
59 int *num_glyphs,
60 cairo_text_cluster_t **clusters,
61 int *num_clusters,
62 cairo_text_cluster_flags_t *flags){
63 cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font);
64 SvgFont* instance = static_cast<SvgFont*>(cairo_font_face_get_user_data(face, &key));
65 return instance->scaled_font_text_to_glyphs(scaled_font, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, flags);
66}
67
68static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t *scaled_font,
69 unsigned long glyph,
70 cairo_t *cr,
71 cairo_text_extents_t *metrics){
72 cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font);
73 SvgFont* instance = static_cast<SvgFont*>(cairo_font_face_get_user_data(face, &key));
74 return instance->scaled_font_render_glyph(scaled_font, glyph, cr, metrics);
75}
76
78 this->face = cairo_user_font_face_create ();
79 cairo_user_font_face_set_init_func (this->face, font_init_cb);
80 cairo_user_font_face_set_render_glyph_func (this->face, font_render_glyph_cb);
81 cairo_user_font_face_set_text_to_glyphs_func(this->face, font_text_to_glyphs_cb);
82
83 cairo_font_face_set_user_data (this->face, &key, (void*)instance, (cairo_destroy_func_t) nullptr);
84}
85
86//******************************//
87// SvgFont class Implementation //
88//******************************//
90 this->font = spfont;
91 this->missingglyph = nullptr;
92 this->userfont = nullptr;
93}
94
95cairo_status_t
96SvgFont::scaled_font_init (cairo_scaled_font_t */*scaled_font*/,
97 cairo_font_extents_t */*metrics*/)
98{
99//TODO
100// metrics->ascent = .75;
101// metrics->descent = .25;
102 return CAIRO_STATUS_SUCCESS;
103}
104
105unsigned int size_of_substring(const char* substring, gchar* str){
106 const gchar* original_substring = substring;
107
108 while((g_utf8_get_char(substring)==g_utf8_get_char(str)) && g_utf8_get_char(substring) != 0 && g_utf8_get_char(str) != 0){
109 substring = g_utf8_next_char(substring);
110 str = g_utf8_next_char(str);
111 }
112 if (g_utf8_get_char(substring)==0)
113 return substring - original_substring;
114 else
115 return 0;
116}
117
118
119namespace {
120
121//TODO: in these functions, verify what happens when using unicode strings.
122
123bool MatchVKerningRule(SPVkern const *vkern,
124 SPGlyph *glyph,
125 char const *previous_unicode,
126 gchar const *previous_glyph_name)
127{
128 bool value = (vkern->u1->contains(previous_unicode[0])
129 || vkern->g1->contains(previous_glyph_name))
130 && (vkern->u2->contains(glyph->unicode[0])
131 || vkern->g2->contains(glyph->glyph_name.c_str()));
132
133 return value;
134}
135
136bool MatchHKerningRule(SPHkern const *hkern,
137 SPGlyph *glyph,
138 char const *previous_unicode,
139 gchar const *previous_glyph_name)
140{
141 bool value = (hkern->u1->contains(previous_unicode[0])
142 || hkern->g1->contains(previous_glyph_name))
143 && (hkern->u2->contains(glyph->unicode[0])
144 || hkern->g2->contains(glyph->glyph_name.c_str()));
145
146 return value;
147}
148
149} // namespace
150
151cairo_status_t
152SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/,
153 const char *utf8,
154 int /*utf8_len*/,
155 cairo_glyph_t **glyphs,
156 int *num_glyphs,
157 cairo_text_cluster_t **/*clusters*/,
158 int */*num_clusters*/,
159 cairo_text_cluster_flags_t */*flags*/)
160{
161 //This function receives a text string to be rendered. It then defines what is the sequence of glyphs that
162 // is used to properly render this string. It also defines the respective coordinates of each glyph. Thus, it
163 // has to read the attributes of the SVGFont hkern and vkern nodes in order to adjust the glyph kerning.
164 //It also determines the usage of the missing-glyph in portions of the string that does not match any of the declared glyphs.
165
166 unsigned long i;
167 int count = 0;
168 gchar* _utf8 = (gchar*) utf8;
169 unsigned int len;
170
171 bool missing;
172 //First we find out what's the number of glyphs needed.
173 while(g_utf8_get_char(_utf8)){
174 missing = true;
175 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
176 if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){
177 //TODO: store this cluster
178 _utf8+=len;
179 count++;
180 missing=false;
181 break;
182 }
183 }
184 if (missing){
185 //TODO: store this cluster
186 _utf8++;
187 count++;
188 }
189 }
190
191
192 //We use that info to allocate memory for the glyphs
193 *glyphs = (cairo_glyph_t*) malloc(count*sizeof(cairo_glyph_t));
194
195 char* previous_unicode = nullptr; //This is used for kerning
196 gchar* previous_glyph_name = nullptr; //This is used for kerning
197
198 count=0;
199 double x=0, y=0;//These vars store the position of the glyph within the rendered string
200 bool is_horizontal_text = true; //TODO
201 _utf8 = (char*) utf8;
202
203 double font_height = units_per_em();
204 while(g_utf8_get_char(_utf8)){
205 len = 0;
206 for (i=0; i < (unsigned long) this->glyphs.size(); i++){
207 //check whether is there a glyph declared on the SVG document
208 // that matches with the text string in its current position
209 if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){
210 for(auto& node: font->children) {
211 if (!previous_unicode) {
212 break;
213 }
214 //apply glyph kerning if appropriate
215 auto hkern = cast<SPHkern>(&node);
216 if (hkern && is_horizontal_text &&
217 MatchHKerningRule(hkern, this->glyphs[i], previous_unicode, previous_glyph_name) ){
218 x -= (hkern->k / font_height);
219 }
220 auto vkern = cast<SPVkern>(&node);
221 if (vkern && !is_horizontal_text &&
222 MatchVKerningRule(vkern, this->glyphs[i], previous_unicode, previous_glyph_name) ){
223 y -= (vkern->k / font_height);
224 }
225 }
226 previous_unicode = const_cast<char*>(this->glyphs[i]->unicode.c_str());//used for kerning checking
227 previous_glyph_name = const_cast<char*>(this->glyphs[i]->glyph_name.c_str());//used for kerning checking
228 (*glyphs)[count].index = i;
229 (*glyphs)[count].x = x;
230 (*glyphs)[count++].y = y;
231
232 //advance glyph coordinates:
233 if (is_horizontal_text) {
234 if (this->glyphs[i]->horiz_adv_x != 0) {
235 x+=(this->glyphs[i]->horiz_adv_x/font_height);
236 } else {
237 x+=(this->font->horiz_adv_x/font_height);
238 }
239 } else {
240 y+=(this->font->vert_adv_y/font_height);
241 }
242 _utf8+=len; //advance 'len' bytes in our string pointer
243 //continue;
244 goto raptorz;
245 }
246 }
247 raptorz:
248 if (len==0){
249 (*glyphs)[count].index = i;
250 (*glyphs)[count].x = x;
251 (*glyphs)[count++].y = y;
252
253 //advance glyph coordinates:
254 if (is_horizontal_text) x+=(this->font->horiz_adv_x/font_height);//TODO: use here the height of the font
255 else y+=(this->font->vert_adv_y/font_height);//TODO: use here the "height" of the font
256
257 _utf8 = g_utf8_next_char(_utf8); //advance 1 char in our string pointer
258 }
259 }
260 *num_glyphs = count;
261 return CAIRO_STATUS_SUCCESS;
262}
263
264void
266 if (!pathv->empty()){
267 //This glyph has a path description on its d attribute, so we render it:
268 cairo_new_path(cr);
269
270 //adjust scale of the glyph
271 Geom::Scale s(1.0/units_per_em());
272 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() )
273
274 feed_pathvector_to_cairo (cr, *pathv, s, area, false, 0);
275 cairo_fill(cr);
276 }
277}
278
279void
280SvgFont::glyph_modified(SPObject* /* blah */, unsigned int /* bleh */){
281 this->refresh();
282 //TODO: update rendering on svgfonts preview widget (in the svg fonts dialog)
283}
284
287 double units_per_em = 1024;
288 for(auto& obj: spfont->children) {
289 if (is<SPFontFace>(&obj)) {
290 //XML Tree being directly used here while it shouldn't be.
291 units_per_em = obj.getRepr()->getAttributeDouble("units_per_em", units_per_em);
292 }
293 }
294
295 double baseline_offset = units_per_em - spfont->horiz_origin_y;
296
297 //This matrix flips y-axis and places the origin at baseline
299 return pathv*m;
300}
301
302cairo_status_t
303SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/,
304 unsigned long glyph,
305 cairo_t *cr,
306 cairo_text_extents_t */*metrics*/)
307{
308 // This method does the actual rendering of glyphs.
309
310 // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document
311 // The id of the missing-glyph is always equal to glyphs.size()
312 // All the other glyphs have ids ranging from 0 to glyphs.size()-1
313
314 if (glyph > this->glyphs.size()) return CAIRO_STATUS_SUCCESS;//TODO: this is an error!
315
316 SPObject *node = nullptr;
317 if (glyph == glyphs.size()){
318 if (!missingglyph) {
319 return CAIRO_STATUS_SUCCESS;
320 }
322 } else {
323 node = glyphs[glyph];
324 }
325
326 if (!is<SPGlyph>(node) && !is<SPMissingGlyph>(node)) {
327 return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
328 }
329
330 auto spfont = cast<SPFont>(node->parent);
331 if (!spfont) {
332 return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
333 }
334
335 //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node
336 // or using the d attribute of a glyph node.
337 // pathv stores the path description from the d attribute:
338 Geom::PathVector pathv;
339
340 auto glyphNode = cast<SPGlyph>(node);
341 if (glyphNode && glyphNode->d) {
342 pathv = sp_svg_read_pathv(glyphNode->d);
343 pathv = flip_coordinate_system(spfont, pathv);
344 render_glyph_path(cr, &pathv);
345 } else {
346 auto missing = cast<SPMissingGlyph>(node);
347 if (missing && missing->d) {
348 pathv = sp_svg_read_pathv(missing->d);
349 pathv = flip_coordinate_system(spfont, pathv);
350 render_glyph_path(cr, &pathv);
351 }
352 }
353
354 if (node->hasChildren()){
355 //render the SVG described on this glyph's child nodes.
356 for(auto& child: node->children) {
357 {
358 auto path = cast<SPPath>(&child);
359 if (path) {
360 pathv = path->curve()->get_pathvector();
361 pathv = flip_coordinate_system(spfont, pathv);
362 render_glyph_path(cr, &pathv);
363 }
364 }
365 if (is<SPObjectGroup>(&child)) {
366 g_warning("TODO: svgfonts: render OBJECTGROUP");
367 }
368 auto use = cast<SPUse>(&child);
369 if (use) {
370 SPItem* item = use->ref->getObject();
371 auto path = cast<SPPath>(item);
372 if (path) {
373 auto shape = cast<SPShape>(item);
374 g_assert(shape != nullptr);
375 pathv = shape->curve()->get_pathvector();
376 pathv = flip_coordinate_system(spfont, pathv);
377 this->render_glyph_path(cr, &pathv);
378 }
379
381 }
382 }
383 }
384
385 return CAIRO_STATUS_SUCCESS;
386}
387
388cairo_font_face_t*
390 if (!this->userfont) {
391 for(auto& node: font->children) {
392 auto glyph = cast<SPGlyph>(&node);
393 if (glyph) {
394 glyphs.push_back(glyph);
395 }
396 auto missing = cast<SPMissingGlyph>(&node);
397 if (missing) {
398 missingglyph = missing;
399 }
400 }
401 this->userfont = new UserFont(this);
402 }
403 return this->userfont->face;
404}
405
407 this->glyphs.clear();
408 delete this->userfont;
409 this->userfont = nullptr;
410}
411
413 double units_per_em = 1024;
414 for (auto& obj: font->children) {
415 if (is<SPFontFace>(&obj)) {
416 //XML Tree being directly used here while it shouldn't be.
417 units_per_em = obj.getRepr()->getAttributeDouble("units-per-em", units_per_em);
418 }
419 }
420 if (units_per_em <= 0.0) {
421 units_per_em = 1024;
422 }
423 return units_per_em;
424}
425
426/*
427 Local Variables:
428 mode:c++
429 c-file-style:"stroustrup"
430 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
431 indent-tabs-mode:nil
432 fill-column:99
433 End:
434*/
435// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
void feed_pathvector_to_cairo(cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width)
Feeds path-creating calls to the cairo context translating them from the PathVector,...
Cairo integration helpers.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Sequence of subpaths.
Definition pathvector.h:122
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Scaling from the origin.
Definition transforms.h:150
bool contains(char const *name)
virtual Node * parent()=0
Get the parent of this node.
double vert_adv_y
Definition sp-font.h:30
double horiz_adv_x
Definition sp-font.h:27
double horiz_origin_y
Definition sp-font.h:26
GlyphNames * g1
UnicodeRange * u2
UnicodeRange * u1
GlyphNames * g2
Glib::ustring glyph_name
Definition sp-glyph.h:41
Glib::ustring unicode
Definition sp-glyph.h:40
Base class for visual SVG elements.
Definition sp-item.h:109
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
sigc::connection connectModified(sigc::slot< void(SPObject *, unsigned int)> slot)
Connects to the modification notification signal.
Definition sp-object.h:705
ChildrenList children
Definition sp-object.h:907
UserFont * userfont
Definition nr-svgfonts.h:51
void render_glyph_path(cairo_t *cr, Geom::PathVector *pathv)
sigc::connection glyph_modified_connection
Definition nr-svgfonts.h:54
SPFont * font
Definition nr-svgfonts.h:50
Geom::PathVector flip_coordinate_system(SPFont *spfont, Geom::PathVector pathv)
SPMissingGlyph * missingglyph
Definition nr-svgfonts.h:53
void refresh()
void glyph_modified(SPObject *, unsigned int)
SvgFont(SPFont *spfont)
cairo_font_face_t * get_font_face()
cairo_status_t scaled_font_render_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics)
std::vector< SPGlyph * > glyphs
Definition nr-svgfonts.h:52
double units_per_em()
cairo_status_t scaled_font_init(cairo_scaled_font_t *scaled_font, cairo_font_extents_t *metrics)
cairo_status_t scaled_font_text_to_glyphs(cairo_scaled_font_t *scaled_font, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *flags)
bool contains(char unicode)
UserFont(SvgFont *instance)
cairo_font_face_t * face
Definition nr-svgfonts.h:33
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
SPItem * item
Inkscape::XML::Node * node
unsigned int size_of_substring(const char *substring, gchar *str)
static cairo_status_t font_text_to_glyphs_cb(cairo_scaled_font_t *scaled_font, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *flags)
static cairo_status_t font_init_cb(cairo_scaled_font_t *scaled_font, cairo_t *, cairo_font_extents_t *metrics)
static cairo_user_data_key_t key
static cairo_status_t font_render_glyph_cb(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics)
struct _cairo cairo_t
Definition path-cairo.h:16
PathVector - a sequence of subpaths.
Ocnode * child[8]
Definition quantize.cpp:33
auto len
Definition safe-printf.h:21
Geom::PathVector sp_svg_read_pathv(char const *str)
Definition svg-path.cpp:37
Affine transformation classes.