#include <iostream>
#include <fontconfig/fontconfig.h>
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_IDS_H
#include FT_SFNT_NAMES_H
#include <glibmm.h>
#include <pango/pango.h>
#include <pango/pangoft2.h>
#include <pango/pangofc-fontmap.h>
#include <pangomm.h>
#include "layout.h"
// fake pango renderer - begin
G_BEGIN_DECLS
#define LAB_TYPE_FAKE_PANGO_RENDERER (lab_fake_pango_renderer_get_type ())
typedef struct _LabFakePangoRenderer LabFakePangoRenderer;
typedef struct _LabFakePangoRendererClass LabFakePangoRendererClass;
#define LAB_FAKE_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LAB_TYPE_FAKE_PANGO_RENDERER, LabFakePangoRendererClass))
#define LAB_FAKE_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LAB_TYPE_FAKE_PANGO_RENDERER))
#define LAB_FAKE_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LAB_TYPE_FAKE_PANGO_RENDERER, LabFakePangoRendererClass))
PangoRenderer* lab_fake_pango_renderer_new(Layout::GlyphList *list);
G_END_DECLS
struct _LabFakePangoRenderer {
PangoRenderer parent_instance;
Layout::Line *line;
};
struct _LabFakePangoRendererClass {
PangoRendererClass parent_class;
};
G_DEFINE_TYPE(LabFakePangoRenderer, lab_fake_pango_renderer, PANGO_TYPE_RENDERER)
static void
lab_fake_pango_renderer_init(LabFakePangoRenderer *renderer)
{ renderer->line = nullptr; }
static void
lab_fake_pango_renderer_draw_glyph(
PangoRenderer *renderer,
PangoFont *font,
PangoGlyph glyph,
double x,
double y );
static void
lab_fake_pango_renderer_class_init(LabFakePangoRendererClass *klass) {
PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS(klass);
renderer_class->draw_glyph = lab_fake_pango_renderer_draw_glyph;
}
LabFakePangoRenderer*
lab_fake_pango_renderer_new()
{ return (LabFakePangoRenderer*)g_object_new(LAB_TYPE_FAKE_PANGO_RENDERER, nullptr); }
// end - fake pango renderer
static void
lab_fake_pango_renderer_draw_glyph(
PangoRenderer *renderer,
PangoFont *font,
PangoGlyph glyph,
double x,
double y )
{
Layout::Line *line = ((LabFakePangoRenderer*)renderer)->line;
assert(line);
if (!line)
return;
const Real k = 1/64.0;
assert(PANGO_IS_FC_FONT(font));
FT_Face face = pango_fc_font_lock_face((PangoFcFont*)font);
FT_Set_Transform(face, nullptr, nullptr);
if (!FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING)) {
const FT_Glyph_Metrics &metrics = face->glyph->metrics;
IntVector2 origin(metrics.vertBearingX - metrics.horiBearingX, 0);
IntPair2 bounds(
IntVector2( metrics.horiBearingX,
-metrics.horiBearingY ) + origin,
IntVector2( metrics.horiBearingX + metrics.width,
-metrics.horiBearingY + metrics.height ) + origin );
Layout::RunList &runs = line->runs;
if (runs.empty() || !runs.back().font || runs.back().font->gobj() != font) {
runs.push_back(Layout::Run());
runs.back().font = Glib::wrap(font, true);
}
Layout::Run &run = runs.back();
run.glyphs.push_back(Layout::Glyph());
Layout::Glyph &g = run.glyphs.back();
g.glyph_index = glyph;
g.origin = Vector2(origin.x, origin.y)*k;
g.position = Vector2(x, y) - g.origin;
g.bounds = Pair2( Vector2(bounds.p0.x, bounds.p0.y)*k,
Vector2(bounds.p1.x, bounds.p1.y)*k );
}
pango_fc_font_unlock_face((PangoFcFont*)font);
}
static void
lab_pango_fc_pattern_set_substitute(FcPattern *pattern, gpointer data) {
const int &args = *(const int*)data;
FcPatternDel(pattern, FC_HINTING);
FcPatternDel(pattern, FC_ANTIALIAS);
FcPatternAddBool(pattern, FC_HINTING, (args & 1) ? FcTrue : FcFalse);
FcPatternAddBool(pattern, FC_ANTIALIAS, (args & 2) ? FcTrue : FcFalse);
}
static GQuark
layout_data_quark() {
static GQuark quark = g_quark_from_static_string("lab_layout_data");
return quark;
}
Glib::RefPtr<Pango::FontMap>
Layout::create_fontmap(bool hinting, bool antialiasing) {
static int args[4] { 0, 1, 2, 3 }; // to avoid encoding of int into pointer
int* data = args + (hinting ? 1 : 0) + (antialiasing ? 2 : 0);
PangoFontMap *font_map = pango_ft2_font_map_new();
pango_ft2_font_map_set_default_substitute(
PANGO_FT2_FONT_MAP(font_map),
lab_pango_fc_pattern_set_substitute,
data,
nullptr );
g_object_set_qdata((GObject*)font_map, layout_data_quark(), data);
return Glib::wrap(font_map);
}
Glib::RefPtr<Pango::Font>
Layout::load_font_file(
const Glib::RefPtr<Pango::Context> &context,
const std::string &filename,
const Pango::FontDescription &font_desc )
{
const std::string fullpath = Glib::path_is_absolute(filename)
? filename : Glib::get_current_dir() + "/" + filename;
Glib::RefPtr<Pango::Font> font;
bool desc_ready = false;
Pango::FontDescription desc = font_desc;
FT_Library library;
if (!FT_Init_FreeType(&library)) {
FT_Face face;
if (!FT_New_Face(library, fullpath.c_str(), 0, &face)) {
desc.set_family(face->family_name);
desc.set_style(
face->style_flags & FT_STYLE_FLAG_ITALIC
? Pango::STYLE_ITALIC : Pango::STYLE_NORMAL );
desc.set_weight(
face->style_flags & FT_STYLE_FLAG_BOLD
? Pango::WEIGHT_BOLD : Pango::WEIGHT_NORMAL );
desc_ready = true;
FT_Done_Face(face);
} else {
std::cerr << "Layout::load_font_file: Cannot load font from file: " << fullpath << std::endl;
}
FT_Done_FreeType(library);
} else {
std::cerr << "Layout::load_font_file: Cannot init FreeType library" << std::endl;
}
if (desc_ready) {
PangoFcFontMap *font_map = PANGO_FC_FONT_MAP(pango_context_get_font_map(context->gobj()));
if (font_map) {
FcConfig *config = pango_fc_font_map_get_config(font_map);
if (config || FcConfigAppFontAddFile(config, (const FcChar8*)fullpath.c_str())) {
pango_fc_font_map_config_changed(font_map);
font = context->load_font(desc);
} else {
pango_fc_font_map_config_changed(font_map);
std::cerr << "Layout::load_font_file: Cannot register font file in FontConfig: " << fullpath << std::endl;
}
} else {
std::cerr << "Layout::load_font_file: Wrong context. FreeType/FontConfig context required" << std::endl;
}
}
return font;
}
Layout::Layout(const Glib::RefPtr<Pango::Layout> &layout):
hinting(),
antialiasing()
{
const int *data = (const int*)g_object_get_qdata(
(GObject*)pango_context_get_font_map( pango_layout_get_context(layout->gobj()) ),
layout_data_quark() );
assert(data);
if (!data)
return;
hinting = (*data) & 1;
antialiasing = (*data) & 2;
const Real k = 1/Real(PANGO_SCALE);
PangoRectangle ink_rect, logical_rect;
pango_layout_get_extents(layout->gobj(), &ink_rect, &logical_rect);
bounds = Pair2( Vector2( ink_rect.x, ink_rect.y )*k,
Vector2( ink_rect.x + ink_rect.width,
ink_rect.y + ink_rect.height )*k );
logical_bounds = Pair2( Vector2( logical_rect.x, logical_rect.y )*k,
Vector2( logical_rect.x + logical_rect.width,
logical_rect.y + logical_rect.height )*k );
LabFakePangoRenderer *renderer = lab_fake_pango_renderer_new();
pango_renderer_activate((PangoRenderer*)renderer);
PangoLayoutIter *iter = pango_layout_get_iter(layout->gobj());
do {
lines.push_back(Line());
Line &line = lines.back();
pango_layout_iter_get_line_extents(iter, &ink_rect, &logical_rect);
int baseline = pango_layout_iter_get_baseline(iter);
line.position = Vector2(logical_rect.x, baseline)*k;
line.bounds = Pair2(
Vector2( ink_rect.x - logical_rect.x,
ink_rect.y - baseline)*k,
Vector2( ink_rect.x - logical_rect.x + ink_rect.width,
ink_rect.y - baseline + ink_rect.height)*k );
line.logical_bounds = Pair2(
Vector2( 0, logical_rect.y )*k,
Vector2( logical_rect.width,
logical_rect.y - baseline + logical_rect.height)*k );
renderer->line = &line;
PangoLayoutLine *l = pango_layout_iter_get_line_readonly(iter);
pango_renderer_draw_layout_line((PangoRenderer*)renderer, l, 0, 0);
} while(pango_layout_iter_next_line(iter));
pango_layout_iter_free(iter);
pango_renderer_deactivate((PangoRenderer*)renderer);
g_object_unref(renderer);
}
TransformedLayout::TransformedLayout(
const RefPtr<Layout> &layout,
const Matrix2 &spacing_matrix,
const Matrix2 &glyph_matrix
):
layout(layout),
spacing_matrix(spacing_matrix),
glyph_matrix(glyph_matrix)
{
if (!layout)
return;
const Pair2 &logical_bounds = layout->get_logical_bounds();
const Layout::LineList &lines = layout->get_lines();
line_bounds.reserve(lines.size());
for(Layout::LineList::const_iterator li = lines.begin(); li != lines.end(); ++li) {
Pair2 lb;
for(Layout::RunList::const_iterator ri = li->runs.begin(); ri != li->runs.end(); ++ri) {
for(Layout::GlyphList::const_iterator gi = ri->glyphs.begin(); gi != ri->glyphs.end(); ++gi) {
const Vector2 position = spacing_matrix*gi->position;
Pair2 gb = glyph_matrix.transform_bounds(gi->bounds);
gb.p0 += position;
gb.p1 += position;
lb |= gb;
}
}
line_bounds.push_back(lb);
const Vector2 p0 = spacing_matrix*li->position;
const Vector2 p1 = spacing_matrix*Vector2(0, li->position.y);
const Vector2 p2 = spacing_matrix*Vector2(logical_bounds.p1.x - li->logical_bounds.p1.x, li->position.y);
bounds |= Pair2(lb.p0 + p0, lb.p1 + p0);
bounds |= Pair2(lb.p0 + p1, lb.p1 + p1);
bounds |= Pair2(lb.p0 + p2, lb.p1 + p2);
}
}