Skip to content

Instantly share code, notes, and snippets.

@simoncozens
Last active August 29, 2015 14:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save simoncozens/6892796dd737212b0651 to your computer and use it in GitHub Desktop.
Save simoncozens/6892796dd737212b0651 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include <hb.h>
#include <hb-ft.h>
#include <fontconfig/fontconfig.h>
typedef struct {
char* family;
char* lang;
double pointSize;
int weight;
int direction;
int slant;
char* style;
char* script;
} fontOptions;
typedef struct {
double width;
double height;
double depth;
} box;
static char* get_font_path(fontOptions f) {
FcResult result;
FcChar8* filename;
char* filename2;
int id;
FcPattern* matched;
FcPattern* p = FcPatternCreate();
FcPatternAddString (p, FC_FAMILY, (FcChar8*)(f.family));
FcPatternAddDouble (p, FC_SIZE, f.pointSize);
if (f.slant)
FcPatternAddInteger(p, FC_SLANT, f.slant);
if (f.weight)
FcPatternAddInteger(p, FC_WEIGHT, f.weight);
/* Add fallback fonts here. Some of the standard 14 should be fine. */
FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Times-Roman");
FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Times");
FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Helvetica");
matched = FcFontMatch (0, p, &result);
if (FcPatternGetString (matched, FC_FILE, 0, &filename) != FcResultMatch)
return NULL;
if (FcPatternGetInteger (matched, FC_INDEX, 0, &id) != FcResultMatch)
return NULL;
filename2 = malloc(strlen(filename)); /* Because otherwise destroying the pattern will reclaim it. */
strcpy(filename2, (char*)filename);
FcPatternDestroy (matched);
FcPatternDestroy (p);
return filename2;
}
void calculate_extents(box* b, hb_glyph_info_t glyph_info, hb_glyph_position_t glyph_pos, FT_Face ft_face) {
const FT_Error error = FT_Load_Glyph(ft_face, glyph_info.codepoint, FT_LOAD_DEFAULT);
if (error) return;
const FT_Glyph_Metrics *ftmetrics = &ft_face->glyph->metrics;
b->width = glyph_pos.x_advance /64.0;
b->height = ftmetrics->horiBearingY / 64.0;
b->depth = (ftmetrics->height - ftmetrics->horiBearingY) / 64.0;
}
int main (int argc, char** argv) {
FT_Library ft_library;
char * font_path;
int device_hdpi = 72;
int device_vdpi = 72;
char * text = argv[1];
fontOptions f = {
.pointSize = 12,
.lang = "en",
.family = "Adobe Garamond Pro",
.script = "latin",
.direction = HB_DIRECTION_LTR,
.weight = 200,
};
FT_Face ft_face;
unsigned int glyph_count;
hb_font_t *hb_ft_font;
hb_buffer_t *buf;
hb_glyph_info_t *glyph_info;
hb_glyph_position_t *glyph_pos;
box glyph_extents;
unsigned int j;
assert(!FT_Init_FreeType(&ft_library));
font_path = get_font_path(f);
printf("Using %s\n", font_path);
assert(!FT_New_Face(ft_library, font_path, 0, &ft_face));
assert(!FT_Set_Char_Size(ft_face, 0, f.pointSize * 64, device_hdpi, device_vdpi ));
/* Get our harfbuzz font structs */
hb_ft_font = hb_ft_font_create(ft_face, NULL);
buf = hb_buffer_create();
if (f.script)
hb_buffer_set_script(buf, hb_tag_from_string(f.script, strlen(f.script)));
if (f.direction)
hb_buffer_set_direction(buf, f.direction);
if (f.lang)
hb_buffer_set_language(buf, hb_language_from_string(f.lang,strlen(f.lang)));
hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text));
hb_shape(hb_ft_font, buf, NULL, 0);
glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
for (j = 0; j < glyph_count; ++j) {
calculate_extents(&glyph_extents, glyph_info[j], glyph_pos[j], ft_face);
printf("CP: %d, <%f> ^%f v%f\n", glyph_info[j].codepoint, glyph_extents.width, glyph_extents.height, glyph_extents.depth);
}
hb_buffer_destroy(buf);
hb_font_destroy(hb_ft_font);
FT_Done_FreeType(ft_library);
free(font_path);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment