Files
zdwm/src/text.c
2025-11-14 12:24:22 +08:00

183 lines
5.6 KiB
C

#include "text.h"
#include <cairo.h>
#include <glib-object.h>
#include <glibconfig.h>
#include <pango/pango-attributes.h>
#include <pango/pango-context.h>
#include <pango/pango-enum-types.h>
#include <pango/pango-font.h>
#include <pango/pango-fontmap.h>
#include <pango/pango-layout.h>
#include <pango/pango-types.h>
#include <pango/pangocairo.h>
#include <stdint.h>
#include <stdio.h>
#include "base.h"
#include "color.h"
static PangoContext *context = nullptr;
static PangoLayout *layout = nullptr;
static PangoAttrList *attr_list = nullptr;
/**
* 初始化绘制文字的 pango 环境
* @param family 多个字体用逗号分割
* @param size 字体大小
* @param dpi
* @return layout 高度,可用于确定 bar 高度
*/
int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi) {
printf("family: %s, size: %u, dpi: %u\n", family, size, dpi);
static const char *layout_family = nullptr;
static uint32_t layout_size = 0;
static uint32_t layout_dpi = 0;
static int text_height = 0;
if (layout_family != nullptr && strcmp(layout_family, family) == 0 &&
layout_size == size && layout_dpi == dpi) {
printf("text pango layout config cached\n");
return text_height;
}
text_clean_pango_layout();
layout_family = family;
layout_size = size;
layout_dpi = dpi;
PangoFontMap *fontmap = pango_cairo_font_map_new();
context = pango_font_map_create_context(fontmap);
pango_cairo_context_set_resolution(context, (double)dpi);
layout = pango_layout_new(context);
PangoFontDescription *desc = pango_font_description_from_string(family);
pango_font_description_set_size(desc, size * PANGO_SCALE);
PangoLanguage *lang = pango_context_get_language(context);
PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, lang);
PangoAttribute *attr = pango_attr_font_desc_new(desc);
attr_list = pango_attr_list_new();
pango_attr_list_insert(attr_list, attr);
pango_layout_set_attributes(layout, attr_list);
pango_layout_set_wrap(layout, PANGO_WRAP_NONE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
int height = pango_font_metrics_get_height(metrics);
int ascent = pango_font_metrics_get_ascent(metrics);
int descent = pango_font_metrics_get_descent(metrics);
text_height =
MAX(PANGO_PIXELS_CEIL(height), PANGO_PIXELS_CEIL(ascent + descent));
pango_font_metrics_unref(metrics);
pango_font_description_free(desc);
g_object_unref(fontmap);
PangoRectangle ink_rect, logical_rect;
pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect);
text_height = MAX(text_height, MAX(ink_rect.height, logical_rect.height));
return text_height;
}
void text_clean_pango_layout(void) {
if (attr_list) {
pango_attr_list_unref(attr_list);
attr_list = nullptr;
}
if (layout) {
g_object_unref(layout);
layout = nullptr;
}
if (context) {
g_object_unref(context);
context = nullptr;
}
}
static void get_layout_size(int *width, int *height) {
PangoRectangle logical_rect;
pango_layout_get_pixel_extents(layout, nullptr, &logical_rect);
if (width) *width = logical_rect.width;
if (height) *height = logical_rect.height;
}
void text_get_size(const char *text, int *width, int *height) {
pango_layout_set_width(layout, -1);
pango_layout_set_text(layout, text, -1);
get_layout_size(width, height);
}
static void text_layout_prepare(bool align_center) {
if (layout == nullptr) {
layout = pango_layout_new(context);
pango_layout_set_attributes(layout, attr_list);
pango_layout_set_wrap(layout, PANGO_WRAP_NONE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
}
PangoAlignment align = align_center ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT;
pango_layout_set_alignment(layout, align);
}
void draw_text(cairo_t *cr, const char *text, color_t *color, area_t area,
bool align_center) {
text_layout_prepare(align_center);
pango_layout_set_width(layout, (int)area.width * PANGO_SCALE);
pango_layout_set_text(layout, text, -1);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
int height = 0;
get_layout_size(nullptr, &height);
double offset_y = ((double)area.height - (double)height) / 2;
pango_cairo_update_layout(cr, layout);
cairo_move_to(cr, (double)area.x, offset_y + (double)area.y);
pango_cairo_show_layout(cr, layout);
}
void draw_rect(cairo_t *cr, area_t area, bool fill, color_t *color,
uint16_t line_width) {
cairo_set_line_width(cr, (double)line_width);
double x = (double)area.x;
double y = (double)area.y;
double width = (double)area.width;
double height = (double)area.height;
if (fill) {
cairo_rectangle(cr, x, y, width, height);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
cairo_fill(cr);
} else {
double half_line_width = (double)line_width / 2;
x += half_line_width;
y += half_line_width;
width -= half_line_width;
height -= half_line_width;
cairo_rectangle(cr, x, y, width, height);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
cairo_stroke(cr);
}
}
void draw_background(cairo_t *cr, color_t *color, area_t area) {
double x = (double)area.x;
double y = (double)area.y;
double width = (double)area.width;
double height = (double)area.height;
cairo_move_to(cr, x, y);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
cairo_rectangle(cr, x, y, width, height);
cairo_fill(cr);
}