What font libaries give just a list of normalised points and curves?

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 :slight_smile: