Font layouting & rendering with cairo and freetype
Asked Answered
B

2

8

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?

Bilski answered 18/4, 2016 at 15:13 Comment(10)
Have you looked at this Cairo doc page? I've never used FreeType with Cairo, so I don't know if that's the key, but if it solves your problem, I can post as an answer.Ecklund
This page explains how to create a cairo_font_face_t using a freetype font. You then use this font to create a scaled font. From there on the way is unclear.Bilski
You could then use Cairo's text functionality to use your FreeType cairo_font_face_t, layout the text, and render. It's not recommended; see the Cairo page regarding use of Pango. It should work though, since you can't use Pango as well. Why doesn't the machine have other libraries available?Ecklund
Well yes - but they mostly explain the so-called "toy text API" - I want to use the glyph API and can't find proper information about how to use it right for getting glyph layout info and rendering. It's for the window manager of Ghost OS (ghostkernel.org), my operating system project; porting Pango brings quite some dependencies that I'm not willing to port (fontconfig...) just to get a layouting functionality. I had good layouting with plain freetype before, so there must be a way.Bilski
So in other words, you want to use FreeType to create an FT_Bitmap, then just apply the bitmap on your Cairo surface?Ecklund
No, I want to do what I wrote in my question :P use cairo to get glyphs for a text for a freetype font, get layout information for each glyph, then draw these glyphs as cairo paths.Bilski
I don't think that cairo provides all the information about a font that you are asking for, you'll have to query freetype directly.Philina
@UliSchlachter see my answer.Bilski
What about "things like kerning"? That was part of the question, but I can't really see that in the answer.Philina
@UliSchlachter as far as I have observed cairo calculates kerning in when layouting the glyphs... I'll make some more tests and if it doesn't, I'll look for a way to add it.Bilski
B
7

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;
    }
}
Bilski answered 24/4, 2016 at 21:33 Comment(0)
E
1

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.

Ecklund answered 18/4, 2016 at 21:23 Comment(5)
See my latest comment - I want cairo to give me the layout information and paths for a glyph. This information must be there, because Pango bases on it. I just can't find any good documentation on it. Well the issue is, Pango requires stuff like fontconfig & the gnome library that I don't want in Ghost - these libs force design decisions I don't want to accept, just to get my characters layed-out. Thanks for your help so far though nice to see interest :)Bilski
I would say then to use the functions you described. They seem to be the way to go. The trouble here really is that Cairo is so limiting, with no kerning and no clear way to easily create a path from the glyphs. You can pretty much only use show_glyphs or show_text_glyphs to display glyphs. I'll edit my answer now I understand a little more.Ecklund
See other answer, thanks for the efforts though! :-)Bilski
I gave you the bounty for your help, so it's not lost.Bilski
Thanks a lot! Sorry I couldn't do more to earn it. Glad you found a solution.Ecklund

© 2022 - 2024 — McMap. All rights reserved.