diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2018-07-08 14:45:32 +0200 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2018-07-08 14:45:32 +0200 |
commit | 899841121260d62fc3f1c1728f82f5c02185dd97 (patch) | |
tree | 643abf1f58c3b627b786d3b89a103a0df956172f /utils/viewer-render.c | |
parent | 5ec478dac9146175de7ba4a1f88ebbab963f4c0f (diff) | |
download | pango-899841121260d62fc3f1c1728f82f5c02185dd97.tar.gz |
Rename pango-view/ dir to utils/
Diffstat (limited to 'utils/viewer-render.c')
-rw-r--r-- | utils/viewer-render.c | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/utils/viewer-render.c b/utils/viewer-render.c new file mode 100644 index 00000000..573cb59a --- /dev/null +++ b/utils/viewer-render.c @@ -0,0 +1,823 @@ +/* viewer-render.c: Common code for rendering in viewers + * + * Copyright (C) 1999, 2004 Red Hat Software + * 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 <errno.h> +#include <math.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <pango/pango.h> + +#include "viewer-render.h" + +gboolean opt_display = TRUE; +int opt_dpi = 96; +gboolean opt_pixels = FALSE; +const char *opt_font = ""; +gboolean opt_header = FALSE; +const char *opt_output = NULL; +int opt_margin_t = 10; +int opt_margin_r = 10; +int opt_margin_b = 10; +int opt_margin_l = 10; +int opt_markup = FALSE; +gboolean opt_rtl = FALSE; +double opt_rotate = 0; +gboolean opt_auto_dir = TRUE; +const char *opt_text = NULL; +gboolean opt_waterfall = FALSE; +int opt_width = -1; +int opt_height = -1; +int opt_indent = 0; +gboolean opt_justify = 0; +int opt_runs = 1; +PangoAlignment opt_align = PANGO_ALIGN_LEFT; +PangoEllipsizeMode opt_ellipsize = PANGO_ELLIPSIZE_NONE; +PangoGravity opt_gravity = PANGO_GRAVITY_SOUTH; +PangoGravityHint opt_gravity_hint = PANGO_GRAVITY_HINT_NATURAL; +HintMode opt_hinting = HINT_DEFAULT; +PangoWrapMode opt_wrap = PANGO_WRAP_WORD_CHAR; +gboolean opt_wrap_set = FALSE; +static const char *opt_pangorc = NULL; /* Unused */ +const PangoViewer *opt_viewer = NULL; +const char *opt_language = NULL; +gboolean opt_single_par = FALSE; +PangoColor opt_fg_color = {0, 0, 0}; +guint16 opt_fg_alpha = 65535; +gboolean opt_bg_set = FALSE; +PangoColor opt_bg_color = {65535, 65535, 65535}; +guint16 opt_bg_alpha = 65535; + +/* Text (or markup) to render */ +static char *text; + +void +fail (const char *format, ...) +{ + const char *msg; + + va_list vap; + va_start (vap, format); + msg = g_strdup_vprintf (format, vap); + g_printerr ("%s: %s\n", g_get_prgname (), msg); + + exit (1); +} + +static PangoLayout * +make_layout(PangoContext *context, + const char *text, + double size) +{ + static PangoFontDescription *font_description; + PangoAlignment align; + PangoLayout *layout; + + layout = pango_layout_new (context); + if (opt_markup) + pango_layout_set_markup (layout, text, -1); + else + pango_layout_set_text (layout, text, -1); + + pango_layout_set_auto_dir (layout, opt_auto_dir); + pango_layout_set_ellipsize (layout, opt_ellipsize); + pango_layout_set_justify (layout, opt_justify); + pango_layout_set_single_paragraph_mode (layout, opt_single_par); + pango_layout_set_wrap (layout, opt_wrap); + + font_description = pango_font_description_from_string (opt_font); + if (size > 0) + pango_font_description_set_size (font_description, size * PANGO_SCALE); + + if (opt_width > 0) + pango_layout_set_width (layout, (opt_width * opt_dpi * PANGO_SCALE + 36) / 72); + + if (opt_height > 0) + pango_layout_set_height (layout, (opt_height * opt_dpi * PANGO_SCALE + 36) / 72); + else + pango_layout_set_height (layout, opt_height); + + if (opt_indent != 0) + pango_layout_set_indent (layout, (opt_indent * opt_dpi * PANGO_SCALE + 36) / 72); + + align = opt_align; + if (align != PANGO_ALIGN_CENTER && + pango_context_get_base_dir (context) != PANGO_DIRECTION_LTR) { + /* pango reverses left and right if base dir ir rtl. so we should + * reverse to cancel that. unfortunately it also does that for + * rtl paragraphs, so we cannot really get left/right. all we get + * is default/other-side. */ + align = PANGO_ALIGN_LEFT + PANGO_ALIGN_RIGHT - align; + } + pango_layout_set_alignment (layout, align); + + pango_layout_set_font_description (layout, font_description); + + pango_font_description_free (font_description); + + return layout; +} + +gchar * +get_options_string (void) +{ + PangoFontDescription *font_description = pango_font_description_from_string (opt_font); + gchar *font_name; + gchar *result; + + if (opt_waterfall) + pango_font_description_unset_fields (font_description, PANGO_FONT_MASK_SIZE); + + font_name = pango_font_description_to_string (font_description); + result = g_strdup_printf ("%s: %s (%d dpi)", opt_viewer->name, font_name, opt_dpi); + pango_font_description_free (font_description); + g_free (font_name); + + return result; +} + +static void +output_body (PangoLayout *layout, + RenderCallback render_cb, + gpointer cb_context, + gpointer cb_data, + int *width, + int *height, + gboolean supports_matrix) +{ + PangoRectangle logical_rect; + int size, start_size, end_size, increment; + int x = 0, y = 0; + + if (!supports_matrix) + { + const PangoMatrix* matrix; + const PangoMatrix identity = PANGO_MATRIX_INIT; + PangoContext *context = pango_layout_get_context (layout); + matrix = pango_context_get_matrix (context); + if (matrix) + { + x += matrix->x0; + y += matrix->y0; + } + pango_context_set_matrix (context, &identity); + pango_layout_context_changed (layout); + } + + if (opt_waterfall) + { + start_size = 8; + end_size = 48; + increment = 4; + } + else + { + start_size = end_size = -1; + increment = 1; + } + + *width = 0; + *height = 0; + + for (size = start_size; size <= end_size; size += increment) + { + if (size > 0) + { + PangoFontDescription *desc = pango_font_description_copy (pango_layout_get_font_description (layout)); + pango_font_description_set_size (desc, size * PANGO_SCALE); + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + } + + pango_layout_get_pixel_extents (layout, NULL, &logical_rect); + + if (render_cb) + (*render_cb) (layout, x, y+*height, cb_context, cb_data); + + *width = MAX (*width, + MAX (logical_rect.x + logical_rect.width, + PANGO_PIXELS (pango_layout_get_width (layout)))); + *height += MAX (logical_rect.y + logical_rect.height, + PANGO_PIXELS (pango_layout_get_height (layout))); + } +} + +static void +set_transform (PangoContext *context, + TransformCallback transform_cb, + gpointer cb_context, + gpointer cb_data, + PangoMatrix *matrix) +{ + pango_context_set_matrix (context, matrix); + if (transform_cb) + (*transform_cb) (context, matrix, cb_context, cb_data); +} + +void +do_output (PangoContext *context, + RenderCallback render_cb, + TransformCallback transform_cb, + gpointer cb_context, + gpointer cb_data, + int *width_out, + int *height_out) +{ + PangoLayout *layout; + PangoRectangle rect; + PangoMatrix matrix = PANGO_MATRIX_INIT; + PangoMatrix *orig_matrix; + gboolean supports_matrix; + int rotated_width, rotated_height; + int x = opt_margin_l; + int y = opt_margin_t; + int width, height; + + width = 0; + height = 0; + + orig_matrix = pango_matrix_copy (pango_context_get_matrix (context)); + /* If the backend sets an all-zero matrix on the context, + * means that it doesn't support transformations. + */ + supports_matrix = !orig_matrix || + (orig_matrix->xx != 0. || orig_matrix->xy != 0. || + orig_matrix->yx != 0. || orig_matrix->yy != 0. || + orig_matrix->x0 != 0. || orig_matrix->y0 != 0.); + + set_transform (context, transform_cb, cb_context, cb_data, NULL); + + pango_context_set_language (context, + opt_language ? pango_language_from_string (opt_language) + : pango_language_get_default ()); + pango_context_set_base_dir (context, + opt_rtl ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR); + + if (opt_header) + { + char *options_string = get_options_string (); + pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH); + layout = make_layout (context, options_string, 10); + pango_layout_get_extents (layout, NULL, &rect); + + width = MAX (width, PANGO_PIXELS (rect.width)); + height += PANGO_PIXELS (rect.height); + + if (render_cb) + (*render_cb) (layout, x, y, cb_context, cb_data); + + y += PANGO_PIXELS (rect.height); + + g_object_unref (layout); + g_free (options_string); + } + + if (opt_rotate != 0) + { + if (supports_matrix) + pango_matrix_rotate (&matrix, opt_rotate); + else + g_printerr ("The backend does not support rotated text\n"); + } + + pango_context_set_base_gravity (context, opt_gravity); + pango_context_set_gravity_hint (context, opt_gravity_hint); + + layout = make_layout (context, text, -1); + + set_transform (context, transform_cb, cb_context, cb_data, &matrix); + + output_body (layout, + NULL, NULL, NULL, + &rotated_width, &rotated_height, + supports_matrix); + + rect.x = rect.y = 0; + rect.width = rotated_width; + rect.height = rotated_height; + + pango_matrix_transform_pixel_rectangle (&matrix, &rect); + + matrix.x0 = x - rect.x; + matrix.y0 = y - rect.y; + + set_transform (context, transform_cb, cb_context, cb_data, &matrix); + + if (render_cb) + output_body (layout, + render_cb, cb_context, cb_data, + &rotated_width, &rotated_height, + supports_matrix); + + width = MAX (width, rect.width); + height += rect.height; + + width += opt_margin_l + opt_margin_r; + height += opt_margin_t + opt_margin_b; + + if (width_out) + *width_out = width; + if (height_out) + *height_out = height; + + pango_context_set_matrix (context, orig_matrix); + pango_matrix_free (orig_matrix); + g_object_unref (layout); +} + +static gboolean +parse_enum (GType type, + int *value, + const char *name, + const char *arg, + gpointer data G_GNUC_UNUSED, + GError **error) +{ + char *possible_values = NULL; + gboolean ret; + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + ret = pango_parse_enum (type, + arg, + value, + FALSE, + &possible_values); +G_GNUC_END_IGNORE_DEPRECATIONS + + if (!ret && error) + { + g_set_error(error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + "Argument for %s must be one of %s", + name, + possible_values); + } + + g_free (possible_values); + + return ret; +} + +static gboolean +parse_align (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + return parse_enum (PANGO_TYPE_ALIGNMENT, (int*)(void*)&opt_align, + name, arg, data, error); +} + +static gboolean +parse_ellipsis (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + return parse_enum (PANGO_TYPE_ELLIPSIZE_MODE, (int*)(void*)&opt_ellipsize, + name, arg, data, error); +} + +static gboolean +parse_gravity (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + return parse_enum (PANGO_TYPE_GRAVITY, (int*)(void*)&opt_gravity, + name, arg, data, error); +} + +static gboolean +parse_gravity_hint (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + return parse_enum (PANGO_TYPE_GRAVITY_HINT, (int*)(void*)&opt_gravity_hint, + name, arg, data, error); +} + +static gboolean +parse_hinting (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data G_GNUC_UNUSED, + GError **error) +{ + gboolean ret = TRUE; + + if (strcmp (arg, "none") == 0) + opt_hinting = HINT_NONE; + else if (strcmp (arg, "auto") == 0) + opt_hinting = HINT_AUTO; + else if (strcmp (arg, "full") == 0) + opt_hinting = HINT_FULL; + else + { + g_set_error(error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + "Argument for --hinting must be one of none/auto/full"); + ret = FALSE; + } + + return ret; +} + +static gboolean +parse_wrap (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + gboolean ret; + if ((ret = parse_enum (PANGO_TYPE_WRAP_MODE, (int*)(void*)&opt_wrap, + name, arg, data, error))) + { + opt_wrap_set = TRUE; + } + return ret; +} + +static gboolean +parse_rgba_color (PangoColor *color, + guint16 *alpha, + const char *name, + const char *arg, + gpointer data G_GNUC_UNUSED, + GError **error) +{ + gboolean ret; + char buf[32]; + int len; + + len = strlen (arg); + /* handle alpha */ + if (*arg == '#' && (len == 5 || len == 9 || len == 17)) + { + int width, bits; + unsigned int a; + + bits = len - 1; + width = bits >> 2; + + strcpy (buf, arg); + arg = buf; + + if (!sscanf (buf + len - width, "%x", &a)) + { + ret = FALSE; + goto err; + } + buf[len - width] = '\0'; + + a <<= (16 - bits); + while (bits < 16) + { + a |= (a >> bits); + bits *= 2; + } + *alpha = a; + } + else + *alpha = 65535; + + ret = pango_color_parse (color, arg); + +err: + if (!ret && error) + { + g_set_error(error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + "Argument for %s must be a color name like red, or CSS-style #rrggbb / #rrggbbaa", + name); + } + + return ret; +} + +static gboolean +parse_foreground (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + return parse_rgba_color (&opt_fg_color, &opt_fg_alpha, + name, arg, data, error); +} + +static gboolean +parse_background (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + opt_bg_set = TRUE; + + if (0 == strcmp ("transparent", arg)) + { + opt_bg_alpha = 0; + return TRUE; + } + + return parse_rgba_color (&opt_bg_color, &opt_bg_alpha, + name, arg, data, error); +} + +static gboolean +parse_margin (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data G_GNUC_UNUSED, + GError **error) +{ + switch (sscanf (arg, "%d%*[ ,]%d%*[ ,]%d%*[ ,]%d", &opt_margin_t, &opt_margin_r, &opt_margin_b, &opt_margin_l)) + { + case 0: + { + g_set_error(error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + "Argument for --margin must be one to four space-separated numbers"); + return FALSE; + } + case 1: opt_margin_r = opt_margin_t; + case 2: opt_margin_b = opt_margin_t; + case 3: opt_margin_l = opt_margin_r; + } + return TRUE; +} + + +static gchar * +backends_to_string (void) +{ + GString *backends = g_string_new (NULL); + const PangoViewer **viewer; + + for (viewer = viewers; *viewer; viewer++) + if ((*viewer)->id) + { + g_string_append (backends, (*viewer)->id); + g_string_append_c (backends, '/'); + } + g_string_truncate (backends, MAX (0, (gint)backends->len - 1)); + + return g_string_free(backends,FALSE); +} + +static int +backends_get_count (void) +{ + const PangoViewer **viewer; + int i = 0; + + for (viewer = viewers; *viewer; viewer++) + if ((*viewer)->id) + i++; + + return i; +} + + +static gchar * +backend_description (void) +{ + GString *description = g_string_new("Pango backend to use for rendering "); + int backends_count = backends_get_count (); + + if (backends_count > 1) + g_string_append_printf(description,"(default: %s)", (*viewers)->id); + else if (backends_count == 1) + g_string_append_printf(description,"(only available: %s)", (*viewers)->id); + else + g_string_append_printf(description,"(no backends found!)"); + + return g_string_free(description,FALSE); + +} + +static gboolean +parse_backend (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data G_GNUC_UNUSED, + GError **error) +{ + gboolean ret = TRUE; + const PangoViewer **viewer; + + for (viewer = viewers; *viewer; viewer++) + if (!g_ascii_strcasecmp ((*viewer)->id, arg)) + break; + + if (*viewer) + opt_viewer = *viewer; + else + { + gchar *backends = backends_to_string (); + + g_set_error(error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + "Available --backend options are: %s", + backends); + g_free(backends); + ret = FALSE; + } + + return ret; +} + + +static G_GNUC_NORETURN gboolean +show_version(const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + g_printf("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION); + + if (PANGO_VERSION != pango_version()) + g_printf("Linked Pango library has a different version: %s\n", pango_version_string ()); + + exit(0); +} + +void +parse_options (int argc, char *argv[]) +{ + gchar *backend_options = backends_to_string (); + GOptionFlags backend_flag = backends_get_count () > 1 ? 0 : G_OPTION_FLAG_HIDDEN; + gchar *backend_desc = backend_description (); + GOptionEntry entries[] = + { + {"no-auto-dir", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_auto_dir, + "No layout direction according to contents", NULL}, + {"backend", 0, backend_flag, G_OPTION_ARG_CALLBACK, &parse_backend, + backend_desc, backend_options}, + {"background", 0, 0, G_OPTION_ARG_CALLBACK, &parse_background, + "Set the background color", "red/#rrggbb/#rrggbbaa/transparent"}, + {"no-display", 'q', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_display, + "Do not display (just write to file or whatever)", NULL}, + {"dpi", 0, 0, G_OPTION_ARG_INT, &opt_dpi, + "Set the resolution", "number"}, + {"align", 0, 0, G_OPTION_ARG_CALLBACK, &parse_align, + "Text alignment", "left/center/right"}, + {"ellipsize", 0, 0, G_OPTION_ARG_CALLBACK, &parse_ellipsis, + "Ellipsization mode", "start/middle/end"}, + {"font", 0, 0, G_OPTION_ARG_STRING, &opt_font, + "Set the font description", "description"}, + {"foreground", 0, 0, G_OPTION_ARG_CALLBACK, &parse_foreground, + "Set the text color", "red/#rrggbb/#rrggbbaa"}, + {"gravity", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity, + "Base gravity: glyph rotation", "south/east/north/west/auto"}, + {"gravity-hint", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity_hint, + "Gravity hint", "natural/strong/line"}, + {"header", 0, 0, G_OPTION_ARG_NONE, &opt_header, + "Display the options in the output", NULL}, + {"height", 0, 0, G_OPTION_ARG_INT, &opt_height, + "Height in points (positive) or number of lines (negative) for ellipsizing", "+points/-numlines"}, + {"hinting", 0, 0, G_OPTION_ARG_CALLBACK, &parse_hinting, + "Hinting style", "none/auto/full"}, + {"indent", 0, 0, G_OPTION_ARG_INT, &opt_indent, + "Width in points to indent paragraphs", "points"}, + {"justify", 0, 0, G_OPTION_ARG_NONE, &opt_justify, + "Align paragraph lines to be justified", NULL}, + {"language", 0, 0, G_OPTION_ARG_STRING, &opt_language, + "Language to use for font selection", "en_US/etc"}, + {"margin", 0, 0, G_OPTION_ARG_CALLBACK, &parse_margin, + "Set the margin on the output in pixels", "CSS-style numbers in pixels"}, + {"markup", 0, 0, G_OPTION_ARG_NONE, &opt_markup, + "Interpret text as Pango markup", NULL}, + {"output", 'o', 0, G_OPTION_ARG_STRING, &opt_output, + "Save rendered image to output file", "file"}, + {"pangorc", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &opt_pangorc, + "Deprecated", "file"}, + {"pixels", 0, 0, G_OPTION_ARG_NONE, &opt_pixels, + "Use pixel units instead of points (sets dpi to 72)", NULL}, + {"rtl", 0, 0, G_OPTION_ARG_NONE, &opt_rtl, + "Set base direction to right-to-left", NULL}, + {"rotate", 0, 0, G_OPTION_ARG_DOUBLE, &opt_rotate, + "Angle at which to rotate results", "degrees"}, + {"runs", 'n', 0, G_OPTION_ARG_INT, &opt_runs, + "Run Pango layout engine this many times", "integer"}, + {"single-par", 0, 0, G_OPTION_ARG_NONE, &opt_single_par, + "Enable single-paragraph mode", NULL}, + {"text", 't', 0, G_OPTION_ARG_STRING, &opt_text, + "Text to display (instead of a file)", "string"}, + {"version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, &show_version, + "Show version numbers", NULL}, + {"waterfall", 0, 0, G_OPTION_ARG_NONE, &opt_waterfall, + "Create a waterfall display", NULL}, + {"width", 'w', 0, G_OPTION_ARG_INT, &opt_width, + "Width in points to which to wrap lines or ellipsize", "points"}, + {"wrap", 0, 0, G_OPTION_ARG_CALLBACK, &parse_wrap, + "Text wrapping mode (needs a width to be set)", "word/char/word-char"}, + {NULL} + }; + GError *error = NULL; + GError *parse_error = NULL; + GOptionContext *context; + size_t len; + const PangoViewer **viewer; + + context = g_option_context_new ("- FILE"); + g_option_context_add_main_entries (context, entries, NULL); + + for (viewer = viewers; *viewer; viewer++) + if ((*viewer)->get_option_group) + { + GOptionGroup *group = (*viewer)->get_option_group (*viewer); + if (group) + g_option_context_add_group (context, group); + } + + if (!g_option_context_parse (context, &argc, &argv, &parse_error)) + { + if (parse_error != NULL) + fail("%s", parse_error->message); + else + fail("Option parse error"); + exit(1); + } + g_option_context_free(context); + g_free(backend_options); + g_free(backend_desc); + + if (opt_pixels) + opt_dpi = 72; + + if ((opt_text && argc != 1) || (!opt_text && argc != 2)) + { + if (opt_text && argc != 1) + fail ("When specifying --text, no file should be given"); + + g_printerr ("Usage: %s [OPTION...] FILE\n", g_get_prgname ()); + exit (1); + } + + /* set up the backend */ + if (!opt_viewer) + { + opt_viewer = *viewers; + if (!opt_viewer) + fail ("No viewer backend found"); + } + + /* Get the text + */ + if (opt_text) + { + text = g_strdup (opt_text); + len = strlen (text); + } + else + { + if (!g_file_get_contents (argv[1], &text, &len, &error)) + fail ("%s\n", error->message); + } + + /* Strip one trailing newline + */ + if (len > 0 && text[len - 1] == '\n') + len--; + if (len > 0 && text[len - 1] == '\r') + len--; + text[len] = '\0'; + + /* Make sure we have valid markup + */ + if (opt_markup && + !pango_parse_markup (text, -1, 0, NULL, NULL, NULL, &error)) + fail ("Cannot parse input as markup: %s", error->message); +} + + +void +finalize (void) +{ + g_free (text); +} |