在使用 cairo 绘制文本时,发现它不能绘制多行文本,搜索到官网 [cairo] automatic multi-line text?,它说需要使用 Pango库,这就痛苦了。
于是研究了一下cairo源码,自己实现了一下。附上函数:
#define HAVE_STDINT_H 1
#define HAVE_CONFIG_H 1
#include "cairo/cairo-private.h"
#include "cairo/cairo-compiler-private.h"
#include "cairo/cairo-error-private.h"
#include "cairo/cairo-fontconfig-private.h"
#include "cairo/cairo-scaled-font-private.h"
#include "cairo/cairoint.h"
#include "cairo/cairo-backend-private.h"
void CCairo::show_multiline_utf8(const char* utf8, int utf8_len, LPRECT lprc, const char* szFontname, int nFontSize, DWORD dtFormat, bool bold)
{
cairo_text_extents_t extents;
cairo_status_t status;
cairo_glyph_t* glyphs = NULL, * last_glyph = NULL;
cairo_text_cluster_t* clusters = NULL;
int num_glyphs, num_clusters;
cairo_text_cluster_flags_t cluster_flags;
cairo_bool_t has_show_text_glyphs;
cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)];
cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH(cairo_text_cluster_t)];
cairo_scaled_font_t* scaled_font;
cairo_glyph_text_info_t info, * i;
cairo_select_font_face(cr, szFontname, CAIRO_FONT_SLANT_NORMAL,bold? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, nFontSize);
RECT rc;
CopyRect(&rc, lprc);
double y = rc.top;
double x = rc.left;
if (unlikely(cr->status))
return;
if (utf8 == NULL)
return;
if (utf8_len <= 0)
{
utf8_len = strlen(utf8);
}
if (utf8_len <= 0)
return;
double x1, x2, y1, y2, font_height, x_offset;
x1 = rc.left;
y1 = rc.top;
x2 = rc.right;
y2 = rc.bottom;
scaled_font = cairo_get_scaled_font(cr);
if (unlikely(scaled_font->status)) {
return;
}
cairo_scaled_font_text_extents(scaled_font, utf8, &extents);
y += extents.height;
cairo_move_to(cr, x, y);
font_height = extents.height;
has_show_text_glyphs =
cairo_surface_has_show_text_glyphs(cairo_get_target(cr));
glyphs = stack_glyphs;
num_glyphs = ARRAY_LENGTH(stack_glyphs);
if (has_show_text_glyphs) {
clusters = stack_clusters;
num_clusters = ARRAY_LENGTH(stack_clusters);
}
else {
clusters = NULL;
num_clusters = 0;
}
cairo_get_current_point(cr, &x, &y);
status = cairo_scaled_font_text_to_glyphs(scaled_font,
x, y,
utf8, utf8_len,
&glyphs, &num_glyphs,
has_show_text_glyphs ? &clusters : NULL, &num_clusters,
&cluster_flags);
if (unlikely(status))
{
if (glyphs && glyphs != stack_glyphs)
cairo_glyph_free(glyphs);
if (clusters && clusters != stack_clusters)
cairo_text_cluster_free(clusters);
if (unlikely(status))
_cairo_status_set_error(&cr->status, _cairo_error(status));
return;
}
if (num_glyphs == 0)
return;
i = NULL;
if (has_show_text_glyphs) {
info.utf8 = utf8;
info.utf8_len = utf8_len;
info.clusters = clusters;
info.num_clusters = num_clusters;
info.cluster_flags = cluster_flags;
i = &info;
}
int line_head = 0;
int line_no = 0;
for (int n = 0; n < num_glyphs; n++)
{
last_glyph = &glyphs[n];
status = cr->backend->glyph_extents(cr, last_glyph, 1, &extents);
if (unlikely(status))
{
if (glyphs && glyphs != stack_glyphs)
cairo_glyph_free(glyphs);
if (clusters && clusters != stack_clusters)
cairo_text_cluster_free(clusters);
if (unlikely(status))
_cairo_status_set_error(&cr->status, _cairo_error(status));
return;
}
if (last_glyph->x + extents.x_advance >= x2)
{
//本行需要计算右对齐
double dj = 0.0;
if (n > line_head) //2个字以上,分散对齐
{
cairo_text_extents_t exts;
cr->backend->glyph_extents(cr, &glyphs[n - 1], 1, &exts);
dj = (x2 - (glyphs[n - 1].x + exts.width)) / (n - line_head);
cr->backend->glyph_extents(cr, &glyphs[line_head], n - line_head, &exts);
double dj2 = ((x2 - x1) - exts.width) / (n - line_head);
for (int m = line_head; m < n; m++)
{
glyphs[m].x += dj * (m - line_head);
}
}
//换行,并调整后续行位置
x_offset = last_glyph->x - x;
y += font_height + 2;
for (int m = n; m < num_glyphs; m++)
{
glyphs[m].x -= x_offset;
glyphs[m].y = y;
}
if (y - extents.height >= y2)
{
num_glyphs = n;
break;
}
line_no++;
line_head = n;
}
}
//cairo_show_glyphs
status = cr->backend->glyphs(cr, glyphs, num_glyphs, i);
if (unlikely(status))
{
if (glyphs && glyphs != stack_glyphs)
cairo_glyph_free(glyphs);
if (clusters && clusters != stack_clusters)
cairo_text_cluster_free(clusters);
if (unlikely(status))
_cairo_status_set_error(&cr->status, _cairo_error(status));
return;
}
last_glyph = &glyphs[num_glyphs - 1];
//cairo_glyph_extents
status = cr->backend->glyph_extents(cr, last_glyph, 1, &extents);
if (unlikely(status))
{
if (glyphs && glyphs != stack_glyphs)
cairo_glyph_free(glyphs);
if (clusters && clusters != stack_clusters)
cairo_text_cluster_free(clusters);
if (unlikely(status))
_cairo_status_set_error(&cr->status, _cairo_error(status));
return;
}
x = last_glyph->x + extents.x_advance;
y = last_glyph->y + extents.y_advance;
cr->backend->move_to(cr, x, y);
if (glyphs && glyphs != stack_glyphs)
cairo_glyph_free(glyphs);
if (clusters && clusters != stack_clusters)
cairo_text_cluster_free(clusters);
if (unlikely(status))
_cairo_status_set_error(&cr->status, _cairo_error(status));
}