I’m a newb in OpenGL and would like to render some 2d text from normalised points & curves, what font libraries would take an absolute file path and just generate a list of normalised points and the curves that go between them?
FreeType allows you to get the path definition. The FT_GlyphSlotRec
structure contains a field of type FT_Outline
which contains the data for the path.
Thx, I’ll give it a look… why must the post be at least 30 characters? seems like such a waste of space when all I wanted to say was thx.
I’ve been looking at the docs but it is unclear how to load individual glyphs from a FT_Face object, mind giving a psuedo example for the character ‘0’, I’m only trying to display some numbers at present and can probably work out the rest once I get one character displayed
Use FT_Load_Glyph
or FT_Load_Char
. The former takes a glyph index, the latter a character code which is converted to a glyph index via FT_Get_Char_Index
. Either function loads the glyph into the glyph field of the FT_Face
object.
Here’s some code which generates a SVG file from a TTF file:
/* gcc -Wall -std=c99 -I/usr/include/freetype2 ft2svg.c -o ft2svg -lfreetype */
#include <stdio.h>
#include <stdlib.h>
#include <ft2build.h>
#include FT_FREETYPE_H
static void error(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
#if 0
void begin_document(void)
{
}
void begin_outline(FT_ULong ch, const FT_Outline* outline)
{
printf("outline for \\x%02lx (%c) {\n", ch, (int) ch);
printf(" flags = 0x%04x\n", outline->flags);
printf(" n_contours = %d\n", outline->n_contours);
printf(" n_points = %d\n", outline->n_points);
}
void begin_contour(int c)
{
printf(" contours[%d] {\n", c);
}
void vertex(unsigned int tags, const FT_Vector* vec)
{
char code = "2*3*"[tags & 3];
printf(" %c %ld, %ld\n", code, vec->x, vec->y);
}
void end_contour(void)
{
printf(" }\n");
}
void end_outline(void)
{
printf("}\n\n");
}
void do_contour(const FT_Outline* outline, int c, int start, int end)
{
begin_contour(c);
for (int i = start; i <= end; i++) {
unsigned char tags = ((unsigned char *)outline->tags)[i];
FT_Vector vec = outline->points[i];
vertex(tags, &vec);
}
end_contour();
}
void end_document(void)
{
}
#else
void begin_document(void)
{
static const char prolog[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
"<svg\n"
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"
" xmlns:cc=\"http://creativecommons.org/ns#\"\n"
" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"
" xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
" xmlns=\"http://www.w3.org/2000/svg\"\n"
" width=\"1000\"\n"
" height=\"1375\"\n"
" id=\"ft2svg\"\n"
" version=\"1.1\">\n"
" <g\n"
" transform=\"scale(0.05,-0.05)\"\n"
" id=\"layer1\">\n";
printf(prolog);
}
void begin_outline(FT_ULong ch, const FT_Outline* outline)
{
long x = ((ch-33) % 10) * 2000;
long y = ((ch-33) / 10) * 2500;
printf("<g id=\"group_%lu\" transform=\"translate(%ld,%ld)\">\n",
ch, x, -y-2000);
printf("<path id=\"glyph_%lu\"\n", ch);
printf(" d=\"");
}
void end_outline(void)
{
printf("\"\n");
printf(" />\n");
printf("</g>\n");
}
enum {
S_START,
S_FIRST,
S_1QUAD,
S_1CUBE,
S_2CUBE,
};
void do_point(int *state, FT_Vector p, unsigned char tags, int last, int ch, int c)
{
static FT_Vector prev;
switch (*state) {
case S_START:
if (!(tags & 1))
fprintf(stderr, "start point is off-curve (glyph %d, contour %d)\n", ch, c);
printf(" M %ld,%ld", p.x, p.y);
*state = S_FIRST;
break;
case S_FIRST:
switch (tags & 3) {
case 1: // on-curve
case 3:
printf(" L %ld,%ld", p.x, p.y);
*state = S_FIRST;
break;
case 0: // quadratic
printf(" Q %ld,%ld", p.x, p.y);
*state = S_1QUAD;
break;
case 2: // cubic
printf(" C %ld,%ld", p.x, p.y);
*state = S_1CUBE;
break;
}
break;
case S_1QUAD:
case S_2CUBE:
switch (tags & 3) {
case 1: // on-curve
case 3:
printf(" %ld,%ld", p.x, p.y);
*state = S_FIRST;
break;
case 0: // quadratic
{
long mx = (prev.x + p.x) / 2;
long my = (prev.y + p.y) / 2;
printf(" %ld,%ld", mx, my);
printf(" Q %ld,%ld", p.x, p.y);
*state = S_1QUAD;
}
break;
case 2: // cubic
{
long mx = (prev.x + p.x) / 2;
long my = (prev.y + p.y) / 2;
printf(" %ld,%ld", mx, my);
printf(" C %ld,%ld", p.x, p.y);
*state = S_1CUBE;
}
break;
}
break;
case S_1CUBE:
switch (tags & 3) {
case 1: // on-curve
case 3: // on-curve
case 0: // quadratic
fprintf(stderr, "error: missing control point for cubic\n");
exit(1);
break;
case 2: // cubic
printf(" %ld,%ld", p.x, p.y);
*state = S_2CUBE;
break;
}
break;
default:
fprintf(stderr, "error: invalid state\n");
exit(1);
break;
}
prev = p;
}
void do_contour(int ch, const FT_Outline* outline, int c, int start, int end)
{
FT_Vector* pts = outline->points;
unsigned char *tags = (unsigned char *)outline->tags;
int state = S_START;
for (int i = start; i <= end; i++)
do_point(&state, pts[i], tags[i], 0, ch, c);
do_point(&state, pts[start], tags[start], 1, ch, c);
printf(" z");
}
void end_document(void)
{
printf("</g>");
printf("</svg>");
}
#endif
int main(int argc, char **argv)
{
FT_Error ans;
FT_Library library;
argv++, argc--;
if (argc < 1 || argc > 2)
error("usage: ftdump <filename> [<index>]");
/* get file name */
const char *filename = *argv;
argv++, argc--;
int font_index = 0;
if (argc > 0)
font_index = atoi(*argv), argv++, argc--;
/* set freetype */
ans = FT_Init_FreeType(&library);
if (ans)
error("unable to initialize FreeType");
FT_Face face;
ans = FT_New_Face(library, filename, font_index, &face);
if (ans == FT_Err_Unknown_File_Format)
error("unknown file format");
else if (ans)
error("unable to access face");
begin_document();
for (FT_ULong ch = 33; ch < 127; ch++) {
static const FT_Int32 flags =
FT_LOAD_NO_SCALE |
FT_LOAD_IGNORE_TRANSFORM |
FT_LOAD_NO_BITMAP |
FT_LOAD_NO_AUTOHINT;
ans = FT_Load_Char(face, ch, flags);
if (ans)
continue;
FT_GlyphSlot glyph = face->glyph;
if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
continue;
const FT_Outline* outline = &glyph->outline;
begin_outline(ch, outline);
int start = 0;
for (int c = 0; c < outline->n_contours; c++) {
int end = outline->contours[c];
do_contour(ch, outline, c, start, end);
start = end + 1;
}
end_outline();
}
end_document();
return 0;
}
Thx, that example was way more precise than I expected, at most I was expecting some code that specifically loaded the glyph for 0 and converted it into normalised xy point list, however this will surely help more