Layout Pango in front of the Cairo surface

In my application, I am using Pango and Cairo to create text textures. These textures have a fixed width, but must scale their height to fit the content of the text. The parent objects involved in this situation then scale their heights to fit the text.

The problem is how I initialized Pango and Cairo, it doesn't allow it. The system is currently configured for:

cairo_surface_t* cairoSurface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, sizeX, sizeY );
cairo_t* cairoContext = cairo_create( cairoSurface );
PangoLayout* pangoLayout = pango_cairo_create_layout( cairoContext );

      

Which fixes the height, at least of the surface, is something I don't want to do, at least not all the time.

My understanding is that if no layout height is specified, it will automatically scale the height, which can then be found through pango_layout_get_size()

. I would like to create a layout first and then use the output of this function to create a surface.

However, it pango_cairo_create_layout()

requires the surface to have already been created, and I haven't been able to find a way to make the layout from pango_layout_new()

via Cairo. The API documents one of the rendering functions pango_cairo_update_layout()

, specifying what pango_cairo_create_layout()

should have been used to create the layout; however, the more important function pango_cairo_show_layout()

doesn't notice such a requirement, and I'm not sure if that means any Pango layout is allowed or not. While I can check if this works, I'm afraid the trial and error might lead me to undefined behavior.

I feel like I'm stuck with a chicken and egg situation, and the documentation for Pango is basically an API reference with a little explanation of how the library should be used. Is there a way to do this right?

+3


source to share


1 answer


I understood this process. Hopefully this information is helpful, although I am not claiming that this is the "correct" way to do all of this, only it works.

First, set up FontConfig. This may not be needed on some systems - it might be ok if you leave it automatic on Linux. On Windows, however, FontConfig is problematic. The easiest way to deal with this is to create a config in memory and point it to where you want it to look for fonts. I pointed to the resource directory of my program. You can use "C: \ Windows \ Fonts", but note that this requires permanent loading. Loading the correct font.conf file is probably the best approach, but I had no luck.

gchar* workingDir = g_get_current_dir();
gchar* resourceDir = g_strjoin( NULL, workingDir, "/Resources", (char*)0 );
FcConfigAppFontAddDir( fontConfig, (const FcChar8*)resourceDir );
g_free(workingDir);
g_free(resourceDir);
FcConfigBuildFonts( fontConfig );
FcConfigSetCurrent( fontConfig );

      

Then you need to create a fontmap, Pango context and Pango layout:

PangoFontMap* fontMap = pango_cairo_font_map_new();
PangoContext* pangoContext = pango_font_map_create_context( fontMap );
PangoLayout* pangoLayout = pango_layout_new( pangoContext );

      

Now using a handcrafted (not from pango_cairo_create_layout()

) layout doesn't seem to automatically load the fonts. Trying to use a font that is on the font map but not loaded causes Pango-Cairo to crash (using what is not listed, just goes by default). Thus, download all the fonts listed in the font map:

FcPattern *p = FcPatternCreate();
FcObjectSet *os = FcObjectSetBuild(FC_FAMILY,NULL);
FcFontSet *fs = FcFontList(fontConfig, p, os);
FcPatternDestroy( p );
FcObjectSetDestroy( os );
for( int i = 0; i < fs->nfont; ++i )
{
    guchar* fontName = FcNameUnparse( fs->fonts[i] );
    PangoFontDescription* fontDesc = pango_font_description_from_string( (gchar*)fontName );
    pango_font_map_load_font( fontMap, pangoContext, fontDesc );
    pango_font_description_free( fontDesc );
    g_free(fontName);
}

      



To specify the width:

pango_layout_set_width( pangoLayout, sizeX * PANGO_SCALE );

      

Also, you have to set the alignment / alignment / etc.

Then you can paste your text:

pango_layout_set_markup( pangoLayout, text.c_str( ), -1 );

      

After that you can get the height of the layout across pango_layout_get_pixel_size()

and use it to create Cairo objects. Then you can do it via:

cairo_move_to(cairoContext, 0, 0);
pango_cairo_update_layout( cairoContext, pangoLayout );
pango_cairo_show_layout( cairoContext, pangoLayout );

      

+1


source







All Articles