/*
7 * bulia byak <buliabyak@users.sf.net>
9 * Copyright (C) 2018 Authors
10 * Released under GNU GPL v2+, read the file
'COPYING' for more information.
20#ifndef PANGO_ENABLE_ENGINE
21#define PANGO_ENABLE_ENGINE
27#include FT_TRUETYPE_TAGS_H
28#include FT_TRUETYPE_TABLES_H
30#include FT_MULTIPLE_MASTERS_H
32#include <pango/pangoft2.h>
33#include <harfbuzz/hb.h>
34#include <harfbuzz/hb-ft.h>
35#include <harfbuzz/hb-ot.h>
37#include <glibmm/regex.h>
67 FT2GeomData *user = (FT2GeomData*)i_user;
70 user->builder.moveTo(p * user->scale);
77 FT2GeomData *user = (FT2GeomData*)i_user;
80 user->builder.lineTo(p * user->scale);
85static int ft2_conic_to(FT_Vector
const *control, FT_Vector
const *to,
void *i_user)
87 FT2GeomData *user = (FT2GeomData*)i_user;
89 user->builder.quadTo(
c * user->scale, p * user->scale);
95static int ft2_cubic_to(FT_Vector
const *control1, FT_Vector
const *control2, FT_Vector
const *to,
void *i_user)
97 FT2GeomData *user = (FT2GeomData*)i_user;
103 user->builder.curveTo(c1 * user->scale, c2 * user->scale, p * user->scale);
182 pango_font_description_free(
descr);
193 FT_Select_Charmap(
face, ft_encoding_unicode);
194 FT_Select_Charmap(
face, ft_encoding_symbol);
196 data = std::make_shared<Data>();
201#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 8
210 if (
auto var = pango_font_description_get_variations(
descr)) {
211 Glib::ustring variations = var;
213 FT_MM_Var *mmvar =
nullptr;
214 FT_Multi_Master mmtype;
215 if (FT_HAS_MULTIPLE_MASTERS(
face) &&
216 FT_Get_MM_Var(
face, &mmvar) == 0 &&
217 FT_Get_Multi_Master(
face, &mmtype) != 0) {
224 auto regex = Glib::Regex::create(
"(\\w{4})=([-+]?\\d*\\.?\\d+([eE][-+]?\\d+)?)");
225 Glib::MatchInfo matchInfo;
227 FT_UInt num_axis =
data->openTypeVarAxes.size();
228 std::vector<FT_Fixed>
w(num_axis, 0);
230 auto tokens = Glib::Regex::split_simple(
",", variations);
231 for (
auto const &token : tokens) {
233 regex->match(token, matchInfo);
234 if (matchInfo.matches()) {
236 float value = std::stod(matchInfo.fetch(2).raw());
239 auto name = matchInfo.fetch(1);
240 if (
name ==
"wdth")
name =
"Width" ;
241 if (
name ==
"wght")
name =
"Weight" ;
242 if (
name ==
"opsz")
name =
"OpticalSize";
243 if (
name ==
"slnt")
name =
"Slant" ;
244 if (
name ==
"ital")
name =
"Italic" ;
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;
255 auto err = FT_Set_Var_Design_Coordinates(
face, num_axis,
w.data());
257 std::cerr <<
"FontInstance::FontInstance(): Error in call to FT_Set_Var_Design_Coordinates(): " << err << std::endl;
276 if (
face->units_per_EM != 0) {
278 auto os2 = (TT_OS2*)FT_Get_Sfnt_Table(
face, ft_sfnt_os2);
280 if (
auto post = (TT_Postscript*)FT_Get_Sfnt_Table(
face, ft_sfnt_post)) {
284 _oblique = post->italicAngle != 0 || (os2 && (os2->fsSelection & 0x201) != 0);
289 _ascent = std::fabs((
double)os2->sTypoAscender /
face->units_per_EM);
290 _descent = std::fabs((
double)os2->sTypoDescender/
face->units_per_EM);
307 if (os2 && os2->version >= 0x0002 && os2->version != 0xffffu) {
309 _xheight = std::fabs((
double)os2->sxHeight /
face->units_per_EM);
312 FT_UInt
index = FT_Get_Char_Index(
face,
'x');
314 FT_Load_Glyph(
face,
index, FT_LOAD_NO_SCALE);
315 _xheight = std::fabs((
double)
face->glyph->metrics.height /
face->units_per_EM);
333 FT_UInt
index = FT_Get_Char_Index(
face, 0x2212);
340 FT_Load_Glyph(
face,
index, FT_LOAD_NO_SCALE);
342 FT_Get_Glyph(
face->glyph, &aglyph);
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;
350 FT_Done_Glyph(aglyph);
356 FT_Load_Glyph(
face,
index, FT_LOAD_NO_SCALE);
358 FT_Get_Glyph(
face->glyph, &aglyph);
360 FT_Glyph_Get_CBox(aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox);
361 double hanging = (double)acbox.yMax /
face->units_per_EM;
364 FT_Done_Glyph(aglyph);
388 unsigned int res = 0;
391 std::cerr <<
"FontInstance::MapUnicodeChar: Unicode codepoint out of range: "
392 << std::hex <<
c << std::dec
395 res = FT_Get_Char_Index(
face,
c);
402 if (!FT_IS_SCALABLE(
face)) {
406 if (
auto it =
data->glyphs.find(glyph_id); it !=
data->glyphs.end()) {
407 return it->second.get();
413 if (FT_Load_Glyph(
face, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) {
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;
425 auto n_g = std::make_unique<FontGlyph>();
431 n_g->h_advance = hb_font_get_glyph_h_advance (
hb_font, glyph_id) / (double)x_scale;
433 n_g->v_advance = -hb_font_get_glyph_v_advance (
hb_font, glyph_id) / (double)y_scale;
442 n_g->v_advance = 1.0;
445 hb_glyph_extents_t extents;
446 bool success = hb_font_get_glyph_extents(
hb_font, glyph_id, &extents);
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);
453 std::cerr <<
"FontInstance::LoadGlyph: Failed to get glyph extents for glyph: glyph_id!"
454 <<
" (" << pango_font_description_to_string(
descr) <<
")"
459 n_g->bbox_pick.setRight(n_g->h_advance);
464 n_g->bbox_draw = n_g->bbox_pick;
467 n_g->bbox_draw.unionWith(n_g->bbox_exact);
471 if (
face->glyph->format == ft_glyph_format_outline) {
472 FT_Outline_Funcs ft2_outline_funcs = {
479 FT2GeomData user(path_builder, 1.0 /
face->units_per_EM);
480 FT_Outline_Decompose(&
face->glyph->outline, &ft2_outline_funcs, &user);
483 path_builder.
flush();
493 n_g->pathvector = std::move(pv);
496 auto ret =
data->glyphs.emplace(glyph_id, std::move(n_g));
498 return ret.first->second.get();
507 if (PangoFcFont *fc_font = PANGO_FC_FONT(
p_font)) {
509 if (FcPatternGetString(fc_font->font_pattern, FC_FILE, 0, (FcChar8 **)&fn)== FcResultMatch) {
510 Glib::ustring out(fn);
513 for (
auto ind = out.find(
'/'); ind != Glib::ustring::npos; ind = out.find(
'/')) {
514 out.replace(ind, 1,
"\\");
533bool FontInstance::FontDecoration(
double &underline_position,
double &underline_thickness,
double &linethrough_position,
double &linethrough_thickness)
const
535 if (
face->units_per_EM == 0) {
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);
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);
551 if (!FT_IS_SCALABLE(
face)) {
555 auto hhea =
reinterpret_cast<TT_HoriHeader*
>(FT_Get_Sfnt_Table(
face, ft_sfnt_hhea));
559 run = hhea->caret_Slope_Run;
560 rise = hhea->caret_Slope_Rise;
572 return g->bbox_exact;
602 return &g->pathvector;
607 auto glyph_iter =
data->openTypeSVGGlyphs.find(glyph_id);
608 if (glyph_iter ==
data->openTypeSVGGlyphs.end()) {
617 if (glyph_iter->second.pixbuf) {
618 return glyph_iter->second.pixbuf.get();
621 Glib::ustring svg =
data->openTypeSVGData[glyph_iter->second.entry_index];
624 Glib::ustring viewbox(
"viewBox=\"0 ");
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);
637 if (matchInfo.matches()) {
641 svg = regex->replace_literal(svg, 0, viewbox,
static_cast<Glib::Regex::MatchFlags
>(0));
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());
653 if (
w <= 0.0 || h <= 0.0) {
654 std::cerr <<
"FontInstance::PixBuf: Invalid glyph width or height!" << std::endl;
662 if (xscale != 1.0 || yscale != 1.0) {
663 Glib::ustring group =
"<g transform=\"matrix(";
664 group += std::to_string(xscale);
666 group += std::to_string(yscale);
667 group += std::to_string(-xtrans);
669 group += std::to_string(-ytrans);
673 Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create(
"<\\s*svg.*?>");
674 regex->match(svg, matchInfo);
675 if (matchInfo.matches()) {
679 svg.insert(
end, group);
681 std::cerr <<
"FontInstance::PixBuf: Could not find <svg> tag!" << std::endl;
685 regex = Glib::Regex::create(
"<\\s*\\/\\s*svg.*?>");
686 regex->match(svg, matchInfo);
687 if (matchInfo.matches()) {
691 svg.insert(
start,
"</g>");
693 std::cerr <<
"FontInstance::PixBuf: Could not find </svg> tag!" << std::endl;
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));
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));
717 glyph_iter->second.pixbuf.reset(pixbuf);
729 return vertical ? g->v_advance : g->h_advance;
734 if (!
data->openTypeTables) {
738 data->openTypeTables.emplace();
742 return *
data->openTypeTables;
_PangoFontDescription PangoFontDescription
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)
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)
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
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
bool FontDecoration(double &underline_position, double &underline_thickness, double &linethrough_position, double &linethrough_thickness) const
FontGlyph const * LoadGlyph(unsigned int glyph_id)
Inkscape::Pixbuf const * PixBuf(unsigned int glyph_id)
double Advance(unsigned int glyph_id, bool vertical)
bool FontMetrics(double &ascent, double &descent, double &leading) const
void acquire(PangoFont *p_font, PangoFontDescription *descr)
Store paths to a PathVector.
PathVector const & peek() const
Retrieve the path.
void flush() override
Flush any internal state of the generator.
bool empty() const
Check whether the vector contains any paths.
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
Class to hold image data for raster images.
static Pixbuf * create_from_buffer(std::string const &, double svgddpi=0, std::string const &fn="")
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.
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_MATHEMATICAL
@ SP_CSS_BASELINE_HANGING
@ SP_CSS_BASELINE_ALPHABETIC
@ SP_CSS_BASELINE_TEXT_BEFORE_EDGE
@ SP_CSS_BASELINE_TEXT_AFTER_EDGE
Glib::RefPtr< Gtk::Builder > builder