summaryrefslogtreecommitdiff
path: root/utils/viewer-pangocairo.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/viewer-pangocairo.c')
-rw-r--r--utils/viewer-pangocairo.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/utils/viewer-pangocairo.c b/utils/viewer-pangocairo.c
new file mode 100644
index 00000000..3e893207
--- /dev/null
+++ b/utils/viewer-pangocairo.c
@@ -0,0 +1,462 @@
+/* viewer-pangocairo.c: PangoCairo viewer backend.
+ *
+ * Copyright (C) 1999,2004,2005 Red Hat, Inc.
+ * Copyright (C) 2001 Sun Microsystems
+ *
+ * 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 "viewer-render.h"
+#include "viewer-cairo.h"
+
+#include <pango/pangocairo.h>
+
+static int opt_annotate = 0;
+
+typedef struct
+{
+ const CairoViewerIface *iface;
+
+ gpointer backend;
+
+ PangoFontMap *fontmap;
+ cairo_font_options_t *font_options;
+} CairoViewer;
+
+static gpointer
+pangocairo_view_create (const PangoViewer *klass G_GNUC_UNUSED)
+{
+ CairoViewer *instance;
+
+ instance = g_slice_new (CairoViewer);
+
+ instance->backend = cairo_viewer_iface_create (&instance->iface);
+
+ instance->fontmap = pango_cairo_font_map_new ();
+ pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (instance->fontmap), opt_dpi);
+
+ instance->font_options = cairo_font_options_create ();
+ if (opt_hinting != HINT_DEFAULT)
+ {
+ if (opt_hinting == HINT_NONE)
+ cairo_font_options_set_hint_style (instance->font_options, CAIRO_HINT_STYLE_NONE);
+ else if (opt_hinting == HINT_FULL)
+ cairo_font_options_set_hint_style (instance->font_options, CAIRO_HINT_STYLE_FULL);
+ }
+
+ return instance;
+}
+
+static void
+pangocairo_view_destroy (gpointer instance)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+
+ cairo_font_options_destroy (c->font_options);
+
+ g_object_unref (c->fontmap);
+
+ c->iface->backend_class->destroy (c->backend);
+
+ cairo_debug_reset_static_data ();
+
+ g_slice_free (CairoViewer, c);
+}
+
+static PangoContext *
+pangocairo_view_get_context (gpointer instance)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+ PangoContext *context;
+
+ context = pango_font_map_create_context (c->fontmap);
+ pango_cairo_context_set_font_options (context, c->font_options);
+
+ return context;
+}
+
+typedef struct
+{
+ gpointer backend;
+
+ cairo_surface_t *cairo;
+} CairoSurface;
+
+static gpointer
+pangocairo_view_create_surface (gpointer instance,
+ int width,
+ int height)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+ CairoSurface *surface;
+
+ surface = g_slice_new (CairoSurface);
+
+ surface->backend = c->iface->backend_class->create_surface (c->backend,
+ width, height);
+
+ surface->cairo = c->iface->create_surface (c->backend,
+ surface->backend,
+ width, height);
+
+ return surface;
+}
+
+static void
+pangocairo_view_destroy_surface (gpointer instance,
+ gpointer surface)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+ CairoSurface *c_surface = (CairoSurface *) surface;
+
+ c->iface->backend_class->destroy_surface (c->backend, c_surface->backend);
+ cairo_surface_destroy (c_surface->cairo);
+
+ g_slice_free (CairoSurface, surface);
+}
+
+static void
+render_callback (PangoLayout *layout,
+ int x,
+ int y,
+ gpointer context,
+ gpointer state)
+{
+ cairo_t *cr = (cairo_t *) context;
+ int annotate = (GPOINTER_TO_INT (state) + opt_annotate) % 3;
+
+ cairo_save (cr);
+ cairo_translate (cr, x, y);
+
+ if (annotate)
+ {
+ cairo_pattern_t *pattern;
+ PangoRectangle ink, logical;
+ double lw = cairo_get_line_width (cr);
+ PangoLayoutIter* iter;
+
+ pango_layout_get_extents (layout, &ink, &logical);
+
+ if (annotate >= 2)
+ {
+ /* draw resolved gravity "roof" in blue */
+ cairo_save (cr);
+ cairo_translate (cr,
+ (double)logical.x / PANGO_SCALE,
+ (double)logical.y / PANGO_SCALE);
+ cairo_scale (cr,
+ (double)logical.width / PANGO_SCALE * 0.5,
+ (double)logical.height / PANGO_SCALE * 0.5);
+ cairo_translate (cr, 1.0, 1.0);
+ cairo_rotate (cr,
+ pango_gravity_to_rotation (
+ pango_context_get_gravity (
+ pango_layout_get_context (layout))));
+ cairo_move_to (cr, -1.0, -1.0);
+ cairo_rel_line_to (cr, +1.0, -0.2); /* / */
+ cairo_rel_line_to (cr, +1.0, +0.2); /* \ */
+ cairo_close_path (cr); /* - */
+ pattern = cairo_pattern_create_linear (0, -1.0, 0, -1.2);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 1.0, 0.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 0.0, 1.0, 0.15);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+ /* once more, without close_path this time */
+ cairo_move_to (cr, -1.0, -1.0);
+ cairo_rel_line_to (cr, +1.0, -0.2); /* / */
+ cairo_rel_line_to (cr, +1.0, +0.2); /* \ */
+ /* silly line_width is not locked :(. get rid of scale. */
+ cairo_restore (cr);
+ cairo_save (cr);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.7, 0.2);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+
+ /* draw block progression arrow in green */
+ cairo_save (cr);
+ cairo_translate (cr,
+ (double)logical.x / PANGO_SCALE,
+ (double)logical.y / PANGO_SCALE);
+ cairo_scale (cr,
+ (double)logical.width / PANGO_SCALE * 0.5,
+ (double)logical.height / PANGO_SCALE * 0.5);
+ cairo_translate (cr, 1.0, 1.0);
+ cairo_move_to (cr, -0.4, -0.7);
+ cairo_rel_line_to (cr, +0.8, 0.0); /* -- */
+ cairo_rel_line_to (cr, 0.0, +0.9); /* | */
+ cairo_rel_line_to (cr, +0.4, 0.0); /* - */
+ cairo_rel_line_to (cr, -0.8, +0.5); /* / */
+ cairo_rel_line_to (cr, -0.8, -0.5); /* \ */
+ cairo_rel_line_to (cr, +0.4, 0.0); /* - */
+ cairo_close_path (cr); /* | */
+ pattern = cairo_pattern_create_linear (0, -0.7, 0, 0.7);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 1.0, 0.0, 0.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 1.0, 0.0, 0.15);
+ cairo_set_source (cr, pattern);
+ cairo_fill_preserve (cr);
+ /* silly line_width is not locked :(. get rid of scale. */
+ cairo_restore (cr);
+ cairo_save (cr);
+ cairo_set_source_rgba (cr, 0.0, 0.7, 0.0, 0.2);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
+
+ /* draw baselines with line direction arrow in orange */
+ cairo_save (cr);
+ cairo_set_source_rgba (cr, 1.0, 0.5, 0.0, 0.5);
+ iter = pango_layout_get_iter (layout);
+ do
+ {
+ PangoLayoutLine *line = pango_layout_iter_get_line (iter);
+ double width = (double)logical.width / PANGO_SCALE;
+
+ y = pango_layout_iter_get_baseline (iter);
+ cairo_save (cr);
+ cairo_translate (cr,
+ (double)logical.x / PANGO_SCALE + width * 0.5,
+ (double)y / PANGO_SCALE);
+ if (line->resolved_dir)
+ cairo_scale (cr, -1, 1);
+ cairo_move_to (cr, -width * .5, -lw*0.2);
+ cairo_rel_line_to (cr, +width * .9, -lw*0.3);
+ cairo_rel_line_to (cr, 0, -lw);
+ cairo_rel_line_to (cr, +width * .1, +lw*1.5);
+ cairo_rel_line_to (cr, -width * .1, +lw*1.5);
+ cairo_rel_line_to (cr, 0, -lw);
+ cairo_rel_line_to (cr, -width * .9, -lw*0.3);
+ cairo_close_path (cr);
+ cairo_fill (cr);
+ cairo_restore (cr);
+ }
+ while (pango_layout_iter_next_line (iter));
+ pango_layout_iter_free (iter);
+ cairo_restore (cr);
+
+ /* draw the logical rect in red */
+ cairo_save (cr);
+ cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.5);
+ cairo_rectangle (cr,
+ (double)logical.x / PANGO_SCALE - lw / 2,
+ (double)logical.y / PANGO_SCALE - lw / 2,
+ (double)logical.width / PANGO_SCALE + lw,
+ (double)logical.height / PANGO_SCALE + lw);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ /* draw the ink rect in green */
+ cairo_save (cr);
+ cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 0.5);
+ cairo_rectangle (cr,
+ (double)ink.x / PANGO_SCALE - lw / 2,
+ (double)ink.y / PANGO_SCALE - lw / 2,
+ (double)ink.width / PANGO_SCALE + lw,
+ (double)ink.height / PANGO_SCALE + lw);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
+
+ cairo_move_to (cr, 0, 0);
+ pango_cairo_show_layout (cr, layout);
+
+ cairo_restore (cr);
+
+ cairo_surface_flush (cairo_get_target (cr));
+}
+
+static void
+transform_callback (PangoContext *context,
+ PangoMatrix *matrix,
+ gpointer cr_context,
+ gpointer state G_GNUC_UNUSED)
+{
+ cairo_t *cr = (cairo_t *)cr_context;
+ cairo_matrix_t cairo_matrix;
+
+ if (matrix)
+ {
+ cairo_matrix.xx = matrix->xx;
+ cairo_matrix.yx = matrix->yx;
+ cairo_matrix.xy = matrix->xy;
+ cairo_matrix.yy = matrix->yy;
+ cairo_matrix.x0 = matrix->x0;
+ cairo_matrix.y0 = matrix->y0;
+ }
+ else
+ {
+ cairo_matrix_init_identity (&cairo_matrix);
+ }
+
+ cairo_set_matrix (cr, &cairo_matrix);
+
+ pango_cairo_update_context (cr, context);
+}
+
+static void
+pangocairo_view_render (gpointer instance,
+ gpointer surface,
+ PangoContext *context,
+ int *width,
+ int *height,
+ gpointer state)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+ cairo_t *cr;
+ CairoSurface *c_surface = (CairoSurface *) surface;
+
+ g_assert (surface);
+
+ cr = cairo_create (c_surface->cairo);
+
+ transform_callback (context, NULL, cr, state);
+
+ c->iface->paint_background (instance, cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba (cr,
+ opt_fg_color.red / 65535.,
+ opt_fg_color.green / 65535.,
+ opt_fg_color.blue / 65535.,
+ opt_fg_alpha / 65535.);
+
+ do_output (context, render_callback, transform_callback, cr, state, width, height);
+
+ cairo_destroy (cr);
+}
+
+#ifdef HAVE_CAIRO_PNG
+static cairo_status_t
+write_func (void *closure,
+ const unsigned char *data,
+ unsigned int length)
+{
+ FILE *stream = (FILE *) closure;
+ unsigned int l;
+
+ l = fwrite (data, 1, length, stream);
+
+ return l == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
+}
+
+static void
+pangocairo_view_write (gpointer instance G_GNUC_UNUSED,
+ gpointer surface,
+ FILE *stream,
+ int width G_GNUC_UNUSED,
+ int height G_GNUC_UNUSED)
+{
+ CairoSurface *c_surface = (CairoSurface *) surface;
+
+ cairo_surface_write_to_png_stream (c_surface->cairo, write_func, stream);
+}
+#endif
+
+static gpointer
+pangocairo_view_create_window (gpointer instance,
+ const char *title,
+ int width,
+ int height)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+
+ if (!c->iface->backend_class->create_window)
+ return NULL;
+
+ return c->iface->backend_class->create_window (c->backend,
+ title,
+ width, height);
+}
+
+static void
+pangocairo_view_destroy_window (gpointer instance,
+ gpointer window)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+
+ c->iface->backend_class->destroy_window (c->backend,
+ window);
+}
+
+static gpointer
+pangocairo_view_display (gpointer instance,
+ gpointer surface,
+ gpointer window,
+ int width, int height,
+ gpointer state)
+{
+ CairoViewer *c = (CairoViewer *) instance;
+ CairoSurface *c_surface = (CairoSurface *) surface;
+
+ return c->iface->backend_class->display (c->backend,
+ c_surface->backend,
+ window,
+ width, height,
+ state);
+}
+
+static GOptionGroup *
+pangocairo_view_get_option_group (const PangoViewer *klass G_GNUC_UNUSED)
+{
+ GOptionEntry entries[] =
+ {
+ {"annotate", 0, 0, G_OPTION_ARG_INT, &opt_annotate,
+ "Annotate the output", "1 or 2"},
+ {NULL}
+ };
+ GOptionGroup *group;
+
+ group = g_option_group_new ("cairo",
+ "Cairo backend options:",
+ "Options understood by the cairo backend",
+ NULL,
+ NULL);
+
+ g_option_group_add_entries (group, entries);
+
+ cairo_viewer_add_options (group);
+
+ return group;
+}
+
+const PangoViewer pangocairo_viewer = {
+ "PangoCairo",
+ "cairo",
+#ifdef HAVE_CAIRO_PNG
+ "png",
+#else
+ NULL,
+#endif
+ pangocairo_view_create,
+ pangocairo_view_destroy,
+ pangocairo_view_get_context,
+ pangocairo_view_create_surface,
+ pangocairo_view_destroy_surface,
+ pangocairo_view_render,
+#ifdef HAVE_CAIRO_PNG
+ pangocairo_view_write,
+#else
+ NULL,
+#endif
+ pangocairo_view_create_window,
+ pangocairo_view_destroy_window,
+ pangocairo_view_display,
+ NULL,
+ NULL,
+ pangocairo_view_get_option_group
+};