Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Font layouting & rendering with cairo and freetype

I have a system that has only the freetype2 and cairo libraries available. What I want to achieve is:

  • getting the glyphs for a UTF-8 text
  • layouting the text, storing position information (by myself)
  • getting cairo paths for each glyph for rendering

Unfortunately the documentation doesn't really explain how it should be done, as they expect one to use a higher level library like Pango.

What I think could be right is: Create a scaled font with cairo_scaled_font_create and then retrieve the glyphs for the text using cairo_scaled_font_text_to_glyphs. cairo_glyph_extents then gives the extents for each glyph. But how can I then get things like kerning and the advance? Also, how can I then get paths for each font?

Are there some more resources on this topic? Are these functions the expected way to go?

like image 309
maxdev Avatar asked Apr 18 '16 15:04

maxdev


People also ask

What is font and layout?

Typography, Layout, and Graphic Design. Typography may be defined as the theory and practice of letter and typeface design. In other words, it is an art concerned with design elements that can be applied to the letters and text (as opposed to, say, images, tables, or other visual enhancements) on a printed page.

What are the 5 main types of fonts?

Typography Basics There are five basic classifications of typefaces: serif, sans serif, script, monospaced, and display.


2 Answers

Okay, so I found what's needed.

You first need to create a cairo_scaled_font_t which represents a font in a specific size. To do so, one can simply use cairo_get_scaled_font after setting a font, it creates a scaled font for the current settings in the context.

Next, you convert the input text using cairo_scaled_font_text_to_glyphs, this gives an array of glyphs and also clusters as output. The cluster mappings represent which part of the UTF-8 string belong to the corresponding glyphs in the glyph array.

To get the extents of glyphs, cairo_scaled_font_glyph_extents is used. It gives dimensions, advances and bearings of each glyph/set of glyphs.

Finally, the paths for glyphs can be put in the context using cairo_glyph_path. These paths can then be drawn as wished.

The following example converts an input string to glyphs, retrieves their extents and renders them:

const char* text = "Hello world";
int fontSize = 14;
cairo_font_face_t* fontFace = ...;

// get the scaled font object
cairo_set_font_face(cr, fontFace);
cairo_set_font_size(cr, fontSize);
auto scaled_face = cairo_get_scaled_font(cr);

// get glyphs for the text
cairo_glyph_t* glyphs = NULL;
int glyph_count;
cairo_text_cluster_t* clusters = NULL;
int cluster_count;
cairo_text_cluster_flags_t clusterflags;

auto stat = cairo_scaled_font_text_to_glyphs(scaled_face, 0, 0, text, strlen(text), &glyphs, &glyph_count, &clusters, &cluster_count,
        &clusterflags);

// check if conversion was successful
if (stat == CAIRO_STATUS_SUCCESS) {

    // text paints on bottom line
    cairo_translate(cr, 0, fontSize);

    // draw each cluster
    int glyph_index = 0;
    int byte_index = 0;

    for (int i = 0; i < cluster_count; i++) {
        cairo_text_cluster_t* cluster = &clusters[i];
        cairo_glyph_t* clusterglyphs = &glyphs[glyph_index];

        // get extents for the glyphs in the cluster
        cairo_text_extents_t extents;
        cairo_scaled_font_glyph_extents(scaled_face, clusterglyphs, cluster->num_glyphs, &extents);
        // ... for later use

        // put paths for current cluster to context
        cairo_glyph_path(cr, clusterglyphs, cluster->num_glyphs);

        // draw black text with green stroke
        cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 1.0);
        cairo_fill_preserve(cr);
        cairo_set_source_rgba(cr, 0, 1, 0, 1.0);
        cairo_set_line_width(cr, 0.5);
        cairo_stroke(cr);

        // glyph/byte position
        glyph_index += cluster->num_glyphs;
        byte_index += cluster->num_bytes;
    }
}
like image 170
maxdev Avatar answered Oct 03 '22 20:10

maxdev


Those functions seem to be the best way, considering Cairo's text system. It just shows even more that Cairo isn't really meant for text. It won't be able to do kerning or paths really. Pango, I believe, would have its own complex code for doing those things.

For best advancement of Ghost, I would recommend porting Pango, since you (or someone else) will probably eventually want it anyway.

like image 20
oldtechaa Avatar answered Oct 03 '22 21:10

oldtechaa