1/* viewer-render.c: Common code for rendering in viewers
2 *
3 * Copyright (C) 1999, 2004 Red Hat Software
4 * Copyright (C) 2001 Sun Microsystems
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21#include "config.h"
22#include <errno.h>
23#include <math.h>
24#include <stdarg.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28
29#include <glib.h>
30#include <glib/gprintf.h>
31#include <pango/pango.h>
32
33#include "viewer-render.h"
34
35gboolean opt_display = TRUE;
36int opt_dpi = 96;
37gboolean opt_pixels = FALSE;
38gboolean opt_pango_units = FALSE;
39const char *opt_font = "";
40gboolean opt_header = FALSE;
41const char *opt_output = NULL;
42int opt_margin_t = 10;
43int opt_margin_r = 10;
44int opt_margin_b = 10;
45int opt_margin_l = 10;
46int opt_markup = FALSE;
47gboolean opt_rtl = FALSE;
48double opt_rotate = 0;
49gboolean opt_auto_dir = TRUE;
50const char *opt_text = NULL;
51gboolean opt_waterfall = FALSE;
52int opt_width = -1;
53int opt_height = -1;
54int opt_indent = 0;
55int opt_spacing = 0;
56double opt_line_spacing = -1.0;
57gboolean opt_justify = 0;
58gboolean opt_justify_last_line = 0;
59int opt_runs = 1;
60PangoAlignment opt_align = PANGO_ALIGN_LEFT;
61PangoEllipsizeMode opt_ellipsize = PANGO_ELLIPSIZE_NONE;
62PangoGravity opt_gravity = PANGO_GRAVITY_SOUTH;
63PangoGravityHint opt_gravity_hint = PANGO_GRAVITY_HINT_NATURAL;
64HintMode opt_hinting = HINT_DEFAULT;
65HintMetrics opt_hint_metrics = HINT_METRICS_DEFAULT;
66SubpixelOrder opt_subpixel_order = SUBPIXEL_DEFAULT;
67Antialias opt_antialias = ANTIALIAS_DEFAULT;
68gboolean opt_subpixel_positions = FALSE;
69PangoWrapMode opt_wrap = PANGO_WRAP_WORD_CHAR;
70gboolean opt_wrap_set = FALSE;
71static const char *opt_pangorc = NULL; /* Unused */
72const PangoViewer *opt_viewer = NULL;
73const char *opt_language = NULL;
74gboolean opt_single_par = FALSE;
75PangoColor opt_fg_color = {0, 0, 0};
76guint16 opt_fg_alpha = 65535;
77gboolean opt_bg_set = FALSE;
78PangoColor opt_bg_color = {65535, 65535, 65535};
79guint16 opt_bg_alpha = 65535;
80gboolean opt_serialized = FALSE;
81const char *opt_serialized_output;
82const char *file_arg;
83
84/* Text (or markup) to render */
85static char *text;
86
87void
88fail (const char *format, ...)
89{
90 const char *msg;
91
92 va_list vap;
93 va_start (vap, format);
94 msg = g_strdup_vprintf (format, args: vap);
95 g_printerr (format: "%s: %s\n", g_get_prgname (), msg);
96
97 exit (status: 1);
98}
99
100static PangoLayout *
101make_layout(PangoContext *context,
102 const char *text,
103 double size)
104{
105 static PangoFontDescription *font_description;
106 PangoAlignment align;
107 PangoLayout *layout;
108
109 if (opt_serialized)
110 {
111 char *data;
112 gsize len;
113 GBytes *bytes;
114 GError *error = NULL;
115
116 if (!g_file_get_contents (filename: file_arg, contents: &data, length: &len, error: &error))
117 fail (format: "%s\n", error->message);
118 bytes = g_bytes_new_take (data, size: len);
119 layout = pango_layout_deserialize (context, bytes, flags: PANGO_LAYOUT_DESERIALIZE_CONTEXT, error: &error);
120 if (!layout)
121 fail (format: "%s\n", error->message);
122 g_bytes_unref (bytes);
123 goto out;
124 }
125
126 layout = pango_layout_new (context);
127 if (opt_markup)
128 pango_layout_set_markup (layout, markup: text, length: -1);
129 else
130 pango_layout_set_text (layout, text, length: -1);
131
132 pango_layout_set_auto_dir (layout, auto_dir: opt_auto_dir);
133 pango_layout_set_ellipsize (layout, ellipsize: opt_ellipsize);
134 pango_layout_set_justify (layout, justify: opt_justify);
135 pango_layout_set_justify_last_line (layout, justify: opt_justify_last_line);
136 pango_layout_set_single_paragraph_mode (layout, setting: opt_single_par);
137 pango_layout_set_wrap (layout, wrap: opt_wrap);
138
139 font_description = pango_font_description_from_string (str: opt_font);
140 if (size > 0)
141 pango_font_description_set_size (desc: font_description, size: size * PANGO_SCALE);
142
143 if (opt_width >= 0)
144 {
145 if (opt_pango_units)
146 pango_layout_set_width (layout, width: opt_width);
147 else
148 pango_layout_set_width (layout, width: (opt_width * opt_dpi * PANGO_SCALE + 36) / 72);
149 }
150
151 if (opt_height >= 0)
152 {
153 if (opt_pango_units)
154 pango_layout_set_width (layout, width: opt_height);
155 else
156 pango_layout_set_height (layout, height: (opt_height * opt_dpi * PANGO_SCALE + 36) / 72);
157 }
158 else
159 pango_layout_set_height (layout, height: opt_height);
160
161 if (opt_indent != 0)
162 {
163 if (opt_pango_units)
164 pango_layout_set_indent (layout, indent: opt_indent);
165 else
166 pango_layout_set_indent (layout, indent: (opt_indent * opt_dpi * PANGO_SCALE + 36) / 72);
167 }
168
169 if (opt_spacing != 0)
170 {
171 if (opt_pango_units)
172 pango_layout_set_spacing (layout, spacing: opt_spacing);
173 else
174 pango_layout_set_spacing (layout, spacing: (opt_spacing * opt_dpi * PANGO_SCALE + 36) / 72);
175 pango_layout_set_line_spacing (layout, factor: 0.0);
176 }
177 if (opt_line_spacing >= 0.0)
178 pango_layout_set_line_spacing (layout, factor: (float)opt_line_spacing);
179
180 align = opt_align;
181 if (align != PANGO_ALIGN_CENTER &&
182 pango_context_get_base_dir (context) != PANGO_DIRECTION_LTR) {
183 /* pango reverses left and right if base dir ir rtl. so we should
184 * reverse to cancel that. unfortunately it also does that for
185 * rtl paragraphs, so we cannot really get left/right. all we get
186 * is default/other-side. */
187 align = PANGO_ALIGN_LEFT + PANGO_ALIGN_RIGHT - align;
188 }
189 pango_layout_set_alignment (layout, alignment: align);
190
191 pango_layout_set_font_description (layout, desc: font_description);
192
193 pango_font_description_free (desc: font_description);
194
195out:
196 if (opt_serialized_output)
197 {
198 GError *error = NULL;
199
200 if (!pango_layout_write_to_file (layout,
201 flags: PANGO_LAYOUT_SERIALIZE_CONTEXT|PANGO_LAYOUT_SERIALIZE_OUTPUT,
202 filename: opt_serialized_output,
203 error: &error))
204 fail (format: "%s\n", error->message);
205 }
206
207 return layout;
208}
209
210gchar *
211get_options_string (void)
212{
213 PangoFontDescription *font_description = pango_font_description_from_string (str: opt_font);
214 gchar *font_name;
215 gchar *result;
216
217 if (opt_waterfall)
218 pango_font_description_unset_fields (desc: font_description, to_unset: PANGO_FONT_MASK_SIZE);
219
220 font_name = pango_font_description_to_string (desc: font_description);
221 result = g_strdup_printf (format: "%s: %s (%d dpi)", opt_viewer->name, font_name, opt_dpi);
222 pango_font_description_free (desc: font_description);
223 g_free (mem: font_name);
224
225 return result;
226}
227
228static void
229output_body (PangoLayout *layout,
230 RenderCallback render_cb,
231 gpointer cb_context,
232 gpointer cb_data,
233 int *width,
234 int *height,
235 gboolean supports_matrix)
236{
237 PangoRectangle logical_rect;
238 int size, start_size, end_size, increment;
239 int x = 0, y = 0;
240
241 if (!supports_matrix)
242 {
243 const PangoMatrix* matrix;
244 const PangoMatrix identity = PANGO_MATRIX_INIT;
245 PangoContext *context = pango_layout_get_context (layout);
246 matrix = pango_context_get_matrix (context);
247 if (matrix)
248 {
249 x += matrix->x0;
250 y += matrix->y0;
251 }
252 pango_context_set_matrix (context, matrix: &identity);
253 pango_layout_context_changed (layout);
254 }
255
256 if (opt_waterfall)
257 {
258 start_size = 8;
259 end_size = 48;
260 increment = 4;
261 }
262 else
263 {
264 start_size = end_size = -1;
265 increment = 1;
266 }
267
268 *width = 0;
269 *height = 0;
270
271 for (size = start_size; size <= end_size; size += increment)
272 {
273 if (size > 0)
274 {
275 PangoFontDescription *desc = pango_font_description_copy (desc: pango_layout_get_font_description (layout));
276 pango_font_description_set_size (desc, size: size * PANGO_SCALE);
277 pango_layout_set_font_description (layout, desc);
278 pango_font_description_free (desc);
279 }
280
281 pango_layout_get_pixel_extents (layout, NULL, logical_rect: &logical_rect);
282
283 if (render_cb)
284 (*render_cb) (layout, x, y+*height, cb_context, cb_data);
285
286 *width = MAX (*width,
287 MAX (logical_rect.x + logical_rect.width,
288 PANGO_PIXELS (pango_layout_get_width (layout))));
289 *height += MAX (logical_rect.y + logical_rect.height,
290 PANGO_PIXELS (pango_layout_get_height (layout)));
291 }
292}
293
294static void
295set_transform (PangoContext *context,
296 TransformCallback transform_cb,
297 gpointer cb_context,
298 gpointer cb_data,
299 PangoMatrix *matrix)
300{
301 pango_context_set_matrix (context, matrix);
302 if (transform_cb)
303 (*transform_cb) (context, matrix, cb_context, cb_data);
304}
305
306void
307do_output (PangoContext *context,
308 RenderCallback render_cb,
309 TransformCallback transform_cb,
310 gpointer cb_context,
311 gpointer cb_data,
312 int *width_out,
313 int *height_out)
314{
315 PangoLayout *layout;
316 PangoRectangle rect;
317 PangoMatrix matrix = PANGO_MATRIX_INIT;
318 PangoMatrix *orig_matrix;
319 gboolean supports_matrix;
320 int rotated_width, rotated_height;
321 int x = opt_margin_l;
322 int y = opt_margin_t;
323 int width, height;
324
325 width = 0;
326 height = 0;
327
328 orig_matrix = pango_matrix_copy (matrix: pango_context_get_matrix (context));
329 /* If the backend sets an all-zero matrix on the context,
330 * means that it doesn't support transformations.
331 */
332 supports_matrix = !orig_matrix ||
333 (orig_matrix->xx != 0. || orig_matrix->xy != 0. ||
334 orig_matrix->yx != 0. || orig_matrix->yy != 0. ||
335 orig_matrix->x0 != 0. || orig_matrix->y0 != 0.);
336
337 set_transform (context, transform_cb, cb_context, cb_data, NULL);
338
339 pango_context_set_language (context,
340 language: opt_language ? pango_language_from_string (language: opt_language)
341 : pango_language_get_default ());
342 pango_context_set_base_dir (context,
343 direction: opt_rtl ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR);
344
345 if (opt_header)
346 {
347 char *options_string = get_options_string ();
348 pango_context_set_base_gravity (context, gravity: PANGO_GRAVITY_SOUTH);
349 layout = make_layout (context, text: options_string, size: 10);
350 pango_layout_get_extents (layout, NULL, logical_rect: &rect);
351
352 width = MAX (width, PANGO_PIXELS (rect.width));
353 height += PANGO_PIXELS (rect.height);
354
355 if (render_cb)
356 (*render_cb) (layout, x, y, cb_context, cb_data);
357
358 y += PANGO_PIXELS (rect.height);
359
360 g_object_unref (object: layout);
361 g_free (mem: options_string);
362 }
363
364 if (opt_rotate != 0)
365 {
366 if (supports_matrix)
367 pango_matrix_rotate (matrix: &matrix, degrees: opt_rotate);
368 else
369 g_printerr (format: "The backend does not support rotated text\n");
370 }
371
372 pango_context_set_base_gravity (context, gravity: opt_gravity);
373 pango_context_set_gravity_hint (context, hint: opt_gravity_hint);
374
375 layout = make_layout (context, text, size: -1);
376 if (opt_serialized && supports_matrix)
377 {
378 const PangoMatrix *context_matrix = pango_context_get_matrix (context: pango_layout_get_context (layout));
379 matrix = context_matrix ? *context_matrix : (PangoMatrix) PANGO_MATRIX_INIT;
380 }
381
382 set_transform (context, transform_cb, cb_context, cb_data, matrix: &matrix);
383
384 output_body (layout,
385 NULL, NULL, NULL,
386 width: &rotated_width, height: &rotated_height,
387 supports_matrix);
388
389 rect.x = rect.y = 0;
390 rect.width = rotated_width;
391 rect.height = rotated_height;
392
393 pango_matrix_transform_pixel_rectangle (matrix: &matrix, rect: &rect);
394
395 matrix.x0 = x - rect.x;
396 matrix.y0 = y - rect.y;
397
398 set_transform (context, transform_cb, cb_context, cb_data, matrix: &matrix);
399
400 if (render_cb)
401 output_body (layout,
402 render_cb, cb_context, cb_data,
403 width: &rotated_width, height: &rotated_height,
404 supports_matrix);
405
406 width = MAX (width, rect.width);
407 height += rect.height;
408
409 width += opt_margin_l + opt_margin_r;
410 height += opt_margin_t + opt_margin_b;
411
412 if (width_out)
413 *width_out = width;
414 if (height_out)
415 *height_out = height;
416
417 pango_context_set_matrix (context, matrix: orig_matrix);
418 pango_matrix_free (matrix: orig_matrix);
419 g_object_unref (object: layout);
420}
421
422static gboolean
423parse_enum (GType type,
424 int *value,
425 const char *name,
426 const char *arg,
427 gpointer data G_GNUC_UNUSED,
428 GError **error)
429{
430 char *possible_values = NULL;
431 gboolean ret;
432
433G_GNUC_BEGIN_IGNORE_DEPRECATIONS
434 ret = pango_parse_enum (type,
435 str: arg,
436 value,
437 FALSE,
438 possible_values: &possible_values);
439G_GNUC_END_IGNORE_DEPRECATIONS
440
441 if (!ret && error)
442 {
443 g_set_error(err: error,
444 G_OPTION_ERROR,
445 code: G_OPTION_ERROR_BAD_VALUE,
446 format: "Argument for %s must be one of %s",
447 name,
448 possible_values);
449 }
450
451 g_free (mem: possible_values);
452
453 return ret;
454}
455
456static gboolean
457parse_align (const char *name,
458 const char *arg,
459 gpointer data,
460 GError **error)
461{
462 return parse_enum (type: PANGO_TYPE_ALIGNMENT, value: (int*)(void*)&opt_align,
463 name, arg, data, error);
464}
465
466static gboolean
467parse_ellipsis (const char *name,
468 const char *arg,
469 gpointer data,
470 GError **error)
471{
472 return parse_enum (type: PANGO_TYPE_ELLIPSIZE_MODE, value: (int*)(void*)&opt_ellipsize,
473 name, arg, data, error);
474}
475
476static gboolean
477parse_gravity (const char *name,
478 const char *arg,
479 gpointer data,
480 GError **error)
481{
482 return parse_enum (type: PANGO_TYPE_GRAVITY, value: (int*)(void*)&opt_gravity,
483 name, arg, data, error);
484}
485
486static gboolean
487parse_gravity_hint (const char *name,
488 const char *arg,
489 gpointer data,
490 GError **error)
491{
492 return parse_enum (type: PANGO_TYPE_GRAVITY_HINT, value: (int*)(void*)&opt_gravity_hint,
493 name, arg, data, error);
494}
495
496static gboolean
497parse_hinting (const char *name G_GNUC_UNUSED,
498 const char *arg,
499 gpointer data G_GNUC_UNUSED,
500 GError **error)
501{
502 gboolean ret = TRUE;
503
504 if (strcmp (s1: arg, s2: "none") == 0)
505 opt_hinting = HINT_NONE;
506 else if (strcmp (s1: arg, s2: "auto") == 0)
507 opt_hinting = HINT_AUTO;
508 else if (strcmp (s1: arg, s2: "slight") == 0)
509 opt_hinting = HINT_SLIGHT;
510 else if (strcmp (s1: arg, s2: "medium") == 0)
511 opt_hinting = HINT_MEDIUM;
512 else if (strcmp (s1: arg, s2: "full") == 0)
513 opt_hinting = HINT_FULL;
514 else
515 {
516 g_set_error(err: error,
517 G_OPTION_ERROR,
518 code: G_OPTION_ERROR_BAD_VALUE,
519 format: "Argument for --hinting must be one of none/auto/slight/medium/full");
520 ret = FALSE;
521 }
522
523 return ret;
524}
525
526static gboolean
527parse_subpixel_order (const char *name,
528 const char *arg,
529 gpointer data,
530 GError **error)
531{
532 gboolean ret = TRUE;
533
534 if (strcmp (s1: arg, s2: "rgb") == 0)
535 opt_subpixel_order = SUBPIXEL_RGB;
536 else if (strcmp (s1: arg, s2: "bgr") == 0)
537 opt_subpixel_order = SUBPIXEL_BGR;
538 else if (strcmp (s1: arg, s2: "vrgb") == 0)
539 opt_subpixel_order = SUBPIXEL_VRGB;
540 else if (strcmp (s1: arg, s2: "vbgr") == 0)
541 opt_subpixel_order = SUBPIXEL_VBGR;
542 else
543 {
544 g_set_error (err: error,
545 G_OPTION_ERROR,
546 code: G_OPTION_ERROR_BAD_VALUE,
547 format: "Argument for --subpixel-order must be one of rgb/bgr/vrgb/vbgr");
548 ret = FALSE;
549 }
550
551 return ret;
552}
553
554static gboolean
555parse_hint_metrics (const char *name,
556 const char *arg,
557 gpointer data,
558 GError **error)
559{
560 gboolean ret = TRUE;
561
562 if (strcmp (s1: arg, s2: "on") == 0)
563 opt_hint_metrics = HINT_METRICS_ON;
564 else if (strcmp (s1: arg, s2: "off") == 0)
565 opt_hint_metrics = HINT_METRICS_OFF;
566 else
567 {
568 g_set_error (err: error,
569 G_OPTION_ERROR,
570 code: G_OPTION_ERROR_BAD_VALUE,
571 format: "Argument for --hint-metrics must be one of on/off");
572 ret = FALSE;
573 }
574
575 return ret;
576}
577
578static gboolean
579parse_antialias (const char *name,
580 const char *arg,
581 gpointer data,
582 GError **error)
583{
584 gboolean ret = TRUE;
585
586 if (strcmp (s1: arg, s2: "none") == 0)
587 opt_antialias = ANTIALIAS_NONE;
588 else if (strcmp (s1: arg, s2: "gray") == 0)
589 opt_antialias = ANTIALIAS_GRAY;
590 else if (strcmp (s1: arg, s2: "subpixel") == 0)
591 opt_antialias = ANTIALIAS_SUBPIXEL;
592 else
593 {
594 g_set_error (err: error,
595 G_OPTION_ERROR,
596 code: G_OPTION_ERROR_BAD_VALUE,
597 format: "Argument for --antialias must be one of none/gray/subpixel");
598 ret = FALSE;
599 }
600
601 return ret;
602}
603static gboolean
604parse_wrap (const char *name,
605 const char *arg,
606 gpointer data,
607 GError **error)
608{
609 gboolean ret;
610 if ((ret = parse_enum (PANGO_TYPE_WRAP_MODE, (int*)(void*)&opt_wrap,
611 name, arg, data, error)))
612 {
613 opt_wrap_set = TRUE;
614 }
615 return ret;
616}
617
618static gboolean
619parse_rgba_color (PangoColor *color,
620 guint16 *alpha,
621 const char *name,
622 const char *arg,
623 gpointer data G_GNUC_UNUSED,
624 GError **error)
625{
626 gboolean ret;
627 char buf[32];
628 int len;
629
630 len = strlen (s: arg);
631 /* handle alpha */
632 if (*arg == '#' && (len == 5 || len == 9 || len == 17))
633 {
634 int width, bits;
635 unsigned int a;
636
637 bits = len - 1;
638 width = bits >> 2;
639
640 strcpy (dest: buf, src: arg);
641 arg = buf;
642
643 if (!sscanf (s: buf + len - width, format: "%x", &a))
644 {
645 ret = FALSE;
646 goto err;
647 }
648 buf[len - width] = '\0';
649
650 a <<= (16 - bits);
651 while (bits < 16)
652 {
653 a |= (a >> bits);
654 bits *= 2;
655 }
656 *alpha = a;
657 }
658 else
659 *alpha = 65535;
660
661 ret = pango_color_parse (color, spec: arg);
662
663err:
664 if (!ret && error)
665 {
666 g_set_error(err: error,
667 G_OPTION_ERROR,
668 code: G_OPTION_ERROR_BAD_VALUE,
669 format: "Argument for %s must be a color name like red, or CSS-style #rrggbb / #rrggbbaa",
670 name);
671 }
672
673 return ret;
674}
675
676static gboolean
677parse_foreground (const char *name,
678 const char *arg,
679 gpointer data,
680 GError **error)
681{
682 return parse_rgba_color (color: &opt_fg_color, alpha: &opt_fg_alpha,
683 name, arg, data, error);
684}
685
686static gboolean
687parse_background (const char *name,
688 const char *arg,
689 gpointer data,
690 GError **error)
691{
692 opt_bg_set = TRUE;
693
694 if (0 == strcmp (s1: "transparent", s2: arg))
695 {
696 opt_bg_alpha = 0;
697 return TRUE;
698 }
699
700 return parse_rgba_color (color: &opt_bg_color, alpha: &opt_bg_alpha,
701 name, arg, data, error);
702}
703
704static gboolean
705parse_margin (const char *name G_GNUC_UNUSED,
706 const char *arg,
707 gpointer data G_GNUC_UNUSED,
708 GError **error)
709{
710 switch (sscanf (s: arg, format: "%d%*[ ,]%d%*[ ,]%d%*[ ,]%d", &opt_margin_t, &opt_margin_r, &opt_margin_b, &opt_margin_l))
711 {
712 default:
713 case 0:
714 {
715 g_set_error(err: error,
716 G_OPTION_ERROR,
717 code: G_OPTION_ERROR_BAD_VALUE,
718 format: "Argument for --margin must be one to four space-separated numbers");
719 return FALSE;
720 }
721 case 1: opt_margin_r = opt_margin_t;
722 G_GNUC_FALLTHROUGH;
723 case 2: opt_margin_b = opt_margin_t;
724 G_GNUC_FALLTHROUGH;
725 case 3: opt_margin_l = opt_margin_r;
726 }
727 return TRUE;
728}
729
730
731static gchar *
732backends_to_string (void)
733{
734 GString *backends = g_string_new (NULL);
735 const PangoViewer **viewer;
736
737 for (viewer = viewers; *viewer; viewer++)
738 if ((*viewer)->id)
739 {
740 g_string_append (string: backends, val: (*viewer)->id);
741 g_string_append_c (backends, '/');
742 }
743 g_string_truncate (string: backends, MAX (0, (gint)backends->len - 1));
744
745 return g_string_free(string: backends,FALSE);
746}
747
748static int
749backends_get_count (void)
750{
751 const PangoViewer **viewer;
752 int i = 0;
753
754 for (viewer = viewers; *viewer; viewer++)
755 if ((*viewer)->id)
756 i++;
757
758 return i;
759}
760
761
762static gchar *
763backend_description (void)
764{
765 GString *description = g_string_new(init: "Pango backend to use for rendering ");
766 int backends_count = backends_get_count ();
767
768 if (backends_count > 1)
769 g_string_append_printf(string: description,format: "(default: %s)", (*viewers)->id);
770 else if (backends_count == 1)
771 g_string_append_printf(string: description,format: "(only available: %s)", (*viewers)->id);
772 else
773 g_string_append_printf(string: description,format: "(no backends found!)");
774
775 return g_string_free(string: description,FALSE);
776
777}
778
779static gboolean
780parse_backend (const char *name G_GNUC_UNUSED,
781 const char *arg,
782 gpointer data G_GNUC_UNUSED,
783 GError **error)
784{
785 gboolean ret = TRUE;
786 const PangoViewer **viewer;
787
788 for (viewer = viewers; *viewer; viewer++)
789 if (!g_ascii_strcasecmp (s1: (*viewer)->id, s2: arg))
790 break;
791
792 if (*viewer)
793 opt_viewer = *viewer;
794 else
795 {
796 gchar *backends = backends_to_string ();
797
798 g_set_error(err: error,
799 G_OPTION_ERROR,
800 code: G_OPTION_ERROR_BAD_VALUE,
801 format: "Available --backend options are: %s",
802 backends);
803 g_free(mem: backends);
804 ret = FALSE;
805 }
806
807 return ret;
808}
809
810
811static G_GNUC_NORETURN gboolean
812show_version(const char *name G_GNUC_UNUSED,
813 const char *arg G_GNUC_UNUSED,
814 gpointer data G_GNUC_UNUSED,
815 GError **error G_GNUC_UNUSED)
816{
817 g_printf(format: "%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
818
819 if (PANGO_VERSION != pango_version())
820 g_printf(format: "Linked Pango library has a different version: %s\n", pango_version_string ());
821
822 exit(status: 0);
823}
824
825void
826parse_options (int argc, char *argv[])
827{
828 gchar *backend_options = backends_to_string ();
829 GOptionFlags backend_flag = backends_get_count () > 1 ? 0 : G_OPTION_FLAG_HIDDEN;
830 gchar *backend_desc = backend_description ();
831 GOptionEntry entries[] =
832 {
833 {"no-auto-dir", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_auto_dir,
834 "No layout direction according to contents", NULL},
835 {"backend", 0, backend_flag, G_OPTION_ARG_CALLBACK, &parse_backend,
836 backend_desc, backend_options},
837 {"background", 0, 0, G_OPTION_ARG_CALLBACK, &parse_background,
838 "Set the background color", "red/#rrggbb/#rrggbbaa/transparent"},
839 {"no-display", 'q', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_display,
840 "Do not display (just write to file or whatever)", NULL},
841 {"dpi", 0, 0, G_OPTION_ARG_INT, &opt_dpi,
842 "Set the resolution", "number"},
843 {"align", 0, 0, G_OPTION_ARG_CALLBACK, &parse_align,
844 "Text alignment", "left/center/right"},
845 {"ellipsize", 0, 0, G_OPTION_ARG_CALLBACK, &parse_ellipsis,
846 "Ellipsization mode", "start/middle/end"},
847 {"font", 0, 0, G_OPTION_ARG_STRING, &opt_font,
848 "Set the font description", "description"},
849 {"foreground", 0, 0, G_OPTION_ARG_CALLBACK, &parse_foreground,
850 "Set the text color", "red/#rrggbb/#rrggbbaa"},
851 {"gravity", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity,
852 "Base gravity: glyph rotation", "south/east/north/west/auto"},
853 {"gravity-hint", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity_hint,
854 "Gravity hint", "natural/strong/line"},
855 {"header", 0, 0, G_OPTION_ARG_NONE, &opt_header,
856 "Display the options in the output", NULL},
857 {"height", 0, 0, G_OPTION_ARG_INT, &opt_height,
858 "Height in points (positive) or number of lines (negative) for ellipsizing", "+points/-numlines"},
859 {"hinting", 0, 0, G_OPTION_ARG_CALLBACK, &parse_hinting,
860 "Hinting style", "none/auto/slight/medium/full"},
861 {"antialias", 0, 0, G_OPTION_ARG_CALLBACK, &parse_antialias,
862 "Antialiasing", "none/gray/subpixel"},
863 {"hint-metrics", 0, 0, G_OPTION_ARG_CALLBACK, &parse_hint_metrics,
864 "Hint metrics", "on/off"},
865 { "subpixel-positions", 0, 0, G_OPTION_ARG_NONE, &opt_subpixel_positions,
866 "Subpixel positioning", NULL},
867 {"subpixel-order", 0, 0, G_OPTION_ARG_CALLBACK, &parse_subpixel_order,
868 "Subpixel order", "rgb/bgr/vrgb/vbgr"},
869 {"indent", 0, 0, G_OPTION_ARG_INT, &opt_indent,
870 "Width in points to indent paragraphs", "points"},
871 {"spacing", 0, 0, G_OPTION_ARG_INT, &opt_spacing,
872 "Spacing in points between lines", "points"},
873 {"line-spacing", 0, 0, G_OPTION_ARG_DOUBLE, &opt_line_spacing,
874 "Spread factor for line height", "factor"},
875 {"justify", 0, 0, G_OPTION_ARG_NONE, &opt_justify,
876 "Stretch paragraph lines to be justified", NULL},
877 {"justify-last-line", 0, 0, G_OPTION_ARG_NONE, &opt_justify_last_line,
878 "Justify the last line of the paragraph", NULL},
879 {"language", 0, 0, G_OPTION_ARG_STRING, &opt_language,
880 "Language to use for font selection", "en_US/etc"},
881 {"margin", 0, 0, G_OPTION_ARG_CALLBACK, &parse_margin,
882 "Set the margin on the output in pixels", "CSS-style numbers in pixels"},
883 {"markup", 0, 0, G_OPTION_ARG_NONE, &opt_markup,
884 "Interpret text as Pango markup", NULL},
885 {"output", 'o', 0, G_OPTION_ARG_STRING, &opt_output,
886 "Save rendered image to output file", "file"},
887 {"pangorc", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &opt_pangorc,
888 "Deprecated", "file"},
889 {"pixels", 0, 0, G_OPTION_ARG_NONE, &opt_pixels,
890 "Use pixel units instead of points (sets dpi to 72)", NULL},
891 {"pango-units", 0, 0, G_OPTION_ARG_NONE, &opt_pango_units,
892 "Use Pango units instead of points", NULL},
893 {"rtl", 0, 0, G_OPTION_ARG_NONE, &opt_rtl,
894 "Set base direction to right-to-left", NULL},
895 {"rotate", 0, 0, G_OPTION_ARG_DOUBLE, &opt_rotate,
896 "Angle at which to rotate results", "degrees"},
897 {"runs", 'n', 0, G_OPTION_ARG_INT, &opt_runs,
898 "Run Pango layout engine this many times", "integer"},
899 {"single-par", 0, 0, G_OPTION_ARG_NONE, &opt_single_par,
900 "Enable single-paragraph mode", NULL},
901 {"text", 't', 0, G_OPTION_ARG_STRING, &opt_text,
902 "Text to display (instead of a file)", "string"},
903 {"version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, &show_version,
904 "Show version numbers", NULL},
905 {"waterfall", 0, 0, G_OPTION_ARG_NONE, &opt_waterfall,
906 "Create a waterfall display", NULL},
907 {"width", 'w', 0, G_OPTION_ARG_INT, &opt_width,
908 "Width in points to which to wrap lines or ellipsize", "points"},
909 {"wrap", 0, 0, G_OPTION_ARG_CALLBACK, &parse_wrap,
910 "Text wrapping mode (needs a width to be set)", "word/char/word-char"},
911 {"serialized", 0, 0, G_OPTION_ARG_NONE, &opt_serialized,
912 "Create layout from a serialized file", "FILE"},
913 {"serialize-to", 0, 0, G_OPTION_ARG_FILENAME, &opt_serialized_output,
914 "Serialize result to a file", "FILE"},
915 {NULL}
916 };
917 GError *error = NULL;
918 GError *parse_error = NULL;
919 GOptionContext *context;
920 size_t len;
921 const PangoViewer **viewer;
922
923 context = g_option_context_new (parameter_string: "- FILE");
924 g_option_context_add_main_entries (context, entries, NULL);
925
926 for (viewer = viewers; *viewer; viewer++)
927 if ((*viewer)->get_option_group)
928 {
929 GOptionGroup *group = (*viewer)->get_option_group (*viewer);
930 if (group)
931 g_option_context_add_group (context, group);
932 }
933
934 if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &parse_error))
935 {
936 if (parse_error != NULL)
937 fail(format: "%s", parse_error->message);
938 else
939 fail(format: "Option parse error");
940 exit(status: 1);
941 }
942 g_option_context_free(context);
943 g_free(mem: backend_options);
944 g_free(mem: backend_desc);
945
946 if (opt_pixels)
947 opt_dpi = 72;
948
949 if ((opt_text && argc != 1) || (!opt_text && argc != 2))
950 {
951 if (opt_text && argc != 1)
952 fail (format: "When specifying --text, no file should be given");
953
954 g_printerr (format: "Usage: %s [OPTION...] FILE\n", g_get_prgname ());
955 exit (status: 1);
956 }
957
958 if (opt_serialized && argc != 2)
959 {
960 g_printerr (format: "Usage: %s [OPTION...] FILE\n", g_get_prgname ());
961 exit (status: 1);
962 }
963
964 /* set up the backend */
965 if (!opt_viewer)
966 {
967 opt_viewer = *viewers;
968 if (!opt_viewer)
969 fail (format: "No viewer backend found");
970 }
971
972 /* Get the text
973 */
974 if (opt_serialized)
975 {
976 file_arg = argv[1];
977 text = g_strdup (str: "");
978 len = 0;
979 }
980 else if (opt_text)
981 {
982 text = g_strdup (str: opt_text);
983 len = strlen (s: text);
984 }
985 else
986 {
987 if (!g_file_get_contents (filename: argv[1], contents: &text, length: &len, error: &error))
988 fail (format: "%s\n", error->message);
989 }
990
991 /* Strip one trailing newline
992 */
993 if (len > 0 && text[len - 1] == '\n')
994 len--;
995 if (len > 0 && text[len - 1] == '\r')
996 len--;
997 text[len] = '\0';
998
999 /* Make sure we have valid markup
1000 */
1001 if (opt_markup &&
1002 !pango_parse_markup (markup_text: text, length: -1, accel_marker: 0, NULL, NULL, NULL, error: &error))
1003 fail (format: "Cannot parse input as markup: %s", error->message);
1004}
1005
1006
1007void
1008finalize (void)
1009{
1010 g_free (mem: text);
1011}
1012

source code of gtk/subprojects/pango/utils/viewer-render.c