"Fossies" - the Fresh Open Source Software Archive

Member "paps-0.7.1/src/paps.c" (26 Oct 2019, 48752 Bytes) of package /linux/privat/paps-0.7.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "paps.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.6.8_vs_0.7.1.

    1 /* Pango
    2  * paps.c: A postscript printing program using pango.
    3  *
    4  * Copyright (C) 2002, 2005 Dov Grobgeld <dov.grobgeld@gmail.com>
    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  */
   22 
   23 #include <pango/pango.h>
   24 #include <pango/pangoft2.h>
   25 #include <pango/pangocairo.h>
   26 #include <cairo/cairo.h>
   27 #include <cairo/cairo-ps.h>
   28 #include <cairo/cairo-pdf.h>
   29 #include <cairo/cairo-svg.h>
   30 #include <errno.h>
   31 #include <langinfo.h>
   32 #include <stdlib.h>
   33 #include <stdio.h>
   34 #include <string.h>
   35 #include <time.h>
   36 #include <locale.h>
   37 #include <math.h>
   38 #include <wchar.h>
   39 #include <libgen.h>
   40 #include <config.h>
   41 
   42 #if ENABLE_NLS
   43 #include <libintl.h>
   44 
   45 #define _(str)      gettext(str)
   46 #ifdef gettext_noop
   47 #define N_(str)     gettext_noop(str)
   48 #else
   49 #define N_(str)     (str)
   50 #endif
   51 
   52 #else   /* NLS is disabled */
   53 #define _(str)      (str)
   54 #define N_(str)     (str)
   55 #endif
   56 
   57 #define BUFSIZE 1024
   58 #define DEFAULT_FONT_FAMILY     "Monospace"
   59 #define DEFAULT_FONT_SIZE       "12"
   60 #define HEADER_FONT_FAMILY      "Monospace Bold"
   61 #define HEADER_FONT_SCALE       "12"
   62 #define MAKE_FONT_NAME(f,s)     f " " s
   63 
   64 /*
   65  * Cairo sets limit on the comment line for cairo_ps_surface_dsc_comment() to
   66  * 255 characters, including the initial percent characters.
   67  */
   68 #define CAIRO_COMMENT_MAX       255
   69 
   70 #define MARGIN_LEFT     36
   71 #define MARGIN_RIGHT    36
   72 #define MARGIN_TOP      36
   73 #define MARGIN_BOTTOM   36
   74 
   75 
   76 typedef enum {
   77     PAPER_TYPE_A4 = 0,
   78     PAPER_TYPE_US_LETTER = 1,
   79     PAPER_TYPE_US_LEGAL = 2,
   80     PAPER_TYPE_A3 = 3
   81 } paper_type_t ;
   82 
   83 typedef enum {
   84     FORMAT_POSTSCRIPT = 0,
   85     FORMAT_PDF = 1,
   86     FORMAT_SVG = 2
   87 } output_format_t ;
   88 
   89 typedef struct  {
   90     double width;
   91     double height;
   92 } paper_size_t;
   93 
   94 static const paper_size_t paper_sizes[] = {
   95     { 595.28, 841.89}, /* A4 */
   96     { 612, 792},       /* US letter */
   97     { 612, 1008},      /* US legal */
   98     { 842, 1190}       /* A3 */
   99 };
  100 
  101 typedef struct {
  102   int column_width;
  103   int column_height;
  104   int num_columns;
  105   int gutter_width;  /* These are all in postscript points=1/72 inch... */
  106   int top_margin;  
  107   int bottom_margin;
  108   int left_margin;
  109   int right_margin;
  110   double page_width;
  111   double page_height;
  112   int header_ypos;
  113   int header_sep;
  114   int header_height;
  115   int footer_height;
  116   gdouble scale_x;
  117   gdouble scale_y;
  118   gboolean do_draw_header;
  119   gboolean do_draw_footer;
  120   gboolean do_duplex;
  121   gboolean do_tumble;
  122   gboolean do_landscape;
  123   gboolean do_justify;
  124   gboolean do_separation_line;
  125   gboolean do_draw_contour;
  126   gboolean do_show_wrap;
  127   gboolean do_use_markup;
  128   gboolean do_stretch_chars;
  129   PangoDirection pango_dir;
  130   const gchar *title;
  131   const gchar *header_font_desc;
  132   gdouble lpi;
  133   gdouble cpi;
  134 } page_layout_t;
  135 
  136 typedef struct {
  137   PangoLayoutLine *pango_line;
  138   PangoRectangle logical_rect;
  139   PangoRectangle ink_rect;
  140   int formfeed;
  141   gboolean wrapped;   // Whether the paragraph was character wrapped
  142 } LineLink;
  143 
  144 typedef struct _Paragraph Paragraph;
  145 
  146 /* Structure representing a paragraph
  147  */
  148 struct _Paragraph {
  149   const char *text;
  150   int length;
  151   int height;   /* Height, in pixels */
  152   int formfeed;
  153   gboolean wrapped; 
  154   gboolean clipped;   // Whether the line was clipped. Used for CPI.
  155   PangoLayout *layout;
  156 };
  157 
  158 /* Information passed in user data when drawing outlines */
  159 static GList *split_paragraphs_into_lines  (page_layout_t   *page_layout,
  160                                             GList           *paragraphs);
  161 static char  *read_file                    (FILE            *file,
  162                                             gchar           *encoding);
  163 static GList *split_text_into_paragraphs   (cairo_t *cr,
  164                                             PangoContext    *pango_context,
  165                                             page_layout_t   *page_layout,
  166                                             int              paint_width,
  167                                             const char      *text);
  168 static int    output_pages                 (cairo_surface_t * surface,
  169                                             cairo_t         *cr,
  170                                             GList           *pango_lines,
  171                                             page_layout_t   *page_layout,
  172                                             gboolean         need_header,
  173                                             PangoContext    *pango_context);
  174 static void   eject_column                 (cairo_t         *cr,
  175                                             page_layout_t   *page_layout,
  176                                             int              column_idx);
  177 static void   eject_page                   (cairo_t         *cr);
  178 static void   start_page                   (cairo_surface_t *surface,
  179                                             cairo_t         *cr, 
  180                                             page_layout_t   *page_layout);
  181 static void   draw_line_to_page            (cairo_t         *cr,
  182                                             int              column_idx,
  183                                             int              column_pos,
  184                                             page_layout_t   *page_layout,
  185                                             PangoLayoutLine *line,
  186                                             gboolean         draw_wrap_character);
  187 static int    draw_page_header_line_to_page(cairo_t         *cr,
  188                                             gboolean         is_footer,
  189                                             page_layout_t   *page_layout,
  190                                             PangoContext    *ctx,
  191                                             int              page);
  192 static void   postscript_dsc_comments      (cairo_surface_t *surface,
  193                                             page_layout_t   *page_layout);
  194 
  195 FILE *output_fh;
  196 static paper_type_t paper_type = PAPER_TYPE_A4;
  197 static gboolean output_format_set = FALSE;
  198 static output_format_t output_format = FORMAT_POSTSCRIPT;
  199 static PangoGravity gravity = PANGO_GRAVITY_AUTO;
  200 static PangoGravityHint gravity_hint = PANGO_GRAVITY_HINT_NATURAL;
  201 static PangoWrapMode opt_wrap = PANGO_WRAP_WORD_CHAR;
  202 static cairo_font_face_t *paps_glyph_face = NULL; /* Special face for paps characters, e.g. newline */
  203 static double glyph_font_size = -1;
  204 
  205 /* Render function for paps glyphs */
  206 static cairo_status_t
  207 paps_render_glyph(cairo_scaled_font_t *scaled_font G_GNUC_UNUSED,
  208                   unsigned long glyph,
  209                   cairo_t *cr,
  210                   cairo_text_extents_t *extents)
  211 {
  212   char ch = (unsigned char)glyph;
  213 
  214   if (ch == 'R' || ch == 'L')
  215   {
  216     // A newline sign that I created with MetaPost
  217     cairo_save(cr);
  218     cairo_scale(cr,0.005,-0.005); // TBD - figure out the scaling.
  219     if (ch == 'L')
  220     {
  221       cairo_scale(cr,-1,1);
  222       // cairo_translate(cr,-120,0);  // Keep glyph protruding to the right.
  223     }
  224     cairo_translate(cr, 20,-50);
  225     cairo_move_to(cr, 0, 175);
  226     cairo_curve_to(cr, 25.69278, 175, 53.912, 177.59557, 71.25053, 158.75053);
  227     cairo_curve_to(cr, 103.52599, 123.67075, 64.54437, 77.19373, 34.99985, 34.99985);
  228     cairo_set_line_width(cr, 25);
  229     cairo_stroke(cr);
  230 
  231     cairo_move_to(cr,0,0);
  232     cairo_line_to(cr,75,0);
  233     cairo_line_to(cr,0,75);
  234     cairo_close_path(cr);
  235     cairo_fill(cr);
  236     cairo_restore(cr);
  237   }
  238   return CAIRO_STATUS_SUCCESS;
  239 }
  240 
  241 static gboolean
  242 _paps_arg_paper_cb(const char *option_name,
  243                    const char *value,
  244                    gpointer    data)
  245 {
  246   gboolean retval = TRUE;
  247   
  248   if (value && *value)
  249     {
  250       if (g_ascii_strcasecmp(value, "legal") == 0)
  251         paper_type = PAPER_TYPE_US_LEGAL;
  252       else if (g_ascii_strcasecmp(value, "letter") == 0)
  253         paper_type = PAPER_TYPE_US_LETTER;
  254       else if (g_ascii_strcasecmp(value, "a4") == 0)
  255         paper_type = PAPER_TYPE_A4;
  256       else if (g_ascii_strcasecmp(value, "a3") == 0)
  257         paper_type = PAPER_TYPE_A3;
  258       else {
  259         retval = FALSE;
  260         fprintf(stderr, _("Unknown page size name: %s.\n"), value);
  261       }
  262     }
  263   else
  264     {
  265       fprintf(stderr, _("You must specify page size.\n"));
  266       retval = FALSE;
  267     }
  268   
  269   return retval;
  270 }
  271 
  272 static gboolean
  273 parse_enum (GType       type,
  274             int        *value,
  275             const char *name,
  276             const char *arg,
  277             gpointer    data G_GNUC_UNUSED,
  278             GError **error)
  279 {
  280   char *possible_values = NULL;
  281   gboolean ret;
  282 
  283   ret = pango_parse_enum (type,
  284                           arg,
  285                           value,
  286                           FALSE,
  287                           &possible_values);
  288 
  289   if (!ret && error)
  290     {
  291       g_set_error(error,
  292                   G_OPTION_ERROR,
  293                   G_OPTION_ERROR_BAD_VALUE,
  294                   _("Argument for %1$s must be one of %2$s"),
  295                   name,
  296                   possible_values);
  297     }
  298 
  299   g_free (possible_values);
  300 
  301   return ret;
  302 }
  303 
  304 static gboolean
  305 parse_wrap (const char *name,
  306             const char *arg,
  307             gpointer    data,
  308             GError    **error)
  309 {
  310   return (parse_enum (PANGO_TYPE_WRAP_MODE, (int*)(void*)&opt_wrap,
  311                       name, arg, data, error));
  312 }
  313 
  314 static gboolean
  315 parse_gravity_hint (const char *name,
  316                     const char *arg,
  317                     gpointer    data,
  318                     GError    **error)
  319 {
  320   return (parse_enum (PANGO_TYPE_GRAVITY_HINT, (int*)(void*)&gravity_hint,
  321                       name, arg, data, error));
  322 }
  323 
  324 static gboolean
  325 parse_gravity (const char *name,
  326                const char *arg,
  327                gpointer    data,
  328                GError    **error)
  329 {
  330   return (parse_enum (PANGO_TYPE_GRAVITY, (int*)(void*)&gravity,
  331                       name, arg, data, error));
  332 }
  333 
  334 
  335 static gboolean
  336 _paps_arg_format_cb(const char *option_name,
  337                     const char *value,
  338                     gpointer    data)
  339 {
  340   gboolean retval = TRUE;
  341   
  342   if (value && *value)
  343     {
  344       output_format_set = TRUE;
  345       if (g_ascii_strcasecmp(value, "pdf") == 0)
  346         output_format = FORMAT_PDF;
  347       else if (g_ascii_strcasecmp(value, "ps") == 0
  348                || g_ascii_strcasecmp(value, "postscript") == 0)
  349         output_format = FORMAT_POSTSCRIPT;
  350       else if (g_ascii_strcasecmp(value, "svg") == 0)
  351         output_format = FORMAT_SVG;
  352       else {
  353         retval = FALSE;
  354         fprintf(stderr, _("Unknown output format: %s.\n"), value);
  355       }
  356     }
  357   else
  358     {
  359       fprintf(stderr, _("You must specify a output format.\n"));
  360       retval = FALSE;
  361     }
  362   
  363   return retval;
  364 }
  365 
  366 static gboolean
  367 _paps_arg_lpi_cb(const gchar *option_name,
  368                  const gchar *value,
  369                  gpointer     data)
  370 {
  371   gboolean retval = TRUE;
  372   gchar *p = NULL;
  373   page_layout_t *page_layout = (page_layout_t*)data;
  374 
  375   if (value && *value)
  376     {
  377       errno = 0;
  378       page_layout->lpi = g_strtod(value, &p);
  379       if ((p && *p) || errno == ERANGE)
  380         {
  381           fprintf(stderr, _("Given LPI value was invalid.\n"));
  382           retval = FALSE;
  383         }
  384     }
  385   else
  386     {
  387       fprintf(stderr, _("You must specify the amount of lines per inch.\n"));
  388       retval = FALSE;
  389     }
  390 
  391   return retval;
  392 }
  393 
  394 static gboolean
  395 _paps_arg_cpi_cb(const gchar *option_name,
  396                  const gchar *value,
  397                  gpointer     data)
  398 {
  399   gboolean retval = TRUE;
  400   gchar *p = NULL;
  401   page_layout_t *page_layout = (page_layout_t*)data;
  402   
  403   if (value && *value)
  404     {
  405       errno = 0;
  406       page_layout->cpi = g_strtod(value, &p);
  407       if ((p && *p) || errno == ERANGE)
  408         {
  409           fprintf(stderr, _("Given CPI value was invalid.\n"));
  410           retval = FALSE;
  411         }
  412     }
  413   else
  414     {
  415       fprintf(stderr, _("You must specify the amount of characters per inch.\n"));
  416       retval = FALSE;
  417     }
  418 
  419   return retval;
  420 }
  421 
  422 
  423 /*
  424  * Return codeset name of the environment's locale. Use UTF8 by default
  425  */
  426 static char*
  427 get_encoding()
  428 {
  429   static char *encoding = NULL;
  430 
  431   if (encoding == NULL)
  432     encoding = nl_langinfo(CODESET);
  433 
  434   return encoding;
  435 }
  436 
  437 static cairo_status_t paps_cairo_write_func(void *closure G_GNUC_UNUSED,
  438                                             const unsigned char *data,
  439                                             unsigned int length)
  440 {
  441   fwrite(data,length,1,output_fh);
  442   return CAIRO_STATUS_SUCCESS;
  443 }
  444 
  445 int main(int argc, char *argv[])
  446 {
  447   gboolean do_landscape = FALSE, do_rtl = FALSE, do_justify = FALSE, do_draw_header = FALSE, do_draw_footer=FALSE;
  448   gboolean do_stretch_chars = FALSE;
  449   gboolean do_use_markup = FALSE;
  450   gboolean do_show_wrap = FALSE; /* Whether to show wrap characters */
  451   int num_columns = 1;
  452   int top_margin = MARGIN_TOP, bottom_margin = MARGIN_BOTTOM,
  453       right_margin = MARGIN_RIGHT, left_margin = MARGIN_LEFT;
  454 
  455   gboolean do_fatal_warnings = FALSE;
  456   const gchar *font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE);
  457   gchar *encoding = NULL;
  458   gchar *output = NULL;
  459   gchar *htitle = NULL;
  460   page_layout_t page_layout;
  461   GOptionContext *ctxt = g_option_context_new("[text file]");
  462   GOptionEntry entries[] = {
  463     {"landscape", 0, 0, G_OPTION_ARG_NONE, &do_landscape,
  464      N_("Landscape output. (Default: portrait)"), NULL},
  465     {"columns", 0, 0, G_OPTION_ARG_INT, &num_columns,
  466      N_("Number of columns output. (Default: 1)"), "NUM"},
  467     {"font", 0, 0, G_OPTION_ARG_STRING, &font,
  468      N_("Set font. (Default: Monospace 12)"), "DESC"},
  469     {"output", 'o', 0, G_OPTION_ARG_STRING, &output,
  470      N_("Output file. (Default: stdout)"), "DESC"},
  471     {"rtl", 0, 0, G_OPTION_ARG_NONE, &do_rtl,
  472      N_("Do right-to-left text layout."), NULL},
  473     {"justify", 0, 0, G_OPTION_ARG_NONE, &do_justify,
  474      N_("Justify the layout."), NULL},
  475     {"wrap", 0, 0, G_OPTION_ARG_CALLBACK, &parse_wrap,
  476      N_("Text wrapping mode [word, char, word-char]. (Default: word-char)"), "WRAP"},
  477     {"show-wrap", 0, 0, G_OPTION_ARG_NONE, &do_show_wrap,
  478      N_("Show characters for wrapping."), NULL},
  479     {"paper", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_paper_cb,
  480      N_("Set paper size [legal, letter, a3, a4]. (Default: a4)"), "PAPER"},
  481     {"gravity", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity,
  482      N_("Base glyph rotation [south, west, north, east, auto]. (Defaut: auto)"), "GRAVITY"},
  483     {"gravity-hint", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity_hint,
  484      N_("Base glyph orientation [natural, strong, line]. (Default: natural)"), "HINT"},
  485     {"format", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_format_cb,
  486      N_("Set output format [pdf, svg, ps]. (Default: ps)"), "FORMAT"},
  487     {"bottom-margin", 0, 0, G_OPTION_ARG_INT, &bottom_margin,
  488      N_("Set bottom margin in postscript point units (1/72 inch). (Default: 36)"), "NUM"},
  489     {"top-margin", 0, 0, G_OPTION_ARG_INT, &top_margin,
  490      N_("Set top margin. (Default: 36)"), "NUM"},
  491     {"right-margin", 0, 0, G_OPTION_ARG_INT, &right_margin,
  492      N_("Set right margin. (Default: 36)"), "NUM"},
  493     {"left-margin", 0, 0, G_OPTION_ARG_INT, &left_margin,
  494      N_("Set left margin. (Default: 36)"), "NUM"},
  495     {"header", 0, 0, G_OPTION_ARG_NONE, &do_draw_header,
  496      N_("Draw page header for each page."), NULL},
  497     {"footer", 0, 0, G_OPTION_ARG_NONE, &do_draw_footer,
  498      "Draw page footer for each page.", NULL},
  499     {"title", 0, 0, G_OPTION_ARG_STRING, &htitle,
  500      N_("Title string for page header (Default: filename/stdin)."), "TITLE"},
  501     {"markup", 0, 0, G_OPTION_ARG_NONE, &do_use_markup,
  502      N_("Interpret input text as pango markup."), NULL},
  503     {"encoding", 0, 0, G_OPTION_ARG_STRING, &encoding,
  504      N_("Assume encoding of input text. (Default: UTF-8)"), "ENCODING"},
  505     {"lpi", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_lpi_cb,
  506      N_("Set the amount of lines per inch."), "REAL"},
  507     {"cpi", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_cpi_cb,
  508      N_("Set the amount of characters per inch."), "REAL"},
  509     /*
  510      * not fixed for cairo backend: disable
  511      *
  512     {"stretch-chars", 0, 0, G_OPTION_ARG_NONE, &do_stretch_chars,
  513      N_("Stretch characters in y-direction to fill lines."), NULL},
  514      */
  515     {"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &do_fatal_warnings,
  516      N_("Make all glib warnings fatal."), "REAL"},
  517 
  518     {NULL}
  519 
  520   };
  521   GError *error = NULL;
  522   FILE *IN = NULL;
  523   GList *paragraphs;
  524   GList *pango_lines;
  525   PangoContext *pango_context;
  526   PangoFontDescription *font_description;
  527   PangoDirection pango_dir = PANGO_DIRECTION_LTR;
  528   PangoFontMap *fontmap;
  529   PangoFontset *fontset;
  530   PangoFontMetrics *metrics;
  531   int gutter_width = 40;
  532   int total_gutter_width;
  533   double page_width = paper_sizes[0].width;
  534   double page_height = paper_sizes[0].height;
  535   int do_tumble = -1;   /* -1 means not initialized */
  536   int do_duplex = -1;
  537   const gchar *header_font_desc = MAKE_FONT_NAME (HEADER_FONT_FAMILY, HEADER_FONT_SCALE);
  538   const gchar *filename_in;
  539   gchar *text;
  540   int header_sep = 20;
  541   int max_width = 0, w;
  542   GOptionGroup *options;
  543   cairo_t *cr;
  544   cairo_surface_t *surface = NULL;
  545   double surface_page_width = 0, surface_page_height = 0;
  546 
  547   /* Set locale from environment */
  548   (void) setlocale(LC_ALL, "");
  549 
  550   /* Setup i18n */
  551   textdomain(GETTEXT_PACKAGE);
  552   bindtextdomain(GETTEXT_PACKAGE, DATADIR "/locale");
  553   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  554 
  555   /* Setup the paps glyph face */
  556   paps_glyph_face = cairo_user_font_face_create();
  557   cairo_user_font_face_set_render_glyph_func(paps_glyph_face, paps_render_glyph);
  558 
  559   /* Init page_layout_t parameters set by the option parsing */
  560   page_layout.cpi = page_layout.lpi = 0;
  561 
  562   options = g_option_group_new("main","","",&page_layout, NULL);
  563   g_option_group_add_entries(options, entries);
  564   g_option_group_set_translation_domain(options, GETTEXT_PACKAGE);
  565   g_option_context_set_main_group(ctxt, options);
  566 #if 0
  567   g_option_context_add_main_entries(ctxt, entries, NULL);
  568 #endif
  569   
  570   /* Parse command line */
  571   if (!g_option_context_parse(ctxt, &argc, &argv, &error))
  572     {
  573       fprintf(stderr, _("Command line error: %s\n"), error->message);
  574       exit(1);
  575     }
  576   
  577   if (do_fatal_warnings)
  578     g_log_set_always_fatal(G_LOG_LEVEL_MASK);
  579 
  580   if (do_rtl)
  581     pango_dir = PANGO_DIRECTION_RTL;
  582   
  583   if (argc > 1)
  584     {
  585       filename_in = argv[1];
  586       IN = fopen(filename_in, "r");
  587       if (!IN)
  588         {
  589           fprintf(stderr, _("Failed to open %s!\n"), filename_in);
  590           exit(1);
  591         }
  592     }
  593   else
  594     {
  595       filename_in = "stdin";
  596       IN = stdin;
  597     }
  598 
  599   // For now always write to stdout
  600   if (output == NULL)
  601     output_fh = stdout;
  602   else
  603     {
  604       output_fh = fopen(output,"wb");
  605       if (!output_fh)
  606         {
  607           fprintf(stderr, _("Failed to open %s for writing!\n"), output);
  608           exit(1);
  609         }
  610     }
  611 
  612   /* Page layout */
  613   page_width = paper_sizes[(int)paper_type].width;
  614   page_height = paper_sizes[(int)paper_type].height;
  615 
  616   /* Deduce output format from file name if not explicitely set */
  617   if (!output_format_set && output != NULL)
  618     {
  619       if (g_str_has_suffix(output, ".svg") || g_str_has_suffix(output, ".SVG"))
  620         output_format = FORMAT_SVG;
  621       else if (g_str_has_suffix(output, ".pdf") || g_str_has_suffix(output, ".PDF"))
  622         output_format = FORMAT_PDF;
  623       /* Otherwise keep postscript default */
  624     }
  625   
  626   /* Swap width and height for landscape except for postscript */
  627   surface_page_width = page_width;
  628   surface_page_height = page_height;
  629   if (output_format != FORMAT_POSTSCRIPT && do_landscape)
  630     {
  631       surface_page_width = page_height;
  632       surface_page_height = page_width;
  633     }
  634         
  635   if (output_format == FORMAT_POSTSCRIPT)
  636     surface = cairo_ps_surface_create_for_stream(&paps_cairo_write_func,
  637                                                  NULL,
  638                                                  surface_page_width,
  639                                                  surface_page_height);
  640   else if (output_format == FORMAT_PDF)
  641     surface = cairo_pdf_surface_create_for_stream(&paps_cairo_write_func,
  642                                                   NULL,
  643                                                   surface_page_width,
  644                                                   surface_page_height);
  645   else 
  646     surface = cairo_svg_surface_create_for_stream(&paps_cairo_write_func,
  647                                                   NULL,
  648                                                   surface_page_width,
  649                                                   surface_page_height);
  650 
  651   cr = cairo_create(surface);
  652 
  653   pango_context = pango_cairo_create_context(cr);
  654   pango_cairo_context_set_resolution(pango_context, 72.0); /* Native postscript resolution */
  655   
  656   /* Setup pango */
  657   pango_context_set_base_dir (pango_context, pango_dir);
  658   pango_context_set_language (pango_context, pango_language_get_default ());
  659   pango_context_set_base_gravity (pango_context, gravity);
  660   pango_context_set_gravity_hint (pango_context, gravity_hint);
  661   
  662   /* create the font description */
  663   font_description = pango_font_description_from_string (font);
  664   if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_FAMILY) == 0)
  665     pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY);
  666   if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_SIZE) == 0)
  667     pango_font_description_set_size (font_description, atoi(DEFAULT_FONT_SIZE) * PANGO_SCALE);
  668 
  669   // Keep the font size for the wrap character.
  670   glyph_font_size = pango_font_description_get_size(font_description) / PANGO_SCALE;
  671   pango_context_set_font_description (pango_context, font_description);
  672 
  673   if (num_columns <= 0) {
  674     fprintf(stderr, _("%s: Invalid input: --columns=%d, using default.\n"), g_get_prgname (), num_columns);
  675     num_columns = 1;
  676   }
  677 
  678   if (num_columns == 1)
  679     total_gutter_width = 0;
  680   else
  681     total_gutter_width = gutter_width * (num_columns - 1);
  682   if (do_landscape)
  683     {
  684       double tmp;
  685       tmp = page_width;
  686       page_width = page_height;
  687       page_height = tmp;
  688       if (do_tumble < 0)
  689         do_tumble = TRUE;
  690       if (do_duplex < 0)
  691         do_duplex = TRUE;
  692     }
  693   else
  694     {
  695       if (do_tumble < 0)
  696         do_tumble = TRUE;
  697       if (do_duplex < 0)
  698         do_duplex = TRUE;
  699     }
  700   
  701   page_layout.page_width = page_width;
  702   page_layout.page_height = page_height;
  703   page_layout.num_columns = num_columns;
  704   page_layout.left_margin = left_margin;
  705   page_layout.right_margin = right_margin;
  706   page_layout.gutter_width = gutter_width;
  707   page_layout.top_margin = top_margin;
  708   page_layout.bottom_margin = bottom_margin;
  709   page_layout.header_ypos = page_layout.top_margin;
  710   page_layout.header_height = 0;
  711   page_layout.footer_height = 0;
  712   page_layout.do_show_wrap = do_show_wrap;
  713   page_layout.scale_x = 1.0L;
  714   page_layout.scale_y = 1.0L;
  715   if (do_draw_header)
  716       page_layout.header_sep =  0; // header_sep;
  717   else
  718       page_layout.header_sep = 0;
  719     
  720   page_layout.column_height = (int)page_height
  721                             - page_layout.top_margin
  722                             - page_layout.header_sep
  723                             - page_layout.bottom_margin;
  724   page_layout.column_width =  ((int)page_layout.page_width
  725                             - page_layout.left_margin - page_layout.right_margin
  726                             - total_gutter_width) / page_layout.num_columns;
  727   page_layout.do_separation_line = TRUE;
  728   page_layout.do_landscape = do_landscape;
  729   page_layout.do_justify = do_justify;
  730   page_layout.do_stretch_chars = do_stretch_chars;
  731   page_layout.do_use_markup = do_use_markup;
  732   page_layout.do_tumble = do_tumble;
  733   page_layout.do_duplex = do_duplex;
  734   page_layout.pango_dir = pango_dir;
  735   if (htitle)
  736      page_layout.title = htitle;
  737   else
  738      page_layout.title = basename((char *)filename_in);
  739   page_layout.header_font_desc = header_font_desc;
  740 
  741   /* calculate x-coordinate scale */
  742   if (page_layout.cpi > 0.0L)
  743     {
  744       gint font_size;
  745 
  746       fontmap = pango_ft2_font_map_new ();
  747       fontset = pango_font_map_load_fontset (fontmap, pango_context, font_description, pango_language_get_default());
  748       metrics = pango_fontset_get_metrics (fontset);
  749       max_width = pango_font_metrics_get_approximate_char_width (metrics);
  750       w = pango_font_metrics_get_approximate_digit_width (metrics);
  751       if (w > max_width)
  752           max_width = w;
  753       page_layout.scale_x = 1 / page_layout.cpi * 72.0 * (gdouble)PANGO_SCALE / (gdouble)max_width;
  754       pango_font_metrics_unref (metrics);
  755       g_object_unref (G_OBJECT (fontmap));
  756 
  757       font_size = pango_font_description_get_size (font_description);
  758       // update the font size to that width
  759       pango_font_description_set_size (font_description, (int)(font_size * page_layout.scale_x));
  760       glyph_font_size = font_size * page_layout.scale_x / PANGO_SCALE;
  761       pango_context_set_font_description (pango_context, font_description);
  762     }
  763 
  764   page_layout.scale_x = page_layout.scale_y = 1.0;
  765 
  766   if (encoding == NULL)
  767     encoding = get_encoding();
  768 
  769   text = read_file(IN, encoding);
  770 
  771   if (output_format == FORMAT_POSTSCRIPT)
  772     postscript_dsc_comments(surface, &page_layout);
  773 
  774   paragraphs = split_text_into_paragraphs(cr,
  775                                           pango_context,
  776                                           &page_layout,
  777                                           page_layout.column_width, 
  778                                           text);
  779   pango_lines = split_paragraphs_into_lines(&page_layout, paragraphs);
  780 
  781   cairo_scale(cr, page_layout.scale_x, page_layout.scale_y);
  782 
  783   output_pages(surface, cr, pango_lines, &page_layout, do_draw_header, pango_context);
  784 
  785   cairo_destroy (cr);
  786   cairo_surface_finish (surface);
  787   cairo_surface_destroy(surface);
  788   g_option_context_free(ctxt);
  789 
  790   return 0;
  791 }
  792 
  793 
  794 /* Read an entire file into a string
  795  */
  796 static char *
  797 read_file (FILE   *file,
  798            gchar  *encoding)
  799 {
  800   GString *inbuf;
  801   char *text;
  802   char buffer[BUFSIZE];
  803   GIConv cvh = NULL;
  804   gsize inc_seq_bytes = 0;
  805 
  806 
  807   if (encoding != NULL)
  808     {
  809       cvh = g_iconv_open ("UTF-8", encoding);
  810       if (cvh == (GIConv)-1)
  811         {
  812           fprintf(stderr, _("%s: Invalid encoding: %s\n"), g_get_prgname (), encoding);
  813           exit(1);
  814         }
  815     }
  816 
  817   inbuf = g_string_new (NULL);
  818   while (1)
  819     {
  820       char *ib, *ob, obuffer[BUFSIZE * 6], *bp;
  821       gsize iblen, ibleft, oblen;
  822 
  823       bp = fgets (buffer+inc_seq_bytes, BUFSIZE-inc_seq_bytes-1, file);
  824       if (inc_seq_bytes)
  825         inc_seq_bytes = 0;
  826 
  827       if (ferror (file))
  828         {
  829           fprintf(stderr, _("%s: Error reading file.\n"), g_get_prgname ());
  830           g_string_free (inbuf, TRUE);
  831           exit(1);
  832         }
  833       else if (bp == NULL)
  834         break;
  835 
  836       if (cvh != NULL)
  837         {
  838           ib = buffer;
  839           iblen = strlen (ib);
  840           ob = bp = obuffer;
  841           oblen = BUFSIZE * 6 - 1;
  842           if (g_iconv (cvh, &ib, &iblen, &ob, &oblen) == (gsize)-1)
  843             {
  844               /*
  845                * EINVAL - incomplete sequence at the end of the buffer. Move the
  846                * incomplete sequence bytes to the beginning of the buffer for
  847                * the next round of conversion.
  848                */
  849               if (errno == EINVAL)
  850                 {
  851                   inc_seq_bytes = iblen;
  852                   memmove (buffer, ib, inc_seq_bytes);
  853                 }
  854               else
  855                 {
  856                   fprintf (stderr, _("%1$s: Error while converting input from '%2$s' to UTF-8.\n"),
  857                     g_get_prgname(), encoding);
  858                   exit(1);
  859                 }
  860              }
  861           obuffer[BUFSIZE * 6 - 1 - oblen] = 0;
  862         }
  863       g_string_append (inbuf, bp);
  864     }
  865 
  866   fclose (file);
  867 
  868   /* Add a trailing new line if it is missing */
  869   if (inbuf->len && inbuf->str[inbuf->len-1] != '\n')
  870     g_string_append(inbuf, "\n");
  871 
  872   text = inbuf->str;
  873   g_string_free (inbuf, FALSE);
  874 
  875   if (encoding != NULL && cvh != NULL)
  876     g_iconv_close(cvh);
  877 
  878   return text;
  879 }
  880 
  881 
  882 /* Take a UTF8 string and break it into paragraphs on \n characters
  883  */
  884 static GList *
  885 split_text_into_paragraphs (cairo_t *cr,
  886                             PangoContext *pango_context,
  887                             page_layout_t *page_layout,
  888                             int paint_width,  /* In pixels */
  889                             const char *text)
  890 {
  891   const char *p = text;
  892   char *next;
  893   gunichar wc;
  894   GList *result = NULL;
  895   const char *last_para = text;
  896 
  897   /* If we are using markup we treat the entire text as a single paragraph.
  898    * I tested it and found that this is much slower than the split and
  899    * assign method used below. Otherwise we might as well use this single
  900    * chunk method always.
  901    */
  902   if (page_layout->do_use_markup)
  903     {
  904       Paragraph *para = g_new (Paragraph, 1);
  905       para->wrapped = FALSE; /* No wrapped chars for markups */
  906       para->clipped = FALSE;
  907       para->text = text;
  908       para->length = strlen(text);
  909       para->layout = pango_layout_new (pango_context);
  910       pango_layout_set_markup (para->layout, para->text, para->length);
  911       pango_layout_set_justify (para->layout, page_layout->do_justify);
  912       pango_layout_set_alignment (para->layout,
  913                                   page_layout->pango_dir == PANGO_DIRECTION_LTR
  914                                       ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT);
  915 
  916       pango_layout_set_width (para->layout, paint_width * PANGO_SCALE);
  917       pango_layout_set_wrap (para->layout, opt_wrap);
  918 
  919       para->height = 0;
  920       
  921       result = g_list_prepend (result, para);
  922     }
  923   else
  924     {
  925 
  926       while (p != NULL && *p)
  927         {
  928           wc = g_utf8_get_char (p);
  929           next = g_utf8_next_char (p);
  930           if (wc == (gunichar)-1)
  931             {
  932               fprintf (stderr, _("%s: Invalid character in input\n"), g_get_prgname ());
  933               wc = 0;
  934             }
  935           if (!*p || !wc || wc == '\r' || wc == '\n' || wc == '\f')
  936             {
  937               Paragraph *para = g_new (Paragraph, 1);
  938               para->wrapped = FALSE;
  939               para->clipped = FALSE;
  940               para->text = last_para;
  941               para->length = p - last_para;
  942               /* handle dos line breaks */
  943               if (wc == '\r' && *next == '\n')
  944                   next = g_utf8_next_char(next);
  945               para->layout = pango_layout_new (pango_context);
  946               if (page_layout->cpi > 0.0L)
  947                 {
  948                   /* figuring out the correct width from the pango_font_metrics_get_approximate_width()
  949                    * is really hard and pango_layout_set_wrap() doesn't work properly then.
  950                    * Those are not reliable to render the characters exactly according to the given CPI.
  951                    * So re-calculate the width to wrap up to be comfortable with CPI.
  952                    */
  953                   wchar_t *wtext = NULL, *wnewtext = NULL;
  954                   gchar *newtext = NULL;
  955                   gsize len, col, i, wwidth = 0;
  956                   PangoRectangle ink_rect, logical_rect;
  957 
  958                   wtext = (wchar_t *)g_utf8_to_ucs4 (para->text, para->length, NULL, NULL, NULL);
  959                   if (wtext == NULL)
  960                     {
  961                       fprintf (stderr, _("%s: Unable to convert UTF-8 to UCS-4.\n"), g_get_prgname ());
  962                     fail:
  963                       g_free (wtext);
  964                       g_free (wnewtext);
  965                       g_free (newtext);
  966                       exit (1);
  967                     }
  968                   len = g_utf8_strlen (para->text, para->length);
  969                   /* the amount of characters that can be put on the line against CPI */
  970                   col = (int)(page_layout->column_width / 72.0 * page_layout->cpi);
  971                   if (len > col)
  972                     {
  973                       /* need to wrap them up */
  974                       wnewtext = g_new (wchar_t, wcslen (wtext) + 1);
  975                       para->clipped = TRUE;
  976                       if (wnewtext == NULL)
  977                         {
  978                           fprintf (stderr, _("%s: Unable to allocate the memory.\n"), g_get_prgname ());
  979                           goto fail;
  980                         }
  981                       for (i = 0; i < len; i++)
  982                         {
  983                           gssize w = wcwidth (wtext[i]);
  984 
  985                           if (w >= 0)
  986                             wwidth += w;
  987                           if (wwidth > col)
  988                             break;
  989                           wnewtext[i] = wtext[i];
  990                         }
  991                       wnewtext[i] = 0L;
  992 
  993                       newtext = g_ucs4_to_utf8 ((const gunichar *)wnewtext, i, NULL, NULL, NULL);
  994                       if (newtext == NULL)
  995                         {
  996                           fprintf (stderr, _("%s: Unable to convert UCS-4 to UTF-8.\n"), g_get_prgname ());
  997                           goto fail;
  998                         }
  999                       pango_layout_set_text (para->layout, newtext, -1);
 1000                       pango_layout_get_extents (para->layout, &ink_rect, &logical_rect);
 1001                       paint_width = logical_rect.width / PANGO_SCALE;
 1002                       g_free (wnewtext);
 1003                       g_free (newtext);
 1004 
 1005                       para->length = i;
 1006                       next = g_utf8_offset_to_pointer (para->text, para->length);
 1007                       wc = g_utf8_get_char (g_utf8_prev_char (next));
 1008                     }
 1009                   else
 1010                     {
 1011                       pango_layout_set_text (para->layout, para->text, para->length);
 1012                     }
 1013 
 1014                   g_free (wtext);
 1015 
 1016                   pango_layout_set_width (para->layout, -1);
 1017                 }
 1018               else
 1019                 {
 1020                   pango_layout_set_text (para->layout, para->text, para->length);
 1021                   pango_layout_set_width (para->layout, paint_width * PANGO_SCALE);
 1022 
 1023                   pango_layout_set_wrap (para->layout, opt_wrap);
 1024 
 1025                   if (opt_wrap == PANGO_WRAP_CHAR)
 1026                       para->wrapped = TRUE;                    
 1027 
 1028                   /* Should we support truncation as well? */
 1029                 }
 1030                   
 1031               pango_layout_set_justify (para->layout, page_layout->do_justify);
 1032               pango_layout_set_alignment (para->layout,
 1033                                           page_layout->pango_dir == PANGO_DIRECTION_LTR
 1034                                           ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT);
 1035 
 1036               para->height = 0;
 1037 
 1038               last_para = next;
 1039             
 1040               if (wc == '\f')
 1041                 para->formfeed = 1;
 1042               else
 1043                 para->formfeed = 0;
 1044 
 1045               result = g_list_prepend (result, para);
 1046             }
 1047           if (!wc) /* incomplete character at end */
 1048             break;
 1049           p = next;
 1050         }
 1051     }
 1052 
 1053   return g_list_reverse (result);
 1054 }
 1055 
 1056 
 1057 
 1058 /* Split a list of paragraphs into a list of lines.
 1059  */
 1060 GList *
 1061 split_paragraphs_into_lines(page_layout_t *page_layout,
 1062                             GList         *paragraphs)
 1063 {
 1064   GList *line_list = NULL;
 1065   int max_height = 0;
 1066   /* Read the file */
 1067 
 1068   /* Now split all the pagraphs into lines */
 1069   GList *par_list;
 1070 
 1071   par_list = paragraphs;
 1072   while(par_list)
 1073     {
 1074       int para_num_lines, i;
 1075       LineLink *line_link;
 1076       Paragraph *para = par_list->data;
 1077 
 1078       para_num_lines = pango_layout_get_line_count(para->layout);
 1079 
 1080       for (i=0; i<para_num_lines; i++)
 1081         {
 1082           PangoRectangle logical_rect, ink_rect;
 1083           
 1084           line_link = g_new(LineLink, 1);
 1085           line_link->formfeed = 0;
 1086           line_link->wrapped = (para->wrapped && i < para_num_lines - 1) || (para->clipped);
 1087           line_link->pango_line = pango_layout_get_line(para->layout, i);
 1088           pango_layout_line_get_extents(line_link->pango_line,
 1089                                         &ink_rect, &logical_rect);
 1090           line_link->logical_rect = logical_rect;
 1091           if (para->formfeed && i == (para_num_lines - 1))
 1092               line_link->formfeed = 1;
 1093           line_link->ink_rect = ink_rect;
 1094           line_list = g_list_prepend(line_list, line_link);
 1095           if (logical_rect.height > max_height)
 1096               max_height = logical_rect.height;
 1097         }
 1098 
 1099       par_list = par_list->next;
 1100     }
 1101   
 1102   /*
 1103    * not fixed for cairo backend: disable
 1104    *
 1105   if (page_layout->do_stretch_chars && page_layout->lpi > 0.0L)
 1106       page_layout->scale_y = 1.0 / page_layout->lpi * 72.0 * PANGO_SCALE / max_height;
 1107    */
 1108 
 1109   return g_list_reverse(line_list);
 1110   
 1111 }
 1112 
 1113 
 1114 /*
 1115  * Define PostScript document header information.
 1116  */
 1117 void
 1118 postscript_dsc_comments(cairo_surface_t *surface, page_layout_t *pl)
 1119 {
 1120   char buf[CAIRO_COMMENT_MAX];
 1121   int x, y;
 1122 
 1123   /*
 1124    * Title
 1125    */
 1126   snprintf(buf, CAIRO_COMMENT_MAX, "%%%%Title: %s", pl->title);
 1127   cairo_ps_surface_dsc_comment (surface, buf);
 1128 
 1129   /*
 1130    * Orientation
 1131    */
 1132   if (pl->do_landscape)
 1133     {
 1134       cairo_ps_surface_dsc_comment (surface, "%%Orientation: Landscape");
 1135       x = (int)pl->page_height;
 1136       y = (int)pl->page_width;
 1137     }
 1138   else
 1139     {
 1140       cairo_ps_surface_dsc_comment (surface, "%%Orientation: Portrait");
 1141       x = (int)pl->page_width;
 1142       y = (int)pl->page_height;
 1143     }
 1144 
 1145   /*
 1146    * Redefine BoundingBox to cover the whole paper. Cairo creates the entry
 1147    * based on the text only. This may affect further processing, such as with
 1148    * convert(1).
 1149    */
 1150   snprintf(buf, CAIRO_COMMENT_MAX, "%%%%BoundingBox: 0 0 %d %d", x, y);
 1151   cairo_ps_surface_dsc_comment (surface, buf);
 1152 
 1153   /*
 1154    * Duplex
 1155    */
 1156   if (pl->do_duplex)
 1157     {
 1158       cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex");
 1159       cairo_ps_surface_dsc_begin_setup(surface);
 1160 
 1161       if (pl->do_tumble)
 1162         cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexTumble");
 1163       else
 1164         cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexNoTumble");
 1165     }
 1166 }
 1167 
 1168 
 1169 int
 1170 output_pages(cairo_surface_t *surface,
 1171              cairo_t       *cr,
 1172              GList         *pango_lines,
 1173              page_layout_t *page_layout,
 1174              gboolean       need_header,
 1175              PangoContext  *pango_context)
 1176 {
 1177   int column_idx = 0;
 1178   int column_y_pos = 0;
 1179   int page_idx = 1;
 1180   int pango_column_height = page_layout->column_height * PANGO_SCALE;
 1181   int height = 0;
 1182   LineLink *prev_line_link = NULL;
 1183 
 1184   start_page(surface, cr, page_layout);
 1185 
 1186   if (need_header)
 1187     draw_page_header_line_to_page(cr, TRUE, page_layout, pango_context, page_idx);
 1188 
 1189   while(pango_lines)
 1190     {
 1191       LineLink *line_link = pango_lines->data;
 1192       PangoLayoutLine *line = line_link->pango_line;
 1193       gboolean draw_wrap_character = page_layout->do_show_wrap && line_link->wrapped;
 1194       
 1195       /* Check if we need to move to next column */
 1196       if ((column_y_pos + line_link->logical_rect.height
 1197            >= pango_column_height) ||
 1198           (prev_line_link && prev_line_link->formfeed))
 1199         {
 1200           column_idx++;
 1201           column_y_pos = 0;
 1202           if (column_idx == page_layout->num_columns)
 1203             {
 1204               column_idx = 0;
 1205               eject_page(cr);
 1206               page_idx++;
 1207               start_page(surface, cr, page_layout);
 1208 
 1209               if (need_header)
 1210                 draw_page_header_line_to_page(cr, TRUE, page_layout, pango_context, page_idx);
 1211             }
 1212           else
 1213             {
 1214               eject_column(cr,
 1215                            page_layout,
 1216                            column_idx
 1217                            );
 1218             }
 1219         }
 1220       if (page_layout->lpi > 0.0L)
 1221         height = (int)(1.0 / page_layout->lpi * 72.0 * PANGO_SCALE);
 1222       else
 1223         height = line_link->logical_rect.height;
 1224       draw_line_to_page(cr,
 1225                         column_idx,
 1226                         column_y_pos+height,
 1227                         page_layout,
 1228                         line,
 1229                         draw_wrap_character);
 1230       column_y_pos += height;
 1231       pango_lines = pango_lines->next;
 1232       prev_line_link = line_link;
 1233     }
 1234   eject_page(cr);
 1235   return page_idx;
 1236 }
 1237 
 1238 void eject_column(cairo_t *cr,
 1239                   page_layout_t *page_layout,
 1240                   int column_idx)
 1241 {
 1242   double x_pos, y_top, y_bot, total_gutter;
 1243 
 1244 #if 0
 1245   fprintf(stderr, "do_separation_line column_idx = %d %d\n", page_layout->do_separation_line, column_idx);
 1246 #endif
 1247   if (!page_layout->do_separation_line)
 1248     return;
 1249 
 1250   if (page_layout->pango_dir == PANGO_DIRECTION_RTL)
 1251     column_idx = (page_layout->num_columns - column_idx);
 1252 
 1253   if (column_idx == 1)
 1254     total_gutter = 1.0 * page_layout->gutter_width /2;
 1255   else
 1256     total_gutter = (column_idx - 0.5) * page_layout->gutter_width;
 1257       
 1258   x_pos = page_layout->left_margin
 1259         + page_layout->column_width * column_idx
 1260       + total_gutter;
 1261 
 1262   y_top = page_layout->top_margin + page_layout->header_height + page_layout->header_sep / 2;
 1263   y_bot = page_layout->page_height - page_layout->bottom_margin - page_layout->footer_height;
 1264 
 1265   cairo_move_to(cr,x_pos, y_top);
 1266   cairo_line_to(cr,x_pos, y_bot);
 1267   cairo_set_line_width(cr, 0.1);
 1268   cairo_stroke(cr);
 1269 }
 1270 
 1271 void eject_page(cairo_t *cr)
 1272 {
 1273   cairo_show_page(cr);
 1274 }
 1275 
 1276 void start_page(cairo_surface_t *surface,
 1277                 cairo_t *cr,
 1278                 page_layout_t *page_layout)
 1279 {
 1280   cairo_identity_matrix(cr);
 1281 
 1282   if (output_format == FORMAT_POSTSCRIPT)
 1283     cairo_ps_surface_dsc_begin_page_setup (surface);
 1284 
 1285   if (page_layout->do_landscape)
 1286     {
 1287       if (output_format == FORMAT_POSTSCRIPT)
 1288         {
 1289           cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Landscape");
 1290           cairo_translate(cr, 0, page_layout->page_width);
 1291           cairo_rotate(cr, 3*M_PI/2);
 1292         }
 1293     }
 1294   else
 1295     {
 1296       if (output_format == FORMAT_POSTSCRIPT)
 1297         cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Portrait");
 1298     }
 1299 }
 1300 
 1301 void
 1302 draw_line_to_page(cairo_t *cr,
 1303                   int column_idx,
 1304                   int column_pos,
 1305                   page_layout_t *page_layout,
 1306                   PangoLayoutLine *line,
 1307                   gboolean draw_wrap_character)
 1308 {
 1309   /* Assume square aspect ratio for now */
 1310   double y_pos = page_layout->top_margin
 1311                + page_layout->header_sep
 1312                + column_pos / PANGO_SCALE;
 1313   double x_pos = page_layout->left_margin
 1314                + column_idx * (page_layout->column_width
 1315                                + page_layout->gutter_width);
 1316   PangoRectangle ink_rect, logical_rect;
 1317 
 1318   /* Do RTL column layout for RTL direction */
 1319   if (page_layout->pango_dir == PANGO_DIRECTION_RTL)
 1320     {
 1321       x_pos = page_layout->left_margin
 1322         + (page_layout->num_columns-1-column_idx)
 1323         * (page_layout->column_width + page_layout->gutter_width);
 1324     }
 1325   
 1326   pango_layout_line_get_extents(line,
 1327                                 &ink_rect,
 1328                                 &logical_rect);
 1329 
 1330   if (page_layout->pango_dir == PANGO_DIRECTION_RTL) {
 1331       x_pos += page_layout->column_width  - logical_rect.width / PANGO_SCALE;
 1332   }
 1333 
 1334   cairo_move_to(cr, x_pos, y_pos);
 1335   pango_cairo_show_layout_line(cr, line);
 1336 
 1337   if (draw_wrap_character)
 1338     {
 1339       cairo_set_font_face(cr, paps_glyph_face);
 1340       cairo_set_font_size(cr, glyph_font_size);
 1341 
 1342       if (page_layout->pango_dir == PANGO_DIRECTION_LTR)
 1343         {
 1344           cairo_move_to(cr, x_pos + page_layout->column_width, y_pos);
 1345           cairo_show_text(cr, "R");
 1346         }
 1347       else
 1348         {
 1349           double left_margin = page_layout->left_margin
 1350             + (page_layout->num_columns-1-column_idx)
 1351             * (page_layout->column_width + page_layout->gutter_width);
 1352 
 1353           cairo_move_to(cr, left_margin, y_pos); 
 1354           cairo_show_text(cr, "L");
 1355         }
 1356     }
 1357 }
 1358 
 1359 /*
 1360  * Provide date string from current locale converted to UTF-8.
 1361  */
 1362 char *
 1363 get_date(char *date, int maxlen)
 1364 {
 1365   time_t t;
 1366   GIConv cvh = NULL;
 1367   GString *inbuf;
 1368   char *ib, *ob, obuffer[BUFSIZE * 6], *bp;
 1369   gsize iblen, oblen;
 1370   static char *date_utf8 = NULL;
 1371 
 1372   if (date_utf8 == NULL) {
 1373     t = time(NULL);
 1374     strftime(date, maxlen, "%c", localtime(&t));
 1375 
 1376     cvh = g_iconv_open("UTF-8", get_encoding());
 1377     if (cvh == (GIConv)-1) {
 1378       fprintf(stderr, _("%s: Invalid encoding: %s\n"), g_get_prgname(), get_encoding());
 1379       exit(1);
 1380     }
 1381 
 1382     inbuf = g_string_new(NULL);
 1383     ib = bp = date;
 1384     iblen = strlen(ib);
 1385     ob = bp = obuffer;
 1386     oblen = BUFSIZE * 6 - 1;
 1387 
 1388     if (g_iconv(cvh, &ib, &iblen, &ob, &oblen) == (gsize)-1) {
 1389       fprintf(stderr, _("%1$s: Error while converting date string from '%2$s' to UTF-8.\n"),
 1390         g_get_prgname(), get_encoding());
 1391       /* Return the unconverted string. */
 1392       g_string_free(inbuf, FALSE);
 1393       g_iconv_close(cvh);
 1394       return date;
 1395     }
 1396 
 1397     obuffer[BUFSIZE * 6 - 1 - oblen] = 0;
 1398     g_string_append(inbuf, bp);
 1399 
 1400     date_utf8 = inbuf->str;
 1401     g_string_free(inbuf, FALSE);
 1402     g_iconv_close(cvh);
 1403   }
 1404 
 1405   return date_utf8;
 1406 }
 1407 
 1408 int
 1409 draw_page_header_line_to_page(cairo_t         *cr,
 1410                               gboolean         is_footer,
 1411                               page_layout_t   *page_layout,
 1412                               PangoContext    *ctx,
 1413                               int              page)
 1414 {
 1415   PangoLayout *layout = pango_layout_new(ctx);
 1416   PangoLayoutLine *line;
 1417   PangoRectangle ink_rect, logical_rect, pagenum_rect;
 1418   /* Assume square aspect ratio for now */
 1419   double x_pos, y_pos;
 1420   gchar *header, date[256];
 1421   int height;
 1422   gdouble line_pos;
 1423 
 1424   /* Reset gravity?? */
 1425 #if 0
 1426   header = g_strdup_printf("<span font_desc=\"%s\">%s</span>\n"
 1427                            "<span font_desc=\"%s\">%s</span>\n"
 1428                            "<span font_desc=\"%s\">%d</span>",
 1429                            page_layout->header_font_desc,
 1430                            page_layout->title,
 1431                            page_layout->header_font_desc,
 1432                            get_date(date, 255),
 1433                            page_layout->header_font_desc,
 1434                            page);
 1435 #endif
 1436   header = g_strdup_printf("<span font_desc=\"%s\">%d</span>\n",
 1437                            page_layout->header_font_desc,
 1438                            page);
 1439 
 1440   pango_layout_set_markup(layout, header, -1);
 1441   g_free(header);
 1442 
 1443   /* output a left edge of header/footer */
 1444   line = pango_layout_get_line(layout, 0);
 1445   pango_layout_line_get_extents(line,
 1446                                 &ink_rect,
 1447                                 &logical_rect);
 1448   x_pos = page_layout->left_margin + (page_layout->page_width-page_layout->left_margin-page_layout->right_margin)*0.5 - 0.5*logical_rect.width/PANGO_SCALE;
 1449   height = logical_rect.height / PANGO_SCALE /3.0;
 1450 
 1451   /* The header is placed right after the margin */
 1452   if (is_footer)
 1453     {
 1454       y_pos = page_layout->page_height - page_layout->bottom_margin*0.5;
 1455       page_layout->footer_height = height;
 1456     }
 1457   else
 1458     {
 1459       y_pos = page_layout->top_margin + height;
 1460       page_layout->header_height = height;
 1461     }
 1462 
 1463   cairo_move_to(cr, x_pos,y_pos);
 1464   pango_cairo_show_layout_line(cr,line);
 1465 
 1466   /* output a right edge of header/footer */
 1467   line = pango_layout_get_line(layout, 2);
 1468   pango_layout_line_get_extents(line,
 1469                                 &ink_rect,
 1470                                 &logical_rect);
 1471   pagenum_rect = logical_rect;
 1472   x_pos = page_layout->page_width - page_layout->right_margin - (logical_rect.width / PANGO_SCALE );
 1473   cairo_move_to(cr, x_pos,y_pos);
 1474   pango_cairo_show_layout_line(cr,line);
 1475 
 1476   /* output a "center" of header/footer */
 1477   line = pango_layout_get_line(layout, 1);
 1478   pango_layout_line_get_extents(line,
 1479                                 &ink_rect,
 1480                                 &logical_rect);
 1481   x_pos = page_layout->page_width - page_layout->right_margin -
 1482       ((logical_rect.width + pagenum_rect.width) / PANGO_SCALE + page_layout->gutter_width);
 1483   cairo_move_to(cr, x_pos,y_pos);
 1484   pango_cairo_show_layout_line(cr,line);
 1485 
 1486   g_object_unref(layout);
 1487 
 1488   /* header separator */
 1489 #if 0
 1490   line_pos = page_layout->top_margin + page_layout->header_height + page_layout->header_sep / 2;
 1491   cairo_move_to(cr, page_layout->left_margin, line_pos);
 1492   cairo_line_to(cr,page_layout->page_width - page_layout->right_margin, line_pos);
 1493   cairo_set_line_width(cr,0.1); // TBD
 1494   cairo_stroke(cr);
 1495 #endif
 1496 
 1497   return logical_rect.height;
 1498 }