summaryrefslogtreecommitdiff
path: root/pango2/pango-renderer.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango2/pango-renderer.c')
-rw-r--r--pango2/pango-renderer.c1731
1 files changed, 1731 insertions, 0 deletions
diff --git a/pango2/pango-renderer.c b/pango2/pango-renderer.c
new file mode 100644
index 00000000..5f1d3de6
--- /dev/null
+++ b/pango2/pango-renderer.c
@@ -0,0 +1,1731 @@
+/* Pango2
+ * pango-renderer.h: Base class for rendering
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+
+#include "pango-renderer.h"
+#include "pango-impl-utils.h"
+#include "pango-layout.h"
+#include "pango-attr-private.h"
+#include "pango-run-private.h"
+#include "pango-line-private.h"
+#include "pango-attributes-private.h"
+#include "pango-glyph-item-private.h"
+
+#define N_RENDER_PARTS 5
+
+#define IS_VALID_PART(part) ((guint)part < N_RENDER_PARTS)
+
+typedef struct _LineState LineState;
+typedef struct _Point Point;
+typedef struct _Pango2RendererPrivate Pango2RendererPrivate;
+
+struct _Point
+{
+ double x, y;
+};
+
+struct _LineState
+{
+ Pango2LineStyle underline;
+ Pango2UnderlinePosition underline_position;
+ Pango2Rectangle underline_rect;
+
+ Pango2LineStyle strikethrough;
+ Pango2Rectangle strikethrough_rect;
+ int strikethrough_glyphs;
+
+ Pango2LineStyle overline;
+ Pango2Rectangle overline_rect;
+
+ int logical_rect_end;
+};
+
+struct _Pango2RendererPrivate
+{
+ Pango2LineStyle underline;
+ Pango2UnderlinePosition underline_position;
+ Pango2LineStyle strikethrough;
+ Pango2LineStyle overline;
+ int active_count;
+
+ Pango2Matrix *matrix;
+ Pango2Context *context;
+
+ Pango2Color color[N_RENDER_PARTS];
+ gboolean color_set[N_RENDER_PARTS];
+
+ Pango2Lines *lines;
+ Pango2Line *line;
+ LineState *line_state;
+};
+
+static void pango2_renderer_finalize (GObject *gobject);
+static void pango2_renderer_default_draw_glyphs (Pango2Renderer *renderer,
+ Pango2Font *font,
+ Pango2GlyphString *glyphs,
+ int x,
+ int y);
+static void pango2_renderer_default_draw_run (Pango2Renderer *renderer,
+ const char *text,
+ Pango2Run *run,
+ int x,
+ int y);
+static void pango2_renderer_default_draw_rectangle (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ int x,
+ int y,
+ int width,
+ int height);
+static void pango2_renderer_default_draw_styled_line (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ Pango2LineStyle style,
+ int x,
+ int y,
+ int width,
+ int height);
+static void pango2_renderer_default_prepare_run (Pango2Renderer *renderer,
+ Pango2Run *run);
+static void pango2_renderer_prepare_run (Pango2Renderer *renderer,
+ Pango2Run *run);
+
+static void
+to_device (Pango2Matrix *matrix,
+ double x,
+ double y,
+ Point *result)
+{
+ if (matrix)
+ {
+ result->x = (x * matrix->xx + y * matrix->xy) / PANGO2_SCALE + matrix->x0;
+ result->y = (x * matrix->yx + y * matrix->yy) / PANGO2_SCALE + matrix->y0;
+ }
+ else
+ {
+ result->x = x / PANGO2_SCALE;
+ result->y = y / PANGO2_SCALE;
+ }
+}
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (Pango2Renderer, pango2_renderer, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (Pango2Renderer))
+
+static void
+pango2_renderer_class_init (Pango2RendererClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ klass->draw_glyphs = pango2_renderer_default_draw_glyphs;
+ klass->draw_run = pango2_renderer_default_draw_run;
+ klass->draw_rectangle = pango2_renderer_default_draw_rectangle;
+ klass->draw_styled_line = pango2_renderer_default_draw_styled_line;
+ klass->prepare_run = pango2_renderer_default_prepare_run;
+
+ gobject_class->finalize = pango2_renderer_finalize;
+}
+
+static void
+pango2_renderer_init (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ priv->matrix = NULL;
+}
+
+static void
+pango2_renderer_finalize (GObject *gobject)
+{
+ Pango2Renderer *renderer = PANGO2_RENDERER (gobject);
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ if (priv->matrix)
+ pango2_matrix_free (priv->matrix);
+
+ G_OBJECT_CLASS (pango2_renderer_parent_class)->finalize (gobject);
+}
+
+static void
+pango2_renderer_activate_with_context (Pango2Renderer *renderer,
+ Pango2Context *context)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ /* We only change the matrix if the renderer isn't already active. */
+ if (!priv->active_count)
+ {
+ pango2_renderer_set_matrix (renderer, context ? pango2_context_get_matrix (context) : NULL);
+ priv->context = context;
+ }
+
+ pango2_renderer_activate (renderer);
+}
+
+static void
+draw_underline (Pango2Renderer *renderer,
+ LineState *state)
+{
+ Pango2Rectangle *rect = &state->underline_rect;
+ Pango2LineStyle underline = state->underline;
+
+ state->underline = PANGO2_LINE_STYLE_NONE;
+ state->underline_position = PANGO2_UNDERLINE_POSITION_NORMAL;
+
+ switch (underline)
+ {
+ case PANGO2_LINE_STYLE_NONE:
+ break;
+ case PANGO2_LINE_STYLE_DOUBLE:
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_UNDERLINE,
+ rect->x,
+ rect->y + 2 * rect->height,
+ rect->width,
+ rect->height);
+ G_GNUC_FALLTHROUGH;
+ case PANGO2_LINE_STYLE_SOLID:
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_UNDERLINE,
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ break;
+ case PANGO2_LINE_STYLE_DOTTED:
+ case PANGO2_LINE_STYLE_DASHED:
+ pango2_renderer_draw_styled_line (renderer,
+ PANGO2_RENDER_PART_UNDERLINE,
+ underline,
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ break;
+ case PANGO2_LINE_STYLE_WAVY:
+ pango2_renderer_draw_styled_line (renderer,
+ PANGO2_RENDER_PART_UNDERLINE,
+ underline,
+ rect->x,
+ rect->y,
+ rect->width,
+ 3 * rect->height);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+draw_overline (Pango2Renderer *renderer,
+ LineState *state)
+{
+ Pango2Rectangle *rect = &state->overline_rect;
+ Pango2LineStyle overline = state->overline;
+
+ state->overline = PANGO2_LINE_STYLE_NONE;
+
+ switch (overline)
+ {
+ case PANGO2_LINE_STYLE_NONE:
+ break;
+ case PANGO2_LINE_STYLE_DOUBLE:
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_OVERLINE,
+ rect->x,
+ rect->y - 2 * rect->height,
+ rect->width,
+ rect->height);
+ G_GNUC_FALLTHROUGH;
+ case PANGO2_LINE_STYLE_SOLID:
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_OVERLINE,
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ break;
+ case PANGO2_LINE_STYLE_DOTTED:
+ case PANGO2_LINE_STYLE_DASHED:
+ pango2_renderer_draw_styled_line (renderer,
+ PANGO2_RENDER_PART_OVERLINE,
+ overline,
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ break;
+ case PANGO2_LINE_STYLE_WAVY:
+ pango2_renderer_draw_styled_line (renderer,
+ PANGO2_RENDER_PART_OVERLINE,
+ overline,
+ rect->x,
+ rect->y,
+ rect->width,
+ 3 * rect->height);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+draw_strikethrough (Pango2Renderer *renderer,
+ LineState *state)
+{
+ Pango2Rectangle *rect = &state->strikethrough_rect;
+
+ if (state->strikethrough_glyphs > 0)
+ {
+ rect->y /= state->strikethrough_glyphs;
+ rect->height /= state->strikethrough_glyphs;
+
+ switch (state->strikethrough)
+ {
+ case PANGO2_LINE_STYLE_NONE:
+ break;
+ case PANGO2_LINE_STYLE_DOUBLE:
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_STRIKETHROUGH,
+ rect->x,
+ rect->y - rect->height,
+ rect->width,
+ rect->height);
+ rect->y += rect->height;
+ G_GNUC_FALLTHROUGH;
+ case PANGO2_LINE_STYLE_SOLID:
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_STRIKETHROUGH,
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ break;
+ case PANGO2_LINE_STYLE_DOTTED:
+ case PANGO2_LINE_STYLE_DASHED:
+ pango2_renderer_draw_styled_line (renderer,
+ PANGO2_RENDER_PART_STRIKETHROUGH,
+ state->strikethrough,
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ break;
+ case PANGO2_LINE_STYLE_WAVY:
+ pango2_renderer_draw_styled_line (renderer,
+ PANGO2_RENDER_PART_STRIKETHROUGH,
+ state->strikethrough,
+ rect->x,
+ rect->y,
+ rect->width,
+ 3 * rect->height);
+ break;
+ default:
+ break;
+ }
+ }
+
+ state->strikethrough = PANGO2_LINE_STYLE_NONE;
+ state->strikethrough_glyphs = 0;
+ rect->x += rect->width;
+ rect->width = 0;
+ rect->y = 0;
+ rect->height = 0;
+}
+
+static void
+handle_line_state_change (Pango2Renderer *renderer,
+ Pango2RenderPart part)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ LineState *state = priv->line_state;
+ if (!state)
+ return;
+
+ if (part == PANGO2_RENDER_PART_UNDERLINE &&
+ state->underline != PANGO2_LINE_STYLE_NONE)
+ {
+ Pango2Rectangle *rect = &state->underline_rect;
+
+ rect->width = state->logical_rect_end - rect->x;
+ draw_underline (renderer, state);
+ state->underline = priv->underline;
+ state->underline_position = priv->underline_position;
+ rect->x = state->logical_rect_end;
+ rect->width = 0;
+ }
+
+ if (part == PANGO2_RENDER_PART_OVERLINE &&
+ state->overline != PANGO2_LINE_STYLE_NONE)
+ {
+ Pango2Rectangle *rect = &state->overline_rect;
+
+ rect->width = state->logical_rect_end - rect->x;
+ draw_overline (renderer, state);
+ state->overline = priv->overline;
+ rect->x = state->logical_rect_end;
+ rect->width = 0;
+ }
+
+ if (part == PANGO2_RENDER_PART_STRIKETHROUGH &&
+ state->strikethrough != PANGO2_LINE_STYLE_NONE)
+ {
+ Pango2Rectangle *rect = &state->strikethrough_rect;
+
+ rect->width = state->logical_rect_end - rect->x;
+ draw_strikethrough (renderer, state);
+ state->strikethrough = priv->strikethrough;
+ }
+}
+
+static void
+add_underline (Pango2Renderer *renderer,
+ LineState *state,
+ Pango2FontMetrics *metrics,
+ int base_x,
+ int base_y,
+ Pango2Rectangle *ink_rect,
+ Pango2Rectangle *logical_rect)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ Pango2Rectangle *current_rect = &state->underline_rect;
+ Pango2Rectangle new_rect;
+
+ int underline_thickness = pango2_font_metrics_get_underline_thickness (metrics);
+ int underline_position = pango2_font_metrics_get_underline_position (metrics);
+
+ new_rect.x = base_x + MIN (ink_rect->x, logical_rect->x);
+ new_rect.width = MAX (ink_rect->width, logical_rect->width);
+ new_rect.height = underline_thickness;
+ new_rect.y = base_y;
+
+ switch (priv->underline)
+ {
+ case PANGO2_LINE_STYLE_NONE:
+ g_assert_not_reached ();
+ break;
+ case PANGO2_LINE_STYLE_SOLID:
+ case PANGO2_LINE_STYLE_DASHED:
+ case PANGO2_LINE_STYLE_DOTTED:
+ if (priv->underline_position == PANGO2_UNDERLINE_POSITION_UNDER)
+ {
+ new_rect.y += ink_rect->y + ink_rect->height + underline_thickness;
+ break;
+ }
+ G_GNUC_FALLTHROUGH;
+ case PANGO2_LINE_STYLE_DOUBLE:
+ case PANGO2_LINE_STYLE_WAVY:
+ new_rect.y -= underline_position;
+ if (state->underline == priv->underline)
+ {
+ new_rect.y = MAX (current_rect->y, new_rect.y);
+ new_rect.height = MAX (current_rect->height, new_rect.height);
+ current_rect->y = new_rect.y;
+ current_rect->height = new_rect.height;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (priv->underline == state->underline &&
+ priv->underline_position == state->underline_position &&
+ new_rect.y == current_rect->y &&
+ new_rect.height == current_rect->height)
+ {
+ current_rect->width = new_rect.x + new_rect.width - current_rect->x;
+ }
+ else
+ {
+ draw_underline (renderer, state);
+
+ *current_rect = new_rect;
+ state->underline = priv->underline;
+ state->underline_position = priv->underline_position;
+ }
+}
+
+static void
+add_overline (Pango2Renderer *renderer,
+ LineState *state,
+ Pango2FontMetrics *metrics,
+ int base_x,
+ int base_y,
+ Pango2Rectangle *ink_rect,
+ Pango2Rectangle *logical_rect)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ Pango2Rectangle *current_rect = &state->overline_rect;
+ Pango2Rectangle new_rect;
+ int underline_thickness = pango2_font_metrics_get_underline_thickness (metrics);
+ int ascent = pango2_font_metrics_get_ascent (metrics);
+
+ new_rect.x = base_x + ink_rect->x;
+ new_rect.width = ink_rect->width;
+ new_rect.height = underline_thickness;
+ new_rect.y = base_y;
+
+ switch (priv->overline)
+ {
+ case PANGO2_LINE_STYLE_NONE:
+ g_assert_not_reached ();
+ break;
+ case PANGO2_LINE_STYLE_SOLID:
+ case PANGO2_LINE_STYLE_DOUBLE:
+ case PANGO2_LINE_STYLE_DASHED:
+ case PANGO2_LINE_STYLE_DOTTED:
+ case PANGO2_LINE_STYLE_WAVY:
+ new_rect.y -= ascent;
+ if (state->overline == priv->overline)
+ {
+ new_rect.y = MIN (current_rect->y, new_rect.y);
+ new_rect.height = MAX (current_rect->height, new_rect.height);
+ current_rect->y = new_rect.y;
+ current_rect->height = new_rect.height;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (priv->overline == state->overline &&
+ new_rect.y == current_rect->y &&
+ new_rect.height == current_rect->height)
+ {
+ current_rect->width = new_rect.x + new_rect.width - current_rect->x;
+ }
+ else
+ {
+ draw_overline (renderer, state);
+
+ *current_rect = new_rect;
+ state->overline = priv->overline;
+ }
+}
+
+static void
+add_strikethrough (Pango2Renderer *renderer,
+ LineState *state,
+ Pango2FontMetrics *metrics,
+ int base_x,
+ int base_y,
+ Pango2Rectangle *ink_rect G_GNUC_UNUSED,
+ Pango2Rectangle *logical_rect,
+ int num_glyphs)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ Pango2Rectangle *current_rect = &state->strikethrough_rect;
+ Pango2Rectangle new_rect;
+
+ int strikethrough_thickness = pango2_font_metrics_get_strikethrough_thickness (metrics);
+ int strikethrough_position = pango2_font_metrics_get_strikethrough_position (metrics);
+
+ new_rect.x = base_x + ink_rect->x;
+ new_rect.width = ink_rect->width;
+ new_rect.y = (base_y - strikethrough_position) * num_glyphs;
+ new_rect.height = strikethrough_thickness * num_glyphs;
+
+ if (state->strikethrough == priv->strikethrough)
+ {
+ current_rect->width = new_rect.x + new_rect.width - current_rect->x;
+ current_rect->y += new_rect.y;
+ current_rect->height += new_rect.height;
+ state->strikethrough_glyphs += num_glyphs;
+ }
+ else
+ {
+ draw_strikethrough (renderer, state);
+
+ *current_rect = new_rect;
+ state->strikethrough = priv->strikethrough;
+ state->strikethrough_glyphs = num_glyphs;
+ }
+}
+
+static void pango2_renderer_draw_runs (Pango2Renderer *renderer,
+ Pango2Line *line,
+ GSList *runs,
+ const char *text,
+ int x,
+ int y);
+
+static void
+draw_shaped_glyphs (Pango2Renderer *renderer,
+ Pango2GlyphString *glyphs,
+ Pango2Attribute *attr,
+ int x,
+ int y)
+{
+ Pango2RendererClass *class = PANGO2_RENDERER_GET_CLASS (renderer);
+ int i;
+
+ if (!class->draw_shape)
+ return;
+
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ Pango2GlyphInfo *gi = &glyphs->glyphs[i];
+ ShapeData *data = (ShapeData *)attr->pointer_value;
+
+ class->draw_shape (renderer,
+ &data->ink_rect,
+ &data->logical_rect,
+ data->data,
+ x, y);
+
+ x += gi->geometry.width;
+ }
+}
+
+/**
+ * pango2_renderer_draw_line:
+ * @renderer: a `Pango2Renderer`
+ * @line: a `Pango2Line`
+ * @x: X position of left edge of baseline, in user space coordinates
+ * in Pango2 units.
+ * @y: Y position of left edge of baseline, in user space coordinates
+ * in Pango2 units.
+ *
+ * Draws @line with the specified `Pango2Renderer`.
+ *
+ * This draws the glyph items that make up the line, as well as
+ * shapes, backgrounds and lines that are specified by the attributes
+ * of those items.
+ */
+void
+pango2_renderer_draw_line (Pango2Renderer *renderer,
+ Pango2Line *line,
+ int x,
+ int y)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ LineState state = { 0, };
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ pango2_renderer_activate_with_context (renderer, line->context);
+
+ priv->line = line;
+ priv->line_state = &state;
+
+ state.underline = PANGO2_LINE_STYLE_NONE;
+ state.underline_position = PANGO2_UNDERLINE_POSITION_NORMAL;
+ state.overline = PANGO2_LINE_STYLE_NONE;
+ state.strikethrough = PANGO2_LINE_STYLE_NONE;
+
+ pango2_renderer_draw_runs (renderer, line, line->runs, line->data->text, x, y);
+
+ /* Finish off any remaining underlines */
+ draw_underline (renderer, &state);
+ draw_overline (renderer, &state);
+ draw_strikethrough (renderer, &state);
+
+ priv->line = NULL;
+ priv->line_state = NULL;
+ priv->line = NULL;
+
+ pango2_renderer_deactivate (renderer);
+}
+
+/**
+ * pango2_renderer_draw_lines:
+ * @renderer: a `Pango2Renderer`
+ * @lines: a `Pango2Lines` object
+ * @x: X position of left edge of baseline, in user space coordinates
+ * in Pango2 units.
+ * @y: Y position of left edge of baseline, in user space coordinates
+ * in Pango2 units.
+ *
+ * Draws @lines with the specified `Pango2Renderer`.
+ */
+void
+pango2_renderer_draw_lines (Pango2Renderer *renderer,
+ Pango2Lines *lines,
+ int x,
+ int y)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ int n;
+ Pango2Line *line;
+ int line_x, line_y;
+ Pango2Line **l;
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ priv->lines = lines;
+
+ l = pango2_lines_get_lines (lines);
+ for (n = 0; n < pango2_lines_get_line_count (lines); n++)
+ {
+ line = l[n];
+ pango2_lines_get_line_position (lines, n, &line_x, &line_y);
+
+ if (n == 0)
+ pango2_renderer_activate_with_context (renderer, line->context);
+
+ pango2_renderer_draw_line (renderer, line, x + line_x, y + line_y);
+ }
+
+ if (n > 0)
+ pango2_renderer_deactivate (renderer);
+
+ priv->lines = NULL;
+}
+
+static void
+pango2_shape_get_extents (int n_chars,
+ Pango2Rectangle *shape_ink,
+ Pango2Rectangle *shape_logical,
+ Pango2Rectangle *ink_rect,
+ Pango2Rectangle *logical_rect)
+{
+ if (n_chars > 0)
+ {
+ ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1));
+ ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1));
+ ink_rect->y = shape_ink->y;
+ ink_rect->height = shape_ink->height;
+
+ logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1));
+ logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * (n_chars - 1));
+ logical_rect->y = shape_logical->y;
+ logical_rect->height = shape_logical->height;
+ }
+ else
+ {
+ ink_rect->x = 0;
+ ink_rect->y = 0;
+ ink_rect->width = 0;
+ ink_rect->height = 0;
+
+ logical_rect->x = 0;
+ logical_rect->y = 0;
+ logical_rect->width = 0;
+ logical_rect->height = 0;
+ }
+}
+
+
+
+static void
+pango2_renderer_draw_runs (Pango2Renderer *renderer,
+ Pango2Line *line,
+ GSList *runs,
+ const char *text,
+ int x,
+ int y)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ GSList *l;
+ int x_off = 0;
+ int glyph_string_width;
+ gboolean got_overall = FALSE;
+ Pango2Rectangle overall_rect;
+
+ for (l = runs; l; l = l->next)
+ {
+ Pango2FontMetrics *metrics;
+ Pango2Run *run = l->data;
+ Pango2GlyphItem *glyph_item = pango2_run_get_glyph_item (run);
+ Pango2Item *item = pango2_run_get_item (run);
+ Pango2GlyphString *glyphs = pango2_run_get_glyphs (run);
+ Pango2Rectangle ink_rect, *ink = NULL;
+ Pango2Rectangle logical_rect, *logical = NULL;
+ ItemProperties properties;
+ int y_off;
+
+ if (item->analysis.flags & PANGO2_ANALYSIS_FLAG_CENTERED_BASELINE)
+ logical = &logical_rect;
+
+ pango2_renderer_prepare_run (renderer, run);
+
+ pango2_item_get_properties (item, &properties);
+
+ if (properties.shape)
+ {
+ ShapeData *data = (ShapeData *)properties.shape->pointer_value;
+
+ ink = &ink_rect;
+ logical = &logical_rect;
+ pango2_shape_get_extents (glyphs->num_glyphs,
+ &data->ink_rect,
+ &data->logical_rect,
+ ink,
+ logical);
+
+ glyph_string_width = logical->width;
+ }
+ else
+ {
+ if (priv->underline != PANGO2_LINE_STYLE_NONE ||
+ priv->overline != PANGO2_LINE_STYLE_NONE ||
+ priv->strikethrough != PANGO2_LINE_STYLE_NONE)
+ {
+ ink = &ink_rect;
+ logical = &logical_rect;
+ }
+ if (G_UNLIKELY (ink || logical))
+ pango2_glyph_string_extents (glyphs, item->analysis.font, ink, logical);
+ if (logical)
+ glyph_string_width = logical_rect.width;
+ else
+ glyph_string_width = pango2_glyph_string_get_width (glyphs);
+ }
+
+ priv->line_state->logical_rect_end = x + x_off + glyph_string_width;
+
+ x_off += glyph_item->start_x_offset;
+ y_off = glyph_item->y_offset;
+
+ if (item->analysis.flags & PANGO2_ANALYSIS_FLAG_CENTERED_BASELINE)
+ {
+ gboolean is_hinted = ((logical_rect.y | logical_rect.height) & (PANGO2_SCALE - 1)) == 0;
+ int adjustment = logical_rect.y + logical_rect.height / 2;
+
+ if (is_hinted)
+ adjustment = PANGO2_UNITS_ROUND (adjustment);
+
+ y_off += adjustment;
+ }
+
+ if (priv->color_set[PANGO2_RENDER_PART_BACKGROUND])
+ {
+ if (!got_overall)
+ {
+ pango2_line_get_extents (line, NULL, &overall_rect);
+ got_overall = TRUE;
+ }
+
+ pango2_renderer_draw_rectangle (renderer,
+ PANGO2_RENDER_PART_BACKGROUND,
+ x + x_off,
+ y + overall_rect.y,
+ glyph_string_width,
+ overall_rect.height);
+ }
+
+ if (properties.shape)
+ draw_shaped_glyphs (renderer, glyphs, properties.shape, x + x_off, y - y_off);
+ else
+ pango2_renderer_draw_run (renderer, text, run, x + x_off, y - y_off);
+
+ if (priv->underline != PANGO2_LINE_STYLE_NONE ||
+ priv->overline != PANGO2_LINE_STYLE_NONE ||
+ priv->strikethrough != PANGO2_LINE_STYLE_NONE)
+ {
+ metrics = pango2_font_get_metrics (item->analysis.font,
+ item->analysis.language);
+
+ if (priv->underline != PANGO2_LINE_STYLE_NONE)
+ add_underline (renderer, priv->line_state, metrics,
+ x + x_off, y - y_off,
+ ink, logical);
+
+ if (priv->overline != PANGO2_LINE_STYLE_NONE)
+ add_overline (renderer, priv->line_state, metrics,
+ x + x_off, y - y_off,
+ ink, logical);
+
+ if (priv->strikethrough != PANGO2_LINE_STYLE_NONE)
+ add_strikethrough (renderer, priv->line_state, metrics,
+ x + x_off, y - y_off,
+ ink, logical, glyphs->num_glyphs);
+
+ pango2_font_metrics_free (metrics);
+ }
+
+ if (priv->underline == PANGO2_LINE_STYLE_NONE &&
+ priv->line_state->underline != PANGO2_LINE_STYLE_NONE)
+ draw_underline (renderer, priv->line_state);
+
+ if (priv->overline == PANGO2_LINE_STYLE_NONE &&
+ priv->line_state->overline != PANGO2_LINE_STYLE_NONE)
+ draw_overline (renderer, priv->line_state);
+
+ if (priv->strikethrough == PANGO2_LINE_STYLE_NONE &&
+ priv->line_state->strikethrough != PANGO2_LINE_STYLE_NONE)
+ draw_strikethrough (renderer, priv->line_state);
+
+ x_off += glyph_string_width;
+ x_off += glyph_item->end_x_offset;
+ }
+}
+
+/**
+ * pango2_renderer_draw_glyphs:
+ * @renderer: a `Pango2Renderer`
+ * @font: a `Pango2Font`
+ * @glyphs: a `Pango2GlyphString`
+ * @x: X position of left edge of baseline, in user space coordinates
+ * in Pango2 units.
+ * @y: Y position of left edge of baseline, in user space coordinates
+ * in Pango2 units.
+ *
+ * Draws the glyphs in @glyphs with the specified `Pango2Renderer`.
+ */
+void
+pango2_renderer_draw_glyphs (Pango2Renderer *renderer,
+ Pango2Font *font,
+ Pango2GlyphString *glyphs,
+ int x,
+ int y)
+{
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ pango2_renderer_activate (renderer);
+
+ PANGO2_RENDERER_GET_CLASS (renderer)->draw_glyphs (renderer, font, glyphs, x, y);
+
+ pango2_renderer_deactivate (renderer);
+}
+
+static void
+pango2_renderer_default_draw_glyphs (Pango2Renderer *renderer,
+ Pango2Font *font,
+ Pango2GlyphString *glyphs,
+ int x,
+ int y)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ int i;
+ int x_position = 0;
+
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ Pango2GlyphInfo *gi = &glyphs->glyphs[i];
+ Point p;
+
+ to_device (priv->matrix,
+ x + x_position + gi->geometry.x_offset,
+ y + gi->geometry.y_offset,
+ &p);
+
+ pango2_renderer_draw_glyph (renderer, font, gi->glyph, p.x, p.y);
+
+ x_position += gi->geometry.width;
+ }
+}
+
+/**
+ * pango2_renderer_draw_run:
+ * @renderer: a `Pango2Renderer`
+ * @text: (nullable): the UTF-8 text that @glyph_item refers to
+ * @run: a `Pango2Run`
+ * @x: X position of left edge of baseline, in user space coordinates
+ * in Pango2 units
+ * @y: Y position of left edge of baseline, in user space coordinates
+ * in Pango2 units
+ *
+ * Draws the glyphs in @run with the specified `Pango2Renderer`,
+ * embedding the text associated with the glyphs in the output if the
+ * output format supports it.
+ *
+ * This is useful for rendering text in PDF.
+ *
+ * Note that this method does not handle attributes in @run.
+ * If you want colors, shapes and lines handled automatically according
+ * to those attributes, you need to use [method@Pango2.Renderer.draw_line]
+ * or [method@Pango2.Renderer.draw_lines].
+ *
+ * Note that @text is the start of the text for layout, which is then
+ * indexed by `run->item->offset`.
+ *
+ * If @text is %NULL, this simply calls [method@Pango2.Renderer.draw_glyphs].
+ *
+ * The default implementation of this method simply falls back to
+ * [method@Pango2.Renderer.draw_glyphs].
+ */
+void
+pango2_renderer_draw_run (Pango2Renderer *renderer,
+ const char *text,
+ Pango2Run *run,
+ int x,
+ int y)
+{
+ if (!text)
+ {
+ Pango2Item *item = pango2_run_get_item (run);
+ Pango2GlyphString *glyphs = pango2_run_get_glyphs (run);
+
+ pango2_renderer_draw_glyphs (renderer, item->analysis.font, glyphs, x, y);
+ return;
+ }
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ pango2_renderer_activate (renderer);
+
+ PANGO2_RENDERER_GET_CLASS (renderer)->draw_run (renderer, text, run, x, y);
+
+ pango2_renderer_deactivate (renderer);
+}
+
+static void
+pango2_renderer_default_draw_run (Pango2Renderer *renderer,
+ const char *text G_GNUC_UNUSED,
+ Pango2Run *run,
+ int x,
+ int y)
+{
+ Pango2Item *item = pango2_run_get_item (run);
+ Pango2GlyphString *glyphs = pango2_run_get_glyphs (run);
+
+ pango2_renderer_draw_glyphs (renderer, item->analysis.font, glyphs, x, y);
+}
+
+/**
+ * pango2_renderer_draw_rectangle:
+ * @renderer: a `Pango2Renderer`
+ * @part: type of object this rectangle is part of
+ * @x: X position at which to draw rectangle, in user space coordinates
+ * in Pango2 units
+ * @y: Y position at which to draw rectangle, in user space coordinates
+ * in Pango2 units
+ * @width: width of rectangle in Pango2 units
+ * @height: height of rectangle in Pango2 units
+ *
+ * Draws an axis-aligned rectangle in user space coordinates with the
+ * specified `Pango2Renderer`.
+ *
+ * This should be called while @renderer is already active.
+ * Use [method@Pango2.Renderer.activate] to activate a renderer.
+ */
+void
+pango2_renderer_draw_rectangle (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (IS_VALID_PART (part));
+ g_return_if_fail (priv->active_count > 0);
+
+ PANGO2_RENDERER_GET_CLASS (renderer)->draw_rectangle (renderer, part, x, y, width, height);
+}
+
+static int
+compare_points (const void *a,
+ const void *b)
+{
+ const Point *pa = a;
+ const Point *pb = b;
+
+ if (pa->y < pb->y)
+ return -1;
+ else if (pa->y > pb->y)
+ return 1;
+ else if (pa->x < pb->x)
+ return -1;
+ else if (pa->x > pb->x)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+draw_rectangle (Pango2Renderer *renderer,
+ Pango2Matrix *matrix,
+ Pango2RenderPart part,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Point points[4];
+
+ /* Convert the points to device coordinates, and sort
+ * in ascending Y order. (Ordering by X for ties)
+ */
+ to_device (matrix, x, y, &points[0]);
+ to_device (matrix, x + width, y, &points[1]);
+ to_device (matrix, x, y + height, &points[2]);
+ to_device (matrix, x + width, y + height, &points[3]);
+
+ qsort (points, 4, sizeof (Point), compare_points);
+
+ /* There are essentially three cases. (There is a fourth
+ * case where trapezoid B is degenerate and we just have
+ * two triangles, but we don't need to handle it separately.)
+ *
+ * 1 2 3
+ *
+ * ______ /\ /\
+ * / / /A \ /A \
+ * / B / /____\ /____\
+ * /_____/ / B / \ B \
+ * /_____/ \_____\
+ * \ C / \ C /
+ * \ / \ /
+ * \/ \/
+ */
+ if (points[0].y == points[1].y)
+ {
+ /* Case 1 (pure shear) */
+ pango2_renderer_draw_trapezoid (renderer, part, /* B */
+ points[0].y, points[0].x, points[1].x,
+ points[2].y, points[2].x, points[3].x);
+ }
+ else if (points[1].x < points[2].x)
+ {
+ /* Case 2 */
+ double tmp_width = ((points[2].x - points[0].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y);
+ double base_width = tmp_width + points[0].x - points[1].x;
+
+ pango2_renderer_draw_trapezoid (renderer, part, /* A */
+ points[0].y, points[0].x, points[0].x,
+ points[1].y, points[1].x, points[1].x + base_width);
+ pango2_renderer_draw_trapezoid (renderer, part, /* B */
+ points[1].y, points[1].x, points[1].x + base_width,
+ points[2].y, points[2].x - base_width, points[2].x);
+ pango2_renderer_draw_trapezoid (renderer, part, /* C */
+ points[2].y, points[2].x - base_width, points[2].x,
+ points[3].y, points[3].x, points[3].x);
+ }
+ else
+ {
+ /* case 3 */
+ double tmp_width = ((points[0].x - points[2].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y);
+ double base_width = tmp_width + points[1].x - points[0].x;
+
+ pango2_renderer_draw_trapezoid (renderer, part, /* A */
+ points[0].y, points[0].x, points[0].x,
+ points[1].y, points[1].x - base_width, points[1].x);
+ pango2_renderer_draw_trapezoid (renderer, part, /* B */
+ points[1].y, points[1].x - base_width, points[1].x,
+ points[2].y, points[2].x, points[2].x + base_width);
+ pango2_renderer_draw_trapezoid (renderer, part, /* C */
+ points[2].y, points[2].x, points[2].x + base_width,
+ points[3].y, points[3].x, points[3].x);
+ }
+}
+
+static void
+pango2_renderer_default_draw_rectangle (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ draw_rectangle (renderer, priv->matrix, part, x, y, width, height);
+}
+
+/**
+ * pango2_renderer_draw_styled_line:
+ * @renderer: a `Pango2Renderer`
+ * @part: type of object this rectangle is part of
+ * @style: the line style
+ * @x: X coordinate of line, in Pango2 units in user coordinate system
+ * @y: Y coordinate of line, in Pango2 units in user coordinate system
+ * @width: width of line, in Pango2 units in user coordinate system
+ * @height: height of line, in Pango2 units in user coordinate system
+ *
+ * Draw a line in the given style.
+ *
+ * For `PANGO2_LINE_STYLE_WAVY`, this should draw a squiggly line that
+ * approximately covers the given rectangle in the style of an underline
+ * used to indicate a spelling error.
+ *
+ * The width of the underline is rounded to an integer number
+ * of up/down segments and the resulting rectangle is centered
+ * in the original rectangle.
+ *
+ * This should be called while @renderer is already active.
+ * Use [method@Pango2.Renderer.activate] to activate a renderer.
+ */
+void
+pango2_renderer_draw_styled_line (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ Pango2LineStyle style,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (IS_VALID_PART (part));
+ g_return_if_fail (priv->active_count > 0);
+
+ PANGO2_RENDERER_GET_CLASS (renderer)->draw_styled_line (renderer, part, style, x, y, width, height);
+}
+
+/* We are drawing an error underline that looks like one of:
+ *
+ * /\ /\ /\ /\ /\ -
+ * / \ / \ / \ / \ / \ |
+ * \ \ /\ \ / / \ \ /\ \ |
+ * \ \/B \ \/ C / \ \/B \ \ | height = HEIGHT_SQUARES * square
+ * \ A \ /\ A \ / \ A \ /\ A \ |
+ * \ \/ \ \/ \ \/ \ \ |
+ * \ / \ / \ / \ / |
+ * \/ \/ \/ \/ -
+ * |---|
+ * unit_width = (HEIGHT_SQUARES - 1) * square
+ *
+ * To do this conveniently, we work in a coordinate system where A,B,C
+ * are axis aligned rectangles. (If fonts were square, the diagrams
+ * would be clearer)
+ *
+ * (0,0)
+ * /\ /\
+ * / \ / \
+ * /\ /\ /\ /
+ * / \/ \/ \/
+ * / \ /\ /
+ * Y axis \/ \/
+ * \ /\
+ * \/ \
+ * \ X axis
+ *
+ * Note that the long side in this coordinate system is HEIGHT_SQUARES + 1
+ * units long
+ *
+ * The diagrams above are shown with HEIGHT_SQUARES an integer, but
+ * that is actually incidental; the value 2.5 below seems better than
+ * either HEIGHT_SQUARES=3 (a little long and skinny) or
+ * HEIGHT_SQUARES=2 (a bit short and stubby)
+ */
+
+#define HEIGHT_SQUARES 2.5
+
+static void
+get_total_matrix (Pango2Matrix *total,
+ const Pango2Matrix *global,
+ int x,
+ int y,
+ int square)
+{
+ Pango2Matrix local;
+ double scale = 0.5 * square;
+
+ /* The local matrix translates from the axis aligned coordinate system
+ * to the original user space coordinate system.
+ */
+ local.xx = scale;
+ local.xy = - scale;
+ local.yx = scale;
+ local.yy = scale;
+ local.x0 = 0;
+ local.y0 = 0;
+
+ *total = *global;
+ pango2_matrix_concat (total, &local);
+
+ total->x0 = (global->xx * x + global->xy * y) / PANGO2_SCALE + global->x0;
+ total->y0 = (global->yx * x + global->yy * y) / PANGO2_SCALE + global->y0;
+}
+
+static void
+pango2_renderer_default_draw_styled_line (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ Pango2LineStyle style,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ int square;
+ int unit_width;
+ int width_units;
+ const Pango2Matrix identity = PANGO2_MATRIX_INIT;
+ const Pango2Matrix *matrix;
+ double dx, dx0, dy0;
+ Pango2Matrix total;
+ int i;
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ if (style != PANGO2_LINE_STYLE_WAVY)
+ {
+ /* subclasses can do better */
+ pango2_renderer_draw_rectangle (renderer, part, x, y, width, height);
+ return;
+ }
+
+ square = height / HEIGHT_SQUARES;
+ unit_width = (HEIGHT_SQUARES - 1) * square;
+ width_units = (width + unit_width / 2) / unit_width;
+
+ x += (width - width_units * unit_width) / 2;
+
+ if (priv->matrix)
+ matrix = priv->matrix;
+ else
+ matrix = &identity;
+
+ get_total_matrix (&total, matrix, x, y, square);
+ dx = unit_width * 2;
+ dx0 = (matrix->xx * dx) / PANGO2_SCALE;
+ dy0 = (matrix->yx * dx) / PANGO2_SCALE;
+
+ i = (width_units - 1) / 2;
+ while (TRUE)
+ {
+ draw_rectangle (renderer, &total, PANGO2_RENDER_PART_UNDERLINE, /* A */
+ 0, 0,
+ HEIGHT_SQUARES * 2 - 1, 1);
+
+ if (i <= 0)
+ break;
+ i--;
+
+ draw_rectangle (renderer, &total, PANGO2_RENDER_PART_UNDERLINE, /* B */
+ HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 3),
+ 1, HEIGHT_SQUARES * 2 - 3);
+
+ total.x0 += dx0;
+ total.y0 += dy0;
+ }
+ if (width_units % 2 == 0)
+ {
+ draw_rectangle (renderer, &total, PANGO2_RENDER_PART_UNDERLINE, /* C */
+ HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 2),
+ 1, HEIGHT_SQUARES * 2 - 2);
+ }
+}
+
+/**
+ * pango2_renderer_draw_trapezoid:
+ * @renderer: a `Pango2Renderer`
+ * @part: type of object this trapezoid is part of
+ * @y1_: Y coordinate of top of trapezoid
+ * @x11: X coordinate of left end of top of trapezoid
+ * @x21: X coordinate of right end of top of trapezoid
+ * @y2: Y coordinate of bottom of trapezoid
+ * @x12: X coordinate of left end of bottom of trapezoid
+ * @x22: X coordinate of right end of bottom of trapezoid
+ *
+ * Draws a trapezoid with the parallel sides aligned with the X axis
+ * using the given `Pango2Renderer`; coordinates are in device space.
+ */
+void
+pango2_renderer_draw_trapezoid (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ double y1_,
+ double x11,
+ double x21,
+ double y2,
+ double x12,
+ double x22)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (priv->active_count > 0);
+
+ if (PANGO2_RENDERER_GET_CLASS (renderer)->draw_trapezoid)
+ PANGO2_RENDERER_GET_CLASS (renderer)->draw_trapezoid (renderer, part,
+ y1_, x11, x21,
+ y2, x12, x22);
+}
+
+/**
+ * pango2_renderer_draw_glyph:
+ * @renderer: a `Pango2Renderer`
+ * @font: a `Pango2Font`
+ * @glyph: the glyph index of a single glyph
+ * @x: X coordinate of left edge of baseline of glyph
+ * @y: Y coordinate of left edge of baseline of glyph
+ *
+ * Draws a single glyph with coordinates in device space.
+ */
+void
+pango2_renderer_draw_glyph (Pango2Renderer *renderer,
+ Pango2Font *font,
+ Pango2Glyph glyph,
+ double x,
+ double y)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (priv->active_count > 0);
+
+ if (glyph == PANGO2_GLYPH_EMPTY) /* glyph PANGO2_GLYPH_EMPTY never renders */
+ return;
+
+ if (PANGO2_RENDERER_GET_CLASS (renderer)->draw_glyph)
+ PANGO2_RENDERER_GET_CLASS (renderer)->draw_glyph (renderer, font, glyph, x, y);
+}
+
+/**
+ * pango2_renderer_activate:
+ * @renderer: a `Pango2Renderer`
+ *
+ * Does initial setup before rendering operations on @renderer.
+ *
+ * [method@Pango2.Renderer.deactivate] should be called when done drawing.
+ * Calls such as [method@Pango2.Renderer.draw_lines] automatically
+ * activate the layout before drawing on it.
+ *
+ * Calls to [method@Pango2.Renderer.activate] and
+ * [method@Pango2.Renderer.deactivate] can be nested and the
+ * renderer will only be initialized and deinitialized once.
+ */
+void
+pango2_renderer_activate (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ priv->active_count++;
+ if (priv->active_count == 1)
+ {
+ if (PANGO2_RENDERER_GET_CLASS (renderer)->begin)
+ PANGO2_RENDERER_GET_CLASS (renderer)->begin (renderer);
+ }
+}
+
+/**
+ * pango2_renderer_deactivate:
+ * @renderer: a `Pango2Renderer`
+ *
+ * Cleans up after rendering operations on @renderer.
+ *
+ * See docs for [method@Pango2.Renderer.activate].
+ */
+void
+pango2_renderer_deactivate (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (priv->active_count > 0);
+
+ if (priv->active_count == 1)
+ {
+ if (PANGO2_RENDERER_GET_CLASS (renderer)->end)
+ PANGO2_RENDERER_GET_CLASS (renderer)->end (renderer);
+ }
+ priv->active_count--;
+}
+
+/**
+ * pango2_renderer_set_color:
+ * @renderer: a `Pango2Renderer`
+ * @part: the part to change the color of
+ * @color: (nullable): the new color or %NULL to unset the current color
+ *
+ * Sets the color for part of the rendering.
+ */
+void
+pango2_renderer_set_color (Pango2Renderer *renderer,
+ Pango2RenderPart part,
+ const Pango2Color *color)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (IS_VALID_PART (part));
+
+ if ((!color && !priv->color_set[part]) ||
+ (color && priv->color_set[part] &&
+ pango2_color_equal (color, &priv->color[part])))
+ return;
+
+ pango2_renderer_part_changed (renderer, part);
+
+ if (color)
+ {
+ priv->color_set[part] = TRUE;
+ priv->color[part] = *color;
+ }
+ else
+ {
+ priv->color_set[part] = FALSE;
+ }
+}
+
+/**
+ * pango2_renderer_get_color:
+ * @renderer: a `Pango2Renderer`
+ * @part: the part to get the color for
+ *
+ * Gets the current rendering color for the specified part.
+ *
+ * Return value: (transfer none) (nullable): the color for the
+ * specified part, or %NULL if it hasn't been set and should be
+ * inherited from the environment.
+ */
+Pango2Color *
+pango2_renderer_get_color (Pango2Renderer *renderer,
+ Pango2RenderPart part)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (PANGO2_IS_RENDERER (renderer), NULL);
+ g_return_val_if_fail (IS_VALID_PART (part), NULL);
+
+ if (priv->color_set[part])
+ return &priv->color[part];
+ else
+ return NULL;
+}
+
+/**
+ * pango2_renderer_part_changed:
+ * @renderer: a `Pango2Renderer`
+ * @part: the part for which rendering has changed.
+ *
+ * Informs Pango2 that the way that the rendering is done
+ * for @part has changed.
+ *
+ * This should be called if the rendering changes in a way that would
+ * prevent multiple pieces being joined together into one drawing call.
+ * For instance, if a subclass of `Pango2Renderer` was to add a stipple
+ * option for drawing underlines, it needs to call
+ *
+ * ```
+ * pango2_renderer_part_changed (render, PANGO2_RENDER_PART_UNDERLINE);
+ * ```
+ *
+ * When the stipple changes or underlines with different stipples
+ * might be joined together. Pango2 automatically calls this for
+ * changes to colors. (See [method@Pango2.Renderer.set_color])
+ */
+void
+pango2_renderer_part_changed (Pango2Renderer *renderer,
+ Pango2RenderPart part)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+ g_return_if_fail (IS_VALID_PART (part));
+ g_return_if_fail (priv->active_count > 0);
+
+ handle_line_state_change (renderer, part);
+
+ if (PANGO2_RENDERER_GET_CLASS (renderer)->part_changed)
+ PANGO2_RENDERER_GET_CLASS (renderer)->part_changed (renderer, part);
+}
+
+/**
+ * pango2_renderer_prepare_run:
+ * @renderer: a `Pango2Renderer`
+ * @run: a `Pango2Run`
+ *
+ * Set up the state of the `Pango2Renderer` for rendering @run.
+ */
+static void
+pango2_renderer_prepare_run (Pango2Renderer *renderer,
+ Pango2Run *run)
+{
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ PANGO2_RENDERER_GET_CLASS (renderer)->prepare_run (renderer, run);
+}
+
+static void
+pango2_renderer_default_prepare_run (Pango2Renderer *renderer,
+ Pango2Run *run)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+ Pango2Color *fg_color = NULL;
+ Pango2Color *bg_color = NULL;
+ Pango2Color *underline_color = NULL;
+ Pango2Color *overline_color = NULL;
+ Pango2Color *strikethrough_color = NULL;
+ GSList *l;
+ Pango2GlyphItem *glyph_item;
+
+ glyph_item = pango2_run_get_glyph_item (run);
+
+ priv->underline = PANGO2_LINE_STYLE_NONE;
+ priv->underline_position = PANGO2_UNDERLINE_POSITION_NORMAL;
+ priv->overline = PANGO2_LINE_STYLE_NONE;
+ priv->strikethrough = PANGO2_LINE_STYLE_NONE;
+
+ for (l = glyph_item->item->analysis.extra_attrs; l; l = l->next)
+ {
+ Pango2Attribute *attr = l->data;
+
+ switch ((int) attr->type)
+ {
+ case PANGO2_ATTR_UNDERLINE:
+ priv->underline = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_UNDERLINE_POSITION:
+ priv->underline_position = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_OVERLINE:
+ priv->overline = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_STRIKETHROUGH:
+ priv->strikethrough = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_FOREGROUND:
+ fg_color = &attr->color_value;
+ break;
+
+ case PANGO2_ATTR_BACKGROUND:
+ bg_color = &attr->color_value;
+ break;
+
+ case PANGO2_ATTR_UNDERLINE_COLOR:
+ underline_color = &attr->color_value;
+ break;
+
+ case PANGO2_ATTR_OVERLINE_COLOR:
+ overline_color = &attr->color_value;
+ break;
+
+ case PANGO2_ATTR_STRIKETHROUGH_COLOR:
+ strikethrough_color = &attr->color_value;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!underline_color)
+ underline_color = fg_color;
+
+ if (!overline_color)
+ overline_color = fg_color;
+
+ if (!strikethrough_color)
+ strikethrough_color = fg_color;
+
+ pango2_renderer_set_color (renderer, PANGO2_RENDER_PART_FOREGROUND, fg_color);
+ pango2_renderer_set_color (renderer, PANGO2_RENDER_PART_BACKGROUND, bg_color);
+ pango2_renderer_set_color (renderer, PANGO2_RENDER_PART_UNDERLINE, underline_color);
+ pango2_renderer_set_color (renderer, PANGO2_RENDER_PART_STRIKETHROUGH, strikethrough_color);
+ pango2_renderer_set_color (renderer, PANGO2_RENDER_PART_OVERLINE, overline_color);
+}
+
+/**
+ * pango2_renderer_set_matrix:
+ * @renderer: a `Pango2Renderer`
+ * @matrix: (nullable): a `Pango2Matrix`, or %NULL to unset any existing matrix
+ * (No matrix set is the same as setting the identity matrix.)
+ *
+ * Sets the transformation matrix that will be applied when rendering.
+ */
+void
+pango2_renderer_set_matrix (Pango2Renderer *renderer,
+ const Pango2Matrix *matrix)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (PANGO2_IS_RENDERER (renderer));
+
+ pango2_matrix_free (priv->matrix);
+ priv->matrix = pango2_matrix_copy (matrix);
+}
+
+/**
+ * pango2_renderer_get_matrix:
+ * @renderer: a `Pango2Renderer`
+ *
+ * Gets the transformation matrix that will be applied when
+ * rendering.
+ *
+ * See [method@Pango2.Renderer.set_matrix].
+ *
+ * Return value: (nullable): the matrix, or %NULL if no matrix has
+ * been set (which is the same as the identity matrix). The returned
+ * matrix is owned by Pango2 and must not be modified or freed.
+ */
+const Pango2Matrix *
+pango2_renderer_get_matrix (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (PANGO2_IS_RENDERER (renderer), NULL);
+
+ return priv->matrix;
+}
+
+/**
+ * pango2_renderer_get_lines:
+ * @renderer: a `Pango2Renderer`
+ *
+ * Gets the `Pango2Lines` currently being rendered using @renderer.
+ *
+ * Calling this function only makes sense from inside a subclass's
+ * methods, like in its draw_shape vfunc, for example.
+ *
+ * The returned layout should not be modified while still being
+ * rendered.
+ *
+ * Return value: (transfer none) (nullable): the `Pango2Lines`, or
+ * %NULL if no layout is being rendered using @renderer at this time.
+ */
+Pango2Lines *
+pango2_renderer_get_lines (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ return priv->lines;
+}
+
+/**
+ * pango2_renderer_get_layout_line:
+ * @renderer: a `Pango2Renderer`
+ *
+ * Gets the line currently being rendered using @renderer.
+ *
+ * Calling this function only makes sense from inside a subclass's
+ * methods, like in its draw_shape vfunc, for example.
+ *
+ * The returned line should not be modified while still being
+ * rendered.
+ *
+ * Return value: (transfer none) (nullable): the line, or %NULL
+ * if no layout line is being rendered using @renderer at this time.
+ */
+Pango2Line *
+pango2_renderer_get_layout_line (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ return priv->line;
+}
+
+/**
+ * pango2_renderer_get_context:
+ * @renderer: a `Pango2Renderer`
+ *
+ * Gets the current context in which @renderer operates.
+ *
+ * Returns: (nullable) (transfer none): the `Pango2Context`
+ */
+Pango2Context *
+pango2_renderer_get_context (Pango2Renderer *renderer)
+{
+ Pango2RendererPrivate *priv = pango2_renderer_get_instance_private (renderer);
+
+ return priv->context;
+}