pspp-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Pspp-cvs] pspp ./ChangeLog ./NEWS ./Smake config/ChangeLo...


From: Ben Pfaff
Subject: [Pspp-cvs] pspp ./ChangeLog ./NEWS ./Smake config/ChangeLo...
Date: Mon, 03 Apr 2006 20:07:55 +0000

CVSROOT:        /cvsroot/pspp
Module name:    pspp
Branch:         
Changes by:     Ben Pfaff <address@hidden>      06/04/03 20:07:55

Modified files:
        .              : ChangeLog NEWS Smake 
        config         : ChangeLog automake.mk devices 
        doc            : concept-index.texi configuring.texi 
                         installing.texi pspp.texinfo 
        po             : en_GB.po pspp.pot 
        src            : procedure.c 
        src/language   : ChangeLog line-buffer.c 
        src/language/data-io: data-list.c list.q print.c 
        src/language/dictionary: sys-file-info.c 
        src/language/stats: crosstabs.q descriptives.c examine.q 
                            frequencies.q oneway.q regression.q t-test.q 
        src/libpspp    : ChangeLog str.c str.h 
        src/output     : ChangeLog ascii.c automake.mk html.c htmlP.h 
                         manager.c output.c output.h postscript.c 
                         table.c table.h 
        tests          : ChangeLog 
        tests/bugs     : examine-missing.sh 
        tests/command  : examine-extremes.sh examine.sh oneway.sh 
                         t-test-1-indep-val.sh t-test-pairs.sh 
                         trimmed-mean.sh 
Added files:
        config/psfonts : Courier-Bold.afm Courier-BoldOblique.afm 
                         Courier-Oblique.afm Courier.afm 
                         Helvetica-Bold.afm Helvetica-BoldOblique.afm 
                         Helvetica-Oblique.afm Helvetica.afm 
                         Times-Bold.afm Times-BoldItalic.afm 
                         Times-Italic.afm Times-Roman.afm 
        src/output     : afm.c afm.h 
Removed files:
        config         : html-prologue ps-prologue 
        src/output     : font.h groff-font.c 

Log message:
        Clean up output subsystem.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/ChangeLog.diff?tr1=1.48&tr2=1.49&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/NEWS.diff?tr1=1.13&tr2=1.14&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/Smake.diff?tr1=1.24&tr2=1.25&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/ChangeLog.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/automake.mk.diff?tr1=1.1&tr2=1.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/devices.diff?tr1=1.5&tr2=1.6&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Courier-Bold.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Courier-BoldOblique.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Courier-Oblique.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Courier.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Helvetica-Bold.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Helvetica-BoldOblique.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Helvetica-Oblique.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Helvetica.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Times-Bold.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Times-BoldItalic.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Times-Italic.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/config/psfonts/Times-Roman.afm?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/doc/concept-index.texi.diff?tr1=1.1&tr2=1.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/doc/configuring.texi.diff?tr1=1.6&tr2=1.7&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/doc/installing.texi.diff?tr1=1.1&tr2=1.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/doc/pspp.texinfo.diff?tr1=1.5&tr2=1.6&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/po/en_GB.po.diff?tr1=1.73&tr2=1.74&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/po/pspp.pot.diff?tr1=1.76&tr2=1.77&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/procedure.c.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/ChangeLog.diff?tr1=1.1&tr2=1.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/line-buffer.c.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/data-io/data-list.c.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/data-io/list.q.diff?tr1=1.4&tr2=1.5&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/data-io/print.c.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/dictionary/sys-file-info.c.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/crosstabs.q.diff?tr1=1.4&tr2=1.5&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/descriptives.c.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/examine.q.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/frequencies.q.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/oneway.q.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/regression.q.diff?tr1=1.5&tr2=1.6&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/language/stats/t-test.q.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/libpspp/ChangeLog.diff?tr1=1.6&tr2=1.7&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/libpspp/str.c.diff?tr1=1.4&tr2=1.5&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/libpspp/str.h.diff?tr1=1.5&tr2=1.6&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/ChangeLog.diff?tr1=1.5&tr2=1.6&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/afm.c?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/afm.h?rev=1.1
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/ascii.c.diff?tr1=1.4&tr2=1.5&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/automake.mk.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/html.c.diff?tr1=1.6&tr2=1.7&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/htmlP.h.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/manager.c.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/output.c.diff?tr1=1.7&tr2=1.8&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/output.h.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/postscript.c.diff?tr1=1.8&tr2=1.9&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/table.c.diff?tr1=1.4&tr2=1.5&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/src/output/table.h.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/ChangeLog.diff?tr1=1.52&tr2=1.53&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/bugs/examine-missing.sh.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/command/examine-extremes.sh.diff?tr1=1.10&tr2=1.11&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/command/examine.sh.diff?tr1=1.14&tr2=1.15&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/command/oneway.sh.diff?tr1=1.16&tr2=1.17&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/command/t-test-1-indep-val.sh.diff?tr1=1.11&tr2=1.12&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/command/t-test-pairs.sh.diff?tr1=1.12&tr2=1.13&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/pspp/pspp/tests/command/trimmed-mean.sh.diff?tr1=1.13&tr2=1.14&r1=text&r2=text

Patches:
Index: pspp/ChangeLog
diff -u pspp/ChangeLog:1.48 pspp/ChangeLog:1.49
--- pspp/ChangeLog:1.48 Thu Mar 30 23:51:01 2006
+++ pspp/ChangeLog      Mon Apr  3 20:07:54 2006
@@ -1,3 +1,7 @@
+Mon Apr  3 11:01:00 2006  Ben Pfaff  <address@hidden>
+
+       * Smake: (GNULIB_MODULES) Add strsep.
+
 Thu Mar 30 15:50:05 2006  Ben Pfaff  <address@hidden>
 
        * Smake: Enable -Wdeclaration-after-statement warning if
Index: pspp/NEWS
diff -u pspp/NEWS:1.13 pspp/NEWS:1.14
--- pspp/NEWS:1.13      Sat Mar  4 00:28:15 2006
+++ pspp/NEWS   Mon Apr  3 20:07:54 2006
@@ -1,11 +1,28 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2006-01-28 19:05:46 blp>
+Time-stamp: <2006-04-03 10:56:18 blp>
 Copyright (C) 1996-9, 2000 Free Software Foundation, Inc.
 See the end for copying conditions.
 
 Please send PSPP bug reports to address@hidden
+
 Changes since 0.4.1:
 
+  Output changes:
+
+    * Output configuration options have changed.  Please refer to the
+      manual for a full description of the available options.
+
+      In consequence, you will need to reinstall your "devices" file.
+      "make install" will do this for you.
+
+    * The PostScript driver now obtains font metrics from AFM files,
+      instead of Groff-format metrics files.  It can now embed
+      PostScript fonts in its output.
+
+      In consequence, you will need to install an AFM file for each
+      font used in PostScript output.  "make install" will install AFM
+      files for the standard PostScript files, including the ones that
+      the PostScript driver uses by default.
 
 Changes since 0.4.0 to 0.4.1:
 
@@ -21,10 +38,10 @@
 
     A new PSPP extension called "scratch files" has been implemented.
     A scratch file, like a system file, consists of a dictionary and
-    any number of cases.  A sufficiently small scratch file is stored
-    in memory; if it grows too large, it is written to disk.  By
-    default, any file handle whose name begins with # is assumed to
-    refer to a scratch file.  
+    any number of cases.  Small scratch files are stored in memory;
+    one that grows too large is written to disk.  By default, any file
+    handle whose name begins with # is assumed to refer to a scratch
+    file.
 
     Scratch files can be used just about anywhere a system or portable
     file can be used.  Also, portable files are now allowed in most
Index: pspp/Smake
diff -u pspp/Smake:1.24 pspp/Smake:1.25
--- pspp/Smake:1.24     Thu Mar 30 23:49:21 2006
+++ pspp/Smake  Mon Apr  3 20:07:54 2006
@@ -35,6 +35,7 @@
        strcspn \
        strerror \
        strftime \
+       strsep \
        strstr \
        strtod \
        strtok_r \
Index: pspp/config/ChangeLog
diff -u pspp/config/ChangeLog:1.3 pspp/config/ChangeLog:1.4
--- pspp/config/ChangeLog:1.3   Sat Mar  4 00:28:15 2006
+++ pspp/config/ChangeLog       Mon Apr  3 20:07:54 2006
@@ -1,3 +1,19 @@
+Mon Apr  3 11:01:16 2006  Ben Pfaff  <address@hidden>
+
+       * automake.mk: (pkgsysconf_DATA) Add AFM files in config/psfonts.
+       Remove config/html-prologue, config/ps-prologue.
+
+       * html-prologue: Removed.
+
+       * ps-prologue: Removed.
+
+       * psfonts/Courier-Bold.afm, psfonts/Courier-BoldOblique.afm,
+       psfonts/Courier-Oblique.afm, psfonts/Courier.afm,
+       psfonts/Helvetica-Bold.afm, psfonts/Helvetica-BoldOblique.afm,
+       psfonts/Helvetica-Oblique.afm, psfonts/Helvetica.afm,
+       psfonts/Times-Bold.afm, psfonts/Times-BoldItalic.afm,
+       psfonts/Times-Italic.afm, psfonts/Times-Roman.afm: New files.
+
 Sat Feb 11 21:58:29 2006  Ben Pfaff  <address@hidden>
 
        * html-prologue: Don't use ${source-file}, which is no longer
Index: pspp/config/automake.mk
diff -u pspp/config/automake.mk:1.1 pspp/config/automake.mk:1.2
--- pspp/config/automake.mk:1.1 Sat Mar  4 00:53:47 2006
+++ pspp/config/automake.mk     Mon Apr  3 20:07:54 2006
@@ -3,10 +3,20 @@
 
 pkgsysconf_DATA = \
        config/devices \
-       config/html-prologue \
        config/papersize \
-       config/ps-prologue
-
+       config/psfonts/Helvetica-Bold.afm \
+       config/psfonts/Times-Bold.afm \
+       config/psfonts/Courier-Bold.afm \
+       config/psfonts/Helvetica-BoldOblique.afm \
+       config/psfonts/Times-BoldItalic.afm \
+       config/psfonts/Courier-BoldOblique.afm \
+       config/psfonts/Helvetica-Oblique.afm \
+       config/psfonts/Times-Italic.afm \
+       config/psfonts/Courier-Oblique.afm \
+       config/psfonts/Helvetica.afm \
+       config/psfonts/Times-Roman.afm \
+       config/psfonts/Courier.afm
+ 
 EXTRA_DIST += $(pkgsysconf_DATA)
 
 # A `private installation' in my terms is just having the appropriate
@@ -14,10 +24,10 @@
 # location.  So I let those files be installed automatically.
 
 private-install:
-       $(mkinstalldirs) $$HOME/.pspp
+       $(mkinstalldirs) $$HOME/.pspp $$HOME/.pspp/psfonts
        cd $(top_srcdir); cp $(pkgsysconf_DATA) $$HOME/.pspp
 
 private-uninstall:
-       -cd $$HOME/.pspp;  $(RM) $(notdir $(pkgsysconf_DATA))
-       -rmdir $$HOME/.pspp
+       -cd $$HOME/.pspp && rm $(notdir $(pkgsysconf_DATA))
+       -rmdir $$HOME/.pspp/psfonts $$HOME/.pspp
 
Index: pspp/config/devices
diff -u pspp/config/devices:1.5 pspp/config/devices:1.6
--- pspp/config/devices:1.5     Wed Dec 31 06:40:48 2003
+++ pspp/config/devices Mon Apr  3 20:07:54 2006
@@ -39,8 +39,6 @@
 # Macros may not be recursive; they may not take arguments.  (However,
 # `definition' is macro-expanded *at time of definition*.)  Macros are
 # referenced with $var or ${var} syntax; the latter is preferred.
-# Macro definitions on the PSPP command-line take precedence without
-# warning.
 
 # Preferred devices.
 default=tty list
@@ -51,118 +49,58 @@
 define tty-output-file "/dev/tty"
 define list-output-file "pspp.list"
 
-define no-attributes bold-on="" italic-on="" bold-italic-on=""
-
 # Generic ASCII devices
-tty-ascii:ascii:screen:char-set=ascii output-file=${tty-output-file} \
-  ${no-attributes}
-list-ascii:ascii:listing:length=66 width=79 char-set=ascii \
-  output-file=${list-output-file} ${no-attributes}
-raw-ascii:ascii:screen:width=9999 length=9999 char-set=ascii \
-  output-file=${list-output-file} ${no-attributes} headers=off paginate=off \
-  squeeze=on top-margin=0 bottom-margin=0
+tty-ascii:ascii:screen:output-file=${tty-output-file}
+list-ascii:ascii:listing:length=66 width=79 output-file=${list-output-file}
+raw-ascii:ascii:screen:width=9999 length=9999 output-file=${list-output-file} \
+  emphasis=none headers=off paginate=off squeeze=on \
+  top-margin=0 bottom-margin=0
 
 # ASCII devices that support bold & underline via backspacing.
-tty-ascii-bi:ascii:screen:char-set=ascii output-file=${tty-output-file}
-list-ascii-bi:ascii:listing:length=66 width=79 char-set=ascii \
-  output-file=${list-output-file}
+tty-ascii-bi:ascii:screen:output-file=${tty-output-file}
+list-ascii-bi:ascii:listing:length=66 width=79 output-file=${list-output-file}
 
 # HTML device.
 html:html::
 
+# PostScript device.
+list-ps:postscript::
+
 # Devices that support the IBM PC line-drawing characters.
 define ibmpc-graphics \
-  box[0000]='\x20' box[0001]='\xb3' box[0002]='\xba' box[0003]='\xba' \
-  box[0010]='\xc4' box[0011]='\xd9' box[0012]='\xbd' box[0013]='\xbd' \
-  box[0020]='\xcd' box[0021]='\xbe' box[0022]='\xbc' box[0023]='\xbc' \
-  box[0030]='\xf0' box[0031]='\xbe' box[0032]='\xbc' box[0033]='\xbc' \
-  box[0100]='\xb3' box[0101]='\xb3' box[0102]='\xc4' box[0103]='\xf0' \
-  box[0110]='\xbf' box[0111]='\xb4' box[0112]='\xb6' box[0113]='\xb6' \
-  box[0120]='\xb8' box[0121]='\xb5' box[0122]='\xb9' box[0123]='\xb9' \
-  box[0130]='\xb8' box[0131]='\xb5' box[0132]='\xb9' box[0133]='\xb9' \
-  box[0200]='\xba' box[0201]='\xba' box[0202]='\xba' box[0203]='\xba' \
-  box[0210]='\xb7' box[0211]='\xb6' box[0212]='\xb6' box[0213]='\xb6' \
-  box[0220]='\xbb' box[0221]='\xb9' box[0222]='\xb9' box[0223]='\xb9' \
-  box[0300]='\xb3' box[0301]='\xba' box[0302]='\xba' box[0303]='\xba' \
-  box[0310]='\xb7' box[0311]='\xb6' box[0312]='\xb6' box[0313]='\xb6' \
-  box[0320]='\xbb' box[0321]='\xb9' box[0322]='\xb9' box[0323]='\xb9' \
-  box[0330]='\xbb' box[0331]='\xb9' box[0332]='\xb9' box[0333]='\xb9' \
-  box[1000]='\xc4' box[1001]='\xc0' box[1002]='\xd3' box[1003]='\xd3' \
-  box[1010]='\xc4' box[1011]='\xc1' box[1012]='\xd0' box[1013]='\xd0' \
-  box[1020]='\xcd' box[1021]='\xcf' box[1022]='\xca' box[1023]='\xca' \
-  box[1030]='\xf0' box[1031]='\xcf' box[1032]='\xca' box[1033]='\xca' \
-  box[1100]='\xda' box[1101]='\xc3' box[1102]='\xc7' box[1103]='\xc7' \
-  box[1110]='\xc2' box[1111]='\xc5' box[1112]='\xd7' box[1113]='\xd7' \
-  box[1120]='\xd1' box[1121]='\xd8' box[1122]='\xce' box[1123]='\xce' \
-  box[1130]='\xd1' box[1131]='\xd8' box[1132]='\xce' box[1133]='\xce' \
-  box[1200]='\xd6' box[1201]='\xc7' box[1202]='\xc7' box[1203]='\xc7' \
-  box[1210]='\xd2' box[1211]='\xd7' box[1212]='\xd7' box[1213]='\xd7' \
-  box[1220]='\xca' box[1221]='\xce' box[1222]='\xce' box[1223]='\xce' \
-  box[1230]='\xca' box[1231]='\xce' box[1232]='\xce' box[1233]='\xce' \
-  box[1300]='\xd6' box[1301]='\xc7' box[1302]='\xc7' box[1303]='\xc7' \
-  box[1310]='\xd2' box[1311]='\xd7' box[1312]='\xd7' box[1313]='\xd7' \
-  box[1320]='\xca' box[1321]='\xce' box[1322]='\xce' box[1323]='\xce' \
-  box[1330]='\xca' box[1331]='\xce' box[1332]='\xce' box[1333]='\xce' \
-  box[2000]='\xcd' box[2001]='\xd4' box[2002]='\xc8' box[2003]='\xc8' \
-  box[2010]='\xcd' box[2011]='\xcf' box[2012]='\xca' box[2013]='\xca' \
-  box[2020]='\xcd' box[2021]='\xcf' box[2022]='\xca' box[2023]='\xca' \
-  box[2030]='\xf0' box[2031]='\xcf' box[2032]='\xca' box[2033]='\xca' \
-  box[2100]='\xd5' box[2101]='\xc6' box[2102]='\xcc' box[2103]='\xcc' \
-  box[2110]='\xd1' box[2111]='\xd8' box[2112]='\xce' box[2113]='\xce' \
-  box[2120]='\xd1' box[2121]='\xd8' box[2122]='\xce' box[2123]='\xce' \
-  box[2130]='\xd1' box[2131]='\xd8' box[2132]='\xce' box[2133]='\xce' \
-  box[2200]='\xc9' box[2201]='\xcc' box[2202]='\xcc' box[2203]='\xcc' \
-  box[2210]='\xcb' box[2211]='\xce' box[2212]='\xce' box[2213]='\xce' \
-  box[2220]='\xcb' box[2221]='\xce' box[2222]='\xce' box[2223]='\xce' \
-  box[2230]='\xcb' box[2231]='\xce' box[2232]='\xce' box[2233]='\xce' \
-  box[2300]='\xc9' box[2301]='\xcc' box[2302]='\xcc' box[2303]='\xce' \
-  box[2310]='\xcb' box[2311]='\xce' box[2312]='\xce' box[2313]='\xce' \
-  box[2320]='\xcb' box[2321]='\xce' box[2322]='\xce' box[2323]='\xce' \
-  box[2330]='\xcb' box[2331]='\xce' box[2332]='\xce' box[2333]='\xce' \
-  box[3000]='\xcd' box[3001]='\xd4' box[3002]='\xc8' box[3003]='\xc8' \
-  box[3010]='\xcd' box[3011]='\xcf' box[3012]='\xca' box[3013]='\xca' \
-  box[3020]='\xcd' box[3021]='\xcf' box[3022]='\xca' box[3023]='\xca' \
-  box[3030]='\xcd' box[3031]='\xcf' box[3032]='\xca' box[3033]='\xca' \
-  box[3100]='\xd5' box[3101]='\xc6' box[3102]='\xcc' box[3103]='\xcc' \
-  box[3110]='\xd1' box[3111]='\xd8' box[3112]='\xce' box[3113]='\xce' \
-  box[3120]='\xd1' box[3121]='\xd8' box[3122]='\xce' box[3123]='\xce' \
-  box[3130]='\xd1' box[3131]='\xd8' box[3132]='\xce' box[3133]='\xce' \
-  box[3200]='\xc9' box[3201]='\xcc' box[3202]='\xcc' box[3203]='\xcc' \
-  box[3210]='\xcb' box[3211]='\xce' box[3212]='\xce' box[3213]='\xce' \
-  box[3220]='\xcb' box[3221]='\xce' box[3222]='\xce' box[3223]='\xce' \
-  box[3230]='\xcb' box[3231]='\xce' box[3232]='\xce' box[3233]='\xce' \
-  box[3300]='\xc9' box[3301]='\xcc' box[3302]='\xcc' box[3303]='\xce' \
-  box[3310]='\xcb' box[3311]='\xce' box[3312]='\xce' box[3313]='\xce' \
-  box[3320]='\xcb' box[3321]='\xce' box[3322]='\xce' box[3323]='\xce' \
-  box[3330]='\xcb' box[3331]='\xce' box[3332]='\xce' box[3333]='\xce'
+  box[0000]='\x20' box[0001]='\xb3' box[0002]='\xba' \
+  box[0010]='\xc4' box[0011]='\xd9' box[0012]='\xbd' \
+  box[0020]='\xcd' box[0021]='\xbe' box[0022]='\xbc' \
+  box[0100]='\xb3' box[0101]='\xb3' box[0102]='\xc4' \
+  box[0110]='\xbf' box[0111]='\xb4' box[0112]='\xb6' \
+  box[0120]='\xb8' box[0121]='\xb5' box[0122]='\xb9' \
+  box[0200]='\xba' box[0201]='\xba' box[0202]='\xba' \
+  box[0210]='\xb7' box[0211]='\xb6' box[0212]='\xb6' \
+  box[0220]='\xbb' box[0221]='\xb9' box[0222]='\xb9' \
+  box[1000]='\xc4' box[1001]='\xc0' box[1002]='\xd3' \
+  box[1010]='\xc4' box[1011]='\xc1' box[1012]='\xd0' \
+  box[1020]='\xcd' box[1021]='\xcf' box[1022]='\xca' \
+  box[1100]='\xda' box[1101]='\xc3' box[1102]='\xc7' \
+  box[1110]='\xc2' box[1111]='\xc5' box[1112]='\xd7' \
+  box[1120]='\xd1' box[1121]='\xd8' box[1122]='\xce' \
+  box[1200]='\xd6' box[1201]='\xc7' box[1202]='\xc7' \
+  box[1210]='\xd2' box[1211]='\xd7' box[1212]='\xd7' \
+  box[1220]='\xca' box[1221]='\xce' box[1222]='\xce' \
+  box[2000]='\xcd' box[2001]='\xd4' box[2002]='\xc8' \
+  box[2010]='\xcd' box[2011]='\xcf' box[2012]='\xca' \
+  box[2020]='\xcd' box[2021]='\xcf' box[2022]='\xca' \
+  box[2100]='\xd5' box[2101]='\xc6' box[2102]='\xcc' \
+  box[2110]='\xd1' box[2111]='\xd8' box[2112]='\xce' \
+  box[2120]='\xd1' box[2121]='\xd8' box[2122]='\xce' \
+  box[2200]='\xc9' box[2201]='\xcc' box[2202]='\xcc' \
+  box[2210]='\xcb' box[2211]='\xce' box[2212]='\xce' \
+  box[2220]='\xcb' box[2221]='\xce' box[2222]='\xce'
 
 tty-ibmpc:ascii:screen:length=$viewlength width=$viewwidth ${ibmpc-graphics} \
   output-file=${tty-output-file}
 list-ibmpc:ascii:listing:length=66 width=79 output-file=${list-output-file} \
   ${ibmpc-graphics}
 
-# PostScript device.  Tested with HP LaserJet 6MP.
-list-ps:postscript::
-
-# Okidata Microline 520 (these use the Microline emulation mode).
-define ml520-common output-file=${list-output-file} ${ibmpc-graphics} \
-  bold-on='\x1b\x54' bold-off='\x1b\x49' init='\x1b\x7b\x21\x1b\x23\x30'
-define ml520-italic italic-on='\x1b\x21\x2f' italic-off='\x1b\x21\x2a' \
-  bold-italic-on='\x1b\x21\x2f\x1b\x54' bold-italic-off='\x1b\x21\x2a\x1b\x49'
-define ml520-ul italic-on='\x1b\x43' italic-off='\x1b\x44' \
-  bold-italic-on='\x1b\x43\x1b\x54' bold-italic-off='\x1b\x44\x1b\x49'
-ml520=ml520-10cpi
-ml520-10cpi:ascii:printer:length=66 width=79 ${ml520-common} ${ml520-italic}
-ml520-10cpi-ul:ascii:printer:length=66 width=79 ${ml520-common} ${ml520-ul}
-ml520-17cpi:ascii:printer:length=66 width=144 ${ml520-common} ${ml520-italic} \
-  cpi=17 init='\x1b\x7b\x21\x1b\x23\x30\x1d'
-ml520-17cpi-ul:ascii:printer:length=66 width=144 ${ml520-common} ${ml520-ul} \
-  cpi=17 init='\x1b\x7b\x21\x1b\x23\x30\x1d'
-ml520-20cpi:ascii:printer:length=66 width=160 ${ml520-common} ${ml520-italic} \
-  cpi=17 init='\x1b\x7b\x21\x1b\x23\x30\x1b\x23\x33'
-ml520-20cpi-ul:ascii:printer:length=66 width=160 ${ml520-common} ${ml520-ul} \
-  cpi=17 init='\x1b\x7b\x21\x1b\x23\x30\x1b\x23\x33'
-
 # Local Variables:
 # fill-prefix: "# "
 # End:
Index: pspp/doc/concept-index.texi
diff -u pspp/doc/concept-index.texi:1.1 pspp/doc/concept-index.texi:1.2
--- pspp/doc/concept-index.texi:1.1     Sat Oct 30 10:14:05 2004
+++ pspp/doc/concept-index.texi Mon Apr  3 20:07:54 2006
@@ -1,4 +1,4 @@
address@hidden Concept Index, Installation, Command Index, Top
address@hidden Concept Index, Configuration, Command Index, Top
 @chapter Concept Index
 @printindex cp
 @setfilename ignored
Index: pspp/doc/configuring.texi
diff -u pspp/doc/configuring.texi:1.6 pspp/doc/configuring.texi:1.7
--- pspp/doc/configuring.texi:1.6       Sun Dec 11 02:48:44 2005
+++ pspp/doc/configuring.texi   Mon Apr  3 20:07:54 2006
@@ -1,18 +1,13 @@
address@hidden Configuration, Portable File Format, Installation, Top
address@hidden Configuration, Portable File Format, Concept Index, Top
 @appendix Configuring PSPP
 @cindex configuration
 @cindex PSPP, configuring
 
-PSPP has dozens of configuration possibilities and hundreds of
-settings.  This is both a bane and a blessing.  On one hand, it's
-possible to easily accommodate diverse ranges of setups.  But, on the
-other, the multitude of possibilities can overwhelm the casual user.
-Fortunately, the configuration mechanisms are profusely described in the
-sections address@hidden
+This chapter describe how to configure PSPP for your system.
 
 @menu
 * File locations::              How PSPP finds config files.
-* Configuration techniques::    Many different methods of address@hidden
+* Configuration techniques::    Many different methods of configuration...
 * Configuration files::         How configuration files are read.
 * Environment variables::       All about environment variables.
 * Output devices::              Describing your terminal(s) and printer(s).
@@ -20,123 +15,19 @@
 * ASCII driver class::          Configuration of character-code devices.
 * HTML driver class::           Configuration for HTML output.
 * Miscellaneous configuring::   Even more configuration variables.
-* Improving output quality::    Hints for producing ever-more-lovely output.
 @end menu
 
 @node File locations, Configuration techniques, Configuration, Configuration
 @section Locating configuration files
 
-PSPP uses the same method to find most of its configuration files:
-
address@hidden
address@hidden
-The @dfn{base name} of the file being sought is determined.
-
address@hidden
-The path to search is determined.  
-
address@hidden
-Each directory in the search path, from left to right, is searched for a
-file with the name of the base name.  The first occurrence is read
-as the configuration file.
address@hidden enumerate
-
-The first two steps are elaborated below for the sake of our pedantic
-friends.
-
address@hidden
address@hidden
-A @dfn{base name} is a file name lacking an absolute directory
-reference.  Some examples of base names are: @file{ps-encodings},
address@hidden, @file{devps/DESC} (under UNIX), @file{devps\DESC} (under
-M$ environments).
-
-Determining the base name is a two-step process:
-
address@hidden a
address@hidden
-If the appropriate environment variable is defined, the value of that
-variable is used (@pxref{Environment variables}).  For instance, when
-searching for the output driver initialization file, the variable
-examined is @code{STAT_OUTPUT_INIT_FILE}.
-
address@hidden
-Otherwise, the compiled-in default is used.  For example, when searching
-for the output driver initialization file, the default base name is
address@hidden
address@hidden enumerate
-
address@hidden note:} If a user-specified base name does contain an
-absolute directory reference, as in a file name like
address@hidden/home/pfaff/fonts/TR}, no path is searched---the file name is used
-exactly as given---and the algorithm terminates.
-
address@hidden
-The path is the first of the following that is defined:
-
address@hidden @bullet
address@hidden
-A variable definition for the path given in the user environment.  This
-is a PSPP-specific environment variable name; for instance,
address@hidden
-
address@hidden 
-In some cases, another, less-specific environment variable is checked.
-For instance, when searching for font files, the PostScript driver first
-checks for a variable with name @code{STAT_GROFF_FONT_PATH}, then for
-one with name @code{GROFF_FONT_PATH}.  (However, font searching has its
-own list of esoteric search rules.)
-
address@hidden
-The configuration file path, which is itself determined by the
-following rules:
-
address@hidden a
address@hidden
-If the command line contains an option of the form @samp{-B @var{path}}
-or @address@hidden, then the value given on the
-rightmost occurrence of such an option is used.
-
address@hidden
-Otherwise, if the environment variable @code{STAT_CONFIG_PATH} is
-defined, the value of that variable is used.
-
address@hidden
-Otherwise, the compiled-in fallback default is used.  On UNIX machines,
-the default fallback path is
-
address@hidden 1
address@hidden
address@hidden/.pspp}
-
address@hidden
address@hidden/usr/local/lib/pspp}
-
address@hidden
address@hidden/usr/lib/pspp}
address@hidden enumerate
-
-On DOS machines, the default fallback path is:
-
address@hidden 1
address@hidden
-All the paths from the DOS search path in the @samp{PATH} environment
-variable, in left-to-right order.
-
address@hidden
address@hidden:\PSPP}, as a last resort.
address@hidden enumerate
-
-Note that the installer of PSPP can easily change this default
-fallback path; thus the above should not be taken as gospel.
address@hidden enumerate
address@hidden itemize
address@hidden enumerate
-
-As a final note: Under DOS, directories given in paths are delimited by
-semicolons (@samp{;}); under UNIX, directories are delimited by colons
-(@samp{:}).  This corresponds with the standard path delimiter under
-these OSes.
+PSPP searches each directory in the configuration file path for most
+configuration files.  The default configuration file path searches first
address@hidden/.pspp}, then the package system configuration directory (usually
address@hidden/usr/local/etc/pspp} or @file{/etc/pspp}).  The value of
+environment variable @env{PSPP_CONFIG_PATH}, if defined, overrides this
+default path.  Finally, @samp{-B @var{path}} or
address@hidden@var{path}} specified on the command line has highest
+priority.
 
 @node Configuration techniques, Configuration files, File locations, 
Configuration
 @section Configuration techniques
@@ -231,55 +122,11 @@
 in the sections below:
 
 @menu
-* Variable values::             Values of variables are determined this way.
 * Environment substitutions::   How environment substitutions are made.
 * Predefined variables::        A few variables are automatically defined.
 @end menu
 
address@hidden Variable values, Environment substitutions, Environment 
variables, Environment variables
address@hidden Values of environment variables
-
-Values for environment variables are obtained by the following means,
-which are arranged in order of decreasing precedence:
-
address@hidden
address@hidden
-Command-line options.  @xref{Invocation}.
-
address@hidden
-The @file{environment} configuration file---more on this below.
-
address@hidden
-Actual environment variables (defined in the shell or other parent
-process).
address@hidden enumerate
-
-The @file{environment} configuration file is located through application
-of the usual algorithm for configuration files (@pxref{File locations}),
-except that its contents do not affect the search path used to find
address@hidden itself.  Use of @file{environment} is discouraged on
-systems that allow an arbitrarily large environment; it is supported for
-use on systems like MS-DOS that limit environment size.
-
address@hidden is composed of lines having the form
address@hidden@address@hidden, where @var{key} and the equals sign
-(@samp{=}) are required, and @var{value} is optional.  If @var{value} is
-given, variable @var{key} is given that value; if @var{value} is absent,
-variable @var{key} is undefined (deleted).  Variables may not be defined
-with a null value.
-
-Environment substitutions are performed on each line in the file
-(@pxref{Environment substitutions}).
-
-See @ref{Configuration files}, for more details on formatting of the
-environment configuration file.
-
address@hidden
address@hidden note:} Support for @file{environment} is not yet
-implemented.
address@hidden quotation
-
address@hidden Environment substitutions, Predefined variables, Variable 
values, Environment variables
address@hidden Environment substitutions, Predefined variables, Environment 
variables, Environment variables
 @subsection Environment substitutions
 
 Much of the power of environment variables lies in the way that they may
@@ -287,24 +134,15 @@
 described below.
 
 The line is scanned from left to right.  In this scan, all characters
-other than dollar signs (@samp{$}) are retained unmolested.  Dollar
-signs, however, introduce an environment variable reference.  References
+other than dollar signs (@samp{$}) are retained without change.  Dollar
+signs introduce environment variable references.  References
 take three forms:
 
 @table @code
 @item address@hidden
-Replaced by the value of environment variable @var{var}, determined as
-specified in @ref{Variable values}.  @var{var} must be one of the
-following:
-
address@hidden @bullet
address@hidden
-One or more letters.
-
address@hidden
-Exactly one nonalphabetic character.  This may not be a left brace
-(@address@hidden).
address@hidden itemize
+Replaced by the value of environment variable @var{var}.  @var{var} must
+consist of either one or more letters, or exactly one non-alphabetic
+character other than a left brace (@address@hidden).
 
 @item address@hidden@address@hidden
 Same as above, but @var{var} may contain any character (except
@@ -737,34 +575,9 @@
 @section The PostScript driver class
 
 The @code{postscript} driver class is used to produce output that is
-acceptable to PostScript printers and to PC-based PostScript
-interpreters such as Ghostscript.  Continuing a long tradition,
-PSPP's PostScript driver is configurable to the point of
-absurdity.
-
-There are actually two PostScript drivers.  The first one,
address@hidden, produces ordinary DSC-compliant PostScript output.
-The second one @samp{epsf}, produces an Encapsulated PostScript file.
-The two drivers are otherwise identical in configuration and in
-operation.
+acceptable to PostScript printers and other interpreters.
 
-The PostScript driver is described in further detail below.
-
address@hidden
-* PS output options::           Output file options.
-* PS page options::             Paper, margins, scaling & rotation, more!
-* PS file options::             Configuration files.
-* PS font options::             Default fonts, font options.
-* PS line options::             Line widths, options.
-* Prologue::                    Details on the PostScript prologue.
-* Encodings::                   Details on PostScript font encodings.
address@hidden menu
-
address@hidden PS output options, PS page options, PostScript driver class, 
PostScript driver class
address@hidden PostScript output options
-
-These options deal with the form of the output and the output file
-itself:
+The available options are listed below.
 
 @table @code
 @item address@hidden
@@ -773,57 +586,6 @@
 (i.e., @code{"pspp.ps"}), a pipe filename (i.e., @code{"|lpr"}), or
 stdout (@code{"-"}).  Default: @code{"pspp.ps"}.
 
address@hidden address@hidden
-
-Most of the time black-and-white PostScript devices are smart enough to
-map colors to shades themselves.  However, you can cause the PSPP
-output driver to do an ugly simulation of this in its own driver by
-turning @code{color} off.  Default: @code{on}.
-
-This is a boolean setting, as are many settings in the PostScript
-driver.  Valid positive boolean values are @samp{on}, @samp{true},
address@hidden, and nonzero integers.  Negative boolean values are
address@hidden, @samp{false}, @samp{no}, and zero.
-
address@hidden address@hidden
-
-One of @code{clean7bit}, @code{clean8bit}, or @code{binary}.  This
-controls what characters will be written to the output file.  PostScript
-produced with @code{clean7bit} can be transmitted over 7-bit
-transmission channels that use ASCII control characters for line
-control.  @code{clean8bit} is similar but allows characters above 127 to
-be written to the output file.  @code{binary} allows any character in
-the output file.  Default: @code{clean7bit}.
-
address@hidden address@hidden
-
-One of @code{cr}, @code{lf}, or @code{crlf}.  This controls what is used
-for new-line in the output file.  Default: @code{cr}.
-
address@hidden address@hidden
-
-Either @code{0} or @code{1}.  If @var{level} is @code{1}, then short
-line segments will be collected and merged into longer ones.  This
-reduces output file size but requires more time and memory.  A
address@hidden of @code{0} has the advantage of being better for
-interactive environments.  @code{1} is the default unless the
address@hidden flag is set; in that case, the default is @code{0}.
-
address@hidden address@hidden
-
-One of @code{0}, @code{1}, or @code{2}, each higher level representing
-correspondingly more aggressive space savings for text in the output
-file and requiring correspondingly more time and memory.  Unfortunately
-the levels presently are all the same.  @code{1} is the default unless
-the @code{screen} flag is set; in that case, the default is @code{0}.
address@hidden table
-
address@hidden PS page options, PS file options, PS output options, PostScript 
driver class
address@hidden PostScript page options
-
-These options affect page setup:
-
address@hidden @code
 @item address@hidden
 
 Controls whether the standard headers showing the time and date and
@@ -849,355 +611,58 @@
 included in the margins; they are in addition to the margins.  For a
 description of dimensions, see @ref{Dimensions}.  Default: @code{0.5in}.
 
address@hidden table
-
address@hidden PS file options, PS font options, PS page options, PostScript 
driver class
address@hidden PostScript file options
-
-Oh, my.  You don't really want to know about the way that the PostScript
-driver deals with files, do you?  Well I suppose you're entitled, but I
-warn you right now: it's not pretty.  Here address@hidden
-
-First let's look at the options that are available:
-
address@hidden @code
-
address@hidden address@hidden
-
-Sets the font directory.  Default: @code{devps}.
-
address@hidden address@hidden
-
-Sets the name of the PostScript prologue file.  You can write your own
-prologue, though I have no idea why you'd want to: see @ref{Prologue}.
-Default: @code{ps-prologue}.
-
address@hidden address@hidden
-
-Sets the name of the Groff-format device description file.  The
-PostScript driver reads this to know about the scaling of fonts
-and so on.  The format of such files is described in the groff_font man page,
-included with Groff.  Default: @code{DESC}.
-
address@hidden address@hidden
-
-Sets the name of the encoding file.  This file contains a list of all
-font encodings that will be needed so that the driver can put all of
-them at the top of the prologue.  @xref{Encodings}.  Default:
address@hidden
-
-If the specified encoding file cannot be found, this error will be
-silently ignored, since most people do not need any encodings besides
-the ones that can be found using @code{auto-encodings}, described below.
-
address@hidden address@hidden
-
-When enabled, the font encodings needed by the default proportional- and
-fixed-pitch fonts will automatically be dumped to the PostScript
-output.  Otherwise, it is assumed that the user has an encoding file
-and knows how to use it (@pxref{Encodings}).  There is probably no good
-reason to turn off this convenient feature.  Default: @code{on}.
-
address@hidden table
-
-Next I suppose it's time to describe the search algorithm.  When the
-PostScript driver needs a file, whether that file be a font, a
-PostScript prologue, or what you will, it searches in this manner:
-
address@hidden
-
address@hidden
-Constructs a path by taking the first of the following that is defined:
-
address@hidden a
-
address@hidden
-Environment variable @code{STAT_GROFF_FONT_PATH}.  @xref{Environment
-variables}.
-
address@hidden
-Environment variable @code{GROFF_FONT_PATH}.
-
address@hidden
-The compiled-in fallback default.
address@hidden enumerate
-
address@hidden
-Constructs a base name from concatenating, in order, the font directory,
-a path separator (@samp{/} or @samp{\}), and the file to be found.  A
-typical base name would be something like @code{devps/ps-encodings}.
address@hidden address@hidden,@var{font-file}[,@var{encoding-file}]]
address@hidden address@hidden,@var{font-file}[,@var{encoding-file}]]
address@hidden address@hidden,@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
address@hidden 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}).
 
address@hidden
-Searches for the base name in the path constructed above.  If the file
-is found, the algorithm terminates.
-
address@hidden
-Searches for the base name in the standard configuration path.  See
address@hidden locations}, for more details.  If the file is found, the
-algorithm terminates.
-
address@hidden
-At this point we remove the font directory and path separator from the
-base name.  Now the base name is simply the file to be found, i.e.,
address@hidden
-
address@hidden
-Searches for the base name in the path constructed in the first step.
-If the file is found, the algorithm terminates.
-
address@hidden
-Searches for the base name in the standard configuration path.  If the
-file is found, the algorithm terminates.
-
address@hidden
-The algorithm terminates unsuccessfully.
address@hidden enumerate
-
-So, as you see, there are several ways to configure the PostScript
-drivers.  Careful selection of techniques can make the configuration
-very flexible indeed.
-
address@hidden PS font options, PS line options, PS file options, PostScript 
driver class
address@hidden PostScript font options
-
-The list of available font options is short and sweet:
-
address@hidden @code
address@hidden address@hidden
-
-Sets the default proportional font.  The name should be that of a
-PostScript font.  Default: @code{"Helvetica"}.
-
address@hidden address@hidden
-
-Sets the default fixed-pitch font.  The name should be that of a
-PostScript font.  Default: @code{"Courier"}.
+Default: proportional font @code{Times-Roman.afm}, emphasis font
address@hidden, fixed-pitch font @code{Courier.afm}.
 
 @item address@hidden
 
 Sets the size of the default fonts, in thousandths of a point.  Default:
address@hidden
-
address@hidden table
-
address@hidden PS line options, Prologue, PS font options, PostScript driver 
class
address@hidden PostScript line options
-
-Most tables contain lines, or rules, between cells.  Some features of
-the way that lines are drawn in PostScript tables are user-definable:
-
address@hidden @code
-
address@hidden address@hidden
-
-Sets the style used for lines used to divide tables into sections.
address@hidden must be either @code{thick}, in which case thick lines are
-used, or @var{double}, in which case double lines are used.  Default:
address@hidden
+10000 (10 point).
 
 @item address@hidden
 
-Sets the line gutter, which is the amount of white space on either side
-of lines that border text or graphics objects.  @xref{Dimensions}.
-Default: @code{0.5pt}.
+Sets the width of white space on either side of lines that border text
+or graphics objects.  @xref{Dimensions}.  Default: @code{1pt}.
 
 @item address@hidden
 
-Sets the line spacing, which is the amount of white space that separates
-lines that are side by side, as in a double line.  Default:
address@hidden
+Sets the spacing between the lines in a double line in a table.
+Default: @code{1pt}.
 
 @item address@hidden
 
-Sets the width of a typical line used in tables.  Default: @code{0.5pt}.
-
address@hidden address@hidden
-
-Sets the width of a thick line used in tables.  Not used if
address@hidden is set to @code{thick}.  Default: @code{1.5pt}.
-
address@hidden table
-
address@hidden Prologue, Encodings, PS line options, PostScript driver class
address@hidden The PostScript prologue
-
-Most PostScript files that are generated mechanically by programs
-consist of two parts: a prologue and a body.  The prologue is generally
-a collection of boilerplate.  Only the body differs greatly between
-two outputs from the same program.
- 
-This is also the strategy used in the PSPP PostScript driver.  In
-general, the prologue supplied with PSPP will be more than sufficient.
-In this case, you will not need to read the rest of this section.
-However, hackers might want to know more.  Read on, if you fall into
-this category.
-
-The prologue is dumped into the output stream essentially unmodified.
-However, two actions are performed on its lines.  First, certain lines
-may be omitted as specified in the prologue file itself.  Second,
-variables are substituted.
-
-The following lines are omitted:
-
address@hidden
address@hidden
-All lines that contain three bangs in a row (@code{!!!}).
-
address@hidden
-Lines that contain @code{!eps}, if the PostScript driver is producing
-ordinary PostScript output.  Otherwise an EPS file is being produced,
-and the line is included in the output, although everything following
address@hidden is deleted.
-
address@hidden
-Lines that contain @code{!ps}, if the PostScript driver is producing EPS
-output.  Otherwise, ordinary PostScript is being produced, and the line
-is included in the output, although everything following @code{!ps} is
-deleted.
address@hidden enumerate
-
-The following are the variables that are substituted.  Only the
-variables listed are substituted; environment variables are not.
address@hidden substitutions}.
-
address@hidden @code
address@hidden bounding-box
-
-The page bounding box, in points, as four space-separated numbers.  For
-U.S. letter size paper, this is @samp{0 0 612 792}.
-
address@hidden creator
-
-PSPP version as a string: @samp{GNU PSPP 0.1b}, for example.
-
address@hidden date
-
-Date the file was created.  Example: @samp{Tue May 21 13:46:22 1991}.
-
address@hidden data
-
-Value of the @code{data} PostScript driver option, as one of the strings
address@hidden, @samp{Clean8Bit}, or @samp{Binary}.
-
address@hidden orientation
-
-Page orientation, as one of the strings @code{Portrait} or
address@hidden
-
address@hidden user
-
-Under multiuser OSes, the user's login name, taken either from the
-environment variable @code{LOGNAME} or, if that fails, the result of the
-C library function @code{getlogin()}.  Defaults to @samp{nobody}.
-
address@hidden host
-
-System hostname as reported by @code{gethostname()}.  Defaults to
address@hidden
-
address@hidden prop-font
-
-Name of the default proportional font, prefixed by the word
address@hidden and a space.  Example: @samp{font Times-Roman}.
-
address@hidden fixed-font
-
-Name of the default fixed-pitch font, prefixed by the word @samp{font}
-and a space.
-
address@hidden scale-factor
-
-The page scaling factor as a floating-point number.  Example:
address@hidden  Note that this is also passed as an argument to the BP
-macro.
-
address@hidden paper-length
address@hidden paper-width
-
-The paper length and paper width, respectively, in thousandths of a
-point.  Note that these are also passed as arguments to the BP macro.
-
address@hidden left-margin
address@hidden top-margin
-
-The left margin and top margin, respectively, in thousandths of a
-point.  Note that these are also passed as arguments to the BP macro.
-
address@hidden title
-
-Document title as a string.  This is not the title specified in the
-PSPP syntax file.  A typical title is the word @samp{PSPP} followed
-by the syntax file name in parentheses.  Example: @samp{PSPP
-(<stdin>)}.
-
address@hidden source-file
-
-PSPP syntax file name.  Example: @samp{mary96/first.stat}.
-
+Sets the width of the lines used in tables.  Default: @code{0.5pt}.
 @end table
 
-Any other questions about the PostScript prologue can best be answered
-by examining the default prologue or the PSPP source.
-
address@hidden Encodings,  , Prologue, PostScript driver class
address@hidden PostScript encodings
-
-PostScript fonts often contain many more than 256 characters, in order
-to accommodate foreign language characters and special symbols.
-PostScript uses @dfn{encodings} to map these onto single-byte symbol
-sets.  Each font can have many different encodings applied to it.
-
-PSPP's PostScript driver needs to know which encoding to apply to each
-font.  It can determine this from the information encapsulated in the
-Groff font description that it reads.  However, there is an additional
-problem---for efficiency, the PostScript driver needs to have a complete
-list of all encodings that will be used in the entire session @emph{when
-it opens the output file}.  For this reason, it can't use the
-information built into the fonts because it doesn't know which fonts
-will be used.
-
-As a stopgap solution, there are two mechanisms for specifying which
-encodings will be used.  The first mechanism is automatic and it is the
-only one that most PSPP users will ever need.  The second mechanism is
-manual, but it is more flexible.  Either mechanism or both may be used
-at one time.
-
-The first mechanism is activated by the @samp{auto-encode} driver option
-(@pxref{PS file options}).  When enabled, @samp{auto-encode} causes the
-PostScript driver to include the encodings used by the default
-proportional and fixed-pitch fonts (@pxref{PS font options}).  Many
-PSPP output files will only need these encodings.
-
-The second mechanism is the file specified by the @samp{encoding-file}
-option (@pxref{PS file options}).  If it exists, this file must consist
-of lines in PSPP configuration-file format (@pxref{Configuration
-files}).  Each line that is not a comment should name a PostScript
-encoding to include in the output.
-
-It is not an error if an encoding is included more than once, by either
-mechanism.  It will appear only once in the output.  It is also not an
-error if an encoding is included in the output but never used.  It
address@hidden an error if an encoding is used but not included by one of
-these mechanisms.  In this case, the built-in PostScript encoding
address@hidden is substituted.
-
 @node ASCII driver class, HTML driver class, PostScript driver class, 
Configuration
 @section The ASCII driver class
 
 The ASCII driver class produces output that can be displayed on a
-terminal or output to printers.  All of its options are highly
-configurable.  The ASCII driver has class name @samp{ascii}.
-
-The ASCII driver is described in further detail below.
+terminal or output to printers.  The ASCII driver has class name
address@hidden
 
address@hidden
-* ASCII output options::        Output file options.
-* ASCII page options::          Page size, margins, more.
-* ASCII font options::          Box character, bold & italics.
address@hidden menu
-
address@hidden ASCII output options, ASCII page options, ASCII driver class, 
ASCII driver class
address@hidden ASCII output options
+The available options are listed below.
 
 @table @code
 @item address@hidden
@@ -1206,55 +671,16 @@
 (e.g., @code{"pspp.txt"}), a pipe filename (e.g., @code{"|lpr"}), or
 stdout (@code{"-"}).  Default: @code{"pspp.list"}.
 
address@hidden address@hidden
-
-One of @samp{ascii} or @samp{latin1}.  This has no effect on output at
-the present time.  Default: @code{ascii}.
-
address@hidden address@hidden
-
-The string written to the output to cause a formfeed.  See also
address@hidden, described below, for a related setting.  Default:
address@hidden"\f"}.
-
address@hidden address@hidden
-
-The string written to the output to cause a new-line (carriage return
-plus linefeed).  The default, which can be specified explicitly with
address@hidden, is to use the system-dependent new-line
-sequence by opening the output file in text mode.  This is usually the
-right choice.
-
-However, @code{newline-string} can be set to any string.  When this is
-done, the output file is opened in binary mode.
-
 @item address@hidden
 
-If set, a formfeed (as set in @code{form-feed-string}, described above)
-will be written to the device after every page.  Default: @code{on}.
+If set, a formfeed will be written at the end of every page.  Default:
address@hidden
 
 @item address@hidden
 
 The distance between tab stops for this device.  If set to 0, tabs will
 not be used in the output.  Default: @code{8}.
 
address@hidden address@hidden
-
-String written to the device before anything else, at the beginning of
-the output.  Default: @code{""} (the empty string).
-
address@hidden address@hidden
-
-String written to the device after everything else, at the end of the
-output.  Default: @code{""} (the empty string).
address@hidden table
-
address@hidden ASCII page options, ASCII font options, ASCII output options, 
ASCII driver class
address@hidden ASCII page options
-
-These options affect page setup:
-
address@hidden @code
 @item address@hidden
 
 If enabled, two lines of header information giving title and subtitle,
@@ -1272,25 +698,6 @@
 Physical width of a page, in characters.  Margins are subtracted from
 this value.  Default: @code{130}.
 
address@hidden address@hidden
-
-Number of lines per vertical inch.  Not currently used.  Default: @code{6}.
-
address@hidden address@hidden
-
-Number of characters per horizontal inch.  Not currently used.  Default:
address@hidden
-
address@hidden address@hidden
-
-Width of the left margin, in characters.  PSPP subtracts this value
-from the page width.  Default: @code{0}.
-
address@hidden address@hidden
-
-Width of the right margin, in characters.  PSPP subtracts this value
-from the page width.  Default: @code{0}.
-
 @item address@hidden
 
 Length of the top margin, in lines.  PSPP subtracts this value from
@@ -1301,14 +708,6 @@
 Length of the bottom margin, in lines.  PSPP subtracts this value from
 the page length.  Default: @code{2}.
 
address@hidden table
-
address@hidden ASCII font options,  , ASCII page options, ASCII driver class
address@hidden ASCII font options
-
-These are the ASCII font options:
-
address@hidden @code
 @item address@hidden@var{box-chars}
 
 The characters used for lines in tables produced by the ASCII driver can
@@ -1316,9 +715,8 @@
 type of line to change; @var{box-chars} is the character or string of
 characters to use for this type of line.
 
address@hidden must be a 4-digit number in base 4.  The digits are in
-the order `right', `bottom', `left', `top'.  The four possibilities for
-each digit are:
address@hidden must be a 4-digit number.  The digits are in the order
+`right', `bottom', `left', `top'.  The possibilities for each digit are:
 
 @table @asis
 @item 0
@@ -1329,10 +727,6 @@
 
 @item 2
 Double line.
-
address@hidden 3
-Special device-defined line, if one is available; otherwise, a double
-line.
 @end table
 
 Examples:
@@ -1378,112 +772,21 @@
 @address@hidden"="}
 
 @item
address@hidden"#"}
address@hidden@code{box[0002]="#"}
address@hidden@code{box[0202]="#"}
-
address@hidden
 @code{box[3000]="="}
 @address@hidden"="}
 @address@hidden"="}
 
 @item
address@hidden"#"}
address@hidden@code{box[0003]="#"}
address@hidden@code{box[0303]="#"}
-
address@hidden
 For all others, @samp{+} is used unless there are double lines or
 special lines, in which case @samp{#} is used.
 @end itemize
 
address@hidden address@hidden
-
-Character sequence written to turn on italics or underline printing.  If
-this is set to @code{overstrike}, then the driver will simulate
-underlining by overstriking with underscore characters (@samp{_}) in the
-manner described by @code{overstrike-style} and
address@hidden  Default: @code{overstrike}.
-
address@hidden address@hidden
-
-Character sequence to turn off italics or underline printing.  Default:
address@hidden""} (the empty string).
-
address@hidden address@hidden
-
-Character sequence written to turn on bold or emphasized printing.  If
-set to @code{overstrike}, then the driver will simulated bold printing
-by overstriking characters in the manner described by
address@hidden and @code{carriage-return-style}.  Default:
address@hidden
-
address@hidden address@hidden
-
-Character sequence to turn off bold or emphasized printing.  Default:
address@hidden""} (the empty string).
-
address@hidden address@hidden
address@hidden address@hidden
 
-Character sequence written to turn on bold-italic printing.  If set to
address@hidden, then the driver will simulate bold-italics by
-overstriking twice, once with the character, a second time with an
-underscore (@samp{_}) character, in the manner described by
address@hidden and @code{carriage-return-style}.  Default:
address@hidden
-
address@hidden address@hidden
-
-Character sequence to turn off bold-italic printing.  Default: @code{""}
-(the empty string).
-
address@hidden address@hidden
-
-Either @code{single} or @code{line}:
-
address@hidden @bullet
address@hidden
-If @code{single} is selected, then, to overstrike a line of text, the
-output driver will output a character, backspace, overstrike, output a
-character, backspace, overstrike, and so on along a line.
-
address@hidden
-If @code{line} is selected then the output driver will output an entire
-line, then backspace or emit a carriage return (as indicated by
address@hidden), then overstrike the entire line at once.
address@hidden itemize
-
address@hidden is recommended for use with ttys and programs that
-understand overstriking in text files, such as the pager @code{less}.
address@hidden will also work with printer devices but results in rapid
-back-and-forth motions of the printhead that can cause the printer to
-physically overheat!
-
address@hidden is recommended for use with printer devices.  Most programs
-that understand overstriking in text files will not properly deal with
address@hidden mode.  
-
-Default: @code{single}.
-
address@hidden address@hidden
-
-Either @code{bs} or @code{cr}.  This option applies only when one or
-more of the font commands is set to @code{overstrike} and, at the same
-time, @code{overstrike-style} is set to @code{line}.
-
address@hidden @bullet
address@hidden
-If @code{bs} is selected then the driver will return to the beginning of
-a line by emitting a sequence of backspace characters (ASCII 8).
-
address@hidden
-If @code{cr} is selected then the driver will return to the beginning of
-a line by emitting a single carriage-return character (ASCII 13).
address@hidden itemize
-
-Although @code{cr} is preferred as being more compact, @code{bs} is more
-general since some devices do not interpret carriage returns in the
-desired manner.  Default: @code{bs}.
+How to emphasize text.  Your choices are @code{bold}, @code{underline},
+or @code{none}.  Bold and underline emphasis are achieved with
+overstriking, which may not be supported by all the software to which
+you might pass the output.
 @end table
 
 @node HTML driver class, Miscellaneous configuring, ASCII driver class, 
Configuration
@@ -1494,7 +797,7 @@
 is very simple.  Currently, the output has a very plain format.  In the
 future, further work may be done on improving the output appearance.
 
-There are few options for use with the @code{html} driver class:
+There are is only one option:
 
 @table @code
 @item address@hidden
@@ -1502,87 +805,9 @@
 File to which output should be sent.  This can be an ordinary filename
 (i.e., @code{"pspp.ps"}), a pipe filename (i.e., @code{"|lpr"}), or
 stdout (@code{"-"}).  Default: @code{"pspp.html"}.
-
address@hidden address@hidden
-
-Sets the name of the PostScript prologue file.  You can write your own
-prologue if you want to customize colors or other settings: see
address@hidden Prologue}.  Default: @code{html-prologue}.
 @end table
 
address@hidden
-* HTML Prologue::               Format of the HTML prologue file.
address@hidden menu
-
address@hidden HTML Prologue,  , HTML driver class, HTML driver class
address@hidden The HTML prologue
-
-HTML files that are generated by PSPP consist of two parts: a prologue
-and a body.  The prologue is a collection of boilerplate.  Only the body
-differs greatly between two outputs.  You can tune the colors and other
-attributes of the output by editing the prologue.
- 
-The prologue is dumped into the output stream essentially unmodified.
-However, two actions are performed on its lines.  First, certain lines
-may be omitted as specified in the prologue file itself.  Second,
-variables are substituted.
-
-The following lines are omitted:
-
address@hidden
address@hidden
-All lines that contain three bangs in a row (@code{!!!}).
-
address@hidden
-Lines that contain @code{!title}, if no title is set for the output.  If
-a title is set, then the characters @code{!title} are removed before the
-line is output.
-
address@hidden
-Lines that contain @code{!subtitle}, if no subtitle is set for the
-output.  If a subtitle is set, then the characters @code{!subtitle} are
-removed before the line is output.
address@hidden enumerate
-
-The following are the variables that are substituted.  Only the
-variables listed are substituted; environment variables are not.
address@hidden substitutions}.
-
address@hidden @code
address@hidden generator
-
-PSPP version as a string: @samp{GNU PSPP 0.1b}, for example.
-
address@hidden date
-
-Date the file was created.  Example: @samp{Tue May 21 13:46:22 1991}.
-
address@hidden user
-
-Under multiuser OSes, the user's login name, taken either from the
-environment variable @code{LOGNAME} or, if that fails, the result of the
-C library function @code{getlogin()}.  Defaults to @samp{nobody}.
-
address@hidden host
-
-System hostname as reported by @code{gethostname()}.  Defaults to
address@hidden
-
address@hidden title
-
-Document title as a string.  This is the title specified in the PSPP
-syntax file.
-
address@hidden subtitle
-
-Document subtitle as a string.  
-
address@hidden source-file
-
-PSPP syntax file name.  Example: @samp{mary96/first.stat}.
address@hidden table
-
address@hidden Miscellaneous configuring, Improving output quality, HTML driver 
class, Configuration
address@hidden Miscellaneous configuring,, HTML driver class, Configuration
 @section Miscellaneous configuration
 
 The following environment variables can be used to further configure
@@ -1670,66 +895,3 @@
 Under MS-DOS only, these variables are consulted after TMPDIR, in this
 order.
 @end table
-
address@hidden Improving output quality,  , Miscellaneous configuring, 
Configuration
address@hidden Improving output quality
-
-When its drivers are set up properly, PSPP can produce output that
-looks very good indeed.  The PostScript driver, suitably configured, can
-produce presentation-quality output.  Here are a few guidelines for
-producing better-looking output, regardless of output driver.  Your
-mileage may vary, of course, and everyone has different esthetic
-preferences.
-
address@hidden @bullet
address@hidden
-Width is important in PSPP output.  Greater output width leads to more
-readable output, to a point.  Try the following to increase the output
-width:
-
address@hidden @minus
address@hidden
-If you're using the ASCII driver with a dot-matrix printer, figure out
-what you need to do to put the printer into compressed mode.  Put that
-string into the @code{init-string} setting.  Try to get 132 columns; 160
-might be better, but you might find that print that tiny is difficult to
-read.
-
address@hidden
-With the PostScript driver, try these ideas:
-
address@hidden +
address@hidden
-Landscape mode.
-
address@hidden
-Legal-size (8.5" x 14") paper in landscape mode.
-
address@hidden
-Reducing font sizes.  If you're using 12-point fonts, try 10 point; if
-you're using 10-point fonts, try 8 point.  Some fonts are more readable
-than others at small sizes.
address@hidden itemize
address@hidden itemize
-
-Try to strike a balance between character size and page width.
-
address@hidden
-Use high-quality fonts.  Many public domain fonts are poor in quality.
-Recently, URW made some high-quality fonts available under the GPL.
-These are probably suitable.
-
address@hidden
-Be sure you're using the proper font metrics.  The font metrics provided
-with PSPP may not correspond to the fonts actually being printed.
-This can cause bizarre-looking output.
-
address@hidden
-Make sure that you're using good ink/ribbon/toner.  Darker print is
-easier to read.
-
address@hidden
-Use plain fonts with serifs, such as Times-Roman or Palatino.  Avoid
-choosing italic or bold fonts as document base fonts.
address@hidden itemize
address@hidden ignored
Index: pspp/doc/installing.texi
diff -u pspp/doc/installing.texi:1.1 pspp/doc/installing.texi:1.2
--- pspp/doc/installing.texi:1.1        Sat Oct 30 10:14:05 2004
+++ pspp/doc/installing.texi    Mon Apr  3 20:07:54 2006
@@ -7,44 +7,9 @@
 @cindex gcc
 @cindex compiler, recommended
 @cindex compiler, gcc
-PSPP conforms to the GNU Coding Standards.  PSPP is written in, and
-requires for proper operation, ANSI/ISO C.  You might want to
-additionally note the following points:
-
address@hidden @bullet
address@hidden
-The compiler and linker must allow for significance of several
-characters in external identifiers.  The exact number is unknown but at
-least 31 is recommended.
-
address@hidden
-The @code{int} type must be 32 bits or wider.
-
address@hidden
-The recommended compiler is gcc 2.7.2.1 or later, but any ANSI compiler
-will do if it fits the above criteria.
address@hidden itemize
-
-Many UNIX variants should work out-of-the-box, as PSPP uses GNU
-autoconf to detect differences between environments.  Please report any
-problems with compilation of PSPP under UNIX and UNIX-like operating
-systems---portability is a major concern of the author.
-
-The pages below give specific instructions for installing PSPP
-on each type of system mentioned above.
-
address@hidden
-* UNIX installation::           Installing on UNIX-like environments.
address@hidden menu
-
address@hidden UNIX installation,  , Installation, Installation
address@hidden UNIX installation
address@hidden UNIX, installing PSPP under
address@hidden installation, under UNIX
address@hidden
-To install PSPP under a UNIX-like operating system, follow the steps
-below in order.  Some of the text below was taken directly from various
-Free Software Foundation sources.
+PSPP is written in ISO C and primarily targeted at UNIX-like
+environments.  To install PSPP under a UNIX-like operating system,
+follow the steps below.
 
 @enumerate
 @item
@@ -60,23 +25,7 @@
 
 You can optionally supply some options to @code{configure} to
 give it hints about how to do its job.  Type @code{./configure --help}
-to see a list of options.  One of the most useful options is
address@hidden, which enables the use of the Checker memory
-debugger under supported operating systems.  Checker must already be
-installed to use this option.  Do not use @samp{--with-checker} if you
-are not debugging PSPP itself.
-
address@hidden @file{Makefile}
address@hidden @file{config.h}
address@hidden @file{pref.h}
address@hidden makefile
address@hidden
-(optional) Edit @file{Makefile}, @file{config.h}, and @file{pref.h}.
-These files are produced by @code{configure}.  Note that most PSPP
-settings can be changed at runtime.
-
address@hidden is only generated by @code{configure} if it does not
-already exist.  (It's copied from @file{prefh.orig}.)
+to see a list of options.
 
 @cindex compiling
 @item
Index: pspp/doc/pspp.texinfo
diff -u pspp/doc/pspp.texinfo:1.5 pspp/doc/pspp.texinfo:1.6
--- pspp/doc/pspp.texinfo:1.5   Sat Apr 16 01:39:25 2005
+++ pspp/doc/pspp.texinfo       Mon Apr  3 20:07:54 2006
@@ -86,7 +86,6 @@
 * Command Index::               Index of PSPP procedures.
 * Concept Index::               Index of concepts.
 
-* Installation::                How to compile and install PSPP.
 * Configuration::               Configuring PSPP.
 
 * Portable File Format::        Format of PSPP portable files.
@@ -117,7 +116,6 @@
 @include command-index.texi
 @include concept-index.texi
 
address@hidden installing.texi
 @include configuring.texi
 
 @include portable-file-format.texi
Index: pspp/po/en_GB.po
diff -u pspp/po/en_GB.po:1.73 pspp/po/en_GB.po:1.74
--- pspp/po/en_GB.po:1.73       Tue Mar 28 06:05:05 2006
+++ pspp/po/en_GB.po    Mon Apr  3 20:07:54 2006
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: PSPP 0.3.1\n"
 "Report-Msgid-Bugs-To: address@hidden"
-"POT-Creation-Date: 2006-03-28 13:51+0800\n"
+"POT-Creation-Date: 2006-04-03 11:09-0700\n"
 "PO-Revision-Date: 2004-01-23 13:04+0800\n"
 "Last-Translator: John Darrington <address@hidden>\n"
 "Language-Team: John Darrington <address@hidden>\n"
@@ -1038,7 +1038,7 @@
 
 #: src/language/command.c:758 src/language/data-io/matrix-data.c:534
 #: src/language/data-io/print.c:336 src/language/data-io/print.c:1051
-#: src/language/dictionary/vector.c:197 src/language/lexer/lexer.c:451
+#: src/language/dictionary/vector.c:197 src/language/lexer/lexer.c:453
 #: src/language/stats/autorecode.c:144 src/language/xforms/select-if.c:57
 #: src/language/xforms/select-if.c:137
 msgid "expecting end of command"
@@ -1729,44 +1729,39 @@
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:149 src/language/stats/descriptives.c:356
+#: src/language/data-io/list.q:151 src/language/stats/descriptives.c:356
 msgid "No variables specified."
 msgstr ""
 
-#: src/language/data-io/list.q:157
+#: src/language/data-io/list.q:159
 #, c-format
 msgid ""
 "The first case (%ld) specified precedes the last case (%ld) specified.  The "
 "values will be swapped."
 msgstr ""
 
-#: src/language/data-io/list.q:165
+#: src/language/data-io/list.q:167
 #, c-format
 msgid ""
 "The first case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:171
+#: src/language/data-io/list.q:173
 #, c-format
 msgid ""
 "The last case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:177
+#: src/language/data-io/list.q:179
 #, c-format
 msgid "The step value %ld is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:203
+#: src/language/data-io/list.q:205
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/language/data-io/list.q:258 src/output/html.c:413
-#, c-format
-msgid "Cannot open first page on HTML device %s."
-msgstr ""
-
-#: src/language/data-io/list.q:438
+#: src/language/data-io/list.q:432
 msgid "Line"
 msgstr ""
 
@@ -2339,11 +2334,11 @@
 "s."
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:588
+#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:590
 msgid "expecting string"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:602
+#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:604
 msgid "expecting integer"
 msgstr ""
 
@@ -2576,98 +2571,98 @@
 msgid "Data format %s is not valid."
 msgstr ""
 
-#: src/language/lexer/lexer.c:262
+#: src/language/lexer/lexer.c:264
 #, c-format
 msgid "%s does not form a valid number."
 msgstr ""
 
-#: src/language/lexer/lexer.c:366
+#: src/language/lexer/lexer.c:368
 #, c-format
 msgid "Bad character in input: `%c'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:368
+#: src/language/lexer/lexer.c:370
 #, c-format
 msgid "Bad character in input: `\\%o'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:399
+#: src/language/lexer/lexer.c:401
 #, c-format
 msgid "Subcommand %s may only be specified once."
 msgstr ""
 
-#: src/language/lexer/lexer.c:407
+#: src/language/lexer/lexer.c:409
 #, c-format
 msgid "missing required subcommand %s"
 msgstr ""
 
-#: src/language/lexer/lexer.c:436
+#: src/language/lexer/lexer.c:438
 #, c-format
 msgid "Syntax error %s at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:439
+#: src/language/lexer/lexer.c:441
 #, c-format
 msgid "Syntax error at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:557 src/language/lexer/lexer.c:574
+#: src/language/lexer/lexer.c:559 src/language/lexer/lexer.c:576
 #, c-format
 msgid "expecting `%s'"
 msgstr ""
 
-#: src/language/lexer/lexer.c:616
+#: src/language/lexer/lexer.c:618
 msgid "expecting number"
 msgstr ""
 
-#: src/language/lexer/lexer.c:630
+#: src/language/lexer/lexer.c:632
 msgid "expecting identifier"
 msgstr ""
 
-#: src/language/lexer/lexer.c:840
+#: src/language/lexer/lexer.c:842
 msgid "<ERROR>"
 msgstr ""
 
-#: src/language/lexer/lexer.c:986
+#: src/language/lexer/lexer.c:988
 msgid "binary"
 msgstr ""
 
-#: src/language/lexer/lexer.c:991
+#: src/language/lexer/lexer.c:993
 msgid "octal"
 msgstr ""
 
-#: src/language/lexer/lexer.c:996
+#: src/language/lexer/lexer.c:998
 msgid "hex"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1006
+#: src/language/lexer/lexer.c:1008
 #, c-format
 msgid "String of %s digits has %d characters, which is not a multiple of %d."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1035
+#: src/language/lexer/lexer.c:1037
 #, c-format
 msgid "`%c' is not a valid %s digit."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1066
+#: src/language/lexer/lexer.c:1068
 msgid "Unterminated string constant."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1120
+#: src/language/lexer/lexer.c:1122
 msgid "Unexpected end of file in string concatenation."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1128
+#: src/language/lexer/lexer.c:1130
 msgid "String expected following `+'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1141
+#: src/language/lexer/lexer.c:1143
 #, c-format
 msgid "String exceeds 255 characters in length (%d characters)."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1156
+#: src/language/lexer/lexer.c:1158
 msgid ""
 "Sorry, literal strings may not contain null characters.  Replacing with "
 "spaces."
@@ -2784,8 +2779,7 @@
 msgid "Opening `%s': %s."
 msgstr ""
 
-#: src/language/line-buffer.c:459 src/output/html.c:341
-#: src/output/postscript.c:1452
+#: src/language/line-buffer.c:459
 #, c-format
 msgid "Reading `%s': %s."
 msgstr ""
@@ -2905,7 +2899,7 @@
 msgstr ""
 
 #: src/language/stats/crosstabs.q:817 src/language/stats/crosstabs.q:1020
-#: src/language/stats/crosstabs.q:1734 src/language/stats/examine.q:859
+#: src/language/stats/crosstabs.q:1740 src/language/stats/examine.q:859
 #: src/language/stats/frequencies.q:1221 src/language/stats/oneway.q:312
 #: src/language/stats/oneway.q:475 src/language/stats/regression.q:294
 msgid "Total"
@@ -2956,6 +2950,10 @@
 msgid "adj. resid."
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1107
+msgid "Chi-square tests."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1110 src/language/stats/crosstabs.q:1137
 #: src/language/stats/crosstabs.q:1157 src/language/stats/crosstabs.q:1178
 #: src/language/stats/examine.q:1369
@@ -2981,6 +2979,10 @@
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1133
+msgid "Symmetric measures."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1136 src/language/stats/crosstabs.q:1177
 msgid "Category"
 msgstr ""
@@ -2997,6 +2999,10 @@
 msgid "Approx. Sig."
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1152
+msgid "Risk estimate."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1156
 #, c-format
 msgid "95%% Confidence Interval"
@@ -3012,136 +3018,140 @@
 msgid "Upper"
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1174
+msgid "Directional measures."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1179 src/ui/gui/var-sheet.c:68
 msgid "Type"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1929
+#: src/language/stats/crosstabs.q:1935
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1930
+#: src/language/stats/crosstabs.q:1936
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1931
+#: src/language/stats/crosstabs.q:1937
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1932
+#: src/language/stats/crosstabs.q:1938
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1933
+#: src/language/stats/crosstabs.q:1939
 msgid "Linear-by-Linear Association"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1970 src/language/stats/crosstabs.q:2040
-#: src/language/stats/crosstabs.q:2099
+#: src/language/stats/crosstabs.q:1976 src/language/stats/crosstabs.q:2046
+#: src/language/stats/crosstabs.q:2105
 msgid "N of Valid Cases"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1986 src/language/stats/crosstabs.q:2115
+#: src/language/stats/crosstabs.q:1992 src/language/stats/crosstabs.q:2121
 msgid "Nominal by Nominal"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1987 src/language/stats/crosstabs.q:2116
+#: src/language/stats/crosstabs.q:1993 src/language/stats/crosstabs.q:2122
 msgid "Ordinal by Ordinal"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1988
+#: src/language/stats/crosstabs.q:1994
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1989
+#: src/language/stats/crosstabs.q:1995
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1994
+#: src/language/stats/crosstabs.q:2000
 msgid "Phi"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1995
+#: src/language/stats/crosstabs.q:2001
 msgid "Cramer's V"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1996
+#: src/language/stats/crosstabs.q:2002
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1997
+#: src/language/stats/crosstabs.q:2003
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1998
+#: src/language/stats/crosstabs.q:2004
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1999
+#: src/language/stats/crosstabs.q:2005
 msgid "Gamma"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2000
+#: src/language/stats/crosstabs.q:2006
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2001
+#: src/language/stats/crosstabs.q:2007
 msgid "Pearson's R"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2002
+#: src/language/stats/crosstabs.q:2008
 msgid "Kappa"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2072
+#: src/language/stats/crosstabs.q:2078
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2075
+#: src/language/stats/crosstabs.q:2081
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2083
+#: src/language/stats/crosstabs.q:2089
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2086
+#: src/language/stats/crosstabs.q:2092
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2117
+#: src/language/stats/crosstabs.q:2123
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2122
+#: src/language/stats/crosstabs.q:2128
 msgid "Lambda"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2123
+#: src/language/stats/crosstabs.q:2129
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2124
+#: src/language/stats/crosstabs.q:2130
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2125
+#: src/language/stats/crosstabs.q:2131
 msgid "Somers' d"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2126
+#: src/language/stats/crosstabs.q:2132
 msgid "Eta"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2131
+#: src/language/stats/crosstabs.q:2137
 msgid "Symmetric"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2132 src/language/stats/crosstabs.q:2133
+#: src/language/stats/crosstabs.q:2138 src/language/stats/crosstabs.q:2139
 #, c-format
 msgid "%s Dependent"
 msgstr ""
@@ -3725,7 +3735,7 @@
 msgid "SE. Mean"
 msgstr ""
 
-#: src/language/stats/t-test.q:702
+#: src/language/stats/t-test.q:703
 msgid "Group Statistics"
 msgstr ""
 
@@ -4132,630 +4142,376 @@
 msgid "installation error"
 msgstr ""
 
-#: src/output/ascii.c:235
+#: src/output/afm.c:150
 #, c-format
-msgid "ASCII driver initializing as `%s'..."
+msgid "opening font metrics file \"%s\""
 msgstr ""
 
-#: src/output/ascii.c:290
-#, c-format
-msgid ""
-"ascii driver: Area of page excluding margins and headers must be at least 59 "
-"characters wide by 15 lines long.  Page as configured is only %d characters "
-"by %d lines."
+#: src/output/afm.c:240
+msgid "first line must be StartFontMetrics"
 msgstr ""
 
-#: src/output/ascii.c:395 src/output/html.c:103 src/output/postscript.c:454
+#: src/output/afm.c:267
 #, c-format
-msgid "%s: Initialization complete."
+msgid "unsupported MappingScheme %d"
 msgstr ""
 
-#: src/output/ascii.c:407 src/output/html.c:115 src/output/postscript.c:467
-#, c-format
-msgid "%s: Beginning closing..."
+#: src/output/afm.c:288
+msgid "required FontName is missing"
 msgstr ""
 
-#: src/output/ascii.c:429 src/output/html.c:120 src/output/postscript.c:486
-#, c-format
-msgid "%s: Finished closing."
+#: src/output/afm.c:395
+msgid "CharMetrics line must start with C or CH"
 msgstr ""
 
-#: src/output/ascii.c:490
+#: src/output/afm.c:536
 #, c-format
-msgid ""
-"Bad index value for `box' key: syntax is box[INDEX], 0 <= INDEX < %d "
-"decimal, with INDEX expressed in base 4."
+msgid "reference to unknown character \"%s\""
 msgstr ""
 
-#: src/output/ascii.c:496
-#, c-format
-msgid "Duplicate value for key `%s'."
+#: src/output/afm.c:594
+msgid "expected end of file"
 msgstr ""
 
-#: src/output/ascii.c:505
-#, c-format
-msgid "Unknown configuration parameter `%s' for ascii device driver."
+#: src/output/afm.c:606
+msgid "syntax error expecting end of line"
 msgstr ""
 
-#: src/output/ascii.c:518
-#, c-format
-msgid ""
-"Unknown character set `%s'.  Valid character sets are `ascii' and `latin1'."
+#: src/output/afm.c:624 src/output/afm.c:661
+msgid "number out of valid range"
 msgstr ""
 
-#: src/output/ascii.c:527
-#, c-format
-msgid ""
-"Unknown overstrike style `%s'.  Valid overstrike styles are `single' and "
-"`line'."
+#: src/output/afm.c:626 src/output/afm.c:663
+msgid "invalid numeric syntax"
 msgstr ""
 
-#: src/output/ascii.c:536
-#, c-format
-msgid ""
-"Unknown carriage return style `%s'.  Valid carriage return styles are `cr' "
-"and `bs'."
+#: src/output/afm.c:642
+msgid "syntax error expecting integer"
 msgstr ""
 
-#: src/output/ascii.c:548 src/output/postscript.c:677
-#, c-format
-msgid "Positive integer required as value for `%s'."
+#: src/output/afm.c:680
+msgid "syntax error expecting number"
 msgstr ""
 
-#: src/output/ascii.c:579
-#, c-format
-msgid "Zero or positive integer required as value for `%s'."
+#: src/output/afm.c:693
+msgid "syntax error in hex constant"
 msgstr ""
 
-#: src/output/ascii.c:650 src/output/postscript.c:635
-#, c-format
-msgid "Boolean value expected for %s."
+#: src/output/afm.c:708
+msgid "syntax error expecting hex constant"
 msgstr ""
 
-#: src/output/ascii.c:682 src/output/ascii.c:697 src/output/ascii.c:714
-#, c-format
-msgid "ASCII output driver: %s: %s"
+#: src/output/afm.c:746
+msgid "unexpected end of line"
 msgstr ""
 
-#: src/output/ascii.c:790
-#, c-format
-msgid "ascii_line_horz: bad hline (%d,%d),%d out of (%d,%d)\n"
+#: src/output/afm.c:797
+msgid "unexpected end of line expecting string"
 msgstr ""
 
-#: src/output/ascii.c:824
+#: src/output/ascii.c:161
 #, c-format
-msgid "ascii_line_vert: bad vline %d,(%d,%d) out of (%d,%d)\n"
+msgid "ascii: opening output file \"%s\""
 msgstr ""
 
-#: src/output/ascii.c:854
+#: src/output/ascii.c:172
 #, c-format
-msgid "ascii_line_intersection: bad intsct (%d,%d) out of (%d,%d)\n"
-msgstr ""
-
-#: src/output/ascii.c:1002
-#, c-format
-msgid "%s: horiz=%d, vert=%d\n"
-msgstr ""
-
-#: src/output/ascii.c:1174
-#, c-format
-msgid "Writing `%s': %s"
-msgstr ""
-
-#: src/output/ascii.c:1584 src/output/postscript.c:2099
-#, c-format
-msgid "%s - Page %d"
-msgstr ""
-
-#: src/output/ascii.c:1641
-msgid "Charts are unsupported with ascii drivers."
-msgstr ""
-
-#: src/output/charts/plot-hist.c:127
-msgid "HISTOGRAM"
-msgstr ""
-
-#: src/output/groff-font.c:107
-#, c-format
-msgid "%s: Opening Groff font file..."
-msgstr ""
-
-#: src/output/groff-font.c:161
-msgid "Missing font name."
-msgstr ""
-
-#: src/output/groff-font.c:171
-msgid "Missing encoding filename."
-msgstr ""
-
-#: src/output/groff-font.c:184
-msgid "Bad spacewidth value."
-msgstr ""
-
-#: src/output/groff-font.c:196
-msgid "Bad slant value."
-msgstr ""
-
-#: src/output/groff-font.c:221
-#, c-format
-msgid "Unknown ligature `%s'."
-msgstr ""
-
-#: src/output/groff-font.c:256
-msgid "Unexpected end of line reading character set."
-msgstr ""
-
-#: src/output/groff-font.c:264
-msgid "Can't use ditto mark for first character."
-msgstr ""
-
-#: src/output/groff-font.c:269
-msgid "Can't ditto into an unnamed character."
-msgstr ""
-
-#: src/output/groff-font.c:286
-#, c-format
-msgid "Missing metrics for character `%s'."
+msgid ""
+"ascii: page excluding margins and headers must be at least 59 characters "
+"wide by 15 lines long, but as configured is only %d characters by %d lines"
 msgstr ""
 
-#: src/output/groff-font.c:295
+#: src/output/ascii.c:233
 #, c-format
-msgid "Missing type for character `%s'."
+msgid "ascii: closing output file \"%s\""
 msgstr ""
 
-#: src/output/groff-font.c:304
+#: src/output/ascii.c:285
 #, c-format
-msgid "Missing code for character `%s'."
-msgstr ""
-
-#: src/output/groff-font.c:323
-msgid "Malformed kernpair."
-msgstr ""
-
-#: src/output/groff-font.c:330
-msgid "Unexpected end of line reading kernpairs."
-msgstr ""
-
-#: src/output/groff-font.c:336
-msgid "Bad kern value."
-msgstr ""
-
-#: src/output/groff-font.c:368
-#, c-format
-msgid "Font read successfully with internal name %s."
-msgstr ""
-
-#: src/output/groff-font.c:389
-msgid "Error reading font."
-msgstr ""
-
-#: src/output/groff-font.c:402
-msgid "installation error: Groff font error: "
+msgid ""
+"ascii: bad index value for `box' key: syntax is box[INDEX], 0 <= INDEX < %d "
+"decimal, with INDEX expressed in base 4"
 msgstr ""
 
-#: src/output/groff-font.c:432
+#: src/output/ascii.c:292
 #, c-format
-msgid "Bad character \\%3o."
+msgid "ascii: multiple values for %s"
 msgstr ""
 
-#: src/output/groff-font.c:687
+#: src/output/ascii.c:300
 #, c-format
-msgid "Groff font error: Cannot find \"%s\"."
+msgid "ascii: unknown parameter `%s'"
 msgstr ""
 
-#: src/output/groff-font.c:752
+#: src/output/ascii.c:314
 #, c-format
-msgid "%s: Opening Groff description file..."
-msgstr ""
-
-#: src/output/groff-font.c:768
-msgid "Multiple `sizes' declarations."
-msgstr ""
-
-#: src/output/groff-font.c:785
-msgid "Unexpected end of file.  Missing 0 terminator to `sizes' command?"
+msgid "ascii: positive integer required as `%s' value"
 msgstr ""
 
-#: src/output/groff-font.c:797 src/output/groff-font.c:804
-#: src/output/groff-font.c:817
-msgid "Bad argument to `sizes'."
+#: src/output/ascii.c:340
+msgid "ascii: `emphasis' value must be `bold', `underline', or `none'"
 msgstr ""
 
-#: src/output/groff-font.c:809
-msgid "Bad range in argument to `sizes'."
-msgstr ""
-
-#: src/output/groff-font.c:838
-msgid "Family name expected."
-msgstr ""
-
-#: src/output/groff-font.c:843
-msgid "This command already specified."
-msgstr ""
-
-#: src/output/groff-font.c:863
+#: src/output/ascii.c:353
 #, c-format
-msgid "%s: Device characteristic already defined."
+msgid "ascii: zero or positive integer required as `%s' value"
 msgstr ""
 
-#: src/output/groff-font.c:869
+#: src/output/ascii.c:384
 #, c-format
-msgid "%s: Invalid numeric format."
+msgid "ascii: boolean value expected for `%s'"
 msgstr ""
 
-#: src/output/groff-font.c:899
-msgid "Missing `res', `unitwidth', and/or `sizes' line(s)."
-msgstr ""
-
-#: src/output/groff-font.c:925
-msgid "Description file read successfully."
-msgstr ""
-
-#: src/output/groff-font.c:957
-msgid "Error reading description file."
-msgstr ""
-
-#: src/output/groff-font.c:1014
-msgid "<<fallback>>"
-msgstr ""
-
-#: src/output/html.c:67
+#: src/output/ascii.c:473
 #, c-format
-msgid "HTML driver initializing as `%s'..."
+msgid "ascii: bad line (%d,%d)-(%d,%d) out of (%d,%d)\n"
 msgstr ""
 
-#: src/output/html.c:171
+#: src/output/ascii.c:683 src/output/postscript.c:832
 #, c-format
-msgid "Unknown configuration parameter `%s' for HTML device driver."
-msgstr ""
-
-#: src/output/html.c:251
-msgid ""
-"Cannot find HTML prologue.  The use of `-vv' on the command line is "
-"suggested as a debugging aid."
-msgstr ""
-
-#: src/output/html.c:256
-#, c-format
-msgid "%s: %s: Opening HTML prologue..."
+msgid "%s - Page %d"
 msgstr ""
 
-#: src/output/html.c:280 src/output/postscript.c:1350
-#: src/output/postscript.c:1361
-msgid "nobody"
+#: src/output/ascii.c:727
+msgid "ascii: charts are unsupported by this driver"
 msgstr ""
 
-#: src/output/html.c:289 src/output/html.c:292 src/output/postscript.c:1357
-#: src/output/postscript.c:1362
-msgid "nowhere"
+#: src/output/charts/plot-hist.c:127
+msgid "HISTOGRAM"
 msgstr ""
 
-#: src/output/html.c:350
+#: src/output/html.c:68
 #, c-format
-msgid "%s: HTML prologue read successfully."
+msgid "opening HTML output file: %s"
 msgstr ""
 
-#: src/output/html.c:354
-#, c-format
-msgid "%s: Error reading HTML prologue."
+#: src/output/html.c:79
+msgid "PSPP Output"
 msgstr ""
 
-#: src/output/html.c:382
+#: src/output/html.c:165
 #, c-format
-msgid "HTML output driver: %s: %s"
+msgid "unknown configuration parameter `%s' for HTML device driver"
 msgstr ""
 
-#: src/output/output.c:160
+#: src/output/output.c:159
 #, c-format
 msgid "Unknown output driver `%s'."
 msgstr ""
 
-#: src/output/output.c:162
+#: src/output/output.c:161
 #, c-format
 msgid "Output driver `%s' referenced but never defined."
 msgstr ""
 
-#: src/output/output.c:255
+#: src/output/output.c:254
 msgid "Using default output driver configuration."
 msgstr ""
 
-#: src/output/output.c:288
+#: src/output/output.c:289
 msgid ""
 "Cannot find output initialization file.  Use `-vvvvv' to view search path."
 msgstr ""
 
-#: src/output/output.c:293
-#, c-format
-msgid "%s: Opening device description file..."
-msgstr ""
-
-#: src/output/output.c:297 src/output/output.c:1205
-#: src/output/postscript.c:1095
+#: src/output/output.c:297 src/output/output.c:1060
 #, c-format
 msgid "Opening %s: %s."
 msgstr ""
 
-#: src/output/output.c:308 src/output/output.c:1216
-#: src/output/postscript.c:1110
+#: src/output/output.c:308 src/output/output.c:1071
 #, c-format
 msgid "Reading %s: %s."
 msgstr ""
 
-#: src/output/output.c:330 src/output/output.c:497
+#: src/output/output.c:330 src/output/output.c:496
 msgid "Syntax error."
 msgstr ""
 
-#: src/output/output.c:340 src/output/postscript.c:1121
+#: src/output/output.c:340
 #, c-format
 msgid "Closing %s: %s."
 msgstr ""
 
-#: src/output/output.c:347
-msgid "Device definition file read successfully."
-msgstr ""
-
-#: src/output/output.c:349
+#: src/output/output.c:348
 msgid "No output drivers are active."
 msgstr ""
 
-#: src/output/output.c:352
+#: src/output/output.c:351
 msgid "Error reading device definition file."
 msgstr ""
 
-#: src/output/output.c:469
+#: src/output/output.c:468
 #, c-format
 msgid ""
 "Driver classes:\n"
 "\t"
 msgstr ""
 
-#: src/output/output.c:598
+#: src/output/output.c:597
 msgid "Syntax error in string constant."
 msgstr ""
 
-#: src/output/output.c:630
+#: src/output/output.c:632
 msgid "Syntax error in options."
 msgstr ""
 
-#: src/output/output.c:640
+#: src/output/output.c:642
 msgid "Syntax error in options (`=' expected)."
 msgstr ""
 
-#: src/output/output.c:647
+#: src/output/output.c:649
 msgid "Syntax error in options (value expected after `=')."
 msgstr ""
 
-#: src/output/output.c:744
+#: src/output/output.c:691
 #, c-format
 msgid "Unknown output driver class `%s'."
 msgstr ""
 
-#: src/output/output.c:751
-#, c-format
-msgid "Can't initialize output driver class `%s'."
-msgstr ""
-
-#: src/output/output.c:758
-#, c-format
-msgid "Can't initialize output driver `%s' of class `%s'."
-msgstr ""
-
-#: src/output/output.c:780
+#: src/output/output.c:712
 #, c-format
 msgid "Unknown device type `%s'."
 msgstr ""
 
-#: src/output/output.c:793
-#, c-format
-msgid "Can't complete initialization of output driver `%s' of class `%s'."
-msgstr ""
-
-#: src/output/output.c:839
-msgid "Driver definition line contains fewer fields than expected"
-msgstr ""
-
-#: src/output/output.c:876
+#: src/output/output.c:731
 #, c-format
-msgid "Can't deinitialize output driver class `%s'."
+msgid "Can't initialize output driver `%s' of class `%s'."
 msgstr ""
 
-#: src/output/output.c:949
-#, c-format
-msgid "Trying to find keyword `%s'...\n"
+#: src/output/output.c:776
+msgid "Driver definition line missing driver name or class name"
 msgstr ""
 
-#: src/output/output.c:1066
+#: src/output/output.c:922
 #, c-format
 msgid "Unit \"%s\" is unknown in dimension \"%s\"."
 msgstr ""
 
-#: src/output/output.c:1081
+#: src/output/output.c:937
 #, c-format
 msgid "Bad dimension \"%s\"."
 msgstr ""
 
-#: src/output/output.c:1107
+#: src/output/output.c:963
 #, c-format
 msgid "`x' expected in paper size `%s'."
 msgstr ""
 
-#: src/output/output.c:1117
+#: src/output/output.c:973
 #, c-format
 msgid "Trailing garbage `%s' on paper size `%s'."
 msgstr ""
 
-#: src/output/output.c:1166
+#: src/output/output.c:1022
 msgid "Paper size name must not be empty."
 msgstr ""
 
-#: src/output/output.c:1197
+#: src/output/output.c:1053
 msgid "Cannot find `papersize' configuration file."
 msgstr ""
 
-#: src/output/output.c:1201
-#, c-format
-msgid "%s: Opening paper size definition file..."
-msgstr ""
-
-#: src/output/output.c:1243
+#: src/output/output.c:1098
 msgid "Syntax error in paper size definition."
 msgstr ""
 
-#: src/output/output.c:1272
-msgid "Paper size definition file read successfully."
-msgstr ""
-
-#: src/output/output.c:1274
+#: src/output/output.c:1127
 msgid "Error reading paper size definition file."
 msgstr ""
 
-#: src/output/output.c:1330
-#, c-format
-msgid "Error closing page on %s device of %s class."
-msgstr ""
-
-#: src/output/output.c:1334
+#: src/output/postscript.c:164
 #, c-format
-msgid "Error opening page on %s device of %s class."
+msgid "opening PostScript output file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:319
-#, c-format
-msgid "PostScript driver initializing as `%s'..."
-msgstr ""
-
-#: src/output/postscript.c:443
+#: src/output/postscript.c:202
 #, c-format
 msgid ""
-"PostScript driver: The defined 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."
+"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."
 msgstr ""
 
-#: src/output/postscript.c:573
+#: src/output/postscript.c:250
 #, c-format
-msgid "Unknown configuration parameter `%s' for PostScript device driver."
+msgid "closing PostScript output file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:589
+#: src/output/postscript.c:310
 #, c-format
-msgid ""
-"Unknown orientation `%s'.  Valid orientations are `portrait' and `landscape'."
+msgid "unknown configuration parameter `%s' for PostScript device driver"
 msgstr ""
 
-#: src/output/postscript.c:601
-msgid ""
-"Unknown value for `data'.  Valid values are `clean7bit', `clean8bit', and "
-"`binary'."
-msgstr ""
-
-#: src/output/postscript.c:610
-msgid "Unknown value for `line-ends'.  Valid values are `lf' and `crlf'."
-msgstr ""
-
-#: src/output/postscript.c:619
-msgid "Unknown value for `line-style'.  Valid values are `thick' and `double'."
-msgstr ""
-
-#: src/output/postscript.c:682
+#: src/output/postscript.c:326
 #, c-format
 msgid ""
-"Default font size must be at least 1 point (value of 1000 for key `%s')."
-msgstr ""
-
-#: src/output/postscript.c:714
-#, c-format
-msgid "Value for `%s' must be a dimension of positive length (i.e., `1in')."
-msgstr ""
-
-#: src/output/postscript.c:778
-#, c-format
-msgid "Nonnegative integer required as value for `%s'."
+"unknown orientation `%s' (valid orientations are `portrait' and `landscape')"
 msgstr ""
 
-#: src/output/postscript.c:904
+#: src/output/postscript.c:338
 #, c-format
-msgid "%s: %s: Opening PostScript font encoding..."
+msgid "boolean value expected for %s"
 msgstr ""
 
-#: src/output/postscript.c:910
+#: src/output/postscript.c:351
 #, c-format
-msgid ""
-"PostScript driver: Cannot open encoding file `%s': %s.  Substituting "
-"ISOLatin1Encoding for missing encoding."
-msgstr ""
-
-#: src/output/postscript.c:952
-msgid "PostScript driver: Invalid numeric format."
+msgid "positive integer value required for `%s'"
 msgstr ""
 
-#: src/output/postscript.c:957
+#: src/output/postscript.c:356
 #, c-format
-msgid ""
-"PostScript driver: Codes must be between 0 and 255.  (%d is not allowed.)"
+msgid "default font size must be at least 1 point (value of 1000 for key `%s')"
 msgstr ""
 
-#: src/output/postscript.c:993
+#: src/output/postscript.c:388
 #, c-format
-msgid "PostScript driver: Error closing encoding file `%s'."
+msgid "value for `%s' must be a dimension of positive length (i.e., `1in')"
 msgstr ""
 
-#: src/output/postscript.c:996
+#: src/output/postscript.c:1182
 #, c-format
-msgid "%s: PostScript font encoding read successfully."
+msgid "\"%s\": bad font specification"
 msgstr ""
 
-#: src/output/postscript.c:1090
+#: src/output/postscript.c:1190
 #, c-format
-msgid "%s: %s: Opening PostScript encoding list file."
+msgid "could not find AFM file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1123
+#: src/output/postscript.c:1204
 #, c-format
-msgid "%s: PostScript encoding list file read successfully."
+msgid "could not find font \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1137
-msgid "<<default encoding>>"
-msgstr ""
-
-#: src/output/postscript.c:1294
-msgid ""
-"Cannot find PostScript prologue.  The use of `-vv' on the command line is "
-"suggested as a debugging aid."
-msgstr ""
-
-#: src/output/postscript.c:1299
+#: src/output/postscript.c:1213
 #, c-format
-msgid "%s: %s: Opening PostScript prologue..."
+msgid "could not find encoding \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1465
+#: src/output/postscript.c:1313
 #, c-format
-msgid "%s: PostScript prologue read successfully."
+msgid "cannot open font file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1469
+#: src/output/postscript.c:1354
 #, c-format
-msgid "%s: Error reading PostScript prologue."
+msgid "reading font file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1639
+#: src/output/postscript.c:1376
 #, c-format
-msgid "PostScript output driver: %s: %s"
+msgid "cannot open font encoding file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:2338
-#, c-format
-msgid "PostScript driver: Cannot find encoding `%s' for PostScript font `%s'."
+#: src/output/postscript.c:1405
+msgid "invalid numeric format"
 msgstr ""
 
-#: src/output/table.c:259
+#: src/output/table.c:239
 #, c-format
 msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
-#: src/output/table.c:334
+#: src/output/table.c:310
 #, c-format
 msgid ""
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
@@ -4918,8 +4674,6 @@
 "  -B, --config-dir=DIR      set configuration directory to DIR\n"
 "  -o, --device=DEVICE       select output driver DEVICE and disable "
 "defaults\n"
-"  -d, --define=VAR[=VALUE]  set environment variable VAR to VALUE, or empty\n"
-"  -u, --undef=VAR           undefine environment variable VAR\n"
 "\n"
 "Input and output:\n"
 "  -f, --out-file=FILE       send output to FILE (overwritten)\n"
@@ -4948,7 +4702,7 @@
 "\n"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:249
+#: src/ui/terminal/command-line.c:247
 #, c-format
 msgid ""
 "\n"
Index: pspp/po/pspp.pot
diff -u pspp/po/pspp.pot:1.76 pspp/po/pspp.pot:1.77
--- pspp/po/pspp.pot:1.76       Tue Mar 28 06:05:06 2006
+++ pspp/po/pspp.pot    Mon Apr  3 20:07:54 2006
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: address@hidden"
-"POT-Creation-Date: 2006-03-28 13:51+0800\n"
+"POT-Creation-Date: 2006-04-03 11:09-0700\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <address@hidden>\n"
 "Language-Team: LANGUAGE <address@hidden>\n"
@@ -1039,7 +1039,7 @@
 
 #: src/language/command.c:758 src/language/data-io/matrix-data.c:534
 #: src/language/data-io/print.c:336 src/language/data-io/print.c:1051
-#: src/language/dictionary/vector.c:197 src/language/lexer/lexer.c:451
+#: src/language/dictionary/vector.c:197 src/language/lexer/lexer.c:453
 #: src/language/stats/autorecode.c:144 src/language/xforms/select-if.c:57
 #: src/language/xforms/select-if.c:137
 msgid "expecting end of command"
@@ -1730,44 +1730,39 @@
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:149 src/language/stats/descriptives.c:356
+#: src/language/data-io/list.q:151 src/language/stats/descriptives.c:356
 msgid "No variables specified."
 msgstr ""
 
-#: src/language/data-io/list.q:157
+#: src/language/data-io/list.q:159
 #, c-format
 msgid ""
 "The first case (%ld) specified precedes the last case (%ld) specified.  The "
 "values will be swapped."
 msgstr ""
 
-#: src/language/data-io/list.q:165
+#: src/language/data-io/list.q:167
 #, c-format
 msgid ""
 "The first case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:171
+#: src/language/data-io/list.q:173
 #, c-format
 msgid ""
 "The last case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:177
+#: src/language/data-io/list.q:179
 #, c-format
 msgid "The step value %ld is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/language/data-io/list.q:203
+#: src/language/data-io/list.q:205
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/language/data-io/list.q:258 src/output/html.c:413
-#, c-format
-msgid "Cannot open first page on HTML device %s."
-msgstr ""
-
-#: src/language/data-io/list.q:438
+#: src/language/data-io/list.q:432
 msgid "Line"
 msgstr ""
 
@@ -2340,11 +2335,11 @@
 "s."
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:588
+#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:590
 msgid "expecting string"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:602
+#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:604
 msgid "expecting integer"
 msgstr ""
 
@@ -2577,98 +2572,98 @@
 msgid "Data format %s is not valid."
 msgstr ""
 
-#: src/language/lexer/lexer.c:262
+#: src/language/lexer/lexer.c:264
 #, c-format
 msgid "%s does not form a valid number."
 msgstr ""
 
-#: src/language/lexer/lexer.c:366
+#: src/language/lexer/lexer.c:368
 #, c-format
 msgid "Bad character in input: `%c'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:368
+#: src/language/lexer/lexer.c:370
 #, c-format
 msgid "Bad character in input: `\\%o'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:399
+#: src/language/lexer/lexer.c:401
 #, c-format
 msgid "Subcommand %s may only be specified once."
 msgstr ""
 
-#: src/language/lexer/lexer.c:407
+#: src/language/lexer/lexer.c:409
 #, c-format
 msgid "missing required subcommand %s"
 msgstr ""
 
-#: src/language/lexer/lexer.c:436
+#: src/language/lexer/lexer.c:438
 #, c-format
 msgid "Syntax error %s at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:439
+#: src/language/lexer/lexer.c:441
 #, c-format
 msgid "Syntax error at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:557 src/language/lexer/lexer.c:574
+#: src/language/lexer/lexer.c:559 src/language/lexer/lexer.c:576
 #, c-format
 msgid "expecting `%s'"
 msgstr ""
 
-#: src/language/lexer/lexer.c:616
+#: src/language/lexer/lexer.c:618
 msgid "expecting number"
 msgstr ""
 
-#: src/language/lexer/lexer.c:630
+#: src/language/lexer/lexer.c:632
 msgid "expecting identifier"
 msgstr ""
 
-#: src/language/lexer/lexer.c:840
+#: src/language/lexer/lexer.c:842
 msgid "<ERROR>"
 msgstr ""
 
-#: src/language/lexer/lexer.c:986
+#: src/language/lexer/lexer.c:988
 msgid "binary"
 msgstr ""
 
-#: src/language/lexer/lexer.c:991
+#: src/language/lexer/lexer.c:993
 msgid "octal"
 msgstr ""
 
-#: src/language/lexer/lexer.c:996
+#: src/language/lexer/lexer.c:998
 msgid "hex"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1006
+#: src/language/lexer/lexer.c:1008
 #, c-format
 msgid "String of %s digits has %d characters, which is not a multiple of %d."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1035
+#: src/language/lexer/lexer.c:1037
 #, c-format
 msgid "`%c' is not a valid %s digit."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1066
+#: src/language/lexer/lexer.c:1068
 msgid "Unterminated string constant."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1120
+#: src/language/lexer/lexer.c:1122
 msgid "Unexpected end of file in string concatenation."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1128
+#: src/language/lexer/lexer.c:1130
 msgid "String expected following `+'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1141
+#: src/language/lexer/lexer.c:1143
 #, c-format
 msgid "String exceeds 255 characters in length (%d characters)."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1156
+#: src/language/lexer/lexer.c:1158
 msgid ""
 "Sorry, literal strings may not contain null characters.  Replacing with "
 "spaces."
@@ -2785,8 +2780,7 @@
 msgid "Opening `%s': %s."
 msgstr ""
 
-#: src/language/line-buffer.c:459 src/output/html.c:341
-#: src/output/postscript.c:1452
+#: src/language/line-buffer.c:459
 #, c-format
 msgid "Reading `%s': %s."
 msgstr ""
@@ -2906,7 +2900,7 @@
 msgstr ""
 
 #: src/language/stats/crosstabs.q:817 src/language/stats/crosstabs.q:1020
-#: src/language/stats/crosstabs.q:1734 src/language/stats/examine.q:859
+#: src/language/stats/crosstabs.q:1740 src/language/stats/examine.q:859
 #: src/language/stats/frequencies.q:1221 src/language/stats/oneway.q:312
 #: src/language/stats/oneway.q:475 src/language/stats/regression.q:294
 msgid "Total"
@@ -2957,6 +2951,10 @@
 msgid "adj. resid."
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1107
+msgid "Chi-square tests."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1110 src/language/stats/crosstabs.q:1137
 #: src/language/stats/crosstabs.q:1157 src/language/stats/crosstabs.q:1178
 #: src/language/stats/examine.q:1369
@@ -2982,6 +2980,10 @@
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1133
+msgid "Symmetric measures."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1136 src/language/stats/crosstabs.q:1177
 msgid "Category"
 msgstr ""
@@ -2998,6 +3000,10 @@
 msgid "Approx. Sig."
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1152
+msgid "Risk estimate."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1156
 #, c-format
 msgid "95%% Confidence Interval"
@@ -3013,136 +3019,140 @@
 msgid "Upper"
 msgstr ""
 
+#: src/language/stats/crosstabs.q:1174
+msgid "Directional measures."
+msgstr ""
+
 #: src/language/stats/crosstabs.q:1179 src/ui/gui/var-sheet.c:68
 msgid "Type"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1929
+#: src/language/stats/crosstabs.q:1935
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1930
+#: src/language/stats/crosstabs.q:1936
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1931
+#: src/language/stats/crosstabs.q:1937
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1932
+#: src/language/stats/crosstabs.q:1938
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1933
+#: src/language/stats/crosstabs.q:1939
 msgid "Linear-by-Linear Association"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1970 src/language/stats/crosstabs.q:2040
-#: src/language/stats/crosstabs.q:2099
+#: src/language/stats/crosstabs.q:1976 src/language/stats/crosstabs.q:2046
+#: src/language/stats/crosstabs.q:2105
 msgid "N of Valid Cases"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1986 src/language/stats/crosstabs.q:2115
+#: src/language/stats/crosstabs.q:1992 src/language/stats/crosstabs.q:2121
 msgid "Nominal by Nominal"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1987 src/language/stats/crosstabs.q:2116
+#: src/language/stats/crosstabs.q:1993 src/language/stats/crosstabs.q:2122
 msgid "Ordinal by Ordinal"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1988
+#: src/language/stats/crosstabs.q:1994
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1989
+#: src/language/stats/crosstabs.q:1995
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1994
+#: src/language/stats/crosstabs.q:2000
 msgid "Phi"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1995
+#: src/language/stats/crosstabs.q:2001
 msgid "Cramer's V"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1996
+#: src/language/stats/crosstabs.q:2002
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1997
+#: src/language/stats/crosstabs.q:2003
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1998
+#: src/language/stats/crosstabs.q:2004
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1999
+#: src/language/stats/crosstabs.q:2005
 msgid "Gamma"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2000
+#: src/language/stats/crosstabs.q:2006
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2001
+#: src/language/stats/crosstabs.q:2007
 msgid "Pearson's R"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2002
+#: src/language/stats/crosstabs.q:2008
 msgid "Kappa"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2072
+#: src/language/stats/crosstabs.q:2078
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2075
+#: src/language/stats/crosstabs.q:2081
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2083
+#: src/language/stats/crosstabs.q:2089
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2086
+#: src/language/stats/crosstabs.q:2092
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2117
+#: src/language/stats/crosstabs.q:2123
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2122
+#: src/language/stats/crosstabs.q:2128
 msgid "Lambda"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2123
+#: src/language/stats/crosstabs.q:2129
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2124
+#: src/language/stats/crosstabs.q:2130
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2125
+#: src/language/stats/crosstabs.q:2131
 msgid "Somers' d"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2126
+#: src/language/stats/crosstabs.q:2132
 msgid "Eta"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2131
+#: src/language/stats/crosstabs.q:2137
 msgid "Symmetric"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2132 src/language/stats/crosstabs.q:2133
+#: src/language/stats/crosstabs.q:2138 src/language/stats/crosstabs.q:2139
 #, c-format
 msgid "%s Dependent"
 msgstr ""
@@ -3726,7 +3736,7 @@
 msgid "SE. Mean"
 msgstr ""
 
-#: src/language/stats/t-test.q:702
+#: src/language/stats/t-test.q:703
 msgid "Group Statistics"
 msgstr ""
 
@@ -4133,630 +4143,376 @@
 msgid "installation error"
 msgstr ""
 
-#: src/output/ascii.c:235
+#: src/output/afm.c:150
 #, c-format
-msgid "ASCII driver initializing as `%s'..."
+msgid "opening font metrics file \"%s\""
 msgstr ""
 
-#: src/output/ascii.c:290
-#, c-format
-msgid ""
-"ascii driver: Area of page excluding margins and headers must be at least 59 "
-"characters wide by 15 lines long.  Page as configured is only %d characters "
-"by %d lines."
+#: src/output/afm.c:240
+msgid "first line must be StartFontMetrics"
 msgstr ""
 
-#: src/output/ascii.c:395 src/output/html.c:103 src/output/postscript.c:454
+#: src/output/afm.c:267
 #, c-format
-msgid "%s: Initialization complete."
+msgid "unsupported MappingScheme %d"
 msgstr ""
 
-#: src/output/ascii.c:407 src/output/html.c:115 src/output/postscript.c:467
-#, c-format
-msgid "%s: Beginning closing..."
+#: src/output/afm.c:288
+msgid "required FontName is missing"
 msgstr ""
 
-#: src/output/ascii.c:429 src/output/html.c:120 src/output/postscript.c:486
-#, c-format
-msgid "%s: Finished closing."
+#: src/output/afm.c:395
+msgid "CharMetrics line must start with C or CH"
 msgstr ""
 
-#: src/output/ascii.c:490
+#: src/output/afm.c:536
 #, c-format
-msgid ""
-"Bad index value for `box' key: syntax is box[INDEX], 0 <= INDEX < %d "
-"decimal, with INDEX expressed in base 4."
+msgid "reference to unknown character \"%s\""
 msgstr ""
 
-#: src/output/ascii.c:496
-#, c-format
-msgid "Duplicate value for key `%s'."
+#: src/output/afm.c:594
+msgid "expected end of file"
 msgstr ""
 
-#: src/output/ascii.c:505
-#, c-format
-msgid "Unknown configuration parameter `%s' for ascii device driver."
+#: src/output/afm.c:606
+msgid "syntax error expecting end of line"
 msgstr ""
 
-#: src/output/ascii.c:518
-#, c-format
-msgid ""
-"Unknown character set `%s'.  Valid character sets are `ascii' and `latin1'."
+#: src/output/afm.c:624 src/output/afm.c:661
+msgid "number out of valid range"
 msgstr ""
 
-#: src/output/ascii.c:527
-#, c-format
-msgid ""
-"Unknown overstrike style `%s'.  Valid overstrike styles are `single' and "
-"`line'."
+#: src/output/afm.c:626 src/output/afm.c:663
+msgid "invalid numeric syntax"
 msgstr ""
 
-#: src/output/ascii.c:536
-#, c-format
-msgid ""
-"Unknown carriage return style `%s'.  Valid carriage return styles are `cr' "
-"and `bs'."
+#: src/output/afm.c:642
+msgid "syntax error expecting integer"
 msgstr ""
 
-#: src/output/ascii.c:548 src/output/postscript.c:677
-#, c-format
-msgid "Positive integer required as value for `%s'."
+#: src/output/afm.c:680
+msgid "syntax error expecting number"
 msgstr ""
 
-#: src/output/ascii.c:579
-#, c-format
-msgid "Zero or positive integer required as value for `%s'."
+#: src/output/afm.c:693
+msgid "syntax error in hex constant"
 msgstr ""
 
-#: src/output/ascii.c:650 src/output/postscript.c:635
-#, c-format
-msgid "Boolean value expected for %s."
+#: src/output/afm.c:708
+msgid "syntax error expecting hex constant"
 msgstr ""
 
-#: src/output/ascii.c:682 src/output/ascii.c:697 src/output/ascii.c:714
-#, c-format
-msgid "ASCII output driver: %s: %s"
+#: src/output/afm.c:746
+msgid "unexpected end of line"
 msgstr ""
 
-#: src/output/ascii.c:790
-#, c-format
-msgid "ascii_line_horz: bad hline (%d,%d),%d out of (%d,%d)\n"
+#: src/output/afm.c:797
+msgid "unexpected end of line expecting string"
 msgstr ""
 
-#: src/output/ascii.c:824
+#: src/output/ascii.c:161
 #, c-format
-msgid "ascii_line_vert: bad vline %d,(%d,%d) out of (%d,%d)\n"
+msgid "ascii: opening output file \"%s\""
 msgstr ""
 
-#: src/output/ascii.c:854
+#: src/output/ascii.c:172
 #, c-format
-msgid "ascii_line_intersection: bad intsct (%d,%d) out of (%d,%d)\n"
-msgstr ""
-
-#: src/output/ascii.c:1002
-#, c-format
-msgid "%s: horiz=%d, vert=%d\n"
-msgstr ""
-
-#: src/output/ascii.c:1174
-#, c-format
-msgid "Writing `%s': %s"
-msgstr ""
-
-#: src/output/ascii.c:1584 src/output/postscript.c:2099
-#, c-format
-msgid "%s - Page %d"
-msgstr ""
-
-#: src/output/ascii.c:1641
-msgid "Charts are unsupported with ascii drivers."
-msgstr ""
-
-#: src/output/charts/plot-hist.c:127
-msgid "HISTOGRAM"
-msgstr ""
-
-#: src/output/groff-font.c:107
-#, c-format
-msgid "%s: Opening Groff font file..."
-msgstr ""
-
-#: src/output/groff-font.c:161
-msgid "Missing font name."
-msgstr ""
-
-#: src/output/groff-font.c:171
-msgid "Missing encoding filename."
-msgstr ""
-
-#: src/output/groff-font.c:184
-msgid "Bad spacewidth value."
-msgstr ""
-
-#: src/output/groff-font.c:196
-msgid "Bad slant value."
-msgstr ""
-
-#: src/output/groff-font.c:221
-#, c-format
-msgid "Unknown ligature `%s'."
-msgstr ""
-
-#: src/output/groff-font.c:256
-msgid "Unexpected end of line reading character set."
-msgstr ""
-
-#: src/output/groff-font.c:264
-msgid "Can't use ditto mark for first character."
-msgstr ""
-
-#: src/output/groff-font.c:269
-msgid "Can't ditto into an unnamed character."
-msgstr ""
-
-#: src/output/groff-font.c:286
-#, c-format
-msgid "Missing metrics for character `%s'."
+msgid ""
+"ascii: page excluding margins and headers must be at least 59 characters "
+"wide by 15 lines long, but as configured is only %d characters by %d lines"
 msgstr ""
 
-#: src/output/groff-font.c:295
+#: src/output/ascii.c:233
 #, c-format
-msgid "Missing type for character `%s'."
+msgid "ascii: closing output file \"%s\""
 msgstr ""
 
-#: src/output/groff-font.c:304
+#: src/output/ascii.c:285
 #, c-format
-msgid "Missing code for character `%s'."
-msgstr ""
-
-#: src/output/groff-font.c:323
-msgid "Malformed kernpair."
-msgstr ""
-
-#: src/output/groff-font.c:330
-msgid "Unexpected end of line reading kernpairs."
-msgstr ""
-
-#: src/output/groff-font.c:336
-msgid "Bad kern value."
-msgstr ""
-
-#: src/output/groff-font.c:368
-#, c-format
-msgid "Font read successfully with internal name %s."
-msgstr ""
-
-#: src/output/groff-font.c:389
-msgid "Error reading font."
-msgstr ""
-
-#: src/output/groff-font.c:402
-msgid "installation error: Groff font error: "
+msgid ""
+"ascii: bad index value for `box' key: syntax is box[INDEX], 0 <= INDEX < %d "
+"decimal, with INDEX expressed in base 4"
 msgstr ""
 
-#: src/output/groff-font.c:432
+#: src/output/ascii.c:292
 #, c-format
-msgid "Bad character \\%3o."
+msgid "ascii: multiple values for %s"
 msgstr ""
 
-#: src/output/groff-font.c:687
+#: src/output/ascii.c:300
 #, c-format
-msgid "Groff font error: Cannot find \"%s\"."
+msgid "ascii: unknown parameter `%s'"
 msgstr ""
 
-#: src/output/groff-font.c:752
+#: src/output/ascii.c:314
 #, c-format
-msgid "%s: Opening Groff description file..."
-msgstr ""
-
-#: src/output/groff-font.c:768
-msgid "Multiple `sizes' declarations."
-msgstr ""
-
-#: src/output/groff-font.c:785
-msgid "Unexpected end of file.  Missing 0 terminator to `sizes' command?"
+msgid "ascii: positive integer required as `%s' value"
 msgstr ""
 
-#: src/output/groff-font.c:797 src/output/groff-font.c:804
-#: src/output/groff-font.c:817
-msgid "Bad argument to `sizes'."
+#: src/output/ascii.c:340
+msgid "ascii: `emphasis' value must be `bold', `underline', or `none'"
 msgstr ""
 
-#: src/output/groff-font.c:809
-msgid "Bad range in argument to `sizes'."
-msgstr ""
-
-#: src/output/groff-font.c:838
-msgid "Family name expected."
-msgstr ""
-
-#: src/output/groff-font.c:843
-msgid "This command already specified."
-msgstr ""
-
-#: src/output/groff-font.c:863
+#: src/output/ascii.c:353
 #, c-format
-msgid "%s: Device characteristic already defined."
+msgid "ascii: zero or positive integer required as `%s' value"
 msgstr ""
 
-#: src/output/groff-font.c:869
+#: src/output/ascii.c:384
 #, c-format
-msgid "%s: Invalid numeric format."
+msgid "ascii: boolean value expected for `%s'"
 msgstr ""
 
-#: src/output/groff-font.c:899
-msgid "Missing `res', `unitwidth', and/or `sizes' line(s)."
-msgstr ""
-
-#: src/output/groff-font.c:925
-msgid "Description file read successfully."
-msgstr ""
-
-#: src/output/groff-font.c:957
-msgid "Error reading description file."
-msgstr ""
-
-#: src/output/groff-font.c:1014
-msgid "<<fallback>>"
-msgstr ""
-
-#: src/output/html.c:67
+#: src/output/ascii.c:473
 #, c-format
-msgid "HTML driver initializing as `%s'..."
+msgid "ascii: bad line (%d,%d)-(%d,%d) out of (%d,%d)\n"
 msgstr ""
 
-#: src/output/html.c:171
+#: src/output/ascii.c:683 src/output/postscript.c:832
 #, c-format
-msgid "Unknown configuration parameter `%s' for HTML device driver."
-msgstr ""
-
-#: src/output/html.c:251
-msgid ""
-"Cannot find HTML prologue.  The use of `-vv' on the command line is "
-"suggested as a debugging aid."
-msgstr ""
-
-#: src/output/html.c:256
-#, c-format
-msgid "%s: %s: Opening HTML prologue..."
+msgid "%s - Page %d"
 msgstr ""
 
-#: src/output/html.c:280 src/output/postscript.c:1350
-#: src/output/postscript.c:1361
-msgid "nobody"
+#: src/output/ascii.c:727
+msgid "ascii: charts are unsupported by this driver"
 msgstr ""
 
-#: src/output/html.c:289 src/output/html.c:292 src/output/postscript.c:1357
-#: src/output/postscript.c:1362
-msgid "nowhere"
+#: src/output/charts/plot-hist.c:127
+msgid "HISTOGRAM"
 msgstr ""
 
-#: src/output/html.c:350
+#: src/output/html.c:68
 #, c-format
-msgid "%s: HTML prologue read successfully."
+msgid "opening HTML output file: %s"
 msgstr ""
 
-#: src/output/html.c:354
-#, c-format
-msgid "%s: Error reading HTML prologue."
+#: src/output/html.c:79
+msgid "PSPP Output"
 msgstr ""
 
-#: src/output/html.c:382
+#: src/output/html.c:165
 #, c-format
-msgid "HTML output driver: %s: %s"
+msgid "unknown configuration parameter `%s' for HTML device driver"
 msgstr ""
 
-#: src/output/output.c:160
+#: src/output/output.c:159
 #, c-format
 msgid "Unknown output driver `%s'."
 msgstr ""
 
-#: src/output/output.c:162
+#: src/output/output.c:161
 #, c-format
 msgid "Output driver `%s' referenced but never defined."
 msgstr ""
 
-#: src/output/output.c:255
+#: src/output/output.c:254
 msgid "Using default output driver configuration."
 msgstr ""
 
-#: src/output/output.c:288
+#: src/output/output.c:289
 msgid ""
 "Cannot find output initialization file.  Use `-vvvvv' to view search path."
 msgstr ""
 
-#: src/output/output.c:293
-#, c-format
-msgid "%s: Opening device description file..."
-msgstr ""
-
-#: src/output/output.c:297 src/output/output.c:1205
-#: src/output/postscript.c:1095
+#: src/output/output.c:297 src/output/output.c:1060
 #, c-format
 msgid "Opening %s: %s."
 msgstr ""
 
-#: src/output/output.c:308 src/output/output.c:1216
-#: src/output/postscript.c:1110
+#: src/output/output.c:308 src/output/output.c:1071
 #, c-format
 msgid "Reading %s: %s."
 msgstr ""
 
-#: src/output/output.c:330 src/output/output.c:497
+#: src/output/output.c:330 src/output/output.c:496
 msgid "Syntax error."
 msgstr ""
 
-#: src/output/output.c:340 src/output/postscript.c:1121
+#: src/output/output.c:340
 #, c-format
 msgid "Closing %s: %s."
 msgstr ""
 
-#: src/output/output.c:347
-msgid "Device definition file read successfully."
-msgstr ""
-
-#: src/output/output.c:349
+#: src/output/output.c:348
 msgid "No output drivers are active."
 msgstr ""
 
-#: src/output/output.c:352
+#: src/output/output.c:351
 msgid "Error reading device definition file."
 msgstr ""
 
-#: src/output/output.c:469
+#: src/output/output.c:468
 #, c-format
 msgid ""
 "Driver classes:\n"
 "\t"
 msgstr ""
 
-#: src/output/output.c:598
+#: src/output/output.c:597
 msgid "Syntax error in string constant."
 msgstr ""
 
-#: src/output/output.c:630
+#: src/output/output.c:632
 msgid "Syntax error in options."
 msgstr ""
 
-#: src/output/output.c:640
+#: src/output/output.c:642
 msgid "Syntax error in options (`=' expected)."
 msgstr ""
 
-#: src/output/output.c:647
+#: src/output/output.c:649
 msgid "Syntax error in options (value expected after `=')."
 msgstr ""
 
-#: src/output/output.c:744
+#: src/output/output.c:691
 #, c-format
 msgid "Unknown output driver class `%s'."
 msgstr ""
 
-#: src/output/output.c:751
-#, c-format
-msgid "Can't initialize output driver class `%s'."
-msgstr ""
-
-#: src/output/output.c:758
-#, c-format
-msgid "Can't initialize output driver `%s' of class `%s'."
-msgstr ""
-
-#: src/output/output.c:780
+#: src/output/output.c:712
 #, c-format
 msgid "Unknown device type `%s'."
 msgstr ""
 
-#: src/output/output.c:793
-#, c-format
-msgid "Can't complete initialization of output driver `%s' of class `%s'."
-msgstr ""
-
-#: src/output/output.c:839
-msgid "Driver definition line contains fewer fields than expected"
-msgstr ""
-
-#: src/output/output.c:876
+#: src/output/output.c:731
 #, c-format
-msgid "Can't deinitialize output driver class `%s'."
+msgid "Can't initialize output driver `%s' of class `%s'."
 msgstr ""
 
-#: src/output/output.c:949
-#, c-format
-msgid "Trying to find keyword `%s'...\n"
+#: src/output/output.c:776
+msgid "Driver definition line missing driver name or class name"
 msgstr ""
 
-#: src/output/output.c:1066
+#: src/output/output.c:922
 #, c-format
 msgid "Unit \"%s\" is unknown in dimension \"%s\"."
 msgstr ""
 
-#: src/output/output.c:1081
+#: src/output/output.c:937
 #, c-format
 msgid "Bad dimension \"%s\"."
 msgstr ""
 
-#: src/output/output.c:1107
+#: src/output/output.c:963
 #, c-format
 msgid "`x' expected in paper size `%s'."
 msgstr ""
 
-#: src/output/output.c:1117
+#: src/output/output.c:973
 #, c-format
 msgid "Trailing garbage `%s' on paper size `%s'."
 msgstr ""
 
-#: src/output/output.c:1166
+#: src/output/output.c:1022
 msgid "Paper size name must not be empty."
 msgstr ""
 
-#: src/output/output.c:1197
+#: src/output/output.c:1053
 msgid "Cannot find `papersize' configuration file."
 msgstr ""
 
-#: src/output/output.c:1201
-#, c-format
-msgid "%s: Opening paper size definition file..."
-msgstr ""
-
-#: src/output/output.c:1243
+#: src/output/output.c:1098
 msgid "Syntax error in paper size definition."
 msgstr ""
 
-#: src/output/output.c:1272
-msgid "Paper size definition file read successfully."
-msgstr ""
-
-#: src/output/output.c:1274
+#: src/output/output.c:1127
 msgid "Error reading paper size definition file."
 msgstr ""
 
-#: src/output/output.c:1330
-#, c-format
-msgid "Error closing page on %s device of %s class."
-msgstr ""
-
-#: src/output/output.c:1334
+#: src/output/postscript.c:164
 #, c-format
-msgid "Error opening page on %s device of %s class."
+msgid "opening PostScript output file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:319
-#, c-format
-msgid "PostScript driver initializing as `%s'..."
-msgstr ""
-
-#: src/output/postscript.c:443
+#: src/output/postscript.c:202
 #, c-format
 msgid ""
-"PostScript driver: The defined 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."
+"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."
 msgstr ""
 
-#: src/output/postscript.c:573
+#: src/output/postscript.c:250
 #, c-format
-msgid "Unknown configuration parameter `%s' for PostScript device driver."
+msgid "closing PostScript output file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:589
+#: src/output/postscript.c:310
 #, c-format
-msgid ""
-"Unknown orientation `%s'.  Valid orientations are `portrait' and `landscape'."
+msgid "unknown configuration parameter `%s' for PostScript device driver"
 msgstr ""
 
-#: src/output/postscript.c:601
-msgid ""
-"Unknown value for `data'.  Valid values are `clean7bit', `clean8bit', and "
-"`binary'."
-msgstr ""
-
-#: src/output/postscript.c:610
-msgid "Unknown value for `line-ends'.  Valid values are `lf' and `crlf'."
-msgstr ""
-
-#: src/output/postscript.c:619
-msgid "Unknown value for `line-style'.  Valid values are `thick' and `double'."
-msgstr ""
-
-#: src/output/postscript.c:682
+#: src/output/postscript.c:326
 #, c-format
 msgid ""
-"Default font size must be at least 1 point (value of 1000 for key `%s')."
-msgstr ""
-
-#: src/output/postscript.c:714
-#, c-format
-msgid "Value for `%s' must be a dimension of positive length (i.e., `1in')."
-msgstr ""
-
-#: src/output/postscript.c:778
-#, c-format
-msgid "Nonnegative integer required as value for `%s'."
+"unknown orientation `%s' (valid orientations are `portrait' and `landscape')"
 msgstr ""
 
-#: src/output/postscript.c:904
+#: src/output/postscript.c:338
 #, c-format
-msgid "%s: %s: Opening PostScript font encoding..."
+msgid "boolean value expected for %s"
 msgstr ""
 
-#: src/output/postscript.c:910
+#: src/output/postscript.c:351
 #, c-format
-msgid ""
-"PostScript driver: Cannot open encoding file `%s': %s.  Substituting "
-"ISOLatin1Encoding for missing encoding."
-msgstr ""
-
-#: src/output/postscript.c:952
-msgid "PostScript driver: Invalid numeric format."
+msgid "positive integer value required for `%s'"
 msgstr ""
 
-#: src/output/postscript.c:957
+#: src/output/postscript.c:356
 #, c-format
-msgid ""
-"PostScript driver: Codes must be between 0 and 255.  (%d is not allowed.)"
+msgid "default font size must be at least 1 point (value of 1000 for key `%s')"
 msgstr ""
 
-#: src/output/postscript.c:993
+#: src/output/postscript.c:388
 #, c-format
-msgid "PostScript driver: Error closing encoding file `%s'."
+msgid "value for `%s' must be a dimension of positive length (i.e., `1in')"
 msgstr ""
 
-#: src/output/postscript.c:996
+#: src/output/postscript.c:1182
 #, c-format
-msgid "%s: PostScript font encoding read successfully."
+msgid "\"%s\": bad font specification"
 msgstr ""
 
-#: src/output/postscript.c:1090
+#: src/output/postscript.c:1190
 #, c-format
-msgid "%s: %s: Opening PostScript encoding list file."
+msgid "could not find AFM file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1123
+#: src/output/postscript.c:1204
 #, c-format
-msgid "%s: PostScript encoding list file read successfully."
+msgid "could not find font \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1137
-msgid "<<default encoding>>"
-msgstr ""
-
-#: src/output/postscript.c:1294
-msgid ""
-"Cannot find PostScript prologue.  The use of `-vv' on the command line is "
-"suggested as a debugging aid."
-msgstr ""
-
-#: src/output/postscript.c:1299
+#: src/output/postscript.c:1213
 #, c-format
-msgid "%s: %s: Opening PostScript prologue..."
+msgid "could not find encoding \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1465
+#: src/output/postscript.c:1313
 #, c-format
-msgid "%s: PostScript prologue read successfully."
+msgid "cannot open font file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1469
+#: src/output/postscript.c:1354
 #, c-format
-msgid "%s: Error reading PostScript prologue."
+msgid "reading font file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:1639
+#: src/output/postscript.c:1376
 #, c-format
-msgid "PostScript output driver: %s: %s"
+msgid "cannot open font encoding file \"%s\""
 msgstr ""
 
-#: src/output/postscript.c:2338
-#, c-format
-msgid "PostScript driver: Cannot find encoding `%s' for PostScript font `%s'."
+#: src/output/postscript.c:1405
+msgid "invalid numeric format"
 msgstr ""
 
-#: src/output/table.c:259
+#: src/output/table.c:239
 #, c-format
 msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
-#: src/output/table.c:334
+#: src/output/table.c:310
 #, c-format
 msgid ""
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
@@ -4919,8 +4675,6 @@
 "  -B, --config-dir=DIR      set configuration directory to DIR\n"
 "  -o, --device=DEVICE       select output driver DEVICE and disable "
 "defaults\n"
-"  -d, --define=VAR[=VALUE]  set environment variable VAR to VALUE, or empty\n"
-"  -u, --undef=VAR           undefine environment variable VAR\n"
 "\n"
 "Input and output:\n"
 "  -f, --out-file=FILE       send output to FILE (overwritten)\n"
@@ -4949,7 +4703,7 @@
 "\n"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:249
+#: src/ui/terminal/command-line.c:247
 #, c-format
 msgid ""
 "\n"
Index: pspp/src/language/ChangeLog
diff -u pspp/src/language/ChangeLog:1.1 pspp/src/language/ChangeLog:1.2
--- pspp/src/language/ChangeLog:1.1     Sat Mar  4 01:11:57 2006
+++ pspp/src/language/ChangeLog Mon Apr  3 20:07:54 2006
@@ -1,3 +1,14 @@
+Mon Apr  3 11:03:36 2006  Ben Pfaff  <address@hidden>
+
+       * list.q: (write_all_headers) Adapt to new html and output
+       internals.
+       (clean_up) Ditto.
+       (write_varname) Ditto.
+       (write_fallback_headers) Ditto.
+       (determine_layout) Ditto.
+       (list_cases) Ditto.
+       
+
 Thu Mar  2 08:40:33 WST 2006 John Darrington <address@hidden>
        
        * Moved files from src directory
Index: pspp/src/language/data-io/data-list.c
diff -u pspp/src/language/data-io/data-list.c:1.3 
pspp/src/language/data-io/data-list.c:1.4
--- pspp/src/language/data-io/data-list.c:1.3   Wed Mar 15 03:29:10 2006
+++ pspp/src/language/data-io/data-list.c       Mon Apr  3 20:07:54 2006
@@ -792,12 +792,12 @@
       tab_text (t, 1, i, TAT_PRINTF, "%d", spec->rec);
       tab_text (t, 2, i, TAT_PRINTF, "%3d-%3d",
                    spec->fc, spec->lc);
-      tab_text (t, 3, i, TAB_LEFT | TAT_FIX,
+      tab_text (t, 3, i, TAB_LEFT | TAB_FIX,
                    fmt_to_string (&spec->input));
     }
 
-  tab_title (t, 1, ngettext ("Reading %d record from %s.",
-                             "Reading %d records from %s.", rec_cnt),
+  tab_title (t, ngettext ("Reading %d record from %s.",
+                          "Reading %d records from %s.", rec_cnt),
              rec_cnt, fh_get_name (fh));
   tab_submit (t);
 }
@@ -908,11 +908,11 @@
     for (i = 1, spec = dls->first; spec; spec = spec->next, i++)
       {
        tab_text (t, 0, i, TAB_LEFT, spec->v->name);
-       tab_text (t, 1, i, TAB_LEFT | TAT_FIX, fmt_to_string (&spec->input));
+       tab_text (t, 1, i, TAB_LEFT | TAB_FIX, fmt_to_string (&spec->input));
       }
   }
 
-  tab_title (t, 1, _("Reading free-form data from %s."), fh_get_name (fh));
+  tab_title (t, _("Reading free-form data from %s."), fh_get_name (fh));
   
   tab_submit (t);
 }
Index: pspp/src/language/data-io/list.q
diff -u pspp/src/language/data-io/list.q:1.4 
pspp/src/language/data-io/list.q:1.5
--- pspp/src/language/data-io/list.q:1.4        Wed Mar 15 03:29:10 2006
+++ pspp/src/language/data-io/list.q    Mon Apr  3 20:07:54 2006
@@ -115,10 +115,12 @@
   struct outp_text text;
   
   assert (d->cp_y + d->font_height <= d->length);
-  text.options = OUTP_T_JUST_LEFT;
-  ls_init (&text.s, s, strlen (s));
+  text.font = OUTP_FIXED;
+  text.justification = OUTP_LEFT;
+  ls_init (&text.string, s, strlen (s));
   text.x = d->cp_x;
   text.y = d->cp_y;
+  text.h = text.v = INT_MAX;
   d->class->text_draw (d, &text);
   d->cp_x = 0;
   d->cp_y += d->font_height;
@@ -252,25 +254,17 @@
        {
          struct html_driver_ext *x = d->ext;
   
-         assert (d->driver_open);
-         if (x->sequence_no == 0 && !d->class->open_page (d))
-           {
-             msg (ME, _("Cannot open first page on HTML device %s."),
-                  d->name);
-             return;
-           }
-
-         fputs ("<TABLE BORDER=1>\n  <TR>\n", x->file.file);
+         fputs ("<TABLE BORDER=1>\n  <TR>\n", x->file);
          
          {
            size_t i;
 
            for (i = 0; i < cmd.n_variables; i++)
-             fprintf (x->file.file, "    <TH><I><B>%s</B></I></TH>\n",
+             fprintf (x->file, "    <TH><EM>%s</EM></TH>\n",
                       cmd.v_variables[i]->name);
          }
 
-         fputs ("  <TR>\n", x->file.file);
+         fputs ("  </TR>\n", x->file);
        }
       else
        assert (0);
@@ -380,16 +374,14 @@
            free (prc->header);
          }
        free (prc);
-      
-       d->class->text_set_font_by_name (d, "PROP");
       }
     else if (d->class == &html_class)
       {
-       if (d->driver_open && d->page_open)
+       if (d->page_open)
          {
            struct html_driver_ext *x = d->ext;
 
-           fputs ("</TABLE>\n", x->file.file);
+           fputs ("</TABLE>\n", x->file);
          }
       }
     else
@@ -405,12 +397,9 @@
 write_varname (struct outp_driver *d, char *string, int indent)
 {
   struct outp_text text;
-
-  text.options = OUTP_T_JUST_LEFT;
-  ls_init (&text.s, string, strlen (string));
-  d->class->text_metrics (d, &text);
+  int width;
   
-  if (d->cp_x + text.h > d->width)
+  if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
     {
       d->cp_y += d->font_height;
       if (d->cp_y + d->font_height > d->length)
@@ -418,10 +407,15 @@
       d->cp_x = indent;
     }
 
+  text.font = OUTP_FIXED;
+  text.justification = OUTP_LEFT;
+  ls_init (&text.string, string, strlen (string));
   text.x = d->cp_x;
   text.y = d->cp_y;
+  text.h = text.v = INT_MAX;
   d->class->text_draw (d, &text);
-  d->cp_x += text.h;
+  d->class->text_metrics (d, &text, &width, NULL);
+  d->cp_x += width;
 }
 
 /* When we can't fit all the values across the page, we write out all
@@ -448,13 +442,15 @@
        outp_eject_page (d);
       
       /* The leader is a string like `Line 1: '.  Write the leader. */
-      sprintf(leader, "%s %d:", Line, ++line_number);
-      text.options = OUTP_T_JUST_LEFT;
-      ls_init (&text.s, leader, strlen (leader));
+      sprintf (leader, "%s %d:", Line, ++line_number);
+      text.font = OUTP_FIXED;
+      text.justification = OUTP_LEFT;
+      ls_init (&text.string, leader, strlen (leader));
       text.x = 0;
       text.y = d->cp_y;
+      text.h = text.v = INT_MAX;
       d->class->text_draw (d, &text);
-      d->cp_x = text.h;
+      d->class->text_metrics (d, &text, &d->cp_x, NULL);
 
       goto entry;
       do
@@ -528,8 +524,7 @@
       
       assert (d->class->special == 0);
 
-      if (!d->page_open)
-       d->class->open_page (d);
+      outp_open_page (d);
       
       max_width = n_chars_width (d);
       largest_page_width = max (largest_page_width, max_width);
@@ -548,7 +543,6 @@
       if (width <= max_width)
        {
          prc->header_rows = 2;
-         d->class->text_set_font_by_name (d, "FIXED");
          continue;
        }
 
@@ -593,8 +587,6 @@
            prc->header_rows = max (prc->header_rows,
                                    strlen (cmd.v_variables[column]->name));
          prc->header_rows++;
-
-         d->class->text_set_font_by_name (d, "FIXED");
          continue;
        }
 
@@ -605,7 +597,6 @@
       d->cp_y += d->font_height;
       write_fallback_headers (d);
       d->cp_y += d->font_height;
-      d->class->text_set_font_by_name (d, "FIXED");
     }
 
   line_buf = xmalloc (max (1022, largest_page_width) + 2);
@@ -692,12 +683,13 @@
        struct html_driver_ext *x = d->ext;
        int column;
 
-       fputs ("  <TR>\n", x->file.file);
+       fputs ("  <TR>\n", x->file);
        
        for (column = 0; column < cmd.n_variables; column++)
          {
            struct variable *v = cmd.v_variables[column];
-           char buf[41];
+           char buf[256];
+            struct fixed_string s;
            
             if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
              data_out (buf, &v->print, case_data (c, v->fv));
@@ -707,13 +699,14 @@
                 case_idx_value.f = case_idx;
                 data_out (buf, &v->print, &case_idx_value); 
               }
-           buf[v->print.w] = 0;
 
-           fprintf (x->file.file, "    <TD ALIGN=RIGHT>%s</TD>\n",
-                    &buf[strspn (buf, " ")]);
+            ls_init (&s, buf, v->print.w);
+            fputs ("    <TD>", x->file);
+            html_put_cell_contents (d, TAB_FIX, &s);
+            fputs ("</TD>\n", x->file);
          }
          
-       fputs ("  </TR>\n", x->file.file);
+       fputs ("  </TR>\n", x->file);
       }
     else
       assert (0);
Index: pspp/src/language/data-io/print.c
diff -u pspp/src/language/data-io/print.c:1.3 
pspp/src/language/data-io/print.c:1.4
--- pspp/src/language/data-io/print.c:1.3       Wed Mar 15 03:29:10 2006
+++ pspp/src/language/data-io/print.c   Mon Apr  3 20:07:54 2006
@@ -810,12 +810,12 @@
        {
          int len = strlen (spec->u.c);
          nspec++;
-         tab_text (t, 0, nspec, TAB_LEFT | TAT_FIX | TAT_PRINTF,
+         tab_text (t, 0, nspec, TAB_LEFT | TAB_FIX | TAT_PRINTF,
                        "\"%s\"", spec->u.c);
          tab_text (t, 1, nspec, TAT_PRINTF, "%d", recno + 1);
          tab_text (t, 2, nspec, TAT_PRINTF, "%3d-%3d",
                        spec->fc + 1, spec->fc + len);
-         tab_text (t, 3, nspec, TAB_LEFT | TAT_FIX | TAT_PRINTF,
+         tab_text (t, 3, nspec, TAB_LEFT | TAB_FIX | TAT_PRINTF,
                        "A%d", len);
          break;
        }
@@ -826,7 +826,7 @@
          tab_text (t, 1, nspec, TAT_PRINTF, "%d", recno + 1);
          tab_text (t, 2, nspec, TAT_PRINTF, "%3d-%3d",
                        spec->fc + 1, spec->fc + spec->u.v.f.w);
-         tab_text (t, 3, nspec, TAB_LEFT | TAT_FIX,
+         tab_text (t, 3, nspec, TAB_LEFT | TAB_FIX,
                        fmt_to_string (&spec->u.v.f));
          break;
        }
@@ -837,12 +837,12 @@
       }
 
   if (fh != NULL)
-    tab_title (t, 1, ngettext ("Writing %d record to %s.",
-                               "Writing %d records to %s.", recno),
+    tab_title (t, ngettext ("Writing %d record to %s.",
+                            "Writing %d records to %s.", recno),
                recno, fh_get_name (fh));
   else
-    tab_title (t, 1, ngettext ("Writing %d record.",
-                               "Writing %d records.", recno), recno);
+    tab_title (t, ngettext ("Writing %d record.",
+                            "Writing %d records.", recno), recno);
   tab_submit (t);
 }
 
@@ -927,7 +927,7 @@
        if (t->writer == NULL)
          {
            buf[len] = 0;
-           tab_output_text (TAT_FIX | TAT_NOWRAP, buf);
+           tab_output_text (TAB_FIX | TAT_NOWRAP, buf);
          }
        else
          {
Index: pspp/src/language/dictionary/sys-file-info.c
diff -u pspp/src/language/dictionary/sys-file-info.c:1.2 
pspp/src/language/dictionary/sys-file-info.c:1.3
--- pspp/src/language/dictionary/sys-file-info.c:1.2    Wed Mar 15 03:29:10 2006
+++ pspp/src/language/dictionary/sys-file-info.c        Mon Apr  3 20:07:54 2006
@@ -96,7 +96,7 @@
   sfm_close_reader (reader);
 
   t = tab_create (2, 9, 0);
-  tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, 8);
+  tab_vline (t, TAL_GAP, 1, 0, 8);
   tab_text (t, 0, 0, TAB_LEFT, _("File:"));
   tab_text (t, 1, 0, TAB_LEFT, fh_get_filename (h));
   tab_text (t, 0, 1, TAB_LEFT, _("Label:"));
@@ -200,7 +200,7 @@
       else
        {
          tab_output_text (TAB_LEFT | TAT_TITLE, _("File label:"));
-         tab_output_text (TAB_LEFT | TAT_FIX, dict_get_label (default_dict));
+         tab_output_text (TAB_LEFT | TAB_FIX, dict_get_label (default_dict));
        }
     }
   else
@@ -311,7 +311,7 @@
                 && len > 0)
            len--;
          buf[len + 1] = 0;
-         tab_output_text (TAB_LEFT | TAT_FIX | TAT_NOWRAP, buf);
+         tab_output_text (TAB_LEFT | TAB_FIX | TAT_NOWRAP, buf);
        }
     }
 }
Index: pspp/src/language/line-buffer.c
diff -u pspp/src/language/line-buffer.c:1.2 pspp/src/language/line-buffer.c:1.3
--- pspp/src/language/line-buffer.c:1.2 Wed Mar 15 03:29:10 2006
+++ pspp/src/language/line-buffer.c     Mon Apr  3 20:07:54 2006
@@ -465,7 +465,7 @@
 
   /* Echo to listing file, if configured to do so. */
   if (get_echo ())
-    tab_output_text (TAB_LEFT | TAT_FIX, ds_c_str (line));
+    tab_output_text (TAB_LEFT | TAB_FIX, ds_c_str (line));
 
   return true;
 }
Index: pspp/src/language/stats/crosstabs.q
diff -u pspp/src/language/stats/crosstabs.q:1.4 
pspp/src/language/stats/crosstabs.q:1.5
--- pspp/src/language/stats/crosstabs.q:1.4     Tue Mar 28 00:02:28 2006
+++ pspp/src/language/stats/crosstabs.q Mon Apr  3 20:07:54 2006
@@ -809,7 +809,7 @@
   int cur_tab = 0;
 
   summary = tab_create (7, 3 + nxtab, 1);
-  tab_title (summary, 0, _("Summary."));
+  tab_title (summary, _("Summary."));
   tab_headers (summary, 1, 0, 3, 0);
   tab_joint_text (summary, 1, 0, 6, 0, TAB_CENTER, _("Cases"));
   tab_joint_text (summary, 1, 1, 2, 1, TAB_CENTER, _("Valid"));
@@ -1088,7 +1088,7 @@
          }
        strcpy (cp, "].");
 
-       tab_title (table, 0, title);
+       tab_title (table, "%s", title);
        local_free (title);
       }
       
@@ -1104,7 +1104,7 @@
                          (pe - pb) / n_cols * 3 / 2 * N_CHISQ + 10, 1);
       tab_headers (chisq, 1 + (nvar - 2), 0, 1, 0);
 
-      tab_title (chisq, 0, "Chi-square tests.");
+      tab_title (chisq, _("Chi-square tests."));
       
       tab_offset (chisq, nvar - 2, 0);
       tab_text (chisq, 0, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
@@ -1130,7 +1130,7 @@
     {
       sym = tab_create (6 + (nvar - 2), (pe - pb) / n_cols * 7 + 10, 1);
       tab_headers (sym, 2 + (nvar - 2), 0, 1, 0);
-      tab_title (sym, 0, "Symmetric measures.");
+      tab_title (sym, _("Symmetric measures."));
 
       tab_offset (sym, nvar - 2, 0);
       tab_text (sym, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
@@ -1149,7 +1149,7 @@
     {
       risk = tab_create (4 + (nvar - 2), (pe - pb) / n_cols * 4 + 10, 1);
       tab_headers (risk, 1 + nvar - 2, 0, 2, 0);
-      tab_title (risk, 0, "Risk estimate.");
+      tab_title (risk, _("Risk estimate."));
 
       tab_offset (risk, nvar - 2, 0);
       tab_joint_text (risk, 2, 0, 3, 0, TAB_CENTER | TAT_TITLE | TAT_PRINTF,
@@ -1171,7 +1171,7 @@
     {
       direct = tab_create (7 + (nvar - 2), (pe - pb) / n_cols * 7 + 10, 1);
       tab_headers (direct, 3 + (nvar - 2), 0, 1, 0);
-      tab_title (direct, 0, "Directional measures.");
+      tab_title (direct, _("Directional measures."));
 
       tab_offset (direct, nvar - 2, 0);
       tab_text (direct, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
@@ -1466,7 +1466,7 @@
   tab_box (t, TAL_2, TAL_2, -1, -1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
   tab_box (t, -1, -1, -1, TAL_1, tab_l (t), tab_t (t) - 1, tab_nc (t) - 1,
           tab_nr (t) - 1);
-  tab_box (t, -1, -1, -1, TAL_1 | TAL_SPACING, 0, tab_t (t), tab_l (t) - 1,
+  tab_box (t, -1, -1, -1, TAL_GAP, 0, tab_t (t), tab_l (t) - 1,
           tab_nr (t) - 1);
   tab_vline (t, TAL_2, tab_l (t), 0, tab_nr (t) - 1);
   tab_dim (t, crosstabs_dim);
@@ -1481,14 +1481,20 @@
   int i;
   
   /* Width of a numerical column. */
-  int c = outp_string_width (d, "0.000000");
+  int c = outp_string_width (d, "0.000000", OUTP_PROPORTIONAL);
   if (cmd.miss == CRS_REPORT)
-    c += outp_string_width (d, "M");
+    c += outp_string_width (d, "M", OUTP_PROPORTIONAL);
 
   /* Set width for header columns. */
   if (t->l != 0)
     {
-      int w = (d->width - t->vr_tot - c * (t->nc - t->l)) / t->l;
+      size_t i;
+      int w;
+
+      w = d->width - c * (t->nc - t->l);
+      for (i = 0; i <= t->nc; i++)
+        w -= t->wrv[i];
+      w /= t->l;
       
       if (w < d->prop_em_width * 8)
        w = d->prop_em_width * 8;
Index: pspp/src/language/stats/descriptives.c
diff -u pspp/src/language/stats/descriptives.c:1.3 
pspp/src/language/stats/descriptives.c:1.4
--- pspp/src/language/stats/descriptives.c:1.3  Wed Mar 15 03:29:11 2006
+++ pspp/src/language/stats/descriptives.c      Mon Apr  3 20:07:54 2006
@@ -540,7 +540,7 @@
   }
   
   t = tab_create (2, cnt + 1, 0);
-  tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
+  tab_title (t, _("Mapping of variables to corresponding Z-scores."));
   tab_columns (t, SOM_COL_DOWN, 1);
   tab_headers (t, 0, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
@@ -915,7 +915,7 @@
          tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
     }
 
-  tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
+  tab_title (t, _("Valid cases = %g; cases with missing value(s) = %g."),
             dsc->valid, dsc->missing_listwise);
 
   tab_submit (t);
Index: pspp/src/language/stats/examine.q
diff -u pspp/src/language/stats/examine.q:1.3 
pspp/src/language/stats/examine.q:1.4
--- pspp/src/language/stats/examine.q:1.3       Wed Mar 15 03:29:11 2006
+++ pspp/src/language/stats/examine.q   Mon Apr  3 20:07:54 2006
@@ -914,7 +914,7 @@
   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
 
 
-  tab_title (tbl, 0, _("Case Processing Summary"));
+  tab_title (tbl, _("Case Processing Summary"));
   
 
   tab_joint_text(tbl, heading_columns, 0, 
@@ -1120,7 +1120,7 @@
 
   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
 
-  tab_title (tbl, 0, _("Extreme Values"));
+  tab_title (tbl, _("Extreme Values"));
 
   tab_vline (tbl, TAL_2, n_cols - 2, 0, n_rows -1);
   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows -1);
@@ -1369,7 +1369,7 @@
   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _("Statistic"));
   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _("Std. Error"));
 
-  tab_title (tbl, 0, _("Descriptives"));
+  tab_title (tbl, _("Descriptives"));
 
 
   for ( i = 0 ; i < n_dep_var ; ++i ) 
@@ -1957,7 +1957,7 @@
   tab_vline (tbl, TAL_2, n_heading_columns, 0, n_rows - 1);
 
 
-  tab_title (tbl, 0, _("Percentiles"));
+  tab_title (tbl, _("Percentiles"));
 
 
   tab_hline (tbl, TAL_1, n_heading_columns, n_cols - 1, 1 );
Index: pspp/src/language/stats/frequencies.q
diff -u pspp/src/language/stats/frequencies.q:1.3 
pspp/src/language/stats/frequencies.q:1.4
--- pspp/src/language/stats/frequencies.q:1.3   Wed Mar 15 03:29:11 2006
+++ pspp/src/language/stats/frequencies.q       Mon Apr  3 20:07:54 2006
@@ -1214,7 +1214,7 @@
     }
 
   tab_box (t, TAL_1, TAL_1,
-          cmd.spaces == FRQ_SINGLE ? -1 : (TAL_1 | TAL_SPACING), TAL_1,
+          cmd.spaces == FRQ_SINGLE ? -1 : TAL_GAP, TAL_1,
           0, 0, 4 + lab, r);
   tab_hline (t, TAL_2, 0, 4 + lab, 2);
   tab_hline (t, TAL_2, 0, 4 + lab, r);
@@ -1224,7 +1224,7 @@
   tab_float (t, 2 + lab, r, TAB_NONE, 100.0, 5, 1);
   tab_float (t, 3 + lab, r, TAB_NONE, 100.0, 5, 1);
 
-  tab_title (t, 1, "%s: %s", v->name, v->label ? v->label : "");
+  tab_title (t, "%s: %s", v->name, v->label ? v->label : "");
   tab_submit (t);
 
 }
@@ -1234,9 +1234,9 @@
 static void
 condensed_dim (struct tab_table *t, struct outp_driver *d)
 {
-  int cum_w = max (outp_string_width (d, _("Cum")),
-                  max (outp_string_width (d, _("Cum")),
-                       outp_string_width (d, "000")));
+  int cum_w = max (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
+                  max (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
+                       outp_string_width (d, "000", OUTP_PROPORTIONAL)));
 
   int i;
 
@@ -1295,10 +1295,10 @@
     }
 
   tab_box (t, TAL_1, TAL_1,
-          cmd.spaces == FRQ_SINGLE ? -1 : (TAL_1 | TAL_SPACING), TAL_1,
+          cmd.spaces == FRQ_SINGLE ? -1 : TAL_GAP, TAL_1,
           0, 0, 3, r - 1);
   tab_hline (t, TAL_2, 0, 3, 2);
-  tab_title (t, 1, "%s: %s", v->name, v->label ? v->label : "");
+  tab_title (t, "%s: %s", v->name, v->label ? v->label : "");
   tab_columns (t, SOM_COL_DOWN, 1);
   tab_submit (t);
 }
@@ -1500,7 +1500,7 @@
 
 
   tab_vline (t, TAL_1 , 2, 0, tab_nr(t) - 1);
-  tab_vline (t, TAL_1 | TAL_SPACING , 1, 0, tab_nr(t) - 1 ) ;
+  tab_vline (t, TAL_GAP , 1, 0, tab_nr(t) - 1 ) ;
   
   r=2; /* N missing and N valid are always dumped */
 
@@ -1537,9 +1537,9 @@
   if (show_varname)
     {
       if (v->label)
-       tab_title (t, 1, "%s: %s", v->name, v->label);
+       tab_title (t, "%s: %s", v->name, v->label);
       else
-       tab_title (t, 0, v->name);
+       tab_title (t, "%s", v->name);
     }
   else
     tab_flags (t, SOMF_NO_TITLE);
Index: pspp/src/language/stats/oneway.q
diff -u pspp/src/language/stats/oneway.q:1.3 
pspp/src/language/stats/oneway.q:1.4
--- pspp/src/language/stats/oneway.q:1.3        Wed Mar 15 03:29:11 2006
+++ pspp/src/language/stats/oneway.q    Mon Apr  3 20:07:54 2006
@@ -355,7 +355,7 @@
     }
 
 
-  tab_title (t, 0, _("ANOVA"));
+  tab_title (t, _("ANOVA"));
   tab_submit (t);
 
 
@@ -412,7 +412,7 @@
   tab_text (t, 9, 1, TAB_CENTER | TAT_TITLE, _("Maximum"));
 
 
-  tab_title (t, 0, _("Descriptives"));
+  tab_title (t, _("Descriptives"));
 
 
   row = 2;
@@ -541,7 +541,7 @@
   tab_text (t,  4, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
   
 
-  tab_title (t, 0, _("Test of Homogeneity of Variances"));
+  tab_title (t, _("Test of Homogeneity of Variances"));
 
   for ( v=0 ; v < n_vars ; ++v ) 
     {
@@ -611,7 +611,7 @@
 
   tab_vline(t, TAL_2, 2, 0, n_rows - 1);
 
-  tab_title (t, 0, _("Contrast Coefficients"));
+  tab_title (t, _("Contrast Coefficients"));
 
   tab_text (t,  0, 2, TAB_LEFT | TAT_TITLE, _("Contrast"));
 
@@ -678,7 +678,7 @@
   tab_vline(t, TAL_2, 3, 0, n_rows - 1);
 
 
-  tab_title (t, 0, _("Contrast Tests"));
+  tab_title (t, _("Contrast Tests"));
 
   tab_text (t,  2, 0, TAB_CENTER | TAT_TITLE, _("Contrast"));
   tab_text (t,  3, 0, TAB_CENTER | TAT_TITLE, _("Value of Contrast"));
Index: pspp/src/language/stats/regression.q
diff -u pspp/src/language/stats/regression.q:1.5 
pspp/src/language/stats/regression.q:1.6
--- pspp/src/language/stats/regression.q:1.5    Thu Mar 16 15:07:04 2006
+++ pspp/src/language/stats/regression.q        Mon Apr  3 20:07:54 2006
@@ -150,7 +150,7 @@
   tab_float (t, 2, 1, TAB_RIGHT, rsq, 10, 2);
   tab_float (t, 3, 1, TAB_RIGHT, adjrsq, 10, 2);
   tab_float (t, 4, 1, TAB_RIGHT, std_error, 10, 2);
-  tab_title (t, 0, _("Model Summary"));
+  tab_title (t, _("Model Summary"));
   tab_submit (t);
 }
 
@@ -252,7 +252,7 @@
       pval = 2 * gsl_cdf_tdist_Q (fabs (t_stat), 1.0);
       tab_float (t, 6, j + 1, 0, pval, 10, 2);
     }
-  tab_title (t, 0, _("Coefficients"));
+  tab_title (t, _("Coefficients"));
   tab_submit (t);
   free (tmp);
 }
@@ -313,7 +313,7 @@
 
   tab_float (t, 6, 1, 0, pval, 8, 3);
 
-  tab_title (t, 0, _("ANOVA"));
+  tab_title (t, _("ANOVA"));
   tab_submit (t);
 }
 static void
@@ -384,7 +384,7 @@
                     gsl_matrix_get (c->cov, row, col), 8, 3);
        }
     }
-  tab_title (t, 0, _("Coefficient Correlations"));
+  tab_title (t, _("Coefficient Correlations"));
   tab_submit (t);
 }
 static void
Index: pspp/src/language/stats/t-test.q
diff -u pspp/src/language/stats/t-test.q:1.3 
pspp/src/language/stats/t-test.q:1.4
--- pspp/src/language/stats/t-test.q:1.3        Wed Mar 15 03:29:11 2006
+++ pspp/src/language/stats/t-test.q    Mon Apr  3 20:07:54 2006
@@ -677,7 +677,7 @@
   this->populate = ssbox_one_sample_populate;
 
   ssbox_base_init(this, hsize,vsize);
-  tab_title (this->t, 0, _("One-Sample Statistics"));
+  tab_title (this->t, _("One-Sample Statistics"));
   tab_vline(this->t, TAL_2, 1,0,vsize - 1);
   tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, _("N"));
   tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
@@ -699,8 +699,8 @@
   this->populate = ssbox_independent_samples_populate;
 
   ssbox_base_init(this, hsize,vsize);
-  tab_title (this->t, 0, _("Group Statistics"));
-  tab_vline(this->t,0,1,0,vsize - 1);
+  tab_vline (this->t, TAL_GAP, 1, 0,vsize - 1);
+  tab_title (this->t, _("Group Statistics"));
   tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, indep_var->name);
   tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("N"));
   tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
@@ -823,8 +823,8 @@
   this->populate = ssbox_paired_populate;
 
   ssbox_base_init(this, hsize,vsize);
-  tab_title (this->t, 0, _("Paired Sample Statistics"));
-  tab_vline(this->t,TAL_0,1,0,vsize-1);
+  tab_title (this->t, _("Paired Sample Statistics"));
+  tab_vline(this->t,TAL_GAP,1,0,vsize-1);
   tab_vline(this->t,TAL_2,2,0,vsize-1);
   tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
   tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("N"));
@@ -962,7 +962,7 @@
   self->populate = trbox_independent_samples_populate;
 
   trbox_base_init(self,cmd->n_variables*2,hsize);
-  tab_title(self->t,0,_("Independent Samples Test"));
+  tab_title(self->t,_("Independent Samples Test"));
   tab_hline(self->t,TAL_1,2,hsize-1,1);
   tab_vline(self->t,TAL_2,2,0,vsize-1);
   tab_vline(self->t,TAL_1,4,0,vsize-1);
@@ -1150,14 +1150,14 @@
   self->populate = trbox_paired_populate;
 
   trbox_base_init(self,n_pairs,hsize);
-  tab_title (self->t, 0, _("Paired Samples Test"));
+  tab_title (self->t, _("Paired Samples Test"));
   tab_hline(self->t,TAL_1,2,6,1);
   tab_vline(self->t,TAL_2,2,0,vsize - 1);
   tab_joint_text(self->t,2,0,6,0,TAB_CENTER,_("Paired Differences"));
   tab_box(self->t,-1,-1,-1,TAL_1, 2,1,6,vsize-1);
   tab_box(self->t,-1,-1,-1,TAL_1, 6,0,hsize-1,vsize-1);
   tab_hline(self->t,TAL_1,5,6, 2);
-  tab_vline(self->t,TAL_0,6,0,1);
+  tab_vline(self->t,TAL_GAP,6,0,1);
 
   tab_joint_text(self->t, 5, 1, 6, 1, TAB_CENTER | TAT_PRINTF, 
                 _("%g%% Confidence Interval of the Difference"),
@@ -1244,7 +1244,7 @@
   self->populate = trbox_one_sample_populate;
 
   trbox_base_init(self, cmd->n_variables,hsize);
-  tab_title (self->t, 0, _("One-Sample Test"));
+  tab_title (self->t, _("One-Sample Test"));
   tab_hline(self->t, TAL_1, 1, hsize - 1, 1);
   tab_vline(self->t, TAL_2, 1, 0, vsize - 1);
 
@@ -1258,7 +1258,7 @@
                 _("%g%% Confidence Interval of the Difference"),
                 cmd->criteria*100.0);
 
-  tab_vline(self->t,TAL_0,6,1,1);
+  tab_vline(self->t,TAL_GAP,6,1,1);
   tab_hline(self->t,TAL_1,5,6,2);
   tab_text (self->t, 1, 2, TAB_CENTER | TAT_TITLE, _("t"));
   tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("df"));
@@ -1359,7 +1359,7 @@
   tab_hline(table, TAL_2, 0, cols - 1, 1);
   tab_vline(table, TAL_2, 2, 0, rows - 1);
   tab_dim(table, tab_natural_dimensions);
-  tab_title(table, 0, _("Paired Samples Correlations"));
+  tab_title(table, _("Paired Samples Correlations"));
 
   /* column headings */
   tab_text(table, 2,0, TAB_CENTER | TAT_TITLE, _("N"));
Index: pspp/src/libpspp/ChangeLog
diff -u pspp/src/libpspp/ChangeLog:1.6 pspp/src/libpspp/ChangeLog:1.7
--- pspp/src/libpspp/ChangeLog:1.6      Fri Mar 31 18:38:59 2006
+++ pspp/src/libpspp/ChangeLog  Mon Apr  3 20:07:54 2006
@@ -1,3 +1,10 @@
+Mon Apr  3 11:10:21 2006  Ben Pfaff  <address@hidden>
+
+       * str.c: (ds_separate) Change interface for cleanliness and
+       consistency with ds_tokenize(), and rewrite to shorten and
+       simplify.  Updated all callers.
+       (ds_tokenize) New function.
+
 Fri Mar 31 10:38:46 2006  Ben Pfaff  <address@hidden>
 
        Add freaderror() analogous to fwriteerror() in gnulib.
Index: pspp/src/libpspp/str.c
diff -u pspp/src/libpspp/str.c:1.4 pspp/src/libpspp/str.c:1.5
--- pspp/src/libpspp/str.c:1.4  Fri Mar 31 00:30:22 2006
+++ pspp/src/libpspp/str.c      Mon Apr  3 20:07:54 2006
@@ -431,7 +431,7 @@
    empty string if no tokens remain.  Returns true if a token was
    obtained, false otherwise.
 
-   Before the first call, initialize *SAVE_IDX to -1.  Do not
+   Before the first call, initialize *SAVE_IDX to 0.  Do not
    modify *SAVE_IDX between calls.
 
    ST divides into exactly one more tokens than it contains
@@ -440,28 +440,36 @@
    empty string contains a single token. */
 bool
 ds_separate (const struct string *st, struct string *token,
-             const char *delimiters, int *save_idx)
+             const char *delimiters, size_t *save_idx)
 {
-  int start_idx;
-
-  ds_clear (token);
-  if (*save_idx < 0) 
+  if (*save_idx <= ds_length (st))
     {
-      *save_idx = 0;
-      if (ds_is_empty (st))
-        return true;
+      size_t length = ds_cspan (st, *save_idx, delimiters);
+      ds_assign_substring (token, st, *save_idx, length);
+      *save_idx += length + 1;
+      return true;
     }
-  else if (*save_idx < ds_length (st))
-    ++*save_idx;
-  else
+  else 
     return false;
+}
 
-  start_idx = *save_idx;
-  while (*save_idx < ds_length (st)
-         && strchr (delimiters, ds_data (st)[*save_idx]) == NULL)
-    ++*save_idx;
-  ds_assign_substring (token, st, start_idx, *save_idx - start_idx);
-  return true;
+/* Divides ST into tokens separated by any of the DELIMITERS,
+   merging adjacent delimiters so that the empty string is never
+   produced as a token.  Each call replaces TOKEN by the next
+   token in ST, or by an empty string if no tokens remain.
+   Returns true if a token was obtained, false otherwise.
+
+   Before the first call, initialize *SAVE_IDX to 0.  Do not
+   modify *SAVE_IDX between calls. */
+bool
+ds_tokenize (const struct string *st, struct string *token,
+             const char *delimiters, size_t *save_idx)
+{
+  size_t start = *save_idx + ds_span (st, *save_idx, delimiters);
+  size_t length = ds_cspan (st, start, delimiters);
+  ds_assign_substring (token, st, start, length);
+  *save_idx = start + length;
+  return length > 0;
 }
 
 /* Returns true if ST is empty, false otherwise. */
Index: pspp/src/libpspp/str.h
diff -u pspp/src/libpspp/str.h:1.5 pspp/src/libpspp/str.h:1.6
--- pspp/src/libpspp/str.h:1.5  Fri Mar 31 00:30:22 2006
+++ pspp/src/libpspp/str.h      Mon Apr  3 20:07:54 2006
@@ -144,7 +144,9 @@
 void ds_trim_spaces (struct string *);
 bool ds_chomp (struct string *, char);
 bool ds_separate (const struct string *src, struct string *token,
-                  const char *delimiters, int *save_idx);
+                  const char *delimiters, size_t *save_idx);
+bool ds_tokenize (const struct string *src, struct string *token,
+                  const char *delimiters, size_t *save_idx);
 
 /* Inspectors. */
 bool ds_is_empty (const struct string *);
Index: pspp/src/output/ChangeLog
diff -u pspp/src/output/ChangeLog:1.5 pspp/src/output/ChangeLog:1.6
--- pspp/src/output/ChangeLog:1.5       Fri Mar 31 00:30:22 2006
+++ pspp/src/output/ChangeLog   Mon Apr  3 20:07:54 2006
@@ -1,3 +1,120 @@
+Mon Apr  3 11:14:38 2006  Ben Pfaff  <address@hidden>
+
+       Rewrite a lot of the output drivers and infrastructure.
+       Started transitioning from msg() to error().
+       Vertical rules in tables now default to putting a small gap
+       between columns, instead of no gap or rule at all.
+       See NEWS for user-visible changes.
+
+       * automake.mk: (output_sources) Add afm.c, afm.h.  Remove font.h,
+       groff-font.c.
+       
+       * afm.c, afm.h: New files.
+
+       * font.h: Removed.
+
+       * groff-font.c: Removed.
+       
+       * ascii.c: Rewrote and simplified.
+
+       * html.c: Ditto.
+
+       * postscript.c: Ditto.
+
+       * output.c: (struct outp_driver_class_list) Move here from
+       output.h.  Remove ref_count member and all references to it.
+       (outp_init) Remove epsf_class references.
+       (init_default_drivers) Use new configure_driver_line() interface.
+       (parse_options) Renamed outp_parse_options(), changed interface.
+       (configure_driver) Changed args from `const char *'s to `const
+       struct string *'s.  Rewrote.  Don't call ->open_global().  Now
+       just calls ->open_driver() instead of ->preopen_driver(),
+       ->option(), ->postopen_driver().
+       (configure_driver_line) Adapt to new configure_driver() interface.
+       (destroy_driver) Don't call ->close_global().
+       (option_cmp) Removed.
+       (outp_match_keyword) Rewrite for simplicity.
+       (outp_open_page) New function.  Changed all equivalent
+       functionality to use this function instead.
+       (outp_close_page) Ditto.
+       (outp_eject_page) Use above functions.
+       (outp_string_width) Add font argument and change all callers to
+       pass one.
+
+       * output.h: (struct rect) Removed.
+       (OUTP_L_*) Name this enumeration "enum outp_line_style".
+       (OUTP_L_SPECIAL) Removed.
+       (struct color) Removed.
+       (OUTP_F_*) Removed.
+       (struct outp_styles) Removed.
+       (OUTP_T_*) Removed.
+       (enum outp_justification) New, containing OUTP_RIGHT, OUTP_LEFT,
+       OUTP_CENTER.
+       (enum outp_font) New, containing OUTP_FIXED, OUTP_PROPORTIONAL,
+       and OUTP_EMPHASIS.
+       (struct outp_text) Replaced `options' member by `font' and
+       `justification'.  Renamed `s' to `string'.  Removed `w', `l'.
+       Updated all usages.
+       (struct outp_class) Removed `magic', `open_global',
+       `close_global', `font_sizes', `preopen_driver', `option',
+       `postopen_driver', `line_horz', `line_vert', `line_intersection',
+       `box', `polyline_begin', `polyline_point', `polyline_end',
+       `text_set_font_by_name', `text_set_font_by_position',
+       `text_set_font_family', `text_get_font_name',
+       `text_get_font_family', `text_set_Size', and `text_get_size'
+       members.  Added `open_driver', `close_driver', `line' members.
+       Changed interface of `open_page', `close_page', `text_metrics',
+       `text_draw' members.  Updated all usages.
+       (struct outp_driver) Rearranged members.  Removed `driver_open',
+       `res', `horiz', `vert', `horiz_line_spacing', `vert_line_spacing'
+       members.
+       (struct outp_option_info) Removed.
+       (struct outp_driver_class_list) Removed.
+       (outp_match_keyword) Changed interface.
+
+       * table.c: (tab_create) Now ignores reallocable argument: tables
+       can always be reallocated.  Use pool_create_container().
+       Initialize vertical rules to UCHAR_MAX.
+       (options_to_font) New function.
+       (tab_destroy) Remove futile assignment.
+       (tab_realloc) Initialize vertical rules to UCHAR_MAX.
+       (text_format) Use xvasprintf() instead of local_alloc().
+       (tab_title) Always format the argument, and drop the option
+       argument.  Change all callers to agree.
+       (tab_natural_width) Adapt to new ->text_metrics() interface.
+       (tab_natural_height) Ditto.
+       (tab_joint_text) Clear rules within the joined cell.  Now
+       necessary because of the default to put spacing between cells.
+       (tab_output_text) Use xvasprintf() instead of local_alloc().
+       Remove special cases for fixed-width font.
+       (rule_to_spacing_type) New function.
+       (tabi_driver) Calculate rule widths manually now that we don't
+       have ->trh or ->trv.  Implement new default for vertical rules.
+       (render_rows) New function.
+       (tabi_render) Rewrite in terms of render_rows() for clarity.
+       (translate_justification) New function.
+       (rule_to_draw_type) New function.
+       (get_hrule) New function.
+       (get_vrule) New function.
+       (render_horz_rule) New function.
+       (render_vert_rule) New function.
+       (render_rule_intersection) New function.
+       (strip_width) New function.
+       (strip_height) New function.
+       (render_cell) New function.
+       (render_strip) Rewrite in terms of new functions.
+
+       * table.h: (TAB_EMPH) New flag.
+       (TAB_FIX) New flag.
+       (TAL_3) Removed.
+       (TAL_GAP) Added.
+       (TAL_SPACING) Removed.
+       (struct tab_table) Members `trh', `hrv', `hr_tot', `vr_tot'
+       removed.
+       [DEBUGGING] (reallocable) Removed.
+       (TAT_FIX) Removed.  All references replaced by TAB_FIX.
+       (TAT_TITLE) Now implies TAB_EMPH.
+               
 Thu Mar 30 16:26:56 2006  Ben Pfaff  <address@hidden>
 
        * output.c: (colon_tokenize) Removed.
Index: pspp/src/output/ascii.c
diff -u pspp/src/output/ascii.c:1.4 pspp/src/output/ascii.c:1.5
--- pspp/src/output/ascii.c:1.4 Wed Mar 15 03:29:11 2006
+++ pspp/src/output/ascii.c     Mon Apr  3 20:07:54 2006
@@ -18,92 +18,49 @@
    02110-1301, USA. */
 
 #include <config.h>
-#include <libpspp/message.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
+
+#include <data/filename.h>
 #include <libpspp/alloc.h>
-#include <libpspp/message.h>
-#include "chart.h"
 #include <libpspp/compiler.h>
-#include <data/filename.h>
-#include <libpspp/misc.h>
-#include "output.h"
+#include <libpspp/message.h>
 #include <libpspp/pool.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
 
+#include "chart.h"
+#include "error.h"
+#include "minmax.h"
+#include "output.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 /* ASCII driver options: (defaults listed first)
 
    output-file="pspp.list"
-   char-set=ascii|latin1
-   form-feed-string="\f"        Written as a formfeed.
-   newline-string=default|"\r\n"|"\n"   
-                                Written as a newline.
    paginate=on|off              Formfeeds are desired?
    tab-width=8                  Width of a tab; 0 to not use tabs.
-   init=""                      Written at beginning of output.
-   done=""                      Written at end of output.
    
    headers=on|off               Put headers at top of page?
+   emphasis=bold|underline|none Style to use for emphasis.
    length=66
    width=130
-   lpi=6                        Only used to determine font size.
-   cpi=10                       
    squeeze=off|on               Squeeze multiple newlines into exactly one.
 
-   left-margin=0
-   right-margin=0
    top-margin=2
    bottom-margin=2
 
    box[x]="strng"               Sets box character X (X in base 4: 0-3333).
-   italic-on=overstrike|"strng" Turns on italic (underline).
-   italic-off=""|"strng"        Turns off italic; ignored for overstrike.
-   bold-on=overstrike|"strng"   Turns on bold.
-   bold-off=""|"strng"          Turns off bold; ignored for overstrike.
-   bold-italic-on=overstrike|"strng" Turns on bold-italic.
-   bold-italic-off=""|"strng"   Turns off bold-italic; ignored for overstrike.
-   overstrike-style=single|line Can we print a whole line then BS over it, or
-   must we go char by char, as on a terminal?
-   carriage-return-style=bs|cr  Must we return the carriage with a sequence of
-   BSes, or will a single CR do it?
  */
 
 /* Disable messages by failed range checks. */
 /*#define SUPPRESS_WARNINGS 1 */
 
-/* Character set. */
-enum
-  {
-    CHS_ASCII,                 /* 7-bit ASCII */
-    CHS_LATIN1                 /* Latin 1; not really supported at the moment 
*/
-  };
-
-/* Overstrike style. */
-enum
-  {
-    OVS_SINGLE,                        /* Overstrike each character: 
"a\b_b\b_c\b_" */
-    OVS_LINE                   /* Overstrike lines: "abc\b\b\b___" (or if
-                                  newline is "\r\n", then "abc\r___").  Easier
-                                  on the printer, doesn't work on a tty. */
-  };
-
-/* Basic output strings. */
-enum
-  {
-    OPS_INIT,                  /* Document initialization string. */
-    OPS_DONE,                  /* Document uninit string. */
-    OPS_FORMFEED,              /* Formfeed string. */
-    OPS_NEWLINE,               /* Newline string. */
-
-    OPS_COUNT                  /* Number of output strings. */
-  };
-
 /* Line styles bit shifts. */
 enum
   {
@@ -115,36 +72,9 @@
     LNS_COUNT = 256
   };
 
-/* Carriage return style. */
-enum
-  {
-    CRS_BS,                    /* Multiple backspaces. */
-    CRS_CR                     /* Single carriage return. */
-  };
-
-/* Assembles a byte from four taystes. */
-#define TAYSTE2BYTE(T, L, B, R)                        \
-       (((T) << LNS_TOP)                       \
-        | ((L) << LNS_LEFT)                    \
-        | ((B) << LNS_BOTTOM)                  \
-        | ((R) << LNS_RIGHT))
-
-/* Extract tayste with shift value S from byte B. */
-#define BYTE2TAYSTE(B, S)                      \
-       (((B) >> (S)) & 3)
-
-/* Font style; take one of the first group |'d with one of the second group. */
-enum
-  {
-    FSTY_ON = 000,             /* Turn font on. */
-    FSTY_OFF = 001,            /* Turn font off. */
-
-    FSTY_ITALIC = 0,           /* Italic font. */
-    FSTY_BOLD = 2,             /* Bold font. */
-    FSTY_BOLD_ITALIC = 4,      /* Bold-italic font. */
-
-    FSTY_COUNT = 6             /* Number of font styles. */
-  };
+/* Character attributes. */
+#define ATTR_EMPHASIS   0x100   /* Bold-face. */
+#define ATTR_BOX        0x200   /* Line drawing character. */
 
 /* A line of text. */
 struct line 
@@ -154,330 +84,195 @@
     int char_cap;               /* Allocated bytes. */
   };
 
+/* How to emphasize text. */
+enum emphasis_style 
+  {
+    EMPH_BOLD,                  /* Overstrike for bold. */
+    EMPH_UNDERLINE,             /* Overstrike for underlining. */
+    EMPH_NONE                   /* No emphasis. */
+  };
+
 /* ASCII output driver extension record. */
 struct ascii_driver_ext
   {
+    struct pool *pool;
+
     /* User parameters. */
-    int char_set;              /* CHS_ASCII/CHS_LATIN1; no-op right now. */
-    int headers;               /* 1=print headers at top of page. */
-    int page_length;           /* Page length in lines. */
-    int page_width;            /* Page width in characters. */
-    int lpi;                   /* Lines per inch. */
-    int cpi;                   /* Characters per inch. */
-    int left_margin;           /* Left margin in characters. */
-    int right_margin;          /* Right margin in characters. */
+    bool headers;              /* Print headers at top of page? */
+    bool paginate;             /* Insert formfeeds? */
+    bool squeeze_blank_lines;   /* Squeeze multiple blank lines into one? */
+    enum emphasis_style emphasis; /* How to emphasize text. */
+    int tab_width;             /* Width of a tab; 0 not to use tabs. */
+
+    int page_length;           /* Page length before subtracting margins. */
     int top_margin;            /* Top margin in lines. */
     int bottom_margin;         /* Bottom margin in lines. */
-    int paginate;              /* 1=insert formfeeds. */
-    int tab_width;             /* Width of a tab; 0 not to use tabs. */
-    struct fixed_string ops[OPS_COUNT]; /* Basic output strings. */
-    struct fixed_string box[LNS_COUNT]; /* Line & box drawing characters. */
-    struct fixed_string fonts[FSTY_COUNT]; /* Font styles; NULL=overstrike. */
-    int overstrike_style;      /* OVS_SINGLE or OVS_LINE. */
-    int carriage_return_style; /* Carriage return style. */
-    int squeeze_blank_lines;    /* 1=squeeze multiple blank lines into one. */
+
+    char *box[LNS_COUNT];       /* Line & box drawing characters. */
 
     /* Internal state. */
-    struct file_ext file;      /* Output file. */
+    char *file_name;            /* Output file name. */
+    FILE *file;                 /* Output file. */
     int page_number;           /* Current page number. */
     struct line *lines;         /* Page content. */
-    int lines_cap;              /* Number of lines allocated. */
-    int w, l;                  /* Actual width & length w/o margins, etc. */
-    int cur_font;              /* Current font by OUTP_F_*. */
-#if DEBUGGING
-    int debug;                 /* Set by som_text_draw(). */
-#endif
+    int line_cap;               /* Number of lines allocated. */
   };
 
-static int postopen (struct file_ext *);
-static int preclose (struct file_ext *);
-
-static struct outp_option_info *option_info;
-
-static int
-ascii_open_global (struct outp_class *this UNUSED)
-{
-  option_info = xmalloc (sizeof *option_info);
-  option_info->initial = 0;
-  option_info->options = 0;
-  return 1;
-}
-
-
-static char *s;
-static int
-ascii_close_global (struct outp_class *this UNUSED)
-{
-  free(option_info->initial);
-  free(option_info->options);
-  free(option_info);
-  free(s);
-  return 1;
-}
-
-static int *
-ascii_font_sizes (struct outp_class *this UNUSED, int *n_valid_sizes)
-{
-  static int valid_sizes[] = {12, 12, 0, 0};
+static int get_default_box_char (size_t idx);
+static bool handle_option (struct outp_driver *this, const char *key,
+                           const struct string *val);
 
-  assert (n_valid_sizes);
-  *n_valid_sizes = 1;
-  return valid_sizes;
-}
-
-static int
-ascii_preopen_driver (struct outp_driver *this)
+static bool
+ascii_open_driver (struct outp_driver *this, const char *options)
 {
   struct ascii_driver_ext *x;
   int i;
-  
-  assert (this->driver_open == 0);
-  msg (VM (1), _("ASCII driver initializing as `%s'..."), this->name);
-  this->ext = x = xmalloc (sizeof *x);
-  x->char_set = CHS_ASCII;
-  x->headers = 1;
+
+  this->width = 79;
+  this->font_height = 1;
+  this->prop_em_width = 1;
+  this->fixed_width = 1;
+  for (i = 0; i < OUTP_L_COUNT; i++)
+    this->horiz_line_width[i] = this->vert_line_width[i] = i != OUTP_L_NONE;
+
+  this->ext = x = pool_create_container (struct ascii_driver_ext, pool);
+  x->headers = true;
+  x->paginate = true;
+  x->squeeze_blank_lines = false;
+  x->emphasis = EMPH_BOLD;
+  x->tab_width = 8;
   x->page_length = 66;
-  x->page_width = 79;
-  x->lpi = 6;
-  x->cpi = 10;
-  x->left_margin = 0;
-  x->right_margin = 0;
   x->top_margin = 2;
   x->bottom_margin = 2;
-  x->paginate = 1;
-  x->tab_width = 8;
-  for (i = 0; i < OPS_COUNT; i++)
-    ls_null (&x->ops[i]);
   for (i = 0; i < LNS_COUNT; i++)
-    ls_null (&x->box[i]);
-  for (i = 0; i < FSTY_COUNT; i++)
-    ls_null (&x->fonts[i]);
-  x->overstrike_style = OVS_SINGLE;
-  x->carriage_return_style = CRS_BS;
-  x->squeeze_blank_lines = 0;
-  x->file.filename = NULL;
-  x->file.mode = "wb";
-  x->file.file = NULL;
-  x->file.sequence_no = &x->page_number;
-  x->file.param = x;
-  x->file.postopen = postopen;
-  x->file.preclose = preclose;
+    x->box[i] = NULL;
+  x->file_name = pool_strdup (x->pool, "pspp.list");
+  x->file = NULL;
   x->page_number = 0;
   x->lines = NULL;
-  x->lines_cap = 0;
-  x->cur_font = OUTP_F_R;
-#if DEBUGGING
-  x->debug = 0;
-#endif
-  return 1;
-}
+  x->line_cap = 0;
 
-static int
-ascii_postopen_driver (struct outp_driver *this)
-{
-  struct ascii_driver_ext *x = this->ext;
-  
-  assert (this->driver_open == 0);
-  
-  if (NULL == x->file.filename)
-    x->file.filename = xstrdup ("pspp.list");
-  
-  x->w = x->page_width - x->left_margin - x->right_margin;
-  x->l = (x->page_length - (x->headers ? 3 : 0) - x->top_margin
-         - x->bottom_margin - 1);
-  if (x->w < 59 || x->l < 15)
+  if (!outp_parse_options (options, handle_option, this))
+    goto error;
+
+  x->file = pool_fopen (x->pool, x->file_name, "w");
+  if (x->file == NULL)
     {
-      msg (SE, _("ascii driver: Area of page excluding margins and headers "
-                "must be at least 59 characters wide by 15 lines long.  Page 
as "
-                "configured is only %d characters by %d lines."), x->w, x->l);
-      return 0;
+      error (0, errno, _("ascii: opening output file \"%s\""), x->file_name);
+      goto error;
     }
+
+  this->length = x->page_length - x->top_margin - x->bottom_margin - 1;
+  if (x->headers)
+    this->length -= 3;
   
-  this->res = x->lpi * x->cpi;
-  this->horiz = x->lpi;
-  this->vert = x->cpi;
-  this->width = x->w * this->horiz;
-  this->length = x->l * this->vert;
-  
-  if (ls_null_p (&x->ops[OPS_FORMFEED]))
-    ls_create (&x->ops[OPS_FORMFEED], "\f");
-  if (ls_null_p (&x->ops[OPS_NEWLINE])
-      || !strcmp (ls_c_str (&x->ops[OPS_NEWLINE]), "default"))
+  if (this->width < 59 || this->length < 15)
     {
-      ls_create (&x->ops[OPS_NEWLINE], "\n");
-      x->file.mode = "wt";
+      error (0, 0,
+             _("ascii: page excluding margins and headers "
+               "must be at least 59 characters wide by 15 lines long, but as "
+               "configured is only %d characters by %d lines"),
+             this->width, this->length);
+      return false;
     }
-  
-  {
-    int i;
-    
-    for (i = 0; i < LNS_COUNT; i++)
-      {
-       char c[2];
-       c[1] = 0;
-       if (!ls_null_p (&x->box[i]))
-         continue;
-       switch (i)
-         {
-         case TAYSTE2BYTE (0, 0, 0, 0):
-           c[0] = ' ';
-           break;
-
-         case TAYSTE2BYTE (0, 1, 0, 0):
-         case TAYSTE2BYTE (0, 1, 0, 1):
-         case TAYSTE2BYTE (0, 0, 0, 1):
-           c[0] = '-';
-           break;
 
-         case TAYSTE2BYTE (1, 0, 0, 0):
-         case TAYSTE2BYTE (1, 0, 1, 0):
-         case TAYSTE2BYTE (0, 0, 1, 0):
-           c[0] = '|';
-           break;
-
-         case TAYSTE2BYTE (0, 3, 0, 0):
-         case TAYSTE2BYTE (0, 3, 0, 3):
-         case TAYSTE2BYTE (0, 0, 0, 3):
-         case TAYSTE2BYTE (0, 2, 0, 0):
-         case TAYSTE2BYTE (0, 2, 0, 2):
-         case TAYSTE2BYTE (0, 0, 0, 2):
-           c[0] = '=';
-           break;
-
-         case TAYSTE2BYTE (3, 0, 0, 0):
-         case TAYSTE2BYTE (3, 0, 3, 0):
-         case TAYSTE2BYTE (0, 0, 3, 0):
-         case TAYSTE2BYTE (2, 0, 0, 0):
-         case TAYSTE2BYTE (2, 0, 2, 0):
-         case TAYSTE2BYTE (0, 0, 2, 0):
-           c[0] = '#';
-           break;
-
-         default:
-           if (BYTE2TAYSTE (i, LNS_LEFT) > 1
-               || BYTE2TAYSTE (i, LNS_TOP) > 1
-               || BYTE2TAYSTE (i, LNS_RIGHT) > 1
-               || BYTE2TAYSTE (i, LNS_BOTTOM) > 1)
-             c[0] = '#';
-           else
-             c[0] = '+';
-           break;
-         }
-       ls_create (&x->box[i], c);
-      }
-  }
-  
-  {
-    int i;
-    
-    this->cp_x = this->cp_y = 0;
-    this->font_height = this->vert;
-    this->prop_em_width = this->horiz;
-    this->fixed_width = this->horiz;
-
-    this->horiz_line_width[0] = 0;
-    this->vert_line_width[0] = 0;
-    
-    for (i = 1; i < OUTP_L_COUNT; i++)
-      {
-       this->horiz_line_width[i] = this->vert;
-       this->vert_line_width[i] = this->horiz;
-      }
-    
-    for (i = 0; i < (1 << OUTP_L_COUNT); i++)
+  for (i = 0; i < LNS_COUNT; i++)
+    if (x->box[i] == NULL) 
       {
-       this->horiz_line_spacing[i] = (i & ~1) ? this->vert : 0;
-       this->vert_line_spacing[i] = (i & ~1) ? this->horiz : 0;
+        char s[2];
+        s[0] = get_default_box_char (i);
+        s[1] = '\0';
+        x->box[i] = pool_strdup (x->pool, s);
       }
-  }
   
-  this->driver_open = 1;
-  msg (VM (2), _("%s: Initialization complete."), this->name);
+  return true;
 
-  return 1;
+ error:
+  pool_destroy (x->pool);
+  return false;
 }
 
 static int
+get_default_box_char (size_t idx)
+{
+  /* Disassemble IDX into components. */
+  unsigned top = (idx >> LNS_TOP) & 3;
+  unsigned left = (idx >> LNS_LEFT) & 3;
+  unsigned bottom = (idx >> LNS_BOTTOM) & 3;
+  unsigned right = (idx >> LNS_RIGHT) & 3;
+
+  /* Reassemble components into nibbles in the order TLBR.
+     This makes it easy to read the case labels. */
+  unsigned value = (top << 12) | (left << 8) | (bottom << 4) | (right << 0);
+  switch (value)
+    {
+    case 0x0000:
+      return ' ';
+
+    case 0x0100: case 0x0101: case 0x0001:
+      return '-';
+
+    case 0x1000: case 0x1010: case 0x0010:
+      return '|';
+
+    case 0x0300: case 0x0303: case 0x0003:
+    case 0x0200: case 0x0202: case 0x0002:
+      return '=';
+
+    default:
+      return left > 1 || top > 1 || right > 1 || bottom > 1 ? '#' : '+';
+    }
+}
+
+static bool
 ascii_close_driver (struct outp_driver *this)
 {
   struct ascii_driver_ext *x = this->ext;
-  int i;
   
-  assert (this->driver_open == 1);
-  msg (VM (2), _("%s: Beginning closing..."), this->name);
+  if (fn_close (x->file_name, x->file) != 0)
+    error (0, errno, _("ascii: closing output file \"%s\""), x->file_name);
+  pool_detach_file (x->pool, x->file);
+  pool_destroy (x->pool);
   
-  x = this->ext;
-  for (i = 0; i < OPS_COUNT; i++)
-    ls_destroy (&x->ops[i]);
-  for (i = 0; i < LNS_COUNT; i++)
-    ls_destroy (&x->box[i]);
-  for (i = 0; i < FSTY_COUNT; i++)
-    ls_destroy (&x->fonts[i]);
-  if (x->lines != NULL) 
-    {
-      int line;
-      
-      for (line = 0; line < x->lines_cap; line++) 
-        free (x->lines[line].chars);
-      free (x->lines); 
-    }
-  fn_close_ext (&x->file);
-  free (x->file.filename);
-  free (x);
-  
-  this->driver_open = 0;
-  msg (VM (3), _("%s: Finished closing."), this->name);
-  
-  return 1;
+  return true;
 }
 
 /* Generic option types. */
 enum
   {
-    pos_int_arg = -10,
-    nonneg_int_arg,
+    boolean_arg,
     string_arg,
-    font_string_arg,
-    boolean_arg
+    nonneg_int_arg,
+    pos_int_arg,
+    output_file_arg
   };
 
 static struct outp_option option_tab[] =
   {
     {"headers", boolean_arg, 0},
-    {"output-file", 1, 0},
-    {"char-set", 2, 0},
-    {"length", pos_int_arg, 0},
-    {"width", pos_int_arg, 1},
-    {"lpi", pos_int_arg, 2},
-    {"cpi", pos_int_arg, 3},
-    {"init", string_arg, 0},
-    {"done", string_arg, 1},
-    {"left-margin", nonneg_int_arg, 0},
-    {"right-margin", nonneg_int_arg, 1},
-    {"top-margin", nonneg_int_arg, 2},
-    {"bottom-margin", nonneg_int_arg, 3},
     {"paginate", boolean_arg, 1},
-    {"form-feed-string", string_arg, 2},
-    {"newline-string", string_arg, 3},
-    {"italic-on", font_string_arg, 0},
-    {"italic-off", font_string_arg, 1},
-    {"bold-on", font_string_arg, 2},
-    {"bold-off", font_string_arg, 3},
-    {"bold-italic-on", font_string_arg, 4},
-    {"bold-italic-off", font_string_arg, 5},
-    {"overstrike-style", 3, 0},
-    {"tab-width", nonneg_int_arg, 4},
-    {"carriage-return-style", 4, 0},
     {"squeeze", boolean_arg, 2},
-    {"", 0, 0},
+
+    {"emphasis", string_arg, 3},
+
+    {"output-file", output_file_arg, 0},
+
+    {"length", pos_int_arg, 0},
+    {"width", pos_int_arg, 1},
+
+    {"top-margin", nonneg_int_arg, 0},
+    {"bottom-margin", nonneg_int_arg, 1},
+    {"tab-width", nonneg_int_arg, 2},
+
+    {NULL, 0, 0},
   };
 
-static void
-ascii_option (struct outp_driver *this, const char *key,
-             const struct string *val)
+static bool
+handle_option (struct outp_driver *this, const char *key,
+               const struct string *val)
 {
   struct ascii_driver_ext *x = this->ext;
-  int cat, subcat;
+  int subcat;
   const char *value;
 
   value = ds_c_str (val);
@@ -487,54 +282,25 @@
       int indx = strtol (&key[4], &tail, 4);
       if (*tail != ']' || indx < 0 || indx > LNS_COUNT)
        {
-         msg (SE, _("Bad index value for `box' key: syntax is box[INDEX], "
-              "0 <= INDEX < %d decimal, with INDEX expressed in base 4."),
-              LNS_COUNT);
-         return;
+         error (0, 0, _("ascii: bad index value for `box' key: syntax "
+                         "is box[INDEX], 0 <= INDEX < %d decimal, with INDEX "
+                         "expressed in base 4"),
+                 LNS_COUNT);
+         return false;
        }
-      if (!ls_null_p (&x->box[indx]))
-       msg (SW, _("Duplicate value for key `%s'."), key);
-      ls_create (&x->box[indx], value);
-      return;
+      if (x->box[indx] != NULL)
+       error (0, 0, _("ascii: multiple values for %s"), key);
+      x->box[indx] = pool_strdup (x->pool, value);
+      return true;
     }
 
-  cat = outp_match_keyword (key, option_tab, option_info, &subcat);
-  switch (cat)
+  switch (outp_match_keyword (key, option_tab, &subcat))
     {
-    case 0:
-      msg (SE, _("Unknown configuration parameter `%s' for ascii device 
driver."),
-          key);
-      break;
-    case 1:
-      free (x->file.filename);
-      x->file.filename = xstrdup (value);
-      break;
-    case 2:
-      if (!strcmp (value, "ascii"))
-       x->char_set = CHS_ASCII;
-      else if (!strcmp (value, "latin1"))
-       x->char_set = CHS_LATIN1;
-      else
-       msg (SE, _("Unknown character set `%s'.  Valid character sets are "
-            "`ascii' and `latin1'."), value);
+    case -1:
+      error (0, 0, _("ascii: unknown parameter `%s'"), key);
       break;
-    case 3:
-      if (!strcmp (value, "single"))
-       x->overstrike_style = OVS_SINGLE;
-      else if (!strcmp (value, "line"))
-       x->overstrike_style = OVS_LINE;
-      else
-       msg (SE, _("Unknown overstrike style `%s'.  Valid overstrike styles "
-            "are `single' and `line'."), value);
-      break;
-    case 4:
-      if (!strcmp (value, "bs"))
-       x->carriage_return_style = CRS_BS;
-      else if (!strcmp (value, "cr"))
-       x->carriage_return_style = CRS_CR;
-      else
-       msg (SE, _("Unknown carriage return style `%s'.  Valid carriage "
-            "return styles are `cr' and `bs'."), value);
+    case output_file_arg:
+      x->file_name = pool_strdup (x->pool, value);
       break;
     case pos_int_arg:
       {
@@ -545,7 +311,8 @@
        arg = strtol (value, &tail, 0);
        if (arg < 1 || errno == ERANGE || *tail)
          {
-           msg (SE, _("Positive integer required as value for `%s'."), key);
+           error (0, 0, _("ascii: positive integer required as `%s' value"),
+                   key);
            break;
          }
        switch (subcat)
@@ -554,19 +321,25 @@
            x->page_length = arg;
            break;
          case 1:
-           x->page_width = arg;
-           break;
-         case 2:
-           x->lpi = arg;
-           break;
-         case 3:
-           x->cpi = arg;
+           this->width = arg;
            break;
          default:
-           assert (0);
+           abort ();
          }
       }
       break;
+    case string_arg:
+      if (!strcmp (value, "bold"))
+        x->emphasis = EMPH_BOLD;
+      else if (!strcmp (value, "underline"))
+        x->emphasis = EMPH_UNDERLINE;
+      else if (!strcmp (value, "none"))
+        x->emphasis = EMPH_NONE;
+      else 
+        error (0, 0,
+               _("ascii: `emphasis' value must be `bold', "
+                 "`underline', or `none'"));
+      break;
     case nonneg_int_arg:
       {
        char *tail;
@@ -576,79 +349,40 @@
        arg = strtol (value, &tail, 0);
        if (arg < 0 || errno == ERANGE || *tail)
          {
-           msg (SE, _("Zero or positive integer required as value for `%s'."),
-                key);
+           error (0, 0,
+                   _("ascii: zero or positive integer required as `%s' value"),
+                   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:
-           x->tab_width = arg;
-           break;
-         default:
-           assert (0);
-         }
-      }
-      break;
-    case string_arg:
-      {
-       struct fixed_string *s;
-       switch (subcat)
-         {
-         case 0:
-           s = &x->ops[OPS_INIT];
-           break;
          case 1:
-           s = &x->ops[OPS_DONE];
+           x->bottom_margin = arg;
            break;
          case 2:
-           s = &x->ops[OPS_FORMFEED];
-           break;
-         case 3:
-           s = &x->ops[OPS_NEWLINE];
+           x->tab_width = arg;
            break;
          default:
-           assert (0);
-            abort ();
-         }
-       ls_create (s, value);
-      }
-      break;
-    case font_string_arg:
-      {
-       if (!strcmp (value, "overstrike"))
-         {
-           ls_destroy (&x->fonts[subcat]);
-           return;
+           abort ();
          }
-       ls_create (&x->fonts[subcat], value);
       }
       break;
     case boolean_arg:
       {
-       int setting;
+       bool setting;
        if (!strcmp (value, "on") || !strcmp (value, "true")
            || !strcmp (value, "yes") || atoi (value))
-         setting = 1;
+         setting = true;
        else if (!strcmp (value, "off") || !strcmp (value, "false")
                 || !strcmp (value, "no") || !strcmp (value, "0"))
-         setting = 0;
+         setting = false;
        else
          {
-           msg (SE, _("Boolean value expected for %s."), key);
-           return;
+           error (0, 0, _("ascii: boolean value expected for `%s'"), key);
+           return false;
          }
        switch (subcat)
          {
@@ -662,988 +396,340 @@
             x->squeeze_blank_lines = setting;
             break;
          default:
-           assert (0);
+           abort ();
          }
       }
       break;
     default:
-      assert (0);
+      abort ();
     }
-}
-
-int
-postopen (struct file_ext *f)
-{
-  struct ascii_driver_ext *x = f->param;
-  struct fixed_string *s = &x->ops[OPS_INIT];
 
-  if (!ls_empty_p (s) && fwrite (ls_c_str (s), ls_length (s), 1, f->file) < 1)
-    {
-      msg (ME, _("ASCII output driver: %s: %s"),
-          f->filename, strerror (errno));
-      return 0;
-    }
-  return 1;
+  return true;
 }
 
-int
-preclose (struct file_ext *f)
-{
-  struct ascii_driver_ext *x = f->param;
-  struct fixed_string *d = &x->ops[OPS_DONE];
-
-  if (!ls_empty_p (d) && fwrite (ls_c_str (d), ls_length (d), 1, f->file) < 1)
-    {
-      msg (ME, _("ASCII output driver: %s: %s"),
-          f->filename, strerror (errno));
-      return 0;
-    }
-  return 1;
-}
-
-static int
+static void
 ascii_open_page (struct outp_driver *this)
 {
   struct ascii_driver_ext *x = this->ext;
   int i;
 
-  assert (this->driver_open && !this->page_open);
   x->page_number++;
-  if (!fn_open_ext (&x->file))
-    {
-      msg (ME, _("ASCII output driver: %s: %s"), x->file.filename,
-          strerror (errno));
-      return 0;
-    }
 
-  if (x->l > x->lines_cap)
+  if (this->length > x->line_cap)
     {
-      x->lines = xnrealloc (x->lines, x->l, sizeof *x->lines);
-      for (i = x->lines_cap; i < x->l; i++) 
+      x->lines = pool_nrealloc (x->pool,
+                                x->lines, this->length, sizeof *x->lines);
+      for (i = x->line_cap; i < this->length; i++) 
         {
           struct line *line = &x->lines[i];
           line->chars = NULL;
           line->char_cap = 0;
         }
-      x->lines_cap = x->l;
+      x->line_cap = this->length;
     }
 
-  for (i = 0; i < x->l; i++)
+  for (i = 0; i < this->length; i++)
     x->lines[i].char_cnt = 0;
-
-  this->page_open = 1;
-  return 1;
 }
 
-/* Ensures that at least the first L characters of line I in the
-   driver identified by struct ascii_driver_ext *X have been cleared out. */
+/* Ensures that at least the first LENGTH characters of line Y in
+   THIS driver identified X have been cleared out. */
 static inline void
-expand_line (struct ascii_driver_ext *x, int i, int l)
-{
-  struct line *line;
-  int j;
-
-  assert (i < x->lines_cap);
-  line = &x->lines[i];
-  if (l > line->char_cap) 
-    {
-      line->char_cap = l * 2;
-      line->chars = xnrealloc (line->chars,
-                               line->char_cap, sizeof *line->chars); 
-    }
-  for (j = line->char_cnt; j < l; j++)
-    line->chars[j] = ' ';
-  line->char_cnt = l;
-}
-
-/* Puts line L at (H,K) in the current output page.  Assumes
-   struct ascii_driver_ext named `ext'. */
-#define draw_line(H, K, L)                             \
-        ext->lines[K].chars[H] = (L) | 0x800
-
-/* Line styles for each position. */
-#define T(STYLE) (STYLE<<LNS_TOP)
-#define L(STYLE) (STYLE<<LNS_LEFT)
-#define B(STYLE) (STYLE<<LNS_BOTTOM)
-#define R(STYLE) (STYLE<<LNS_RIGHT)
-
-static void
-ascii_line_horz (struct outp_driver *this, const struct rect *r,
-                const struct color *c UNUSED, int style)
+expand_line (struct outp_driver *this, int y, int length)
 {
   struct ascii_driver_ext *ext = this->ext;
-  int x1 = r->x1 / this->horiz;
-  int x2 = r->x2 / this->horiz;
-  int y1 = r->y1 / this->vert;
-  int x;
-
-  assert (this->driver_open && this->page_open);
-  if (x1 == x2)
-    return;
-#if DEBUGGING
-  if (x1 > x2
-      || x1 < 0 || x1 >= ext->w
-      || x2 <= 0 || x2 > ext->w
-      || y1 < 0 || y1 >= ext->l)
+  struct line *line = &ext->lines[y];
+  if (line->char_cnt < length) 
     {
-#if !SUPPRESS_WARNINGS
-      printf (_("ascii_line_horz: bad hline (%d,%d),%d out of (%d,%d)\n"),
-             x1, x2, y1, ext->w, ext->l);
-#endif
-      return;
+      int x;
+      if (line->char_cap < length) 
+        {
+          line->char_cap = MIN (length * 2, this->width);
+          line->chars = pool_nrealloc (ext->pool,
+                                       line->chars,
+                                       line->char_cap, sizeof *line->chars); 
+        }
+      for (x = line->char_cnt; x < length; x++)
+        line->chars[x] = ' ';
+      line->char_cnt = length; 
     }
-#endif
-
-  if (ext->lines[y1].char_cnt < x2)
-    expand_line (ext, y1, x2);
-
-  for (x = x1; x < x2; x++)
-    draw_line (x, y1, (style << LNS_LEFT) | (style << LNS_RIGHT));
 }
 
 static void
-ascii_line_vert (struct outp_driver *this, const struct rect *r,
-                const struct color *c UNUSED, int style)
+ascii_line (struct outp_driver *this, 
+            int x0, int y0, int x1, int y1,
+            enum outp_line_style top, enum outp_line_style left,
+            enum outp_line_style bottom, enum outp_line_style right)
 {
   struct ascii_driver_ext *ext = this->ext;
-  int x1 = r->x1 / this->horiz;
-  int y1 = r->y1 / this->vert;
-  int y2 = r->y2 / this->vert;
   int y;
+  unsigned short value;
 
-  assert (this->driver_open && this->page_open);
-  if (y1 == y2)
-    return;
+  assert (this->page_open);
 #if DEBUGGING
-  if (y1 > y2
-      || x1 < 0 || x1 >= ext->w
-      || y1 < 0 || y1 >= ext->l
-      || y2 < 0 || y2 > ext->l)
+  if (x0 < 0 || x1 > this->width || y0 < 0 || y1 > this->length)
     {
 #if !SUPPRESS_WARNINGS
-      printf (_("ascii_line_vert: bad vline %d,(%d,%d) out of (%d,%d)\n"),
-             x1, y1, y2, ext->w, ext->l);
+      printf (_("ascii: bad line (%d,%d)-(%d,%d) out of (%d,%d)\n"),
+             x0, y0, x1, y1, this->width, this->length);
 #endif
       return;
     }
 #endif
 
-  for (y = y1; y < y2; y++)
-    if (ext->lines[y].char_cnt <= x1)
-      expand_line (ext, y, x1 + 1);
-
-  for (y = y1; y < y2; y++)
-    draw_line (x1, y, (style << LNS_TOP) | (style << LNS_BOTTOM));
-}
-
-static void
-ascii_line_intersection (struct outp_driver *this, const struct rect *r,
-                        const struct color *c UNUSED,
-                        const struct outp_styles *style)
-{
-  struct ascii_driver_ext *ext = this->ext;
-  int x = r->x1 / this->horiz;
-  int y = r->y1 / this->vert;
-  int l;
-
-  assert (this->driver_open && this->page_open);
-#if DEBUGGING
-  if (x < 0 || x >= ext->w || y < 0 || y >= ext->l)
+  value = ((left << LNS_LEFT) | (right << LNS_RIGHT)
+           | (top << LNS_TOP) | (bottom << LNS_BOTTOM) | ATTR_BOX);
+  for (y = y0; y < y1; y++) 
     {
-#if !SUPPRESS_WARNINGS
-      printf (_("ascii_line_intersection: bad intsct (%d,%d) out of 
(%d,%d)\n"),
-             x, y, ext->w, ext->l);
-#endif
-      return;
-    }
-#endif
-
-  l = ((style->l << LNS_LEFT) | (style->r << LNS_RIGHT)
-       | (style->t << LNS_TOP) | (style->b << LNS_BOTTOM));
-
-  if (ext->lines[y].char_cnt <= x)
-    expand_line (ext, y, x + 1);
-  draw_line (x, y, l);
-}
+      int x;
 
-/* FIXME: Later we could set this up so that for certain devices it
-   performs shading? */
-static void
-ascii_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED,
-          const struct color *bord UNUSED, const struct color *fill UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-
-/* Polylines not supported. */
-static void
-ascii_polyline_begin (struct outp_driver *this UNUSED, const struct color *c 
UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-static void
-ascii_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y 
UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-static void
-ascii_polyline_end (struct outp_driver *this UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-
-static void
-ascii_text_set_font_by_name (struct outp_driver * this, const char *s)
-{
-  struct ascii_driver_ext *x = this->ext;
-  int len = strlen (s);
-
-  assert (this->driver_open && this->page_open);
-  x->cur_font = OUTP_F_R;
-  if (len == 0)
-    return;
-  if (s[len - 1] == 'I')
-    {
-      if (len > 1 && s[len - 2] == 'B')
-       x->cur_font = OUTP_F_BI;
-      else
-       x->cur_font = OUTP_F_I;
+      expand_line (this, y, x1);
+      for (x = x0; x < x1; x++)
+        ext->lines[y].chars[x] = value; 
     }
-  else if (s[len - 1] == 'B')
-    x->cur_font = OUTP_F_B;
 }
 
 static void
-ascii_text_set_font_by_position (struct outp_driver *this, int pos)
+text_draw (struct outp_driver *this,
+           enum outp_font font,
+           int x, int y,
+           enum outp_justification justification, int width,
+           const char *string, size_t length)
 {
-  struct ascii_driver_ext *x = this->ext;
-  assert (this->driver_open && this->page_open);
-  x->cur_font = pos >= 0 && pos < 4 ? pos : 0;
-}
-
-static void
-ascii_text_set_font_by_family (struct outp_driver *this UNUSED, const char *s 
UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
+  struct ascii_driver_ext *ext = this->ext;
+  unsigned short attr = font == OUTP_EMPHASIS ? ATTR_EMPHASIS : 0;
 
-static const char *
-ascii_text_get_font_name (struct outp_driver *this)
-{
-  struct ascii_driver_ext *x = this->ext;
+  int line_len;
 
-  assert (this->driver_open && this->page_open);
-  switch (x->cur_font)
+  switch (justification)
     {
-    case OUTP_F_R:
-      return "R";
-    case OUTP_F_I:
-      return "I";
-    case OUTP_F_B:
-      return "B";
-    case OUTP_F_BI:
-      return "BI";
+    case OUTP_LEFT:
+      break;
+    case OUTP_CENTER:
+      x += (width - length + 1) / 2;
+      break;
+    case OUTP_RIGHT:
+      x += width - length;
+      break;
     default:
-      assert (0);
+      abort ();
     }
-  abort ();
-}
 
-static const char *
-ascii_text_get_font_family (struct outp_driver *this UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-  return "";
-}
+  if (y >= this->length || x >= this->width)
+    return;
 
-static int
-ascii_text_set_size (struct outp_driver *this, int size)
-{
-  assert (this->driver_open && this->page_open);
-  return size == this->vert;
-}
+  if (x + length > this->width)
+    length = this->width - x;
 
-static int
-ascii_text_get_size (struct outp_driver *this, int *em_width)
-{
-  assert (this->driver_open && this->page_open);
-  if (em_width)
-    *em_width = this->horiz;
-  return this->vert;
-}
+  line_len = x + length;
 
-static void text_draw (struct outp_driver *this, struct outp_text *t);
+  expand_line (this, y, line_len);
+  while (length-- > 0)
+    ext->lines[y].chars[x++] = *string++ | attr;
+}
 
 /* Divides the text T->S into lines of width T->H.  Sets T->V to the
    number of lines necessary.  Actually draws the text if DRAW is
-   nonzero.
-
-   You probably don't want to look at this code. */
+   true. */
 static void
-delineate (struct outp_driver *this, struct outp_text *t, int draw)
+delineate (struct outp_driver *this, const struct outp_text *text, bool draw,
+           int *width, int *height)
 {
-  /* Width we're fitting everything into. */
-  int width = t->h / this->horiz;
-
-  /* Maximum `y' position we can write to. */
-  int max_y;
+  int max_width;
+  int height_left;
 
-  /* Current position in string, character following end of string. */
-  const char *s = ls_c_str (&t->s);
-  const char *end = ls_end (&t->s);
+  const char *cp = ls_c_str (&text->string);
 
-  /* Temporary struct outp_text to pass to low-level function. */
-  struct outp_text temp;
+  max_width = 0;
+  height_left = text->v;
 
-#if DEBUGGING && 0
-  if (!ext->debug)
+  while (height_left > 0)
     {
-      ext->debug = 1;
-      printf (_("%s: horiz=%d, vert=%d\n"), this->name, this->horiz, 
this->vert);
-    }
-#endif
-
-  if (!width)
-    {
-      t->h = t->v = 0;
-      return;
-    }
+      size_t chars_left;
+      size_t line_len;
+      const char *end;
 
-  if (draw)
-    {
-      temp.options = t->options;
-      ls_shallow_copy (&temp.s, &t->s);
-      temp.h = t->h / this->horiz;
-      temp.x = t->x / this->horiz;
-    }
-  else
-    t->y = 0;
-  temp.y = t->y / this->vert;
-
-  if (t->options & OUTP_T_VERT)
-    max_y = (t->v / this->vert) + temp.y - 1;
-  else
-    max_y = INT_MAX;
-  
-  while (end - s > width)
-    {
-      const char *beg = s;
-      const char *space;
+      /* Initially the line is up to text->h characters long. */
+      chars_left = ls_end (&text->string) - cp;
+      if (chars_left == 0)
+        break;
+      line_len = MIN (chars_left, text->h);
 
-      /* Find first space before &s[width]. */
-      space = &s[width];
-      for (;;)
-       {
-         if (space > s)
-           {
-             if (!isspace ((unsigned char) space[-1]))
-               {
-                 space--;
-                 continue;
-               }
-             else
-               s = space;
-           }
-         else
-           s = space = &s[width];
-         break;
-       }
+      /* A new-line terminates the line prematurely. */
+      end = memchr (cp, '\n', line_len);
+      if (end != NULL)
+        line_len = end - cp;
 
+      /* Don't cut off words if it can be avoided. */
+      if (cp + line_len < ls_end (&text->string)) 
+        {
+          size_t space_len = line_len;
+          while (space_len > 0 && !isspace ((unsigned char) cp[space_len]))
+            space_len--;
+          if (space_len > 0)
+            line_len = space_len;
+        }
+      
       /* Draw text. */
       if (draw)
-       {
-         ls_init (&temp.s, beg, space - beg);
-         temp.w = space - beg;
-         text_draw (this, &temp);
-       }
-      if (++temp.y > max_y)
-       return;
+        text_draw (this,
+                   text->font,
+                   text->x, text->y + (text->v - height_left),
+                   text->justification, text->h,
+                   cp, line_len);
 
-      /* Find first nonspace after space. */
-      while (s < end && isspace ((unsigned char) *s))
-       s++;
-    }
-  if (s < end)
-    {
-      if (draw)
-       {
-         ls_init (&temp.s, s, end - s);
-         temp.w = end - s;
-         text_draw (this, &temp);
-       }
-      temp.y++;
+      /* Update. */
+      height_left--;
+      if (line_len > max_width)
+        max_width = line_len;
+
+      /* Next line. */
+      cp += line_len;
+      if (cp < ls_end (&text->string) && isspace ((unsigned char) *cp))
+        cp++;
     }
 
-  t->v = (temp.y * this->vert) - t->y;
+  if (width != NULL)
+    *width = max_width;
+  if (height != NULL)
+    *height = text->v - height_left;
 }
 
 static void
-ascii_text_metrics (struct outp_driver *this, struct outp_text *t)
+ascii_text_metrics (struct outp_driver *this, const struct outp_text *t,
+                    int *width, int *height)
 {
-  assert (this->driver_open && this->page_open);
-  if (!(t->options & OUTP_T_HORZ))
-    {
-      t->v = this->vert;
-      t->h = ls_length (&t->s) * this->horiz;
-    }
-  else
-    delineate (this, t, 0);
+  delineate (this, t, false, width, height);
 }
 
 static void
-ascii_text_draw (struct outp_driver *this, struct outp_text *t)
+ascii_text_draw (struct outp_driver *this, const struct outp_text *t)
 {
-  /* FIXME: orientations not supported. */
-  assert (this->driver_open && this->page_open);
-  if (!(t->options & OUTP_T_HORZ))
-    {
-      struct outp_text temp;
-
-      temp.options = t->options;
-      temp.s = t->s;
-      temp.h = temp.v = 0;
-      temp.x = t->x / this->horiz;
-      temp.y = t->y / this->vert;
-      text_draw (this, &temp);
-      ascii_text_metrics (this, t);
-      
-      return;
-    }
-  delineate (this, t, 1);
+  assert (this->page_open);
+  delineate (this, t, true, NULL, NULL);
 }
 
-static void
-text_draw (struct outp_driver *this, struct outp_text *t)
-{
-  struct ascii_driver_ext *ext = this->ext;
-  unsigned attr = ext->cur_font << 8;
-
-  int x = t->x;
-  int y = t->y;
-
-  char *s = ls_c_str (&t->s);
-
-  /* Expand the line with the assumption that S takes up LEN character
-     spaces (sometimes it takes up less). */
-  int min_len;
-
-  assert (this->driver_open && this->page_open);
-  switch (t->options & OUTP_T_JUST_MASK)
-    {
-    case OUTP_T_JUST_LEFT:
-      break;
-    case OUTP_T_JUST_CENTER:
-      x -= (t->h - t->w) / 2;  /* fall through */
-    case OUTP_T_JUST_RIGHT:
-      x += (t->h - t->w);
-      break;
-    default:
-      assert (0);
-    }
-
-  if (!(t->y < ext->l && x < ext->w))
-    return;
-  min_len = min (x + ls_length (&t->s), ext->w);
-  if (ext->lines[t->y].char_cnt < min_len)
-    expand_line (ext, t->y, min_len);
-
-  {
-    int len = ls_length (&t->s);
-
-    if (len + x > ext->w)
-      len = ext->w - x;
-    while (len--)
-      ext->lines[y].chars[x++] = *s++ | attr;
-  }
-}
 
 /* ascii_close_page () and support routines. */
 
-#define LINE_BUF_SIZE 1024
-static char *line_buf;
-static char *line_p;
-
-static inline int
-commit_line_buf (struct outp_driver *this)
-{
-  struct ascii_driver_ext *x = this->ext;
-  
-  if ((int) fwrite (line_buf, 1, line_p - line_buf, x->file.file)
-      < line_p - line_buf)
-    {
-      msg (ME, _("Writing `%s': %s"), x->file.filename, strerror (errno));
-      return 0;
-    }
-
-  line_p = line_buf;
-  return 1;
-}
-
-/* Writes everything from BP to EP exclusive into line_buf, or to
-   THIS->output if line_buf overflows. */
-static inline void
-output_string (struct outp_driver *this, const char *bp, const char *ep)
-{
-  if (LINE_BUF_SIZE - (line_p - line_buf) >= ep - bp)
-    {
-      memcpy (line_p, bp, ep - bp);
-      line_p += ep - bp;
-    }
-  else
-    while (bp < ep)
-      {
-       if (LINE_BUF_SIZE - (line_p - line_buf) <= 1 && !commit_line_buf (this))
-         return;
-       *line_p++ = *bp++;
-      }
-}
-
-/* Writes everything from BP to EP exclusive into line_buf, or to
-   THIS->output if line_buf overflows.  Returns 1 if additional passes
-   over the line are required.  FIXME: probably could do a lot of
-   optimization here. */
-static inline int
-output_shorts (struct outp_driver *this,
-              const unsigned short *bp, const unsigned short *ep)
+/* Writes the LENGTH characters in S to OUT.  */
+static void
+output_line (struct outp_driver *this, const struct line *line,
+             struct string *out)
 {
   struct ascii_driver_ext *ext = this->ext;
-  size_t remaining = LINE_BUF_SIZE - (line_p - line_buf);
-  int result = 0;
+  const unsigned short *s = line->chars;
+  size_t length;
 
-  for (; bp < ep; bp++)
-    {
-      if (*bp & 0x800)
-       {
-         struct fixed_string *box = &ext->box[*bp & 0xff];
-         size_t len = ls_length (box);
-
-         if (remaining >= len)
-           {
-             memcpy (line_p, ls_c_str (box), len);
-             line_p += len;
-             remaining -= len;
-           }
-         else
-           {
-             if (!commit_line_buf (this))
-               return 0;
-             output_string (this, ls_c_str (box), ls_end (box));
-             remaining = LINE_BUF_SIZE - (line_p - line_buf);
-           }
-       }
-      else if (*bp & 0x0300)
-       {
-         struct fixed_string *on;
-         char buf[5];
-         int len;
-
-         switch (*bp & 0x0300)
-           {
-           case OUTP_F_I << 8:
-             on = &ext->fonts[FSTY_ON | FSTY_ITALIC];
-             break;
-           case OUTP_F_B << 8:
-             on = &ext->fonts[FSTY_ON | FSTY_BOLD];
-             break;
-           case OUTP_F_BI << 8:
-             on = &ext->fonts[FSTY_ON | FSTY_BOLD_ITALIC];
-             break;
-           default:
-             assert (0);
-              abort ();
-           }
-         if (!on)
-           {
-             if (ext->overstrike_style == OVS_SINGLE)
-               switch (*bp & 0x0300)
-                 {
-                 case OUTP_F_I << 8:
-                   buf[0] = '_';
-                   buf[1] = '\b';
-                   buf[2] = *bp;
-                   len = 3;
-                   break;
-                 case OUTP_F_B << 8:
-                   buf[0] = *bp;
-                   buf[1] = '\b';
-                   buf[2] = *bp;
-                   len = 3;
-                   break;
-                 case OUTP_F_BI << 8:
-                   buf[0] = '_';
-                   buf[1] = '\b';
-                   buf[2] = *bp;
-                   buf[3] = '\b';
-                   buf[4] = *bp;
-                   len = 5;
-                   break;
-                 default:
-                   assert (0);
-                    abort ();
-                 }
-             else
-               {
-                 buf[0] = *bp;
-                 result = len = 1;
-               }
-           }
-         else
-           {
-             buf[0] = *bp;
-             len = 1;
-           }
-         output_string (this, buf, &buf[len]);
-       }
-      else if (remaining)
-       {
-         *line_p++ = *bp;
-         remaining--;
-       }
-      else
-       {
-         if (!commit_line_buf (this))
-           return 0;
-         remaining = LINE_BUF_SIZE - (line_p - line_buf);
-         *line_p++ = *bp;
-       }
-    }
-
-  return result;
-}
-
-/* Writes CH into line_buf N times, or to THIS->output if line_buf
-   overflows. */
-static inline void
-output_char (struct outp_driver *this, int n, char ch)
-{
-  if (LINE_BUF_SIZE - (line_p - line_buf) >= n)
-    {
-      memset (line_p, ch, n);
-      line_p += n;
-    }
-  else
-    while (n--)
+  for (length = line->char_cnt; length-- > 0; s++)
+    if (*s & ATTR_BOX)
+      ds_puts (out, ext->box[*s & 0xff]);
+    else
       {
-       if (LINE_BUF_SIZE - (line_p - line_buf) <= 1 && !commit_line_buf (this))
-         return;
-       *line_p++ = ch;
+        if (*s & ATTR_EMPHASIS) 
+          {
+            if (ext->emphasis == EMPH_BOLD)
+              {
+                ds_putc (out, *s);
+                ds_putc (out, '\b'); 
+              }
+            else if (ext->emphasis == EMPH_UNDERLINE)
+              ds_puts (out, "_\b"); 
+          }
+        ds_putc (out, *s);
       }
 }
 
-/* Advance the carriage from column 0 to the left margin. */
 static void
-advance_to_left_margin (struct outp_driver *this)
+append_lr_justified (struct string *out, int width,
+                     const char *left, const char *right)
 {
-  struct ascii_driver_ext *ext = this->ext;
-  int margin;
-
-  margin = ext->left_margin;
-  if (margin == 0)
-    return;
-  if (ext->tab_width && margin >= ext->tab_width)
+  ds_putc_multiple (out, ' ', width);
+  if (left != NULL) 
     {
-      output_char (this, margin / ext->tab_width, '\t');
-      margin %= ext->tab_width;
+      size_t length = MIN (strlen (left), width);
+      memcpy (ds_end (out) - width, left, length); 
     }
-  if (margin)
-    output_char (this, margin, ' ');
-}
-
-/* Move the output file carriage N_CHARS left, to the left margin. */
-static void
-return_carriage (struct outp_driver *this, int n_chars)
-{
-  struct ascii_driver_ext *ext = this->ext;
-
-  switch (ext->carriage_return_style)
+  if (right != NULL)
     {
-    case CRS_BS:
-      output_char (this, n_chars, '\b');
-      break;
-    case CRS_CR:
-      output_char (this, 1, '\r');
-      advance_to_left_margin (this);
-      break;
-    default:
-      assert (0);
-      abort ();
+      size_t length = MIN (strlen (right), width);
+      memcpy (ds_end (out) - length, right, length);
     }
+  ds_putc (out, '\n');
 }
 
-/* Writes COUNT lines from the line buffer in THIS, starting at line
-   number FIRST. */
 static void
-output_lines (struct outp_driver *this, int first, int count)
+dump_output (struct outp_driver *this, struct string *out) 
 {
-  struct ascii_driver_ext *ext = this->ext;
-  int line_num;
-
-  struct fixed_string *newline = &ext->ops[OPS_NEWLINE];
-
-  int n_chars;
-  int n_passes;
-
-  if (NULL == ext->file.file)
-    return;
-
-  /* Iterate over all the lines to be output. */
-  for (line_num = first; line_num < first + count; line_num++)
-    {
-      struct line *line = &ext->lines[line_num];
-      unsigned short *p = line->chars;
-      unsigned short *end_p = p + line->char_cnt;
-      unsigned short *bp, *ep;
-      unsigned short attr = 0;
-
-      assert (end_p >= p);
-
-      /* Squeeze multiple blank lines into a single blank line if
-         requested. */
-      if (ext->squeeze_blank_lines
-          && line_num > first
-          && ext->lines[line_num].char_cnt == 0
-          && ext->lines[line_num - 1].char_cnt == 0)
-        continue;
-
-      /* Output every character in the line in the appropriate
-         manner. */
-      n_passes = 1;
-      bp = ep = p;
-      n_chars = 0;
-      advance_to_left_margin (this);
-      for (;;)                 
-       {
-         while (ep < end_p && attr == (*ep & 0x0300))
-           ep++;
-         if (output_shorts (this, bp, ep))
-           n_passes = 2;
-         n_chars += ep - bp;
-         bp = ep;
-
-         if (bp >= end_p)
-           break;
-
-         /* Turn off old font. */
-         if (attr != (OUTP_F_R << 8))
-           {
-             struct fixed_string *off;
-
-             switch (attr)
-               {
-               case OUTP_F_I << 8:
-                 off = &ext->fonts[FSTY_OFF | FSTY_ITALIC];
-                 break;
-               case OUTP_F_B << 8:
-                 off = &ext->fonts[FSTY_OFF | FSTY_BOLD];
-                 break;
-               case OUTP_F_BI << 8:
-                 off = &ext->fonts[FSTY_OFF | FSTY_BOLD_ITALIC];
-                 break;
-               default:
-                 assert (0);
-                  abort ();
-               }
-             if (off)
-               output_string (this, ls_c_str (off), ls_end (off));
-           }
-
-         /* Turn on new font. */
-         attr = (*bp & 0x0300);
-         if (attr != (OUTP_F_R << 8))
-           {
-             struct fixed_string *on;
-
-             switch (attr)
-               {
-               case OUTP_F_I << 8:
-                 on = &ext->fonts[FSTY_ON | FSTY_ITALIC];
-                 break;
-               case OUTP_F_B << 8:
-                 on = &ext->fonts[FSTY_ON | FSTY_BOLD];
-                 break;
-               case OUTP_F_BI << 8:
-                 on = &ext->fonts[FSTY_ON | FSTY_BOLD_ITALIC];
-                 break;
-               default:
-                 assert (0);
-                  abort ();
-               }
-             if (on)
-               output_string (this, ls_c_str (on), ls_end (on));
-           }
-
-         ep = bp + 1;
-       }
-      if (n_passes > 1)
-       {
-         char ch;
-
-         return_carriage (this, n_chars);
-         n_chars = 0;
-         bp = ep = p;
-         for (;;)
-           {
-             while (ep < end_p && (*ep & 0x0300) == (OUTP_F_R << 8))
-               ep++;
-             if (ep >= end_p)
-               break;
-             output_char (this, ep - bp, ' ');
-
-             switch (*ep & 0x0300)
-               {
-               case OUTP_F_I << 8:
-                 ch = '_';
-                 break;
-               case OUTP_F_B << 8:
-                 ch = *ep;
-                 break;
-               case OUTP_F_BI << 8:
-                 ch = *ep;
-                 n_passes = 3;
-                 break;
-                default:
-                  assert (0);
-                  abort ();
-               }
-             output_char (this, 1, ch);
-             n_chars += ep - bp + 1;
-             bp = ep + 1;
-             ep = bp;
-           }
-       }
-      if (n_passes > 2)
-       {
-         return_carriage (this, n_chars);
-         bp = ep = p;
-         for (;;)
-           {
-             while (ep < end_p && (*ep & 0x0300) != (OUTP_F_BI << 8))
-               ep++;
-             if (ep >= end_p)
-               break;
-             output_char (this, ep - bp, ' ');
-             output_char (this, 1, '_');
-             bp = ep + 1;
-             ep = bp;
-           }
-       }
-
-      output_string (this, ls_c_str (newline), ls_end (newline));
-    }
+  struct ascii_driver_ext *x = this->ext;
+  fwrite (ds_data (out), ds_length (out), 1, x->file);
+  ds_clear (out);
 }
 
-
-static int
+static void
 ascii_close_page (struct outp_driver *this)
 {
-  static int s_len;
-
   struct ascii_driver_ext *x = this->ext;
-  int nl_len, ff_len, total_len;
-  char *cp;
-  int i;
-
-  assert (this->driver_open && this->page_open);
-  
-  if (!line_buf)
-    line_buf = xmalloc (LINE_BUF_SIZE);
-  line_p = line_buf;
-
-  nl_len = ls_length (&x->ops[OPS_NEWLINE]);
-  if (x->top_margin)
-    {
-      total_len = x->top_margin * nl_len;
-      if (s_len < total_len)
-       {
-         s_len = total_len;
-         s = xrealloc (s, s_len);
-       }
-      for (cp = s, i = 0; i < x->top_margin; i++)
-       {
-         memcpy (cp, ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
-         cp += nl_len;
-       }
-      output_string (this, s, &s[total_len]);
-    }
+  struct string out;
+  int line_num;
+ 
+  ds_init (&out, 128);
+ 
+  ds_putc_multiple (&out, '\n', x->top_margin);
   if (x->headers)
     {
-      int len;
-
-      total_len = nl_len + x->w;
-      if (s_len < total_len + 1)
-       {
-         s_len = total_len + 1;
-         s = xrealloc (s, s_len);
-       }
-      
-      memset (s, ' ', x->w);
-
-      {
-       char temp[40];
-
-       snprintf (temp, 80, _("%s - Page %d"), get_start_date (),
-                  x->page_number);
-       memcpy (&s[x->w - strlen (temp)], temp, strlen (temp));
-      }
-
-      if (outp_title && outp_subtitle)
-       {
-         len = min ((int) strlen (outp_title), x->w);
-         memcpy (s, outp_title, len);
-       }
-      memcpy (&s[x->w], ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
-      output_string (this, s, &s[total_len]);
-
-      memset (s, ' ', x->w);
-      len = strlen (version) + 3 + strlen (host_system);
-      if (len < x->w)
-       sprintf (&s[x->w - len], "%s - %s" , version, host_system);
-      if (outp_subtitle || outp_title)
-       {
-         char *string = outp_subtitle ? outp_subtitle : outp_title;
-         len = min ((int) strlen (string), x->w);
-         memcpy (s, string, len);
-       }
-      memcpy (&s[x->w], ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
-      output_string (this, s, &s[total_len]);
-      output_string (this, &s[x->w], &s[total_len]);
-    }
-  if (line_p != line_buf && !commit_line_buf (this))
-    return 0;
-
-  output_lines (this, 0, x->l);
-
-  ff_len = ls_length (&x->ops[OPS_FORMFEED]);
-  total_len = x->bottom_margin * nl_len + ff_len;
-  if (s_len < total_len)
-    s = xrealloc (s, total_len);
-  for (cp = s, i = 0; i < x->bottom_margin; i++)
+      char *r1, *r2;
+ 
+      r1 = xasprintf (_("%s - Page %d"), get_start_date (), x->page_number);
+      r2 = xasprintf ("%s - %s" , version, host_system);
+ 
+      append_lr_justified (&out, this->width, outp_title, r1);
+      append_lr_justified (&out, this->width, outp_subtitle, r2);
+      ds_putc (&out, '\n');
+ 
+      free (r1);
+      free (r2);
+    }
+  dump_output (this, &out);
+ 
+  for (line_num = 0; line_num < this->length; line_num++)
     {
-      memcpy (cp, ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
-      cp += nl_len;
+ 
+      /* Squeeze multiple blank lines into a single blank line if
+         requested. */
+      if (x->squeeze_blank_lines) 
+        {
+          if (line_num >= x->line_cap)
+            break;
+          if (line_num > 0
+              && x->lines[line_num].char_cnt == 0
+              && x->lines[line_num - 1].char_cnt == 0)
+            continue; 
+        }
+ 
+      if (line_num < x->line_cap) 
+        output_line (this, &x->lines[line_num], &out); 
+      ds_putc (&out, '\n');
+      dump_output (this, &out);
     }
-  memcpy (cp, ls_c_str (&x->ops[OPS_FORMFEED]), ff_len);
-  if ( x->paginate ) 
-         output_string (this, s, &s[total_len]);
-
-  if (line_p != line_buf && !commit_line_buf (this))
-    return 0;
+ 
+  ds_putc_multiple (&out, '\n', x->bottom_margin);
+  if (x->paginate) 
+    ds_putc (&out, '\f');
 
-  this->page_open = 0;
-  return 1;
+  dump_output (this, &out);
+  ds_destroy (&out);
 }
 
-
-
 static void
-ascii_chart_initialise(struct outp_driver *d UNUSED, struct chart *ch )
+ascii_chart_initialise (struct outp_driver *d UNUSED, struct chart *ch)
 {
-  msg(MW, _("Charts are unsupported with ascii drivers."));
+  error (0, 0, _("ascii: charts are unsupported by this driver"));
   ch->lp = 0;
 }
 
 static void 
-ascii_chart_finalise(struct outp_driver *d UNUSED, struct chart *ch UNUSED)
+ascii_chart_finalise (struct outp_driver *d UNUSED, struct chart *ch UNUSED)
 {
   
 }
@@ -1652,15 +738,8 @@
 {
   "ascii",
   0,
-  0,
 
-  ascii_open_global,
-  ascii_close_global,
-  ascii_font_sizes,
-
-  ascii_preopen_driver,
-  ascii_option,
-  ascii_postopen_driver,
+  ascii_open_driver,
   ascii_close_driver,
 
   ascii_open_page,
@@ -1668,22 +747,7 @@
 
   NULL,
 
-  ascii_line_horz,
-  ascii_line_vert,
-  ascii_line_intersection,
-
-  ascii_box,
-  ascii_polyline_begin,
-  ascii_polyline_point,
-  ascii_polyline_end,
-
-  ascii_text_set_font_by_name,
-  ascii_text_set_font_by_position,
-  ascii_text_set_font_by_family,
-  ascii_text_get_font_name,
-  ascii_text_get_font_family,
-  ascii_text_set_size,
-  ascii_text_get_size,
+  ascii_line,
   ascii_text_metrics,
   ascii_text_draw,
 
Index: pspp/src/output/automake.mk
diff -u pspp/src/output/automake.mk:1.2 pspp/src/output/automake.mk:1.3
--- pspp/src/output/automake.mk:1.2     Wed Mar 15 03:29:11 2006
+++ pspp/src/output/automake.mk Mon Apr  3 20:07:54 2006
@@ -6,9 +6,9 @@
 noinst_LIBRARIES += src/output/liboutput.a 
 
 output_sources = \
+       src/output/afm.c \
+       src/output/afm.h \
        src/output/ascii.c \
-       src/output/font.h \
-       src/output/groff-font.c \
        src/output/html.c \
        src/output/htmlP.h \
        src/output/output.c \
Index: pspp/src/output/html.c
diff -u pspp/src/output/html.c:1.6 pspp/src/output/html.c:1.7
--- pspp/src/output/html.c:1.6  Tue Mar 28 06:05:07 2006
+++ pspp/src/output/html.c      Mon Apr  3 20:07:54 2006
@@ -31,6 +31,7 @@
 #include <libpspp/compiler.h>
 #include <libpspp/message.h>
 #include <data/filename.h>
+#include "error.h"
 #include "getline.h"
 #include "getlogin_r.h"
 #include "output.h"
@@ -42,361 +43,137 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* Prototypes. */
-static int postopen (struct file_ext *);
-static int preclose (struct file_ext *);
+static void escape_string (FILE *file,
+                           const char *text, size_t length,
+                           const char *space);
+static bool handle_option (struct outp_driver *this,
+                           const char *key, const struct string *val);
+static void print_title_tag (FILE *file, const char *name,
+                             const char *content);
 
-static int
-html_open_global (struct outp_class *this UNUSED)
-{
-  return 1;
-}
-
-static int
-html_close_global (struct outp_class *this UNUSED)
-{
-  return 1;
-}
-
-static int
-html_preopen_driver (struct outp_driver *this)
+static bool
+html_open_driver (struct outp_driver *this, const char *options)
 {
   struct html_driver_ext *x;
 
-  assert (this->driver_open == 0);
-  msg (VM (1), _("HTML driver initializing as `%s'..."), this->name);
-
   this->ext = x = xmalloc (sizeof *x);
-  this->res = 0;
-  this->horiz = this->vert = 0;
-  this->width = this->length = 0;
-
-  this->cp_x = this->cp_y = 0;
-
-  x->prologue_fn = NULL;
+  x->file_name = xstrdup ("pspp.html");
+  x->file = NULL;
 
-  x->file.filename = NULL;
-  x->file.mode = "w";
-  x->file.file = NULL;
-  x->file.sequence_no = &x->sequence_no;
-  x->file.param = this;
-  x->file.postopen = postopen;
-  x->file.preclose = preclose;
+  outp_parse_options (options, handle_option, this);
 
-  x->sequence_no = 0;
-
-  return 1;
+  x->file = fn_open (x->file_name, "w");
+  if (x->file == NULL)
+    {
+      error (0, errno, _("opening HTML output file: %s"), x->file_name);
+      goto error;
+    }
+ 
+  fputs ("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
+         "   \"http://www.w3.org/TR/html4/loose.dtd\";>\n", x->file);
+  fputs ("<HTML>\n", x->file);
+  fputs ("<HEAD>\n", x->file);
+  /* The <TITLE> tag is required, so we use a default if the user
+     didn't provide one. */
+  print_title_tag (x->file,
+                   "TITLE", outp_title ? outp_title : _("PSPP Output"));
+  fprintf (x->file, "<META NAME=\"generator\" CONTENT=\"%s\">\n", version);
+  fputs ("<META HTTP-EQUIV=\"Content-Type\" "
+         "CONTENT=\"text/html; charset=ISO-8859-1\">\n", x->file);
+  fputs ("</HEAD>\n", x->file);
+  fputs ("<BODY BGCOLOR=\"#ffffff\" TEXT=\"#000000\"\n", x->file);
+  fputs (" LINK=\"#1f00ff\" ALINK=\"#ff0000\" VLINK=\"#9900dd\">\n", x->file);
+  print_title_tag (x->file, "H1", outp_title);
+  print_title_tag (x->file, "H2", outp_subtitle);
+
+  return true;
+
+ error:
+  this->class->close_driver (this);
+  return false;
 }
 
-static int
-html_postopen_driver (struct outp_driver *this)
+/* Emits <NAME>CONTENT</NAME> to the output, escaping CONTENT as
+   necessary for HTML. */
+static void
+print_title_tag (FILE *file, const char *name, const char *content) 
 {
-  struct html_driver_ext *x = this->ext;
-
-  assert (this->driver_open == 0);
-  if (NULL == x->file.filename)
-    x->file.filename = xstrdup ("pspp.html");
-       
-  if (x->prologue_fn == NULL)
-    x->prologue_fn = xstrdup ("html-prologue");
-
-  msg (VM (2), _("%s: Initialization complete."), this->name);
-  this->driver_open = 1;
-
-  return 1;
+  if (content != NULL) 
+    {
+      fprintf (file, "<%s>", name);
+      escape_string (file, content, strlen (content), " ");
+      fprintf (file, "</%s>\n", name);
+    }
 }
 
-static int
+static bool
 html_close_driver (struct outp_driver *this)
 {
   struct html_driver_ext *x = this->ext;
-
-  assert (this->driver_open);
-  msg (VM (2), _("%s: Beginning closing..."), this->name);
-  fn_close_ext (&x->file);
-  free (x->prologue_fn);
-  free (x->file.filename);
+  bool ok;
+ 
+  if (x->file != NULL) 
+    {
+      fprintf (x->file,
+               "</BODY>\n"
+               "</HTML>\n"
+               "<!-- end of file -->\n");
+      ok = fn_close (x->file_name, x->file) == 0;
+      x->file = NULL; 
+    }
+  else
+    ok = true;
+  free (x->file_name);
   free (x);
-  msg (VM (3), _("%s: Finished closing."), this->name);
-  this->driver_open = 0;
-  
-  return 1;
+   
+  return ok;
 }
 
-
-/* Link the image contained in FILENAME to the 
-   HTML stream in file F. */
-static int
-link_image (struct file_ext *f, char *filename)
+/* Link the image contained in FILE_NAME to the 
+   HTML stream in FILE. */
+static void
+link_image (FILE *file, char *file_name)
 {
-  fprintf (f->file,
-          "<IMG SRC=\"%s\"/>", filename);
-
-  if (ferror (f->file))
-    return 0;
-
-  return 1;
-}
-
+  fprintf (file, "<IMG SRC=\"%s\"/>", file_name);
+ }
 
 /* Generic option types. */
 enum
-{
-  boolean_arg = -10,
-  string_arg,
-  nonneg_int_arg
-};
+  {
+    string_arg,
+    nonneg_int_arg
+  };
 
 /* All the options that the HTML driver supports. */
 static struct outp_option option_tab[] =
-{
-  /* *INDENT-OFF* */
-  {"output-file",              1,              0},
-  {"prologue-file",            string_arg,     0},
-  {"", 0, 0},
-  /* *INDENT-ON* */
-};
-static struct outp_option_info option_info;
+  {
+    {"output-file",            string_arg,     0},
+    {NULL, 0, 0},
+  };
 
-static void
-html_option (struct outp_driver *this, const char *key, const struct string 
*val)
+static bool
+handle_option (struct outp_driver *this,
+               const char *key, const struct string *val)
 {
   struct html_driver_ext *x = this->ext;
-  int cat, subcat;
+  int subcat;
 
-  cat = outp_match_keyword (key, option_tab, &option_info, &subcat);
-  switch (cat)
+  switch (outp_match_keyword (key, option_tab, &subcat))
     {
-    case 0:
-      msg (SE, _("Unknown configuration parameter `%s' for HTML device "
-          "driver."), key);
-      break;
-    case 1:
-      free (x->file.filename);
-      x->file.filename = xstrdup (ds_c_str (val));
+    case -1:
+      error (0, 0,
+             _("unknown configuration parameter `%s' for HTML device driver"),
+             key);
       break;
     case string_arg:
-      {
-       char **dest;
-       switch (subcat)
-         {
-         case 0:
-           dest = &x->prologue_fn;
-           break;
-         default:
-           assert (0);
-            abort ();
-         }
-       if (*dest)
-         free (*dest);
-       *dest = xstrdup (ds_c_str (val));
-      }
+      free (x->file_name);
+      x->file_name = xstrdup (ds_c_str (val));
       break;
     default:
-      assert (0);
+      abort ();
     }
-}
-
-/* Variables for the prologue. */
-struct html_variable
-  {
-    const char *key;
-    const char *value;
-  };
   
-static struct html_variable *html_var_tab;
-
-/* Searches html_var_tab for a html_variable with key KEY, and returns
-   the associated value. */
-static const char *
-html_get_var (const char *key)
-{
-  struct html_variable *v;
-
-  for (v = html_var_tab; v->key; v++)
-    if (!strcmp (key, v->key))
-      return v->value;
-  return NULL;
-}
-
-/* Writes the HTML prologue to file F. */
-static int
-postopen (struct file_ext *f)
-{
-  static struct html_variable dict[] =
-    {
-      {"generator", 0},
-      {"date", 0},
-      {"user", 0},
-      {"host", 0},
-      {"title", 0},
-      {"subtitle", 0},
-      {0, 0},
-    };
-  char login[128], host[128];
-  time_t curtime;
-  struct tm *loctime;
-
-  struct outp_driver *this = f->param;
-  struct html_driver_ext *x = this->ext;
-
-  char *prologue_fn = fn_search_path (x->prologue_fn, config_path, NULL);
-  FILE *prologue_file;
-
-  char *buf = NULL;
-  size_t buf_size = 0;
-
-  if (prologue_fn == NULL)
-    {
-      msg (IE, _("Cannot find HTML prologue.  The use of `-vv' "
-                "on the command line is suggested as a debugging aid."));
-      return 0;
-    }
-
-  msg (VM (1), _("%s: %s: Opening HTML prologue..."), this->name, prologue_fn);
-  prologue_file = fopen (prologue_fn, "rb");
-  if (prologue_file == NULL)
-    {
-      fclose (prologue_file);
-      free (prologue_fn);
-      msg (IE, "%s: %s", prologue_fn, strerror (errno));
-      goto error;
-    }
-
-  dict[0].value = version;
-
-  curtime = time (NULL);
-  loctime = localtime (&curtime);
-  dict[1].value = asctime (loctime);
-  {
-    char *cp = strchr (dict[1].value, '\n');
-    if (cp)
-      *cp = 0;
-  }
-
-  if (getenv ("LOGNAME") != NULL)
-    str_copy_rpad (login, sizeof login, getenv ("LOGNAME"));
-  else if (getlogin_r (login, sizeof login))
-    strcpy (login, _("nobody"));
-  dict[2].value = login;
-
-#ifdef HAVE_UNISTD_H
-  if (gethostname (host, 128) == -1)
-    {
-      if (errno == ENAMETOOLONG)
-       host[127] = 0;
-      else
-       strcpy (host, _("nowhere"));
-    }
-#else
-  strcpy (host, _("nowhere"));
-#endif
-  dict[3].value = host;
-
-  dict[4].value = outp_title ? outp_title : "";
-  dict[5].value = outp_subtitle ? outp_subtitle : "";
-
-  html_var_tab = dict;
-  while (-1 != getline (&buf, &buf_size, prologue_file))
-    {
-
-      int len;
-
-      if (strstr (buf, "!!!"))
-       continue;
-      
-      {
-       char *cp = strstr (buf, "!title");
-       if (cp)
-         {
-           if (outp_title == NULL)
-             continue;
-           else
-             *cp = '\0';
-         }
-      }
-      
-      {
-       char *cp = strstr (buf, "!subtitle");
-       if (cp)
-         {
-           if (outp_subtitle == NULL)
-             continue;
-           else
-             *cp = '\0';
-         }
-      }
-      
-      /* PORTME: Line terminator. */
-      struct string line;
-      ds_create(&line, buf);
-      fn_interp_vars(&line, html_get_var);
-      len = ds_length(&line);
-      fwrite (ds_c_str(&line), len, 1, f->file);
-      if (ds_c_str(&line)[len - 1] != '\n')
-       putc ('\n', f->file);
-      ds_destroy(&line);
-    }
-  if (ferror (f->file))
-    msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno));
-  fclose (prologue_file);
-
-  free (prologue_fn);
-  free (buf);
-
-  if (ferror (f->file))
-    goto error;
-
-  msg (VM (2), _("%s: HTML prologue read successfully."), this->name);
-  return 1;
-
-error:
-  msg (VM (1), _("%s: Error reading HTML prologue."), this->name);
-  return 0;
-}
-
-/* Writes the HTML epilogue to file F. */
-static int
-preclose (struct file_ext *f)
-{
-  fprintf (f->file,
-          "</BODY>\n"
-          "</HTML>\n"
-          "<!-- end of file -->\n");
-
-  if (ferror (f->file))
-    return 0;
-  return 1;
-}
-
-static int
-html_open_page (struct outp_driver *this)
-{
-  struct html_driver_ext *x = this->ext;
-
-  assert (this->driver_open && this->page_open == 0);
-  x->sequence_no++;
-  if (!fn_open_ext (&x->file))
-    {
-      if (errno)
-       msg (ME, _("HTML output driver: %s: %s"), x->file.filename,
-            strerror (errno));
-      return 0;
-    }
-
-  if (!ferror (x->file.file))
-    this->page_open = 1;
-  return !ferror (x->file.file);
-}
-
-static int
-html_close_page (struct outp_driver *this)
-{
-  struct html_driver_ext *x = this->ext;
-
-  assert (this->driver_open && this->page_open);
-  this->page_open = 0;
-  return !ferror (x->file.file);
+  return true;
 }
 
 static void output_tab_table (struct outp_driver *, struct tab_table *);
@@ -407,14 +184,7 @@
   extern struct som_table_class tab_table_class;
   struct html_driver_ext *x = this->ext;
   
-  assert (this->driver_open && this->page_open);
-  if (x->sequence_no == 0 && !html_open_page (this))
-    {
-      msg (ME, _("Cannot open first page on HTML device %s."), this->name);
-      return;
-    }
-
-  assert ( s->class == &tab_table_class ) ;
+  assert (s->class == &tab_table_class ) ;
 
   switch (s->type) 
     {
@@ -422,49 +192,76 @@
       output_tab_table ( this, (struct tab_table *) s->ext);
       break;
     case SOM_CHART:
-      link_image( &x->file, ((struct chart *)s->ext)->filename);
+      link_image (x->file, ((struct chart *)s->ext)->filename);
       break;
     default:
-      assert(0);
-      break;
+      abort ();
     }
-
 }
 
-/* Write string S of length LEN to file F, escaping characters as
-   necessary for HTML. */
+/* Write LENGTH characters in TEXT to file F, escaping characters
+   as necessary for HTML.  Spaces are replaced by SPACE, which
+   should be " " or "&nbsp;". */
 static void
-escape_string (FILE *f, char *s, int len)
+escape_string (FILE *file,
+               const char *text, size_t length,
+               const char *space)
+{
+  while (length-- > 0)
+    {
+      char c = *text++;
+      switch (c)
+        {
+        case '&':
+          fputs ("&amp;", file);
+          break;
+        case '<':
+          fputs ("&lt;", file);
+          break;
+        case '>':
+          fputs ("&gt;", file);
+          break;
+        case ' ':
+          fputs (space, file);
+          break;
+        default:
+          putc (c, file);
+          break;
+        }
+    }
+}
+  
+/* Outputs content for a cell with options OPTS and contents
+   TEXT. */
+void
+html_put_cell_contents (struct outp_driver *this,
+                        unsigned int opts, struct fixed_string *text)
 {
-  char *ep = &s[len];
-  char *bp, *cp;
+  struct html_driver_ext *x = this->ext;
 
-  for (bp = cp = s; bp < ep; bp = cp)
+  if (!(opts & TAB_EMPTY)) 
     {
-      while (cp < ep && *cp != '&' && *cp != '<' && *cp != '>' && *cp)
-       cp++;
-      if (cp > bp)
-       fwrite (bp, 1, cp - bp, f);
-      if (cp < ep)
-       switch (*cp++)
-         {
-         case '&':
-           fputs ("&amp;", f);
-           break;
-         case '<':
-           fputs ("&lt;", f);
-           break;
-         case '>':
-           fputs ("&gt;", f);
-           break;
-         case 0:
-           break;
-         default:
-           assert (0);
-         }
+      if (opts & TAB_EMPH)
+        fputs ("<EM>", x->file);
+      if (opts & TAB_FIX) 
+        {
+          fputs ("<TT>", x->file);
+          escape_string (x->file, ls_c_str (text), ls_length (text), "&nbsp;");
+          fputs ("</TT>", x->file);
+        }
+      else 
+        {
+          size_t initial_spaces = strspn (ls_c_str (text), " \t");
+          escape_string (x->file,
+                         ls_c_str (text) + initial_spaces,
+                         ls_length (text) - initial_spaces,
+                         " "); 
+        }
+      if (opts & TAB_EMPH)
+        fputs ("</EM>", x->file);
     }
 }
-  
+
 /* Write table T to THIS output driver. */
 static void
 output_tab_table (struct outp_driver *this, struct tab_table *t)
@@ -473,22 +270,21 @@
   
   if (t->nr == 1 && t->nc == 1)
     {
-      fputs ("<P>", x->file.file);
-      if (!ls_empty_p (t->cc))
-       escape_string (x->file.file, ls_c_str (t->cc), ls_length (t->cc));
-      fputs ("</P>\n", x->file.file);
+      fputs ("<P>", x->file);
+      html_put_cell_contents (this, t->ct[0], t->cc);
+      fputs ("</P>\n", x->file);
       
       return;
     }
 
-  fputs ("<TABLE BORDER=1>\n", x->file.file);
+  fputs ("<TABLE BORDER=1>\n", x->file);
   
   if (!ls_empty_p (&t->title))
     {
-      fprintf (x->file.file, "  <TR>\n    <TH COLSPAN=%d>", t->nc);
-      escape_string (x->file.file, ls_c_str (&t->title),
-                    ls_length (&t->title));
-      fputs ("</TH>\n  </TR>\n", x->file.file);
+      fprintf (x->file, "  <CAPTION>");
+      escape_string (x->file, ls_c_str (&t->title), ls_length (&t->title),
+                     " ");
+      fputs ("</CAPTION>\n", x->file);
     }
   
   {
@@ -499,17 +295,15 @@
       {
        int c;
        
-       fputs ("  <TR>\n", x->file.file);
+       fputs ("  <TR>\n", x->file);
        for (c = 0; c < t->nc; c++, ct++)
          {
             struct fixed_string *cc;
-           int tag;
-           char header[128];
-           char *cp;
+            const char *tag;
             struct tab_joined_cell *j = NULL;
 
             cc = t->cc + c + r * t->nc;
-           if (*ct & TAB_JOIN) 
+           if (*ct & TAB_JOIN)
               {
                 j = (struct tab_joined_cell *) ls_c_str (cc);
                 cc = &j->contents;
@@ -517,62 +311,34 @@
                   continue; 
               }
 
-           if (r < t->t || r >= t->nr - t->b
-               || c < t->l || c >= t->nc - t->r)
-             tag = 'H';
-           else
-             tag = 'D';
-           cp = stpcpy (header, "    <T");
-           *cp++ = tag;
-           
-           switch (*ct & TAB_ALIGN_MASK)
-             {
-             case TAB_RIGHT:
-               cp = stpcpy (cp, " ALIGN=RIGHT");
-               break;
-             case TAB_LEFT:
-               break;
-             case TAB_CENTER:
-               cp = stpcpy (cp, " ALIGN=CENTER");
-               break;
-             default:
-               assert (0);
-             }
-
+            /* Output <TD> or <TH> tag. */
+            tag = (r < t->t || r >= t->nr - t->b
+                   || c < t->l || c >= t->nc - t->r) ? "TH" : "TD";
+            fprintf (x->file, "    <%s ALIGN=%s",
+                     tag,
+                     (*ct & TAB_ALIGN_MASK) == TAB_LEFT ? "LEFT"
+                     : (*ct & TAB_ALIGN_MASK) == TAB_RIGHT ? "RIGHT"
+                     : "CENTER");
            if (*ct & TAB_JOIN)
              {
                if (j->x2 - j->x1 > 1)
-                 cp = spprintf (cp, " COLSPAN=%d", j->x2 - j->x1);
+                 fprintf (x->file, " COLSPAN=%d", j->x2 - j->x1);
                if (j->y2 - j->y1 > 1)
-                 cp = spprintf (cp, " ROWSPAN=%d", j->y2 - j->y1);
-
-                cc = &j->contents;
+                 fprintf (x->file, " ROWSPAN=%d", j->y2 - j->y1);
              }
-           
-           strcpy (cp, ">");
-           fputs (header, x->file.file);
-           
-           if ( ! (*ct & TAB_EMPTY)  ) 
-             {
-               char *s = ls_c_str (cc);
-               size_t l = ls_length (cc);
+           putc ('>', x->file);
 
-               while (l && isspace ((unsigned char) *s))
-                 {
-                   l--;
-                   s++;
-                 }
-             
-               escape_string (x->file.file, s, l);
-             }
+            /* Output cell contents. */
+            html_put_cell_contents (this, *ct, cc);
 
-           fprintf (x->file.file, "</T%c>\n", tag);
+            /* Output </TH> or </TD>. */
+           fprintf (x->file, "</%s>\n", tag);
          }
-       fputs ("  </TR>\n", x->file.file);
+       fputs ("  </TR>\n", x->file);
       }
   }
              
-  fputs ("</TABLE>\n\n", x->file.file);
+  fputs ("</TABLE>\n\n", x->file);
 }
 
 static void
@@ -602,45 +368,21 @@
 
 /* HTML driver class. */
 struct outp_class html_class =
-{
-  "html",
-  0xfaeb,
-  1,
-
-  html_open_global,
-  html_close_global,
-  NULL,
-
-  html_preopen_driver,
-  html_option,
-  html_postopen_driver,
-  html_close_driver,
-
-  html_open_page,
-  html_close_page,
-
-  html_submit,
-
-  NULL,
-  NULL,
-  NULL,
-
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
+  {
+    "html",
+    1,
 
-  html_initialise_chart,
-  html_finalise_chart
+    html_open_driver,
+    html_close_driver,
 
-};
+    NULL,
+    NULL,
+
+    html_submit,
+
+    NULL,
+    NULL,
+    NULL,
+    html_initialise_chart,
+    html_finalise_chart
+  };
Index: pspp/src/output/htmlP.h
diff -u pspp/src/output/htmlP.h:1.2 pspp/src/output/htmlP.h:1.3
--- pspp/src/output/htmlP.h:1.2 Wed Mar 15 03:29:11 2006
+++ pspp/src/output/htmlP.h     Mon Apr  3 20:07:54 2006
@@ -25,14 +25,14 @@
 /* HTML output driver extension record. */
 struct html_driver_ext
   {
-    /* User parameters. */
-    char *prologue_fn;         /* Prologue's filename relative to font dir. */
-
-    /* Internal state. */
-    struct file_ext file;      /* Output file. */
-    int sequence_no;           /* Sequence number. */
+    char *file_name;
+    FILE *file;
   };
 
 extern struct outp_class html_class;
 
+struct outp_driver;
+void html_put_cell_contents (struct outp_driver *this,
+                             unsigned int opts, struct fixed_string *text);
+
 #endif /* !htmlP_h */
Index: pspp/src/output/manager.c
diff -u pspp/src/output/manager.c:1.3 pspp/src/output/manager.c:1.4
--- pspp/src/output/manager.c:1.3       Wed Mar 15 03:29:11 2006
+++ pspp/src/output/manager.c   Mon Apr  3 20:07:54 2006
@@ -147,13 +147,7 @@
   bool fits_width, fits_length;
   d = driver;
 
-  assert (d->driver_open);
-  if (!d->page_open && !d->class->open_page (d))
-    {
-      d->device = OUTP_DEV_DISABLED;
-      return;
-    }
-  
+  outp_open_page (d);
   if (d->class->special || entity->type == SOM_CHART)
     {
       driver->class->submit (d, entity);
Index: pspp/src/output/output.c
diff -u pspp/src/output/output.c:1.7 pspp/src/output/output.c:1.8
--- pspp/src/output/output.c:1.7        Fri Mar 31 00:30:22 2006
+++ pspp/src/output/output.c    Mon Apr  3 20:07:54 2006
@@ -68,6 +68,13 @@
 static struct outp_defn *outp_macros;
 static struct outp_names *outp_configure_vec;
 
+/* A list of driver classes. */
+struct outp_driver_class_list
+  {
+    struct outp_class *class;
+    struct outp_driver_class_list *next;
+  };
+
 struct outp_driver_class_list *outp_class_list;
 struct outp_driver *outp_driver_list;
 
@@ -80,8 +87,8 @@
 
 static void destroy_driver (struct outp_driver *);
 static void configure_driver_line (struct string *);
-static void configure_driver (const char *, const char *,
-                              const char *, const char *);
+static void configure_driver (const struct string *, const struct string *,
+                              const struct string *, const struct string *);
 
 /* Add a class to the class list. */
 static void
@@ -90,7 +97,6 @@
   struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
 
   new_list->class = class;
-  new_list->ref_count = 0;
 
   if (!outp_class_list)
     {
@@ -221,13 +227,11 @@
 {
   extern struct outp_class ascii_class;
   extern struct outp_class postscript_class;
-  extern struct outp_class epsf_class;
   extern struct outp_class html_class;
 
   char def[] = "default";
 
   add_class (&html_class);
-  add_class (&epsf_class);
   add_class (&postscript_class);
   add_class (&ascii_class);
 
@@ -252,11 +256,15 @@
 static void
 init_default_drivers (void) 
 {
+  struct string s;
+
   msg (MM, _("Using default output driver configuration."));
-  configure_driver ("list-ascii", "ascii", "listing",
-                    "length=66 width=79 char-set=ascii "
-                    "output-file=\"pspp.list\" "
-                    "bold-on=\"\" italic-on=\"\" bold-italic-on=\"\"");
+
+  ds_create (&s,
+             "list:ascii:listing:"
+             "length=66 width=79 output-file=\"pspp.list\"");
+  configure_driver_line (&s);
+  ds_destroy (&s);
 }
 
 /* Reads the initialization file; initializes
@@ -290,7 +298,6 @@
       goto exit;
     }
 
-  msg (VM (1), _("%s: Opening device description file..."), init_fn);
   f = fopen (init_fn, "r");
   if (f == NULL)
     {
@@ -344,7 +351,6 @@
 
   if (result) 
     {
-      msg (VM (2), _("Device definition file read successfully."));
       if (outp_driver_list == NULL) 
         msg (MW, _("No output drivers are active.")); 
     }
@@ -612,16 +618,19 @@
   return 1;
 }
 
-/* Applies the user-specified options in string S to output driver D
-   (at configuration time). */
-static void
-parse_options (const char *s, struct outp_driver * d)
+bool
+outp_parse_options (const char *options,
+                    bool (*callback) (struct outp_driver *, const char *key,
+                                      const struct string *value),
+                    struct outp_driver *driver)
 {
-  prog = s;
+  bool ok = true;
+
+  prog = options;
   op_token = -1;
 
   ds_init (&op_tokstr, 64);
-  while (tokener ())
+  while (ok && tokener ())
     {
       char key[65];
 
@@ -647,9 +656,11 @@
          msg (IS, _("Syntax error in options (value expected after `=')."));
          break;
        }
-      d->class->option (d, key, &op_tokstr);
+      ok = callback (driver, key, &op_tokstr);
     }
   ds_destroy (&op_tokstr);
+
+  return ok;
 }
 
 /* Find the driver in outp_driver_list with name NAME. */
@@ -669,97 +680,79 @@
    Adds a driver to outp_driver_list pursuant to the specification
    provided.  */
 static void
-configure_driver (const char *driver_name, const char *class_name,
-                  const char *device_type, const char *options)
+configure_driver (const struct string *driver_name,
+                  const struct string *class_name,
+                  const struct string *device_type,
+                  const struct string *options)
 {
-  struct outp_driver *d = NULL, *iter;
-  struct outp_driver_class_list *c = NULL;
-
-  d = xmalloc (sizeof *d);
-  d->class = NULL;
-  d->name = xstrdup (driver_name);
-  d->driver_open = 0;
-  d->page_open = 0;
-  d->next = d->prev = NULL;
-  d->device = OUTP_DEV_NONE;
-  d->ext = NULL;
+  struct outp_driver *d, *iter;
+  struct outp_driver_class_list *c;
+  int device;
 
+  /* Find class. */
   for (c = outp_class_list; c; c = c->next)
-    if (!strcmp (c->class->name, class_name))
+    if (!strcmp (c->class->name, ds_c_str (class_name)))
       break;
-  if (!c)
+  if (c == NULL)
     {
-      msg (IS, _("Unknown output driver class `%s'."), class_name);
-      goto error;
+      msg (IS, _("Unknown output driver class `%s'."), ds_c_str (class_name));
+      return;
     }
   
-  d->class = c->class;
-  if (!c->ref_count && !d->class->open_global (d->class))
-    {
-      msg (IS, _("Can't initialize output driver class `%s'."),
-          d->class->name);
-      goto error;
-    }
-  c->ref_count++;
-  if (!d->class->preopen_driver (d))
-    {
-      msg (IS, _("Can't initialize output driver `%s' of class `%s'."),
-          d->name, d->class->name);
-      goto error;
-    }
-
-  /* Device types. */
+  /* Parse device type. */
+  device = 0;
   if (device_type != NULL)
     {
-      char *copy = xstrdup (device_type);
-      char *sp, *type;
+      struct string token = DS_INITIALIZER;
+      size_t save_idx = 0;
 
-      for (type = strtok_r (copy, " \t\r\v", &sp); type;
-          type = strtok_r (NULL, " \t\r\v", &sp))
-       {
+      while (ds_tokenize (device_type, &token, " \t\r\v", &save_idx)) 
+        {
+          const char *type = ds_c_str (&token);
          if (!strcmp (type, "listing"))
-           d->device |= OUTP_DEV_LISTING;
+           device |= OUTP_DEV_LISTING;
          else if (!strcmp (type, "screen"))
-           d->device |= OUTP_DEV_SCREEN;
+           device |= OUTP_DEV_SCREEN;
          else if (!strcmp (type, "printer"))
-           d->device |= OUTP_DEV_PRINTER;
+           device |= OUTP_DEV_PRINTER;
          else
-           {
-             msg (IS, _("Unknown device type `%s'."), type);
-              free (copy);
-             goto error;
-           }
+            msg (IS, _("Unknown device type `%s'."), type);
        }
-      free (copy);
+      ds_destroy (&token);
     }
-  
-  /* Options. */
-  if (options != NULL)
-    parse_options (options, d);
-  if (!d->class->postopen_driver (d))
-    {
-      msg (IS, _("Can't complete initialization of output driver `%s' of "
-          "class `%s'."), d->name, d->class->name);
-      goto error;
+
+  /* Open the device. */
+  d = xmalloc (sizeof *d);
+  d->next = d->prev = NULL;
+  d->class = c->class;
+  d->name = xstrdup (ds_c_str (driver_name));
+  d->page_open = false;
+  d->device = OUTP_DEV_NONE;
+  d->cp_x = d->cp_y = 0;
+  d->ext = NULL;
+  d->prc = NULL;
+
+  /* Open driver. */
+  if (!d->class->open_driver (d, ds_c_str (options)))
+    {
+      msg (IS, _("Can't initialize output driver `%s' of class `%s'."),
+          d->name, d->class->name);
+      free (d->name);
+      free (d);
+      return;
     }
 
   /* Find like-named driver and delete. */
   iter = find_driver (d->name);
-  if (iter)
+  if (iter != NULL)
     destroy_driver (iter);
 
   /* Add to list. */
   d->next = outp_driver_list;
   d->prev = NULL;
-  if (outp_driver_list)
+  if (outp_driver_list != NULL)
     outp_driver_list->prev = d;
   outp_driver_list = d;
-  return;
-
-error:
-  if (d)
-    destroy_driver (d);
-  return;
 }
 
 /* String LINE is in format:
@@ -770,12 +763,12 @@
 configure_driver_line (struct string *line)
 {
   struct string tokens[4];
-  int save_idx;
+  size_t save_idx;
   size_t i;
 
   fn_interp_vars (line, find_defn_value);
 
-  save_idx = -1;
+  save_idx = 0;
   for (i = 0; i < 4; i++) 
     {
       struct string *token = &tokens[i];
@@ -785,8 +778,7 @@
     }
 
   if (!ds_is_empty (&tokens[0]) && !ds_is_empty (&tokens[1]))
-    configure_driver (ds_c_str (&tokens[0]), ds_c_str (&tokens[1]), 
-                      ds_c_str (&tokens[2]), ds_c_str (&tokens[3]));
+    configure_driver (&tokens[0], &tokens[1], &tokens[2], &tokens[3]);
   else
     msg (IS, _("Driver definition line missing driver name or class name"));
 
@@ -798,27 +790,17 @@
 static void
 destroy_driver (struct outp_driver *d)
 {
-  if (d->page_open)
-    d->class->close_page (d);
+  outp_close_page (d);
   if (d->class)
     {
       struct outp_driver_class_list *c;
 
-      if (d->driver_open)
-       d->class->close_driver (d);
+      d->class->close_driver (d);
 
       for (c = outp_class_list; c; c = c->next)
        if (c->class == d->class)
          break;
       assert (c != NULL);
-      
-      c->ref_count--;
-      if (c->ref_count == 0)
-       {
-         if (!d->class->close_global (d->class))
-           msg (IS, _("Can't deinitialize output driver class `%s'."),
-                d->class->name);
-       }
     }
   free (d->name);
 
@@ -831,82 +813,20 @@
     outp_driver_list = d->next;
 }
 
-static int
-option_cmp (const void *a, const void *b)
-{
-  const struct outp_option *o1 = a;
-  const struct outp_option *o2 = b;
-  return strcmp (o1->keyword, o2->keyword);
-}
-
-/* Tries to match S as one of the keywords in TAB, with corresponding
-   information structure INFO.  Returns category code or 0 on failure;
-   if category code is negative then stores subcategory in *SUBCAT. */
+/* Tries to match S as one of the keywords in TAB, with
+   corresponding information structure INFO.  Returns category
+   code and stores subcategory in *SUBCAT on success.  Returns -1
+   on failure. */
 int
-outp_match_keyword (const char *s, struct outp_option *tab,
-                   struct outp_option_info *info, int *subcat)
+outp_match_keyword (const char *s, struct outp_option *tab, int *subcat)
 {
-  char *cp;
-  struct outp_option *oip;
-
-  /* Form hash table. */
-  if (NULL == info->initial)
-    {
-      /* Count items. */
-      int count, i;
-      char s[256], *cp;
-      struct outp_option *ptr[255], **oip;
-
-      for (count = 0; tab[count].keyword[0]; count++)
-       ;
-
-      /* Sort items. */
-      qsort (tab, count, sizeof *tab, option_cmp);
-
-      cp = s;
-      oip = ptr;
-      *cp = tab[0].keyword[0];
-      *oip++ = &tab[0];
-      for (i = 0; i < count; i++)
-       if (tab[i].keyword[0] != *cp)
-         {
-           *++cp = tab[i].keyword[0];
-           *oip++ = &tab[i];
-         }
-      *++cp = 0;
-
-      info->initial = xstrdup (s);
-      info->options = xnmalloc (cp - s, sizeof *info->options);
-      memcpy (info->options, ptr, sizeof *info->options * (cp - s));
-    }
-
-  cp = info->initial;
-  oip = *info->options;
-
-  if (s[0] == 0)
-    return 0;
-  cp = strchr (info->initial, s[0]);
-  if (!cp)
-    return 0;
-#if 0
-  printf (_("Trying to find keyword `%s'...\n"), s);
-#endif
-  oip = info->options[cp - info->initial];
-  while (oip->keyword[0] == s[0])
-    {
-#if 0
-      printf ("- %s\n", oip->keyword);
-#endif
-      if (!strcmp (s, oip->keyword))
-       {
-         if (oip->cat < 0)
-           *subcat = oip->subcat;
-         return oip->cat;
-       }
-      oip++;
-    }
-
-  return 0;
+  for (; tab->keyword != NULL; tab++)
+    if (!strcmp (s, tab->keyword))
+      {
+        *subcat = tab->subcat;
+        return tab->cat;
+      }
+  return -1;
 }
 
 /* Encapsulate two characters in a single int. */
@@ -1141,7 +1061,6 @@
       goto exit;
     }
 
-  msg (VM (1), _("%s: Opening paper size definition file..."), pprsz_fn);
   f = fopen (pprsz_fn, "r");
   if (!f)
     {
@@ -1211,9 +1130,7 @@
   if (free_it)
     free (size);
 
-  if (result)
-    msg (VM (2), _("Paper size definition file read successfully."));
-  else
+  if (!result)
     msg (VM (1), _("Error reading paper size definition file."));
   
   return result;
@@ -1238,9 +1155,7 @@
        d = d->next;
 
       if (d == NULL
-         || (d->driver_open
-             && (d->device == 0
-                 || (d->device & disabled_devices) != d->device)))
+         || (d->device == 0 || (d->device & disabled_devices) != d->device))
        break;
     }
 
@@ -1258,40 +1173,57 @@
     disabled_devices |= device;
 }
 
-/* Ejects the paper on device D, if the page is not blank. */
-int
-outp_eject_page (struct outp_driver *d)
+/* Opens a page on driver D (if one is not open). */
+void
+outp_open_page (struct outp_driver *d) 
 {
-  if (d->page_open == 0)
-    return 1;
-  
-  if (d->cp_y != 0)
+  if (!d->page_open) 
     {
       d->cp_x = d->cp_y = 0;
 
-      if (d->class->close_page (d) == 0)
-       msg (ME, _("Error closing page on %s device of %s class."),
-            d->name, d->class->name);
-      if (d->class->open_page (d) == 0)
-       {
-         msg (ME, _("Error opening page on %s device of %s class."),
-              d->name, d->class->name);
-         return 0;
-       }
+      d->page_open = true;
+      if (d->class->open_page != NULL)
+        d->class->open_page (d);
+    }
+}
+
+/* Closes the page on driver D (if one is open). */
+void
+outp_close_page (struct outp_driver *d) 
+{
+  if (d->page_open) 
+    {
+      if (d->class->close_page != NULL)
+        d->class->close_page (d);
+      d->page_open = false;
+    }
+}
+
+/* Ejects the paper on device D, if a page is open and is not
+   blank. */
+void
+outp_eject_page (struct outp_driver *d)
+{
+  if (d->page_open && d->cp_y != 0)
+    {
+      outp_close_page (d);
+      outp_open_page (d);
     }
-  return 1;
 }
 
 /* Returns the width of string S, in device units, when output on
    device D. */
 int
-outp_string_width (struct outp_driver *d, const char *s)
+outp_string_width (struct outp_driver *d, const char *s, enum outp_font font)
 {
   struct outp_text text;
+  int width;
+  
+  text.font = font;
+  text.justification = OUTP_LEFT;
+  ls_init (&text.string, (char *) s, strlen (s));
+  text.h = text.v = INT_MAX;
+  d->class->text_metrics (d, &text, &width, NULL);
 
-  text.options = OUTP_T_JUST_LEFT;
-  ls_init (&text.s, (char *) s, strlen (s));
-  d->class->text_metrics (d, &text);
-
-  return text.h;
+  return width;
 }
Index: pspp/src/output/output.h
diff -u pspp/src/output/output.h:1.2 pspp/src/output/output.h:1.3
--- pspp/src/output/output.h:1.2        Wed Mar 15 03:29:11 2006
+++ pspp/src/output/output.h    Mon Apr  3 20:07:54 2006
@@ -25,91 +25,39 @@
 #include <libpspp/str.h>
 
 
-/* A rectangle. */
-struct rect
+/* Line styles.  */
+enum outp_line_style
   {
-    int x1, y1;                        /* Upper left. */
-    int x2, y2;                        /* Lower right, not part of the 
rectangle. */
+    OUTP_L_NONE,               /* No line. */
+    OUTP_L_SINGLE,             /* Single line. */
+    OUTP_L_DOUBLE,             /* Double line. */
+    OUTP_L_COUNT
   };
 
-/* Color descriptor. */
-struct color
+/* Text justification. */
+enum outp_justification
   {
-    int flags;                 /* 0=normal, 1=transparent (ignore r,g,b). */
-    int r;                     /* Red component, 0-65535. */
-    int g;                     /* Green component, 0-65535. */
-    int b;                     /* Blue component, 0-65535. */
+    OUTP_RIGHT,                 /* Right justification. */
+    OUTP_LEFT,                  /* Left justification. */
+    OUTP_CENTER,                /* Center justification. */
   };
 
-/* Mount positions for the four basic fonts.  Do not change the values. */
-enum
-  {
-    OUTP_F_R,                  /* Roman font. */
-    OUTP_F_I,                  /* Italic font. */
-    OUTP_F_B,                  /* Bold font. */
-    OUTP_F_BI                  /* Bold-italic font. */
-  };
-
-/* Line styles.  These must match:
-   som.h:SLIN_*
-   ascii.c:ascii_line_*() 
-   postscript.c:ps_line_*() */
-enum
-  {
-    OUTP_L_NONE = 0,           /* No line. */
-    OUTP_L_SINGLE = 1,         /* Single line. */
-    OUTP_L_DOUBLE = 2,         /* Double line. */
-    OUTP_L_SPECIAL = 3,                /* Special line of driver-defined 
style. */
-
-    OUTP_L_COUNT               /* Number of line styles. */
-  };
-
-/* Contains a line style for each part of an intersection. */
-struct outp_styles
-  {
-    int l;                     /* left */
-    int t;                     /* top */
-    int r;                     /* right */
-    int b;                     /* bottom */
-  };
-
-/* Text display options. */
-enum
+enum outp_font 
   {
-    OUTP_T_NONE = 0,
-
-    /* Must match tab.h:TAB_*. */
-    OUTP_T_JUST_MASK = 00003,  /* Justification mask. */
-    OUTP_T_JUST_RIGHT = 00000, /* Right justification. */
-    OUTP_T_JUST_LEFT = 00001,  /* Left justification. */
-    OUTP_T_JUST_CENTER = 00002,        /* Center justification. */
-
-    OUTP_T_HORZ = 00010,       /* Horizontal size is specified. */
-    OUTP_T_VERT = 00020,       /* (Max) vertical size is specified. */
-
-    OUTP_T_0 = 00140,          /* Normal orientation. */
-    OUTP_T_CC90 = 00040,       /* 90 degrees counterclockwise. */
-    OUTP_T_CC180 = 00100,      /* 180 degrees counterclockwise. */
-    OUTP_T_CC270 = 00140,      /* 270 degrees counterclockwise. */
-    OUTP_T_C90 = 00140,                /* 90 degrees clockwise. */
-    OUTP_T_C180 = 00100,       /* 180 degrees clockwise. */
-    OUTP_T_C270 = 00040,       /* 270 degrees clockwise. */
-
-    /* Internal use by drivers only. */
-    OUTP_T_INTERNAL_DRAW = 01000       /* 1=Draw the text, 0=Metrics only. */
+    OUTP_FIXED,                 /* Fixed-width font. */
+    OUTP_PROPORTIONAL,          /* Proportional font. */
+    OUTP_EMPHASIS,              /* Proportional font used for emphasis. */
+    OUTP_FONT_CNT               /* Number of fonts. */
   };
 
 /* Describes text output. */
 struct outp_text
   {
-    /* Public. */
-    int options;               /* What is specified. */
-    struct fixed_string s;     /* String. */
+    enum outp_font font;
+    enum outp_justification justification;
+    struct fixed_string string;
     int h, v;                  /* Horizontal, vertical size. */
     int x, y;                  /* Position. */
-
-    /* Internal use only. */
-    int w, l;                  /* Width, length. */
   };
 
 struct som_entity;
@@ -119,61 +67,27 @@
 /* Defines a class of output driver. */
 struct outp_class
   {
-    /* Basic class information. */
     const char *name;          /* Name of this driver class. */
-    int magic;                 /* Driver-specific constant. */
     int special;               /* Boolean value. */
 
-    /* Static member functions. */
-    int (*open_global) (struct outp_class *);
-    int (*close_global) (struct outp_class *);
-    int *(*font_sizes) (struct outp_class *, int *n_valid_sizes);
-
-    /* Virtual member functions. */
-    int (*preopen_driver) (struct outp_driver *);
-    void (*option) (struct outp_driver *, const char *key,
-                   const struct string *value);
-    int (*postopen_driver) (struct outp_driver *);
-    int (*close_driver) (struct outp_driver *);
+    bool (*open_driver) (struct outp_driver *, const char *options);
+    bool (*close_driver) (struct outp_driver *);
 
-    int (*open_page) (struct outp_driver *);
-    int (*close_page) (struct outp_driver *);
+    void (*open_page) (struct outp_driver *);
+    void (*close_page) (struct outp_driver *);
 
-    /* special != 0: Used to submit entities for output. */
+    /* special != 0 only. */
     void (*submit) (struct outp_driver *, struct som_entity *);
     
-    /* special != 0: Methods below need not be defined. */
-    
-    /* Line methods. */
-    void (*line_horz) (struct outp_driver *, const struct rect *,
-                      const struct color *, int style);
-    void (*line_vert) (struct outp_driver *, const struct rect *,
-                      const struct color *, int style);
-    void (*line_intersection) (struct outp_driver *, const struct rect *,
-                              const struct color *,
-                              const struct outp_styles *style);
-
-    /* Drawing methods. */
-    void (*box) (struct outp_driver *, const struct rect *,
-                const struct color *bord, const struct color *fill);
-    void (*polyline_begin) (struct outp_driver *, const struct color *);
-    void (*polyline_point) (struct outp_driver *, int, int);
-    void (*polyline_end) (struct outp_driver *);
-
-    /* Text methods. */
-    void (*text_set_font_by_name) (struct outp_driver *, const char *s);
-    void (*text_set_font_by_position) (struct outp_driver *, int);
-    void (*text_set_font_family) (struct outp_driver *, const char *s);
-    const char *(*text_get_font_name) (struct outp_driver *);
-    const char *(*text_get_font_family) (struct outp_driver *);
-    int (*text_set_size) (struct outp_driver *, int);
-    int (*text_get_size) (struct outp_driver *, int *em_width);
-    void (*text_metrics) (struct outp_driver *, struct outp_text *);
-    void (*text_draw) (struct outp_driver *, struct outp_text *);
-
+    /* special == 0 only.  */
+    void (*line) (struct outp_driver *, int x0, int y0, int x1, int y1,
+                  enum outp_line_style top, enum outp_line_style left,
+                  enum outp_line_style bottom, enum outp_line_style right);
+    void (*text_metrics) (struct outp_driver *, const struct outp_text *,
+                          int *width, int *height);
+    void (*text_draw) (struct outp_driver *, const struct outp_text *);
     void (*initialise_chart)(struct outp_driver *, struct chart *);
     void (*finalise_chart)(struct outp_driver *, struct chart *);
-
   };
 
 /* Device types. */
@@ -183,31 +97,24 @@
     OUTP_DEV_LISTING = 001,    /* Listing device. */
     OUTP_DEV_SCREEN = 002,     /* Screen device. */
     OUTP_DEV_PRINTER = 004,    /* Printer device. */
-    OUTP_DEV_DISABLED = 010    /* Broken device. */
   };
 
 /* Defines the configuration of an output driver. */
 struct outp_driver
   {
-    struct outp_class *class;          /* Driver class. */
+    struct outp_driver *next, *prev; /* List of drivers. */
+    struct outp_class *class;  /* Driver class. */
     char *name;                        /* Name of this driver. */
-    int driver_open;           /* 1=driver is open, 0=driver is closed. */
-    int page_open;             /* 1=page is open, 0=page is closed. */
-
-    struct outp_driver *next, *prev;   /* Next, previous output driver in 
list. */
-
+    bool page_open;            /* 1=page is open, 0=page is closed. */
     int device;                        /* Zero or more of OUTP_DEV_*. */
-    int res, horiz, vert;      /* Device resolution. */
-    int width, length;         /* Page size. */
-
     int cp_x, cp_y;            /* Current position. */
+
+    int width, length;         /* Page size. */
     int font_height;           /* Default font character height. */
     int prop_em_width;         /* Proportional font em width. */
     int fixed_width;           /* Fixed-pitch font character width. */
     int horiz_line_width[OUTP_L_COUNT];        /* Width of horizontal lines. */
     int vert_line_width[OUTP_L_COUNT]; /* Width of vertical lines. */
-    int horiz_line_spacing[1 << OUTP_L_COUNT];
-    int vert_line_spacing[1 << OUTP_L_COUNT];
 
     void *ext;                 /* Private extension record. */
     void *prc;                 /* Per-procedure extension record. */
@@ -221,21 +128,6 @@
     int subcat;                        /* Subcategory. */
   };
 
-/* Information structure for the keyword recognizer. */
-struct outp_option_info
-  {
-    char *initial;                     /* Initial characters. */
-    struct outp_option **options;      /* Search starting points. */
-  };
-
-/* A list of driver classes. */
-struct outp_driver_class_list
-  {
-    int ref_count;
-    struct outp_class *class;
-    struct outp_driver_class_list *next;
-  };
-
 /* List of configured output drivers. */
 extern struct outp_driver *outp_driver_list;
 
@@ -256,15 +148,20 @@
 void outp_enable_device (int enable, int device);
 struct outp_driver *outp_drivers (struct outp_driver *);
 
-int outp_match_keyword (const char *, struct outp_option *,
-                       struct outp_option_info *, int *);
+bool outp_parse_options (const char *options,
+                         bool (*) (struct outp_driver *, const char *key,
+                                   const struct string *value),
+                         struct outp_driver *);
+int outp_match_keyword (const char *, struct outp_option *, int *);
 
 int outp_evaluate_dimension (char *, char **);
 int outp_get_paper_size (char *, int *h, int *v);
 
-int outp_eject_page (struct outp_driver *);
+void outp_open_page (struct outp_driver *);
+void outp_close_page (struct outp_driver *);
+void outp_eject_page (struct outp_driver *);
 
-int outp_string_width (struct outp_driver *, const char *);
+int outp_string_width (struct outp_driver *, const char *, enum outp_font);
 
 /* Imported from som-frnt.c. */
 void som_destroy_driver (struct outp_driver *);
Index: pspp/src/output/postscript.c
diff -u pspp/src/output/postscript.c:1.8 pspp/src/output/postscript.c:1.9
--- pspp/src/output/postscript.c:1.8    Fri Mar 31 00:30:22 2006
+++ pspp/src/output/postscript.c        Mon Apr  3 20:07:54 2006
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -20,502 +20,250 @@
 #include <config.h>
 
 #include <ctype.h>
-#include "chart.h"
-#include <libpspp/message.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <time.h>
-
-#if HAVE_UNISTD_H
 #include <unistd.h>
-#endif
 
 #include <libpspp/alloc.h>
 #include <libpspp/bit-vector.h>
 #include <libpspp/compiler.h>
+#include <libpspp/freaderror.h>
+#include <libpspp/hash.h>
 #include <libpspp/message.h>
+#include <libpspp/misc.h>
+#include <libpspp/start-date.h>
+#include <libpspp/version.h>
+
 #include <data/filename.h>
-#include "font.h"
+
+#include "afm.h"
+#include "chart.h"
+#include "error.h"
 #include "getline.h"
-#include <libpspp/hash.h>
 #include "intprops.h"
-#include <libpspp/misc.h>
-#include "output.h"
 #include "manager.h"
-#include <libpspp/start-date.h>
-#include <libpspp/version.h>
+#include "minmax.h"
+#include "output.h"
+#include "size_max.h"
+#include "strsep.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* FIXMEs:
-
-   optimize-text-size not implemented.
-   
-   Line buffering is the only possibility; page buffering should also
-   be possible.
-
-   max-fonts-simult
-   
-   Should add a field to give a file that has a list of fonts
-   typically used.
-   
-   Should add an option that tells the driver it can emit %%Include:'s.
-   
-   Should have auto-encode=true stream-edit or whatever to allow
-   addition to list of encodings.
-   
-   Should align fonts of different sizes along their baselines (see
-   text()).  */
-
 /* PostScript driver options: (defaults listed first)
 
    output-file="pspp.ps"
-   color=yes|no
-   data=clean7bit|clean8bit|binary
-   line-ends=lf|crlf
 
    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
 
-   font-dir=devps
-   prologue-file=ps-prologue
-   device-file=DESC
-   encoding-file=ps-encodings
-   auto-encode=true|false
-
-   prop-font-family=T
-   fixed-font-family=C
+   prop-font=Times-Roman
+   emph-font=Times-Italic
+   fixed-font=Courier
    font-size=10000
 
-   line-style=thick|double
-   line-gutter=0.5pt
-   line-spacing=0.5pt
+   line-gutter=1pt
+   line-spacing=1pt
    line-width=0.5pt
-   line-width-thick=1pt
-
-   optimize-text-size=1|0|2
-   optimize-line-size=1|0
-   max-fonts-simult=0     Max # of fonts in printer memory at once (0=infinite)
  */
 
-/* The number of `psus' (PostScript driver UnitS) per inch.  Although
-   this is a #define, the value is expected never to change.  If it
-   does, review all uses.  */
+/* The number of `psus' (PostScript driver UnitS) per inch. */
 #define PSUS 72000
 
-/* Magic numbers for PostScript and EPSF drivers. */
-enum
-  {
-    MAGIC_PS,
-    MAGIC_EPSF
-  };
-
-/* Orientations. */
-enum
-  {
-    OTN_PORTRAIT,              /* Portrait. */
-    OTN_LANDSCAPE              /* Landscape. */
-  };
-
-/* Output options. */
-enum
-  {
-    OPO_MIRROR_HORZ = 001,     /* 1=Mirror across a horizontal axis. */
-    OPO_MIRROR_VERT = 002,     /* 1=Mirror across a vertical axis. */
-    OPO_ROTATE_180 = 004,      /* 1=Rotate the page 180 degrees. */
-    OPO_COLOR = 010,           /* 1=Enable color. */
-    OPO_HEADERS = 020,         /* 1=Draw headers at top of page. */
-    OPO_AUTO_ENCODE = 040,     /* 1=Add encodings semi-intelligently. */
-    OPO_DOUBLE_LINE = 0100     /* 1=Double lines instead of thick lines. */
-  };
-
-/* Data allowed in output. */
-enum
-  {
-    ODA_CLEAN7BIT,             /* 0x09, 0x0a, 0x0d, 0x1b...0x7e */
-    ODA_CLEAN8BIT,             /* 0x09, 0x0a, 0x0d, 0x1b...0xff */
-    ODA_BINARY,                        /* 0x00...0xff */
-    ODA_COUNT
-  };
-
-/* Types of lines for purpose of caching. */
-enum
-  {
-    horz,                      /* Single horizontal. */
-    dbl_horz,                  /* Double horizontal. */
-    spl_horz,                  /* Special horizontal. */
-    vert,                      /* Single vertical. */
-    dbl_vert,                  /* Double vertical. */
-    spl_vert,                  /* Special vertical. */
-    n_line_types
-  };
-
-/* Cached line. */
-struct line_form
-  {
-    int ind;                   /* Independent var.  Don't reorder. */
-    int mdep;                  /* Maximum number of dependent var pairs. */
-    int ndep;                  /* Current number of dependent var pairs. */
-    int dep[1][2];             /* Dependent var pairs. */
-  };
-
-/* Contents of ps_driver_ext.loaded. */
-struct font_entry
-  {
-    char *dit;                 /* Font Groff name. */
-    struct font_desc *font;    /* Font descriptor. */
-  };
-
-/* Combines a font with a font size for benefit of generated code. */
-struct ps_font_combo
+/* A PostScript font. */
+struct font
   {
-    struct font_entry *font;   /* Font. */
-    int size;                  /* Font size. */
-    int index;                 /* PostScript index. */
-  };
-
-/* A font encoding. */
-struct ps_encoding
-  {
-    char *filename;            /* Normalized filename of this encoding. */
-    int index;                 /* Index value. */
+    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
   {
-    /* User parameters. */
-    int orientation;           /* OTN_PORTRAIT or OTN_LANDSCAPE. */
-    int output_options;                /* OPO_*. */
-    int data;                  /* ODA_*. */
+    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. */
 
-    char eol[3];               /* End of line--CR, LF, or CRLF. */
-    
-    char *font_dir;            /* Font directory relative to font path. */
-    char *prologue_fn;         /* Prologue's filename relative to font dir. */
-    char *desc_fn;             /* DESC filename relative to font dir. */
-    char *encoding_fn;         /* Encoding's filename relative to font dir. */
-
-    char *prop_family;         /* Default proportional font family. */
-    char *fixed_family;                /* Default fixed-pitch font family. */
-    int font_size;             /* Default font size (psus). */
-
     int line_gutter;           /* Space around lines. */
     int line_space;            /* Space between lines. */
     int line_width;            /* Width of lines. */
-    int line_width_thick;      /* Width of thick lines. */
 
-    int text_opt;              /* Text optimization level. */
-    int line_opt;              /* Line optimization level. */
-    int max_fonts;             /* Max # of simultaneous fonts (0=infinite). */
-
-    /* Internal state. */
-    struct file_ext file;      /* Output file. */
-    int page_number;           /* Current page number. */
-    int file_page_number;      /* Page number in this file. */
-    int w, l;                  /* Paper size. */
-    struct hsh_table *lines[n_line_types];     /* Line buffers. */
-    
-    struct font_entry *prop;   /* Default Roman proportional font. */
-    struct font_entry *fixed;  /* Default Roman fixed-pitch font. */
-    struct hsh_table *loaded;  /* Fonts in memory. */
-
-    struct hsh_table *combos;  /* Combinations of fonts with font sizes. */
-    struct ps_font_combo *last_font;   /* PostScript selected font. */
-    int next_combo;            /* Next font combo position index. */
-
-    struct hsh_table *encodings;/* Set of encodings. */
-    int next_encoding;         /* Next font encoding index. */
-
-    /* Currently selected font. */
-    struct font_entry *current;        /* Current font. */
-    char *family;              /* Font family. */
-    int size;                  /* Size in psus. */
+    struct font *fonts[OUTP_FONT_CNT];
+    int last_font;              /* Index of last font set with setfont. */
   }
 ps_driver_ext;
 
 /* Transform logical y-ordinate Y into a page ordinate. */
 #define YT(Y) (this->length - (Y))
 
-/* Prototypes. */
-static int postopen (struct file_ext *);
-static int preclose (struct file_ext *);
+static bool handle_option (struct outp_driver *this, const char *key,
+                           const struct string *val);
 static void draw_headers (struct outp_driver *this);
 
-static int compare_font_entry (const void *, const void *, void *param);
-static unsigned hash_font_entry (const void *, void *param);
-static void free_font_entry (void *, void *foo);
-static struct font_entry *load_font (struct outp_driver *, const char *dit);
-static void init_fonts (void);
-static void done_fonts (void);
-
-static void dump_lines (struct outp_driver *this);
-
-static void read_ps_encodings (struct outp_driver *this);
-static int compare_ps_encoding (const void *pa, const void *pb, void *foo);
-static unsigned hash_ps_encoding (const void *pa, void *foo);
-static void free_ps_encoding (void *a, void *foo);
-static void add_encoding (struct outp_driver *this, char *filename);
-static struct ps_encoding *default_encoding (struct outp_driver *this);
-
-static int compare_ps_combo (const void *pa, const void *pb, void *foo);
-static unsigned hash_ps_combo (const void *pa, void *foo);
-static void free_ps_combo (void *a, void *foo);
+static void write_ps_prologue (struct outp_driver *);
 
-static char *quote_ps_name (char *dest, const char *string);
-static char *quote_ps_string (char *dest, const char *string);
+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);
 
 /* Driver initialization. */
 
-static int
-ps_open_global (struct outp_class *this UNUSED)
-{
-  init_fonts ();
-  groff_init ();
-  return 1;
-}
-
-static int
-ps_close_global (struct outp_class *this UNUSED)
-{
-  groff_done ();
-  done_fonts ();
-  return 1;
-}
-
-static int *
-ps_font_sizes (struct outp_class *this UNUSED, int *n_valid_sizes)
-{
-  /* Allow fonts up to 1" in height. */
-  static int valid_sizes[] =
-  {1, PSUS, 0, 0};
-
-  assert (n_valid_sizes != NULL);
-  *n_valid_sizes = 1;
-  return valid_sizes;
-}
-
-static int
-ps_preopen_driver (struct outp_driver *this)
+static bool
+ps_open_driver (struct outp_driver *this, const char *options)
 {
   struct ps_driver_ext *x;
-  
-  int i;
+  size_t i;
 
-  assert (this->driver_open == 0);
-  msg (VM (1), _("PostScript driver initializing as `%s'..."), this->name);
-       
-  this->ext = x = xmalloc (sizeof *x);
-  this->res = PSUS;
-  this->horiz = this->vert = 1;
   this->width = this->length = 0;
+  this->font_height = PSUS * 10 / 72;
 
-  x->orientation = OTN_PORTRAIT;
-  x->output_options = OPO_COLOR | OPO_HEADERS | OPO_AUTO_ENCODE;
-  x->data = ODA_CLEAN7BIT;
-       
-  x->left_margin = x->right_margin =
-    x->top_margin = x->bottom_margin = PSUS / 2;
-       
-  strcpy (x->eol, "\n");
-
-  x->font_dir = NULL;
-  x->prologue_fn = NULL;
-  x->desc_fn = NULL;
-  x->encoding_fn = NULL;
-
-  x->prop_family = NULL;
-  x->fixed_family = NULL;
-  x->font_size = PSUS * 10 / 72;
-
-  x->line_gutter = PSUS / 144;
-  x->line_space = PSUS / 144;
-  x->line_width = PSUS / 144;
-  x->line_width_thick = PSUS / 48;
-
-  x->text_opt = -1;
-  x->line_opt = -1;
-  x->max_fonts = 0;
-
-  x->file.filename = NULL;
-  x->file.mode = "wb";
-  x->file.file = NULL;
-  x->file.sequence_no = &x->page_number;
-  x->file.param = this;
-  x->file.postopen = postopen;
-  x->file.preclose = preclose;
+  this->ext = x = xmalloc (sizeof *x);
+  x->file_name = xstrdup ("pspp.ps");
+  x->file = NULL;
+  x->draw_headers = true;
   x->page_number = 0;
-  x->w = x->l = 0;
-
-  x->file_page_number = 0;
-  for (i = 0; i < n_line_types; i++)
-    x->lines[i] = NULL;
-  x->last_font = NULL;
-
-  x->prop = NULL;
-  x->fixed = NULL;
-  x->loaded = NULL;
-
-  x->next_combo = 0;
-  x->combos = NULL;
-
-  x->encodings = hsh_create (31, compare_ps_encoding, hash_ps_encoding,
-                            free_ps_encoding, NULL);
-  x->next_encoding = 0;
-
-  x->current = NULL;
-  x->family = NULL;
-  x->size = 0;
-
-  return 1;
-}
+  x->portrait = true;
+  x->paper_width = PSUS * 17 / 2;
+  x->paper_length = PSUS * 11;
+  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;
 
-static int
-ps_postopen_driver (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  
-  assert (this->driver_open == 0);
+  outp_parse_options (options, handle_option, this);
 
-  if (this->width == 0)
+  x->file = fn_open (x->file_name, "w");
+  if (x->file == NULL)
     {
-      this->width = PSUS * 17 / 2;     /* Defaults to 8.5"x11". */
-      this->length = PSUS * 11;
+      error (0, errno, _("opening PostScript output file \"%s\""),
+             x->file_name);
+      goto error;
     }
 
-  if (x->text_opt == -1)
-    x->text_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
-  if (x->line_opt == -1)
-    x->line_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
-
-  x->w = this->width;
-  x->l = this->length;
-  if (x->orientation == OTN_LANDSCAPE)
+  if (x->portrait) 
     {
-      int temp = this->width;
-      this->width = this->length;
-      this->length = temp;
+      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->output_options & OPO_HEADERS)
+  if (x->draw_headers)
     {
-      this->length -= 3 * x->font_size;
-      x->top_margin += 3 * x->font_size;
+      int header_length = 3 * this->font_height;
+      this->length -= header_length;
+      x->top_margin += header_length;
     }
-  if (NULL == x->file.filename)
-    x->file.filename = xstrdup ("pspp.ps");
-
-  if (x->font_dir == NULL)
-    x->font_dir = xstrdup ("devps");
-  if (x->prologue_fn == NULL)
-    x->prologue_fn = xstrdup ("ps-prologue");
-  if (x->desc_fn == NULL)
-    x->desc_fn = xstrdup ("DESC");
-  if (x->encoding_fn == NULL)
-    x->encoding_fn = xstrdup ("ps-encodings");
-
-  if (x->prop_family == NULL)
-    x->prop_family = xstrdup ("H");
-  if (x->fixed_family == NULL)
-    x->fixed_family = xstrdup ("C");
-
-  read_ps_encodings (this);
 
-  x->family = NULL;
-  x->size = PSUS / 6;
+  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 / x->font_size < 15)
+  if (this->length / this->font_height < 15)
     {
-      msg (SE, _("PostScript driver: The defined 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 / x->font_size,
-          x->font_size / 1000, x->font_size % 1000);
-      return 0;
+      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->driver_open = 1;
-  msg (VM (2), _("%s: Initialization complete."), this->name);
-
-  return 1;
+  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);
+
+  return true;
+
+ error:
+  this->class->close_driver (this);
+  return false;
 }
 
-static int
+static bool
 ps_close_driver (struct outp_driver *this)
 {
   struct ps_driver_ext *x = this->ext;
-  
-  int i;
+  bool ok;
+  size_t i;
 
-  assert (this->driver_open == 1);
-  msg (VM (2), _("%s: Beginning closing..."), this->name);
-  
-  fn_close_ext (&x->file);
-  free (x->file.filename);
-  free (x->font_dir);
-  free (x->prologue_fn);
-  free (x->desc_fn);
-  free (x->encoding_fn);
-  free (x->prop_family);
-  free (x->fixed_family);
-  free (x->family);
-  for (i = 0; i < n_line_types; i++)
-    hsh_destroy (x->lines[i]);
-  hsh_destroy (x->encodings);
-  hsh_destroy (x->combos);
-  hsh_destroy (x->loaded);
+  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);
-  
-  this->driver_open = 0;
-  msg (VM (3), _("%s: Finished closing."), this->name);
-
-  return 1;
-}
-
-/* font_entry comparison function for hash tables. */
-static int
-compare_font_entry (const void *a, const void *b, void *foobar UNUSED)
-{
-  return strcmp (((struct font_entry *) a)->dit, ((struct font_entry *) 
b)->dit);
-}
-
-/* font_entry hash function for hash tables. */
-static unsigned
-hash_font_entry (const void *fe_, void *foobar UNUSED)
-{
-  const struct font_entry *fe = fe_;
-  return hsh_hash_string (fe->dit);
-}
 
-/* font_entry destructor function for hash tables. */
-static void
-free_font_entry (void *pa, void *foo UNUSED)
-{
-  struct font_entry *a = pa;
-  free (a->dit);
-  free (a);
+  return ok;
 }
 
 /* Generic option types. */
 enum
 {
-  boolean_arg = -10,
+  output_file_arg,
+  paper_size_arg,
+  orientation_arg,
+  line_style_arg,
+  boolean_arg,
   pos_int_arg,
   dimension_arg,
   string_arg,
@@ -525,145 +273,71 @@
 /* All the options that the PostScript driver supports. */
 static struct outp_option option_tab[] =
 {
-  /* *INDENT-OFF* */
-  {"output-file",              1,              0},
-  {"paper-size",               2,              0},
-  {"orientation",              3,              0},
-  {"color",                    boolean_arg,    0},
-  {"data",                     4,              0},
-  {"auto-encode",              boolean_arg,    5},
+  {"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-dir",                 string_arg,     0},
-  {"prologue-file",            string_arg,     1},
-  {"device-file",              string_arg,     2},
-  {"encoding-file",            string_arg,     3},
-  {"prop-font-family",         string_arg,     5},
-  {"fixed-font-family",                string_arg,     6},
   {"font-size",                        pos_int_arg,    4},
-  {"optimize-text-size",       nonneg_int_arg, 0},
-  {"optimize-line-size",       nonneg_int_arg, 1},
-  {"max-fonts-simult",         nonneg_int_arg, 2},
-  {"line-ends",                        6,              0},
-  {"line-style",               7,              0},
+
+  {"line-width",               dimension_arg,  0},
+  {"line-gutter",              dimension_arg,  1},
   {"line-width",               dimension_arg,  2},
-  {"line-gutter",              dimension_arg,  3},
-  {"line-width",               dimension_arg,  4},
-  {"line-width-thick",         dimension_arg,  5},
-  {"", 0, 0},
-  /* *INDENT-ON* */
+  {NULL, 0, 0},
 };
-static struct outp_option_info option_info;
 
-static void
-ps_option (struct outp_driver *this, const char *key, const struct string *val)
+static bool
+handle_option (struct outp_driver *this, const char *key,
+               const struct string *val)
 {
   struct ps_driver_ext *x = this->ext;
-  int cat, subcat;
+  int subcat;
   char *value = ds_c_str (val);
 
-  cat = outp_match_keyword (key, option_tab, &option_info, &subcat);
-
-  switch (cat)
+  switch (outp_match_keyword (key, option_tab, &subcat))
     {
-    case 0:
-      msg (SE, _("Unknown configuration parameter `%s' for PostScript device "
-          "driver."), key);
+    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 1:
-      free (x->file.filename);
-      x->file.filename = xstrdup (value);
-      break;
-    case 2:
+    case paper_size_arg:
       outp_get_paper_size (value, &this->width, &this->length);
       break;
-    case 3:
+    case orientation_arg:
       if (!strcmp (value, "portrait"))
-       x->orientation = OTN_PORTRAIT;
+       x->portrait = true;
       else if (!strcmp (value, "landscape"))
-       x->orientation = OTN_LANDSCAPE;
-      else
-       msg (SE, _("Unknown orientation `%s'.  Valid orientations are "
-            "`portrait' and `landscape'."), value);
-      break;
-    case 4:
-      if (!strcmp (value, "clean7bit") || !strcmp (value, "Clean7Bit"))
-       x->data = ODA_CLEAN7BIT;
-      else if (!strcmp (value, "clean8bit")
-              || !strcmp (value, "Clean8Bit"))
-       x->data = ODA_CLEAN8BIT;
-      else if (!strcmp (value, "binary") || !strcmp (value, "Binary"))
-       x->data = ODA_BINARY;
-      else
-       msg (SE, _("Unknown value for `data'.  Valid values are `clean7bit', "
-            "`clean8bit', and `binary'."));
-      break;
-    case 6:
-      if (!strcmp (value, "lf"))
-       strcpy (x->eol, "\n");
-      else if (!strcmp (value, "crlf"))
-       strcpy (x->eol, "\r\n");
-      else
-       msg (SE, _("Unknown value for `line-ends'.  Valid values are `lf' and "
-                  "`crlf'."));
-      break;
-    case 7:
-      if (!strcmp (value, "thick"))
-       x->output_options &= ~OPO_DOUBLE_LINE;
-      else if (!strcmp (value, "double"))
-       x->output_options |= OPO_DOUBLE_LINE;
+       x->portrait = false;
       else
-       msg (SE, _("Unknown value for `line-style'.  Valid values are `thick' "
-                  "and `double'."));
+       error (0, 0, _("unknown orientation `%s' (valid orientations are "
+                       "`portrait' and `landscape')"), value);
       break;
     case boolean_arg:
-      {
-       int setting;
-       int mask;
-
-       if (!strcmp (value, "on") || !strcmp (value, "true")
-           || !strcmp (value, "yes") || atoi (value))
-         setting = 1;
-       else if (!strcmp (value, "off") || !strcmp (value, "false")
-                || !strcmp (value, "no") || !strcmp (value, "0"))
-         setting = 0;
-       else
-         {
-           msg (SE, _("Boolean value expected for %s."), key);
-           return;
-         }
-       switch (subcat)
-         {
-         case 0:
-           mask = OPO_COLOR;
-           break;
-         case 1:
-           mask = OPO_HEADERS;
-           break;
-         case 2:
-           mask = OPO_MIRROR_HORZ;
-           break;
-         case 3:
-           mask = OPO_MIRROR_VERT;
-           break;
-         case 4:
-           mask = OPO_ROTATE_180;
-           break;
-         case 5:
-           mask = OPO_AUTO_ENCODE;
-           break;
-         default:
-           assert (0);
-            abort ();
-         }
-       if (setting)
-         x->output_options |= mask;
-       else
-         x->output_options &= ~mask;
-      }
+      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:
       {
@@ -674,13 +348,13 @@
        arg = strtol (value, &tail, 0);
        if (arg < 1 || errno == ERANGE || *tail)
          {
-           msg (SE, _("Positive integer required as value for `%s'."), key);
+           error (0, 0, _("positive integer value required for `%s'"), key);
            break;
          }
        if ((subcat == 4 || subcat == 5) && arg < 1000)
          {
-           msg (SE, _("Default font size must be at least 1 point (value "
-                "of 1000 for key `%s')."), key);
+           error (0, 0, _("default font size must be at least 1 point (value "
+                           "of 1000 for key `%s')"), key);
            break;
          }
        switch (subcat)
@@ -698,10 +372,10 @@
            x->bottom_margin = arg;
            break;
          case 4:
-           x->font_size = arg;
+           this->font_height = arg;
            break;
          default:
-           assert (0);
+           abort ();
          }
       }
       break;
@@ -711,2167 +385,709 @@
 
        if (dimension <= 0)
          {
-           msg (SE, _("Value for `%s' must be a dimension of positive "
-                "length (i.e., `1in')."), key);
+           error (0, 0, _("value for `%s' must be a dimension of positive "
+                           "length (i.e., `1in')"), key);
            break;
          }
        switch (subcat)
          {
-         case 2:
+         case 0:
            x->line_width = dimension;
            break;
-         case 3:
+         case 1:
            x->line_gutter = dimension;
            break;
-         case 4:
+         case 2:
            x->line_width = dimension;
            break;
-         case 5:
-           x->line_width_thick = dimension;
-           break;
          default:
-           assert (0);
+           abort ();
          }
       }
       break;
     case string_arg:
       {
-       char **dest;
-       switch (subcat)
-         {
-         case 0:
-           dest = &x->font_dir;
-           break;
-         case 1:
-           dest = &x->prologue_fn;
-           break;
-         case 2:
-           dest = &x->desc_fn;
-           break;
-         case 3:
-           dest = &x->encoding_fn;
-           break;
-         case 5:
-           dest = &x->prop_family;
-           break;
-         case 6:
-           dest = &x->fixed_family;
-           break;
-         default:
-           assert (0);
-            abort ();
-         }
-       if (*dest)
-         free (*dest);
-       *dest = xstrdup (value);
-      }
-      break;
-    case nonneg_int_arg:
-      {
-       char *tail;
-       int arg;
-
-       errno = 0;
-       arg = strtol (value, &tail, 0);
-       if (arg < 0 || errno == ERANGE || *tail)
-         {
-           msg (SE, _("Nonnegative integer required as value for `%s'."), key);
-           break;
-         }
-       switch (subcat)
-         {
-         case 0:
-           x->text_opt = arg;
-           break;
-         case 1:
-           x->line_opt = arg;
-           break;
-         case 2:
-           x->max_fonts = arg;
-           break;
-         default:
-           assert (0);
-         }
+        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:
-      assert (0);
+      abort ();
     }
+
+  return true;
 }
 
 /* Looks for a PostScript font file or config file in all the
    appropriate places.  Returns the filename on success, NULL on
    failure. */
-/* PORTME: Filename operations. */
 static char *
-find_ps_file (struct outp_driver *this, const char *name)
+find_ps_file (const char *name)
 {
-  struct ps_driver_ext *x = this->ext;
-  char *cp;
-
-  /* x->font_dir + name: "devps/ps-encodings". */
-  char *basename;
-
-  /* Usually equal to groff_font_path. */
-  char *pathname;
-
-  /* Final filename. */
-  char *fn;
-
-  /* Make basename. */
-  basename = local_alloc (strlen (x->font_dir) + 1 + strlen (name) + 1);
-  cp = stpcpy (basename, x->font_dir);
-  *cp++ = DIR_SEPARATOR;
-  strcpy (cp, name);
-
-  /* Decide on search path. */
-  {
-    const char *pre_pathname;
-    
-    pre_pathname = getenv ("STAT_GROFF_FONT_PATH");
-    if (pre_pathname == NULL)
-      pre_pathname = getenv ("GROFF_FONT_PATH");
-    if (pre_pathname == NULL)
-      pre_pathname = groff_font_path;
-    pathname = fn_tilde_expand (pre_pathname);
-  }
-
-  /* Search all possible places for the file. */
-  fn = fn_search_path (basename, pathname, NULL);
-  if (fn == NULL)
-    fn = fn_search_path (basename, config_path, NULL);
-  if (fn == NULL)
-    fn = fn_search_path (name, pathname, NULL);
-  if (fn == NULL)
-    fn = fn_search_path (name, config_path, NULL);
-  free (pathname);
-  local_free (basename);
-
-  return fn;
+  if (fn_absolute_p (name))
+    return xstrdup (name);
+  else
+    {
+      char *base_name = xasprintf ("psfonts%c%s", DIR_SEPARATOR, name);
+      char *file_name = fn_search_path (base_name, config_path, NULL);
+      free (base_name);
+      return file_name;
+    }
 }
 
-/* Encodings. */
-
-/* Hash table comparison function for ps_encoding's. */
-static int
-compare_ps_encoding (const void *pa, const void *pb, void *foo UNUSED)
-{
-  const struct ps_encoding *a = pa;
-  const struct ps_encoding *b = pb;
-
-  return strcmp (a->filename, b->filename);
-}
-
-/* Hash table hash function for ps_encoding's. */
-static unsigned
-hash_ps_encoding (const void *pa, void *foo UNUSED)
-{
-  const struct ps_encoding *a = pa;
-
-  return hsh_hash_string (a->filename);
-}
-
-/* Hash table free function for ps_encoding's. */
-static void
-free_ps_encoding (void *pa, void *foo UNUSED)
-{
-  struct ps_encoding *a = pa;
-
-  free (a->filename);
-  free (a);
-}
+/* Basic file operations. */
 
-/* Iterates through the list of encodings used for this driver
-   instance, reads each of them from disk, and writes them as
-   PostScript code to the output file. */
+/* Writes the PostScript prologue to file F. */
 static void
-output_encodings (struct outp_driver *this)
+write_ps_prologue (struct outp_driver *this)
 {
   struct ps_driver_ext *x = this->ext;
+  size_t embedded_cnt, preloaded_cnt;
+  size_t i;
 
-  struct hsh_iterator iter;
-  struct ps_encoding *pe;
+  fputs ("%!PS-Adobe-3.0\n", x->file);
+  fputs ("%%Pages: (atend)\n", x->file);
 
-  struct string line, buf;
-
-  ds_init (&line, 128);
-  ds_init (&buf, 128);
-  for (pe = hsh_first (x->encodings, &iter); pe != NULL;
-       pe = hsh_next (x->encodings, &iter)) 
-    {
-      FILE *f;
-
-      msg (VM (1), _("%s: %s: Opening PostScript font encoding..."),
-          this->name, pe->filename);
-      
-      f = fopen (pe->filename, "r");
-      if (!f)
-       {
-         msg (IE, _("PostScript driver: Cannot open encoding file `%s': %s.  "
-              "Substituting ISOLatin1Encoding for missing encoding."),
-              pe->filename, strerror (errno));
-         fprintf (x->file.file, "/E%x ISOLatin1Encoding def%s",
-                  pe->index, x->eol);
-       }
-      else
-       {
-         struct file_locator where;
-         
-         const char *tab[256];
-
-         char *pschar;
-         char *code;
-         int code_val;
-         char *fubar;
-
-         const char *notdef = ".notdef";
-
-         int i;
-
-         for (i = 0; i < 256; i++)
-           tab[i] = notdef;
-
-         where.filename = pe->filename;
-         where.line_number = 0;
-         err_push_file_locator (&where);
-
-         while (ds_get_config_line (f, &buf, &where.line_number))
-           {
-             char *sp; 
-
-             if (buf.length == 0) 
-               continue;
-
-             pschar = strtok_r (ds_c_str (&buf), " \t\r\n", &sp);
-             code = strtok_r (NULL, " \t\r\n", &sp);
-             if (*pschar == 0 || *code == 0)
-               continue;
-             code_val = strtol (code, &fubar, 0);
-             if (*fubar)
-               {
-                 msg (IS, _("PostScript driver: Invalid numeric format."));
-                 continue;
-               }
-             if (code_val < 0 || code_val > 255)
-               {
-                 msg (IS, _("PostScript driver: Codes must be between 0 "
-                            "and 255.  (%d is not allowed.)"), code_val);
-                 break;
-               }
-             tab[code_val] = local_alloc (strlen (pschar) + 1);
-             strcpy ((char *) (tab[code_val]), pschar);
-           }
-         err_pop_file_locator (&where);
-
-         ds_clear (&line);
-         ds_printf (&line, "/E%x[", pe->index);
-         for (i = 0; i < 257; i++)
-           {
-             char temp[288];
-
-             if (i < 256)
-               {
-                 quote_ps_name (temp, tab[i]);
-                 if (tab[i] != notdef)
-                   local_free (tab[i]);
-               }
-             else
-               strcpy (temp, "]def");
-             
-             if (ds_length (&line) + strlen (temp) > 70)
-               {
-                 ds_puts (&line, x->eol);
-                 fputs (ds_c_str (&line), x->file.file);
-                 ds_clear (&line);
-               }
-             ds_puts (&line, temp);
-           }
-         ds_puts (&line, x->eol);
-         fputs (ds_c_str (&line), x->file.file);
-
-         if (fclose (f) == EOF)
-           msg (MW, _("PostScript driver: Error closing encoding file `%s'."),
-                pe->filename);
+  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;
 
-         msg (VM (2), _("%s: PostScript font encoding read successfully."),
-              this->name);
-       }
+  for (cp = string; *cp != '\0'; cp++)
+    {
+      unsigned char c = *cp;
+      if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
+          && (cp == string || !isdigit (c)))
+        {
+          struct string out = DS_INITIALIZER;
+          ds_putc (&out, '<');
+         for (cp = string; *cp != '\0'; cp++)
+            {
+              c = *cp;
+              ds_printf (&out, "%02x", c);
+            }
+         ds_puts (&out, ">cvn");
+          return ds_c_str (&out);
+        }
     }
-  ds_destroy (&line);
-  ds_destroy (&buf);
+  return xasprintf ("/%s", string);
 }
 
-/* Finds the ps_encoding in THIS that corresponds to the file with
-   name NORM_FILENAME, which must have previously been normalized with
-   normalize_filename(). */
-static struct ps_encoding *
-get_encoding (struct outp_driver *this, const char *norm_filename)
+static void
+ps_open_page (struct outp_driver *this)
 {
   struct ps_driver_ext *x = this->ext;
-  struct ps_encoding *pe;
 
-  pe = (struct ps_encoding *) hsh_find (x->encodings, (void *) &norm_filename);
-  return pe;
-}
-
-/* Searches the filesystem for an encoding file with name FILENAME;
-   returns its malloc'd, normalized name if found, otherwise NULL. */
-static char *
-find_encoding_file (struct outp_driver *this, char *filename)
-{
-  char *cp, *temp;
+  /* Assure page independence. */
+  x->last_font = -1;
 
-  if (filename == NULL)
-    return NULL;
-  while (isspace ((unsigned char) *filename))
-    filename++;
-  for (cp = filename; *cp && !isspace ((unsigned char) *cp); cp++)
-    ;
-  if (cp == filename)
-    return NULL;
-  *cp = 0;
+  x->page_number++;
 
-  temp = find_ps_file (this, filename);
-  if (temp == NULL)
-    return NULL;
+  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);
 
-  filename = fn_normalize (temp);
-  assert (filename != NULL);
-  free (temp);
+  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);
 
-  return filename;
+  if (x->draw_headers)
+    draw_headers (this);
 }
 
-/* Adds the encoding represented by the not-necessarily-normalized
-   file FILENAME to the list of encodings, if it exists and is not
-   already in the list. */
 static void
-add_encoding (struct outp_driver *this, char *filename)
+ps_close_page (struct outp_driver *this)
 {
   struct ps_driver_ext *x = this->ext;
-  struct ps_encoding **pe;
-
-  filename = find_encoding_file (this, filename);
-  if (!filename)
-    return;
-
-  pe = (struct ps_encoding **) hsh_probe (x->encodings, &filename);
-  if (*pe)
-    {
-      free (filename);
-      return;
-    }
-  *pe = xmalloc (sizeof **pe);
-  (*pe)->filename = filename;
-  (*pe)->index = x->next_encoding++;
-}
-
-/* Finds the file on disk that contains the list of encodings to
-   include in the output file, then adds those encodings to the list
-   of encodings. */
-static void
-read_ps_encodings (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  /* Encodings file. */
-  char *encoding_fn;           /* `ps-encodings' filename. */
-  FILE *f;
-
-  struct string line;
-  struct file_locator where;
-
-  /* It's okay if there's no list of encodings; not everyone cares. */
-  encoding_fn = find_ps_file (this, x->encoding_fn);
-  if (encoding_fn == NULL)
-    return;
-  free (encoding_fn);
-
-  msg (VM (1), _("%s: %s: Opening PostScript encoding list file."),
-       this->name, encoding_fn);
-  f = fopen (encoding_fn, "r");
-  if (!f)
-    {
-      msg (IE, _("Opening %s: %s."), encoding_fn, strerror (errno));
-      return;
-    }
-
-  where.filename = encoding_fn;
-  where.line_number = 0;
-  err_push_file_locator (&where);
-
-  ds_init (&line, 128);
-    
-  for (;;)
-    {
-      if (!ds_get_config_line (f, &line, &where.line_number))
-       {
-         if (ferror (f))
-           msg (ME, _("Reading %s: %s."), encoding_fn, strerror (errno));
-         break;
-       }
-
-      add_encoding (this, line.string);
-    }
-
-  ds_destroy (&line);
-  err_pop_file_locator (&where);
-  
-  if (-1 == fclose (f))
-    msg (MW, _("Closing %s: %s."), encoding_fn, strerror (errno));
-
-  msg (VM (2), _("%s: PostScript encoding list file read successfully."), 
this->name);
-}
-
-/* Creates a default encoding for driver D that can be substituted for
-   an unavailable encoding. */
-struct ps_encoding *
-default_encoding (struct outp_driver *d)
-{
-  struct ps_driver_ext *x = d->ext;
-  static struct ps_encoding *enc;
-
-  if (!enc)
-    {
-      enc = xmalloc (sizeof *enc);
-      enc->filename = xstrdup (_("<<default encoding>>"));
-      enc->index = x->next_encoding++;
-    }
-  return enc;
-}
-
-/* Basic file operations. */
-
-/* Variables for the prologue. */
-struct ps_variable
-  {
-    const char *key;
-    const char *value;
-  };
-
-static struct ps_variable *ps_var_tab;
-
-/* Searches ps_var_tab for a ps_variable with key KEY, and returns the
-   associated value. */
-static const char *
-ps_get_var (const char *key)
-{
-  struct ps_variable *v;
-
-  for (v = ps_var_tab; v->key; v++)
-    if (!strcmp (key, v->key))
-      return v->value;
-  return NULL;
-}
-
-/* Writes the PostScript prologue to file F. */
-static int
-postopen (struct file_ext *f)
-{
-  static struct ps_variable dict[] =
-  {
-    {"bounding-box", 0},
-    {"creator", 0},
-    {"date", 0},
-    {"data", 0},
-    {"orientation", 0},
-    {"user", 0},
-    {"host", 0},
-    {"prop-font", 0},
-    {"fixed-font", 0},
-    {"scale-factor", 0},
-    {"paper-width", 0},
-    {"paper-length", 0},
-    {"left-margin", 0},
-    {"top-margin", 0},
-    {"line-width", 0},
-    {"line-width-thick", 0},
-    {"title", 0},
-    {0, 0},
-  };
-  char boundbox[INT_STRLEN_BOUND (int) * 4 + 4];
-#if HAVE_UNISTD_H
-  char host[128];
-#endif
-  char scaling[INT_STRLEN_BOUND (int) + 5];
-  time_t curtime;
-  struct tm *loctime;
-  char *p, *cp;
-  char paper_width[INT_STRLEN_BOUND (int) + 1];
-  char paper_length[INT_STRLEN_BOUND (int) + 1];
-  char left_margin[INT_STRLEN_BOUND (int) + 1];
-  char top_margin[INT_STRLEN_BOUND (int) + 1];
-  char line_width[INT_STRLEN_BOUND (int) + 1];
-  char line_width_thick[INT_STRLEN_BOUND (int) + 1];
-
-  struct outp_driver *this = f->param;
-  struct ps_driver_ext *x = this->ext;
-
-  char *prologue_fn = find_ps_file (this, x->prologue_fn);
-  FILE *prologue_file;
-
-  char *buf = NULL;
-  size_t buf_size = 0;
-
-  x->loaded = hsh_create (31, compare_font_entry, hash_font_entry,
-                         free_font_entry, NULL);
-  
-  {
-    char *font_name = local_alloc (2 + max (strlen (x->prop_family),
-                                           strlen (x->fixed_family)));
-    
-    strcpy (stpcpy (font_name, x->prop_family), "R");
-    x->prop = load_font (this, font_name);
-
-    strcpy (stpcpy (font_name, x->fixed_family), "R");
-    x->fixed = load_font (this, font_name);
-
-    local_free(font_name);
-  }
-
-  x->current = x->prop;
-  x->family = xstrdup (x->prop_family);
-  x->size = x->font_size;
-  
-  {
-    int *h = this->horiz_line_width, *v = this->vert_line_width;
-    
-    this->cp_x = this->cp_y = 0;
-    this->font_height = x->font_size;
-    {
-      struct char_metrics *metric;
-
-      metric = font_get_char_metrics (x->prop->font, '0');
-      this->prop_em_width = ((metric
-                             ? metric->width : x->prop->font->space_width)
-                            * x->font_size / 1000);
-
-      metric = font_get_char_metrics (x->fixed->font, '0');
-      this->fixed_width = ((metric
-                           ? metric->width : x->fixed->font->space_width)
-                          * x->font_size / 1000);
-    }
-        
-    h[0] = v[0] = 0;
-    h[1] = v[1] = 2 * x->line_gutter + x->line_width;
-    if (x->output_options & OPO_DOUBLE_LINE)
-      h[2] = v[2] = 2 * x->line_gutter + 2 * x->line_width + x->line_space;
-    else
-      h[2] = v[2] = 2 * x->line_gutter + x->line_width_thick;
-    h[3] = v[3] = 2 * x->line_gutter + x->line_width;
-    
-    {
-      int i;
-      
-      for (i = 0; i < (1 << OUTP_L_COUNT); i++)
-       {
-         int bit;
-
-         /* Maximum width of any line type so far. */
-         int max = 0;
-
-         for (bit = 0; bit < OUTP_L_COUNT; bit++)
-           if ((i & (1 << bit)) && h[bit] > max)
-             max = h[bit];
-         this->horiz_line_spacing[i] = this->vert_line_spacing[i] = max;
-       }
-    }
-  }
-
-  if (x->output_options & OPO_AUTO_ENCODE)
-    {
-      /* It's okay if this is done more than once since add_encoding()
-         is idempotent over identical encodings. */
-      add_encoding (this, x->prop->font->encoding);
-      add_encoding (this, x->fixed->font->encoding);
-    }
-
-  x->file_page_number = 0;
-
-  errno = 0;
-  if (prologue_fn == NULL)
-    {
-      msg (IE, _("Cannot find PostScript prologue.  The use of `-vv' "
-                "on the command line is suggested as a debugging aid."));
-      return 0;
-    }
-
-  msg (VM (1), _("%s: %s: Opening PostScript prologue..."),
-       this->name, prologue_fn);
-  prologue_file = fopen (prologue_fn, "rb");
-  if (prologue_file == NULL)
-    {
-      fclose (prologue_file);
-      free (prologue_fn);
-      msg (IE, "%s: %s", prologue_fn, strerror (errno));
-      goto error;
-    }
-
-  sprintf (boundbox, "0 0 %d %d",
-          x->w / (PSUS / 72) + (x->w % (PSUS / 72) > 0),
-          x->l / (PSUS / 72) + (x->l % (PSUS / 72) > 0));
-  dict[0].value = boundbox;
-
-  dict[1].value = (char *) version;
-
-  curtime = time (NULL);
-  loctime = localtime (&curtime);
-  dict[2].value = asctime (loctime);
-  cp = strchr (dict[2].value, '\n');
-  if (cp)
-    *cp = 0;
-
-  switch (x->data)
-    {
-    case ODA_CLEAN7BIT:
-      dict[3].value = "Clean7Bit";
-      break;
-    case ODA_CLEAN8BIT:
-      dict[3].value = "Clean8Bit";
-      break;
-    case ODA_BINARY:
-      dict[3].value = "Binary";
-      break;
-    default:
-      assert (0);
-    }
-
-  if (x->orientation == OTN_PORTRAIT)
-    dict[4].value = "Portrait";
-  else
-    dict[4].value = "Landscape";
-
-  /* PORTME: Determine username, net address. */
-#if HAVE_UNISTD_H
-  dict[5].value = getenv ("LOGNAME");
-  if (!dict[5].value)
-    dict[5].value = getlogin ();
-  if (!dict[5].value)
-    dict[5].value = _("nobody");
-
-  if (gethostname (host, 128) == -1)
-    {
-      if (errno == ENAMETOOLONG)
-       host[127] = 0;
-      else
-       strcpy (host, _("nowhere"));
-    }
-  dict[6].value = host;
-#else /* !HAVE_UNISTD_H */
-  dict[5].value = _("nobody");
-  dict[6].value = _("nowhere");
-#endif /* !HAVE_UNISTD_H */
-
-  cp = stpcpy (p = local_alloc (288), "font ");
-  quote_ps_string (cp, x->prop->font->internal_name);
-  dict[7].value = p;
-
-  cp = stpcpy (p = local_alloc (288), "font ");
-  quote_ps_string (cp, x->fixed->font->internal_name);
-  dict[8].value = p;
-
-  sprintf (scaling, "%.3f", PSUS / 72.0);
-  dict[9].value = scaling;
-
-  sprintf (paper_width, "%g", x->w / (PSUS / 72.0));
-  dict[10].value = paper_width;
-
-  sprintf (paper_length, "%g", x->l / (PSUS / 72.0));
-  dict[11].value = paper_length;
-
-  sprintf (left_margin, "%d", x->left_margin);
-  dict[12].value = left_margin;
-
-  sprintf (top_margin, "%d", x->top_margin);
-  dict[13].value = top_margin;
-
-  sprintf (line_width, "%d", x->line_width);
-  dict[14].value = line_width;
-
-  sprintf (line_width, "%d", x->line_width_thick);
-  dict[15].value = line_width_thick;
-  
-  if (!outp_title)
-    {
-      dict[16].value = cp = local_alloc (16);
-      strcpy (cp, "PSPP");
-    }
-  else
-    {
-      dict[16].value = local_alloc (strlen (outp_title) + 1);
-      strcpy ((char *) (dict[16].value), outp_title);
-    }
-  
-  ps_var_tab = dict;
-  while (-1 != getline (&buf, &buf_size, prologue_file))
-    {
-      char *cp;
-
-      int len;
-
-      cp = strstr (buf, "!eps");
-      if (cp)
-       {
-         if (this->class->magic == MAGIC_PS)
-           continue;
-         else
-           *cp = '\0';
-       }
-      else
-       {
-         cp = strstr (buf, "!ps");
-         if (cp)
-           {
-             if (this->class->magic == MAGIC_EPSF)
-               continue;
-             else
-               *cp = '\0';
-           } else {
-             if (strstr (buf, "!!!"))
-               continue;
-           }
-       }
-
-      if (!strncmp (buf, "!encodings", 10))
-       output_encodings (this);
-      else
-       {
-         struct string line;
-         ds_create(&line, buf);
-         fn_interp_vars(&line, ps_get_var);
-         ds_ltrim_spaces(&line);
-         len = ds_length(&line);
-         fwrite (ds_c_str(&line), len, 1, f->file);
-
-         ds_destroy(&line);
-
-         fputs (x->eol, f->file);
-       }
-    }
-  if (ferror (f->file))
-    msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno));
-  fclose (prologue_file);
-
-  free (prologue_fn);
-  free (buf);
-
-  local_free (dict[7].value);
-  local_free (dict[8].value);
-  local_free (dict[16].value);
-
-  if (ferror (f->file))
-    goto error;
-
-  msg (VM (2), _("%s: PostScript prologue read successfully."), this->name);
-  return 1;
-
-error:
-  msg (VM (1), _("%s: Error reading PostScript prologue."), this->name);
-  return 0;
-}
-
-/* Writes the string STRING to buffer DEST (of at least 288
-   characters) as a PostScript name object.  Returns a pointer
-   to the null terminator of the resultant string. */
-static char *
-quote_ps_name (char *dest, const char *string)
-{
-  const char *sp;
-
-  for (sp = string; *sp; sp++)
-    switch (*sp)
-      {
-      case 'a':
-      case 'f':
-      case 'k':
-      case 'p':
-      case 'u':
-      case 'b':
-      case 'g':
-      case 'l':
-      case 'q':
-      case 'v':
-      case 'c':
-      case 'h':
-      case 'm':
-      case 'r':
-      case 'w':
-      case 'd':
-      case 'i':
-      case 'n':
-      case 's':
-      case 'x':
-      case 'e':
-      case 'j':
-      case 'o':
-      case 't':
-      case 'y':
-      case 'z':
-      case 'A':
-      case 'F':
-      case 'K':
-      case 'P':
-      case 'U':
-      case 'B':
-      case 'G':
-      case 'L':
-      case 'Q':
-      case 'V':
-      case 'C':
-      case 'H':
-      case 'M':
-      case 'R':
-      case 'W':
-      case 'D':
-      case 'I':
-      case 'N':
-      case 'S':
-      case 'X':
-      case 'E':
-      case 'J':
-      case 'O':
-      case 'T':
-      case 'Y':
-      case 'Z':
-      case '@':
-      case '^':
-      case '_':
-      case '|':
-      case '!':
-      case '$':
-      case '&':
-      case ':':
-      case ';':
-      case '.':
-      case ',':
-      case '-':
-      case '+':
-       break;
-      default:
-       {
-         char *dp = dest;
-
-         *dp++ = '<';
-         for (sp = string; *sp && dp < &dest[256]; sp++)
-           {
-             sprintf (dp, "%02x", (unsigned char) *sp);
-             dp += 2;
-           }
-         return stpcpy (dp, ">cvn");
-       }
-      }
-  dest[0] = '/';
-  return stpcpy (&dest[1], string);
-}
-
-/* Adds the string STRING to buffer DEST as a PostScript quoted
-   string; returns a pointer to the null terminator added.  Will not
-   add more than 235 characters. */
-static char *
-quote_ps_string (char *dest, const char *string)
-{
-  const char *sp = string;
-  char *dp = dest;
-
-  *dp++ = '(';
-  for (; *sp && dp < &dest[235]; sp++)
-    if (*sp == '(')
-      dp = stpcpy (dp, "\\(");
-    else if (*sp == ')')
-      dp = stpcpy (dp, "\\)");
-    else if (*sp < 32 || (unsigned char) *sp > 127)
-      dp = spprintf (dp, "\\%3o", *sp);
-    else
-      *dp++ = *sp;
-  return stpcpy (dp, ")");
-}
-
-/* Writes the PostScript epilogue to file F. */
-static int
-preclose (struct file_ext *f)
-{
-  struct outp_driver *this = f->param;
-  struct ps_driver_ext *x = this->ext;
-  struct hsh_iterator iter;
-  struct font_entry *fe;
-
-  fprintf (f->file,
-          ("%%%%Trailer%s"
-           "%%%%Pages: %d%s"
-           "%%%%DocumentNeededResources:%s"),
-          x->eol, x->file_page_number, x->eol, x->eol);
-
-  for (fe = hsh_first (x->loaded, &iter); fe != NULL;
-       fe = hsh_next (x->loaded, &iter)) 
-    {
-      char buf[256], *cp;
-
-      cp = stpcpy (buf, "%%+ font ");
-      cp = quote_ps_string (cp, fe->font->internal_name);
-      strcpy (cp, x->eol);
-      fputs (buf, f->file);
-    }
-
-  hsh_destroy (x->loaded);
-  x->loaded = NULL;
-  hsh_destroy (x->combos);
-  x->combos = NULL;
-  x->last_font = NULL;
-  x->next_combo = 0;
-
-  fprintf (f->file, "%%EOF%s", x->eol);
-  if (ferror (f->file))
-    return 0;
-  return 1;
-}
-
-static int
-ps_open_page (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  assert (this->driver_open && !this->page_open);
-      
-  x->page_number++;
-  if (!fn_open_ext (&x->file))
-    {
-      if (errno)
-       msg (ME, _("PostScript output driver: %s: %s"), x->file.filename,
-            strerror (errno));
-      return 0;
-    }
-  x->file_page_number++;
-
-  hsh_destroy (x->combos);
-  x->combos = hsh_create (31, compare_ps_combo, hash_ps_combo,
-                         free_ps_combo, NULL);
-  x->last_font = NULL;
-  x->next_combo = 0;
-
-  fprintf (x->file.file,
-          "%%%%Page: %d %d%s"
-          "%%%%BeginPageSetup%s"
-          "/pg save def 0.001 dup scale%s",
-          x->page_number, x->file_page_number, x->eol,
-          x->eol,
-          x->eol);
-
-  if (x->orientation == OTN_LANDSCAPE)
-    fprintf (x->file.file,
-            "%d 0 translate 90 rotate%s",
-            x->w, x->eol);
-
-  if (x->bottom_margin != 0 || x->left_margin != 0)
-    fprintf (x->file.file,
-            "%d %d translate%s",
-            x->left_margin, x->bottom_margin, x->eol);
-
-  fprintf (x->file.file,
-          "/LW %d def/TW %d def %d setlinewidth%s"
-          "%%%%EndPageSetup%s",
-          x->line_width, x->line_width_thick, x->line_width, x->eol,
-          x->eol);
-
-  if (!ferror (x->file.file))
-    {
-      this->page_open = 1;
-      if (x->output_options & OPO_HEADERS)
-       draw_headers (this);
-    }
-
-  this->cp_y = 0;
-
-  return !ferror (x->file.file);
-}
-
-static int
-ps_close_page (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  assert (this->driver_open && this->page_open);
-  
-  if (x->line_opt)
-    dump_lines (this);
-
-  fprintf (x->file.file,
-          "%%PageTrailer%s"
-          "EP%s",
-          x->eol, x->eol);
-
-  this->page_open = 0;
-  return !ferror (x->file.file);
+  fputs ("%%PageTrailer\n"
+         "EP\n",
+         x->file);
 }
 
 static void
 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
 {
-  switch (s->type) 
+  switch (s->type)
     {
     case SOM_CHART:
       break;
     default:
-      assert(0);
-      break;
-    }
-}
-
-/* Lines. */
-
-/* qsort() comparison function for int tuples. */
-static int
-int_2_compare (const void *a_, const void *b_)
-{
-  const int *a = a_;
-  const int *b = b_;
-
-  return *a < *b ? -1 : *a > *b;
-}
-
-/* Hash table comparison function for cached lines. */
-static int
-compare_line (const void *a_, const void *b_, void *foo UNUSED)
-{
-  const struct line_form *a = a_;
-  const struct line_form *b = b_;
-
-  return a->ind < b->ind ? -1 : a->ind > b->ind;
-}
-
-/* Hash table hash function for cached lines. */
-static unsigned
-hash_line (const void *pa, void *foo UNUSED)
-{
-  const struct line_form *a = pa;
-
-  return a->ind;
-}
-
-/* Hash table free function for cached lines. */
-static void
-free_line (void *pa, void *foo UNUSED)
-{
-  free (pa);
-}
-
-/* Writes PostScript code to draw a line from (x1,y1) to (x2,y2) to
-   the output file. */
-#define dump_line(x1, y1, x2, y2)                      \
-       fprintf (ext->file.file, "%d %d %d %d L%s",     \
-                x1, YT (y1), x2, YT (y2), ext->eol)
-
-/* Write PostScript code to draw a thick line from (x1,y1) to (x2,y2)
-   to the output file. */
-#define dump_thick_line(x1, y1, x2, y2)                        \
-       fprintf (ext->file.file, "%d %d %d %d TL%s",    \
-                x1, YT (y1), x2, YT (y2), ext->eol)
-
-/* Writes a line of type TYPE to THIS driver's output file.  The line
-   (or its center, in the case of double lines) has its independent
-   axis coordinate at IND; it extends from DEP1 to DEP2 on the
-   dependent axis. */
-static void
-dump_fancy_line (struct outp_driver *this, int type, int ind, int dep1, int 
dep2)
-{
-  struct ps_driver_ext *ext = this->ext;
-  int ofs = ext->line_space / 2 + ext->line_width / 2;
-
-  switch (type)
-    {
-    case horz:
-      dump_line (dep1, ind, dep2, ind);
-      break;
-    case dbl_horz:
-      if (ext->output_options & OPO_DOUBLE_LINE)
-       {
-         dump_line (dep1, ind - ofs, dep2, ind - ofs);
-         dump_line (dep1, ind + ofs, dep2, ind + ofs);
-       }
-      else
-       dump_thick_line (dep1, ind, dep2, ind);
-      break;
-    case spl_horz:
-      assert (0);
-    case vert:
-      dump_line (ind, dep1, ind, dep2);
-      break;
-    case dbl_vert:
-      if (ext->output_options & OPO_DOUBLE_LINE)
-       {
-         dump_line (ind - ofs, dep1, ind - ofs, dep2);
-         dump_line (ind + ofs, dep1, ind + ofs, dep2);
-       }
-      else
-       dump_thick_line (ind, dep1, ind, dep2);
-      break;
-    case spl_vert:
-      assert (0);
-    default:
-      assert (0);
-    }
-}
-
-#undef dump_line
-
-/* Writes all the cached lines to the output file, then clears the
-   cache. */
-static void
-dump_lines (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  struct hsh_iterator iter;
-  int type;
-
-  for (type = 0; type < n_line_types; type++)
-    {
-      struct line_form *line;
-
-      if (x->lines[type] == NULL) 
-        continue;
-
-      for (line = hsh_first (x->lines[type], &iter); line != NULL;
-           line = hsh_next (x->lines[type], &iter)) 
-        {
-         int i;
-         int lo = INT_MIN, hi;
-
-         qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare);
-         lo = line->dep[0][0];
-         hi = line->dep[0][1];
-         for (i = 1; i < line->ndep; i++)
-           if (line->dep[i][0] <= hi + 1)
-             {
-               int min_hi = line->dep[i][1];
-               if (min_hi > hi)
-                 hi = min_hi;
-             }
-           else
-             {
-               dump_fancy_line (this, type, line->ind, lo, hi);
-               lo = line->dep[i][0];
-               hi = line->dep[i][1];
-             }
-         dump_fancy_line (this, type, line->ind, lo, hi);
-       }
-
-      hsh_destroy (x->lines[type]);
-      x->lines[type] = NULL;
-    }
-}
-
-/* (Same args as dump_fancy_line()).  Either dumps the line directly
-   to the output file, or adds it to the cache, depending on the
-   user-selected line optimization mode. */
-static void
-line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
-{
-  struct ps_driver_ext *ext = this->ext;
-  struct line_form **f;
-
-  assert (dep2 >= dep1);
-  if (ext->line_opt == 0)
-    {
-      dump_fancy_line (this, type, ind, dep1, dep2);
-      return;
-    }
-
-  if (ext->lines[type] == NULL)
-    ext->lines[type] = hsh_create (31, compare_line, hash_line,
-                                  free_line, NULL);
-  f = (struct line_form **) hsh_probe (ext->lines[type], &ind);
-  if (*f == NULL)
-    {
-      *f = xmalloc (sizeof **f + sizeof (int[15][2]));
-      (*f)->ind = ind;
-      (*f)->mdep = 16;
-      (*f)->ndep = 1;
-      (*f)->dep[0][0] = dep1;
-      (*f)->dep[0][1] = dep2;
-      return;
-    }
-  if ((*f)->ndep >= (*f)->mdep)
-    {
-      (*f)->mdep += 16;
-      *f = xrealloc (*f, sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1));
-    }
-  (*f)->dep[(*f)->ndep][0] = dep1;
-  (*f)->dep[(*f)->ndep][1] = dep2;
-  (*f)->ndep++;
-}
-
-static void
-ps_line_horz (struct outp_driver *this, const struct rect *r,
-             const struct color *c UNUSED, int style)
-{
-  /* Must match output.h:OUTP_L_*. */
-  static const int types[OUTP_L_COUNT] =
-  {-1, horz, dbl_horz, spl_horz};
-
-  int y = (r->y1 + r->y2) / 2;
-
-  assert (this->driver_open && this->page_open);
-  assert (style >= 0 && style < OUTP_L_COUNT);
-  style = types[style];
-  if (style != -1)
-    line (this, style, y, r->x1, r->x2);
-}
-
-static void
-ps_line_vert (struct outp_driver *this, const struct rect *r,
-             const struct color *c UNUSED, int style)
-{
-  /* Must match output.h:OUTP_L_*. */
-  static const int types[OUTP_L_COUNT] =
-  {-1, vert, dbl_vert, spl_vert};
-
-  int x = (r->x1 + r->x2) / 2;
-
-  assert (this->driver_open && this->page_open);
-  assert (style >= 0 && style < OUTP_L_COUNT);
-  style = types[style];
-  if (style != -1)
-    line (this, style, x, r->y1, r->y2);
-}
-
-#define L (style->l != OUTP_L_NONE)
-#define R (style->r != OUTP_L_NONE)
-#define T (style->t != OUTP_L_NONE)
-#define B (style->b != OUTP_L_NONE)
-
-static void
-ps_line_intersection (struct outp_driver *this, const struct rect *r,
-                     const struct color *c UNUSED,
-                     const struct outp_styles *style)
-{
-  struct ps_driver_ext *ext = this->ext;
-
-  int x = (r->x1 + r->x2) / 2;
-  int y = (r->y1 + r->y2) / 2;
-  int ofs = (ext->line_space + ext->line_width) / 2;
-  int x1 = x - ofs, x2 = x + ofs;
-  int y1 = y - ofs, y2 = y + ofs;
-
-  assert (this->driver_open && this->page_open);
-  assert (!((style->l != style->r && style->l != OUTP_L_NONE
-            && style->r != OUTP_L_NONE)
-           || (style->t != style->b && style->t != OUTP_L_NONE
-               && style->b != OUTP_L_NONE)));
-
-  switch ((style->l | style->r) | ((style->t | style->b) << 8))
-    {
-    case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8):
-    case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8):
-    case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8):
-      if (L)
-       line (this, horz, y, r->x1, x);
-      if (R)
-       line (this, horz, y, x, r->x2);
-      if (T)
-       line (this, vert, x, r->y1, y);
-      if (B)
-       line (this, vert, x, y, r->y2);
-      break;
-    case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8):
-    case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8):
-      if (L)
-       line (this, horz, y, r->x1, x1);
-      if (R)
-       line (this, horz, y, x2, r->x2);
-      if (T)
-       line (this, dbl_vert, x, r->y1, y);
-      if (B)
-       line (this, dbl_vert, x, y, r->y2);
-      if ((L && R) && !(T && B))
-       line (this, horz, y, x1, x2);
-      break;
-    case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8):
-    case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8):
-      if (L)
-       line (this, dbl_horz, y, r->x1, x);
-      if (R)
-       line (this, dbl_horz, y, x, r->x2);
-      if (T)
-       line (this, vert, x, r->y1, y);
-      if (B)
-       line (this, vert, x, y, r->y2);
-      if ((T && B) && !(L && R))
-       line (this, vert, x, y1, y2);
-      break;
-    case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8):
-      if (L)
-       line (this, dbl_horz, y, r->x1, x);
-      if (R)
-       line (this, dbl_horz, y, x, r->x2);
-      if (T)
-       line (this, dbl_vert, x, r->y1, y);
-      if (B)
-       line (this, dbl_vert, x, y, r->y2);
-      if (T && B && !L)
-       line (this, vert, x1, y1, y2);
-      if (T && B && !R)
-       line (this, vert, x2, y1, y2);
-      if (L && R && !T)
-       line (this, horz, y1, x1, x2);
-      if (L && R && !B)
-       line (this, horz, y2, x1, x2);
+      abort ();
       break;
-    default:
-      assert (0);
     }
 }
-
-static void
-ps_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED,
-       const struct color *bord UNUSED, const struct color *fill UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-
-static void 
-ps_polyline_begin (struct outp_driver *this UNUSED,
-                  const struct color *c UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-static void 
-ps_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-static void 
-ps_polyline_end (struct outp_driver *this UNUSED)
-{
-  assert (this->driver_open && this->page_open);
-}
-
-/* Returns the width of string S for THIS driver. */
-static int
-text_width (struct outp_driver *this, char *s)
-{
-  struct outp_text text;
-
-  text.options = OUTP_T_JUST_LEFT;
-  ls_init (&text.s, s, strlen (s));
-  this->class->text_metrics (this, &text);
-  return text.h;
-}
-
-/* Write string S at location (X,Y) with width W for THIS driver. */
-static void
-out_text_plain (struct outp_driver *this, char *s, int x, int y, int w)
-{
-  struct outp_text text;
-
-  text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
-  ls_init (&text.s, s, strlen (s));
-  text.h = w;
-  text.v = this->font_height;
-  text.x = x;
-  text.y = y;
-  this->class->text_draw (this, &text);
-}
-
-/* Draw top of page headers for THIS driver. */
-static void
-draw_headers (struct outp_driver *this)
-{
-  struct ps_driver_ext *ext = this->ext;
-  
-  struct font_entry *old_current = ext->current;
-  char *old_family = xstrdup (ext->family); /* FIXME */
-  int old_size = ext->size;
-
-  int fh = this->font_height;
-  int y = -3 * fh;
-
-  fprintf (ext->file.file, "%d %d %d %d GB%s",
-          0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter),
-          ext->eol);
-  this->class->text_set_font_family (this, "T");
-
-  y += ext->line_width + ext->line_gutter;
-  
-  {
-    int rh_width;
-    char buf[128];
-
-    sprintf (buf, _("%s - Page %d"), get_start_date (), ext->page_number);
-    rh_width = text_width (this, buf);
-
-    out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
-                   y, rh_width);
-
-    if (outp_title && outp_subtitle)
-      out_text_plain (this, outp_title, this->prop_em_width, y,
-                     this->width - 3 * this->prop_em_width - rh_width);
-
-    y += fh;
-  }
-  
-  {
-    int rh_width;
-    char buf[128];
-    char *string = outp_subtitle ? outp_subtitle : outp_title;
-
-    sprintf (buf, "%s - %s", version, host_system);
-    rh_width = text_width (this, buf);
-    
-    out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
-                   y, rh_width);
-
-    if (string)
-      out_text_plain (this, string, this->prop_em_width, y,
-                     this->width - 3 * this->prop_em_width - rh_width);
-
-    y += fh;
-  }
-
-  ext->current = old_current;
-  free (ext->family);
-  ext->family = old_family;
-  ext->size = old_size;
-}
-
 
-/* Text. */
-
-static void
-ps_text_set_font_by_name (struct outp_driver *this, const char *dit)
-{
-  struct ps_driver_ext *x = this->ext;
-  struct font_entry *fe;
-
-  assert (this->driver_open && this->page_open);
-  
-  /* Short-circuit common fonts. */
-  if (!strcmp (dit, "PROP"))
-    {
-      x->current = x->prop;
-      x->size = x->font_size;
-      return;
-    }
-  else if (!strcmp (dit, "FIXED"))
-    {
-      x->current = x->fixed;
-      x->size = x->font_size;
-      return;
-    }
-
-  /* Find font_desc corresponding to Groff name dit. */
-  fe = hsh_find (x->loaded, &dit);
-  if (fe == NULL)
-    fe = load_font (this, dit);
-  x->current = fe;
-}
-
-static void
-ps_text_set_font_by_position (struct outp_driver *this, int pos)
-{
-  struct ps_driver_ext *x = this->ext;
-  char *dit;
-
-  assert (this->driver_open && this->page_open);
-
-  /* Determine font name by suffixing position string to font family
-     name. */
-  {
-    char *cp;
-
-    dit = local_alloc (strlen (x->family) + 3);
-    cp = stpcpy (dit, x->family);
-    switch (pos)
-      {
-      case OUTP_F_R:
-       *cp++ = 'R';
-       break;
-      case OUTP_F_I:
-       *cp++ = 'I';
-       break;
-      case OUTP_F_B:
-       *cp++ = 'B';
-       break;
-      case OUTP_F_BI:
-       *cp++ = 'B';
-       *cp++ = 'I';
-       break;
-      default:
-       assert(0);
-      }
-    *cp++ = 0;
-  }
-  
-  /* Find font_desc corresponding to Groff name dit. */
-  {
-    struct font_entry *fe = hsh_find (x->loaded, &dit);
-    if (fe == NULL)
-      fe = load_font (this, dit);
-    x->current = fe;
-  }
-
-  local_free (dit);
-}
-
+/* Draws a line from (x0,y0) to (x1,y1). */
 static void
-ps_text_set_font_family (struct outp_driver *this, const char *s)
+dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
 {
-  struct ps_driver_ext *x = this->ext;
-
-  assert (this->driver_open && this->page_open);
-  
-  free(x->family);
-  x->family = xstrdup (s);
+  struct ps_driver_ext *ext = this->ext;
+  fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
 }
 
-static const char *
-ps_text_get_font_name (struct outp_driver *this)
+/* 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)
 {
-  struct ps_driver_ext *x = this->ext;
-
-  assert (this->driver_open && this->page_open);
-  return x->current->font->name;
+  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);
+    }
 }
 
-static const char *
-ps_text_get_font_family (struct outp_driver *this)
+/* 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)
 {
-  struct ps_driver_ext *x = this->ext;
-  
-  assert (this->driver_open && this->page_open);
-  return x->family;
+  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);
+    }
 }
 
-static int
-ps_text_set_size (struct outp_driver *this, int size)
-{
-  struct ps_driver_ext *x = this->ext;
+/* 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;
 
-  assert (this->driver_open && this->page_open);
-  x->size = PSUS / 72000 * size;
-  return 1;
-}
+  /* Offset from center of each line in a pair of double lines. */
+  int double_line_ofs = (ext->line_space + ext->line_width) / 2;
 
-static int
-ps_text_get_size (struct outp_driver *this, int *em_width)
-{
-  struct ps_driver_ext *x = this->ext;
+  /* 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;
 
-  assert (this->driver_open && this->page_open);
-  if (em_width)
-    *em_width = (x->current->font->space_width * x->size) / 1000;
-  return x->size / (PSUS / 72000);
-}
+  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);
+    }
 
-/* An output character. */
-struct output_char
-  {
-    struct font_entry *font;   /* Font of character. */
-    int size;                  /* Size of character. */
-    int x, y;                  /* Location of character. */
-    unsigned char ch;          /* Character. */
-    char separate;             /* Must be separate from previous char. */
-  };
+  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);
+    }
+}
 
-/* Hash table comparison function for ps_combo structs. */
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+   and with the given JUSTIFICATION for THIS driver. */
 static int
-compare_ps_combo (const void *pa, const void *pb, void *foo UNUSED)
+draw_text (struct outp_driver *this,
+           const char *string, int x, int y, int max_width,
+           enum outp_justification justification)
 {
-  const struct ps_font_combo *a = pa;
-  const struct ps_font_combo *b = pb;
+  struct outp_text text;
+  int width;
 
-  return !((a->font == b->font) && (a->size == b->size));
+  text.font = OUTP_PROPORTIONAL;
+  text.justification = justification;
+  ls_init (&text.string, (char *) string, strlen (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;
 }
 
-/* Hash table hash function for ps_combo structs. */
-static unsigned
-hash_ps_combo (const void *pa, void *foo UNUSED)
-{
-  const struct ps_font_combo *a = pa;
-  unsigned name_hash = hsh_hash_string (a->font->font->internal_name);
-  return name_hash ^ hsh_hash_int (a->size);
+/* 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);
 }
 
-/* Hash table free function for ps_combo structs. */
+/* Draw top of page headers for THIS driver. */
 static void
-free_ps_combo (void *a, void *foo UNUSED)
+draw_headers (struct outp_driver *this)
 {
-  free (a);
-}
+  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;
 
-/* Causes PostScript code to be output that switches to the font
-   CP->FONT and font size CP->SIZE.  The first time a particular
-   font/size combination is used on a particular page, this involves
-   outputting PostScript code to load the font. */
+  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);
+}
+
+/* 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
-switch_font (struct outp_driver *this, const struct output_char *cp)
+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 ps_font_combo srch, **fc;
+  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;
 
-  srch.font = cp->font;
-  srch.size = cp->size;
+  fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
 
-  fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch);
-  if (*fc)
+  if (ext->last_font != font)
     {
-      fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol);
+      ext->last_font = font;
+      fprintf (ext->file, "F%d setfont\n", font);
     }
-  else
+
+  ds_init (&out, 0);
+  for (i = 0; i < char_cnt; i = j)
     {
-      char *filename;
-      struct ps_encoding *encoding;
-      char buf[512], *bp;
-
-      *fc = xmalloc (sizeof **fc);
-      (*fc)->font = cp->font;
-      (*fc)->size = cp->size;
-      (*fc)->index = ext->next_combo++;
+      for (j = i + 1; j < char_cnt; j++)
+        if (kerns[j] != 0)
+          break;
 
-      filename = find_encoding_file (this, cp->font->font->encoding);
-      if (filename)
-       {
-         encoding = get_encoding (this, filename);
-         free (filename);
-       }
-      else
-       {
-         msg (IE, _("PostScript driver: Cannot find encoding `%s' for "
-              "PostScript font `%s'."), cp->font->font->encoding,
-              cp->font->font->internal_name);
-         encoding = default_encoding (this);
-       }
+      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_c_str (&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. */
 
-      if (cp->font != ext->fixed && cp->font != ext->prop)
-       {
-         bp = stpcpy (buf, "%%IncludeResource: font ");
-         bp = quote_ps_string (bp, cp->font->font->internal_name);
-         bp = stpcpy (bp, ext->eol);
-       }
-      else
-       bp = buf;
+    /* Statistics. */
+    int max_width;             /* Widest line so far. */
+  };
 
-      bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index,
-                    cp->size);
-      bp = quote_ps_name (bp, cp->font->font->internal_name);
-      sprintf (bp, " SF%s", ext->eol);
-      fputs (buf, ext->file.file);
-    }
-  ext->last_font = *fc;
-}
-
-/* (write_text) Writes the accumulated line buffer to the output
-   file. */
-#define output_line()                          \
-       do                                      \
-         {                                     \
-            lp = stpcpy (lp, ext->eol);                \
-           *lp = 0;                            \
-           fputs (line, ext->file.file);       \
-           lp = line;                          \
-         }                                     \
-        while (0)
-
-/* (write_text) Adds the string representing number X to the line
-   buffer, flushing the buffer to disk beforehand if necessary. */
-#define put_number(X)                          \
-       do                                      \
-         {                                     \
-           int n = nsprintf (number, "%d", X); \
-           if (n + lp > &line[75])             \
-             output_line ();                   \
-           lp = stpcpy (lp, number);           \
-         }                                     \
-       while (0)
-
-/* Outputs PostScript code to THIS driver's output file to display the
-   characters represented by the output_char's between CP and END,
-   using the associated outp_text T to determine formatting.  WIDTH is
-   the width of the output region; WIDTH_LEFT is the amount of the
-   WIDTH that is not taken up by text (so it can be used to determine
-   justification). */
+/* Adjusts S to complete a line of text,
+   and draws the current line if appropriate. */
 static void
-write_text (struct outp_driver *this,
-           const struct output_char *cp, const struct output_char *end,
-           struct outp_text *t, int width UNUSED, int width_left)
+finish_line (struct outp_driver *this, struct text_state *s)
 {
-  struct ps_driver_ext *ext = this->ext;
-  int ofs;
-
-  int last_y;
-
-  char number[INT_STRLEN_BOUND (int) + 1];
-  char line[80];
-  char *lp;
+  int width;
 
-  switch (t->options & OUTP_T_JUST_MASK)
+  if (s->draw)
     {
-    case OUTP_T_JUST_LEFT:
-      ofs = 0;
-      break;
-    case OUTP_T_JUST_RIGHT:
-      ofs = width_left;
-      break;
-    case OUTP_T_JUST_CENTER:
-      ofs = width_left / 2;
-      break;
-    default:
-      assert (0);
-      abort ();
+      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;
     }
 
-  lp = line;
-  last_y = INT_MIN;
-  while (cp < end)
-    {
-      int x = cp->x + ofs;
-      int y = cp->y + (cp->font->font->ascent * cp->size / 1000);
-
-      if (ext->last_font == NULL
-         || cp->font != ext->last_font->font
-         || cp->size != ext->last_font->size)
-       switch_font (this, cp);
-
-      *lp++ = '(';
-      do
-       {
-         /* PORTME! */
-         static unsigned char literal_chars[ODA_COUNT][32] =
-         {
-           {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-           },
-           {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-           },
-           {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-           }
-         };
-
-         if (TEST_BIT (literal_chars[ext->data], cp->ch))
-           *lp++ = cp->ch;
-         else
-           switch ((char) cp->ch)
-             {
-             case '(':
-               lp = stpcpy (lp, "\\(");
-               break;
-             case ')':
-               lp = stpcpy (lp, "\\)");
-               break;
-             default:
-               lp = spprintf (lp, "\\%03o", cp->ch);
-               break;
-             }
-         cp++;
-       }
-      while (cp < end && lp < &line[70] && cp->separate == 0);
-      *lp++ = ')';
-
-      put_number (x);
+  /* Update maximum width. */
+  width = s->text->h - s->width_left;
+  if (width > s->max_width)
+    s->max_width = width;
 
-      if (y != last_y)
-       {
-         *lp++ = ' ';
-         put_number (YT (y));
-         *lp++ = ' ';
-         *lp++ = 'S';
-         last_y = y;
-       }
-      else
-       {
-         *lp++ = ' ';
-         *lp++ = 'T';
-       }
+  /* Move to next line. */
+  s->width_left = s->text->h;
+  s->height_left -= this->font_height;
 
-      if (lp >= &line[70])
-       output_line ();
-    }
-  if (lp != line)
-    output_line ();
+  /* No spaces on this line yet. */
+  s->space_char = NULL;
 }
 
-#undef output_line
-#undef put_number
-
-/* Displays the text in outp_text T, if DRAW is nonzero; or, merely
-   determine the text metrics, if DRAW is zero. */
+/* 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, struct outp_text *t, int draw)
+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;
 
-  /* Output. */
-  struct output_char *buf;     /* Output buffer. */
-  struct output_char *buf_end; /* End of output buffer. */
-  struct output_char *buf_loc; /* Current location in output buffer. */
-
-  /* Saved state. */
-  struct font_entry *old_current = ext->current;
-  char *old_family = xstrdup (ext->family); /* FIXME */
-  int old_size = ext->size;
-
-  /* Input string. */
-  char *cp, *end;
-
-  /* Current location. */
-  int x, y;
-
-  /* Keeping track of what's left over. */
-  int width;                   /* Width available for characters. */
-  int width_left, height_left; /* Width, height left over. */
-  int max_height;              /* Tallest character on this line so far. */
-
-  /* Previous character. */
-  int prev_char;
-
-  /* Information about location of previous space. */
-  char *space_char;            /* Character after space. */
-  struct output_char *space_buf_loc; /* Buffer location after space. */
-  int space_width_left;                /* Width of characters before space. */
-
-  /* Name of the current character. */
-  const char *char_name;
-  char local_char_name[2] = {0, 0};
-
-  local_char_name[0] = local_char_name[1] = 0;
-
-  buf = local_alloc (sizeof *buf * 128);
-  buf_end = &buf[128];
-  buf_loc = buf;
-
-  assert (!ls_null_p (&t->s));
-  cp = ls_c_str (&t->s);
-  end = ls_end (&t->s);
-  if (draw)
-    {
-      x = t->x;
-      y = t->y;
-    }
-  else
-    x = y = 0;
-  width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX;
-  height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX;
-  max_height = 0;
-  prev_char = -1;
-  space_char = NULL;
-  space_buf_loc = NULL;
-  space_width_left = 0;
-  
+  s.text = text;
+  s.draw = draw;
 
-  if (!width || !height_left)
-    goto exit;
+  s.glyphs = NULL;
+  s.glyph_kerns = NULL;
+  glyph_cap = 0;
 
-  while (cp < end)
-    {
-      struct char_metrics *metric;
-      int cur_char;
-      int kern_amt;
-      int char_width;
-      int separate = 0;
+  s.glyph_cnt = 0;
+  s.width_left = s.text->h;
+  s.height_left = s.text->v;
 
-      /* Set char_name to the name of the character or ligature at
-         *cp. */
-      local_char_name[0] = *cp;
-      char_name = local_char_name;
-      if (ext->current->font->ligatures && *cp == 'f')
-       {
-         int lig = 0;
-          char_name = NULL;
+  s.space_char = 0;
 
-         if (cp < end - 1)
-           switch (cp[1])
-             {
-             case 'i':
-               lig = LIG_fi, char_name = "fi";
-               break;
-             case 'l':
-               lig = LIG_fl, char_name = "fl";
-               break;
-             case 'f':
-               if (cp < end - 2)
-                 switch (cp[2])
-                   {
-                   case 'i':
-                     lig = LIG_ffi, char_name = "ffi";
-                     goto got_ligature;
-                   case 'l':
-                     lig = LIG_ffl, char_name = "ffl";
-                     goto got_ligature;
-                   }
-               lig = LIG_ff, char_name = "ff";
-             got_ligature:
-               break;
-             }
-         if ((lig & ext->current->font->ligatures) == 0)
-           {
-             local_char_name[0] = *cp; /* 'f' */
-             char_name = local_char_name;
-           }
-       }
-      else if (*cp == '\n')
-       {
-         if (draw)
-           {
-             write_text (this, buf, buf_loc, t, width, width_left);
-             buf_loc = buf;
-             x = t->x;
-             y += max_height;
-           }
-
-         width_left = width;
-         height_left -= max_height;
-         max_height = 0;
-         kern_amt = 0;
-         separate = 1;
-         cp++;
-
-         /* FIXME: when we're page buffering it will be necessary to
-            set separate to 1. */
-         continue;
-       }
-      cp += strlen (char_name);
+  s.max_width = 0;
 
-      /* Figure out what size this character is, and what kern
-         adjustment we need. */
-      cur_char = font_char_name_to_index (char_name);
-      metric = font_get_char_metrics (ext->current->font, cur_char);
-      if (!metric)
-       {
-         static struct char_metrics m;
-         metric = &m;
-         m.width = ext->current->font->space_width;
-         m.code = *char_name;
-       }
-      kern_amt = font_get_kern_adjust (ext->current->font, prev_char,
-                                      cur_char);
-      if (kern_amt)
-       {
-         kern_amt = (kern_amt * ext->size / 1000);
-         separate = 1;
-       }
-      char_width = metric->width * ext->size / 1000;
+  cp = ls_c_str (&s.text->string);
+  while (s.height_left >= this->font_height && cp < ls_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 < ls_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_char == space_index && buf_loc > buf)
+      if (cur->code == ' ' && cp > ls_c_str (&s.text->string))
        {
-         space_char = cp;
-         space_buf_loc = buf_loc;
-         space_width_left = width_left;
+         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;
+            }
        }
 
-      /* Drop down to a new line if there's no room left on this
-         line. */
-      if (char_width + kern_amt > width_left)
-       {
-         /* Regress to previous space, if any. */
-         if (space_char)
-           {
-             cp = space_char;
-             width_left = space_width_left;
-             buf_loc = space_buf_loc;
-           }
-
-         if (draw)
-           {
-             write_text (this, buf, buf_loc, t, width, width_left);
-             buf_loc = buf;
-             x = t->x;
-             y += max_height;
-           }
-
-         width_left = width;
-         height_left -= max_height;
-         max_height = 0;
-         kern_amt = 0;
-
-         if (space_char)
-           {
-             space_char = NULL;
-             prev_char = -1;
-             /* FIXME: when we're page buffering it will be
-                necessary to set separate to 1. */
-             continue;
-           }
-         separate = 1;
-       }
-      if (ext->size > max_height)
-       max_height = ext->size;
-      if (max_height > height_left)
-       goto exit;
+      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++;
 
-      /* Actually draw the character. */
-      if (draw)
-       {
-         if (buf_loc >= buf_end)
-           {
-             int buf_len = buf_end - buf;
-
-             if (buf_len == 128)
-               {
-                 struct output_char *new_buf;
-
-                 new_buf = xmalloc (sizeof *new_buf * 256);
-                 memcpy (new_buf, buf, sizeof *new_buf * 128);
-                 buf_loc = new_buf + 128;
-                 buf_end = new_buf + 256;
-                 local_free (buf);
-                 buf = new_buf;
-               }
-             else
-               {
-                 buf = xnrealloc (buf, buf_len * 2, sizeof *buf);
-                 buf_loc = buf + buf_len;
-                 buf_end = buf + buf_len * 2;
-               }
-           }
-
-         x += kern_amt;
-         buf_loc->font = ext->current;
-         buf_loc->size = ext->size;
-         buf_loc->x = x;
-         buf_loc->y = y;
-         buf_loc->ch = metric->code;
-         buf_loc->separate = separate;
-         buf_loc++;
-         x += char_width;
-       }
+      s.width_left -= char_width + kern_adjust;
+    }
+  if (s.height_left >= this->font_height && s.glyph_cnt > 0)
+    finish_line (this, &s);
 
-      /* Prepare for next iteration. */
-      width_left -= char_width + kern_amt;
-      prev_char = cur_char;
-    }
-  height_left -= max_height;
-  if (buf_loc > buf && draw)
-    write_text (this, buf, buf_loc, t, width, width_left);
-
-exit:
-  if (!(t->options & OUTP_T_HORZ))
-    t->h = INT_MAX - width_left;
-  if (!(t->options & OUTP_T_VERT))
-    t->v = INT_MAX - height_left;
-  else
-    t->v -= height_left;
-  if (buf_end - buf == 128)
-    local_free (buf);
-  else
-    free (buf);
-  ext->current = old_current;
-  free (ext->family);
-  ext->family = old_family;
-  ext->size = old_size;
+  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, struct outp_text *t)
+ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
+                 int *width, int *height)
 {
-  assert (this->driver_open && this->page_open);
-  text (this, t, 0);
+  text (this, t, false, width, height);
 }
 
 static void
-ps_text_draw (struct outp_driver *this, struct outp_text *t)
+ps_text_draw (struct outp_driver *this, const struct outp_text *t)
 {
-  assert (this->driver_open && this->page_open);
-  text (this, t, 1);
+  assert (this->page_open);
+  text (this, t, true, NULL, NULL);
 }
 
-/* Font loader. */
-
-/* Translate a filename to a font. */
-struct filename2font
-  {
-    char *filename;            /* Normalized filename. */
-    struct font_desc *font;
-  };
-
-/* Table of `filename2font's. */
-static struct hsh_table *ps_fonts;
-
-/* Hash table comparison function for filename2font structs. */
-static int
-compare_filename2font (const void *a, const void *b, void *param UNUSED)
-{
-  return strcmp (((struct filename2font *) a)->filename,
-                ((struct filename2font *) b)->filename);
-}
-
-/* Hash table hash function for filename2font structs. */
-static unsigned
-hash_filename2font (const void *f2f_, void *param UNUSED)
-{
-  const struct filename2font *f2f = f2f_;
-  return hsh_hash_string (f2f->filename);
-}
-
-/* Initializes the global font list by creating the hash table for
-   translation of filenames to font_desc structs. */
-static void
-init_fonts (void)
-{
-  ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font,
-                        NULL, NULL);
-}
-
-static void
-done_fonts (void)
-{
- hsh_destroy (ps_fonts);
-}
-
-/* Loads the font having Groff name DIT into THIS driver instance.
-   Specifically, adds it into the THIS driver's `loaded' hash
-   table. */
-static struct font_entry *
-load_font (struct outp_driver *this, const char *dit)
-{
-  struct ps_driver_ext *x = this->ext;
-  char *filename1, *filename2;
-  void **entry;
-  struct font_entry *fe;
-
-  filename1 = find_ps_file (this, dit);
-  if (!filename1)
-    filename1 = xstrdup (dit);
-  filename2 = fn_normalize (filename1);
-  free (filename1);
-
-  entry = hsh_probe (ps_fonts, &filename2);
-  if (*entry == NULL)
-    {
-      struct filename2font *f2f;
-      struct font_desc *f = groff_read_font (filename2);
-
-      if (f == NULL)
-       {
-         if (x->fixed)
-           f = x->fixed->font;
-         else
-           f = default_font ();
-       }
-      
-      f2f = xmalloc (sizeof *f2f);
-      f2f->filename = filename2;
-      f2f->font = f;
-      *entry = f2f;
-    }
-  else
-    free (filename2);
-
-  fe = xmalloc (sizeof *fe);
-  fe->dit = xstrdup (dit);
-  fe->font = ((struct filename2font *) * entry)->font;
-  *hsh_probe (x->loaded, &dit) = fe;
-
-  return fe;
-}
-
 static void
 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
 {
@@ -2884,12 +1100,12 @@
   int x_origin, y_origin;
 
   ch->file = tmpfile ();
-  if (ch->file == NULL) 
+  if (ch->file == NULL)
     {
       ch->lp = NULL;
       return;
     }
-  
+
   size = this->width < this->length ? this->width : this->length;
   x_origin = x->left_margin + (size - this->width) / 2;
   y_origin = x->bottom_margin + (size - this->length) / 2;
@@ -2905,7 +1121,7 @@
 #endif
 }
 
-static void 
+static void
 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
 {
 #ifndef NO_CHARTS
@@ -2913,103 +1129,311 @@
   char buf[BUFSIZ];
   static int doc_num = 0;
 
-  if (this->page_open) 
-    {
-      this->class->close_page (this);
-      this->page_open = 0; 
-    }
-  this->class->open_page (this);
-  fprintf (x->file.file,
-           "/sp save def%s"
-           "%d %d translate 1000 dup scale%s"
-           "userdict begin%s"
-           "/showpage { } def%s"
-           "0 setgray 0 setlinecap 1 setlinewidth%s"
-           "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear%s"
-           "%%%%BeginDocument: %d%s",
-           x->eol,
-           -x->left_margin, -x->bottom_margin, x->eol,
-           x->eol,
-           x->eol,
-           x->eol,
-           x->eol,
-           doc_num++, x->eol);
+  outp_eject_page (this);
+  fprintf (x->file,
+           "/sp save def\n"
+           "%d %d translate 1000 dup scale\n"
+           "userdict begin\n"
+           "/showpage { } def\n"
+           "0 setgray 0 setlinecap 1 setlinewidth\n"
+           "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
+           "%%%%BeginDocument: %d\n",
+           -x->left_margin, -x->bottom_margin,
+           doc_num++);
 
   rewind (ch->file);
-  while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file.file))
+  while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
     continue;
   fclose (ch->file);
 
-  fprintf (x->file.file,
-           "%%%%EndDocument%s"
-           "end%s"
-           "sp restore%s",
-           x->eol,
-           x->eol,
-           x->eol);
-  this->class->close_page (this);
-  this->page_open = 0;
+  fputs ("%%%%EndDocument\n"
+         "end\n"
+         "sp restore\n",
+         x->file);
+  outp_close_page (this);
 #endif
 }
+
+static void embed_font (struct outp_driver *this, struct font *font);
+static void reencode_font (struct outp_driver *this, struct font *font);
 
-/* PostScript driver class. */
-struct outp_class postscript_class =
+/* 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)
 {
-  "postscript",
-  MAGIC_PS,
-  0,
+  if (font != NULL)
+    {
+      afm_close (font->metrics);
+      free (font->embed_fn);
+      free (font->encoding_fn);
+      free (font);
+    }
+}
 
-  ps_open_global,
-  ps_close_global,
-  ps_font_sizes,
-
-  ps_preopen_driver,
-  ps_option,
-  ps_postopen_driver,
-  ps_close_driver,
+/* 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;
 
-  ps_open_page,
-  ps_close_page,
+  if (font->embed_fn != NULL)
+    embed_font (this, font);
+  else
+    fprintf (x->file, "%%%%IncludeResource: font %s\n",
+             afm_get_findfont_name (font->metrics));
 
-  ps_submit,
+  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);
 
-  ps_line_horz,
-  ps_line_vert,
-  ps_line_intersection,
-
-  ps_box,
-  ps_polyline_begin,
-  ps_polyline_point,
-  ps_polyline_end,
-
-  ps_text_set_font_by_name,
-  ps_text_set_font_by_position,
-  ps_text_set_font_family,
-  ps_text_get_font_name,
-  ps_text_get_font_family,
-  ps_text_set_size,
-  ps_text_get_size,
-  ps_text_metrics,
-  ps_text_draw,
+  if (font->encoding_fn != NULL)
+    reencode_font (this, font);
 
-  ps_chart_initialise,
-  ps_chart_finalise
-};
+  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;
 
-/* EPSF driver class.  FIXME: Probably doesn't work right. */
-struct outp_class epsf_class =
+  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 (&line, 0);
+  while (ds_get_config_line (file, &line, &line_number))
+    {
+      char *pschar, *code;
+      char *save_ptr, *tail;
+      int code_val;
+
+      if (ds_is_empty (&line) == 0)
+        continue;
+
+      pschar = strtok_r (ds_c_str (&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. */
+struct outp_class postscript_class =
 {
-  "epsf",
-  MAGIC_EPSF,
+  "postscript",
   0,
 
-  ps_open_global,
-  ps_close_global,
-  ps_font_sizes,
-
-  ps_preopen_driver,
-  ps_option,
-  ps_postopen_driver,
+  ps_open_driver,
   ps_close_driver,
 
   ps_open_page,
@@ -3017,26 +1441,10 @@
 
   ps_submit,
 
-  ps_line_horz,
-  ps_line_vert,
-  ps_line_intersection,
-
-  ps_box,
-  ps_polyline_begin,
-  ps_polyline_point,
-  ps_polyline_end,
-
-  ps_text_set_font_by_name,
-  ps_text_set_font_by_position,
-  ps_text_set_font_family,
-  ps_text_get_font_name,
-  ps_text_get_font_family,
-  ps_text_set_size,
-  ps_text_get_size,
+  ps_line,
   ps_text_metrics,
   ps_text_draw,
 
   ps_chart_initialise,
   ps_chart_finalise
-
 };
Index: pspp/src/output/table.c
diff -u pspp/src/output/table.c:1.4 pspp/src/output/table.c:1.5
--- pspp/src/output/table.c:1.4 Wed Mar 15 03:29:11 2006
+++ pspp/src/output/table.c     Mon Apr  3 20:07:54 2006
@@ -29,6 +29,7 @@
 #include <data/format.h>
 #include <libpspp/magic.h>
 #include <libpspp/misc.h>
+#include "minmax.h"
 #include "output.h"
 #include <libpspp/pool.h>
 #include "manager.h"
@@ -42,23 +43,22 @@
 struct som_table_class tab_table_class;
 static char *command_name;
 
-/* Creates a table with NC columns and NR rows.  If REALLOCABLE is
-   nonzero then the table's size can be increased later; otherwise,
-   its size can only be reduced. */
-struct tab_table *
-tab_create (int nc, int nr, int reallocable)
+/* Returns the font to use for a cell with the given OPTIONS. */
+static enum outp_font
+options_to_font (unsigned options) 
 {
-  void *(*alloc_func) (struct pool *, size_t n);
-  void *(*nalloc_func) (struct pool *, size_t n, size_t s);
+  return (options & TAB_FIX ? OUTP_FIXED
+          : options & TAB_EMPH ? OUTP_EMPHASIS
+          : OUTP_PROPORTIONAL);
+}
 
+/* Creates a table with NC columns and NR rows. */
+struct tab_table *
+tab_create (int nc, int nr, int reallocable UNUSED)
+{
   struct tab_table *t;
-  
-  {
-    struct pool *container = pool_create ();
-    t = pool_alloc (container, sizeof *t);
-    t->container = container;
-  }
-  
+
+  t = pool_create_container (struct tab_table, container);
   t->col_style = TAB_COL_NONE;
   t->col_group = 0;
   ls_null (&t->title);
@@ -67,38 +67,26 @@
   t->nc = t->cf = nc;
   t->l = t->r = t->t = t->b = 0;
 
-  nalloc_func = reallocable ? pool_nmalloc : pool_nalloc;
-  alloc_func = reallocable ? pool_malloc : pool_alloc;
-#if DEBUGGING
-  t->reallocable = reallocable;
-#endif
-
-  t->cc = nalloc_func (t->container, nr * nc, sizeof *t->cc);
-  t->ct = alloc_func (t->container, nr * nc);
+  t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
+  t->ct = pool_malloc (t->container, nr * nc);
   memset (t->ct, TAB_EMPTY, nc * nr);
 
-  t->rh = nalloc_func (t->container, nc, nr + 1);
+  t->rh = pool_nmalloc (t->container, nc, nr + 1);
   memset (t->rh, 0, nc * (nr + 1));
 
-  t->hrh = nalloc_func (t->container, nr + 1, sizeof *t->hrh);
+  t->hrh = pool_nmalloc (t->container, nr + 1, sizeof *t->hrh);
   memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
 
-  t->trh = alloc_func (t->container, nr + 1);
-  memset (t->trh, 0, nr + 1);
-
-  t->rv = nalloc_func (t->container, nr, nc + 1);
-  memset (t->rv, 0, (nc + 1) * nr);
+  t->rv = pool_nmalloc (t->container, nr, nc + 1);
+  memset (t->rv, UCHAR_MAX, nr * (nc + 1));
 
-  t->wrv = nalloc_func (t->container, nc + 1, sizeof *t->wrv);
+  t->wrv = pool_nmalloc (t->container, nc + 1, sizeof *t->wrv);
   memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
 
-  t->trv = alloc_func (t->container, nc + 1);
-  memset (t->trv, 0, nc + 1);
-
   t->dim = NULL;
   t->w = t->h = NULL;
   t->col_ofs = t->row_ofs = 0;
-  
+
   return t;
 }
 
@@ -108,7 +96,6 @@
 {
   assert (t != NULL);
   pool_destroy (t->container);
-  t=0;
 }
 
 /* Sets the width and height of a table, in columns and rows,
@@ -142,9 +129,6 @@
   int ro, co;
   
   assert (t != NULL);
-#if DEBUGGING
-  assert (t->reallocable);
-#endif
   ro = t->row_ofs;
   co = t->col_ofs;
   if (ro || co)
@@ -187,14 +171,12 @@
 
       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
-      t->trh = pool_realloc (t->container, t->trh, nr + 1);
-      t->hrh = pool_nrealloc (t->container, t->hrh, nr + 1, sizeof *t->hrh);
       
       if (nr > t->nr)
        {
-         memset (&t->rh[nc * (t->nr + 1)], 0, (nr - t->nr) * nc);
-         memset (&t->rv[(nc + 1) * t->nr], 0, (nr - t->nr) * (nc + 1));
-         memset (&t->trh[t->nr + 1], 0, nr - t->nr);
+         memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
+         memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
+                  (nr - t->nr) * (nc + 1));
        }
     }
 
@@ -247,8 +229,6 @@
 void
 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
 {
-  int y;
-
   assert (t != NULL);
 
 #if DEBUGGING
@@ -278,10 +258,9 @@
 
   if (style != -1)
     {
-      if ((style & TAL_SPACING) == 0)
-       for (y = y1; y <= y2; y++)
-         t->rv[x + (t->cf + 1) * y] = style;
-      t->trv[x] |= (1 << (style & ~TAL_SPACING));
+      int y;
+      for (y = y1; y <= y2; y++)
+        t->rv[x + (t->cf + 1) * y] = style;
     }
 }
 
@@ -290,8 +269,6 @@
 void
 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
 {
-  int x;
-
   assert (t != NULL);
 
   x1 += t->col_ofs;
@@ -306,10 +283,9 @@
 
   if (style != -1)
     {
-      if ((style & TAL_SPACING) == 0)
-       for (x = x1; x <= x2; x++)
-         t->rh[x + t->cf * y] = style;
-      t->trh[y] |= (1 << (style & ~TAL_SPACING));
+      int x;
+      for (x = x1; x <= x2; x++)
+        t->rh[x + t->cf * y] = style;
     }
 }
 
@@ -357,26 +333,20 @@
   if (f_h != -1)
     {
       int x;
-      if ((f_h & TAL_SPACING) == 0)
-       for (x = x1; x <= x2; x++)
-         {
-           t->rh[x + t->cf * y1] = f_h;
-           t->rh[x + t->cf * (y2 + 1)] = f_h;
-         }
-      t->trh[y1] |= (1 << (f_h & ~TAL_SPACING));
-      t->trh[y2 + 1] |= (1 << (f_h & ~TAL_SPACING));
+      for (x = x1; x <= x2; x++)
+        {
+          t->rh[x + t->cf * y1] = f_h;
+          t->rh[x + t->cf * (y2 + 1)] = f_h;
+        }
     }
   if (f_v != -1)
     {
       int y;
-      if ((f_v & TAL_SPACING) == 0)
-       for (y = y1; y <= y2; y++)
-         {
-           t->rv[x1 + (t->cf + 1) * y] = f_v;
-           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
-         }
-      t->trv[x1] |= (1 << (f_v & ~TAL_SPACING));
-      t->trv[x2 + 1] |= (1 << (f_v & ~TAL_SPACING));
+      for (y = y1; y <= y2; y++)
+        {
+          t->rv[x1 + (t->cf + 1) * y] = f_v;
+          t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
+        }
     }
 
   if (i_h != -1)
@@ -387,11 +357,8 @@
        {
          int x;
 
-         if ((i_h & TAL_SPACING) == 0)
-           for (x = x1; x <= x2; x++)
-             t->rh[x + t->cf * y] = i_h;
-
-         t->trh[y] |= (1 << (i_h & ~TAL_SPACING));
+          for (x = x1; x <= x2; x++)
+            t->rh[x + t->cf * y] = i_h;
        }
     }
   if (i_v != -1)
@@ -402,11 +369,8 @@
        {
          int y;
          
-         if ((i_v & TAL_SPACING) == 0)
-           for (y = y1; y <= y2; y++)
-             t->rv[x + (t->cf + 1) * y] = i_v;
-
-         t->trv[x] |= (1 << (i_v & ~TAL_SPACING));
+          for (y = y1; y <= y2; y++)
+            t->rv[x + (t->cf + 1) * y] = i_v;
        }
     }
 }
@@ -417,37 +381,29 @@
 text_format (struct tab_table *table, int opt, const char *text, va_list args,
             struct fixed_string *s)
 {
-  int len;
+  char *tmp = NULL;
   
   assert (table != NULL && text != NULL && s != NULL);
   
-  if (opt & TAT_PRINTF)
-    {
-      char *temp_buf = local_alloc (1024);
-      
-      len = nvsprintf (temp_buf, text, args);
-      text = temp_buf;
-    }
-  else
-    len = strlen (text);
+  if (opt & TAT_PRINTF) 
+    text = tmp = xvasprintf (text, args);
 
-  ls_create_buffer (s, text, len);
+  ls_create_buffer (s, text, strlen (text));
   pool_register (table->container, free, s->string);
   
-  if (opt & TAT_PRINTF)
-    local_free (text);
+  free (tmp);
 }
 
-/* Set the title of table T to TITLE, which is formatted with printf
-   if FORMAT is nonzero. */
+/* Set the title of table T to TITLE, which is formatted as if
+   passed to printf(). */
 void
-tab_title (struct tab_table *t, int format, const char *title, ...)
+tab_title (struct tab_table *t, const char *title, ...)
 {
   va_list args;
 
   assert (t != NULL && title != NULL);
   va_start (args, title);
-  text_format (t, format ? TAT_PRINTF : TAT_NONE, title, args, &t->title);
+  text_format (t, TAT_PRINTF, title, args, &t->title);
   va_end (args);
 }
 
@@ -476,17 +432,20 @@
       {
        struct outp_text text;
        unsigned char opt = t->ct[c + r * t->cf];
+        int w;
                
        if (opt & (TAB_JOIN | TAB_EMPTY))
          continue;
 
-       text.s = t->cc[c + r * t->cf];
-       assert (!ls_null_p (&text.s));
-       text.options = OUTP_T_JUST_LEFT;
-
-       d->class->text_metrics (d, &text);
-       if (text.h > width)
-         width = text.h;
+       text.string = t->cc[c + r * t->cf];
+       assert (!ls_null_p (&text.string));
+       text.justification = OUTP_LEFT;
+        text.font = options_to_font (opt);
+        text.h = text.v = INT_MAX;
+
+       d->class->text_metrics (d, &text, &w, NULL);
+       if (w > width)
+         width = w;
       }
   }
 
@@ -525,19 +484,22 @@
       {
        struct outp_text text;
        unsigned char opt = t->ct[c + r * t->cf];
+        int h;
 
        assert (t->w[c] != NOT_INT);
        if (opt & (TAB_JOIN | TAB_EMPTY))
          continue;
 
-       text.s = t->cc[c + r * t->cf];
-       assert (!ls_null_p (&text.s));
-       text.options = OUTP_T_HORZ | OUTP_T_JUST_LEFT;
+       text.string = t->cc[c + r * t->cf];
+       assert (!ls_null_p (&text.string));
+        text.justification = OUTP_LEFT;
+        text.font = options_to_font (opt);
        text.h = t->w[c];
-       d->class->text_metrics (d, &text);
+        text.v = INT_MAX;
+       d->class->text_metrics (d, &text, NULL, &h);
 
-       if (text.v > height)
-         height = text.v;
+       if (h > height)
+         height = h;
       }
   }
 
@@ -670,7 +632,7 @@
       return;
     }
 #endif
-    
+
   va_start (args, text);
   text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
   table->ct[c + r * table->cf] = opt;
@@ -710,6 +672,8 @@
       return;
     }
 #endif
+
+  tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
   
   j = pool_alloc (table->container, sizeof *j);
   j->hit = 0;
@@ -803,60 +767,23 @@
 tab_output_text (int options, const char *buf, ...)
 {
   struct tab_table *t = tab_create (1, 1, 0);
+  char *tmp_buf = NULL;
 
-  assert (buf != NULL);
   if (options & TAT_PRINTF)
     {
       va_list args;
-      char *temp_buf = local_alloc (4096);
       
       va_start (args, buf);
-      nvsprintf (temp_buf, buf, args);
-      buf = temp_buf;
+      buf = tmp_buf = xvasprintf (buf, args);
       va_end (args);
     }
   
-  if (options & TAT_FIX)
-    {
-      struct outp_driver *d;
-
-      for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-       {
-         if (!d->page_open)
-           d->class->open_page (d);
-
-          if (d->class->text_set_font_by_name != NULL)
-            d->class->text_set_font_by_name (d, "FIXED");
-          else 
-            {
-              /* FIXME */
-            }
-       }
-    }
-
-  tab_text (t, 0, 0, options &~ TAT_PRINTF, buf);
+  tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
-  if (options & TAT_NOWRAP)
-    tab_dim (t, nowrap_dim);
-  else
-    tab_dim (t, wrap_dim);
+  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
   tab_submit (t);
-
-  if (options & TAT_FIX)
-    {
-      struct outp_driver *d;
-
-      for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-        if (d->class->text_set_font_by_name != NULL)
-          d->class->text_set_font_by_name (d, "PROP");
-        else 
-          {
-            /* FIXME */
-          }
-    }
   
-  if (options & TAT_PRINTF)
-    local_free (buf);
+  free (tmp_buf);
 }
 
 /* Set table flags to FLAGS. */
@@ -944,20 +871,64 @@
   t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
 }
 
+/* Returns the line style to use for spacing purposes for a rule
+   of the given TYPE. */
+static enum outp_line_style
+rule_to_spacing_type (unsigned char type) 
+{
+  switch (type) 
+    {
+    case TAL_0:
+      return OUTP_L_NONE;
+    case TAL_GAP:
+    case TAL_1:
+      return OUTP_L_SINGLE;
+    case TAL_2:
+      return OUTP_L_DOUBLE;
+    default:
+      abort ();
+    }
+}
+
 /* Set the current output device to DRIVER. */
 static void
 tabi_driver (struct outp_driver *driver)
 {
+  int c, r;
   int i;
-
+  
   assert (driver != NULL);
   d = driver;
   
   /* Figure out sizes of rules. */
-  for (t->hr_tot = i = 0; i <= t->nr; i++)
-    t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]];
-  for (t->vr_tot = i = 0; i <= t->nc; i++)
-    t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]];
+  for (r = 0; r <= t->nr; r++) 
+    {
+      int width = 0;
+      for (c = 0; c < t->nc; c++) 
+        {
+          unsigned char rh = t->rh[c + r * t->cf];
+          int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
+          if (w > width)
+            width = w; 
+        }
+      t->hrh[r] = width; 
+    }
+
+  for (c = 0; c <= t->nc; c++) 
+    {
+      int width = 0;
+      for (r = 0; r < t->nr; r++) 
+        {
+          unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
+          int w;
+          if (*rv == UCHAR_MAX)
+            *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
+          w = driver->vert_line_width[rule_to_spacing_type (*rv)];
+          if (w > width)
+            width = w;
+        }
+      t->wrv[c] = width; 
+    }
 
 #if DEBUGGING
   for (i = 0; i < t->nr; i++)
@@ -1091,7 +1062,9 @@
       d = &t->h[start];
       r = &t->hrh[start + 1];
       total = t->ht + t->hb;
-    } else {
+    }
+  else
+    {
       assert (start >= 0 && start < t->nc);
       n = t->nc - t->r;
       d = &t->w[start];
@@ -1209,8 +1182,9 @@
   {
     struct outp_text text;
 
-    text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
-    ls_init (&text.s, buf, cp - buf);
+    text.font = OUTP_PROPORTIONAL;
+    text.justification = OUTP_LEFT;
+    ls_init (&text.string, buf, cp - buf);
     text.h = d->width;
     text.v = d->font_height;
     text.x = 0;
@@ -1221,14 +1195,32 @@
 
 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
 
-/* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column
-   X2 and row Y2 are not included in the rectangle, at the current
-   position on the current output device.  Draws headers as well. */
+/* Renders columns C0...C1, plus headers, of rows R0...R1,
+   at the given vertical position Y.
+   C0 and C1 count vertical rules as columns,
+   but R0 and R1 do not count horizontal rules as rows.
+   Returns the vertical position after rendering. */
+static int
+render_rows (int y, int c0, int c1, int r0, int r1)
+{
+  int r;
+  for (r = r0; r < r1; r++) 
+    {
+      int x = d->cp_x;
+      x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
+      x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
+      x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
+      y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
+    }
+  return y;
+}
+
+/* Draws table region (C0,R0)-(C1,R1), plus headers, at the
+   current position on the current output device.  */
 static void
-tabi_render (int x1, int y1, int x2, int y2)
+tabi_render (int c0, int r0, int c1, int r1)
 {
-  int i, y;
-  int ranges[3][2];
+  int y;
   
   tab_hit++;
 
@@ -1236,32 +1228,9 @@
   if (!(t->flags & SOMF_NO_TITLE))
     y += d->font_height;
 
-  /* Top headers. */
-  ranges[0][0] = 0;
-  ranges[0][1] = t->t * 2 + 1;
-
-  /* Requested rows. */
-  ranges[1][0] = y1 * 2 + 1;
-  ranges[1][1] = y2 * 2;
-
-  /* Bottom headers. */
-  ranges[2][0] = (t->nr - t->b) * 2;
-  ranges[2][1] = t->nr * 2 + 1;
-
-  for (i = 0; i < 3; i++) 
-    {
-      int r;
-
-      for (r = ranges[i][0]; r < ranges[i][1]; r++) 
-        {
-          int x = d->cp_x;
-          x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2);
-          x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2);
-          x += render_strip (x, y, r, (t->nc - t->r) * 2,
-                             t->nc * 2 + 1, y1, y2);
-          y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
-        }
-    }
+  y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
+  y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
+  y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
 }
 
 struct som_table_class tab_table_class =
@@ -1290,151 +1259,215 @@
     tabi_render,
   };
 
-/* Render contiguous strip consisting of columns C1...C2, exclusive,
-   on row R, at location (X,Y).  Return width of the strip thus
-   rendered.
-
-   Renders joined cells, even those outside the strip, within the
-   rendering region (C1,R1)-(C2,R2).
-
-   For the purposes of counting rows and columns in this function
-   only, horizontal rules are considered rows and vertical rules are
-   considered columns.
+static enum outp_justification
+translate_justification (unsigned int opt)
+{
+  switch (opt & TAB_ALIGN_MASK) 
+    {
+    case TAB_RIGHT:
+      return OUTP_RIGHT;
+    case TAB_LEFT:
+      return OUTP_LEFT;
+    case TAB_CENTER:
+      return OUTP_CENTER;
+    default:
+      abort ();
+    }
+}
+
+/* Returns the line style to use for drawing a rule of the given
+   TYPE. */
+static enum outp_line_style
+rule_to_draw_type (unsigned char type) 
+{
+  switch (type) 
+    {
+    case TAL_0:
+    case TAL_GAP:
+      return OUTP_L_NONE;
+    case TAL_1:
+      return OUTP_L_SINGLE;
+    case TAL_2:
+      return OUTP_L_DOUBLE;
+    default:
+      abort ();
+    }
+}
+
+/* Returns the horizontal rule at the given column and row. */
+static int
+get_hrule (int c, int r) 
+{
+  return t->rh[c + r * t->cf];
+}
 
-   FIXME: Doesn't use r1?  Huh?  */
+/* Returns the vertical rule at the given column and row. */
 static int
-render_strip (int x, int y, int r, int c1, int c2, int r1 UNUSED, int r2)
+get_vrule (int c, int r) 
+{
+  return t->rv[c + r * (t->cf + 1)];
+}
+
+/* Renders the horizontal rule at the given column and row
+   at (X,Y) on the page. */
+static void
+render_horz_rule (int x, int y, int c, int r)
 {
-  int x_origin = x;
+  enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
+  if (style != OUTP_L_NONE)
+    d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
+                    OUTP_L_NONE, style, OUTP_L_NONE, style);
+}
+
+/* Renders the vertical rule at the given column and row
+   at (X,Y) on the page. */
+static void
+render_vert_rule (int x, int y, int c, int r)
+{
+  enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
+  if (style != OUTP_L_NONE)
+    d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
+                    style, OUTP_L_NONE, style, OUTP_L_NONE);
+}
+
+/* Renders the rule intersection at the given column and row
+   at (X,Y) on the page. */
+static void
+render_rule_intersection (int x, int y, int c, int r)
+{
+  /* Bounds of intersection. */
+  int x0 = x;
+  int y0 = y;
+  int x1 = x + t->wrv[c];
+  int y1 = y + t->hrh[r];
+
+  /* Lines on each side of intersection. */
+  int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
+  int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
+  int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
+  int right = c < t->nc ? get_hrule (c, r) : TAL_0;
+
+  /* Output style for each line. */
+  enum outp_line_style o_top = rule_to_draw_type (top);
+  enum outp_line_style o_left = rule_to_draw_type (left);
+  enum outp_line_style o_bottom = rule_to_draw_type (bottom);
+  enum outp_line_style o_right = rule_to_draw_type (right);
+
+  if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
+      || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
+    d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
+}
 
-  /* Horizontal rules. */
-  if ((r & 1) == 0)
+/* Returns the width of columns C1...C2 exclusive,
+   including interior but not exterior rules. */
+static int
+strip_width (int c1, int c2)
+{
+  int width = 0;
+  int c;
+
+  for (c = c1; c < c2; c++) 
+    width += t->w[c] + t->wrv[c + 1];
+  if (c1 < c2)
+    width -= t->wrv[c2];
+  return width;
+}
+
+/* Returns the height of rows R1...R2 exclusive,
+   including interior but not exterior rules. */
+static int
+strip_height (int r1, int r2)
+{
+  int height = 0;
+  int r;
+
+  for (r = r1; r < r2; r++) 
+    height += t->h[r] + t->hrh[r + 1];
+  if (r1 < r2)
+    height -= t->hrh[r2];
+  return height;
+}
+
+/* Renders the cell at the given column and row at (X,Y) on the
+   page.  Also renders joined cells that extend as far to the
+   right as C1 and as far down as R1. */
+static void
+render_cell (int x, int y, int c, int r, int c1, int r1)
+{
+  const int index = c + (r * t->cf);
+  unsigned char type = t->ct[index];
+  struct fixed_string *content = &t->cc[index];
+  
+  if (!(type & TAB_JOIN))
     {
-      int hrh = t->hrh[r / 2];
-      int c;
+      if (!(type & TAB_EMPTY))
+        {
+          struct outp_text text;
+          text.font = options_to_font (type);
+          text.justification = translate_justification (type);
+          text.string = *content;
+          text.h = t->w[c];
+          text.v = t->h[r];
+          text.x = x;
+          text.y = y;
+          d->class->text_draw (d, &text);
+        }
+    }
+  else
+    {
+      struct tab_joined_cell *j
+        = (struct tab_joined_cell *) ls_c_str (content);
 
-      for (c = c1; c < c2; c++)
-       {
-         if (c & 1)
-           {
-             int style = t->rh[(c / 2) + (r / 2 * t->cf)];
-
-             if (style != TAL_0)
-               {
-                 const struct color clr = {0, 0, 0, 0};
-                 struct rect rct;
-
-                 rct.x1 = x;
-                 rct.y1 = y;
-                 rct.x2 = x + t->w[c / 2];
-                 rct.y2 = y + hrh;
-                 d->class->line_horz (d, &rct, &clr, style);
-               }
-             x += t->w[c / 2];
-           } else {
-             const struct color clr = {0, 0, 0, 0};
-             struct rect rct;
-             struct outp_styles s;
-
-             rct.x1 = x;
-             rct.y1 = y;
-             rct.x2 = x + t->wrv[c / 2];
-             rct.y2 = y + hrh;
-
-             s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0;
-             s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0;
-             s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0;
-             s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0;
-
-             if (s.t | s.b | s.l | s.r)
-               d->class->line_intersection (d, &rct, &clr, &s);
-             
-             x += t->wrv[c / 2];
-           }
-       }
-    } else {
-      int c;
+      if (j->hit != tab_hit)
+        {
+          j->hit = tab_hit;
 
-      for (c = c1; c < c2; c++)
-       {
-         if (c & 1)
-           {
-             const int index = (c / 2) + (r / 2 * t->cf);
-
-             if (!(t->ct[index] & TAB_JOIN))
-               {
-                 struct outp_text text;
-
-                 text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
-                                 | OUTP_T_HORZ | OUTP_T_VERT);
-                 if ((t->ct[index] & TAB_EMPTY) == 0)
-                   {
-                     text.s = t->cc[index];
-                     assert (!ls_null_p (&text.s));
-                     text.h = t->w[c / 2];
-                     text.v = t->h[r / 2];
-                     text.x = x;
-                     text.y = y;
-                     d->class->text_draw (d, &text);
-                   }
-               } else {
-                 struct tab_joined_cell *j =
-                   (struct tab_joined_cell *) ls_c_str (&t->cc[index]);
-
-                 if (j->hit != tab_hit)
-                   {
-                     j->hit = tab_hit;
-
-                     if (j->x1 == c / 2 && j->y1 == r / 2)
-                       {
-                         struct outp_text text;
-
-                         text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
-                                         | OUTP_T_HORZ | OUTP_T_VERT);
-                         text.s = j->contents;
-                         text.x = x;
-                         text.y = y;
-                         
-                         {
-                           int c;
-
-                           for (c = j->x1, text.h = -t->wrv[j->x2];
-                                c < j->x2 && c < c2 / 2; c++) 
-                                text.h += t->w[c] + t->wrv[c + 1]; 
-                         }
-                         
-                         {
-                           int r;
-
-                           for (r = j->y1, text.v = -t->hrh[j->y2];
-                                r < j->y2 && r < r2 / 2; r++)
-                             text.v += t->h[r] + t->hrh[r + 1];
-                         }
-                         d->class->text_draw (d, &text);
-                       }
-                   }
-               }
-             x += t->w[c / 2];
-           } else {
-             int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))];
-
-             if (style != TAL_0)
-               {
-                 const struct color clr = {0, 0, 0, 0};
-                 struct rect rct;
-
-                 rct.x1 = x;
-                 rct.y1 = y;
-                 rct.x2 = x + t->wrv[c / 2];
-                 rct.y2 = y + t->h[r / 2];
-                 d->class->line_vert (d, &rct, &clr, style);
-               }
-             x += t->wrv[c / 2];
-           }
-       }
+          if (j->x1 == c && j->y1 == r)
+            {
+              struct outp_text text;
+              text.font = options_to_font (type);
+              text.justification = translate_justification (type);
+              text.string = j->contents;
+              text.x = x;
+              text.y = y;
+              text.h = strip_width (j->x1, MIN (j->x2, c1));
+              text.v = strip_height (j->y1, MIN (j->y2, r1));
+              d->class->text_draw (d, &text);
+            }
+        }
     }
+}
 
-  return x - x_origin;
+/* Render contiguous strip consisting of columns C0...C1, exclusive,
+   on row R, at (X,Y).  Returns X position after rendering.
+   Also renders joined cells that extend beyond that strip,
+   cropping them to lie within rendering region (C0,R0)-(C1,R1).
+   C0 and C1 count vertical rules as columns.
+   R counts horizontal rules as rows, but R0 and R1 do not. */
+static int
+render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
+{
+  int c;
+
+  for (c = c0; c < c1; c++)
+    if (c & 1) 
+      {
+        if (r & 1)
+          render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
+        else
+          render_horz_rule (x, y, c / 2, r / 2);
+        x += t->w[c / 2];
+      }
+    else
+      {
+        if (r & 1)
+          render_vert_rule (x, y, c / 2, r / 2);
+        else
+          render_rule_intersection (x, y, c / 2, r / 2);
+        x += t->wrv[c / 2];
+      }
+  
+  return x;
 }
 
 /* Sets COMMAND_NAME as the name of the current command,
Index: pspp/src/output/table.h
diff -u pspp/src/output/table.h:1.3 pspp/src/output/table.h:1.4
--- pspp/src/output/table.h:1.3 Wed Mar 15 03:29:11 2006
+++ pspp/src/output/table.h     Mon Apr  3 20:07:54 2006
@@ -28,27 +28,28 @@
   {
     TAB_NONE = 0,
 
-    /* Must match output.h: OUTP_T_JUST_*. */
     TAB_ALIGN_MASK = 03,       /* Alignment mask. */
     TAB_RIGHT = 00,            /* Right justify. */
     TAB_LEFT = 01,             /* Left justify. */
     TAB_CENTER = 02,           /* Center. */
 
-    /* Oddball cell types. */
-    TAB_JOIN = 010,            /* Joined cell. */
-    TAB_EMPTY = 020            /* Empty cell. */
+    /* Cell types. */
+    TAB_JOIN = 004,            /* Joined cell. */
+    TAB_EMPTY = 010,           /* Empty cell. */
+
+    /* Flags. */
+    TAB_EMPH = 020,             /* Emphasize cell contents. */
+    TAB_FIX = 040,              /* Use fixed font. */
   };
 
-/* Line styles.  These must match output.h:OUTP_L_*. */
+/* Line styles. */
 enum
   {
     TAL_0 = 0,                 /* No line. */
     TAL_1 = 1,                 /* Single line. */
     TAL_2 = 2,                 /* Double line. */
-    TAL_3 = 3,                 /* Special line of driver-defined style. */
+    TAL_GAP = 3,                /* Spacing but no line. */
     TAL_COUNT,                 /* Number of line styles. */
-
-    TAL_SPACING = 0200         /* Don't draw the line, just reserve space. */
   };
 
 /* Column styles.  Must correspond to SOM_COL_*. */
@@ -87,9 +88,7 @@
     struct fixed_string *cc;   /* Cell contents; fixed_string *[nr][nc]. */
     unsigned char *ct;         /* Cell types; unsigned char[nr][nc]. */
     unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
-    unsigned char *trh;                /* Types of horiz rules; [nr+1]. */
     unsigned char *rv;         /* Vert rules; unsigned char[nr][nc+1]. */
-    unsigned char *trv;                /* Types of vert rules; [nc+1]. */
     tab_dim_func *dim;         /* Calculates cell widths and heights. */
 
     /* Calculated during output. */
@@ -98,13 +97,9 @@
     int *hrh;                  /* Heights of horizontal rules; [nr+1]. */
     int *wrv;                  /* Widths of vertical rules; [nc+1]. */
     int wl, wr, ht, hb;                /* Width/height of header rows/columns. 
*/
-    int hr_tot, vr_tot;                /* Hrules total height, vrules total 
width. */
 
     /* Editing info. */
     int col_ofs, row_ofs;      /* X and Y offsets. */
-#if DEBUGGING
-    int reallocable;           /* Can table be reallocated? */
-#endif
   };
 
 extern int tab_hit;
@@ -134,7 +129,8 @@
 void tab_realloc (struct tab_table *, int nc, int nr);
 void tab_headers (struct tab_table *, int l, int r, int t, int b);
 void tab_columns (struct tab_table *, int style, int group);
-void tab_title (struct tab_table *, int format, const char *, ...);
+void tab_title (struct tab_table *, const char *, ...)
+     PRINTF_FORMAT (2, 3);
 void tab_flags (struct tab_table *, unsigned);
 void tab_submit (struct tab_table *);
 
@@ -155,8 +151,7 @@
   {
     TAT_NONE = 0,              /* No options. */
     TAT_PRINTF = 0x0100,       /* Format the text string with sprintf. */
-    TAT_TITLE = 0x0204,                /* Title attributes. */
-    TAT_FIX = 0x0400,          /* Use fixed-pitch font. */
+    TAT_TITLE = 0x0200 | TAB_EMPH, /* Title attributes. */
     TAT_NOWRAP = 0x0800         /* No text wrap (tab_output_text() only). */
   };
 
Index: pspp/src/procedure.c
diff -u pspp/src/procedure.c:1.3 pspp/src/procedure.c:1.4
--- pspp/src/procedure.c:1.3    Tue Mar 21 00:33:05 2006
+++ pspp/src/procedure.c        Mon Apr  3 20:07:54 2006
@@ -881,8 +881,8 @@
 
   t = tab_create (3, split_cnt + 1, 0);
   tab_dim (t, tab_natural_dimensions);
-  tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, split_cnt);
-  tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, split_cnt);
+  tab_vline (t, TAL_GAP, 1, 0, split_cnt);
+  tab_vline (t, TAL_GAP, 2, 0, split_cnt);
   tab_text (t, 0, 0, TAB_NONE, _("Variable"));
   tab_text (t, 1, 0, TAB_LEFT, _("Value"));
   tab_text (t, 2, 0, TAB_LEFT, _("Label"));
Index: pspp/tests/ChangeLog
diff -u pspp/tests/ChangeLog:1.52 pspp/tests/ChangeLog:1.53
--- pspp/tests/ChangeLog:1.52   Thu Mar 23 17:15:17 2006
+++ pspp/tests/ChangeLog        Mon Apr  3 20:07:54 2006
@@ -1,3 +1,7 @@
+Mon Apr  3 12:32:36 2006  Ben Pfaff  <address@hidden>
+
+       * Updated tests to match changes in output formatting.
+
 2006-03-23  Jason Stover  <address@hidden>
 
        * command/regression.sh: New test.
Index: pspp/tests/bugs/examine-missing.sh
diff -u pspp/tests/bugs/examine-missing.sh:1.3 
pspp/tests/bugs/examine-missing.sh:1.4
--- pspp/tests/bugs/examine-missing.sh:1.3      Sat Mar 11 07:16:40 2006
+++ pspp/tests/bugs/examine-missing.sh  Mon Apr  3 20:07:54 2006
@@ -70,7 +70,7 @@
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="run program"
-$SUPERVISOR $PSPP -o raw-ascii $TESTFILE > /dev/null
+$SUPERVISOR $PSPP -o raw-ascii $TESTFILE > /dev/null 2>&1
 if [ $? -ne 0 ] ; then fail ; fi
 
 pass;
Index: pspp/tests/command/examine-extremes.sh
diff -u pspp/tests/command/examine-extremes.sh:1.10 
pspp/tests/command/examine-extremes.sh:1.11
--- pspp/tests/command/examine-extremes.sh:1.10 Sat Mar 11 07:16:40 2006
+++ pspp/tests/command/examine-extremes.sh      Mon Apr  3 20:07:54 2006
@@ -106,23 +106,23 @@
 #V1#23|   100%|0|     0%|23|   100%#
 #==#==#=======#=#=======#==#=======#
 1.2 EXAMINE.  Extreme Values
-#==========#===========#=====#
-#          #Case Number|Value#
-#==========#===========#=====#
-#V1Highest1#         21|20.00#
-#         2#         20|19.00#
-#         3#         19|18.00#
-#         4#         19|18.00#
-#         5#         18|17.00#
-#         6#         17|16.00#
-#  --------#-----------+-----#
-#   Lowest1#          1| 1.00#
-#         2#          2| 2.00#
-#         3#          4| 3.00#
-#         4#          3| 3.00#
-#         5#          3| 3.00#
-#         6#          5| 4.00#
-#==========#===========#=====#
+#============#===========#=====#
+#            #Case Number|Value#
+#============#===========#=====#
+#V1 Highest 1#         21|20.00#
+#           2#         20|19.00#
+#           3#         19|18.00#
+#           4#         19|18.00#
+#           5#         18|17.00#
+#           6#         17|16.00#
+#  ----------#-----------+-----#
+#    Lowest 1#          1| 1.00#
+#           2#          2| 2.00#
+#           3#          4| 3.00#
+#           4#          3| 3.00#
+#           5#          3| 3.00#
+#           6#          5| 4.00#
+#============#===========#=====#
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
Index: pspp/tests/command/examine.sh
diff -u pspp/tests/command/examine.sh:1.14 pspp/tests/command/examine.sh:1.15
--- pspp/tests/command/examine.sh:1.14  Sat Mar 11 07:16:40 2006
+++ pspp/tests/command/examine.sh       Mon Apr  3 20:07:55 2006
@@ -110,22 +110,22 @@
 +--------+------+
 Case#  QUALITY        W    BRAND
 ----- -------- -------- --------
-    1     3.00     1.00     1.00 
-    2     2.00     2.00     1.00 
-    3     1.00     2.00     1.00 
-    4     1.00     1.00     1.00 
-    5     4.00     1.00     1.00 
-    6     4.00     1.00     1.00 
-    7     5.00     1.00     2.00 
-    8     2.00     1.00     2.00 
-    9     4.00     4.00     2.00 
-   10     2.00     1.00     2.00 
-   11     3.00     1.00     2.00 
-   12     7.00     1.00     3.00 
-   13     4.00     2.00     3.00 
-   14     5.00     3.00     3.00 
-   15     3.00     1.00     3.00 
-   16     6.00     1.00     3.00 
+    1     3.00     1.00     1.00
+    2     2.00     2.00     1.00
+    3     1.00     2.00     1.00
+    4     1.00     1.00     1.00
+    5     4.00     1.00     1.00
+    6     4.00     1.00     1.00
+    7     5.00     1.00     2.00
+    8     2.00     1.00     2.00
+    9     4.00     4.00     2.00
+   10     2.00     1.00     2.00
+   11     3.00     1.00     2.00
+   12     7.00     1.00     3.00
+   13     4.00     2.00     3.00
+   14     5.00     3.00     3.00
+   15     3.00     1.00     3.00
+   16     6.00     1.00     3.00
 2.1 EXAMINE.  Case Processing Summary
 #===============#===============================#
 #               #             Cases             #
@@ -137,121 +137,121 @@
 #Breaking Strain#24|   100%|0|     0%|24|   100%#
 #===============#==#=======#=#=======#==#=======#
 2.2 EXAMINE.  Extreme Values
-#=======================#===========#=====#
-#                       #Case Number|Value#
-#=======================#===========#=====#
-#Breaking StrainHighest1#         12| 7.00#
-#                      2#         16| 6.00#
-#                      3#         14| 5.00#
-#               --------#-----------+-----#
-#                Lowest1#          4| 1.00#
-#                      2#          3| 1.00#
-#                      3#          3| 1.00#
-#=======================#===========#=====#
+#=========================#===========#=====#
+#                         #Case Number|Value#
+#=========================#===========#=====#
+#Breaking Strain Highest 1#         12| 7.00#
+#                        2#         16| 6.00#
+#                               3#         14| 5.00#
+#               ----------#-----------+-----#
+#                 Lowest 1#          4| 1.00#
+#                        2#          3| 1.00#
+#                        3#          3| 1.00#
+#=========================#===========#=====#
 2.3 EXAMINE.  Descriptives
-#==========================================================#=========#==========#
-#                                                          #Statistic|Std. 
Error#
-#==========================================================#=========#==========#
-#Breaking StrainMean                                       #   3.54  |   .324  
 #
-#               95% Confidence Interval for MeanLower Bound#  3.562  |         
 #
-#                                               Upper Bound#  3.521  |         
 #
-#               5% Trimmed Mean                            #   3.50  |         
 #
-#               Median                                     #   4.00  |         
 #
-#               Variance                                   #  2.520  |         
 #
-#               Std. Deviation                             #  1.587  |         
 #
-#               Minimum                                    #  1.000  |         
 #
-#               Maximum                                    #  7.000  |         
 #
-#               Range                                      #  6.000  |         
 #
-#               Interquartile Range                        #   2.75  |         
 #
-#               Skewness                                   #   .059  |   .472  
 #
-#               Kurtosis                                   #  -.358  |   .918  
 #
-#==========================================================#=========#==========#
+#============================================================#=========#==========#
+#                                                            #Statistic|Std. 
Error#
+#============================================================#=========#==========#
+#Breaking Strain Mean                                        #   3.54  |   
.324   #
+#                95% Confidence Interval for Mean Lower Bound#  3.562  |       
   #
+#                                                 Upper Bound#  3.521  |       
   #
+#                5% Trimmed Mean                             #   3.50  |       
   #
+#                Median                                      #   4.00  |       
   #
+#                Variance                                    #  2.520  |       
   #
+#                Std. Deviation                              #  1.587  |       
   #
+#                Minimum                                     #  1.000  |       
   #
+#                Maximum                                     #  7.000  |       
   #
+#                Range                                       #  6.000  |       
   #
+#                Interquartile Range                         #   2.75  |       
   #
+#                Skewness                                    #   .059  |   
.472   #
+#                Kurtosis                                    #  -.358  |   
.918   #
+#============================================================#=========#==========#
 2.4 EXAMINE.  Case Processing Summary
-#===========================#=============================#
-#                           #            Cases            #
-#                           #---------+---------+---------#
-#                           #  Valid  | Missing |  Total  #
-#                           #-+-------+-+-------+-+-------#
-#               Manufacturer#N|Percent|N|Percent|N|Percent#
-#===========================#=#=======#=#=======#=#=======#
-#Breaking StrainAspeger     #8|   100%|0|     0%|8|   100%#
-#               Bloggs      #8|   100%|0|     0%|8|   100%#
-#               Charlies    #8|   100%|0|     0%|8|   100%#
-#===========================#=#=======#=#=======#=#=======#
+#============================#=============================#
+#                            #            Cases            #
+#                            #---------+---------+---------#
+#                            #  Valid  | Missing |  Total  #
+#                            #-+-------+-+-------+-+-------#
+#                Manufacturer#N|Percent|N|Percent|N|Percent#
+#============================#=#=======#=#=======#=#=======#
+#Breaking Strain Aspeger     #8|   100%|0|     0%|8|   100%#
+#                Bloggs      #8|   100%|0|     0%|8|   100%#
+#                Charlies    #8|   100%|0|     0%|8|   100%#
+#============================#=#=======#=#=======#=#=======#
 2.5 EXAMINE.  Extreme Values
-#===================================#===========#=====#
-#               Manufacturer        #Case Number|Value#
-#===================================#===========#=====#
-#Breaking StrainAspeger     Highest1#          6| 4.00#
-#                                  2#          5| 4.00#
-#                                  3#          1| 3.00#
-#                           --------#-----------+-----#
-#                            Lowest1#          4| 1.00#
-#                                  2#          3| 1.00#
-#                                  3#          3| 1.00#
-#               --------------------#-----------+-----#
-#               Bloggs      Highest1#          7| 5.00#
-#                                  2#          9| 4.00#
-#                                  3#          9| 4.00#
-#                           --------#-----------+-----#
-#                            Lowest1#         10| 2.00#
-#                                  2#          8| 2.00#
-#                                  3#         11| 3.00#
-#               --------------------#-----------+-----#
-#               Charlies    Highest1#         12| 7.00#
-#                                  2#         16| 6.00#
-#                                  3#         14| 5.00#
-#                           --------#-----------+-----#
-#                            Lowest1#         15| 3.00#
-#                                  2#         13| 4.00#
-#                                  3#         13| 4.00#
-#===================================#===========#=====#
+#======================================#===========#=====#
+#                Manufacturer          #Case Number|Value#
+#======================================#===========#=====#
+#Breaking Strain Aspeger      Highest 1#          6| 4.00#
+#                                     2#          5| 4.00#
+#                                     3#          1| 3.00#
+#                            ----------#-----------+-----#
+#                              Lowest 1#          4| 1.00#
+#                                     2#          3| 1.00#
+#                                     3#          3| 1.00#
+#               -----------------------#-----------+-----#
+#                Bloggs       Highest 1#          7| 5.00#
+#                                     2#          9| 4.00#
+#                                     3#          9| 4.00#
+#                            ----------#-----------+-----#
+#                              Lowest 1#         10| 2.00#
+#                                     2#          8| 2.00#
+#                                     3#         11| 3.00#
+#               -----------------------#-----------+-----#
+#                       Charlies     Highest 1#         12| 7.00#
+#                                     2#         16| 6.00#
+#                                     3#         14| 5.00#
+#                            ----------#-----------+-----#
+#                              Lowest 1#         15| 3.00#
+#                                     2#         13| 4.00#
+#                                     3#         13| 4.00#
+#======================================#===========#=====#
 2.6 EXAMINE.  Descriptives
-#======================================================================#=========#==========#
-#               Manufacturer                                           
#Statistic|Std. Error#
-#======================================================================#=========#==========#
-#Breaking StrainAspeger     Mean                                       #   
2.25  |   .453   #
-#                           95% Confidence Interval for MeanLower Bound#  
2.279  |          #
-#                                                           Upper Bound#  
2.221  |          #
-#                           5% Trimmed Mean                            #   
2.22  |          #
-#                           Median                                     #   
2.00  |          #
-#                           Variance                                   #  
1.643  |          #
-#                           Std. Deviation                             #  
1.282  |          #
-#                           Minimum                                    #  
1.000  |          #
-#                           Maximum                                    #  
4.000  |          #
-#                           Range                                      #  
3.000  |          #
-#                           Interquartile Range                        #   
2.75  |          #
-#                           Skewness                                   #   
.475  |   .752   #
-#                           Kurtosis                                   #  
-1.546 |   1.481  #
-#               
-------------------------------------------------------#---------+----------#
-#               Bloggs      Mean                                       #   
3.50  |   .378   #
-#                           95% Confidence Interval for MeanLower Bound#  
3.525  |          #
-#                                                           Upper Bound#  
3.475  |          #
-#                           5% Trimmed Mean                            #   
3.50  |          #
-#                           Median                                     #   
4.00  |          #
-#                           Variance                                   #  
1.143  |          #
-#                           Std. Deviation                             #  
1.069  |          #
-#                           Minimum                                    #  
2.000  |          #
-#                           Maximum                                    #  
5.000  |          #
-#                           Range                                      #  
3.000  |          #
-#                           Interquartile Range                        #   
1.75  |          #
-#                           Skewness                                   #  
-.468  |   .752   #
-#                           Kurtosis                                   #  
-.831  |   1.481  #
-#               
-------------------------------------------------------#---------+----------#
-#               Charlies    Mean                                       #   
4.88  |   .441   #
-#                           95% Confidence Interval for MeanLower Bound#  
4.904  |          #
-#                                                           Upper Bound#  
4.846  |          #
-#                           5% Trimmed Mean                            #   
4.86  |          #
-#                           Median                                     #   
5.00  |          #
-#                           Variance                                   #  
1.554  |          #
-#                           Std. Deviation                             #  
1.246  |          #
-#                           Minimum                                    #  
3.000  |          #
-#                           Maximum                                    #  
7.000  |          #
-#                           Range                                      #  
4.000  |          #
-#                           Interquartile Range                        #   
1.75  |          #
-#                           Skewness                                   #   
.304  |   .752   #
-#                           Kurtosis                                   #   
.146  |   1.481  #
-#======================================================================#=========#==========#
+#=========================================================================#=========#==========#
+#                Manufacturer                                            
#Statistic|Std. Error#
+#=========================================================================#=========#==========#
+#Breaking Strain Aspeger      Mean                                        #   
2.25  |   .453   #
+#                             95% Confidence Interval for Mean Lower Bound#  
2.279  |          #
+#                                                              Upper Bound#  
2.221  |          #
+#                             5% Trimmed Mean                             #   
2.22  |          #
+#                             Median                                      #   
2.00  |          #
+#                             Variance                                    #  
1.643  |          #
+#                             Std. Deviation                              #  
1.282  |          #
+#                             Minimum                                     #  
1.000  |          #
+#                             Maximum                                     #  
4.000  |          #
+#                             Range                                       #  
3.000  |          #
+#                             Interquartile Range                         #   
2.75  |          #
+#                             Skewness                                    #   
.475  |   .752   #
+#                             Kurtosis                                    #  
-1.546 |   1.481  #
+#               
----------------------------------------------------------#---------+----------#
+#                Bloggs       Mean                                        #   
3.50  |   .378   #
+#                             95% Confidence Interval for Mean Lower Bound#  
3.525  |          #
+#                                                              Upper Bound#  
3.475  |          #
+#                             5% Trimmed Mean                             #   
3.50  |          #
+#                             Median                                      #   
4.00  |          #
+#                             Variance                                    #  
1.143  |          #
+#                             Std. Deviation                              #  
1.069  |          #
+#                             Minimum                                     #  
2.000  |          #
+#                             Maximum                                     #  
5.000  |          #
+#                             Range                                       #  
3.000  |          #
+#                             Interquartile Range                         #   
1.75  |          #
+#                             Skewness                                    #  
-.468  |   .752   #
+#                             Kurtosis                                    #  
-.831  |   1.481  #
+#               
----------------------------------------------------------#---------+----------#
+#                Charlies     Mean                                        #   
4.88  |   .441   #
+#                             95% Confidence Interval for Mean Lower Bound#  
4.904  |          #
+#                                                              Upper Bound#  
4.846  |          #
+#                             5% Trimmed Mean                             #   
4.86  |          #
+#                             Median                                      #   
5.00  |          #
+#                             Variance                                    #  
1.554  |          #
+#                             Std. Deviation                              #  
1.246  |          #
+#                             Minimum                                     #  
3.000  |          #
+#                             Maximum                                     #  
7.000  |          #
+#                             Range                                       #  
4.000  |          #
+#                             Interquartile Range                         #   
1.75  |          #
+#                             Skewness                                    #   
.304  |   .752   #
+#                             Kurtosis                                    #   
.146  |   1.481  #
+#=========================================================================#=========#==========#
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
Index: pspp/tests/command/oneway.sh
diff -u pspp/tests/command/oneway.sh:1.16 pspp/tests/command/oneway.sh:1.17
--- pspp/tests/command/oneway.sh:1.16   Sat Mar 11 07:16:40 2006
+++ pspp/tests/command/oneway.sh        Mon Apr  3 20:07:55 2006
@@ -107,7 +107,7 @@
 +--------+------+
 2.1 ONEWAY.  Descriptives
 
#===============#========#==#====#==============#==========#=======================#=======#=======#
-#               |        #  |    |              |          |    95% Confidence 
    |       |       #
+#               |        #  |    |              |          |95% Confidence 
Interval|       |       #
 #               |        #  |    |              |          
+-----------+-----------+       |       #
 #               |        # N|Mean|Std. Deviation|Std. Error|Lower Bound|Upper 
Bound|Minimum|Maximum#
 
#===============#========#==#====#==============#==========#===========#===========#=======#=======#
Index: pspp/tests/command/t-test-1-indep-val.sh
diff -u pspp/tests/command/t-test-1-indep-val.sh:1.11 
pspp/tests/command/t-test-1-indep-val.sh:1.12
--- pspp/tests/command/t-test-1-indep-val.sh:1.11       Sat Mar 11 07:16:40 2006
+++ pspp/tests/command/t-test-1-indep-val.sh    Mon Apr  3 20:07:55 2006
@@ -109,7 +109,7 @@
 #===========#==#====#==============#========#
 2.2 T-TEST.  Independent Samples Test
 
#==============================#=========#===============================================================================#
-#                              #Levene's |                          t-test for 
Equality of Means                         #
+#                              # Levene's|                          t-test for 
Equality of Means                         #
 #                              
#----+----+-----+------+---------------+---------------+---------------------+------------#
 #                              #    |    |     |      |               |        
       |                     |    95%     #
 #                              #    |    |     |      |               |        
       |                     +------+-----#
Index: pspp/tests/command/t-test-pairs.sh
diff -u pspp/tests/command/t-test-pairs.sh:1.12 
pspp/tests/command/t-test-pairs.sh:1.13
--- pspp/tests/command/t-test-pairs.sh:1.12     Sat Mar 11 07:16:40 2006
+++ pspp/tests/command/t-test-pairs.sh  Mon Apr  3 20:07:55 2006
@@ -100,7 +100,7 @@
 
#===========#=====================================================#======#==#===============#
 #           #                  Paired Differences                 |      |  |  
             #
 #           #-------+--------------+---------------+--------------+      |  |  
             #
-#           #       |              |               |     95%      |      |  |  
             #
+#           #       |              |               |95% Confidence|      |  |  
             #
 #           #       |              |               +-------+------+      |  |  
             #
 #           #  Mean |Std. Deviation|Std. Error Mean| Lower | Upper|   t  
|df|Sig. (2-tailed)#
 
#===========#=======#==============#===============#=======#======#======#==#===============#
Index: pspp/tests/command/trimmed-mean.sh
diff -u pspp/tests/command/trimmed-mean.sh:1.13 
pspp/tests/command/trimmed-mean.sh:1.14
--- pspp/tests/command/trimmed-mean.sh:1.13     Sat Mar 11 07:16:40 2006
+++ pspp/tests/command/trimmed-mean.sh  Mon Apr  3 20:07:55 2006
@@ -99,23 +99,23 @@
 #X#52|   100%|0|     0%|52|   100%#
 #=#==#=======#=#=======#==#=======#
 2.2 EXAMINE.  Descriptives
-#============================================#=========#==========#
-#                                            #Statistic|Std. Error#
-#============================================#=========#==========#
-#XMean                                       #   2.02  |   .034   #
-# 95% Confidence Interval for MeanLower Bound#  2.021  |          #
-#                                 Upper Bound#  2.017  |          #
-# 5% Trimmed Mean                            #   2.00  |          #
-# Median                                     #   2.00  |          #
-# Variance                                   #   .058  |          #
-# Std. Deviation                             #   .242  |          #
-# Minimum                                    #  1.000  |          #
-# Maximum                                    #  3.000  |          #
-# Range                                      #  2.000  |          #
-# Interquartile Range                        #   .00   |          #
-# Skewness                                   #  1.194  |   .330   #
-# Kurtosis                                   #  15.732 |   .650   #
-#============================================#=========#==========#
+#==============================================#=========#==========#
+#                                              #Statistic|Std. Error#
+#==============================================#=========#==========#
+#X Mean                                        #   2.02  |   .034   #
+#  95% Confidence Interval for Mean Lower Bound#  2.021  |          #
+#                                   Upper Bound#  2.017  |          #
+#  5% Trimmed Mean                             #   2.00  |          #
+#  Median                                      #   2.00  |          #
+#  Variance                                    #   .058  |          #
+#  Std. Deviation                              #   .242  |          #
+#  Minimum                                     #  1.000  |          #
+#  Maximum                                     #  3.000  |          #
+#  Range                                       #  2.000  |          #
+#  Interquartile Range                         #   .00   |          #
+#  Skewness                                    #  1.194  |   .330   #
+#  Kurtosis                                    #  15.732 |   .650   #
+#==============================================#=========#==========#
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]