diff options
author | Owen Taylor <otaylor@redhat.com> | 2003-09-15 19:54:39 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2003-09-15 19:54:39 +0000 |
commit | 0bb8e3c03a4b0de57366cdde64a2b35cb55ed216 (patch) | |
tree | bce4b1174634ff36c6f32b13b50d90ef06ddc9ad /examples | |
parent | 48254364ad24eaa6d34f4f58a2f960695a80b8d0 (diff) | |
download | pango-0bb8e3c03a4b0de57366cdde64a2b35cb55ed216.tar.gz |
Borrow argument parsing code from GDK.
Mon Sep 15 15:48:25 2003 Owen Taylor <otaylor@redhat.com>
* examples/Makefile.am examples/argcontext.c examples/argcontext.h:
Borrow argument parsing code from GDK.
* examples/pangoft2topgm.c: Use argcontext.[ch]. Add options
--header to display options in a header in the image, --text
to pass the text on the command line, --width to set a
wrap width. Add autoconversion to non-PGM output formats
via 'convert' for --output="foo.png" etc.
Diffstat (limited to 'examples')
-rw-r--r-- | examples/Makefile.am | 5 | ||||
-rw-r--r-- | examples/argcontext.c | 248 | ||||
-rw-r--r-- | examples/argcontext.h | 76 | ||||
-rw-r--r-- | examples/pangoft2topgm.c | 298 | ||||
-rw-r--r-- | examples/renderdemo.c | 298 |
5 files changed, 700 insertions, 225 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am index 9b451564..f13b957d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -21,7 +21,10 @@ if HAVE_FREETYPE ft2_programs = pangoft2topgm endif -pangoft2topgm_SOURCES = pangoft2topgm.c +pangoft2topgm_SOURCES = \ + pangoft2topgm.c \ + argcontext.c \ + argcontext.h pangoft2topgm_LDADD = \ ../pango/libpango-$(PANGO_API_VERSION).la \ ../pango/libpangoft2-$(PANGO_API_VERSION).la \ diff --git a/examples/argcontext.c b/examples/argcontext.c new file mode 100644 index 00000000..7160266c --- /dev/null +++ b/examples/argcontext.c @@ -0,0 +1,248 @@ +/* argcontext.c - Simple command line argument parsing + * Copyright 1999, 2003 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "argcontext.h" + +struct _ArgContext +{ + GPtrArray *tables; + gpointer cb_data; +}; + +GQuark +arg_context_error_quark (void) +{ + static GQuark q = 0; + if (q == 0) + q = g_quark_from_static_string ("arg-context-error-quark"); + + return q; +} + +ArgContext * +arg_context_new (gpointer cb_data) +{ + ArgContext *result = g_new (ArgContext, 1); + result->tables = g_ptr_array_new (); + result->cb_data = cb_data; + + return result; +} + +void +arg_context_free (ArgContext *context) +{ + g_ptr_array_free (context->tables, TRUE); + g_free (context); +} + +void +arg_context_add_table (ArgContext *context, + const ArgDesc *table) +{ + g_ptr_array_add (context->tables, (ArgDesc *)table); +} + +static gboolean +parse_int (const char *arg_name, + const char *arg, + gint *result, + GError **error) +{ + gchar *end; + glong tmp = strtol (arg, &end, 0); + + errno = 0; + + if (*arg == '\0' || *end != '\0') + { + g_set_error (error, + ARG_CONTEXT_ERROR, ARG_CONTEXT_ERROR_BAD_VALUE, + "Cannot parse integer value '%s' for --%s", + arg, arg_name); + return FALSE; + } + + *result = tmp; + if (*result != tmp || errno == ERANGE) + { + g_set_error (error, + ARG_CONTEXT_ERROR, ARG_CONTEXT_ERROR_BAD_VALUE, + "Integer value '%s' for %s out of range", + arg, arg_name); + return FALSE; + } + + return TRUE; +} + +void +arg_context_print_help (ArgContext *context) +{ + int j, k; + int max_name_len = 0; + + for (j = 0; j < context->tables->len; j++) + { + ArgDesc *table = context->tables->pdata[j]; + for (k = 0; table[k].name; k++) + max_name_len = MAX (strlen (table[k].name), max_name_len); + } + + for (j = 0; j < context->tables->len; j++) + { + ArgDesc *table = context->tables->pdata[j]; + for (k = 0; table[k].name; k++) + g_print (" --%-*s %s\n", + max_name_len, table[k].name, + table[k].help ? table[k].help : ""); + } +} + +gboolean +arg_context_parse (ArgContext *context, + gint *argc, + gchar ***argv, + GError **error) +{ + int i, j, k; + + if (argc && argv) + { + for (i = 1; i < *argc; i++) + { + char *arg; + + if ((*argv)[i][0] == '-' && (*argv)[i][1] != '-') + { + g_set_error (error, + ARG_CONTEXT_ERROR, ARG_CONTEXT_ERROR_UNKNOWN_OPTION, + "Unknown option %s", (*argv)[i]); + return FALSE; + } + + if (!((*argv)[i][0] == '-' && (*argv)[i][1] == '-')) + continue; + + arg = (*argv)[i] + 2; + + /* '--' terminates list of arguments */ + if (arg == 0) + { + (*argv)[i] = NULL; + break; + } + + for (j = 0; j < context->tables->len; j++) + { + ArgDesc *table = context->tables->pdata[j]; + for (k = 0; table[k].name; k++) + { + switch (table[k].type) + { + case ARG_STRING: + case ARG_CALLBACK: + case ARG_INT: + { + int len = strlen (table[k].name); + + if (strncmp (arg, table[k].name, len) == 0 && + (arg[len] == '=' || arg[len] == 0)) + { + char *value = NULL; + + (*argv)[i] = NULL; + + if (arg[len] == '=') + value = arg + len + 1; + else if (i < *argc - 1) + { + value = (*argv)[i + 1]; + (*argv)[i+1] = NULL; + i++; + } + else + value = ""; + + switch (table[k].type) + { + case ARG_STRING: + *(gchar **)table[k].location = value; + break; + case ARG_INT: + if (!parse_int (table[k].name, value, + (gint *)table[k].location, + error)) + return FALSE; + break; + case ARG_CALLBACK: + (*table[k].callback)(context, table[k].name, value, context->cb_data); + break; + default: + ; + } + + goto next_arg; + } + break; + } + case ARG_BOOL: + case ARG_NOBOOL: + if (strcmp (arg, table[k].name) == 0) + { + (*argv)[i] = NULL; + + *(gboolean *)table[k].location = (table[k].type == ARG_BOOL) ? TRUE : FALSE; + goto next_arg; + } + } + } + } + g_set_error (error, + ARG_CONTEXT_ERROR, ARG_CONTEXT_ERROR_UNKNOWN_OPTION, + "Unknown option --%s", + arg); + return FALSE; + + next_arg: + ; + } + + for (i = 1; i < *argc; i++) + { + for (k = i; k < *argc; k++) + if ((*argv)[k] != NULL) + break; + + if (k > i) + { + k -= i; + for (j = i + k; j < *argc; j++) + (*argv)[j-k] = (*argv)[j]; + *argc -= k; + } + } + } + + return TRUE; +} diff --git a/examples/argcontext.h b/examples/argcontext.h new file mode 100644 index 00000000..9749c8b2 --- /dev/null +++ b/examples/argcontext.h @@ -0,0 +1,76 @@ +/* argcontext.h - Simple command line argument parsing + * Copyright 1999, 2003 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + */ + +#ifndef __ARG_CONTEXT_H__ +#define __ARG_CONTEXT_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef enum +{ + ARG_STRING, + ARG_INT, + ARG_BOOL, + ARG_NOBOOL, + ARG_CALLBACK +} ArgType; + +typedef struct _ArgContext ArgContext; +typedef struct _ArgDesc ArgDesc; + +#define ARG_CONTEXT_ERROR arg_context_error_quark () + +typedef enum +{ + ARG_CONTEXT_ERROR_UNKNOWN_OPTION, + ARG_CONTEXT_ERROR_BAD_VALUE, + ARG_CONTEXT_ERROR_FAILED +} ArgContextError; + +typedef void (*ArgFunc) (ArgContext *arg_context, + const char *name, + const char *arg, + gpointer data); + +struct _ArgDesc +{ + const char *name; + const char *help; + ArgType type; + gpointer location; + ArgFunc callback; +}; + +GQuark arg_context_error_quark (void); + +ArgContext *arg_context_new (gpointer cb_data); +void arg_context_free (ArgContext *context); +void arg_context_print_help (ArgContext *context); +void arg_context_add_table (ArgContext *context, + const ArgDesc *table); +gboolean arg_context_parse (ArgContext *context, + gint *argc, + gchar ***argv, + GError **error); + +G_END_DECLS + +#endif /* __ARG_CONTEXT_H__ */ diff --git a/examples/pangoft2topgm.c b/examples/pangoft2topgm.c index 257dc8bb..f223f89e 100644 --- a/examples/pangoft2topgm.c +++ b/examples/pangoft2topgm.c @@ -24,10 +24,7 @@ #define BUFSIZE 1024 #define MALLOCSIZE 1024 #define DEFAULT_FONT_FAMILY "Sans" -#define DEFAULT_FONT_SIZE 36 - -#include <pango/pango.h> -#include <pango/pangoft2.h> +#define DEFAULT_FONT_SIZE 18 #include <errno.h> #include <stdarg.h> @@ -35,22 +32,31 @@ #include <stdio.h> #include <string.h> -static char *prog_name; -static PangoContext *context; +#include "argcontext.h" -static gboolean opt_display = FALSE; -static int opt_dpi = 96; +#include <pango/pango.h> +#include <pango/pangoft2.h> #define _MAKE_FONT_NAME(family, size) family " " #size #define MAKE_FONT_NAME(family, size) _MAKE_FONT_NAME(family, size) -static char *opt_font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE); -static PangoDirection opt_dir = PANGO_DIRECTION_LTR; +static char *prog_name; +static PangoContext *context; + +static char *tmpfile_name; static char *outfile_name; + +static gboolean opt_display = FALSE; +static int opt_dpi = 96; +static char *opt_font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE); +static gboolean opt_header = FALSE; static char *opt_output = NULL; static int opt_margin = 10; static int opt_markup = FALSE; +static gboolean opt_rtl = FALSE; +static char *opt_text = NULL; static gboolean opt_waterfall = FALSE; +static int opt_width = -1; static void fail (const char *format, ...) G_GNUC_PRINTF (1, 2); @@ -70,6 +76,20 @@ fail (const char *format, ...) exit (1); } +PangoFontDescription * +get_font_description (void) +{ + PangoFontDescription *font_description = pango_font_description_from_string (opt_font); + + if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_FAMILY) == 0) + pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); + + if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_SIZE) == 0) + pango_font_description_set_size (font_description, DEFAULT_FONT_SIZE * PANGO_SCALE); + + return font_description; +} + static PangoLayout * make_layout(PangoContext *context, const char *text, @@ -85,26 +105,42 @@ make_layout(PangoContext *context, else pango_layout_set_text (layout, text, -1); - font_description = pango_font_description_from_string (opt_font); - - if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_FAMILY) == 0) - pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); - + font_description = get_font_description (); if (size > 0) pango_font_description_set_size (font_description, size * PANGO_SCALE); - else if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_SIZE) == 0) - pango_font_description_set_size (font_description, DEFAULT_FONT_SIZE * PANGO_SCALE); + if (opt_width > 0) + pango_layout_set_width (layout, (opt_width * opt_dpi * PANGO_SCALE + 32) / 72); + base_dir = pango_context_get_base_dir (context); pango_layout_set_alignment (layout, base_dir == PANGO_DIRECTION_LTR ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); pango_layout_set_font_description (layout, font_description); + pango_font_description_free (font_description); return layout; } +static gchar * +get_options_string (void) +{ + PangoFontDescription *font_description = get_font_description (); + 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: dpi=%d", font_name, opt_dpi); + pango_font_description_free (font_description); + g_free (font_name); + + return result; +} + static void do_output (PangoContext *context, const char *text, @@ -121,6 +157,24 @@ do_output (PangoContext *context, *width = 0; *height = 0; + if (opt_header) + { + char *options_string = get_options_string (); + layout = make_layout (context, options_string, 10); + pango_layout_get_extents (layout, NULL, &logical_rect); + + *width = MAX (*width, PANGO_PIXELS (logical_rect.width)); + *height += PANGO_PIXELS (logical_rect.height); + + if (bitmap) + pango_ft2_render_layout (bitmap, layout, x, y); + + y += PANGO_PIXELS (logical_rect.height); + + g_object_unref (layout); + g_free (options_string); + } + if (opt_waterfall) { start_size = 8; @@ -153,17 +207,21 @@ do_output (PangoContext *context, *height += 2 * opt_margin; } -int int_arg (const char *arg_name, const char *arg) +static void +show_help (ArgContext *context, + const char *name, + const char *arg, + gpointer data) { - char *end; - long result = strtol (arg, &end, 0); - if (*arg == '\0' || *end != '\0') - { - fail ("Cannot parse integer value '%s' for %s.", - arg, arg_name); - } - - return result; + g_print ("%s - An example viewer for the pango ft2 extension\n" + "\n" + "Syntax:\n" + " %s [options] FILE\n" + "\n" + "Options:\n", + prog_name, prog_name); + arg_context_print_help (context); + exit (0); } int main(int argc, char *argv[]) @@ -172,9 +230,38 @@ int main(int argc, char *argv[]) char *text; size_t len; char *p; - int argp; PangoFontMap *fontmap; GError *error = NULL; + ArgContext *arg_context; + gboolean do_convert = FALSE; + + static const ArgDesc args[] = { + { "display", "Show output using ImageMagick", + ARG_BOOL, &opt_display }, + { "dpi", "Set the dpi'", + ARG_INT, &opt_dpi }, + { "font", "Set the font name", + ARG_STRING, &opt_font }, + { "header", "Display the options in the output", + ARG_BOOL, &opt_header }, + { "help", "Show this output", + ARG_CALLBACK, NULL, show_help, }, + { "margin", "Set the margin on the output in pixels", + ARG_INT, &opt_margin }, + { "markup", "Interpret contents as Pango markup", + ARG_BOOL, &opt_markup }, + { "output", "Name of output file", + ARG_STRING, &opt_output }, + { "rtl", "Set base dir to RTL", + ARG_BOOL, &opt_rtl }, + { "text", "Text to display (instead of a file)", + ARG_STRING, &opt_text }, + { "waterfall", "Create a waterfall display", + ARG_BOOL, &opt_waterfall }, + { "width", "Width in points to which to wrap output", + ARG_INT, &opt_width }, + { NULL } + }; prog_name = g_path_get_basename (argv[0]); @@ -183,93 +270,43 @@ int main(int argc, char *argv[]) if (g_file_test ("./pangorc", G_FILE_TEST_EXISTS)) putenv ("PANGO_RC_FILE=./pangorc"); - /* Parse command line */ - argp=1; - while(argp < argc && argv[argp][0] == '-') + arg_context = arg_context_new (NULL); + arg_context_add_table (arg_context, args); + + if (!arg_context_parse (arg_context, &argc, &argv, &error)) + fail ("%s", error->message); + + if ((opt_text && argc != 1) || + (!opt_text && argc != 2)) { - char *opt = argv[argp++]; - if (strcmp(opt, "--help") == 0) - { - printf("%s - An example viewer for the pango ft2 extension\n" - "\n" - "Syntax:\n" - " %s [options] FILE\n" - "\n" - "Options:\n" - " --display Show output using ImageMagick rather than writing to a file.\n" - " --dpi d Set the dpi.Default is '%d'.\n" - " --font Set the font name. Default is '%s'.\n" - " --margin m Set the margin on the output in pixels. Default is %d.\n" - " --markup Interpret contents as Pango markup.\n" - " --output f Name of output file [short form, -o].\n" - " --rtl Set base dir to RTL. Default is LTR.\n" - " --waterfall Create a waterfall display." - " --width Width of drawing window. Default is 500.\n", - prog_name, prog_name, opt_dpi, opt_font, opt_margin); - exit(0); - } - if (strcmp(opt, "--display") == 0) - { - opt_display = TRUE; - continue; - } - if (strcmp(opt, "--dpi") == 0) - { - opt_dpi = int_arg("--dpi", argv[argp++]); - continue; - } - if (strcmp(opt, "--font") == 0) - { - opt_font = argv[argp++]; - continue; - } - if (strcmp(opt, "--margin") == 0) - { - opt_margin = int_arg("--margin", argv[argp++]); - continue; - } - if (strcmp(opt, "--markup") == 0) - { - opt_markup = TRUE; - continue; - } - if (strcmp(opt, "--output") == 0 || - strcmp(opt, "-o") == 0) - { - opt_output = argv[argp++]; - continue; - } - if (strcmp(opt, "--waterfall") == 0) - { - opt_waterfall = TRUE; - continue; - } - if (strcmp(opt, "--rtl") == 0) - { - opt_dir = PANGO_DIRECTION_RTL; - continue; - } - fail ("Unknown option %s!\n", opt); + if (opt_text && argc == 2) + fail ("When specifying --text, no file should be given"); + + g_printerr ("Usage: %s [options] FILE\n", prog_name); + exit (1); } + if (!opt_display && !opt_output) { g_printerr ("%s: --output not specified, assuming --display\n", prog_name); opt_display = TRUE; } - if (argp + 1 != argc) + /* Get the text + */ + if (opt_text) { - g_printerr ("Usage: %s [options] FILE\n", prog_name); - exit (1); + text = g_strdup (opt_text); + len = strlen (text); } - - /* Get the text in the supplied file - */ - if (!g_file_get_contents (argv[argp++], &text, &len, &error)) - fail ("%s\n", error->message); - if (!g_utf8_validate (text, len, NULL)) - fail ("Text is not valid UTF-8"); - + else + { + if (!g_file_get_contents (argv[1], &text, &len, &error)) + fail ("%s\n", error->message); + if (!g_utf8_validate (text, len, NULL)) + fail ("Text is not valid UTF-8"); + } + /* Strip trailing whitespace */ p = text + len; @@ -292,7 +329,13 @@ int main(int argc, char *argv[]) if (opt_output) { - outfile_name = opt_output; + if (!(g_str_has_suffix (opt_output, ".pgm") || + g_str_has_suffix (opt_output, ".PGM"))) + do_convert = TRUE; + } + + if (opt_output && !do_convert) + { outfile = fopen (opt_output, "wb"); if (!outfile) @@ -302,7 +345,7 @@ int main(int argc, char *argv[]) else /* --display */ { /* This may need to be G_OS_UNIX guarded for fdopen */ - int fd = g_file_open_tmp ("pangoft2pgmXXXXXX", &outfile_name, &error); + int fd = g_file_open_tmp ("pangoft2pgmXXXXXX", &tmpfile_name, &error); if (fd == 1) fail ("Cannot open temporary file: %s\n", error->message); outfile = fdopen (fd, "wb"); @@ -315,7 +358,8 @@ int main(int argc, char *argv[]) context = pango_ft2_font_map_create_context (PANGO_FT2_FONT_MAP (fontmap)); pango_context_set_language (context, pango_language_from_string ("en_US")); - pango_context_set_base_dir (context, opt_dir); + pango_context_set_base_dir (context, + opt_rtl ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR); /* Write contents as pgm file */ { @@ -333,7 +377,7 @@ int main(int argc, char *argv[]) bitmap.num_grays = 256; bitmap.pixel_mode = ft_pixel_mode_grays; memset (buf, 0x00, bitmap.pitch * bitmap.rows); - + do_output (context, text, &bitmap, &width, &height); /* Invert bitmap to get black text on white background */ @@ -358,17 +402,47 @@ int main(int argc, char *argv[]) if (fclose(outfile) == EOF) fail ("Error writing output file: %s\n", g_strerror (errno)); + /* Convert to a different format, if necessary */ + if (do_convert) + { + int exit_status; + + gchar *command = g_strdup_printf ("convert %s %s", + tmpfile_name, + opt_output); + if (!g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error)) + fail ("When running ImageMagick 'convert' command: %s\n", + error->message); + + if (tmpfile_name) + { + remove (tmpfile_name); + tmpfile_name = NULL; + } + + if (exit_status) + exit (1); + } + if (opt_display) { int exit_status; - gchar *command = g_strdup_printf ("display %s", outfile_name); + gchar *title = get_options_string (); + gchar *title_quoted = g_shell_quote (title); + + gchar *command = g_strdup_printf ("display -title %s %s", + title_quoted, + opt_output ? opt_output: tmpfile_name); if (!g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error)) fail ("When running ImageMagick 'display' command: %s\n", error->message); g_free (command); - if (!opt_output) - remove (outfile_name); + g_free (title); + g_free (title_quoted); + + if (tmpfile_name) + remove (tmpfile_name); if (exit_status) exit (1); diff --git a/examples/renderdemo.c b/examples/renderdemo.c index 257dc8bb..f223f89e 100644 --- a/examples/renderdemo.c +++ b/examples/renderdemo.c @@ -24,10 +24,7 @@ #define BUFSIZE 1024 #define MALLOCSIZE 1024 #define DEFAULT_FONT_FAMILY "Sans" -#define DEFAULT_FONT_SIZE 36 - -#include <pango/pango.h> -#include <pango/pangoft2.h> +#define DEFAULT_FONT_SIZE 18 #include <errno.h> #include <stdarg.h> @@ -35,22 +32,31 @@ #include <stdio.h> #include <string.h> -static char *prog_name; -static PangoContext *context; +#include "argcontext.h" -static gboolean opt_display = FALSE; -static int opt_dpi = 96; +#include <pango/pango.h> +#include <pango/pangoft2.h> #define _MAKE_FONT_NAME(family, size) family " " #size #define MAKE_FONT_NAME(family, size) _MAKE_FONT_NAME(family, size) -static char *opt_font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE); -static PangoDirection opt_dir = PANGO_DIRECTION_LTR; +static char *prog_name; +static PangoContext *context; + +static char *tmpfile_name; static char *outfile_name; + +static gboolean opt_display = FALSE; +static int opt_dpi = 96; +static char *opt_font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE); +static gboolean opt_header = FALSE; static char *opt_output = NULL; static int opt_margin = 10; static int opt_markup = FALSE; +static gboolean opt_rtl = FALSE; +static char *opt_text = NULL; static gboolean opt_waterfall = FALSE; +static int opt_width = -1; static void fail (const char *format, ...) G_GNUC_PRINTF (1, 2); @@ -70,6 +76,20 @@ fail (const char *format, ...) exit (1); } +PangoFontDescription * +get_font_description (void) +{ + PangoFontDescription *font_description = pango_font_description_from_string (opt_font); + + if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_FAMILY) == 0) + pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); + + if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_SIZE) == 0) + pango_font_description_set_size (font_description, DEFAULT_FONT_SIZE * PANGO_SCALE); + + return font_description; +} + static PangoLayout * make_layout(PangoContext *context, const char *text, @@ -85,26 +105,42 @@ make_layout(PangoContext *context, else pango_layout_set_text (layout, text, -1); - font_description = pango_font_description_from_string (opt_font); - - if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_FAMILY) == 0) - pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); - + font_description = get_font_description (); if (size > 0) pango_font_description_set_size (font_description, size * PANGO_SCALE); - else if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_SIZE) == 0) - pango_font_description_set_size (font_description, DEFAULT_FONT_SIZE * PANGO_SCALE); + if (opt_width > 0) + pango_layout_set_width (layout, (opt_width * opt_dpi * PANGO_SCALE + 32) / 72); + base_dir = pango_context_get_base_dir (context); pango_layout_set_alignment (layout, base_dir == PANGO_DIRECTION_LTR ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); pango_layout_set_font_description (layout, font_description); + pango_font_description_free (font_description); return layout; } +static gchar * +get_options_string (void) +{ + PangoFontDescription *font_description = get_font_description (); + 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: dpi=%d", font_name, opt_dpi); + pango_font_description_free (font_description); + g_free (font_name); + + return result; +} + static void do_output (PangoContext *context, const char *text, @@ -121,6 +157,24 @@ do_output (PangoContext *context, *width = 0; *height = 0; + if (opt_header) + { + char *options_string = get_options_string (); + layout = make_layout (context, options_string, 10); + pango_layout_get_extents (layout, NULL, &logical_rect); + + *width = MAX (*width, PANGO_PIXELS (logical_rect.width)); + *height += PANGO_PIXELS (logical_rect.height); + + if (bitmap) + pango_ft2_render_layout (bitmap, layout, x, y); + + y += PANGO_PIXELS (logical_rect.height); + + g_object_unref (layout); + g_free (options_string); + } + if (opt_waterfall) { start_size = 8; @@ -153,17 +207,21 @@ do_output (PangoContext *context, *height += 2 * opt_margin; } -int int_arg (const char *arg_name, const char *arg) +static void +show_help (ArgContext *context, + const char *name, + const char *arg, + gpointer data) { - char *end; - long result = strtol (arg, &end, 0); - if (*arg == '\0' || *end != '\0') - { - fail ("Cannot parse integer value '%s' for %s.", - arg, arg_name); - } - - return result; + g_print ("%s - An example viewer for the pango ft2 extension\n" + "\n" + "Syntax:\n" + " %s [options] FILE\n" + "\n" + "Options:\n", + prog_name, prog_name); + arg_context_print_help (context); + exit (0); } int main(int argc, char *argv[]) @@ -172,9 +230,38 @@ int main(int argc, char *argv[]) char *text; size_t len; char *p; - int argp; PangoFontMap *fontmap; GError *error = NULL; + ArgContext *arg_context; + gboolean do_convert = FALSE; + + static const ArgDesc args[] = { + { "display", "Show output using ImageMagick", + ARG_BOOL, &opt_display }, + { "dpi", "Set the dpi'", + ARG_INT, &opt_dpi }, + { "font", "Set the font name", + ARG_STRING, &opt_font }, + { "header", "Display the options in the output", + ARG_BOOL, &opt_header }, + { "help", "Show this output", + ARG_CALLBACK, NULL, show_help, }, + { "margin", "Set the margin on the output in pixels", + ARG_INT, &opt_margin }, + { "markup", "Interpret contents as Pango markup", + ARG_BOOL, &opt_markup }, + { "output", "Name of output file", + ARG_STRING, &opt_output }, + { "rtl", "Set base dir to RTL", + ARG_BOOL, &opt_rtl }, + { "text", "Text to display (instead of a file)", + ARG_STRING, &opt_text }, + { "waterfall", "Create a waterfall display", + ARG_BOOL, &opt_waterfall }, + { "width", "Width in points to which to wrap output", + ARG_INT, &opt_width }, + { NULL } + }; prog_name = g_path_get_basename (argv[0]); @@ -183,93 +270,43 @@ int main(int argc, char *argv[]) if (g_file_test ("./pangorc", G_FILE_TEST_EXISTS)) putenv ("PANGO_RC_FILE=./pangorc"); - /* Parse command line */ - argp=1; - while(argp < argc && argv[argp][0] == '-') + arg_context = arg_context_new (NULL); + arg_context_add_table (arg_context, args); + + if (!arg_context_parse (arg_context, &argc, &argv, &error)) + fail ("%s", error->message); + + if ((opt_text && argc != 1) || + (!opt_text && argc != 2)) { - char *opt = argv[argp++]; - if (strcmp(opt, "--help") == 0) - { - printf("%s - An example viewer for the pango ft2 extension\n" - "\n" - "Syntax:\n" - " %s [options] FILE\n" - "\n" - "Options:\n" - " --display Show output using ImageMagick rather than writing to a file.\n" - " --dpi d Set the dpi.Default is '%d'.\n" - " --font Set the font name. Default is '%s'.\n" - " --margin m Set the margin on the output in pixels. Default is %d.\n" - " --markup Interpret contents as Pango markup.\n" - " --output f Name of output file [short form, -o].\n" - " --rtl Set base dir to RTL. Default is LTR.\n" - " --waterfall Create a waterfall display." - " --width Width of drawing window. Default is 500.\n", - prog_name, prog_name, opt_dpi, opt_font, opt_margin); - exit(0); - } - if (strcmp(opt, "--display") == 0) - { - opt_display = TRUE; - continue; - } - if (strcmp(opt, "--dpi") == 0) - { - opt_dpi = int_arg("--dpi", argv[argp++]); - continue; - } - if (strcmp(opt, "--font") == 0) - { - opt_font = argv[argp++]; - continue; - } - if (strcmp(opt, "--margin") == 0) - { - opt_margin = int_arg("--margin", argv[argp++]); - continue; - } - if (strcmp(opt, "--markup") == 0) - { - opt_markup = TRUE; - continue; - } - if (strcmp(opt, "--output") == 0 || - strcmp(opt, "-o") == 0) - { - opt_output = argv[argp++]; - continue; - } - if (strcmp(opt, "--waterfall") == 0) - { - opt_waterfall = TRUE; - continue; - } - if (strcmp(opt, "--rtl") == 0) - { - opt_dir = PANGO_DIRECTION_RTL; - continue; - } - fail ("Unknown option %s!\n", opt); + if (opt_text && argc == 2) + fail ("When specifying --text, no file should be given"); + + g_printerr ("Usage: %s [options] FILE\n", prog_name); + exit (1); } + if (!opt_display && !opt_output) { g_printerr ("%s: --output not specified, assuming --display\n", prog_name); opt_display = TRUE; } - if (argp + 1 != argc) + /* Get the text + */ + if (opt_text) { - g_printerr ("Usage: %s [options] FILE\n", prog_name); - exit (1); + text = g_strdup (opt_text); + len = strlen (text); } - - /* Get the text in the supplied file - */ - if (!g_file_get_contents (argv[argp++], &text, &len, &error)) - fail ("%s\n", error->message); - if (!g_utf8_validate (text, len, NULL)) - fail ("Text is not valid UTF-8"); - + else + { + if (!g_file_get_contents (argv[1], &text, &len, &error)) + fail ("%s\n", error->message); + if (!g_utf8_validate (text, len, NULL)) + fail ("Text is not valid UTF-8"); + } + /* Strip trailing whitespace */ p = text + len; @@ -292,7 +329,13 @@ int main(int argc, char *argv[]) if (opt_output) { - outfile_name = opt_output; + if (!(g_str_has_suffix (opt_output, ".pgm") || + g_str_has_suffix (opt_output, ".PGM"))) + do_convert = TRUE; + } + + if (opt_output && !do_convert) + { outfile = fopen (opt_output, "wb"); if (!outfile) @@ -302,7 +345,7 @@ int main(int argc, char *argv[]) else /* --display */ { /* This may need to be G_OS_UNIX guarded for fdopen */ - int fd = g_file_open_tmp ("pangoft2pgmXXXXXX", &outfile_name, &error); + int fd = g_file_open_tmp ("pangoft2pgmXXXXXX", &tmpfile_name, &error); if (fd == 1) fail ("Cannot open temporary file: %s\n", error->message); outfile = fdopen (fd, "wb"); @@ -315,7 +358,8 @@ int main(int argc, char *argv[]) context = pango_ft2_font_map_create_context (PANGO_FT2_FONT_MAP (fontmap)); pango_context_set_language (context, pango_language_from_string ("en_US")); - pango_context_set_base_dir (context, opt_dir); + pango_context_set_base_dir (context, + opt_rtl ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR); /* Write contents as pgm file */ { @@ -333,7 +377,7 @@ int main(int argc, char *argv[]) bitmap.num_grays = 256; bitmap.pixel_mode = ft_pixel_mode_grays; memset (buf, 0x00, bitmap.pitch * bitmap.rows); - + do_output (context, text, &bitmap, &width, &height); /* Invert bitmap to get black text on white background */ @@ -358,17 +402,47 @@ int main(int argc, char *argv[]) if (fclose(outfile) == EOF) fail ("Error writing output file: %s\n", g_strerror (errno)); + /* Convert to a different format, if necessary */ + if (do_convert) + { + int exit_status; + + gchar *command = g_strdup_printf ("convert %s %s", + tmpfile_name, + opt_output); + if (!g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error)) + fail ("When running ImageMagick 'convert' command: %s\n", + error->message); + + if (tmpfile_name) + { + remove (tmpfile_name); + tmpfile_name = NULL; + } + + if (exit_status) + exit (1); + } + if (opt_display) { int exit_status; - gchar *command = g_strdup_printf ("display %s", outfile_name); + gchar *title = get_options_string (); + gchar *title_quoted = g_shell_quote (title); + + gchar *command = g_strdup_printf ("display -title %s %s", + title_quoted, + opt_output ? opt_output: tmpfile_name); if (!g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error)) fail ("When running ImageMagick 'display' command: %s\n", error->message); g_free (command); - if (!opt_output) - remove (outfile_name); + g_free (title); + g_free (title_quoted); + + if (tmpfile_name) + remove (tmpfile_name); if (exit_status) exit (1); |