Skip to content

Instantly share code, notes, and snippets.

@obyknovenius
Created May 25, 2023 08:42
Show Gist options
  • Save obyknovenius/0b62460dbd73ca3c2588befc4aa7d716 to your computer and use it in GitHub Desktop.
Save obyknovenius/0b62460dbd73ca3c2588befc4aa7d716 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <hb.h>
#include <hb-ft.h>
#include <cairo.h>
#include <cairo-ft.h>
#define FONT_SIZE 16
#define OFFSET 0
int
main(int argc, char **argv)
{
const char *fontfile = "LiberationSerif-Regular.ttf";
const char *text = "Words, words, words moooore";
//const char *text = " moooore";
/* Initialize FreeType and create FreeType font face. */
FT_Library ft_library;
FT_Face ft_face;
FT_Error ft_error;
if ((ft_error = FT_Init_FreeType (&ft_library)))
abort();
if ((ft_error = FT_New_Face (ft_library, fontfile, 0, &ft_face)))
abort();
if ((ft_error = FT_Set_Char_Size (ft_face, FONT_SIZE*64, FONT_SIZE*64, 0, 0)))
abort();
/* Create hb-ft font. */
hb_font_t *hb_font;
hb_font = hb_ft_font_create (ft_face, NULL);
hb_ft_font_set_load_flags (hb_font, FT_LOAD_NO_HINTING);
/* Create hb-buffer and populate. */
hb_buffer_t *hb_buffer;
hb_buffer = hb_buffer_create ();
hb_buffer_add_utf8 (hb_buffer, text, -1, 0, -1);
hb_buffer_guess_segment_properties (hb_buffer);
/* Shape it! */
hb_shape (hb_font, hb_buffer, NULL, 0);
/* Get glyph information and positions out of the buffer. */
unsigned int len = hb_buffer_get_length (hb_buffer);
hb_glyph_info_t *info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);
/* Print them out as is. */
printf ("Raw buffer contents:\n");
for (unsigned int i = 0; i < len; i++)
{
hb_codepoint_t gid = info[i].codepoint;
unsigned int cluster = info[i].cluster;
double x_advance = pos[i].x_advance / 64.;
double y_advance = pos[i].y_advance / 64.;
double x_offset = pos[i].x_offset / 64.;
double y_offset = pos[i].y_offset / 64.;
char glyphname[32];
hb_font_get_glyph_name (hb_font, gid, glyphname, sizeof (glyphname));
printf ("glyph='%s' cluster=%d advance=(%g,%g) offset=(%g,%g)\n",
glyphname, cluster, x_advance, y_advance, x_offset, y_offset);
}
printf ("Converted to absolute positions:\n");
/* And converted to absolute positions. */
{
double current_x = 0;
double current_y = 0;
for (unsigned int i = 0; i < len; i++)
{
hb_codepoint_t gid = info[i].codepoint;
unsigned int cluster = info[i].cluster;
double x_position = current_x + pos[i].x_offset / 64.;
double y_position = current_y + pos[i].y_offset / 64.;
char glyphname[32];
hb_font_get_glyph_name (hb_font, gid, glyphname, sizeof (glyphname));
printf ("glyph='%s' cluster=%d position=(%g,%g)\n",
glyphname, cluster, x_position, y_position);
current_x += pos[i].x_advance / 64.;
current_y += pos[i].y_advance / 64.;
}
}
/* Draw, using cairo. */
double width = 0;
double height = 30;
for (unsigned int i = 0; i < len; i++)
{
width += pos[i].x_advance / 64.;
height -= pos[i].y_advance / 64.;
}
if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer)))
height += FONT_SIZE;
else
width += FONT_SIZE;
/* Set up cairo surface. */
cairo_surface_t *cairo_surface;
cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
ceil(width),
ceil(height));
cairo_t *cr;
cr = cairo_create (cairo_surface);
cairo_set_source_rgba (cr, 1., 1., 1., 1.);
cairo_paint (cr);
cairo_set_source_rgba (cr, 0., 0., 0., 1.);
//cairo_translate (cr, OFFSET, 0.);
/* Set up cairo font face. */
cairo_font_face_t *cairo_face;
cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, FT_LOAD_NO_HINTING);
cairo_set_font_face (cr, cairo_face);
cairo_set_font_size (cr, FONT_SIZE);
/*
cairo_font_options_t *options = cairo_font_options_create();
cairo_get_font_options(cr, options);
fprintf(stderr, "hint metrics: %d\n", (int)cairo_font_options_get_hint_metrics(options));
fprintf(stderr, "hint style: %d\n", (int)cairo_font_options_get_hint_style(options));
*/
cairo_font_options_t *options = cairo_font_options_create();
cairo_set_font_options(cr, options);
/* Set up baseline. */
if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer)))
{
cairo_font_extents_t font_extents;
cairo_font_extents (cr, &font_extents);
double baseline = (FONT_SIZE - font_extents.height) * .5 + font_extents.ascent;
cairo_translate (cr, 0, baseline);
}
else
{
cairo_translate (cr, FONT_SIZE * .5, 0);
}
printf ("Cairo positions:\n");
cairo_glyph_t *cairo_glyphs = cairo_glyph_allocate (len);
double current_x = 0;
double current_y = 0;
for (unsigned int i = 0; i < len; i++)
{
cairo_glyphs[i].index = info[i].codepoint;
cairo_glyphs[i].x = current_x + pos[i].x_offset / 64.;
cairo_glyphs[i].y = -(current_y + pos[i].y_offset / 64.);
current_x += pos[i].x_advance / 64.;
current_y += pos[i].y_advance / 64.;
hb_codepoint_t gid = info[i].codepoint;
char glyphname[32];
hb_font_get_glyph_name (hb_font, gid, glyphname, sizeof (glyphname));
printf ("glyph='%s' index=%lu position=(%g,%g)\n",
glyphname, cairo_glyphs[i].index, cairo_glyphs[i].x, cairo_glyphs[i].y);
}
cairo_glyph_t *another_cairo_glyphs = cairo_glyph_allocate (len);
for (int i = 0; i < len; i++) {
another_cairo_glyphs[i].index = cairo_glyphs[i].index;
another_cairo_glyphs[i].x = cairo_glyphs[i].x;
another_cairo_glyphs[i].y = cairo_glyphs[i].y + 30;
}
cairo_show_glyphs (cr, cairo_glyphs, len);
cairo_glyph_free (cairo_glyphs);
cairo_show_glyphs (cr, another_cairo_glyphs + 1, len - 1);
cairo_glyph_free (another_cairo_glyphs);
cairo_surface_write_to_png (cairo_surface, "out.png");
cairo_font_face_destroy (cairo_face);
cairo_destroy (cr);
cairo_surface_destroy (cairo_surface);
hb_buffer_destroy (hb_buffer);
hb_font_destroy (hb_font);
FT_Done_Face (ft_face);
FT_Done_FreeType (ft_library);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment