output: Remove PostScript driver. fc11-i386-build50 fc11-x64-build47 lenny-x64-build71 sid-i386-build117
authorBen Pfaff <blp@gnu.org>
Sun, 6 Dec 2009 04:44:01 +0000 (20:44 -0800)
committerBen Pfaff <blp@gnu.org>
Sun, 6 Dec 2009 04:44:01 +0000 (20:44 -0800)
The Cairo driver is a better way to produce PostScript output.  There seems
little value in maintaining the historical PostScript driver.

Removing the PostScript driver also makes for less work in revamping the
output subsystem, since there are now fewer drivers to update.

NEWS
doc/configuring.texi
src/output/afm.c [deleted file]
src/output/afm.h [deleted file]
src/output/automake.mk
src/output/cairo.c
src/output/output.c
src/output/output.h
src/output/postscript.c [deleted file]

diff --git a/NEWS b/NEWS
index 5f952324f12229c7e3a110dfd688f7b0fa5e9c26..24f1d4a80cce1218ebbb91c2785bd6cdc79f0aea 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2009-10-24 08:12:04 blp>
+Time-stamp: <2009-12-05 20:39:07 blp>
 Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -12,6 +12,8 @@ Changes from 0.7.2 to 0.7.3:
    not have Cairo and Pango installed, you must run `configure' with
    --without-cairo.
 
+ * The PostScript driver has been removed.
+
 Changes from 0.7.1 to 0.7.2:
 
  * Updated Perl module interface.
index a1007d921da87716f8015c8e751e3b99b90090ff..357388c8b28809656f0deb579fcd0326379bbc79 100644 (file)
@@ -14,7 +14,6 @@ This chapter describe how to configure PSPP for your system.
 * Cairo driver class::          Configuration of Cairo devices.
 * ASCII driver class::          Configuration of character-code devices.
 * HTML driver class::           Configuration for HTML output.
-* PostScript driver class::     Configuration of PostScript devices.
 * Miscellaneous configuring::   Even more configuration variables.
 @end menu
 
@@ -805,104 +804,6 @@ format.  The name should contain a single @samp{#}, which is replaced by
 the chart number.  Default: @file{"pspp-#.png"}.
 @end table
 
-@node PostScript driver class
-@section The PostScript driver class
-
-The @code{postscript} driver class is used to produce output that is
-acceptable to PostScript printers and other interpreters.
-The PostScript driver class does not support charts.
-
-The PostScript driver class is deprecated.  It is likely to be removed
-in a future version of PSPP.  We suggest that you use the Cairo driver
-class instead, which can output PostScript as well and has better font
-support, including support for international character sets, and does
-support charts.
-
-The available options are listed below.
-
-@table @code
-@item output-file=@var{file-name}
-
-File to which output should be sent.  This can be an ordinary file name
-(i.e., @code{"pspp.ps"}), a pipe (i.e., @code{"|lpr"}), or
-stdout (@code{"-"}).  Default: @code{"pspp.ps"}.
-
-@item headers=@var{boolean}
-
-Controls whether the standard headers showing the time and date and
-title and subtitle are printed at the top of each page.  Default:
-@code{on}.
-
-@item paper-size=@var{paper-size}
-
-Paper size.  You may specify a name (e.g.@: @code{a4}, @code{letter})
-or measurements (e.g.@: @code{210x297}, @code{8.5x11in}).
-
-The default paper size is taken from the @env{PAPERSIZE} environment
-variable or the file indicated by the @env{PAPERCONF} environment
-variable, if either variable is set.  If not, and your system supports
-the @code{LC_PAPER} locale category, then the default paper size is
-taken from the locale.  Otherwise, if @file{/etc/papersize} exists,
-the default paper size is read from it.  As a last resort, A4 paper is
-assumed.
-
-@item orientation=@var{orientation}
-
-Either @code{portrait} or @code{landscape}.  Default: @code{portrait}.
-
-@item left-margin=@var{dimension}
-@itemx right-margin=@var{dimension}
-@itemx top-margin=@var{dimension}
-@itemx bottom-margin=@var{dimension}
-
-Sets the margins around the page.  The headers, if enabled, are not
-included in the margins; they are in addition to the margins.  For a
-description of dimensions, see @ref{Dimensions}.  Default: @code{0.5in}.
-
-@item prop-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-@itemx emph-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-@itemx fixed-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-
-Sets the font used for proportional, emphasized, or fixed-pitch text.
-The only required value is @var{afm-file}, the AFM file for the font.
-
-If specified, @var{font-file} will be downloaded to the printer at the
-beginning of the print job.  The font file may be in PFA or PFB format.
-
-The font is reencoded as specified in @var{encoding-file}, if specified.
-Each line in @var{encoding-file} should consist of a PostScript
-character name and a decimal encoding value (between 0 and 255),
-separated by white space.  Blank lines and comments introduced by
-@samp{#} are also allowed.
-
-The files specified on these options are located as follows.  If
-the file name begins with @samp{/}, then it is taken as an absolute
-path.  Otherwise, PSPP searches its configuration path for the specified
-name prefixed by @code{psfonts/} (@pxref{File locations}).
-
-Default: proportional font @code{Times-Roman.afm}, emphasis font
-@code{Times-Italic.afm}, fixed-pitch font @code{Courier.afm}.
-
-@item font-size=@var{font-size}
-
-Sets the size of the default fonts, in thousandths of a point.  Default:
-10000 (10 point).
-
-@item line-gutter=@var{dimension}
-
-Sets the width of white space on either side of lines that border text
-or graphics objects.  @xref{Dimensions}.  Default: @code{1pt}.
-
-@item line-spacing=@var{dimension}
-
-Sets the spacing between the lines in a double line in a table.
-Default: @code{1pt}.
-
-@item line-width=@var{dimension}
-
-Sets the width of the lines used in tables.  Default: @code{0.5pt}.
-@end table
-
 @node Miscellaneous configuring
 @section Miscellaneous configuration
 
diff --git a/src/output/afm.c b/src/output/afm.c
deleted file mode 100644 (file)
index 480aa15..0000000
+++ /dev/null
@@ -1,1156 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-#include "afm.h"
-#include "c-ctype.h"
-#include "c-strtod.h"
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <setjmp.h>
-#include "error.h"
-#include "minmax.h"
-#include <libpspp/assertion.h>
-#include <libpspp/pool.h>
-#include <libpspp/str.h>
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* A kern pair entry. */
-struct afm_kern_pair
-  {
-    struct afm_character *successor; /* Second character. */
-    int adjust;                 /* Adjustment. */
-  };
-
-/* A ligature. */
-struct afm_ligature
-  {
-    struct afm_character *successor; /* Second character. */
-    struct afm_character *ligature;  /* Resulting ligature. */
-  };
-
-/* How to map between byte strings and character values. */
-enum mapping_scheme
-  {
-    MAP_UNKNOWN,                /* Not yet determined. */
-    MAP_ONE_BYTE,               /* 8-bit coding. */
-    MAP_TWO_BYTE,               /* 16-bit coding. */
-    MAP_ESCAPE,                 /* 8-bit coding with escape to change fonts. */
-    MAP_DOUBLE_ESCAPE,          /* 8-bit coding with multiple escapes. */
-    MAP_SHIFT                   /* 8-bit coding with 2 fonts that toggle. */
-  };
-
-/* AFM file data.  */
-struct afm
-  {
-    struct pool *pool;         /* Containing pool. */
-    char *findfont_name;        /* Name for PostScript /findfont operator. */
-    int ascent;                 /* Height above the baseline (non-negative). */
-    int descent;                /* Depth below the baseline (non-negative). */
-
-    /* Encoding characters into strings. */
-    enum mapping_scheme mapping; /* Basic mapping scheme. */
-    char escape_char;           /* MAP_ESCAPE only: escape character to use. */
-    char shift_out;             /* MAP_SHIFT only: selects font 0. */
-    char shift_in;              /* MAP_SHIFT only: selects font 1. */
-
-    /* Characters. */
-    struct afm_character *undefined_codes[256];
-    struct afm_character **codes[256];
-    struct afm_character **chars;
-    size_t char_cnt;
-  };
-
-/* AFM file parser. */
-struct parser
-  {
-    struct pool *pool;          /* Containing pool. */
-    struct afm *afm;            /* AFM being parsed. */
-    FILE *file;                 /* File being parsed. */
-    const char *file_name;      /* Name of file being parsed. */
-    int line_number;            /* Current line number in file. */
-    jmp_buf bail_out;           /* longjmp() target for error handling. */
-
-    size_t char_allocated;
-    int max_code;
-  };
-
-static struct afm *create_afm (void);
-static struct afm_character *create_character (struct afm *);
-
-static void afm_error (struct parser *, const char *, ...)
-     PRINTF_FORMAT (2, 3)
-     NO_RETURN;
-
-static void parse_afm (struct parser *);
-static void skip_section (struct parser *, const char *end_key);
-static bool parse_set_specific (struct parser *, const char *end_key);
-static void parse_direction (struct parser *);
-static void parse_char_metrics (struct parser *);
-static void parse_kern_pairs (struct parser *);
-static void add_kern_pair (struct parser *p,
-                           struct afm_character *, struct afm_character *,
-                           int adjust);
-
-static int skip_spaces (struct parser *);
-static char *parse_key (struct parser *);
-static void skip_line (struct parser *);
-static void force_eol (struct parser *);
-static bool get_integer (struct parser *, int *);
-static int force_get_integer (struct parser *);
-static bool get_number (struct parser *, int *);
-static int force_get_number (struct parser *);
-static bool get_hex_code (struct parser *, int *);
-static int force_get_hex_code (struct parser *);
-static bool get_word (struct parser *, char **);
-static char *force_get_word (struct parser *);
-static bool get_string (struct parser *, char **);
-static char *force_get_string (struct parser *);
-
-static struct afm_character *get_char_by_name (struct parser *, const char *);
-static struct afm_character *get_char_by_code (const struct afm *, int code);
-
-/* Reads FILE_NAME as an AFM file and returns the metrics data.
-   Returns a null pointer if the file cannot be parsed. */
-struct afm *
-afm_open (const char *file_name)
-{
-  struct afm *volatile afm;
-  struct parser *parser;
-
-  parser = pool_create_container (struct parser, pool);
-  afm = parser->afm = create_afm ();
-  parser->file = pool_fopen (parser->pool, file_name, "r");
-  parser->file_name = file_name;
-  parser->line_number = 0;
-  if (parser->file == NULL)
-    {
-      error (0, errno, _("opening font metrics file \"%s\""), file_name);
-      goto error;
-    }
-
-  if (setjmp (parser->bail_out))
-    goto error;
-
-  parse_afm (parser);
-  pool_destroy (parser->pool);
-  return afm;
-
- error:
-  pool_destroy (parser->pool);
-  pool_destroy (afm->pool);
-  return create_afm ();
-}
-
-/* Creates and returns an empty set of metrics. */
-static struct afm *
-create_afm (void)
-{
-  struct afm *afm;
-  struct afm_character *def_char;
-  size_t i;
-
-  afm = pool_create_container (struct afm, pool);
-  afm->findfont_name = NULL;
-  afm->ascent = 0;
-  afm->descent = 0;
-  afm->mapping = MAP_UNKNOWN;
-  afm->escape_char = 255;
-  afm->shift_out = 14;
-  afm->shift_in = 15;
-  def_char = create_character (afm);
-  for (i = 0; i < 256; i++)
-    afm->undefined_codes[i] = def_char;
-  for (i = 0; i < 256; i++)
-    afm->codes[i] = afm->undefined_codes;
-  afm->chars = NULL;
-  afm->char_cnt = 0;
-
-  return afm;
-}
-
-/* Creates and returns an initialized character within AFM. */
-static struct afm_character *
-create_character (struct afm *afm)
-{
-  struct afm_character *c = pool_alloc (afm->pool, sizeof *c);
-  c->code = ' ';
-  c->name = NULL;
-  c->width = 12000;
-  c->ascent = 0;
-  c->descent = 0;
-  c->kern_pairs = NULL;
-  c->kern_pair_cnt = 0;
-  c->ligatures = NULL;
-  c->ligature_cnt = 0;
-  return c;
-}
-
-/* Reports the given MESSAGE at the current line in parser P
-   and bails out with longjmp(). */
-static void
-afm_error (struct parser *p, const char *message, ...)
-{
-  va_list args;
-  char *msg;
-
-  va_start (args, message);
-  msg = xasprintf (message, args);
-  va_end (args);
-
-  error_at_line (0, 0, p->file_name, p->line_number, "%s", msg);
-  free (msg);
-
-  longjmp (p->bail_out, 1);
-}
-
-/* Parses an AFM file with parser P. */
-static void
-parse_afm (struct parser *p)
-{
-  char *key;
-
-  p->char_allocated = 0;
-  p->max_code = 0;
-
-  key = force_get_word (p);
-  if (strcmp (key, "StartFontMetrics"))
-    afm_error (p, _("first line must be StartFontMetrics"));
-  skip_line (p);
-
-  do
-    {
-      key = parse_key (p);
-      if (!strcmp (key, "FontName"))
-        p->afm->findfont_name = pool_strdup (p->afm->pool,
-                                             force_get_string (p));
-      else if (!strcmp (key, "Ascender"))
-        p->afm->ascent = force_get_integer (p);
-      else if (!strcmp (key, "Descender"))
-        p->afm->descent = force_get_integer (p);
-      else if (!strcmp (key, "MappingScheme"))
-        {
-          int scheme = force_get_integer (p);
-          if (scheme == 4)
-            p->afm->mapping = MAP_ONE_BYTE;
-          else if (scheme == 2 || scheme == 5 || scheme == 6)
-            p->afm->mapping = MAP_TWO_BYTE;
-          else if (scheme == 3)
-            p->afm->mapping = MAP_ESCAPE;
-          else if (scheme == 7)
-            p->afm->mapping = MAP_DOUBLE_ESCAPE;
-          else if (scheme == 8)
-            p->afm->mapping = MAP_SHIFT;
-          else
-            afm_error (p, _("unsupported MappingScheme %d"), scheme);
-        }
-      else if (!strcmp (key, "EscChar"))
-        p->afm->escape_char = force_get_integer (p);
-      else if (!strcmp (key, "StartDirection"))
-        parse_direction (p);
-      else if (!strcmp (key, "StartCharMetrics"))
-        parse_char_metrics (p);
-      else if (!strcmp (key, "StartKernPairs")
-               || !strcmp (key, "StartKernPairs0"))
-        parse_kern_pairs (p);
-      else if (!strcmp (key, "StartTrackKern"))
-        skip_section (p, "EndTrackKern");
-      else if (!strcmp (key, "StartComposites"))
-        skip_section (p, "EndComposites");
-      else
-        skip_line (p);
-    }
-  while (strcmp (key, "EndFontMetrics"));
-
-  if (p->afm->findfont_name == NULL)
-    afm_error (p, _("required FontName is missing"));
-  if (p->afm->mapping == MAP_UNKNOWN)
-    {
-      /* There seem to be a number of fonts out there that use a
-         2-byte encoding but don't announce it with
-         MappingScheme. */
-      p->afm->mapping = p->max_code > 255 ? MAP_TWO_BYTE : MAP_ONE_BYTE;
-    }
-}
-
-/* Reads lines from parser P until one starts with END_KEY. */
-static void
-skip_section (struct parser *p, const char *end_key)
-{
-  const char *key;
-  skip_line (p);
-  do
-    {
-      key = parse_key (p);
-      skip_line (p);
-    }
-  while (strcmp (key, end_key));
-}
-
-/* Attempts to read an integer from parser P.
-   If one is found, and it is nonzero, skips lines until END_KEY
-   is encountered and returns false.
-   Otherwise, skips the rest of the line and returns true.
-   (This is useful because AFM files can have multiple sets of
-   metrics.  Set 0 is for normal text, other sets are for
-   vertical text, etc.  We only care about set 0.) */
-static bool
-parse_set_specific (struct parser *p, const char *end_key)
-{
-  int set;
-
-  if (get_integer (p, &set) && set != 0)
-    {
-      skip_section (p, end_key);
-      return false;
-    }
-  else
-    {
-      force_eol (p);
-      return true;
-    }
-}
-
-/* Parses a StartDirection...EndDirection section in parser P. */
-static void
-parse_direction (struct parser *p)
-{
-  const char *key;
-
-  if (!parse_set_specific (p, "EndDirection"))
-    return;
-
-  do
-    {
-      key = parse_key (p);
-      if (!strcmp (key, "CharWidth"))
-        p->afm->codes[0][0]->width = force_get_integer (p);
-      skip_line (p);
-    }
-  while (strcmp (key, "EndDirection"));
-}
-
-/* Parses a StartCharMetrics...EndCharMetrics section in parser
-   P. */
-static void
-parse_char_metrics (struct parser *p)
-{
-  struct parsing_ligature
-    {
-      struct afm_character *first;
-      char *successor;
-      char *ligature;
-    };
-
-  struct parsing_ligature *ligatures = NULL;
-  size_t ligature_cnt = 0;
-  size_t ligature_allocated = 0;
-
-  size_t i;
-
-  skip_line (p);
-
-  for (;;)
-    {
-      char *key;
-      struct afm_character *c;
-
-      key = parse_key (p);
-      if (!strcmp (key, "EndCharMetrics"))
-        break;
-
-      if (p->afm->char_cnt == p->char_allocated)
-        p->afm->chars = pool_2nrealloc (p->afm->pool, p->afm->chars,
-                                        &p->char_allocated,
-                                        sizeof *p->afm->chars);
-      c = create_character (p->afm);
-
-      if (!strcmp (key, "C"))
-        c->code = force_get_integer (p);
-      else if (!strcmp (key, "CH"))
-        c->code = force_get_hex_code (p);
-      else
-        afm_error (p, _("CharMetrics line must start with C or CH"));
-      if (c->code < 0 || c->code > 65535)
-        c->code = -1;
-
-      if (c->code > p->max_code)
-        p->max_code = c->code;
-
-      p->afm->chars[p->afm->char_cnt++] = c;
-      if (c->code != -1)
-        p->afm->codes[c->code >> 8][c->code & 0xff] = c;
-
-      key = force_get_word (p);
-      while (!strcmp (key, ";"))
-        {
-          if (!get_word (p, &key))
-            break;
-
-          if (!strcmp (key, "N"))
-            c->name = force_get_word (p);
-          else if (!strcmp (key, "WX") || !strcmp (key, "W0X"))
-            c->width = force_get_number (p);
-          else if (!strcmp (key, "W") || !strcmp (key, "W0"))
-            {
-              c->width = force_get_number (p);
-              force_get_number (p);
-            }
-          else if (!strcmp (key, "B"))
-            {
-              int llx, lly, urx, ury;
-              llx = force_get_number (p);
-              lly = force_get_number (p);
-              urx = force_get_number (p);
-              ury = force_get_number (p);
-              c->ascent = MAX (0, ury);
-              c->descent = MAX (0, -lly);
-            }
-          else if (!strcmp (key, "L"))
-            {
-              struct parsing_ligature *ligature;
-              if (ligature_cnt == ligature_allocated)
-                ligatures = pool_2nrealloc (p->pool, ligatures,
-                                            &ligature_allocated,
-                                            sizeof *ligatures);
-              ligature = &ligatures[ligature_cnt++];
-              ligature->first = c;
-              ligature->successor = force_get_word (p);
-              ligature->ligature = force_get_word (p);
-            }
-          else
-            {
-              while (strcmp (key, ";"))
-                key = force_get_word (p);
-              continue;
-            }
-          if (!get_word (p, &key))
-            break;
-        }
-    }
-  skip_line (p);
-
-  for (i = 0; i < ligature_cnt; i++)
-    {
-      struct parsing_ligature *src = &ligatures[i];
-      struct afm_ligature *dst;
-      src->first->ligatures = pool_nrealloc (p->afm->pool,
-                                             src->first->ligatures,
-                                             src->first->ligature_cnt + 1,
-                                             sizeof *src->first->ligatures);
-      dst = &src->first->ligatures[src->first->ligature_cnt++];
-      dst->successor = get_char_by_name (p, src->successor);
-      dst->ligature = get_char_by_name (p, src->ligature);
-    }
-}
-
-/* Parses a StartKernPairs...EndKernPairs section in parser P. */
-static void
-parse_kern_pairs (struct parser *p)
-{
-  char *key;
-
-  skip_line (p);
-
-  do
-    {
-      struct afm_character *c1, *c2;
-      int adjust;
-
-      key = parse_key (p);
-      if (!strcmp (key, "KP") || !strcmp (key, "KPX"))
-        {
-          c1 = get_char_by_name (p, force_get_word (p));
-          c2 = get_char_by_name (p, force_get_word (p));
-          adjust = force_get_number (p);
-          if (!strcmp (key, "KP"))
-            force_get_number (p);
-          add_kern_pair (p, c1, c2, adjust);
-        }
-      else if (!strcmp (key, "KPH"))
-        {
-          c1 = get_char_by_code (p->afm, force_get_hex_code (p));
-          c2 = get_char_by_code (p->afm, force_get_hex_code (p));
-          adjust = force_get_number (p);
-          force_get_number (p);
-          add_kern_pair (p, c1, c2, adjust);
-        }
-      else
-        skip_line (p);
-    }
-  while (strcmp (key, "EndKernPairs"));
-}
-
-/* Adds a kern pair that adjusts (FIRST, SECOND) by ADJUST units
-   to the metrics within parser P. */
-static void
-add_kern_pair (struct parser *p, struct afm_character *first,
-               struct afm_character *second, int adjust)
-{
-  struct afm_kern_pair *kp;
-
-  first->kern_pairs = pool_nrealloc (p->afm->pool, first->kern_pairs,
-                                     first->kern_pair_cnt + 1,
-                                     sizeof *first->kern_pairs);
-  kp = &first->kern_pairs[first->kern_pair_cnt++];
-  kp->successor = second;
-  kp->adjust = adjust;
-}
-
-/* Returns the character with the given NAME with the metrics for
-   parser P.  Reports an error if no character has the given
-   name. */
-static struct afm_character *
-get_char_by_name (struct parser *p, const char *name)
-{
-  size_t i;
-
-  for (i = 0; i < p->afm->char_cnt; i++)
-    {
-      struct afm_character *c = p->afm->chars[i];
-      if (c->name != NULL && !strcmp (c->name, name))
-        return c;
-    }
-  afm_error (p, _("reference to unknown character \"%s\""), name);
-}
-
-/* Returns the character with the given CODE within AFM.
-   Returns a default character if the font doesn't have a
-   character with that code. */
-static struct afm_character *
-get_char_by_code (const struct afm *afm, int code_)
-{
-  uint16_t code = code_;
-  return afm->codes[code >> 8][code & 0xff];
-}
-\f
-/* Skips white space, except for new-lines, within parser P. */
-static int
-skip_spaces (struct parser *p)
-{
-  int c;
-  while (isspace (c = getc (p->file)) && c != '\n')
-    continue;
-  ungetc (c, p->file);
-  return c;
-}
-
-/* Parses a word at the beginning of a line.
-   Skips comments.
-   Reports an error if not at the beginning of a line. */
-static char *
-parse_key (struct parser *p)
-{
-  force_eol (p);
-  for (;;)
-    {
-      char *key;
-
-      do
-        {
-          p->line_number++;
-          getc (p->file);
-        }
-      while (skip_spaces (p) == '\n');
-
-      key = force_get_word (p);
-      if (strcmp (key, "Comment"))
-        return key;
-
-      skip_line (p);
-    }
-}
-
-/* Skips to the next line within parser P. */
-static void
-skip_line (struct parser *p)
-{
-  for (;;)
-    {
-      int c = getc (p->file);
-      if (c == EOF)
-        afm_error (p, _("expected end of file"));
-      if (c == '\n')
-        break;
-    }
-  ungetc ('\n', p->file);
-}
-
-/* Ensures that parser P is at the end of a line. */
-static void
-force_eol (struct parser *p)
-{
-  if (skip_spaces (p) != '\n')
-    afm_error (p, _("syntax error expecting end of line"));
-}
-
-/* Tries to read an integer into *INTEGER at the current position
-   in parser P.
-   Returns success. */
-static bool
-get_integer (struct parser *p, int *integer)
-{
-  int c = skip_spaces (p);
-  if (isdigit (c) || c == '-')
-    {
-      char *tail;
-      long tmp;
-
-      errno = 0;
-      tmp = strtol (force_get_word (p), &tail, 10);
-      if (errno == ERANGE || tmp < INT_MIN || tmp > INT_MAX)
-        afm_error (p, _("number out of valid range"));
-      if (*tail != '\0')
-        afm_error (p, _("invalid numeric syntax"));
-      *integer = tmp;
-
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Returns an integer read from the current position in P.
-   Reports an error if unsuccessful. */
-static int
-force_get_integer (struct parser *p)
-{
-  int integer;
-  if (!get_integer (p, &integer))
-    afm_error (p, _("syntax error expecting integer"));
-  return integer;
-}
-
-/* Tries to read a floating-point number at the current position
-   in parser P.  Stores the number's integer part into *INTEGER.
-   Returns success. */
-static bool
-get_number (struct parser *p, int *integer)
-{
-  int c = skip_spaces (p);
-  if (c == '-' || c == '.' || isdigit (c))
-    {
-      char *tail;
-      double number;
-
-      errno = 0;
-      number = c_strtod (force_get_word (p), &tail);
-      if (errno == ERANGE || number < INT_MIN || number > INT_MAX)
-        afm_error (p, _("number out of valid range"));
-      if (*tail != '\0')
-        afm_error (p, _("invalid numeric syntax"));
-      *integer = number;
-
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Returns the integer part of a floating-point number read from
-   the current position in P.
-   Reports an error if unsuccessful. */
-static int
-force_get_number (struct parser *p)
-{
-  int integer;
-  if (!get_number (p, &integer))
-    afm_error (p, _("syntax error expecting number"));
-  return integer;
-}
-
-/* Tries to read an integer expressed in hexadecimal into
-   *INTEGER from P.
-   Returns success. */
-static bool
-get_hex_code (struct parser *p, int *integer)
-{
-  if (skip_spaces (p) == '<')
-    {
-      if (fscanf (p->file, "<%x", integer) != 1 || getc (p->file) != '>')
-        afm_error (p, _("syntax error in hex constant"));
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Reads an integer expressed in hexadecimal and returns its
-   value.
-   Reports an error if unsuccessful. */
-static int
-force_get_hex_code (struct parser *p)
-{
-  int integer;
-  if (!get_hex_code (p, &integer))
-    afm_error (p, _("syntax error expecting hex constant"));
-  return integer;
-}
-
-/* Tries to read a word from P into *WORD.
-   The word is allocated in P's pool.
-   Returns success. */
-static bool
-get_word (struct parser *p, char **word)
-{
-  if (skip_spaces (p) != '\n')
-    {
-      struct string s;
-      int c;
-
-      ds_init_empty (&s);
-      while (!isspace (c = getc (p->file)) && c != EOF)
-        ds_put_char (&s, c);
-      ungetc (c, p->file);
-      *word = ds_cstr (&s);
-      pool_register (p->pool, free, *word);
-      return true;
-    }
-  else
-    {
-      *word = NULL;
-      return false;
-    }
-}
-
-/* Reads a word from P and returns it.
-   The word is allocated in P's pool.
-   Reports an error if unsuccessful. */
-static char *
-force_get_word (struct parser *p)
-{
-  char *word;
-  if (!get_word (p, &word))
-    afm_error (p, _("unexpected end of line"));
-  return word;
-}
-
-/* Reads a string, consisting of the remainder of the current
-   line, from P, and stores it in *STRING.
-   Leading and trailing spaces are removed.
-   The word is allocated in P's pool.
-   Returns true if a non-empty string was successfully read,
-   false otherwise. */
-static bool
-get_string (struct parser *p, char **string)
-{
-  struct string s = DS_EMPTY_INITIALIZER;
-
-  skip_spaces (p);
-  for (;;)
-    {
-      int c = getc (p->file);
-      if (c == EOF || c == '\n')
-        break;
-      ds_put_char (&s, c);
-    }
-  ungetc ('\n', p->file);
-  ds_rtrim (&s, ss_cstr (CC_SPACES));
-
-  if (!ds_is_empty (&s))
-    {
-      *string = ds_cstr (&s);
-      pool_register (p->pool, free, *string);
-      return true;
-    }
-  else
-    {
-      *string = NULL;
-      ds_destroy (&s);
-      return false;
-    }
-}
-
-/* Reads a string, consisting of the remainder of the current
-   line, from P, and returns it.
-   Leading and trailing spaces are removed.
-   The word is allocated in P's pool.
-   Reports an error if the string is empty. */
-static char *
-force_get_string (struct parser *p)
-{
-  char *string;
-  if (!get_string (p, &string))
-    afm_error (p, _("unexpected end of line expecting string"));
-  return string;
-}
-\f
-/* Closes AFM and frees its storage. */
-void
-afm_close (struct afm *afm)
-{
-  if (afm != NULL)
-    pool_destroy (afm->pool);
-}
-
-/* Returns the string that must be passed to the PostScript
-   "findfont" operator to obtain AFM's font. */
-const char *
-afm_get_findfont_name (const struct afm *afm)
-{
-  return afm->findfont_name;
-}
-
-/* Returns the ascent for AFM, that is, the font's height above
-   the baseline, in units of 1/1000 of the nominal font size. */
-int
-afm_get_ascent (const struct afm *afm)
-{
-  return afm->ascent;
-}
-
-/* Returns the descent for AFM, that is, the font's depth below
-   the baseline, in units of 1/1000 of the nominal font size. */
-int
-afm_get_descent (const struct afm *afm)
-{
-  return afm->descent;
-}
-
-/* Returns the character numbered CODE within AFM,
-   or a default character if the font has none. */
-const struct afm_character *
-afm_get_character (const struct afm *afm, int code)
-{
-  return get_char_by_code (afm, code);
-}
-
-/* Returns the ligature formed when FIRST is followed by SECOND,
-   or a null pointer if there is no such ligature. */
-const struct afm_character *
-afm_get_ligature (const struct afm_character *first,
-                  const struct afm_character *second)
-{
-  size_t i;
-
-  for (i = 0; i < first->ligature_cnt; i++)
-    if (first->ligatures[i].successor == second)
-      return first->ligatures[i].ligature;
-  return NULL;
-}
-
-/* Returns the pair kerning x-adjustment when FIRST is followed
-   by SECOND, or 0 if no pair kerning should be done for the
-   given pair of characters. */
-int
-afm_get_kern_adjustment (const struct afm_character *first,
-                         const struct afm_character *second)
-{
-  size_t i;
-
-  for (i = 0; i < first->kern_pair_cnt; i++)
-    if (first->kern_pairs[i].successor == second)
-      return first->kern_pairs[i].adjust;
-  return 0;
-}
-\f
-/* Encodes the N characters in S as a PostScript string in OUT,
-   using a single-byte encoding.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_one_byte (const struct afm_character **s, size_t n,
-                 struct string *out)
-{
-  ds_put_char (out, '(');
-  for (; n > 0; s++, n--)
-    {
-      uint8_t code = (*s)->code;
-      if (code != (*s)->code)
-        break;
-
-      if (code == '(' || code == ')' || code == '\\')
-        ds_put_format (out, "\\%c", code);
-      else if (!c_isprint (code))
-        ds_put_format (out, "\\%03o", code);
-      else
-        ds_put_char (out, code);
-    }
-  ds_put_char (out, ')');
-  return n;
-}
-
-/* State of binary encoder for PostScript. */
-struct binary_encoder
-  {
-    struct string *out;         /* Output string. */
-    uint32_t b;                 /* Accumulated bytes for base-85 encoding. */
-    size_t n;                   /* Number of bytes in b (0...3). */
-  };
-
-/* Initializes encoder E for output to OUT. */
-static void
-binary_init (struct binary_encoder *e, struct string *out)
-{
-  e->out = out;
-  e->b = e->n = 0;
-}
-
-/* Returns the character that represents VALUE in ASCII85
-   encoding. */
-static int
-value_to_ascii85 (int value)
-{
-  assert (value >= 0 && value < 85);
-#if C_CTYPE_ASCII
-  return value + 33;
-#else
-  return ("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK"
-          "LMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu")[value];
-#endif
-}
-
-/* Appends the first N characters of the ASCII85 representation
-   of B to string OUT. */
-static void
-append_ascii85_block (unsigned b, size_t n, struct string *out)
-{
-  char c[5];
-  int i;
-
-  for (i = 4; i >= 0; i--)
-    {
-      c[i] = value_to_ascii85 (b % 85);
-      b /= 85;
-    }
-  ds_put_substring (out, ss_buffer (c, n));
-}
-
-/* Encodes BYTE with encoder E. */
-static void
-binary_put (struct binary_encoder *e, uint8_t byte)
-{
-  e->b = (e->b << 8) | byte;
-  e->n++;
-  if (e->n % 4 == 0)
-    {
-      if (e->n == 4)
-        ds_put_cstr (e->out, "<~");
-
-      if (e->b != 0)
-        append_ascii85_block (e->b, 5, e->out);
-      else
-        ds_put_char (e->out, 'z');
-    }
-}
-
-/* Finishes up encoding with E. */
-static void
-binary_finish (struct binary_encoder *e)
-{
-  if (e->n >= 4)
-    {
-      /* We output at least one complete ASCII85 block.
-         Finish up. */
-      size_t n = e->n % 4;
-      if (n > 0)
-        append_ascii85_block (e->b << 8 * (4 - n), n + 1, e->out);
-      ds_put_cstr (e->out, "~>");
-    }
-  else if (e->n > 0)
-    {
-      /* It's cheaper (or at least the same cost) to encode this
-         string in hexadecimal. */
-      uint32_t b;
-      size_t i;
-
-      ds_put_cstr (e->out, "<");
-      b = e->b << 8 * (4 - e->n);
-      for (i = 0; i < e->n; i++)
-        {
-          ds_put_format (e->out, "%02x", b >> 24);
-          b <<= 8;
-        }
-      ds_put_cstr (e->out, ">");
-    }
-  else
-    {
-      /* Empty string. */
-      ds_put_cstr (e->out, "()");
-    }
-}
-
-/* Encodes the N characters in S into encoder E,
-   using a two-byte encoding.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_two_byte (const struct afm_character **s, size_t n,
-                 struct binary_encoder *e)
-{
-  for (; n > 0; s++, n--)
-    {
-      uint16_t code = (*s)->code;
-      if (code != (*s)->code)
-        break;
-
-      binary_put (e, code >> 8);
-      binary_put (e, code);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into encoder E,
-   using an escape-based encoding with ESCAPE_CHAR as escape.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_escape (const struct afm_character **s, size_t n,
-               unsigned char escape_char,
-               struct binary_encoder *e)
-{
-  uint8_t cur_font = 0;
-
-  for (; n > 0; s++, n--)
-    {
-      uint16_t code = (*s)->code;
-      uint8_t font_num = code >> 8;
-      uint8_t char_num = code & 0xff;
-      if (code != (*s)->code)
-        break;
-
-      if (font_num != cur_font)
-        {
-          if (font_num == escape_char)
-            break;
-          binary_put (e, escape_char);
-          binary_put (e, font_num);
-          cur_font = font_num;
-        }
-      binary_put (e, char_num);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into encoder E,
-   using an double escape-based encoding with ESCAPE_CHAR as
-   escape.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_double_escape (const struct afm_character **s, size_t n,
-                      unsigned char escape_char,
-                      struct binary_encoder *e)
-{
-  unsigned cur_font = 0;
-
-  for (; n > 0; s++, n--)
-    {
-      unsigned font_num = (*s)->code >> 8;
-      uint8_t char_num = (*s)->code & 0xff;
-      if ((*s)->code & ~0x1ffff)
-        break;
-
-      if (font_num != cur_font)
-        {
-          if (font_num == (escape_char & 0xff))
-            break;
-          if (font_num >= 256)
-            binary_put (e, escape_char);
-          binary_put (e, escape_char);
-          binary_put (e, font_num & 0xff);
-          cur_font = font_num;
-        }
-      binary_put (e, char_num);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into encoder E,
-   using a shift-based encoding with SHIFT_IN and SHIFT_OUT as
-   shift characters.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_shift (const struct afm_character **s, size_t n,
-              unsigned char shift_in, unsigned char shift_out,
-              struct binary_encoder *e)
-{
-  unsigned cur_font = 0;
-
-  for (; n > 0; s++, n--)
-    {
-      int font_num = ((*s)->code & 0x100) != 0;
-      uint8_t char_num = (*s)->code & 0xff;
-      if ((*s)->code & ~0x1ff)
-        break;
-
-      if (font_num != cur_font)
-        {
-          binary_put (e, font_num ? shift_out : shift_in);
-          cur_font = font_num;
-        }
-      binary_put (e, char_num);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into a PostScript string in OUT,
-   according to AFM's character encoding.
-   Returns the number of characters successfully encoded,
-   which may be less than N if an unencodable character was
-   encountered. */
-size_t
-afm_encode_string (const struct afm *afm,
-                   const struct afm_character **s, size_t n,
-                   struct string *out)
-{
-  size_t initial_length = ds_length (out);
-  size_t chars_left;
-
-  if (afm->mapping == MAP_ONE_BYTE)
-    chars_left = encode_one_byte (s, n, out);
-  else
-    {
-      struct binary_encoder e;
-
-      binary_init (&e, out);
-      switch (afm->mapping)
-        {
-        case MAP_TWO_BYTE:
-          chars_left = encode_two_byte (s, n, &e);
-          break;
-
-        case MAP_ESCAPE:
-          chars_left = encode_escape (s, n, afm->escape_char, &e);
-          break;
-
-        case MAP_DOUBLE_ESCAPE:
-          chars_left = encode_double_escape (s, n, afm->escape_char, &e);
-          break;
-
-        case MAP_SHIFT:
-          chars_left = encode_shift (s, n, afm->shift_in, afm->shift_out, &e);
-          break;
-
-        default:
-          NOT_REACHED ();
-        }
-      binary_finish (&e);
-    }
-
-  if (chars_left == n)
-    ds_truncate (out, initial_length);
-  return n - chars_left;
-}
diff --git a/src/output/afm.h b/src/output/afm.h
deleted file mode 100644 (file)
index 6525af6..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef AFM_H
-#define AFM_H 1
-
-#include <stddef.h>
-#include <libpspp/str.h>
-
-/* Metrics for a single character.  */
-struct afm_character
-  {
-    int code;                   /* Non-negative character code, -1 if none. */
-    const char *name;           /* Character name, if any. */
-    int width;                 /* Width. */
-    int ascent;                        /* Height above baseline, never negative. */
-    int descent;                /* Depth below baseline, never negative. */
-
-    /* Pairwise kerning data for this character in the first
-       position, other characters in the second position. */
-    struct afm_kern_pair *kern_pairs;
-    size_t kern_pair_cnt;
-
-    /* Ligature data for this character in the first position,
-       other characters in the second position. */
-    struct afm_ligature *ligatures;
-    size_t ligature_cnt;
-  };
-
-struct afm *afm_open (const char *file_name);
-void afm_close (struct afm *);
-
-int afm_get_ascent (const struct afm *);
-int afm_get_descent (const struct afm *);
-const char *afm_get_findfont_name (const struct afm *);
-
-const struct afm_character *afm_get_character (const struct afm *,
-                                               int code);
-const struct afm_character *afm_get_ligature (const struct afm_character *,
-                                              const struct afm_character *);
-int afm_get_kern_adjustment (const struct afm_character *,
-                             const struct afm_character *);
-
-size_t afm_encode_string (const struct afm *,
-                          const struct afm_character **, size_t,
-                          struct string *);
-
-#endif /* afm.h */
index eb9f7b78e78ddcd9194020fe18049ca2676cf6bc..ad8e4529c8cf0695f2790abac5a5322ff4c0ade5 100644 (file)
@@ -5,8 +5,6 @@ noinst_LTLIBRARIES += src/output/liboutput.la
 src_output_liboutput_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(AM_CPPFLAGS) 
 
 src_output_liboutput_la_SOURCES = \
-       src/output/afm.c \
-       src/output/afm.h \
        src/output/ascii.c \
        src/output/chart.c \
        src/output/chart.h \
@@ -33,7 +31,6 @@ src_output_liboutput_la_SOURCES = \
        src/output/odt.c \
        src/output/output.c \
        src/output/output.h \
-       src/output/postscript.c \
        src/output/table.c \
        src/output/table.h
 if HAVE_CAIRO
index b0b4f7d3e1fe0dd0be16577171e21faccc3f4c2c..162009c421407d6c706c839789edcfed2503a34c 100644 (file)
@@ -21,7 +21,6 @@
 #include <libpspp/assertion.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
-#include <output/afm.h>
 #include <output/chart-provider.h>
 #include <output/manager.h>
 #include <output/output.h>
index b97e2de3051349aa78eb87b642ec28610c351623..2f4788f9a0016fe3a5f6b74a752e11062ba4c9f5 100644 (file)
@@ -233,7 +233,6 @@ outp_init (void)
   char def[] = "default";
 
   add_class (&html_class);
-  add_class (&postscript_class);
   add_class (&ascii_class);
 #ifdef HAVE_CAIRO
   add_class (&cairo_class);
index 8ae5c04be32e48c16ba2674fb5bf628b3e9e23b8..398781f05f547fef57f429386e20af6da4993b31 100644 (file)
@@ -171,7 +171,6 @@ void som_destroy_driver (struct outp_driver *);
 
 /* Common drivers. */
 extern const struct outp_class ascii_class;
-extern const struct outp_class postscript_class;
 #ifdef HAVE_CAIRO
 extern const struct outp_class cairo_class;
 #endif
diff --git a/src/output/postscript.c b/src/output/postscript.c
deleted file mode 100644 (file)
index 394ebbb..0000000
+++ /dev/null
@@ -1,1384 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <data/file-name.h>
-#include <libpspp/assertion.h>
-#include <libpspp/bit-vector.h>
-#include <libpspp/compiler.h>
-#include <libpspp/freaderror.h>
-#include <libpspp/hash.h>
-#include <libpspp/misc.h>
-#include <libpspp/start-date.h>
-#include <libpspp/version.h>
-#include <output/afm.h>
-#include <output/chart-provider.h>
-#include <output/chart.h>
-#include <output/manager.h>
-#include <output/output.h>
-
-#include "error.h"
-#include "intprops.h"
-#include "minmax.h"
-#include "xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* PostScript driver options: (defaults listed first)
-
-   output-file="pspp.ps"
-
-   paper-size=letter (see "papersize" file)
-   orientation=portrait|landscape
-   headers=on|off
-
-   left-margin=0.5in
-   right-margin=0.5in
-   top-margin=0.5in
-   bottom-margin=0.5in
-
-   prop-font=Times-Roman
-   emph-font=Times-Italic
-   fixed-font=Courier
-   font-size=10000
-
-   line-gutter=1pt
-   line-spacing=1pt
-   line-width=0.5pt
- */
-
-/* The number of `psus' (PostScript driver UnitS) per inch. */
-#define PSUS 72000
-
-/* A PostScript font. */
-struct font
-  {
-    struct afm *metrics;        /* Metrics. */
-    char *embed_fn;             /* Name of file to embed. */
-    char *encoding_fn;          /* Name of file with encoding. */
-  };
-
-/* PostScript output driver extension record. */
-struct ps_driver_ext
-  {
-    char *file_name;            /* Output file name. */
-    FILE *file;                 /* Output file. */
-
-    bool draw_headers;          /* Draw headers at top of page? */
-    int page_number;           /* Current page number. */
-
-    bool portrait;              /* Portrait mode? */
-    int paper_width;            /* Width of paper before dropping margins. */
-    int paper_length;           /* Length of paper before dropping margins. */
-    int left_margin;           /* Left margin in psus. */
-    int right_margin;          /* Right margin in psus. */
-    int top_margin;            /* Top margin in psus. */
-    int bottom_margin;         /* Bottom margin in psus. */
-
-    int line_gutter;           /* Space around lines. */
-    int line_space;            /* Space between lines. */
-    int line_width;            /* Width of lines. */
-
-    struct font *fonts[OUTP_FONT_CNT];
-    int last_font;              /* Index of last font set with setfont. */
-
-    int doc_num;                /* %%DocumentNumber counter. */
-  };
-
-/* Transform logical y-ordinate Y into a page ordinate. */
-#define YT(Y) (this->length - (Y))
-
-static bool handle_option (void *this, const char *key,
-                           const struct string *val);
-static void draw_headers (struct outp_driver *this);
-
-static void write_ps_prologue (struct outp_driver *);
-
-static char *quote_ps_name (const char *string);
-
-static struct font *load_font (const char *string);
-static void free_font (struct font *);
-static void setup_font (struct outp_driver *this, struct font *, int index);
-\f
-/* Driver initialization. */
-
-static bool
-ps_open_driver (const char *name, int types, struct substring options)
-{
-  struct outp_driver *this;
-  struct ps_driver_ext *x;
-  size_t i;
-
-  this = outp_allocate_driver (&postscript_class, name, types);
-  this->width = this->length = 0;
-  this->font_height = PSUS * 10 / 72;
-
-  this->ext = x = xmalloc (sizeof *x);
-  x->file_name = xstrdup ("pspp.ps");
-  x->file = NULL;
-  x->draw_headers = true;
-  x->page_number = 0;
-  x->portrait = true;
-  outp_get_paper_size ("", &x->paper_width, &x->paper_length);
-  x->left_margin = PSUS / 2;
-  x->right_margin = PSUS / 2;
-  x->top_margin = PSUS / 2;
-  x->bottom_margin = PSUS / 2;
-  x->line_gutter = PSUS / 72;
-  x->line_space = PSUS / 72;
-  x->line_width = PSUS / 144;
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    x->fonts[i] = NULL;
-  x->doc_num = 0;
-
-  outp_parse_options (this->name, options, handle_option, this);
-
-  x->file = fn_open (x->file_name, "w");
-  if (x->file == NULL)
-    {
-      error (0, errno, _("opening PostScript output file \"%s\""),
-             x->file_name);
-      goto error;
-    }
-
-  if (x->portrait)
-    {
-      this->width = x->paper_width;
-      this->length = x->paper_length;
-    }
-  else
-    {
-      this->width = x->paper_length;
-      this->length = x->paper_width;
-    }
-  this->width -= x->left_margin + x->right_margin;
-  this->length -= x->top_margin + x->bottom_margin;
-  if (x->draw_headers)
-    {
-      int header_length = 3 * this->font_height;
-      this->length -= header_length;
-      x->top_margin += header_length;
-    }
-
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    if (x->fonts[i] == NULL)
-      {
-        const char *default_fonts[OUTP_FONT_CNT];
-        default_fonts[OUTP_FIXED] = "Courier.afm";
-        default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
-        default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
-        x->fonts[i] = load_font (default_fonts[i]);
-        if (x->fonts[i] == NULL)
-          goto error;
-      }
-
-  if (this->length / this->font_height < 15)
-    {
-      error (0, 0, _("The defined PostScript page is not long "
-                     "enough to hold margins and headers, plus least 15 "
-                     "lines of the default fonts.  In fact, there's only "
-                     "room for %d lines of each font at the default size "
-                     "of %d.%03d points."),
-          this->length / this->font_height,
-          this->font_height / 1000, this->font_height % 1000);
-      goto error;
-    }
-
-  this->fixed_width =
-    afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
-    * this->font_height / 1000;
-  this->prop_em_width =
-    afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
-    * this->font_height / 1000;
-
-  this->horiz_line_width[OUTP_L_NONE] = 0;
-  this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
-  this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
-                                           + 2 * x->line_width);
-  memcpy (this->vert_line_width, this->horiz_line_width,
-          sizeof this->vert_line_width);
-
-  write_ps_prologue (this);
-
-  outp_register_driver (this);
-  return true;
-
- error:
-  this->class->close_driver (this);
-  outp_free_driver (this);
-  return false;
-}
-
-static bool
-ps_close_driver (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  bool ok = true;
-  size_t i;
-
-  if (x->file != NULL)
-    {
-      fprintf (x->file,
-               "%%%%Trailer\n"
-               "%%%%Pages: %d\n"
-               "%%%%EOF\n",
-               x->page_number);
-
-      ok = fn_close (x->file_name, x->file) == 0;
-      if (!ok)
-        error (0, errno, _("closing PostScript output file \"%s\""),
-               x->file_name);
-    }
-
-  free (x->file_name);
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    free_font (x->fonts[i]);
-  free (x);
-
-  return ok;
-}
-
-/* Generic option types. */
-enum
-{
-  output_file_arg,
-  paper_size_arg,
-  orientation_arg,
-  line_style_arg,
-  boolean_arg,
-  pos_int_arg,
-  dimension_arg,
-  string_arg,
-  nonneg_int_arg
-};
-
-/* All the options that the PostScript driver supports. */
-static const struct outp_option option_tab[] =
-{
-  {"output-file",              output_file_arg,0},
-  {"paper-size",               paper_size_arg, 0},
-  {"orientation",              orientation_arg,0},
-
-  {"headers",                  boolean_arg,    1},
-
-  {"prop-font",                string_arg,     OUTP_PROPORTIONAL},
-  {"emph-font",                string_arg,     OUTP_EMPHASIS},
-  {"fixed-font",               string_arg,     OUTP_FIXED},
-
-  {"left-margin",              pos_int_arg,    0},
-  {"right-margin",             pos_int_arg,    1},
-  {"top-margin",               pos_int_arg,    2},
-  {"bottom-margin",            pos_int_arg,    3},
-  {"font-size",                        pos_int_arg,    4},
-
-  {"line-width",               dimension_arg,  0},
-  {"line-gutter",              dimension_arg,  1},
-  {"line-width",               dimension_arg,  2},
-  {NULL, 0, 0},
-};
-
-static bool
-handle_option (void *this_, const char *key,
-               const struct string *val)
-{
-  struct outp_driver *this = this_;
-  struct ps_driver_ext *x = this->ext;
-  int subcat;
-  char *value = ds_cstr (val);
-
-  switch (outp_match_keyword (key, option_tab, &subcat))
-    {
-    case -1:
-      error (0, 0,
-             _("unknown configuration parameter `%s' for PostScript device "
-               "driver"), key);
-      break;
-    case output_file_arg:
-      free (x->file_name);
-      x->file_name = xstrdup (value);
-      break;
-    case paper_size_arg:
-      outp_get_paper_size (value, &this->width, &this->length);
-      break;
-    case orientation_arg:
-      if (!strcmp (value, "portrait"))
-       x->portrait = true;
-      else if (!strcmp (value, "landscape"))
-       x->portrait = false;
-      else
-       error (0, 0, _("unknown orientation `%s' (valid orientations are "
-                       "`portrait' and `landscape')"), value);
-      break;
-    case boolean_arg:
-      if (!strcmp (value, "on") || !strcmp (value, "true")
-          || !strcmp (value, "yes") || atoi (value))
-        x->draw_headers = true;
-      else if (!strcmp (value, "off") || !strcmp (value, "false")
-               || !strcmp (value, "no") || !strcmp (value, "0"))
-        x->draw_headers = false;
-      else
-        {
-          error (0, 0, _("boolean value expected for %s"), key);
-          return false;
-        }
-      break;
-    case pos_int_arg:
-      {
-       char *tail;
-       int arg;
-
-       errno = 0;
-       arg = strtol (value, &tail, 0);
-       if (arg < 1 || errno == ERANGE || *tail)
-         {
-           error (0, 0, _("positive integer value required for `%s'"), key);
-           break;
-         }
-       if ((subcat == 4 || subcat == 5) && arg < 1000)
-         {
-           error (0, 0, _("default font size must be at least 1 point (value "
-                           "of 1000 for key `%s')"), key);
-           break;
-         }
-       switch (subcat)
-         {
-         case 0:
-           x->left_margin = arg;
-           break;
-         case 1:
-           x->right_margin = arg;
-           break;
-         case 2:
-           x->top_margin = arg;
-           break;
-         case 3:
-           x->bottom_margin = arg;
-           break;
-         case 4:
-           this->font_height = arg;
-           break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case dimension_arg:
-      {
-       int dimension = outp_evaluate_dimension (value);
-
-       if (dimension <= 0)
-          break;
-       switch (subcat)
-         {
-         case 0:
-           x->line_width = dimension;
-           break;
-         case 1:
-           x->line_gutter = dimension;
-           break;
-         case 2:
-           x->line_width = dimension;
-           break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case string_arg:
-      {
-        struct font *font = load_font (value);
-        if (font != NULL)
-          {
-            struct font **dst = &x->fonts[subcat];
-            if (*dst != NULL)
-              free_font (*dst);
-            *dst = font;
-          }
-      }
-      break;
-    default:
-      NOT_REACHED ();
-    }
-
-  return true;
-}
-
-/* Looks for a PostScript font file or config file in all the
-   appropriate places.  Returns the file name on success, NULL on
-   failure. */
-static char *
-find_ps_file (const char *name)
-{
-  if (fn_is_absolute (name))
-    return xstrdup (name);
-  else
-    {
-      char *base_name = xasprintf ("psfonts/%s", name);
-      char *file_name = fn_search_path (base_name, config_path);
-      free (base_name);
-      return file_name;
-    }
-}
-\f
-/* Basic file operations. */
-
-/* Writes the PostScript prologue to file F. */
-static void
-write_ps_prologue (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  size_t embedded_cnt, preloaded_cnt;
-  size_t i;
-
-  fputs ("%!PS-Adobe-3.0\n", x->file);
-  fputs ("%%Pages: (atend)\n", x->file);
-
-  embedded_cnt = preloaded_cnt = 0;
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    {
-      bool embed = x->fonts[i]->embed_fn != NULL;
-      embedded_cnt += embed;
-      preloaded_cnt += !embed;
-    }
-  if (preloaded_cnt > 0)
-    {
-      fputs ("%%DocumentNeededResources: font", x->file);
-      for (i = 0; i < OUTP_FONT_CNT; i++)
-        {
-          struct font *f = x->fonts[i];
-          if (f->embed_fn == NULL)
-            fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
-        }
-      fputs ("\n", x->file);
-    }
-  if (embedded_cnt > 0)
-    {
-      fputs ("%%DocumentSuppliedResources: font", x->file);
-      for (i = 0; i < OUTP_FONT_CNT; i++)
-        {
-          struct font *f = x->fonts[i];
-          if (f->embed_fn != NULL)
-            fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
-        }
-      fputs ("\n", x->file);
-    }
-  fputs ("%%Copyright: This prologue is public domain.\n", x->file);
-  fprintf (x->file, "%%%%Creator: %s\n", version);
-  fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
-           x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
-  fprintf (x->file, "%%%%Orientation: %s\n",
-           x->portrait ? "Portrait" : "Landscape");
-  fputs ("%%EndComments\n", x->file);
-  fputs ("%%BeginDefaults\n", x->file);
-  fputs ("%%PageResources: font", x->file);
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
-  fputs ("\n", x->file);
-  fputs ("%%EndDefaults\n", x->file);
-  fputs ("%%BeginProlog\n", x->file);
-  fputs ("/ED{exch def}bind def\n", x->file);
-  fputs ("/L{moveto lineto stroke}bind def\n", x->file);
-  fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
-  fputs ("/S{show}bind def\n", x->file);
-  fputs ("/GS{glyphshow}def\n", x->file);
-  fputs ("/RF{\n", x->file);
-  fputs (" exch dup maxlength 1 add dict begin\n", x->file);
-  fputs (" {\n", x->file);
-  fputs ("  1 index/FID ne{def}{pop pop}ifelse\n", x->file);
-  fputs (" }forall\n", x->file);
-  fputs (" /Encoding ED\n", x->file);
-  fputs (" currentdict end\n", x->file);
-  fputs ("}bind def\n", x->file);
-  fputs ("/F{setfont}bind def\n", x->file);
-  fputs ("/EP{\n", x->file);
-  fputs (" pg restore\n", x->file);
-  fputs (" showpage\n", x->file);
-  fputs ("}bind def\n", x->file);
-  fputs ("/GB{\n", x->file);
-  fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
-  fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
-         x->file);
-  fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
-  fputs ("}bind def\n", x->file);
-  fputs ("/K{0 rmoveto}bind def\n", x->file);
-  fputs ("%%EndProlog\n", x->file);
-  fputs ("%%BeginSetup\n", x->file);
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    setup_font (this, x->fonts[i], i);
-  fputs ("%%EndSetup\n", x->file);
-}
-
-/* Returns STRING as a Postscript name, which is just '/'
-   followed by STRING unless characters need to be quoted.
-   The caller must free the string. */
-static char *
-quote_ps_name (const char *string)
-{
-  const char *cp;
-
-  for (cp = string; *cp != '\0'; cp++)
-    {
-      unsigned char c = *cp;
-      if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
-          && (cp == string || !isdigit (c)))
-        {
-          struct string out = DS_EMPTY_INITIALIZER;
-          ds_put_char (&out, '<');
-         for (cp = string; *cp != '\0'; cp++)
-            {
-              c = *cp;
-              ds_put_format (&out, "%02x", c);
-            }
-         ds_put_cstr (&out, ">cvn");
-          return ds_cstr (&out);
-        }
-    }
-  return xasprintf ("/%s", string);
-}
-
-static void
-ps_open_page (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  /* Assure page independence. */
-  x->last_font = -1;
-
-  x->page_number++;
-
-  fprintf (x->file,
-          "%%%%Page: %d %d\n"
-          "%%%%BeginPageSetup\n"
-          "/pg save def 0.001 dup scale\n",
-          x->page_number, x->page_number);
-
-  if (!x->portrait)
-    fprintf (x->file,
-            "%d 0 translate 90 rotate\n",
-            x->paper_width);
-
-  if (x->bottom_margin != 0 || x->left_margin != 0)
-    fprintf (x->file,
-            "%d %d translate\n",
-            x->left_margin, x->bottom_margin);
-
-  fprintf (x->file,
-          "/LW %d def %d setlinewidth\n"
-          "%%%%EndPageSetup\n",
-          x->line_width, x->line_width);
-
-  if (x->draw_headers)
-    draw_headers (this);
-}
-
-static void
-ps_close_page (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  fputs ("%%PageTrailer\n"
-         "EP\n",
-         x->file);
-}
-
-static void
-ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
-  switch (s->type)
-    {
-    default:
-      NOT_REACHED ();
-    }
-}
-\f
-/* Draws a line from (x0,y0) to (x1,y1). */
-static void
-dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
-{
-  struct ps_driver_ext *ext = this->ext;
-  fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
-}
-
-/* Draws a horizontal line X0...X2 at Y if LEFT says so,
-   shortening it to X0...X1 if SHORTEN is true.
-   Draws a horizontal line X1...X3 at Y if RIGHT says so,
-   shortening it to X2...X3 if SHORTEN is true. */
-static void
-horz_line (struct outp_driver *this,
-           int x0, int x1, int x2, int x3, int y,
-           enum outp_line_style left, enum outp_line_style right,
-           bool shorten)
-{
-  if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
-    dump_line (this, x0, y, x3, y);
-  else
-    {
-      if (left != OUTP_L_NONE)
-        dump_line (this, x0, y, shorten ? x1 : x2, y);
-      if (right != OUTP_L_NONE)
-        dump_line (this, shorten ? x2 : x1, y, x3, y);
-    }
-}
-
-/* Draws a vertical line Y0...Y2 at X if TOP says so,
-   shortening it to Y0...Y1 if SHORTEN is true.
-   Draws a vertical line Y1...Y3 at X if BOTTOM says so,
-   shortening it to Y2...Y3 if SHORTEN is true. */
-static void
-vert_line (struct outp_driver *this,
-           int y0, int y1, int y2, int y3, int x,
-           enum outp_line_style top, enum outp_line_style bottom,
-           bool shorten)
-{
-  if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
-    dump_line (this, x, y0, x, y3);
-  else
-    {
-      if (top != OUTP_L_NONE)
-        dump_line (this, x, y0, x, shorten ? y1 : y2);
-      if (bottom != OUTP_L_NONE)
-        dump_line (this, x, shorten ? y2 : y1, x, y3);
-    }
-}
-
-/* Draws a generalized intersection of lines in the rectangle
-   (X0,Y0)-(X3,Y3).  The line coming from the top to the center
-   is of style TOP, from left to center of style LEFT, from
-   bottom to center of style BOTTOM, and from right to center of
-   style RIGHT. */
-static void
-ps_line (struct outp_driver *this,
-         int x0, int y0, int x3, int y3,
-         enum outp_line_style top, enum outp_line_style left,
-         enum outp_line_style bottom, enum outp_line_style right)
-{
-  /* The algorithm here is somewhat subtle, to allow it to handle
-     all the kinds of intersections that we need.
-
-     Three additional ordinates are assigned along the x axis.  The
-     first is xc, midway between x0 and x3.  The others are x1 and
-     x2; for a single vertical line these are equal to xc, and for
-     a double vertical line they are the ordinates of the left and
-     right half of the double line.
-
-     yc, y1, and y2 are assigned similarly along the y axis.
-
-     The following diagram shows the coordinate system and output
-     for double top and bottom lines, single left line, and no
-     right line:
-
-                 x0       x1 xc  x2      x3
-               y0 ________________________
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-     y1 = y2 = yc |#########     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-               y3 |________#_____#_______|
-  */
-  struct ps_driver_ext *ext = this->ext;
-
-  /* Offset from center of each line in a pair of double lines. */
-  int double_line_ofs = (ext->line_space + ext->line_width) / 2;
-
-  /* Are the lines along each axis single or double?
-     (It doesn't make sense to have different kinds of line on the
-     same axis, so we don't try to gracefully handle that case.) */
-  bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
-  bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
-
-  /* When horizontal lines are doubled,
-     the left-side line along y1 normally runs from x0 to x2,
-     and the right-side line along y1 from x3 to x1.
-     If the top-side line is also doubled, we shorten the y1 lines,
-     so that the left-side line runs only to x1,
-     and the right-side line only to x2.
-     Otherwise, the horizontal line at y = y1 below would cut off
-     the intersection, which looks ugly:
-               x0       x1     x2      x3
-             y0 ________________________
-                |        #     #       |
-                |        #     #       |
-                |        #     #       |
-                |        #     #       |
-             y1 |#########     ########|
-                |                      |
-                |                      |
-             y2 |######################|
-                |                      |
-                |                      |
-             y3 |______________________|
-     It is more of a judgment call when the horizontal line is
-     single.  We actually choose to cut off the line anyhow, as
-     shown in the first diagram above.
-  */
-  bool shorten_y1_lines = top == OUTP_L_DOUBLE;
-  bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
-  bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
-  int horz_line_ofs = double_vert ? double_line_ofs : 0;
-  int xc = (x0 + x3) / 2;
-  int x1 = xc - horz_line_ofs;
-  int x2 = xc + horz_line_ofs;
-
-  bool shorten_x1_lines = left == OUTP_L_DOUBLE;
-  bool shorten_x2_lines = right == OUTP_L_DOUBLE;
-  bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
-  int vert_line_ofs = double_horz ? double_line_ofs : 0;
-  int yc = (y0 + y3) / 2;
-  int y1 = yc - vert_line_ofs;
-  int y2 = yc + vert_line_ofs;
-
-  if (!double_horz)
-    horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
-  else
-    {
-      horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
-      horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
-    }
-
-  if (!double_vert)
-    vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
-  else
-    {
-      vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
-      vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
-    }
-}
-
-/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
-   and with the given JUSTIFICATION for THIS driver. */
-static int
-draw_text (struct outp_driver *this,
-           const char *string, int x, int y, int max_width,
-           enum outp_justification justification)
-{
-  struct outp_text text;
-  int width;
-
-  text.font = OUTP_PROPORTIONAL;
-  text.justification = justification;
-  text.string = ss_cstr (string);
-  text.h = max_width;
-  text.v = this->font_height;
-  text.x = x;
-  text.y = y;
-  this->class->text_metrics (this, &text, &width, NULL);
-  this->class->text_draw (this, &text);
-  return width;
-}
-
-/* Writes LEFT left-justified and RIGHT right-justified within
-   (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
-static void
-draw_header_line (struct outp_driver *this,
-                  const char *left, const char *right,
-                  int x0, int x1, int y)
-{
-  int right_width = 0;
-  if (right != NULL)
-    right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
-                   + this->prop_em_width);
-  if (left != NULL)
-    draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
-}
-
-/* Draw top of page headers for THIS driver. */
-static void
-draw_headers (struct outp_driver *this)
-{
-  struct ps_driver_ext *ext = this->ext;
-  char *r1, *r2;
-  int x0, x1;
-  int y;
-
-  y = -3 * this->font_height;
-  x0 = this->prop_em_width;
-  x1 = this->width - this->prop_em_width;
-
-  /* Draw box. */
-  fprintf (ext->file, "%d %d %d %d GB\n",
-          0, YT (y),
-           this->width, YT (y + 2 * this->font_height + ext->line_gutter));
-  y += ext->line_width + ext->line_gutter;
-
-  r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
-  r2 = xasprintf ("%s - %s", version, host_system);
-
-  draw_header_line (this, outp_title, r1, x0, x1, y);
-  y += this->font_height;
-
-  draw_header_line (this, outp_subtitle, r2, x0, x1, y);
-
-  free (r1);
-  free (r2);
-}
-\f
-/* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
-   given FONT.
-   The characters are justified according to JUSTIFICATION in a
-   field that has WIDTH_LEFT space remaining after the characters
-   themselves are accounted for.
-   Before character I is written, its x-position is adjusted by
-   KERNS[I]. */
-static void
-write_text (struct outp_driver *this,
-            int x0, int y0,
-            enum outp_font font,
-            enum outp_justification justification,
-            const struct afm_character **chars, int *kerns, size_t char_cnt,
-            int width_left)
-{
-  struct ps_driver_ext *ext = this->ext;
-  struct afm *afm = ext->fonts[font]->metrics;
-  struct string out;
-  size_t i, j;
-
-  if (justification == OUTP_RIGHT)
-    x0 += width_left;
-  else if (justification == OUTP_CENTER)
-    x0 += width_left / 2;
-  y0 += afm_get_ascent (afm) * this->font_height / 1000;
-
-  fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
-
-  if (ext->last_font != font)
-    {
-      ext->last_font = font;
-      fprintf (ext->file, "F%d setfont\n", font);
-    }
-
-  ds_init_empty (&out);
-  for (i = 0; i < char_cnt; i = j)
-    {
-      for (j = i + 1; j < char_cnt; j++)
-        if (kerns[j] != 0)
-          break;
-
-      if (kerns[i] != 0)
-        fprintf (ext->file, "%d K", kerns[i]);
-      while (i < j)
-        {
-          size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
-          if (encoded > 0)
-            {
-              fprintf (ext->file, "%sS\n", ds_cstr (&out));
-              ds_clear (&out);
-              i += encoded;
-            }
-
-          if (i < j)
-            {
-              fprintf (ext->file, "/%s GS\n", chars[i]->name);
-              i++;
-            }
-        }
-    }
-  ds_destroy (&out);
-}
-
-/* State of a text formatting operation. */
-struct text_state
-  {
-    /* Input. */
-    const struct outp_text *text;
-    bool draw;
-
-    /* Output. */
-    const struct afm_character **glyphs;
-    int *glyph_kerns;
-
-    /* State. */
-    size_t glyph_cnt;           /* Number of glyphs output. */
-    int width_left;            /* Width left over. */
-    int height_left;            /* Height left over. */
-
-    /* State as of last space. */
-    const char *space_char;     /* Just past last space. */
-    size_t space_glyph_cnt;     /* Number of glyphs as of last space. */
-    int space_width_left;       /* Width left over as of last space. */
-
-    /* Statistics. */
-    int max_width;             /* Widest line so far. */
-  };
-
-/* Adjusts S to complete a line of text,
-   and draws the current line if appropriate. */
-static void
-finish_line (struct outp_driver *this, struct text_state *s)
-{
-  int width;
-
-  if (s->draw)
-    {
-      write_text (this,
-                  s->text->x, s->text->y + (s->text->v - s->height_left),
-                  s->text->font,
-                  s->text->justification,
-                  s->glyphs, s->glyph_kerns, s->glyph_cnt,
-                  s->width_left);
-      s->glyph_cnt = 0;
-    }
-
-  /* Update maximum width. */
-  width = s->text->h - s->width_left;
-  if (width > s->max_width)
-    s->max_width = width;
-
-  /* Move to next line. */
-  s->width_left = s->text->h;
-  s->height_left -= this->font_height;
-
-  /* No spaces on this line yet. */
-  s->space_char = NULL;
-}
-
-/* Format TEXT on THIS driver.
-   If DRAW is nonzero, draw the text.
-   The width of the widest line is stored into *WIDTH, if WIDTH
-   is nonnull.
-   The total height of the text written is stored into *HEIGHT,
-   if HEIGHT is nonnull. */
-static void
-text (struct outp_driver *this, const struct outp_text *text, bool draw,
-      int *width, int *height)
-{
-  struct ps_driver_ext *ext = this->ext;
-  struct afm *afm = ext->fonts[text->font]->metrics;
-  const char *cp;
-  size_t glyph_cap;
-  struct text_state s;
-
-  s.text = text;
-  s.draw = draw;
-
-  s.glyphs = NULL;
-  s.glyph_kerns = NULL;
-  glyph_cap = 0;
-
-  s.glyph_cnt = 0;
-  s.width_left = s.text->h;
-  s.height_left = s.text->v;
-
-  s.space_char = 0;
-
-  s.max_width = 0;
-
-  cp = ss_data (s.text->string);
-  while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
-    {
-      const struct afm_character *cur;
-      int char_width;
-      int kern_adjust;
-
-      if (*cp == '\n')
-        {
-          finish_line (this, &s);
-          cp++;
-          continue;
-        }
-
-      /* Get character and resolve ligatures. */
-      cur = afm_get_character (afm, *cp);
-      while (++cp < ss_end (s.text->string))
-        {
-          const struct afm_character *next = afm_get_character (afm, *cp);
-          const struct afm_character *ligature = afm_get_ligature (cur, next);
-          if (ligature == NULL)
-            break;
-          cur = ligature;
-        }
-      char_width = cur->width * this->font_height / 1000;
-
-      /* Get kern adjustment. */
-      if (s.glyph_cnt > 0)
-        kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
-                       * this->font_height / 1000);
-      else
-        kern_adjust = 0;
-
-      /* Record the current status if this is a space character. */
-      if (cur->code == ' ' && cp > ss_data (s.text->string))
-       {
-         s.space_char = cp;
-         s.space_glyph_cnt = s.glyph_cnt;
-          s.space_width_left = s.width_left;
-       }
-
-      /* Enough room on this line? */
-      if (char_width + kern_adjust > s.width_left)
-       {
-         if (s.space_char == NULL)
-            {
-              finish_line (this, &s);
-              kern_adjust = 0;
-            }
-          else
-            {
-              cp = s.space_char;
-              s.glyph_cnt = s.space_glyph_cnt;
-              s.width_left = s.space_width_left;
-              finish_line (this, &s);
-              continue;
-            }
-       }
-
-      if (s.glyph_cnt >= glyph_cap)
-        {
-          glyph_cap = 2 * (glyph_cap + 8);
-          s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
-          s.glyph_kerns = xnrealloc (s.glyph_kerns,
-                                     glyph_cap, sizeof *s.glyph_kerns);
-        }
-      s.glyphs[s.glyph_cnt] = cur;
-      s.glyph_kerns[s.glyph_cnt] = kern_adjust;
-      s.glyph_cnt++;
-
-      s.width_left -= char_width + kern_adjust;
-    }
-  if (s.height_left >= this->font_height && s.glyph_cnt > 0)
-    finish_line (this, &s);
-
-  if (width != NULL)
-    *width = s.max_width;
-  if (height != NULL)
-    *height = text->v - s.height_left;
-  free (s.glyphs);
-  free (s.glyph_kerns);
-}
-
-static void
-ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
-                 int *width, int *height)
-{
-  text (this, t, false, width, height);
-}
-
-static void
-ps_text_draw (struct outp_driver *this, const struct outp_text *t)
-{
-  assert (this->page_open);
-  text (this, t, true, NULL, NULL);
-}
-\f
-static void embed_font (struct outp_driver *this, struct font *font);
-static void reencode_font (struct outp_driver *this, struct font *font);
-
-/* Loads and returns the font for STRING, which has the format
-   "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
-   PFA or PFB file's name, and ENC is the encoding file's name.
-   PFA and ENC are optional.
-   Returns a null pointer if unsuccessful. */
-static struct font *
-load_font (const char *string_)
-{
-  char *string = xstrdup (string_);
-  struct font *font;
-  char *position = string;
-  char *token;
-  char *afm_file_name;
-
-  font = xmalloc (sizeof *font);
-  font->metrics = NULL;
-  font->embed_fn = NULL;
-  font->encoding_fn = NULL;
-
-  token = strsep (&position, ",");
-  if (token == NULL)
-    {
-      error (0, 0, _("\"%s\": bad font specification"), string);
-      goto error;
-    }
-
-  /* Read AFM file. */
-  afm_file_name = find_ps_file (token);
-  if (afm_file_name == NULL)
-    {
-      error (0, 0, _("could not find AFM file \"%s\""), token);
-      goto error;
-    }
-  font->metrics = afm_open (afm_file_name);
-  free (afm_file_name);
-  if (font->metrics == NULL)
-    goto error;
-
-  /* Find font file to embed. */
-  token = strsep (&position, ",");
-  if (token != NULL && *token != '\0')
-    {
-      font->embed_fn = find_ps_file (token);
-      if (font->embed_fn == NULL)
-        error (0, 0, _("could not find font \"%s\""), token);
-    }
-
-  /* Find encoding. */
-  token = strsep (&position, ",");
-  if (token != NULL && *token == '\0')
-    {
-      font->encoding_fn = find_ps_file (token);
-      if (font->encoding_fn == NULL)
-        error (0, 0, _("could not find encoding \"%s\""), token);
-    }
-
-  free (string);
-  return font;
-
- error:
-  free (string);
-  free_font (font);
-  return NULL;
-}
-
-/* Frees FONT. */
-static void
-free_font (struct font *font)
-{
-  if (font != NULL)
-    {
-      afm_close (font->metrics);
-      free (font->embed_fn);
-      free (font->encoding_fn);
-      free (font);
-    }
-}
-
-/* Emits PostScript code to embed FONT (if necessary), scale it
-   to the proper size, re-encode it (if necessary), and store the
-   resulting font as an object named F#, where INDEX is
-   substituted for #. */
-static void
-setup_font (struct outp_driver *this, struct font *font, int index)
-{
-  struct ps_driver_ext *x = this->ext;
-  char *ps_name;
-
-  if (font->embed_fn != NULL)
-    embed_font (this, font);
-  else
-    fprintf (x->file, "%%%%IncludeResource: font %s\n",
-             afm_get_findfont_name (font->metrics));
-
-  ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
-  fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
-  free (ps_name);
-
-  if (font->encoding_fn != NULL)
-    reencode_font (this, font);
-
-  fprintf (x->file, "/F%d ED\n", index);
-}
-
-/* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
-   end-of-file or on error. */
-static void
-copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
-{
-  while (copy_bytes > 0)
-    {
-      char buffer[BUFSIZ];
-      unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
-      size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
-      size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
-      if (write_bytes != chunk_bytes)
-        break;
-      copy_bytes -= chunk_bytes;
-    }
-}
-
-/* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
-   end-of-file or on error.  The bytes are translated into
-   hexadecimal during copying and broken into lines with
-   new-line characters. */
-static void
-copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
-{
-  unsigned long i;
-
-  for (i = 0; i < copy_bytes; i++)
-    {
-      int c = getc (src);
-      if (c == EOF)
-        break;
-      if (i > 0 && i % 36 == 0)
-        putc ('\n', dst);
-      fprintf (dst, "%02X", c);
-    }
-  putc ('\n', dst);
-}
-
-/* Embeds the given FONT into THIS driver's output stream. */
-static void
-embed_font (struct outp_driver *this, struct font *font)
-{
-  struct ps_driver_ext *x = this->ext;
-  FILE *file;
-  int c;
-
-  file = fopen (font->embed_fn, "rb");
-  if (file == NULL)
-    {
-      error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
-      return;
-    }
-
-  fprintf (x->file, "%%%%BeginResource: font %s\n",
-           afm_get_findfont_name (font->metrics));
-
-  c = getc (file);
-  ungetc (c, file);
-  if (c != 128)
-    {
-      /* PFA file.  Copy literally. */
-      copy_bytes_literally (file, x->file, ULONG_MAX);
-    }
-  else
-    {
-      /* PFB file.  Translate as specified in Adobe Technical
-         Note #5040. */
-      while ((c = getc (file)) == 128)
-        {
-          int type;
-          unsigned long length;
-
-          type = getc (file);
-          if (type == 3)
-            break;
-
-          length = getc (file);
-          length |= (unsigned long) getc (file) << 8;
-          length |= (unsigned long) getc (file) << 16;
-          length |= (unsigned long) getc (file) << 24;
-
-          if (type == 1)
-            copy_bytes_literally (file, x->file, length);
-          else if (type == 2)
-            copy_bytes_as_hex (file, x->file, length);
-          else
-            break;
-        }
-    }
-  if (freaderror (file))
-    error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
-  fputs ("%%EndResource\n", x->file);
-}
-
-/* Re-encodes FONT according to the specified encoding. */
-static void
-reencode_font (struct outp_driver *this, struct font *font)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  struct string line;
-
-  int line_number;
-  FILE *file;
-
-  char *tab[256];
-
-  int i;
-
-  file = fopen (font->encoding_fn, "r");
-  if (file == NULL)
-    {
-      error (errno, 0, _("cannot open font encoding file \"%s\""),
-             font->encoding_fn);
-      return;
-    }
-
-  for (i = 0; i < 256; i++)
-    tab[i] = NULL;
-
-  line_number = 0;
-
-  ds_init_empty (&line);
-  while (ds_read_config_line (&line, &line_number, file))
-    {
-      char *pschar, *code;
-      char *save_ptr, *tail;
-      int code_val;
-
-      if (ds_is_empty (&line) == 0)
-        continue;
-
-      pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
-      code = strtok_r (NULL, " \t\r\n", &save_ptr);
-      if (pschar == NULL || code == NULL)
-        continue;
-
-      code_val = strtol (code, &tail, 0);
-      if (*tail)
-        {
-          error_at_line (0, 0, font->encoding_fn, line_number,
-                         _("invalid numeric format"));
-          continue;
-        }
-      if (code_val < 0 || code_val > 255)
-        continue;
-      if (tab[code_val] != 0)
-        free (tab[code_val]);
-      tab[code_val] = xstrdup (pschar);
-    }
-  ds_destroy (&line);
-
-  fputs ("[", x->file);
-  for (i = 0; i < 256; i++)
-    {
-      char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
-      fprintf (x->file, "%s\n", name);
-      free (name);
-      free (tab[i]);
-    }
-  fputs ("] RF\n", x->file);
-
-  if (freaderror (file) != 0)
-    error (errno, 0, _("closing Postscript encoding \"%s\""),
-           font->encoding_fn);
-}
-
-/* PostScript driver class. */
-const struct outp_class postscript_class =
-{
-  "postscript",
-  0,
-
-  ps_open_driver,
-  ps_close_driver,
-
-  ps_open_page,
-  ps_close_page,
-  NULL,
-
-  NULL,                         /* output_chart */
-
-  ps_submit,
-
-  ps_line,
-  ps_text_metrics,
-  ps_text_draw,
-};