Skip to content

Instantly share code, notes, and snippets.

@michaeljclark
Created December 21, 2021 22:59
Show Gist options
  • Save michaeljclark/22ddfb74c09e2aabd30994a991325fc9 to your computer and use it in GitHub Desktop.
Save michaeljclark/22ddfb74c09e2aabd30994a991325fc9 to your computer and use it in GitHub Desktop.
loads TrueType Bézier curves for codepoint using FreeType
/*
* ftload.cc loads TrueType Bézier curves for codepoint using FreeType
*
* Compile: c++ -std=c++17 -o ftload ftload.cc -Ithird_party/glm \
* $(pkg-config --cflags --libs freetype2)
*
* Usage: ./ftload <ttf-file> <dpi> <size> <code-point>
*
* Example: ./ftload DejaVuSans.ttf 72 32 99
*/
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <glm/glm.hpp>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_MODULE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
using vec2 = glm::vec2;
template <typename T> using vector = std::vector<T>;
typedef const FT_Vector ft_vec;
#define Panic(...) do { fprintf(stderr, __VA_ARGS__); exit(9); } while(0);
enum EdgeType { Linear = 2, Quadratic = 3, Cubic = 4, };
struct Edge {
int type;
vec2 p[4];
};
struct Contour {
int edge_offset, edge_count;
};
struct Shape {
int contour_offset, contour_count, edge_offset, edge_count;
vec2 offset, size;
};
struct Context {
vector<Shape> shapes;
vector<Contour> contours;
vector<Edge> edges;
vec2 pos;
};
static void new_shape(Context &ctx, vec2 offset, vec2 size) {
ctx.shapes.emplace_back(Shape{(int)ctx.contours.size(), 0,
(int)ctx.edges.size(), 0, offset, size });
}
static void new_contour(Context &ctx) {
ctx.contours.emplace_back(Contour{(int)ctx.edges.size(), 0});
ctx.shapes.back().contour_count++;
}
static void new_edge(Context &ctx, Edge&& e) {
ctx.edges.emplace_back(e);
ctx.contours.back().edge_count++;
ctx.shapes.back().edge_count++;
}
static Context& ctx(void *u) { return *static_cast<Context*>(u); }
static vec2 pt(ft_vec *v) { return vec2(v->x/64.f, v->y/64.f); }
static int ft_move_to(ft_vec *p, void *u) {
new_contour(ctx(u));
ctx(u).pos = pt(p);
return 0;
}
static int ft_line_to(ft_vec *p, void *u) {
new_edge(ctx(u), Edge{Linear, { ctx(u).pos, pt(p) }});
ctx(u).pos = pt(p);
return 0;
}
static int ftConicTo(ft_vec *c, ft_vec *p, void *u) {
new_edge(ctx(u), Edge{Quadratic, { ctx(u).pos, pt(c), pt(p) }});
ctx(u).pos = pt(p);
return 0;
}
static int ftCubicTo(ft_vec *c1, ft_vec *c2, ft_vec *p, void *u) {
new_edge(ctx(u), Edge{Cubic, { ctx(u).pos, pt(c1), pt(c2), pt(p) }});
ctx(u).pos = pt(p);
return 0;
}
static vec2 ft_glyph_offset(FT_Glyph_Metrics *m) {
return vec2(m->horiBearingX, m->horiBearingY-m->height);
}
static vec2 ft_glyph_size(FT_Glyph_Metrics *m) {
return vec2(m->width, m->height);
}
static void load_glyph(Context &ctx, FT_Face ftface, int sz, int dpi, int glyph)
{
FT_Outline_Funcs ftfuncs = { ft_move_to, ft_line_to, ftConicTo, ftCubicTo };
FT_Glyph_Metrics *m = &ftface->glyph->metrics;
FT_Error fterr;
if ((fterr = FT_Set_Char_Size(ftface, 0, sz, dpi, dpi)))
Panic("error: FT_Set_Char_Size failed: fterr=%d\n", fterr);
if ((fterr = FT_Load_Glyph(ftface, glyph, 0)))
Panic("error: FT_Load_Glyph failed: fterr=%d\n", fterr);
new_shape(ctx, ft_glyph_offset(m), ft_glyph_size(m));
if ((fterr = FT_Outline_Decompose(&ftface->glyph->outline, &ftfuncs, &ctx)))
Panic("error: FT_Outline_Decompose failed: fterr=%d\n", fterr);
}
static const char* edge_formats[] = {
"\t edge %zu Unknown\n",
"\t edge %zu Linear (%f,%f) - (%f, %f)\n",
"\t edge %zu Quadratic (%f,%f) - [%f, %f] - (%f, %f)\n",
"\t edge %zu Cubic (%f,%f) - [%f, %f] - [%f, %f] - (%f, %f)\n",
};
static void dump_glyph(Context &ctx, int shape)
{
Shape &s = ctx.shapes[shape];
for (size_t i = 0; i < s.contour_count; i++) {
Contour &c = ctx.contours[s.contour_offset + i];
printf("contour %zu\n", i);
for (size_t j = 0; j < c.edge_count; j++) {
Edge &e = ctx.edges[c.edge_offset + j];
int fmt = e.type >= 2 ? e.type <= 4 ? e.type-1 : 0 : 0;
printf(edge_formats[fmt],
j, e.p[0].x, e.p[0].y, e.p[1].x, e.p[1].y,
e.p[2].x, e.p[2].y, e.p[3].x, e.p[3].y);
}
}
}
int main(int argc, char **argv)
{
FT_Error fterr;
FT_Library ftlib;
FT_Face ftface;
Context ctx;
if (argc != 5)
Panic("usage: %s <ttf-file> <dpi> <size> <code-point>\n", argv[0]);
const char * fontpath = argv[1];
int sz = atoi(argv[2]), dpi = atoi(argv[3]), cp = atoi(argv[4]);
if ((fterr = FT_Init_FreeType(&ftlib)))
Panic("error: FT_Init_FreeType failed: fterr=%d\n", fterr);
if ((fterr = FT_New_Face(ftlib, fontpath, 0, &ftface)))
Panic("error: FT_New_Face failed: fterr=%d, path=%s\n",
fterr, fontpath);
FT_Select_Charmap(ftface, FT_ENCODING_UNICODE);
load_glyph(ctx, ftface, sz, dpi, FT_Get_Char_Index(ftface, cp));
dump_glyph(ctx, 0);
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment