[pango/pango2: 90/178] docs: Add an example for user fonts
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/pango2: 90/178] docs: Add an example for user fonts
- Date: Wed, 22 Jun 2022 20:10:15 +0000 (UTC)
commit 4924c6452f6c0a23d253ffc9375cb695fc26c598
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Jun 12 10:04:16 2022 -0400
docs: Add an example for user fonts
docs/bullets.png | Bin 0 -> 13858 bytes
docs/meson.build | 3 +
docs/pango.toml.in | 2 +
docs/pango_user.md | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++
examples/cairoshape.c | 91 ++++++--------
5 files changed, 381 insertions(+), 51 deletions(-)
---
diff --git a/docs/bullets.png b/docs/bullets.png
new file mode 100644
index 000000000..2aea92cd1
Binary files /dev/null and b/docs/bullets.png differ
diff --git a/docs/meson.build b/docs/meson.build
index 1b6b50b89..f8aa9141d 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -5,6 +5,8 @@ pango_content_files = [
'pango_markup.md',
'pango_fonts.md',
'pango_bidi.md',
+ 'pango_cairo.md',
+ 'pango_user.md',
'pango-name.png',
'layout-light.png',
'layout-dark.png',
@@ -51,6 +53,7 @@ pango_content_files = [
'line-height3-light.png',
'line-height3-dark.png',
'rotated-text.png',
+ 'bullets.png',
]
doc_conf = configuration_data()
diff --git a/docs/pango.toml.in b/docs/pango.toml.in
index b68aecdef..4584c870b 100644
--- a/docs/pango.toml.in
+++ b/docs/pango.toml.in
@@ -49,6 +49,7 @@ content_files = [
"pango_fonts.md",
"pango_bidi.md",
"pango_cairo.md",
+ "pango_user.md",
]
content_images = [
@@ -97,6 +98,7 @@ content_images = [
"line-height3-light.png",
"line-height3-dark.png",
"rotated-text.png",
+ "bullets.png",
]
urlmap_file = "urlmap.js"
diff --git a/docs/pango_user.md b/docs/pango_user.md
new file mode 100644
index 000000000..0938042f6
--- /dev/null
+++ b/docs/pango_user.md
@@ -0,0 +1,336 @@
+---
+Title: Rendering with user fonts
+---
+
+# Rendering with user fonts
+
+Most of the time, text is rendered using fonts that are ready-made and provided
+in formats such as TrueType or OpenType. Pango supports such fonts with
+[class@PangoHbFace]. But there are fonts in custom formats that HarfBuzz might
+not support. And sometimes, it is more convenient to use a drawing API to render
+glyphs on-the-spot, maybe with fancy effects.
+
+For these cases, Pango provides the [class@PangoUserFace] implementation of
+`PangoFontFace` that uses callbacks for its functionality. This lets you embed
+custom drawing into your text, fully integrated with Pango's text layout
+capabilities.
+
+## A user font example
+
+```
+#include <stdio.h>
+#include <string.h>
+
+#include <pango/pangocairo.h>
+
+#define BULLET "•"
+#define HEART "♥"
+
+const char text[] =
+"The GNOME project provides three things:\n"
+"\n"
+" • The GNOME desktop environment\n"
+" • The GNOME development platform\n"
+" • Planet GNOME\n"
+" ♥ Lots of love";
+
+typedef struct {
+ double width, height;
+ const char *path;
+} MiniSvg;
+
+static MiniSvg GnomeFootLogo = {
+ 96.2152, 118.26,
+ "M 86.068,1 C 61.466,0 56.851,35.041 70.691,35.041 C 84.529,35.041 110.671,0 86.068,0 z "
+ "M 45.217,30.699 C 52.586,31.149 60.671,2.577 46.821,4.374 C 32.976,6.171 37.845,30.249 45.217,30.699 z "
+ "M 11.445,48.453 C 16.686,46.146 12.12,23.581 3.208,29.735 C -5.7,35.89 6.204,50.759 11.445,48.453 z "
+ "M 26.212,36.642 C 32.451,35.37 32.793,9.778 21.667,14.369 C 10.539,18.961 19.978,37.916 26.212,36.642 L
26.212,36.642 z "
+ "M 58.791,93.913 C 59.898,102.367 52.589,106.542 45.431,101.092 C 22.644,83.743 83.16,75.088 79.171,51.386
C 75.86,31.712 15.495,37.769 8.621,68.553 C 3.968,89.374 27.774,118.26 52.614,118.26 C 64.834,118.26
78.929,107.226 81.566,93.248 C 83.58,82.589 57.867,86.86 58.791,93.913 L 58.791,93.913 z "
+ "\0"
+};
+
+static void
+mini_svg_render (MiniSvg *shape, cairo_t *cr)
+{
+ double x, y;
+ char op[2];
+ int len;
+ double x1, y1, x2, y2, x3, y3;
+ const char *p;
+
+ cairo_get_current_point (cr, &x, &y);
+ cairo_translate (cr, x, y);
+
+ for (p = shape->path; sscanf (p, "%1s %n", op, &len), p += len, *p;)
+ switch (*op)
+ {
+ case 'M':
+ sscanf (p, "%lf,%lf %n", &x, &y, &len); p += len;
+ cairo_move_to (cr, x, y);
+ break;
+ case 'L':
+ sscanf (p, "%lf,%lf %n", &x, &y, &len); p += len;
+ cairo_line_to (cr, x, y);
+ break;
+ case 'C':
+ sscanf (p, "%lf,%lf %lf,%lf %lf,%lf %n", &x1, &y1, &x2, &y2, &x3, &y3, &len); p += len;
+ cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+ break;
+ case 'z':
+ cairo_close_path (cr);
+ break;
+ default:
+ g_warning ("Invalid MiniSvg operation '%c'", *op);
+ break;
+ }
+
+ cairo_fill (cr);
+}
+
+static PangoLayout *
+get_layout (cairo_t *cr)
+{
+ PangoLayout *layout;
+ PangoAttrList *attrs;
+ PangoFontDescription *font_desc;
+ const char *p;
+
+ /* Create a PangoLayout, set the font and text */
+ layout = pango_cairo_create_layout (cr);
+ pango_layout_set_text (layout, text, -1);
+
+ font_desc = pango_font_description_from_string ("Cantarell 12");
+ pango_layout_set_font_description (layout, font_desc);
+ pango_font_description_free (font_desc);
+
+ attrs = pango_attr_list_new ();
+
+ font_desc = pango_font_description_from_string ("Bullets 12");
+
+ for (p = text; (p = strstr (p, BULLET)); p += strlen (BULLET))
+ {
+ PangoAttribute *attr;
+
+ attr = pango_attr_font_desc_new (font_desc);
+ attr->start_index = p - text;
+ attr->end_index = attr->start_index + strlen (BULLET);
+ pango_attr_list_insert (attrs, attr);
+ }
+
+ for (p = text; (p = strstr (p, HEART)); p += strlen (HEART))
+ {
+ PangoAttribute *attr;
+
+ attr = pango_attr_font_desc_new (font_desc);
+ attr->start_index = p - text;
+ attr->end_index = attr->start_index + strlen (HEART);
+ pango_attr_list_insert (attrs, attr);
+ }
+
+ pango_font_description_free (font_desc);
+
+ pango_layout_set_attributes (layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ return layout;
+}
+
+static void
+measure_text (cairo_t *cr, int *width, int *height)
+{
+ PangoLayout *layout = get_layout (cr);
+ PangoLines *lines = pango_layout_get_lines (layout);
+ PangoRectangle ext;
+
+ pango_lines_get_extents (lines, NULL, &ext);
+ pango_extents_to_pixels (&ext, NULL);
+
+ *width = ext.width + 20;
+ *height = ext.height + 20;
+}
+
+static void
+draw_text (cairo_t *cr)
+{
+ PangoLayout *layout = get_layout (cr);
+ PangoLines *lines = pango_layout_get_lines (layout);
+
+ cairo_move_to (cr, 10, 10);
+ pango_cairo_show_lines (cr, lines);
+
+ g_object_unref (layout);
+}
+
+static gboolean
+glyph_cb (PangoUserFace *face,
+ hb_codepoint_t unicode,
+ hb_codepoint_t *glyph,
+ gpointer data)
+{
+ if (unicode == 0x2022 || unicode == 0x2665)
+ {
+ *glyph = unicode;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+glyph_info_cb (PangoUserFace *face,
+ int size,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ hb_position_t *h_advance,
+ hb_position_t *v_advance,
+ gboolean *is_color,
+ gpointer user_data)
+{
+ if (glyph == 0x2022 || glyph == 0x2665)
+ {
+ extents->x_bearing = 0;
+ extents->y_bearing = - size;
+ extents->width = size;
+ extents->height = size;
+
+ *h_advance = size;
+ *v_advance = size;
+ *is_color = glyph == 0x2665;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+font_info_cb (PangoUserFace *face,
+ int size,
+ hb_font_extents_t *extents,
+ gpointer user_data)
+{
+ extents->ascender = size;
+ extents->descender = 0;
+ extents->line_gap = 0;
+
+ return TRUE;
+}
+
+static gboolean
+render_cb (PangoUserFace *face,
+ int size,
+ hb_codepoint_t glyph,
+ gpointer user_data,
+ const char *backend_id,
+ gpointer backend_data)
+{
+ cairo_t *cr = backend_data;
+
+ if (strcmp (backend_id, "cairo") != 0)
+ return FALSE;
+
+ if (glyph == 0x2022)
+ {
+ MiniSvg *shape = &GnomeFootLogo;
+
+ cairo_move_to (cr, 0, -1);
+ cairo_scale (cr, 1. / shape->width, 1. / shape->height);
+
+ mini_svg_render (shape, cr);
+
+ return TRUE;
+ }
+ else if (glyph == 0x2665)
+ {
+ cairo_set_source_rgb (cr, 1., 0., 0.);
+
+ cairo_move_to (cr, .5, .0);
+ cairo_line_to (cr, .9, -.4);
+ cairo_curve_to (cr, 1.1, -.8, .5, -.9, .5, -.5);
+ cairo_curve_to (cr, .5, -.9, -.1, -.8, .1, -.4);
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+setup_fontmap (void)
+{
+ PangoFontMap *fontmap = pango_font_map_get_default ();
+ PangoFontDescription *desc;
+ PangoUserFace *face;
+
+ desc = pango_font_description_new ();
+ pango_font_description_set_family (desc, "Bullets");
+
+ /* Create our fancy user font, "Bullets Black" */
+ face = pango_user_face_new (font_info_cb,
+ glyph_cb,
+ glyph_info_cb,
+ NULL,
+ render_cb,
+ NULL, NULL, "Black", desc);
+
+ /* And add it to the default fontmap */
+ pango_font_map_add_face (fontmap, PANGO_FONT_FACE (face));
+
+ pango_font_description_free (desc);
+}
+
+int
+main (int argc, char **argv)
+{
+ cairo_t *cr;
+ char *filename;
+ cairo_status_t status;
+ cairo_surface_t *surface;
+ int width, height;
+
+ if (argc != 2)
+ {
+ g_printerr ("Usage: cairoshape OUTPUT_FILENAME\n");
+ return 1;
+ }
+
+ filename = argv[1];
+
+ setup_fontmap ();
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+ cr = cairo_create (surface);
+ measure_text (cr, &width, &height);
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ /* Now create the final surface and draw to it. */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_paint (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.5);
+ draw_text (cr);
+ cairo_destroy (cr);
+
+ /* Write out the surface as PNG */
+ status = cairo_surface_write_to_png (surface, filename);
+ cairo_surface_destroy (surface);
+
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ g_printerr ("Could not save png to '%s': %s\n", filename, cairo_status_to_string (status));
+ return 1;
+ }
+
+ return 0;
+}
+```
+
+Once you build and run the example code above, you should see the
+following result:
+
+![Output of the example](bullets.png)
diff --git a/examples/cairoshape.c b/examples/cairoshape.c
index d3a90bd8d..2fdb9c150 100644
--- a/examples/cairoshape.c
+++ b/examples/cairoshape.c
@@ -1,8 +1,5 @@
-/* Example code to show how to use pangocairo to render arbitrary shapes
- * inside a text layout, positioned by Pango. This has become possibly
- * using the following API added in Pango 1.18:
- *
- * pango_cairo_context_set_shape_renderer ()
+/* Example code to show how to use user fonts to render arbitrary content
+ * inside a text layout, positioned by Pango.
*
* This examples uses a small parser to convert shapes in the format of
* SVG paths to cairo instructions. You can typically extract these from
@@ -12,22 +9,18 @@
* text and automatically adds PangoAttribtues to the layout to replace
* each of the with a rendering of the GNOME Foot logo.
*
- *
* Written by Behdad Esfahbod, 2007
+ * Updated for user fonts by Matthias Clasen, 2022
*
* Permission to use, copy, modify, distribute, and sell this example
* for any purpose is hereby granted without fee.
* It is provided "as is" without express or implied warranty.
*/
-
#include <stdio.h>
#include <string.h>
#include <pango/pangocairo.h>
-#include <pango/pangofc-fontmap.h>
-
-static PangoFontMap *fontmap;
#define BULLET "•"
#define HEART "♥"
@@ -56,15 +49,13 @@ static MiniSvg GnomeFootLogo = {
};
static void
-mini_svg_render (MiniSvg *shape,
- cairo_t *cr,
- gboolean do_path)
+mini_svg_render (MiniSvg *shape, cairo_t *cr)
{
double x, y;
- const char *p;
char op[2];
int len;
double x1, y1, x2, y2, x3, y3;
+ const char *p;
cairo_get_current_point (cr, &x, &y);
cairo_translate (cr, x, y);
@@ -92,28 +83,28 @@ mini_svg_render (MiniSvg *shape,
break;
}
- if (!do_path)
- cairo_fill (cr);
+ cairo_fill (cr);
}
static PangoLayout *
get_layout (cairo_t *cr)
{
- PangoContext *context;
PangoLayout *layout;
PangoAttrList *attrs;
+ PangoFontDescription *font_desc;
const char *p;
/* Create a PangoLayout, set the font and text */
- context = pango_font_map_create_context (fontmap);
- layout = pango_layout_new (context);
- g_object_unref (context);
-
+ layout = pango_cairo_create_layout (cr);
pango_layout_set_text (layout, text, -1);
+ font_desc = pango_font_description_from_string ("Cantarell 12");
+ pango_layout_set_font_description (layout, font_desc);
+ pango_font_description_free (font_desc);
+
attrs = pango_attr_list_new ();
- PangoFontDescription *font_desc = pango_font_description_from_string ("Bullets 12");
+ font_desc = pango_font_description_from_string ("Bullets 12");
for (p = text; (p = strstr (p, BULLET)); p += strlen (BULLET))
{
@@ -133,11 +124,6 @@ get_layout (cairo_t *cr)
attr->start_index = p - text;
attr->end_index = attr->start_index + strlen (HEART);
pango_attr_list_insert (attrs, attr);
-
- attr = pango_attr_fallback_new (0);
- attr->start_index = p - text;
- attr->end_index = attr->start_index + strlen (HEART);
- pango_attr_list_insert (attrs, attr);
}
pango_font_description_free (font_desc);
@@ -145,30 +131,28 @@ get_layout (cairo_t *cr)
pango_layout_set_attributes (layout, attrs);
pango_attr_list_unref (attrs);
-
return layout;
}
static void
-draw_text (cairo_t *cr, int *width, int *height)
+measure_text (cairo_t *cr, int *width, int *height)
{
PangoLayout *layout = get_layout (cr);
PangoLines *lines = pango_layout_get_lines (layout);
+ PangoRectangle ext;
- pango_layout_write_to_file (layout, "out.layout");
+ pango_lines_get_extents (lines, NULL, &ext);
+ pango_extents_to_pixels (&ext, NULL);
- /* Adds a fixed 10-pixel margin on the sides. */
+ *width = ext.width + 20;
+ *height = ext.height + 20;
+}
- if (width || height)
- {
- PangoRectangle ext;
- pango_lines_get_extents (lines, NULL, &ext);
- pango_extents_to_pixels (&ext, NULL);
- if (width)
- *width = ext.width + 20;
- if (height)
- *height = ext.height + 20;
- }
+static void
+draw_text (cairo_t *cr)
+{
+ PangoLayout *layout = get_layout (cr);
+ PangoLines *lines = pango_layout_get_lines (layout);
cairo_move_to (cr, 10, 10);
pango_cairo_show_lines (cr, lines);
@@ -182,8 +166,7 @@ glyph_cb (PangoUserFace *face,
hb_codepoint_t *glyph,
gpointer data)
{
- if (unicode == 0x2022 || /* bullet */
- unicode == 0x2665) /* heart */
+ if (unicode == 0x2022 || unicode == 0x2665)
{
*glyph = unicode;
return TRUE;
@@ -211,7 +194,6 @@ glyph_info_cb (PangoUserFace *face,
*h_advance = size;
*v_advance = size;
-
*is_color = glyph == 0x2665;
return TRUE;
@@ -253,7 +235,9 @@ render_cb (PangoUserFace *face,
cairo_move_to (cr, 0, -1);
cairo_scale (cr, 1. / shape->width, 1. / shape->height);
- mini_svg_render (shape, cr, FALSE);
+ mini_svg_render (shape, cr);
+
+ return TRUE;
}
else if (glyph == 0x2665)
{
@@ -265,26 +249,32 @@ render_cb (PangoUserFace *face,
cairo_curve_to (cr, .5, -.9, -.1, -.8, .1, -.4);
cairo_close_path (cr);
cairo_fill (cr);
+
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
static void
-setup_fontmap (PangoFontMap *fontmap)
+setup_fontmap (void)
{
+ PangoFontMap *fontmap = pango_font_map_get_default ();
PangoFontDescription *desc;
PangoUserFace *face;
desc = pango_font_description_new ();
pango_font_description_set_family (desc, "Bullets");
+ /* Create our fancy user font, "Bullets Black" */
face = pango_user_face_new (font_info_cb,
glyph_cb,
glyph_info_cb,
NULL,
render_cb,
NULL, NULL, "Black", desc);
+
+ /* And add it to the default fontmap */
pango_font_map_add_face (fontmap, PANGO_FONT_FACE (face));
pango_font_description_free (desc);
@@ -307,12 +297,11 @@ main (int argc, char **argv)
filename = argv[1];
- fontmap = PANGO_FONT_MAP (pango_fc_font_map_new ());
- setup_fontmap (PANGO_FONT_MAP (fontmap));
+ setup_fontmap ();
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
cr = cairo_create (surface);
- draw_text (cr, &width, &height);
+ measure_text (cr, &width, &height);
cairo_destroy (cr);
cairo_surface_destroy (surface);
@@ -323,7 +312,7 @@ main (int argc, char **argv)
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_paint (cr);
cairo_set_source_rgb (cr, 0.0, 0.0, 0.5);
- draw_text (cr, NULL, NULL);
+ draw_text (cr);
cairo_destroy (cr);
/* Write out the surface as PNG */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]