fontconfig 2.16 warns on every start when Pango reaches it before FcInit(); calling it first keeps stderr quiet.
169 lines
3.9 KiB
C
169 lines
3.9 KiB
C
#include "dat.h"
|
|
#include <fontconfig/fontconfig.h>
|
|
#include <pango/pangocairo.h>
|
|
#include "fn.h"
|
|
|
|
static PangoFontMap *fontmap;
|
|
static PangoContext *context;
|
|
static PangoLayout *layout;
|
|
|
|
static void
|
|
resetlayout(void)
|
|
{
|
|
pango_layout_set_width(layout, -1);
|
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
|
}
|
|
|
|
void
|
|
textclose(void)
|
|
{
|
|
if(layout != nil)
|
|
g_object_unref(layout);
|
|
if(context != nil)
|
|
g_object_unref(context);
|
|
if(fontmap != nil)
|
|
g_object_unref(fontmap);
|
|
layout = nil;
|
|
context = nil;
|
|
fontmap = nil;
|
|
}
|
|
|
|
static void
|
|
setfont(void)
|
|
{
|
|
PangoFontDescription *font;
|
|
PangoRectangle r;
|
|
int size;
|
|
|
|
font = pango_font_description_new();
|
|
pango_font_description_set_family(font, "sans");
|
|
pango_font_description_set_absolute_size(font, Fontsz * PANGO_SCALE);
|
|
pango_layout_set_font_description(layout, font);
|
|
pango_layout_set_text(layout, "Mg", -1);
|
|
pango_layout_get_pixel_extents(layout, nil, &r);
|
|
if(r.height > 0){
|
|
/* Fontsz is the popup row height, not a point size. */
|
|
size = Fontsz * PANGO_SCALE * Fontsz / r.height;
|
|
pango_font_description_set_absolute_size(font, max(size, PANGO_SCALE));
|
|
pango_layout_set_font_description(layout, font);
|
|
}
|
|
pango_font_description_free(font);
|
|
}
|
|
|
|
int
|
|
textinit(void)
|
|
{
|
|
textclose();
|
|
FcInit();
|
|
fontmap = pango_cairo_font_map_new();
|
|
if(fontmap == nil){
|
|
fprint(2, "strans: popup: can't initialize PangoCairo\n");
|
|
return 0;
|
|
}
|
|
pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(fontmap), 96);
|
|
context = pango_font_map_create_context(fontmap);
|
|
if(context != nil)
|
|
layout = pango_layout_new(context);
|
|
if(context == nil || layout == nil){
|
|
fprint(2, "strans: popup: can't create text layout\n");
|
|
textclose();
|
|
return 0;
|
|
}
|
|
pango_layout_set_single_paragraph_mode(layout, TRUE);
|
|
setfont();
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
settext(Str *s)
|
|
{
|
|
char utf[Maxutf];
|
|
int n;
|
|
|
|
if(layout == nil || s == nil || s->n <= 0)
|
|
return 0;
|
|
n = stoutf(s, utf, sizeof utf);
|
|
pango_layout_set_text(layout, utf, n);
|
|
return n > 0;
|
|
}
|
|
|
|
static void
|
|
textextents(PangoRectangle *r)
|
|
{
|
|
PangoRectangle ink, logical;
|
|
int x0, y0, x1, y1;
|
|
|
|
pango_layout_get_pixel_extents(layout, &ink, &logical);
|
|
x0 = min(ink.x, logical.x);
|
|
y0 = min(ink.y, logical.y);
|
|
x1 = max(ink.x + ink.width, logical.x + logical.width);
|
|
y1 = max(ink.y + ink.height, logical.y + logical.height);
|
|
r->x = x0;
|
|
r->y = y0;
|
|
r->width = max(x1 - x0, 0);
|
|
r->height = max(y1 - y0, 0);
|
|
}
|
|
|
|
int
|
|
textwidth(Str *s)
|
|
{
|
|
PangoRectangle r;
|
|
|
|
if(layout == nil)
|
|
return 0;
|
|
resetlayout();
|
|
if(!settext(s))
|
|
return 0;
|
|
textextents(&r);
|
|
return r.width;
|
|
}
|
|
|
|
/*
|
|
* Draws s at (x, y) into the w-by-h buffer, ellipsized to fit pixels and
|
|
* clipped to its own row so a tall fallback face cannot paint a
|
|
* neighbour.
|
|
*/
|
|
void
|
|
textdraw(u32int *buf, int w, int h, int x, int y, int fit, u32int color,
|
|
Str *s)
|
|
{
|
|
PangoRectangle r;
|
|
cairo_surface_t *surface;
|
|
cairo_t *cr;
|
|
int b, g, red, stride;
|
|
|
|
if(layout == nil || fit <= 0)
|
|
return;
|
|
resetlayout();
|
|
pango_layout_set_width(layout, fit * PANGO_SCALE);
|
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
|
if(w <= 0 || h <= 0 || !settext(s))
|
|
return;
|
|
stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
|
|
if(stride < 0 || (vlong)stride != (vlong)w * sizeof buf[0])
|
|
return;
|
|
surface = cairo_image_surface_create_for_data((uchar*)buf,
|
|
CAIRO_FORMAT_RGB24, w, h, stride);
|
|
if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS){
|
|
cairo_surface_destroy(surface);
|
|
return;
|
|
}
|
|
cairo_surface_mark_dirty(surface);
|
|
cr = cairo_create(surface);
|
|
if(cairo_status(cr) == CAIRO_STATUS_SUCCESS){
|
|
pango_cairo_update_layout(cr, layout);
|
|
textextents(&r);
|
|
cairo_rectangle(cr, x, y, fit, Fontsz);
|
|
cairo_clip(cr);
|
|
cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y);
|
|
red = color >> 16 & 0xff;
|
|
g = color >> 8 & 0xff;
|
|
b = color & 0xff;
|
|
cairo_set_source_rgb(cr, red / 255.0, g / 255.0, b / 255.0);
|
|
pango_cairo_show_layout(cr, layout);
|
|
}
|
|
cairo_destroy(cr);
|
|
cairo_surface_flush(surface);
|
|
cairo_surface_destroy(surface);
|
|
}
|