Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
font-instance.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * fred
7 * bulia byak <buliabyak@users.sf.net>
8 *
9 * Copyright (C) 2018 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#ifdef HAVE_CONFIG_H
14# include "config.h" // only include where actually required!
15#endif
16
17#include <iostream>
18#include <iomanip>
19
20#ifndef PANGO_ENABLE_ENGINE
21#define PANGO_ENABLE_ENGINE
22#endif
23
24#include <ft2build.h>
25#include FT_OUTLINE_H
26#include FT_BBOX_H
27#include FT_TRUETYPE_TAGS_H
28#include FT_TRUETYPE_TABLES_H
29#include FT_GLYPH_H
30#include FT_MULTIPLE_MASTERS_H
31
32#include <pango/pangoft2.h>
33#include <harfbuzz/hb.h>
34#include <harfbuzz/hb-ft.h>
35#include <harfbuzz/hb-ot.h>
36
37#include <glibmm/regex.h>
38
39#include <2geom/pathvector.h>
40#include <2geom/path-sink.h>
43
44#include "display/cairo-utils.h" // Inkscape::Pixbuf
45
46/*
47 * Outline extraction
48 */
49
50struct FT2GeomData
51{
52 FT2GeomData(Geom::PathBuilder &b, double s)
53 : builder(b)
54 , last(0, 0)
55 , scale(s)
56 {
57 }
58
60 Geom::Point last;
61 double scale;
62};
63
64// outline as returned by freetype
65static int ft2_move_to(FT_Vector const *to, void * i_user)
66{
67 FT2GeomData *user = (FT2GeomData*)i_user;
68 Geom::Point p(to->x, to->y);
69 // printf("m t=%f %f\n",p[0],p[1]);
70 user->builder.moveTo(p * user->scale);
71 user->last = p;
72 return 0;
73}
74
75static int ft2_line_to(FT_Vector const *to, void *i_user)
76{
77 FT2GeomData *user = (FT2GeomData*)i_user;
78 Geom::Point p(to->x, to->y);
79 // printf("l t=%f %f\n",p[0],p[1]);
80 user->builder.lineTo(p * user->scale);
81 user->last = p;
82 return 0;
83}
84
85static int ft2_conic_to(FT_Vector const *control, FT_Vector const *to, void *i_user)
86{
87 FT2GeomData *user = (FT2GeomData*)i_user;
88 Geom::Point p(to->x, to->y), c(control->x, control->y);
89 user->builder.quadTo(c * user->scale, p * user->scale);
90 // printf("b c=%f %f t=%f %f\n",c[0],c[1],p[0],p[1]);
91 user->last = p;
92 return 0;
93}
94
95static int ft2_cubic_to(FT_Vector const *control1, FT_Vector const *control2, FT_Vector const *to, void *i_user)
96{
97 FT2GeomData *user = (FT2GeomData*)i_user;
98 Geom::Point p(to->x, to->y);
99 Geom::Point c1(control1->x, control1->y);
100 Geom::Point c2(control2->x, control2->y);
101 // printf("c c1=%f %f c2=%f %f t=%f %f\n",c1[0],c1[1],c2[0],c2[1],p[0],p[1]);
102 //user->theP->CubicTo(p,3*(c1-user->last),3*(p-c2));
103 user->builder.curveTo(c1 * user->scale, c2 * user->scale, p * user->scale);
104 user->last = p;
105 return 0;
106}
107
108/*
109 *
110 */
111
135
140
141/*
142 * The following two functions isolate all the C-style resource ownership logic.
143 */
144
145// Either acquires all the necessary pointers to resources, or acquires nothing and throws CtorException.
147{
148 p_font = p_font_;
149 descr = descr_;
150 hb_font_copy = nullptr;
151 face = nullptr;
152 hb_face = nullptr;
153
154 hb_font = pango_font_get_hb_font(p_font); // Pango owns hb_font.
155 if (!hb_font) {
156 release();
157 throw CtorException("Failed to get harfbuzz font");
158 }
159 hb_face = hb_font_get_face(hb_font);
160
161 // hb_font is immutable, yet we need to act on it (with set_funcs) to extract the freetype face
162 hb_font_copy = hb_font_create_sub_font(hb_font);
163 hb_ft_font_set_funcs(hb_font_copy);
164 face = hb_ft_font_lock_face(hb_font_copy);
165
166 if (!face) {
167 release();
168 throw CtorException("Failed to get freetype face");
169 }
170}
171
172// Release the resources acquired by acquire().
174{
175 if (hb_font_copy) {
176 if (face) {
177 hb_ft_font_unlock_face(hb_font_copy);
178 }
179 hb_font_destroy(hb_font_copy);
180 }
181
182 pango_font_description_free(descr);
183 g_object_unref(p_font);
184}
185
187{
188 auto hb_font = pango_font_get_hb_font(p_font); // Pango owns hb_font.
189 assert(hb_font); // Guaranteed since already tested in acquire().
190
191 has_svg = hb_ot_color_has_svg(hb_face); // SVG glyphs Since HB 2.1.0
192
193 FT_Select_Charmap(face, ft_encoding_unicode);
194 FT_Select_Charmap(face, ft_encoding_symbol);
195
196 data = std::make_shared<Data>();
198 readOpenTypeSVGTable(hb_font, data->openTypeSVGGlyphs, data->openTypeSVGData);
199 readOpenTypeFvarAxes(face, data->openTypeVarAxes);
200
201#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 8 // 2.8 does not seem to work even though it has some support.
202
203 // 'font-variation-settings' support.
204 // The font returned from pango_fc_font_lock_face does not include variation settings. We must set them.
205
206 // We need to:
207 // Extract axes with values from Pango font description.
208 // Replace default axis values with extracted values.
209
210 if (auto var = pango_font_description_get_variations(descr)) {
211 Glib::ustring variations = var;
212
213 FT_MM_Var *mmvar = nullptr;
214 FT_Multi_Master mmtype;
215 if (FT_HAS_MULTIPLE_MASTERS(face) && // Font has variables
216 FT_Get_MM_Var(face, &mmvar) == 0 && // We found the data
217 FT_Get_Multi_Master(face, &mmtype) != 0) { // It's not an Adobe MM font
218
219 // std::cout << " Multiple Masters: variables: " << mmvar->num_axis
220 // << " named styles: " << mmvar->num_namedstyles << std::endl;
221
222 // Get the required values from Pango Font Description
223 // Need to check format of values from Pango, for the moment accept any format.
224 auto regex = Glib::Regex::create("(\\w{4})=([-+]?\\d*\\.?\\d+([eE][-+]?\\d+)?)");
225 Glib::MatchInfo matchInfo;
226
227 FT_UInt num_axis = data->openTypeVarAxes.size();
228 std::vector<FT_Fixed> w(num_axis, 0);
229
230 auto tokens = Glib::Regex::split_simple(",", variations);
231 for (auto const &token : tokens) {
232
233 regex->match(token, matchInfo);
234 if (matchInfo.matches()) {
235
236 float value = std::stod(matchInfo.fetch(2).raw()); // Should clamp value
237
238 // Translate the "named" axes.
239 auto name = matchInfo.fetch(1);
240 if (name == "wdth") name = "Width" ; // 'font-stretch'
241 if (name == "wght") name = "Weight" ; // 'font-weight'
242 if (name == "opsz") name = "OpticalSize"; // 'font-optical-sizing' (indirectly)
243 if (name == "slnt") name = "Slant" ; // 'font-style'
244 if (name == "ital") name = "Italic" ; // 'font-style'
245
246 auto it = data->openTypeVarAxes.find(name);
247 if (it != data->openTypeVarAxes.end()) {
248 it->second.set_val = value;
249 w[it->second.index] = value * 65536;
250 }
251 }
252 }
253
254 // Set design coordinates
255 auto err = FT_Set_Var_Design_Coordinates(face, num_axis, w.data());
256 if (err) {
257 std::cerr << "FontInstance::FontInstance(): Error in call to FT_Set_Var_Design_Coordinates(): " << err << std::endl;
258 }
259
260 // FT_Done_MM_Var(mmlib, mmvar);
261 }
262 }
263
264#endif // FreeType
265}
266
267// Internal function to find baselines
269{
270 // CSS2 recommends using the OS/2 values sTypoAscender and sTypoDescender for the Typographic ascender and descender values:
271 // http://www.w3.org/TR/CSS2/visudet.html#sTypoAscender
272 // On Windows, the typographic ascender and descender are taken from the otmMacAscent and
273 // otmMacDescent values:
274 // http://microsoft.public.win32.programmer.gdi.narkive.com/LV6k4BDh/msdn-documentation-outlinetextmetrics-clarification
275 // The otmAscent and otmDescent values are the maximum ascent and maximum descent of all the glyphs in a font.
276 if (face->units_per_EM != 0) { // If zero then it's a bitmap font.
277
278 auto os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
279
280 if (auto post = (TT_Postscript*)FT_Get_Sfnt_Table(face, ft_sfnt_post)) {
281 _italic_angle = FTFixedToDouble(post->italicAngle);
282 _fixed_width = post->isFixedPitch != 0;
283 // fsSelection mask: oblique/italic = 0x201
284 _oblique = post->italicAngle != 0 || (os2 && (os2->fsSelection & 0x201) != 0);
285 }
286
287 if (os2) {
288 _family_class = os2->sFamilyClass;
289 _ascent = std::fabs((double)os2->sTypoAscender / face->units_per_EM);
290 _descent = std::fabs((double)os2->sTypoDescender/ face->units_per_EM);
291 } else {
292 _ascent = std::fabs((double)face->ascender / face->units_per_EM);
293 _descent = std::fabs((double)face->descender / face->units_per_EM);
294 }
295 _ascent_max = std::fabs((double)face->ascender / face->units_per_EM);
296 _descent_max = std::fabs((double)face->descender / face->units_per_EM);
297 _design_units = face->units_per_EM;
298
299 // In CSS em size is ascent + descent... which should be 1. If not, adjust so it is.
300 double em = _ascent + _descent;
301 if (em > 0.0) {
302 _ascent /= em;
303 _descent /= em;
304 }
305
306 // x-height
307 if (os2 && os2->version >= 0x0002 && os2->version != 0xffffu) {
308 // Only os/2 version 2 and above have sxHeight, 0xffff marks "old Mac fonts" without table
309 _xheight = std::fabs((double)os2->sxHeight / face->units_per_EM);
310 } else {
311 // Measure 'x' height in font. Recommended option by XSL standard if no sxHeight.
312 FT_UInt index = FT_Get_Char_Index(face, 'x');
313 if (index != 0) {
314 FT_Load_Glyph(face, index, FT_LOAD_NO_SCALE);
315 _xheight = std::fabs((double)face->glyph->metrics.height / face->units_per_EM);
316 } else {
317 // No 'x' in font!
318 _xheight = 0.5;
319 }
320 }
321
322 // Baselines defined relative to alphabetic.
323 _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent; // Recommendation
324 _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent; // Guess
326 _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent; // Definition
327 _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight; // Definition
330
331 // Better math baseline:
332 // Try center of minus sign
333 FT_UInt index = FT_Get_Char_Index(face, 0x2212); //'−'
334 // If no minus sign, try hyphen
335 if (index == 0) {
336 index = FT_Get_Char_Index(face, '-');
337 }
338
339 if (index != 0) {
340 FT_Load_Glyph(face, index, FT_LOAD_NO_SCALE);
341 FT_Glyph aglyph;
342 FT_Get_Glyph(face->glyph, &aglyph);
343 FT_BBox acbox;
344 FT_Glyph_Get_CBox(aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox);
345 double math = (double)(acbox.yMin + acbox.yMax) / 2.0 / face->units_per_EM;
347 // std::cout << "Math baseline: - bbox: y_min: " << acbox.yMin
348 // << " y_max: " << acbox.yMax
349 // << " math: " << math << std::endl;
350 FT_Done_Glyph(aglyph);
351 }
352
353 // Find hanging baseline... assume it is at top of 'म'.
354 index = FT_Get_Char_Index(face, 0x092E); // 'म'
355 if (index != 0) {
356 FT_Load_Glyph(face, index, FT_LOAD_NO_SCALE);
357 FT_Glyph aglyph;
358 FT_Get_Glyph(face->glyph, &aglyph);
359 FT_BBox acbox;
360 FT_Glyph_Get_CBox(aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox);
361 double hanging = (double)acbox.yMax / face->units_per_EM;
363 // std::cout << "Hanging baseline: प: " << hanging << std::endl;
364 FT_Done_Glyph(aglyph);
365 }
366 }
367
368 // const gchar *family = pango_font_description_get_family(descr);
369 // std::cout << "Font: " << (family?family:"null") << std::endl;
370 // std::cout << " ascent: " << _ascent << std::endl;
371 // std::cout << " descent: " << _descent << std::endl;
372 // std::cout << " x-height: " << _xheight << std::endl;
373 // std::cout << " max ascent: " << _ascent_max << std::endl;
374 // std::cout << " max descent: " << _descent_max << std::endl;
375 // std::cout << " Baselines:" << std::endl;
376 // std::cout << " alphabetic: " << _baselines[ SP_CSS_BASELINE_ALPHABETIC ] << std::endl;
377 // std::cout << " ideographic: " << _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] << std::endl;
378 // std::cout << " hanging: " << _baselines[ SP_CSS_BASELINE_HANGING ] << std::endl;
379 // std::cout << " math: " << _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] << std::endl;
380 // std::cout << " central: " << _baselines[ SP_CSS_BASELINE_CENTRAL ] << std::endl;
381 // std::cout << " middle: " << _baselines[ SP_CSS_BASELINE_MIDDLE ] << std::endl;
382 // std::cout << " text_before: " << _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] << std::endl;
383 // std::cout << " text_after: " << _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] << std::endl;
384}
385
387{
388 unsigned int res = 0;
389 if (c > 0x10ffff) {
390 // >= 0xf0000 is out of range for assigned codepoints, above is for private use.
391 std::cerr << "FontInstance::MapUnicodeChar: Unicode codepoint out of range: "
392 << std::hex << c << std::dec
393 << std::endl;
394 } else {
395 res = FT_Get_Char_Index(face, c);
396 }
397 return res;
398}
399
400FontGlyph const *FontInstance::LoadGlyph(unsigned int glyph_id)
401{
402 if (!FT_IS_SCALABLE(face)) {
403 return nullptr; // bitmap font
404 }
405
406 if (auto it = data->glyphs.find(glyph_id); it != data->glyphs.end()) {
407 return it->second.get(); // already loaded
408 }
409
410 Geom::PathBuilder path_builder;
411
412 // Note: Bitmap only fonts (i.e. some color fonts) ignore FT_LOAD_NO_BITMAP.
413 if (FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) {
414 return nullptr; // error
415 }
416
417 // Find scale, used by both metrics and paths.
418 int x_scale = 0;
419 int y_scale = 0;
420 hb_font_get_scale(hb_font, &x_scale, &y_scale);
421 if (x_scale != y_scale) {
422 std::cerr << "FontInstance::LoadGlyph: x scale not equal to y scale!" << std::endl;
423 }
424
425 auto n_g = std::make_unique<FontGlyph>();
426
427 // Find metrics ----------------------------------
428
429 // Use harfbuzz as freetype doesn't return proper values for bitmap fonts.
430
431 n_g->h_advance = hb_font_get_glyph_h_advance (hb_font, glyph_id) / (double)x_scale; // Since HB 0.9.2
432 if (openTypeTableList.contains("vmtx")) {
433 n_g->v_advance = -hb_font_get_glyph_v_advance (hb_font, glyph_id) / (double)y_scale;
434 } else {
435 // Don't use harfbuzz synthisized vertical metrics, it's wrong (includes line gap?)!
436 // CSS3 Writing modes dictates that if vertical font metrics are missing we must
437 // synthisize them. No method is specified. The SVG 1.1 spec suggests using the em
438 // height (which is not theFace->height as that includes leading). The em height
439 // is ascender + descender (descender positive). Note: The "Requirements for
440 // Japanese Text Layout" W3C document says that Japanese kanji should be "set
441 // solid" which implies that vertical (and horizontal) advance should be 1em.
442 n_g->v_advance = 1.0;
443 }
444
445 hb_glyph_extents_t extents;
446 bool success = hb_font_get_glyph_extents(hb_font, glyph_id, &extents); // Since HB 0.9.2
447 if (success) {
448 n_g->bbox_exact = Geom::Rect(extents.x_bearing / (double)x_scale,
449 extents.y_bearing / (double)y_scale,
450 (extents.x_bearing + extents.width) / (double)x_scale,
451 (extents.y_bearing + extents.height) / (double)y_scale);
452 } else {
453 std::cerr << "FontInstance::LoadGlyph: Failed to get glyph extents for glyph: glyph_id!"
454 << " (" << pango_font_description_to_string(descr) << ")"
455 << std::endl;
456 }
457
458 // Nominal design space of glyph.
459 n_g->bbox_pick.setRight(n_g->h_advance);
460 n_g->bbox_pick.setBottom( _ascent_max);
461 n_g->bbox_pick.setTop( -_descent_max);
462
463 // Any place that might be inked, including any text decoration.
464 n_g->bbox_draw = n_g->bbox_pick; // Design space for glyph
465 n_g->bbox_draw.setBottom( _ascent_max * 1.1); // Expand to allow for text decoration
466 n_g->bbox_draw.setTop( -_descent_max * 1.1);
467 n_g->bbox_draw.unionWith(n_g->bbox_exact); // Extend if glyph leaks outside of design space.
468
469 // Find path vector ------------------------------
470
471 if (face->glyph->format == ft_glyph_format_outline) {
472 FT_Outline_Funcs ft2_outline_funcs = {
477 0, 0
478 };
479 FT2GeomData user(path_builder, 1.0 / face->units_per_EM);
480 FT_Outline_Decompose(&face->glyph->outline, &ft2_outline_funcs, &user);
481 }
482
483 path_builder.flush();
484
485 Geom::PathVector pv = path_builder.peek();
486
487 // close all paths
488 for (auto &i : pv) {
489 i.close();
490 }
491
492 if (!pv.empty()) {
493 n_g->pathvector = std::move(pv);
494 }
495
496 auto ret = data->glyphs.emplace(glyph_id, std::move(n_g));
497
498 return ret.first->second.get();
499}
500
504Glib::ustring FontInstance::GetFilename() const
505{
506 if (p_font) {
507 if (PangoFcFont *fc_font = PANGO_FC_FONT(p_font)) {
508 char *fn;
509 if (FcPatternGetString(fc_font->font_pattern, FC_FILE, 0, (FcChar8 **)&fn)== FcResultMatch) {
510 Glib::ustring out(fn);
511#ifdef _WIN32
512 // Filenames from fontconfig something have backslashes on windows instead of forward slashes.
513 for (auto ind = out.find('/'); ind != Glib::ustring::npos; ind = out.find('/')) {
514 out.replace(ind, 1, "\\");
515 }
516#endif
517 return out;
518 }
519 }
520 }
521 return "";
522}
523
524bool FontInstance::FontMetrics(double &ascent, double &descent, double &xheight) const
525{
526 ascent = _ascent;
527 descent = _descent;
528 xheight = _xheight;
529
530 return true;
531}
532
533bool FontInstance::FontDecoration(double &underline_position, double &underline_thickness, double &linethrough_position, double &linethrough_thickness) const
534{
535 if (face->units_per_EM == 0) {
536 return false; // bitmap font
537 }
538 underline_position = std::fabs((double)face->underline_position / face->units_per_EM);
539 underline_thickness = std::fabs((double)face->underline_thickness / face->units_per_EM);
540 // there is no specific linethrough information, mock it up from other font fields
541 linethrough_position = std::fabs((double)face->ascender / 3.0 / face->units_per_EM);
542 linethrough_thickness = std::fabs((double)face->underline_thickness / face->units_per_EM);
543 return true;
544}
545
546bool FontInstance::FontSlope(double &run, double &rise) const
547{
548 run = 0.0;
549 rise = 1.0;
550
551 if (!FT_IS_SCALABLE(face)) {
552 return false; // bitmap font
553 }
554
555 auto hhea = reinterpret_cast<TT_HoriHeader*>(FT_Get_Sfnt_Table(face, ft_sfnt_hhea));
556 if (!hhea) {
557 return false;
558 }
559 run = hhea->caret_Slope_Run;
560 rise = hhea->caret_Slope_Rise;
561
562 return true;
563}
564
566{
567 auto g = LoadGlyph(glyph_id);
568 if (!g) {
569 return {};
570 }
571
572 return g->bbox_exact;
573}
574
575Geom::Rect FontInstance::BBoxPick(unsigned int glyph_id)
576{
577 auto g = LoadGlyph(glyph_id);
578 if (!g) {
579 return {0, 0, 1, 1}; // em box
580 }
581
582 return g->bbox_pick;
583}
584
585Geom::Rect FontInstance::BBoxDraw(unsigned int glyph_id)
586{
587 auto g = LoadGlyph(glyph_id);
588 if (!g) {
589 return {};
590 }
591
592 return g->bbox_draw;
593}
594
595Geom::PathVector const *FontInstance::PathVector(unsigned int glyph_id)
596{
597 auto g = LoadGlyph(glyph_id);
598 if (!g) {
599 return nullptr;
600 }
601
602 return &g->pathvector;
603}
604
605Inkscape::Pixbuf const *FontInstance::PixBuf(unsigned int glyph_id)
606{
607 auto glyph_iter = data->openTypeSVGGlyphs.find(glyph_id);
608 if (glyph_iter == data->openTypeSVGGlyphs.end()) {
609 return nullptr; // out of range
610 }
611
612 // Glyphs are layed out in the +x, -y quadrant (assuming viewBox origin is 0,0).
613 // We need to shift the viewBox by the height inorder to generate pixbuf!
614 // To do: glyphs must draw overflow so we actually need larger pixbuf!
615 // To do: Error handling.
616
617 if (glyph_iter->second.pixbuf) {
618 return glyph_iter->second.pixbuf.get(); // already loaded
619 }
620
621 Glib::ustring svg = data->openTypeSVGData[glyph_iter->second.entry_index];
622
623 // Create new viewbox which determines pixbuf size.
624 Glib::ustring viewbox("viewBox=\"0 ");
625 viewbox += std::to_string(-_design_units);
626 viewbox += " ";
627 viewbox += std::to_string(_design_units*2); // Noto emoji leaks outside of em-box.
628 viewbox += " ";
629 viewbox += std::to_string(_design_units*2);
630 viewbox += "\"";
631
632 // Search for existing viewbox
633 static auto regex = Glib::Regex::create("viewBox=\"\\s*(\\d*\\.?\\d+)\\s*,?\\s*(\\d*\\.?\\d+)\\s*,?\\s*(\\d+\\.?\\d+)\\s*,?\\s*(\\d+\\.?\\d+)\\s*\"", Glib::Regex::CompileFlags::OPTIMIZE);
634 Glib::MatchInfo matchInfo;
635 regex->match(svg, matchInfo);
636
637 if (matchInfo.matches()) {
638 // We have viewBox! We must transform so viewBox corresponds to design units.
639
640 // Replace viewbox
641 svg = regex->replace_literal(svg, 0, viewbox, static_cast<Glib::Regex::MatchFlags>(0));
642
643 // Insert group with required transform to map glyph to new viewbox.
644 double x = std::stod(matchInfo.fetch(1).raw());
645 double y = std::stod(matchInfo.fetch(2).raw());
646 double w = std::stod(matchInfo.fetch(3).raw());
647 double h = std::stod(matchInfo.fetch(4).raw());
648 // std::cout << " x: " << x
649 // << " y: " << y
650 // << " w: " << w
651 // << " h: " << h << std::endl;
652
653 if (w <= 0.0 || h <= 0.0) {
654 std::cerr << "FontInstance::PixBuf: Invalid glyph width or height!" << std::endl;
655 } else {
656
657 double xscale = _design_units/w;
658 double yscale = _design_units/h;
659 double xtrans = _design_units/w * x;
660 double ytrans = _design_units/h * y;
661
662 if (xscale != 1.0 || yscale != 1.0) {
663 Glib::ustring group = "<g transform=\"matrix(";
664 group += std::to_string(xscale);
665 group += ", 0, 0, ";
666 group += std::to_string(yscale);
667 group += std::to_string(-xtrans);
668 group += ", ";
669 group += std::to_string(-ytrans);
670 group += ")\">";
671
672 // Insert start group tag after initial <svg>
673 Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("<\\s*svg.*?>");
674 regex->match(svg, matchInfo);
675 if (matchInfo.matches()) {
676 int start = -1;
677 int end = -1;
678 matchInfo.fetch_pos(0, start, end);
679 svg.insert(end, group);
680 } else {
681 std::cerr << "FontInstance::PixBuf: Could not find <svg> tag!" << std::endl;
682 }
683
684 // Insert end group tag before final </svg> (To do: make sure it is final </svg>)
685 regex = Glib::Regex::create("<\\s*\\/\\s*svg.*?>");
686 regex->match(svg, matchInfo);
687 if (matchInfo.matches()) {
688 int start = -1;
689 int end = -1;
690 matchInfo.fetch_pos(0, start, end);
691 svg.insert(start, "</g>");
692 } else {
693 std::cerr << "FontInstance::PixBuf: Could not find </svg> tag!" << std::endl;
694 }
695 }
696 }
697
698 } else {
699 // No viewBox! We insert one. (To do: Look at 'width' and 'height' to see if we must scale.)
700 Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("<\\s*svg");
701 viewbox.insert(0, "<svg ");
702 svg = regex->replace_literal(svg, 0, viewbox, static_cast<Glib::Regex::MatchFlags>(0));
703 }
704
705 // Make glyph visible.
706 auto pattern = Glib::ustring::compose("(id=\"\\s*glyph%1\\s*\")\\s*visibility=\"hidden\"", glyph_id);
707 auto regex2 = Glib::Regex::create(pattern, Glib::Regex::CompileFlags::OPTIMIZE);
708 svg = regex2->replace(svg, 0, "\\1", static_cast<Glib::Regex::MatchFlags>(0));
709
710 // Finally create pixbuf!
711 auto pixbuf = Inkscape::Pixbuf::create_from_buffer(svg.raw());
712
713 // Ensure exists in cairo format before locking it down. (Rendering code requires cairo format.)
714 pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_CAIRO);
715
716 // And cache it.
717 glyph_iter->second.pixbuf.reset(pixbuf);
718
719 return pixbuf;
720}
721
722double FontInstance::Advance(unsigned int glyph_id, bool vertical)
723{
724 auto g = LoadGlyph(glyph_id);
725 if (!g) {
726 return 0;
727 }
728
729 return vertical ? g->v_advance : g->h_advance;
730}
731
732std::map<Glib::ustring, OTSubstitution> const &FontInstance::get_opentype_tables()
733{
734 if (!data->openTypeTables) {
735 auto hb_font = pango_font_get_hb_font(p_font);
736 assert(hb_font);
737
738 data->openTypeTables.emplace();
739 readOpenTypeGsubTable(hb_font, *data->openTypeTables);
740 }
741
742 return *data->openTypeTables;
743}
744
745/*
746 Local Variables:
747 mode:c++
748 c-file-style:"stroustrup"
749 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
750 indent-tabs-mode:nil
751 fill-column:99
752 End:
753 */
754// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
_PangoFontDescription PangoFontDescription
Definition Layout-TNG.h:44
void readOpenTypeTableList(hb_font_t *hb_font, std::unordered_set< std::string > &list)
void readOpenTypeGsubTable(hb_font_t *hb_font, std::map< Glib::ustring, OTSubstitution > &tables)
void readOpenTypeFvarAxes(const FT_Face ft_face, std::map< Glib::ustring, OTVarAxis > &axes)
void readOpenTypeSVGTable(hb_font_t *hb_font, std::map< unsigned int, SVGGlyphEntry > &glyphs, std::map< int, std::string > &svgs)
double FTFixedToDouble(FT_Fixed value)
double scale
Definition aa.cpp:228
struct _PangoFont PangoFont
Cairo integration helpers.
Glib::ustring GetFilename() const
Attempt to get the ttf filename for this font instance.
Geom::PathVector const * PathVector(unsigned int glyph_id)
hb_face_t * hb_face
std::map< Glib::ustring, OTSubstitution > const & get_opentype_tables()
double _baselines[SP_CSS_BASELINE_SIZE]
PangoFontDescription * descr
std::shared_ptr< Data > data
Geom::Rect BBoxPick(unsigned int glyph_id)
unsigned short _family_class
double _italic_angle
double _descent_max
Geom::Rect BBoxDraw(unsigned int glyph_id)
bool FontSlope(double &run, double &rise) const
Geom::Rect BBoxExact(unsigned int glyph_id)
FontInstance(PangoFont *p_font, PangoFontDescription *descr)
Constructor; takes ownership of both arguments, which must be non-null. Throws CtorException on failu...
std::unordered_set< std::string > openTypeTableList
unsigned int MapUnicodeChar(gunichar c) const
hb_font_t * hb_font_copy
bool FontDecoration(double &underline_position, double &underline_thickness, double &linethrough_position, double &linethrough_thickness) const
void find_font_metrics()
FontGlyph const * LoadGlyph(unsigned int glyph_id)
Inkscape::Pixbuf const * PixBuf(unsigned int glyph_id)
hb_font_t * hb_font
double Advance(unsigned int glyph_id, bool vertical)
PangoFont * p_font
bool FontMetrics(double &ascent, double &descent, double &leading) const
void acquire(PangoFont *p_font, PangoFontDescription *descr)
Store paths to a PathVector.
Definition path-sink.h:226
PathVector const & peek() const
Retrieve the path.
Definition path-sink.h:236
void flush() override
Flush any internal state of the generator.
Definition path-sink.h:196
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
Class to hold image data for raster images.
Definition cairo-utils.h:31
static Pixbuf * create_from_buffer(std::string const &, double svgddpi=0, std::string const &fn="")
const double w
Definition conic-4.cpp:19
double c[8][4]
Struct describing a single glyph in a font.
static int ft2_cubic_to(FT_Vector const *control1, FT_Vector const *control2, FT_Vector const *to, void *i_user)
static int ft2_move_to(FT_Vector const *to, void *i_user)
static int ft2_conic_to(FT_Vector const *control, FT_Vector const *to, void *i_user)
static int ft2_line_to(FT_Vector const *to, void *i_user)
The data describing a single loaded font.
Geom::Point start
Geom::Point end
callback interface for SVG path data
PathVector - a sequence of subpaths.
Exception thrown if construction fails.
@ SP_CSS_BASELINE_CENTRAL
@ SP_CSS_BASELINE_IDEOGRAPHIC
@ SP_CSS_BASELINE_MIDDLE
@ SP_CSS_BASELINE_AUTO
@ SP_CSS_BASELINE_MATHEMATICAL
@ SP_CSS_BASELINE_HANGING
@ SP_CSS_BASELINE_ALPHABETIC
@ SP_CSS_BASELINE_TEXT_BEFORE_EDGE
@ SP_CSS_BASELINE_TEXT_AFTER_EDGE
int index
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder
unsigned int gunichar