Merge branch 'master' into output fc11-i386-build61 fc11-x64-build58 lenny-x64-build82 sid-i386-build128
authorJohn Darrington <john@darrington.wattle.id.au>
Fri, 18 Dec 2009 19:34:12 +0000 (20:34 +0100)
committerJohn Darrington <john@darrington.wattle.id.au>
Fri, 18 Dec 2009 19:34:12 +0000 (20:34 +0100)
Conflicts:

NEWS
configure.ac
src/output/charts/dummy-chart.c

170 files changed:
INSTALL
NEWS
THANKS
acinclude.m4
config/automake.mk
config/devices
config/psfonts/Courier-Bold.afm [deleted file]
config/psfonts/Courier-BoldOblique.afm [deleted file]
config/psfonts/Courier-Oblique.afm [deleted file]
config/psfonts/Courier.afm [deleted file]
config/psfonts/Helvetica-Bold.afm [deleted file]
config/psfonts/Helvetica-BoldOblique.afm [deleted file]
config/psfonts/Helvetica-Oblique.afm [deleted file]
config/psfonts/Helvetica.afm [deleted file]
config/psfonts/Times-Bold.afm [deleted file]
config/psfonts/Times-BoldItalic.afm [deleted file]
config/psfonts/Times-Italic.afm [deleted file]
config/psfonts/Times-Roman.afm [deleted file]
configure.ac
doc/configuring.texi
lib/automake.mk
perl-module/lib/PSPP.pm
src/data/automake.mk
src/data/caseproto.c
src/data/caseproto.h
src/data/casereader-project.c [new file with mode: 0644]
src/data/casereader-select.c [new file with mode: 0644]
src/data/casereader-translator.c
src/data/casereader.c
src/data/casereader.h
src/data/casewindow.c
src/data/datasheet.c
src/data/dictionary.c
src/data/file-name.c
src/data/file-name.h
src/data/procedure.c
src/data/subcase.c
src/data/subcase.h
src/data/value-labels.c
src/data/value-labels.h
src/data/variable.c
src/language/automake.mk
src/language/command.c
src/language/control/repeat.c
src/language/data-io/data-parser.c
src/language/data-io/list.q
src/language/data-io/print.c
src/language/dictionary/split-file.c
src/language/dictionary/sys-file-info.c
src/language/lexer/variable-parser.c
src/language/stats/aggregate.c
src/language/stats/binomial.c
src/language/stats/chisquare.c
src/language/stats/correlations.c
src/language/stats/crosstabs.q
src/language/stats/descriptives.c
src/language/stats/examine.q
src/language/stats/frequencies.q
src/language/stats/npar-summary.c
src/language/stats/npar.q
src/language/stats/oneway.q
src/language/stats/regression.q
src/language/stats/reliability.q
src/language/stats/roc.c
src/language/stats/roc.h [new file with mode: 0644]
src/language/stats/sign.c
src/language/stats/t-test.q
src/language/stats/wilcoxon.c
src/language/syntax-file.c
src/language/syntax-string-source.c
src/language/utilities/echo.c
src/language/xforms/count.c
src/libpspp/abt.c
src/libpspp/abt.h
src/libpspp/automake.mk
src/libpspp/bt.c
src/libpspp/bt.h
src/libpspp/cast.h [new file with mode: 0644]
src/libpspp/hash.h
src/libpspp/heap.h
src/libpspp/hmap.h
src/libpspp/hmapx.c
src/libpspp/hmapx.h
src/libpspp/ll.c
src/libpspp/ll.h
src/libpspp/llx.c
src/libpspp/range-map.h
src/libpspp/range-set.c
src/libpspp/range-set.h
src/libpspp/sparse-array.c
src/libpspp/str.c
src/libpspp/string-map.c [new file with mode: 0644]
src/libpspp/string-map.h [new file with mode: 0644]
src/libpspp/string-set.c [new file with mode: 0644]
src/libpspp/string-set.h [new file with mode: 0644]
src/libpspp/taint.c
src/libpspp/tmpfile.c
src/libpspp/tower.c
src/libpspp/tower.h
src/math/box-whisker.c
src/math/box-whisker.h
src/math/histogram.c
src/math/histogram.h
src/math/levene.c
src/math/np.c
src/math/np.h
src/math/order-stats.c
src/math/percentiles.c
src/math/percentiles.h
src/math/trimmed-mean.c
src/math/trimmed-mean.h
src/math/tukey-hinges.c
src/math/tukey-hinges.h
src/output/afm.c [deleted file]
src/output/afm.h [deleted file]
src/output/ascii.c
src/output/automake.mk
src/output/cairo.c [new file with mode: 0644]
src/output/cairo.h [new file with mode: 0644]
src/output/chart-provider.h [new file with mode: 0644]
src/output/chart.c
src/output/chart.h
src/output/charts/Makefile [deleted file]
src/output/charts/automake.mk [deleted file]
src/output/charts/barchart.c [deleted file]
src/output/charts/barchart.h [deleted file]
src/output/charts/box-whisker.c [deleted file]
src/output/charts/box-whisker.h [deleted file]
src/output/charts/boxplot.c [new file with mode: 0644]
src/output/charts/boxplot.h [new file with mode: 0644]
src/output/charts/cartesian.c
src/output/charts/cartesian.h
src/output/charts/dummy-chart.c [deleted file]
src/output/charts/np-plot.c [new file with mode: 0644]
src/output/charts/np-plot.h [new file with mode: 0644]
src/output/charts/piechart.c
src/output/charts/piechart.h
src/output/charts/plot-chart.c
src/output/charts/plot-chart.h
src/output/charts/plot-hist.c
src/output/charts/plot-hist.h
src/output/charts/roc-chart.c [new file with mode: 0644]
src/output/charts/roc-chart.h [new file with mode: 0644]
src/output/html.c
src/output/manager.c
src/output/manager.h
src/output/odt.c [new file with mode: 0644]
src/output/output.c
src/output/output.h
src/output/postscript.c [deleted file]
src/output/table.c
src/output/table.h
src/ui/automake.mk
src/ui/gui/automake.mk
src/ui/gui/executor.c
src/ui/gui/find-dialog.c
src/ui/gui/output-viewer.glade
src/ui/gui/psppire-output-window.c
src/ui/gui/psppire-output-window.h
src/ui/gui/psppire.c
src/ui/gui/syntax-editor-source.c
src/ui/gui/val-labs-dialog.c
src/ui/terminal/automake.mk
src/ui/terminal/read-line.c
tests/automake.mk
tests/libpspp/hmap-test.c
tests/libpspp/hmapx-test.c
tests/libpspp/range-set-test.c
tests/libpspp/string-map-test.c [new file with mode: 0644]
tests/libpspp/string-set-test.c [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
index 4f1bc84cef9b3c3eda18fc9165cdbc0f5e61d5aa..0f4f5c9687567dfaa9c322407387f889ed7d19be 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -33,12 +33,13 @@ The following packages are required to install PSPP:
       If you don't have a version already, you can install GNU
       libiconv (http://www.gnu.org/software/libiconv/).
 
-The following package is required to enable PSPP's graphing features.
-If you cannot arrange to install it, you must run `configure' with
---without-libplot.
+The following packages are required to enable PSPP's graphing
+features.  If you cannot arrange to install them, you must run
+`configure' with --without-cairo.
 
-    * libplot, from GNU plotutils
-      (http://www.gnu.org/software/plotutils/).
+    * Cairo (http://cairographics.org/), version 1.5 or later.
+
+    * Pango (http://www.pango.org/), version 1.22 or later.
 
 The following packages are required to enable PSPPIRE, the graphical
 user interface for PSPP.  If you cannot install them or do not wish to
@@ -209,14 +210,17 @@ suffix on their names by giving `configure' the  option
 Optional Features
 =================
 
-`--without-libplot'
-    Don't compile in support for charts (using libplot).  This is
-    useful if your system doesn't have the libplot library.
+`--without-cairo'
+    Don't compile in support for charts (using Cairo and Pango).  This
+    is useful if your system lacks these libraries.
 
 `--without-gui'
     Don't build the PSPPIRE gui.  Use this option if you only want to
     build the command line version of PSPP.
 
+    Cairo and Pango required to build the GUI, so --without-cairo
+    implies --without-gui.
+
 `--with-gui-tools'
     Build the gui developer tools.  There is no reason to use this
     option unless you're involved with the development of PSPP
diff --git a/NEWS b/NEWS
index 149d1deadbad2b44269d9a6b787ac9ed9ed10c80..ab21afde986538b1ac362b74cedc3191f243112f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,19 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2009-10-06 20:46:21 blp>
+Time-stamp: <2009-12-05 20:39:07 blp>
 Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
 See the end for copying conditions.
 
 Please send PSPP bug reports to bug-gnu-pspp@gnu.org.
 
+Changes from 0.7.2 to 0.7.3:
+
+ * Charts are now produced with Cairo and Pango, instead of libplot.
+   Without them, the new graphing features will not work.  If you do
+   not have Cairo and Pango installed, you must run `configure' with
+   --without-cairo.
+
+ * The PostScript driver has been removed.
+
 Changes from 0.7.1 to 0.7.2:
 
  * Updated Perl module interface.
diff --git a/THANKS b/THANKS
index 5af731084b7a32f5e571832a10a4e2680609afca..b5bd839e9db0e18c3cba22f313d95c1b5ef2aec6 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -12,5 +12,3 @@ Thanks to...
      * François Pinard for advice on proceeding with development.
 
      * Jim Van Zandt for Debian packaging and suggestions.
-
-     * The authors of libplot and libgsl for providing those libraries.
index 307ce149a4344f3f5e373146cac3fefea7b5a08c..14d11dc70a26e44bc22f28bac8ca5418360c4843 100644 (file)
@@ -43,40 +43,6 @@ AC_DEFUN([PSPP_PERL],
   AC_SUBST([VERSION_FOR_PERL])
 ])
 
-dnl Check that libplot is available.
-AC_DEFUN([PSPP_LIBPLOT],
-[
-  AC_ARG_WITH(
-    libplot, 
-    [AS_HELP_STRING([--without-libplot],
-                    [don't compile in support of charts (using libplot)])])
-
-  if test x"$with_libplot" != x"no" ; then 
-    # Check whether we can link against libplot without any extra libraries.
-    AC_CHECK_LIB(plot, pl_newpl_r, [LIBPLOT_LIBS="-lplot"])
-
-    # Check whether we can link against libplot if we also link X.
-    if test x"$LIBPLOT_LIBS" = x""; then
-      AC_PATH_XTRA
-      extra_libs="-lXaw -lXmu -lXt $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS -lm"
-      AC_CHECK_LIB(plot, pl_newpl_r,
-                  [LIBPLOT_LIBS="-lplot $extra_libs"
-                    LDFLAGS="$LDFLAGS $X_LIBS"],,
-                  [$extra_libs])
-    fi
-
-    # Still can't link?
-    if test x"$LIBPLOT_LIBS" = x""; then
-      PSPP_REQUIRED_PREREQ([libplot (or use --without-libplot)])
-    fi
-
-    # Set up to make everything work.
-    LIBS="$LIBPLOT_LIBS $LIBS"
-    AC_DEFINE(HAVE_LIBPLOT, 1,
-              [Define to 1 if you have the `libplot' library (-lplot).])
-  fi
-])
-
 dnl PSPP_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
 dnl Check whether the given C compiler OPTION is accepted.
 dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
index 88b63ac1b0bc59a9f449c18165a235b4cc186f01..509706e571bbbdaff246c8c91cca6c90923ba613 100644 (file)
@@ -4,19 +4,4 @@
 dist_pkgsysconf_DATA = \
        config/devices
 
-psfontsdir = $(pkgsysconfdir)/psfonts
-dist_psfonts_DATA = \
-       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 += config/OChangeLog
index 48513fe6bc83899b127136dad62994f2a28cc72c..3bc685a004fb7eb2cdf871a4e696b89557f37651 100644 (file)
@@ -80,6 +80,13 @@ html:html::
 # PostScript device.
 list-ps:postscript::
 
+# Cairo devices.
+pdf:cairo:listing:
+svg:cairo:listing:output-type=svg output-file="pspp.svg"
+ps-cairo:cairo:listing:output-type=ps output-file="pspp-cairo.ps"
+
+odt:odf:listing:debug=off output-file="pspp.odt"
+
 # Devices that support the IBM PC line-drawing characters.
 define ibmpc-graphics \
   box[0000]='\x20' box[0001]='\xb3' box[0002]='\xba' \
@@ -115,6 +122,89 @@ tty-ibmpc:ascii:screen:length=$viewlength width=$viewwidth ${ibmpc-graphics} \
 list-ibmpc:ascii:listing:length=66 width=79 output-file=${list-output-file} \
   ${ibmpc-graphics}
 
+# Devices that support Unicode line-drawing characters in UTF-8 encoding.
+# PSPP doesn't support a \u escape but if it did then this is how these
+# would appear:
+#
+#     define utf8-graphics \
+#       box[0000]='\u0020' box[1000]='\u2576' box[2000]='\u2550' \
+#       box[0100]='\u2577' box[1100]='\u256D' box[2100]='\u2552' \
+#       box[0200]='\u2551' box[1200]='\u2553' box[2200]='\u2554' \
+#       box[0010]='\u2574' box[1010]='\u2500' box[2010]='\u2550' \
+#       box[0110]='\u256E' box[1110]='\u252C' box[2110]='\u2564' \
+#       box[0210]='\u2556' box[1210]='\u2565' box[2210]='\u2566' \
+#       box[0020]='\u2550' box[1020]='\u2550' box[2020]='\u2550' \
+#       box[0120]='\u2555' box[1120]='\u2564' box[2120]='\u2564' \
+#       box[0220]='\u2557' box[1220]='\u2566' box[2220]='\u2566' \
+#       box[0001]='\u2575' box[1001]='\u2570' box[2001]='\u2558' \
+#       box[0101]='\u2502' box[1101]='\u251C' box[2101]='\u255E' \
+#       box[0201]='\u2551' box[1201]='\u255F' box[2201]='\u2560' \
+#       box[0011]='\u256F' box[1011]='\u2534' box[2011]='\u2567' \
+#       box[0111]='\u2524' box[1111]='\u253C' box[2111]='\u256A' \
+#       box[0211]='\u2562' box[1211]='\u256B' box[2211]='\u256C' \
+#       box[0021]='\u255B' box[1021]='\u2567' box[2021]='\u2567' \
+#       box[0121]='\u2561' box[1121]='\u256A' box[2121]='\u256A' \
+#       box[0221]='\u2563' box[1221]='\u256C' box[2221]='\u256C' \
+#       box[0002]='\u2551' box[1002]='\u2559' box[2002]='\u255A' \
+#       box[0102]='\u2551' box[1102]='\u255F' box[2102]='\u2560' \
+#       box[0202]='\u2551' box[1202]='\u255F' box[2202]='\u2560' \
+#       box[0012]='\u255C' box[1012]='\u2568' box[2012]='\u2569' \
+#       box[0112]='\u2562' box[1112]='\u256A' box[2112]='\u256C' \
+#       box[0212]='\u2562' box[1212]='\u256B' box[2212]='\u256C' \
+#       box[0022]='\u255D' box[1022]='\u2569' box[2022]='\u2569' \
+#       box[0122]='\u2563' box[1122]='\u256C' box[2122]='\u256C' \
+#       box[0222]='\u2563' box[1222]='\u256C' box[2222]='\u256C'
+#
+# Instead, we encode them in UTF-8 by hand below.
+#
+# Here is a little Perl program that I used to do this translation:
+#
+#     sub utf8_encode {
+#         my $val = hex($_[0]);
+#         my $d0 = 0xe0 | ($val >> 12);
+#         my $d1 = 0x80 | (($val >> 6) & 0x3f);
+#         my $d2 = 0x80 | ($val & 0x3f);
+#         return sprintf('\x%02x\x%02x\x%02x', $d0, $d1, $d2);
+#     }
+#     while (<>) {
+#         s/\\u(....)/utf8_encode($1)/ge;
+#         print $_;
+#     }
+#
+define utf8-graphics \
+  box[0000]='\xe0\x80\xa0' box[1000]='\xe2\x95\xb6' box[2000]='\xe2\x95\x90' \
+  box[0100]='\xe2\x95\xb7' box[1100]='\xe2\x95\xad' box[2100]='\xe2\x95\x92' \
+  box[0200]='\xe2\x95\x91' box[1200]='\xe2\x95\x93' box[2200]='\xe2\x95\x94' \
+  box[0010]='\xe2\x95\xb4' box[1010]='\xe2\x94\x80' box[2010]='\xe2\x95\x90' \
+  box[0110]='\xe2\x95\xae' box[1110]='\xe2\x94\xac' box[2110]='\xe2\x95\xa4' \
+  box[0210]='\xe2\x95\x96' box[1210]='\xe2\x95\xa5' box[2210]='\xe2\x95\xa6' \
+  box[0020]='\xe2\x95\x90' box[1020]='\xe2\x95\x90' box[2020]='\xe2\x95\x90' \
+  box[0120]='\xe2\x95\x95' box[1120]='\xe2\x95\xa4' box[2120]='\xe2\x95\xa4' \
+  box[0220]='\xe2\x95\x97' box[1220]='\xe2\x95\xa6' box[2220]='\xe2\x95\xa6' \
+  box[0001]='\xe2\x95\xb5' box[1001]='\xe2\x95\xb0' box[2001]='\xe2\x95\x98' \
+  box[0101]='\xe2\x94\x82' box[1101]='\xe2\x94\x9c' box[2101]='\xe2\x95\x9e' \
+  box[0201]='\xe2\x95\x91' box[1201]='\xe2\x95\x9f' box[2201]='\xe2\x95\xa0' \
+  box[0011]='\xe2\x95\xaf' box[1011]='\xe2\x94\xb4' box[2011]='\xe2\x95\xa7' \
+  box[0111]='\xe2\x94\xa4' box[1111]='\xe2\x94\xbc' box[2111]='\xe2\x95\xaa' \
+  box[0211]='\xe2\x95\xa2' box[1211]='\xe2\x95\xab' box[2211]='\xe2\x95\xac' \
+  box[0021]='\xe2\x95\x9b' box[1021]='\xe2\x95\xa7' box[2021]='\xe2\x95\xa7' \
+  box[0121]='\xe2\x95\xa1' box[1121]='\xe2\x95\xaa' box[2121]='\xe2\x95\xaa' \
+  box[0221]='\xe2\x95\xa3' box[1221]='\xe2\x95\xac' box[2221]='\xe2\x95\xac' \
+  box[0002]='\xe2\x95\x91' box[1002]='\xe2\x95\x99' box[2002]='\xe2\x95\x9a' \
+  box[0102]='\xe2\x95\x91' box[1102]='\xe2\x95\x9f' box[2102]='\xe2\x95\xa0' \
+  box[0202]='\xe2\x95\x91' box[1202]='\xe2\x95\x9f' box[2202]='\xe2\x95\xa0' \
+  box[0012]='\xe2\x95\x9c' box[1012]='\xe2\x95\xa8' box[2012]='\xe2\x95\xa9' \
+  box[0112]='\xe2\x95\xa2' box[1112]='\xe2\x95\xaa' box[2112]='\xe2\x95\xac' \
+  box[0212]='\xe2\x95\xa2' box[1212]='\xe2\x95\xab' box[2212]='\xe2\x95\xac' \
+  box[0022]='\xe2\x95\x9d' box[1022]='\xe2\x95\xa9' box[2022]='\xe2\x95\xa9' \
+  box[0122]='\xe2\x95\xa3' box[1122]='\xe2\x95\xac' box[2122]='\xe2\x95\xac' \
+  box[0222]='\xe2\x95\xa3' box[1222]='\xe2\x95\xac' box[2222]='\xe2\x95\xac'
+
+tty-utf8:ascii:screen:length=$viewlength width=$viewwidth ${utf8-graphics} \
+  output-file=${tty-output-file} emphasis=none
+list-utf8:ascii:listing:length=66 width=79 output-file=${list-output-file} \
+  ${utf8-graphics} emphasis=none
+
 # Local Variables:
 # fill-prefix: "# "
 # End:
diff --git a/config/psfonts/Courier-Bold.afm b/config/psfonts/Courier-Bold.afm
deleted file mode 100644 (file)
index dbfdddb..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1989 1990 Adobe Systems Incorporated. All rights reserved.
-Comment Creation Date: Thu Jan 18 16:13:14 1990
-Comment UniqueID 27058
-Comment VMusage 28444 40812
-FontName Courier-Bold
-FullName Courier Bold
-FamilyName Courier
-Weight Bold
-ItalicAngle 0
-IsFixedPitch true
-FontBBox -40 -206 786 801
-UnderlinePosition -100
-UnderlineThickness 50
-Version 002.003
-Notice Copyright (c) 1989 1990 Adobe Systems Incorporated. All rights reserved.
-EncodingScheme AdobeStandardEncoding
-CapHeight 562
-XHeight 439
-Ascender 626
-Descender -142
-StartCharMetrics 260
-C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 600 ; N exclam ; B 212 -15 388 572 ;
-C 34 ; WX 600 ; N quotedbl ; B 145 297 455 562 ;
-C 35 ; WX 600 ; N numbersign ; B 66 -45 534 651 ;
-C 36 ; WX 600 ; N dollar ; B 92 -126 509 666 ;
-C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ;
-C 38 ; WX 600 ; N ampersand ; B 46 -15 536 543 ;
-C 39 ; WX 600 ; N quoteright ; B 188 297 412 562 ;
-C 40 ; WX 600 ; N parenleft ; B 229 -102 451 616 ;
-C 41 ; WX 600 ; N parenright ; B 149 -102 371 616 ;
-C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ;
-C 43 ; WX 600 ; N plus ; B 81 0 519 439 ;
-C 44 ; WX 600 ; N comma ; B 140 -101 374 164 ;
-C 45 ; WX 600 ; N hyphen ; B 110 213 490 303 ;
-C 46 ; WX 600 ; N period ; B 206 -15 394 151 ;
-C 47 ; WX 600 ; N slash ; B 105 -77 496 626 ;
-C 48 ; WX 600 ; N zero ; B 97 -15 503 616 ;
-C 49 ; WX 600 ; N one ; B 91 0 529 616 ;
-C 50 ; WX 600 ; N two ; B 71 0 489 616 ;
-C 51 ; WX 600 ; N three ; B 73 -15 491 616 ;
-C 52 ; WX 600 ; N four ; B 63 0 497 616 ;
-C 53 ; WX 600 ; N five ; B 80 -15 511 601 ;
-C 54 ; WX 600 ; N six ; B 100 -15 511 616 ;
-C 55 ; WX 600 ; N seven ; B 65 0 484 601 ;
-C 56 ; WX 600 ; N eight ; B 93 -15 507 616 ;
-C 57 ; WX 600 ; N nine ; B 89 -15 500 616 ;
-C 58 ; WX 600 ; N colon ; B 206 -15 394 405 ;
-C 59 ; WX 600 ; N semicolon ; B 140 -101 394 405 ;
-C 60 ; WX 600 ; N less ; B 66 -15 503 453 ;
-C 61 ; WX 600 ; N equal ; B 81 89 519 349 ;
-C 62 ; WX 600 ; N greater ; B 97 -15 534 453 ;
-C 63 ; WX 600 ; N question ; B 108 -15 491 580 ;
-C 64 ; WX 600 ; N at ; B 26 -15 574 616 ;
-C 65 ; WX 600 ; N A ; B 1 0 599 562 ;
-C 66 ; WX 600 ; N B ; B 40 0 563 562 ;
-C 67 ; WX 600 ; N C ; B 32 -18 554 580 ;
-C 68 ; WX 600 ; N D ; B 40 0 584 562 ;
-C 69 ; WX 600 ; N E ; B 40 0 545 562 ;
-C 70 ; WX 600 ; N F ; B 54 0 555 562 ;
-C 71 ; WX 600 ; N G ; B 32 -18 584 580 ;
-C 72 ; WX 600 ; N H ; B 30 0 570 562 ;
-C 73 ; WX 600 ; N I ; B 87 0 513 562 ;
-C 74 ; WX 600 ; N J ; B 47 -18 591 562 ;
-C 75 ; WX 600 ; N K ; B 36 0 584 562 ;
-C 76 ; WX 600 ; N L ; B 49 0 568 562 ;
-C 77 ; WX 600 ; N M ; B 3 0 597 562 ;
-C 78 ; WX 600 ; N N ; B 18 -12 600 562 ;
-C 79 ; WX 600 ; N O ; B 32 -18 568 580 ;
-C 80 ; WX 600 ; N P ; B 58 0 549 562 ;
-C 81 ; WX 600 ; N Q ; B 32 -123 568 580 ;
-C 82 ; WX 600 ; N R ; B 34 0 589 562 ;
-C 83 ; WX 600 ; N S ; B 57 -22 543 582 ;
-C 84 ; WX 600 ; N T ; B 31 0 569 562 ;
-C 85 ; WX 600 ; N U ; B 14 -18 586 562 ;
-C 86 ; WX 600 ; N V ; B -3 0 603 562 ;
-C 87 ; WX 600 ; N W ; B -8 0 608 562 ;
-C 88 ; WX 600 ; N X ; B 22 0 578 562 ;
-C 89 ; WX 600 ; N Y ; B 22 0 579 562 ;
-C 90 ; WX 600 ; N Z ; B 72 0 529 562 ;
-C 91 ; WX 600 ; N bracketleft ; B 255 -102 465 616 ;
-C 92 ; WX 600 ; N backslash ; B 105 -77 496 626 ;
-C 93 ; WX 600 ; N bracketright ; B 135 -102 345 616 ;
-C 94 ; WX 600 ; N asciicircum ; B 108 249 492 616 ;
-C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
-C 96 ; WX 600 ; N quoteleft ; B 188 297 412 562 ;
-C 97 ; WX 600 ; N a ; B 45 -15 560 454 ;
-C 98 ; WX 600 ; N b ; B 10 -15 574 626 ;
-C 99 ; WX 600 ; N c ; B 50 -15 539 459 ;
-C 100 ; WX 600 ; N d ; B 30 -15 581 626 ;
-C 101 ; WX 600 ; N e ; B 50 -15 553 454 ;
-C 102 ; WX 600 ; N f ; B 93 0 537 626 ; L i fi ; L l fl ;
-C 103 ; WX 600 ; N g ; B 40 -146 570 454 ;
-C 104 ; WX 600 ; N h ; B 15 0 582 626 ;
-C 105 ; WX 600 ; N i ; B 87 0 513 648 ;
-C 106 ; WX 600 ; N j ; B 73 -146 430 648 ;
-C 107 ; WX 600 ; N k ; B 30 0 575 626 ;
-C 108 ; WX 600 ; N l ; B 87 0 513 626 ;
-C 109 ; WX 600 ; N m ; B -12 0 616 454 ;
-C 110 ; WX 600 ; N n ; B 28 0 582 454 ;
-C 111 ; WX 600 ; N o ; B 40 -15 560 454 ;
-C 112 ; WX 600 ; N p ; B 9 -142 560 454 ;
-C 113 ; WX 600 ; N q ; B 30 -142 581 454 ;
-C 114 ; WX 600 ; N r ; B 57 0 570 454 ;
-C 115 ; WX 600 ; N s ; B 78 -17 525 459 ;
-C 116 ; WX 600 ; N t ; B 57 -15 522 562 ;
-C 117 ; WX 600 ; N u ; B 9 -15 559 439 ;
-C 118 ; WX 600 ; N v ; B 9 0 591 439 ;
-C 119 ; WX 600 ; N w ; B -8 0 608 439 ;
-C 120 ; WX 600 ; N x ; B 16 0 584 439 ;
-C 121 ; WX 600 ; N y ; B 6 -142 591 439 ;
-C 122 ; WX 600 ; N z ; B 91 0 510 439 ;
-C 123 ; WX 600 ; N braceleft ; B 170 -102 454 616 ;
-C 124 ; WX 600 ; N bar ; B 255 -77 345 626 ;
-C 125 ; WX 600 ; N braceright ; B 146 -102 430 616 ;
-C 126 ; WX 600 ; N asciitilde ; B 81 124 520 307 ;
-C 161 ; WX 600 ; N exclamdown ; B 212 -146 388 449 ;
-C 162 ; WX 600 ; N cent ; B 76 -49 508 614 ;
-C 163 ; WX 600 ; N sterling ; B 82 -28 548 611 ;
-C 164 ; WX 600 ; N fraction ; B 30 -60 571 661 ;
-C 165 ; WX 600 ; N yen ; B 20 0 580 591 ;
-C 166 ; WX 600 ; N florin ; B -20 -131 562 616 ;
-C 167 ; WX 600 ; N section ; B 103 -70 497 580 ;
-C 168 ; WX 600 ; N currency ; B 54 49 546 517 ;
-C 169 ; WX 600 ; N quotesingle ; B 237 297 363 562 ;
-C 170 ; WX 600 ; N quotedblleft ; B 81 297 519 562 ;
-C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ;
-C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ;
-C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ;
-C 174 ; WX 600 ; N fi ; B 22 0 583 626 ;
-C 175 ; WX 600 ; N fl ; B 22 0 583 626 ;
-C 177 ; WX 600 ; N endash ; B 75 213 525 303 ;
-C 178 ; WX 600 ; N dagger ; B 116 -70 484 580 ;
-C 179 ; WX 600 ; N daggerdbl ; B 116 -70 484 580 ;
-C 180 ; WX 600 ; N periodcentered ; B 206 136 394 302 ;
-C 182 ; WX 600 ; N paragraph ; B 16 -70 566 580 ;
-C 183 ; WX 600 ; N bullet ; B 150 142 450 420 ;
-C 184 ; WX 600 ; N quotesinglbase ; B 188 -140 412 125 ;
-C 185 ; WX 600 ; N quotedblbase ; B 81 -140 519 125 ;
-C 186 ; WX 600 ; N quotedblright ; B 81 297 519 562 ;
-C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ;
-C 188 ; WX 600 ; N ellipsis ; B 36 -15 564 96 ;
-C 189 ; WX 600 ; N perthousand ; B -40 -15 786 616 ;
-C 191 ; WX 600 ; N questiondown ; B 109 -146 492 449 ;
-C 193 ; WX 600 ; N grave ; B 132 508 395 661 ;
-C 194 ; WX 600 ; N acute ; B 205 508 468 661 ;
-C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ;
-C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ;
-C 197 ; WX 600 ; N macron ; B 88 505 512 585 ;
-C 198 ; WX 600 ; N breve ; B 83 468 517 631 ;
-C 199 ; WX 600 ; N dotaccent ; B 240 505 360 625 ;
-C 200 ; WX 600 ; N dieresis ; B 148 505 452 625 ;
-C 202 ; WX 600 ; N ring ; B 198 481 402 678 ;
-C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ;
-C 205 ; WX 600 ; N hungarumlaut ; B 95 508 578 661 ;
-C 206 ; WX 600 ; N ogonek ; B 169 -199 367 0 ;
-C 207 ; WX 600 ; N caron ; B 103 493 497 667 ;
-C 208 ; WX 600 ; N emdash ; B 0 213 600 303 ;
-C 225 ; WX 600 ; N AE ; B -29 0 562 562 ;
-C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ;
-C 232 ; WX 600 ; N Lslash ; B 49 0 568 562 ;
-C 233 ; WX 600 ; N Oslash ; B 32 -22 568 584 ;
-C 234 ; WX 600 ; N OE ; B 0 0 570 562 ;
-C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ;
-C 241 ; WX 600 ; N ae ; B 6 -15 591 454 ;
-C 245 ; WX 600 ; N dotlessi ; B 87 0 513 439 ;
-C 248 ; WX 600 ; N lslash ; B 87 0 513 626 ;
-C 249 ; WX 600 ; N oslash ; B 40 -24 560 463 ;
-C 250 ; WX 600 ; N oe ; B -8 -15 601 454 ;
-C 251 ; WX 600 ; N germandbls ; B 32 -15 586 626 ;
-C -1 ; WX 600 ; N scedilla ; B 78 -206 525 459 ;
-C -1 ; WX 600 ; N notegraphic ; B 87 -15 513 572 ;
-C -1 ; WX 600 ; N Ocircumflex ; B 32 -18 568 780 ;
-C -1 ; WX 600 ; N ll ; B -2 0 590 626 ;
-C -1 ; WX 600 ; N otilde ; B 40 -15 560 636 ;
-C -1 ; WX 600 ; N scaron ; B 78 -17 525 667 ;
-C -1 ; WX 600 ; N divide ; B 81 -15 519 453 ;
-C -1 ; WX 600 ; N Thorn ; B 58 0 547 562 ;
-C -1 ; WX 600 ; N format ; B 5 -146 95 598 ;
-C -1 ; WX 600 ; N largebullet ; B 258 239 342 323 ;
-C -1 ; WX 600 ; N Eth ; B 40 0 584 562 ;
-C -1 ; WX 600 ; N Odieresis ; B 32 -18 568 748 ;
-C -1 ; WX 600 ; N onesuperior ; B 158 230 442 616 ;
-C -1 ; WX 600 ; N dectab ; B 18 0 582 290 ;
-C -1 ; WX 600 ; N Ydieresis ; B 22 0 579 748 ;
-C -1 ; WX 600 ; N merge ; B 144 -15 456 487 ;
-C -1 ; WX 600 ; N IJ ; B 2 -18 612 562 ;
-C -1 ; WX 600 ; N ccedilla ; B 50 -206 539 459 ;
-C -1 ; WX 600 ; N multiply ; B 81 0 520 439 ;
-C -1 ; WX 600 ; N degree ; B 86 243 474 616 ;
-C -1 ; WX 600 ; N prescription ; B 34 -15 589 562 ;
-C -1 ; WX 600 ; N indent ; B 70 52 530 364 ;
-C -1 ; WX 600 ; N Otilde ; B 32 -18 568 759 ;
-C -1 ; WX 600 ; N thorn ; B -4 -142 560 626 ;
-C -1 ; WX 600 ; N mu ; B 9 -142 559 439 ;
-C -1 ; WX 600 ; N Yacute ; B 22 0 579 784 ;
-C -1 ; WX 600 ; N threesuperior ; B 148 222 423 616 ;
-C -1 ; WX 600 ; N logicalnot ; B 81 59 519 349 ;
-C -1 ; WX 600 ; N Ugrave ; B 14 -18 586 784 ;
-C -1 ; WX 600 ; N eth ; B 68 -27 533 626 ;
-C -1 ; WX 600 ; N left ; B 70 52 530 364 ;
-C -1 ; WX 600 ; N Ecircumflex ; B 40 0 545 780 ;
-C -1 ; WX 600 ; N edieresis ; B 50 -15 553 625 ;
-C -1 ; WX 600 ; N Ograve ; B 32 -18 568 784 ;
-C -1 ; WX 600 ; N down ; B 144 -15 456 422 ;
-C -1 ; WX 600 ; N Agrave ; B 1 0 599 784 ;
-C -1 ; WX 600 ; N atilde ; B 45 -15 560 636 ;
-C -1 ; WX 600 ; N up ; B 144 0 456 437 ;
-C -1 ; WX 600 ; N eacute ; B 50 -15 553 661 ;
-C -1 ; WX 600 ; N graybox ; B 76 0 525 599 ;
-C -1 ; WX 600 ; N lira ; B 82 -28 548 611 ;
-C -1 ; WX 600 ; N Icircumflex ; B 87 0 513 780 ;
-C -1 ; WX 600 ; N Adieresis ; B 1 0 599 748 ;
-C -1 ; WX 600 ; N yacute ; B 6 -142 591 661 ;
-C -1 ; WX 600 ; N icircumflex ; B 63 0 513 657 ;
-C -1 ; WX 600 ; N adieresis ; B 45 -15 560 625 ;
-C -1 ; WX 600 ; N zcaron ; B 91 0 510 667 ;
-C -1 ; WX 600 ; N Scaron ; B 57 -22 543 790 ;
-C -1 ; WX 600 ; N minus ; B 81 174 519 264 ;
-C -1 ; WX 600 ; N Aring ; B 1 0 599 801 ;
-C -1 ; WX 600 ; N Ucircumflex ; B 14 -18 586 780 ;
-C -1 ; WX 600 ; N plusminus ; B 81 0 519 461 ;
-C -1 ; WX 600 ; N ograve ; B 40 -15 560 661 ;
-C -1 ; WX 600 ; N Edieresis ; B 40 0 545 748 ;
-C -1 ; WX 600 ; N brokenbar ; B 255 -77 345 626 ;
-C -1 ; WX 600 ; N Idieresis ; B 87 0 513 748 ;
-C -1 ; WX 600 ; N acircumflex ; B 45 -15 560 657 ;
-C -1 ; WX 600 ; N ydieresis ; B 6 -142 591 625 ;
-C -1 ; WX 600 ; N Oacute ; B 32 -18 568 784 ;
-C -1 ; WX 600 ; N Egrave ; B 40 0 545 784 ;
-C -1 ; WX 600 ; N center ; B 40 14 560 580 ;
-C -1 ; WX 600 ; N threequarters ; B -20 -60 675 661 ;
-C -1 ; WX 600 ; N tab ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N ecircumflex ; B 50 -15 553 657 ;
-C -1 ; WX 600 ; N Eacute ; B 40 0 545 784 ;
-C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ;
-C -1 ; WX 600 ; N square ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N onehalf ; B -20 -60 675 661 ;
-C -1 ; WX 600 ; N onequarter ; B -20 -60 692 661 ;
-C -1 ; WX 600 ; N Uacute ; B 14 -18 586 784 ;
-C -1 ; WX 600 ; N Atilde ; B 1 0 599 759 ;
-C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
-C -1 ; WX 600 ; N Igrave ; B 87 0 513 784 ;
-C -1 ; WX 600 ; N Iacute ; B 87 0 513 784 ;
-C -1 ; WX 600 ; N Acircumflex ; B 1 0 599 780 ;
-C -1 ; WX 600 ; N Udieresis ; B 14 -18 586 748 ;
-C -1 ; WX 600 ; N Gcaron ; B 32 -18 584 790 ;
-C -1 ; WX 600 ; N Aacute ; B 1 0 599 784 ;
-C -1 ; WX 600 ; N LL ; B -35 0 635 562 ;
-C -1 ; WX 600 ; N twosuperior ; B 153 230 426 616 ;
-C -1 ; WX 600 ; N Scedilla ; B 57 -206 543 582 ;
-C -1 ; WX 600 ; N arrowboth ; B -24 143 624 455 ;
-C -1 ; WX 600 ; N udieresis ; B 9 -15 559 625 ;
-C -1 ; WX 600 ; N odieresis ; B 40 -15 560 625 ;
-C -1 ; WX 600 ; N aring ; B 45 -15 560 678 ;
-C -1 ; WX 600 ; N ij ; B 16 -146 564 648 ;
-C -1 ; WX 600 ; N arrowdown ; B 144 -15 456 608 ;
-C -1 ; WX 600 ; N igrave ; B 87 0 513 661 ;
-C -1 ; WX 600 ; N aacute ; B 45 -15 560 661 ;
-C -1 ; WX 600 ; N stop ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N ocircumflex ; B 40 -15 560 657 ;
-C -1 ; WX 600 ; N gcaron ; B 40 -146 570 667 ;
-C -1 ; WX 600 ; N iacute ; B 87 0 513 661 ;
-C -1 ; WX 600 ; N Ntilde ; B 18 -12 600 759 ;
-C -1 ; WX 600 ; N idieresis ; B 87 0 513 625 ;
-C -1 ; WX 600 ; N Ccedilla ; B 32 -206 554 580 ;
-C -1 ; WX 600 ; N arrowright ; B -24 143 624 455 ;
-C -1 ; WX 600 ; N ucircumflex ; B 9 -15 559 657 ;
-C -1 ; WX 600 ; N Idot ; B 87 0 513 748 ;
-C -1 ; WX 600 ; N agrave ; B 45 -15 560 661 ;
-C -1 ; WX 600 ; N ntilde ; B 28 0 582 636 ;
-C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
-C -1 ; WX 600 ; N return ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N Zcaron ; B 72 0 529 790 ;
-C -1 ; WX 600 ; N uacute ; B 9 -15 559 661 ;
-C -1 ; WX 600 ; N overscore ; B 0 579 600 629 ;
-C -1 ; WX 600 ; N egrave ; B 50 -15 553 661 ;
-C -1 ; WX 600 ; N ugrave ; B 9 -15 559 661 ;
-C -1 ; WX 600 ; N oacute ; B 40 -15 560 661 ;
-C -1 ; WX 600 ; N arrowleft ; B -24 143 624 455 ;
-C -1 ; WX 600 ; N arrowup ; B 144 0 456 623 ;
-EndCharMetrics
-StartComposites 62
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 30 123 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -30 123 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis -20 123 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave -50 123 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring -10 123 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde -30 123 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 0 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 30 123 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 0 123 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 0 123 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 0 123 ;
-CC Gcaron 2 ; PCC G 0 0 ; PCC caron 10 123 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 123 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 0 123 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 0 123 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 123 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 0 123 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 0 123 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 0 123 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 0 123 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 0 123 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 0 123 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 0 123 ;
-CC Scedilla 2 ; PCC S 0 0 ; PCC cedilla 20 0 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 30 123 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 0 123 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 0 123 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave -30 123 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 30 123 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 0 123 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 0 123 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex -20 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis -10 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave -30 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 0 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ;
-CC gcaron 2 ; PCC g 0 0 ; PCC caron -40 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -40 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -40 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ;
-CC scedilla 2 ; PCC s 0 0 ; PCC cedilla 0 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 0 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -20 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis -20 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 30 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 10 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 0 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Courier-BoldOblique.afm b/config/psfonts/Courier-BoldOblique.afm
deleted file mode 100644 (file)
index 77e50d7..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1989 1990 Adobe Systems Incorporated. All rights reserved.
-Comment Creation Date: Thu Jan 18 17:20:38 1990
-Comment UniqueID 27068
-Comment VMusage 6910 48006
-FontName Courier-BoldOblique
-FullName Courier Bold Oblique
-FamilyName Courier
-Weight Bold
-ItalicAngle -12
-IsFixedPitch true
-FontBBox -46 -206 868 801
-UnderlinePosition -100
-UnderlineThickness 50
-Version 002.003
-Notice Copyright (c) 1989 1990 Adobe Systems Incorporated. All rights reserved.
-EncodingScheme AdobeStandardEncoding
-CapHeight 562
-XHeight 439
-Ascender 626
-Descender -142
-StartCharMetrics 260
-C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 600 ; N exclam ; B 224 -15 484 572 ;
-C 34 ; WX 600 ; N quotedbl ; B 226 297 574 562 ;
-C 35 ; WX 600 ; N numbersign ; B 100 -45 628 651 ;
-C 36 ; WX 600 ; N dollar ; B 97 -126 619 666 ;
-C 37 ; WX 600 ; N percent ; B 103 -15 624 616 ;
-C 38 ; WX 600 ; N ampersand ; B 72 -15 582 543 ;
-C 39 ; WX 600 ; N quoteright ; B 251 297 531 562 ;
-C 40 ; WX 600 ; N parenleft ; B 276 -102 582 616 ;
-C 41 ; WX 600 ; N parenright ; B 127 -102 434 616 ;
-C 42 ; WX 600 ; N asterisk ; B 179 219 597 601 ;
-C 43 ; WX 600 ; N plus ; B 118 0 575 439 ;
-C 44 ; WX 600 ; N comma ; B 119 -101 409 164 ;
-C 45 ; WX 600 ; N hyphen ; B 155 213 554 303 ;
-C 46 ; WX 600 ; N period ; B 219 -15 410 151 ;
-C 47 ; WX 600 ; N slash ; B 96 -77 622 626 ;
-C 48 ; WX 600 ; N zero ; B 146 -15 583 616 ;
-C 49 ; WX 600 ; N one ; B 103 0 547 616 ;
-C 50 ; WX 600 ; N two ; B 71 0 583 616 ;
-C 51 ; WX 600 ; N three ; B 82 -15 561 616 ;
-C 52 ; WX 600 ; N four ; B 92 0 544 616 ;
-C 53 ; WX 600 ; N five ; B 87 -15 611 601 ;
-C 54 ; WX 600 ; N six ; B 146 -15 642 616 ;
-C 55 ; WX 600 ; N seven ; B 157 0 612 601 ;
-C 56 ; WX 600 ; N eight ; B 125 -15 594 616 ;
-C 57 ; WX 600 ; N nine ; B 86 -15 582 616 ;
-C 58 ; WX 600 ; N colon ; B 219 -15 464 405 ;
-C 59 ; WX 600 ; N semicolon ; B 119 -101 464 405 ;
-C 60 ; WX 600 ; N less ; B 113 -15 584 453 ;
-C 61 ; WX 600 ; N equal ; B 100 89 593 349 ;
-C 62 ; WX 600 ; N greater ; B 109 -15 581 453 ;
-C 63 ; WX 600 ; N question ; B 193 -15 581 580 ;
-C 64 ; WX 600 ; N at ; B 76 -15 627 616 ;
-C 65 ; WX 600 ; N A ; B 1 0 617 562 ;
-C 66 ; WX 600 ; N B ; B 40 0 619 562 ;
-C 67 ; WX 600 ; N C ; B 85 -18 664 580 ;
-C 68 ; WX 600 ; N D ; B 40 0 654 562 ;
-C 69 ; WX 600 ; N E ; B 40 0 654 562 ;
-C 70 ; WX 600 ; N F ; B 54 0 668 562 ;
-C 71 ; WX 600 ; N G ; B 85 -18 664 580 ;
-C 72 ; WX 600 ; N H ; B 30 0 689 562 ;
-C 73 ; WX 600 ; N I ; B 87 0 632 562 ;
-C 74 ; WX 600 ; N J ; B 69 -18 710 562 ;
-C 75 ; WX 600 ; N K ; B 36 0 676 562 ;
-C 76 ; WX 600 ; N L ; B 49 0 625 562 ;
-C 77 ; WX 600 ; N M ; B 3 0 716 562 ;
-C 78 ; WX 600 ; N N ; B 18 -12 719 562 ;
-C 79 ; WX 600 ; N O ; B 84 -18 636 580 ;
-C 80 ; WX 600 ; N P ; B 58 0 634 562 ;
-C 81 ; WX 600 ; N Q ; B 84 -123 636 580 ;
-C 82 ; WX 600 ; N R ; B 34 0 607 562 ;
-C 83 ; WX 600 ; N S ; B 64 -22 662 582 ;
-C 84 ; WX 600 ; N T ; B 96 0 668 562 ;
-C 85 ; WX 600 ; N U ; B 116 -18 705 562 ;
-C 86 ; WX 600 ; N V ; B 99 0 722 562 ;
-C 87 ; WX 600 ; N W ; B 94 0 727 562 ;
-C 88 ; WX 600 ; N X ; B 22 0 679 562 ;
-C 89 ; WX 600 ; N Y ; B 124 0 698 562 ;
-C 90 ; WX 600 ; N Z ; B 72 0 626 562 ;
-C 91 ; WX 600 ; N bracketleft ; B 233 -102 596 616 ;
-C 92 ; WX 600 ; N backslash ; B 231 -77 487 626 ;
-C 93 ; WX 600 ; N bracketright ; B 113 -102 476 616 ;
-C 94 ; WX 600 ; N asciicircum ; B 171 249 555 616 ;
-C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
-C 96 ; WX 600 ; N quoteleft ; B 251 297 531 562 ;
-C 97 ; WX 600 ; N a ; B 72 -15 578 454 ;
-C 98 ; WX 600 ; N b ; B 23 -15 626 626 ;
-C 99 ; WX 600 ; N c ; B 91 -15 621 459 ;
-C 100 ; WX 600 ; N d ; B 71 -15 634 626 ;
-C 101 ; WX 600 ; N e ; B 91 -15 594 454 ;
-C 102 ; WX 600 ; N f ; B 93 0 667 626 ; L i fi ; L l fl ;
-C 103 ; WX 600 ; N g ; B 51 -146 663 454 ;
-C 104 ; WX 600 ; N h ; B 28 0 600 626 ;
-C 105 ; WX 600 ; N i ; B 87 0 531 648 ;
-C 106 ; WX 600 ; N j ; B 47 -146 568 648 ;
-C 107 ; WX 600 ; N k ; B 43 0 632 626 ;
-C 108 ; WX 600 ; N l ; B 87 0 531 626 ;
-C 109 ; WX 600 ; N m ; B -12 0 634 454 ;
-C 110 ; WX 600 ; N n ; B 28 0 600 454 ;
-C 111 ; WX 600 ; N o ; B 81 -15 612 454 ;
-C 112 ; WX 600 ; N p ; B -21 -142 612 454 ;
-C 113 ; WX 600 ; N q ; B 71 -142 674 454 ;
-C 114 ; WX 600 ; N r ; B 57 0 644 454 ;
-C 115 ; WX 600 ; N s ; B 77 -17 597 459 ;
-C 116 ; WX 600 ; N t ; B 132 -15 556 562 ;
-C 117 ; WX 600 ; N u ; B 84 -15 577 439 ;
-C 118 ; WX 600 ; N v ; B 84 0 684 439 ;
-C 119 ; WX 600 ; N w ; B 67 0 701 439 ;
-C 120 ; WX 600 ; N x ; B 16 0 660 439 ;
-C 121 ; WX 600 ; N y ; B -10 -142 684 439 ;
-C 122 ; WX 600 ; N z ; B 91 0 603 439 ;
-C 123 ; WX 600 ; N braceleft ; B 216 -102 585 616 ;
-C 124 ; WX 600 ; N bar ; B 239 -77 478 626 ;
-C 125 ; WX 600 ; N braceright ; B 124 -102 494 616 ;
-C 126 ; WX 600 ; N asciitilde ; B 120 124 573 307 ;
-C 161 ; WX 600 ; N exclamdown ; B 207 -146 468 449 ;
-C 162 ; WX 600 ; N cent ; B 131 -49 594 614 ;
-C 163 ; WX 600 ; N sterling ; B 117 -28 640 611 ;
-C 164 ; WX 600 ; N fraction ; B 27 -60 702 661 ;
-C 165 ; WX 600 ; N yen ; B 110 0 706 591 ;
-C 166 ; WX 600 ; N florin ; B -46 -131 691 616 ;
-C 167 ; WX 600 ; N section ; B 94 -70 599 580 ;
-C 168 ; WX 600 ; N currency ; B 77 49 643 517 ;
-C 169 ; WX 600 ; N quotesingle ; B 318 297 482 562 ;
-C 170 ; WX 600 ; N quotedblleft ; B 144 297 638 562 ;
-C 171 ; WX 600 ; N guillemotleft ; B 63 70 638 446 ;
-C 172 ; WX 600 ; N guilsinglleft ; B 196 70 544 446 ;
-C 173 ; WX 600 ; N guilsinglright ; B 166 70 514 446 ;
-C 174 ; WX 600 ; N fi ; B 22 0 633 626 ;
-C 175 ; WX 600 ; N fl ; B 22 0 633 626 ;
-C 177 ; WX 600 ; N endash ; B 120 213 589 303 ;
-C 178 ; WX 600 ; N dagger ; B 188 -70 573 580 ;
-C 179 ; WX 600 ; N daggerdbl ; B 134 -70 574 580 ;
-C 180 ; WX 600 ; N periodcentered ; B 251 136 442 302 ;
-C 182 ; WX 600 ; N paragraph ; B 71 -70 689 580 ;
-C 183 ; WX 600 ; N bullet ; B 207 142 513 420 ;
-C 184 ; WX 600 ; N quotesinglbase ; B 158 -140 439 125 ;
-C 185 ; WX 600 ; N quotedblbase ; B 51 -140 546 125 ;
-C 186 ; WX 600 ; N quotedblright ; B 144 297 638 562 ;
-C 187 ; WX 600 ; N guillemotright ; B 72 70 647 446 ;
-C 188 ; WX 600 ; N ellipsis ; B 43 -15 574 96 ;
-C 189 ; WX 600 ; N perthousand ; B 24 -15 815 616 ;
-C 191 ; WX 600 ; N questiondown ; B 111 -146 499 449 ;
-C 193 ; WX 600 ; N grave ; B 272 508 503 661 ;
-C 194 ; WX 600 ; N acute ; B 313 508 608 661 ;
-C 195 ; WX 600 ; N circumflex ; B 212 483 606 657 ;
-C 196 ; WX 600 ; N tilde ; B 200 493 642 636 ;
-C 197 ; WX 600 ; N macron ; B 195 505 636 585 ;
-C 198 ; WX 600 ; N breve ; B 217 468 651 631 ;
-C 199 ; WX 600 ; N dotaccent ; B 359 505 482 625 ;
-C 200 ; WX 600 ; N dieresis ; B 267 505 574 625 ;
-C 202 ; WX 600 ; N ring ; B 319 481 528 678 ;
-C 203 ; WX 600 ; N cedilla ; B 169 -206 367 0 ;
-C 205 ; WX 600 ; N hungarumlaut ; B 203 508 718 661 ;
-C 206 ; WX 600 ; N ogonek ; B 144 -199 350 0 ;
-C 207 ; WX 600 ; N caron ; B 238 493 632 667 ;
-C 208 ; WX 600 ; N emdash ; B 45 213 664 303 ;
-C 225 ; WX 600 ; N AE ; B -29 0 667 562 ;
-C 227 ; WX 600 ; N ordfeminine ; B 189 196 526 580 ;
-C 232 ; WX 600 ; N Lslash ; B 49 0 625 562 ;
-C 233 ; WX 600 ; N Oslash ; B 48 -22 672 584 ;
-C 234 ; WX 600 ; N OE ; B 51 0 675 562 ;
-C 235 ; WX 600 ; N ordmasculine ; B 189 196 542 580 ;
-C 241 ; WX 600 ; N ae ; B 30 -15 641 454 ;
-C 245 ; WX 600 ; N dotlessi ; B 87 0 531 439 ;
-C 248 ; WX 600 ; N lslash ; B 87 0 574 626 ;
-C 249 ; WX 600 ; N oslash ; B 54 -24 637 463 ;
-C 250 ; WX 600 ; N oe ; B 29 -15 651 454 ;
-C 251 ; WX 600 ; N germandbls ; B 32 -15 618 626 ;
-C -1 ; WX 600 ; N scedilla ; B 77 -206 597 459 ;
-C -1 ; WX 600 ; N notegraphic ; B 99 -15 609 572 ;
-C -1 ; WX 600 ; N Ocircumflex ; B 84 -18 636 780 ;
-C -1 ; WX 600 ; N ll ; B 11 0 643 626 ;
-C -1 ; WX 600 ; N otilde ; B 81 -15 642 636 ;
-C -1 ; WX 600 ; N scaron ; B 77 -17 632 667 ;
-C -1 ; WX 600 ; N divide ; B 118 -15 575 453 ;
-C -1 ; WX 600 ; N Thorn ; B 58 0 613 562 ;
-C -1 ; WX 600 ; N format ; B -26 -146 222 598 ;
-C -1 ; WX 600 ; N largebullet ; B 317 239 403 323 ;
-C -1 ; WX 600 ; N Eth ; B 40 0 654 562 ;
-C -1 ; WX 600 ; N Odieresis ; B 84 -18 636 748 ;
-C -1 ; WX 600 ; N onesuperior ; B 218 230 506 616 ;
-C -1 ; WX 600 ; N dectab ; B 18 0 601 290 ;
-C -1 ; WX 600 ; N Ydieresis ; B 124 0 698 748 ;
-C -1 ; WX 600 ; N merge ; B 174 -15 526 487 ;
-C -1 ; WX 600 ; N IJ ; B 2 -18 731 562 ;
-C -1 ; WX 600 ; N ccedilla ; B 91 -206 621 459 ;
-C -1 ; WX 600 ; N multiply ; B 94 0 600 439 ;
-C -1 ; WX 600 ; N degree ; B 173 243 569 616 ;
-C -1 ; WX 600 ; N prescription ; B 34 -15 632 562 ;
-C -1 ; WX 600 ; N indent ; B 106 52 574 364 ;
-C -1 ; WX 600 ; N Otilde ; B 84 -18 668 759 ;
-C -1 ; WX 600 ; N thorn ; B -21 -142 612 626 ;
-C -1 ; WX 600 ; N mu ; B 60 -142 577 439 ;
-C -1 ; WX 600 ; N Yacute ; B 124 0 698 784 ;
-C -1 ; WX 600 ; N threesuperior ; B 203 222 515 616 ;
-C -1 ; WX 600 ; N logicalnot ; B 136 59 593 349 ;
-C -1 ; WX 600 ; N Ugrave ; B 116 -18 705 784 ;
-C -1 ; WX 600 ; N eth ; B 103 -27 651 626 ;
-C -1 ; WX 600 ; N left ; B 114 52 582 364 ;
-C -1 ; WX 600 ; N Ecircumflex ; B 40 0 654 780 ;
-C -1 ; WX 600 ; N edieresis ; B 91 -15 594 625 ;
-C -1 ; WX 600 ; N Ograve ; B 84 -18 636 784 ;
-C -1 ; WX 600 ; N down ; B 174 -15 486 422 ;
-C -1 ; WX 600 ; N Agrave ; B 1 0 617 784 ;
-C -1 ; WX 600 ; N atilde ; B 72 -15 642 636 ;
-C -1 ; WX 600 ; N up ; B 204 0 516 437 ;
-C -1 ; WX 600 ; N eacute ; B 91 -15 608 661 ;
-C -1 ; WX 600 ; N graybox ; B 76 0 652 599 ;
-C -1 ; WX 600 ; N lira ; B 117 -28 640 611 ;
-C -1 ; WX 600 ; N Icircumflex ; B 87 0 632 780 ;
-C -1 ; WX 600 ; N Adieresis ; B 1 0 617 748 ;
-C -1 ; WX 600 ; N yacute ; B -10 -142 684 661 ;
-C -1 ; WX 600 ; N icircumflex ; B 87 0 566 657 ;
-C -1 ; WX 600 ; N adieresis ; B 72 -15 578 625 ;
-C -1 ; WX 600 ; N zcaron ; B 91 0 632 667 ;
-C -1 ; WX 600 ; N Scaron ; B 64 -22 662 790 ;
-C -1 ; WX 600 ; N minus ; B 118 174 575 264 ;
-C -1 ; WX 600 ; N Aring ; B 1 0 617 801 ;
-C -1 ; WX 600 ; N Ucircumflex ; B 116 -18 705 780 ;
-C -1 ; WX 600 ; N plusminus ; B 81 0 592 461 ;
-C -1 ; WX 600 ; N ograve ; B 81 -15 612 661 ;
-C -1 ; WX 600 ; N Edieresis ; B 40 0 654 748 ;
-C -1 ; WX 600 ; N brokenbar ; B 239 -77 478 626 ;
-C -1 ; WX 600 ; N Idieresis ; B 87 0 632 748 ;
-C -1 ; WX 600 ; N acircumflex ; B 72 -15 586 657 ;
-C -1 ; WX 600 ; N ydieresis ; B -10 -142 684 625 ;
-C -1 ; WX 600 ; N Oacute ; B 84 -18 636 784 ;
-C -1 ; WX 600 ; N Egrave ; B 40 0 654 784 ;
-C -1 ; WX 600 ; N center ; B 103 14 623 580 ;
-C -1 ; WX 600 ; N threequarters ; B 35 -60 725 661 ;
-C -1 ; WX 600 ; N tab ; B 19 0 641 562 ;
-C -1 ; WX 600 ; N ecircumflex ; B 91 -15 606 657 ;
-C -1 ; WX 600 ; N Eacute ; B 40 0 665 784 ;
-C -1 ; WX 600 ; N trademark ; B 86 230 868 562 ;
-C -1 ; WX 600 ; N square ; B 19 0 700 562 ;
-C -1 ; WX 600 ; N onehalf ; B 50 -60 742 661 ;
-C -1 ; WX 600 ; N onequarter ; B 50 -60 742 661 ;
-C -1 ; WX 600 ; N Uacute ; B 116 -18 705 784 ;
-C -1 ; WX 600 ; N Atilde ; B 1 0 638 759 ;
-C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
-C -1 ; WX 600 ; N Igrave ; B 87 0 632 784 ;
-C -1 ; WX 600 ; N Iacute ; B 87 0 635 784 ;
-C -1 ; WX 600 ; N Acircumflex ; B 1 0 617 780 ;
-C -1 ; WX 600 ; N Udieresis ; B 116 -18 705 748 ;
-C -1 ; WX 600 ; N Gcaron ; B 85 -18 669 790 ;
-C -1 ; WX 600 ; N Aacute ; B 1 0 665 784 ;
-C -1 ; WX 600 ; N LL ; B -35 0 684 562 ;
-C -1 ; WX 600 ; N twosuperior ; B 202 230 531 616 ;
-C -1 ; WX 600 ; N Scedilla ; B 64 -206 662 582 ;
-C -1 ; WX 600 ; N arrowboth ; B 40 143 688 455 ;
-C -1 ; WX 600 ; N udieresis ; B 84 -15 577 625 ;
-C -1 ; WX 600 ; N odieresis ; B 81 -15 612 625 ;
-C -1 ; WX 600 ; N aring ; B 72 -15 578 678 ;
-C -1 ; WX 600 ; N ij ; B 16 -146 702 648 ;
-C -1 ; WX 600 ; N arrowdown ; B 174 -15 486 608 ;
-C -1 ; WX 600 ; N igrave ; B 87 0 531 661 ;
-C -1 ; WX 600 ; N aacute ; B 72 -15 608 661 ;
-C -1 ; WX 600 ; N stop ; B 19 0 700 562 ;
-C -1 ; WX 600 ; N ocircumflex ; B 81 -15 612 657 ;
-C -1 ; WX 600 ; N gcaron ; B 51 -146 663 667 ;
-C -1 ; WX 600 ; N iacute ; B 87 0 608 661 ;
-C -1 ; WX 600 ; N Ntilde ; B 18 -12 719 759 ;
-C -1 ; WX 600 ; N idieresis ; B 87 0 534 625 ;
-C -1 ; WX 600 ; N Ccedilla ; B 85 -206 664 580 ;
-C -1 ; WX 600 ; N arrowright ; B 32 143 688 455 ;
-C -1 ; WX 600 ; N ucircumflex ; B 84 -15 586 657 ;
-C -1 ; WX 600 ; N Idot ; B 87 0 632 748 ;
-C -1 ; WX 600 ; N agrave ; B 72 -15 578 661 ;
-C -1 ; WX 600 ; N ntilde ; B 28 0 642 636 ;
-C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
-C -1 ; WX 600 ; N return ; B 79 0 700 562 ;
-C -1 ; WX 600 ; N Zcaron ; B 72 0 659 790 ;
-C -1 ; WX 600 ; N uacute ; B 84 -15 608 661 ;
-C -1 ; WX 600 ; N overscore ; B 123 579 734 629 ;
-C -1 ; WX 600 ; N egrave ; B 91 -15 594 661 ;
-C -1 ; WX 600 ; N ugrave ; B 84 -15 577 661 ;
-C -1 ; WX 600 ; N oacute ; B 81 -15 612 661 ;
-C -1 ; WX 600 ; N arrowleft ; B 40 143 695 455 ;
-C -1 ; WX 600 ; N arrowup ; B 243 0 555 623 ;
-EndCharMetrics
-StartComposites 62
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 56 123 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -4 123 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 6 123 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave -24 123 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 16 123 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde -4 123 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 0 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 56 123 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 26 123 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 26 123 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 26 123 ;
-CC Gcaron 2 ; PCC G 0 0 ; PCC caron 36 123 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 26 123 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 26 123 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 26 123 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 26 123 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 26 123 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 26 123 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 26 123 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 26 123 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 26 123 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 26 123 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 26 123 ;
-CC Scedilla 2 ; PCC S 0 0 ; PCC cedilla 20 0 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 56 123 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 26 123 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 26 123 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave -4 123 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 56 123 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 26 123 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 26 123 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex -20 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis -10 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave -30 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 0 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ;
-CC gcaron 2 ; PCC g 0 0 ; PCC caron -40 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -40 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -40 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ;
-CC scedilla 2 ; PCC s 0 0 ; PCC cedilla 0 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 0 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -20 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis -20 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 30 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 10 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 0 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Courier-Oblique.afm b/config/psfonts/Courier-Oblique.afm
deleted file mode 100644 (file)
index 4dc9419..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1989, 1990 Adobe Systems Incorporated. All rights reserved.
-Comment Creation Date: Thu Jan 18 18:32:01 1990
-Comment UniqueID 27082
-Comment VMusage 6415 46660
-FontName Courier-Oblique
-FullName Courier Oblique
-FamilyName Courier
-Weight Medium
-ItalicAngle -12
-IsFixedPitch true
-FontBBox -30 -157 734 805
-UnderlinePosition -100
-UnderlineThickness 50
-Version 002.003
-Notice Copyright (c) 1989, 1990 Adobe Systems Incorporated. All rights reserved.
-EncodingScheme AdobeStandardEncoding
-CapHeight 562
-XHeight 426
-Ascender 629
-Descender -157
-StartCharMetrics 260
-C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 600 ; N exclam ; B 243 -15 457 572 ;
-C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ;
-C 35 ; WX 600 ; N numbersign ; B 142 -32 587 639 ;
-C 36 ; WX 600 ; N dollar ; B 115 -126 589 662 ;
-C 37 ; WX 600 ; N percent ; B 147 -15 596 622 ;
-C 38 ; WX 600 ; N ampersand ; B 94 -15 572 543 ;
-C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ;
-C 40 ; WX 600 ; N parenleft ; B 320 -108 565 622 ;
-C 41 ; WX 600 ; N parenright ; B 144 -108 389 622 ;
-C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ;
-C 43 ; WX 600 ; N plus ; B 128 0 562 426 ;
-C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ;
-C 45 ; WX 600 ; N hyphen ; B 161 238 549 278 ;
-C 46 ; WX 600 ; N period ; B 238 -15 382 109 ;
-C 47 ; WX 600 ; N slash ; B 111 -80 606 629 ;
-C 48 ; WX 600 ; N zero ; B 161 -15 568 622 ;
-C 49 ; WX 600 ; N one ; B 105 0 505 622 ;
-C 50 ; WX 600 ; N two ; B 77 0 561 622 ;
-C 51 ; WX 600 ; N three ; B 89 -15 531 622 ;
-C 52 ; WX 600 ; N four ; B 120 0 532 622 ;
-C 53 ; WX 600 ; N five ; B 105 -15 589 607 ;
-C 54 ; WX 600 ; N six ; B 162 -15 629 622 ;
-C 55 ; WX 600 ; N seven ; B 191 0 605 607 ;
-C 56 ; WX 600 ; N eight ; B 139 -15 581 622 ;
-C 57 ; WX 600 ; N nine ; B 100 -15 567 622 ;
-C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ;
-C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ;
-C 60 ; WX 600 ; N less ; B 86 -2 603 428 ;
-C 61 ; WX 600 ; N equal ; B 108 101 582 325 ;
-C 62 ; WX 600 ; N greater ; B 87 -2 604 428 ;
-C 63 ; WX 600 ; N question ; B 224 -15 576 572 ;
-C 64 ; WX 600 ; N at ; B 134 -15 575 622 ;
-C 65 ; WX 600 ; N A ; B 10 0 597 562 ;
-C 66 ; WX 600 ; N B ; B 50 0 609 562 ;
-C 67 ; WX 600 ; N C ; B 100 -18 651 580 ;
-C 68 ; WX 600 ; N D ; B 50 0 638 562 ;
-C 69 ; WX 600 ; N E ; B 60 0 653 562 ;
-C 70 ; WX 600 ; N F ; B 60 0 653 562 ;
-C 71 ; WX 600 ; N G ; B 90 -18 641 580 ;
-C 72 ; WX 600 ; N H ; B 39 0 680 562 ;
-C 73 ; WX 600 ; N I ; B 103 0 616 562 ;
-C 74 ; WX 600 ; N J ; B 59 -18 678 562 ;
-C 75 ; WX 600 ; N K ; B 45 0 664 562 ;
-C 76 ; WX 600 ; N L ; B 54 0 598 562 ;
-C 77 ; WX 600 ; N M ; B 11 0 708 562 ;
-C 78 ; WX 600 ; N N ; B 14 -13 705 562 ;
-C 79 ; WX 600 ; N O ; B 94 -18 625 580 ;
-C 80 ; WX 600 ; N P ; B 86 0 637 562 ;
-C 81 ; WX 600 ; N Q ; B 95 -129 625 580 ;
-C 82 ; WX 600 ; N R ; B 45 0 590 562 ;
-C 83 ; WX 600 ; N S ; B 83 -20 643 580 ;
-C 84 ; WX 600 ; N T ; B 116 0 658 562 ;
-C 85 ; WX 600 ; N U ; B 132 -18 695 562 ;
-C 86 ; WX 600 ; N V ; B 115 -13 716 562 ;
-C 87 ; WX 600 ; N W ; B 115 -13 716 562 ;
-C 88 ; WX 600 ; N X ; B 30 0 668 562 ;
-C 89 ; WX 600 ; N Y ; B 143 0 688 562 ;
-C 90 ; WX 600 ; N Z ; B 86 0 610 562 ;
-C 91 ; WX 600 ; N bracketleft ; B 253 -108 574 622 ;
-C 92 ; WX 600 ; N backslash ; B 256 -80 461 629 ;
-C 93 ; WX 600 ; N bracketright ; B 135 -108 456 622 ;
-C 94 ; WX 600 ; N asciicircum ; B 175 359 587 622 ;
-C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
-C 96 ; WX 600 ; N quoteleft ; B 294 328 506 562 ;
-C 97 ; WX 600 ; N a ; B 83 -15 559 441 ;
-C 98 ; WX 600 ; N b ; B 36 -15 618 629 ;
-C 99 ; WX 600 ; N c ; B 113 -15 607 441 ;
-C 100 ; WX 600 ; N d ; B 92 -15 633 629 ;
-C 101 ; WX 600 ; N e ; B 113 -15 591 441 ;
-C 102 ; WX 600 ; N f ; B 121 0 655 629 ; L i fi ; L l fl ;
-C 103 ; WX 600 ; N g ; B 68 -157 650 441 ;
-C 104 ; WX 600 ; N h ; B 40 0 582 629 ;
-C 105 ; WX 600 ; N i ; B 102 0 505 657 ;
-C 106 ; WX 600 ; N j ; B 59 -157 543 657 ;
-C 107 ; WX 600 ; N k ; B 65 0 626 629 ;
-C 108 ; WX 600 ; N l ; B 102 0 505 629 ;
-C 109 ; WX 600 ; N m ; B 2 0 606 441 ;
-C 110 ; WX 600 ; N n ; B 33 0 575 441 ;
-C 111 ; WX 600 ; N o ; B 102 -15 588 441 ;
-C 112 ; WX 600 ; N p ; B -17 -157 598 441 ;
-C 113 ; WX 600 ; N q ; B 92 -157 675 441 ;
-C 114 ; WX 600 ; N r ; B 67 0 631 441 ;
-C 115 ; WX 600 ; N s ; B 92 -15 570 441 ;
-C 116 ; WX 600 ; N t ; B 157 -15 534 561 ;
-C 117 ; WX 600 ; N u ; B 111 -15 562 426 ;
-C 118 ; WX 600 ; N v ; B 100 -10 674 426 ;
-C 119 ; WX 600 ; N w ; B 82 -10 692 426 ;
-C 120 ; WX 600 ; N x ; B 27 0 648 426 ;
-C 121 ; WX 600 ; N y ; B -30 -157 643 426 ;
-C 122 ; WX 600 ; N z ; B 106 0 586 426 ;
-C 123 ; WX 600 ; N braceleft ; B 240 -108 562 622 ;
-C 124 ; WX 600 ; N bar ; B 265 -80 453 629 ;
-C 125 ; WX 600 ; N braceright ; B 147 -108 470 622 ;
-C 126 ; WX 600 ; N asciitilde ; B 110 160 585 269 ;
-C 161 ; WX 600 ; N exclamdown ; B 232 -157 445 430 ;
-C 162 ; WX 600 ; N cent ; B 158 -49 581 614 ;
-C 163 ; WX 600 ; N sterling ; B 131 -21 614 611 ;
-C 164 ; WX 600 ; N fraction ; B 23 -57 706 665 ;
-C 165 ; WX 600 ; N yen ; B 135 0 693 594 ;
-C 166 ; WX 600 ; N florin ; B -19 -143 664 622 ;
-C 167 ; WX 600 ; N section ; B 111 -78 583 580 ;
-C 168 ; WX 600 ; N currency ; B 99 65 621 499 ;
-C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ;
-C 170 ; WX 600 ; N quotedblleft ; B 213 328 576 562 ;
-C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ;
-C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ;
-C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ;
-C 174 ; WX 600 ; N fi ; B 10 0 612 629 ;
-C 175 ; WX 600 ; N fl ; B 10 0 612 629 ;
-C 177 ; WX 600 ; N endash ; B 126 238 584 278 ;
-C 178 ; WX 600 ; N dagger ; B 226 -78 537 580 ;
-C 179 ; WX 600 ; N daggerdbl ; B 171 -78 537 580 ;
-C 180 ; WX 600 ; N periodcentered ; B 273 152 418 276 ;
-C 182 ; WX 600 ; N paragraph ; B 107 -78 623 562 ;
-C 183 ; WX 600 ; N bullet ; B 231 137 478 376 ;
-C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ;
-C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ;
-C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ;
-C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ;
-C 188 ; WX 600 ; N ellipsis ; B 51 -15 566 97 ;
-C 189 ; WX 600 ; N perthousand ; B 57 -15 627 622 ;
-C 191 ; WX 600 ; N questiondown ; B 112 -157 464 430 ;
-C 193 ; WX 600 ; N grave ; B 294 497 484 672 ;
-C 194 ; WX 600 ; N acute ; B 348 497 612 672 ;
-C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ;
-C 196 ; WX 600 ; N tilde ; B 219 503 617 606 ;
-C 197 ; WX 600 ; N macron ; B 232 525 600 565 ;
-C 198 ; WX 600 ; N breve ; B 279 501 576 609 ;
-C 199 ; WX 600 ; N dotaccent ; B 379 508 452 580 ;
-C 200 ; WX 600 ; N dieresis ; B 313 508 519 580 ;
-C 202 ; WX 600 ; N ring ; B 344 483 492 627 ;
-C 203 ; WX 600 ; N cedilla ; B 221 -151 340 10 ;
-C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ;
-C 206 ; WX 600 ; N ogonek ; B 207 -151 348 0 ;
-C 207 ; WX 600 ; N caron ; B 262 492 614 669 ;
-C 208 ; WX 600 ; N emdash ; B 51 238 659 278 ;
-C 225 ; WX 600 ; N AE ; B 10 0 648 562 ;
-C 227 ; WX 600 ; N ordfeminine ; B 216 259 504 580 ;
-C 232 ; WX 600 ; N Lslash ; B 54 0 598 562 ;
-C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ;
-C 234 ; WX 600 ; N OE ; B 66 0 665 562 ;
-C 235 ; WX 600 ; N ordmasculine ; B 217 259 530 580 ;
-C 241 ; WX 600 ; N ae ; B 47 -15 620 441 ;
-C 245 ; WX 600 ; N dotlessi ; B 102 0 505 426 ;
-C 248 ; WX 600 ; N lslash ; B 102 0 559 629 ;
-C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ;
-C 250 ; WX 600 ; N oe ; B 61 -15 609 441 ;
-C 251 ; WX 600 ; N germandbls ; B 55 -15 610 629 ;
-C -1 ; WX 600 ; N scedilla ; B 92 -151 570 441 ;
-C -1 ; WX 600 ; N notegraphic ; B 143 -15 557 572 ;
-C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 775 ;
-C -1 ; WX 600 ; N ll ; B 40 0 609 629 ;
-C -1 ; WX 600 ; N otilde ; B 102 -15 617 606 ;
-C -1 ; WX 600 ; N scaron ; B 92 -15 614 669 ;
-C -1 ; WX 600 ; N divide ; B 128 1 562 426 ;
-C -1 ; WX 600 ; N Thorn ; B 86 0 599 562 ;
-C -1 ; WX 600 ; N format ; B -28 -157 169 598 ;
-C -1 ; WX 600 ; N largebullet ; B 322 227 388 290 ;
-C -1 ; WX 600 ; N Eth ; B 50 0 638 562 ;
-C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 716 ;
-C -1 ; WX 600 ; N onesuperior ; B 238 249 481 622 ;
-C -1 ; WX 600 ; N dectab ; B 18 0 590 227 ;
-C -1 ; WX 600 ; N Ydieresis ; B 143 0 688 716 ;
-C -1 ; WX 600 ; N merge ; B 192 -15 497 436 ;
-C -1 ; WX 600 ; N IJ ; B 39 -18 681 562 ;
-C -1 ; WX 600 ; N ccedilla ; B 113 -151 607 441 ;
-C -1 ; WX 600 ; N multiply ; B 93 -1 599 426 ;
-C -1 ; WX 600 ; N degree ; B 214 269 576 622 ;
-C -1 ; WX 600 ; N prescription ; B 34 -15 610 562 ;
-C -1 ; WX 600 ; N indent ; B 110 75 574 341 ;
-C -1 ; WX 600 ; N Otilde ; B 94 -18 644 732 ;
-C -1 ; WX 600 ; N thorn ; B -17 -157 598 629 ;
-C -1 ; WX 600 ; N mu ; B 79 -157 562 426 ;
-C -1 ; WX 600 ; N Yacute ; B 143 0 688 793 ;
-C -1 ; WX 600 ; N threesuperior ; B 220 240 494 622 ;
-C -1 ; WX 600 ; N logicalnot ; B 148 64 582 325 ;
-C -1 ; WX 600 ; N Ugrave ; B 132 -18 695 793 ;
-C -1 ; WX 600 ; N eth ; B 102 -15 629 629 ;
-C -1 ; WX 600 ; N left ; B 114 75 578 341 ;
-C -1 ; WX 600 ; N Ecircumflex ; B 60 0 653 775 ;
-C -1 ; WX 600 ; N edieresis ; B 113 -15 591 580 ;
-C -1 ; WX 600 ; N Ograve ; B 94 -18 625 793 ;
-C -1 ; WX 600 ; N down ; B 192 -15 458 422 ;
-C -1 ; WX 600 ; N Agrave ; B 10 0 597 793 ;
-C -1 ; WX 600 ; N atilde ; B 83 -15 617 606 ;
-C -1 ; WX 600 ; N up ; B 232 0 498 437 ;
-C -1 ; WX 600 ; N eacute ; B 113 -15 612 672 ;
-C -1 ; WX 600 ; N graybox ; B 76 0 652 599 ;
-C -1 ; WX 600 ; N lira ; B 125 -21 614 611 ;
-C -1 ; WX 600 ; N Icircumflex ; B 103 0 616 775 ;
-C -1 ; WX 600 ; N Adieresis ; B 10 0 597 716 ;
-C -1 ; WX 600 ; N yacute ; B -30 -157 643 672 ;
-C -1 ; WX 600 ; N icircumflex ; B 102 0 551 654 ;
-C -1 ; WX 600 ; N adieresis ; B 83 -15 559 580 ;
-C -1 ; WX 600 ; N zcaron ; B 106 0 624 669 ;
-C -1 ; WX 600 ; N Scaron ; B 83 -20 673 805 ;
-C -1 ; WX 600 ; N minus ; B 128 195 562 232 ;
-C -1 ; WX 600 ; N Aring ; B 10 0 597 753 ;
-C -1 ; WX 600 ; N Ucircumflex ; B 132 -18 695 775 ;
-C -1 ; WX 600 ; N plusminus ; B 87 0 583 514 ;
-C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ;
-C -1 ; WX 600 ; N Edieresis ; B 60 0 653 716 ;
-C -1 ; WX 600 ; N brokenbar ; B 265 -80 453 629 ;
-C -1 ; WX 600 ; N Idieresis ; B 103 0 616 716 ;
-C -1 ; WX 600 ; N acircumflex ; B 83 -15 581 654 ;
-C -1 ; WX 600 ; N ydieresis ; B -30 -157 643 580 ;
-C -1 ; WX 600 ; N Oacute ; B 94 -18 638 793 ;
-C -1 ; WX 600 ; N Egrave ; B 60 0 653 793 ;
-C -1 ; WX 600 ; N center ; B 103 14 623 580 ;
-C -1 ; WX 600 ; N threequarters ; B 28 -57 711 665 ;
-C -1 ; WX 600 ; N tab ; B 19 0 641 562 ;
-C -1 ; WX 600 ; N ecircumflex ; B 113 -15 591 654 ;
-C -1 ; WX 600 ; N Eacute ; B 60 0 668 793 ;
-C -1 ; WX 600 ; N trademark ; B 108 263 710 562 ;
-C -1 ; WX 600 ; N square ; B 19 0 700 562 ;
-C -1 ; WX 600 ; N onehalf ; B 42 -57 725 665 ;
-C -1 ; WX 600 ; N onequarter ; B 48 -57 731 665 ;
-C -1 ; WX 600 ; N Uacute ; B 132 -18 695 793 ;
-C -1 ; WX 600 ; N Atilde ; B 10 0 644 732 ;
-C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
-C -1 ; WX 600 ; N Igrave ; B 103 0 616 793 ;
-C -1 ; WX 600 ; N Iacute ; B 103 0 638 793 ;
-C -1 ; WX 600 ; N Acircumflex ; B 10 0 597 775 ;
-C -1 ; WX 600 ; N Udieresis ; B 132 -18 695 716 ;
-C -1 ; WX 600 ; N Gcaron ; B 90 -18 643 805 ;
-C -1 ; WX 600 ; N Aacute ; B 10 0 658 793 ;
-C -1 ; WX 600 ; N LL ; B 15 0 640 562 ;
-C -1 ; WX 600 ; N twosuperior ; B 237 249 528 622 ;
-C -1 ; WX 600 ; N Scedilla ; B 83 -151 643 580 ;
-C -1 ; WX 600 ; N arrowboth ; B 36 122 692 476 ;
-C -1 ; WX 600 ; N udieresis ; B 111 -15 562 580 ;
-C -1 ; WX 600 ; N odieresis ; B 102 -15 588 580 ;
-C -1 ; WX 600 ; N aring ; B 83 -15 559 627 ;
-C -1 ; WX 600 ; N ij ; B 44 -157 623 657 ;
-C -1 ; WX 600 ; N arrowdown ; B 157 -15 511 608 ;
-C -1 ; WX 600 ; N igrave ; B 102 0 505 672 ;
-C -1 ; WX 600 ; N aacute ; B 83 -15 612 672 ;
-C -1 ; WX 600 ; N stop ; B 19 0 700 562 ;
-C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ;
-C -1 ; WX 600 ; N gcaron ; B 68 -157 650 669 ;
-C -1 ; WX 600 ; N iacute ; B 102 0 612 672 ;
-C -1 ; WX 600 ; N Ntilde ; B 14 -13 705 732 ;
-C -1 ; WX 600 ; N idieresis ; B 102 0 505 580 ;
-C -1 ; WX 600 ; N Ccedilla ; B 100 -151 651 580 ;
-C -1 ; WX 600 ; N arrowright ; B 35 122 688 476 ;
-C -1 ; WX 600 ; N ucircumflex ; B 111 -15 571 654 ;
-C -1 ; WX 600 ; N Idot ; B 103 0 616 716 ;
-C -1 ; WX 600 ; N agrave ; B 83 -15 559 672 ;
-C -1 ; WX 600 ; N ntilde ; B 33 0 617 606 ;
-C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
-C -1 ; WX 600 ; N return ; B 79 0 700 562 ;
-C -1 ; WX 600 ; N Zcaron ; B 86 0 643 805 ;
-C -1 ; WX 600 ; N uacute ; B 111 -15 602 672 ;
-C -1 ; WX 600 ; N overscore ; B 123 579 734 629 ;
-C -1 ; WX 600 ; N egrave ; B 113 -15 591 672 ;
-C -1 ; WX 600 ; N ugrave ; B 111 -15 562 672 ;
-C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ;
-C -1 ; WX 600 ; N arrowleft ; B 40 122 692 476 ;
-C -1 ; WX 600 ; N arrowup ; B 218 0 572 623 ;
-EndCharMetrics
-StartComposites 62
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 46 121 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -4 121 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis -1 136 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave -4 121 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 12 126 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 27 126 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 0 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 56 121 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 26 121 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 29 136 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 26 121 ;
-CC Gcaron 2 ; PCC G 0 0 ; PCC caron 29 136 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 26 121 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 26 121 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 29 136 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 26 121 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 27 126 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 26 121 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 26 121 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 29 136 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 26 121 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 27 126 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 59 136 ;
-CC Scedilla 2 ; PCC S 0 0 ; PCC cedilla 0 0 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 56 121 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 26 121 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 29 136 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave -4 121 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 56 121 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 29 136 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 29 136 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 0 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 0 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 0 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 0 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ;
-CC gcaron 2 ; PCC g 0 0 ; PCC caron -30 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -30 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -30 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -30 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ;
-CC scedilla 2 ; PCC s 0 0 ; PCC cedilla 0 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute -10 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -10 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 0 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute -20 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis -10 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 10 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Courier.afm b/config/psfonts/Courier.afm
deleted file mode 100644 (file)
index 3688908..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1989, 1990 Adobe Systems Incorporated. All rights reserved.
-Comment Creation Date: Thu Jan 18 17:48:17 1990
-Comment UniqueID 27077
-Comment VMusage 27874 40242
-FontName Courier
-FullName Courier
-FamilyName Courier
-Weight Medium
-ItalicAngle 0
-IsFixedPitch true
-FontBBox -28 -157 628 805
-UnderlinePosition -100
-UnderlineThickness 50
-Version 002.003
-Notice Copyright (c) 1989, 1990 Adobe Systems Incorporated. All rights reserved.
-EncodingScheme AdobeStandardEncoding
-CapHeight 562
-XHeight 426
-Ascender 629
-Descender -157
-StartCharMetrics 260
-C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
-C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
-C 35 ; WX 600 ; N numbersign ; B 100 -32 500 639 ;
-C 36 ; WX 600 ; N dollar ; B 112 -126 489 662 ;
-C 37 ; WX 600 ; N percent ; B 97 -15 515 622 ;
-C 38 ; WX 600 ; N ampersand ; B 70 -15 531 543 ;
-C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ;
-C 40 ; WX 600 ; N parenleft ; B 276 -108 433 622 ;
-C 41 ; WX 600 ; N parenright ; B 167 -108 324 622 ;
-C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;
-C 43 ; WX 600 ; N plus ; B 87 0 513 426 ;
-C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;
-C 45 ; WX 600 ; N hyphen ; B 110 238 490 278 ;
-C 46 ; WX 600 ; N period ; B 229 -15 371 109 ;
-C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;
-C 48 ; WX 600 ; N zero ; B 113 -15 487 622 ;
-C 49 ; WX 600 ; N one ; B 102 0 498 622 ;
-C 50 ; WX 600 ; N two ; B 77 0 464 622 ;
-C 51 ; WX 600 ; N three ; B 82 -15 459 622 ;
-C 52 ; WX 600 ; N four ; B 88 0 493 622 ;
-C 53 ; WX 600 ; N five ; B 99 -15 490 607 ;
-C 54 ; WX 600 ; N six ; B 118 -15 497 622 ;
-C 55 ; WX 600 ; N seven ; B 89 0 476 607 ;
-C 56 ; WX 600 ; N eight ; B 109 -15 491 622 ;
-C 57 ; WX 600 ; N nine ; B 103 -15 482 622 ;
-C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;
-C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;
-C 60 ; WX 600 ; N less ; B 41 -2 519 428 ;
-C 61 ; WX 600 ; N equal ; B 87 101 513 325 ;
-C 62 ; WX 600 ; N greater ; B 81 -2 559 428 ;
-C 63 ; WX 600 ; N question ; B 136 -15 485 572 ;
-C 64 ; WX 600 ; N at ; B 84 -15 526 622 ;
-C 65 ; WX 600 ; N A ; B 10 0 590 562 ;
-C 66 ; WX 600 ; N B ; B 50 0 552 562 ;
-C 67 ; WX 600 ; N C ; B 48 -18 533 580 ;
-C 68 ; WX 600 ; N D ; B 50 0 567 562 ;
-C 69 ; WX 600 ; N E ; B 60 0 543 562 ;
-C 70 ; WX 600 ; N F ; B 60 0 538 562 ;
-C 71 ; WX 600 ; N G ; B 38 -18 568 580 ;
-C 72 ; WX 600 ; N H ; B 39 0 561 562 ;
-C 73 ; WX 600 ; N I ; B 103 0 497 562 ;
-C 74 ; WX 600 ; N J ; B 41 -18 559 562 ;
-C 75 ; WX 600 ; N K ; B 45 0 575 562 ;
-C 76 ; WX 600 ; N L ; B 54 0 547 562 ;
-C 77 ; WX 600 ; N M ; B 11 0 589 562 ;
-C 78 ; WX 600 ; N N ; B 14 -13 586 562 ;
-C 79 ; WX 600 ; N O ; B 43 -18 557 580 ;
-C 80 ; WX 600 ; N P ; B 86 0 551 562 ;
-C 81 ; WX 600 ; N Q ; B 43 -129 557 580 ;
-C 82 ; WX 600 ; N R ; B 45 0 581 562 ;
-C 83 ; WX 600 ; N S ; B 79 -20 522 580 ;
-C 84 ; WX 600 ; N T ; B 45 0 556 562 ;
-C 85 ; WX 600 ; N U ; B 24 -18 576 562 ;
-C 86 ; WX 600 ; N V ; B 3 -13 597 562 ;
-C 87 ; WX 600 ; N W ; B 3 -13 597 562 ;
-C 88 ; WX 600 ; N X ; B 30 0 570 562 ;
-C 89 ; WX 600 ; N Y ; B 31 0 569 562 ;
-C 90 ; WX 600 ; N Z ; B 86 0 514 562 ;
-C 91 ; WX 600 ; N bracketleft ; B 276 -108 442 622 ;
-C 92 ; WX 600 ; N backslash ; B 125 -80 475 629 ;
-C 93 ; WX 600 ; N bracketright ; B 158 -108 324 622 ;
-C 94 ; WX 600 ; N asciicircum ; B 94 359 506 622 ;
-C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
-C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;
-C 97 ; WX 600 ; N a ; B 60 -15 552 441 ;
-C 98 ; WX 600 ; N b ; B 21 -15 568 629 ;
-C 99 ; WX 600 ; N c ; B 73 -15 522 441 ;
-C 100 ; WX 600 ; N d ; B 52 -15 584 629 ;
-C 101 ; WX 600 ; N e ; B 73 -15 541 441 ;
-C 102 ; WX 600 ; N f ; B 121 0 524 629 ; L i fi ; L l fl ;
-C 103 ; WX 600 ; N g ; B 52 -157 559 441 ;
-C 104 ; WX 600 ; N h ; B 25 0 575 629 ;
-C 105 ; WX 600 ; N i ; B 102 0 498 657 ;
-C 106 ; WX 600 ; N j ; B 89 -157 403 657 ;
-C 107 ; WX 600 ; N k ; B 50 0 573 629 ;
-C 108 ; WX 600 ; N l ; B 102 0 498 629 ;
-C 109 ; WX 600 ; N m ; B 2 0 598 441 ;
-C 110 ; WX 600 ; N n ; B 33 0 568 441 ;
-C 111 ; WX 600 ; N o ; B 62 -15 538 441 ;
-C 112 ; WX 600 ; N p ; B 16 -157 548 441 ;
-C 113 ; WX 600 ; N q ; B 52 -157 584 441 ;
-C 114 ; WX 600 ; N r ; B 67 0 552 441 ;
-C 115 ; WX 600 ; N s ; B 94 -15 506 441 ;
-C 116 ; WX 600 ; N t ; B 74 -15 503 561 ;
-C 117 ; WX 600 ; N u ; B 28 -15 555 426 ;
-C 118 ; WX 600 ; N v ; B 17 -10 583 426 ;
-C 119 ; WX 600 ; N w ; B -1 -10 601 426 ;
-C 120 ; WX 600 ; N x ; B 27 0 573 426 ;
-C 121 ; WX 600 ; N y ; B -12 -157 552 426 ;
-C 122 ; WX 600 ; N z ; B 106 0 495 426 ;
-C 123 ; WX 600 ; N braceleft ; B 189 -108 430 622 ;
-C 124 ; WX 600 ; N bar ; B 282 -80 319 629 ;
-C 125 ; WX 600 ; N braceright ; B 170 -108 411 622 ;
-C 126 ; WX 600 ; N asciitilde ; B 67 160 534 269 ;
-C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;
-C 162 ; WX 600 ; N cent ; B 103 -49 493 614 ;
-C 163 ; WX 600 ; N sterling ; B 91 -21 518 611 ;
-C 164 ; WX 600 ; N fraction ; B 31 -57 569 665 ;
-C 165 ; WX 600 ; N yen ; B 33 0 567 594 ;
-C 166 ; WX 600 ; N florin ; B 11 -143 532 622 ;
-C 167 ; WX 600 ; N section ; B 120 -78 481 580 ;
-C 168 ; WX 600 ; N currency ; B 80 65 520 499 ;
-C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;
-C 170 ; WX 600 ; N quotedblleft ; B 143 328 457 562 ;
-C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;
-C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;
-C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;
-C 174 ; WX 600 ; N fi ; B 10 0 590 629 ;
-C 175 ; WX 600 ; N fl ; B 10 0 590 629 ;
-C 177 ; WX 600 ; N endash ; B 75 238 525 278 ;
-C 178 ; WX 600 ; N dagger ; B 148 -78 452 580 ;
-C 179 ; WX 600 ; N daggerdbl ; B 148 -78 452 580 ;
-C 180 ; WX 600 ; N periodcentered ; B 229 152 371 276 ;
-C 182 ; WX 600 ; N paragraph ; B 57 -78 504 562 ;
-C 183 ; WX 600 ; N bullet ; B 179 137 421 376 ;
-C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;
-C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;
-C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;
-C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;
-C 188 ; WX 600 ; N ellipsis ; B 44 -15 556 97 ;
-C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;
-C 191 ; WX 600 ; N questiondown ; B 115 -157 464 430 ;
-C 193 ; WX 600 ; N grave ; B 151 497 378 672 ;
-C 194 ; WX 600 ; N acute ; B 242 497 469 672 ;
-C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ;
-C 196 ; WX 600 ; N tilde ; B 109 503 491 606 ;
-C 197 ; WX 600 ; N macron ; B 120 525 480 565 ;
-C 198 ; WX 600 ; N breve ; B 153 501 447 609 ;
-C 199 ; WX 600 ; N dotaccent ; B 264 508 336 580 ;
-C 200 ; WX 600 ; N dieresis ; B 198 508 402 580 ;
-C 202 ; WX 600 ; N ring ; B 228 483 372 627 ;
-C 203 ; WX 600 ; N cedilla ; B 249 -151 358 10 ;
-C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;
-C 206 ; WX 600 ; N ogonek ; B 227 -151 370 0 ;
-C 207 ; WX 600 ; N caron ; B 124 492 476 669 ;
-C 208 ; WX 600 ; N emdash ; B 0 238 600 278 ;
-C 225 ; WX 600 ; N AE ; B 10 0 543 562 ;
-C 227 ; WX 600 ; N ordfeminine ; B 161 259 437 580 ;
-C 232 ; WX 600 ; N Lslash ; B 54 0 547 562 ;
-C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;
-C 234 ; WX 600 ; N OE ; B 14 0 560 562 ;
-C 235 ; WX 600 ; N ordmasculine ; B 162 259 438 580 ;
-C 241 ; WX 600 ; N ae ; B 26 -15 563 441 ;
-C 245 ; WX 600 ; N dotlessi ; B 102 0 498 426 ;
-C 248 ; WX 600 ; N lslash ; B 102 0 498 629 ;
-C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ;
-C 250 ; WX 600 ; N oe ; B 26 -15 552 441 ;
-C 251 ; WX 600 ; N germandbls ; B 55 -15 581 629 ;
-C -1 ; WX 600 ; N scedilla ; B 94 -151 506 441 ;
-C -1 ; WX 600 ; N notegraphic ; B 136 -15 464 572 ;
-C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 775 ;
-C -1 ; WX 600 ; N ll ; B 25 0 560 629 ;
-C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ;
-C -1 ; WX 600 ; N scaron ; B 94 -15 506 669 ;
-C -1 ; WX 600 ; N divide ; B 87 1 513 426 ;
-C -1 ; WX 600 ; N Thorn ; B 86 0 531 562 ;
-C -1 ; WX 600 ; N format ; B 5 -157 42 598 ;
-C -1 ; WX 600 ; N largebullet ; B 268 227 332 290 ;
-C -1 ; WX 600 ; N Eth ; B 30 0 567 562 ;
-C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 716 ;
-C -1 ; WX 600 ; N onesuperior ; B 179 249 421 622 ;
-C -1 ; WX 600 ; N dectab ; B 18 0 582 227 ;
-C -1 ; WX 600 ; N Ydieresis ; B 31 0 569 716 ;
-C -1 ; WX 600 ; N merge ; B 167 -15 433 436 ;
-C -1 ; WX 600 ; N IJ ; B 39 -18 562 562 ;
-C -1 ; WX 600 ; N ccedilla ; B 73 -151 522 441 ;
-C -1 ; WX 600 ; N multiply ; B 87 -1 514 426 ;
-C -1 ; WX 600 ; N degree ; B 123 269 477 622 ;
-C -1 ; WX 600 ; N prescription ; B 34 -15 570 562 ;
-C -1 ; WX 600 ; N indent ; B 70 75 530 341 ;
-C -1 ; WX 600 ; N Otilde ; B 43 -18 557 732 ;
-C -1 ; WX 600 ; N thorn ; B 1 -157 548 629 ;
-C -1 ; WX 600 ; N mu ; B 28 -157 555 426 ;
-C -1 ; WX 600 ; N Yacute ; B 31 0 569 793 ;
-C -1 ; WX 600 ; N threesuperior ; B 162 240 399 622 ;
-C -1 ; WX 600 ; N logicalnot ; B 87 64 513 325 ;
-C -1 ; WX 600 ; N Ugrave ; B 24 -18 576 793 ;
-C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ;
-C -1 ; WX 600 ; N left ; B 70 75 530 341 ;
-C -1 ; WX 600 ; N Ecircumflex ; B 60 0 543 775 ;
-C -1 ; WX 600 ; N edieresis ; B 73 -15 541 580 ;
-C -1 ; WX 600 ; N Ograve ; B 43 -18 557 793 ;
-C -1 ; WX 600 ; N down ; B 167 -15 433 422 ;
-C -1 ; WX 600 ; N Agrave ; B 10 0 590 793 ;
-C -1 ; WX 600 ; N atilde ; B 60 -15 552 606 ;
-C -1 ; WX 600 ; N up ; B 167 0 433 437 ;
-C -1 ; WX 600 ; N eacute ; B 73 -15 541 672 ;
-C -1 ; WX 600 ; N graybox ; B 76 0 525 599 ;
-C -1 ; WX 600 ; N lira ; B 80 -21 518 611 ;
-C -1 ; WX 600 ; N Icircumflex ; B 103 0 497 775 ;
-C -1 ; WX 600 ; N Adieresis ; B 10 0 590 716 ;
-C -1 ; WX 600 ; N yacute ; B -12 -157 552 672 ;
-C -1 ; WX 600 ; N icircumflex ; B 94 0 498 654 ;
-C -1 ; WX 600 ; N adieresis ; B 60 -15 552 580 ;
-C -1 ; WX 600 ; N zcaron ; B 106 0 495 669 ;
-C -1 ; WX 600 ; N Scaron ; B 79 -20 522 805 ;
-C -1 ; WX 600 ; N minus ; B 87 195 513 232 ;
-C -1 ; WX 600 ; N Aring ; B 10 0 590 753 ;
-C -1 ; WX 600 ; N Ucircumflex ; B 24 -18 576 775 ;
-C -1 ; WX 600 ; N plusminus ; B 87 0 513 514 ;
-C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ;
-C -1 ; WX 600 ; N Edieresis ; B 60 0 543 716 ;
-C -1 ; WX 600 ; N brokenbar ; B 282 -80 319 629 ;
-C -1 ; WX 600 ; N Idieresis ; B 103 0 497 716 ;
-C -1 ; WX 600 ; N acircumflex ; B 60 -15 552 654 ;
-C -1 ; WX 600 ; N ydieresis ; B -12 -157 552 580 ;
-C -1 ; WX 600 ; N Oacute ; B 43 -18 557 793 ;
-C -1 ; WX 600 ; N Egrave ; B 60 0 543 793 ;
-C -1 ; WX 600 ; N center ; B 40 14 560 580 ;
-C -1 ; WX 600 ; N threequarters ; B 22 -57 574 665 ;
-C -1 ; WX 600 ; N tab ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N ecircumflex ; B 73 -15 541 654 ;
-C -1 ; WX 600 ; N Eacute ; B 60 0 543 793 ;
-C -1 ; WX 600 ; N trademark ; B 10 263 591 562 ;
-C -1 ; WX 600 ; N square ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;
-C -1 ; WX 600 ; N onequarter ; B 6 -57 594 665 ;
-C -1 ; WX 600 ; N Uacute ; B 24 -18 576 793 ;
-C -1 ; WX 600 ; N Atilde ; B 10 0 590 732 ;
-C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
-C -1 ; WX 600 ; N Igrave ; B 103 0 497 793 ;
-C -1 ; WX 600 ; N Iacute ; B 103 0 497 793 ;
-C -1 ; WX 600 ; N Acircumflex ; B 10 0 590 775 ;
-C -1 ; WX 600 ; N Udieresis ; B 24 -18 576 716 ;
-C -1 ; WX 600 ; N Gcaron ; B 38 -18 568 805 ;
-C -1 ; WX 600 ; N Aacute ; B 10 0 590 793 ;
-C -1 ; WX 600 ; N LL ; B 15 0 585 562 ;
-C -1 ; WX 600 ; N twosuperior ; B 184 249 417 622 ;
-C -1 ; WX 600 ; N Scedilla ; B 79 -151 522 580 ;
-C -1 ; WX 600 ; N arrowboth ; B -28 122 628 476 ;
-C -1 ; WX 600 ; N udieresis ; B 28 -15 555 580 ;
-C -1 ; WX 600 ; N odieresis ; B 62 -15 538 580 ;
-C -1 ; WX 600 ; N aring ; B 60 -15 552 627 ;
-C -1 ; WX 600 ; N ij ; B 44 -157 483 657 ;
-C -1 ; WX 600 ; N arrowdown ; B 123 -15 477 608 ;
-C -1 ; WX 600 ; N igrave ; B 102 0 498 672 ;
-C -1 ; WX 600 ; N aacute ; B 60 -15 552 672 ;
-C -1 ; WX 600 ; N stop ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;
-C -1 ; WX 600 ; N gcaron ; B 52 -157 559 669 ;
-C -1 ; WX 600 ; N iacute ; B 102 0 498 672 ;
-C -1 ; WX 600 ; N Ntilde ; B 14 -13 586 732 ;
-C -1 ; WX 600 ; N idieresis ; B 102 0 498 580 ;
-C -1 ; WX 600 ; N Ccedilla ; B 48 -151 533 580 ;
-C -1 ; WX 600 ; N arrowright ; B -24 122 624 476 ;
-C -1 ; WX 600 ; N ucircumflex ; B 28 -15 555 654 ;
-C -1 ; WX 600 ; N Idot ; B 103 0 497 716 ;
-C -1 ; WX 600 ; N agrave ; B 60 -15 552 672 ;
-C -1 ; WX 600 ; N ntilde ; B 33 0 568 606 ;
-C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
-C -1 ; WX 600 ; N return ; B 19 0 581 562 ;
-C -1 ; WX 600 ; N Zcaron ; B 86 0 514 805 ;
-C -1 ; WX 600 ; N uacute ; B 28 -15 555 672 ;
-C -1 ; WX 600 ; N overscore ; B 0 579 600 629 ;
-C -1 ; WX 600 ; N egrave ; B 73 -15 541 672 ;
-C -1 ; WX 600 ; N ugrave ; B 28 -15 555 672 ;
-C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ;
-C -1 ; WX 600 ; N arrowleft ; B -24 122 624 476 ;
-C -1 ; WX 600 ; N arrowup ; B 123 0 477 623 ;
-EndCharMetrics
-StartComposites 62
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 20 121 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -30 121 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis -30 136 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave -30 121 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring -15 126 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 0 126 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 0 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 30 121 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 0 121 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 0 136 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 0 121 ;
-CC Gcaron 2 ; PCC G 0 0 ; PCC caron 0 136 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 121 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 0 121 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 0 136 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 121 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 0 126 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 0 121 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 0 121 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 0 136 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 0 121 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 0 126 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 30 136 ;
-CC Scedilla 2 ; PCC S 0 0 ; PCC cedilla 0 0 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 30 121 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 0 121 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 0 136 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave -30 121 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 30 121 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 0 136 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 0 136 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 0 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 0 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 0 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 0 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ;
-CC gcaron 2 ; PCC g 0 0 ; PCC caron -30 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -30 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -30 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -30 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ;
-CC scedilla 2 ; PCC s 0 0 ; PCC cedilla 0 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute -10 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -10 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 0 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute -20 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis -10 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 10 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Helvetica-Bold.afm b/config/psfonts/Helvetica-Bold.afm
deleted file mode 100644 (file)
index a1e1b33..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.
-Comment Creation Date: Thu Mar 15 09:43:00 1990
-Comment UniqueID 28357
-Comment VMusage 26878 33770
-FontName Helvetica-Bold
-FullName Helvetica Bold
-FamilyName Helvetica
-Weight Bold
-ItalicAngle 0
-IsFixedPitch false
-FontBBox -170 -228 1003 962
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.007
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 718
-XHeight 532
-Ascender 718
-Descender -207
-StartCharMetrics 228
-C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ;
-C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ;
-C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ;
-C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ;
-C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ;
-C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ;
-C 39 ; WX 278 ; N quoteright ; B 69 445 209 718 ;
-C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ;
-C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ;
-C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ;
-C 43 ; WX 584 ; N plus ; B 40 0 544 506 ;
-C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ;
-C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ;
-C 46 ; WX 278 ; N period ; B 64 0 214 146 ;
-C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ;
-C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ;
-C 49 ; WX 556 ; N one ; B 69 0 378 710 ;
-C 50 ; WX 556 ; N two ; B 26 0 511 710 ;
-C 51 ; WX 556 ; N three ; B 27 -19 516 710 ;
-C 52 ; WX 556 ; N four ; B 27 0 526 710 ;
-C 53 ; WX 556 ; N five ; B 27 -19 516 698 ;
-C 54 ; WX 556 ; N six ; B 31 -19 520 710 ;
-C 55 ; WX 556 ; N seven ; B 25 0 528 698 ;
-C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ;
-C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ;
-C 58 ; WX 333 ; N colon ; B 92 0 242 512 ;
-C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ;
-C 60 ; WX 584 ; N less ; B 38 -8 546 514 ;
-C 61 ; WX 584 ; N equal ; B 40 87 544 419 ;
-C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ;
-C 63 ; WX 611 ; N question ; B 60 0 556 727 ;
-C 64 ; WX 975 ; N at ; B 118 -19 856 737 ;
-C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
-C 66 ; WX 722 ; N B ; B 76 0 669 718 ;
-C 67 ; WX 722 ; N C ; B 44 -19 684 737 ;
-C 68 ; WX 722 ; N D ; B 76 0 685 718 ;
-C 69 ; WX 667 ; N E ; B 76 0 621 718 ;
-C 70 ; WX 611 ; N F ; B 76 0 587 718 ;
-C 71 ; WX 778 ; N G ; B 44 -19 713 737 ;
-C 72 ; WX 722 ; N H ; B 71 0 651 718 ;
-C 73 ; WX 278 ; N I ; B 64 0 214 718 ;
-C 74 ; WX 556 ; N J ; B 22 -18 484 718 ;
-C 75 ; WX 722 ; N K ; B 87 0 722 718 ;
-C 76 ; WX 611 ; N L ; B 76 0 583 718 ;
-C 77 ; WX 833 ; N M ; B 69 0 765 718 ;
-C 78 ; WX 722 ; N N ; B 69 0 654 718 ;
-C 79 ; WX 778 ; N O ; B 44 -19 734 737 ;
-C 80 ; WX 667 ; N P ; B 76 0 627 718 ;
-C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ;
-C 82 ; WX 722 ; N R ; B 76 0 677 718 ;
-C 83 ; WX 667 ; N S ; B 39 -19 629 737 ;
-C 84 ; WX 611 ; N T ; B 14 0 598 718 ;
-C 85 ; WX 722 ; N U ; B 72 -19 651 718 ;
-C 86 ; WX 667 ; N V ; B 19 0 648 718 ;
-C 87 ; WX 944 ; N W ; B 16 0 929 718 ;
-C 88 ; WX 667 ; N X ; B 14 0 653 718 ;
-C 89 ; WX 667 ; N Y ; B 15 0 653 718 ;
-C 90 ; WX 611 ; N Z ; B 25 0 586 718 ;
-C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ;
-C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ;
-C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ;
-C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ;
-C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
-C 96 ; WX 278 ; N quoteleft ; B 69 454 209 727 ;
-C 97 ; WX 556 ; N a ; B 29 -14 527 546 ;
-C 98 ; WX 611 ; N b ; B 61 -14 578 718 ;
-C 99 ; WX 556 ; N c ; B 34 -14 524 546 ;
-C 100 ; WX 611 ; N d ; B 34 -14 551 718 ;
-C 101 ; WX 556 ; N e ; B 23 -14 528 546 ;
-C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ;
-C 103 ; WX 611 ; N g ; B 40 -217 553 546 ;
-C 104 ; WX 611 ; N h ; B 65 0 546 718 ;
-C 105 ; WX 278 ; N i ; B 69 0 209 725 ;
-C 106 ; WX 278 ; N j ; B 3 -214 209 725 ;
-C 107 ; WX 556 ; N k ; B 69 0 562 718 ;
-C 108 ; WX 278 ; N l ; B 69 0 209 718 ;
-C 109 ; WX 889 ; N m ; B 64 0 826 546 ;
-C 110 ; WX 611 ; N n ; B 65 0 546 546 ;
-C 111 ; WX 611 ; N o ; B 34 -14 578 546 ;
-C 112 ; WX 611 ; N p ; B 62 -207 578 546 ;
-C 113 ; WX 611 ; N q ; B 34 -207 552 546 ;
-C 114 ; WX 389 ; N r ; B 64 0 373 546 ;
-C 115 ; WX 556 ; N s ; B 30 -14 519 546 ;
-C 116 ; WX 333 ; N t ; B 10 -6 309 676 ;
-C 117 ; WX 611 ; N u ; B 66 -14 545 532 ;
-C 118 ; WX 556 ; N v ; B 13 0 543 532 ;
-C 119 ; WX 778 ; N w ; B 10 0 769 532 ;
-C 120 ; WX 556 ; N x ; B 15 0 541 532 ;
-C 121 ; WX 556 ; N y ; B 10 -214 539 532 ;
-C 122 ; WX 500 ; N z ; B 20 0 480 532 ;
-C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ;
-C 124 ; WX 280 ; N bar ; B 84 -19 196 737 ;
-C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ;
-C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ;
-C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ;
-C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ;
-C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ;
-C 164 ; WX 167 ; N fraction ; B -170 -19 336 710 ;
-C 165 ; WX 556 ; N yen ; B -9 0 565 698 ;
-C 166 ; WX 556 ; N florin ; B -10 -210 516 737 ;
-C 167 ; WX 556 ; N section ; B 34 -184 522 727 ;
-C 168 ; WX 556 ; N currency ; B -3 76 559 636 ;
-C 169 ; WX 238 ; N quotesingle ; B 70 447 168 718 ;
-C 170 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ;
-C 171 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ;
-C 173 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ;
-C 174 ; WX 611 ; N fi ; B 10 0 542 727 ;
-C 175 ; WX 611 ; N fl ; B 10 0 542 727 ;
-C 177 ; WX 556 ; N endash ; B 0 227 556 333 ;
-C 178 ; WX 556 ; N dagger ; B 36 -171 520 718 ;
-C 179 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ;
-C 180 ; WX 278 ; N periodcentered ; B 58 172 220 334 ;
-C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ;
-C 183 ; WX 350 ; N bullet ; B 10 194 340 524 ;
-C 184 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ;
-C 185 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ;
-C 186 ; WX 500 ; N quotedblright ; B 64 445 436 718 ;
-C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ;
-C 188 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ;
-C 189 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ;
-C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ;
-C 193 ; WX 333 ; N grave ; B -23 604 225 750 ;
-C 194 ; WX 333 ; N acute ; B 108 604 356 750 ;
-C 195 ; WX 333 ; N circumflex ; B -10 604 343 750 ;
-C 196 ; WX 333 ; N tilde ; B -17 610 350 737 ;
-C 197 ; WX 333 ; N macron ; B -6 604 339 678 ;
-C 198 ; WX 333 ; N breve ; B -2 604 335 750 ;
-C 199 ; WX 333 ; N dotaccent ; B 104 614 230 729 ;
-C 200 ; WX 333 ; N dieresis ; B 6 614 327 729 ;
-C 202 ; WX 333 ; N ring ; B 59 568 275 776 ;
-C 203 ; WX 333 ; N cedilla ; B 6 -228 245 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ;
-C 206 ; WX 333 ; N ogonek ; B 71 -228 304 0 ;
-C 207 ; WX 333 ; N caron ; B -10 604 343 750 ;
-C 208 ; WX 1000 ; N emdash ; B 0 227 1000 333 ;
-C 225 ; WX 1000 ; N AE ; B 5 0 954 718 ;
-C 227 ; WX 370 ; N ordfeminine ; B 22 276 347 737 ;
-C 232 ; WX 611 ; N Lslash ; B -20 0 583 718 ;
-C 233 ; WX 778 ; N Oslash ; B 33 -27 744 745 ;
-C 234 ; WX 1000 ; N OE ; B 37 -19 961 737 ;
-C 235 ; WX 365 ; N ordmasculine ; B 6 276 360 737 ;
-C 241 ; WX 889 ; N ae ; B 29 -14 858 546 ;
-C 245 ; WX 278 ; N dotlessi ; B 69 0 209 532 ;
-C 248 ; WX 278 ; N lslash ; B -18 0 296 718 ;
-C 249 ; WX 611 ; N oslash ; B 22 -29 589 560 ;
-C 250 ; WX 944 ; N oe ; B 34 -14 912 546 ;
-C 251 ; WX 611 ; N germandbls ; B 69 -14 579 731 ;
-C -1 ; WX 611 ; N Zcaron ; B 25 0 586 936 ;
-C -1 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ;
-C -1 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ;
-C -1 ; WX 556 ; N atilde ; B 29 -14 527 737 ;
-C -1 ; WX 278 ; N icircumflex ; B -37 0 316 750 ;
-C -1 ; WX 333 ; N threesuperior ; B 8 271 326 710 ;
-C -1 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ;
-C -1 ; WX 611 ; N thorn ; B 62 -208 578 718 ;
-C -1 ; WX 556 ; N egrave ; B 23 -14 528 750 ;
-C -1 ; WX 333 ; N twosuperior ; B 9 283 324 710 ;
-C -1 ; WX 556 ; N eacute ; B 23 -14 528 750 ;
-C -1 ; WX 611 ; N otilde ; B 34 -14 578 737 ;
-C -1 ; WX 722 ; N Aacute ; B 20 0 702 936 ;
-C -1 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ;
-C -1 ; WX 556 ; N yacute ; B 10 -214 539 750 ;
-C -1 ; WX 611 ; N udieresis ; B 66 -14 545 729 ;
-C -1 ; WX 834 ; N threequarters ; B 16 -19 799 710 ;
-C -1 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ;
-C -1 ; WX 722 ; N Eth ; B -5 0 685 718 ;
-C -1 ; WX 556 ; N edieresis ; B 23 -14 528 729 ;
-C -1 ; WX 611 ; N ugrave ; B 66 -14 545 750 ;
-C -1 ; WX 1000 ; N trademark ; B 44 306 956 718 ;
-C -1 ; WX 611 ; N ograve ; B 34 -14 578 750 ;
-C -1 ; WX 556 ; N scaron ; B 30 -14 519 750 ;
-C -1 ; WX 278 ; N Idieresis ; B -21 0 300 915 ;
-C -1 ; WX 611 ; N uacute ; B 66 -14 545 750 ;
-C -1 ; WX 556 ; N agrave ; B 29 -14 527 750 ;
-C -1 ; WX 611 ; N ntilde ; B 65 0 546 737 ;
-C -1 ; WX 556 ; N aring ; B 29 -14 527 776 ;
-C -1 ; WX 500 ; N zcaron ; B 20 0 480 750 ;
-C -1 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ;
-C -1 ; WX 722 ; N Ntilde ; B 69 0 654 923 ;
-C -1 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ;
-C -1 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ;
-C -1 ; WX 278 ; N Iacute ; B 64 0 329 936 ;
-C -1 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ;
-C -1 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ;
-C -1 ; WX 667 ; N Scaron ; B 39 -19 629 936 ;
-C -1 ; WX 667 ; N Edieresis ; B 76 0 621 915 ;
-C -1 ; WX 278 ; N Igrave ; B -50 0 214 936 ;
-C -1 ; WX 556 ; N adieresis ; B 29 -14 527 729 ;
-C -1 ; WX 778 ; N Ograve ; B 44 -19 734 936 ;
-C -1 ; WX 667 ; N Egrave ; B 76 0 621 936 ;
-C -1 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ;
-C -1 ; WX 737 ; N registered ; B -11 -19 748 737 ;
-C -1 ; WX 778 ; N Otilde ; B 44 -19 734 923 ;
-C -1 ; WX 834 ; N onequarter ; B 26 -19 766 710 ;
-C -1 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ;
-C -1 ; WX 667 ; N Thorn ; B 76 0 627 718 ;
-C -1 ; WX 584 ; N divide ; B 40 -42 544 548 ;
-C -1 ; WX 722 ; N Atilde ; B 20 0 702 923 ;
-C -1 ; WX 722 ; N Uacute ; B 72 -19 651 936 ;
-C -1 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ;
-C -1 ; WX 584 ; N logicalnot ; B 40 108 544 419 ;
-C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ;
-C -1 ; WX 278 ; N idieresis ; B -21 0 300 729 ;
-C -1 ; WX 278 ; N iacute ; B 69 0 329 750 ;
-C -1 ; WX 556 ; N aacute ; B 29 -14 527 750 ;
-C -1 ; WX 584 ; N plusminus ; B 40 0 544 506 ;
-C -1 ; WX 584 ; N multiply ; B 40 1 545 505 ;
-C -1 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ;
-C -1 ; WX 584 ; N minus ; B 40 197 544 309 ;
-C -1 ; WX 333 ; N onesuperior ; B 26 283 237 710 ;
-C -1 ; WX 667 ; N Eacute ; B 76 0 621 936 ;
-C -1 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ;
-C -1 ; WX 737 ; N copyright ; B -11 -19 749 737 ;
-C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
-C -1 ; WX 611 ; N odieresis ; B 34 -14 578 729 ;
-C -1 ; WX 611 ; N oacute ; B 34 -14 578 750 ;
-C -1 ; WX 400 ; N degree ; B 57 426 343 712 ;
-C -1 ; WX 278 ; N igrave ; B -50 0 209 750 ;
-C -1 ; WX 611 ; N mu ; B 66 -207 545 532 ;
-C -1 ; WX 778 ; N Oacute ; B 44 -19 734 936 ;
-C -1 ; WX 611 ; N eth ; B 34 -14 578 737 ;
-C -1 ; WX 722 ; N Adieresis ; B 20 0 702 915 ;
-C -1 ; WX 667 ; N Yacute ; B 15 0 653 936 ;
-C -1 ; WX 280 ; N brokenbar ; B 84 -19 196 737 ;
-C -1 ; WX 834 ; N onehalf ; B 26 -19 794 710 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 209
-
-KPX A y -30
-KPX A w -30
-KPX A v -40
-KPX A u -30
-KPX A Y -110
-KPX A W -60
-KPX A V -80
-KPX A U -50
-KPX A T -90
-KPX A Q -40
-KPX A O -40
-KPX A G -50
-KPX A C -40
-
-KPX B U -10
-KPX B A -30
-
-KPX D period -30
-KPX D comma -30
-KPX D Y -70
-KPX D W -40
-KPX D V -40
-KPX D A -40
-
-KPX F period -100
-KPX F comma -100
-KPX F a -20
-KPX F A -80
-
-KPX J u -20
-KPX J period -20
-KPX J comma -20
-KPX J A -20
-
-KPX K y -40
-KPX K u -30
-KPX K o -35
-KPX K e -15
-KPX K O -30
-
-KPX L y -30
-KPX L quoteright -140
-KPX L quotedblright -140
-KPX L Y -120
-KPX L W -80
-KPX L V -110
-KPX L T -90
-
-KPX O period -40
-KPX O comma -40
-KPX O Y -70
-KPX O X -50
-KPX O W -50
-KPX O V -50
-KPX O T -40
-KPX O A -50
-
-KPX P period -120
-KPX P o -40
-KPX P e -30
-KPX P comma -120
-KPX P a -30
-KPX P A -100
-
-KPX Q period 20
-KPX Q comma 20
-KPX Q U -10
-
-KPX R Y -50
-KPX R W -40
-KPX R V -50
-KPX R U -20
-KPX R T -20
-KPX R O -20
-
-KPX T y -60
-KPX T w -60
-KPX T u -90
-KPX T semicolon -40
-KPX T r -80
-KPX T period -80
-KPX T o -80
-KPX T hyphen -120
-KPX T e -60
-KPX T comma -80
-KPX T colon -40
-KPX T a -80
-KPX T O -40
-KPX T A -90
-
-KPX U period -30
-KPX U comma -30
-KPX U A -50
-
-KPX V u -60
-KPX V semicolon -40
-KPX V period -120
-KPX V o -90
-KPX V hyphen -80
-KPX V e -50
-KPX V comma -120
-KPX V colon -40
-KPX V a -60
-KPX V O -50
-KPX V G -50
-KPX V A -80
-
-KPX W y -20
-KPX W u -45
-KPX W semicolon -10
-KPX W period -80
-KPX W o -60
-KPX W hyphen -40
-KPX W e -35
-KPX W comma -80
-KPX W colon -10
-KPX W a -40
-KPX W O -20
-KPX W A -60
-
-KPX Y u -100
-KPX Y semicolon -50
-KPX Y period -100
-KPX Y o -100
-KPX Y e -80
-KPX Y comma -100
-KPX Y colon -50
-KPX Y a -90
-KPX Y O -70
-KPX Y A -110
-
-KPX a y -20
-KPX a w -15
-KPX a v -15
-KPX a g -10
-
-KPX b y -20
-KPX b v -20
-KPX b u -20
-KPX b l -10
-
-KPX c y -10
-KPX c l -20
-KPX c k -20
-KPX c h -10
-
-KPX colon space -40
-
-KPX comma space -40
-KPX comma quoteright -120
-KPX comma quotedblright -120
-
-KPX d y -15
-KPX d w -15
-KPX d v -15
-KPX d d -10
-
-KPX e y -15
-KPX e x -15
-KPX e w -15
-KPX e v -15
-KPX e period 20
-KPX e comma 10
-
-KPX f quoteright 30
-KPX f quotedblright 30
-KPX f period -10
-KPX f o -20
-KPX f e -10
-KPX f comma -10
-
-KPX g g -10
-KPX g e 10
-
-KPX h y -20
-
-KPX k o -15
-
-KPX l y -15
-KPX l w -15
-
-KPX m y -30
-KPX m u -20
-
-KPX n y -20
-KPX n v -40
-KPX n u -10
-
-KPX o y -20
-KPX o x -30
-KPX o w -15
-KPX o v -20
-
-KPX p y -15
-
-KPX period space -40
-KPX period quoteright -120
-KPX period quotedblright -120
-
-KPX quotedblright space -80
-
-KPX quoteleft quoteleft -46
-
-KPX quoteright v -20
-KPX quoteright space -80
-KPX quoteright s -60
-KPX quoteright r -40
-KPX quoteright quoteright -46
-KPX quoteright l -20
-KPX quoteright d -80
-
-KPX r y 10
-KPX r v 10
-KPX r t 20
-KPX r s -15
-KPX r q -20
-KPX r period -60
-KPX r o -20
-KPX r hyphen -20
-KPX r g -15
-KPX r d -20
-KPX r comma -60
-KPX r c -20
-
-KPX s w -15
-
-KPX semicolon space -40
-
-KPX space quoteleft -60
-KPX space quotedblleft -80
-KPX space Y -120
-KPX space W -80
-KPX space V -80
-KPX space T -100
-
-KPX v period -80
-KPX v o -30
-KPX v comma -80
-KPX v a -20
-
-KPX w period -40
-KPX w o -20
-KPX w comma -40
-
-KPX x e -10
-
-KPX y period -80
-KPX y o -25
-KPX y e -10
-KPX y comma -80
-KPX y a -30
-
-KPX z e 10
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 195 186 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 195 186 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 195 186 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 195 186 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 195 186 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 195 186 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 215 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 167 186 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 167 186 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 167 186 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 167 186 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute -27 186 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -27 186 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -27 186 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave -27 186 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 195 186 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 186 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 186 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 186 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 186 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 186 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 167 186 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 186 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 195 186 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 195 186 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 195 186 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 167 186 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 167 186 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 186 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 112 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 132 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 139 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 139 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 139 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 139 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 112 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 139 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 139 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 112 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 112 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Helvetica-BoldOblique.afm b/config/psfonts/Helvetica-BoldOblique.afm
deleted file mode 100644 (file)
index b6cff41..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.
-Comment Creation Date: Thu Mar 15 10:44:33 1990
-Comment UniqueID 28371
-Comment VMusage 7614 43068
-FontName Helvetica-BoldOblique
-FullName Helvetica Bold Oblique
-FamilyName Helvetica
-Weight Bold
-ItalicAngle -12
-IsFixedPitch false
-FontBBox -174 -228 1114 962
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.007
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 718
-XHeight 532
-Ascender 718
-Descender -207
-StartCharMetrics 228
-C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 333 ; N exclam ; B 94 0 397 718 ;
-C 34 ; WX 474 ; N quotedbl ; B 193 447 529 718 ;
-C 35 ; WX 556 ; N numbersign ; B 60 0 644 698 ;
-C 36 ; WX 556 ; N dollar ; B 67 -115 622 775 ;
-C 37 ; WX 889 ; N percent ; B 136 -19 901 710 ;
-C 38 ; WX 722 ; N ampersand ; B 89 -19 732 718 ;
-C 39 ; WX 278 ; N quoteright ; B 167 445 362 718 ;
-C 40 ; WX 333 ; N parenleft ; B 76 -208 470 734 ;
-C 41 ; WX 333 ; N parenright ; B -25 -208 369 734 ;
-C 42 ; WX 389 ; N asterisk ; B 146 387 481 718 ;
-C 43 ; WX 584 ; N plus ; B 82 0 610 506 ;
-C 44 ; WX 278 ; N comma ; B 28 -168 245 146 ;
-C 45 ; WX 333 ; N hyphen ; B 73 215 379 345 ;
-C 46 ; WX 278 ; N period ; B 64 0 245 146 ;
-C 47 ; WX 278 ; N slash ; B -37 -19 468 737 ;
-C 48 ; WX 556 ; N zero ; B 86 -19 617 710 ;
-C 49 ; WX 556 ; N one ; B 173 0 529 710 ;
-C 50 ; WX 556 ; N two ; B 26 0 619 710 ;
-C 51 ; WX 556 ; N three ; B 65 -19 608 710 ;
-C 52 ; WX 556 ; N four ; B 60 0 598 710 ;
-C 53 ; WX 556 ; N five ; B 64 -19 636 698 ;
-C 54 ; WX 556 ; N six ; B 85 -19 619 710 ;
-C 55 ; WX 556 ; N seven ; B 125 0 676 698 ;
-C 56 ; WX 556 ; N eight ; B 69 -19 616 710 ;
-C 57 ; WX 556 ; N nine ; B 78 -19 615 710 ;
-C 58 ; WX 333 ; N colon ; B 92 0 351 512 ;
-C 59 ; WX 333 ; N semicolon ; B 56 -168 351 512 ;
-C 60 ; WX 584 ; N less ; B 82 -8 655 514 ;
-C 61 ; WX 584 ; N equal ; B 58 87 633 419 ;
-C 62 ; WX 584 ; N greater ; B 36 -8 609 514 ;
-C 63 ; WX 611 ; N question ; B 165 0 671 727 ;
-C 64 ; WX 975 ; N at ; B 186 -19 954 737 ;
-C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
-C 66 ; WX 722 ; N B ; B 76 0 764 718 ;
-C 67 ; WX 722 ; N C ; B 107 -19 789 737 ;
-C 68 ; WX 722 ; N D ; B 76 0 777 718 ;
-C 69 ; WX 667 ; N E ; B 76 0 757 718 ;
-C 70 ; WX 611 ; N F ; B 76 0 740 718 ;
-C 71 ; WX 778 ; N G ; B 108 -19 817 737 ;
-C 72 ; WX 722 ; N H ; B 71 0 804 718 ;
-C 73 ; WX 278 ; N I ; B 64 0 367 718 ;
-C 74 ; WX 556 ; N J ; B 60 -18 637 718 ;
-C 75 ; WX 722 ; N K ; B 87 0 858 718 ;
-C 76 ; WX 611 ; N L ; B 76 0 611 718 ;
-C 77 ; WX 833 ; N M ; B 69 0 918 718 ;
-C 78 ; WX 722 ; N N ; B 69 0 807 718 ;
-C 79 ; WX 778 ; N O ; B 107 -19 823 737 ;
-C 80 ; WX 667 ; N P ; B 76 0 738 718 ;
-C 81 ; WX 778 ; N Q ; B 107 -52 823 737 ;
-C 82 ; WX 722 ; N R ; B 76 0 778 718 ;
-C 83 ; WX 667 ; N S ; B 81 -19 718 737 ;
-C 84 ; WX 611 ; N T ; B 140 0 751 718 ;
-C 85 ; WX 722 ; N U ; B 116 -19 804 718 ;
-C 86 ; WX 667 ; N V ; B 172 0 801 718 ;
-C 87 ; WX 944 ; N W ; B 169 0 1082 718 ;
-C 88 ; WX 667 ; N X ; B 14 0 791 718 ;
-C 89 ; WX 667 ; N Y ; B 168 0 806 718 ;
-C 90 ; WX 611 ; N Z ; B 25 0 737 718 ;
-C 91 ; WX 333 ; N bracketleft ; B 21 -196 462 722 ;
-C 92 ; WX 278 ; N backslash ; B 124 -19 307 737 ;
-C 93 ; WX 333 ; N bracketright ; B -18 -196 423 722 ;
-C 94 ; WX 584 ; N asciicircum ; B 131 323 591 698 ;
-C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ;
-C 96 ; WX 278 ; N quoteleft ; B 165 454 361 727 ;
-C 97 ; WX 556 ; N a ; B 55 -14 583 546 ;
-C 98 ; WX 611 ; N b ; B 61 -14 645 718 ;
-C 99 ; WX 556 ; N c ; B 79 -14 599 546 ;
-C 100 ; WX 611 ; N d ; B 82 -14 704 718 ;
-C 101 ; WX 556 ; N e ; B 70 -14 593 546 ;
-C 102 ; WX 333 ; N f ; B 87 0 469 727 ; L i fi ; L l fl ;
-C 103 ; WX 611 ; N g ; B 38 -217 666 546 ;
-C 104 ; WX 611 ; N h ; B 65 0 629 718 ;
-C 105 ; WX 278 ; N i ; B 69 0 363 725 ;
-C 106 ; WX 278 ; N j ; B -42 -214 363 725 ;
-C 107 ; WX 556 ; N k ; B 69 0 670 718 ;
-C 108 ; WX 278 ; N l ; B 69 0 362 718 ;
-C 109 ; WX 889 ; N m ; B 64 0 909 546 ;
-C 110 ; WX 611 ; N n ; B 65 0 629 546 ;
-C 111 ; WX 611 ; N o ; B 82 -14 643 546 ;
-C 112 ; WX 611 ; N p ; B 18 -207 645 546 ;
-C 113 ; WX 611 ; N q ; B 80 -207 665 546 ;
-C 114 ; WX 389 ; N r ; B 64 0 489 546 ;
-C 115 ; WX 556 ; N s ; B 63 -14 584 546 ;
-C 116 ; WX 333 ; N t ; B 100 -6 422 676 ;
-C 117 ; WX 611 ; N u ; B 98 -14 658 532 ;
-C 118 ; WX 556 ; N v ; B 126 0 656 532 ;
-C 119 ; WX 778 ; N w ; B 123 0 882 532 ;
-C 120 ; WX 556 ; N x ; B 15 0 648 532 ;
-C 121 ; WX 556 ; N y ; B 42 -214 652 532 ;
-C 122 ; WX 500 ; N z ; B 20 0 583 532 ;
-C 123 ; WX 389 ; N braceleft ; B 94 -196 518 722 ;
-C 124 ; WX 280 ; N bar ; B 80 -19 353 737 ;
-C 125 ; WX 389 ; N braceright ; B -18 -196 407 722 ;
-C 126 ; WX 584 ; N asciitilde ; B 115 163 577 343 ;
-C 161 ; WX 333 ; N exclamdown ; B 50 -186 353 532 ;
-C 162 ; WX 556 ; N cent ; B 79 -118 599 628 ;
-C 163 ; WX 556 ; N sterling ; B 50 -16 635 718 ;
-C 164 ; WX 167 ; N fraction ; B -174 -19 487 710 ;
-C 165 ; WX 556 ; N yen ; B 60 0 713 698 ;
-C 166 ; WX 556 ; N florin ; B -50 -210 669 737 ;
-C 167 ; WX 556 ; N section ; B 61 -184 598 727 ;
-C 168 ; WX 556 ; N currency ; B 27 76 680 636 ;
-C 169 ; WX 238 ; N quotesingle ; B 165 447 321 718 ;
-C 170 ; WX 500 ; N quotedblleft ; B 160 454 588 727 ;
-C 171 ; WX 556 ; N guillemotleft ; B 135 76 571 484 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 130 76 353 484 ;
-C 173 ; WX 333 ; N guilsinglright ; B 99 76 322 484 ;
-C 174 ; WX 611 ; N fi ; B 87 0 696 727 ;
-C 175 ; WX 611 ; N fl ; B 87 0 695 727 ;
-C 177 ; WX 556 ; N endash ; B 48 227 627 333 ;
-C 178 ; WX 556 ; N dagger ; B 118 -171 626 718 ;
-C 179 ; WX 556 ; N daggerdbl ; B 46 -171 628 718 ;
-C 180 ; WX 278 ; N periodcentered ; B 110 172 276 334 ;
-C 182 ; WX 556 ; N paragraph ; B 98 -191 688 700 ;
-C 183 ; WX 350 ; N bullet ; B 83 194 420 524 ;
-C 184 ; WX 278 ; N quotesinglbase ; B 41 -146 236 127 ;
-C 185 ; WX 500 ; N quotedblbase ; B 36 -146 463 127 ;
-C 186 ; WX 500 ; N quotedblright ; B 162 445 589 718 ;
-C 187 ; WX 556 ; N guillemotright ; B 104 76 540 484 ;
-C 188 ; WX 1000 ; N ellipsis ; B 92 0 939 146 ;
-C 189 ; WX 1000 ; N perthousand ; B 76 -19 1038 710 ;
-C 191 ; WX 611 ; N questiondown ; B 53 -195 559 532 ;
-C 193 ; WX 333 ; N grave ; B 136 604 353 750 ;
-C 194 ; WX 333 ; N acute ; B 236 604 515 750 ;
-C 195 ; WX 333 ; N circumflex ; B 118 604 471 750 ;
-C 196 ; WX 333 ; N tilde ; B 113 610 507 737 ;
-C 197 ; WX 333 ; N macron ; B 122 604 483 678 ;
-C 198 ; WX 333 ; N breve ; B 156 604 494 750 ;
-C 199 ; WX 333 ; N dotaccent ; B 235 614 385 729 ;
-C 200 ; WX 333 ; N dieresis ; B 137 614 482 729 ;
-C 202 ; WX 333 ; N ring ; B 200 568 420 776 ;
-C 203 ; WX 333 ; N cedilla ; B -37 -228 220 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B 137 604 645 750 ;
-C 206 ; WX 333 ; N ogonek ; B 41 -228 264 0 ;
-C 207 ; WX 333 ; N caron ; B 149 604 502 750 ;
-C 208 ; WX 1000 ; N emdash ; B 48 227 1071 333 ;
-C 225 ; WX 1000 ; N AE ; B 5 0 1100 718 ;
-C 227 ; WX 370 ; N ordfeminine ; B 92 276 465 737 ;
-C 232 ; WX 611 ; N Lslash ; B 34 0 611 718 ;
-C 233 ; WX 778 ; N Oslash ; B 35 -27 894 745 ;
-C 234 ; WX 1000 ; N OE ; B 99 -19 1114 737 ;
-C 235 ; WX 365 ; N ordmasculine ; B 92 276 485 737 ;
-C 241 ; WX 889 ; N ae ; B 56 -14 923 546 ;
-C 245 ; WX 278 ; N dotlessi ; B 69 0 322 532 ;
-C 248 ; WX 278 ; N lslash ; B 40 0 407 718 ;
-C 249 ; WX 611 ; N oslash ; B 22 -29 701 560 ;
-C 250 ; WX 944 ; N oe ; B 82 -14 977 546 ;
-C 251 ; WX 611 ; N germandbls ; B 69 -14 657 731 ;
-C -1 ; WX 611 ; N Zcaron ; B 25 0 737 936 ;
-C -1 ; WX 556 ; N ccedilla ; B 79 -228 599 546 ;
-C -1 ; WX 556 ; N ydieresis ; B 42 -214 652 729 ;
-C -1 ; WX 556 ; N atilde ; B 55 -14 619 737 ;
-C -1 ; WX 278 ; N icircumflex ; B 69 0 444 750 ;
-C -1 ; WX 333 ; N threesuperior ; B 91 271 441 710 ;
-C -1 ; WX 556 ; N ecircumflex ; B 70 -14 593 750 ;
-C -1 ; WX 611 ; N thorn ; B 18 -208 645 718 ;
-C -1 ; WX 556 ; N egrave ; B 70 -14 593 750 ;
-C -1 ; WX 333 ; N twosuperior ; B 69 283 449 710 ;
-C -1 ; WX 556 ; N eacute ; B 70 -14 627 750 ;
-C -1 ; WX 611 ; N otilde ; B 82 -14 646 737 ;
-C -1 ; WX 722 ; N Aacute ; B 20 0 750 936 ;
-C -1 ; WX 611 ; N ocircumflex ; B 82 -14 643 750 ;
-C -1 ; WX 556 ; N yacute ; B 42 -214 652 750 ;
-C -1 ; WX 611 ; N udieresis ; B 98 -14 658 729 ;
-C -1 ; WX 834 ; N threequarters ; B 99 -19 839 710 ;
-C -1 ; WX 556 ; N acircumflex ; B 55 -14 583 750 ;
-C -1 ; WX 722 ; N Eth ; B 62 0 777 718 ;
-C -1 ; WX 556 ; N edieresis ; B 70 -14 594 729 ;
-C -1 ; WX 611 ; N ugrave ; B 98 -14 658 750 ;
-C -1 ; WX 1000 ; N trademark ; B 179 306 1109 718 ;
-C -1 ; WX 611 ; N ograve ; B 82 -14 643 750 ;
-C -1 ; WX 556 ; N scaron ; B 63 -14 614 750 ;
-C -1 ; WX 278 ; N Idieresis ; B 64 0 494 915 ;
-C -1 ; WX 611 ; N uacute ; B 98 -14 658 750 ;
-C -1 ; WX 556 ; N agrave ; B 55 -14 583 750 ;
-C -1 ; WX 611 ; N ntilde ; B 65 0 646 737 ;
-C -1 ; WX 556 ; N aring ; B 55 -14 583 776 ;
-C -1 ; WX 500 ; N zcaron ; B 20 0 586 750 ;
-C -1 ; WX 278 ; N Icircumflex ; B 64 0 484 936 ;
-C -1 ; WX 722 ; N Ntilde ; B 69 0 807 923 ;
-C -1 ; WX 611 ; N ucircumflex ; B 98 -14 658 750 ;
-C -1 ; WX 667 ; N Ecircumflex ; B 76 0 757 936 ;
-C -1 ; WX 278 ; N Iacute ; B 64 0 528 936 ;
-C -1 ; WX 722 ; N Ccedilla ; B 107 -228 789 737 ;
-C -1 ; WX 778 ; N Odieresis ; B 107 -19 823 915 ;
-C -1 ; WX 667 ; N Scaron ; B 81 -19 718 936 ;
-C -1 ; WX 667 ; N Edieresis ; B 76 0 757 915 ;
-C -1 ; WX 278 ; N Igrave ; B 64 0 367 936 ;
-C -1 ; WX 556 ; N adieresis ; B 55 -14 594 729 ;
-C -1 ; WX 778 ; N Ograve ; B 107 -19 823 936 ;
-C -1 ; WX 667 ; N Egrave ; B 76 0 757 936 ;
-C -1 ; WX 667 ; N Ydieresis ; B 168 0 806 915 ;
-C -1 ; WX 737 ; N registered ; B 55 -19 834 737 ;
-C -1 ; WX 778 ; N Otilde ; B 107 -19 823 923 ;
-C -1 ; WX 834 ; N onequarter ; B 132 -19 806 710 ;
-C -1 ; WX 722 ; N Ugrave ; B 116 -19 804 936 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 116 -19 804 936 ;
-C -1 ; WX 667 ; N Thorn ; B 76 0 716 718 ;
-C -1 ; WX 584 ; N divide ; B 82 -42 610 548 ;
-C -1 ; WX 722 ; N Atilde ; B 20 0 741 923 ;
-C -1 ; WX 722 ; N Uacute ; B 116 -19 804 936 ;
-C -1 ; WX 778 ; N Ocircumflex ; B 107 -19 823 936 ;
-C -1 ; WX 584 ; N logicalnot ; B 105 108 633 419 ;
-C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ;
-C -1 ; WX 278 ; N idieresis ; B 69 0 455 729 ;
-C -1 ; WX 278 ; N iacute ; B 69 0 488 750 ;
-C -1 ; WX 556 ; N aacute ; B 55 -14 627 750 ;
-C -1 ; WX 584 ; N plusminus ; B 40 0 625 506 ;
-C -1 ; WX 584 ; N multiply ; B 57 1 635 505 ;
-C -1 ; WX 722 ; N Udieresis ; B 116 -19 804 915 ;
-C -1 ; WX 584 ; N minus ; B 82 197 610 309 ;
-C -1 ; WX 333 ; N onesuperior ; B 148 283 388 710 ;
-C -1 ; WX 667 ; N Eacute ; B 76 0 757 936 ;
-C -1 ; WX 722 ; N Acircumflex ; B 20 0 706 936 ;
-C -1 ; WX 737 ; N copyright ; B 56 -19 835 737 ;
-C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
-C -1 ; WX 611 ; N odieresis ; B 82 -14 643 729 ;
-C -1 ; WX 611 ; N oacute ; B 82 -14 654 750 ;
-C -1 ; WX 400 ; N degree ; B 175 426 467 712 ;
-C -1 ; WX 278 ; N igrave ; B 69 0 326 750 ;
-C -1 ; WX 611 ; N mu ; B 22 -207 658 532 ;
-C -1 ; WX 778 ; N Oacute ; B 107 -19 823 936 ;
-C -1 ; WX 611 ; N eth ; B 82 -14 670 737 ;
-C -1 ; WX 722 ; N Adieresis ; B 20 0 716 915 ;
-C -1 ; WX 667 ; N Yacute ; B 168 0 806 936 ;
-C -1 ; WX 280 ; N brokenbar ; B 80 -19 353 737 ;
-C -1 ; WX 834 ; N onehalf ; B 132 -19 858 710 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 209
-
-KPX A y -30
-KPX A w -30
-KPX A v -40
-KPX A u -30
-KPX A Y -110
-KPX A W -60
-KPX A V -80
-KPX A U -50
-KPX A T -90
-KPX A Q -40
-KPX A O -40
-KPX A G -50
-KPX A C -40
-
-KPX B U -10
-KPX B A -30
-
-KPX D period -30
-KPX D comma -30
-KPX D Y -70
-KPX D W -40
-KPX D V -40
-KPX D A -40
-
-KPX F period -100
-KPX F comma -100
-KPX F a -20
-KPX F A -80
-
-KPX J u -20
-KPX J period -20
-KPX J comma -20
-KPX J A -20
-
-KPX K y -40
-KPX K u -30
-KPX K o -35
-KPX K e -15
-KPX K O -30
-
-KPX L y -30
-KPX L quoteright -140
-KPX L quotedblright -140
-KPX L Y -120
-KPX L W -80
-KPX L V -110
-KPX L T -90
-
-KPX O period -40
-KPX O comma -40
-KPX O Y -70
-KPX O X -50
-KPX O W -50
-KPX O V -50
-KPX O T -40
-KPX O A -50
-
-KPX P period -120
-KPX P o -40
-KPX P e -30
-KPX P comma -120
-KPX P a -30
-KPX P A -100
-
-KPX Q period 20
-KPX Q comma 20
-KPX Q U -10
-
-KPX R Y -50
-KPX R W -40
-KPX R V -50
-KPX R U -20
-KPX R T -20
-KPX R O -20
-
-KPX T y -60
-KPX T w -60
-KPX T u -90
-KPX T semicolon -40
-KPX T r -80
-KPX T period -80
-KPX T o -80
-KPX T hyphen -120
-KPX T e -60
-KPX T comma -80
-KPX T colon -40
-KPX T a -80
-KPX T O -40
-KPX T A -90
-
-KPX U period -30
-KPX U comma -30
-KPX U A -50
-
-KPX V u -60
-KPX V semicolon -40
-KPX V period -120
-KPX V o -90
-KPX V hyphen -80
-KPX V e -50
-KPX V comma -120
-KPX V colon -40
-KPX V a -60
-KPX V O -50
-KPX V G -50
-KPX V A -80
-
-KPX W y -20
-KPX W u -45
-KPX W semicolon -10
-KPX W period -80
-KPX W o -60
-KPX W hyphen -40
-KPX W e -35
-KPX W comma -80
-KPX W colon -10
-KPX W a -40
-KPX W O -20
-KPX W A -60
-
-KPX Y u -100
-KPX Y semicolon -50
-KPX Y period -100
-KPX Y o -100
-KPX Y e -80
-KPX Y comma -100
-KPX Y colon -50
-KPX Y a -90
-KPX Y O -70
-KPX Y A -110
-
-KPX a y -20
-KPX a w -15
-KPX a v -15
-KPX a g -10
-
-KPX b y -20
-KPX b v -20
-KPX b u -20
-KPX b l -10
-
-KPX c y -10
-KPX c l -20
-KPX c k -20
-KPX c h -10
-
-KPX colon space -40
-
-KPX comma space -40
-KPX comma quoteright -120
-KPX comma quotedblright -120
-
-KPX d y -15
-KPX d w -15
-KPX d v -15
-KPX d d -10
-
-KPX e y -15
-KPX e x -15
-KPX e w -15
-KPX e v -15
-KPX e period 20
-KPX e comma 10
-
-KPX f quoteright 30
-KPX f quotedblright 30
-KPX f period -10
-KPX f o -20
-KPX f e -10
-KPX f comma -10
-
-KPX g g -10
-KPX g e 10
-
-KPX h y -20
-
-KPX k o -15
-
-KPX l y -15
-KPX l w -15
-
-KPX m y -30
-KPX m u -20
-
-KPX n y -20
-KPX n v -40
-KPX n u -10
-
-KPX o y -20
-KPX o x -30
-KPX o w -15
-KPX o v -20
-
-KPX p y -15
-
-KPX period space -40
-KPX period quoteright -120
-KPX period quotedblright -120
-
-KPX quotedblright space -80
-
-KPX quoteleft quoteleft -46
-
-KPX quoteright v -20
-KPX quoteright space -80
-KPX quoteright s -60
-KPX quoteright r -40
-KPX quoteright quoteright -46
-KPX quoteright l -20
-KPX quoteright d -80
-
-KPX r y 10
-KPX r v 10
-KPX r t 20
-KPX r s -15
-KPX r q -20
-KPX r period -60
-KPX r o -20
-KPX r hyphen -20
-KPX r g -15
-KPX r d -20
-KPX r comma -60
-KPX r c -20
-
-KPX s w -15
-
-KPX semicolon space -40
-
-KPX space quoteleft -60
-KPX space quotedblleft -80
-KPX space Y -120
-KPX space W -80
-KPX space V -80
-KPX space T -100
-
-KPX v period -80
-KPX v o -30
-KPX v comma -80
-KPX v a -20
-
-KPX w period -40
-KPX w o -20
-KPX w comma -40
-
-KPX x e -10
-
-KPX y period -80
-KPX y o -25
-KPX y e -10
-KPX y comma -80
-KPX y a -30
-
-KPX z e 10
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 235 186 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 235 186 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 235 186 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 235 186 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 235 186 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 235 186 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 215 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 207 186 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 207 186 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 207 186 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 207 186 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 13 186 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 13 186 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 13 186 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 13 186 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 235 186 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 263 186 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 263 186 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 263 186 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 263 186 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 263 186 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 207 186 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 235 186 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 235 186 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 235 186 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 235 186 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 207 186 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 207 186 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 179 186 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 112 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 132 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 139 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 139 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 139 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 139 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 112 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 139 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 139 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 112 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 112 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Helvetica-Oblique.afm b/config/psfonts/Helvetica-Oblique.afm
deleted file mode 100644 (file)
index 3d69eb7..0000000
+++ /dev/null
@@ -1,612 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All rights reserved.
-Comment Creation Date: Thu Mar 15 10:24:18 1990
-Comment UniqueID 28362
-Comment VMusage 7572 42473
-FontName Helvetica-Oblique
-FullName Helvetica Oblique
-FamilyName Helvetica
-Weight Medium
-ItalicAngle -12
-IsFixedPitch false
-FontBBox -170 -225 1116 931
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.006
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All rights reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 718
-XHeight 523
-Ascender 718
-Descender -207
-StartCharMetrics 228
-C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 278 ; N exclam ; B 90 0 340 718 ;
-C 34 ; WX 355 ; N quotedbl ; B 168 463 438 718 ;
-C 35 ; WX 556 ; N numbersign ; B 73 0 631 688 ;
-C 36 ; WX 556 ; N dollar ; B 69 -115 617 775 ;
-C 37 ; WX 889 ; N percent ; B 147 -19 889 703 ;
-C 38 ; WX 667 ; N ampersand ; B 77 -15 647 718 ;
-C 39 ; WX 222 ; N quoteright ; B 151 463 310 718 ;
-C 40 ; WX 333 ; N parenleft ; B 108 -207 454 733 ;
-C 41 ; WX 333 ; N parenright ; B -9 -207 337 733 ;
-C 42 ; WX 389 ; N asterisk ; B 165 431 475 718 ;
-C 43 ; WX 584 ; N plus ; B 85 0 606 505 ;
-C 44 ; WX 278 ; N comma ; B 56 -147 214 106 ;
-C 45 ; WX 333 ; N hyphen ; B 93 232 357 322 ;
-C 46 ; WX 278 ; N period ; B 87 0 214 106 ;
-C 47 ; WX 278 ; N slash ; B -21 -19 452 737 ;
-C 48 ; WX 556 ; N zero ; B 93 -19 608 703 ;
-C 49 ; WX 556 ; N one ; B 207 0 508 703 ;
-C 50 ; WX 556 ; N two ; B 26 0 617 703 ;
-C 51 ; WX 556 ; N three ; B 75 -19 610 703 ;
-C 52 ; WX 556 ; N four ; B 61 0 576 703 ;
-C 53 ; WX 556 ; N five ; B 68 -19 621 688 ;
-C 54 ; WX 556 ; N six ; B 91 -19 615 703 ;
-C 55 ; WX 556 ; N seven ; B 137 0 669 688 ;
-C 56 ; WX 556 ; N eight ; B 74 -19 607 703 ;
-C 57 ; WX 556 ; N nine ; B 82 -19 609 703 ;
-C 58 ; WX 278 ; N colon ; B 87 0 301 516 ;
-C 59 ; WX 278 ; N semicolon ; B 56 -147 301 516 ;
-C 60 ; WX 584 ; N less ; B 94 11 641 495 ;
-C 61 ; WX 584 ; N equal ; B 63 115 628 390 ;
-C 62 ; WX 584 ; N greater ; B 50 11 597 495 ;
-C 63 ; WX 556 ; N question ; B 161 0 610 727 ;
-C 64 ; WX 1015 ; N at ; B 215 -19 965 737 ;
-C 65 ; WX 667 ; N A ; B 14 0 654 718 ;
-C 66 ; WX 667 ; N B ; B 74 0 712 718 ;
-C 67 ; WX 722 ; N C ; B 108 -19 782 737 ;
-C 68 ; WX 722 ; N D ; B 81 0 764 718 ;
-C 69 ; WX 667 ; N E ; B 86 0 762 718 ;
-C 70 ; WX 611 ; N F ; B 86 0 736 718 ;
-C 71 ; WX 778 ; N G ; B 111 -19 799 737 ;
-C 72 ; WX 722 ; N H ; B 77 0 799 718 ;
-C 73 ; WX 278 ; N I ; B 91 0 341 718 ;
-C 74 ; WX 500 ; N J ; B 47 -19 581 718 ;
-C 75 ; WX 667 ; N K ; B 76 0 808 718 ;
-C 76 ; WX 556 ; N L ; B 76 0 555 718 ;
-C 77 ; WX 833 ; N M ; B 73 0 914 718 ;
-C 78 ; WX 722 ; N N ; B 76 0 799 718 ;
-C 79 ; WX 778 ; N O ; B 105 -19 826 737 ;
-C 80 ; WX 667 ; N P ; B 86 0 737 718 ;
-C 81 ; WX 778 ; N Q ; B 105 -56 826 737 ;
-C 82 ; WX 722 ; N R ; B 88 0 773 718 ;
-C 83 ; WX 667 ; N S ; B 90 -19 713 737 ;
-C 84 ; WX 611 ; N T ; B 148 0 750 718 ;
-C 85 ; WX 722 ; N U ; B 123 -19 797 718 ;
-C 86 ; WX 667 ; N V ; B 173 0 800 718 ;
-C 87 ; WX 944 ; N W ; B 169 0 1081 718 ;
-C 88 ; WX 667 ; N X ; B 19 0 790 718 ;
-C 89 ; WX 667 ; N Y ; B 167 0 806 718 ;
-C 90 ; WX 611 ; N Z ; B 23 0 741 718 ;
-C 91 ; WX 278 ; N bracketleft ; B 21 -196 403 722 ;
-C 92 ; WX 278 ; N backslash ; B 140 -19 291 737 ;
-C 93 ; WX 278 ; N bracketright ; B -14 -196 368 722 ;
-C 94 ; WX 469 ; N asciicircum ; B 42 264 539 688 ;
-C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ;
-C 96 ; WX 222 ; N quoteleft ; B 165 470 323 725 ;
-C 97 ; WX 556 ; N a ; B 61 -15 559 538 ;
-C 98 ; WX 556 ; N b ; B 58 -15 584 718 ;
-C 99 ; WX 500 ; N c ; B 74 -15 553 538 ;
-C 100 ; WX 556 ; N d ; B 84 -15 652 718 ;
-C 101 ; WX 556 ; N e ; B 84 -15 578 538 ;
-C 102 ; WX 278 ; N f ; B 86 0 416 728 ; L i fi ; L l fl ;
-C 103 ; WX 556 ; N g ; B 42 -220 610 538 ;
-C 104 ; WX 556 ; N h ; B 65 0 573 718 ;
-C 105 ; WX 222 ; N i ; B 67 0 308 718 ;
-C 106 ; WX 222 ; N j ; B -60 -210 308 718 ;
-C 107 ; WX 500 ; N k ; B 67 0 600 718 ;
-C 108 ; WX 222 ; N l ; B 67 0 308 718 ;
-C 109 ; WX 833 ; N m ; B 65 0 852 538 ;
-C 110 ; WX 556 ; N n ; B 65 0 573 538 ;
-C 111 ; WX 556 ; N o ; B 83 -14 585 538 ;
-C 112 ; WX 556 ; N p ; B 14 -207 584 538 ;
-C 113 ; WX 556 ; N q ; B 84 -207 605 538 ;
-C 114 ; WX 333 ; N r ; B 77 0 446 538 ;
-C 115 ; WX 500 ; N s ; B 63 -15 529 538 ;
-C 116 ; WX 278 ; N t ; B 102 -7 368 669 ;
-C 117 ; WX 556 ; N u ; B 94 -15 600 523 ;
-C 118 ; WX 500 ; N v ; B 119 0 603 523 ;
-C 119 ; WX 722 ; N w ; B 125 0 820 523 ;
-C 120 ; WX 500 ; N x ; B 11 0 594 523 ;
-C 121 ; WX 500 ; N y ; B 15 -214 600 523 ;
-C 122 ; WX 500 ; N z ; B 31 0 571 523 ;
-C 123 ; WX 334 ; N braceleft ; B 92 -196 445 722 ;
-C 124 ; WX 260 ; N bar ; B 90 -19 324 737 ;
-C 125 ; WX 334 ; N braceright ; B 0 -196 354 722 ;
-C 126 ; WX 584 ; N asciitilde ; B 111 180 580 326 ;
-C 161 ; WX 333 ; N exclamdown ; B 77 -195 326 523 ;
-C 162 ; WX 556 ; N cent ; B 95 -115 584 623 ;
-C 163 ; WX 556 ; N sterling ; B 49 -16 634 718 ;
-C 164 ; WX 167 ; N fraction ; B -170 -19 482 703 ;
-C 165 ; WX 556 ; N yen ; B 81 0 699 688 ;
-C 166 ; WX 556 ; N florin ; B -52 -207 654 737 ;
-C 167 ; WX 556 ; N section ; B 76 -191 584 737 ;
-C 168 ; WX 556 ; N currency ; B 60 99 646 603 ;
-C 169 ; WX 191 ; N quotesingle ; B 157 463 285 718 ;
-C 170 ; WX 333 ; N quotedblleft ; B 138 470 461 725 ;
-C 171 ; WX 556 ; N guillemotleft ; B 146 108 554 446 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 137 108 340 446 ;
-C 173 ; WX 333 ; N guilsinglright ; B 111 108 314 446 ;
-C 174 ; WX 500 ; N fi ; B 86 0 587 728 ;
-C 175 ; WX 500 ; N fl ; B 86 0 585 728 ;
-C 177 ; WX 556 ; N endash ; B 51 240 623 313 ;
-C 178 ; WX 556 ; N dagger ; B 135 -159 622 718 ;
-C 179 ; WX 556 ; N daggerdbl ; B 52 -159 623 718 ;
-C 180 ; WX 278 ; N periodcentered ; B 129 190 257 315 ;
-C 182 ; WX 537 ; N paragraph ; B 126 -173 650 718 ;
-C 183 ; WX 350 ; N bullet ; B 91 202 413 517 ;
-C 184 ; WX 222 ; N quotesinglbase ; B 21 -149 180 106 ;
-C 185 ; WX 333 ; N quotedblbase ; B -6 -149 318 106 ;
-C 186 ; WX 333 ; N quotedblright ; B 124 463 448 718 ;
-C 187 ; WX 556 ; N guillemotright ; B 120 108 528 446 ;
-C 188 ; WX 1000 ; N ellipsis ; B 115 0 908 106 ;
-C 189 ; WX 1000 ; N perthousand ; B 88 -19 1029 703 ;
-C 191 ; WX 611 ; N questiondown ; B 85 -201 534 525 ;
-C 193 ; WX 333 ; N grave ; B 170 593 337 734 ;
-C 194 ; WX 333 ; N acute ; B 248 593 475 734 ;
-C 195 ; WX 333 ; N circumflex ; B 147 593 438 734 ;
-C 196 ; WX 333 ; N tilde ; B 125 606 490 722 ;
-C 197 ; WX 333 ; N macron ; B 143 627 468 684 ;
-C 198 ; WX 333 ; N breve ; B 167 595 476 731 ;
-C 199 ; WX 333 ; N dotaccent ; B 249 604 362 706 ;
-C 200 ; WX 333 ; N dieresis ; B 168 604 443 706 ;
-C 202 ; WX 333 ; N ring ; B 214 572 402 756 ;
-C 203 ; WX 333 ; N cedilla ; B 2 -225 232 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B 157 593 565 734 ;
-C 206 ; WX 333 ; N ogonek ; B 43 -225 249 0 ;
-C 207 ; WX 333 ; N caron ; B 177 593 468 734 ;
-C 208 ; WX 1000 ; N emdash ; B 51 240 1067 313 ;
-C 225 ; WX 1000 ; N AE ; B 8 0 1097 718 ;
-C 227 ; WX 370 ; N ordfeminine ; B 100 304 449 737 ;
-C 232 ; WX 556 ; N Lslash ; B 41 0 555 718 ;
-C 233 ; WX 778 ; N Oslash ; B 43 -19 890 737 ;
-C 234 ; WX 1000 ; N OE ; B 98 -19 1116 737 ;
-C 235 ; WX 365 ; N ordmasculine ; B 100 304 468 737 ;
-C 241 ; WX 889 ; N ae ; B 61 -15 909 538 ;
-C 245 ; WX 278 ; N dotlessi ; B 95 0 294 523 ;
-C 248 ; WX 222 ; N lslash ; B 41 0 347 718 ;
-C 249 ; WX 611 ; N oslash ; B 29 -22 647 545 ;
-C 250 ; WX 944 ; N oe ; B 83 -15 964 538 ;
-C 251 ; WX 611 ; N germandbls ; B 67 -15 658 728 ;
-C -1 ; WX 611 ; N Zcaron ; B 23 0 741 929 ;
-C -1 ; WX 500 ; N ccedilla ; B 74 -225 553 538 ;
-C -1 ; WX 500 ; N ydieresis ; B 15 -214 600 706 ;
-C -1 ; WX 556 ; N atilde ; B 61 -15 592 722 ;
-C -1 ; WX 278 ; N icircumflex ; B 95 0 411 734 ;
-C -1 ; WX 333 ; N threesuperior ; B 90 270 436 703 ;
-C -1 ; WX 556 ; N ecircumflex ; B 84 -15 578 734 ;
-C -1 ; WX 556 ; N thorn ; B 14 -207 584 718 ;
-C -1 ; WX 556 ; N egrave ; B 84 -15 578 734 ;
-C -1 ; WX 333 ; N twosuperior ; B 64 281 449 703 ;
-C -1 ; WX 556 ; N eacute ; B 84 -15 587 734 ;
-C -1 ; WX 556 ; N otilde ; B 83 -14 602 722 ;
-C -1 ; WX 667 ; N Aacute ; B 14 0 683 929 ;
-C -1 ; WX 556 ; N ocircumflex ; B 83 -14 585 734 ;
-C -1 ; WX 500 ; N yacute ; B 15 -214 600 734 ;
-C -1 ; WX 556 ; N udieresis ; B 94 -15 600 706 ;
-C -1 ; WX 834 ; N threequarters ; B 130 -19 861 703 ;
-C -1 ; WX 556 ; N acircumflex ; B 61 -15 559 734 ;
-C -1 ; WX 722 ; N Eth ; B 69 0 764 718 ;
-C -1 ; WX 556 ; N edieresis ; B 84 -15 578 706 ;
-C -1 ; WX 556 ; N ugrave ; B 94 -15 600 734 ;
-C -1 ; WX 1000 ; N trademark ; B 186 306 1056 718 ;
-C -1 ; WX 556 ; N ograve ; B 83 -14 585 734 ;
-C -1 ; WX 500 ; N scaron ; B 63 -15 552 734 ;
-C -1 ; WX 278 ; N Idieresis ; B 91 0 458 901 ;
-C -1 ; WX 556 ; N uacute ; B 94 -15 600 734 ;
-C -1 ; WX 556 ; N agrave ; B 61 -15 559 734 ;
-C -1 ; WX 556 ; N ntilde ; B 65 0 592 722 ;
-C -1 ; WX 556 ; N aring ; B 61 -15 559 756 ;
-C -1 ; WX 500 ; N zcaron ; B 31 0 571 734 ;
-C -1 ; WX 278 ; N Icircumflex ; B 91 0 452 929 ;
-C -1 ; WX 722 ; N Ntilde ; B 76 0 799 917 ;
-C -1 ; WX 556 ; N ucircumflex ; B 94 -15 600 734 ;
-C -1 ; WX 667 ; N Ecircumflex ; B 86 0 762 929 ;
-C -1 ; WX 278 ; N Iacute ; B 91 0 489 929 ;
-C -1 ; WX 722 ; N Ccedilla ; B 108 -225 782 737 ;
-C -1 ; WX 778 ; N Odieresis ; B 105 -19 826 901 ;
-C -1 ; WX 667 ; N Scaron ; B 90 -19 713 929 ;
-C -1 ; WX 667 ; N Edieresis ; B 86 0 762 901 ;
-C -1 ; WX 278 ; N Igrave ; B 91 0 351 929 ;
-C -1 ; WX 556 ; N adieresis ; B 61 -15 559 706 ;
-C -1 ; WX 778 ; N Ograve ; B 105 -19 826 929 ;
-C -1 ; WX 667 ; N Egrave ; B 86 0 762 929 ;
-C -1 ; WX 667 ; N Ydieresis ; B 167 0 806 901 ;
-C -1 ; WX 737 ; N registered ; B 54 -19 837 737 ;
-C -1 ; WX 778 ; N Otilde ; B 105 -19 826 917 ;
-C -1 ; WX 834 ; N onequarter ; B 150 -19 802 703 ;
-C -1 ; WX 722 ; N Ugrave ; B 123 -19 797 929 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 123 -19 797 929 ;
-C -1 ; WX 667 ; N Thorn ; B 86 0 712 718 ;
-C -1 ; WX 584 ; N divide ; B 85 -19 606 524 ;
-C -1 ; WX 667 ; N Atilde ; B 14 0 699 917 ;
-C -1 ; WX 722 ; N Uacute ; B 123 -19 797 929 ;
-C -1 ; WX 778 ; N Ocircumflex ; B 105 -19 826 929 ;
-C -1 ; WX 584 ; N logicalnot ; B 106 108 628 390 ;
-C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ;
-C -1 ; WX 278 ; N idieresis ; B 95 0 416 706 ;
-C -1 ; WX 278 ; N iacute ; B 95 0 448 734 ;
-C -1 ; WX 556 ; N aacute ; B 61 -15 587 734 ;
-C -1 ; WX 584 ; N plusminus ; B 39 0 618 506 ;
-C -1 ; WX 584 ; N multiply ; B 50 0 642 506 ;
-C -1 ; WX 722 ; N Udieresis ; B 123 -19 797 901 ;
-C -1 ; WX 584 ; N minus ; B 85 216 606 289 ;
-C -1 ; WX 333 ; N onesuperior ; B 166 281 371 703 ;
-C -1 ; WX 667 ; N Eacute ; B 86 0 762 929 ;
-C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ;
-C -1 ; WX 737 ; N copyright ; B 54 -19 837 737 ;
-C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ;
-C -1 ; WX 556 ; N odieresis ; B 83 -14 585 706 ;
-C -1 ; WX 556 ; N oacute ; B 83 -14 587 734 ;
-C -1 ; WX 400 ; N degree ; B 169 411 468 703 ;
-C -1 ; WX 278 ; N igrave ; B 95 0 310 734 ;
-C -1 ; WX 556 ; N mu ; B 24 -207 600 523 ;
-C -1 ; WX 778 ; N Oacute ; B 105 -19 826 929 ;
-C -1 ; WX 556 ; N eth ; B 81 -15 617 737 ;
-C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ;
-C -1 ; WX 667 ; N Yacute ; B 167 0 806 929 ;
-C -1 ; WX 260 ; N brokenbar ; B 90 -19 324 737 ;
-C -1 ; WX 834 ; N onehalf ; B 114 -19 839 703 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 250
-
-KPX A y -40
-KPX A w -40
-KPX A v -40
-KPX A u -30
-KPX A Y -100
-KPX A W -50
-KPX A V -70
-KPX A U -50
-KPX A T -120
-KPX A Q -30
-KPX A O -30
-KPX A G -30
-KPX A C -30
-
-KPX B period -20
-KPX B comma -20
-KPX B U -10
-
-KPX C period -30
-KPX C comma -30
-
-KPX D period -70
-KPX D comma -70
-KPX D Y -90
-KPX D W -40
-KPX D V -70
-KPX D A -40
-
-KPX F r -45
-KPX F period -150
-KPX F o -30
-KPX F e -30
-KPX F comma -150
-KPX F a -50
-KPX F A -80
-
-KPX J u -20
-KPX J period -30
-KPX J comma -30
-KPX J a -20
-KPX J A -20
-
-KPX K y -50
-KPX K u -30
-KPX K o -40
-KPX K e -40
-KPX K O -50
-
-KPX L y -30
-KPX L quoteright -160
-KPX L quotedblright -140
-KPX L Y -140
-KPX L W -70
-KPX L V -110
-KPX L T -110
-
-KPX O period -40
-KPX O comma -40
-KPX O Y -70
-KPX O X -60
-KPX O W -30
-KPX O V -50
-KPX O T -40
-KPX O A -20
-
-KPX P period -180
-KPX P o -50
-KPX P e -50
-KPX P comma -180
-KPX P a -40
-KPX P A -120
-
-KPX Q U -10
-
-KPX R Y -50
-KPX R W -30
-KPX R V -50
-KPX R U -40
-KPX R T -30
-KPX R O -20
-
-KPX S period -20
-KPX S comma -20
-
-KPX T y -120
-KPX T w -120
-KPX T u -120
-KPX T semicolon -20
-KPX T r -120
-KPX T period -120
-KPX T o -120
-KPX T hyphen -140
-KPX T e -120
-KPX T comma -120
-KPX T colon -20
-KPX T a -120
-KPX T O -40
-KPX T A -120
-
-KPX U period -40
-KPX U comma -40
-KPX U A -40
-
-KPX V u -70
-KPX V semicolon -40
-KPX V period -125
-KPX V o -80
-KPX V hyphen -80
-KPX V e -80
-KPX V comma -125
-KPX V colon -40
-KPX V a -70
-KPX V O -40
-KPX V G -40
-KPX V A -80
-
-KPX W y -20
-KPX W u -30
-KPX W period -80
-KPX W o -30
-KPX W hyphen -40
-KPX W e -30
-KPX W comma -80
-KPX W a -40
-KPX W O -20
-KPX W A -50
-
-KPX Y u -110
-KPX Y semicolon -60
-KPX Y period -140
-KPX Y o -140
-KPX Y i -20
-KPX Y hyphen -140
-KPX Y e -140
-KPX Y comma -140
-KPX Y colon -60
-KPX Y a -140
-KPX Y O -85
-KPX Y A -110
-
-KPX a y -30
-KPX a w -20
-KPX a v -20
-
-KPX b y -20
-KPX b v -20
-KPX b u -20
-KPX b period -40
-KPX b l -20
-KPX b comma -40
-KPX b b -10
-
-KPX c k -20
-KPX c comma -15
-
-KPX colon space -50
-
-KPX comma quoteright -100
-KPX comma quotedblright -100
-
-KPX e y -20
-KPX e x -30
-KPX e w -20
-KPX e v -30
-KPX e period -15
-KPX e comma -15
-
-KPX f quoteright 50
-KPX f quotedblright 60
-KPX f period -30
-KPX f o -30
-KPX f e -30
-KPX f dotlessi -28
-KPX f comma -30
-KPX f a -30
-
-KPX g r -10
-
-KPX h y -30
-
-KPX k o -20
-KPX k e -20
-
-KPX m y -15
-KPX m u -10
-
-KPX n y -15
-KPX n v -20
-KPX n u -10
-
-KPX o y -30
-KPX o x -30
-KPX o w -15
-KPX o v -15
-KPX o period -40
-KPX o comma -40
-
-KPX oslash z -55
-KPX oslash y -70
-KPX oslash x -85
-KPX oslash w -70
-KPX oslash v -70
-KPX oslash u -55
-KPX oslash t -55
-KPX oslash s -55
-KPX oslash r -55
-KPX oslash q -55
-KPX oslash period -95
-KPX oslash p -55
-KPX oslash o -55
-KPX oslash n -55
-KPX oslash m -55
-KPX oslash l -55
-KPX oslash k -55
-KPX oslash j -55
-KPX oslash i -55
-KPX oslash h -55
-KPX oslash g -55
-KPX oslash f -55
-KPX oslash e -55
-KPX oslash d -55
-KPX oslash comma -95
-KPX oslash c -55
-KPX oslash b -55
-KPX oslash a -55
-
-KPX p y -30
-KPX p period -35
-KPX p comma -35
-
-KPX period space -60
-KPX period quoteright -100
-KPX period quotedblright -100
-
-KPX quotedblright space -40
-
-KPX quoteleft quoteleft -57
-
-KPX quoteright space -70
-KPX quoteright s -50
-KPX quoteright r -50
-KPX quoteright quoteright -57
-KPX quoteright d -50
-
-KPX r y 30
-KPX r v 30
-KPX r u 15
-KPX r t 40
-KPX r semicolon 30
-KPX r period -50
-KPX r p 30
-KPX r n 25
-KPX r m 25
-KPX r l 15
-KPX r k 15
-KPX r i 15
-KPX r comma -50
-KPX r colon 30
-KPX r a -10
-
-KPX s w -30
-KPX s period -15
-KPX s comma -15
-
-KPX semicolon space -50
-
-KPX space quoteleft -60
-KPX space quotedblleft -30
-KPX space Y -90
-KPX space W -40
-KPX space V -50
-KPX space T -50
-
-KPX v period -80
-KPX v o -25
-KPX v e -25
-KPX v comma -80
-KPX v a -25
-
-KPX w period -60
-KPX w o -10
-KPX w e -10
-KPX w comma -60
-KPX w a -15
-
-KPX x e -30
-
-KPX y period -100
-KPX y o -20
-KPX y e -20
-KPX y comma -100
-KPX y a -20
-
-KPX z o -15
-KPX z e -15
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 208 195 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 208 195 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 208 195 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 208 195 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 204 175 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 208 195 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 195 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 208 195 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 208 195 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 208 195 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 208 195 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 14 195 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 14 195 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 14 195 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 14 195 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 246 195 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 264 195 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 264 195 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 264 195 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 264 195 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 264 195 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 208 195 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 236 195 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 236 195 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 236 195 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 236 195 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 208 195 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 208 195 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 180 195 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 102 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 84 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 102 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 112 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 112 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 112 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 112 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 112 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 84 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 112 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 112 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 112 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Helvetica.afm b/config/psfonts/Helvetica.afm
deleted file mode 100644 (file)
index 1eb3b44..0000000
+++ /dev/null
@@ -1,612 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All rights reserved.
-Comment Creation Date: Thu Mar 15 08:58:00 1990
-Comment UniqueID 28352
-Comment VMusage 26389 33281
-FontName Helvetica
-FullName Helvetica
-FamilyName Helvetica
-Weight Medium
-ItalicAngle 0
-IsFixedPitch false
-FontBBox -166 -225 1000 931
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.006
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All rights reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 718
-XHeight 523
-Ascender 718
-Descender -207
-StartCharMetrics 228
-C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 278 ; N exclam ; B 90 0 187 718 ;
-C 34 ; WX 355 ; N quotedbl ; B 70 463 285 718 ;
-C 35 ; WX 556 ; N numbersign ; B 28 0 529 688 ;
-C 36 ; WX 556 ; N dollar ; B 32 -115 520 775 ;
-C 37 ; WX 889 ; N percent ; B 39 -19 850 703 ;
-C 38 ; WX 667 ; N ampersand ; B 44 -15 645 718 ;
-C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
-C 40 ; WX 333 ; N parenleft ; B 68 -207 299 733 ;
-C 41 ; WX 333 ; N parenright ; B 34 -207 265 733 ;
-C 42 ; WX 389 ; N asterisk ; B 39 431 349 718 ;
-C 43 ; WX 584 ; N plus ; B 39 0 545 505 ;
-C 44 ; WX 278 ; N comma ; B 87 -147 191 106 ;
-C 45 ; WX 333 ; N hyphen ; B 44 232 289 322 ;
-C 46 ; WX 278 ; N period ; B 87 0 191 106 ;
-C 47 ; WX 278 ; N slash ; B -17 -19 295 737 ;
-C 48 ; WX 556 ; N zero ; B 37 -19 519 703 ;
-C 49 ; WX 556 ; N one ; B 101 0 359 703 ;
-C 50 ; WX 556 ; N two ; B 26 0 507 703 ;
-C 51 ; WX 556 ; N three ; B 34 -19 522 703 ;
-C 52 ; WX 556 ; N four ; B 25 0 523 703 ;
-C 53 ; WX 556 ; N five ; B 32 -19 514 688 ;
-C 54 ; WX 556 ; N six ; B 38 -19 518 703 ;
-C 55 ; WX 556 ; N seven ; B 37 0 523 688 ;
-C 56 ; WX 556 ; N eight ; B 38 -19 517 703 ;
-C 57 ; WX 556 ; N nine ; B 42 -19 514 703 ;
-C 58 ; WX 278 ; N colon ; B 87 0 191 516 ;
-C 59 ; WX 278 ; N semicolon ; B 87 -147 191 516 ;
-C 60 ; WX 584 ; N less ; B 48 11 536 495 ;
-C 61 ; WX 584 ; N equal ; B 39 115 545 390 ;
-C 62 ; WX 584 ; N greater ; B 48 11 536 495 ;
-C 63 ; WX 556 ; N question ; B 56 0 492 727 ;
-C 64 ; WX 1015 ; N at ; B 147 -19 868 737 ;
-C 65 ; WX 667 ; N A ; B 14 0 654 718 ;
-C 66 ; WX 667 ; N B ; B 74 0 627 718 ;
-C 67 ; WX 722 ; N C ; B 44 -19 681 737 ;
-C 68 ; WX 722 ; N D ; B 81 0 674 718 ;
-C 69 ; WX 667 ; N E ; B 86 0 616 718 ;
-C 70 ; WX 611 ; N F ; B 86 0 583 718 ;
-C 71 ; WX 778 ; N G ; B 48 -19 704 737 ;
-C 72 ; WX 722 ; N H ; B 77 0 646 718 ;
-C 73 ; WX 278 ; N I ; B 91 0 188 718 ;
-C 74 ; WX 500 ; N J ; B 17 -19 428 718 ;
-C 75 ; WX 667 ; N K ; B 76 0 663 718 ;
-C 76 ; WX 556 ; N L ; B 76 0 537 718 ;
-C 77 ; WX 833 ; N M ; B 73 0 761 718 ;
-C 78 ; WX 722 ; N N ; B 76 0 646 718 ;
-C 79 ; WX 778 ; N O ; B 39 -19 739 737 ;
-C 80 ; WX 667 ; N P ; B 86 0 622 718 ;
-C 81 ; WX 778 ; N Q ; B 39 -56 739 737 ;
-C 82 ; WX 722 ; N R ; B 88 0 684 718 ;
-C 83 ; WX 667 ; N S ; B 49 -19 620 737 ;
-C 84 ; WX 611 ; N T ; B 14 0 597 718 ;
-C 85 ; WX 722 ; N U ; B 79 -19 644 718 ;
-C 86 ; WX 667 ; N V ; B 20 0 647 718 ;
-C 87 ; WX 944 ; N W ; B 16 0 928 718 ;
-C 88 ; WX 667 ; N X ; B 19 0 648 718 ;
-C 89 ; WX 667 ; N Y ; B 14 0 653 718 ;
-C 90 ; WX 611 ; N Z ; B 23 0 588 718 ;
-C 91 ; WX 278 ; N bracketleft ; B 63 -196 250 722 ;
-C 92 ; WX 278 ; N backslash ; B -17 -19 295 737 ;
-C 93 ; WX 278 ; N bracketright ; B 28 -196 215 722 ;
-C 94 ; WX 469 ; N asciicircum ; B -14 264 483 688 ;
-C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
-C 96 ; WX 222 ; N quoteleft ; B 65 470 169 725 ;
-C 97 ; WX 556 ; N a ; B 36 -15 530 538 ;
-C 98 ; WX 556 ; N b ; B 58 -15 517 718 ;
-C 99 ; WX 500 ; N c ; B 30 -15 477 538 ;
-C 100 ; WX 556 ; N d ; B 35 -15 499 718 ;
-C 101 ; WX 556 ; N e ; B 40 -15 516 538 ;
-C 102 ; WX 278 ; N f ; B 14 0 262 728 ; L i fi ; L l fl ;
-C 103 ; WX 556 ; N g ; B 40 -220 499 538 ;
-C 104 ; WX 556 ; N h ; B 65 0 491 718 ;
-C 105 ; WX 222 ; N i ; B 67 0 155 718 ;
-C 106 ; WX 222 ; N j ; B -16 -210 155 718 ;
-C 107 ; WX 500 ; N k ; B 67 0 501 718 ;
-C 108 ; WX 222 ; N l ; B 67 0 155 718 ;
-C 109 ; WX 833 ; N m ; B 65 0 769 538 ;
-C 110 ; WX 556 ; N n ; B 65 0 491 538 ;
-C 111 ; WX 556 ; N o ; B 35 -14 521 538 ;
-C 112 ; WX 556 ; N p ; B 58 -207 517 538 ;
-C 113 ; WX 556 ; N q ; B 35 -207 494 538 ;
-C 114 ; WX 333 ; N r ; B 77 0 332 538 ;
-C 115 ; WX 500 ; N s ; B 32 -15 464 538 ;
-C 116 ; WX 278 ; N t ; B 14 -7 257 669 ;
-C 117 ; WX 556 ; N u ; B 68 -15 489 523 ;
-C 118 ; WX 500 ; N v ; B 8 0 492 523 ;
-C 119 ; WX 722 ; N w ; B 14 0 709 523 ;
-C 120 ; WX 500 ; N x ; B 11 0 490 523 ;
-C 121 ; WX 500 ; N y ; B 11 -214 489 523 ;
-C 122 ; WX 500 ; N z ; B 31 0 469 523 ;
-C 123 ; WX 334 ; N braceleft ; B 42 -196 292 722 ;
-C 124 ; WX 260 ; N bar ; B 94 -19 167 737 ;
-C 125 ; WX 334 ; N braceright ; B 42 -196 292 722 ;
-C 126 ; WX 584 ; N asciitilde ; B 61 180 523 326 ;
-C 161 ; WX 333 ; N exclamdown ; B 118 -195 215 523 ;
-C 162 ; WX 556 ; N cent ; B 51 -115 513 623 ;
-C 163 ; WX 556 ; N sterling ; B 33 -16 539 718 ;
-C 164 ; WX 167 ; N fraction ; B -166 -19 333 703 ;
-C 165 ; WX 556 ; N yen ; B 3 0 553 688 ;
-C 166 ; WX 556 ; N florin ; B -11 -207 501 737 ;
-C 167 ; WX 556 ; N section ; B 43 -191 512 737 ;
-C 168 ; WX 556 ; N currency ; B 28 99 528 603 ;
-C 169 ; WX 191 ; N quotesingle ; B 59 463 132 718 ;
-C 170 ; WX 333 ; N quotedblleft ; B 38 470 307 725 ;
-C 171 ; WX 556 ; N guillemotleft ; B 97 108 459 446 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 88 108 245 446 ;
-C 173 ; WX 333 ; N guilsinglright ; B 88 108 245 446 ;
-C 174 ; WX 500 ; N fi ; B 14 0 434 728 ;
-C 175 ; WX 500 ; N fl ; B 14 0 432 728 ;
-C 177 ; WX 556 ; N endash ; B 0 240 556 313 ;
-C 178 ; WX 556 ; N dagger ; B 43 -159 514 718 ;
-C 179 ; WX 556 ; N daggerdbl ; B 43 -159 514 718 ;
-C 180 ; WX 278 ; N periodcentered ; B 77 190 202 315 ;
-C 182 ; WX 537 ; N paragraph ; B 18 -173 497 718 ;
-C 183 ; WX 350 ; N bullet ; B 18 202 333 517 ;
-C 184 ; WX 222 ; N quotesinglbase ; B 53 -149 157 106 ;
-C 185 ; WX 333 ; N quotedblbase ; B 26 -149 295 106 ;
-C 186 ; WX 333 ; N quotedblright ; B 26 463 295 718 ;
-C 187 ; WX 556 ; N guillemotright ; B 97 108 459 446 ;
-C 188 ; WX 1000 ; N ellipsis ; B 115 0 885 106 ;
-C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 703 ;
-C 191 ; WX 611 ; N questiondown ; B 91 -201 527 525 ;
-C 193 ; WX 333 ; N grave ; B 14 593 211 734 ;
-C 194 ; WX 333 ; N acute ; B 122 593 319 734 ;
-C 195 ; WX 333 ; N circumflex ; B 21 593 312 734 ;
-C 196 ; WX 333 ; N tilde ; B -4 606 337 722 ;
-C 197 ; WX 333 ; N macron ; B 10 627 323 684 ;
-C 198 ; WX 333 ; N breve ; B 13 595 321 731 ;
-C 199 ; WX 333 ; N dotaccent ; B 121 604 212 706 ;
-C 200 ; WX 333 ; N dieresis ; B 40 604 293 706 ;
-C 202 ; WX 333 ; N ring ; B 75 572 259 756 ;
-C 203 ; WX 333 ; N cedilla ; B 45 -225 259 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B 31 593 409 734 ;
-C 206 ; WX 333 ; N ogonek ; B 73 -225 287 0 ;
-C 207 ; WX 333 ; N caron ; B 21 593 312 734 ;
-C 208 ; WX 1000 ; N emdash ; B 0 240 1000 313 ;
-C 225 ; WX 1000 ; N AE ; B 8 0 951 718 ;
-C 227 ; WX 370 ; N ordfeminine ; B 24 304 346 737 ;
-C 232 ; WX 556 ; N Lslash ; B -20 0 537 718 ;
-C 233 ; WX 778 ; N Oslash ; B 39 -19 740 737 ;
-C 234 ; WX 1000 ; N OE ; B 36 -19 965 737 ;
-C 235 ; WX 365 ; N ordmasculine ; B 25 304 341 737 ;
-C 241 ; WX 889 ; N ae ; B 36 -15 847 538 ;
-C 245 ; WX 278 ; N dotlessi ; B 95 0 183 523 ;
-C 248 ; WX 222 ; N lslash ; B -20 0 242 718 ;
-C 249 ; WX 611 ; N oslash ; B 28 -22 537 545 ;
-C 250 ; WX 944 ; N oe ; B 35 -15 902 538 ;
-C 251 ; WX 611 ; N germandbls ; B 67 -15 571 728 ;
-C -1 ; WX 611 ; N Zcaron ; B 23 0 588 929 ;
-C -1 ; WX 500 ; N ccedilla ; B 30 -225 477 538 ;
-C -1 ; WX 500 ; N ydieresis ; B 11 -214 489 706 ;
-C -1 ; WX 556 ; N atilde ; B 36 -15 530 722 ;
-C -1 ; WX 278 ; N icircumflex ; B -6 0 285 734 ;
-C -1 ; WX 333 ; N threesuperior ; B 5 270 325 703 ;
-C -1 ; WX 556 ; N ecircumflex ; B 40 -15 516 734 ;
-C -1 ; WX 556 ; N thorn ; B 58 -207 517 718 ;
-C -1 ; WX 556 ; N egrave ; B 40 -15 516 734 ;
-C -1 ; WX 333 ; N twosuperior ; B 4 281 323 703 ;
-C -1 ; WX 556 ; N eacute ; B 40 -15 516 734 ;
-C -1 ; WX 556 ; N otilde ; B 35 -14 521 722 ;
-C -1 ; WX 667 ; N Aacute ; B 14 0 654 929 ;
-C -1 ; WX 556 ; N ocircumflex ; B 35 -14 521 734 ;
-C -1 ; WX 500 ; N yacute ; B 11 -214 489 734 ;
-C -1 ; WX 556 ; N udieresis ; B 68 -15 489 706 ;
-C -1 ; WX 834 ; N threequarters ; B 45 -19 810 703 ;
-C -1 ; WX 556 ; N acircumflex ; B 36 -15 530 734 ;
-C -1 ; WX 722 ; N Eth ; B 0 0 674 718 ;
-C -1 ; WX 556 ; N edieresis ; B 40 -15 516 706 ;
-C -1 ; WX 556 ; N ugrave ; B 68 -15 489 734 ;
-C -1 ; WX 1000 ; N trademark ; B 46 306 903 718 ;
-C -1 ; WX 556 ; N ograve ; B 35 -14 521 734 ;
-C -1 ; WX 500 ; N scaron ; B 32 -15 464 734 ;
-C -1 ; WX 278 ; N Idieresis ; B 13 0 266 901 ;
-C -1 ; WX 556 ; N uacute ; B 68 -15 489 734 ;
-C -1 ; WX 556 ; N agrave ; B 36 -15 530 734 ;
-C -1 ; WX 556 ; N ntilde ; B 65 0 491 722 ;
-C -1 ; WX 556 ; N aring ; B 36 -15 530 756 ;
-C -1 ; WX 500 ; N zcaron ; B 31 0 469 734 ;
-C -1 ; WX 278 ; N Icircumflex ; B -6 0 285 929 ;
-C -1 ; WX 722 ; N Ntilde ; B 76 0 646 917 ;
-C -1 ; WX 556 ; N ucircumflex ; B 68 -15 489 734 ;
-C -1 ; WX 667 ; N Ecircumflex ; B 86 0 616 929 ;
-C -1 ; WX 278 ; N Iacute ; B 91 0 292 929 ;
-C -1 ; WX 722 ; N Ccedilla ; B 44 -225 681 737 ;
-C -1 ; WX 778 ; N Odieresis ; B 39 -19 739 901 ;
-C -1 ; WX 667 ; N Scaron ; B 49 -19 620 929 ;
-C -1 ; WX 667 ; N Edieresis ; B 86 0 616 901 ;
-C -1 ; WX 278 ; N Igrave ; B -13 0 188 929 ;
-C -1 ; WX 556 ; N adieresis ; B 36 -15 530 706 ;
-C -1 ; WX 778 ; N Ograve ; B 39 -19 739 929 ;
-C -1 ; WX 667 ; N Egrave ; B 86 0 616 929 ;
-C -1 ; WX 667 ; N Ydieresis ; B 14 0 653 901 ;
-C -1 ; WX 737 ; N registered ; B -14 -19 752 737 ;
-C -1 ; WX 778 ; N Otilde ; B 39 -19 739 917 ;
-C -1 ; WX 834 ; N onequarter ; B 73 -19 756 703 ;
-C -1 ; WX 722 ; N Ugrave ; B 79 -19 644 929 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 79 -19 644 929 ;
-C -1 ; WX 667 ; N Thorn ; B 86 0 622 718 ;
-C -1 ; WX 584 ; N divide ; B 39 -19 545 524 ;
-C -1 ; WX 667 ; N Atilde ; B 14 0 654 917 ;
-C -1 ; WX 722 ; N Uacute ; B 79 -19 644 929 ;
-C -1 ; WX 778 ; N Ocircumflex ; B 39 -19 739 929 ;
-C -1 ; WX 584 ; N logicalnot ; B 39 108 545 390 ;
-C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ;
-C -1 ; WX 278 ; N idieresis ; B 13 0 266 706 ;
-C -1 ; WX 278 ; N iacute ; B 95 0 292 734 ;
-C -1 ; WX 556 ; N aacute ; B 36 -15 530 734 ;
-C -1 ; WX 584 ; N plusminus ; B 39 0 545 506 ;
-C -1 ; WX 584 ; N multiply ; B 39 0 545 506 ;
-C -1 ; WX 722 ; N Udieresis ; B 79 -19 644 901 ;
-C -1 ; WX 584 ; N minus ; B 39 216 545 289 ;
-C -1 ; WX 333 ; N onesuperior ; B 43 281 222 703 ;
-C -1 ; WX 667 ; N Eacute ; B 86 0 616 929 ;
-C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ;
-C -1 ; WX 737 ; N copyright ; B -14 -19 752 737 ;
-C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ;
-C -1 ; WX 556 ; N odieresis ; B 35 -14 521 706 ;
-C -1 ; WX 556 ; N oacute ; B 35 -14 521 734 ;
-C -1 ; WX 400 ; N degree ; B 54 411 346 703 ;
-C -1 ; WX 278 ; N igrave ; B -13 0 184 734 ;
-C -1 ; WX 556 ; N mu ; B 68 -207 489 523 ;
-C -1 ; WX 778 ; N Oacute ; B 39 -19 739 929 ;
-C -1 ; WX 556 ; N eth ; B 35 -15 522 737 ;
-C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ;
-C -1 ; WX 667 ; N Yacute ; B 14 0 653 929 ;
-C -1 ; WX 260 ; N brokenbar ; B 94 -19 167 737 ;
-C -1 ; WX 834 ; N onehalf ; B 43 -19 773 703 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 250
-
-KPX A y -40
-KPX A w -40
-KPX A v -40
-KPX A u -30
-KPX A Y -100
-KPX A W -50
-KPX A V -70
-KPX A U -50
-KPX A T -120
-KPX A Q -30
-KPX A O -30
-KPX A G -30
-KPX A C -30
-
-KPX B period -20
-KPX B comma -20
-KPX B U -10
-
-KPX C period -30
-KPX C comma -30
-
-KPX D period -70
-KPX D comma -70
-KPX D Y -90
-KPX D W -40
-KPX D V -70
-KPX D A -40
-
-KPX F r -45
-KPX F period -150
-KPX F o -30
-KPX F e -30
-KPX F comma -150
-KPX F a -50
-KPX F A -80
-
-KPX J u -20
-KPX J period -30
-KPX J comma -30
-KPX J a -20
-KPX J A -20
-
-KPX K y -50
-KPX K u -30
-KPX K o -40
-KPX K e -40
-KPX K O -50
-
-KPX L y -30
-KPX L quoteright -160
-KPX L quotedblright -140
-KPX L Y -140
-KPX L W -70
-KPX L V -110
-KPX L T -110
-
-KPX O period -40
-KPX O comma -40
-KPX O Y -70
-KPX O X -60
-KPX O W -30
-KPX O V -50
-KPX O T -40
-KPX O A -20
-
-KPX P period -180
-KPX P o -50
-KPX P e -50
-KPX P comma -180
-KPX P a -40
-KPX P A -120
-
-KPX Q U -10
-
-KPX R Y -50
-KPX R W -30
-KPX R V -50
-KPX R U -40
-KPX R T -30
-KPX R O -20
-
-KPX S period -20
-KPX S comma -20
-
-KPX T y -120
-KPX T w -120
-KPX T u -120
-KPX T semicolon -20
-KPX T r -120
-KPX T period -120
-KPX T o -120
-KPX T hyphen -140
-KPX T e -120
-KPX T comma -120
-KPX T colon -20
-KPX T a -120
-KPX T O -40
-KPX T A -120
-
-KPX U period -40
-KPX U comma -40
-KPX U A -40
-
-KPX V u -70
-KPX V semicolon -40
-KPX V period -125
-KPX V o -80
-KPX V hyphen -80
-KPX V e -80
-KPX V comma -125
-KPX V colon -40
-KPX V a -70
-KPX V O -40
-KPX V G -40
-KPX V A -80
-
-KPX W y -20
-KPX W u -30
-KPX W period -80
-KPX W o -30
-KPX W hyphen -40
-KPX W e -30
-KPX W comma -80
-KPX W a -40
-KPX W O -20
-KPX W A -50
-
-KPX Y u -110
-KPX Y semicolon -60
-KPX Y period -140
-KPX Y o -140
-KPX Y i -20
-KPX Y hyphen -140
-KPX Y e -140
-KPX Y comma -140
-KPX Y colon -60
-KPX Y a -140
-KPX Y O -85
-KPX Y A -110
-
-KPX a y -30
-KPX a w -20
-KPX a v -20
-
-KPX b y -20
-KPX b v -20
-KPX b u -20
-KPX b period -40
-KPX b l -20
-KPX b comma -40
-KPX b b -10
-
-KPX c k -20
-KPX c comma -15
-
-KPX colon space -50
-
-KPX comma quoteright -100
-KPX comma quotedblright -100
-
-KPX e y -20
-KPX e x -30
-KPX e w -20
-KPX e v -30
-KPX e period -15
-KPX e comma -15
-
-KPX f quoteright 50
-KPX f quotedblright 60
-KPX f period -30
-KPX f o -30
-KPX f e -30
-KPX f dotlessi -28
-KPX f comma -30
-KPX f a -30
-
-KPX g r -10
-
-KPX h y -30
-
-KPX k o -20
-KPX k e -20
-
-KPX m y -15
-KPX m u -10
-
-KPX n y -15
-KPX n v -20
-KPX n u -10
-
-KPX o y -30
-KPX o x -30
-KPX o w -15
-KPX o v -15
-KPX o period -40
-KPX o comma -40
-
-KPX oslash z -55
-KPX oslash y -70
-KPX oslash x -85
-KPX oslash w -70
-KPX oslash v -70
-KPX oslash u -55
-KPX oslash t -55
-KPX oslash s -55
-KPX oslash r -55
-KPX oslash q -55
-KPX oslash period -95
-KPX oslash p -55
-KPX oslash o -55
-KPX oslash n -55
-KPX oslash m -55
-KPX oslash l -55
-KPX oslash k -55
-KPX oslash j -55
-KPX oslash i -55
-KPX oslash h -55
-KPX oslash g -55
-KPX oslash f -55
-KPX oslash e -55
-KPX oslash d -55
-KPX oslash comma -95
-KPX oslash c -55
-KPX oslash b -55
-KPX oslash a -55
-
-KPX p y -30
-KPX p period -35
-KPX p comma -35
-
-KPX period space -60
-KPX period quoteright -100
-KPX period quotedblright -100
-
-KPX quotedblright space -40
-
-KPX quoteleft quoteleft -57
-
-KPX quoteright space -70
-KPX quoteright s -50
-KPX quoteright r -50
-KPX quoteright quoteright -57
-KPX quoteright d -50
-
-KPX r y 30
-KPX r v 30
-KPX r u 15
-KPX r t 40
-KPX r semicolon 30
-KPX r period -50
-KPX r p 30
-KPX r n 25
-KPX r m 25
-KPX r l 15
-KPX r k 15
-KPX r i 15
-KPX r comma -50
-KPX r colon 30
-KPX r a -10
-
-KPX s w -30
-KPX s period -15
-KPX s comma -15
-
-KPX semicolon space -50
-
-KPX space quoteleft -60
-KPX space quotedblleft -30
-KPX space Y -90
-KPX space W -40
-KPX space V -50
-KPX space T -50
-
-KPX v period -80
-KPX v o -25
-KPX v e -25
-KPX v comma -80
-KPX v a -25
-
-KPX w period -60
-KPX w o -10
-KPX w e -10
-KPX w comma -60
-KPX w a -15
-
-KPX x e -30
-
-KPX y period -100
-KPX y o -20
-KPX y e -20
-KPX y comma -100
-KPX y a -20
-
-KPX z o -15
-KPX z e -15
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 167 195 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 167 195 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 167 195 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 167 195 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 167 175 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 167 195 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 195 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 167 195 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 167 195 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 167 195 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 167 195 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute -27 195 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -27 195 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -27 195 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave -27 195 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 205 195 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 195 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 195 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 195 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 195 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 195 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 167 195 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 195 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 195 195 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 195 195 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 195 195 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 167 195 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 167 195 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 195 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 102 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 84 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 102 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 112 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 112 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 112 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 112 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 112 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 84 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 112 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 112 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 112 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Times-Bold.afm b/config/psfonts/Times-Bold.afm
deleted file mode 100644 (file)
index 55207f9..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.
-Comment Creation Date: Tue Mar 20 12:17:14 1990
-Comment UniqueID 28417
-Comment VMusage 30458 37350
-FontName Times-Bold
-FullName Times Bold
-FamilyName Times
-Weight Bold
-ItalicAngle 0
-IsFixedPitch false
-FontBBox -168 -218 1000 935
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.007
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 676
-XHeight 461
-Ascender 676
-Descender -205
-StartCharMetrics 228
-C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 333 ; N exclam ; B 81 -13 251 691 ;
-C 34 ; WX 555 ; N quotedbl ; B 83 404 472 691 ;
-C 35 ; WX 500 ; N numbersign ; B 4 0 496 700 ;
-C 36 ; WX 500 ; N dollar ; B 29 -99 472 750 ;
-C 37 ; WX 1000 ; N percent ; B 124 -14 877 692 ;
-C 38 ; WX 833 ; N ampersand ; B 62 -16 787 691 ;
-C 39 ; WX 333 ; N quoteright ; B 79 356 263 691 ;
-C 40 ; WX 333 ; N parenleft ; B 46 -168 306 694 ;
-C 41 ; WX 333 ; N parenright ; B 27 -168 287 694 ;
-C 42 ; WX 500 ; N asterisk ; B 56 255 447 691 ;
-C 43 ; WX 570 ; N plus ; B 33 0 537 506 ;
-C 44 ; WX 250 ; N comma ; B 39 -180 223 155 ;
-C 45 ; WX 333 ; N hyphen ; B 44 171 287 287 ;
-C 46 ; WX 250 ; N period ; B 41 -13 210 156 ;
-C 47 ; WX 278 ; N slash ; B -24 -19 302 691 ;
-C 48 ; WX 500 ; N zero ; B 24 -13 476 688 ;
-C 49 ; WX 500 ; N one ; B 65 0 442 688 ;
-C 50 ; WX 500 ; N two ; B 17 0 478 688 ;
-C 51 ; WX 500 ; N three ; B 16 -14 468 688 ;
-C 52 ; WX 500 ; N four ; B 19 0 475 688 ;
-C 53 ; WX 500 ; N five ; B 22 -8 470 676 ;
-C 54 ; WX 500 ; N six ; B 28 -13 475 688 ;
-C 55 ; WX 500 ; N seven ; B 17 0 477 676 ;
-C 56 ; WX 500 ; N eight ; B 28 -13 472 688 ;
-C 57 ; WX 500 ; N nine ; B 26 -13 473 688 ;
-C 58 ; WX 333 ; N colon ; B 82 -13 251 472 ;
-C 59 ; WX 333 ; N semicolon ; B 82 -180 266 472 ;
-C 60 ; WX 570 ; N less ; B 31 -8 539 514 ;
-C 61 ; WX 570 ; N equal ; B 33 107 537 399 ;
-C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ;
-C 63 ; WX 500 ; N question ; B 57 -13 445 689 ;
-C 64 ; WX 930 ; N at ; B 108 -19 822 691 ;
-C 65 ; WX 722 ; N A ; B 9 0 689 690 ;
-C 66 ; WX 667 ; N B ; B 16 0 619 676 ;
-C 67 ; WX 722 ; N C ; B 49 -19 687 691 ;
-C 68 ; WX 722 ; N D ; B 14 0 690 676 ;
-C 69 ; WX 667 ; N E ; B 16 0 641 676 ;
-C 70 ; WX 611 ; N F ; B 16 0 583 676 ;
-C 71 ; WX 778 ; N G ; B 37 -19 755 691 ;
-C 72 ; WX 778 ; N H ; B 21 0 759 676 ;
-C 73 ; WX 389 ; N I ; B 20 0 370 676 ;
-C 74 ; WX 500 ; N J ; B 3 -96 479 676 ;
-C 75 ; WX 778 ; N K ; B 30 0 769 676 ;
-C 76 ; WX 667 ; N L ; B 19 0 638 676 ;
-C 77 ; WX 944 ; N M ; B 14 0 921 676 ;
-C 78 ; WX 722 ; N N ; B 16 -18 701 676 ;
-C 79 ; WX 778 ; N O ; B 35 -19 743 691 ;
-C 80 ; WX 611 ; N P ; B 16 0 600 676 ;
-C 81 ; WX 778 ; N Q ; B 35 -176 743 691 ;
-C 82 ; WX 722 ; N R ; B 26 0 715 676 ;
-C 83 ; WX 556 ; N S ; B 35 -19 513 692 ;
-C 84 ; WX 667 ; N T ; B 31 0 636 676 ;
-C 85 ; WX 722 ; N U ; B 16 -19 701 676 ;
-C 86 ; WX 722 ; N V ; B 16 -18 701 676 ;
-C 87 ; WX 1000 ; N W ; B 19 -15 981 676 ;
-C 88 ; WX 722 ; N X ; B 16 0 699 676 ;
-C 89 ; WX 722 ; N Y ; B 15 0 699 676 ;
-C 90 ; WX 667 ; N Z ; B 28 0 634 676 ;
-C 91 ; WX 333 ; N bracketleft ; B 67 -149 301 678 ;
-C 92 ; WX 278 ; N backslash ; B -25 -19 303 691 ;
-C 93 ; WX 333 ; N bracketright ; B 32 -149 266 678 ;
-C 94 ; WX 581 ; N asciicircum ; B 73 311 509 676 ;
-C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
-C 96 ; WX 333 ; N quoteleft ; B 70 356 254 691 ;
-C 97 ; WX 500 ; N a ; B 25 -14 488 473 ;
-C 98 ; WX 556 ; N b ; B 17 -14 521 676 ;
-C 99 ; WX 444 ; N c ; B 25 -14 430 473 ;
-C 100 ; WX 556 ; N d ; B 25 -14 534 676 ;
-C 101 ; WX 444 ; N e ; B 25 -14 426 473 ;
-C 102 ; WX 333 ; N f ; B 14 0 389 691 ; L i fi ; L l fl ;
-C 103 ; WX 500 ; N g ; B 28 -206 483 473 ;
-C 104 ; WX 556 ; N h ; B 16 0 534 676 ;
-C 105 ; WX 278 ; N i ; B 16 0 255 691 ;
-C 106 ; WX 333 ; N j ; B -57 -203 263 691 ;
-C 107 ; WX 556 ; N k ; B 22 0 543 676 ;
-C 108 ; WX 278 ; N l ; B 16 0 255 676 ;
-C 109 ; WX 833 ; N m ; B 16 0 814 473 ;
-C 110 ; WX 556 ; N n ; B 21 0 539 473 ;
-C 111 ; WX 500 ; N o ; B 25 -14 476 473 ;
-C 112 ; WX 556 ; N p ; B 19 -205 524 473 ;
-C 113 ; WX 556 ; N q ; B 34 -205 536 473 ;
-C 114 ; WX 444 ; N r ; B 29 0 434 473 ;
-C 115 ; WX 389 ; N s ; B 25 -14 361 473 ;
-C 116 ; WX 333 ; N t ; B 20 -12 332 630 ;
-C 117 ; WX 556 ; N u ; B 16 -14 537 461 ;
-C 118 ; WX 500 ; N v ; B 21 -14 485 461 ;
-C 119 ; WX 722 ; N w ; B 23 -14 707 461 ;
-C 120 ; WX 500 ; N x ; B 12 0 484 461 ;
-C 121 ; WX 500 ; N y ; B 16 -205 480 461 ;
-C 122 ; WX 444 ; N z ; B 21 0 420 461 ;
-C 123 ; WX 394 ; N braceleft ; B 22 -175 340 698 ;
-C 124 ; WX 220 ; N bar ; B 66 -19 154 691 ;
-C 125 ; WX 394 ; N braceright ; B 54 -175 372 698 ;
-C 126 ; WX 520 ; N asciitilde ; B 29 173 491 333 ;
-C 161 ; WX 333 ; N exclamdown ; B 82 -203 252 501 ;
-C 162 ; WX 500 ; N cent ; B 53 -140 458 588 ;
-C 163 ; WX 500 ; N sterling ; B 21 -14 477 684 ;
-C 164 ; WX 167 ; N fraction ; B -168 -12 329 688 ;
-C 165 ; WX 500 ; N yen ; B -64 0 547 676 ;
-C 166 ; WX 500 ; N florin ; B 0 -155 498 706 ;
-C 167 ; WX 500 ; N section ; B 57 -132 443 691 ;
-C 168 ; WX 500 ; N currency ; B -26 61 526 613 ;
-C 169 ; WX 278 ; N quotesingle ; B 75 404 204 691 ;
-C 170 ; WX 500 ; N quotedblleft ; B 32 356 486 691 ;
-C 171 ; WX 500 ; N guillemotleft ; B 23 36 473 415 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 51 36 305 415 ;
-C 173 ; WX 333 ; N guilsinglright ; B 28 36 282 415 ;
-C 174 ; WX 556 ; N fi ; B 14 0 536 691 ;
-C 175 ; WX 556 ; N fl ; B 14 0 536 691 ;
-C 177 ; WX 500 ; N endash ; B 0 181 500 271 ;
-C 178 ; WX 500 ; N dagger ; B 47 -134 453 691 ;
-C 179 ; WX 500 ; N daggerdbl ; B 45 -132 456 691 ;
-C 180 ; WX 250 ; N periodcentered ; B 41 248 210 417 ;
-C 182 ; WX 540 ; N paragraph ; B 0 -186 519 676 ;
-C 183 ; WX 350 ; N bullet ; B 35 198 315 478 ;
-C 184 ; WX 333 ; N quotesinglbase ; B 79 -180 263 155 ;
-C 185 ; WX 500 ; N quotedblbase ; B 14 -180 468 155 ;
-C 186 ; WX 500 ; N quotedblright ; B 14 356 468 691 ;
-C 187 ; WX 500 ; N guillemotright ; B 27 36 477 415 ;
-C 188 ; WX 1000 ; N ellipsis ; B 82 -13 917 156 ;
-C 189 ; WX 1000 ; N perthousand ; B 7 -29 995 706 ;
-C 191 ; WX 500 ; N questiondown ; B 55 -201 443 501 ;
-C 193 ; WX 333 ; N grave ; B 8 528 246 713 ;
-C 194 ; WX 333 ; N acute ; B 86 528 324 713 ;
-C 195 ; WX 333 ; N circumflex ; B -2 528 335 704 ;
-C 196 ; WX 333 ; N tilde ; B -16 547 349 674 ;
-C 197 ; WX 333 ; N macron ; B 1 565 331 637 ;
-C 198 ; WX 333 ; N breve ; B 15 528 318 691 ;
-C 199 ; WX 333 ; N dotaccent ; B 103 537 230 667 ;
-C 200 ; WX 333 ; N dieresis ; B -2 537 335 667 ;
-C 202 ; WX 333 ; N ring ; B 60 527 273 740 ;
-C 203 ; WX 333 ; N cedilla ; B 68 -218 294 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B -13 528 425 713 ;
-C 206 ; WX 333 ; N ogonek ; B 90 -173 319 44 ;
-C 207 ; WX 333 ; N caron ; B -2 528 335 704 ;
-C 208 ; WX 1000 ; N emdash ; B 0 181 1000 271 ;
-C 225 ; WX 1000 ; N AE ; B 4 0 951 676 ;
-C 227 ; WX 300 ; N ordfeminine ; B -1 397 301 688 ;
-C 232 ; WX 667 ; N Lslash ; B 19 0 638 676 ;
-C 233 ; WX 778 ; N Oslash ; B 35 -74 743 737 ;
-C 234 ; WX 1000 ; N OE ; B 22 -5 981 684 ;
-C 235 ; WX 330 ; N ordmasculine ; B 18 397 312 688 ;
-C 241 ; WX 722 ; N ae ; B 33 -14 693 473 ;
-C 245 ; WX 278 ; N dotlessi ; B 16 0 255 461 ;
-C 248 ; WX 278 ; N lslash ; B -22 0 303 676 ;
-C 249 ; WX 500 ; N oslash ; B 25 -92 476 549 ;
-C 250 ; WX 722 ; N oe ; B 22 -14 696 473 ;
-C 251 ; WX 556 ; N germandbls ; B 19 -12 517 691 ;
-C -1 ; WX 667 ; N Zcaron ; B 28 0 634 914 ;
-C -1 ; WX 444 ; N ccedilla ; B 25 -218 430 473 ;
-C -1 ; WX 500 ; N ydieresis ; B 16 -205 480 667 ;
-C -1 ; WX 500 ; N atilde ; B 25 -14 488 674 ;
-C -1 ; WX 278 ; N icircumflex ; B -36 0 301 704 ;
-C -1 ; WX 300 ; N threesuperior ; B 3 268 297 688 ;
-C -1 ; WX 444 ; N ecircumflex ; B 25 -14 426 704 ;
-C -1 ; WX 556 ; N thorn ; B 19 -205 524 676 ;
-C -1 ; WX 444 ; N egrave ; B 25 -14 426 713 ;
-C -1 ; WX 300 ; N twosuperior ; B 0 275 300 688 ;
-C -1 ; WX 444 ; N eacute ; B 25 -14 426 713 ;
-C -1 ; WX 500 ; N otilde ; B 25 -14 476 674 ;
-C -1 ; WX 722 ; N Aacute ; B 9 0 689 923 ;
-C -1 ; WX 500 ; N ocircumflex ; B 25 -14 476 704 ;
-C -1 ; WX 500 ; N yacute ; B 16 -205 480 713 ;
-C -1 ; WX 556 ; N udieresis ; B 16 -14 537 667 ;
-C -1 ; WX 750 ; N threequarters ; B 23 -12 733 688 ;
-C -1 ; WX 500 ; N acircumflex ; B 25 -14 488 704 ;
-C -1 ; WX 722 ; N Eth ; B 6 0 690 676 ;
-C -1 ; WX 444 ; N edieresis ; B 25 -14 426 667 ;
-C -1 ; WX 556 ; N ugrave ; B 16 -14 537 713 ;
-C -1 ; WX 1000 ; N trademark ; B 24 271 977 676 ;
-C -1 ; WX 500 ; N ograve ; B 25 -14 476 713 ;
-C -1 ; WX 389 ; N scaron ; B 25 -14 363 704 ;
-C -1 ; WX 389 ; N Idieresis ; B 20 0 370 877 ;
-C -1 ; WX 556 ; N uacute ; B 16 -14 537 713 ;
-C -1 ; WX 500 ; N agrave ; B 25 -14 488 713 ;
-C -1 ; WX 556 ; N ntilde ; B 21 0 539 674 ;
-C -1 ; WX 500 ; N aring ; B 25 -14 488 740 ;
-C -1 ; WX 444 ; N zcaron ; B 21 0 420 704 ;
-C -1 ; WX 389 ; N Icircumflex ; B 20 0 370 914 ;
-C -1 ; WX 722 ; N Ntilde ; B 16 -18 701 884 ;
-C -1 ; WX 556 ; N ucircumflex ; B 16 -14 537 704 ;
-C -1 ; WX 667 ; N Ecircumflex ; B 16 0 641 914 ;
-C -1 ; WX 389 ; N Iacute ; B 20 0 370 923 ;
-C -1 ; WX 722 ; N Ccedilla ; B 49 -218 687 691 ;
-C -1 ; WX 778 ; N Odieresis ; B 35 -19 743 877 ;
-C -1 ; WX 556 ; N Scaron ; B 35 -19 513 914 ;
-C -1 ; WX 667 ; N Edieresis ; B 16 0 641 877 ;
-C -1 ; WX 389 ; N Igrave ; B 20 0 370 923 ;
-C -1 ; WX 500 ; N adieresis ; B 25 -14 488 667 ;
-C -1 ; WX 778 ; N Ograve ; B 35 -19 743 923 ;
-C -1 ; WX 667 ; N Egrave ; B 16 0 641 923 ;
-C -1 ; WX 722 ; N Ydieresis ; B 15 0 699 877 ;
-C -1 ; WX 747 ; N registered ; B 26 -19 721 691 ;
-C -1 ; WX 778 ; N Otilde ; B 35 -19 743 884 ;
-C -1 ; WX 750 ; N onequarter ; B 28 -12 743 688 ;
-C -1 ; WX 722 ; N Ugrave ; B 16 -19 701 923 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 16 -19 701 914 ;
-C -1 ; WX 611 ; N Thorn ; B 16 0 600 676 ;
-C -1 ; WX 570 ; N divide ; B 33 -31 537 537 ;
-C -1 ; WX 722 ; N Atilde ; B 9 0 689 884 ;
-C -1 ; WX 722 ; N Uacute ; B 16 -19 701 923 ;
-C -1 ; WX 778 ; N Ocircumflex ; B 35 -19 743 914 ;
-C -1 ; WX 570 ; N logicalnot ; B 33 108 537 399 ;
-C -1 ; WX 722 ; N Aring ; B 9 0 689 935 ;
-C -1 ; WX 278 ; N idieresis ; B -36 0 301 667 ;
-C -1 ; WX 278 ; N iacute ; B 16 0 290 713 ;
-C -1 ; WX 500 ; N aacute ; B 25 -14 488 713 ;
-C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ;
-C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ;
-C -1 ; WX 722 ; N Udieresis ; B 16 -19 701 877 ;
-C -1 ; WX 570 ; N minus ; B 33 209 537 297 ;
-C -1 ; WX 300 ; N onesuperior ; B 28 275 273 688 ;
-C -1 ; WX 667 ; N Eacute ; B 16 0 641 923 ;
-C -1 ; WX 722 ; N Acircumflex ; B 9 0 689 914 ;
-C -1 ; WX 747 ; N copyright ; B 26 -19 721 691 ;
-C -1 ; WX 722 ; N Agrave ; B 9 0 689 923 ;
-C -1 ; WX 500 ; N odieresis ; B 25 -14 476 667 ;
-C -1 ; WX 500 ; N oacute ; B 25 -14 476 713 ;
-C -1 ; WX 400 ; N degree ; B 57 402 343 688 ;
-C -1 ; WX 278 ; N igrave ; B -26 0 255 713 ;
-C -1 ; WX 556 ; N mu ; B 33 -206 536 461 ;
-C -1 ; WX 778 ; N Oacute ; B 35 -19 743 923 ;
-C -1 ; WX 500 ; N eth ; B 25 -14 476 691 ;
-C -1 ; WX 722 ; N Adieresis ; B 9 0 689 877 ;
-C -1 ; WX 722 ; N Yacute ; B 15 0 699 928 ;
-C -1 ; WX 220 ; N brokenbar ; B 66 -19 154 691 ;
-C -1 ; WX 750 ; N onehalf ; B -7 -12 775 688 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 283
-
-KPX A y -74
-KPX A w -90
-KPX A v -100
-KPX A u -50
-KPX A quoteright -74
-KPX A quotedblright 0
-KPX A p -25
-KPX A Y -100
-KPX A W -130
-KPX A V -145
-KPX A U -50
-KPX A T -95
-KPX A Q -45
-KPX A O -45
-KPX A G -55
-KPX A C -55
-
-KPX B period 0
-KPX B comma 0
-KPX B U -10
-KPX B A -30
-
-KPX D period -20
-KPX D comma 0
-KPX D Y -40
-KPX D W -40
-KPX D V -40
-KPX D A -35
-
-KPX F r 0
-KPX F period -110
-KPX F o -25
-KPX F i 0
-KPX F e -25
-KPX F comma -92
-KPX F a -25
-KPX F A -90
-
-KPX G period 0
-KPX G comma 0
-
-KPX J u -15
-KPX J period -20
-KPX J o -15
-KPX J e -15
-KPX J comma 0
-KPX J a -15
-KPX J A -30
-
-KPX K y -45
-KPX K u -15
-KPX K o -25
-KPX K e -25
-KPX K O -30
-
-KPX L y -55
-KPX L quoteright -110
-KPX L quotedblright -20
-KPX L Y -92
-KPX L W -92
-KPX L V -92
-KPX L T -92
-
-KPX N period 0
-KPX N comma 0
-KPX N A -20
-
-KPX O period 0
-KPX O comma 0
-KPX O Y -50
-KPX O X -40
-KPX O W -50
-KPX O V -50
-KPX O T -40
-KPX O A -40
-
-KPX P period -110
-KPX P o -20
-KPX P e -20
-KPX P comma -92
-KPX P a -10
-KPX P A -74
-
-KPX Q period -20
-KPX Q comma 0
-KPX Q U -10
-
-KPX R Y -35
-KPX R W -35
-KPX R V -55
-KPX R U -30
-KPX R T -40
-KPX R O -30
-
-KPX S period 0
-KPX S comma 0
-
-KPX T y -74
-KPX T w -74
-KPX T u -92
-KPX T semicolon -74
-KPX T r -74
-KPX T period -90
-KPX T o -92
-KPX T i -18
-KPX T hyphen -92
-KPX T h 0
-KPX T e -92
-KPX T comma -74
-KPX T colon -74
-KPX T a -92
-KPX T O -18
-KPX T A -90
-
-KPX U period -50
-KPX U comma -50
-KPX U A -60
-
-KPX V u -92
-KPX V semicolon -92
-KPX V period -145
-KPX V o -100
-KPX V i -37
-KPX V hyphen -74
-KPX V e -100
-KPX V comma -129
-KPX V colon -92
-KPX V a -92
-KPX V O -45
-KPX V G -30
-KPX V A -135
-
-KPX W y -60
-KPX W u -50
-KPX W semicolon -55
-KPX W period -92
-KPX W o -75
-KPX W i -18
-KPX W hyphen -37
-KPX W h 0
-KPX W e -65
-KPX W comma -92
-KPX W colon -55
-KPX W a -65
-KPX W O -10
-KPX W A -120
-
-KPX Y u -92
-KPX Y semicolon -92
-KPX Y period -92
-KPX Y o -111
-KPX Y i -37
-KPX Y hyphen -92
-KPX Y e -111
-KPX Y comma -92
-KPX Y colon -92
-KPX Y a -85
-KPX Y O -35
-KPX Y A -110
-
-KPX a y 0
-KPX a w 0
-KPX a v -25
-KPX a t 0
-KPX a p 0
-KPX a g 0
-KPX a b 0
-
-KPX b y 0
-KPX b v -15
-KPX b u -20
-KPX b period -40
-KPX b l 0
-KPX b comma 0
-KPX b b -10
-
-KPX c y 0
-KPX c period 0
-KPX c l 0
-KPX c k 0
-KPX c h 0
-KPX c comma 0
-
-KPX colon space 0
-
-KPX comma space 0
-KPX comma quoteright -55
-KPX comma quotedblright -45
-
-KPX d y 0
-KPX d w -15
-KPX d v 0
-KPX d period 0
-KPX d d 0
-KPX d comma 0
-
-KPX e y 0
-KPX e x 0
-KPX e w 0
-KPX e v -15
-KPX e period 0
-KPX e p 0
-KPX e g 0
-KPX e comma 0
-KPX e b 0
-
-KPX f quoteright 55
-KPX f quotedblright 50
-KPX f period -15
-KPX f o -25
-KPX f l 0
-KPX f i -25
-KPX f f 0
-KPX f e 0
-KPX f dotlessi -35
-KPX f comma -15
-KPX f a 0
-
-KPX g y 0
-KPX g r 0
-KPX g period -15
-KPX g o 0
-KPX g i 0
-KPX g g 0
-KPX g e 0
-KPX g comma 0
-KPX g a 0
-
-KPX h y -15
-
-KPX i v -10
-
-KPX k y -15
-KPX k o -15
-KPX k e -10
-
-KPX l y 0
-KPX l w 0
-
-KPX m y 0
-KPX m u 0
-
-KPX n y 0
-KPX n v -40
-KPX n u 0
-
-KPX o y 0
-KPX o x 0
-KPX o w -10
-KPX o v -10
-KPX o g 0
-
-KPX p y 0
-
-KPX period quoteright -55
-KPX period quotedblright -55
-
-KPX quotedblleft quoteleft 0
-KPX quotedblleft A -10
-
-KPX quotedblright space 0
-
-KPX quoteleft quoteleft -63
-KPX quoteleft A -10
-
-KPX quoteright v -20
-KPX quoteright t 0
-KPX quoteright space -74
-KPX quoteright s -37
-KPX quoteright r -20
-KPX quoteright quoteright -63
-KPX quoteright quotedblright 0
-KPX quoteright l 0
-KPX quoteright d -20
-
-KPX r y 0
-KPX r v -10
-KPX r u 0
-KPX r t 0
-KPX r s 0
-KPX r r 0
-KPX r q -18
-KPX r period -100
-KPX r p -10
-KPX r o -18
-KPX r n -15
-KPX r m 0
-KPX r l 0
-KPX r k 0
-KPX r i 0
-KPX r hyphen -37
-KPX r g -10
-KPX r e -18
-KPX r d 0
-KPX r comma -92
-KPX r c -18
-KPX r a 0
-
-KPX s w 0
-
-KPX space quoteleft 0
-KPX space quotedblleft 0
-KPX space Y -55
-KPX space W -30
-KPX space V -45
-KPX space T -30
-KPX space A -55
-
-KPX v period -70
-KPX v o -10
-KPX v e -10
-KPX v comma -55
-KPX v a -10
-
-KPX w period -70
-KPX w o -10
-KPX w h 0
-KPX w e 0
-KPX w comma -55
-KPX w a 0
-
-KPX x e 0
-
-KPX y period -70
-KPX y o -25
-KPX y e -10
-KPX y comma -55
-KPX y a 0
-
-KPX z o 0
-KPX z e 0
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 188 210 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 188 210 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 188 210 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 188 210 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 180 195 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 188 210 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 208 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 174 210 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 174 210 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 174 210 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 174 210 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 28 210 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 28 210 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 28 210 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 28 210 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 195 210 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 210 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 210 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 210 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 210 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 210 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 210 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 222 210 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 222 210 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 222 210 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 222 210 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 210 215 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 215 210 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 167 210 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 77 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 77 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 77 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 77 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 77 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 77 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 69 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 62 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 62 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 62 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 62 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -34 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -34 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -34 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -34 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 112 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 105 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 105 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 105 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 105 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 56 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Times-BoldItalic.afm b/config/psfonts/Times-BoldItalic.afm
deleted file mode 100644 (file)
index 25ab54e..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.
-Comment Creation Date: Tue Mar 20 13:14:55 1990
-Comment UniqueID 28425
-Comment VMusage 32721 39613
-FontName Times-BoldItalic
-FullName Times Bold Italic
-FamilyName Times
-Weight Bold
-ItalicAngle -15
-IsFixedPitch false
-FontBBox -200 -218 996 921
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.009
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 669
-XHeight 462
-Ascender 699
-Descender -205
-StartCharMetrics 228
-C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 389 ; N exclam ; B 67 -13 370 684 ;
-C 34 ; WX 555 ; N quotedbl ; B 136 398 536 685 ;
-C 35 ; WX 500 ; N numbersign ; B -33 0 533 700 ;
-C 36 ; WX 500 ; N dollar ; B -20 -100 497 733 ;
-C 37 ; WX 833 ; N percent ; B 39 -10 793 692 ;
-C 38 ; WX 778 ; N ampersand ; B 5 -19 699 682 ;
-C 39 ; WX 333 ; N quoteright ; B 98 369 302 685 ;
-C 40 ; WX 333 ; N parenleft ; B 28 -179 344 685 ;
-C 41 ; WX 333 ; N parenright ; B -44 -179 271 685 ;
-C 42 ; WX 500 ; N asterisk ; B 65 249 456 685 ;
-C 43 ; WX 570 ; N plus ; B 33 0 537 506 ;
-C 44 ; WX 250 ; N comma ; B -60 -182 144 134 ;
-C 45 ; WX 333 ; N hyphen ; B 2 166 271 282 ;
-C 46 ; WX 250 ; N period ; B -9 -13 139 135 ;
-C 47 ; WX 278 ; N slash ; B -64 -18 342 685 ;
-C 48 ; WX 500 ; N zero ; B 17 -14 477 683 ;
-C 49 ; WX 500 ; N one ; B 5 0 419 683 ;
-C 50 ; WX 500 ; N two ; B -27 0 446 683 ;
-C 51 ; WX 500 ; N three ; B -15 -13 450 683 ;
-C 52 ; WX 500 ; N four ; B -15 0 503 683 ;
-C 53 ; WX 500 ; N five ; B -11 -13 487 669 ;
-C 54 ; WX 500 ; N six ; B 23 -15 509 679 ;
-C 55 ; WX 500 ; N seven ; B 52 0 525 669 ;
-C 56 ; WX 500 ; N eight ; B 3 -13 476 683 ;
-C 57 ; WX 500 ; N nine ; B -12 -10 475 683 ;
-C 58 ; WX 333 ; N colon ; B 23 -13 264 459 ;
-C 59 ; WX 333 ; N semicolon ; B -25 -183 264 459 ;
-C 60 ; WX 570 ; N less ; B 31 -8 539 514 ;
-C 61 ; WX 570 ; N equal ; B 33 107 537 399 ;
-C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ;
-C 63 ; WX 500 ; N question ; B 79 -13 470 684 ;
-C 64 ; WX 832 ; N at ; B 63 -18 770 685 ;
-C 65 ; WX 667 ; N A ; B -67 0 593 683 ;
-C 66 ; WX 667 ; N B ; B -24 0 624 669 ;
-C 67 ; WX 667 ; N C ; B 32 -18 677 685 ;
-C 68 ; WX 722 ; N D ; B -46 0 685 669 ;
-C 69 ; WX 667 ; N E ; B -27 0 653 669 ;
-C 70 ; WX 667 ; N F ; B -13 0 660 669 ;
-C 71 ; WX 722 ; N G ; B 21 -18 706 685 ;
-C 72 ; WX 778 ; N H ; B -24 0 799 669 ;
-C 73 ; WX 389 ; N I ; B -32 0 406 669 ;
-C 74 ; WX 500 ; N J ; B -46 -99 524 669 ;
-C 75 ; WX 667 ; N K ; B -21 0 702 669 ;
-C 76 ; WX 611 ; N L ; B -22 0 590 669 ;
-C 77 ; WX 889 ; N M ; B -29 -12 917 669 ;
-C 78 ; WX 722 ; N N ; B -27 -15 748 669 ;
-C 79 ; WX 722 ; N O ; B 27 -18 691 685 ;
-C 80 ; WX 611 ; N P ; B -27 0 613 669 ;
-C 81 ; WX 722 ; N Q ; B 27 -208 691 685 ;
-C 82 ; WX 667 ; N R ; B -29 0 623 669 ;
-C 83 ; WX 556 ; N S ; B 2 -18 526 685 ;
-C 84 ; WX 611 ; N T ; B 50 0 650 669 ;
-C 85 ; WX 722 ; N U ; B 67 -18 744 669 ;
-C 86 ; WX 667 ; N V ; B 65 -18 715 669 ;
-C 87 ; WX 889 ; N W ; B 65 -18 940 669 ;
-C 88 ; WX 667 ; N X ; B -24 0 694 669 ;
-C 89 ; WX 611 ; N Y ; B 73 0 659 669 ;
-C 90 ; WX 611 ; N Z ; B -11 0 590 669 ;
-C 91 ; WX 333 ; N bracketleft ; B -37 -159 362 674 ;
-C 92 ; WX 278 ; N backslash ; B -1 -18 279 685 ;
-C 93 ; WX 333 ; N bracketright ; B -56 -157 343 674 ;
-C 94 ; WX 570 ; N asciicircum ; B 67 304 503 669 ;
-C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
-C 96 ; WX 333 ; N quoteleft ; B 128 369 332 685 ;
-C 97 ; WX 500 ; N a ; B -21 -14 455 462 ;
-C 98 ; WX 500 ; N b ; B -14 -13 444 699 ;
-C 99 ; WX 444 ; N c ; B -5 -13 392 462 ;
-C 100 ; WX 500 ; N d ; B -21 -13 517 699 ;
-C 101 ; WX 444 ; N e ; B 5 -13 398 462 ;
-C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ;
-C 103 ; WX 500 ; N g ; B -52 -203 478 462 ;
-C 104 ; WX 556 ; N h ; B -13 -9 498 699 ;
-C 105 ; WX 278 ; N i ; B 2 -9 263 684 ;
-C 106 ; WX 278 ; N j ; B -189 -207 279 684 ;
-C 107 ; WX 500 ; N k ; B -23 -8 483 699 ;
-C 108 ; WX 278 ; N l ; B 2 -9 290 699 ;
-C 109 ; WX 778 ; N m ; B -14 -9 722 462 ;
-C 110 ; WX 556 ; N n ; B -6 -9 493 462 ;
-C 111 ; WX 500 ; N o ; B -3 -13 441 462 ;
-C 112 ; WX 500 ; N p ; B -120 -205 446 462 ;
-C 113 ; WX 500 ; N q ; B 1 -205 471 462 ;
-C 114 ; WX 389 ; N r ; B -21 0 389 462 ;
-C 115 ; WX 389 ; N s ; B -19 -13 333 462 ;
-C 116 ; WX 278 ; N t ; B -11 -9 281 594 ;
-C 117 ; WX 556 ; N u ; B 15 -9 492 462 ;
-C 118 ; WX 444 ; N v ; B 16 -13 401 462 ;
-C 119 ; WX 667 ; N w ; B 16 -13 614 462 ;
-C 120 ; WX 500 ; N x ; B -46 -13 469 462 ;
-C 121 ; WX 444 ; N y ; B -94 -205 392 462 ;
-C 122 ; WX 389 ; N z ; B -43 -78 368 449 ;
-C 123 ; WX 348 ; N braceleft ; B 5 -187 436 686 ;
-C 124 ; WX 220 ; N bar ; B 66 -18 154 685 ;
-C 125 ; WX 348 ; N braceright ; B -129 -187 302 686 ;
-C 126 ; WX 570 ; N asciitilde ; B 54 173 516 333 ;
-C 161 ; WX 389 ; N exclamdown ; B 19 -205 322 492 ;
-C 162 ; WX 500 ; N cent ; B 42 -143 439 576 ;
-C 163 ; WX 500 ; N sterling ; B -32 -12 510 683 ;
-C 164 ; WX 167 ; N fraction ; B -169 -14 324 683 ;
-C 165 ; WX 500 ; N yen ; B 33 0 628 669 ;
-C 166 ; WX 500 ; N florin ; B -87 -156 537 707 ;
-C 167 ; WX 500 ; N section ; B 36 -143 459 685 ;
-C 168 ; WX 500 ; N currency ; B -26 34 526 586 ;
-C 169 ; WX 278 ; N quotesingle ; B 128 398 268 685 ;
-C 170 ; WX 500 ; N quotedblleft ; B 53 369 513 685 ;
-C 171 ; WX 500 ; N guillemotleft ; B 12 32 468 415 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 32 32 303 415 ;
-C 173 ; WX 333 ; N guilsinglright ; B 10 32 281 415 ;
-C 174 ; WX 556 ; N fi ; B -188 -205 514 703 ;
-C 175 ; WX 556 ; N fl ; B -186 -205 553 704 ;
-C 177 ; WX 500 ; N endash ; B -40 178 477 269 ;
-C 178 ; WX 500 ; N dagger ; B 91 -145 494 685 ;
-C 179 ; WX 500 ; N daggerdbl ; B 10 -139 493 685 ;
-C 180 ; WX 250 ; N periodcentered ; B 51 257 199 405 ;
-C 182 ; WX 500 ; N paragraph ; B -57 -193 562 669 ;
-C 183 ; WX 350 ; N bullet ; B 0 175 350 525 ;
-C 184 ; WX 333 ; N quotesinglbase ; B -5 -182 199 134 ;
-C 185 ; WX 500 ; N quotedblbase ; B -57 -182 403 134 ;
-C 186 ; WX 500 ; N quotedblright ; B 53 369 513 685 ;
-C 187 ; WX 500 ; N guillemotright ; B 12 32 468 415 ;
-C 188 ; WX 1000 ; N ellipsis ; B 40 -13 852 135 ;
-C 189 ; WX 1000 ; N perthousand ; B 7 -29 996 706 ;
-C 191 ; WX 500 ; N questiondown ; B 30 -205 421 492 ;
-C 193 ; WX 333 ; N grave ; B 85 516 297 697 ;
-C 194 ; WX 333 ; N acute ; B 139 516 379 697 ;
-C 195 ; WX 333 ; N circumflex ; B 40 516 367 690 ;
-C 196 ; WX 333 ; N tilde ; B 48 536 407 655 ;
-C 197 ; WX 333 ; N macron ; B 51 553 393 623 ;
-C 198 ; WX 333 ; N breve ; B 71 516 387 678 ;
-C 199 ; WX 333 ; N dotaccent ; B 163 525 293 655 ;
-C 200 ; WX 333 ; N dieresis ; B 55 525 397 655 ;
-C 202 ; WX 333 ; N ring ; B 127 516 340 729 ;
-C 203 ; WX 333 ; N cedilla ; B -80 -218 156 5 ;
-C 205 ; WX 333 ; N hungarumlaut ; B 69 516 498 697 ;
-C 206 ; WX 333 ; N ogonek ; B -40 -173 189 44 ;
-C 207 ; WX 333 ; N caron ; B 79 516 411 690 ;
-C 208 ; WX 1000 ; N emdash ; B -40 178 977 269 ;
-C 225 ; WX 944 ; N AE ; B -64 0 918 669 ;
-C 227 ; WX 266 ; N ordfeminine ; B 16 399 330 685 ;
-C 232 ; WX 611 ; N Lslash ; B -22 0 590 669 ;
-C 233 ; WX 722 ; N Oslash ; B 27 -125 691 764 ;
-C 234 ; WX 944 ; N OE ; B 23 -8 946 677 ;
-C 235 ; WX 300 ; N ordmasculine ; B 56 400 347 685 ;
-C 241 ; WX 722 ; N ae ; B -5 -13 673 462 ;
-C 245 ; WX 278 ; N dotlessi ; B 2 -9 238 462 ;
-C 248 ; WX 278 ; N lslash ; B -13 -9 301 699 ;
-C 249 ; WX 500 ; N oslash ; B -3 -119 441 560 ;
-C 250 ; WX 722 ; N oe ; B 6 -13 674 462 ;
-C 251 ; WX 500 ; N germandbls ; B -200 -200 473 705 ;
-C -1 ; WX 611 ; N Zcaron ; B -11 0 590 897 ;
-C -1 ; WX 444 ; N ccedilla ; B -24 -218 392 462 ;
-C -1 ; WX 444 ; N ydieresis ; B -94 -205 438 655 ;
-C -1 ; WX 500 ; N atilde ; B -21 -14 491 655 ;
-C -1 ; WX 278 ; N icircumflex ; B -2 -9 325 690 ;
-C -1 ; WX 300 ; N threesuperior ; B 17 265 321 683 ;
-C -1 ; WX 444 ; N ecircumflex ; B 5 -13 423 690 ;
-C -1 ; WX 500 ; N thorn ; B -120 -205 446 699 ;
-C -1 ; WX 444 ; N egrave ; B 5 -13 398 697 ;
-C -1 ; WX 300 ; N twosuperior ; B 2 274 313 683 ;
-C -1 ; WX 444 ; N eacute ; B 5 -13 435 697 ;
-C -1 ; WX 500 ; N otilde ; B -3 -13 491 655 ;
-C -1 ; WX 667 ; N Aacute ; B -67 0 593 904 ;
-C -1 ; WX 500 ; N ocircumflex ; B -3 -13 451 690 ;
-C -1 ; WX 444 ; N yacute ; B -94 -205 435 697 ;
-C -1 ; WX 556 ; N udieresis ; B 15 -9 494 655 ;
-C -1 ; WX 750 ; N threequarters ; B 7 -14 726 683 ;
-C -1 ; WX 500 ; N acircumflex ; B -21 -14 455 690 ;
-C -1 ; WX 722 ; N Eth ; B -31 0 700 669 ;
-C -1 ; WX 444 ; N edieresis ; B 5 -13 443 655 ;
-C -1 ; WX 556 ; N ugrave ; B 15 -9 492 697 ;
-C -1 ; WX 1000 ; N trademark ; B 32 263 968 669 ;
-C -1 ; WX 500 ; N ograve ; B -3 -13 441 697 ;
-C -1 ; WX 389 ; N scaron ; B -19 -13 439 690 ;
-C -1 ; WX 389 ; N Idieresis ; B -32 0 445 862 ;
-C -1 ; WX 556 ; N uacute ; B 15 -9 492 697 ;
-C -1 ; WX 500 ; N agrave ; B -21 -14 455 697 ;
-C -1 ; WX 556 ; N ntilde ; B -6 -9 504 655 ;
-C -1 ; WX 500 ; N aring ; B -21 -14 455 729 ;
-C -1 ; WX 389 ; N zcaron ; B -43 -78 424 690 ;
-C -1 ; WX 389 ; N Icircumflex ; B -32 0 420 897 ;
-C -1 ; WX 722 ; N Ntilde ; B -27 -15 748 862 ;
-C -1 ; WX 556 ; N ucircumflex ; B 15 -9 492 690 ;
-C -1 ; WX 667 ; N Ecircumflex ; B -27 0 653 897 ;
-C -1 ; WX 389 ; N Iacute ; B -32 0 412 904 ;
-C -1 ; WX 667 ; N Ccedilla ; B 32 -218 677 685 ;
-C -1 ; WX 722 ; N Odieresis ; B 27 -18 691 862 ;
-C -1 ; WX 556 ; N Scaron ; B 2 -18 526 897 ;
-C -1 ; WX 667 ; N Edieresis ; B -27 0 653 862 ;
-C -1 ; WX 389 ; N Igrave ; B -32 0 406 904 ;
-C -1 ; WX 500 ; N adieresis ; B -21 -14 471 655 ;
-C -1 ; WX 722 ; N Ograve ; B 27 -18 691 904 ;
-C -1 ; WX 667 ; N Egrave ; B -27 0 653 904 ;
-C -1 ; WX 611 ; N Ydieresis ; B 73 0 659 862 ;
-C -1 ; WX 747 ; N registered ; B 30 -18 718 685 ;
-C -1 ; WX 722 ; N Otilde ; B 27 -18 691 862 ;
-C -1 ; WX 750 ; N onequarter ; B 7 -14 721 683 ;
-C -1 ; WX 722 ; N Ugrave ; B 67 -18 744 904 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 67 -18 744 897 ;
-C -1 ; WX 611 ; N Thorn ; B -27 0 573 669 ;
-C -1 ; WX 570 ; N divide ; B 33 -29 537 535 ;
-C -1 ; WX 667 ; N Atilde ; B -67 0 593 862 ;
-C -1 ; WX 722 ; N Uacute ; B 67 -18 744 904 ;
-C -1 ; WX 722 ; N Ocircumflex ; B 27 -18 691 897 ;
-C -1 ; WX 606 ; N logicalnot ; B 51 108 555 399 ;
-C -1 ; WX 667 ; N Aring ; B -67 0 593 921 ;
-C -1 ; WX 278 ; N idieresis ; B 2 -9 360 655 ;
-C -1 ; WX 278 ; N iacute ; B 2 -9 352 697 ;
-C -1 ; WX 500 ; N aacute ; B -21 -14 463 697 ;
-C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ;
-C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ;
-C -1 ; WX 722 ; N Udieresis ; B 67 -18 744 862 ;
-C -1 ; WX 606 ; N minus ; B 51 209 555 297 ;
-C -1 ; WX 300 ; N onesuperior ; B 30 274 301 683 ;
-C -1 ; WX 667 ; N Eacute ; B -27 0 653 904 ;
-C -1 ; WX 667 ; N Acircumflex ; B -67 0 593 897 ;
-C -1 ; WX 747 ; N copyright ; B 30 -18 718 685 ;
-C -1 ; WX 667 ; N Agrave ; B -67 0 593 904 ;
-C -1 ; WX 500 ; N odieresis ; B -3 -13 466 655 ;
-C -1 ; WX 500 ; N oacute ; B -3 -13 463 697 ;
-C -1 ; WX 400 ; N degree ; B 83 397 369 683 ;
-C -1 ; WX 278 ; N igrave ; B 2 -9 260 697 ;
-C -1 ; WX 576 ; N mu ; B -60 -207 516 449 ;
-C -1 ; WX 722 ; N Oacute ; B 27 -18 691 904 ;
-C -1 ; WX 500 ; N eth ; B -3 -13 454 699 ;
-C -1 ; WX 667 ; N Adieresis ; B -67 0 593 862 ;
-C -1 ; WX 611 ; N Yacute ; B 73 0 659 904 ;
-C -1 ; WX 220 ; N brokenbar ; B 66 -18 154 685 ;
-C -1 ; WX 750 ; N onehalf ; B -9 -14 723 683 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 283
-
-KPX A y -74
-KPX A w -74
-KPX A v -74
-KPX A u -30
-KPX A quoteright -74
-KPX A quotedblright 0
-KPX A p 0
-KPX A Y -70
-KPX A W -100
-KPX A V -95
-KPX A U -50
-KPX A T -55
-KPX A Q -55
-KPX A O -50
-KPX A G -60
-KPX A C -65
-
-KPX B period 0
-KPX B comma 0
-KPX B U -10
-KPX B A -25
-
-KPX D period 0
-KPX D comma 0
-KPX D Y -50
-KPX D W -40
-KPX D V -50
-KPX D A -25
-
-KPX F r -50
-KPX F period -129
-KPX F o -70
-KPX F i -40
-KPX F e -100
-KPX F comma -129
-KPX F a -95
-KPX F A -100
-
-KPX G period 0
-KPX G comma 0
-
-KPX J u -40
-KPX J period -10
-KPX J o -40
-KPX J e -40
-KPX J comma -10
-KPX J a -40
-KPX J A -25
-
-KPX K y -20
-KPX K u -20
-KPX K o -25
-KPX K e -25
-KPX K O -30
-
-KPX L y -37
-KPX L quoteright -55
-KPX L quotedblright 0
-KPX L Y -37
-KPX L W -37
-KPX L V -37
-KPX L T -18
-
-KPX N period 0
-KPX N comma 0
-KPX N A -30
-
-KPX O period 0
-KPX O comma 0
-KPX O Y -50
-KPX O X -40
-KPX O W -50
-KPX O V -50
-KPX O T -40
-KPX O A -40
-
-KPX P period -129
-KPX P o -55
-KPX P e -50
-KPX P comma -129
-KPX P a -40
-KPX P A -85
-
-KPX Q period 0
-KPX Q comma 0
-KPX Q U -10
-
-KPX R Y -18
-KPX R W -18
-KPX R V -18
-KPX R U -40
-KPX R T -30
-KPX R O -40
-
-KPX S period 0
-KPX S comma 0
-
-KPX T y -37
-KPX T w -37
-KPX T u -37
-KPX T semicolon -74
-KPX T r -37
-KPX T period -92
-KPX T o -95
-KPX T i -37
-KPX T hyphen -92
-KPX T h 0
-KPX T e -92
-KPX T comma -92
-KPX T colon -74
-KPX T a -92
-KPX T O -18
-KPX T A -55
-
-KPX U period 0
-KPX U comma 0
-KPX U A -45
-
-KPX V u -55
-KPX V semicolon -74
-KPX V period -129
-KPX V o -111
-KPX V i -55
-KPX V hyphen -70
-KPX V e -111
-KPX V comma -129
-KPX V colon -74
-KPX V a -111
-KPX V O -30
-KPX V G -10
-KPX V A -85
-
-KPX W y -55
-KPX W u -55
-KPX W semicolon -55
-KPX W period -74
-KPX W o -80
-KPX W i -37
-KPX W hyphen -50
-KPX W h 0
-KPX W e -90
-KPX W comma -74
-KPX W colon -55
-KPX W a -85
-KPX W O -15
-KPX W A -74
-
-KPX Y u -92
-KPX Y semicolon -92
-KPX Y period -74
-KPX Y o -111
-KPX Y i -55
-KPX Y hyphen -92
-KPX Y e -111
-KPX Y comma -92
-KPX Y colon -92
-KPX Y a -92
-KPX Y O -25
-KPX Y A -74
-
-KPX a y 0
-KPX a w 0
-KPX a v 0
-KPX a t 0
-KPX a p 0
-KPX a g 0
-KPX a b 0
-
-KPX b y 0
-KPX b v 0
-KPX b u -20
-KPX b period -40
-KPX b l 0
-KPX b comma 0
-KPX b b -10
-
-KPX c y 0
-KPX c period 0
-KPX c l 0
-KPX c k -10
-KPX c h -10
-KPX c comma 0
-
-KPX colon space 0
-
-KPX comma space 0
-KPX comma quoteright -95
-KPX comma quotedblright -95
-
-KPX d y 0
-KPX d w 0
-KPX d v 0
-KPX d period 0
-KPX d d 0
-KPX d comma 0
-
-KPX e y 0
-KPX e x 0
-KPX e w 0
-KPX e v 0
-KPX e period 0
-KPX e p 0
-KPX e g 0
-KPX e comma 0
-KPX e b -10
-
-KPX f quoteright 55
-KPX f quotedblright 0
-KPX f period -10
-KPX f o -10
-KPX f l 0
-KPX f i 0
-KPX f f -18
-KPX f e -10
-KPX f dotlessi -30
-KPX f comma -10
-KPX f a 0
-
-KPX g y 0
-KPX g r 0
-KPX g period 0
-KPX g o 0
-KPX g i 0
-KPX g g 0
-KPX g e 0
-KPX g comma 0
-KPX g a 0
-
-KPX h y 0
-
-KPX i v 0
-
-KPX k y 0
-KPX k o -10
-KPX k e -30
-
-KPX l y 0
-KPX l w 0
-
-KPX m y 0
-KPX m u 0
-
-KPX n y 0
-KPX n v -40
-KPX n u 0
-
-KPX o y -10
-KPX o x -10
-KPX o w -25
-KPX o v -15
-KPX o g 0
-
-KPX p y 0
-
-KPX period quoteright -95
-KPX period quotedblright -95
-
-KPX quotedblleft quoteleft 0
-KPX quotedblleft A 0
-
-KPX quotedblright space 0
-
-KPX quoteleft quoteleft -74
-KPX quoteleft A 0
-
-KPX quoteright v -15
-KPX quoteright t -37
-KPX quoteright space -74
-KPX quoteright s -74
-KPX quoteright r -15
-KPX quoteright quoteright -74
-KPX quoteright quotedblright 0
-KPX quoteright l 0
-KPX quoteright d -15
-
-KPX r y 0
-KPX r v 0
-KPX r u 0
-KPX r t 0
-KPX r s 0
-KPX r r 0
-KPX r q 0
-KPX r period -65
-KPX r p 0
-KPX r o 0
-KPX r n 0
-KPX r m 0
-KPX r l 0
-KPX r k 0
-KPX r i 0
-KPX r hyphen 0
-KPX r g 0
-KPX r e 0
-KPX r d 0
-KPX r comma -65
-KPX r c 0
-KPX r a 0
-
-KPX s w 0
-
-KPX space quoteleft 0
-KPX space quotedblleft 0
-KPX space Y -70
-KPX space W -70
-KPX space V -70
-KPX space T 0
-KPX space A -37
-
-KPX v period -37
-KPX v o -15
-KPX v e -15
-KPX v comma -37
-KPX v a 0
-
-KPX w period -37
-KPX w o -15
-KPX w h 0
-KPX w e -10
-KPX w comma -37
-KPX w a -10
-
-KPX x e -10
-
-KPX y period -37
-KPX y o 0
-KPX y e 0
-KPX y comma -37
-KPX y a 0
-
-KPX z o 0
-KPX z e 0
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 172 207 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 187 207 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 167 207 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 172 207 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 157 192 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 167 207 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 167 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 172 207 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 187 207 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 187 207 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 172 207 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 33 207 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 53 207 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 48 207 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 33 207 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 210 207 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 200 207 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 230 207 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 215 207 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 200 207 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 215 207 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 207 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 210 207 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 230 207 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 230 207 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 200 207 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 154 207 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 169 207 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 207 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 84 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 84 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 74 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 74 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 84 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 46 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 46 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -42 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -37 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -37 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 97 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 69 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 74 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 112 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 97 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 102 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 56 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 41 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 13 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Times-Italic.afm b/config/psfonts/Times-Italic.afm
deleted file mode 100644 (file)
index 6d7a003..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.
-Comment Creation Date: Tue Mar 20 13:14:56 1990
-Comment UniqueID 28427
-Comment VMusage 32912 39804
-FontName Times-Italic
-FullName Times Italic
-FamilyName Times
-Weight Medium
-ItalicAngle -15.5
-IsFixedPitch false
-FontBBox -169 -217 1010 883
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.007
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 653
-XHeight 441
-Ascender 683
-Descender -205
-StartCharMetrics 228
-C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ;
-C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ;
-C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ;
-C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ;
-C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ;
-C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ;
-C 39 ; WX 333 ; N quoteright ; B 151 436 290 666 ;
-C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ;
-C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ;
-C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ;
-C 43 ; WX 675 ; N plus ; B 86 0 590 506 ;
-C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ;
-C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ;
-C 46 ; WX 250 ; N period ; B 27 -11 138 100 ;
-C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ;
-C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ;
-C 49 ; WX 500 ; N one ; B 49 0 409 676 ;
-C 50 ; WX 500 ; N two ; B 12 0 452 676 ;
-C 51 ; WX 500 ; N three ; B 15 -7 465 676 ;
-C 52 ; WX 500 ; N four ; B 1 0 479 676 ;
-C 53 ; WX 500 ; N five ; B 15 -7 491 666 ;
-C 54 ; WX 500 ; N six ; B 30 -7 521 686 ;
-C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ;
-C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ;
-C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ;
-C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ;
-C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ;
-C 60 ; WX 675 ; N less ; B 84 -8 592 514 ;
-C 61 ; WX 675 ; N equal ; B 86 120 590 386 ;
-C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ;
-C 63 ; WX 500 ; N question ; B 132 -12 472 664 ;
-C 64 ; WX 920 ; N at ; B 118 -18 806 666 ;
-C 65 ; WX 611 ; N A ; B -51 0 564 668 ;
-C 66 ; WX 611 ; N B ; B -8 0 588 653 ;
-C 67 ; WX 667 ; N C ; B 66 -18 689 666 ;
-C 68 ; WX 722 ; N D ; B -8 0 700 653 ;
-C 69 ; WX 611 ; N E ; B -1 0 634 653 ;
-C 70 ; WX 611 ; N F ; B 8 0 645 653 ;
-C 71 ; WX 722 ; N G ; B 52 -18 722 666 ;
-C 72 ; WX 722 ; N H ; B -8 0 767 653 ;
-C 73 ; WX 333 ; N I ; B -8 0 384 653 ;
-C 74 ; WX 444 ; N J ; B -6 -18 491 653 ;
-C 75 ; WX 667 ; N K ; B 7 0 722 653 ;
-C 76 ; WX 556 ; N L ; B -8 0 559 653 ;
-C 77 ; WX 833 ; N M ; B -18 0 873 653 ;
-C 78 ; WX 667 ; N N ; B -20 -15 727 653 ;
-C 79 ; WX 722 ; N O ; B 60 -18 699 666 ;
-C 80 ; WX 611 ; N P ; B 0 0 605 653 ;
-C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ;
-C 82 ; WX 611 ; N R ; B -13 0 588 653 ;
-C 83 ; WX 500 ; N S ; B 17 -18 508 667 ;
-C 84 ; WX 556 ; N T ; B 59 0 633 653 ;
-C 85 ; WX 722 ; N U ; B 102 -18 765 653 ;
-C 86 ; WX 611 ; N V ; B 76 -18 688 653 ;
-C 87 ; WX 833 ; N W ; B 71 -18 906 653 ;
-C 88 ; WX 611 ; N X ; B -29 0 655 653 ;
-C 89 ; WX 556 ; N Y ; B 78 0 633 653 ;
-C 90 ; WX 556 ; N Z ; B -6 0 606 653 ;
-C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ;
-C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ;
-C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ;
-C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ;
-C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
-C 96 ; WX 333 ; N quoteleft ; B 171 436 310 666 ;
-C 97 ; WX 500 ; N a ; B 17 -11 476 441 ;
-C 98 ; WX 500 ; N b ; B 23 -11 473 683 ;
-C 99 ; WX 444 ; N c ; B 30 -11 425 441 ;
-C 100 ; WX 500 ; N d ; B 15 -13 527 683 ;
-C 101 ; WX 444 ; N e ; B 31 -11 412 441 ;
-C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ;
-C 103 ; WX 500 ; N g ; B 8 -206 472 441 ;
-C 104 ; WX 500 ; N h ; B 19 -9 478 683 ;
-C 105 ; WX 278 ; N i ; B 49 -11 264 654 ;
-C 106 ; WX 278 ; N j ; B -124 -207 276 654 ;
-C 107 ; WX 444 ; N k ; B 14 -11 461 683 ;
-C 108 ; WX 278 ; N l ; B 41 -11 279 683 ;
-C 109 ; WX 722 ; N m ; B 12 -9 704 441 ;
-C 110 ; WX 500 ; N n ; B 14 -9 474 441 ;
-C 111 ; WX 500 ; N o ; B 27 -11 468 441 ;
-C 112 ; WX 500 ; N p ; B -75 -205 469 441 ;
-C 113 ; WX 500 ; N q ; B 25 -209 483 441 ;
-C 114 ; WX 389 ; N r ; B 45 0 412 441 ;
-C 115 ; WX 389 ; N s ; B 16 -13 366 442 ;
-C 116 ; WX 278 ; N t ; B 37 -11 296 546 ;
-C 117 ; WX 500 ; N u ; B 42 -11 475 441 ;
-C 118 ; WX 444 ; N v ; B 21 -18 426 441 ;
-C 119 ; WX 667 ; N w ; B 16 -18 648 441 ;
-C 120 ; WX 444 ; N x ; B -27 -11 447 441 ;
-C 121 ; WX 444 ; N y ; B -24 -206 426 441 ;
-C 122 ; WX 389 ; N z ; B -2 -81 380 428 ;
-C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ;
-C 124 ; WX 275 ; N bar ; B 105 -18 171 666 ;
-C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ;
-C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ;
-C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ;
-C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ;
-C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ;
-C 164 ; WX 167 ; N fraction ; B -169 -10 337 676 ;
-C 165 ; WX 500 ; N yen ; B 27 0 603 653 ;
-C 166 ; WX 500 ; N florin ; B 25 -182 507 682 ;
-C 167 ; WX 500 ; N section ; B 53 -162 461 666 ;
-C 168 ; WX 500 ; N currency ; B -22 53 522 597 ;
-C 169 ; WX 214 ; N quotesingle ; B 132 421 241 666 ;
-C 170 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ;
-C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ;
-C 173 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ;
-C 174 ; WX 500 ; N fi ; B -141 -207 481 681 ;
-C 175 ; WX 500 ; N fl ; B -141 -204 518 682 ;
-C 177 ; WX 500 ; N endash ; B -6 197 505 243 ;
-C 178 ; WX 500 ; N dagger ; B 101 -159 488 666 ;
-C 179 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ;
-C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ;
-C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ;
-C 183 ; WX 350 ; N bullet ; B 40 191 310 461 ;
-C 184 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ;
-C 185 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ;
-C 186 ; WX 556 ; N quotedblright ; B 151 436 499 666 ;
-C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ;
-C 188 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ;
-C 189 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ;
-C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ;
-C 193 ; WX 333 ; N grave ; B 121 492 311 664 ;
-C 194 ; WX 333 ; N acute ; B 180 494 403 664 ;
-C 195 ; WX 333 ; N circumflex ; B 91 492 385 661 ;
-C 196 ; WX 333 ; N tilde ; B 100 517 427 624 ;
-C 197 ; WX 333 ; N macron ; B 99 532 411 583 ;
-C 198 ; WX 333 ; N breve ; B 117 492 418 650 ;
-C 199 ; WX 333 ; N dotaccent ; B 207 508 305 606 ;
-C 200 ; WX 333 ; N dieresis ; B 107 508 405 606 ;
-C 202 ; WX 333 ; N ring ; B 155 492 355 691 ;
-C 203 ; WX 333 ; N cedilla ; B -30 -217 182 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ;
-C 206 ; WX 333 ; N ogonek ; B -20 -169 200 40 ;
-C 207 ; WX 333 ; N caron ; B 121 492 426 661 ;
-C 208 ; WX 889 ; N emdash ; B -6 197 894 243 ;
-C 225 ; WX 889 ; N AE ; B -27 0 911 653 ;
-C 227 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ;
-C 232 ; WX 556 ; N Lslash ; B -8 0 559 653 ;
-C 233 ; WX 722 ; N Oslash ; B 60 -105 699 722 ;
-C 234 ; WX 944 ; N OE ; B 49 -8 964 666 ;
-C 235 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ;
-C 241 ; WX 667 ; N ae ; B 23 -11 640 441 ;
-C 245 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ;
-C 248 ; WX 278 ; N lslash ; B 37 -11 307 683 ;
-C 249 ; WX 500 ; N oslash ; B 28 -135 469 554 ;
-C 250 ; WX 667 ; N oe ; B 20 -12 646 441 ;
-C 251 ; WX 500 ; N germandbls ; B -168 -207 493 679 ;
-C -1 ; WX 556 ; N Zcaron ; B -6 0 606 873 ;
-C -1 ; WX 444 ; N ccedilla ; B 26 -217 425 441 ;
-C -1 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ;
-C -1 ; WX 500 ; N atilde ; B 17 -11 511 624 ;
-C -1 ; WX 278 ; N icircumflex ; B 34 -11 328 661 ;
-C -1 ; WX 300 ; N threesuperior ; B 43 268 339 676 ;
-C -1 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ;
-C -1 ; WX 500 ; N thorn ; B -75 -205 469 683 ;
-C -1 ; WX 444 ; N egrave ; B 31 -11 412 664 ;
-C -1 ; WX 300 ; N twosuperior ; B 33 271 324 676 ;
-C -1 ; WX 444 ; N eacute ; B 31 -11 459 664 ;
-C -1 ; WX 500 ; N otilde ; B 27 -11 496 624 ;
-C -1 ; WX 611 ; N Aacute ; B -51 0 564 876 ;
-C -1 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ;
-C -1 ; WX 444 ; N yacute ; B -24 -206 459 664 ;
-C -1 ; WX 500 ; N udieresis ; B 42 -11 479 606 ;
-C -1 ; WX 750 ; N threequarters ; B 23 -10 736 676 ;
-C -1 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ;
-C -1 ; WX 722 ; N Eth ; B -8 0 700 653 ;
-C -1 ; WX 444 ; N edieresis ; B 31 -11 451 606 ;
-C -1 ; WX 500 ; N ugrave ; B 42 -11 475 664 ;
-C -1 ; WX 980 ; N trademark ; B 30 247 957 653 ;
-C -1 ; WX 500 ; N ograve ; B 27 -11 468 664 ;
-C -1 ; WX 389 ; N scaron ; B 16 -13 454 661 ;
-C -1 ; WX 333 ; N Idieresis ; B -8 0 435 818 ;
-C -1 ; WX 500 ; N uacute ; B 42 -11 477 664 ;
-C -1 ; WX 500 ; N agrave ; B 17 -11 476 664 ;
-C -1 ; WX 500 ; N ntilde ; B 14 -9 476 624 ;
-C -1 ; WX 500 ; N aring ; B 17 -11 476 691 ;
-C -1 ; WX 389 ; N zcaron ; B -2 -81 434 661 ;
-C -1 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ;
-C -1 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ;
-C -1 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ;
-C -1 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ;
-C -1 ; WX 333 ; N Iacute ; B -8 0 413 876 ;
-C -1 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ;
-C -1 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ;
-C -1 ; WX 500 ; N Scaron ; B 17 -18 520 873 ;
-C -1 ; WX 611 ; N Edieresis ; B -1 0 634 818 ;
-C -1 ; WX 333 ; N Igrave ; B -8 0 384 876 ;
-C -1 ; WX 500 ; N adieresis ; B 17 -11 489 606 ;
-C -1 ; WX 722 ; N Ograve ; B 60 -18 699 876 ;
-C -1 ; WX 611 ; N Egrave ; B -1 0 634 876 ;
-C -1 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ;
-C -1 ; WX 760 ; N registered ; B 41 -18 719 666 ;
-C -1 ; WX 722 ; N Otilde ; B 60 -18 699 836 ;
-C -1 ; WX 750 ; N onequarter ; B 33 -10 736 676 ;
-C -1 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ;
-C -1 ; WX 611 ; N Thorn ; B 0 0 569 653 ;
-C -1 ; WX 675 ; N divide ; B 86 -11 590 517 ;
-C -1 ; WX 611 ; N Atilde ; B -51 0 566 836 ;
-C -1 ; WX 722 ; N Uacute ; B 102 -18 765 876 ;
-C -1 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ;
-C -1 ; WX 675 ; N logicalnot ; B 86 108 590 386 ;
-C -1 ; WX 611 ; N Aring ; B -51 0 564 883 ;
-C -1 ; WX 278 ; N idieresis ; B 49 -11 353 606 ;
-C -1 ; WX 278 ; N iacute ; B 49 -11 356 664 ;
-C -1 ; WX 500 ; N aacute ; B 17 -11 487 664 ;
-C -1 ; WX 675 ; N plusminus ; B 86 0 590 506 ;
-C -1 ; WX 675 ; N multiply ; B 93 8 582 497 ;
-C -1 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ;
-C -1 ; WX 675 ; N minus ; B 86 220 590 286 ;
-C -1 ; WX 300 ; N onesuperior ; B 43 271 284 676 ;
-C -1 ; WX 611 ; N Eacute ; B -1 0 634 876 ;
-C -1 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ;
-C -1 ; WX 760 ; N copyright ; B 41 -18 719 666 ;
-C -1 ; WX 611 ; N Agrave ; B -51 0 564 876 ;
-C -1 ; WX 500 ; N odieresis ; B 27 -11 489 606 ;
-C -1 ; WX 500 ; N oacute ; B 27 -11 487 664 ;
-C -1 ; WX 400 ; N degree ; B 101 390 387 676 ;
-C -1 ; WX 278 ; N igrave ; B 49 -11 284 664 ;
-C -1 ; WX 500 ; N mu ; B -30 -209 497 428 ;
-C -1 ; WX 722 ; N Oacute ; B 60 -18 699 876 ;
-C -1 ; WX 500 ; N eth ; B 27 -11 482 683 ;
-C -1 ; WX 611 ; N Adieresis ; B -51 0 564 818 ;
-C -1 ; WX 556 ; N Yacute ; B 78 0 633 876 ;
-C -1 ; WX 275 ; N brokenbar ; B 105 -18 171 666 ;
-C -1 ; WX 750 ; N onehalf ; B 34 -10 749 676 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 283
-
-KPX A y -55
-KPX A w -55
-KPX A v -55
-KPX A u -20
-KPX A quoteright -37
-KPX A quotedblright 0
-KPX A p 0
-KPX A Y -55
-KPX A W -95
-KPX A V -105
-KPX A U -50
-KPX A T -37
-KPX A Q -40
-KPX A O -40
-KPX A G -35
-KPX A C -30
-
-KPX B period 0
-KPX B comma 0
-KPX B U -10
-KPX B A -25
-
-KPX D period 0
-KPX D comma 0
-KPX D Y -40
-KPX D W -40
-KPX D V -40
-KPX D A -35
-
-KPX F r -55
-KPX F period -135
-KPX F o -105
-KPX F i -45
-KPX F e -75
-KPX F comma -135
-KPX F a -75
-KPX F A -115
-
-KPX G period 0
-KPX G comma 0
-
-KPX J u -35
-KPX J period -25
-KPX J o -25
-KPX J e -25
-KPX J comma -25
-KPX J a -35
-KPX J A -40
-
-KPX K y -40
-KPX K u -40
-KPX K o -40
-KPX K e -35
-KPX K O -50
-
-KPX L y -30
-KPX L quoteright -37
-KPX L quotedblright 0
-KPX L Y -20
-KPX L W -55
-KPX L V -55
-KPX L T -20
-
-KPX N period 0
-KPX N comma 0
-KPX N A -27
-
-KPX O period 0
-KPX O comma 0
-KPX O Y -50
-KPX O X -40
-KPX O W -50
-KPX O V -50
-KPX O T -40
-KPX O A -55
-
-KPX P period -135
-KPX P o -80
-KPX P e -80
-KPX P comma -135
-KPX P a -80
-KPX P A -90
-
-KPX Q period 0
-KPX Q comma 0
-KPX Q U -10
-
-KPX R Y -18
-KPX R W -18
-KPX R V -18
-KPX R U -40
-KPX R T 0
-KPX R O -40
-
-KPX S period 0
-KPX S comma 0
-
-KPX T y -74
-KPX T w -74
-KPX T u -55
-KPX T semicolon -65
-KPX T r -55
-KPX T period -74
-KPX T o -92
-KPX T i -55
-KPX T hyphen -74
-KPX T h 0
-KPX T e -92
-KPX T comma -74
-KPX T colon -55
-KPX T a -92
-KPX T O -18
-KPX T A -50
-
-KPX U period -25
-KPX U comma -25
-KPX U A -40
-
-KPX V u -74
-KPX V semicolon -74
-KPX V period -129
-KPX V o -111
-KPX V i -74
-KPX V hyphen -55
-KPX V e -111
-KPX V comma -129
-KPX V colon -65
-KPX V a -111
-KPX V O -30
-KPX V G 0
-KPX V A -60
-
-KPX W y -70
-KPX W u -55
-KPX W semicolon -65
-KPX W period -92
-KPX W o -92
-KPX W i -55
-KPX W hyphen -37
-KPX W h 0
-KPX W e -92
-KPX W comma -92
-KPX W colon -65
-KPX W a -92
-KPX W O -25
-KPX W A -60
-
-KPX Y u -92
-KPX Y semicolon -65
-KPX Y period -92
-KPX Y o -92
-KPX Y i -74
-KPX Y hyphen -74
-KPX Y e -92
-KPX Y comma -92
-KPX Y colon -65
-KPX Y a -92
-KPX Y O -15
-KPX Y A -50
-
-KPX a y 0
-KPX a w 0
-KPX a v 0
-KPX a t 0
-KPX a p 0
-KPX a g -10
-KPX a b 0
-
-KPX b y 0
-KPX b v 0
-KPX b u -20
-KPX b period -40
-KPX b l 0
-KPX b comma 0
-KPX b b 0
-
-KPX c y 0
-KPX c period 0
-KPX c l 0
-KPX c k -20
-KPX c h -15
-KPX c comma 0
-
-KPX colon space 0
-
-KPX comma space 0
-KPX comma quoteright -140
-KPX comma quotedblright -140
-
-KPX d y 0
-KPX d w 0
-KPX d v 0
-KPX d period 0
-KPX d d 0
-KPX d comma 0
-
-KPX e y -30
-KPX e x -20
-KPX e w -15
-KPX e v -15
-KPX e period -15
-KPX e p 0
-KPX e g -40
-KPX e comma -10
-KPX e b 0
-
-KPX f quoteright 92
-KPX f quotedblright 0
-KPX f period -15
-KPX f o 0
-KPX f l 0
-KPX f i -20
-KPX f f -18
-KPX f e 0
-KPX f dotlessi -60
-KPX f comma -10
-KPX f a 0
-
-KPX g y 0
-KPX g r 0
-KPX g period -15
-KPX g o 0
-KPX g i 0
-KPX g g -10
-KPX g e -10
-KPX g comma -10
-KPX g a 0
-
-KPX h y 0
-
-KPX i v 0
-
-KPX k y -10
-KPX k o -10
-KPX k e -10
-
-KPX l y 0
-KPX l w 0
-
-KPX m y 0
-KPX m u 0
-
-KPX n y 0
-KPX n v -40
-KPX n u 0
-
-KPX o y 0
-KPX o x 0
-KPX o w 0
-KPX o v -10
-KPX o g -10
-
-KPX p y 0
-
-KPX period quoteright -140
-KPX period quotedblright -140
-
-KPX quotedblleft quoteleft 0
-KPX quotedblleft A 0
-
-KPX quotedblright space 0
-
-KPX quoteleft quoteleft -111
-KPX quoteleft A 0
-
-KPX quoteright v -10
-KPX quoteright t -30
-KPX quoteright space -111
-KPX quoteright s -40
-KPX quoteright r -25
-KPX quoteright quoteright -111
-KPX quoteright quotedblright 0
-KPX quoteright l 0
-KPX quoteright d -25
-
-KPX r y 0
-KPX r v 0
-KPX r u 0
-KPX r t 0
-KPX r s -10
-KPX r r 0
-KPX r q -37
-KPX r period -111
-KPX r p 0
-KPX r o -45
-KPX r n 0
-KPX r m 0
-KPX r l 0
-KPX r k 0
-KPX r i 0
-KPX r hyphen -20
-KPX r g -37
-KPX r e -37
-KPX r d -37
-KPX r comma -111
-KPX r c -37
-KPX r a -15
-
-KPX s w 0
-
-KPX space quoteleft 0
-KPX space quotedblleft 0
-KPX space Y -75
-KPX space W -40
-KPX space V -35
-KPX space T -18
-KPX space A -18
-
-KPX v period -74
-KPX v o 0
-KPX v e 0
-KPX v comma -74
-KPX v a 0
-
-KPX w period -74
-KPX w o 0
-KPX w h 0
-KPX w e 0
-KPX w comma -74
-KPX w a 0
-
-KPX x e 0
-
-KPX y period -55
-KPX y o 0
-KPX y e 0
-KPX y comma -55
-KPX y a 0
-
-KPX z o 0
-KPX z e 0
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 139 212 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 144 212 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 139 212 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 149 212 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 129 192 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 139 212 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 167 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 149 212 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 169 212 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 159 212 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 149 212 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 10 212 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 40 212 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 30 212 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 10 212 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 177 212 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 195 212 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 230 212 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 230 212 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 205 212 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 215 212 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 94 212 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 212 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 215 212 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 225 212 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 215 212 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 132 212 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 142 212 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 112 212 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 84 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 84 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 84 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 84 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 84 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 46 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 56 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -47 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -57 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -52 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 49 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 74 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 69 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 74 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 74 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 74 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 84 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 56 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 36 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 8 0 ;
-EndComposites
-EndFontMetrics
diff --git a/config/psfonts/Times-Roman.afm b/config/psfonts/Times-Roman.afm
deleted file mode 100644 (file)
index e5092b5..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-StartFontMetrics 2.0
-Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.
-Comment Creation Date: Tue Mar 20 12:15:44 1990
-Comment UniqueID 28416
-Comment VMusage 30487 37379
-FontName Times-Roman
-FullName Times Roman
-FamilyName Times
-Weight Roman
-ItalicAngle 0
-IsFixedPitch false
-FontBBox -168 -218 1000 898
-UnderlinePosition -100
-UnderlineThickness 50
-Version 001.007
-Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated.  All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries.
-EncodingScheme AdobeStandardEncoding
-CapHeight 662
-XHeight 450
-Ascender 683
-Descender -217
-StartCharMetrics 228
-C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
-C 33 ; WX 333 ; N exclam ; B 130 -9 238 676 ;
-C 34 ; WX 408 ; N quotedbl ; B 77 431 331 676 ;
-C 35 ; WX 500 ; N numbersign ; B 5 0 496 662 ;
-C 36 ; WX 500 ; N dollar ; B 44 -87 457 727 ;
-C 37 ; WX 833 ; N percent ; B 61 -13 772 676 ;
-C 38 ; WX 778 ; N ampersand ; B 42 -13 750 676 ;
-C 39 ; WX 333 ; N quoteright ; B 79 433 218 676 ;
-C 40 ; WX 333 ; N parenleft ; B 48 -177 304 676 ;
-C 41 ; WX 333 ; N parenright ; B 29 -177 285 676 ;
-C 42 ; WX 500 ; N asterisk ; B 69 265 432 676 ;
-C 43 ; WX 564 ; N plus ; B 30 0 534 506 ;
-C 44 ; WX 250 ; N comma ; B 56 -141 195 102 ;
-C 45 ; WX 333 ; N hyphen ; B 39 194 285 257 ;
-C 46 ; WX 250 ; N period ; B 70 -11 181 100 ;
-C 47 ; WX 278 ; N slash ; B -9 -14 287 676 ;
-C 48 ; WX 500 ; N zero ; B 24 -14 476 676 ;
-C 49 ; WX 500 ; N one ; B 111 0 394 676 ;
-C 50 ; WX 500 ; N two ; B 30 0 475 676 ;
-C 51 ; WX 500 ; N three ; B 43 -14 431 676 ;
-C 52 ; WX 500 ; N four ; B 12 0 472 676 ;
-C 53 ; WX 500 ; N five ; B 32 -14 438 688 ;
-C 54 ; WX 500 ; N six ; B 34 -14 468 684 ;
-C 55 ; WX 500 ; N seven ; B 20 -8 449 662 ;
-C 56 ; WX 500 ; N eight ; B 56 -14 445 676 ;
-C 57 ; WX 500 ; N nine ; B 30 -22 459 676 ;
-C 58 ; WX 278 ; N colon ; B 81 -11 192 459 ;
-C 59 ; WX 278 ; N semicolon ; B 80 -141 219 459 ;
-C 60 ; WX 564 ; N less ; B 28 -8 536 514 ;
-C 61 ; WX 564 ; N equal ; B 30 120 534 386 ;
-C 62 ; WX 564 ; N greater ; B 28 -8 536 514 ;
-C 63 ; WX 444 ; N question ; B 68 -8 414 676 ;
-C 64 ; WX 921 ; N at ; B 116 -14 809 676 ;
-C 65 ; WX 722 ; N A ; B 15 0 706 674 ;
-C 66 ; WX 667 ; N B ; B 17 0 593 662 ;
-C 67 ; WX 667 ; N C ; B 28 -14 633 676 ;
-C 68 ; WX 722 ; N D ; B 16 0 685 662 ;
-C 69 ; WX 611 ; N E ; B 12 0 597 662 ;
-C 70 ; WX 556 ; N F ; B 12 0 546 662 ;
-C 71 ; WX 722 ; N G ; B 32 -14 709 676 ;
-C 72 ; WX 722 ; N H ; B 19 0 702 662 ;
-C 73 ; WX 333 ; N I ; B 18 0 315 662 ;
-C 74 ; WX 389 ; N J ; B 10 -14 370 662 ;
-C 75 ; WX 722 ; N K ; B 34 0 723 662 ;
-C 76 ; WX 611 ; N L ; B 12 0 598 662 ;
-C 77 ; WX 889 ; N M ; B 12 0 863 662 ;
-C 78 ; WX 722 ; N N ; B 12 -11 707 662 ;
-C 79 ; WX 722 ; N O ; B 34 -14 688 676 ;
-C 80 ; WX 556 ; N P ; B 16 0 542 662 ;
-C 81 ; WX 722 ; N Q ; B 34 -178 701 676 ;
-C 82 ; WX 667 ; N R ; B 17 0 659 662 ;
-C 83 ; WX 556 ; N S ; B 42 -14 491 676 ;
-C 84 ; WX 611 ; N T ; B 17 0 593 662 ;
-C 85 ; WX 722 ; N U ; B 14 -14 705 662 ;
-C 86 ; WX 722 ; N V ; B 16 -11 697 662 ;
-C 87 ; WX 944 ; N W ; B 5 -11 932 662 ;
-C 88 ; WX 722 ; N X ; B 10 0 704 662 ;
-C 89 ; WX 722 ; N Y ; B 22 0 703 662 ;
-C 90 ; WX 611 ; N Z ; B 9 0 597 662 ;
-C 91 ; WX 333 ; N bracketleft ; B 88 -156 299 662 ;
-C 92 ; WX 278 ; N backslash ; B -9 -14 287 676 ;
-C 93 ; WX 333 ; N bracketright ; B 34 -156 245 662 ;
-C 94 ; WX 469 ; N asciicircum ; B 24 297 446 662 ;
-C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
-C 96 ; WX 333 ; N quoteleft ; B 115 433 254 676 ;
-C 97 ; WX 444 ; N a ; B 37 -10 442 460 ;
-C 98 ; WX 500 ; N b ; B 3 -10 468 683 ;
-C 99 ; WX 444 ; N c ; B 25 -10 412 460 ;
-C 100 ; WX 500 ; N d ; B 27 -10 491 683 ;
-C 101 ; WX 444 ; N e ; B 25 -10 424 460 ;
-C 102 ; WX 333 ; N f ; B 20 0 383 683 ; L i fi ; L l fl ;
-C 103 ; WX 500 ; N g ; B 28 -218 470 460 ;
-C 104 ; WX 500 ; N h ; B 9 0 487 683 ;
-C 105 ; WX 278 ; N i ; B 16 0 253 683 ;
-C 106 ; WX 278 ; N j ; B -70 -218 194 683 ;
-C 107 ; WX 500 ; N k ; B 7 0 505 683 ;
-C 108 ; WX 278 ; N l ; B 19 0 257 683 ;
-C 109 ; WX 778 ; N m ; B 16 0 775 460 ;
-C 110 ; WX 500 ; N n ; B 16 0 485 460 ;
-C 111 ; WX 500 ; N o ; B 29 -10 470 460 ;
-C 112 ; WX 500 ; N p ; B 5 -217 470 460 ;
-C 113 ; WX 500 ; N q ; B 24 -217 488 460 ;
-C 114 ; WX 333 ; N r ; B 5 0 335 460 ;
-C 115 ; WX 389 ; N s ; B 51 -10 348 460 ;
-C 116 ; WX 278 ; N t ; B 13 -10 279 579 ;
-C 117 ; WX 500 ; N u ; B 9 -10 479 450 ;
-C 118 ; WX 500 ; N v ; B 19 -14 477 450 ;
-C 119 ; WX 722 ; N w ; B 21 -14 694 450 ;
-C 120 ; WX 500 ; N x ; B 17 0 479 450 ;
-C 121 ; WX 500 ; N y ; B 14 -218 475 450 ;
-C 122 ; WX 444 ; N z ; B 27 0 418 450 ;
-C 123 ; WX 480 ; N braceleft ; B 100 -181 350 680 ;
-C 124 ; WX 200 ; N bar ; B 67 -14 133 676 ;
-C 125 ; WX 480 ; N braceright ; B 130 -181 380 680 ;
-C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ;
-C 161 ; WX 333 ; N exclamdown ; B 97 -218 205 467 ;
-C 162 ; WX 500 ; N cent ; B 53 -138 448 579 ;
-C 163 ; WX 500 ; N sterling ; B 12 -8 490 676 ;
-C 164 ; WX 167 ; N fraction ; B -168 -14 331 676 ;
-C 165 ; WX 500 ; N yen ; B -53 0 512 662 ;
-C 166 ; WX 500 ; N florin ; B 7 -189 490 676 ;
-C 167 ; WX 500 ; N section ; B 70 -148 426 676 ;
-C 168 ; WX 500 ; N currency ; B -22 58 522 602 ;
-C 169 ; WX 180 ; N quotesingle ; B 48 431 133 676 ;
-C 170 ; WX 444 ; N quotedblleft ; B 43 433 414 676 ;
-C 171 ; WX 500 ; N guillemotleft ; B 42 33 456 416 ;
-C 172 ; WX 333 ; N guilsinglleft ; B 63 33 285 416 ;
-C 173 ; WX 333 ; N guilsinglright ; B 48 33 270 416 ;
-C 174 ; WX 556 ; N fi ; B 31 0 521 683 ;
-C 175 ; WX 556 ; N fl ; B 32 0 521 683 ;
-C 177 ; WX 500 ; N endash ; B 0 201 500 250 ;
-C 178 ; WX 500 ; N dagger ; B 59 -149 442 676 ;
-C 179 ; WX 500 ; N daggerdbl ; B 58 -153 442 676 ;
-C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ;
-C 182 ; WX 453 ; N paragraph ; B -22 -154 450 662 ;
-C 183 ; WX 350 ; N bullet ; B 40 196 310 466 ;
-C 184 ; WX 333 ; N quotesinglbase ; B 79 -141 218 102 ;
-C 185 ; WX 444 ; N quotedblbase ; B 45 -141 416 102 ;
-C 186 ; WX 444 ; N quotedblright ; B 30 433 401 676 ;
-C 187 ; WX 500 ; N guillemotright ; B 44 33 458 416 ;
-C 188 ; WX 1000 ; N ellipsis ; B 111 -11 888 100 ;
-C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 706 ;
-C 191 ; WX 444 ; N questiondown ; B 30 -218 376 466 ;
-C 193 ; WX 333 ; N grave ; B 19 507 242 678 ;
-C 194 ; WX 333 ; N acute ; B 93 507 317 678 ;
-C 195 ; WX 333 ; N circumflex ; B 11 507 322 674 ;
-C 196 ; WX 333 ; N tilde ; B 1 532 331 638 ;
-C 197 ; WX 333 ; N macron ; B 11 547 322 601 ;
-C 198 ; WX 333 ; N breve ; B 26 507 307 664 ;
-C 199 ; WX 333 ; N dotaccent ; B 118 523 216 623 ;
-C 200 ; WX 333 ; N dieresis ; B 18 523 315 623 ;
-C 202 ; WX 333 ; N ring ; B 67 512 266 711 ;
-C 203 ; WX 333 ; N cedilla ; B 52 -215 261 0 ;
-C 205 ; WX 333 ; N hungarumlaut ; B -3 507 377 678 ;
-C 206 ; WX 333 ; N ogonek ; B 64 -165 249 0 ;
-C 207 ; WX 333 ; N caron ; B 11 507 322 674 ;
-C 208 ; WX 1000 ; N emdash ; B 0 201 1000 250 ;
-C 225 ; WX 889 ; N AE ; B 0 0 863 662 ;
-C 227 ; WX 276 ; N ordfeminine ; B 4 394 270 676 ;
-C 232 ; WX 611 ; N Lslash ; B 12 0 598 662 ;
-C 233 ; WX 722 ; N Oslash ; B 34 -80 688 734 ;
-C 234 ; WX 889 ; N OE ; B 30 -6 885 668 ;
-C 235 ; WX 310 ; N ordmasculine ; B 6 394 304 676 ;
-C 241 ; WX 667 ; N ae ; B 38 -10 632 460 ;
-C 245 ; WX 278 ; N dotlessi ; B 16 0 253 460 ;
-C 248 ; WX 278 ; N lslash ; B 19 0 259 683 ;
-C 249 ; WX 500 ; N oslash ; B 29 -112 470 551 ;
-C 250 ; WX 722 ; N oe ; B 30 -10 690 460 ;
-C 251 ; WX 500 ; N germandbls ; B 12 -9 468 683 ;
-C -1 ; WX 611 ; N Zcaron ; B 9 0 597 886 ;
-C -1 ; WX 444 ; N ccedilla ; B 25 -215 412 460 ;
-C -1 ; WX 500 ; N ydieresis ; B 14 -218 475 623 ;
-C -1 ; WX 444 ; N atilde ; B 37 -10 442 638 ;
-C -1 ; WX 278 ; N icircumflex ; B -16 0 295 674 ;
-C -1 ; WX 300 ; N threesuperior ; B 15 262 291 676 ;
-C -1 ; WX 444 ; N ecircumflex ; B 25 -10 424 674 ;
-C -1 ; WX 500 ; N thorn ; B 5 -217 470 683 ;
-C -1 ; WX 444 ; N egrave ; B 25 -10 424 678 ;
-C -1 ; WX 300 ; N twosuperior ; B 1 270 296 676 ;
-C -1 ; WX 444 ; N eacute ; B 25 -10 424 678 ;
-C -1 ; WX 500 ; N otilde ; B 29 -10 470 638 ;
-C -1 ; WX 722 ; N Aacute ; B 15 0 706 890 ;
-C -1 ; WX 500 ; N ocircumflex ; B 29 -10 470 674 ;
-C -1 ; WX 500 ; N yacute ; B 14 -218 475 678 ;
-C -1 ; WX 500 ; N udieresis ; B 9 -10 479 623 ;
-C -1 ; WX 750 ; N threequarters ; B 15 -14 718 676 ;
-C -1 ; WX 444 ; N acircumflex ; B 37 -10 442 674 ;
-C -1 ; WX 722 ; N Eth ; B 16 0 685 662 ;
-C -1 ; WX 444 ; N edieresis ; B 25 -10 424 623 ;
-C -1 ; WX 500 ; N ugrave ; B 9 -10 479 678 ;
-C -1 ; WX 980 ; N trademark ; B 30 256 957 662 ;
-C -1 ; WX 500 ; N ograve ; B 29 -10 470 678 ;
-C -1 ; WX 389 ; N scaron ; B 39 -10 350 674 ;
-C -1 ; WX 333 ; N Idieresis ; B 18 0 315 835 ;
-C -1 ; WX 500 ; N uacute ; B 9 -10 479 678 ;
-C -1 ; WX 444 ; N agrave ; B 37 -10 442 678 ;
-C -1 ; WX 500 ; N ntilde ; B 16 0 485 638 ;
-C -1 ; WX 444 ; N aring ; B 37 -10 442 711 ;
-C -1 ; WX 444 ; N zcaron ; B 27 0 418 674 ;
-C -1 ; WX 333 ; N Icircumflex ; B 11 0 322 886 ;
-C -1 ; WX 722 ; N Ntilde ; B 12 -11 707 850 ;
-C -1 ; WX 500 ; N ucircumflex ; B 9 -10 479 674 ;
-C -1 ; WX 611 ; N Ecircumflex ; B 12 0 597 886 ;
-C -1 ; WX 333 ; N Iacute ; B 18 0 317 890 ;
-C -1 ; WX 667 ; N Ccedilla ; B 28 -215 633 676 ;
-C -1 ; WX 722 ; N Odieresis ; B 34 -14 688 835 ;
-C -1 ; WX 556 ; N Scaron ; B 42 -14 491 886 ;
-C -1 ; WX 611 ; N Edieresis ; B 12 0 597 835 ;
-C -1 ; WX 333 ; N Igrave ; B 18 0 315 890 ;
-C -1 ; WX 444 ; N adieresis ; B 37 -10 442 623 ;
-C -1 ; WX 722 ; N Ograve ; B 34 -14 688 890 ;
-C -1 ; WX 611 ; N Egrave ; B 12 0 597 890 ;
-C -1 ; WX 722 ; N Ydieresis ; B 22 0 703 835 ;
-C -1 ; WX 760 ; N registered ; B 38 -14 722 676 ;
-C -1 ; WX 722 ; N Otilde ; B 34 -14 688 850 ;
-C -1 ; WX 750 ; N onequarter ; B 37 -14 718 676 ;
-C -1 ; WX 722 ; N Ugrave ; B 14 -14 705 890 ;
-C -1 ; WX 722 ; N Ucircumflex ; B 14 -14 705 886 ;
-C -1 ; WX 556 ; N Thorn ; B 16 0 542 662 ;
-C -1 ; WX 564 ; N divide ; B 30 -10 534 516 ;
-C -1 ; WX 722 ; N Atilde ; B 15 0 706 850 ;
-C -1 ; WX 722 ; N Uacute ; B 14 -14 705 890 ;
-C -1 ; WX 722 ; N Ocircumflex ; B 34 -14 688 886 ;
-C -1 ; WX 564 ; N logicalnot ; B 30 108 534 386 ;
-C -1 ; WX 722 ; N Aring ; B 15 0 706 898 ;
-C -1 ; WX 278 ; N idieresis ; B -9 0 288 623 ;
-C -1 ; WX 278 ; N iacute ; B 16 0 290 678 ;
-C -1 ; WX 444 ; N aacute ; B 37 -10 442 678 ;
-C -1 ; WX 564 ; N plusminus ; B 30 0 534 506 ;
-C -1 ; WX 564 ; N multiply ; B 38 8 527 497 ;
-C -1 ; WX 722 ; N Udieresis ; B 14 -14 705 835 ;
-C -1 ; WX 564 ; N minus ; B 30 220 534 286 ;
-C -1 ; WX 300 ; N onesuperior ; B 57 270 248 676 ;
-C -1 ; WX 611 ; N Eacute ; B 12 0 597 890 ;
-C -1 ; WX 722 ; N Acircumflex ; B 15 0 706 886 ;
-C -1 ; WX 760 ; N copyright ; B 38 -14 722 676 ;
-C -1 ; WX 722 ; N Agrave ; B 15 0 706 890 ;
-C -1 ; WX 500 ; N odieresis ; B 29 -10 470 623 ;
-C -1 ; WX 500 ; N oacute ; B 29 -10 470 678 ;
-C -1 ; WX 400 ; N degree ; B 57 390 343 676 ;
-C -1 ; WX 278 ; N igrave ; B -8 0 253 678 ;
-C -1 ; WX 500 ; N mu ; B 36 -218 512 450 ;
-C -1 ; WX 722 ; N Oacute ; B 34 -14 688 890 ;
-C -1 ; WX 500 ; N eth ; B 29 -10 471 686 ;
-C -1 ; WX 722 ; N Adieresis ; B 15 0 706 835 ;
-C -1 ; WX 722 ; N Yacute ; B 22 0 703 890 ;
-C -1 ; WX 200 ; N brokenbar ; B 67 -14 133 676 ;
-C -1 ; WX 750 ; N onehalf ; B 31 -14 746 676 ;
-EndCharMetrics
-StartKernData
-StartKernPairs 283
-
-KPX A y -92
-KPX A w -92
-KPX A v -74
-KPX A u 0
-KPX A quoteright -111
-KPX A quotedblright 0
-KPX A p 0
-KPX A Y -105
-KPX A W -90
-KPX A V -135
-KPX A U -55
-KPX A T -111
-KPX A Q -55
-KPX A O -55
-KPX A G -40
-KPX A C -40
-
-KPX B period 0
-KPX B comma 0
-KPX B U -10
-KPX B A -35
-
-KPX D period 0
-KPX D comma 0
-KPX D Y -55
-KPX D W -30
-KPX D V -40
-KPX D A -40
-
-KPX F r 0
-KPX F period -80
-KPX F o -15
-KPX F i 0
-KPX F e 0
-KPX F comma -80
-KPX F a -15
-KPX F A -74
-
-KPX G period 0
-KPX G comma 0
-
-KPX J u 0
-KPX J period 0
-KPX J o 0
-KPX J e 0
-KPX J comma 0
-KPX J a 0
-KPX J A -60
-
-KPX K y -25
-KPX K u -15
-KPX K o -35
-KPX K e -25
-KPX K O -30
-
-KPX L y -55
-KPX L quoteright -92
-KPX L quotedblright 0
-KPX L Y -100
-KPX L W -74
-KPX L V -100
-KPX L T -92
-
-KPX N period 0
-KPX N comma 0
-KPX N A -35
-
-KPX O period 0
-KPX O comma 0
-KPX O Y -50
-KPX O X -40
-KPX O W -35
-KPX O V -50
-KPX O T -40
-KPX O A -35
-
-KPX P period -111
-KPX P o 0
-KPX P e 0
-KPX P comma -111
-KPX P a -15
-KPX P A -92
-
-KPX Q period 0
-KPX Q comma 0
-KPX Q U -10
-
-KPX R Y -65
-KPX R W -55
-KPX R V -80
-KPX R U -40
-KPX R T -60
-KPX R O -40
-
-KPX S period 0
-KPX S comma 0
-
-KPX T y -80
-KPX T w -80
-KPX T u -45
-KPX T semicolon -55
-KPX T r -35
-KPX T period -74
-KPX T o -80
-KPX T i -35
-KPX T hyphen -92
-KPX T h 0
-KPX T e -70
-KPX T comma -74
-KPX T colon -50
-KPX T a -80
-KPX T O -18
-KPX T A -93
-
-KPX U period 0
-KPX U comma 0
-KPX U A -40
-
-KPX V u -75
-KPX V semicolon -74
-KPX V period -129
-KPX V o -129
-KPX V i -60
-KPX V hyphen -100
-KPX V e -111
-KPX V comma -129
-KPX V colon -74
-KPX V a -111
-KPX V O -40
-KPX V G -15
-KPX V A -135
-
-KPX W y -73
-KPX W u -50
-KPX W semicolon -37
-KPX W period -92
-KPX W o -80
-KPX W i -40
-KPX W hyphen -65
-KPX W h 0
-KPX W e -80
-KPX W comma -92
-KPX W colon -37
-KPX W a -80
-KPX W O -10
-KPX W A -120
-
-KPX Y u -111
-KPX Y semicolon -92
-KPX Y period -129
-KPX Y o -110
-KPX Y i -55
-KPX Y hyphen -111
-KPX Y e -100
-KPX Y comma -129
-KPX Y colon -92
-KPX Y a -100
-KPX Y O -30
-KPX Y A -120
-
-KPX a y 0
-KPX a w -15
-KPX a v -20
-KPX a t 0
-KPX a p 0
-KPX a g 0
-KPX a b 0
-
-KPX b y 0
-KPX b v -15
-KPX b u -20
-KPX b period -40
-KPX b l 0
-KPX b comma 0
-KPX b b 0
-
-KPX c y -15
-KPX c period 0
-KPX c l 0
-KPX c k 0
-KPX c h 0
-KPX c comma 0
-
-KPX colon space 0
-
-KPX comma space 0
-KPX comma quoteright -70
-KPX comma quotedblright -70
-
-KPX d y 0
-KPX d w 0
-KPX d v 0
-KPX d period 0
-KPX d d 0
-KPX d comma 0
-
-KPX e y -15
-KPX e x -15
-KPX e w -25
-KPX e v -25
-KPX e period 0
-KPX e p 0
-KPX e g -15
-KPX e comma 0
-KPX e b 0
-
-KPX f quoteright 55
-KPX f quotedblright 0
-KPX f period 0
-KPX f o 0
-KPX f l 0
-KPX f i -20
-KPX f f -25
-KPX f e 0
-KPX f dotlessi -50
-KPX f comma 0
-KPX f a -10
-
-KPX g y 0
-KPX g r 0
-KPX g period 0
-KPX g o 0
-KPX g i 0
-KPX g g 0
-KPX g e 0
-KPX g comma 0
-KPX g a -5
-
-KPX h y -5
-
-KPX i v -25
-
-KPX k y -15
-KPX k o -10
-KPX k e -10
-
-KPX l y 0
-KPX l w -10
-
-KPX m y 0
-KPX m u 0
-
-KPX n y -15
-KPX n v -40
-KPX n u 0
-
-KPX o y -10
-KPX o x 0
-KPX o w -25
-KPX o v -15
-KPX o g 0
-
-KPX p y -10
-
-KPX period quoteright -70
-KPX period quotedblright -70
-
-KPX quotedblleft quoteleft 0
-KPX quotedblleft A -80
-
-KPX quotedblright space 0
-
-KPX quoteleft quoteleft -74
-KPX quoteleft A -80
-
-KPX quoteright v -50
-KPX quoteright t -18
-KPX quoteright space -74
-KPX quoteright s -55
-KPX quoteright r -50
-KPX quoteright quoteright -74
-KPX quoteright quotedblright 0
-KPX quoteright l -10
-KPX quoteright d -50
-
-KPX r y 0
-KPX r v 0
-KPX r u 0
-KPX r t 0
-KPX r s 0
-KPX r r 0
-KPX r q 0
-KPX r period -55
-KPX r p 0
-KPX r o 0
-KPX r n 0
-KPX r m 0
-KPX r l 0
-KPX r k 0
-KPX r i 0
-KPX r hyphen -20
-KPX r g -18
-KPX r e 0
-KPX r d 0
-KPX r comma -40
-KPX r c 0
-KPX r a 0
-
-KPX s w 0
-
-KPX space quoteleft 0
-KPX space quotedblleft 0
-KPX space Y -90
-KPX space W -30
-KPX space V -50
-KPX space T -18
-KPX space A -55
-
-KPX v period -65
-KPX v o -20
-KPX v e -15
-KPX v comma -65
-KPX v a -25
-
-KPX w period -65
-KPX w o -10
-KPX w h 0
-KPX w e 0
-KPX w comma -65
-KPX w a -10
-
-KPX x e -15
-
-KPX y period -65
-KPX y o 0
-KPX y e 0
-KPX y comma -65
-KPX y a 0
-
-KPX z o 0
-KPX z e 0
-EndKernPairs
-EndKernData
-StartComposites 58
-CC Aacute 2 ; PCC A 0 0 ; PCC acute 195 212 ;
-CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 195 212 ;
-CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 195 212 ;
-CC Agrave 2 ; PCC A 0 0 ; PCC grave 195 212 ;
-CC Aring 2 ; PCC A 0 0 ; PCC ring 185 187 ;
-CC Atilde 2 ; PCC A 0 0 ; PCC tilde 195 212 ;
-CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 167 0 ;
-CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 212 ;
-CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 212 ;
-CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 212 ;
-CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 212 ;
-CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 212 ;
-CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 0 212 ;
-CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 0 212 ;
-CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 212 ;
-CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 195 212 ;
-CC Oacute 2 ; PCC O 0 0 ; PCC acute 195 212 ;
-CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 195 212 ;
-CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 195 212 ;
-CC Ograve 2 ; PCC O 0 0 ; PCC grave 195 212 ;
-CC Otilde 2 ; PCC O 0 0 ; PCC tilde 195 212 ;
-CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 212 ;
-CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 212 ;
-CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 195 212 ;
-CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 195 212 ;
-CC Ugrave 2 ; PCC U 0 0 ; PCC grave 195 212 ;
-CC Yacute 2 ; PCC Y 0 0 ; PCC acute 195 212 ;
-CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 195 212 ;
-CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 212 ;
-CC aacute 2 ; PCC a 0 0 ; PCC acute 56 0 ;
-CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 56 0 ;
-CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 56 0 ;
-CC agrave 2 ; PCC a 0 0 ; PCC grave 56 0 ;
-CC aring 2 ; PCC a 0 0 ; PCC ring 56 0 ;
-CC atilde 2 ; PCC a 0 0 ; PCC tilde 56 0 ;
-CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ;
-CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ;
-CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ;
-CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 56 0 ;
-CC egrave 2 ; PCC e 0 0 ; PCC grave 56 0 ;
-CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ;
-CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ;
-CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ;
-CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ;
-CC ntilde 2 ; PCC n 0 0 ; PCC tilde 84 0 ;
-CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ;
-CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ;
-CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ;
-CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ;
-CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ;
-CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ;
-CC uacute 2 ; PCC u 0 0 ; PCC acute 84 0 ;
-CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 84 0 ;
-CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 84 0 ;
-CC ugrave 2 ; PCC u 0 0 ; PCC grave 84 0 ;
-CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ;
-CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ;
-CC zcaron 2 ; PCC z 0 0 ; PCC caron 56 0 ;
-EndComposites
-EndFontMetrics
index 1ef538bca60ae2c120a433df8aca080f616e5963..1dc8e55896a723ffe50322b9beec0353eda0a116 100644 (file)
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script.
 
 dnl Initialize.
 AC_PREREQ(2.60)
-AC_INIT([pspp],[0.7.2],[bug-gnu-pspp@gnu.org])
+AC_INIT([pspp],[0.7.3],[bug-gnu-pspp@gnu.org])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE
 
@@ -41,27 +41,39 @@ dnl Checks for libraries.
 AC_SYS_LARGEFILE
 AC_SEARCH_LIBS([sin], [m])
 AC_SEARCH_LIBS([dcgettext], [intl])
-PSPP_LIBPLOT
 PSPP_LC_PAPER
-AM_CONDITIONAL(WITHCHARTS, test x"$with_libplot" != x"no")
 
 
 AC_ARG_VAR([PSPP_LDFLAGS], [linker flags to be used for linking the pspp binary only])
 AC_ARG_VAR([PSPPIRE_LDFLAGS], [linker flags to be used for linking the psppire binary only])
 
-
-AC_ARG_WITH(
-  gui, 
-  [AS_HELP_STRING([--without-gui], [don't build the PSPPIRE gui])])
-
-required_gtk_version=2.12
-
-if test x"$with_gui" != x"no" ; then 
-  PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $required_gtk_version,,
-    [PSPP_REQUIRED_PREREQ([gtk+ 2.0 v$required_gtk_version or later (or use --without-gui)])])
+# Support for Cairo and Pango.
+AC_ARG_WITH([cairo],
+  [AS_HELP_STRING(
+    [--without-cairo], 
+    [Don't build support for charts (using Cairo and Pango);
+     implies --without-gui])],
+  [], [with_cairo=yes])
+AM_CONDITIONAL([HAVE_CAIRO], [test "$with_cairo" != no])
+if test "$with_cairo" != no; then
+  PKG_CHECK_MODULES([CAIRO], [cairo >= 1.5 pango >= 1.20 pangocairo], 
+    [CPPFLAGS="$CPPFLAGS $CAIRO_CFLAGS"
+     AC_DEFINE([HAVE_CAIRO], 1, 
+       [Define to 1 if Cairo and Pango are available.])],
+    [PSPP_REQUIRED_PREREQ([cairo 1.5 or later and pango 1.20 or later (or use --without-cairo)])])
 fi
-AM_CONDITIONAL(WITHGUI, test x"$with_gui" != x"no")
 
+# Support for GUI.
+AC_ARG_WITH([gui], 
+  [AS_HELP_STRING([--without-gui], 
+                  [Don't build the PSPPIRE GUI (using GTK+)])],
+  [], [with_gui=yes])
+AM_CONDITIONAL([HAVE_GUI], 
+               [test "$with_cairo" != no && test "$with_gui" != "no"])
+if test "$with_cairo" != no && test "$with_gui" != "no"; then
+  PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.12], [],
+    [PSPP_REQUIRED_PREREQ([gtk+ 2.0 version 2.12 or later (or use --without-gui)])])
+fi
 
 dnl Checks needed for psql reader
 
index 164d9ab1aac182a38c04120ef1028e8fb30684aa..357388c8b28809656f0deb579fcd0326379bbc79 100644 (file)
@@ -11,7 +11,7 @@ This chapter describe how to configure PSPP for your system.
 * Configuration files::         How configuration files are read.
 * Environment variables::       All about environment variables.
 * Output devices::              Describing your terminal(s) and printer(s).
-* PostScript driver class::     Configuration of PostScript devices.
+* Cairo driver class::          Configuration of Cairo devices.
 * ASCII driver class::          Configuration of character-code devices.
 * HTML driver class::           Configuration for HTML output.
 * Miscellaneous configuring::   Even more configuration variables.
@@ -324,7 +324,7 @@ A unique identifier, used to determine whether to enable the driver.
 
 @item class name
 One of the predefined driver classes supported by PSPP.  The
-currently supported driver classes include `postscript' and `ascii'.
+currently supported driver classes include `cairo' and `ascii'.
 
 @item device type(s)
 Zero or more of the following keywords, delimited by spaces:
@@ -540,26 +540,27 @@ interpreted; only the lower 8 bits are used.
 Tokens, outside of quoted strings, are delimited by white space or equals
 signs.
 
-@node PostScript driver class
-@section The PostScript driver class
+@node Cairo driver class
+@section The Cairo driver class
 
-The @code{postscript} driver class is used to produce output that is
-acceptable to PostScript printers and other interpreters.
+The @code{cairo} driver class can produce output in PDF, PostScript,
+and SVG formats.  It has full support for international character
+sets.
+
+The Cairo driver is only available if your copy of PSPP was built with
+the Cairo library.
 
 The available options are listed below.
 
 @table @code
 @item output-file=@var{file-name}
 
-File to which output should be sent.  This can be an ordinary file name
-(i.e., @code{"pspp.ps"}), a pipe (i.e., @code{"|lpr"}), or
-stdout (@code{"-"}).  Default: @code{"pspp.ps"}.
+File to which output should be sent.  Default: @code{"pspp.pdf"}.
 
-@item headers=@var{boolean}
+@item output-type=@var{output-type}
 
-Controls whether the standard headers showing the time and date and
-title and subtitle are printed at the top of each page.  Default:
-@code{on}.
+Type of output to write to the output file, one of @code{pdf},
+@code{ps}, or @code{svg}.  Default: @code{pdf}.
 
 @item paper-size=@var{paper-size}
 
@@ -578,6 +579,12 @@ assumed.
 
 Either @code{portrait} or @code{landscape}.  Default: @code{portrait}.
 
+@item headers=@var{boolean}
+
+Controls whether the standard headers showing the time and date and
+title and subtitle are printed at the top of each page.  Default:
+@code{on}.
+
 @item left-margin=@var{dimension}
 @itemx right-margin=@var{dimension}
 @itemx top-margin=@var{dimension}
@@ -587,29 +594,17 @@ Sets the margins around the page.  The headers, if enabled, are not
 included in the margins; they are in addition to the margins.  For a
 description of dimensions, see @ref{Dimensions}.  Default: @code{0.5in}.
 
-@item prop-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-@itemx emph-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-@itemx fixed-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
+@item prop-font=@var{font-name}
+@itemx emph-font=@var{font-name}
+@itemx fixed-font=@var{font-name}
 
 Sets the font used for proportional, emphasized, or fixed-pitch text.
-The only required value is @var{afm-file}, the AFM file for the font.
-
-If specified, @var{font-file} will be downloaded to the printer at the
-beginning of the print job.  The font file may be in PFA or PFB format.
-
-The font is reencoded as specified in @var{encoding-file}, if specified.
-Each line in @var{encoding-file} should consist of a PostScript
-character name and a decimal encoding value (between 0 and 255),
-separated by white space.  Blank lines and comments introduced by
-@samp{#} are also allowed.
-
-The files specified on these options are located as follows.  If
-the file name begins with @samp{/}, then it is taken as an absolute
-path.  Otherwise, PSPP searches its configuration path for the specified
-name prefixed by @code{psfonts/} (@pxref{File locations}).
+Most systems support CSS-like font names such as ``serif'' and
+``monospace'', but a wide range of system-specific font are likely to
+be supported as well.
 
-Default: proportional font @code{Times-Roman.afm}, emphasis font
-@code{Times-Italic.afm}, fixed-pitch font @code{Courier.afm}.
+Default: proportional font @code{serif}, emphasis font @code{serif
+italic}, fixed-pitch font @code{monospace}.
 
 @item font-size=@var{font-size}
 
@@ -653,14 +648,11 @@ a single @samp{#}, which is replaced by the chart number.  Default:
 @file{"pspp-#.png"}.
 
 @item chart-type=@var{type}.
-Type of charts to output.  Available types typically include @samp{X},
-@samp{png}, @samp{gif}, @samp{svg}, @samp{ps}, @samp{cgm}, @samp{fig},
-@samp{pcl}, @samp{hpgl}, @samp{regis}, @samp{tek}, and @samp{meta}.
+Type of charts to output, either @samp{png} or @samp{none}.
 Default: @samp{png}.
 
-You may specify @samp{none} to disable chart output.  Charts are also
-disabled if your installation of PSPP was compiled without
-@code{libplot}.
+Charts are always disabled if your installation of PSPP was compiled
+without the @code{cairo} library.
 
 @item paginate=@var{boolean}
 
index 6b20c67478909fe6232730b58d7b4056de720490..9bde4afcf26d77b9950e960cdfd29063b7cbbbeb 100644 (file)
@@ -2,7 +2,7 @@
 
 include $(top_srcdir)/lib/linreg/automake.mk
 
-if WITHGUI
+if HAVE_GUI
 include $(top_srcdir)/lib/gtk-contrib/automake.mk
 endif
 
index 2dccd10a36973e560e8be7d267b92f9bdb918ac1..fd2f5a5f5d579ae20f933248af74088524d15c43 100644 (file)
@@ -21,7 +21,7 @@ None by default.
 
 =cut
 BEGIN {
-       $PSPP::VERSION='0.7.2';
+       $PSPP::VERSION='0.7.3';
        require XSLoader;
        XSLoader::load('PSPP', $PSPP::VERSION);
 }
index a249f5ad4e9557a8c0f5a6e28366dcc322182c1d..ebfb680a09316b0a0fa486067616d77153cf5fcb 100644 (file)
@@ -25,7 +25,9 @@ src_data_libdata_la_SOURCES = \
        src/data/caseinit.c \
        src/data/caseinit.h \
        src/data/casereader-filter.c \
+       src/data/casereader-project.c \
        src/data/casereader-provider.h \
+       src/data/casereader-select.c \
        src/data/casereader-translator.c \
        src/data/casereader.c \
        src/data/casereader.h \
index 1a40213a537d295f78241a0c35a0594ba3a6262a..9837013acb8bee86cb6d30bf0876a706a05dbc71 100644 (file)
@@ -320,7 +320,7 @@ caseproto_free__ (struct caseproto *proto)
 void
 caseproto_refresh_long_string_cache__ (const struct caseproto *proto_)
 {
-  struct caseproto *proto = (struct caseproto *) proto_;
+  struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
   size_t n, i;
 
   assert (proto->long_strings == NULL);
index b85a9f32d9260f02d6339b8f0852caca8518722f..b0f45c418c47aae8017b0469a5307cff6b9cd7f6 100644 (file)
@@ -22,6 +22,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <data/value.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 
 /* Case prototype.
@@ -144,7 +145,7 @@ void caseproto_free__ (struct caseproto *);
 static inline struct caseproto *
 caseproto_ref (const struct caseproto *proto_)
 {
-  struct caseproto *proto = (struct caseproto *) proto_;
+  struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
   proto->ref_cnt++;
   return proto;
 }
diff --git a/src/data/casereader-project.c b/src/data/casereader-project.c
new file mode 100644 (file)
index 0000000..116a335
--- /dev/null
@@ -0,0 +1,106 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <data/casereader-provider.h>
+#include <data/subcase.h>
+
+#include "gl/xalloc.h"
+
+static bool
+projection_is_no_op (const struct casereader *reader, const struct subcase *sc)
+{
+  size_t n = subcase_get_n_fields (sc);
+  size_t i;
+
+  if (n != caseproto_get_n_widths (casereader_get_proto (reader)))
+    return false;
+
+  for (i = 0; i < n; i++)
+    if (subcase_get_case_index (sc, i) != i)
+      return false;
+
+  return true;
+}
+
+struct casereader_project
+  {
+    struct subcase old_sc;
+    struct subcase new_sc;
+  };
+
+static struct ccase *
+project_case (struct ccase *old, casenumber idx UNUSED, const void *project_)
+{
+  const struct casereader_project *project = project_;
+  struct ccase *new = case_create (subcase_get_proto (&project->new_sc));
+  subcase_copy (&project->old_sc, old, &project->new_sc, new);
+  case_unref (old);
+  return new;
+}
+
+static bool
+destroy_projection (void *project_)
+{
+  struct casereader_project *project = project_;
+  subcase_destroy (&project->old_sc);
+  subcase_destroy (&project->new_sc);
+  free (project);
+  return true;
+}
+
+/* Returns a casereader in which each row is obtained by extracting the subcase
+   SC from the corresponding row of SUBREADER. */
+struct casereader *
+casereader_project (struct casereader *subreader, const struct subcase *sc)
+{
+  if (projection_is_no_op (subreader, sc))
+    return casereader_rename (subreader);
+  else
+    {
+      struct casereader_project *project = xmalloc (sizeof *project);
+      const struct caseproto *proto;
+
+      subcase_clone (&project->old_sc, sc);
+      proto = subcase_get_proto (&project->old_sc);
+
+      subcase_init_empty (&project->new_sc);
+      subcase_add_proto_always (&project->new_sc, proto);
+
+      return casereader_translate_stateless (subreader, proto,
+                                             project_case, destroy_projection,
+                                             project);
+    }
+}
+
+/* Returns a casereader in which each row is obtained by extracting the value
+   with index COLUMN from the corresponding row of SUBREADER. */
+struct casereader *
+casereader_project_1 (struct casereader *subreader, int column)
+{
+  const struct caseproto *subproto = casereader_get_proto (subreader);
+  struct casereader *reader;
+  struct subcase sc;
+
+  subcase_init (&sc, column, caseproto_get_width (subproto, column),
+                SC_ASCEND);
+  reader = casereader_project (subreader, &sc);
+  subcase_destroy (&sc);
+
+  return reader;
+}
+
diff --git a/src/data/casereader-select.c b/src/data/casereader-select.c
new file mode 100644 (file)
index 0000000..a26ef67
--- /dev/null
@@ -0,0 +1,81 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <data/casereader-provider.h>
+
+#include "gl/xalloc.h"
+
+struct casereader_select
+  {
+    casenumber by;
+    casenumber i;
+  };
+
+static bool
+casereader_select_include (const struct ccase *c UNUSED, void *cs_)
+{
+  struct casereader_select *cs = cs_;
+  if (++cs->i >= cs->by)
+    {
+      cs->i = 0;
+      return true;
+    }
+  else
+    return false;
+}
+
+static bool
+casereader_select_destroy (void *cs_)
+{
+  struct casereader_select *cs = cs_;
+  free (cs);
+  return true;
+}
+
+/* Returns a casereader that contains cases FIRST though LAST, exclusive, of
+   those within SUBREADER.  (The first case in SUBREADER is number 0.)  If BY
+   is greater than 1, then it specifies a step between cases, e.g.  a BY value
+   of 2 causes the cases numbered FIRST + 1, FIRST + 3, FIRST + 5, and so on
+   to be omitted.
+
+   The caller gives up ownership of SUBREADER, transferring it into this
+   function. */
+struct casereader *
+casereader_select (struct casereader *subreader,
+                   casenumber first, casenumber last, casenumber by)
+{
+  if (by == 0)
+    by = 1;
+
+  casereader_advance (subreader, first);
+  if (last >= first)
+    casereader_truncate (subreader, (last - first) / by * by);
+
+  if (by == 1)
+    return casereader_rename (subreader);
+  else
+    {
+      struct casereader_select *cs = xmalloc (sizeof *cs);
+      cs->by = by;
+      cs->i = 0;
+      return casereader_create_filter_func (subreader,
+                                            casereader_select_include,
+                                            casereader_select_destroy,
+                                            cs, NULL);
+    }
+}
index feffa15c4510ba97e57cd95705cd9afd290c1e04..27ea413460aa501a61d4d81ae97a9c9c8f1d50df 100644 (file)
@@ -46,6 +46,11 @@ static const struct casereader_class casereader_translator_class;
    INPUT and auxiliary data AUX.  TRANSLATE must destroy its
    input case.
 
+   TRANSLATE may be stateful, that is, the output for a given
+   case may depend on previous cases.  If TRANSLATE is stateless,
+   then you may want to use casereader_translate_stateless
+   instead, since it sometimes performs better.
+
    The cases returned by TRANSLATE must match OUTPUT_PROTO.
 
    When the translating casereader is destroyed, DESTROY will be
@@ -106,7 +111,110 @@ static const struct casereader_class casereader_translator_class =
     NULL,
     NULL,
   };
+\f
+/* Casereader that applies a user-supplied function to translate
+   each case into another in a stateless fashion. */
+
+/* A statelessly translating casereader. */
+struct casereader_stateless_translator
+  {
+    struct casereader *subreader; /* Source of input cases. */
+
+    casenumber case_offset;
+    struct ccase *(*translate) (struct ccase *input, casenumber,
+                                const void *aux);
+    bool (*destroy) (void *aux);
+    void *aux;
+  };
+
+static const struct casereader_random_class
+casereader_stateless_translator_class;
+
+/* Creates and returns a new casereader whose cases are produced by reading
+   from SUBREADER and passing through the TRANSLATE function.  TRANSLATE must
+   takes ownership of its input case and returns a translated case, populating
+   the translated case based on INPUT and auxiliary data AUX.
+
+   TRANSLATE must be stateless, that is, the output for a given case must not
+   depend on previous cases.  This is because cases may be retrieved in
+   arbitrary order, and some cases may be retrieved multiple times, and some
+   cases may be skipped and never retrieved at all.  If TRANSLATE is stateful,
+   use casereader_create_translator instead.
+
+   The casenumber argument to the TRANSLATE function is the absolute case
+   number in SUBREADER, that is, 0 when the first case in SUBREADER is being
+   translated, 1 when the second case is being translated, and so on.
+
+   The cases returned by TRANSLATE must match OUTPUT_PROTO.
+
+   When the stateless translating casereader is destroyed, DESTROY will be
+   called to allow any auxiliary data maintained by TRANSLATE to be freed.
+
+   After this function is called, SUBREADER must not ever again be referenced
+   directly.  It will be destroyed automatically when the translating
+   casereader is destroyed. */
+struct casereader *
+casereader_translate_stateless (
+  struct casereader *subreader,
+  const struct caseproto *output_proto,
+  struct ccase *(*translate) (struct ccase *input, casenumber,
+                              const void *aux),
+  bool (*destroy) (void *aux),
+  void *aux)
+{
+  struct casereader_stateless_translator *cst = xmalloc (sizeof *cst);
+  struct casereader *reader;
+  cst->subreader = casereader_rename (subreader);
+  cst->translate = translate;
+  cst->destroy = destroy;
+  cst->aux = aux;
+  reader = casereader_create_random (
+    output_proto, casereader_get_case_cnt (cst->subreader),
+    &casereader_stateless_translator_class, cst);
+  taint_propagate (casereader_get_taint (cst->subreader),
+                   casereader_get_taint (reader));
+  return reader;
+}
+
+/* Internal read function for stateless translating casereader. */
+static struct ccase *
+casereader_stateless_translator_read (struct casereader *reader UNUSED,
+                                      void *cst_, casenumber idx)
+{
+  struct casereader_stateless_translator *cst = cst_;
+  struct ccase *tmp = casereader_peek (cst->subreader, idx);
+  if (tmp != NULL)
+    tmp = cst->translate (tmp, cst->case_offset + idx, cst->aux);
+  return tmp;
+}
+
+/* Internal destroy function for translating casereader. */
+static void
+casereader_stateless_translator_destroy (struct casereader *reader UNUSED,
+                                         void *cst_)
+{
+  struct casereader_stateless_translator *cst = cst_;
+  casereader_destroy (cst->subreader);
+  cst->destroy (cst->aux);
+  free (cst);
+}
+
+static void
+casereader_stateless_translator_advance (struct casereader *reader UNUSED,
+                                         void *cst_, casenumber cnt)
+{
+  struct casereader_stateless_translator *cst = cst_;
+  cst->case_offset += casereader_advance (cst->subreader, cnt);
+}
 
+/* Casereader class for stateless translating casereader. */
+static const struct casereader_random_class
+casereader_stateless_translator_class =
+  {
+    casereader_stateless_translator_read,
+    casereader_stateless_translator_destroy,
+    casereader_stateless_translator_advance,
+  };
 \f
 
 struct casereader_append_numeric
index a1550ac57560e5fb623d12ede14b7e1a65ca661b..57afff844d251acc9bbffbb40f1b9eb9c7232986 100644 (file)
@@ -108,7 +108,7 @@ casereader_destroy (struct casereader *reader)
 struct casereader *
 casereader_clone (const struct casereader *reader_)
 {
-  struct casereader *reader = (struct casereader *) reader_;
+  struct casereader *reader = CONST_CAST (struct casereader *, reader_);
   struct casereader *clone;
   if ( reader == NULL ) 
     return NULL;
@@ -257,6 +257,19 @@ casereader_get_case_cnt (struct casereader *reader)
   return reader->case_cnt;
 }
 
+static casenumber
+casereader_count_cases__ (struct casereader *reader, casenumber max_cases)
+{
+  struct casereader *clone;
+  casenumber n_cases;
+
+  clone = casereader_clone (reader);
+  n_cases = casereader_advance (clone, max_cases);
+  casereader_destroy (clone);
+
+  return n_cases;
+}
+
 /* Returns the number of cases that will be read by successive
    calls to casereader_read for READER, assuming that no errors
    occur.  Upon an error condition, the case count drops to 0, so
@@ -270,22 +283,24 @@ casenumber
 casereader_count_cases (struct casereader *reader)
 {
   if (reader->case_cnt == CASENUMBER_MAX)
-    {
-      casenumber n_cases = 0;
-      struct ccase *c;
-
-      struct casereader *clone = casereader_clone (reader);
-
-      for (; (c = casereader_read (clone)) != NULL; case_unref (c))
-        n_cases++;
-
-      casereader_destroy (clone);
-      reader->case_cnt = n_cases;
-    }
-
+    reader->case_cnt = casereader_count_cases__ (reader, CASENUMBER_MAX);
   return reader->case_cnt;
 }
 
+/* Truncates READER to at most N cases. */
+void
+casereader_truncate (struct casereader *reader, casenumber n)
+{
+  /* This could be optimized, if it ever becomes too expensive, by adding a
+     "max_cases" member to struct casereader.  We could also add a "truncate"
+     function to the casereader implementation, to allow the casereader to
+     throw away data that cannot ever be read. */
+  if (reader->case_cnt == CASENUMBER_MAX)
+    reader->case_cnt = casereader_count_cases__ (reader, n);
+  if (reader->case_cnt > n)
+    reader->case_cnt = n;
+}
+
 /* Returns the prototype for the cases in READER.  The caller
    must not unref the returned prototype. */
 const struct caseproto *
@@ -294,6 +309,26 @@ casereader_get_proto (const struct casereader *reader)
   return reader->proto;
 }
 
+/* Skips past N cases in READER, stopping when the last case in
+   READER has been read or on an input error.  Returns the number
+   of cases successfully skipped. */
+casenumber
+casereader_advance (struct casereader *reader, casenumber n)
+{
+  casenumber i;
+
+  for (i = 0; i < n; i++)
+    {
+      struct ccase *c = casereader_read (reader);
+      if (c == NULL)
+        break;
+      case_unref (c);
+    }
+
+  return i;
+}
+
+
 /* Copies all the cases in READER to WRITER, propagating errors
    appropriately. */
 void
@@ -664,3 +699,42 @@ static const struct casereader_random_class shim_class =
     shim_destroy,
     shim_advance,
   };
+\f
+static const struct casereader_class casereader_null_class;
+
+/* Returns a casereader with no cases.  The casereader has the prototype
+   specified by PROTO.  PROTO may be specified as a null pointer, in which case
+   the casereader has no variables. */
+struct casereader *
+casereader_create_empty (const struct caseproto *proto_)
+{
+  struct casereader *reader;
+  struct caseproto *proto;
+
+  proto = proto_ != NULL ? caseproto_ref (proto_) : caseproto_create ();
+  reader = casereader_create_sequential (NULL, proto, 0,
+                                         &casereader_null_class, NULL);
+  caseproto_unref (proto);
+
+  return reader;
+}
+
+static struct ccase *
+casereader_null_read (struct casereader *reader UNUSED, void *aux UNUSED)
+{
+  return NULL;
+}
+
+static void
+casereader_null_destroy (struct casereader *reader UNUSED, void *aux UNUSED)
+{
+  /* Nothing to do. */
+}
+
+static const struct casereader_class casereader_null_class =
+  {
+    casereader_null_read,
+    casereader_null_destroy,
+    NULL,                       /* clone */
+    NULL,                       /* peek */
+  };
index 3b903bbfd52d66cdba8ffe0e960fa7f0d1ef2474..a78afaeb27096974107ff1b17409e43318929e37 100644 (file)
@@ -58,6 +58,7 @@
 struct dictionary;
 struct casereader;
 struct casewriter;
+struct subcase;
 
 struct ccase *casereader_read (struct casereader *);
 bool casereader_destroy (struct casereader *);
@@ -77,10 +78,14 @@ const struct taint *casereader_get_taint (const struct casereader *);
 
 casenumber casereader_get_case_cnt (struct casereader *);
 casenumber casereader_count_cases (struct casereader *);
+void casereader_truncate (struct casereader *, casenumber);
 const struct caseproto *casereader_get_proto (const struct casereader *);
 
+casenumber casereader_advance (struct casereader *, casenumber);
 void casereader_transfer (struct casereader *, struct casewriter *);
 \f
+struct casereader *casereader_create_empty (const struct caseproto *);
+
 struct casereader *
 casereader_create_filter_func (struct casereader *,
                                bool (*include) (const struct ccase *,
@@ -112,6 +117,22 @@ casereader_create_translator (struct casereader *,
                               bool (*destroy) (void *aux),
                               void *aux);
 
+struct casereader *
+casereader_translate_stateless (struct casereader *,
+                                const struct caseproto *output_proto,
+                                struct ccase *(*translate) (struct ccase *,
+                                                            casenumber idx,
+                                                            const void *aux),
+                                bool (*destroy) (void *aux),
+                                void *aux);
+
+struct casereader *casereader_project (struct casereader *,
+                                       const struct subcase *);
+struct casereader *casereader_project_1 (struct casereader *, int column);
+struct casereader *casereader_select (struct casereader *,
+                                      casenumber first, casenumber last,
+                                      casenumber by);
+
 /* A function which creates a numberic value from an existing case */
 typedef double new_value_func (const struct ccase *, casenumber, void *);
 
index 9b04b9413c61bbace1e5b189d68e65eebe152482..936b6aa940051be14a422fde8a8773f22388eeb7 100644 (file)
@@ -168,7 +168,7 @@ casewindow_pop_tail (struct casewindow *cw, casenumber case_cnt)
 struct ccase *
 casewindow_get_case (const struct casewindow *cw_, casenumber case_idx)
 {
-  struct casewindow *cw = (struct casewindow *) cw_;
+  struct casewindow *cw = CONST_CAST (struct casewindow *, cw_);
 
   assert (case_idx >= 0 && case_idx < casewindow_get_case_cnt (cw));
   if (casewindow_error (cw))
index fa24d8ce77014e980cb79e4700ffc63832c5d061..4abc526f0cb2a720cb8e3023eb7d732effdb5da4 100644 (file)
@@ -275,7 +275,7 @@ datasheet_destroy (struct datasheet *ds)
 const struct caseproto *
 datasheet_get_proto (const struct datasheet *ds_)
 {
-  struct datasheet *ds = (struct datasheet *) ds_;
+  struct datasheet *ds = CONST_CAST (struct datasheet *, ds_);
   if (ds->proto == NULL)
     {
       size_t i;
@@ -548,7 +548,7 @@ datasheet_get_row (const struct datasheet *ds, casenumber row)
 {
   size_t n_columns = datasheet_get_n_columns (ds);
   struct ccase *c = case_create (datasheet_get_proto (ds));
-  if (rw_case ((struct datasheet *) ds, OP_READ,
+  if (rw_case (CONST_CAST (struct datasheet *, ds), OP_READ,
                row, 0, n_columns, case_data_all_rw (c)))
     return c;
   else
@@ -582,7 +582,8 @@ datasheet_get_value (const struct datasheet *ds, casenumber row,
                      size_t column, union value *value)
 {
   assert (row >= 0);
-  return rw_case ((struct datasheet *) ds, OP_READ, row, column, 1, value);
+  return rw_case (CONST_CAST (struct datasheet *, ds), OP_READ,
+                  row, column, 1, value);
 }
 
 /* Stores VALUE into DS in the given ROW and COLUMN.  VALUE must
index ab653d874193a46d1f9a83a03f1e15ac4e09e7a6..1ddf5575aa68c4877f77fea845d3b4e6ea567ccc 100644 (file)
@@ -1016,7 +1016,7 @@ dict_set_case_limit (struct dictionary *d, casenumber case_limit)
 const struct caseproto *
 dict_get_proto (const struct dictionary *d_)
 {
-  struct dictionary *d = (struct dictionary *) d_;
+  struct dictionary *d = CONST_CAST (struct dictionary *, d_);
   if (d->proto == NULL)
     {
       size_t i;
@@ -1375,7 +1375,7 @@ dict_clear_vectors (struct dictionary *d)
 struct attrset *
 dict_get_attributes (const struct dictionary *d) 
 {
-  return (struct attrset *) &d->attributes;
+  return CONST_CAST (struct attrset *, &d->attributes);
 }
 
 /* Replaces D's attributes set by a copy of ATTRS. */
index 91229595924c7bfe7fe11d1486970ae598950f5c..cf58b6db1a24a27cb0f38b7d8b79417e62be4ef5 100644 (file)
@@ -63,13 +63,13 @@ fn_init (void)
 /* Functions for performing operations on file names. */
 
 
-/* Substitutes $variables in SRC, putting the result in DST,
-   properly handling the case where SRC is a substring of DST.
-   Variables are as defined by GETENV. Supports $var and ${var}
-   syntaxes; $$ substitutes as $. */
+/* Copies from SRC to DST, calling INSERT_VARIABLE to handle each
+   instance of $var or ${var} in SRC.  $$ is replaced by $. */
 void
-fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
-                struct string *dst_)
+fn_interp_vars (struct substring src,
+                void (*insert_variable) (const char *var,
+                                         struct string *dst, void *aux),
+                void *aux, struct string *dst_)
 {
   struct string dst = DS_EMPTY_INITIALIZER;
   int c;
@@ -84,8 +84,7 @@ fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
         else
           {
             struct substring var_name;
-            size_t start;
-            const char *value;
+            char *var;
 
             if (ss_match_char (&src, '('))
               ss_get_until (&src, ')', &var_name);
@@ -95,12 +94,9 @@ fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
               ss_get_chars (&src, MAX (1, ss_span (src, ss_cstr (CC_ALNUM))),
                             &var_name);
 
-            start = ds_length (&dst);
-            ds_put_substring (&dst, var_name);
-            value = getenv (ds_cstr (&dst) + start);
-            ds_truncate (&dst, start);
-
-            ds_put_cstr (&dst, value);
+            var = ss_xstrdup (var_name);
+            insert_variable (var, &dst, aux);
+            free (var);
           }
       }
 
@@ -108,6 +104,14 @@ fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
   ds_destroy (&dst);
 }
 
+static void
+insert_env_var (const char *var, struct string *dst, void *aux UNUSED)
+{
+  const char *value = fn_getenv (var);
+  if (value != NULL)
+    ds_put_cstr (dst, value);
+}
+
 /* Searches for a configuration file with name NAME in the path
    given by PATH, which is environment-interpolated.
    Directories in PATH are delimited by ':'.  Returns the
@@ -126,7 +130,7 @@ fn_search_path (const char *base_name, const char *path_)
 
   /* Interpolate environment variables. */
   ds_init_cstr (&path, path_);
-  fn_interp_vars (ds_ss (&path), fn_getenv, &path);
+  fn_interp_vars (ds_ss (&path), insert_env_var, NULL, &path);
 
   verbose_msg (2, _("searching for \"%s\" in path \"%s\""),
                base_name, ds_cstr (&path));
index d34bd4196fea046db722197aa403db964a423deb..327d716e7f57723da0922a0179779fe9cd15e620 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -28,8 +28,9 @@ extern const char *config_path;
 void fn_init (void);
 
 void fn_interp_vars (struct substring src,
-                     const char *(*getenv) (const char *),
-                     struct string *dst);
+                     void (*insert_variable) (const char *var,
+                                              struct string *dst, void *aux),
+                     void *aux, struct string *dst);
 char *fn_search_path (const char *base_name, const char *path);
 char *fn_dir_name (const char *fn);
 char *fn_extension (const char *fn);
index b762214d10e20a33d6bbdf89ac52253a67589453..5e013a14af767d80a097a4c46f577ed77c4c3210 100644 (file)
@@ -682,7 +682,7 @@ dataset_end_of_command (struct dataset *ds)
       else
         {
           const struct taint *taint = casereader_get_taint (ds->source);
-          taint_reset_successor_taint ((struct taint *) taint);
+          taint_reset_successor_taint (CONST_CAST (struct taint *, taint));
           assert (!taint_has_tainted_successor (taint));
         }
     }
index 6ffaa4c2bed24a603e3ce0aa267c16661e82f471..32056215e4ace37d4fcd9b544c3e48c493d82dec 100644 (file)
@@ -99,6 +99,27 @@ subcase_destroy (struct subcase *sc)
   caseproto_unref (sc->proto);
 }
 
+/* Returns true if VAR already has a field in SC,
+   false otherwise. */
+bool
+subcase_contains_var (const struct subcase *sc, const struct variable *var)
+{
+  return subcase_contains (sc, var_get_case_index (var));
+}
+
+/* Returns true if CASE_INDEX already has a field in SC,
+   false otherwise. */
+bool
+subcase_contains (const struct subcase *sc, int case_index)
+{
+  size_t i;
+
+  for (i = 0; i < sc->n_fields; i++)
+    if (sc->fields[i].case_index == case_index)
+      return true;
+
+  return false;
+}
 
 /* Add a field for VAR to SC, with DIRECTION as the sort order.
    Returns true if successful, false if VAR already has a field
@@ -107,8 +128,13 @@ bool
 subcase_add_var (struct subcase *sc, const struct variable *var,
                  enum subcase_direction direction)
 {
-  return subcase_add (sc, var_get_case_index (var),
-                     var_get_width (var), direction);
+  if (!subcase_contains_var (sc, var))
+    {
+      subcase_add_var_always (sc, var, direction);
+      return true;
+    }
+  else
+    return false;
 }
 
 /* Add a field for CASE_INDEX, WIDTH to SC, with DIRECTION as the sort order.
@@ -116,14 +142,35 @@ subcase_add_var (struct subcase *sc, const struct variable *var,
    in SC. */
 bool
 subcase_add (struct subcase *sc, int case_index, int width,
-                 enum subcase_direction direction)
+             enum subcase_direction direction)
 {
-  struct subcase_field *field;
-  size_t i;
+  if (!subcase_contains (sc, case_index))
+    {
+      subcase_add_always (sc, case_index, width, direction);
+      return true;
+    }
+  else
+    return false;
+}
 
-  for (i = 0; i < sc->n_fields; i++)
-    if (sc->fields[i].case_index == case_index)
-      return false;
+/* Add a field for VAR to SC, with DIRECTION as the sort order,
+   regardless of whether VAR already has a field in SC. */
+void
+subcase_add_var_always (struct subcase *sc, const struct variable *var,
+                        enum subcase_direction direction)
+{
+  return subcase_add_always (sc, var_get_case_index (var),
+                             var_get_width (var), direction);
+}
+
+/* Add a field for CASE_INDEX, WIDTH to SC, with DIRECTION as the
+   sort order, regardless of whether CASE_INDEX already has a
+   field in SC. */
+void
+subcase_add_always (struct subcase *sc, int case_index, int width,
+                    enum subcase_direction direction)
+{
+  struct subcase_field *field;
 
   sc->fields = xnrealloc (sc->fields, sc->n_fields + 1, sizeof *sc->fields);
   field = &sc->fields[sc->n_fields++];
@@ -131,7 +178,27 @@ subcase_add (struct subcase *sc, int case_index, int width,
   field->width = width;
   field->direction = direction;
   invalidate_proto (sc);
-  return true;
+}
+
+/* Adds a field to SC for each column in PROTO, so that SC
+   contains all of the columns in PROTO in the same order as a
+   case conforming to PROTO.  The fields are added with
+   ascending direction. */
+void
+subcase_add_proto_always (struct subcase *sc, const struct caseproto *proto)
+{
+  size_t n = caseproto_get_n_widths (proto);
+  size_t i;
+
+  sc->fields = xnrealloc (sc->fields, sc->n_fields + n, sizeof *sc->fields);
+  for (i = 0; i < n; i++)
+    {
+      struct subcase_field *field = &sc->fields[sc->n_fields++];
+      field->case_index = i;
+      field->width = caseproto_get_width (proto, i);
+      field->direction = SC_ASCEND;
+    }
+  invalidate_proto (sc);
 }
 
 /* Obtains a caseproto for a case described by SC.  The caller
@@ -139,7 +206,7 @@ subcase_add (struct subcase *sc, int case_index, int width,
 const struct caseproto *
 subcase_get_proto (const struct subcase *sc_)
 {
-  struct subcase *sc = (struct subcase *) sc_;
+  struct subcase *sc = CONST_CAST (struct subcase *, sc_);
 
   if (sc->proto == NULL)
     {
index 6e59da1d3679ac5b4c89f8a1b6a6ca6bc910627c..71bf6fd7cc33d4227a4e3c4380f6773a1bfc599a 100644 (file)
@@ -60,17 +60,27 @@ void subcase_clone (struct subcase *, const struct subcase *);
 void subcase_clear (struct subcase *);
 void subcase_destroy (struct subcase *);
 
-bool subcase_add (struct subcase *sc, int index, int width,
-                 enum subcase_direction direction);
+bool subcase_contains (const struct subcase *, int case_index);
+bool subcase_contains_var (const struct subcase *, const struct variable *);
 
+bool subcase_add (struct subcase *, int case_index, int width,
+                 enum subcase_direction direction);
 bool subcase_add_var (struct subcase *, const struct variable *,
                       enum subcase_direction);
 
+void subcase_add_always (struct subcase *sc, int case_index, int width,
+                         enum subcase_direction direction);
+void subcase_add_var_always (struct subcase *, const struct variable *,
+                             enum subcase_direction);
+void subcase_add_proto_always (struct subcase *, const struct caseproto *);
+
 const struct caseproto *subcase_get_proto (const struct subcase *);
 
 static inline bool subcase_is_empty (const struct subcase *);
 static inline size_t subcase_get_n_fields (const struct subcase *);
 
+static inline size_t subcase_get_case_index (const struct subcase *,
+                                             size_t idx);
 static inline enum subcase_direction subcase_get_direction (
   const struct subcase *, size_t idx);
 
@@ -100,6 +110,12 @@ bool subcase_equal_cx (const struct subcase *,
 bool subcase_equal_xx (const struct subcase *,
                        const union value *a, const union value *b);
 
+static inline size_t
+subcase_get_case_index (const struct subcase *sc, size_t idx)
+{
+  return sc->fields[idx].case_index;
+}
+
 static inline enum subcase_direction
 subcase_get_direction (const struct subcase *sc, size_t idx)
 {
index c8061f7bd5d7fee54c47f5e65aa739b442f10dce..0fe829cebe1781c4ccc2d791756d534b88153e0c 100644 (file)
@@ -24,6 +24,7 @@
 #include <data/value.h>
 #include <data/variable.h>
 #include <libpspp/array.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash-functions.h>
 #include <libpspp/hmap.h>
@@ -169,7 +170,7 @@ void
 val_labs_replace (struct val_labs *vls, const union value *value,
                   const char *label)
 {
-  struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value);
+  struct val_lab *vl = val_labs_lookup (vls, value);
   if (vl != NULL)
     {
       atom_destroy (vl->label);
@@ -181,9 +182,8 @@ val_labs_replace (struct val_labs *vls, const union value *value,
 
 /* Removes LABEL from VLS. */
 void
-val_labs_remove (struct val_labs *vls, const struct val_lab *label_)
+val_labs_remove (struct val_labs *vls, struct val_lab *label)
 {
-  struct val_lab *label = (struct val_lab *) label_;
   hmap_delete (&vls->labels, &label->node);
   value_destroy (&label->value, vls->width);
   atom_destroy (label->label);
@@ -203,7 +203,7 @@ val_labs_find (const struct val_labs *vls, const union value *value)
 /* Searches VLS for a value label for VALUE.  If successful,
    returns the value label; otherwise, returns a null pointer.
    Returns a null pointer if VLS is null. */
-const struct val_lab *
+struct val_lab *
 val_labs_lookup (const struct val_labs *vls, const union value *value)
 {
   if (vls != NULL)
index 53d13a389700eb586af6aaf14bd82bd540311235..460ab84c17c919d821c4c477a2c3fd033b520bbb 100644 (file)
@@ -70,7 +70,7 @@ size_t val_labs_count (const struct val_labs *);
 
 /* Looking up value labels. */
 const char *val_labs_find (const struct val_labs *, const union value *);
-const struct val_lab *val_labs_lookup (const struct val_labs *,
+struct val_lab *val_labs_lookup (const struct val_labs *,
                                        const union value *);
 
 /* Basic properties. */
@@ -82,7 +82,7 @@ void val_labs_set_width (struct val_labs *, int new_width);
 /* Adding value labels. */
 bool val_labs_add (struct val_labs *, const union value *, const char *);
 void val_labs_replace (struct val_labs *, const union value *, const char *);
-void val_labs_remove (struct val_labs *, const struct val_lab *);
+void val_labs_remove (struct val_labs *, struct val_lab *);
 
 /* Iterating through value labels. */
 const struct val_lab *val_labs_first (const struct val_labs *);
index d1e308640d05268df79f8639848cef04c618ba23..05edc57e2db85a3c02df4fbe3a16a6747d92c4bc 100644 (file)
@@ -957,7 +957,7 @@ void *
 var_attach_aux (const struct variable *v_,
                 void *aux, void (*aux_dtor) (struct variable *))
 {
-  struct variable *v = (struct variable *) v_ ; /* cast away const  */
+  struct variable *v = CONST_CAST (struct variable *, v_);
   assert (v->aux == NULL);
   assert (aux != NULL);
   v->aux = aux;
@@ -1015,7 +1015,7 @@ var_get_obs_vals (const struct variable *v)
 void
 var_set_obs_vals (const struct variable *v_, struct cat_vals *cat_vals)
 {
-  struct variable *v = (struct variable *) v_ ; /* cast away const */
+  struct variable *v = CONST_CAST (struct variable *, v_ );
   cat_stored_values_destroy (v->obs_vals);
   v->obs_vals = cat_vals;
 }
@@ -1035,7 +1035,7 @@ var_has_obs_vals (const struct variable *v)
 struct attrset *
 var_get_attributes (const struct variable *v) 
 {
-  return (struct attrset *) &v->attributes;
+  return CONST_CAST (struct attrset *, &v->attributes);
 }
 
 /* Replaces variable V's attributes set by a copy of ATTRS. */
index 4210b7fce45f8f05153f9fd4bd2cd28881255b01..3052b52353ca54416ffbb53f66c4354787bf6924 100644 (file)
@@ -13,9 +13,6 @@ include $(top_srcdir)/src/language/expressions/automake.mk
 noinst_LTLIBRARIES +=  src/language/liblanguage.la
 
 
-src_language_liblanguage_la_LIBADD = \
-       src/output/charts/libcharts.la
-
 src_language_liblanguage_la_SOURCES = \
        src/language/syntax-file.c \
        src/language/syntax-file.h \
index 32dba0b4f7cd0ca7760f18315e13ae0b845069a0..4c3612fb290977674247b7bd2d66801afa56f818 100644 (file)
@@ -37,7 +37,6 @@
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 #include <output/manager.h>
-#include <output/table.h>
 #include <libpspp/getl.h>
 
 #if HAVE_SYS_WAIT_H
@@ -231,9 +230,9 @@ do_parse_command (struct lexer *lexer,
 
   /* Execute command. */
   msg_set_command_name (command->name);
-  tab_set_command_name (command->name);
+  som_set_command_name (command->name);
   result = command->function (lexer, ds);
-  tab_set_command_name (NULL);
+  som_set_command_name (NULL);
   msg_set_command_name (NULL);
 
   assert (cmd_result_is_valid (result));
index 32847bb4a9298bd100f0cb80e8489097adc7d8ca..cb608f769d5d4b91adbb5c21fa5ae3d4403a2c08 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
+#include <libpspp/cast.h>
 #include <libpspp/ll.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
@@ -512,10 +513,10 @@ find_substitution (struct repeat_block *block, struct substring name)
 /* Makes appropriate DO REPEAT macro substitutions within the
    repeated lines. */
 static void
-do_repeat_filter (struct getl_interface *block_,
-                  struct string *line)
+do_repeat_filter (struct getl_interface *interface, struct string *line)
 {
-  struct repeat_block *block = (struct repeat_block *) block_;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   bool in_apos, in_quote, dot;
   struct substring input;
   struct string output;
@@ -557,7 +558,8 @@ do_repeat_filter (struct getl_interface *block_,
 static struct repeat_line *
 current_line (const struct getl_interface *interface)
 {
-  struct repeat_block *block = (struct repeat_block *) interface;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   return (block->cur_line != ll_null (&block->lines)
           ? ll_data (block->cur_line, struct repeat_line, ll)
           : NULL);
@@ -570,7 +572,8 @@ static bool
 do_repeat_read  (struct getl_interface *interface,
                  struct string *output)
 {
-  struct repeat_block *block = (struct repeat_block *) interface;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   struct repeat_line *line;
 
   block->cur_line = ll_next (block->cur_line);
@@ -591,9 +594,10 @@ do_repeat_read  (struct getl_interface *interface,
 /* Frees a DO REPEAT block.
    Called by getl to close out the DO REPEAT block. */
 static void
-do_repeat_close (struct getl_interface *block_)
+do_repeat_close (struct getl_interface *interface)
 {
-  struct repeat_block *block = (struct repeat_block *) block_;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   pool_destroy (block->pool);
 }
 
index 020f8e4c7e5a20a0caabc4add9f228f98937d1b6..fe78aeb36ed272f309481870d79ea6669d48d735 100644 (file)
@@ -644,8 +644,8 @@ dump_fixed_table (const struct data_parser *parser,
   struct tab_table *t;
   size_t i;
 
-  t = tab_create (4, parser->field_cnt + 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  t = tab_create (4, parser->field_cnt + 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
@@ -653,7 +653,7 @@ dump_fixed_table (const struct data_parser *parser,
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
@@ -685,14 +685,14 @@ dump_delimited_table (const struct data_parser *parser,
   struct tab_table *t;
   size_t i;
 
-  t = tab_create (2, parser->field_cnt + 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  t = tab_create (2, parser->field_cnt + 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
index c3f9b0881b959fac98a3cffac628662ffe143299..614fa1271e71b5ed3180e2f4e98e1b454745b769 100644 (file)
@@ -35,7 +35,7 @@
 #include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/compiler.h>
-#include <libpspp/message.h>
+#include <libpspp/ll.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <output/htmlP.h>
 /* (functions) */
 
 /* Layout for one output driver. */
-struct list_ext
+struct list_target
   {
+    struct ll ll;
+    struct outp_driver *driver;
     int type;          /* 0=Values and labels fit across the page. */
     size_t n_vertical; /* Number of labels to list vertically. */
     size_t header_rows;        /* Number of header rows. */
@@ -84,11 +86,12 @@ static void write_line (struct outp_driver *d, const char *s);
 
 /* Other functions. */
 static void list_case (const struct ccase *, casenumber case_idx,
-                       const struct dataset *);
-static void determine_layout (void);
-static void clean_up (void);
-static void write_header (struct outp_driver *);
-static void write_all_headers (struct casereader *, const struct dataset*);
+                       const struct dataset *, struct ll_list *targets);
+static void determine_layout (struct ll_list *targets);
+static void clean_up (struct ll_list *targets);
+static void write_header (struct list_target *);
+static void write_all_headers (struct casereader *, const struct dataset *,
+                               struct ll_list *targets);
 
 /* Returns the number of text lines that can fit on the remainder of
    the page. */
@@ -136,6 +139,7 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
   struct variable *casenum_var = NULL;
   struct casegrouper *grouper;
   struct casereader *group;
+  struct ll_list targets;
   casenumber case_idx;
   bool ok;
 
@@ -229,7 +233,7 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
       cmd.v_variables[0] = casenum_var;
     }
 
-  determine_layout ();
+  determine_layout (&targets);
 
   case_idx = 0;
   for (grouper = casegrouper_create_splits (proc_open (ds), dict);
@@ -238,13 +242,13 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
     {
       struct ccase *c;
 
-      write_all_headers (group, ds);
+      write_all_headers (group, ds, &targets);
       for (; (c = casereader_read (group)) != NULL; case_unref (c))
         {
           case_idx++;
           if (case_idx >= cmd.first && case_idx <= cmd.last
               && (case_idx - cmd.first) % cmd.step == 0)
-            list_case (c, case_idx, ds);
+            list_case (c, case_idx, ds, &targets);
         }
     }
   ok = casegrouper_destroy (grouper);
@@ -252,7 +256,7 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
 
   ds_destroy(&line_buffer);
 
-  clean_up ();
+  clean_up (&targets);
 
   var_destroy (casenum_var);
 
@@ -262,9 +266,10 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
 /* Writes headers to all devices.  This is done at the beginning of
    each SPLIT FILE group. */
 static void
-write_all_headers (struct casereader *input, const struct dataset *ds)
+write_all_headers (struct casereader *input, const struct dataset *ds,
+                   struct ll_list *targets)
 {
-  struct outp_driver *d;
+  struct list_target *target;
   struct ccase *c;
 
   c = casereader_peek (input, 0);
@@ -273,12 +278,13 @@ write_all_headers (struct casereader *input, const struct dataset *ds)
   output_split_file_values (ds, c);
   case_unref (c);
 
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+  ll_for_each (target, struct list_target, ll, targets)
     {
+      struct outp_driver *d = target->driver;
       if (!d->class->special)
        {
          d->cp_y += d->font_height;            /* Blank line. */
-         write_header (d);
+         write_header (target);
        }
       else if (d->class == &html_class)
        {
@@ -304,38 +310,38 @@ write_all_headers (struct casereader *input, const struct dataset *ds)
 /* Writes the headers.  Some of them might be vertical; most are
    probably horizontal. */
 static void
-write_header (struct outp_driver *d)
+write_header (struct list_target *target)
 {
-  struct list_ext *prc = d->prc;
+  struct outp_driver *d = target->driver;
 
-  if (!prc->header_rows)
+  if (d->class->special || !target->header_rows)
     return;
 
-  if (n_lines_remaining (d) < prc->header_rows + 1)
+  if (n_lines_remaining (d) < target->header_rows + 1)
     {
       outp_eject_page (d);
-      assert (n_lines_remaining (d) >= prc->header_rows + 1);
+      assert (n_lines_remaining (d) >= target->header_rows + 1);
     }
 
   /* Design the header. */
-  if (!prc->header)
+  if (!target->header)
     {
       size_t i;
       size_t x;
 
       /* Allocate, initialize header. */
-      prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
+      target->header = xnmalloc (target->header_rows, sizeof *target->header);
       {
        int w = n_chars_width (d);
-       for (i = 0; i < prc->header_rows; i++)
+       for (i = 0; i < target->header_rows; i++)
          {
-           prc->header[i] = xmalloc (w + 1);
-           memset (prc->header[i], ' ', w);
+           target->header[i] = xmalloc (w + 1);
+           memset (target->header[i], ' ', w);
          }
       }
 
       /* Put in vertical names. */
-      for (i = x = 0; i < prc->n_vertical; i++)
+      for (i = x = 0; i < target->n_vertical; i++)
        {
          const struct variable *v = cmd.v_variables[i];
           const char *name = var_get_name (v);
@@ -343,10 +349,10 @@ write_header (struct outp_driver *d)
           const struct fmt_spec *print = var_get_print_format (v);
          size_t j;
 
-         memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
+         memset (&target->header[target->header_rows - 1][x], '-', print->w);
          x += print->w - 1;
          for (j = 0; j < name_len; j++)
-           prc->header[name_len - j - 1][x] = name[j];
+           target->header[name_len - j - 1][x] = name[j];
          x += 2;
        }
 
@@ -358,21 +364,21 @@ write_header (struct outp_driver *d)
           size_t name_len = strlen (name);
           const struct fmt_spec *print = var_get_print_format (v);
 
-         memset (&prc->header[prc->header_rows - 1][x], '-',
+         memset (&target->header[target->header_rows - 1][x], '-',
                  MAX (print->w, (int) name_len));
          if ((int) name_len < print->w)
            x += print->w - name_len;
-         memcpy (&prc->header[0][x], name, name_len);
+         memcpy (&target->header[0][x], name, name_len);
          x += name_len + 1;
        }
 
       /* Add null bytes. */
-      for (i = 0; i < prc->header_rows; i++)
+      for (i = 0; i < target->header_rows; i++)
        {
          for (x = n_chars_width (d); x >= 1; x--)
-           if (prc->header[i][x - 1] != ' ')
+           if (target->header[i][x - 1] != ' ')
              {
-               prc->header[i][x] = 0;
+               target->header[i][x] = 0;
                break;
              }
          assert (x);
@@ -380,49 +386,52 @@ write_header (struct outp_driver *d)
     }
 
   /* Write out the header, in back-to-front order except for the last line. */
-  if (prc->header_rows >= 2)
+  if (target->header_rows >= 2)
     {
       size_t i;
 
-      for (i = prc->header_rows - 1; i-- != 0; )
-        write_line (d, prc->header[i]);
+      for (i = target->header_rows - 1; i-- != 0; )
+        write_line (d, target->header[i]);
     }
-  write_line (d, prc->header[prc->header_rows - 1]);
+  write_line (d, target->header[target->header_rows - 1]);
 }
 
 
 /* Frees up all the memory we've allocated. */
 static void
-clean_up (void)
+clean_up (struct ll_list *targets)
 {
-  struct outp_driver *d;
+  struct list_target *target, *next;
 
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    if (d->class->special == 0)
-      {
-       struct list_ext *prc = d->prc;
-       size_t i;
-
-       if (prc->header)
-         {
-           for (i = 0; i < prc->header_rows; i++)
-             free (prc->header[i]);
-           free (prc->header);
-         }
-       free (prc);
-      }
-    else if (d->class == &html_class)
-      {
-       if (d->page_open)
-         {
-           struct html_driver_ext *x = d->ext;
+  ll_for_each_safe (target, next, struct list_target, ll, targets)
+    {
+      struct outp_driver *d = target->driver;
+      if (d->class->special == 0)
+        {
+          if (target->header)
+            {
+              size_t i;
+              for (i = 0; i < target->header_rows; i++)
+                free (target->header[i]);
+              free (target->header);
+            }
+        }
+      else if (d->class == &html_class)
+        {
+          if (d->page_open)
+            {
+              struct html_driver_ext *x = d->ext;
 
-           fputs ("</TABLE>\n", x->file);
-         }
-      }
-    else
-      NOT_REACHED ();
+              fputs ("</TABLE>\n", x->file);
+            }
+        }
+      else
+        NOT_REACHED ();
 
+      ll_remove (&target->ll);
+      free (target);
+    }
+  
   free (cmd.v_variables);
 }
 
@@ -541,7 +550,7 @@ write_fallback_headers (struct outp_driver *d)
    This is complicated by the fact that we have to do all this for
    every output driver, not just once.  */
 static void
-determine_layout (void)
+determine_layout (struct ll_list *targets)
 {
   struct outp_driver *d;
 
@@ -549,6 +558,7 @@ determine_layout (void)
      size buffer to allocate. */
   int largest_page_width = 0;
 
+  ll_init (targets);
   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
     {
       size_t column;   /* Current column. */
@@ -556,11 +566,17 @@ determine_layout (void)
       int height;       /* Height of vertical names. */
       int max_width;   /* Page width. */
 
-      struct list_ext *prc;
+      struct list_target *target;
+
+      target = xmalloc (sizeof *target);
+      ll_push_tail (targets, &target->ll);
+      target->driver = d;
+      target->type = 0;
+      target->n_vertical = 0;
+      target->header = NULL;
 
       if (d->class == &html_class)
        continue;
-
       assert (d->class->special == 0);
 
       outp_open_page (d);
@@ -568,11 +584,6 @@ determine_layout (void)
       max_width = n_chars_width (d);
       largest_page_width = MAX (largest_page_width, max_width);
 
-      prc = d->prc = xmalloc (sizeof *prc);
-      prc->type = 0;
-      prc->n_vertical = 0;
-      prc->header = NULL;
-
       /* Try layout #1. */
       for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
        {
@@ -583,7 +594,7 @@ determine_layout (void)
        }
       if (width <= max_width)
        {
-         prc->header_rows = 2;
+         target->header_rows = 2;
          continue;
        }
 
@@ -605,7 +616,7 @@ determine_layout (void)
       if (width <= max_width && height <= SHORT_NAME_LEN)
        {
 #ifndef NDEBUG
-         prc->n_vertical = SIZE_MAX;
+         target->n_vertical = SIZE_MAX;
 #endif
          for (column = cmd.n_variables; column-- != 0; )
            {
@@ -615,30 +626,30 @@ determine_layout (void)
              int trial_width = width - fmt_width + MAX (fmt_width, name_len);
              if (trial_width > max_width)
                {
-                 prc->n_vertical = column + 1;
+                 target->n_vertical = column + 1;
                  break;
                }
              width = trial_width;
            }
-         assert (prc->n_vertical != SIZE_MAX);
+         assert (target->n_vertical != SIZE_MAX);
 
-         prc->n_vertical = cmd.n_variables;
+         target->n_vertical = cmd.n_variables;
          /* Finally determine the length of the headers. */
-         for (prc->header_rows = 0, column = 0;
-              column < prc->n_vertical;
+         for (target->header_rows = 0, column = 0;
+              column < target->n_vertical;
               column++)
             {
               const struct variable *var = cmd.v_variables[column];
               size_t name_len = strlen (var_get_name (var));
-              prc->header_rows = MAX (prc->header_rows, name_len);
+              target->header_rows = MAX (target->header_rows, name_len);
             }
-         prc->header_rows++;
+         target->header_rows++;
          continue;
        }
 
       /* Otherwise use the ugly fallback listing format. */
-      prc->type = 1;
-      prc->header_rows = 0;
+      target->type = 1;
+      target->header_rows = 0;
 
       d->cp_y += d->font_height;
       write_fallback_headers (d);
@@ -651,122 +662,123 @@ determine_layout (void)
 /* Writes case C to output. */
 static void
 list_case (const struct ccase *c, casenumber case_idx,
-           const struct dataset *ds)
+           const struct dataset *ds, struct ll_list *targets)
 {
   struct dictionary *dict = dataset_dict (ds);
-  struct outp_driver *d;
+  const char *encoding = dict_get_encoding (dict);
+  struct list_target *target;
 
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    if (d->class->special == 0)
-      {
-       const struct list_ext *prc = d->prc;
-       const int max_width = n_chars_width (d);
-       int column;
-
-       if (!prc->header_rows)
-         {
-           ds_put_format(&line_buffer, "%8s: ",
-                          var_get_name (cmd.v_variables[0]));
-         }
-
-
-       for (column = 0; column < cmd.n_variables; column++)
-         {
-           const struct variable *v = cmd.v_variables[column];
-            const struct fmt_spec *print = var_get_print_format (v);
-           int width;
-
-           if (prc->type == 0 && column >= prc->n_vertical)
-              {
-                int name_len = strlen (var_get_name (v));
-                width = MAX (name_len, print->w);
-              }
-           else
-             width = print->w;
-
-           if (width + ds_length(&line_buffer) > max_width &&
-               ds_length(&line_buffer) != 0)
-             {
-               if (!n_lines_remaining (d))
-                 {
-                   outp_eject_page (d);
-                   write_header (d);
-                 }
+  ll_for_each (target, struct list_target, ll, targets)
+    {
+      struct outp_driver *d = target->driver;
 
-               write_line (d, ds_cstr (&line_buffer));
-               ds_clear(&line_buffer);
+      if (d->class->special == 0)
+        {
+          const int max_width = n_chars_width (d);
+          int column;
 
-               if (!prc->header_rows)
-                  ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
-             }
+          if (!target->header_rows)
+            {
+              ds_put_format(&line_buffer, "%8s: ",
+                            var_get_name (cmd.v_variables[0]));
+            }
 
-           if (width > print->w)
-              ds_put_char_multiple(&line_buffer, ' ', width - print->w);
 
-            if (fmt_is_string (print->type)
-                || dict_contains_var (dict, v))
-             {
-               char *s = data_out (case_data (c, v), dict_get_encoding (dict), print);
-               ds_put_cstr (&line_buffer, s);
-               free (s);
-             }
-            else
-              {
-               char *s;
-                union value case_idx_value;
-                case_idx_value.f = case_idx;
-                s = data_out (&case_idx_value, dict_get_encoding (dict), print);
-               ds_put_cstr (&line_buffer, s);
-               free (s);
-              }
-
-           ds_put_char (&line_buffer, ' ');
-         }
+          for (column = 0; column < cmd.n_variables; column++)
+            {
+              const struct variable *v = cmd.v_variables[column];
+              const struct fmt_spec *print = var_get_print_format (v);
+              int width;
+              char *s;
+
+              if (target->type == 0 && column >= target->n_vertical)
+                {
+                  int name_len = strlen (var_get_name (v));
+                  width = MAX (name_len, print->w);
+                }
+              else
+                width = print->w;
+
+              if (width + ds_length(&line_buffer) > max_width &&
+                  ds_length(&line_buffer) != 0)
+                {
+                  if (!n_lines_remaining (d))
+                    {
+                      outp_eject_page (d);
+                      write_header (target);
+                    }
+
+                  write_line (d, ds_cstr (&line_buffer));
+                  ds_clear(&line_buffer);
+
+                  if (!target->header_rows)
+                    ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
+                }
+
+              if (width > print->w)
+                ds_put_char_multiple(&line_buffer, ' ', width - print->w);
+
+              if (fmt_is_string (print->type) || dict_contains_var (dict, v))
+                s = data_out (case_data (c, v), encoding, print);
+              else
+                {
+                  union value case_idx_value;
+                  case_idx_value.f = case_idx;
+                  s = data_out (&case_idx_value, encoding, print);
+                }
+
+              ds_put_cstr (&line_buffer, s);
+              free (s);
+              ds_put_char(&line_buffer, ' ');
+            }
 
-       if (!n_lines_remaining (d))
-         {
-           outp_eject_page (d);
-           write_header (d);
-         }
+          if (!n_lines_remaining (d))
+            {
+              outp_eject_page (d);
+              write_header (target);
+            }
 
-       write_line (d, ds_cstr (&line_buffer));
-       ds_clear(&line_buffer);
-      }
-    else if (d->class == &html_class)
-      {
-       struct html_driver_ext *x = d->ext;
-       int column;
+          write_line (d, ds_cstr (&line_buffer));
+          ds_clear(&line_buffer);
+        }
+      else if (d->class == &html_class)
+        {
+          struct html_driver_ext *x = d->ext;
+          int column;
 
-       fputs ("  <TR>\n", x->file);
+          fputs ("  <TR>\n", x->file);
 
-       for (column = 0; column < cmd.n_variables; column++)
-         {
-           const struct variable *v = cmd.v_variables[column];
-            const struct fmt_spec *print = var_get_print_format (v);
-           char *s = NULL;
-
-            if (fmt_is_string (print->type)
-                || dict_contains_var (dict, v))
-             s = data_out (case_data (c, v), dict_get_encoding (dict), print);
-            else
-              {
-                union value case_idx_value;
-                case_idx_value.f = case_idx;
-                s = data_out (&case_idx_value, dict_get_encoding (dict), print);
-              }
-
-            fputs ("    <TD>", x->file);
-            html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
-           free (s);
-            fputs ("</TD>\n", x->file);
-         }
+          for (column = 0; column < cmd.n_variables; column++)
+            {
+              const struct variable *v = cmd.v_variables[column];
+              const struct fmt_spec *print = var_get_print_format (v);
+              char *s;
+
+              if (fmt_is_string (print->type)
+                  || dict_contains_var (dict, v))
+                s = data_out (case_data (c, v), encoding, print);
+              else
+                {
+                  union value case_idx_value;
+                  case_idx_value.f = case_idx;
+                  s = data_out (&case_idx_value, encoding, print);
+                }
+
+              fputs ("    <TD>", x->file);
+              html_put_cell_contents (d, TAB_FIX, ss_cstr (s));
+              fputs ("</TD>\n", x->file);
+
+              free (s);
+            }
 
-       fputs ("  </TR>\n", x->file);
-      }
-    else
-      NOT_REACHED ();
+          fputs ("  </TR>\n", x->file);
+        }
+      else
+        NOT_REACHED ();
+    }
 }
 
+
 /*
    Local Variables:
    mode: c
index 2cfa0177c77950f26495aa9a5bd421ab8df73488..9c8f56e84289196182fc10bb8599e26a35c8f3b3 100644 (file)
@@ -396,8 +396,8 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   int row;
 
   spec_cnt = ll_count (&trns->specs);
-  t = tab_create (4, spec_cnt + 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  t = tab_create (4, spec_cnt + 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, spec_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
   tab_headers (t, 0, 0, 1, 0);
@@ -405,7 +405,7 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   row = 1;
   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
     {
index 5d2b42d758eed2d1e305d05046d59cf0aa290877..d2f59ccc813fe8661435bb03bee0cbf1a01caa20 100644 (file)
@@ -77,8 +77,8 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
   if (split_cnt == 0)
     return;
 
-  t = tab_create (3, split_cnt + 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  t = tab_create (3, split_cnt + 1);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   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"));
@@ -95,6 +95,7 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
       tab_text_format (t, 0, i + 1, TAB_LEFT, "%s", var_get_name (v));
 
       s = data_out (case_data (c, v), dict_get_encoding (dict), print);
+
       tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, s);
 
       free (s);
index f68a830b83810ba6eec7d4e73b42ab40b041ca91..6c44a0c3baaefefbda7129b081442468106d7c9c 100644 (file)
@@ -69,17 +69,18 @@ static int describe_variable (const struct variable *v, struct tab_table *t,
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-sysfile_info_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+sysfile_info_dim (struct tab_rendering *r, void *aux UNUSED)
 {
+  const struct tab_table *t = r->table;
   static const int max[] = {20, 5, 35, 3, 0};
   const int *p;
   int i;
 
   for (p = max; *p; p++)
-    t->w[p - max] = MIN (tab_natural_width (t, d, p - max),
-                        *p * d->prop_em_width);
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+    r->w[p - max] = MIN (tab_natural_width (r, p - max),
+                         *p * r->driver->prop_em_width);
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = tab_natural_height (r, i);
 }
 
 /* SYSFILE INFO utility. */
@@ -108,7 +109,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
     }
   casereader_destroy (reader);
 
-  t = tab_create (2, 11, 0);
+  t = tab_create (2, 11);
   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_file_name (h));
@@ -160,11 +161,11 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
                    dict_get_encoding(d) ? dict_get_encoding(d) : _("Unknown"));
 
 
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_submit (t);
 
-  t = tab_create (4, 1 + 2 * dict_get_var_cnt (d), 1);
-  tab_dim (t, sysfile_info_dim, NULL);
+  t = tab_create (4, 1 + 2 * dict_get_var_cnt (d));
+  tab_dim (t, sysfile_info_dim, NULL, NULL);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
   tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
@@ -339,44 +340,45 @@ display_documents (const struct dictionary *dict)
     }
 }
 
-static int _flags;
+struct variables_dim_aux
+  {
+    int flags;
+  };
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-variables_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+variables_dim (struct tab_rendering *r, void *aux_)
 {
-  int pc;
-  int i;
+  const struct outp_driver *d = r->driver;
+  struct variables_dim_aux *aux = aux_;
 
-  t->w[0] = tab_natural_width (t, d, 0);
-  if (_flags & (DF_VALUE_LABELS | DF_VARIABLE_LABELS | DF_MISSING_VALUES
-                | DF_AT_ATTRIBUTES | DF_ATTRIBUTES))
+  tab_natural_dimensions (r, NULL);
+  if (aux->flags & (DF_VALUE_LABELS | DF_VARIABLE_LABELS | DF_MISSING_VALUES
+                    | DF_AT_ATTRIBUTES | DF_ATTRIBUTES))
     {
-      t->w[1] = MAX (tab_natural_width (t, d, 1), d->prop_em_width * 5);
-      t->w[2] = MAX (tab_natural_width (t, d, 2), d->prop_em_width * 35);
-      pc = 3;
+      r->w[1] = MAX (r->w[1], d->prop_em_width * 5);
+      r->w[2] = MAX (r->w[2], d->prop_em_width * 35);
     }
-  else
-    pc = 1;
-  if (_flags & DF_DICT_INDEX)
-    t->w[pc] = tab_natural_width (t, d, pc);
+}
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+static void
+variables_dim_free (void *aux_)
+{
+  struct variables_dim_aux *aux = aux_;
+  free (aux);
 }
 
 static void
 display_variables (const struct variable **vl, size_t n, int flags)
 {
   struct tab_table *t;
+  struct variables_dim_aux *aux;
   int nc;                      /* Number of columns. */
   int pc;                      /* `Position column' */
   int r;                       /* Current row. */
   size_t i;
 
-  _flags = flags;
-
   /* One column for the name,
      two columns for general description,
      one column for dictionary index. */
@@ -387,7 +389,7 @@ display_variables (const struct variable **vl, size_t n, int flags)
   if (flags & DF_DICT_INDEX)
     nc++;
 
-  t = tab_create (nc, n + 5, 1);
+  t = tab_create (nc, n + 5);
   tab_headers (t, 0, 0, 1, 0);
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
@@ -397,7 +399,10 @@ display_variables (const struct variable **vl, size_t n, int flags)
                      ? _("Description") : _("Label")));
   if (flags & DF_DICT_INDEX)
     tab_text (t, pc, 0, TAB_LEFT | TAT_TITLE, _("Position"));
-  tab_dim (t, variables_dim, NULL);
+
+  aux = xmalloc (sizeof *aux);
+  aux->flags = flags;
+  tab_dim (t, variables_dim, variables_dim_free, aux);
 
   r = 1;
   for (i = 0; i < n; i++)
@@ -413,7 +418,7 @@ display_variables (const struct variable **vl, size_t n, int flags)
   if (flags & ~DF_DICT_INDEX)
     tab_vline (t, TAL_1, nc - 1, 0, r - 1);
   tab_resize (t, -1, r);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_submit (t);
 }
 \f
@@ -478,15 +483,15 @@ display_data_file_attributes (struct attrset *set, int flags)
   if (!n_attrs)
     return;
 
-  t = tab_create (2, n_attrs + 1, 0);
+  t = tab_create (2, n_attrs + 1);
   tab_headers (t, 0, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, -1, TAL_1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
   tab_hline (t, TAL_2, 0, 1, 1); 
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Attribute"));
   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Value"));
   display_attributes (t, set, flags, 0, 1);
-  tab_columns (t, TAB_COL_DOWN, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_columns (t, TAB_COL_DOWN);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_title (t, "Custom data file attributes.");
   tab_submit (t);
 }
@@ -713,10 +718,10 @@ display_vectors (const struct dictionary *dict, int sorted)
   if (sorted)
     qsort (vl, nvec, sizeof *vl, compare_vector_ptrs_by_name);
 
-  t = tab_create (4, nrow + 1, 0);
+  t = tab_create (4, nrow + 1);
   tab_headers (t, 0, 0, 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_columns (t, TAB_COL_DOWN);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, nrow);
   tab_box (t, -1, -1, -1, TAL_1, 0, 0, 3, nrow);
   tab_hline (t, TAL_2, 0, 3, 1);
index 1194110efa3ac850ed1584451e2782ec12870b83..8a39dbf0b2d2d6b6684766d8352d4cbf02cfdd59 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -757,7 +757,7 @@ array_var_set_get_var (const struct var_set *vs, size_t idx)
 {
   struct array_var_set *avs = vs->aux;
 
-  return (struct variable *) avs->var[idx];
+  return CONST_CAST (struct variable *, avs->var[idx]);
 }
 
 /* If VS contains a variable named NAME, sets *IDX to its index
index 08d2f5e15c7d06176f1787b5e81017c7da54ad83..891e0ccaa3cb8f7c4bb4ba0da73feb1d456a1843 100644 (file)
@@ -965,20 +965,20 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case MEDIAN:
            {
              struct casereader *sorted_reader;
-             struct order_stats *median = percentile_create (0.5, i->cc);
+             struct percentile *median = percentile_create (0.5, i->cc);
+              struct order_stats *os = &median->parent;
 
              sorted_reader = casewriter_make_reader (i->writer);
 
-             order_stats_accumulate (&median, 1,
+             order_stats_accumulate (&os, 1,
                                      sorted_reader,
                                      i->weight,
                                      i->subject,
                                      i->exclude);
 
-             v->f = percentile_calculate ((struct percentile *) median,
-                                          PC_HAVERAGE);
+             v->f = percentile_calculate (median, PC_HAVERAGE);
 
-             statistic_destroy ((struct statistic *) median);
+             statistic_destroy (&median->parent.parent);
            }
            break;
          case SD:
index 26e0257ca14030ca3d58a2ae10acd8f960b45d2c..bc87a94da3316b0c0cf0b99c0035092a7d93a8cb 100644 (file)
@@ -187,16 +187,16 @@ binomial_execute (const struct dataset *ds,
       const struct fmt_spec *wfmt = wvar ?
        var_get_print_format (wvar) : & F_8_0;
 
-      struct tab_table *table = tab_create (7, ost->n_vars * 3 + 1, 0);
+      struct tab_table *table = tab_create (7, ost->n_vars * 3 + 1);
 
-      tab_dim (table, tab_natural_dimensions, NULL);
+      tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
       tab_title (table, _("Binomial Test"));
 
       tab_headers (table, 2, 0, 1, 0);
 
       tab_box (table, TAL_1, TAL_1, -1, TAL_1,
-               0, 0, table->nc - 1, tab_nr(table) - 1 );
+               0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
       for (v = 0 ; v < ost->n_vars; ++v)
         {
index 4593df4116eda81be5230a64ee2eee6c5f9fa15f..bc1b6474ff16c167d06ea94ca55de828475d7a9e 100644 (file)
@@ -31,6 +31,7 @@
 #include <language/stats/freq.h>
 #include <language/stats/npar.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
 #include <libpspp/message.h>
@@ -179,8 +180,8 @@ create_variable_frequency_table (const struct dictionary *dict,
       return NULL;
     }
 
-  table = tab_create(4, n_cells + 2, 0);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  table = tab_create(4, n_cells + 2);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, var_to_string(var));
   tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
@@ -190,7 +191,7 @@ create_variable_frequency_table (const struct dictionary *dict,
   tab_headers (table, 1, 0, 1, 0);
 
   tab_box (table, TAL_1, TAL_1, -1, -1,
-          0, 0, table->nc - 1, tab_nr(table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 1);
 
@@ -199,7 +200,7 @@ create_variable_frequency_table (const struct dictionary *dict,
     tab_vline (table, TAL_1, i, 0, tab_nr(table) - 1);
 
 
-  tab_text (table, 0, table->nr - 1, TAB_LEFT, _("Total"));
+  tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
 
   return table;
 }
@@ -215,8 +216,8 @@ create_combo_frequency_table (const struct chisquare_test *test)
 
   int n_cells = test->hi - test->lo + 1;
 
-  table = tab_create(1 + ost->n_vars * 4, n_cells + 3, 0);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  table = tab_create(1 + ost->n_vars * 4, n_cells + 3);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Frequencies"));
   for ( i = 0 ; i < ost->n_vars ; ++i )
@@ -254,12 +255,12 @@ create_combo_frequency_table (const struct chisquare_test *test)
   tab_headers (table, 1, 0, 2, 0);
 
   tab_box (table, TAL_1, TAL_1, -1, -1,
-          0, 0, table->nc - 1, tab_nr(table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
   tab_hline (table, TAL_1, 1, tab_nc(table) - 1, 1);
   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 2);
 
-  tab_text (table, 0, table->nr - 1, TAB_LEFT, _("Total"));
+  tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
 
   return table;
 }
@@ -271,8 +272,8 @@ create_stats_table (const struct chisquare_test *test)
   const struct one_sample_test *ost = (const struct one_sample_test*) test;
 
   struct tab_table *table;
-  table = tab_create (1 + ost->n_vars, 4, 0);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  table = tab_create (1 + ost->n_vars, 4);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
   tab_title (table, _("Test Statistics"));
   tab_headers (table, 1, 0, 1, 0);
 
@@ -305,8 +306,9 @@ chisquare_execute (const struct dataset *ds,
 {
   const struct dictionary *dict = dataset_dict (ds);
   int v, i;
-  struct one_sample_test *ost = (struct one_sample_test *) test;
-  struct chisquare_test *cst = (struct chisquare_test *) test;
+  struct chisquare_test *cst = UP_CAST (test, struct chisquare_test,
+                                        parent.parent);
+  struct one_sample_test *ost = &cst->parent;
   int n_cells = 0;
   double total_expected = 0.0;
   const struct variable *wvar = dict_get_weight (dict);
index 605609a7d68cc559e42cc605cf2543f49e9a441d..c575205ccf2e8b8858e06383854e13f340baaf98 100644 (file)
@@ -94,9 +94,9 @@ output_descriptives (const struct corr *corr, const gsl_matrix *means,
   const int heading_columns = 1;
   const int heading_rows = 1;
 
-  struct tab_table *t = tab_create (nc, nr, 0);
+  struct tab_table *t = tab_create (nc, nr);
   tab_title (t, _("Descriptive Statistics"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -189,9 +189,9 @@ output_correlation (const struct corr *corr, const struct corr_opts *opts,
   /* One header row */
   nr += heading_rows;
 
-  t = tab_create (nc, nr, 0);
+  t = tab_create (nc, nr);
   tab_title (t, _("Correlations"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
index 99fa41d9c056a039ef9d92fdc9090758539c9d15..5695ed6478b64490095ea0c8abde83873fb38030 100644 (file)
@@ -201,6 +201,12 @@ struct crosstabs_proc
     unsigned int statistics;    /* Bit k is 1 if statistic k is requested. */
   };
 
+/* Auxiliary data structure for tab_dim. */
+struct crosstabs_dim_aux
+  {
+    enum mv_class exclude;
+  };
+
 static void
 init_proc (struct crosstabs_proc *proc, struct dataset *ds)
 {
@@ -230,8 +236,8 @@ free_proc (struct crosstabs_proc *proc)
 
          The rest of the data was allocated and destroyed at a
          lower level already. */
-      free (pt);
     }
+  free (proc->pivots);
 }
 
 static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds,
@@ -836,7 +842,7 @@ make_summary_table (struct crosstabs_proc *proc)
   struct string name;
   int i;
 
-  summary = tab_create (7, 3 + proc->n_pivots, 1);
+  summary = tab_create (7, 3 + proc->n_pivots);
   tab_title (summary, _("Summary."));
   tab_headers (summary, 1, 0, 3, 0);
   tab_joint_text (summary, 1, 0, 6, 0, TAB_CENTER, _("Cases"));
@@ -914,8 +920,8 @@ static void display_symmetric (struct crosstabs_proc *, struct pivot_table *,
 static void display_risk (struct pivot_table *, struct tab_table *);
 static void display_directional (struct crosstabs_proc *, struct pivot_table *,
                                  struct tab_table *);
-static void crosstabs_dim (struct tab_table *, struct outp_driver *,
-                           void *proc);
+static void crosstabs_dim (struct tab_rendering *, void *aux);
+static void crosstabs_dim_free (void *aux);
 static void table_value_missing (struct crosstabs_proc *proc,
                                  struct tab_table *table, int c, int r,
                                 unsigned char opt, const union value *v,
@@ -1147,8 +1153,7 @@ create_crosstab_table (struct crosstabs_proc *proc, struct pivot_table *pt)
   int i;
 
   table = tab_create (pt->n_consts + 1 + pt->n_cols + 1,
-                      (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10,
-                      true);
+                      (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10);
   tab_headers (table, pt->n_consts + 1, 0, 2, 0);
 
   /* First header line. */
@@ -1224,8 +1229,7 @@ create_chisq_table (struct pivot_table *pt)
   struct tab_table *chisq;
 
   chisq = tab_create (6 + (pt->n_vars - 2),
-                      pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10,
-                      1);
+                      pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10);
   tab_headers (chisq, 1 + (pt->n_vars - 2), 0, 1, 0);
 
   tab_title (chisq, _("Chi-square tests."));
@@ -1252,7 +1256,7 @@ create_sym_table (struct pivot_table *pt)
   struct tab_table *sym;
 
   sym = tab_create (6 + (pt->n_vars - 2),
-                    pt->n_entries / pt->n_cols * 7 + 10, 1);
+                    pt->n_entries / pt->n_cols * 7 + 10);
   tab_headers (sym, 2 + (pt->n_vars - 2), 0, 1, 0);
   tab_title (sym, _("Symmetric measures."));
 
@@ -1274,8 +1278,7 @@ create_risk_table (struct pivot_table *pt)
 {
   struct tab_table *risk;
 
-  risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10,
-                     1);
+  risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10);
   tab_headers (risk, 1 + pt->n_vars - 2, 0, 2, 0);
   tab_title (risk, _("Risk estimate."));
 
@@ -1300,7 +1303,7 @@ create_direct_table (struct pivot_table *pt)
   struct tab_table *direct;
 
   direct = tab_create (7 + (pt->n_vars - 2),
-                       pt->n_entries / pt->n_cols * 7 + 10, 1);
+                       pt->n_entries / pt->n_cols * 7 + 10);
   tab_headers (direct, 3 + (pt->n_vars - 2), 0, 1, 0);
   tab_title (direct, _("Directional measures."));
 
@@ -1348,6 +1351,7 @@ static void
 submit (struct crosstabs_proc *proc, struct pivot_table *pt,
         struct tab_table *t)
 {
+  struct crosstabs_dim_aux *aux;
   int i;
 
   if (t == NULL)
@@ -1370,33 +1374,39 @@ submit (struct crosstabs_proc *proc, struct pivot_table *pt,
   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, proc);
+
+  aux = xmalloc (sizeof *aux);
+  aux->exclude = proc->exclude;
+  tab_dim (t, crosstabs_dim, crosstabs_dim_free, aux);
+
   tab_submit (t);
 }
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-crosstabs_dim (struct tab_table *t, struct outp_driver *d, void *proc_)
+crosstabs_dim (struct tab_rendering *r, void *aux_)
 {
-  struct crosstabs_proc *proc = proc_;
+  const struct tab_table *t = r->table;
+  struct outp_driver *d = r->driver;
+  struct crosstabs_dim_aux *aux = aux_;
   int i;
 
   /* Width of a numerical column. */
   int c = outp_string_width (d, "0.000000", OUTP_PROPORTIONAL);
-  if (proc->exclude == MV_NEVER)
+  if (aux->exclude == MV_NEVER)
     c += outp_string_width (d, "M", OUTP_PROPORTIONAL);
 
   /* Set width for header columns. */
-  if (t->l != 0)
+  if (tab_l (t) != 0)
     {
       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;
+      w = d->width - c * (tab_nc (t) - tab_l (t));
+      for (i = 0; i <= tab_nc (t); i++)
+        w -= r->wrv[i];
+      w /= tab_l (t);
 
       if (w < d->prop_em_width * 8)
        w = d->prop_em_width * 8;
@@ -1404,15 +1414,22 @@ crosstabs_dim (struct tab_table *t, struct outp_driver *d, void *proc_)
       if (w > d->prop_em_width * 15)
        w = d->prop_em_width * 15;
 
-      for (i = 0; i < t->l; i++)
-       t->w[i] = w;
+      for (i = 0; i < tab_l (t); i++)
+       r->w[i] = w;
     }
 
-  for (i = t->l; i < t->nc; i++)
-    t->w[i] = c;
+  for (i = tab_l (t); i < tab_nc (t); i++)
+    r->w[i] = c;
+
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = tab_natural_height (r, i);
+}
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+static void
+crosstabs_dim_free (void *aux_)
+{
+  struct crosstabs_dim_aux *aux = aux_;
+  free (aux);
 }
 
 static bool
@@ -1513,26 +1530,21 @@ table_value_missing (struct crosstabs_proc *proc,
                      struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
-  struct substring s;
-  const struct fmt_spec *print = var_get_print_format (var);
-
   const char *label = var_lookup_value_label (var, v);
-  if (label)
-    {
-      tab_text (table, c, r, TAB_LEFT, label);
-      return;
-    }
-
-  s = ss_cstr (data_out_pool (v, dict_get_encoding (proc->dict), print,
-                            table->container));
-  if (proc->exclude == MV_NEVER && var_is_num_missing (var, v->f, MV_USER))
-    s.string[s.length++] = 'M';
-  while (s.length && *s.string == ' ')
+  if (label != NULL)
+    tab_text (table, c, r, TAB_LEFT, label);
+  else
     {
-      s.length--;
-      s.string++;
+      const struct fmt_spec *print = var_get_print_format (var);
+      if (proc->exclude == MV_NEVER && var_is_value_missing (var, v, MV_USER))
+        {
+          char *s = data_out (v, dict_get_encoding (proc->dict), print);
+          tab_text_format (table, c, r, opt, "%sM", s + strspn (s, " "));
+          free (s);
+        }
+      else
+        tab_value (table, c, r, opt, v, proc->dict, print);
     }
-  tab_raw (table, c, r, opt, &s);
 }
 
 /* Draws a line across TABLE at the current row to indicate the most
@@ -1561,22 +1573,22 @@ format_cell_entry (struct tab_table *table, int c, int r, double value,
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
-  struct substring s;
+  char suffixes[3];
+  int suffix_len;
+  char *s;
 
   v.f = value;
-  s = ss_cstr (data_out_pool (&v, dict_get_encoding (dict), &f, table->container));
+  s = data_out (&v, dict_get_encoding (dict), &f);
 
-  while (*s.string == ' ')
-    {
-      s.length--;
-      s.string++;
-    }
+  suffix_len = 0;
   if (suffix != 0)
-    s.string[s.length++] = suffix;
+    suffixes[suffix_len++] = suffix;
   if (mark_missing)
-    s.string[s.length++] = 'M';
+    suffixes[suffix_len++] = 'M';
+  suffixes[suffix_len] = '\0';
 
-  tab_raw (table, c, r, TAB_RIGHT, &s);
+  tab_text_format (table, c, r, TAB_RIGHT, "%s%s",
+                   s + strspn (s, " "), suffixes);
 }
 
 /* Displays the crosstabulation table. */
index 6b57fd297d8475ad63bcecc320a8c0c1b2d9201f..e78d771e147a95ea5630360d8e9c120984f96a14 100644 (file)
@@ -552,15 +552,15 @@ dump_z_table (struct dsc_proc *dsc)
        cnt++;
   }
 
-  t = tab_create (2, cnt + 1, 0);
+  t = tab_create (2, cnt + 1);
   tab_title (t, _("Mapping of variables to corresponding Z-scores."));
-  tab_columns (t, SOM_COL_DOWN, 1);
+  tab_columns (t, SOM_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   {
     size_t i, y;
@@ -873,13 +873,13 @@ display (struct dsc_proc *dsc)
     sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
           descriptives_compare_dsc_vars, dsc);
 
-  t = tab_create (nc, dsc->var_cnt + 1, 0);
+  t = tab_create (nc, dsc->var_cnt + 1);
   tab_headers (t, 1, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
   tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   nc = 0;
   tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
index 08077942340234031939cded549e87886d5119ee..4ca3af966505080338b2ff85679885ed1bd7a963 100644 (file)
@@ -48,8 +48,8 @@
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
 #include <math/moments.h>
-#include <output/charts/box-whisker.h>
-#include <output/charts/cartesian.h>
+#include <output/charts/boxplot.h>
+#include <output/charts/np-plot.h>
 #include <output/manager.h>
 #include <output/table.h>
 
@@ -104,11 +104,11 @@ struct factor_metrics
   struct percentile **ptl;
   size_t n_ptiles;
 
-  struct statistic *tukey_hinges;
-  struct statistic *box_whisker;
-  struct statistic *trimmed_mean;
-  struct statistic *histogram;
-  struct order_stats *np;
+  struct tukey_hinges *tukey_hinges;
+  struct box_whisker *box_whisker;
+  struct trimmed_mean *trimmed_mean;
+  struct histogram *histogram;
+  struct np *np;
 
   /* Three quartiles indexing into PTL */
   struct percentile **quartiles;
@@ -179,12 +179,12 @@ factor_destroy (struct xfactor *fctr)
          moments1_destroy (result->metrics[v].moments);
          extrema_destroy (result->metrics[v].minima);
          extrema_destroy (result->metrics[v].maxima);
-         statistic_destroy (result->metrics[v].trimmed_mean);
-         statistic_destroy (result->metrics[v].tukey_hinges);
-         statistic_destroy (result->metrics[v].box_whisker);
-         statistic_destroy (result->metrics[v].histogram);
+         statistic_destroy (&result->metrics[v].trimmed_mean->parent.parent);
+         statistic_destroy (&result->metrics[v].tukey_hinges->parent.parent);
+         statistic_destroy (&result->metrics[v].box_whisker->parent.parent);
+         statistic_destroy (&result->metrics[v].histogram->parent);
          for (i = 0 ; i < result->metrics[v].n_ptiles; ++i)
-           statistic_destroy ((struct statistic *) result->metrics[v].ptl[i]);
+           statistic_destroy (&result->metrics[v].ptl[i]->parent.parent);
          free (result->metrics[v].ptl);
          free (result->metrics[v].quartiles);
          casereader_destroy (result->metrics[v].up_reader);
@@ -320,82 +320,6 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 };
 
 
-/* Plot the normal and detrended normal plots for RESULT.
-   Label the plots with LABEL */
-static void
-np_plot (struct np *np, const char *label)
-{
-  double yfirst = 0, ylast = 0;
-
-  double x_lower;
-  double x_upper;
-  double slack;
-
-  /* Normal Plot */
-  struct chart *np_chart;
-
-  /* Detrended Normal Plot */
-  struct chart *dnp_chart;
-
-  /* The slope and intercept of the ideal normal probability line */
-  const double slope = 1.0 / np->stddev;
-  const double intercept = -np->mean / np->stddev;
-
-  if ( np->n < 1.0 )
-    {
-      msg (MW, _("Not creating plot because data set is empty."));
-      return ;
-    }
-
-  np_chart = chart_create ();
-  dnp_chart = chart_create ();
-
-  if ( !np_chart || ! dnp_chart )
-    return ;
-
-  chart_write_title (np_chart, _("Normal Q-Q Plot of %s"), label);
-  chart_write_xlabel (np_chart, _("Observed Value"));
-  chart_write_ylabel (np_chart, _("Expected Normal"));
-
-  chart_write_title (dnp_chart, _("Detrended Normal Q-Q Plot of %s"),
-                    label);
-  chart_write_xlabel (dnp_chart, _("Observed Value"));
-  chart_write_ylabel (dnp_chart, _("Dev from Normal"));
-
-  yfirst = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
-  ylast = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
-
-  /* Need to make sure that both the scatter plot and the ideal fit into the
-     plot */
-  x_lower = MIN (np->y_min, (yfirst - intercept) / slope) ;
-  x_upper = MAX (np->y_max, (ylast  - intercept) / slope) ;
-  slack = (x_upper - x_lower)  * 0.05 ;
-
-  chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
-  chart_write_xscale (dnp_chart, np->y_min, np->y_max, 5);
-
-  chart_write_yscale (np_chart, yfirst, ylast, 5);
-  chart_write_yscale (dnp_chart, np->dns_min, np->dns_max, 5);
-
-  {
-    struct casereader *reader = casewriter_make_reader (np->writer);
-    struct ccase *c;
-    while ((c = casereader_read (reader)) != NULL)
-      {
-       chart_datum (np_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_NS)->f);
-       chart_datum (dnp_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_DNS)->f);
-
-       case_unref (c);
-      }
-    casereader_destroy (reader);
-  }
-
-  chart_line (dnp_chart, 0, 0, np->y_min, np->y_max , CHART_DIM_X);
-  chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
-
-  chart_submit (np_chart);
-  chart_submit (dnp_chart);
-}
 
 
 static void
@@ -412,20 +336,37 @@ show_npplot (const struct variable **dependent_var,
           ll != ll_null (&fctr->result_list);
           ll = ll_next (ll))
        {
-         struct string str;
+         struct string label;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
-
-         ds_init_empty (&str);
-         ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
-
-         factor_to_string (fctr, result, &str);
-
-         np_plot ((struct np*) result->metrics[v].np, ds_cstr(&str));
-
-         statistic_destroy ((struct statistic *)result->metrics[v].np);
-
-         ds_destroy (&str);
+          struct chart *npp, *dnpp;
+          struct casereader *reader;
+          struct np *np;
+
+         ds_init_empty (&label);
+         ds_put_format (&label, "%s ", var_get_name (dependent_var[v]));
+         factor_to_string (fctr, result, &label);
+
+          np = result->metrics[v].np;
+          reader = casewriter_make_reader (np->writer);
+          npp = np_plot_create (np, reader, ds_cstr (&label));
+          dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
+
+         ds_destroy (&label);
+
+          if (npp == NULL || dnpp == NULL)
+            {
+              msg (MW, _("Not creating NP plot because data set is empty."));
+              chart_unref (npp);
+              chart_unref (dnpp);
+            }
+          else
+            {
+              chart_submit (npp);
+              chart_submit (dnpp);
+            }
+
+         statistic_destroy (&np->parent.parent);
        }
     }
 }
@@ -448,15 +389,25 @@ show_histogram (const struct variable **dependent_var,
          struct string str;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
+          struct histogram *histogram;
+          double mean, var, n;
+
+          histogram = result->metrics[v].histogram;
+          if (histogram == NULL)
+            {
+              /* Probably all values are SYSMIS. */
+              continue;
+            }
 
          ds_init_empty (&str);
          ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
 
          factor_to_string (fctr, result, &str);
 
-         histogram_plot ((struct histogram *) result->metrics[v].histogram,
-                         ds_cstr (&str),
-                         (struct moments1 *) result->metrics[v].moments);
+          moments1_calculate (result->metrics[v].moments,
+                              &n, &mean, &var, NULL,  NULL);
+          chart_submit (histogram_chart_create (histogram, ds_cstr (&str),
+                                                n, mean, sqrt (var), false));
 
          ds_destroy (&str);
        }
@@ -474,25 +425,18 @@ show_boxplot_groups (const struct variable **dependent_var,
 
   for (v = 0; v < n_dep_var; ++v)
     {
-      struct ll *ll;
-      int f = 0;
-      struct chart *ch = chart_create ();
+      const struct factor_result *result;
+      struct boxplot *boxplot;
       double y_min = DBL_MAX;
       double y_max = -DBL_MAX;
+      char *title;
 
-      for (ll = ll_head (&fctr->result_list);
-          ll != ll_null (&fctr->result_list);
-          ll = ll_next (ll))
+      ll_for_each (result, struct factor_result, ll, &fctr->result_list)
        {
+          struct factor_metrics *metrics = &result->metrics[v];
+         const struct ll_list *max_list = extrema_list (metrics->maxima);
+         const struct ll_list *min_list = extrema_list (metrics->minima);
          const struct extremum  *max, *min;
-         const struct factor_result *result =
-           ll_data (ll, struct factor_result, ll);
-
-         const struct ll_list *max_list =
-           extrema_list (result->metrics[v].maxima);
-
-         const struct ll_list *min_list =
-           extrema_list (result->metrics[v].minima);
 
          if ( ll_is_empty (max_list))
            {
@@ -500,52 +444,34 @@ show_boxplot_groups (const struct variable **dependent_var,
              continue;
            }
 
-         max = (const struct extremum *)
-           ll_data (ll_head(max_list), struct extremum, ll);
-
-          min = (const struct extremum *)
-           ll_data (ll_head (min_list), struct extremum, ll);
+         max = ll_data (ll_head(max_list), struct extremum, ll);
+          min = ll_data (ll_head (min_list), struct extremum, ll);
 
          y_max = MAX (y_max, max->value);
          y_min = MIN (y_min, min->value);
        }
 
-      boxplot_draw_yscale (ch, y_max, y_min);
-
-      if ( fctr->indep_var[0])
-       chart_write_title (ch, _("Boxplot of %s vs. %s"),
+      if (fctr->indep_var[0])
+       title = xasprintf (_("Boxplot of %s vs. %s"),
                           var_to_string (dependent_var[v]),
-                          var_to_string (fctr->indep_var[0]) );
+                          var_to_string (fctr->indep_var[0]));
       else
-       chart_write_title (ch, _("Boxplot of %s"),
-                          var_to_string (dependent_var[v]));
+       title = xasprintf (_("Boxplot of %s"),
+                           var_to_string (dependent_var[v]));
+      boxplot = boxplot_create (y_min, y_max, title);
+      free (title);
 
-      for (ll = ll_head (&fctr->result_list);
-          ll != ll_null (&fctr->result_list);
-          ll = ll_next (ll))
+      ll_for_each (result, struct factor_result, ll, &fctr->result_list)
        {
-         const struct factor_result *result =
-           ll_data (ll, struct factor_result, ll);
-
-         struct string str;
-         const double box_width = (ch->data_right - ch->data_left)
-           / (ll_count (&fctr->result_list) * 2.0 ) ;
-
-         const double box_centre = (f++ * 2 + 1) * box_width + ch->data_left;
-
-         ds_init_empty (&str);
+          struct factor_metrics *metrics = &result->metrics[v];
+         struct string str = DS_EMPTY_INITIALIZER;
          factor_to_string_concise (fctr, result, &str);
-
-         boxplot_draw_boxplot (ch,
-                               box_centre, box_width,
-                               (const struct box_whisker *)
-                                result->metrics[v].box_whisker,
-                               ds_cstr (&str));
-
+          boxplot_add_box (boxplot, metrics->box_whisker, ds_cstr (&str));
+          metrics->box_whisker = NULL;
          ds_destroy (&str);
        }
 
-      chart_submit (ch);
+      chart_submit (boxplot_get_chart (boxplot));
     }
 }
 
@@ -558,74 +484,42 @@ show_boxplot_variables (const struct variable **dependent_var,
                        )
 
 {
+  const struct factor_result *result;
   int v;
-  struct ll *ll;
-  const struct ll_list *result_list = &fctr->result_list;
-
-  for (ll = ll_head (result_list);
-       ll != ll_null (result_list);
-       ll = ll_next (ll))
 
+  ll_for_each (result, struct factor_result, ll, &fctr->result_list)
     {
       struct string title;
-      struct chart *ch = chart_create ();
       double y_min = DBL_MAX;
       double y_max = -DBL_MAX;
-
-      const struct factor_result *result =
-       ll_data (ll, struct factor_result, ll);
-
-      const double box_width = (ch->data_right - ch->data_left)
-       / (n_dep_var * 2.0 ) ;
+      struct boxplot *boxplot;
 
       for (v = 0; v < n_dep_var; ++v)
        {
-         const struct ll *max_ll =
-           ll_head (extrema_list (result->metrics[v].maxima));
-         const struct ll *min_ll =
-           ll_head (extrema_list (result->metrics[v].minima));
-
-         const struct extremum  *max =
-           (const struct extremum *) ll_data (max_ll, struct extremum, ll);
-
-          const struct extremum  *min =
-           (const struct extremum *) ll_data (min_ll, struct extremum, ll);
+          const struct factor_metrics *metrics = &result->metrics[v];
+         const struct ll *max_ll = ll_head (extrema_list (metrics->maxima));
+         const struct ll *min_ll = ll_head (extrema_list (metrics->minima));
+         const struct extremum *max = ll_data (max_ll, struct extremum, ll);
+          const struct extremum *min = ll_data (min_ll, struct extremum, ll);
 
          y_max = MAX (y_max, max->value);
          y_min = MIN (y_min, min->value);
        }
 
-
-      boxplot_draw_yscale (ch, y_max, y_min);
-
       ds_init_empty (&title);
       factor_to_string (fctr, result, &title);
-
-#if 0
-      ds_put_format (&title, "%s = ", var_get_name (fctr->indep_var[0]));
-      var_append_value_name (fctr->indep_var[0], &result->value[0], &title);
-#endif
-
-      chart_write_title (ch, ds_cstr (&title));
+      boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
       ds_destroy (&title);
 
       for (v = 0; v < n_dep_var; ++v)
        {
-         struct string str;
-         const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
-
-         ds_init_empty (&str);
-         ds_init_cstr (&str, var_get_name (dependent_var[v]));
-
-         boxplot_draw_boxplot (ch,
-                               box_centre, box_width,
-                               (const struct box_whisker *) result->metrics[v].box_whisker,
-                               ds_cstr (&str));
-
-         ds_destroy (&str);
+          struct factor_metrics *metrics = &result->metrics[v];
+          boxplot_add_box (boxplot, metrics->box_whisker,
+                           var_get_name (dependent_var[v]));
+          metrics->box_whisker = NULL;
        }
 
-      chart_submit (ch);
+      chart_submit (boxplot_get_chart (boxplot));
     }
 }
 
@@ -674,16 +568,14 @@ output_examine (const struct dictionary *dict)
       if ( cmd.sbc_percentiles)
        show_percentiles (dependent_vars, n_dependent_vars, factor);
 
-      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
-         cmd.cmp == XMN_GROUPS)
-       show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
-
-
-      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
-         cmd.cmp == XMN_VARIABLES)
-       show_boxplot_variables (dependent_vars, n_dependent_vars,
-                               factor);
-
+      if (cmd.a_plot[XMN_PLT_BOXPLOT])
+        {
+          if (cmd.cmp == XMN_GROUPS)
+            show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
+          else if (cmd.cmp == XMN_VARIABLES)
+            show_boxplot_variables (dependent_vars, n_dependent_vars, factor);
+        }
+      
       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
        show_histogram (dependent_vars, n_dependent_vars, factor);
 
@@ -989,15 +881,13 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
 
          metric->n_ptiles = percentile_list.n_data;
 
-         metric->ptl = xcalloc (metric->n_ptiles,
-                                sizeof (struct percentile *));
+         metric->ptl = xcalloc (metric->n_ptiles, sizeof *metric->ptl);
 
          metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
 
          for (i = 0 ; i < metric->n_ptiles; ++i)
            {
-             metric->ptl[i] = (struct percentile *)
-               percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
+             metric->ptl[i] = percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
 
              if ( percentile_list.data[i] == 25)
                metric->quartiles[0] = metric->ptl[i];
@@ -1018,18 +908,18 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
              n_os ++;
            }
 
-         os = xcalloc (sizeof (struct order_stats *), n_os);
+          os = xcalloc (n_os, sizeof *os);
 
          for (i = 0 ; i < metric->n_ptiles ; ++i )
            {
-             os[i] = (struct order_stats *) metric->ptl[i];
+             os[i] = &metric->ptl[i]->parent;
            }
 
-         os[i] = (struct order_stats *) metric->tukey_hinges;
-         os[i+1] = (struct order_stats *) metric->trimmed_mean;
+         os[i] = &metric->tukey_hinges->parent;
+         os[i+1] = &metric->trimmed_mean->parent;
 
          if (cmd->a_plot[XMN_PLT_NPPLOT])
-           os[i+2] = metric->np;
+           os[i+2] = &metric->np->parent;
 
          order_stats_accumulate (os, n_os,
                                  casereader_clone (metric->up_reader),
@@ -1080,7 +970,7 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
            {
              struct factor_metrics *metric = &result->metrics[v];
              if ( metric->histogram)
-               histogram_add ((struct histogram *) metric->histogram,
+               histogram_add (metric->histogram,
                               case_data (c, dependent_vars[v])->f, weight);
            }
          case_unref (c);
@@ -1096,13 +986,13 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
          struct factor_metrics *metric = &result->metrics[v];
           int n_vals = caseproto_get_n_widths (casereader_get_proto (
                                                  metric->up_reader));
+          struct order_stats *os;
 
          metric->box_whisker =
-           box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
-                               cmd->v_id, n_vals - 1);
+           box_whisker_create ( metric->tukey_hinges, cmd->v_id, n_vals - 1);
 
-         order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
-                                 1,
+          os = &metric->box_whisker->parent;
+         order_stats_accumulate ( &os, 1,
                                  casereader_clone (metric->up_reader),
                                  wv, dependent_vars[v], MV_ANY);
        }
@@ -1242,10 +1132,10 @@ show_summary (const struct variable **dependent_var, int n_dep_var,
 
   n_cols = heading_columns + 6;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -1479,10 +1369,10 @@ show_descriptives (const struct variable **dependent_var,
 
   n_cols = heading_columns + 2;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -1658,7 +1548,7 @@ show_descriptives (const struct variable **dependent_var,
          tab_double (tbl, n_cols - 2,
                     heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
                     TAB_CENTER,
-                    trimmed_mean_calculate ((struct trimmed_mean *) result->metrics[v].trimmed_mean),
+                    trimmed_mean_calculate (result->metrics[v].trimmed_mean),
                     NULL);
 
 
@@ -1791,10 +1681,10 @@ show_extremes (const struct variable **dependent_var,
 
   n_cols = heading_columns + 2;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -1995,10 +1885,10 @@ show_percentiles (const struct variable **dependent_var,
 
   n_cols = heading_columns + n_percentiles;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -2078,8 +1968,7 @@ show_percentiles (const struct variable **dependent_var,
 
          tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
 
-         tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
-                                 hinges);
+         tukey_hinges_calculate (result->metrics[v].tukey_hinges, hinges);
 
          for (j = 0; j < n_percentiles; ++j)
            {
index 5a704d0d019f1f0d723aac1c8b81b455f02c0f7a..e1d6fb0e3dfcaa95518dc2f60b346da92199887b 100644 (file)
@@ -612,13 +612,14 @@ postcalc (const struct dataset *ds)
 
          hist = freq_tab_to_hist (ft,v);
 
-         histogram_plot_n (hist, var_to_string(v),
+          chart_submit (histogram_chart_create (
+                          hist, var_to_string(v),
                          vf->tab.valid_cases,
                          d[frq_mean],
                          d[frq_stddev],
-                         normal);
+                         normal));
 
-         statistic_destroy ((struct statistic *)hist);
+         statistic_destroy (&hist->parent);
        }
 
       if ( chart == GFT_PIE)
@@ -1000,26 +1001,39 @@ compare_freq_alpha_d (const void *a_, const void *b_, const void *v_)
 \f
 /* Frequency table display. */
 
+struct full_dim_aux
+  {
+    bool show_labels;
+  };
+
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-full_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+full_dim (struct tab_rendering *r, void *aux_)
 {
-  int i = 0;
-  int columns = 5;
+  const struct outp_driver *d = r->driver;
+  const struct tab_table *t = r->table;
+  const struct full_dim_aux *aux = aux_;
+  int i;
 
-  if (cmd.labels == FRQ_LABELS)
+  for (i = 0; i < tab_nc (t); i++)
     {
-    t->w[0] = MIN (tab_natural_width (t, d, 0), d->prop_em_width * 15);
-      i = 1;
-      columns ++;
+      r->w[i] = tab_natural_width (r, i);
+      if (aux->show_labels && i == 0)
+        r->w[i] = MIN (r->w[i], d->prop_em_width * 15);
+      else
+        r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
     }
 
-  for (;i < columns; i++)
-    t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = d->font_height;
+}
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = d->font_height;
+static void
+full_dim_free (void *aux_)
+{
+  struct full_dim_aux *aux = aux_;
+  free (aux);
 }
 
 /* Displays a full frequency table for variable V. */
@@ -1046,12 +1060,17 @@ dump_full (const struct variable *v, const struct variable *wv)
 
   const bool lab = (cmd.labels == FRQ_LABELS);
 
+  struct full_dim_aux *aux;
+
   vf = get_var_freqs (v);
   ft = &vf->tab;
   n_categories = ft->n_valid + ft->n_missing;
-  t = tab_create (5 + lab, n_categories + 2, 0);
+  t = tab_create (5 + lab, n_categories + 2);
   tab_headers (t, 0, 0, 1, 0);
-  tab_dim (t, full_dim, NULL);
+
+  aux = xmalloc (sizeof *aux);
+  aux->show_labels = lab;
+  tab_dim (t, full_dim, full_dim_free, aux);
 
   if (lab)
     tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Value Label"));
@@ -1121,20 +1140,26 @@ dump_full (const struct variable *v, const struct variable *wv)
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-condensed_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+condensed_dim (struct tab_rendering *r, void *aux UNUSED)
 {
-  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)));
+  struct outp_driver *d = r->driver;
+  const struct tab_table *t = r->table;
+
+  int cum_width = outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL);
+  int zeros_width = outp_string_width (d, "000", OUTP_PROPORTIONAL);
+  int max_width = MAX (cum_width, zeros_width);
 
   int i;
 
   for (i = 0; i < 2; i++)
-    t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+    {
+      r->w[i] = tab_natural_width (r, i);
+      r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
+    }
   for (i = 2; i < 4; i++)
-    t->w[i] = cum_w;
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = d->font_height;
+    r->w[i] = max_width;
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = d->font_height;
 }
 
 /* Display condensed frequency table for variable V. */
@@ -1153,7 +1178,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   vf = get_var_freqs (v);
   ft = &vf->tab;
   n_categories = ft->n_valid + ft->n_missing;
-  t = tab_create (4, n_categories + 2, 0);
+  t = tab_create (4, n_categories + 2);
 
   tab_headers (t, 0, 0, 2, 0);
   tab_text (t, 0, 1, TAB_CENTER | TAT_TITLE, _("Value"));
@@ -1161,7 +1186,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Cum"));
   tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
-  tab_dim (t, condensed_dim, NULL);
+  tab_dim (t, condensed_dim, NULL, NULL);
 
   r = 2;
   for (f = ft->valid; f < ft->missing; f++)
@@ -1191,7 +1216,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
           0, 0, 3, r - 1);
   tab_hline (t, TAL_2, 0, 3, 2);
   tab_title (t, "%s", var_to_string (v));
-  tab_columns (t, SOM_COL_DOWN, 1);
+  tab_columns (t, SOM_COL_DOWN);
   tab_submit (t);
 }
 \f
@@ -1359,8 +1384,8 @@ dump_statistics (const struct variable *v, bool show_varname,
     }
   calc_stats (v, stat_value);
 
-  t = tab_create (3, n_stats + n_percentiles + 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  t = tab_create (3, n_stats + n_percentiles + 2);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_box (t, TAL_1, TAL_1, -1, -1 , 0 , 0 , 2, tab_nr(t) - 1) ;
 
@@ -1401,7 +1426,7 @@ dump_statistics (const struct variable *v, bool show_varname,
                  var_get_print_format (v));
     }
 
-  tab_columns (t, SOM_COL_DOWN, 1);
+  tab_columns (t, SOM_COL_DOWN);
   if (show_varname)
     tab_title (t, "%s", var_to_string (v));
   else
@@ -1420,7 +1445,7 @@ freq_tab_to_hist (const struct freq_tab *ft, const struct variable *var)
   double x_min = DBL_MAX;
   double x_max = -DBL_MAX;
 
-  struct statistic *hist;
+  struct histogram *hist;
   const double bins = 11;
 
   struct hsh_iterator hi;
@@ -1442,10 +1467,10 @@ freq_tab_to_hist (const struct freq_tab *ft, const struct variable *var)
   for( i = 0 ; i < ft->n_valid ; ++i )
     {
       frq = &ft->valid[i];
-      histogram_add ((struct histogram *)hist, frq->value.f, frq->count);
+      histogram_add (hist, frq->value.f, frq->count);
     }
 
-  return (struct histogram *)hist;
+  return hist;
 }
 
 
@@ -1477,7 +1502,7 @@ freq_tab_to_slice_array(const struct freq_tab *frq_tab,
 
       ds_init_empty (&slices[i].label);
       var_append_value_name (var, &frq->value, &slices[i].label);
-      slices[i].magnetude = frq->count;
+      slices[i].magnitude = frq->count;
     }
 
   return slices;
@@ -1494,14 +1519,12 @@ do_piechart(const struct variable *var, const struct freq_tab *frq_tab)
 
   slices = freq_tab_to_slice_array(frq_tab, var, &n_slices);
 
-  piechart_plot(var_to_string(var), slices, n_slices);
+  chart_submit (piechart_create (var_to_string(var), slices, n_slices));
 
   for (i = 0 ; i < n_slices ; ++i )
-    {
-      ds_destroy (&slices[i].label);
-    }
+    ds_destroy (&slices[i].label);
 
-  free(slices);
+  free (slices);
 }
 
 
index d626dffac5f72075a582c370cb5940ec3845e2d1..8e6f11a8efa3aeb20591c7bf3476b8096c00e292 100644 (file)
@@ -102,16 +102,16 @@ do_summary_box (const struct descriptives *desc,
   if ( desc ) columns += 5;
   if ( quartiles ) columns += 3;
 
-  table = tab_create (columns, 2 + n_vars, 0);
+  table = tab_create (columns, 2 + n_vars);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Descriptive Statistics"));
 
   tab_headers (table, 1, 0, 1, 0);
 
   tab_box (table, TAL_1, TAL_1, -1, TAL_1,
-          0, 0, table->nc - 1, tab_nr(table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
   tab_hline (table, TAL_2, 0, tab_nc (table) -1, 2);
   tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
index bbccce671f075b161b1e95170d07197c6a3ac69d..8c6f98f82cf669275376313a03650b9ba5de0ff8 100644 (file)
@@ -32,6 +32,7 @@
 #include <language/stats/chisquare.h>
 #include <language/stats/wilcoxon.h>
 #include <language/stats/sign.h>
+#include <libpspp/cast.h>
 #include <libpspp/hash.h>
 #include <libpspp/pool.h>
 #include <libpspp/taint.h>
@@ -223,10 +224,11 @@ npar_custom_chisquare (struct lexer *lexer, struct dataset *ds,
   struct npar_specs *specs = aux;
 
   struct chisquare_test *cstp = pool_alloc(specs->pool, sizeof(*cstp));
-  struct one_sample_test *tp = (struct one_sample_test *) cstp;
+  struct one_sample_test *tp = &cstp->parent;
+  struct npar_test *nt = &tp->parent;
 
-  ((struct npar_test *)tp)->execute = chisquare_execute;
-  ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
+  nt->execute = chisquare_execute;
+  nt->insert_variables = one_sample_insert_variables;
 
   if (!parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
                                   &tp->vars, &tp->n_vars,
@@ -316,7 +318,7 @@ npar_custom_chisquare (struct lexer *lexer, struct dataset *ds,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
 
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -328,10 +330,11 @@ npar_custom_binomial (struct lexer *lexer, struct dataset *ds,
 {
   struct npar_specs *specs = aux;
   struct binomial_test *btp = pool_alloc(specs->pool, sizeof(*btp));
-  struct one_sample_test *tp = (struct one_sample_test *) btp;
+  struct one_sample_test *tp = &btp->parent;
+  struct npar_test *nt = &tp->parent;
 
-  ((struct npar_test *)tp)->execute = binomial_execute;
-  ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
+  nt->execute = binomial_execute;
+  nt->insert_variables = one_sample_insert_variables;
 
   btp->category1 = btp->category2 = btp->cutpoint = SYSMIS;
 
@@ -381,7 +384,7 @@ npar_custom_binomial (struct lexer *lexer, struct dataset *ds,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
 
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -412,7 +415,7 @@ parse_two_sample_related_test (struct lexer *lexer,
   const struct variable **vlist2;
   size_t n_vlist2;
 
-  ((struct npar_test *)test_parameters)->insert_variables = two_sample_insert_variables;
+  test_parameters->parent.insert_variables = two_sample_insert_variables;
 
   if (!parse_variables_const_pool (lexer, pool,
                                   dict,
@@ -512,7 +515,8 @@ npar_custom_wilcoxon (struct lexer *lexer,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc (specs->pool, sizeof(*tp));
-  ((struct npar_test *)tp)->execute = wilcoxon_execute;
+  struct npar_test *nt = &tp->parent;
+  nt->execute = wilcoxon_execute;
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd,
                                      tp, specs->pool) )
@@ -522,7 +526,7 @@ npar_custom_wilcoxon (struct lexer *lexer,
   specs->test = pool_realloc (specs->pool,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -535,7 +539,8 @@ npar_custom_mcnemar (struct lexer *lexer,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
-  ((struct npar_test *)tp)->execute = NULL;
+  struct npar_test *nt = &tp->parent;
+  nt->execute = NULL;
 
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds),
@@ -546,7 +551,7 @@ npar_custom_mcnemar (struct lexer *lexer,
   specs->test = pool_realloc (specs->pool,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -558,7 +563,9 @@ npar_custom_sign (struct lexer *lexer, struct dataset *ds,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
-  ((struct npar_test *) tp)->execute = sign_execute;
+  struct npar_test *nt = &tp->parent;
+
+  nt->execute = sign_execute;
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd,
                                      tp, specs->pool) )
@@ -568,7 +575,7 @@ npar_custom_sign (struct lexer *lexer, struct dataset *ds,
   specs->test = pool_realloc (specs->pool,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -579,7 +586,7 @@ one_sample_insert_variables (const struct npar_test *test,
                             struct const_hsh_table *var_hash)
 {
   int i;
-  struct one_sample_test *ost = (struct one_sample_test *) test;
+  struct one_sample_test *ost = UP_CAST (test, struct one_sample_test, parent);
 
   for ( i = 0 ; i < ost->n_vars ; ++i )
     const_hsh_insert (var_hash, ost->vars[i]);
index 0f6b20a1db04cdf9503e4a21cd67cb2f22ecf6bf..ddb84d896fa1ac6701f8f4634071525a363ad3f9 100644 (file)
@@ -259,9 +259,9 @@ show_anova_table (void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
 
   tab_box (t,
@@ -370,9 +370,9 @@ show_descriptives (const struct dictionary *dict)
   for ( v = 0; v < n_vars; ++v )
     n_rows += group_proc_get (vars[v])->n_groups + 1;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
 
   /* Put a frame around the entire box, and vertical lines inside */
@@ -517,9 +517,9 @@ show_homogeneity (void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 1, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -577,9 +577,9 @@ show_contrast_coeffs (short *bad_contrast)
 
   struct tab_table *t;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -661,9 +661,9 @@ show_contrast_tests (short *bad_contrast)
 
   struct tab_table *t;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 3, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
index 41a23f3e2580ebd9a9aac6ff289b89803c6f8da2..b61ae586b488292d23fda2b8ee9b96daab4d37f2 100644 (file)
@@ -149,8 +149,8 @@ reg_stats_r (pspp_linreg_cache * c)
   rsq = c->ssm / c->sst;
   adjrsq = 1.0 - (1.0 - rsq) * (c->n_obs - 1.0) / (c->n_obs - c->n_indeps);
   std_error = sqrt (pspp_linreg_mse (c));
-  t = tab_create (n_cols, n_rows, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  t = tab_create (n_cols, n_rows);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -191,9 +191,9 @@ reg_stats_coeff (pspp_linreg_cache * c)
   assert (c != NULL);
   n_rows = c->n_coeffs + 3;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -288,9 +288,9 @@ reg_stats_anova (pspp_linreg_cache * c)
   struct tab_table *t;
 
   assert (c != NULL);
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
 
@@ -379,9 +379,9 @@ reg_stats_bcov (pspp_linreg_cache * c)
   assert (c != NULL);
   n_cols = c->n_indeps + 1 + 2;
   n_rows = 2 * (c->n_indeps + 1);
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
index 681669d18999fdac3c5516c9815b9079aae99dfb..dfb81367912a3bcd96a25aa00bb964145a98f0ab 100644 (file)
@@ -381,9 +381,9 @@ run_reliability (struct casereader *input, struct dataset *ds,
 
 
   {
-    struct tab_table *tab = tab_create(1, 1, 0);
+    struct tab_table *tab = tab_create(1, 1);
 
-    tab_dim (tab, tab_natural_dimensions, NULL);
+    tab_dim (tab, tab_natural_dimensions, NULL, NULL);
     tab_flags (tab, SOMF_NO_TITLE );
 
     tab_text_format (tab, 0, 0, 0, "Scale: %s", ds_cstr (&rel->scale_name));
@@ -425,10 +425,10 @@ reliability_statistics (const struct reliability *rel)
   int heading_columns = rol[rel->model].heading_cols;
   int heading_rows = rol[rel->model].heading_rows;
 
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_title (tbl, _("Reliability Statistics"));
 
@@ -468,10 +468,10 @@ reliability_summary_total (const struct reliability *rel)
   const int heading_rows = 1;
   const int n_rows = rel->sc[0].n_items + heading_rows ;
 
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_title (tbl, _("Item-Total Statistics"));
 
@@ -678,10 +678,10 @@ case_processing_summary (casenumber n_valid, casenumber n_missing,
   int heading_columns = 2;
   int heading_rows = 1;
   struct tab_table *tbl;
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_title (tbl, _("Case Processing Summary"));
 
index 86aee1f9d1f5e6a9662f1a605ebf560db52b19b7..1f2691d8919e01eac157688d053f218c426581af 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <config.h>
 
+#include <language/stats/roc.h>
+
 #include <data/procedure.h>
 #include <language/lexer/variable-parser.h>
 #include <language/lexer/value-parser.h>
@@ -36,8 +38,8 @@
 #include <gsl/gsl_cdf.h>
 #include <output/table.h>
 
-#include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
+#include <output/chart.h>
+#include <output/charts/roc-chart.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -386,13 +388,6 @@ struct roc_state
   double max;
 };
 
-#define CUTPOINT 0
-#define TP 1
-#define FN 2
-#define TN 3
-#define FP 4
-
-
 /* 
    Return a new casereader based upon CUTPOINT_RDR.
    The number of "positive" cases are placed into
@@ -418,7 +413,7 @@ accumulate_counts (struct casereader *cutpoint_rdr,
   for ( ; (cpc = casereader_read (r) ); case_unref (cpc))
     {
       struct ccase *new_case;
-      const double cp = case_data_idx (cpc, CUTPOINT)->f;
+      const double cp = case_data_idx (cpc, ROC_CUTPOINT)->f;
 
       assert (cp != SYSMIS);
 
@@ -582,7 +577,7 @@ process_positive_group (const struct variable *var, struct casereader *reader,
   return process_group (var, reader, gt, dict, &rs->n1,
                        &rs->cutpoint_rdr,
                        ge,
-                       TP, FN);
+                       ROC_TP, ROC_FN);
 }
 
 /*
@@ -600,7 +595,7 @@ process_negative_group (const struct variable *var, struct casereader *reader,
   return process_group (var, reader, lt, dict, &rs->n2,
                        &rs->cutpoint_rdr,
                        lt,
-                       TN, FP);
+                       ROC_TN, ROC_FP);
 }
 
 
@@ -611,11 +606,11 @@ append_cutpoint (struct casewriter *writer, double cutpoint)
 {
   struct ccase *cc = case_create (casewriter_get_proto (writer));
 
-  case_data_rw_idx (cc, CUTPOINT)->f = cutpoint;
-  case_data_rw_idx (cc, TP)->f = 0;
-  case_data_rw_idx (cc, FN)->f = 0;
-  case_data_rw_idx (cc, TN)->f = 0;
-  case_data_rw_idx (cc, FP)->f = 0;
+  case_data_rw_idx (cc, ROC_CUTPOINT)->f = cutpoint;
+  case_data_rw_idx (cc, ROC_TP)->f = 0;
+  case_data_rw_idx (cc, ROC_FN)->f = 0;
+  case_data_rw_idx (cc, ROC_TN)->f = 0;
+  case_data_rw_idx (cc, ROC_FP)->f = 0;
 
   casewriter_write (writer, cc);
 }
@@ -623,9 +618,9 @@ append_cutpoint (struct casewriter *writer, double cutpoint)
 
 /* 
    Create and initialise the rs[x].cutpoint_rdr casereaders.  That is, the readers will
-   be created with width 5, ready to take the values (cutpoint, TP, FN, TN, FP), and the
+   be created with width 5, ready to take the values (cutpoint, ROC_TP, ROC_FN, ROC_TN, ROC_FP), and the
    reader will be populated with its final number of cases.
-   However on exit from this function, only CUTPOINT entries will be set to their final
+   However on exit from this function, only ROC_CUTPOINT entries will be set to their final
    value.  The other entries will be initialised to zero.
 */
 static void
@@ -637,13 +632,13 @@ prepare_cutpoints (struct cmd_roc *roc, struct roc_state *rs, struct casereader
   struct caseproto *proto = caseproto_create ();
 
   struct subcase ordering;
-  subcase_init (&ordering, CUTPOINT, 0, SC_ASCEND);
+  subcase_init (&ordering, ROC_CUTPOINT, 0, SC_ASCEND);
 
   proto = caseproto_add_width (proto, 0); /* cutpoint */
-  proto = caseproto_add_width (proto, 0); /* TP */
-  proto = caseproto_add_width (proto, 0); /* FN */
-  proto = caseproto_add_width (proto, 0); /* TN */
-  proto = caseproto_add_width (proto, 0); /* FP */
+  proto = caseproto_add_width (proto, 0); /* ROC_TP */
+  proto = caseproto_add_width (proto, 0); /* ROC_FN */
+  proto = caseproto_add_width (proto, 0); /* ROC_TN */
+  proto = caseproto_add_width (proto, 0); /* ROC_FP */
 
   for (i = 0 ; i < roc->n_vars; ++i)
     {
@@ -935,7 +930,7 @@ show_auc  (struct roc_state *rs, const struct cmd_roc *roc)
   const int n_fields = roc->print_se ? 5 : 1;
   const int n_cols = roc->n_vars > 1 ? n_fields + 1: n_fields;
   const int n_rows = 2 + roc->n_vars;
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
 
   if ( roc->n_vars > 1)
     tab_title (tbl, _("Area Under the Curve"));
@@ -944,7 +939,7 @@ show_auc  (struct roc_state *rs, const struct cmd_roc *roc)
 
   tab_headers (tbl, n_cols - n_fields, 0, 1, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_text (tbl, n_cols - n_fields, 1, TAT_TITLE, _("Area"));
 
@@ -1030,13 +1025,13 @@ show_summary (const struct cmd_roc *roc)
 {
   const int n_cols = 3;
   const int n_rows = 4;
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
 
   tab_title (tbl, _("Case Summary"));
 
   tab_headers (tbl, 1, 0, 2, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_box (tbl,
           TAL_2, TAL_2,
@@ -1088,7 +1083,7 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
   for (i = 0; i < roc->n_vars; ++i)
     n_rows += casereader_count_cases (rs[i].cutpoint_rdr);
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
 
   if ( roc->n_vars > 1)
     tab_title (tbl, _("Coordinates of the Curve"));
@@ -1098,7 +1093,7 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
 
   tab_headers (tbl, 1, 0, 1, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_hline (tbl, TAL_2, 0, n_cols - 1, 1);
 
@@ -1134,21 +1129,21 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
       for (; (cc = casereader_read (r)) != NULL;
           case_unref (cc), x++)
        {
-         const double se = case_data_idx (cc, TP)->f /
+         const double se = case_data_idx (cc, ROC_TP)->f /
            (
-            case_data_idx (cc, TP)->f
+            case_data_idx (cc, ROC_TP)->f
             +
-            case_data_idx (cc, FN)->f
+            case_data_idx (cc, ROC_FN)->f
             );
 
-         const double sp = case_data_idx (cc, TN)->f /
+         const double sp = case_data_idx (cc, ROC_TN)->f /
            (
-            case_data_idx (cc, TN)->f
+            case_data_idx (cc, ROC_TN)->f
             +
-            case_data_idx (cc, FP)->f
+            case_data_idx (cc, ROC_FP)->f
             );
 
-         tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, CUTPOINT)->f,
+         tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, ROC_CUTPOINT)->f,
                      var_get_print_format (roc->vars[i]));
 
          tab_double (tbl, n_cols - 2, x, 0, se, NULL);
@@ -1162,68 +1157,25 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
 }
 
 
-static void
-draw_roc (struct roc_state *rs, const struct cmd_roc *roc)
-{
-  int i;
-
-  struct chart *roc_chart = chart_create ();
-
-  chart_write_title (roc_chart, _("ROC Curve"));
-  chart_write_xlabel (roc_chart, _("1 - Specificity"));
-  chart_write_ylabel (roc_chart, _("Sensitivity"));
-
-  chart_write_xscale (roc_chart, 0, 1, 5);
-  chart_write_yscale (roc_chart, 0, 1, 5);
-
-  if ( roc->reference )
-    {
-      chart_line (roc_chart, 1.0, 0,
-                 0.0, 1.0,
-                 CHART_DIM_X);
-    }
-
-  for (i = 0; i < roc->n_vars; ++i)
-    {
-      struct ccase *cc;
-      struct casereader *r = casereader_clone (rs[i].cutpoint_rdr);
-
-      chart_vector_start (roc_chart, var_get_name (roc->vars[i]));
-      for (; (cc = casereader_read (r)) != NULL;
-          case_unref (cc))
-       {
-         double se = case_data_idx (cc, TP)->f;
-         double sp = case_data_idx (cc, TN)->f;
-
-         se /= case_data_idx (cc, FN)->f +
-           case_data_idx (cc, TP)->f ;
-
-         sp /= case_data_idx (cc, TN)->f +
-           case_data_idx (cc, FP)->f ;
-
-         chart_vector (roc_chart, 1 - sp, se);
-       }
-      chart_vector_end (roc_chart);
-      casereader_destroy (r);
-    }
-
-  chart_write_legend (roc_chart);
-
-  chart_submit (roc_chart);
-}
-
-
 static void
 output_roc (struct roc_state *rs, const struct cmd_roc *roc)
 {
   show_summary (roc);
 
   if ( roc->curve )
-    draw_roc (rs, roc);
+    {
+      struct roc_chart *rc;
+      size_t i;
+
+      rc = roc_chart_create (roc->reference);
+      for (i = 0; i < roc->n_vars; i++)
+        roc_chart_add_var (rc, var_get_name (roc->vars[i]),
+                           rs[i].cutpoint_rdr);
+      chart_submit (roc_chart_get_chart (rc));
+    }
 
   show_auc (rs, roc);
 
-
   if ( roc->print_coords )
     show_coords (rs, roc);
 }
diff --git a/src/language/stats/roc.h b/src/language/stats/roc.h
new file mode 100644 (file)
index 0000000..5d63c96
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LANGUAGE_STATS_ROC_H
+#define LANGUAGE_STATS_ROC_H 1
+
+/* These are case indexes into the cutpoint case readers for ROC
+   output, used by roc.c and roc-chart.c. */
+#define ROC_CUTPOINT 0
+#define ROC_TP 1
+#define ROC_FN 2
+#define ROC_TN 3
+#define ROC_FP 4
+
+#endif /* language/stats/roc.h */
index a5a272126007c557a2c006b652d73cf7e9fe4436..d5970c6ff0a61297803374c92755762cdca1f6d0 100644 (file)
@@ -52,12 +52,12 @@ output_frequency_table (const struct two_sample_test *t2s,
                        const struct dictionary *dict)
 {
   int i;
-  struct tab_table *table = tab_create (3, 1 + 4 * t2s->n_pairs, 0);
+  struct tab_table *table = tab_create (3, 1 + 4 * t2s->n_pairs);
 
   const struct variable *wv = dict_get_weight (dict);
   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Frequencies"));
 
@@ -65,11 +65,11 @@ output_frequency_table (const struct two_sample_test *t2s,
 
   /* Vertical lines inside the box */
   tab_box (table, 0, 0, -1, TAL_1,
-          1, 0, table->nc - 1, tab_nr (table) - 1 );
+          1, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   tab_text (table,  2, 0,  TAB_CENTER, _("N"));
 
@@ -86,7 +86,7 @@ output_frequency_table (const struct two_sample_test *t2s,
 
       ds_destroy (&pair_name);
 
-      tab_hline (table, TAL_1, 0, table->nc - 1, 1 + i * 4);
+      tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1 + i * 4);
 
       tab_text (table,  1, 1 + i * 4,  TAB_LEFT, _("Negative Differences"));
       tab_text (table,  1, 2 + i * 4,  TAB_LEFT, _("Positive Differences"));
@@ -108,26 +108,26 @@ output_statistics_table (const struct two_sample_test *t2s,
                         const struct sign_test_params *param)
 {
   int i;
-  struct tab_table *table = tab_create (1 + t2s->n_pairs, 4, 0);
+  struct tab_table *table = tab_create (1 + t2s->n_pairs, 4);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Test Statistics"));
 
   tab_headers (table, 0, 1,  0, 1);
 
-  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
-  tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+  tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
 
 
   /* Vertical lines inside the box */
   tab_box (table, -1, -1, -1, TAL_1,
           0, 0,
-          table->nc - 1, tab_nr (table) - 1);
+          tab_nc (table) - 1, tab_nr (table) - 1);
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1,
+          0, 0, tab_nc (table) - 1,
           tab_nr (table) - 1);
 
   tab_text (table,  0, 1, TAT_TITLE | TAB_LEFT,
index 8f23cefd4afa54c8be6cfebe7031128a913e3616..a483bd9e35f2dea56afd12eb16ab6905725120aa 100644 (file)
@@ -477,13 +477,13 @@ static void
 ssbox_base_init (struct ssbox *this, int cols, int rows)
 {
   this->finalize = ssbox_base_finalize;
-  this->t = tab_create (cols, rows, 0);
+  this->t = tab_create (cols, rows);
 
-  tab_columns (this->t, SOM_COL_DOWN, 1);
+  tab_columns (this->t, SOM_COL_DOWN);
   tab_headers (this->t, 0, 0, 1, 0);
   tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (this->t, TAL_2, 0, cols- 1, 1);
-  tab_dim (this->t, tab_natural_dimensions, NULL);
+  tab_dim (this->t, tab_natural_dimensions, NULL, NULL);
 }
 \f
 /* ssbox implementations. */
@@ -1069,11 +1069,11 @@ trbox_base_init (struct trbox *self, size_t data_rows, int cols)
   const size_t rows = 3 + data_rows;
 
   self->finalize = trbox_base_finalize;
-  self->t = tab_create (cols, rows, 0);
+  self->t = tab_create (cols, rows);
   tab_headers (self->t, 0, 0, 3, 0);
   tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols - 1, rows - 1);
   tab_hline (self->t, TAL_2, 0, cols- 1, 3);
-  tab_dim (self->t, tab_natural_dimensions, NULL);
+  tab_dim (self->t, tab_natural_dimensions, NULL, NULL);
 }
 
 /* Base finalizer for the trbox */
@@ -1093,14 +1093,14 @@ pscbox (struct t_test_proc *proc)
 
   struct tab_table *table;
 
-  table = tab_create (cols, rows, 0);
+  table = tab_create (cols, rows);
 
-  tab_columns (table, SOM_COL_DOWN, 1);
+  tab_columns (table, SOM_COL_DOWN);
   tab_headers (table, 0, 0, 1, 0);
   tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (table, TAL_2, 0, cols - 1, 1);
   tab_vline (table, TAL_2, 2, 0, rows - 1);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
   tab_title (table, _("Paired Samples Correlations"));
 
   /* column headings */
index 310206c0e7bf88961e820c05f97831f8568da5a6..c7e302779e63a8937c64a707eeb74126823e948c 100644 (file)
@@ -225,9 +225,9 @@ show_ranks_box (const struct wilcoxon_state *ws,
   const struct variable *wv = dict_get_weight (dict);
   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 
-  struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs, 0);
+  struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Ranks"));
 
@@ -235,11 +235,11 @@ show_ranks_box (const struct wilcoxon_state *ws,
 
   /* Vertical lines inside the box */
   tab_box (table, 0, 0, -1, TAL_1,
-          1, 0, table->nc - 1, tab_nr (table) - 1 );
+          1, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
 
   tab_text (table,  2, 0,  TAB_CENTER, _("N"));
@@ -261,7 +261,7 @@ show_ranks_box (const struct wilcoxon_state *ws,
       tab_text (table, 1, 3 + i * 4, TAB_LEFT, _("Ties"));
       tab_text (table, 1, 4 + i * 4, TAB_LEFT, _("Total"));
 
-      tab_hline (table, TAL_1, 0, table->nc - 1, 1 + i * 4);
+      tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1 + i * 4);
 
 
       tab_text (table, 0, 1 + i * 4, TAB_LEFT, ds_cstr (&pair_name));
@@ -290,8 +290,8 @@ show_ranks_box (const struct wilcoxon_state *ws,
 
     }
 
-  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
-  tab_vline (table, TAL_2, 2, 0, table->nr - 1);
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+  tab_vline (table, TAL_2, 2, 0, tab_nr (table) - 1);
 
 
   tab_submit (table);
@@ -306,9 +306,9 @@ show_tests_box (const struct wilcoxon_state *ws,
                )
 {
   size_t i;
-  struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3, 0);
+  struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Test Statistics"));
 
@@ -316,11 +316,11 @@ show_tests_box (const struct wilcoxon_state *ws,
 
   /* Vertical lines inside the box */
   tab_box (table, 0, 0, -1, TAL_1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
 
   tab_text (table,  0, 1,  TAB_LEFT, _("Z"));
@@ -377,8 +377,8 @@ show_tests_box (const struct wilcoxon_state *ws,
        }
     }
 
-  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
-  tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+  tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
 
 
   tab_submit (table);
index 0771ade3a9e92dccc0c3063a7c14d13014c010e3..9b5fd0430b56879fd92e1996c732cbe16e0c2068 100644 (file)
@@ -29,6 +29,7 @@
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
@@ -62,14 +63,16 @@ struct syntax_file_source
 static const char *
 name (const struct getl_interface *s)
 {
-  const struct syntax_file_source *sfs = (const struct syntax_file_source *) s;
+  const struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                                  parent);
   return sfs->fn;
 }
 
 static int
 line_number (const struct getl_interface *s)
 {
-  const struct syntax_file_source *sfs = (const struct syntax_file_source *) s;
+  const struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                                  parent);
   return sfs->ln;
 }
 
@@ -80,7 +83,8 @@ static bool
 read_syntax_file (struct getl_interface *s,
                   struct string *line)
 {
-  struct syntax_file_source *sfs = (struct syntax_file_source *) s;
+  struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                            parent);
 
   /* Open file, if not yet opened. */
   if (sfs->syntax_file == NULL)
@@ -121,7 +125,8 @@ read_syntax_file (struct getl_interface *s,
 static void
 syntax_close (struct getl_interface *s)
 {
-  struct syntax_file_source *sfs = (struct syntax_file_source *) s;
+  struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                            parent);
 
   if (sfs->syntax_file && EOF == fn_close (sfs->fn, sfs->syntax_file))
     msg (MW, _("Closing `%s': %s."), sfs->fn, strerror (errno));
@@ -151,6 +156,6 @@ create_syntax_file_source (const char *fn)
   ss->parent.name = name ;
   ss->parent.location = line_number;
 
-  return (struct getl_interface *) ss;
+  return &ss->parent;
 }
 
index 94a56f97aacb55ae642b3eac0290cd8531f2b2c3..405141cd50a7fd1734f387281b5b344d5a7e74f9 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical interface for PSPP.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
 
 #include <config.h>
 
+#include <libpspp/cast.h>
 #include <libpspp/getl.h>
 #include <libpspp/compiler.h>
 #include <libpspp/str.h>
@@ -60,7 +61,8 @@ location (const struct getl_interface *i UNUSED)
 static void
 do_close (struct getl_interface *i )
 {
-  struct syntax_string_source *sss = (struct syntax_string_source *) i;
+  struct syntax_string_source *sss = UP_CAST (i, struct syntax_string_source,
+                                              parent);
 
   ds_destroy (&sss->buffer);
 
@@ -73,7 +75,8 @@ static bool
 read_single_line (struct getl_interface *i,
                  struct string *line)
 {
-  struct syntax_string_source *sss = (struct syntax_string_source *) i;
+  struct syntax_string_source *sss = UP_CAST (i, struct syntax_string_source,
+                                              parent);
 
   size_t next;
 
@@ -120,7 +123,7 @@ create_syntax_string_source (const char *format, ...)
   sss->parent.location = location;
 
 
-  return (struct getl_interface *) sss;
+  return &sss->parent;
 }
 
 /* Return the syntax currently contained in S.
index 19e9f9e54bf36b860b46f8d4681ce61660931b10..3d3c2c40e73ebde043d0be174f37205e41b3d5f8 100644 (file)
@@ -33,9 +33,9 @@ cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED)
   if (lex_token (lexer) != T_STRING)
     return CMD_FAILURE;
 
-  tab = tab_create(1, 1, 0);
+  tab = tab_create(1, 1);
 
-  tab_dim (tab, tab_natural_dimensions, NULL);
+  tab_dim (tab, tab_natural_dimensions, NULL, NULL);
   tab_flags (tab, SOMF_NO_TITLE );
 
   tab_text(tab, 0, 0, 0, ds_cstr (lex_tokstr (lexer)));
index 8ce2d12576697beb5562fc65fc4a03522ce2a9a0..4fb889b17717ae3a0dec8bc98ee5a81ea6e219b9 100644 (file)
@@ -352,7 +352,7 @@ count_trns_proc (void *trns_, struct ccase **c,
 static bool
 count_trns_free (void *trns_)
 {
-  struct count_trns *trns = (struct count_trns *) trns_;
+  struct count_trns *trns = trns_;
   pool_destroy (trns->pool);
   return true;
 }
index 74b2ceb387f54d04e669cfbadb7eb4e7589c89a3..b776612922f2579b290f3a3657f12adb8efee0fe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
 #endif
 
 #include <libpspp/abt.h>
+#include <libpspp/cast.h>
 
 #include <stdbool.h>
 
@@ -145,8 +146,8 @@ insert_relative (struct abt *abt, const struct abt_node *p, bool after,
           p = p->down[dir];
           dir = !after;
         }
-      ((struct abt_node *) p)->down[dir] = node;
-      node->up = (struct abt_node *) p;
+      CONST_CAST (struct abt_node *, p)->down[dir] = node;
+      node->up = CONST_CAST (struct abt_node *, p);
       abt_reaugmented (abt, node);
     }
 
@@ -280,7 +281,7 @@ abt_find (const struct abt *abt, const struct abt_node *target)
     {
       cmp = abt->compare (target, p, abt->aux);
       if (cmp == 0)
-        return (struct abt_node *) p;
+        return CONST_CAST (struct abt_node *, p);
     }
 
   return NULL;
@@ -307,7 +308,7 @@ abt_next (const struct abt *abt, const struct abt_node *p)
       p = p->down[1];
       while (p->down[0] != NULL)
         p = p->down[0];
-      return (struct abt_node *) p;
+      return CONST_CAST (struct abt_node *, p);
     }
 }
 
@@ -332,7 +333,7 @@ abt_prev (const struct abt *abt, const struct abt_node *p)
       p = p->down[0];
       while (p->down[1] != NULL)
         p = p->down[1];
-      return (struct abt_node *) p;
+      return CONST_CAST (struct abt_node *, p);
     }
 }
 
index 3801eb86636c14d2d4a73420329d7c568768d630..24d7268a2d6543a383361c50914d796e52e7b6c1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    tree paper.  */
 
 #include <stddef.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
-#define abt_data(NODE, STRUCT, MEMBER)                                  \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define abt_data(NODE, STRUCT, MEMBER)                          \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct abt_node *),      \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* Node in an augmented binary tree. */
 struct abt_node
index 746756d8900ba0207e45ae77718ed8dbab8dda91..30a5424d2e87ce828c42ddcb119412b56af2ce8e 100644 (file)
@@ -65,6 +65,10 @@ src_libpspp_libpspp_la_SOURCES = \
        src/libpspp/sparse-xarray.h \
        src/libpspp/start-date.c \
        src/libpspp/start-date.h \
+       src/libpspp/string-map.c \
+       src/libpspp/string-map.h \
+       src/libpspp/string-set.c \
+       src/libpspp/string-set.h \
        src/libpspp/str.c \
        src/libpspp/str.h \
        src/libpspp/taint.c \
index 26eb982b25d47c26c9699c24018a0910cb2dbcd5..751c8fe764cd6f71aeec94be3550855f8dd3ce6e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -67,6 +67,8 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include <libpspp/cast.h>
+
 static void rebalance_subtree (struct bt *, struct bt_node *, size_t);
 
 static struct bt_node **down_link (struct bt *, struct bt_node *);
@@ -250,7 +252,7 @@ bt_find (const struct bt *bt, const struct bt_node *target)
     {
       cmp = bt->compare (target, p, bt->aux);
       if (cmp == 0)
-        return (struct bt_node *) p;
+        return CONST_CAST (struct bt_node *, p);
     }
 
   return NULL;
@@ -283,7 +285,7 @@ bt_find_ge (const struct bt *bt, const struct bt_node *target)
             break;
         }
     }
-  return (struct bt_node *) q;
+  return CONST_CAST (struct bt_node *, q);
 }
 
 /* Searches BT for, and returns, the last node in in-order whose
@@ -314,7 +316,7 @@ bt_find_le (const struct bt *bt, const struct bt_node *target)
             break;
         }
     }
-  return (struct bt_node *) q;
+  return CONST_CAST (struct bt_node *, q);
 }
 
 /* Returns the node in BT following P in in-order.
@@ -338,7 +340,7 @@ bt_next (const struct bt *bt, const struct bt_node *p)
       p = p->down[1];
       while (p->down[0] != NULL)
         p = p->down[0];
-      return (struct bt_node *) p;
+      return CONST_CAST (struct bt_node *, p);
     }
 }
 
@@ -363,7 +365,7 @@ bt_prev (const struct bt *bt, const struct bt_node *p)
       p = p->down[0];
       while (p->down[1] != NULL)
         p = p->down[1];
-      return (struct bt_node *) p;
+      return CONST_CAST (struct bt_node *, p);
     }
 }
 
index 340b8760166c31fec29ea42ae8ecc50c24911806..7045632ef204e2edd228dc4dd8473174318212be 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    fully encapsulated. */
 
 #include <stddef.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
-#define bt_data(NODE, STRUCT, MEMBER)                                  \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define bt_data(NODE, STRUCT, MEMBER)                           \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct bt_node *),       \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* Node in a balanced binary tree. */
 struct bt_node
diff --git a/src/libpspp/cast.h b/src/libpspp/cast.h
new file mode 100644 (file)
index 0000000..1e33857
--- /dev/null
@@ -0,0 +1,92 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_CAST_H
+#define LIBPSPP_CAST_H 1
+
+#include <stddef.h>
+
+/* Expands to a void expression that checks that POINTER is an
+   expression whose type is a qualified or unqualified version of
+   a type compatible with TYPE (a pointer type) and, if not,
+   causes a compiler warning to be issued (on typical compilers).
+
+   Examples:
+
+   int *ip;
+   const int *cip;
+   const int **cipp;
+   int ***ippp;
+   double *dp;
+
+   // None of these causes a warning:
+   CHECK_POINTER_HAS_TYPE (ip, int *);
+   CHECK_POINTER_HAS_TYPE (ip, const int *);
+   CHECK_POINTER_HAS_TYPE (cip, int *);
+   CHECK_POINTER_HAS_TYPE (cip, const int *);
+   CHECK_POINTER_HAS_TYPE (dp, double *);
+   CHECK_POINTER_HAS_TYPE (dp, const double *);
+   CHECK_POINTER_HAS_TYPE (cipp, const int **);
+   CHECK_POINTER_HAS_TYPE (cipp, const int *const *);
+   CHECK_POINTER_HAS_TYPE (ippp, int ***);
+   CHECK_POINTER_HAS_TYPE (ippp, int **const *);
+
+   // None of these causes a warning either, although it is unusual to
+   // const-qualify a pointer like this (it's like declaring a "const int",
+   // for example).
+   CHECK_POINTER_HAS_TYPE (ip, int *const);
+   CHECK_POINTER_HAS_TYPE (ip, const int *const);
+   CHECK_POINTER_HAS_TYPE (cip, int *const);
+   CHECK_POINTER_HAS_TYPE (cip, const int *const);
+   CHECK_POINTER_HAS_TYPE (cipp, const int **const);
+   CHECK_POINTER_HAS_TYPE (cipp, const int *const *const);
+   CHECK_POINTER_HAS_TYPE (ippp, int ***const);
+   CHECK_POINTER_HAS_TYPE (ippp, int **const *const);
+
+   // Provokes a warning because "int" is not compatible with "double":
+   CHECK_POINTER_HAS_TYPE (dp, int *);
+
+   // Provoke warnings because C's type compatibility rules only allow
+   // adding a "const" qualifier to the outermost pointer:
+   CHECK_POINTER_HAS_TYPE (ippp, const int ***);
+   CHECK_POINTER_HAS_TYPE (ippp, int *const**);
+*/
+#define CHECK_POINTER_HAS_TYPE(POINTER, TYPE)           \
+        ((void) sizeof ((TYPE) (POINTER) == (POINTER)))
+
+/* Given expressions A and B, both of which have pointer type,
+   expands to a void expression that causes a compiler warning if
+   A and B are not pointers to qualified or unqualified versions
+   of compatible types.
+
+   Examples similar to those given for CHECK_POINTER_HAS_TYPE,
+   above, can easily be devised. */
+#define CHECK_POINTER_COMPATIBILITY(A, B) ((void) sizeof ((A) == (B)))
+
+/* Equivalent to casting POINTER to TYPE, but also issues a
+   warning if the cast changes anything other than an outermost
+   "const" or "volatile" qualifier. */
+#define CONST_CAST(TYPE, POINTER)                       \
+        (CHECK_POINTER_HAS_TYPE (POINTER, TYPE),        \
+         (TYPE) (POINTER))
+
+/* Given POINTER, a pointer to the given MEMBER within structure
+   STRUCT, returns the address of the STRUCT. */
+#define UP_CAST(POINTER, STRUCT, MEMBER)                                \
+        (CHECK_POINTER_COMPATIBILITY (&((STRUCT *) 0)->MEMBER, POINTER), \
+         (STRUCT *) ((char *) (POINTER) - offsetof (STRUCT, MEMBER)))
+
+#endif /* libpspp/cast.h */
index 57fc2678d09548ad50922b9130298afb6cf80cac..e586bcc0c18d1ae79242dc5a6ba42068db5b1f63 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
index 134224373d2ec2dbf6536b4d7909799df59b06d6..3a3c516a03485ab7e4a5ecac923719c3425c4ee0 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -70,6 +70,7 @@
 #ifndef LIBPSPP_HEAP_H
 #define LIBPSPP_HEAP_H 1
 
+#include <libpspp/cast.h>
 #include <stdbool.h>
 #include <stddef.h>
 
@@ -78,8 +79,9 @@ struct pool;
 /* Returns the data structure corresponding to the given heap
    NODE, assuming that NODE is embedded as the given MEMBER name
    in data type STRUCT. */
-#define heap_data(NODE, STRUCT, MEMBER)                                 \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define heap_data(NODE, STRUCT, MEMBER)                         \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct heap_node *),     \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* A node in a heap.  Opaque.
    One of these structures must be embedded in your heap node. */
index e73d84fd153764ec96935c6242dc2388820c5757..1592f803bedb17bd9c1ca4dc285f5bd6e72fd1c4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
        }
    */
 
+#include <stdbool.h>
 #include <stddef.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT.  NODE must not be a null pointer. */
 #define HMAP_DATA(NODE, STRUCT, MEMBER)                         \
-  ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+        (CHECK_POINTER_HAS_TYPE (NODE, struct hmap_node *),     \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* Like HMAP_DATA, except that a null NODE yields a null pointer
    result. */
@@ -180,6 +183,7 @@ static inline struct hmap_node *hmap_next (const struct hmap *,
                                            const struct hmap_node *);
 
 /* Counting. */
+static bool hmap_is_empty (const struct hmap *);
 static inline size_t hmap_count (const struct hmap *);
 static inline size_t hmap_capacity (const struct hmap *);
 
@@ -440,6 +444,14 @@ hmap_next (const struct hmap *map, const struct hmap_node *node)
           : hmap_first_nonempty_bucket__ (map, (node->hash & map->mask) + 1));
 }
 
+/* Returns true if MAP currently contains no data items, false
+   otherwise. */
+static inline bool
+hmap_is_empty (const struct hmap *map)
+{
+  return map->count == 0;
+}
+
 /* Returns the number of data items currently in MAP. */
 static inline size_t
 hmap_count (const struct hmap *map) 
index d73245084178025d2f550c47af66487590310b7a..437b2bdba5a7aad4ad301f672fc3681b5f2010fd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@ hmapx_destroy (struct hmapx *map)
 {
   if (map != NULL) 
     {
-      if (hmapx_count (map) > 0) 
+      if (!(hmapx_is_empty (map)))
         {
           struct hmapx_node *node, *next;
           for (node = hmapx_first (map); node != NULL; node = next)
index 32a4452fe2417d58157ca8da27e079f2052fcc88..d463504a52de33ca1a1cc45bde187dfaaadd71f7 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -165,6 +165,7 @@ static inline struct hmapx_node *hmapx_next (const struct hmapx *,
                                              const struct hmapx_node *);
 
 /* Counting. */
+static inline bool hmapx_is_empty (const struct hmapx *);
 static inline size_t hmapx_count (const struct hmapx *);
 static inline size_t hmapx_capacity (const struct hmapx *);
 
@@ -404,6 +405,14 @@ hmapx_next (const struct hmapx *map, const struct hmapx_node *node)
   return HMAP_NEXT (node, struct hmapx_node, hmap_node, &map->hmap);
 }
 
+/* Returns true if MAP currently contains no data items, false
+   otherwise. */
+static inline bool
+hmapx_is_empty (const struct hmapx *map)
+{
+  return hmap_is_empty (&map->hmap);
+}
+
 /* Returns the number of data items currently in MAP. */
 static inline size_t
 hmapx_count (const struct hmapx *map) 
index b9d595bc97a00212e9ebc1c156a1a01f75ed1556..e6533f2cd25fccf5beb2cbf9a4da31c279022027 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -169,7 +169,7 @@ ll_find_equal (const struct ll *r0, const struct ll *r1,
   for (x = r0; x != r1; x = ll_next (x))
     if (compare (x, target, aux) == 0)
       break;
-  return (struct ll *) x;
+  return CONST_CAST (struct ll *, x);
 }
 
 /* Returns the first node in R0...R1 for which PREDICATE returns
@@ -185,7 +185,7 @@ ll_find_if (const struct ll *r0, const struct ll *r1,
   for (x = r0; x != r1; x = ll_next (x))
     if (predicate (x, aux))
       break;
-  return (struct ll *) x;
+  return CONST_CAST (struct ll *, x);
 }
 
 /* Compares each pair of adjacent nodes in R0...R1
@@ -203,10 +203,10 @@ ll_find_adjacent_equal (const struct ll *r0, const struct ll *r1,
 
       for (x = r0, y = ll_next (x); y != r1; x = y, y = ll_next (y))
         if (compare (x, y, aux) == 0)
-          return (struct ll *) x;
+          return CONST_CAST (struct ll *, x);
     }
 
-  return (struct ll *) r1;
+  return CONST_CAST (struct ll *, r1);
 }
 
 /* Returns the number of nodes in R0...R1.
@@ -272,7 +272,7 @@ ll_max (const struct ll *r0, const struct ll *r1,
         if (compare (x, max, aux) > 0)
           max = x;
     }
-  return (struct ll *) max;
+  return CONST_CAST (struct ll *, max);
 }
 
 /* Returns the least node in R0...R1 according to COMPARE given
@@ -291,7 +291,7 @@ ll_min (const struct ll *r0, const struct ll *r1,
         if (compare (x, min, aux) < 0)
           min = x;
     }
-  return (struct ll *) min;
+  return CONST_CAST (struct ll *, min);
 }
 
 /* Lexicographically compares A0...A1 to B0...B1.
@@ -474,7 +474,7 @@ ll_find_run (const struct ll *r0, const struct ll *r1,
       while (r0 != r1 && compare (ll_prev (r0), r0, aux) <= 0);
     }
 
-  return (struct ll *) r0;
+  return CONST_CAST (struct ll *, r0);
 }
 
 /* Merges B0...B1 into A0...A1 according to COMPARE given
@@ -681,6 +681,6 @@ ll_find_partition (const struct ll *r0, const struct ll *r1,
     if (predicate (x, aux))
       return NULL;
 
-  return (struct ll *) partition;
+  return CONST_CAST (struct ll *, partition);
 }
 \f
index 65ecf55f2cad759a50085a85e90f1e3c79cf7f02..bf871f6b0f842a0a42726de48364aad08ea4d3ea 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -50,6 +50,9 @@
 #include <assert.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <libpspp/cast.h>
+
+#include <libpspp/cast.h>
 
 /* Embedded, circular doubly linked list.
 
 /* Returns the data structure corresponding to the given node LL,
    assuming that LL is embedded as the given MEMBER name in data
    type STRUCT. */
-#define ll_data(LL, STRUCT, MEMBER)                                    \
-        ((STRUCT *) ((char *) (LL) - offsetof (STRUCT, MEMBER)))
+#define ll_data(LL, STRUCT, MEMBER)                     \
+        (CHECK_POINTER_HAS_TYPE(LL, struct ll *),       \
+         UP_CAST(LL, STRUCT, MEMBER))
 
 /* Linked list node. */
 struct ll
@@ -378,7 +382,7 @@ ll_tail (const struct ll_list *list)
 static inline struct ll *
 ll_null (const struct ll_list *list)
 {
-  return (struct ll *) &list->null;
+  return CONST_CAST (struct ll *, &list->null);
 }
 
 /* Returns the node following LL in its list,
index 4dc82bffeb66219bf37d12f7cff7131a0c73cccc..c58f840c40a6db106d250e0cb6b5a9dec39efa5f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -232,7 +232,7 @@ llx_find_equal (const struct llx *r0, const struct llx *r1,
   for (x = r0; x != r1; x = llx_next (x))
     if (compare (llx_data (x), target, aux) == 0)
       break;
-  return (struct llx *) x;
+  return CONST_CAST (struct llx *, x);
 }
 
 /* Returns the first node in R0...R1 for which PREDICATE returns
@@ -248,7 +248,7 @@ llx_find_if (const struct llx *r0, const struct llx *r1,
   for (x = r0; x != r1; x = llx_next (x))
     if (predicate (llx_data (x), aux))
       break;
-  return (struct llx *) x;
+  return CONST_CAST (struct llx *, x);
 }
 
 /* Compares each pair of adjacent nodes in R0...R1
@@ -266,10 +266,10 @@ llx_find_adjacent_equal (const struct llx *r0, const struct llx *r1,
 
       for (x = r0, y = llx_next (x); y != r1; x = y, y = llx_next (y))
         if (compare (llx_data (x), llx_data (y), aux) == 0)
-          return (struct llx *) x;
+          return CONST_CAST (struct llx *, x);
     }
 
-  return (struct llx *) r1;
+  return CONST_CAST (struct llx *, r1);
 }
 
 /* Returns the number of nodes in R0...R1.
@@ -329,7 +329,7 @@ llx_max (const struct llx *r0, const struct llx *r1,
         if (compare (llx_data (x), llx_data (max), aux) > 0)
           max = x;
     }
-  return (struct llx *) max;
+  return CONST_CAST (struct llx *, max);
 }
 
 /* Returns the least node in R0...R1 according to COMPARE given
@@ -348,7 +348,7 @@ llx_min (const struct llx *r0, const struct llx *r1,
         if (compare (llx_data (x), llx_data (min), aux) < 0)
           min = x;
     }
-  return (struct llx *) min;
+  return CONST_CAST (struct llx *, min);
 }
 
 /* Lexicographically compares A0...A1 to B0...B1.
@@ -521,7 +521,7 @@ llx_find_run (const struct llx *r0, const struct llx *r1,
                                   llx_data (r0), aux) <= 0);
     }
 
-  return (struct llx *) r0;
+  return CONST_CAST (struct llx *, r0);
 }
 
 /* Merges B0...B1 into A0...A1 according to COMPARE given
@@ -734,7 +734,7 @@ llx_find_partition (const struct llx *r0, const struct llx *r1,
     if (predicate (llx_data (x), aux))
       return NULL;
 
-  return (struct llx *) partition;
+  return CONST_CAST (struct llx *, partition);
 }
 \f
 /* Allocates and returns a node using malloc. */
index 3dd77c9ffac30f2fb85429df8a35091ce108c7ef..024fe93790e507a052e14e4990152b9b17c7ea91 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <stdbool.h>
 
 #include <libpspp/bt.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
 #define range_map_data(NODE, STRUCT, MEMBER)                            \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+        (CHECK_POINTER_HAS_TYPE (NODE, struct range_map_node *),        \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* A range map node, to be embedded in the data value. */
 struct range_map_node
index efa3b4d670e7e06c3c560ebc64ade1bf4d45ae21..cd17fe02a4e2130544d5d37687cf46210ff2de39 100644 (file)
@@ -287,7 +287,7 @@ range_set_allocate_fully (struct range_set *rs, unsigned long int request,
 bool
 range_set_contains (const struct range_set *rs_, unsigned long int position)
 {
-  struct range_set *rs = (struct range_set *) rs_;
+  struct range_set *rs = CONST_CAST (struct range_set *, rs_);
   if (position < rs->cache_end && position >= rs->cache_start)
     return rs->cache_value;
   else
@@ -328,7 +328,7 @@ range_set_contains (const struct range_set *rs_, unsigned long int position)
 unsigned long int
 range_set_scan (const struct range_set *rs_, unsigned long int start)
 {
-  struct range_set *rs = (struct range_set *) rs_;
+  struct range_set *rs = CONST_CAST (struct range_set *, rs_);
   unsigned long int retval = ULONG_MAX;
   struct bt_node *bt_node;
 
index 941692b40029098ea8224ea4d0a4f0e1b9026385..ee7dac23137d3c39b392b13cb11c7d560c71fc79 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <stdbool.h>
 #include <libpspp/bt.h>
+#include <libpspp/cast.h>
 
 /* A set of ranges. */
 struct range_set
@@ -122,7 +123,7 @@ static inline const struct range_set_node *
 range_set_next (const struct range_set *rs, const struct range_set_node *node)
 {
   return (node != NULL
-          ? range_set_next__ (rs, (struct range_set_node *) node)
+          ? range_set_next__ (rs, CONST_CAST (struct range_set_node *, node))
           : range_set_first__ (rs));
 }
 
@@ -147,7 +148,7 @@ static inline const struct range_set_node *
 range_set_prev (const struct range_set *rs, const struct range_set_node *node)
 {
   return (node != NULL
-          ? range_set_prev__ (rs, (struct range_set_node *) node)
+          ? range_set_prev__ (rs, CONST_CAST (struct range_set_node *, node))
           : range_set_last__ (rs));
 }
 
index 28398d5cc86321e9e648a9c0ac30c052c1d26641..298f2caf9eb9ae09108aa247fc6db7b000183de1 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 
@@ -577,7 +578,7 @@ leaf_size (const struct sparse_array *spar)
 static struct leaf_node *
 find_leaf_node (const struct sparse_array *spar_, unsigned long int key)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
+  struct sparse_array *spar = CONST_CAST (struct sparse_array *, spar_);
   const union pointer *p;
   int level;
 
@@ -679,7 +680,7 @@ static void *
 scan_forward (const struct sparse_array *spar_, unsigned long int start,
               unsigned long int *found)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
+  struct sparse_array *spar = CONST_CAST (struct sparse_array *, spar_);
 
   /* Check the cache. */
   if (start >> BITS_PER_LEVEL == spar->cache_ofs)
@@ -761,7 +762,7 @@ static void *
 scan_reverse (const struct sparse_array *spar_, unsigned long int start,
               unsigned long int *found)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
+  struct sparse_array *spar = CONST_CAST (struct sparse_array *, spar_);
 
   /* Check the cache. */
   if (start >> BITS_PER_LEVEL == spar->cache_ofs)
index afe32de9f2049bfcc5a80ac7a95b36b348be4cb7..8243aa0690aad24a9b519db9bd1838724d32d729 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/pool.h>
 
@@ -1214,7 +1215,7 @@ ds_capacity (const struct string *st)
 char *
 ds_cstr (const struct string *st_)
 {
-  struct string *st = (struct string *) st_;
+  struct string *st = CONST_CAST (struct string *, st_);
   if (st->ss.string == NULL)
     ds_extend (st, 1);
   st->ss.string[st->ss.length] = '\0';
diff --git a/src/libpspp/string-map.c b/src/libpspp/string-map.c
new file mode 100644 (file)
index 0000000..4812479
--- /dev/null
@@ -0,0 +1,366 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <libpspp/string-map.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/hash-functions.h>
+#include <libpspp/string-set.h>
+
+#include "gl/xalloc.h"
+
+static struct string_map_node *string_map_find_node__ (
+  const struct string_map *, const char *key, unsigned int hash);
+static bool string_map_delete__ (struct string_map *, const char *key,
+                                 unsigned int hash);
+static struct string_map_node *string_map_insert__ (struct string_map *,
+                                                    char *key, char *value,
+                                                    unsigned int hash);
+
+/* Sets NODE's value to a copy of NEW_VALUE and returns the node's previous
+   value.  The caller is responsible for freeing the returned string (with
+   free()). */
+char *
+string_map_node_swap_value (struct string_map_node *node,
+                            const char *new_value)
+{
+  return string_map_node_swap_value_nocopy (node, xstrdup (new_value));
+}
+
+/* Sets NODE's value to NEW_VALUE, which must be a malloc()'d string,
+   transferring ownership of NEW_VALUE to the node.  Returns the node's
+   previous value, which the caller is responsible for freeing (with
+   free()). */
+char *
+string_map_node_swap_value_nocopy (struct string_map_node *node,
+                                   char *new_value)
+{
+  char *old_value = node->value;
+  node->value = new_value;
+  return old_value;
+}
+
+/* Replaces NODE's value by a copy of VALUE. */
+void
+string_map_node_set_value (struct string_map_node *node, const char *value)
+{
+  string_map_node_set_value_nocopy (node, xstrdup (value));
+}
+
+/* Replaces NODE's value by VALUE, which must be a malloc()'d string,
+   transferring ownership of VALUE to the node.. */
+void
+string_map_node_set_value_nocopy (struct string_map_node *node, char *value)
+{
+  free (node->value);
+  node->value = value;
+}
+
+/* Frees NODE and and its key and value.  Ordinarily nodes are owned by
+   string_maps, but this function should only be used by a caller that owns
+   NODE, such as one that has called string_map_delete_nofree() for the
+   node. */
+void
+string_map_node_destroy (struct string_map_node *node)
+{
+  free (node->key);
+  free (node->value);
+  free (node);
+}
+\f
+/* Initializes MAP as an initially empty string map. */
+void
+string_map_init (struct string_map *map)
+{
+  hmap_init (&map->hmap);
+}
+
+/* Initializes MAP as a new string map that initially contains the same pairs
+   as OLD. */
+void
+string_map_clone (struct string_map *map, const struct string_map *old)
+{
+  const struct string_map_node *node;
+  const char *key, *value;
+
+  string_map_init (map);
+  hmap_reserve (&map->hmap, string_map_count (old));
+  STRING_MAP_FOR_EACH_KEY_VALUE (key, value, node, old)
+    string_map_insert__ (map, xstrdup (key), xstrdup (value),
+                         node->hmap_node.hash);
+}
+
+/* Exchanges the contents of string maps A and B. */
+void
+string_map_swap (struct string_map *a, struct string_map *b)
+{
+  hmap_swap (&a->hmap, &b->hmap);
+}
+
+/* Frees MAP and its nodes and key-value pairs. */
+void
+string_map_destroy (struct string_map *map)
+{
+  if (map != NULL)
+    {
+      string_map_clear (map);
+      hmap_destroy (&map->hmap);
+    }
+}
+
+/* Returns true if MAP contains KEY as a key, otherwise false. */
+bool
+string_map_contains (const struct string_map *map, const char *key)
+{
+  return string_map_find_node (map, key) != NULL;
+}
+
+/* If MAP contains KEY as a key, returns the corresponding value.  Otherwise,
+   returns a null pointer. */
+const char *
+string_map_find (const struct string_map *map, const char *key)
+{
+  const struct string_map_node *node = string_map_find_node (map, key);
+  return node != NULL ? node->value : NULL;
+}
+
+/* If MAP contains KEY as a key, returns the corresponding node.  Otherwise,
+   returns a null pointer. */
+struct string_map_node *
+string_map_find_node (const struct string_map *map, const char *key)
+{
+  return string_map_find_node__ (map, key, hash_string (key, 0));
+}
+
+/* If MAP contains KEY as a key, deletes that key's node and returns its value,
+   which the caller is responsible for freeing (using free()).  Otherwise,
+   returns a null pointer. */
+char *
+string_map_find_and_delete (struct string_map *map, const char *key)
+{
+  struct string_map_node *node = string_map_find_node (map, key);
+  char *value = NULL;
+  if (node != NULL)
+    {
+      value = node->value;
+      node->value = NULL;
+      string_map_delete_node (map, node);
+    }
+  return value;
+}
+
+/* If MAP does not contain KEY as a key, inserts a new node containing copies
+   of KEY and VALUE and returns the new node.  Otherwise, returns the existing
+   node that contains KEY. */
+struct string_map_node *
+string_map_insert (struct string_map *map, const char *key, const char *value)
+{
+  unsigned int hash = hash_string (key, 0);
+  struct string_map_node *node = string_map_find_node__ (map, key, hash);
+  if (node == NULL)
+    node = string_map_insert__ (map, xstrdup (key), xstrdup (value), hash);
+  return node;
+}
+
+/* If MAP does not contain KEY as a key, inserts a new node containing KEY and
+   VALUE and returns the new node.  Otherwise, returns the existing node that
+   contains KEY.  Either way, ownership of KEY and VALUE is transferred to
+   MAP. */
+struct string_map_node *
+string_map_insert_nocopy (struct string_map *map, char *key, char *value)
+{
+  unsigned int hash = hash_string (key, 0);
+  struct string_map_node *node = string_map_find_node__ (map, key, hash);
+  if (node == NULL)
+    node = string_map_insert__ (map, key, value, hash);
+  else
+    {
+      free (key);
+      free (value);
+    }
+  return node;
+}
+
+/* If MAP does not contain KEY as a key, inserts a new node containing copies
+   of KEY and VALUE.  Otherwise, replaces the existing node's value by a copy
+   of VALUE.  Returns the node. */
+struct string_map_node *
+string_map_replace (struct string_map *map, const char *key, const char *value)
+{
+  unsigned int hash = hash_string (key, 0);
+  struct string_map_node *node = string_map_find_node__ (map, key, hash);
+  if (node == NULL)
+    node = string_map_insert__ (map, xstrdup (key), xstrdup (value), hash);
+  else
+    string_map_node_set_value (node, value);
+  return node;
+}
+
+/* If MAP does not contain KEY as a key, inserts a new node containing KEY and
+   VALUE.  Otherwise, replaces the existing node's value by VALUE.  Either way,
+   ownership of KEY and VALUE is transferred to MAP.  Returns the node. */
+struct string_map_node *
+string_map_replace_nocopy (struct string_map *map, char *key, char *value)
+{
+  unsigned int hash = hash_string (key, 0);
+  struct string_map_node *node = string_map_find_node__ (map, key, hash);
+  if (node == NULL)
+    node = string_map_insert__ (map, key, value, hash);
+  else
+    {
+      free (key);
+      string_map_node_set_value_nocopy (node, value);
+    }
+  return node;
+}
+
+/* Searches MAP for a node with KEY as its key.  If found, deletes the node
+   and its key and value and returns true.  Otherwise, returns false without
+   modifying MAP. */
+bool
+string_map_delete (struct string_map *map, const char *key)
+{
+  return string_map_delete__ (map, key, hash_string (key, 0));
+}
+
+/* Deletes NODE from MAP and destroys the node and its key and value. */
+void
+string_map_delete_node (struct string_map *map, struct string_map_node *node)
+{
+  string_map_delete_nofree (map, node);
+  string_map_node_destroy (node);
+}
+
+/* Deletes NODE from MAP.  Transfers ownership of NODE to the caller, which
+   becomes responsible for destroying it. */
+void
+string_map_delete_nofree (struct string_map *map, struct string_map_node *node)
+{
+  hmap_delete (&map->hmap, &node->hmap_node);
+}
+
+/* Removes all nodes from MAP and frees them and their keys and values. */
+void
+string_map_clear (struct string_map *map)
+{
+  struct string_map_node *node, *next;
+
+  STRING_MAP_FOR_EACH_NODE_SAFE (node, next, map)
+    string_map_delete_node (map, node);
+}
+
+/* Inserts a copy of each of the nodes in SRC into DST.  When SRC and DST both
+   have a particular key, the value in DST's node is left unchanged. */
+void
+string_map_insert_map (struct string_map *dst, const struct string_map *src)
+{
+  const struct string_map_node *node;
+
+  STRING_MAP_FOR_EACH_NODE (node, src)
+    {
+      if (!string_map_find_node__ (dst, node->key, node->hmap_node.hash))
+        string_map_insert__ (dst, xstrdup (node->key), xstrdup (node->value),
+                             node->hmap_node.hash);
+    }
+}
+
+/* Inserts a copy of each of the nodes in SRC into DST.  When SRC and DST both
+   have a particular key, the value in DST's node is replaced by a copy of the
+   value in SRC's node. */
+void
+string_map_replace_map (struct string_map *dst, const struct string_map *src)
+{
+  const struct string_map_node *snode;
+
+  STRING_MAP_FOR_EACH_NODE (snode, src)
+    {
+      struct string_map_node *dnode;
+      dnode = string_map_find_node__ (dst, snode->key, snode->hmap_node.hash);
+      if (dnode != NULL)
+        string_map_node_set_value (dnode, snode->value);
+      else
+        string_map_insert__ (dst, xstrdup (snode->key), xstrdup (snode->value),
+                             snode->hmap_node.hash);
+    }
+}
+
+/* Inserts each of the keys in MAP into KEYS.  KEYS must already have been
+   initialized (using string_set_init()). */
+void
+string_map_get_keys (const struct string_map *map, struct string_set *keys)
+{
+  const struct string_map_node *node;
+  const char *key;
+
+  STRING_MAP_FOR_EACH_KEY (key, node, map)
+    string_set_insert (keys, key);
+}
+
+/* Inserts each of the values in MAP into VALUES.  VALUES must already have
+   been initialized (using string_set_init()). */
+void
+string_map_get_values (const struct string_map *map, struct string_set *values)
+{
+  const struct string_map_node *node;
+  const char *value;
+
+  STRING_MAP_FOR_EACH_VALUE (value, node, map)
+    string_set_insert (values, value);
+}
+\f
+static struct string_map_node *
+string_map_find_node__ (const struct string_map *map, const char *key,
+                        unsigned int hash)
+{
+  struct string_map_node *node;
+
+  HMAP_FOR_EACH_WITH_HASH (node, struct string_map_node, hmap_node,
+                           hash, &map->hmap)
+    if (!strcmp (key, node->key))
+      return node;
+
+  return NULL;
+}
+
+static bool
+string_map_delete__ (struct string_map *map, const char *key,
+                     unsigned int hash)
+{
+  struct string_map_node *node = string_map_find_node__ (map, key, hash);
+  if (node != NULL)
+    {
+      string_map_delete_node (map, node);
+      return true;
+    }
+  else
+    return false;
+}
+
+static struct string_map_node *
+string_map_insert__ (struct string_map *map, char *key, char *value,
+                     unsigned int hash)
+{
+  struct string_map_node *node = xmalloc (sizeof *node);
+  node->key = key;
+  node->value = value;
+  hmap_insert (&map->hmap, &node->hmap_node, hash);
+  return node;
+}
+
diff --git a/src/libpspp/string-map.h b/src/libpspp/string-map.h
new file mode 100644 (file)
index 0000000..f8ffabc
--- /dev/null
@@ -0,0 +1,213 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_STRING_MAP_H
+#define LIBPSPP_STRING_MAP_H
+
+/* Map from a unique string key to a string value.
+
+   This is a convenient wrapper around a "struct hmap" for storing string
+   key-value pairs. */
+
+#include <stdbool.h>
+#include <libpspp/hmap.h>
+
+struct string_set;
+
+/* A node within a string map. */
+struct string_map_node
+  {
+    struct hmap_node hmap_node;
+    char *key;
+    char *value;
+  };
+
+/* Returns the string key within NODE.  The caller must not modify or free the
+   returned string. */
+static inline const char *
+string_map_node_get_key (const struct string_map_node *node)
+{
+  return node->key;
+}
+
+/* Returns the string key within NODE.  The caller must not free the returned
+   string. */
+static inline const char *
+string_map_node_get_value (const struct string_map_node *node)
+{
+  return node->value;
+}
+
+/* Returns the string key within NODE.  The caller must not free the returned
+   string. */
+static inline char *
+string_map_node_get_value_rw (struct string_map_node *node)
+{
+  return node->value;
+}
+
+char *string_map_node_swap_value (struct string_map_node *,
+                                  const char *new_value);
+char *string_map_node_swap_value_nocopy (struct string_map_node *,
+                                         char *new_value);
+void string_map_node_set_value (struct string_map_node *, const char *value);
+void string_map_node_set_value_nocopy (struct string_map_node *, char *value);
+void string_map_node_destroy (struct string_map_node *);
+\f
+/* Unordered map from unique string keys to string values. */
+struct string_map
+  {
+    struct hmap hmap;
+  };
+
+/* Suitable for use as the initializer for a string_map named MAP.  Typical
+   usage:
+       struct string_map map = STRING_MAP_INITIALIZER (map);
+   STRING_MAP_INITIALIZER is an alternative to calling string_map_init. */
+#define STRING_MAP_INITIALIZER(MAP) { HMAP_INITIALIZER ((MAP).hmap) }
+
+void string_map_init (struct string_map *);
+void string_map_clone (struct string_map *, const struct string_map *);
+void string_map_swap (struct string_map *, struct string_map *);
+void string_map_destroy (struct string_map *);
+
+static inline size_t string_map_count (const struct string_map *);
+static inline bool string_map_is_empty (const struct string_map *);
+
+bool string_map_contains (const struct string_map *, const char *);
+const char *string_map_find (const struct string_map *, const char *);
+struct string_map_node *string_map_find_node (const struct string_map *,
+                                              const char *);
+char *string_map_find_and_delete (struct string_map *, const char *key);
+
+struct string_map_node *string_map_insert (struct string_map *,
+                                           const char *key, const char *value);
+struct string_map_node *string_map_insert_nocopy (struct string_map *,
+                                                  char *key, char *value);
+struct string_map_node *string_map_replace (struct string_map *,
+                                           const char *key, const char *value);
+struct string_map_node *string_map_replace_nocopy (struct string_map *,
+                                                   char *key, char *value);
+bool string_map_delete (struct string_map *, const char *);
+void string_map_delete_node (struct string_map *, struct string_map_node *);
+void string_map_delete_nofree (struct string_map *, struct string_map_node *);
+
+void string_map_clear (struct string_map *);
+void string_map_insert_map (struct string_map *, const struct string_map *);
+void string_map_replace_map (struct string_map *, const struct string_map *);
+
+void string_map_get_keys (const struct string_map *, struct string_set *);
+void string_map_get_values (const struct string_map *, struct string_set *);
+
+static inline struct string_map_node *string_map_first (
+  const struct string_map *);
+static inline struct string_map_node *string_map_next (
+  const struct string_map *, const struct string_map_node *);
+
+/* Macros for conveniently iterating through a string_map, e.g. to print all of
+   the key-value pairs in "my_map":
+
+   struct string_map_node *node;
+   const char *key, *value;
+
+   STRING_MAP_FOR_EACH_KEY_VALUE (key, value, node, &my_map)
+     printf ("%s=%s\n", key, value);
+   */
+#define STRING_MAP_FOR_EACH_NODE(NODE, MAP)                     \
+        for ((NODE) = string_map_first (MAP); (NODE) != NULL;   \
+             (NODE) = string_map_next (MAP, NODE))
+#define STRING_MAP_FOR_EACH_NODE_SAFE(NODE, NEXT, MAP)          \
+        for ((NODE) = string_map_first (MAP);                   \
+             ((NODE) != NULL                                    \
+              && ((NEXT) = string_map_next (MAP, NODE), 1));    \
+             (NODE) = (NEXT))
+#define STRING_MAP_FOR_EACH_KEY(KEY, NODE, MAP)                 \
+        for ((NODE) = string_map_first (MAP);                   \
+             ((NODE) != NULL                                    \
+              && ((KEY) = string_map_node_get_key (NODE), 1));  \
+             (NODE) = string_map_next (MAP, NODE))
+#define STRING_MAP_FOR_EACH_KEY_SAFE(KEY, NODE, NEXT, MAP)      \
+        for ((NODE) = string_map_first (MAP);                   \
+             ((NODE) != NULL                                    \
+              && ((KEY) = string_map_node_get_key (NODE), 1)    \
+              && ((NEXT) = string_map_next (MAP, NODE), 1));    \
+             (NODE) = (NEXT))
+#define STRING_MAP_FOR_EACH_VALUE(VALUE, NODE, MAP)     \
+        for ((NODE) = string_map_first (MAP);           \
+             ((NODE) != NULL                            \
+              && ((VALUE) = (NODE)->value, 1));         \
+             (NODE) = string_map_next (MAP, NODE))
+#define STRING_MAP_FOR_EACH_VALUE_SAFE(VALUE, NODE, NEXT, MAP)  \
+        for ((NODE) = string_map_first (MAP);                   \
+             ((NODE) != NULL                                    \
+              && ((VALUE) = (NODE)->value, 1)                   \
+              && ((NEXT) = string_map_next (MAP, NODE), 1));    \
+             (NODE) = (NEXT))
+#define STRING_MAP_FOR_EACH_KEY_VALUE(KEY, VALUE, NODE, MAP)    \
+        for ((NODE) = string_map_first (MAP);                   \
+             ((NODE) != NULL                                    \
+              && ((KEY) = string_map_node_get_key (NODE), 1)    \
+              && ((VALUE) = (NODE)->value, 1));                 \
+             (NODE) = string_map_next (MAP, NODE))
+#define STRING_MAP_FOR_EACH_KEY_VALUE_SAFE(KEY, VALUE, NODE, NEXT, MAP) \
+        for ((NODE) = string_map_first (MAP);                           \
+             ((NODE) != NULL                                            \
+              && ((KEY) = string_map_node_get_key (NODE), 1)            \
+              && ((VALUE) = (NODE)->value, 1)                           \
+              && ((NEXT) = string_map_next (MAP, NODE), 1));            \
+             (NODE) = (NEXT))
+\f
+/* Returns the number of key-value pairs currently in MAP. */
+static inline size_t
+string_map_count (const struct string_map *map)
+{
+  return hmap_count (&map->hmap);
+}
+
+/* Returns true if MAP currently contains no key-value pairs, false
+   otherwise. */
+static inline bool
+string_map_is_empty (const struct string_map *map)
+{
+  return hmap_is_empty (&map->hmap);
+}
+
+/* Returns the first node in MAP, or a null pointer if MAP is empty.  See the
+   hmap_first function for information about complexity (O(1) amortized) and
+   ordering (arbitrary).
+
+   The STRING_MAP_FOR_EACH family of macros provide convenient ways to iterate
+   over all the nodes in a string map. */
+static inline struct string_map_node *
+string_map_first (const struct string_map *map)
+{
+  return HMAP_FIRST (struct string_map_node, hmap_node, &map->hmap);
+}
+
+/* Returns the next node in MAP following NODE, or a null pointer if NODE is
+   the last node in MAP.  See the hmap_next function for information about
+   complexity (O(1) amortized) and ordering (arbitrary).
+
+   The STRING_MAP_FOR_EACH family of macros provide convenient ways to iterate
+   over all the nodes in a string map. */
+static inline struct string_map_node *
+string_map_next (const struct string_map *map,
+                 const struct string_map_node *node)
+{
+  return HMAP_NEXT (node, struct string_map_node, hmap_node, &map->hmap);
+}
+
+#endif /* libpspp/string-map.h */
diff --git a/src/libpspp/string-set.c b/src/libpspp/string-set.c
new file mode 100644 (file)
index 0000000..3fd370d
--- /dev/null
@@ -0,0 +1,261 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+/* If you add routines in this file, please add a corresponding test to
+   string-set-test.c. */
+
+#include <config.h>
+
+#include <libpspp/string-set.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/hash-functions.h>
+
+#include "gl/xalloc.h"
+
+static struct string_set_node *string_set_find_node__ (
+  const struct string_set *, const char *, unsigned int hash);
+static void string_set_insert__ (struct string_set *, char *,
+                                 unsigned int hash);
+static bool string_set_delete__ (struct string_set *, const char *,
+                                 unsigned int hash);
+
+/* Initializes SET as a new string set that is initially empty. */
+void
+string_set_init (struct string_set *set)
+{
+  hmap_init (&set->hmap);
+}
+
+/* Initializes SET as a new string set that initially contains the same strings
+   as OLD. */
+void
+string_set_clone (struct string_set *set, const struct string_set *old)
+{
+  const struct string_set_node *node;
+  const char *s;
+
+  string_set_init (set);
+  hmap_reserve (&set->hmap, string_set_count (old));
+  STRING_SET_FOR_EACH (s, node, old)
+    string_set_insert__ (set, xstrdup (s), node->hmap_node.hash);
+}
+
+/* Exchanges the contents of string sets A and B. */
+void
+string_set_swap (struct string_set *a, struct string_set *b)
+{
+  hmap_swap (&a->hmap, &b->hmap);
+}
+
+/* Frees SET and its nodes and strings. */
+void
+string_set_destroy (struct string_set *set)
+{
+  if (set != NULL)
+    {
+      string_set_clear (set);
+      hmap_destroy (&set->hmap);
+    }
+}
+
+/* Returns true if SET contains S, false otherwise. */
+bool
+string_set_contains (const struct string_set *set, const char *s)
+{
+  return string_set_find_node (set, s) != NULL;
+}
+
+/* Returns the node in SET that contains S, or a null pointer if SET does not
+   contain S. */
+struct string_set_node *
+string_set_find_node (const struct string_set *set, const char *s)
+{
+  return string_set_find_node__ (set, s, hash_string (s, 0));
+}
+
+/* Inserts a copy of S into SET.  Returns true if successful, false if SET
+   is unchanged because it already contained S. */
+bool
+string_set_insert (struct string_set *set, const char *s)
+{
+  unsigned int hash = hash_string (s, 0);
+  if (!string_set_find_node__ (set, s, hash))
+    {
+      string_set_insert__ (set, xstrdup (s), hash);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Inserts S, which must be a malloc'd string, into SET, transferring ownership
+   of S to SET.  Returns true if successful, false if SET is unchanged because
+   it already contained a copy of S.  (In the latter case, S is freed.) */
+bool
+string_set_insert_nocopy (struct string_set *set, char *s)
+{
+  unsigned int hash = hash_string (s, 0);
+  if (!string_set_find_node__ (set, s, hash))
+    {
+      string_set_insert__ (set, s, hash);
+      return true;
+    }
+  else
+    {
+      free (s);
+      return false;
+    }
+}
+
+/* Deletes S from SET.  Returns true if successful, false if SET is unchanged
+   because it did not contain a copy of S. */
+bool
+string_set_delete (struct string_set *set, const char *s)
+{
+  return string_set_delete__ (set, s, hash_string (s, 0));
+}
+
+/* Deletes NODE from SET, and frees NODE and its string. */
+void
+string_set_delete_node (struct string_set *set, struct string_set_node *node)
+{
+  free (string_set_delete_nofree (set, node));
+}
+
+/* Deletes NODE from SET and frees NODE.  Returns the string that NODE
+   contained, transferring ownership to the caller. */
+char *
+string_set_delete_nofree (struct string_set *set, struct string_set_node *node)
+{
+  char *string = node->string;
+  hmap_delete (&set->hmap, &node->hmap_node);
+  free (node);
+  return string;
+}
+
+/* Removes all nodes from SET. */
+void
+string_set_clear (struct string_set *set)
+{
+  struct string_set_node *node, *next;
+
+  HMAP_FOR_EACH_SAFE (node, next, struct string_set_node, hmap_node,
+                      &set->hmap)
+    string_set_delete_node (set, node);
+}
+
+/* Calculates A = union(A, B).
+
+   If B may be modified, string_set_union_and_intersection() is
+   faster than this function. */
+void
+string_set_union (struct string_set *a, const struct string_set *b)
+{
+  struct string_set_node *node;
+  HMAP_FOR_EACH (node, struct string_set_node, hmap_node, &b->hmap)
+    if (!string_set_find_node__ (a, node->string, node->hmap_node.hash))
+      string_set_insert__ (a, xstrdup (node->string), node->hmap_node.hash);
+}
+
+/* Calculates A = union(A, B) and B = intersect(A, B).
+
+   If only the intersection is needed, string_set_intersect() is
+   faster. */
+void
+string_set_union_and_intersection (struct string_set *a, struct string_set *b)
+{
+  struct string_set_node *node, *next;
+
+  HMAP_FOR_EACH_SAFE (node, next, struct string_set_node, hmap_node,
+                      &b->hmap)
+    if (!string_set_find_node__ (a, node->string, node->hmap_node.hash))
+      {
+        hmap_delete (&b->hmap, &node->hmap_node);
+        hmap_insert (&a->hmap, &node->hmap_node, node->hmap_node.hash);
+      }
+}
+
+/* Calculates A = intersect(A, B). */
+void
+string_set_intersect (struct string_set *a, const struct string_set *b)
+{
+  struct string_set_node *node, *next;
+
+  HMAP_FOR_EACH_SAFE (node, next, struct string_set_node, hmap_node,
+                      &a->hmap)
+    if (!string_set_find_node__ (b, node->string, node->hmap_node.hash))
+      string_set_delete_node (a, node);
+}
+
+/* Removes from A all of the strings in B. */
+void
+string_set_subtract (struct string_set *a, const struct string_set *b)
+{
+  struct string_set_node *node, *next;
+
+  if (string_set_count (a) < string_set_count (b))
+    {
+      HMAP_FOR_EACH_SAFE (node, next, struct string_set_node, hmap_node,
+                          &a->hmap)
+        if (string_set_find_node__ (b, node->string, node->hmap_node.hash))
+          string_set_delete_node (a, node);
+    }
+  else
+    {
+      HMAP_FOR_EACH (node, struct string_set_node, hmap_node, &b->hmap)
+        string_set_delete__ (a, node->string, node->hmap_node.hash);
+    }
+}
+\f
+/* Internal functions. */
+
+static struct string_set_node *
+string_set_find_node__ (const struct string_set *set, const char *s,
+                        unsigned int hash)
+{
+  struct string_set_node *node;
+
+  HMAP_FOR_EACH_WITH_HASH (node, struct string_set_node, hmap_node,
+                           hash, &set->hmap)
+    if (!strcmp (s, node->string))
+      return node;
+
+  return NULL;
+}
+
+static void
+string_set_insert__ (struct string_set *set, char *s, unsigned int hash)
+{
+  struct string_set_node *node = xmalloc (sizeof *node);
+  node->string = s;
+  hmap_insert (&set->hmap, &node->hmap_node, hash);
+}
+
+static bool
+string_set_delete__ (struct string_set *set, const char *s, unsigned int hash)
+{
+  struct string_set_node *node = string_set_find_node__ (set, s, hash);
+  if (node != NULL)
+    {
+      string_set_delete_node (set, node);
+      return true;
+    }
+  else
+    return false;
+}
diff --git a/src/libpspp/string-set.h b/src/libpspp/string-set.h
new file mode 100644 (file)
index 0000000..a604e15
--- /dev/null
@@ -0,0 +1,148 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_STRING_SET_H
+#define LIBPSPP_STRING_SET_H
+
+/* Set of unique strings.
+
+   This is a convenient wrapper around a "struct hmap" for storing strings. */
+
+#include <stdbool.h>
+#include <libpspp/hmap.h>
+
+/* A node in the string set. */
+struct string_set_node
+  {
+    struct hmap_node hmap_node;
+    char *string;
+  };
+
+/* Returns the string within NODE.  The caller must not modify or free the
+   returned string. */
+static inline const char *
+string_set_node_get_string (const struct string_set_node *node)
+{
+  return node->string;
+}
+\f
+/* An unordered set of unique strings. */
+struct string_set
+  {
+    struct hmap hmap;
+  };
+
+/* Suitable for use as the initializer for a string_set named SET.  Typical
+   usage:
+       struct string_set set = STRING_SET_INITIALIZER (set);
+   STRING_SET_INITIALIZER is an alternative to calling string_set_init. */
+#define STRING_SET_INITIALIZER(SET) { HMAP_INITIALIZER ((SET).hmap) }
+
+void string_set_init (struct string_set *);
+void string_set_clone (struct string_set *, const struct string_set *);
+void string_set_swap (struct string_set *, struct string_set *);
+void string_set_destroy (struct string_set *);
+
+static inline size_t string_set_count (const struct string_set *);
+static inline bool string_set_is_empty (const struct string_set *);
+
+bool string_set_contains (const struct string_set *, const char *);
+struct string_set_node *string_set_find_node (const struct string_set *,
+                                              const char *);
+
+bool string_set_insert (struct string_set *, const char *);
+bool string_set_insert_nocopy (struct string_set *, char *);
+bool string_set_delete (struct string_set *, const char *);
+void string_set_delete_node (struct string_set *, struct string_set_node *);
+char *string_set_delete_nofree (struct string_set *, struct string_set_node *);
+
+void string_set_clear (struct string_set *);
+void string_set_union (struct string_set *, const struct string_set *);
+void string_set_union_and_intersection (struct string_set *,
+                                        struct string_set *);
+void string_set_intersect (struct string_set *, const struct string_set *);
+void string_set_subtract (struct string_set *, const struct string_set *);
+
+static inline const struct string_set_node *string_set_first (
+  const struct string_set *);
+static inline const struct string_set_node *string_set_next (
+  const struct string_set *, const struct string_set_node *);
+
+/* Macros for conveniently iterating through a string_set, e.g. to print all of
+   the strings in "my_set":
+
+   struct string_set_node *node;
+   const char *string;
+
+   STRING_SET_FOR_EACH (string, node, &my_set)
+     puts (string);
+   */
+#define STRING_SET_FOR_EACH(STRING, NODE, SET)                  \
+        for ((NODE) = string_set_first (SET);                   \
+             ((NODE) != NULL                                    \
+              ? ((STRING) = string_set_node_get_string (NODE),  \
+                 1)                                             \
+              : 0);                                             \
+             (NODE) = string_set_next (SET, NODE))
+#define STRING_SET_FOR_EACH_SAFE(STRING, NODE, NEXT, SET)       \
+        for ((NODE) = string_set_first (SET);                   \
+             ((NODE) != NULL                                    \
+              ? ((STRING) = string_set_node_get_string (NODE),  \
+                 (NEXT) = string_set_next (SET, NODE),          \
+                 1)                                             \
+              : 0);                                             \
+             (NODE) = (NEXT))
+\f
+/* Returns the number of strings currently in SET. */
+static inline size_t
+string_set_count (const struct string_set *set)
+{
+  return hmap_count (&set->hmap);
+}
+
+/* Returns true if SET currently contains no strings, false otherwise. */
+static inline bool
+string_set_is_empty (const struct string_set *set)
+{
+  return hmap_is_empty (&set->hmap);
+}
+
+/* Returns the first node in SET, or a null pointer if SET is empty.  See the
+   hmap_first function for information about complexity (O(1) amortized) and
+   ordering (arbitrary).
+
+   The STRING_SET_FOR_EACH and STRING_SET_FOR_EACH_SAFE macros provide
+   convenient ways to iterate over all the nodes in a string set. */
+static inline const struct string_set_node *
+string_set_first (const struct string_set *set)
+{
+  return HMAP_FIRST (struct string_set_node, hmap_node, &set->hmap);
+}
+
+/* Returns the next node in SET following NODE, or a null pointer if NODE is
+   the last node in SET.  See the hmap_next function for information about
+   complexity (O(1) amortized) and ordering (arbitrary).
+
+   The STRING_SET_FOR_EACH and STRING_SET_FOR_EACH_SAFE macros provide
+   convenient ways to iterate over all the nodes in a string set. */
+static inline const struct string_set_node *
+string_set_next (const struct string_set *set,
+                 const struct string_set_node *node)
+{
+  return HMAP_NEXT (node, struct string_set_node, hmap_node, &set->hmap);
+}
+
+#endif /* libpspp/string-set.h */
index 3a74587bb5918ae8cee61766619187b5ca758532..4c1cecb97cdbbdbcdd2302e91f2741e7a68a8bc2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@
 
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 
 #include "xalloc.h"
 
@@ -79,7 +80,7 @@ taint_create (void)
 struct taint *
 taint_clone (const struct taint *taint_)
 {
-  struct taint *taint = (struct taint *) taint_;
+  struct taint *taint = CONST_CAST (struct taint *, taint_);
 
   assert (taint->ref_cnt > 0);
   taint->ref_cnt++;
@@ -139,8 +140,8 @@ taint_destroy (struct taint *taint)
 void
 taint_propagate (const struct taint *from_, const struct taint *to_)
 {
-  struct taint *from = (struct taint *) from_;
-  struct taint *to = (struct taint *) to_;
+  struct taint *from = CONST_CAST (struct taint *, from_);
+  struct taint *to = CONST_CAST (struct taint *, to_);
 
   if (from != to)
     {
@@ -165,7 +166,7 @@ taint_is_tainted (const struct taint *taint)
 void
 taint_set_taint (const struct taint *taint_)
 {
-  struct taint *taint = (struct taint *) taint_;
+  struct taint *taint = CONST_CAST (struct taint *, taint_);
   if (!taint->tainted)
     recursively_set_taint (taint);
 }
@@ -186,7 +187,7 @@ taint_has_tainted_successor (const struct taint *taint)
 void
 taint_reset_successor_taint (const struct taint *taint_)
 {
-  struct taint *taint = (struct taint *) taint_;
+  struct taint *taint = CONST_CAST (struct taint *, taint_);
 
   if (taint->tainted_successor)
     {
index 0d3093646faa6a9d20a941048760f0d0d11d8be6..aff135d0cc0a1c27b0ea9af1d58dec6beb08d19a 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 
 #include "error.h"
 #include "xalloc.h"
@@ -81,7 +82,7 @@ tmpfile_destroy (struct tmpfile *tf)
 static bool
 do_seek (const struct tmpfile *tf_, off_t offset)
 {
-  struct tmpfile *tf = (struct tmpfile *) tf_;
+  struct tmpfile *tf = CONST_CAST (struct tmpfile *, tf_);
 
   if (!tmpfile_error (tf))
     {
@@ -106,7 +107,7 @@ do_seek (const struct tmpfile *tf_, off_t offset)
 static bool
 do_read (const struct tmpfile *tf_, void *buffer, size_t bytes)
 {
-  struct tmpfile *tf = (struct tmpfile *) tf_;
+  struct tmpfile *tf = CONST_CAST (struct tmpfile *, tf_);
 
   assert (!tmpfile_error (tf));
   if (bytes > 0 && fread (buffer, bytes, 1, tf->file) != 1)
index e8d253d086731c61dd539962cbc79f9c2b3b0750..9157987774e555c625d63af41a9343dc4447c574 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 #include <limits.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 
 static struct tower_node *abt_to_tower_node (const struct abt_node *);
@@ -184,7 +185,7 @@ tower_lookup (const struct tower *t_,
               unsigned long height,
               unsigned long *node_start)
 {
-  struct tower *t = (struct tower *) t_;
+  struct tower *t = CONST_CAST (struct tower *, t_);
   struct abt_node *p;
 
   assert (height < tower_height (t));
@@ -237,7 +238,7 @@ tower_lookup (const struct tower *t_,
 struct tower_node *
 tower_get (const struct tower *t_, unsigned long int index) 
 {
-  struct tower *t = (struct tower *) t_;
+  struct tower *t = CONST_CAST (struct tower *, t_);
   struct abt_node *p;
 
   assert (index < tower_count (t));
index 246984a2c1bfd6cd1c46100fbe95f84140843a92..9be8231c98ad7c9b0c7fc217a14a513d2813aa92 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <stdbool.h>
 #include <libpspp/abt.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
-#define tower_data(NODE, STRUCT, MEMBER)                            \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define tower_data(NODE, STRUCT, MEMBER)                        \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct tower_node *),    \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* A node within a tower. */
 struct tower_node
index 288fc072ef119ccb2c49b2001a015a34834378ad..de4124efe16c5beb799c7b5d0b2af42743ff79d7 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 #include "tukey-hinges.h"
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <math.h>
 #include <float.h>
 #include <data/val-type.h>
@@ -30,8 +31,8 @@
 static void
 destroy (struct statistic *s)
 {
-  struct order_stats *os = (struct order_stats *) s;
-  struct box_whisker *bw = (struct box_whisker *) s;
+  struct box_whisker *bw = UP_CAST (s, struct box_whisker, parent.parent);
+  struct order_stats *os = &bw->parent;
   struct ll *ll;
 
   for (ll = ll_head (&bw->outliers); ll != ll_null (&bw->outliers); )
@@ -53,7 +54,7 @@ static void
 acc (struct statistic *s, const struct ccase *cx,
      double c UNUSED, double cc UNUSED, double y)
 {
-  struct box_whisker *bw = (struct box_whisker *) s;
+  struct box_whisker *bw = UP_CAST (s, struct box_whisker, parent.parent);
   bool extreme;
   struct outlier *o;
 
@@ -110,13 +111,13 @@ box_whisker_outliers (const struct box_whisker *bw)
   return &bw->outliers;
 }
 
-struct statistic *
+struct box_whisker *
 box_whisker_create (const struct tukey_hinges *th,
                    const struct variable *id_var,  size_t casenumber_idx)
 {
   struct box_whisker *w = xzalloc (sizeof (*w));
-  struct order_stats *os = (struct order_stats *) w;
-  struct statistic *stat = (struct statistic *) w;
+  struct order_stats *os = &w->parent;
+  struct statistic *stat = &os->parent;
 
   os->n_k = 0;
 
@@ -135,5 +136,5 @@ box_whisker_create (const struct tukey_hinges *th,
 
   ll_init (&w->outliers);
 
-  return stat;
+  return w;
 }
index 5202b646727b184ccc22b3ffcfd7e14f3c32c8fd..bef091e83b448dafc5c601fab3d04eb0d60f19f6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -52,7 +52,7 @@ struct box_whisker
   const struct variable *id_var;
 };
 
-struct statistic * box_whisker_create (const struct tukey_hinges *,
+struct box_whisker * box_whisker_create (const struct tukey_hinges *,
                                         const struct variable *, size_t);
 
 void box_whisker_whiskers (const struct box_whisker *bw, double whiskers[2]);
index 67079398d169ec58737c6f3b94a7d243b9fc8516..3c88c3858f61c6b1c2230843a7b9151a47902956 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 
 #include <gsl/gsl_histogram.h>
 #include "chart-geometry.h"
@@ -28,7 +29,8 @@
 void
 histogram_add (struct histogram *h, double y, double c)
 {
-  ((struct statistic *)h)->accumulate ((struct statistic *) h, NULL, c, 0, y);
+  struct statistic *stat = &h->parent;
+  stat->accumulate (stat, NULL, c, 0, y);
 }
 
 
@@ -36,7 +38,7 @@ histogram_add (struct histogram *h, double y, double c)
 static void
 acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc UNUSED, double y)
 {
-  struct histogram *hist = (struct histogram *) s;
+  struct histogram *hist = UP_CAST (s, struct histogram, parent);
 
   gsl_histogram_accumulate (hist->gsl_hist, y, c);
 }
@@ -45,17 +47,17 @@ acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc UNU
 static void
 destroy (struct statistic *s)
 {
-  struct histogram *h = (struct histogram *) s;
+  struct histogram *h = UP_CAST (s, struct histogram, parent);
   gsl_histogram_free (h->gsl_hist);
   free (s);
 }
 
 
-struct statistic *
+struct histogram *
 histogram_create (int bins, double min, double max)
 {
   struct histogram *h = xmalloc (sizeof *h);
-  struct statistic *stat = (struct statistic *) h;
+  struct statistic *stat = &h->parent;
   double upper_limit, lower_limit;
 
   double bin_width = chart_rounded_tick ((max - min) / (double) bins);
@@ -78,6 +80,6 @@ histogram_create (int bins, double min, double max)
   stat->accumulate = acc;
   stat->destroy = destroy;
 
-  return stat;
+  return h;
 }
 
index b2b204ee808098c2bf378ef99e8419c6d4223988..bc4a5ae6c1354d9711a467ceba8cf904a875a8fa 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@ struct histogram
   gsl_histogram *gsl_hist;
 };
 
-struct statistic * histogram_create (int bins, double max, double min);
+struct histogram * histogram_create (int bins, double max, double min);
 
 void histogram_add (struct histogram *h, double y, double c);
 
index 7bd582105f940c28ffeb26506d4c5fe941ff728d..f7e269991346cd0c6cb076eacec04bcbbd62590c 100644 (file)
@@ -246,9 +246,7 @@ levene2_precalc (struct levene_info *l)
       struct hsh_table *hash = group_proc_get (var)->group_hash;
 
 
-      for(g = (struct group_statistics *) hsh_first(hash,&hi);
-         g != 0 ;
-         g = (struct group_statistics *) hsh_next(hash,&hi) )
+      for (g = hsh_first(hash,&hi); g != 0; g = hsh_next(hash, &hi))
        {
          g->lz_mean = g->lz_total / g->n ;
        }
@@ -308,9 +306,7 @@ levene2_postcalc (struct levene_info *l)
       struct group_proc *gp = group_proc_get (var);
       struct hsh_table *hash = gp->group_hash;
 
-      for(g = (struct group_statistics *) hsh_first(hash,&hi);
-         g != 0 ;
-         g = (struct group_statistics *) hsh_next(hash,&hi) )
+      for (g = hsh_first(hash, &hi); g != 0; g = hsh_next(hash, &hi))
        {
          lz_numerator += g->n * pow2(g->lz_mean - l->lz[v].grand_mean );
        }
index e61bf58e117aaf915d596bc26b8875152f0f0deb..b631820903e6ebf4e41a536ebe7ea964924af918 100644 (file)
@@ -24,6 +24,7 @@
 #include <data/case.h>
 #include <data/casewriter.h>
 #include <libpspp/compiler.h>
+#include <libpspp/cast.h>
 #include <libpspp/misc.h>
 #include <math/moments.h>
 
@@ -32,8 +33,8 @@
 static void
 destroy (struct statistic *stat)
 {
-  struct order_stats *os = (struct order_stats *) stat;
-  free (os);
+  struct np *np = UP_CAST (stat, struct np, parent.parent);
+  free (np);
 }
 
 
@@ -42,7 +43,7 @@ acc (struct statistic *s, const struct ccase *cx UNUSED,
      double c, double cc, double y)
 {
   struct ccase *cp;
-  struct np *np = (struct np *) s;
+  struct np *np = UP_CAST (s, struct np, parent.parent);
   double rank = np->prev_cc + (c + 1) / 2.0;
 
   double ns = gsl_cdf_ugaussian_Pinv (rank / ( np->n + 1 ));
@@ -69,13 +70,13 @@ acc (struct statistic *s, const struct ccase *cx UNUSED,
   np->prev_cc = cc;
 }
 
-struct order_stats *
+struct np *
 np_create (const struct moments1 *m)
 {
   double variance;
   struct np *np = xzalloc (sizeof (*np));
-  struct statistic *stat = (struct statistic *) np;
-  struct order_stats *os = (struct order_stats *) np;
+  struct order_stats *os = &np->parent;
+  struct statistic *stat = &os->parent;
   struct caseproto *proto;
   int i;
 
@@ -98,5 +99,5 @@ np_create (const struct moments1 *m)
   stat->destroy = destroy;
   stat->accumulate = acc;
 
-  return os;
+  return np;
 }
index 7db51f73b223fbcf7ade345afec06c45b5687fb9..b5265bd41f72c57d5b895a578a428971957fb61a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -54,6 +54,6 @@ struct np
 };
 
 
-struct order_stats * np_create (const struct moments1 *);
+struct np * np_create (const struct moments1 *);
 
 #endif
index 1b6aa131ea745ba6c0b55b33a54f3a6d39ceddbe..e550d2b2eb55f66fd4baea366e7a058abb849443 100644 (file)
@@ -90,7 +90,7 @@ update_k_values (const struct ccase *cx, double y_i, double c_i, double cc_i,
     {
       int k;
       struct order_stats *tos = os[j];
-      struct statistic  *stat = (struct statistic *) tos;
+      struct statistic  *stat = &tos->parent;
       for (k = 0 ; k < tos->n_k; ++k)
        {
          struct k *myk = &tos->k[k];
index bf99de163ffbaafe2af8711685e9a640a0256784..c76bb492ba767a90ffa2c03c8d35bf8a7d532ab6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
 #define N_(msgid) msgid
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <data/val-type.h>
 #include <gl/xalloc.h>
 #include <data/variable.h>
@@ -44,7 +45,7 @@ const char *const ptile_alg_desc[] = {
 double
 percentile_calculate (const struct percentile *ptl, enum pc_alg alg)
 {
-  struct percentile *mutable = (struct percentile *) ptl;
+  struct percentile *mutable = CONST_CAST (struct percentile *, ptl);
   const struct order_stats *os = &ptl->parent;
 
   assert (os->cc == ptl->w);
@@ -154,18 +155,19 @@ percentile_calculate (const struct percentile *ptl, enum pc_alg alg)
 static void
 destroy (struct statistic *stat)
 {
-  struct order_stats *os = (struct order_stats *) stat;
+  struct percentile *ptl = UP_CAST (stat, struct percentile, parent.parent);
+  struct order_stats *os = &ptl->parent;
   free (os->k);
-  free (os);
+  free (ptl);
 }
 
 
-struct order_stats *
+struct percentile *
 percentile_create (double p, double W)
 {
   struct percentile *ptl = xzalloc (sizeof (*ptl));
-  struct order_stats *os = (struct order_stats *) ptl;
-  struct statistic *stat = (struct statistic *) ptl;
+  struct order_stats *os = &ptl->parent;
+  struct statistic *stat = &os->parent;
 
   assert (p >= 0);
   assert (p <= 1.0);
@@ -186,6 +188,6 @@ percentile_create (double p, double W)
 
   stat->destroy = destroy;
 
-  return os;
+  return ptl;
 }
 
index 0dd09820945e1bafaee5a017ba3756cb4197783b..ff46bea6dce45611b88c621df4f7e86a30d97534 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -52,7 +52,7 @@ struct percentile
 /* Create the Pth percentile.
    W is the total sum of weights in the data set
 */
-struct order_stats *percentile_create (double p, double W);
+struct percentile *percentile_create (double p, double W);
 
 /* Return the value of the percentile */
 double percentile_calculate (const struct percentile *ptl, enum pc_alg alg);
index da3d4240e5b232533de6c5c31073b53adda49f48..d1cc6b708279098ab8d263b95923d9b31c968ed6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <math.h>
 #include <data/val-type.h>
 
@@ -27,8 +28,8 @@
 static void
 acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc, double y)
 {
-  struct trimmed_mean *tm = (struct trimmed_mean *) s;
-  struct order_stats *os = (struct order_stats *) s;
+  struct trimmed_mean *tm = UP_CAST (s, struct trimmed_mean, parent.parent);
+  struct order_stats *os = &tm->parent;
 
   if ( cc > os->k[0].tc && cc < os->k[1].tc)
       tm->sum += c * y;
@@ -40,17 +41,18 @@ acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc, do
 static void
 destroy (struct statistic *s)
 {
-  struct order_stats *os = (struct order_stats *) s;
+  struct trimmed_mean *tm = UP_CAST (s, struct trimmed_mean, parent.parent);
+  struct order_stats *os = &tm->parent;
   free (os->k);
-  free (s);
+  free (tm);
 }
 
-struct statistic *
+struct trimmed_mean *
 trimmed_mean_create (double W, double tail)
 {
   struct trimmed_mean *tm = xzalloc (sizeof (*tm));
-  struct order_stats *os = (struct order_stats *) tm;
-  struct statistic *stat = (struct statistic *) tm;
+  struct order_stats *os = &tm->parent;
+  struct statistic *stat = &os->parent;
 
   os->n_k = 2;
   os->k = xcalloc (sizeof (*os->k), 2);
@@ -68,7 +70,7 @@ trimmed_mean_create (double W, double tail)
   tm->w = W;
   tm->tail = tail;
 
-  return stat;
+  return tm;
 }
 
 
index 9339cab983ff9fba971d2cd2703ee81f17be6f66..c667b1be7ffcd9f9486606022c521143f5f08e6e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -36,7 +36,7 @@ struct trimmed_mean
   double tail;
 };
 
-struct statistic * trimmed_mean_create (double W, double c_min);
+struct trimmed_mean * trimmed_mean_create (double W, double c_min);
 double trimmed_mean_calculate (const struct trimmed_mean *);
 
 #endif
index 95a79c1d30026da280cbc6fa2542518c0ff4ad1f..22ab45210c38c98066eb4bb654663114cb496ddd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <math.h>
 
 void
@@ -59,19 +60,20 @@ tukey_hinges_calculate (const struct tukey_hinges *th, double hinge[3])
 static void
 destroy (struct statistic *s)
 {
-  struct order_stats *os = (struct order_stats *) s;
+  struct tukey_hinges *th = UP_CAST (s, struct tukey_hinges, parent.parent);
+  struct order_stats *os = &th->parent;
 
   free (os->k);
   free (s);
 };
 
-struct statistic *
+struct tukey_hinges *
 tukey_hinges_create (double W, double c_min)
 {
   double d;
   struct tukey_hinges *th = xzalloc (sizeof (*th));
-  struct order_stats *os = (struct order_stats *) th;
-  struct statistic *stat = (struct statistic *) th;
+  struct order_stats *os = &th->parent;
+  struct statistic *stat = &os->parent;
 
   assert (c_min >= 0);
 
@@ -97,5 +99,5 @@ tukey_hinges_create (double W, double c_min)
 
   stat->destroy = destroy;
 
-  return stat;
+  return th;
 }
index d87691f8b01a4dceb78fddfc9294c17053e8690b..4b509da1d4321a8396510fd4c08b40d801214ffe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ struct tukey_hinges
   struct order_stats parent;
 };
 
-struct statistic * tukey_hinges_create (double W, double c_min);
+struct tukey_hinges * tukey_hinges_create (double W, double c_min);
 
 
 void tukey_hinges_calculate (const struct tukey_hinges *h, double hinge[3]);
diff --git a/src/output/afm.c b/src/output/afm.c
deleted file mode 100644 (file)
index 480aa15..0000000
+++ /dev/null
@@ -1,1156 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-#include "afm.h"
-#include "c-ctype.h"
-#include "c-strtod.h"
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <setjmp.h>
-#include "error.h"
-#include "minmax.h"
-#include <libpspp/assertion.h>
-#include <libpspp/pool.h>
-#include <libpspp/str.h>
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* A kern pair entry. */
-struct afm_kern_pair
-  {
-    struct afm_character *successor; /* Second character. */
-    int adjust;                 /* Adjustment. */
-  };
-
-/* A ligature. */
-struct afm_ligature
-  {
-    struct afm_character *successor; /* Second character. */
-    struct afm_character *ligature;  /* Resulting ligature. */
-  };
-
-/* How to map between byte strings and character values. */
-enum mapping_scheme
-  {
-    MAP_UNKNOWN,                /* Not yet determined. */
-    MAP_ONE_BYTE,               /* 8-bit coding. */
-    MAP_TWO_BYTE,               /* 16-bit coding. */
-    MAP_ESCAPE,                 /* 8-bit coding with escape to change fonts. */
-    MAP_DOUBLE_ESCAPE,          /* 8-bit coding with multiple escapes. */
-    MAP_SHIFT                   /* 8-bit coding with 2 fonts that toggle. */
-  };
-
-/* AFM file data.  */
-struct afm
-  {
-    struct pool *pool;         /* Containing pool. */
-    char *findfont_name;        /* Name for PostScript /findfont operator. */
-    int ascent;                 /* Height above the baseline (non-negative). */
-    int descent;                /* Depth below the baseline (non-negative). */
-
-    /* Encoding characters into strings. */
-    enum mapping_scheme mapping; /* Basic mapping scheme. */
-    char escape_char;           /* MAP_ESCAPE only: escape character to use. */
-    char shift_out;             /* MAP_SHIFT only: selects font 0. */
-    char shift_in;              /* MAP_SHIFT only: selects font 1. */
-
-    /* Characters. */
-    struct afm_character *undefined_codes[256];
-    struct afm_character **codes[256];
-    struct afm_character **chars;
-    size_t char_cnt;
-  };
-
-/* AFM file parser. */
-struct parser
-  {
-    struct pool *pool;          /* Containing pool. */
-    struct afm *afm;            /* AFM being parsed. */
-    FILE *file;                 /* File being parsed. */
-    const char *file_name;      /* Name of file being parsed. */
-    int line_number;            /* Current line number in file. */
-    jmp_buf bail_out;           /* longjmp() target for error handling. */
-
-    size_t char_allocated;
-    int max_code;
-  };
-
-static struct afm *create_afm (void);
-static struct afm_character *create_character (struct afm *);
-
-static void afm_error (struct parser *, const char *, ...)
-     PRINTF_FORMAT (2, 3)
-     NO_RETURN;
-
-static void parse_afm (struct parser *);
-static void skip_section (struct parser *, const char *end_key);
-static bool parse_set_specific (struct parser *, const char *end_key);
-static void parse_direction (struct parser *);
-static void parse_char_metrics (struct parser *);
-static void parse_kern_pairs (struct parser *);
-static void add_kern_pair (struct parser *p,
-                           struct afm_character *, struct afm_character *,
-                           int adjust);
-
-static int skip_spaces (struct parser *);
-static char *parse_key (struct parser *);
-static void skip_line (struct parser *);
-static void force_eol (struct parser *);
-static bool get_integer (struct parser *, int *);
-static int force_get_integer (struct parser *);
-static bool get_number (struct parser *, int *);
-static int force_get_number (struct parser *);
-static bool get_hex_code (struct parser *, int *);
-static int force_get_hex_code (struct parser *);
-static bool get_word (struct parser *, char **);
-static char *force_get_word (struct parser *);
-static bool get_string (struct parser *, char **);
-static char *force_get_string (struct parser *);
-
-static struct afm_character *get_char_by_name (struct parser *, const char *);
-static struct afm_character *get_char_by_code (const struct afm *, int code);
-
-/* Reads FILE_NAME as an AFM file and returns the metrics data.
-   Returns a null pointer if the file cannot be parsed. */
-struct afm *
-afm_open (const char *file_name)
-{
-  struct afm *volatile afm;
-  struct parser *parser;
-
-  parser = pool_create_container (struct parser, pool);
-  afm = parser->afm = create_afm ();
-  parser->file = pool_fopen (parser->pool, file_name, "r");
-  parser->file_name = file_name;
-  parser->line_number = 0;
-  if (parser->file == NULL)
-    {
-      error (0, errno, _("opening font metrics file \"%s\""), file_name);
-      goto error;
-    }
-
-  if (setjmp (parser->bail_out))
-    goto error;
-
-  parse_afm (parser);
-  pool_destroy (parser->pool);
-  return afm;
-
- error:
-  pool_destroy (parser->pool);
-  pool_destroy (afm->pool);
-  return create_afm ();
-}
-
-/* Creates and returns an empty set of metrics. */
-static struct afm *
-create_afm (void)
-{
-  struct afm *afm;
-  struct afm_character *def_char;
-  size_t i;
-
-  afm = pool_create_container (struct afm, pool);
-  afm->findfont_name = NULL;
-  afm->ascent = 0;
-  afm->descent = 0;
-  afm->mapping = MAP_UNKNOWN;
-  afm->escape_char = 255;
-  afm->shift_out = 14;
-  afm->shift_in = 15;
-  def_char = create_character (afm);
-  for (i = 0; i < 256; i++)
-    afm->undefined_codes[i] = def_char;
-  for (i = 0; i < 256; i++)
-    afm->codes[i] = afm->undefined_codes;
-  afm->chars = NULL;
-  afm->char_cnt = 0;
-
-  return afm;
-}
-
-/* Creates and returns an initialized character within AFM. */
-static struct afm_character *
-create_character (struct afm *afm)
-{
-  struct afm_character *c = pool_alloc (afm->pool, sizeof *c);
-  c->code = ' ';
-  c->name = NULL;
-  c->width = 12000;
-  c->ascent = 0;
-  c->descent = 0;
-  c->kern_pairs = NULL;
-  c->kern_pair_cnt = 0;
-  c->ligatures = NULL;
-  c->ligature_cnt = 0;
-  return c;
-}
-
-/* Reports the given MESSAGE at the current line in parser P
-   and bails out with longjmp(). */
-static void
-afm_error (struct parser *p, const char *message, ...)
-{
-  va_list args;
-  char *msg;
-
-  va_start (args, message);
-  msg = xasprintf (message, args);
-  va_end (args);
-
-  error_at_line (0, 0, p->file_name, p->line_number, "%s", msg);
-  free (msg);
-
-  longjmp (p->bail_out, 1);
-}
-
-/* Parses an AFM file with parser P. */
-static void
-parse_afm (struct parser *p)
-{
-  char *key;
-
-  p->char_allocated = 0;
-  p->max_code = 0;
-
-  key = force_get_word (p);
-  if (strcmp (key, "StartFontMetrics"))
-    afm_error (p, _("first line must be StartFontMetrics"));
-  skip_line (p);
-
-  do
-    {
-      key = parse_key (p);
-      if (!strcmp (key, "FontName"))
-        p->afm->findfont_name = pool_strdup (p->afm->pool,
-                                             force_get_string (p));
-      else if (!strcmp (key, "Ascender"))
-        p->afm->ascent = force_get_integer (p);
-      else if (!strcmp (key, "Descender"))
-        p->afm->descent = force_get_integer (p);
-      else if (!strcmp (key, "MappingScheme"))
-        {
-          int scheme = force_get_integer (p);
-          if (scheme == 4)
-            p->afm->mapping = MAP_ONE_BYTE;
-          else if (scheme == 2 || scheme == 5 || scheme == 6)
-            p->afm->mapping = MAP_TWO_BYTE;
-          else if (scheme == 3)
-            p->afm->mapping = MAP_ESCAPE;
-          else if (scheme == 7)
-            p->afm->mapping = MAP_DOUBLE_ESCAPE;
-          else if (scheme == 8)
-            p->afm->mapping = MAP_SHIFT;
-          else
-            afm_error (p, _("unsupported MappingScheme %d"), scheme);
-        }
-      else if (!strcmp (key, "EscChar"))
-        p->afm->escape_char = force_get_integer (p);
-      else if (!strcmp (key, "StartDirection"))
-        parse_direction (p);
-      else if (!strcmp (key, "StartCharMetrics"))
-        parse_char_metrics (p);
-      else if (!strcmp (key, "StartKernPairs")
-               || !strcmp (key, "StartKernPairs0"))
-        parse_kern_pairs (p);
-      else if (!strcmp (key, "StartTrackKern"))
-        skip_section (p, "EndTrackKern");
-      else if (!strcmp (key, "StartComposites"))
-        skip_section (p, "EndComposites");
-      else
-        skip_line (p);
-    }
-  while (strcmp (key, "EndFontMetrics"));
-
-  if (p->afm->findfont_name == NULL)
-    afm_error (p, _("required FontName is missing"));
-  if (p->afm->mapping == MAP_UNKNOWN)
-    {
-      /* There seem to be a number of fonts out there that use a
-         2-byte encoding but don't announce it with
-         MappingScheme. */
-      p->afm->mapping = p->max_code > 255 ? MAP_TWO_BYTE : MAP_ONE_BYTE;
-    }
-}
-
-/* Reads lines from parser P until one starts with END_KEY. */
-static void
-skip_section (struct parser *p, const char *end_key)
-{
-  const char *key;
-  skip_line (p);
-  do
-    {
-      key = parse_key (p);
-      skip_line (p);
-    }
-  while (strcmp (key, end_key));
-}
-
-/* Attempts to read an integer from parser P.
-   If one is found, and it is nonzero, skips lines until END_KEY
-   is encountered and returns false.
-   Otherwise, skips the rest of the line and returns true.
-   (This is useful because AFM files can have multiple sets of
-   metrics.  Set 0 is for normal text, other sets are for
-   vertical text, etc.  We only care about set 0.) */
-static bool
-parse_set_specific (struct parser *p, const char *end_key)
-{
-  int set;
-
-  if (get_integer (p, &set) && set != 0)
-    {
-      skip_section (p, end_key);
-      return false;
-    }
-  else
-    {
-      force_eol (p);
-      return true;
-    }
-}
-
-/* Parses a StartDirection...EndDirection section in parser P. */
-static void
-parse_direction (struct parser *p)
-{
-  const char *key;
-
-  if (!parse_set_specific (p, "EndDirection"))
-    return;
-
-  do
-    {
-      key = parse_key (p);
-      if (!strcmp (key, "CharWidth"))
-        p->afm->codes[0][0]->width = force_get_integer (p);
-      skip_line (p);
-    }
-  while (strcmp (key, "EndDirection"));
-}
-
-/* Parses a StartCharMetrics...EndCharMetrics section in parser
-   P. */
-static void
-parse_char_metrics (struct parser *p)
-{
-  struct parsing_ligature
-    {
-      struct afm_character *first;
-      char *successor;
-      char *ligature;
-    };
-
-  struct parsing_ligature *ligatures = NULL;
-  size_t ligature_cnt = 0;
-  size_t ligature_allocated = 0;
-
-  size_t i;
-
-  skip_line (p);
-
-  for (;;)
-    {
-      char *key;
-      struct afm_character *c;
-
-      key = parse_key (p);
-      if (!strcmp (key, "EndCharMetrics"))
-        break;
-
-      if (p->afm->char_cnt == p->char_allocated)
-        p->afm->chars = pool_2nrealloc (p->afm->pool, p->afm->chars,
-                                        &p->char_allocated,
-                                        sizeof *p->afm->chars);
-      c = create_character (p->afm);
-
-      if (!strcmp (key, "C"))
-        c->code = force_get_integer (p);
-      else if (!strcmp (key, "CH"))
-        c->code = force_get_hex_code (p);
-      else
-        afm_error (p, _("CharMetrics line must start with C or CH"));
-      if (c->code < 0 || c->code > 65535)
-        c->code = -1;
-
-      if (c->code > p->max_code)
-        p->max_code = c->code;
-
-      p->afm->chars[p->afm->char_cnt++] = c;
-      if (c->code != -1)
-        p->afm->codes[c->code >> 8][c->code & 0xff] = c;
-
-      key = force_get_word (p);
-      while (!strcmp (key, ";"))
-        {
-          if (!get_word (p, &key))
-            break;
-
-          if (!strcmp (key, "N"))
-            c->name = force_get_word (p);
-          else if (!strcmp (key, "WX") || !strcmp (key, "W0X"))
-            c->width = force_get_number (p);
-          else if (!strcmp (key, "W") || !strcmp (key, "W0"))
-            {
-              c->width = force_get_number (p);
-              force_get_number (p);
-            }
-          else if (!strcmp (key, "B"))
-            {
-              int llx, lly, urx, ury;
-              llx = force_get_number (p);
-              lly = force_get_number (p);
-              urx = force_get_number (p);
-              ury = force_get_number (p);
-              c->ascent = MAX (0, ury);
-              c->descent = MAX (0, -lly);
-            }
-          else if (!strcmp (key, "L"))
-            {
-              struct parsing_ligature *ligature;
-              if (ligature_cnt == ligature_allocated)
-                ligatures = pool_2nrealloc (p->pool, ligatures,
-                                            &ligature_allocated,
-                                            sizeof *ligatures);
-              ligature = &ligatures[ligature_cnt++];
-              ligature->first = c;
-              ligature->successor = force_get_word (p);
-              ligature->ligature = force_get_word (p);
-            }
-          else
-            {
-              while (strcmp (key, ";"))
-                key = force_get_word (p);
-              continue;
-            }
-          if (!get_word (p, &key))
-            break;
-        }
-    }
-  skip_line (p);
-
-  for (i = 0; i < ligature_cnt; i++)
-    {
-      struct parsing_ligature *src = &ligatures[i];
-      struct afm_ligature *dst;
-      src->first->ligatures = pool_nrealloc (p->afm->pool,
-                                             src->first->ligatures,
-                                             src->first->ligature_cnt + 1,
-                                             sizeof *src->first->ligatures);
-      dst = &src->first->ligatures[src->first->ligature_cnt++];
-      dst->successor = get_char_by_name (p, src->successor);
-      dst->ligature = get_char_by_name (p, src->ligature);
-    }
-}
-
-/* Parses a StartKernPairs...EndKernPairs section in parser P. */
-static void
-parse_kern_pairs (struct parser *p)
-{
-  char *key;
-
-  skip_line (p);
-
-  do
-    {
-      struct afm_character *c1, *c2;
-      int adjust;
-
-      key = parse_key (p);
-      if (!strcmp (key, "KP") || !strcmp (key, "KPX"))
-        {
-          c1 = get_char_by_name (p, force_get_word (p));
-          c2 = get_char_by_name (p, force_get_word (p));
-          adjust = force_get_number (p);
-          if (!strcmp (key, "KP"))
-            force_get_number (p);
-          add_kern_pair (p, c1, c2, adjust);
-        }
-      else if (!strcmp (key, "KPH"))
-        {
-          c1 = get_char_by_code (p->afm, force_get_hex_code (p));
-          c2 = get_char_by_code (p->afm, force_get_hex_code (p));
-          adjust = force_get_number (p);
-          force_get_number (p);
-          add_kern_pair (p, c1, c2, adjust);
-        }
-      else
-        skip_line (p);
-    }
-  while (strcmp (key, "EndKernPairs"));
-}
-
-/* Adds a kern pair that adjusts (FIRST, SECOND) by ADJUST units
-   to the metrics within parser P. */
-static void
-add_kern_pair (struct parser *p, struct afm_character *first,
-               struct afm_character *second, int adjust)
-{
-  struct afm_kern_pair *kp;
-
-  first->kern_pairs = pool_nrealloc (p->afm->pool, first->kern_pairs,
-                                     first->kern_pair_cnt + 1,
-                                     sizeof *first->kern_pairs);
-  kp = &first->kern_pairs[first->kern_pair_cnt++];
-  kp->successor = second;
-  kp->adjust = adjust;
-}
-
-/* Returns the character with the given NAME with the metrics for
-   parser P.  Reports an error if no character has the given
-   name. */
-static struct afm_character *
-get_char_by_name (struct parser *p, const char *name)
-{
-  size_t i;
-
-  for (i = 0; i < p->afm->char_cnt; i++)
-    {
-      struct afm_character *c = p->afm->chars[i];
-      if (c->name != NULL && !strcmp (c->name, name))
-        return c;
-    }
-  afm_error (p, _("reference to unknown character \"%s\""), name);
-}
-
-/* Returns the character with the given CODE within AFM.
-   Returns a default character if the font doesn't have a
-   character with that code. */
-static struct afm_character *
-get_char_by_code (const struct afm *afm, int code_)
-{
-  uint16_t code = code_;
-  return afm->codes[code >> 8][code & 0xff];
-}
-\f
-/* Skips white space, except for new-lines, within parser P. */
-static int
-skip_spaces (struct parser *p)
-{
-  int c;
-  while (isspace (c = getc (p->file)) && c != '\n')
-    continue;
-  ungetc (c, p->file);
-  return c;
-}
-
-/* Parses a word at the beginning of a line.
-   Skips comments.
-   Reports an error if not at the beginning of a line. */
-static char *
-parse_key (struct parser *p)
-{
-  force_eol (p);
-  for (;;)
-    {
-      char *key;
-
-      do
-        {
-          p->line_number++;
-          getc (p->file);
-        }
-      while (skip_spaces (p) == '\n');
-
-      key = force_get_word (p);
-      if (strcmp (key, "Comment"))
-        return key;
-
-      skip_line (p);
-    }
-}
-
-/* Skips to the next line within parser P. */
-static void
-skip_line (struct parser *p)
-{
-  for (;;)
-    {
-      int c = getc (p->file);
-      if (c == EOF)
-        afm_error (p, _("expected end of file"));
-      if (c == '\n')
-        break;
-    }
-  ungetc ('\n', p->file);
-}
-
-/* Ensures that parser P is at the end of a line. */
-static void
-force_eol (struct parser *p)
-{
-  if (skip_spaces (p) != '\n')
-    afm_error (p, _("syntax error expecting end of line"));
-}
-
-/* Tries to read an integer into *INTEGER at the current position
-   in parser P.
-   Returns success. */
-static bool
-get_integer (struct parser *p, int *integer)
-{
-  int c = skip_spaces (p);
-  if (isdigit (c) || c == '-')
-    {
-      char *tail;
-      long tmp;
-
-      errno = 0;
-      tmp = strtol (force_get_word (p), &tail, 10);
-      if (errno == ERANGE || tmp < INT_MIN || tmp > INT_MAX)
-        afm_error (p, _("number out of valid range"));
-      if (*tail != '\0')
-        afm_error (p, _("invalid numeric syntax"));
-      *integer = tmp;
-
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Returns an integer read from the current position in P.
-   Reports an error if unsuccessful. */
-static int
-force_get_integer (struct parser *p)
-{
-  int integer;
-  if (!get_integer (p, &integer))
-    afm_error (p, _("syntax error expecting integer"));
-  return integer;
-}
-
-/* Tries to read a floating-point number at the current position
-   in parser P.  Stores the number's integer part into *INTEGER.
-   Returns success. */
-static bool
-get_number (struct parser *p, int *integer)
-{
-  int c = skip_spaces (p);
-  if (c == '-' || c == '.' || isdigit (c))
-    {
-      char *tail;
-      double number;
-
-      errno = 0;
-      number = c_strtod (force_get_word (p), &tail);
-      if (errno == ERANGE || number < INT_MIN || number > INT_MAX)
-        afm_error (p, _("number out of valid range"));
-      if (*tail != '\0')
-        afm_error (p, _("invalid numeric syntax"));
-      *integer = number;
-
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Returns the integer part of a floating-point number read from
-   the current position in P.
-   Reports an error if unsuccessful. */
-static int
-force_get_number (struct parser *p)
-{
-  int integer;
-  if (!get_number (p, &integer))
-    afm_error (p, _("syntax error expecting number"));
-  return integer;
-}
-
-/* Tries to read an integer expressed in hexadecimal into
-   *INTEGER from P.
-   Returns success. */
-static bool
-get_hex_code (struct parser *p, int *integer)
-{
-  if (skip_spaces (p) == '<')
-    {
-      if (fscanf (p->file, "<%x", integer) != 1 || getc (p->file) != '>')
-        afm_error (p, _("syntax error in hex constant"));
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Reads an integer expressed in hexadecimal and returns its
-   value.
-   Reports an error if unsuccessful. */
-static int
-force_get_hex_code (struct parser *p)
-{
-  int integer;
-  if (!get_hex_code (p, &integer))
-    afm_error (p, _("syntax error expecting hex constant"));
-  return integer;
-}
-
-/* Tries to read a word from P into *WORD.
-   The word is allocated in P's pool.
-   Returns success. */
-static bool
-get_word (struct parser *p, char **word)
-{
-  if (skip_spaces (p) != '\n')
-    {
-      struct string s;
-      int c;
-
-      ds_init_empty (&s);
-      while (!isspace (c = getc (p->file)) && c != EOF)
-        ds_put_char (&s, c);
-      ungetc (c, p->file);
-      *word = ds_cstr (&s);
-      pool_register (p->pool, free, *word);
-      return true;
-    }
-  else
-    {
-      *word = NULL;
-      return false;
-    }
-}
-
-/* Reads a word from P and returns it.
-   The word is allocated in P's pool.
-   Reports an error if unsuccessful. */
-static char *
-force_get_word (struct parser *p)
-{
-  char *word;
-  if (!get_word (p, &word))
-    afm_error (p, _("unexpected end of line"));
-  return word;
-}
-
-/* Reads a string, consisting of the remainder of the current
-   line, from P, and stores it in *STRING.
-   Leading and trailing spaces are removed.
-   The word is allocated in P's pool.
-   Returns true if a non-empty string was successfully read,
-   false otherwise. */
-static bool
-get_string (struct parser *p, char **string)
-{
-  struct string s = DS_EMPTY_INITIALIZER;
-
-  skip_spaces (p);
-  for (;;)
-    {
-      int c = getc (p->file);
-      if (c == EOF || c == '\n')
-        break;
-      ds_put_char (&s, c);
-    }
-  ungetc ('\n', p->file);
-  ds_rtrim (&s, ss_cstr (CC_SPACES));
-
-  if (!ds_is_empty (&s))
-    {
-      *string = ds_cstr (&s);
-      pool_register (p->pool, free, *string);
-      return true;
-    }
-  else
-    {
-      *string = NULL;
-      ds_destroy (&s);
-      return false;
-    }
-}
-
-/* Reads a string, consisting of the remainder of the current
-   line, from P, and returns it.
-   Leading and trailing spaces are removed.
-   The word is allocated in P's pool.
-   Reports an error if the string is empty. */
-static char *
-force_get_string (struct parser *p)
-{
-  char *string;
-  if (!get_string (p, &string))
-    afm_error (p, _("unexpected end of line expecting string"));
-  return string;
-}
-\f
-/* Closes AFM and frees its storage. */
-void
-afm_close (struct afm *afm)
-{
-  if (afm != NULL)
-    pool_destroy (afm->pool);
-}
-
-/* Returns the string that must be passed to the PostScript
-   "findfont" operator to obtain AFM's font. */
-const char *
-afm_get_findfont_name (const struct afm *afm)
-{
-  return afm->findfont_name;
-}
-
-/* Returns the ascent for AFM, that is, the font's height above
-   the baseline, in units of 1/1000 of the nominal font size. */
-int
-afm_get_ascent (const struct afm *afm)
-{
-  return afm->ascent;
-}
-
-/* Returns the descent for AFM, that is, the font's depth below
-   the baseline, in units of 1/1000 of the nominal font size. */
-int
-afm_get_descent (const struct afm *afm)
-{
-  return afm->descent;
-}
-
-/* Returns the character numbered CODE within AFM,
-   or a default character if the font has none. */
-const struct afm_character *
-afm_get_character (const struct afm *afm, int code)
-{
-  return get_char_by_code (afm, code);
-}
-
-/* Returns the ligature formed when FIRST is followed by SECOND,
-   or a null pointer if there is no such ligature. */
-const struct afm_character *
-afm_get_ligature (const struct afm_character *first,
-                  const struct afm_character *second)
-{
-  size_t i;
-
-  for (i = 0; i < first->ligature_cnt; i++)
-    if (first->ligatures[i].successor == second)
-      return first->ligatures[i].ligature;
-  return NULL;
-}
-
-/* Returns the pair kerning x-adjustment when FIRST is followed
-   by SECOND, or 0 if no pair kerning should be done for the
-   given pair of characters. */
-int
-afm_get_kern_adjustment (const struct afm_character *first,
-                         const struct afm_character *second)
-{
-  size_t i;
-
-  for (i = 0; i < first->kern_pair_cnt; i++)
-    if (first->kern_pairs[i].successor == second)
-      return first->kern_pairs[i].adjust;
-  return 0;
-}
-\f
-/* Encodes the N characters in S as a PostScript string in OUT,
-   using a single-byte encoding.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_one_byte (const struct afm_character **s, size_t n,
-                 struct string *out)
-{
-  ds_put_char (out, '(');
-  for (; n > 0; s++, n--)
-    {
-      uint8_t code = (*s)->code;
-      if (code != (*s)->code)
-        break;
-
-      if (code == '(' || code == ')' || code == '\\')
-        ds_put_format (out, "\\%c", code);
-      else if (!c_isprint (code))
-        ds_put_format (out, "\\%03o", code);
-      else
-        ds_put_char (out, code);
-    }
-  ds_put_char (out, ')');
-  return n;
-}
-
-/* State of binary encoder for PostScript. */
-struct binary_encoder
-  {
-    struct string *out;         /* Output string. */
-    uint32_t b;                 /* Accumulated bytes for base-85 encoding. */
-    size_t n;                   /* Number of bytes in b (0...3). */
-  };
-
-/* Initializes encoder E for output to OUT. */
-static void
-binary_init (struct binary_encoder *e, struct string *out)
-{
-  e->out = out;
-  e->b = e->n = 0;
-}
-
-/* Returns the character that represents VALUE in ASCII85
-   encoding. */
-static int
-value_to_ascii85 (int value)
-{
-  assert (value >= 0 && value < 85);
-#if C_CTYPE_ASCII
-  return value + 33;
-#else
-  return ("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK"
-          "LMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu")[value];
-#endif
-}
-
-/* Appends the first N characters of the ASCII85 representation
-   of B to string OUT. */
-static void
-append_ascii85_block (unsigned b, size_t n, struct string *out)
-{
-  char c[5];
-  int i;
-
-  for (i = 4; i >= 0; i--)
-    {
-      c[i] = value_to_ascii85 (b % 85);
-      b /= 85;
-    }
-  ds_put_substring (out, ss_buffer (c, n));
-}
-
-/* Encodes BYTE with encoder E. */
-static void
-binary_put (struct binary_encoder *e, uint8_t byte)
-{
-  e->b = (e->b << 8) | byte;
-  e->n++;
-  if (e->n % 4 == 0)
-    {
-      if (e->n == 4)
-        ds_put_cstr (e->out, "<~");
-
-      if (e->b != 0)
-        append_ascii85_block (e->b, 5, e->out);
-      else
-        ds_put_char (e->out, 'z');
-    }
-}
-
-/* Finishes up encoding with E. */
-static void
-binary_finish (struct binary_encoder *e)
-{
-  if (e->n >= 4)
-    {
-      /* We output at least one complete ASCII85 block.
-         Finish up. */
-      size_t n = e->n % 4;
-      if (n > 0)
-        append_ascii85_block (e->b << 8 * (4 - n), n + 1, e->out);
-      ds_put_cstr (e->out, "~>");
-    }
-  else if (e->n > 0)
-    {
-      /* It's cheaper (or at least the same cost) to encode this
-         string in hexadecimal. */
-      uint32_t b;
-      size_t i;
-
-      ds_put_cstr (e->out, "<");
-      b = e->b << 8 * (4 - e->n);
-      for (i = 0; i < e->n; i++)
-        {
-          ds_put_format (e->out, "%02x", b >> 24);
-          b <<= 8;
-        }
-      ds_put_cstr (e->out, ">");
-    }
-  else
-    {
-      /* Empty string. */
-      ds_put_cstr (e->out, "()");
-    }
-}
-
-/* Encodes the N characters in S into encoder E,
-   using a two-byte encoding.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_two_byte (const struct afm_character **s, size_t n,
-                 struct binary_encoder *e)
-{
-  for (; n > 0; s++, n--)
-    {
-      uint16_t code = (*s)->code;
-      if (code != (*s)->code)
-        break;
-
-      binary_put (e, code >> 8);
-      binary_put (e, code);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into encoder E,
-   using an escape-based encoding with ESCAPE_CHAR as escape.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_escape (const struct afm_character **s, size_t n,
-               unsigned char escape_char,
-               struct binary_encoder *e)
-{
-  uint8_t cur_font = 0;
-
-  for (; n > 0; s++, n--)
-    {
-      uint16_t code = (*s)->code;
-      uint8_t font_num = code >> 8;
-      uint8_t char_num = code & 0xff;
-      if (code != (*s)->code)
-        break;
-
-      if (font_num != cur_font)
-        {
-          if (font_num == escape_char)
-            break;
-          binary_put (e, escape_char);
-          binary_put (e, font_num);
-          cur_font = font_num;
-        }
-      binary_put (e, char_num);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into encoder E,
-   using an double escape-based encoding with ESCAPE_CHAR as
-   escape.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_double_escape (const struct afm_character **s, size_t n,
-                      unsigned char escape_char,
-                      struct binary_encoder *e)
-{
-  unsigned cur_font = 0;
-
-  for (; n > 0; s++, n--)
-    {
-      unsigned font_num = (*s)->code >> 8;
-      uint8_t char_num = (*s)->code & 0xff;
-      if ((*s)->code & ~0x1ffff)
-        break;
-
-      if (font_num != cur_font)
-        {
-          if (font_num == (escape_char & 0xff))
-            break;
-          if (font_num >= 256)
-            binary_put (e, escape_char);
-          binary_put (e, escape_char);
-          binary_put (e, font_num & 0xff);
-          cur_font = font_num;
-        }
-      binary_put (e, char_num);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into encoder E,
-   using a shift-based encoding with SHIFT_IN and SHIFT_OUT as
-   shift characters.
-   Returns the number of characters remaining after all those
-   that could be successfully encoded were. */
-static size_t
-encode_shift (const struct afm_character **s, size_t n,
-              unsigned char shift_in, unsigned char shift_out,
-              struct binary_encoder *e)
-{
-  unsigned cur_font = 0;
-
-  for (; n > 0; s++, n--)
-    {
-      int font_num = ((*s)->code & 0x100) != 0;
-      uint8_t char_num = (*s)->code & 0xff;
-      if ((*s)->code & ~0x1ff)
-        break;
-
-      if (font_num != cur_font)
-        {
-          binary_put (e, font_num ? shift_out : shift_in);
-          cur_font = font_num;
-        }
-      binary_put (e, char_num);
-    }
-  return n;
-}
-
-/* Encodes the N characters in S into a PostScript string in OUT,
-   according to AFM's character encoding.
-   Returns the number of characters successfully encoded,
-   which may be less than N if an unencodable character was
-   encountered. */
-size_t
-afm_encode_string (const struct afm *afm,
-                   const struct afm_character **s, size_t n,
-                   struct string *out)
-{
-  size_t initial_length = ds_length (out);
-  size_t chars_left;
-
-  if (afm->mapping == MAP_ONE_BYTE)
-    chars_left = encode_one_byte (s, n, out);
-  else
-    {
-      struct binary_encoder e;
-
-      binary_init (&e, out);
-      switch (afm->mapping)
-        {
-        case MAP_TWO_BYTE:
-          chars_left = encode_two_byte (s, n, &e);
-          break;
-
-        case MAP_ESCAPE:
-          chars_left = encode_escape (s, n, afm->escape_char, &e);
-          break;
-
-        case MAP_DOUBLE_ESCAPE:
-          chars_left = encode_double_escape (s, n, afm->escape_char, &e);
-          break;
-
-        case MAP_SHIFT:
-          chars_left = encode_shift (s, n, afm->shift_in, afm->shift_out, &e);
-          break;
-
-        default:
-          NOT_REACHED ();
-        }
-      binary_finish (&e);
-    }
-
-  if (chars_left == n)
-    ds_truncate (out, initial_length);
-  return n - chars_left;
-}
diff --git a/src/output/afm.h b/src/output/afm.h
deleted file mode 100644 (file)
index 6525af6..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef AFM_H
-#define AFM_H 1
-
-#include <stddef.h>
-#include <libpspp/str.h>
-
-/* Metrics for a single character.  */
-struct afm_character
-  {
-    int code;                   /* Non-negative character code, -1 if none. */
-    const char *name;           /* Character name, if any. */
-    int width;                 /* Width. */
-    int ascent;                        /* Height above baseline, never negative. */
-    int descent;                /* Depth below baseline, never negative. */
-
-    /* Pairwise kerning data for this character in the first
-       position, other characters in the second position. */
-    struct afm_kern_pair *kern_pairs;
-    size_t kern_pair_cnt;
-
-    /* Ligature data for this character in the first position,
-       other characters in the second position. */
-    struct afm_ligature *ligatures;
-    size_t ligature_cnt;
-  };
-
-struct afm *afm_open (const char *file_name);
-void afm_close (struct afm *);
-
-int afm_get_ascent (const struct afm *);
-int afm_get_descent (const struct afm *);
-const char *afm_get_findfont_name (const struct afm *);
-
-const struct afm_character *afm_get_character (const struct afm *,
-                                               int code);
-const struct afm_character *afm_get_ligature (const struct afm_character *,
-                                              const struct afm_character *);
-int afm_get_kern_adjustment (const struct afm_character *,
-                             const struct afm_character *);
-
-size_t afm_encode_string (const struct afm *,
-                          const struct afm_character **, size_t,
-                          struct string *);
-
-#endif /* afm.h */
index 29d7b76b2940d76dbc0af108827922785f8e71ce..b224b3ebea9e54bdfac5d6859a18793583b046f6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <libpspp/pool.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/output.h>
 
-#include "chart.h"
 #include "error.h"
 #include "minmax.h"
-#include "output.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -44,7 +45,7 @@
    output-file="pspp.list"
    append=no|yes                If output-file exists, append to it?
    chart-files="pspp-#.png"     Name used for charts.
-   chart-type=png               Format of charts (use "none" to disable).
+   chart-type=png|none
 
    paginate=on|off              Formfeeds are desired?
    tab-width=8                  Width of a tab; 0 to not use tabs.
@@ -108,7 +109,7 @@ struct ascii_driver_ext
     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. */
-    const char *chart_type;     /* Type of charts to output; NULL for none. */
+    bool enable_charts;         /* Enable charts? */
     const char *chart_file_name; /* Name of files used for charts. */
 
     bool auto_width;            /* Use viewwidth as page width? */
@@ -133,15 +134,17 @@ struct ascii_driver_ext
 static void ascii_flush (struct outp_driver *);
 static int get_default_box_char (size_t idx);
 static bool update_page_size (struct outp_driver *, bool issue_error);
-static bool handle_option (struct outp_driver *this, const char *key,
+static bool handle_option (void *this, const char *key,
                            const struct string *val);
 
 static bool
-ascii_open_driver (struct outp_driver *this, struct substring options)
+ascii_open_driver (const char *name, int types, struct substring options)
 {
+  struct outp_driver *this;
   struct ascii_driver_ext *x;
   int i;
 
+  this = outp_allocate_driver (&ascii_class, name, types);
   this->width = 79;
   this->font_height = 1;
   this->prop_em_width = 1;
@@ -157,7 +160,7 @@ ascii_open_driver (struct outp_driver *this, struct substring options)
   x->emphasis = EMPH_BOLD;
   x->tab_width = 8;
   x->chart_file_name = pool_strdup (x->pool, "pspp-#.png");
-  x->chart_type = pool_strdup (x->pool, "png");
+  x->enable_charts = true;
   x->auto_width = false;
   x->auto_length = false;
   x->page_length = 66;
@@ -172,9 +175,9 @@ ascii_open_driver (struct outp_driver *this, struct substring options)
   x->page_number = 0;
   x->lines = NULL;
   x->line_cap = 0;
-  x->chart_cnt = 0;
+  x->chart_cnt = 1;
 
-  if (!outp_parse_options (options, handle_option, this))
+  if (!outp_parse_options (this->name, options, handle_option, this))
     goto error;
 
   if (!update_page_size (this, true))
@@ -189,10 +192,13 @@ ascii_open_driver (struct outp_driver *this, struct substring options)
         x->box[i] = pool_strdup (x->pool, s);
       }
 
+  outp_register_driver (this);
+
   return true;
 
  error:
   pool_destroy (x->pool);
+  outp_free_driver (this);
   return false;
 }
 
@@ -312,9 +318,10 @@ static const struct outp_option option_tab[] =
   };
 
 static bool
-handle_option (struct outp_driver *this, const char *key,
+handle_option (void *this_, const char *key,
                const struct string *val)
 {
+  struct outp_driver *this = this_;
   struct ascii_driver_ext *x = this->ext;
   int subcat;
   const char *value;
@@ -478,10 +485,16 @@ handle_option (struct outp_driver *this, const char *key,
             error (0, 0, _("`chart-files' value must contain `#'"));
           break;
         case 2:
-          if (value[0] != '\0')
-            x->chart_type = pool_strdup (x->pool, value);
+          if (!strcmp (value, "png"))
+            x->enable_charts = true;
+          else if (!strcmp (value, "none"))
+            x->enable_charts = false;
           else
-            x->chart_type = NULL;
+            {
+              error (0, 0,
+                     _("ascii: `png' or `none' expected for `chart-type'"));
+              return false;
+            }
           break;
         case 3:
           x->init = pool_strdup (x->pool, value);
@@ -603,15 +616,6 @@ ascii_line (struct outp_driver *this,
     }
 }
 
-static void
-ascii_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
-  extern struct som_table_class tab_table_class;
-
-  assert (s->class == &tab_table_class);
-  assert (s->type == SOM_CHART);
-}
-
 static void
 text_draw (struct outp_driver *this,
            enum outp_font font,
@@ -866,19 +870,15 @@ ascii_flush (struct outp_driver *this)
 }
 
 static void
-ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
+ascii_output_chart (struct outp_driver *this, const struct chart *chart)
 {
   struct ascii_driver_ext *x = this->ext;
   struct outp_text t;
+  char *file_name;
   char *text;
 
-  if (x->chart_type == NULL)
-    return;
-
-  /* Initialize chart. */
-  chart_init_separate (ch, x->chart_type, x->chart_file_name, ++x->chart_cnt);
-  if (ch->file_name == NULL)
-    return;
+  /* Draw chart into separate file */
+  file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
 
   /* Mention chart in output.
      First advance current position. */
@@ -895,7 +895,7 @@ ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
     }
 
   /* Then write the text. */
-  text = xasprintf ("See %s for a chart.", ch->file_name);
+  text = xasprintf ("See %s for a chart.", file_name);
   t.font = OUTP_FIXED;
   t.justification = OUTP_LEFT;
   t.string = ss_cstr (text);
@@ -906,17 +906,10 @@ ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
   ascii_text_draw (this, &t);
   this->cp_y++;
 
+  free (file_name);
   free (text);
 }
 
-static void
-ascii_chart_finalise (struct outp_driver *this, struct chart *ch)
-{
-  struct ascii_driver_ext *x = this->ext;
-  if (x->chart_type != NULL)
-    chart_finalise_separate (ch);
-}
-
 const struct outp_class ascii_class =
 {
   "ascii",
@@ -929,12 +922,11 @@ const struct outp_class ascii_class =
   ascii_close_page,
   ascii_flush,
 
-  ascii_submit,
+  ascii_output_chart,
+
+  NULL,                         /* submit */
 
   ascii_line,
   ascii_text_metrics,
   ascii_text_draw,
-
-  ascii_chart_initialise,
-  ascii_chart_finalise
 };
index ec92c559e044723180977db31ca43708c7f637bd..7e233750b8f3c5652206e3fb846250ba4947610c 100644 (file)
@@ -1,35 +1,40 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-
-include $(top_srcdir)/src/output/charts/automake.mk
-
 noinst_LTLIBRARIES += src/output/liboutput.la 
 
-output_sources = \
-       src/output/afm.c \
-       src/output/afm.h \
+src_output_liboutput_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(AM_CPPFLAGS) 
+
+src_output_liboutput_la_SOURCES = \
        src/output/ascii.c \
+       src/output/chart.c \
+       src/output/chart.h \
+       src/output/charts/boxplot.c \
+       src/output/charts/boxplot.h \
+       src/output/charts/cartesian.c \
+       src/output/charts/cartesian.h \
+       src/output/charts/np-plot.c \
+       src/output/charts/np-plot.h \
+       src/output/charts/piechart.c \
+       src/output/charts/piechart.h \
+       src/output/charts/plot-chart.c \
+       src/output/charts/plot-chart.h \
+       src/output/charts/plot-hist.c \
+       src/output/charts/plot-hist.h \
+       src/output/charts/roc-chart.c \
+       src/output/charts/roc-chart.h \
        src/output/html.c \
        src/output/htmlP.h \
        src/output/journal.c \
        src/output/journal.h \
-       src/output/output.c \
-       src/output/output.h \
-       src/output/postscript.c \
        src/output/manager.c \
        src/output/manager.h \
-       src/output/chart.h \
-       src/output/table.c src/output/table.h
-
-
-if WITHCHARTS
-src_output_liboutput_la_SOURCES = $(output_sources) src/output/chart.c
-
-EXTRA_DIST += src/output/dummy-chart.c
-else
-src_output_liboutput_la_SOURCES = $(output_sources) src/output/dummy-chart.c
-
-EXTRA_DIST += src/output/chart.c
+       src/output/odt.c \
+       src/output/output.c \
+       src/output/output.h \
+       src/output/table.c \
+       src/output/table.h
+if HAVE_CAIRO
+src_output_liboutput_la_SOURCES += src/output/cairo.c
 endif
 
 EXTRA_DIST += src/output/OChangeLog
diff --git a/src/output/cairo.c b/src/output/cairo.c
new file mode 100644 (file)
index 0000000..162009c
--- /dev/null
@@ -0,0 +1,910 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/cairo.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/start-date.h>
+#include <libpspp/version.h>
+#include <output/chart-provider.h>
+#include <output/manager.h>
+#include <output/output.h>
+
+#include <cairo/cairo-pdf.h>
+#include <cairo/cairo-ps.h>
+#include <cairo/cairo-svg.h>
+#include <cairo/cairo.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdlib.h>
+
+#include "error.h"
+#include "intprops.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Cairo driver options: (defaults listed first)
+
+   output-file="pspp.pdf"
+   output-type=pdf|ps|png|svg
+   paper-size=letter (see "papersize" file)
+   orientation=portrait|landscape
+   headers=on|off
+
+   left-margin=0.5in
+   right-margin=0.5in
+   top-margin=0.5in
+   bottom-margin=0.5in
+
+   prop-font=serif
+   emph-font=serif italic
+   fixed-font=monospace
+   font-size=10000
+
+   line-gutter=1pt
+   line-spacing=1pt
+   line-width=0.5pt
+ */
+
+/* Measurements as we present to the rest of PSPP. */
+#define XR_POINT PANGO_SCALE
+#define XR_INCH (XR_POINT * 72)
+
+/* Conversions to and from points. */
+static double
+xr_to_pt (int x)
+{
+  return x / (double) XR_POINT;
+}
+
+static int
+pt_to_xr (double x)
+{
+  return x * XR_POINT + 0.5;
+}
+
+/* Output types. */
+enum xr_output_type
+  {
+    XR_PDF,
+    XR_PS,
+    XR_SVG
+  };
+
+/* A font for use with Cairo. */
+struct xr_font
+  {
+    char *string;
+    PangoFontDescription *desc;
+    PangoLayout *layout;
+    PangoFontMetrics *metrics;
+  };
+
+/* Cairo output driver extension record. */
+struct xr_driver_ext
+  {
+    cairo_t *cairo;
+    struct xr_font fonts[OUTP_FONT_CNT];
+
+    bool draw_headers;          /* Draw headers at top of page? */
+    int page_number;           /* Current page number. */
+
+    int line_gutter;           /* Space around lines. */
+    int line_space;            /* Space between lines. */
+    int line_width;            /* Width of lines. */
+  };
+
+struct xr_driver_options
+  {
+    struct outp_driver *driver;
+
+    char *file_name;            /* Output file name. */
+    enum xr_output_type file_type; /* Type of output file. */
+
+
+    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 XR units. */
+    int right_margin;          /* Right margin in XR units. */
+    int top_margin;            /* Top margin in XR units. */
+    int bottom_margin;         /* Bottom margin in XR units. */
+  };
+
+static bool handle_option (void *options, const char *key,
+                           const struct string *val);
+static void draw_headers (struct outp_driver *this);
+
+static bool load_font (struct outp_driver *this, struct xr_font *);
+static void free_font (struct xr_font *);
+static int text_width (struct outp_driver *, const char *, enum outp_font);
+\f
+/* Driver initialization. */
+
+static struct outp_driver *
+xr_allocate (const char *name, int types)
+{
+  struct outp_driver *this;
+  struct xr_driver_ext *x;
+  size_t i;
+
+  this = outp_allocate_driver (&cairo_class, name, types);
+  this->width = this->length = 0;
+  this->font_height = XR_POINT * 10;
+  this->ext = x = xzalloc (sizeof *x);
+  x->cairo = NULL;
+  x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
+  x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
+  x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
+  for (i = 0; i < OUTP_FONT_CNT; i++)
+    {
+      struct xr_font *font = &x->fonts[i];
+      font->desc = NULL;
+      font->metrics = NULL;
+      font->layout = NULL;
+    }
+  x->draw_headers = true;
+  x->page_number = 0;
+  x->line_gutter = XR_POINT;
+  x->line_space = XR_POINT;
+  x->line_width = XR_POINT / 2;
+
+  return this;
+}
+
+static bool
+xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
+{
+  struct xr_driver_ext *x = this->ext;
+  int i;
+
+  x->cairo = cairo;
+
+  cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
+
+  for (i = 0; i < OUTP_FONT_CNT; i++)
+    if (!load_font (this, &x->fonts[i]))
+      return false;
+
+  this->fixed_width = text_width (this, "0", OUTP_FIXED);
+  this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
+
+  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);
+
+  return true;
+}
+
+struct outp_driver *
+xr_create_driver (cairo_t *cairo)
+{
+  struct outp_driver *this;
+
+  this = xr_allocate ("cairo", 0);
+  this->width = INT_MAX / 8;
+  this->length = INT_MAX / 8;
+  if (!xr_set_cairo (this, cairo))
+    {
+      this->class->close_driver (this);
+      outp_free_driver (this);
+      return NULL;
+    }
+  return this;
+}
+
+static bool
+xr_open_driver (const char *name, int types, struct substring option_string)
+{
+  struct outp_driver *this;
+  struct xr_driver_ext *x;
+  struct xr_driver_options options;
+  cairo_surface_t *surface;
+  cairo_status_t status;
+  double width_pt, length_pt;
+
+  this = xr_allocate (name, types);
+  x = this->ext;
+
+  options.driver = this;
+  options.file_name = xstrdup ("pspp.pdf");
+  options.file_type = XR_PDF;
+  options.portrait = true;
+  outp_get_paper_size ("", &options.paper_width, &options.paper_length);
+  options.left_margin = XR_INCH / 2;
+  options.right_margin = XR_INCH / 2;
+  options.top_margin = XR_INCH / 2;
+  options.bottom_margin = XR_INCH / 2;
+
+  outp_parse_options (this->name, option_string, handle_option, &options);
+
+  width_pt = options.paper_width / 1000.0;
+  length_pt = options.paper_length / 1000.0;
+  if (options.portrait)
+    {
+      this->width = pt_to_xr (width_pt);
+      this->length = pt_to_xr (length_pt);
+    }
+  else
+    {
+      this->width = pt_to_xr (width_pt);
+      this->length = pt_to_xr (length_pt);
+    }
+  if (x->draw_headers)
+    options.top_margin += 3 * this->font_height;
+  this->width -= options.left_margin + options.right_margin;
+  this->length -= options.top_margin + options.bottom_margin;
+
+  if (options.file_type == XR_PDF)
+    surface = cairo_pdf_surface_create (options.file_name,
+                                        width_pt, length_pt);
+  else if (options.file_type == XR_PS)
+    surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
+  else if (options.file_type == XR_SVG)
+    surface = cairo_svg_surface_create (options.file_name,
+                                        width_pt, length_pt);
+  else
+    NOT_REACHED ();
+
+  status = cairo_surface_status (surface);
+  if (status != CAIRO_STATUS_SUCCESS)
+    {
+      error (0, 0, _("opening output file \"%s\": %s"),
+             options.file_name, cairo_status_to_string (status));
+      cairo_surface_destroy (surface);
+      goto error;
+    }
+
+  x->cairo = cairo_create (surface);
+  cairo_surface_destroy (surface);
+
+  cairo_translate (x->cairo,
+                   xr_to_pt (options.left_margin),
+                   xr_to_pt (options.top_margin));
+
+  if (this->length / this->font_height < 15)
+    {
+      error (0, 0, _("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."),
+             this->length / this->font_height);
+      goto error;
+    }
+
+  if (!xr_set_cairo (this, x->cairo))
+    goto error;
+
+  outp_register_driver (this);
+  free (options.file_name);
+  return true;
+
+ error:
+  this->class->close_driver (this);
+  outp_free_driver (this);
+  free (options.file_name);
+  return false;
+}
+
+static bool
+xr_close_driver (struct outp_driver *this)
+{
+  struct xr_driver_ext *x = this->ext;
+  bool ok = true;
+  size_t i;
+
+  if (x->cairo != NULL)
+    {
+      cairo_status_t status;
+
+      cairo_surface_finish (cairo_get_target (x->cairo));
+      status = cairo_status (x->cairo);
+      if (status != CAIRO_STATUS_SUCCESS)
+        error (0, 0, _("error writing output file for %s driver: %s"),
+               this->name, cairo_status_to_string (status));
+      cairo_destroy (x->cairo);
+    }
+
+  for (i = 0; i < OUTP_FONT_CNT; i++)
+    free_font (&x->fonts[i]);
+  free (x);
+
+  return ok;
+}
+
+/* Generic option types. */
+enum
+{
+  output_file_arg,
+  output_type_arg,
+  paper_size_arg,
+  orientation_arg,
+  line_style_arg,
+  boolean_arg,
+  dimension_arg,
+  string_arg,
+  nonneg_int_arg
+};
+
+/* All the options that the Cairo driver supports. */
+static const struct outp_option option_tab[] =
+{
+  {"output-file",              output_file_arg,0},
+  {"output-type",               output_type_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",              dimension_arg,  0},
+  {"right-margin",             dimension_arg,  1},
+  {"top-margin",               dimension_arg,  2},
+  {"bottom-margin",            dimension_arg,  3},
+  {"font-size",                        dimension_arg,  4},
+  {"line-width",               dimension_arg,  5},
+  {"line-gutter",              dimension_arg,  6},
+  {"line-width",               dimension_arg,  7},
+  {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+  struct xr_driver_options *options = options_;
+  struct outp_driver *this = options->driver;
+  struct xr_driver_ext *x = this->ext;
+  int subcat;
+  char *value = ds_cstr (val);
+
+  switch (outp_match_keyword (key, option_tab, &subcat))
+    {
+    case -1:
+      error (0, 0,
+             _("unknown configuration parameter `%s' for %s device "
+               "driver"), key, this->class->name);
+      break;
+    case output_file_arg:
+      free (options->file_name);
+      options->file_name = xstrdup (value);
+      break;
+    case output_type_arg:
+      if (!strcmp (value, "pdf"))
+        options->file_type = XR_PDF;
+      else if (!strcmp (value, "ps"))
+        options->file_type = XR_PS;
+      else if (!strcmp (value, "svg"))
+        options->file_type = XR_SVG;
+      else
+        {
+          error (0, 0, _("unknown Cairo output type \"%s\""), value);
+          return false;
+        }
+      break;
+    case paper_size_arg:
+      outp_get_paper_size (value,
+                           &options->paper_width, &options->paper_length);
+      break;
+    case orientation_arg:
+      if (!strcmp (value, "portrait"))
+       options->portrait = true;
+      else if (!strcmp (value, "landscape"))
+       options->portrait = false;
+      else
+       error (0, 0, _("unknown orientation `%s' (valid orientations are "
+                       "`portrait' and `landscape')"), value);
+      break;
+    case boolean_arg:
+      if (!strcmp (value, "on") || !strcmp (value, "true")
+          || !strcmp (value, "yes") || atoi (value))
+        x->draw_headers = true;
+      else if (!strcmp (value, "off") || !strcmp (value, "false")
+               || !strcmp (value, "no") || !strcmp (value, "0"))
+        x->draw_headers = false;
+      else
+        {
+          error (0, 0, _("boolean value expected for %s"), key);
+          return false;
+        }
+      break;
+    case dimension_arg:
+      {
+       int dimension = outp_evaluate_dimension (value);
+
+       if (dimension <= 0)
+          break;
+       switch (subcat)
+         {
+         case 0:
+           options->left_margin = dimension;
+           break;
+         case 1:
+           options->right_margin = dimension;
+           break;
+         case 2:
+           options->top_margin = dimension;
+           break;
+         case 3:
+           options->bottom_margin = dimension;
+           break;
+         case 4:
+           this->font_height = dimension;
+           break;
+         case 5:
+           x->line_width = dimension;
+           break;
+         case 6:
+           x->line_gutter = dimension;
+           break;
+         case 7:
+           x->line_width = dimension;
+           break;
+         default:
+           NOT_REACHED ();
+         }
+      }
+      break;
+    case string_arg:
+      free (x->fonts[subcat].string);
+      x->fonts[subcat].string = ds_xstrdup (val);
+      break;
+    default:
+      NOT_REACHED ();
+    }
+
+  return true;
+}
+\f
+/* Basic file operations. */
+
+static void
+xr_open_page (struct outp_driver *this)
+{
+  struct xr_driver_ext *x = this->ext;
+
+  x->page_number++;
+
+  if (x->draw_headers)
+    draw_headers (this);
+}
+
+static void
+xr_close_page (struct outp_driver *this)
+{
+  struct xr_driver_ext *x = this->ext;
+  cairo_show_page (x->cairo);
+}
+
+static void
+xr_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+  struct xr_driver_ext *x = this->ext;
+  struct chart_geometry geom;
+
+  outp_eject_page (this);
+  outp_open_page (this);
+
+  cairo_save (x->cairo);
+  cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
+  cairo_scale (x->cairo, 1.0, -1.0);
+  chart_geometry_init (x->cairo, &geom,
+                       xr_to_pt (this->width), xr_to_pt (this->length));
+  chart_draw (chart, x->cairo, &geom);
+  chart_geometry_free (x->cairo, &geom);
+  cairo_restore (x->cairo);
+
+  outp_close_page (this);
+}
+\f
+/* Draws a line from (x0,y0) to (x1,y1). */
+static void
+dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
+{
+  struct xr_driver_ext *x = this->ext;
+  cairo_new_path (x->cairo);
+  cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
+  cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
+  cairo_stroke (x->cairo);
+}
+
+/* Draws a horizontal line X0...X2 at Y if LEFT says so,
+   shortening it to X0...X1 if SHORTEN is true.
+   Draws a horizontal line X1...X3 at Y if RIGHT says so,
+   shortening it to X2...X3 if SHORTEN is true. */
+static void
+horz_line (struct outp_driver *this,
+           int x0, int x1, int x2, int x3, int y,
+           enum outp_line_style left, enum outp_line_style right,
+           bool shorten)
+{
+  if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
+    dump_line (this, x0, y, x3, y);
+  else
+    {
+      if (left != OUTP_L_NONE)
+        dump_line (this, x0, y, shorten ? x1 : x2, y);
+      if (right != OUTP_L_NONE)
+        dump_line (this, shorten ? x2 : x1, y, x3, y);
+    }
+}
+
+/* Draws a vertical line Y0...Y2 at X if TOP says so,
+   shortening it to Y0...Y1 if SHORTEN is true.
+   Draws a vertical line Y1...Y3 at X if BOTTOM says so,
+   shortening it to Y2...Y3 if SHORTEN is true. */
+static void
+vert_line (struct outp_driver *this,
+           int y0, int y1, int y2, int y3, int x,
+           enum outp_line_style top, enum outp_line_style bottom,
+           bool shorten)
+{
+  if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
+    dump_line (this, x, y0, x, y3);
+  else
+    {
+      if (top != OUTP_L_NONE)
+        dump_line (this, x, y0, x, shorten ? y1 : y2);
+      if (bottom != OUTP_L_NONE)
+        dump_line (this, x, shorten ? y2 : y1, x, y3);
+    }
+}
+
+/* Draws a generalized intersection of lines in the rectangle
+   (X0,Y0)-(X3,Y3).  The line coming from the top to the center
+   is of style TOP, from left to center of style LEFT, from
+   bottom to center of style BOTTOM, and from right to center of
+   style RIGHT. */
+static void
+xr_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 xr_driver_ext *ext = this->ext;
+
+  /* Offset from center of each line in a pair of double lines. */
+  int double_line_ofs = (ext->line_space + ext->line_width) / 2;
+
+  /* Are the lines along each axis single or double?
+     (It doesn't make sense to have different kinds of line on the
+     same axis, so we don't try to gracefully handle that case.) */
+  bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
+  bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
+
+  /* When horizontal lines are doubled,
+     the left-side line along y1 normally runs from x0 to x2,
+     and the right-side line along y1 from x3 to x1.
+     If the top-side line is also doubled, we shorten the y1 lines,
+     so that the left-side line runs only to x1,
+     and the right-side line only to x2.
+     Otherwise, the horizontal line at y = y1 below would cut off
+     the intersection, which looks ugly:
+               x0       x1     x2      x3
+             y0 ________________________
+                |        #     #       |
+                |        #     #       |
+                |        #     #       |
+                |        #     #       |
+             y1 |#########     ########|
+                |                      |
+                |                      |
+             y2 |######################|
+                |                      |
+                |                      |
+             y3 |______________________|
+     It is more of a judgment call when the horizontal line is
+     single.  We actually choose to cut off the line anyhow, as
+     shown in the first diagram above.
+  */
+  bool shorten_y1_lines = top == OUTP_L_DOUBLE;
+  bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
+  bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
+  int horz_line_ofs = double_vert ? double_line_ofs : 0;
+  int xc = (x0 + x3) / 2;
+  int x1 = xc - horz_line_ofs;
+  int x2 = xc + horz_line_ofs;
+
+  bool shorten_x1_lines = left == OUTP_L_DOUBLE;
+  bool shorten_x2_lines = right == OUTP_L_DOUBLE;
+  bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
+  int vert_line_ofs = double_horz ? double_line_ofs : 0;
+  int yc = (y0 + y3) / 2;
+  int y1 = yc - vert_line_ofs;
+  int y2 = yc + vert_line_ofs;
+
+  if (!double_horz)
+    horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
+  else
+    {
+      horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
+      horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
+    }
+
+  if (!double_vert)
+    vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
+  else
+    {
+      vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
+      vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
+    }
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+   and with the given JUSTIFICATION for THIS driver. */
+static int
+draw_text (struct outp_driver *this,
+           const char *string, int x, int y, int max_width,
+           enum outp_justification justification)
+{
+  struct outp_text text;
+  int width;
+
+  text.font = OUTP_PROPORTIONAL;
+  text.justification = justification;
+  text.string = ss_cstr (string);
+  text.h = max_width;
+  text.v = this->font_height;
+  text.x = x;
+  text.y = y;
+  this->class->text_metrics (this, &text, &width, NULL);
+  this->class->text_draw (this, &text);
+  return width;
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+   and with the given JUSTIFICATION for THIS driver. */
+static int
+text_width (struct outp_driver *this, const char *string, enum outp_font font)
+{
+  struct outp_text text;
+  int width;
+
+  text.font = font;
+  text.justification = OUTP_LEFT;
+  text.string = ss_cstr (string);
+  text.h = INT_MAX;
+  text.v = this->font_height;
+  text.x = 0;
+  text.y = 0;
+  this->class->text_metrics (this, &text, &width, NULL);
+  return width;
+}
+
+/* Writes LEFT left-justified and RIGHT right-justified within
+   (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
+static void
+draw_header_line (struct outp_driver *this,
+                  const char *left, const char *right,
+                  int x0, int x1, int y)
+{
+  int right_width = 0;
+  if (right != NULL)
+    right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
+                   + this->prop_em_width);
+  if (left != NULL)
+    draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
+}
+
+/* Draw top of page headers for THIS driver. */
+static void
+draw_headers (struct outp_driver *this)
+{
+  struct xr_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. */
+  cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
+                   xr_to_pt (2 * (this->font_height
+                                  + ext->line_width + ext->line_gutter)));
+  cairo_save (ext->cairo);
+  cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
+  cairo_fill_preserve (ext->cairo);
+  cairo_restore (ext->cairo);
+  cairo_stroke (ext->cairo);
+
+  y += ext->line_width + ext->line_gutter;
+
+  r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
+  r2 = xasprintf ("%s - %s", version, host_system);
+
+  draw_header_line (this, outp_title, r1, x0, x1, y);
+  y += this->font_height;
+
+  draw_header_line (this, outp_subtitle, r2, x0, x1, y);
+
+  free (r1);
+  free (r2);
+}
+\f
+/* Format TEXT on THIS driver.
+   If DRAW is nonzero, draw the text.
+   The width of the widest line is stored into *WIDTH, if WIDTH
+   is nonnull.
+   The total height of the text written is stored into *HEIGHT,
+   if HEIGHT is nonnull. */
+static void
+text (struct outp_driver *this, const struct outp_text *text, bool draw,
+      int *width, int *height)
+{
+  struct xr_driver_ext *ext = this->ext;
+  struct xr_font *font = &ext->fonts[text->font];
+
+  pango_layout_set_text (font->layout,
+                         text->string.string, text->string.length);
+  pango_layout_set_alignment (
+    font->layout,
+    (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
+     : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
+     : PANGO_ALIGN_CENTER));
+  pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
+  pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
+  /* XXX need to limit number of lines to those that fit in text->v. */
+
+  if (draw)
+    {
+      int x = text->x;
+      if (text->justification != OUTP_LEFT && text->h != INT_MAX)
+        {
+          int w, h, excess;
+          pango_layout_get_size (font->layout, &w, &h);
+          excess = text->h - w;
+          if (excess > 0)
+            {
+              if (text->justification == OUTP_CENTER)
+                x += excess / 2;
+              else
+                x += excess;
+            }
+        }
+      cairo_save (ext->cairo);
+      cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
+      pango_cairo_show_layout (ext->cairo, font->layout);
+      cairo_restore (ext->cairo);
+    }
+
+  if (width != NULL || height != NULL)
+    {
+      int w, h;
+      pango_layout_get_size (font->layout, &w, &h);
+      if (width != NULL)
+        *width = w;
+      if (height != NULL)
+        *height = h;
+    }
+}
+
+static void
+xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
+                 int *width, int *height)
+{
+  text (this, t, false, width, height);
+}
+
+static void
+xr_text_draw (struct outp_driver *this, const struct outp_text *t)
+{
+  text (this, t, true, NULL, NULL);
+}
+\f
+/* Attempts to load FONT, initializing its other members based on
+   its 'string' member and the information in THIS.  Returns true
+   if successful, otherwise false. */
+static bool
+load_font (struct outp_driver *this, struct xr_font *font)
+{
+  struct xr_driver_ext *x = this->ext;
+  PangoContext *context;
+  PangoLanguage *language;
+
+  font->desc = pango_font_description_from_string (font->string);
+  if (font->desc == NULL)
+    {
+      error (0, 0, _("\"%s\": bad font specification"), font->string);
+      return false;
+    }
+  pango_font_description_set_absolute_size (font->desc, this->font_height);
+
+  font->layout = pango_cairo_create_layout (x->cairo);
+  pango_layout_set_font_description (font->layout, font->desc);
+
+  language = pango_language_get_default ();
+  context = pango_layout_get_context (font->layout);
+  font->metrics = pango_context_get_metrics (context, font->desc, language);
+
+  return true;
+}
+
+/* Frees FONT. */
+static void
+free_font (struct xr_font *font)
+{
+  free (font->string);
+  if (font->desc != NULL)
+    pango_font_description_free (font->desc);
+  pango_font_metrics_unref (font->metrics);
+  g_object_unref (font->layout);
+}
+
+/* Cairo driver class. */
+const struct outp_class cairo_class =
+{
+  "cairo",
+  0,
+
+  xr_open_driver,
+  xr_close_driver,
+
+  xr_open_page,
+  xr_close_page,
+  NULL,
+
+  xr_output_chart,
+
+  NULL,
+
+  xr_line,
+  xr_text_metrics,
+  xr_text_draw,
+};
diff --git a/src/output/cairo.h b/src/output/cairo.h
new file mode 100644 (file)
index 0000000..c4f8a81
--- /dev/null
@@ -0,0 +1,24 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CAIRO_H
+#define OUTPUT_CAIRO_H 1
+
+#include <cairo/cairo.h>
+
+struct outp_driver *xr_create_driver (cairo_t *);
+
+#endif /* output/cairo.h */
diff --git a/src/output/chart-provider.h b/src/output/chart-provider.h
new file mode 100644 (file)
index 0000000..9becb6f
--- /dev/null
@@ -0,0 +1,89 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_PROVIDER_H
+#define OUTPUT_CHART_PROVIDER_H 1
+
+#include <cairo/cairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <output/chart.h>
+
+struct chart_colour
+  {
+    uint8_t red;
+    uint8_t green;
+    uint8_t blue;
+  };
+
+/* The geometry of a chart. */
+struct chart_geometry
+  {
+    int data_top   ;
+    int data_right ;
+    int data_bottom;
+    int data_left  ;
+
+    int abscissa_top;
+
+    int ordinate_right ;
+
+    int title_bottom ;
+
+    /* Legend. */
+    int legend_left ;
+    int legend_right ;
+    const char **dataset;
+    int n_datasets;
+
+    /* Default font size for the plot. */
+    double font_size;
+
+    struct chart_colour fill_colour;
+
+    /* Stuff Particular to Cartesians (and Boxplots ) */
+    double ordinate_scale;
+    double abscissa_scale;
+    double x_min;
+    double x_max;
+    double y_min;
+    double y_max;
+    bool in_path;
+  };
+
+struct chart_class
+  {
+    void (*draw) (const struct chart *, cairo_t *, struct chart_geometry *);
+    void (*destroy) (struct chart *);
+  };
+
+struct chart
+  {
+    const struct chart_class *class;
+    int ref_cnt;
+  };
+
+void chart_init (struct chart *, const struct chart_class *);
+
+void chart_geometry_init (cairo_t *, struct chart_geometry *,
+                          double width, double length);
+void chart_geometry_free (cairo_t *, struct chart_geometry *);
+
+void chart_draw (const struct chart *, cairo_t *, struct chart_geometry *);
+char *chart_draw_png (const struct chart *, const char *file_name_template,
+                      int number);
+
+#endif /* output/chart-provider.h */
index e324901cc4df1d445f3fc8c7a4b64296bca55bca..e31422bc6bb35799243d68337d41914ae059c430 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <config.h>
 
 #include <output/chart.h>
+#include <output/chart-provider.h>
 
 #include <assert.h>
+#include <cairo/cairo.h>
 #include <errno.h>
 #include <float.h>
 #include <math.h>
@@ -27,8 +29,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <plot.h>
-
 #include <libpspp/str.h>
 #include <output/manager.h>
 #include <output/output.h>
 
 extern struct som_table_class tab_table_class;
 
-struct chart *
-chart_create(void)
+void
+chart_init (struct chart *chart, const struct chart_class *class)
 {
-  struct chart *chart;
-  struct outp_driver *d;
-
-  d = outp_drivers (NULL);
-  if (d == NULL)
-    return NULL;
-
-  chart = xmalloc (sizeof *chart);
-  chart->lp = NULL;
-  d->class->initialise_chart(d, chart);
-  if (!chart->lp)
-    {
-      free (chart);
-      return NULL;
-    }
-
-  if (pl_openpl_r (chart->lp) < 0)      /* open Plotter */
-    return NULL;
-
-  pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
-  pl_flinewidth_r (chart->lp, 0.25);    /* set line thickness */
-  pl_pencolorname_r (chart->lp, "black");
-
-  pl_erase_r (chart->lp);               /* erase graphics display */
-  pl_filltype_r(chart->lp,0);
-
-  pl_savestate_r(chart->lp);
-
-  /* Set default chartetry */
-  chart->data_top =   900;
-  chart->data_right = 800;
-  chart->data_bottom = 120;
-  chart->data_left = 150;
-  chart->abscissa_top = 70;
-  chart->ordinate_right = 120;
-  chart->title_bottom = 920;
-  chart->legend_left = 810;
-  chart->legend_right = 1000;
-  chart->font_size = 0;
-  chart->in_path = false;
-  chart->dataset = NULL;
-  chart->n_datasets = 0;
-  strcpy(chart->fill_colour,"red");
-
-  /* Get default font size */
-  if ( !chart->font_size)
-    chart->font_size = pl_fontsize_r(chart->lp, -1);
-
-  /* Draw the data area */
-  pl_box_r(chart->lp,
-          chart->data_left, chart->data_bottom,
-          chart->data_right, chart->data_top);
+  chart->class = class;
+  chart->ref_cnt = 1;
+}
 
-  return chart;
+void
+chart_geometry_init (cairo_t *cr, struct chart_geometry *geom,
+                     double width, double length)
+{
+  /* Set default chartetry. */
+  geom->data_top = 0.900 * length;
+  geom->data_right = 0.800 * width;
+  geom->data_bottom = 0.120 * length;
+  geom->data_left = 0.150 * width;
+  geom->abscissa_top = 0.070 * length;
+  geom->ordinate_right = 0.120 * width;
+  geom->title_bottom = 0.920 * length;
+  geom->legend_left = 0.810 * width;
+  geom->legend_right = width;
+  geom->font_size = 15.0;
+  geom->in_path = false;
+  geom->dataset = NULL;
+  geom->n_datasets = 0;
+
+  geom->fill_colour.red = 255;
+  geom->fill_colour.green = 0;
+  geom->fill_colour.blue = 0;
+
+  cairo_set_line_width (cr, 1.0);
+
+  cairo_rectangle (cr, geom->data_left, geom->data_bottom,
+                   geom->data_right - geom->data_left,
+                   geom->data_top - geom->data_bottom);
+  cairo_stroke (cr);
 }
 
 void
-chart_submit(struct chart *chart)
+chart_geometry_free (cairo_t *cr UNUSED, struct chart_geometry *geom)
 {
   int i;
-  struct som_entity s;
-  struct outp_driver *d;
-
-  if ( ! chart )
-     return ;
-
-  pl_restorestate_r(chart->lp);
-
-  s.class = &tab_table_class;
-  s.ext = chart;
-  s.type = SOM_CHART;
-  som_submit (&s);
 
-  if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
-    {
-      fprintf (stderr, "Couldn't close Plotter\n");
-    }
-
-  pl_deletepl_r(chart->lp);
+  for (i = 0 ; i < geom->n_datasets; ++i)
+    free (geom->dataset[i]);
+  free (geom->dataset);
+}
 
-  pl_deleteplparams(chart->pl_params);
+void
+chart_draw (const struct chart *chart, cairo_t *cr,
+            struct chart_geometry *geom)
+{
+  chart->class->draw (chart, cr, geom);
+}
 
-  d = outp_drivers (NULL);
-  d->class->finalise_chart(d, chart);
+char *
+chart_draw_png (const struct chart *chart, const char *file_name_template,
+                int number)
+{
+  const int width = 640;
+  const int length = 480;
+
+  struct chart_geometry geom;
+  cairo_surface_t *surface;
+  cairo_status_t status;
+  const char *number_pos;
+  char *file_name;
+  cairo_t *cr;
+
+  number_pos = strchr (file_name_template, '#');
+  if (number_pos != NULL)
+    file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
+                           file_name_template, number, number_pos + 1);
+  else
+    file_name = xstrdup (file_name_template);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
+  cr = cairo_create (surface);
+
+  cairo_translate (cr, 0.0, length);
+  cairo_scale (cr, 1.0, -1.0);
+
+  cairo_save (cr);
+  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+  cairo_rectangle (cr, 0, 0, width, length);
+  cairo_fill (cr);
+  cairo_restore (cr);
+
+  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+  chart_geometry_init (cr, &geom, width, length);
+  chart_draw (chart, cr, &geom);
+  chart_geometry_free (cr, &geom);
+
+  status = cairo_surface_write_to_png (surface, file_name);
+  if (status != CAIRO_STATUS_SUCCESS)
+    error (0, 0, _("writing output file \"%s\": %s"),
+           file_name, cairo_status_to_string (status));
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  return file_name;
+}
 
-  for (i = 0 ; i < chart->n_datasets; ++i)
-    free (chart->dataset[i]);
-  free (chart->dataset);
 
-  free(chart);
+struct chart *
+chart_ref (const struct chart *chart_)
+{
+  struct chart *chart = CONST_CAST (struct chart *, chart_);
+  chart->ref_cnt++;
+  return chart;
 }
 
 void
-chart_init_separate (struct chart *ch, const char *type,
-                     const char *file_name_tmpl, int number)
+chart_unref (struct chart *chart)
 {
-  FILE *fp;
-  int number_pos;
-
-  number_pos = strchr (file_name_tmpl, '#') - file_name_tmpl;
-  ch->file_name = xasprintf ("%.*s%d%s",
-                             number_pos, file_name_tmpl,
-                             number,
-                             file_name_tmpl + number_pos + 1);
-  fp = fopen (ch->file_name, "wb");
-  if (fp == NULL)
+  if (chart != NULL)
     {
-      error (0, errno, _("creating \"%s\""), ch->file_name);
-      free (ch->file_name);
-      ch->file_name = NULL;
-      return;
+      assert (chart->ref_cnt > 0);
+      if (--chart->ref_cnt == 0)
+        chart->class->destroy (chart);
     }
-
-  ch->pl_params = pl_newplparams ();
-  ch->lp = pl_newpl_r (type, 0, fp, stderr, ch->pl_params);
 }
 
 void
-chart_finalise_separate (struct chart *ch)
+chart_submit (struct chart *chart)
 {
-  free (ch->file_name);
+#ifdef HAVE_CAIRO
+  struct outp_driver *d;
+
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    if (d->class->output_chart != NULL)
+      d->class->output_chart (d, chart);
+#endif
+
+  chart_unref (chart);
 }
index 4da12ecfe5b0afba3ea7035680fca5b1ea1d9af9..d6b9b3d49e38222ecd8ba36ce49c87f69037003a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
+#ifndef OUTPUT_CHART_H
+#define OUTPUT_CHART_H 1
 
-#include <math/chart-geometry.h>
-#include <libpspp/str.h>
-#include "manager.h"
-#include "output.h"
+#include <cairo/cairo.h>
 
-#include "xalloc.h"
+struct chart;
 
-#ifndef CHART_H
-#define CHART_H
+struct chart *chart_ref (const struct chart *);
+void chart_unref (struct chart *);
 
-#ifndef NO_CHARTS
-#include <plot.h>
-#endif
+void chart_submit (struct chart *);
 
-struct chart {
-
-#ifndef NO_CHARTS
-  plPlotter *lp ;
-  plPlotterParams *pl_params;
-#else
-  void *lp;
-#endif
-  char *file_name;
-  FILE *file;
-
-  /* The geometry of the chart
-     See diagram at the foot of this file.
-   */
-
-  int data_top   ;
-  int data_right ;
-  int data_bottom;
-  int data_left  ;
-
-  int abscissa_top;
-
-  int ordinate_right ;
-
-  int title_bottom ;
-
-  int legend_left ;
-  int legend_right ;
-  const char **dataset;
-  int n_datasets;
-
-
-  /* Default font size for the plot (if zero, then use plotter default) */
-  int font_size;
-
-  char fill_colour[10];
-
-  /* Stuff Particular to Cartesians (and Boxplots ) */
-  double ordinate_scale;
-  double abscissa_scale;
-  double x_min;
-  double x_max;
-  double y_min;
-  double y_max;
-  bool in_path;
-};
-
-
-
-struct chart * chart_create(void);
-void chart_submit(struct chart *ch);
-
-/* Helper functions for output drivers that put each chart into a
-   separate file. */
-void chart_init_separate (struct chart *, const char *type,
-                          const char *file_name_tmpl, int number);
-
-void chart_finalise_separate (struct chart *);
-
-#endif
+#endif /* output/chart.h */
diff --git a/src/output/charts/Makefile b/src/output/charts/Makefile
deleted file mode 100644 (file)
index c1a052e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-all:
-       $(MAKE) -C /home/res/jmd/PSPP/pspp 
diff --git a/src/output/charts/automake.mk b/src/output/charts/automake.mk
deleted file mode 100644 (file)
index ab0ff51..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-## Process this file with automake to produce Makefile.in  -*- makefile -*-
-
-noinst_LTLIBRARIES += src/output/charts/libcharts.la
-
-chart_sources = \
-       src/output/charts/barchart.c \
-       src/output/charts/barchart.h \
-       src/output/charts/box-whisker.c \
-       src/output/charts/box-whisker.h \
-       src/output/charts/cartesian.c \
-       src/output/charts/cartesian.h \
-       src/output/charts/piechart.c \
-       src/output/charts/piechart.h \
-       src/output/charts/plot-chart.h \
-       src/output/charts/plot-chart.c \
-       src/output/charts/plot-hist.c \
-       src/output/charts/plot-hist.h
-
-if WITHCHARTS
-src_output_charts_libcharts_la_SOURCES = \
-       $(chart_sources)
-
-EXTRA_DIST += src/output/charts/dummy-chart.c
-else
-src_output_charts_libcharts_la_SOURCES =  \
-       src/output/charts/dummy-chart.c
-
-EXTRA_DIST += $(chart_sources)
-
-AM_CPPFLAGS += -DNO_CHARTS
-
-endif
-
-EXTRA_DIST += src/output/charts/OChangeLog
diff --git a/src/output/charts/barchart.c b/src/output/charts/barchart.c
deleted file mode 100644 (file)
index f3b1001..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-#include <config.h>
-
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <math.h>
-#include <output/charts/barchart.h>
-#include <output/chart.h>
-#include <output/charts/plot-chart.h>
-
-#define CATAGORIES 6
-#define SUB_CATAGORIES 3
-
-static const    double x_min = 0;
-static const    double x_max = 15.0;
-
-static const char *cat_labels[] =
-  {
-    "Age",
-    "Intelligence",
-    "Wealth",
-    "Emotional",
-    "cat 5",
-    "cat 6",
-    "cat 7",
-    "cat 8",
-    "cat 9",
-    "cat 10",
-    "cat 11"
-  };
-
-
-
-
-/* Subcatagories */
-static const double data1[] =
-{
-  28,83,
-  34,
-  29,13,
-   9,4,
-   3,3,
-   2,0,
-   1,0,
-   0,
-   1,1
-};
-
-
-static const double data2[] =
-{
-  45,13,
-   9,4,
-   3,43,
-   2,0,
-   1,20,
-   0,0,
-  1,1,
-  0,0
-};
-
-static const double data3[] =
-  {
-    23,18,
-    0, 45,23, 9, 40, 24,4, 8
-  };
-
-
-static const char subcat_name[]="Gender";
-
-
-struct subcat {
-  const double *data;
-  const char *label;
-};
-
-static const struct subcat sub_catagory[SUB_CATAGORIES] =
-  {
-    {data1, "male"},
-    {data2, "female"},
-    {data3, "47xxy"}
-  };
-
-
-
-static const    double y_min = 0;
-static const    double y_max = 120.0;
-static const    double y_tick = 20.0;
-
-
-
-static void write_legend(struct chart *chart) ;
-
-
-void
-draw_barchart(struct chart *ch, const char *title,
-             const char *xlabel, const char *ylabel, enum bar_opts opt)
-{
-  double d;
-  int i;
-
-  double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
-
-  double bar_width = interval_size / 1.1 ;
-
-  double ordinate_scale = fabs(ch->data_top -  ch->data_bottom) /
-    fabs(y_max - y_min) ;
-
-  if ( opt != BAR_STACKED )
-      bar_width /= SUB_CATAGORIES;
-
-  /* Move to data bottom-left */
-  pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
-
-  pl_savestate_r(ch->lp);
-  pl_filltype_r(ch->lp,1);
-
-  /* Draw the data */
-  for (i = 0 ; i < CATAGORIES ; ++i )
-    {
-      int sc;
-      double ystart=0.0;
-      double x = i * interval_size;
-
-      pl_savestate_r(ch->lp);
-
-      draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ), "%s",
-                cat_labels[i]);
-
-      for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
-       {
-
-         pl_savestate_r(ch->lp);
-         pl_fillcolorname_r(ch->lp,data_colour[sc % N_CHART_COLOURS]);
-
-         switch ( opt )
-           {
-           case BAR_GROUPED:
-             pl_fboxrel_r(ch->lp,
-                          x + (sc * bar_width ), 0,
-                          x + (sc + 1) * bar_width,
-                            sub_catagory[sc].data[i] * ordinate_scale );
-             break;
-
-
-           case BAR_STACKED:
-
-             pl_fboxrel_r(ch->lp,
-                          x, ystart,
-                          x + bar_width,
-                          ystart + sub_catagory[sc].data[i] * ordinate_scale );
-
-             ystart +=    sub_catagory[sc].data[i] * ordinate_scale ;
-
-             break;
-
-           default:
-             break;
-           }
-         pl_restorestate_r(ch->lp);
-       }
-
-      pl_restorestate_r(ch->lp);
-    }
-  pl_restorestate_r(ch->lp);
-
-  for ( d = y_min; d <= y_max ; d += y_tick )
-    {
-
-      draw_tick (ch, TICK_ORDINATE,
-                (d - y_min ) * ordinate_scale, "%g", d);
-
-    }
-
-  /* Write the abscissa label */
-  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
-  pl_alabel_r(ch->lp,0,'t',xlabel);
-
-
-  /* Write the ordinate label */
-  pl_savestate_r(ch->lp);
-  pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
-  pl_textangle_r(ch->lp,90);
-  pl_alabel_r(ch->lp,0,0,ylabel);
-  pl_restorestate_r(ch->lp);
-
-
-  chart_write_title(ch, "%s", title);
-
-  write_legend(ch);
-
-
-}
-
-
-
-
-
-static void
-write_legend(struct chart *chart)
-{
-  int sc;
-
-  pl_savestate_r(chart->lp);
-
-  pl_filltype_r(chart->lp,1);
-
-  pl_move_r(chart->lp, chart->legend_left,
-           chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
-
-  pl_alabel_r(chart->lp,0,'b',subcat_name);
-
-  for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
-    {
-      pl_fmove_r(chart->lp,
-                chart->legend_left,
-                chart->data_bottom + chart->font_size * sc  * 1.5);
-
-      pl_savestate_r(chart->lp);
-      pl_fillcolorname_r(chart->lp,data_colour[sc]);
-      pl_fboxrel_r (chart->lp,
-                   0,0,
-                   chart->font_size, chart->font_size);
-      pl_restorestate_r(chart->lp);
-
-      pl_fmove_r(chart->lp,
-                chart->legend_left + chart->font_size * 1.5,
-                chart->data_bottom + chart->font_size * sc  * 1.5);
-
-      pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
-    }
-
-
-  pl_restorestate_r(chart->lp);
-}
diff --git a/src/output/charts/barchart.h b/src/output/charts/barchart.h
deleted file mode 100644 (file)
index 0983272..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef BARCHART_H
-#define BARCHART_H
-
-#include <output/chart.h>
-
-enum  bar_opts {
-  BAR_GROUPED =  0,
-  BAR_STACKED,
-  BAR_RANGE
-};
-
-void draw_barchart(struct chart *ch, const char *title,
-             const char *xlabel, const char *ylabel, enum bar_opts opt);
-
-#endif
diff --git a/src/output/charts/box-whisker.c b/src/output/charts/box-whisker.c
deleted file mode 100644 (file)
index 33c445b..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-#include <config.h>
-
-#include <math.h>
-#include <assert.h>
-#include <libpspp/misc.h>
-
-#include <output/charts/box-whisker.h>
-#include <output/charts/plot-chart.h>
-
-#include <output/chart.h>
-#include <math/chart-geometry.h>
-#include <math/box-whisker.h>
-
-/* Draw a box-and-whiskers plot
-*/
-
-/* Draw an OUTLIER on the plot CH
- * at CENTRELINE
- */
-static void
-draw_case (struct chart *ch, double centreline,
-          const struct outlier *outlier)
-{
-
-#define MARKER_CIRCLE 4
-#define MARKER_STAR 3
-
-  pl_fmarker_r(ch->lp,
-              centreline,
-              ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale,
-              outlier->extreme ? MARKER_STAR : MARKER_CIRCLE,
-              20);
-
-  pl_moverel_r(ch->lp, 10,0);
-
-  pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
-}
-
-
-void
-boxplot_draw_boxplot (struct chart *ch,
-                     double box_centre,
-                     double box_width,
-                     const struct box_whisker *bw,
-                     const char *name)
-{
-  double whisker[2];
-  double hinge[3];
-  struct ll *ll;
-
-  const struct ll_list *outliers;
-
-  const double box_left = box_centre - box_width / 2.0;
-
-  const double box_right = box_centre + box_width / 2.0;
-
-  double box_bottom ;
-  double box_top ;
-  double bottom_whisker ;
-  double top_whisker ;
-
-  box_whisker_whiskers (bw, whisker);
-  box_whisker_hinges (bw, hinge);
-
-  box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
-
-  box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
-
-  bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
-    ch->ordinate_scale;
-
-  top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
-
-  pl_savestate_r(ch->lp);
-
-  /* Draw the box */
-  pl_savestate_r (ch->lp);
-  pl_fillcolorname_r (ch->lp, ch->fill_colour);
-  pl_filltype_r (ch->lp,1);
-  pl_fbox_r (ch->lp,
-           box_left,
-           box_bottom,
-           box_right,
-           box_top);
-
-  pl_restorestate_r (ch->lp);
-
-  /* Draw the median */
-  pl_savestate_r (ch->lp);
-  pl_linewidth_r (ch->lp, 5);
-  pl_fline_r (ch->lp,
-            box_left,
-            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
-            box_right,
-            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
-  pl_restorestate_r (ch->lp);
-
-  /* Draw the bottom whisker */
-  pl_fline_r (ch->lp,
-            box_left,
-            bottom_whisker,
-            box_right,
-            bottom_whisker);
-
-  /* Draw top whisker */
-  pl_fline_r (ch->lp,
-            box_left,
-            top_whisker,
-            box_right,
-            top_whisker);
-
-
-  /* Draw centre line.
-     (bottom half) */
-  pl_fline_r (ch->lp,
-            box_centre, bottom_whisker,
-            box_centre, box_bottom);
-
-  /* (top half) */
-  pl_fline_r (ch->lp,
-            box_centre, top_whisker,
-            box_centre, box_top);
-
-  outliers = box_whisker_outliers (bw);
-  for (ll = ll_head (outliers);
-       ll != ll_null (outliers); ll = ll_next (ll))
-    {
-      const struct outlier *outlier = ll_data (ll, struct outlier, ll);
-      draw_case (ch, box_centre, outlier);
-    }
-
-  /* Draw  tick  mark on x axis */
-  draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, "%s", name);
-
-  pl_restorestate_r(ch->lp);
-}
-
-void
-boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
-{
-  double y_tick;
-  double d;
-
-  if ( !ch )
-     return ;
-
-  ch->y_max  = y_max;
-  ch->y_min  = y_min;
-
-  y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
-
-  ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
-
-  ch->y_max = ( floor( ch->y_max  / y_tick ) + 1.0  ) * y_tick;
-
-  ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
-    / fabs(ch->y_max - ch->y_min) ;
-
-  /* Move to data bottom-left */
-  pl_move_r(ch->lp,
-           ch->data_left, ch->data_bottom);
-
-  for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
-    {
-      draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
-    }
-}
diff --git a/src/output/charts/box-whisker.h b/src/output/charts/box-whisker.h
deleted file mode 100644 (file)
index 7b2c4b8..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef BOX_WHISKER_H
-#define BOX_WHISKER_H
-
-struct chart ;
-struct box_whisker;
-
-void boxplot_draw_boxplot (struct chart *ch,
-                          double box_centre,
-                          double box_width,
-                          const struct box_whisker *w,
-                          const char *name);
-
-
-void boxplot_draw_yscale (struct chart *ch , double y_max, double y_min);
-
-#endif
diff --git a/src/output/charts/boxplot.c b/src/output/charts/boxplot.c
new file mode 100644 (file)
index 0000000..015385e
--- /dev/null
@@ -0,0 +1,263 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+
+#include <config.h>
+
+#include <output/charts/boxplot.h>
+
+#include <math.h>
+#include <assert.h>
+#include <cairo/cairo.h>
+
+#include <libpspp/cast.h>
+#include <libpspp/misc.h>
+#include <math/chart-geometry.h>
+#include <math/box-whisker.h>
+#include <output/chart.h>
+#include <output/chart-provider.h>
+#include <output/charts/plot-chart.h>
+
+/* Draw a box-and-whiskers plot
+*/
+
+struct box
+  {
+    struct box_whisker *bw;
+    char *label;
+  };
+
+struct boxplot
+  {
+    struct chart chart;
+    double y_min;
+    double y_max;
+    char *title;
+    struct box *boxes;
+    size_t n_boxes, boxes_allocated;
+  };
+
+static const struct chart_class boxplot_chart_class;
+
+struct boxplot *
+boxplot_create (double y_min, double y_max, const char *title)
+{
+  struct boxplot *boxplot = xmalloc (sizeof *boxplot);
+  chart_init (&boxplot->chart, &boxplot_chart_class);
+  boxplot->y_min = y_min;
+  boxplot->y_max = y_max;
+  boxplot->title = xstrdup (title);
+  boxplot->boxes = NULL;
+  boxplot->n_boxes = boxplot->boxes_allocated = 0;
+  return boxplot;
+}
+
+void
+boxplot_add_box (struct boxplot *boxplot,
+                 struct box_whisker *bw, const char *label)
+{
+  struct box *box;
+  if (boxplot->n_boxes >= boxplot->boxes_allocated)
+    boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated,
+                                 sizeof *boxplot->boxes);
+  box = &boxplot->boxes[boxplot->n_boxes++];
+  box->bw = bw;
+  box->label = xstrdup (label);
+}
+
+struct chart *
+boxplot_get_chart (struct boxplot *boxplot)
+{
+  return &boxplot->chart;
+}
+
+/* Draw an OUTLIER on the plot CH
+ * at CENTRELINE
+ */
+static void
+draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline,
+          const struct outlier *outlier)
+{
+  double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale;
+  chart_draw_marker (cr, centreline, y,
+                     outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE,
+                     20);
+
+  cairo_move_to (cr, centreline + 10, y);
+  chart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
+}
+
+static void
+boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom,
+                  double box_centre,
+                  double box_width,
+                  const struct box_whisker *bw,
+                  const char *name)
+{
+  double whisker[2];
+  double hinge[3];
+  struct ll *ll;
+
+  const struct ll_list *outliers;
+
+  const double box_left = box_centre - box_width / 2.0;
+
+  const double box_right = box_centre + box_width / 2.0;
+
+  double box_bottom ;
+  double box_top ;
+  double bottom_whisker ;
+  double top_whisker ;
+
+  box_whisker_whiskers (bw, whisker);
+  box_whisker_hinges (bw, hinge);
+
+  box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
+
+  box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
+
+  bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
+    geom->ordinate_scale;
+
+  top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
+
+  /* Draw the box */
+  cairo_rectangle (cr,
+                   box_left,
+                   box_bottom,
+                   box_right - box_left,
+                   box_top - box_bottom);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        geom->fill_colour.red / 255.0,
+                        geom->fill_colour.green / 255.0,
+                        geom->fill_colour.blue / 255.0);
+  cairo_fill (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
+
+  /* Draw the median */
+  cairo_save (cr);
+  cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
+  cairo_move_to (cr,
+                 box_left,
+                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  cairo_line_to (cr,
+                 box_right,
+                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  cairo_stroke (cr);
+  cairo_restore (cr);
+
+  /* Draw the bottom whisker */
+  cairo_move_to (cr, box_left, bottom_whisker);
+  cairo_line_to (cr, box_right, bottom_whisker);
+  cairo_stroke (cr);
+
+  /* Draw top whisker */
+  cairo_move_to (cr, box_left, top_whisker);
+  cairo_line_to (cr, box_right, top_whisker);
+  cairo_stroke (cr);
+
+  /* Draw centre line.
+     (bottom half) */
+  cairo_move_to (cr, box_centre, bottom_whisker);
+  cairo_line_to (cr, box_centre, box_bottom);
+  cairo_stroke (cr);
+
+  /* (top half) */
+  cairo_move_to (cr, box_centre, top_whisker);
+  cairo_line_to (cr, box_centre, box_top);
+  cairo_stroke (cr);
+
+  outliers = box_whisker_outliers (bw);
+  for (ll = ll_head (outliers);
+       ll != ll_null (outliers); ll = ll_next (ll))
+    {
+      const struct outlier *outlier = ll_data (ll, struct outlier, ll);
+      draw_case (cr, geom, box_centre, outlier);
+    }
+
+  /* Draw  tick  mark on x axis */
+  draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
+}
+
+static void
+boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom,
+                     double y_max, double y_min)
+{
+  double y_tick;
+  double d;
+
+  geom->y_max = y_max;
+  geom->y_min = y_min;
+
+  y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
+
+  geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
+
+  geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
+
+  geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
+                          / fabs (geom->y_max - geom->y_min));
+
+  for (d = geom->y_min; d <= geom->y_max; d += y_tick)
+    draw_tick (cr, geom, TICK_ORDINATE,
+               (d - geom->y_min) * geom->ordinate_scale, "%g", d);
+}
+
+static void
+boxplot_chart_draw (const struct chart *chart, cairo_t *cr,
+                    struct chart_geometry *geom)
+{
+  const struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+  double box_width;
+  size_t i;
+
+  boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min);
+  chart_write_title (cr, geom, "%s", boxplot->title);
+
+  box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0;
+  for (i = 0; i < boxplot->n_boxes; i++)
+    {
+      const struct box *box = &boxplot->boxes[i];
+      const double box_centre = (i * 2 + 1) * box_width + geom->data_left;
+      boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);
+    }
+}
+
+static void
+boxplot_chart_destroy (struct chart *chart)
+{
+  struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+  size_t i;
+
+  free (boxplot->title);
+  for (i = 0; i < boxplot->n_boxes; i++)
+    {
+      struct box *box = &boxplot->boxes[i];
+      struct statistic *statistic = &box->bw->parent.parent;
+      statistic->destroy (statistic);
+      free (box->label);
+    }
+  free (boxplot->boxes);
+  free (boxplot);
+}
+
+static const struct chart_class boxplot_chart_class =
+  {
+    boxplot_chart_draw,
+    boxplot_chart_destroy
+  };
diff --git a/src/output/charts/boxplot.h b/src/output/charts/boxplot.h
new file mode 100644 (file)
index 0000000..1e9d007
--- /dev/null
@@ -0,0 +1,27 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_BOXPLOT_H
+#define OUTPUT_CHARTS_BOXPLOT_H 1
+
+struct box_whisker;
+
+struct boxplot *boxplot_create (double y_min, double y_max, const char *title);
+void boxplot_add_box (struct boxplot *,
+                      struct box_whisker *, const char *label);
+struct chart *boxplot_get_chart (struct boxplot *);
+
+#endif /* output/charts/boxplot.h */
index cb2f346b1b4134a0092cb322e3fe2a7a16177c55..eabcf516e8e1cdc7b84a148646c3aef38de956bc 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <config.h>
 
+#include <output/charts/cartesian.h>
+
+#include <cairo/cairo.h>
 #include <math.h>
 #include <assert.h>
 
 #include <output/chart.h>
-
+#include <output/chart-provider.h>
 #include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
 #include <libpspp/compiler.h>
 
+#include "xalloc.h"
 
 /* Start a new vector called NAME */
 void
-chart_vector_start (struct chart *ch, const char *name)
+chart_vector_start (cairo_t *cr, struct chart_geometry *geom, const char *name)
 {
-  if ( ! ch )
-    return ;
+  const struct chart_colour *colour;
 
-  pl_savestate_r (ch->lp);
+  cairo_save (cr);
 
-  pl_colorname_r (ch->lp, data_colour [ch->n_datasets % N_CHART_COLOURS]);
+  colour = &data_colour[geom->n_datasets % N_CHART_COLOURS];
+  cairo_set_source_rgb (cr,
+                        colour->red / 255.0,
+                        colour->green / 255.0,
+                        colour->blue / 255.0);
 
-  ch->n_datasets++;
-  ch->dataset = xrealloc (ch->dataset, ch->n_datasets * sizeof (*ch->dataset));
+  geom->n_datasets++;
+  geom->dataset = xrealloc (geom->dataset,
+                            geom->n_datasets * sizeof (*geom->dataset));
 
-  ch->dataset[ch->n_datasets - 1] = strdup (name);
+  geom->dataset[geom->n_datasets - 1] = strdup (name);
 }
 
 /* Plot a data point */
 void
-chart_datum (struct chart *ch, int dataset UNUSED, double x, double y)
+chart_datum (cairo_t *cr, const struct chart_geometry *geom,
+             int dataset UNUSED, double x, double y)
 {
-  if ( ! ch )
-    return ;
-
-  {
-    const double x_pos =
-      (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
+  double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
 
-    const double y_pos =
-      (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
-    pl_savestate_r(ch->lp);
-
-    pl_fmarker_r(ch->lp, x_pos, y_pos, 6, 15);
-
-    pl_restorestate_r(ch->lp);
-  }
+  chart_draw_marker (cr, x_pos, y_pos, MARKER_SQUARE, 15);
 }
 
 void
-chart_vector_end (struct chart *ch)
+chart_vector_end (cairo_t *cr, struct chart_geometry *geom)
 {
-  pl_endpath_r (ch->lp);
-  pl_colorname_r (ch->lp, "black");
-  ch->in_path = false;
-  pl_restorestate_r (ch->lp);
+  cairo_stroke (cr);
+  cairo_restore (cr);
+  geom->in_path = false;
 }
 
 /* Plot a data point */
 void
-chart_vector (struct chart *ch, double x, double y)
+chart_vector (cairo_t *cr, struct chart_geometry *geom, double x, double y)
 {
-  if ( ! ch )
-    return ;
-
-  {
-    const double x_pos =
-      (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
-    const double y_pos =
-      (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
-    if ( ch->in_path)
-      pl_fcont_r (ch->lp, x_pos, y_pos);
-    else
-      {
-       pl_fmove_r (ch->lp, x_pos, y_pos);
-       ch->in_path = true;
-      }
-  }
+  const double x_pos =
+    (x - geom->x_min) * geom->abscissa_scale + geom->data_left ;
+
+  const double y_pos =
+    (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ;
+
+  if (geom->in_path)
+    cairo_line_to (cr, x_pos, y_pos);
+  else
+    {
+      cairo_move_to (cr, x_pos, y_pos);
+      geom->in_path = true;
+    }
 }
 
 
@@ -107,20 +97,17 @@ chart_vector (struct chart *ch, double x, double y)
    y axis otherwise the x axis
 */
 void
-chart_line (struct chart *ch, double slope, double intercept,
+chart_line(cairo_t *cr, const struct chart_geometry *geom,
+           double slope, double intercept,
           double limit1, double limit2, enum CHART_DIM lim_dim)
 {
   double x1, y1;
-  double x2, y2 ;
-
-  if ( ! ch )
-    return ;
-
+  double x2, y2;
 
   if ( lim_dim == CHART_DIM_Y )
     {
-      x1 = ( limit1 - intercept ) / slope ;
-      x2 = ( limit2 - intercept ) / slope ;
+      x1 = ( limit1 - intercept ) / slope;
+      x2 = ( limit2 - intercept ) / slope;
       y1 = limit1;
       y2 = limit2;
     }
@@ -132,14 +119,12 @@ chart_line (struct chart *ch, double slope, double intercept,
       y2 = slope * x2 + intercept;
     }
 
-  y1 = (y1 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-  y2 = (y2 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-  x1 = (x1 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-  x2 = (x2 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_fline_r(ch->lp, x1, y1, x2, y2);
+  y1 = (y1 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  y2 = (y2 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  x1 = (x1 - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  x2 = (x2 - geom->x_min) * geom->abscissa_scale + geom->data_left;
 
-  pl_restorestate_r(ch->lp);
+  cairo_move_to (cr, x1, y1);
+  cairo_line_to (cr, x2, y2);
+  cairo_stroke (cr);
 }
index 0874b9cc61d4d8f507188a00f07c44ca614176cf..3c21db6efc444c87cca867bbe184867dd1e707fe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,6 +19,9 @@
 #ifndef CARTESIAN_H
 #define CARTESIAN_H
 
+#include <cairo/cairo.h>
+#include <libpspp/compiler.h>
+#include <output/chart.h>
 
 enum CHART_DIM
   {
@@ -26,20 +29,24 @@ enum CHART_DIM
     CHART_DIM_Y
   };
 
+struct chart_geometry;
 
-void chart_vector_start (struct chart *ch, const char *name);
-void chart_vector (struct chart *ch, double x, double y);
-void chart_vector_end (struct chart *ch);
+void  chart_vector_start (cairo_t *, struct chart_geometry *,
+                          const char *name);
+void chart_vector_end (cairo_t *, struct chart_geometry *);
+void chart_vector (cairo_t *, struct chart_geometry *, double x, double y);
 
 /* Plot a data point */
-void chart_datum (struct chart *ch, int dataset UNUSED, double x, double y);
+void chart_datum(cairo_t *, const struct chart_geometry *,
+                 int dataset UNUSED, double x, double y);
 
 /* Draw a line with slope SLOPE and intercept INTERCEPT.
    between the points limit1 and limit2.
    If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
    y axis otherwise the x axis
 */
-void chart_line (struct chart *ch, double slope, double intercept,
+void chart_line(cairo_t *, const struct chart_geometry *,
+                double slope, double intercept,
                double limit1, double limit2, enum CHART_DIM lim_dim);
 
 
diff --git a/src/output/charts/dummy-chart.c b/src/output/charts/dummy-chart.c
deleted file mode 100644 (file)
index e00a41f..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2005 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-/* Stubs for plotting routines.
-   This module is linked only when charts are not supported */
-
-#include "config.h"
-#include <output/chart.h>
-#include <output/charts/box-whisker.h>
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-#include <output/charts/plot-hist.h>
-#include <output/charts/cartesian.h>
-#include <gsl/gsl_histogram.h>
-#include <libpspp/compiler.h>
-
-#ifndef NO_CHARTS
-#error This file should be used only when compiling without charts.
-#endif
-
-void
-chart_write_legend (struct chart *ch UNUSED)
-{
-}
-
-void
-chart_vector (struct chart *ch UNUSED, double x UNUSED, double y UNUSED)
-{
-}
-
-void
-chart_vector_end (struct chart *ch UNUSED)
-{
-}
-
-void
-chart_vector_start (struct chart *ch UNUSED, const char *name UNUSED)
-{
-}
-
-void
-chart_write_title (struct chart *chart UNUSED, const char *title UNUSED, ...)
-{
-}
-
-
-void
-chart_write_xscale (struct chart *ch UNUSED,
-                   double min UNUSED, double max UNUSED, int ticks UNUSED)
-{
-}
-
-
-void
-chart_write_yscale (struct chart *ch UNUSED UNUSED,
-                   double smin UNUSED, double smax UNUSED, int ticks UNUSED)
-{
-}
-
-
-void
-chart_write_xlabel (struct chart *ch UNUSED, const char *label UNUSED)
-{
-}
-
-void
-chart_write_ylabel (struct chart *ch UNUSED, const char *label UNUSED)
-{
-}
-
-
-void
-chart_line (struct chart *ch UNUSED,
-           double slope UNUSED, double intercept UNUSED,
-           double limit1 UNUSED, double limit2 UNUSED,
-           enum CHART_DIM lim_dim UNUSED)
-{
-}
-
-
-void
-chart_datum (struct chart *ch UNUSED, int dataset UNUSED UNUSED,
-            double x UNUSED, double y UNUSED)
-{
-}
-
-void
-histogram_plot (const struct histogram *hist UNUSED,
-               const char *label UNUSED,
-               const struct moments1 *m UNUSED)
-{
-}
-
-void
-histogram_plot_n (const struct histogram *hist UNUSED,
-                 const char *label UNUSED,
-                 double n UNUSED, double mean UNUSED, double stddev UNUSED,
-                 bool show_normal UNUSED)
-{
-}
-
-
-void
-boxplot_draw_yscale (struct chart *ch UNUSED,
-                    double y_max UNUSED, double y_min UNUSED)
-{
-}
-
-void
-boxplot_draw_boxplot (struct chart *ch UNUSED,
-                     double box_centre UNUSED,
-                     double box_width UNUSED,
-                     const struct box_whisker *w UNUSED,
-                     const char *name UNUSED)
-{
-}
-
-
-
-
-void
-piechart_plot (const char *title UNUSED,
-              const struct slice *slices UNUSED, int n_slices UNUSED)
-{
-}
diff --git a/src/output/charts/np-plot.c b/src/output/charts/np-plot.c
new file mode 100644 (file)
index 0000000..c077b87
--- /dev/null
@@ -0,0 +1,196 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/np-plot.h>
+
+#include <gsl/gsl_cdf.h>
+
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <libpspp/cast.h>
+#include <libpspp/message.h>
+#include <math/np.h>
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+
+#include "gl/minmax.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* An NP or DNP plot. */
+struct np_plot_chart
+  {
+    struct chart chart;
+    char *label;
+    struct casereader *data;
+
+    /* Copied directly from struct np. */
+    double y_min, y_max;
+    double dns_min, dns_max;
+
+    /* Calculated. */
+    double slope, intercept;
+    double y_first, y_last;
+    double x_lower, x_upper;
+    double slack;
+  };
+
+static const struct chart_class np_plot_chart_class;
+static const struct chart_class dnp_plot_chart_class;
+
+static struct chart *
+make_np_plot (const struct chart_class *class,
+              const struct np *np, const struct casereader *reader,
+              const char *label)
+{
+  struct np_plot_chart *npp;
+
+  if (np->n < 1.0)
+    return NULL;
+
+  npp = xmalloc (sizeof *npp);
+  chart_init (&npp->chart, class);
+  npp->label = xstrdup (label);
+  npp->data = casereader_clone (reader);
+  npp->y_min = np->y_min;
+  npp->y_max = np->y_max;
+  npp->dns_min = np->dns_min;
+  npp->dns_max = np->dns_max;
+
+  /* Slope and intercept of the ideal normal probability line. */
+  npp->slope = 1.0 / np->stddev;
+  npp->intercept = -np->mean / np->stddev;
+
+  npp->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
+  npp->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
+
+  /* Need to make sure that both the scatter plot and the ideal fit into the
+     plot. */
+  npp->x_lower = MIN (np->y_min, (npp->y_first - npp->intercept) / npp->slope);
+  npp->x_upper = MAX (np->y_max, (npp->y_last  - npp->intercept) / npp->slope);
+  npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
+
+  return &npp->chart;
+}
+
+/* Creates and returns a normal probability plot corresponding to
+   the calculations in NP and the data in READER, and label the
+   plot with LABEL.  The data in READER must have Y-values in
+   value index NP_IDX_Y and NS-values in value index NP_IDX_NS.
+
+   Returns a null pointer if the data set is empty.
+
+   The caller retains ownership of NP and READER. */
+struct chart *
+np_plot_create (const struct np *np, const struct casereader *reader,
+                const char *label)
+{
+  return make_np_plot (&np_plot_chart_class, np, reader, label);
+}
+
+/* Creates and returns a detrended normal probability plot
+   corresponding to the calculations in NP and the data in
+   READER, and label the plot with LABEL.  The data in READER
+   must have Y-values in value index NP_IDX_Y and DNS-values in
+   value index NP_IDX_DNS.
+
+   Returns a null pointer if the data set is empty.
+
+   The caller retains ownership of NP and READER. */
+struct chart *
+dnp_plot_create (const struct np *np, const struct casereader *reader,
+                 const char *label)
+{
+  return make_np_plot (&dnp_plot_chart_class, np, reader, label);
+}
+
+static void
+np_plot_chart_draw (const struct chart *chart, cairo_t *cr,
+                    struct chart_geometry *geom)
+{
+  const struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart,
+                                             chart);
+  struct casereader *data;
+  struct ccase *c;
+
+  chart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
+  chart_write_xlabel (cr, geom, _("Observed Value"));
+  chart_write_ylabel (cr, geom, _("Expected Normal"));
+  chart_write_xscale (cr, geom,
+                      npp->x_lower - npp->slack,
+                      npp->x_upper + npp->slack, 5);
+  chart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
+
+  data = casereader_clone (npp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (cr, geom, 0,
+                 case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_NS)->f);
+  casereader_destroy (data);
+
+  chart_line (cr, geom, npp->slope, npp->intercept,
+              npp->y_first, npp->y_last, CHART_DIM_Y);
+}
+
+static void
+dnp_plot_chart_draw (const struct chart *chart, cairo_t *cr,
+                     struct chart_geometry *geom)
+{
+  const struct np_plot_chart *dnpp = UP_CAST (chart, struct np_plot_chart,
+                                              chart);
+  struct casereader *data;
+  struct ccase *c;
+
+  chart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
+                     dnpp->label);
+  chart_write_xlabel (cr, geom, _("Observed Value"));
+  chart_write_ylabel (cr, geom, _("Dev from Normal"));
+  chart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
+  chart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
+
+  data = casereader_clone (dnpp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_DNS)->f);
+  casereader_destroy (data);
+
+  chart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
+}
+
+static void
+np_plot_chart_destroy (struct chart *chart)
+{
+  struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart, chart);
+  casereader_destroy (npp->data);
+  free (npp->label);
+  free (npp);
+}
+
+static const struct chart_class np_plot_chart_class =
+  {
+    np_plot_chart_draw,
+    np_plot_chart_destroy
+  };
+
+static const struct chart_class dnp_plot_chart_class =
+  {
+    dnp_plot_chart_draw,
+    np_plot_chart_destroy
+  };
diff --git a/src/output/charts/np-plot.h b/src/output/charts/np-plot.h
new file mode 100644 (file)
index 0000000..c974235
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_NP_PLOT_H
+#define OUTPUT_CHARTS_NP_PLOT_H 1
+
+struct casereader;
+struct np;
+
+struct chart *np_plot_create (const struct np *, const struct casereader *,
+                              const char *label);
+struct chart *dnp_plot_create (const struct np *, const struct casereader *,
+                               const char *label);
+
+#endif /* output/charts/np-plot.h */
index d7db80e921a6f573991ed0be14e1eaf583e8527d..935c6eb0fed9784024c54e571e53936e07c09c7b 100644 (file)
 
 #include <config.h>
 
-#include <float.h>
+#include <output/charts/piechart.h>
+
 #include <assert.h>
+#include <float.h>
+#include <gsl/gsl_math.h>
 #include <math.h>
 #include <stdio.h>
 
-
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-
-#include <output/chart.h>
-#include <libpspp/str.h>
 #include <data/value-labels.h>
+#include <libpspp/cast.h>
+#include <libpspp/str.h>
+#include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
 
 #include "minmax.h"
 
+struct piechart
+  {
+    struct chart chart;
+    char *title;
+    struct slice *slices;
+    int n_slices;
+  };
 
-/* Pie charts of course need to know Pi :) */
-#ifndef M_PI
-#define M_PI ( 22.0 / 7.0 )
-#endif
-
-
+static const struct chart_class piechart_class;
 
 /* Draw a single slice of the pie */
 static void
-draw_segment(struct chart *ch,
+draw_segment(cairo_t *,
             double centre_x, double centre_y,
             double radius,
             double start_angle, double segment_angle,
-            const char *colour) ;
+            const struct chart_colour *) ;
 
 
 
-/* Draw a piechart */
-void
-piechart_plot(const char *title, const struct slice *slices, int n_slices)
+/* Creates and returns a chart that will render a piechart with
+   the given TITLE and the N_SLICES described in SLICES. */
+struct chart *
+piechart_create (const char *title, const struct slice *slices, int n_slices)
 {
+  struct piechart *pie;
   int i;
-  double total_magnetude=0;
-
-  struct chart *ch;
 
-  double left_label;
-  double right_label;
+  pie = xmalloc (sizeof *pie);
+  chart_init (&pie->chart, &piechart_class);
+  pie->title = xstrdup (title);
+  pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
+  for (i = 0; i < n_slices; i++)
+    {
+      const struct slice *src = &slices[i];
+      struct slice *dst = &pie->slices[i];
 
-  double centre_x;
-  double centre_y;
+      ds_init_string (&dst->label, &src->label);
+      dst->magnitude = src->magnitude;
+    }
+  pie->n_slices = n_slices;
+  return &pie->chart;
+}
 
+static void
+piechart_draw (const struct chart *chart, cairo_t *cr,
+               struct chart_geometry *geom)
+{
+  const struct piechart *pie = UP_CAST (chart, struct piechart, chart);
+  double total_magnitude;
+  double left_label, right_label;
+  double centre_x, centre_y;
   double radius;
+  double angle;
+  int i;
 
-  ch = chart_create ();
-  if (ch == NULL)
-    return;
+  centre_x = (geom->data_right + geom->data_left) / 2.0 ;
+  centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
 
-  left_label = ch->data_left + (ch->data_right - ch->data_left)/10.0;
-  right_label = ch->data_right - (ch->data_right - ch->data_left)/10.0;
-  centre_x = (ch->data_right + ch->data_left ) / 2.0;
-  centre_y = (ch->data_top + ch->data_bottom ) / 2.0;
-  radius = MIN (5.0 / 12.0 * (ch->data_top - ch->data_bottom),
-                1.0 / 4.0 * (ch->data_right - ch->data_left));
+  left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
+  right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
 
-  chart_write_title(ch, "%s", title);
+  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+                1.0 / 4.0 * (geom->data_right - geom->data_left));
 
-  for (i = 0 ; i < n_slices ; ++i )
-    total_magnetude += slices[i].magnetude;
+  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+                1.0 / 4.0 * (geom->data_right - geom->data_left));
 
-  for (i = 0 ; i < n_slices ; ++i )
-    {
-      static double angle=0.0;
+  chart_write_title (cr, geom, "%s", pie->title);
 
+  total_magnitude = 0.0;
+  for (i = 0; i < pie->n_slices; i++)
+    total_magnitude += pie->slices[i].magnitude;
+
+  angle = 0.0;
+  for (i = 0; i < pie->n_slices ; ++i )
+    {
       const double segment_angle =
-       slices[i].magnetude / total_magnetude * 2 * M_PI ;
+       pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
 
       const double label_x = centre_x -
        radius * sin(angle + segment_angle/2.0);
@@ -97,120 +120,78 @@ piechart_plot(const char *title, const struct slice *slices, int n_slices)
        radius * cos(angle + segment_angle/2.0);
 
       /* Fill the segment */
-      draw_segment(ch,
-                  centre_x, centre_y, radius,
-                  angle, segment_angle,
-                  data_colour[i % N_CHART_COLOURS]);
+      draw_segment (cr,
+                    centre_x, centre_y, radius,
+                    angle, segment_angle,
+                    &data_colour[i % N_CHART_COLOURS]);
 
       /* Now add the labels */
       if ( label_x < centre_x )
        {
-         pl_line_r(ch->lp, label_x, label_y,
-                   left_label, label_y );
-         pl_moverel_r(ch->lp,0,5);
-         pl_alabel_r (ch->lp, 0, 0, ds_cstr (&slices[i].label));
+          cairo_move_to (cr, label_x, label_y);
+          cairo_line_to (cr, left_label, label_y);
+          cairo_stroke (cr);
+         cairo_move_to (cr, left_label, label_y + 5);
+         chart_label (cr, 'l', 'x', geom->font_size,
+                       ds_cstr (&pie->slices[i].label));
        }
       else
        {
-         pl_line_r(ch->lp,
-                   label_x, label_y,
-                   right_label, label_y
-                   );
-         pl_moverel_r(ch->lp,0,5);
-         pl_alabel_r (ch->lp, 'r', 0, ds_cstr (&slices[i].label));
+         cairo_move_to (cr, label_x, label_y);
+          cairo_line_to (cr, right_label, label_y);
+          cairo_stroke (cr);
+         cairo_move_to (cr, right_label, label_y + 5);
+         chart_label (cr, 'r', 'x', geom->font_size,
+                       ds_cstr (&pie->slices[i].label));
        }
 
       angle += segment_angle;
-
     }
 
   /* Draw an outline to the pie */
-  pl_filltype_r(ch->lp,0);
-  pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
-
-  chart_submit(ch);
+  cairo_arc (cr, centre_x, centre_y, radius, 0, 2 * M_PI);
+  cairo_stroke (cr);
 }
 
+/* Draw a single slice of the pie */
 static void
-fill_segment(struct chart *ch,
-            double x0, double y0,
-            double radius,
-            double start_angle, double segment_angle) ;
-
-
-/* Fill a segment with the current fill colour */
-static void
-fill_segment(struct chart *ch,
+draw_segment(cairo_t *cr,
             double x0, double y0,
             double radius,
-            double start_angle, double segment_angle)
+            double start_angle, double segment_angle,
+            const struct chart_colour *colour)
 {
-
-  const double start_x  = x0 - radius * sin(start_angle);
-  const double start_y  = y0 + radius * cos(start_angle);
-
-  const double stop_x   =
-    x0 - radius * sin(start_angle + segment_angle);
-
-  const double stop_y   =
-    y0 + radius * cos(start_angle + segment_angle);
-
-  assert(segment_angle <= 2 * M_PI);
-  assert(segment_angle >= 0);
-
-  if ( segment_angle > M_PI )
-    {
-      /* Then we must draw it in two halves */
-      fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
-      fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
-                  segment_angle / 2.0 );
-    }
-  else
-    {
-      pl_move_r(ch->lp, x0, y0);
-
-      pl_cont_r(ch->lp, stop_x, stop_y);
-      pl_cont_r(ch->lp, start_x, start_y);
-
-      pl_arc_r(ch->lp,
-              x0, y0,
-              stop_x, stop_y,
-              start_x, start_y
-              );
-
-      pl_endpath_r(ch->lp);
-    }
+  cairo_move_to (cr, x0, y0);
+  cairo_arc (cr, x0, y0, radius, start_angle, start_angle + segment_angle);
+  cairo_line_to (cr, x0, y0);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        colour->red / 255.0,
+                        colour->green / 255.0,
+                        colour->blue / 255.0);
+  cairo_fill_preserve (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
 }
 
-
-
-/* Draw a single slice of the pie */
 static void
-draw_segment(struct chart *ch,
-            double x0, double y0,
-            double radius,
-            double start_angle, double segment_angle,
-            const char *colour)
+piechart_destroy (struct chart *chart)
 {
-  const double start_x  = x0 - radius * sin(start_angle);
-  const double start_y  = y0 + radius * cos(start_angle);
-
-  pl_savestate_r(ch->lp);
-
-  pl_savestate_r(ch->lp);
-  pl_colorname_r(ch->lp, colour);
-
-  pl_pentype_r(ch->lp,1);
-  pl_filltype_r(ch->lp,1);
-
-  fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
-  pl_restorestate_r(ch->lp);
-
-  /* Draw line dividing segments */
-  pl_pentype_r(ch->lp, 1);
-  pl_fline_r(ch->lp, x0, y0, start_x, start_y);
-
+  struct piechart *pie = UP_CAST (chart, struct piechart, chart);
+  int i;
 
-  pl_restorestate_r(ch->lp);
+  free (pie->title);
+  for (i = 0; i < pie->n_slices; i++)
+    {
+      struct slice *slice = &pie->slices[i];
+      ds_destroy (&slice->label);
+    }
+  free (pie->slices);
+  free (pie);
 }
 
+static const struct chart_class piechart_class =
+  {
+    piechart_draw,
+    piechart_destroy
+  };
index 96540401e7406f61e33259a7117e983d18e78fb9..39a0c2d5d28e7c05766b93e62a4f0d91b6f4e292 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 struct slice {
   struct string label;
-  double magnetude;
+  double magnitude;
 };
 
-/* Draw a piechart */
-void piechart_plot(const char *title,
-                  const struct slice *slices, int n_slices);
+struct chart *piechart_create (const char *title,
+                               const struct slice *, int n_slices);
 
 #endif
 
index 5641db1213be2cd070d66cffb7daf479823076f6..cda6d1ec83ce5f431d6e75f93e3a607005308bc3 100644 (file)
 
 #include <config.h>
 
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
-
 #include <output/charts/plot-chart.h>
 
-#include <math/chart-geometry.h>
-
-
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
 
-#include <libpspp/str.h>
 #include <libpspp/assertion.h>
+#include <libpspp/str.h>
+#include <math/chart-geometry.h>
+#include <output/chart-provider.h>
 #include <output/manager.h>
 #include <output/output.h>
 
 #include "xalloc.h"
 
-const char *const data_colour[N_CHART_COLOURS] =
+#if ! PANGO_VERSION_CHECK (2, 22, 0)
+int pango_layout_get_baseline (PangoLayout    *layout);
+
+/* Shamelessly copied from the pango source */
+int
+pango_layout_get_baseline (PangoLayout    *layout)
+{
+  int baseline;
+
+  /* XXX this is so inefficient */
+  PangoLayoutIter *iter = pango_layout_get_iter (layout);
+  baseline = pango_layout_iter_get_baseline (iter);
+  pango_layout_iter_free (iter);
+
+  return baseline;
+}
+#endif
+
+
+
+const struct chart_colour data_colour[N_CHART_COLOURS] =
   {
-    "brown",
-    "red",
-    "orange",
-    "yellow",
-    "green",
-    "blue",
-    "violet",
-    "grey",
-    "pink"
+    { 165, 42, 42 },            /* brown */
+    { 255, 0, 0 },              /* red */
+    { 255, 165, 0 },            /* orange */
+    { 255, 255, 0 },            /* yellow */
+    { 0, 255, 0 },              /* green */
+    { 0, 0, 255 },              /* blue */
+    { 238, 130, 238 },          /* violet */
+    { 190, 190, 190 },          /* grey */
+    { 255, 192, 203 },          /* pink */
   };
 
+void
+chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker,
+                   double size)
+{
+  cairo_save (cr);
+  cairo_translate (cr, x, y);
+  cairo_scale (cr, size / 2.0, size / 2.0);
+  cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
+  switch (marker)
+    {
+    case MARKER_CIRCLE:
+      cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
+      cairo_stroke (cr);
+      break;
+
+    case MARKER_ASTERISK:
+      cairo_move_to (cr, 0, -1.0); /* | */
+      cairo_line_to (cr, 0, 1.0);
+      cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
+      cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
+      cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
+      cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
+      cairo_stroke (cr);
+      break;
+
+    case MARKER_SQUARE:
+      cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
+      cairo_stroke (cr);
+      break;
+    }
+  cairo_restore (cr);
+}
 
+void
+chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size,
+             const char *string)
+{
+  PangoFontDescription *desc;
+  PangoLayout *layout;
+  double x, y;
+
+  desc = pango_font_description_from_string ("sans serif");
+  if (desc == NULL)
+    {
+      cairo_new_path (cr);
+      return;
+    }
+  pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
+
+  cairo_save (cr);
+  cairo_get_current_point (cr, &x, &y);
+  cairo_translate (cr, x, y);
+  cairo_move_to (cr, 0, 0);
+  cairo_scale (cr, 1.0, -1.0);
+
+  layout = pango_cairo_create_layout (cr);
+  pango_layout_set_font_description (layout, desc);
+  pango_layout_set_text (layout, string, -1);
+  if (horz_justify != 'l')
+    {
+      int width_pango;
+      double width;
+
+      pango_layout_get_size (layout, &width_pango, NULL);
+      width = (double) width_pango / PANGO_SCALE;
+      if (horz_justify == 'r')
+        cairo_rel_move_to (cr, -width, 0);
+      else
+        cairo_rel_move_to (cr, -width / 2.0, 0);
+    }
+  if (vert_justify == 'x')
+    {
+      int baseline_pango = pango_layout_get_baseline (layout);
+      double baseline = (double) baseline_pango / PANGO_SCALE;
+      cairo_rel_move_to (cr, 0, -baseline);
+    }
+  else if (vert_justify != 't')
+    {
+      int height_pango;
+      double height;
+
+      pango_layout_get_size (layout, NULL, &height_pango);
+      height = (double) height_pango / PANGO_SCALE;
+      if (vert_justify == 'b')
+        cairo_rel_move_to (cr, 0, -height);
+      else if (vert_justify == 'c')
+        cairo_rel_move_to (cr, 0, -height / 2.0);
+    }
+  pango_cairo_show_layout (cr, layout);
+  g_object_unref (layout);
+
+  cairo_restore (cr);
+
+  cairo_new_path (cr);
+
+  pango_font_description_free (desc);
+}
 
 /* Draw a tick mark at position
    If label is non zero, then print it at the tick mark
 */
 void
-draw_tick(struct chart *chart,
-         enum tick_orientation orientation,
-         double position,
-         const char *label, ...)
+draw_tick (cairo_t *cr, const struct chart_geometry *geom,
+           enum tick_orientation orientation,
+           double position,
+           const char *label, ...)
 {
   const int tickSize = 10;
+  double x, y;
 
-  assert(chart);
-
-  pl_savestate_r(chart->lp);
+  cairo_move_to (cr, geom->data_left, geom->data_bottom);
 
-  pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
-
-  if ( orientation == TICK_ABSCISSA )
-    pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
-  else if (orientation == TICK_ORDINATE )
-      pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
+  if (orientation == TICK_ABSCISSA)
+    {
+      cairo_rel_move_to (cr, position, 0);
+      cairo_rel_line_to (cr, 0, -tickSize);
+    }
+  else if (orientation == TICK_ORDINATE)
+    {
+      cairo_rel_move_to (cr, 0, position);
+      cairo_rel_line_to (cr, -tickSize, 0);
+    }
   else
     NOT_REACHED ();
+  cairo_get_current_point (cr, &x, &y);
 
-  if ( label ) {
-    char buf[10];
-    va_list ap;
-    va_start(ap,label);
-    vsnprintf(buf,10,label,ap);
+  cairo_stroke (cr);
 
-    if ( orientation == TICK_ABSCISSA )
-      pl_alabel_r(chart->lp, 'c','t', buf);
-    else if (orientation == TICK_ORDINATE )
-      {
-       if ( fabs(position) < DBL_EPSILON )
-           pl_moverel_r(chart->lp, 0, 10);
-
-       pl_alabel_r(chart->lp, 'r','c', buf);
-      }
-
-    va_end(ap);
-  }
-
-  pl_restorestate_r(chart->lp);
+  if (label != NULL)
+    {
+      va_list ap;
+      char *s;
+
+      cairo_move_to (cr, x, y);
+
+      va_start (ap, label);
+      s = xvasprintf (label, ap);
+      if (orientation == TICK_ABSCISSA)
+        chart_label (cr, 'c', 't', geom->font_size, s);
+      else if (orientation == TICK_ORDINATE)
+        {
+          if (fabs (position) < DBL_EPSILON)
+           cairo_rel_move_to (cr, 0, 10);
+          chart_label (cr, 'r', 'c', geom->font_size, s);
+        }
+      free (s);
+      va_end (ap);
+    }
 }
 
 
 /* Write the title on a chart*/
 void
-chart_write_title(struct chart *chart, const char *title, ...)
+chart_write_title (cairo_t *cr, const struct chart_geometry *geom,
+                   const char *title, ...)
 {
   va_list ap;
-  char buf[100];
-
-  if ( ! chart )
-         return ;
+  char *s;
 
-  pl_savestate_r(chart->lp);
-  pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
-  pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
+  cairo_save (cr);
+  cairo_move_to (cr, geom->data_left, geom->title_bottom);
 
-  va_start(ap,title);
-  vsnprintf(buf,100,title,ap);
-  pl_alabel_r(chart->lp,0,0,buf);
-  va_end(ap);
+  va_start(ap, title);
+  s = xvasprintf (title, ap);
+  chart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
+  free (s);
+  va_end (ap);
 
-  pl_restorestate_r(chart->lp);
+  cairo_restore (cr);
 }
 
 
 /* Set the scale for the abscissa */
 void
-chart_write_xscale(struct chart *ch, double min, double max, int ticks)
+chart_write_xscale (cairo_t *cr, struct chart_geometry *geom,
+                    double min, double max, int ticks)
 {
   double x;
 
   const double tick_interval =
-    chart_rounded_tick(max - min) / (double) ticks);
+    chart_rounded_tick ((max - min) / (double) ticks);
 
-  assert ( ch );
-
-
-  ch->x_max = ceil( max / tick_interval ) * tick_interval ;
-  ch->x_min = floor ( min / tick_interval ) * tick_interval ;
-
-
-  ch->abscissa_scale = fabs(ch->data_right - ch->data_left) /
-    fabs(ch->x_max - ch->x_min);
-
-  for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
-    {
-      draw_tick (ch, TICK_ABSCISSA,
-                (x - ch->x_min) * ch->abscissa_scale, "%g", x);
-    }
+  geom->x_max = ceil (max / tick_interval) * tick_interval;
+  geom->x_min = floor (min / tick_interval) * tick_interval;
+  geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
+    fabs(geom->x_max - geom->x_min);
 
+  for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
+    draw_tick (cr, geom, TICK_ABSCISSA,
+               (x - geom->x_min) * geom->abscissa_scale, "%g", x);
 }
 
 
 /* Set the scale for the ordinate */
 void
-chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
+chart_write_yscale (cairo_t *cr, struct chart_geometry *geom,
+                    double smin, double smax, int ticks)
 {
   double y;
 
   const double tick_interval =
-    chart_rounded_tick( (smax - smin) / (double) ticks);
-
-  if ( !ch )
-         return;
+    chart_rounded_tick ((smax - smin) / (double) ticks);
 
-  ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ;
-  ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
+  geom->y_max = ceil (smax / tick_interval) * tick_interval;
+  geom->y_min = floor (smin / tick_interval) * tick_interval;
 
-  ch->ordinate_scale =
-    fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
+  geom->ordinate_scale =
+    (fabs (geom->data_top - geom->data_bottom)
+     / fabs (geom->y_max - geom->y_min));
 
-  for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
-    {
-    draw_tick (ch, TICK_ORDINATE,
-              (y - ch->y_min) * ch->ordinate_scale, "%g", y);
-    }
+  for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
+    draw_tick (cr, geom, TICK_ORDINATE,
+              (y - geom->y_min) * geom->ordinate_scale, "%g", y);
 }
 
-
 /* Write the abscissa label */
 void
-chart_write_xlabel(struct chart *ch, const char *label)
+chart_write_xlabel (cairo_t *cr, const struct chart_geometry *geom,
+                    const char *label)
 {
-  if ( ! ch )
-    return ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
-  pl_alabel_r(ch->lp,0,'t',label);
-
-  pl_restorestate_r(ch->lp);
-
+  cairo_move_to (cr, geom->data_left, geom->abscissa_top);
+  chart_label (cr, 'l', 't', geom->font_size, label);
 }
 
-
-
 /* Write the ordinate label */
 void
-chart_write_ylabel(struct chart *ch, const char *label)
+chart_write_ylabel (cairo_t *cr, const struct chart_geometry *geom,
+                    const char *label)
 {
-  if ( ! ch )
-    return ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_move_r(ch->lp, ch->data_bottom, ch->ordinate_right);
-  pl_textangle_r(ch->lp, 90);
-  pl_alabel_r(ch->lp, 0, 0, label);
-
-  pl_restorestate_r(ch->lp);
+  cairo_save (cr);
+  cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right);
+  cairo_move_to (cr, 0, 0);
+  cairo_rotate (cr, M_PI / 2.0);
+  chart_label (cr, 'l', 'x', geom->font_size, label);
+  cairo_restore (cr);
 }
 
 
 void
-chart_write_legend (struct chart *ch)
+chart_write_legend (cairo_t *cr, const struct chart_geometry *geom)
 {
   int i;
-  const int vstep = ch->font_size * 2;
+  const int vstep = geom->font_size * 2;
   const int xpad = 10;
   const int ypad = 10;
   const int swatch = 20;
-  const int legend_top = ch->data_top;
+  const int legend_top = geom->data_top;
   const int legend_bottom = legend_top -
-    (vstep * ch->n_datasets + 2 * ypad );
-
-  if ( ! ch )
-    return ;
+    (vstep * geom->n_datasets + 2 * ypad );
 
-  pl_savestate_r (ch->lp);
+  cairo_save (cr);
 
-  pl_box_r (ch->lp, ch->legend_left, legend_top,
-           ch->legend_right - xpad, legend_bottom);
+  cairo_rectangle (cr, geom->legend_left, legend_top,
+                   geom->legend_right - xpad - geom->legend_left,
+                   legend_bottom - legend_top);
+  cairo_stroke (cr);
 
-  for (i = 0 ; i < ch->n_datasets ; ++i )
+  for (i = 0 ; i < geom->n_datasets ; ++i )
     {
-      const int ypos = vstep * (i + 1);
-      const int xpos = ch->legend_left + xpad;
-      pl_move_r (ch->lp, xpos, legend_top - ypos);
-
-      pl_savestate_r(ch->lp);
-       pl_fillcolorname_r (ch->lp, data_colour [ i % N_CHART_COLOURS]);
-
-       pl_pentype_r (ch->lp, 1);
-       pl_filltype_r (ch->lp, 1);
-       pl_boxrel_r (ch->lp, 0, 0, swatch, swatch);
-
-
-      pl_moverel_r (ch->lp, swatch, 0);
-      pl_alabel_r (ch->lp, 0, 0, ch->dataset[i]);
-
-      pl_restorestate_r (ch->lp);
+      const int ypos = legend_top - vstep * (i + 1);
+      const int xpos = geom->legend_left + xpad;
+      const struct chart_colour *colour;
+
+      cairo_move_to (cr, xpos, ypos);
+
+      cairo_save (cr);
+      colour = &data_colour [ i % N_CHART_COLOURS];
+      cairo_set_source_rgb (cr,
+                            colour->red / 255.0,
+                            colour->green / 255.0,
+                            colour->blue / 255.0);
+      cairo_rectangle (cr, xpos, ypos, swatch, swatch);
+      cairo_fill_preserve (cr);
+      cairo_stroke (cr);
+      cairo_restore (cr);
+
+      cairo_move_to (cr, xpos + swatch * 1.5, ypos);
+      chart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
     }
 
-  pl_restorestate_r (ch->lp);
+  cairo_restore (cr);
 }
index cfbaa4df8ddc396ee4d2338dee87acfd349bf0a5..896b630b137e7245f679c15c5437e7b8a47168fe 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
+#ifndef PLOT_CHART_H
+#define PLOT_CHART_H
+
+#include <cairo/cairo.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 
 #include <math/chart-geometry.h>
 #include <output/chart.h>
+#include <output/chart-provider.h>
 
 #include <libpspp/compiler.h>
 #include <libpspp/str.h>
 #include <output/manager.h>
 #include <output/output.h>
 
-#include "xalloc.h"
-
-#ifndef PLOT_CHART_H
-#define PLOT_CHART_H
-
 #define N_CHART_COLOURS 9
-extern const char *const data_colour[];
+extern const struct chart_colour data_colour[];
 
 enum tick_orientation
   {
@@ -45,34 +45,54 @@ enum tick_orientation
     TICK_ORDINATE
   };
 
+struct chart_geometry;
+
+
+enum marker_type
+  {
+    MARKER_CIRCLE,              /* Hollow circle. */
+    MARKER_ASTERISK,            /* Asterisk (*). */
+    MARKER_SQUARE               /* Hollow square. */
+  };
+
+void chart_draw_marker (cairo_t *, double x, double y, enum marker_type,
+                        double size);
+
+void chart_label (cairo_t *, int horz_justify, int vert_justify,
+                  double font_size, const char *);
 
 /* Draw a tick mark at position
    If label is non zero, then print it at the tick mark
 */
-void draw_tick(struct chart *chart,
+void draw_tick(cairo_t *, const struct chart_geometry *,
          enum tick_orientation orientation,
          double position,
               const char *label, ...)
-  PRINTF_FORMAT (4, 5);
+  PRINTF_FORMAT (5, 6);
 
 
 /* Write the title on a chart*/
-void   chart_write_title(struct chart *chart, const char *title, ...)
-  PRINTF_FORMAT (2, 3);
+void   chart_write_title(cairo_t *, const struct chart_geometry *,
+                         const char *title, ...)
+  PRINTF_FORMAT (3, 4);
 
 
 /* Set the scale for the abscissa */
-void  chart_write_xscale(struct chart *ch, double min, double max, int ticks);
+void  chart_write_xscale(cairo_t *, struct chart_geometry *,
+                         double min, double max, int ticks);
 
 
 /* Set the scale for the ordinate */
-void  chart_write_yscale(struct chart *ch, double smin, double smax, int ticks);
+void  chart_write_yscale(cairo_t *, struct chart_geometry *,
+                         double smin, double smax, int ticks);
 
-void chart_write_xlabel(struct chart *ch, const char *label) ;
+void chart_write_xlabel(cairo_t *, const struct chart_geometry *,
+                        const char *label) ;
 
 /* Write the ordinate label */
-void  chart_write_ylabel(struct chart *ch, const char *label);
+void  chart_write_ylabel(cairo_t *, const struct chart_geometry *,
+                         const char *label);
 
-void chart_write_legend (struct chart *ch);
+void chart_write_legend (cairo_t *, const struct chart_geometry *);
 
 #endif
index fbf1925e635e165c3c096d119bace8123fa0d5bc..334818b4cad9428975f8c2e1623ea567ff57ac5c 100644 (file)
@@ -18,7 +18,6 @@
 #include <config.h>
 
 #include <stdio.h>
-#include <plot.h>
 #include <math.h>
 #include <gsl/gsl_histogram.h>
 #include <gsl/gsl_randist.h>
 
 #include <output/charts/plot-hist.h>
 #include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
 
 #include <data/variable.h>
+#include <libpspp/cast.h>
 #include <libpspp/hash.h>
 #include <output/chart.h>
 #include <math/histogram.h>
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+static const struct chart_class histogram_chart_class;
+
 /* Write the legend of the chart */
 static void
-histogram_write_legend (struct chart *ch, double n, double mean, double stddev)
+histogram_write_legend (cairo_t *cr, const struct chart_geometry *geom,
+                        double n, double mean, double stddev)
 {
-  char buf[100];
-
-  if (!ch)
-    return ;
-
-  pl_savestate_r (ch->lp);
+  double y = geom->data_bottom;
+  cairo_save (cr);
 
-  sprintf (buf, "N = %.2f", n);
-  pl_move_r (ch->lp, ch->legend_left, ch->data_bottom);
-  pl_alabel_r (ch->lp, 0, 'b', buf);
+  if (n != SYSMIS)
+    {
+      char *buf = xasprintf ("N = %.2f", n);
+      cairo_move_to (cr, geom->legend_left, y);
+      chart_label (cr, 'l', 'b', geom->font_size, buf);
+      y += geom->font_size * 1.5;
+      free (buf);
+    }
 
-  sprintf (buf, "Mean = %.1f", mean);
-  pl_fmove_r (ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
-  pl_alabel_r (ch->lp, 0, 'b', buf);
+  if (mean != SYSMIS)
+    {
+      char *buf = xasprintf ("Mean = %.1f", mean);
+      cairo_move_to (cr,geom->legend_left, y);
+      chart_label (cr, 'l', 'b', geom->font_size, buf);
+      y += geom->font_size * 1.5;
+      free (buf);
+    }
 
-  sprintf (buf, "Std. Dev = %.2f", stddev);
-  pl_fmove_r (ch->lp, ch->legend_left, ch->data_bottom + ch->font_size * 1.5 * 2);
-  pl_alabel_r (ch->lp, 0, 'b', buf);
+  if (stddev != SYSMIS)
+    {
+      char *buf = xasprintf ("Std. Dev = %.2f", stddev);
+      cairo_move_to (cr, geom->legend_left, y);
+      chart_label (cr, 'l', 'b', geom->font_size, buf);
+      free (buf);
+    }
 
-  pl_restorestate_r (ch->lp);
+  cairo_restore (cr);
 }
 
-static void hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar);
-
-
 static void
-hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar)
+hist_draw_bar (cairo_t *cr, const struct chart_geometry *geom,
+               const gsl_histogram *h, int bar)
 {
-  if (!ch)
-    return ;
-
-  {
-    double upper;
-    double lower;
-    double height;
-
-    const size_t bins = gsl_histogram_bins (hist->gsl_hist);
-    const double x_pos = (ch->data_right - ch->data_left) * bar / (double) bins ;
-    const double width = (ch->data_right - ch->data_left) / (double) bins ;
-
-    assert ( 0 == gsl_histogram_get_range (hist->gsl_hist, bar, &lower, &upper));
-
-    assert ( upper >= lower);
-
-    height = gsl_histogram_get (hist->gsl_hist, bar) *
-     (ch->data_top - ch->data_bottom) / gsl_histogram_max_val (hist->gsl_hist);
-
-    pl_savestate_r (ch->lp);
-    pl_move_r (ch->lp,ch->data_left, ch->data_bottom);
-    pl_fillcolorname_r (ch->lp, ch->fill_colour);
-    pl_filltype_r (ch->lp,1);
-
-
-    pl_fboxrel_r (ch->lp,
-                x_pos, 0,
-                x_pos + width, height);
-
-    pl_restorestate_r (ch->lp);
-
-    draw_tick (ch, TICK_ABSCISSA,
-               x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
-  }
+  double upper;
+  double lower;
+  double height;
+
+  const size_t bins = gsl_histogram_bins (h);
+  const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
+  const double width = (geom->data_right - geom->data_left) / (double) bins ;
+
+  assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
+
+  assert ( upper >= lower);
+
+  height = gsl_histogram_get (h, bar) *
+    (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
+
+  cairo_rectangle (cr, geom->data_left + x_pos, geom->data_bottom,
+                   width, height);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        geom->fill_colour.red / 255.0,
+                        geom->fill_colour.green / 255.0,
+                        geom->fill_colour.blue / 255.0);
+  cairo_fill_preserve (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
+
+  draw_tick (cr, geom, TICK_ABSCISSA,
+             x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
 }
 
-
-
-void
-histogram_plot (const struct histogram *hist,
-               const char *label,
-               const struct moments1 *m)
+struct histogram_chart
+  {
+    struct chart chart;
+    gsl_histogram *gsl_hist;
+    char *label;
+    double n;
+    double mean;
+    double stddev;
+    bool show_normal;
+  };
+
+/* Plots a histogram of the data in HIST with the given LABEL.
+   Labels the histogram with each of N, MEAN, and STDDEV that is
+   not SYSMIS.  If all three are not SYSMIS and SHOW_NORMAL is
+   true, also draws a normal curve on the histogram. */
+struct chart *
+histogram_chart_create (const struct histogram *hist, const char *label,
+                        double n, double mean, double stddev,
+                        bool show_normal)
 {
-  double mean, var, n;
-
-  moments1_calculate (m, &n, &mean, &var, NULL,  NULL);
-
-  histogram_plot_n (hist, label, n, mean, sqrt(var), m);
+  struct histogram_chart *h;
+
+  h = xmalloc (sizeof *h);
+  chart_init (&h->chart, &histogram_chart_class);
+  h->gsl_hist = hist->gsl_hist ? gsl_histogram_clone (hist->gsl_hist) : NULL;
+  h->label = xstrdup (label);
+  h->n = n;
+  h->mean = mean;
+  h->stddev = stddev;
+  h->show_normal = show_normal;
+  return &h->chart;
 }
 
-
-/* This function is deprecated.  Don't use it in new code */
-void
-histogram_plot_n (const struct histogram *hist,
-                 const char *label,
-                 double n, double mean, double stddev,
-                 bool show_normal)
+static void
+histogram_chart_draw (const struct chart *chart, cairo_t *cr,
+                      struct chart_geometry *geom)
 {
+  struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
   int i;
   int bins;
 
-  struct chart *ch = chart_create ();
-
-  chart_write_title (ch, _("HISTOGRAM"));
+  chart_write_title (cr, geom, _("HISTOGRAM"));
 
-  chart_write_ylabel (ch, _("Frequency"));
-  chart_write_xlabel (ch, label);
+  chart_write_ylabel (cr, geom, _("Frequency"));
+  chart_write_xlabel (cr, geom, h->label);
 
-  if ( ! hist ) /* If this happens, probably all values are SYSMIS */
+  if (h->gsl_hist == NULL)
     {
-      chart_submit (ch);
+      /* Probably all values are SYSMIS. */
       return;
     }
-  else
-    {
-      bins = gsl_histogram_bins (hist->gsl_hist);
-    }
 
-  chart_write_yscale (ch, 0, gsl_histogram_max_val (hist->gsl_hist), 5);
+  bins = gsl_histogram_bins (h->gsl_hist);
+
+  chart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
 
-  for ( i = 0 ; i < bins ; ++i )
-    hist_draw_bar (ch, hist, i);
+  for (i = 0; i < bins; i++)
+    hist_draw_bar (cr, geom, h->gsl_hist, i);
 
-  histogram_write_legend (ch, n, mean, stddev);
+  histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
 
-  if (show_normal)
+  if (h->show_normal
+      && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
     {
       /* Draw the normal curve */
+      double d;
+      double x_min, x_max, not_used;
+      double abscissa_scale;
+      double ordinate_scale;
+      double range;
 
-      double d ;
-      double x_min, x_max, not_used ;
-      double abscissa_scale ;
-      double ordinate_scale ;
-      double range ;
-
-      gsl_histogram_get_range (hist->gsl_hist, 0, &x_min, &not_used);
+      gsl_histogram_get_range (h->gsl_hist, 0, &x_min, &not_used);
       range = not_used - x_min;
-      gsl_histogram_get_range (hist->gsl_hist, bins - 1, &not_used, &x_max);
+      gsl_histogram_get_range (h->gsl_hist, bins - 1, &not_used, &x_max);
 
-      abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
-      ordinate_scale = (ch->data_top - ch->data_bottom) /
-       gsl_histogram_max_val (hist->gsl_hist) ;
+      abscissa_scale = (geom->data_right - geom->data_left) / (x_max - x_min);
+      ordinate_scale = (geom->data_top - geom->data_bottom) /
+       gsl_histogram_max_val (h->gsl_hist);
 
-      pl_move_r (ch->lp, ch->data_left, ch->data_bottom);
-      for ( d = ch->data_left;
-           d <= ch->data_right ;
-           d += (ch->data_right - ch->data_left) / 100.0)
+      cairo_move_to (cr, geom->data_left, geom->data_bottom);
+      for (d = geom->data_left;
+          d <= geom->data_right;
+          d += (geom->data_right - geom->data_left) / 100.0)
        {
-         const double x = (d - ch->data_left) / abscissa_scale + x_min ;
-         const double y = n * range *
-           gsl_ran_gaussian_pdf (x - mean, stddev);
+         const double x = (d - geom->data_left) / abscissa_scale + x_min;
+         const double y = h->n * range *
+           gsl_ran_gaussian_pdf (x - h->mean, h->stddev);
 
-         pl_fcont_r (ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
+          cairo_line_to (cr, d, geom->data_bottom  + y * ordinate_scale);
 
        }
-      pl_endpath_r (ch->lp);
+      cairo_stroke (cr);
     }
-
-  chart_submit (ch);
 }
 
 
+static void
+histogram_chart_destroy (struct chart *chart)
+{
+  struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
+  if (h->gsl_hist != NULL)
+    gsl_histogram_free (h->gsl_hist);
+  free (h->label);
+  free (h);
+}
+
+static const struct chart_class histogram_chart_class =
+  {
+    histogram_chart_draw,
+    histogram_chart_destroy
+  };
index 606206d5012c47d4e7b239e0a0230bf1788cea8e..1e5b59edb5d8458badf707b3f6c21a424afbc3c2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#ifndef PLOT_HIST_H
-#define PLOT_HIST_H
+#ifndef OUTPUT_PLOT_HIST_H
+#define OUTPUT_PLOT_HIST_H
 
 #include <stdbool.h>
 
 struct chart;
-struct moments1;
 struct histogram;
 
-/* Plot M onto histogram HIST and label it with LABEL */
-void histogram_plot (const struct histogram *hist,
-                    const char *label,  const struct moments1 *m);
-
-
-/* A wrapper aroud histogram_plot.
-   Don't use this function.  It's legacy only */
-void histogram_plot_n (const struct histogram *hist,
-                      const char *label,
-                      double n, double mean, double var,
-                      bool show_normal);
-
-
-#endif
+/* Creates and returns a new chart that depicts a histogram of
+   the data in HIST with the given LABEL.  Labels the histogram
+   with each of N, MEAN, and STDDEV that is not SYSMIS.  If all
+   three are not SYSMIS and SHOW_NORMAL is true, also draws a
+   normal curve on the histogram. */
+struct chart *histogram_chart_create (const struct histogram *hist,
+                                      const char *label,
+                                      double n, double mean, double stddev,
+                                      bool show_normal);
+
+#endif /* output/plot-hist.h */
diff --git a/src/output/charts/roc-chart.c b/src/output/charts/roc-chart.c
new file mode 100644 (file)
index 0000000..2094ede
--- /dev/null
@@ -0,0 +1,148 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/roc-chart.h>
+
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+#include <data/casereader.h>
+#include <language/stats/roc.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+struct roc_var
+  {
+    char *name;
+    struct casereader *cutpoint_reader;
+  };
+
+struct roc_chart
+  {
+    struct chart chart;
+    bool reference;
+    struct roc_var *vars;
+    size_t n_vars;
+    size_t allocated_vars;
+  };
+
+static const struct chart_class roc_chart_class;
+
+struct roc_chart *
+roc_chart_create (bool reference)
+{
+  struct roc_chart *rc = xmalloc (sizeof *rc);
+  chart_init (&rc->chart, &roc_chart_class);
+  rc->reference = reference;
+  rc->vars = NULL;
+  rc->n_vars = 0;
+  rc->allocated_vars = 0;
+  return rc;
+}
+
+void
+roc_chart_add_var (struct roc_chart *rc, const char *var_name,
+                   const struct casereader *cutpoint_reader)
+{
+  struct roc_var *rv;
+
+  if (rc->n_vars >= rc->allocated_vars)
+    rc->vars = x2nrealloc (rc->vars, &rc->allocated_vars, sizeof *rc->vars);
+
+  rv = &rc->vars[rc->n_vars++];
+  rv->name = xstrdup (var_name);
+  rv->cutpoint_reader = casereader_clone (cutpoint_reader);
+}
+
+struct chart *
+roc_chart_get_chart (struct roc_chart *rc)
+{
+  return &rc->chart;
+}
+
+static void
+roc_chart_draw (const struct chart *chart, cairo_t *cr,
+                struct chart_geometry *geom)
+{
+  const struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+  size_t i;
+
+  chart_write_title (cr, geom, _("ROC Curve"));
+  chart_write_xlabel (cr, geom, _("1 - Specificity"));
+  chart_write_ylabel (cr, geom, _("Sensitivity"));
+
+  chart_write_xscale (cr, geom, 0, 1, 5);
+  chart_write_yscale (cr, geom, 0, 1, 5);
+
+  if ( rc->reference )
+    {
+      chart_line (cr, geom, 1.0, 0,
+                 0.0, 1.0,
+                 CHART_DIM_X);
+    }
+
+  for (i = 0; i < rc->n_vars; ++i)
+    {
+      const struct roc_var *rv = &rc->vars[i];
+      struct casereader *r = casereader_clone (rv->cutpoint_reader);
+      struct ccase *cc;
+
+      chart_vector_start (cr, geom, rv->name);
+      for (; (cc = casereader_read (r)) != NULL; case_unref (cc))
+       {
+         double se = case_data_idx (cc, ROC_TP)->f;
+         double sp = case_data_idx (cc, ROC_TN)->f;
+
+         se /= case_data_idx (cc, ROC_FN)->f + case_data_idx (cc, ROC_TP)->f ;
+         sp /= case_data_idx (cc, ROC_TN)->f + case_data_idx (cc, ROC_FP)->f ;
+
+         chart_vector (cr, geom, 1 - sp, se);
+       }
+      chart_vector_end (cr, geom);
+      casereader_destroy (r);
+    }
+
+  chart_write_legend (cr, geom);
+}
+
+static void
+roc_chart_destroy (struct chart *chart)
+{
+  struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+  size_t i;
+
+  for (i = 0; i < rc->n_vars; i++)
+    {
+      struct roc_var *rv = &rc->vars[i];
+      free (rv->name);
+      casereader_destroy (rv->cutpoint_reader);
+    }
+  free (rc->vars);
+  free (rc);
+}
+
+static const struct chart_class roc_chart_class =
+  {
+    roc_chart_draw,
+    roc_chart_destroy
+  };
+
+
diff --git a/src/output/charts/roc-chart.h b/src/output/charts/roc-chart.h
new file mode 100644 (file)
index 0000000..dca8420
--- /dev/null
@@ -0,0 +1,29 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_ROC_CHART_H
+#define OUTPUT_CHARTS_ROC_CHART_H 1
+
+#include <stdbool.h>
+
+struct casereader;
+
+struct roc_chart *roc_chart_create (bool reference);
+void roc_chart_add_var (struct roc_chart *, const char *var_name,
+                        const struct casereader *cutpoint_reader);
+struct chart *roc_chart_get_chart (struct roc_chart *);
+
+#endif /* output/charts/roc-chart.h */
index 893c18527b7ff30e431762ede6658332e6fc5e46..dfa524a7cfd5f60cd6120023283d1106c635c35a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <data/file-name.h>
-#include "error.h"
-#include "output.h"
-#include "manager.h"
-#include "table.h"
+#include <output/chart-provider.h>
+#include <output/output.h>
+#include <output/manager.h>
+#include <output/table.h>
 #include <libpspp/version.h>
 
+#include "error.h"
 #include "xalloc.h"
 
 #include "gettext.h"
 static void escape_string (FILE *file,
                            const char *text, size_t length,
                            const char *space);
-static bool handle_option (struct outp_driver *this,
+static bool handle_option (void *this,
                            const char *key, const struct string *val);
 static void print_title_tag (FILE *file, const char *name,
                              const char *content);
 
 static bool
-html_open_driver (struct outp_driver *this, struct substring options)
+html_open_driver (const char *name, int types, struct substring options)
 {
+  struct outp_driver *this;
   struct html_driver_ext *x;
 
+  this = outp_allocate_driver (&html_class, name, types);
   this->ext = x = xmalloc (sizeof *x);
   x->file_name = xstrdup ("pspp.html");
   x->chart_file_name = xstrdup ("pspp-#.png");
   x->file = NULL;
-  x->chart_cnt = 0;
+  x->chart_cnt = 1;
 
-  outp_parse_options (options, handle_option, this);
+  outp_parse_options (name, options, handle_option, this);
 
   x->file = fn_open (x->file_name, "w");
   if (x->file == NULL)
@@ -89,10 +92,12 @@ html_open_driver (struct outp_driver *this, struct substring options)
   print_title_tag (x->file, "H1", outp_title);
   print_title_tag (x->file, "H2", outp_subtitle);
 
+  outp_register_driver (this);
   return true;
 
  error:
   this->class->close_driver (this);
+  outp_free_driver (this);
   return false;
 }
 
@@ -133,14 +138,6 @@ html_close_driver (struct outp_driver *this)
   return ok;
 }
 
-/* Link the image contained in FILE_NAME to the
-   HTML stream in FILE. */
-static void
-link_image (FILE *file, char *file_name)
-{
-  fprintf (file, "<IMG SRC=\"%s\"/>", file_name);
- }
-
 /* Generic option types. */
 enum
   {
@@ -157,9 +154,9 @@ static const struct outp_option option_tab[] =
   };
 
 static bool
-handle_option (struct outp_driver *this,
-               const char *key, const struct string *val)
+handle_option (void *this_, const char *key, const struct string *val)
 {
+  struct outp_driver *this = this_;
   struct html_driver_ext *x = this->ext;
   int subcat;
 
@@ -199,11 +196,21 @@ handle_option (struct outp_driver *this,
 
 static void output_tab_table (struct outp_driver *, struct tab_table *);
 
+static void
+html_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+  struct html_driver_ext *x = this->ext;
+  char *file_name;
+
+  file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
+  fprintf (x->file, "<IMG SRC=\"%s\"/>", file_name);
+  free (file_name);
+}
+
 static void
 html_submit (struct outp_driver *this, struct som_entity *s)
 {
   extern struct som_table_class tab_table_class;
-  struct html_driver_ext *x = this->ext;
 
   assert (s->class == &tab_table_class ) ;
 
@@ -212,9 +219,6 @@ html_submit (struct outp_driver *this, struct som_entity *s)
     case SOM_TABLE:
       output_tab_table ( this, (struct tab_table *) s->ext);
       break;
-    case SOM_CHART:
-      link_image (x->file, ((struct chart *)s->ext)->file_name);
-      break;
     default:
       NOT_REACHED ();
     }
@@ -289,7 +293,7 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
 {
   struct html_driver_ext *x = this->ext;
 
-  if (t->nr == 1 && t->nc == 1)
+  if (tab_nr (t) == 1 && tab_nc (t) == 1)
     {
       fputs ("<P>", x->file);
       html_put_cell_contents (this, t->ct[0], *t->cc);
@@ -311,18 +315,18 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
     int r;
     unsigned char *ct = t->ct;
 
-    for (r = 0; r < t->nr; r++)
+    for (r = 0; r < tab_nr (t); r++)
       {
        int c;
 
        fputs ("  <TR>\n", x->file);
-       for (c = 0; c < t->nc; c++, ct++)
+       for (c = 0; c < tab_nc (t); c++, ct++)
          {
             struct substring *cc;
             const char *tag;
             struct tab_joined_cell *j = NULL;
 
-            cc = t->cc + c + r * t->nc;
+            cc = t->cc + c + r * tab_nc (t);
            if (*ct & TAB_JOIN)
               {
                 j = (struct tab_joined_cell *) ss_data (*cc);
@@ -332,8 +336,8 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
               }
 
             /* Output <TD> or <TH> tag. */
-            tag = (r < t->t || r >= t->nr - t->b
-                   || c < t->l || c >= t->nc - t->r) ? "TH" : "TD";
+            tag = (r < tab_t (t) || r >= tab_nr (t) - tab_b (t)
+                   || c < tab_l (t) || c >= tab_nc (t) - tab_r (t)) ? "TH" : "TD";
             fprintf (x->file, "    <%s ALIGN=%s",
                      tag,
                      (*ct & TAB_ALIGN_MASK) == TAB_LEFT ? "LEFT"
@@ -361,19 +365,6 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
   fputs ("</TABLE>\n\n", x->file);
 }
 
-static void
-html_initialise_chart (struct outp_driver *this UNUSED, struct chart *ch)
-{
-  struct html_driver_ext *x = this->ext;
-  chart_init_separate (ch, "png", x->chart_file_name, ++x->chart_cnt);
-}
-
-static void
-html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch)
-{
-  chart_finalise_separate (ch);
-}
-
 
 
 /* HTML driver class. */
@@ -389,11 +380,11 @@ const struct outp_class html_class =
     NULL,
     NULL,
 
+    html_output_chart,
+
     html_submit,
 
     NULL,
     NULL,
     NULL,
-    html_initialise_chart,
-    html_finalise_chart
   };
index 0b2fdfe1cf2ab53fc1fdaaf4ce3decf23b14f0ad..9b9013933ae87ed3fde10e6d93e83441a41a8ad2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "manager.h"
+
+#include <output/manager.h>
+
 #include <stdio.h>
 #include <stdlib.h>
+
 #include <libpspp/assertion.h>
-#include "output.h"
+#include <output/output.h>
+
+#include "gl/xalloc.h"
 
 /* Table. */
-int table_num = 1;
-int subtable_num;
+static int table_num = 1;
+static int subtable_num;
+
+/* Name of PSPP's current command, or NULL if outside a command. */
+static char *command_name;
 \f
+struct som_entity *
+som_entity_clone (struct som_entity *entity)
+{
+  struct som_entity *copy = xmemdup (entity, sizeof *entity);
+  copy->command_name = xstrdup (entity->command_name);
+  return copy;
+}
+
+void
+som_entity_destroy (struct som_entity *entity)
+{
+  if (entity != NULL)
+    {
+      free (entity->command_name);
+      free (entity);
+    }
+}
+
 /* Increments table_num so different procedures' output can be
    distinguished. */
 void
@@ -37,6 +63,15 @@ som_new_series (void)
     }
 }
 
+/* Sets COMMAND_NAME as the name of the current command,
+   for embedding in output. */
+void
+som_set_command_name (const char *command_name_)
+{
+  free (command_name);
+  command_name = command_name_ ? xstrdup (command_name_) : NULL;
+}
+
 /* Ejects the paper for all active devices. */
 void
 som_eject_page (void)
@@ -68,30 +103,16 @@ som_blank_line (void)
       d->cp_y += d->font_height;
 }
 \f
-/* Driver. */
-static struct outp_driver *d = 0;
-
-/* Table. */
-static struct som_entity *t = 0;
-
-/* Flags. */
-static unsigned flags;
-
-/* Number of columns, rows. */
-static int nc, nr;
-
-/* Number of columns or rows in left, right, top, bottom headers. */
-static int hl, hr, ht, hb;
-
-/* Column style. */
-static int cs;
-
-/* Table height, width. */
-static int th, tw;
-
-static void render_columns (void);
-static void render_simple (void);
-static void render_segments (void);
+static void render_columns (void *r, struct outp_driver *, struct som_entity *,
+                            int tw, int th,
+                            int hl, int hr, int ht, int hb);
+static void render_simple (void *r, struct outp_driver *, struct som_entity *,
+                           int tw, int th,
+                           int hl, int hr, int ht, int hb);
+static void render_segments (void *r, struct outp_driver *,
+                             struct som_entity *,
+                             int tw, int th,
+                             int hl, int hr, int ht, int hb);
 
 static void output_entity (struct outp_driver *, struct som_entity *);
 
@@ -99,84 +120,132 @@ static void output_entity (struct outp_driver *, struct som_entity *);
 void
 som_submit (struct som_entity *t)
 {
+  struct outp_driver *d;
+  unsigned int flags;
+
 #if DEBUGGING
   static int entry;
 
   assert (entry++ == 0);
 #endif
 
-  if ( t->type == SOM_TABLE)
-    {
-      t->class->table (t);
-      t->class->flags (&flags);
-      t->class->count (&nc, &nr);
-      t->class->headers (&hl, &hr, &ht, &hb);
+  t->class->flags (t, &flags);
+  if (!(flags & SOMF_NO_TITLE))
+    subtable_num++;
+  t->table_num = table_num;
+  t->subtable_num = subtable_num;
+  t->command_name = command_name ? xstrdup (command_name) : NULL;
 
+  if (t->type == SOM_TABLE)
+    {
+      int hl, hr, ht, hb;
+      int nc, nr;
 
-#if DEBUGGING
+      t->class->count (t, &nc, &nr);
+      t->class->headers (t, &hl, &hr, &ht, &hb);
       if (hl + hr > nc || ht + hb > nr)
        {
-         printf ("headers: (l,r)=(%d,%d), (t,b)=(%d,%d) in table size (%d,%d)\n",
-                 hl, hr, ht, hb, nc, nr);
+         fprintf (stderr, "headers: (l,r)=(%d,%d), (t,b)=(%d,%d) "
+                   "in table size (%d,%d)\n",
+                   hl, hr, ht, hb, nc, nr);
          NOT_REACHED ();
        }
       else if (hl + hr == nc)
-       printf ("warning: headers (l,r)=(%d,%d) in table width %d\n", hl, hr, nc);
+       fprintf (stderr, "warning: headers (l,r)=(%d,%d) in table width %d\n",
+                hl, hr, nc);
       else if (ht + hb == nr)
-       printf ("warning: headers (t,b)=(%d,%d) in table height %d\n", ht, hb, nr);
-#endif
+       fprintf (stderr, "warning: headers (t,b)=(%d,%d) in table height %d\n",
+                ht, hb, nr);
+    }
+
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    output_entity (d, t);
 
-      t->class->columns (&cs);
+#if DEBUGGING
+  assert (--entry == 0);
+#endif
+}
 
-      if (!(flags & SOMF_NO_TITLE))
-       subtable_num++;
+static bool
+check_fits_width (struct som_entity *t, const struct outp_driver *d, void *r)
+{
+  int hl, hr, ht, hb;
+  int nc, nr;
+  int i;
 
+  t->class->headers (t, &hl, &hr, &ht, &hb);
+  t->class->count (t, &nc, &nr);
+  for (i = hl; i < nc - hr; i++)
+    {
+      int end, actual;
+      t->class->cumulate (r, SOM_COLUMNS, i, &end, d->width, &actual);
+      if (end == i)
+        return false;
     }
 
-  {
-    struct outp_driver *d;
+  return true;
+}
 
-    for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-       output_entity (d, t);
+static bool
+check_fits_length (struct som_entity *t, const struct outp_driver *d, void *r)
+{
+  int hl, hr, ht, hb;
+  int nc, nr;
+  int i;
 
-  }
+  t->class->headers (t, &hl, &hr, &ht, &hb);
+  t->class->count (t, &nc, &nr);
+  for (i = ht; i < nr - hb; i++)
+    {
+      int end, actual;
+      t->class->cumulate (r, SOM_ROWS, i, &end, d->length, &actual);
+      if (end == i)
+        return false;
+    }
 
-#if DEBUGGING
-  assert (--entry == 0);
-#endif
+  return true;
 }
 
-/* Output entity ENTITY to driver DRIVER. */
+/* Output entity T to driver D. */
 static void
-output_entity (struct outp_driver *driver, struct som_entity *entity)
+output_entity (struct outp_driver *d, struct som_entity *t)
 {
   bool fits_width, fits_length;
-  d = driver;
+  unsigned int flags;
+  int hl, hr, ht, hb;
+  int tw, th;
+  int nc, nr;
+  int cs;
+  void *r;
 
   outp_open_page (d);
-  if (d->class->special || entity->type == SOM_CHART)
+  if (d->class->special)
     {
-      driver->class->submit (d, entity);
+      d->class->submit (d, t);
       return;
     }
 
-  t = entity;
+  t->class->headers (t, &hl, &hr, &ht, &hb);
+  t->class->count (t, &nc, &nr);
+  t->class->columns (t, &cs);
+  t->class->flags (t, &flags);
+
+  r = t->class->render_init (t, d, hl, hr, ht, hb);
 
-  t->class->driver (d);
-  t->class->area (&tw, &th);
-  fits_width = t->class->fits_width (d->width);
-  fits_length = t->class->fits_length (d->length);
+  fits_width = check_fits_width (t, d, r);
+  fits_length = check_fits_length (t, d, r);
   if (!fits_width || !fits_length)
     {
-      int tl, tr, tt, tb;
-      tl = fits_width ? hl : 0;
-      tr = fits_width ? hr : 0;
-      tt = fits_length ? ht : 0;
-      tb = fits_length ? hb : 0;
-      t->class->set_headers (tl, tr, tt, tb);
-      t->class->driver (d);
-      t->class->area (&tw, &th);
+      t->class->render_free (r);
+
+      if (!fits_width)
+        hl = hr = 0;
+      if (!fits_length)
+        ht = hb = 0;
+
+      r = t->class->render_init (t, d, hl, hr, ht, hb);
     }
+  t->class->area (r, &tw, &th);
 
   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
     d->cp_y += d->font_height;
@@ -184,22 +253,29 @@ output_entity (struct outp_driver *driver, struct som_entity *entity)
   if (cs != SOM_COL_NONE
       && 2 * (tw + d->prop_em_width) <= d->width
       && nr - (ht + hb) > 5)
-    render_columns ();
+    render_columns (r, d, t, tw, th, hl, hr, ht, hb);
   else if (tw < d->width && th + d->cp_y < d->length)
-    render_simple ();
+    render_simple (r, d, t, tw, th, hl, hr, ht, hb);
   else
-    render_segments ();
+    render_segments (r, d, t, tw, th, hl, hr, ht, hb);
 
-  t->class->set_headers (hl, hr, ht, hb);
+  t->class->render_free (r);
 }
 
 /* Render the table into multiple columns. */
 static void
-render_columns (void)
+render_columns (void *r, struct outp_driver *d, struct som_entity *t,
+                int tw, int th UNUSED,
+                int hl UNUSED, int hr UNUSED, int ht, int hb)
 {
   int y0, y1;
   int max_len = 0;
   int index = 0;
+  int nc, nr;
+  int cs;
+
+  t->class->count (t, &nc, &nr);
+  t->class->columns (t, &cs);
 
   assert (cs == SOM_COL_DOWN);
   assert (d->cp_x == 0);
@@ -208,7 +284,7 @@ render_columns (void)
     {
       int len;
 
-      t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
+      t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
 
       if (y0 == y1)
        {
@@ -220,8 +296,9 @@ render_columns (void)
          if (len > max_len)
            max_len = len;
 
-         t->class->title (index++, 0);
-         t->class->render (0, y0, nc, y1);
+         t->class->title (r, index++, 0, t->table_num, t->subtable_num,
+                           t->command_name);
+         t->class->render (r, 0, y0, nc, y1);
 
          d->cp_x += tw + 2 * d->prop_em_width;
          if (d->cp_x + tw > d->width)
@@ -242,33 +319,44 @@ render_columns (void)
 
 /* Render the table by itself on the current page. */
 static void
-render_simple (void)
+render_simple (void *r, struct outp_driver *d, struct som_entity *t,
+               int tw, int th,
+               int hl, int hr, int ht, int hb)
 {
+  int nc, nr;
+
+  t->class->count (t, &nc, &nr);
+
   assert (d->cp_x == 0);
   assert (tw < d->width && th + d->cp_y < d->length);
 
-  t->class->title (0, 0);
-  t->class->render (hl, ht, nc - hr, nr - hb);
+  t->class->title (r, 0, 0, t->table_num, t->subtable_num, t->command_name);
+  t->class->render (r, hl, ht, nc - hr, nr - hb);
   d->cp_y += th;
 }
 
 /* General table breaking routine. */
 static void
-render_segments (void)
+render_segments (void *r, struct outp_driver *d, struct som_entity *t,
+                 int tw UNUSED, int th UNUSED,
+                 int hl, int hr, int ht, int hb)
 {
   int count = 0;
 
   int x_index;
   int x0, x1;
 
+  int nc, nr;
+
   assert (d->cp_x == 0);
 
+  t->class->count (t, &nc, &nr);
   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
     {
       int y_index;
       int y0, y1;
 
-      t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
+      t->class->cumulate (r, SOM_COLUMNS, x0, &x1, d->width, NULL);
       if (x_index == 0 && x1 != nc - hr)
        x_index++;
 
@@ -279,7 +367,7 @@ render_segments (void)
          if (count++ != 0 && d->cp_y != 0)
            d->cp_y += d->font_height;
 
-         t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
+         t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
          if (y_index == 0 && y1 != nr - hb)
            y_index++;
 
@@ -290,9 +378,10 @@ render_segments (void)
            }
           else
             {
-             t->class->title (x_index ? x_index : y_index,
-                              x_index ? y_index : 0);
-             t->class->render (x0, y0, x1, y1);
+             t->class->title (r, x_index ? x_index : y_index,
+                              x_index ? y_index : 0,
+                               t->table_num, t->subtable_num, t->command_name);
+             t->class->render (r, x0, y0, x1, y1);
 
              d->cp_y += len;
            }
index 58d0c12d48594862a7e838b0f8f3ee52b50fa605..e7276982fc5c5a064b89cc8e0af364d43283a38f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -37,8 +37,7 @@
 
 enum som_type
   {
-    SOM_TABLE,
-    SOM_CHART
+    SOM_TABLE
   } ;
 
 /* Entity (Table or Chart) . */
@@ -46,9 +45,15 @@ struct som_entity
   {
     const struct som_table_class *class;       /* Table class. */
     enum som_type type;                 /* Table or Chart */
-    void *ext;                         /* Owned by */
+    void *ext;                         /* Owned by table or chart class. */
+    int table_num;                      /* Table number. */
+    int subtable_num;                   /* Sub-table number. */
+    char *command_name;                 /* Command that yielded this output. */
   };
 
+struct som_entity *som_entity_clone (struct som_entity *);
+void som_entity_destroy (struct som_entity *);
+
 /* Group styles. */
 enum
   {
@@ -59,8 +64,8 @@ enum
 /* Cumulation types. */
 enum
   {
-    SOM_ROWS, SOM_ROW = SOM_ROWS,      /* Rows. */
-    SOM_COLUMNS, SOM_COLUMN = SOM_COLUMNS      /* Columns. */
+    SOM_ROWS,                   /* Rows. */
+    SOM_COLUMNS                 /* Columns. */
   };
 
 /* Flags. */
@@ -75,40 +80,29 @@ enum
 struct outp_driver;
 struct som_table_class
   {
-    /* Set table, driver. */
-    void (*table) (struct som_entity *);
-    void (*driver) (struct outp_driver *);
-
-    /* Query columns and rows. */
-    void (*count) (int *n_columns, int *n_rows);
-    void (*area) (int *horiz, int *vert);
-    void (*width) (int *columns);
-    void (*height) (int *rows);
-    void (*columns) (int *style);
-    int (*breakable) (int row);                                /* ? */
-    void (*headers) (int *l, int *r, int *t, int *b);
-    void (*join) (int *(column[2]), int *(row[2]));    /* ? */
-    void (*cumulate) (int cumtype, int start, int *end, int max, int *actual);
-    void (*flags) (unsigned *);
-    bool (*fits_width) (int width);
-    bool (*fits_length) (int length);
-
-    /* Set columns and rows. */
-    void (*set_width) (int column, int width);         /* ? */
-    void (*set_height) (int row, int height);          /* ? */
-    void (*set_headers) (int l, int r, int t, int b);
-
-    /* Rendering. */
-    void (*title) (int x, int y);
-    void (*render) (int x1, int y1, int x2, int y2);
+    /* Operations on tables. */
+    void (*count) (struct som_entity *, int *n_columns, int *n_rows);
+    void (*columns) (struct som_entity *, int *style);
+    void (*headers) (struct som_entity *, int *l, int *r, int *t, int *b);
+    void (*flags) (struct som_entity *, unsigned *);
+
+    /* Creating and freeing driver-specific table rendering data. */
+    void *(*render_init) (struct som_entity *, struct outp_driver *,
+                          int l, int r, int t, int b);
+    void (*render_free) (void *);
+
+    /* Rendering operations. */
+    void (*area) (void *, int *horiz, int *vert);
+    void (*cumulate) (void *, int cumtype, int start, int *end,
+                      int max, int *actual);
+    void (*title) (void *, int x, int y, int table_num, int subtable_num,
+                   const char *command_name);
+    void (*render) (void *, int x1, int y1, int x2, int y2);
   };
 
-/* Table indexes. */
-extern int table_num;
-extern int subtable_num;
-
 /* Submission. */
 void som_new_series (void);
+void som_set_command_name (const char *);
 void som_submit (struct som_entity *t);
 
 /* Miscellaneous. */
diff --git a/src/output/odt.c b/src/output/odt.c
new file mode 100644 (file)
index 0000000..8471b6b
--- /dev/null
@@ -0,0 +1,583 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* A driver for creating OpenDocument Format text files from PSPP's output */
+
+#include <libpspp/assertion.h>
+#include <libpspp/version.h>
+
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
+
+#include <time.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <libgen.h>
+
+#include <libxml/xmlwriter.h>
+
+#include "xalloc.h"
+
+#include "error.h"
+
+#define _xml(X) (const xmlChar *)(X)
+
+
+struct odf_driver_options
+{
+  struct outp_driver *driver;
+  
+  char *file_name;            /* Output file name. */
+  bool debug;
+};
+
+
+struct odt_driver_ext 
+{
+  /* The name of the temporary directory used to construct the ODF */
+  char *dirname;
+
+  /* Writer for the content.xml file */
+  xmlTextWriterPtr content_wtr;
+
+  /* Writer fot the manifest.xml file */
+  xmlTextWriterPtr manifest_wtr;
+
+  struct odf_driver_options opts;
+};
+
+
+
+/* Create the "mimetype" file needed by ODF */
+static void
+create_mimetype (const char *dirname)
+{
+  FILE *fp;
+  struct string filename;
+  ds_init_cstr (&filename, dirname);
+  ds_put_cstr (&filename, "/mimetype");
+  fp = fopen (ds_cstr (&filename), "w");
+  ds_destroy (&filename);
+
+  assert (fp);
+  fprintf (fp, "application/vnd.oasis.opendocument.text");
+  fclose (fp);
+}
+
+/* Create a new XML file called FILENAME in the temp directory, and return a writer for it */
+static xmlTextWriterPtr
+create_writer (const struct odt_driver_ext *driver, const char *filename)
+{
+  char *copy = NULL;
+  xmlTextWriterPtr w;
+  struct string str;
+  ds_init_cstr (&str, driver->dirname);
+  ds_put_cstr (&str, "/");
+  ds_put_cstr (&str, filename);
+
+  /* dirname modifies its argument, so we must copy it */
+  copy = xstrdup (ds_cstr (&str));
+  mkdir (dirname (copy), 0700);
+  free (copy);
+
+  w = xmlNewTextWriterFilename (ds_cstr (&str), 0);
+
+  ds_destroy (&str);
+
+  xmlTextWriterStartDocument (w, NULL, "UTF-8", NULL);
+
+  return w;
+}
+
+
+static void
+register_file (struct odt_driver_ext *x, const char *filename)
+{
+  assert (x->manifest_wtr);
+  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"),  _xml("text/xml"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"),  _xml (filename));
+  xmlTextWriterEndElement (x->manifest_wtr);
+}
+
+static void
+write_style_data (struct odt_driver_ext *x)
+{
+  xmlTextWriterPtr w = create_writer (x, "styles.xml");
+  register_file (x, "styles.xml");
+
+  xmlTextWriterStartElement (w, _xml ("office:document-styles"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"),
+                              _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:style"),
+                              _xml ("urn:oasis:names:tc:opendocument:xmlns:style:1.0"));
+
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:fo"),
+                              _xml ("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0") );
+
+  xmlTextWriterWriteAttribute (w, _xml ("office:version"),  _xml ("1.1"));
+                              
+
+
+  xmlTextWriterStartElement (w, _xml ("office:styles"));
+
+
+  {
+    xmlTextWriterStartElement (w, _xml ("style:style"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+                                _xml ("Standard"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+                                _xml ("paragraph"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+                                _xml ("text"));
+
+    xmlTextWriterEndElement (w); /* style:style */
+  }
+
+  {
+    xmlTextWriterStartElement (w, _xml ("style:style"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+                                _xml ("Table_20_Contents"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:display-name"),
+                                _xml ("Table Contents"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+                                _xml ("paragraph"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:parent-style-name"),
+                                _xml ("Standard"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+                                _xml ("extra"));
+
+    xmlTextWriterEndElement (w); /* style:style */
+  }
+
+  {
+    xmlTextWriterStartElement (w, _xml ("style:style"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+                                _xml ("Table_20_Heading"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:display-name"),
+                                _xml ("Table Heading"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+                                _xml ("paragraph"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:parent-style-name"),
+                                _xml ("Table_20_Contents"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+                                _xml ("extra"));
+
+
+    xmlTextWriterStartElement (w, _xml ("style:text-properties"));
+    xmlTextWriterWriteAttribute (w, _xml ("fo:font-weight"), _xml ("bold"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:font-weight-asian"), _xml ("bold"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:font-weight-complex"), _xml ("bold"));
+    xmlTextWriterEndElement (w); /* style:text-properties */
+
+    xmlTextWriterEndElement (w); /* style:style */
+  }
+
+
+  xmlTextWriterEndElement (w); /* office:styles */
+  xmlTextWriterEndElement (w); /* office:document-styles */
+
+  xmlTextWriterEndDocument (w);
+  xmlFreeTextWriter (w);
+}
+
+static void
+write_meta_data (struct odt_driver_ext *x)
+{
+  xmlTextWriterPtr w = create_writer (x, "meta.xml");
+  register_file (x, "meta.xml");
+
+  xmlTextWriterStartElement (w, _xml ("office:document-meta"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"), _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:dc"),  _xml ("http://purl.org/dc/elements/1.1/"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:meta"), _xml ("urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:ooo"), _xml("http://openoffice.org/2004/office"));
+  xmlTextWriterWriteAttribute (w, _xml ("office:version"),  _xml("1.1"));
+
+  xmlTextWriterStartElement (w, _xml ("office:meta"));
+  {
+    xmlTextWriterStartElement (w, _xml ("meta:generator"));
+    xmlTextWriterWriteString (w, _xml (stat_version));
+    xmlTextWriterEndElement (w);
+  }
+
+
+  {
+    char buf[30];
+    struct passwd *pw = getpwuid (getuid ());
+    time_t t = time (NULL);
+    struct tm *tm =  localtime (&t);
+
+    strftime (buf, 30, "%Y-%m-%dT%H:%M:%S", tm);
+
+    xmlTextWriterStartElement (w, _xml ("meta:initial-creator"));
+    xmlTextWriterWriteString (w, _xml (strtok (pw->pw_gecos, ",")));
+    xmlTextWriterEndElement (w);
+
+    xmlTextWriterStartElement (w, _xml ("meta:creation-date"));
+    xmlTextWriterWriteString (w, _xml (buf));
+    xmlTextWriterEndElement (w);
+
+    xmlTextWriterStartElement (w, _xml ("dc:creator"));
+    xmlTextWriterWriteString (w, _xml (strtok (pw->pw_gecos, ",")));
+
+    xmlTextWriterEndElement (w);
+
+    xmlTextWriterStartElement (w, _xml ("dc:date"));
+    xmlTextWriterWriteString (w, _xml (buf));
+    xmlTextWriterEndElement (w);
+  }
+
+  xmlTextWriterEndElement (w);
+  xmlTextWriterEndElement (w);
+  xmlTextWriterEndDocument (w);
+  xmlFreeTextWriter (w);
+}
+
+enum
+{
+  output_file_arg,
+  boolean_arg,
+};
+
+static const struct outp_option option_tab[] =
+{
+  {"output-file",              output_file_arg,0},
+
+  {"debug",                    boolean_arg,    1},
+
+  {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+  struct odf_driver_options *options = options_;
+  struct outp_driver *this = options->driver;
+  int subcat;
+  char *value = ds_cstr (val);
+
+  switch (outp_match_keyword (key, option_tab, &subcat))
+    {
+    case -1:
+      error (0, 0,
+             _("unknown configuration parameter `%s' for %s device "
+               "driver"), key, this->class->name);
+      break;
+    case output_file_arg:
+      free (options->file_name);
+      options->file_name = xstrdup (value);
+      break;
+    case boolean_arg:
+      if (!strcmp (value, "on") || !strcmp (value, "true")
+          || !strcmp (value, "yes") || atoi (value))
+        options->debug = true;
+      else if (!strcmp (value, "off") || !strcmp (value, "false")
+               || !strcmp (value, "no") || !strcmp (value, "0"))
+        options->debug = false;
+      else
+        {
+          error (0, 0, _("boolean value expected for %s"), key);
+          return false;
+        }
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+
+  return true;
+}
+
+
+static bool
+odt_open_driver (const char *name, int types, struct substring option_string)
+{
+  struct odt_driver_ext *x;
+  struct outp_driver *this = outp_allocate_driver (&odt_class, name, types);
+
+  this->ext = x = xmalloc (sizeof *x);
+
+  x->opts.driver = this;
+  x->opts.file_name = xstrdup ("pspp.pdt");
+  x->opts.debug = false;
+
+  outp_parse_options (this->name, option_string, handle_option, &x->opts);
+
+  outp_register_driver (this);
+
+  x->dirname = xstrdup ("odt-XXXXXX");
+  mkdtemp (x->dirname);
+
+  create_mimetype (x->dirname);
+
+  /* Create the manifest */
+  x->manifest_wtr = create_writer (x, "META-INF/manifest.xml");
+
+  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:manifest"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("xmlns:manifest"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"));
+
+
+  /* Add a manifest entry for the document as a whole */
+  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"),  _xml("application/vnd.oasis.opendocument.text"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"),  _xml("/"));
+  xmlTextWriterEndElement (x->manifest_wtr);
+
+
+  write_meta_data (x);
+  write_style_data (x);
+
+  x->content_wtr = create_writer (x, "content.xml");
+  register_file (x, "content.xml");
+
+
+  /* Some necessary junk at the start */
+  xmlTextWriterStartElement (x->content_wtr, _xml("office:document-content"));
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:office"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:text"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:table"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:table:1.0"));
+
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:version"), _xml("1.1"));
+
+  xmlTextWriterStartElement (x->content_wtr, _xml("office:body"));
+  xmlTextWriterStartElement (x->content_wtr, _xml("office:text"));
+
+
+
+  /* Close the manifest */
+  xmlTextWriterEndElement (x->manifest_wtr);
+  xmlTextWriterEndDocument (x->manifest_wtr);
+  xmlFreeTextWriter (x->manifest_wtr);
+
+  return true;
+}
+
+static bool
+odt_close_driver (struct outp_driver *this)
+{
+  struct string zip_cmd;
+  struct string rm_cmd;
+  struct odt_driver_ext *x = this->ext;
+
+  xmlTextWriterEndElement (x->content_wtr); /* office:text */
+  xmlTextWriterEndElement (x->content_wtr); /* office:body */
+  xmlTextWriterEndElement (x->content_wtr); /* office:document-content */
+
+  xmlTextWriterEndDocument (x->content_wtr);
+  xmlFreeTextWriter (x->content_wtr);
+
+  /* Zip up the directory */
+  ds_init_empty (&zip_cmd);
+  ds_put_format (&zip_cmd,
+                "cd %s ; rm -f ../%s; zip -q -X ../%s mimetype; zip -q -X -u -r ../pspp.odt .",
+                x->dirname, x->opts.file_name, x->opts.file_name);
+  system (ds_cstr (&zip_cmd));
+  ds_destroy (&zip_cmd);
+
+
+  if ( !x->opts.debug )
+    {
+      /* Remove the temp dir */
+      ds_init_empty (&rm_cmd);
+      ds_put_format (&rm_cmd, "rm -r %s", x->dirname);
+      system (ds_cstr (&rm_cmd));
+      ds_destroy (&rm_cmd);
+    }
+  else
+    fprintf (stderr, "Not removing directory %s\n", x->dirname);
+
+  free (x->dirname);
+  free (x);
+
+  return true;
+}
+
+static void
+odt_open_page (struct outp_driver *this UNUSED)
+{
+}
+
+static void
+odt_close_page (struct outp_driver *this UNUSED)
+{
+}
+
+static void
+odt_output_chart (struct outp_driver *this UNUSED, const struct chart *chart UNUSED)
+{
+ printf ("%s\n", __FUNCTION__);
+}
+
+
+/* Submit a table to the ODT driver */
+static void
+odt_submit (struct outp_driver *this, struct som_entity *e)
+{
+  int r, c;
+  
+  struct odt_driver_ext *x = this->ext;
+  struct tab_table *tab = e->ext;
+
+
+  /* Write a heading for the table */
+  xmlTextWriterStartElement (x->content_wtr, _xml("text:h"));
+  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("text:level"), "%d", e->subtable_num == 1 ? 2 : 3);
+  xmlTextWriterWriteString (x->content_wtr, _xml (tab->title) );
+  xmlTextWriterEndElement (x->content_wtr);
+
+  /* Start table */
+  xmlTextWriterStartElement (x->content_wtr, _xml("table:table"));
+  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:name"), 
+                                    "TABLE-%d.%d", e->table_num, e->subtable_num);
+
+
+  /* Start column definitions */
+  xmlTextWriterStartElement (x->content_wtr, _xml("table:table-column"));
+  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:number-columns-repeated"), "%d", tab_nc (tab));
+  xmlTextWriterEndElement (x->content_wtr);
+
+
+  /* Deal with row headers */
+  if ( tab_t (tab) > 0)
+    xmlTextWriterStartElement (x->content_wtr, _xml("table:table-header-rows"));
+    
+
+  /* Write all the rows */
+  for (r = 0 ; r < tab_nr (tab); ++r)
+    {
+      int spanned_columns = 0;
+      /* Start row definition */
+      xmlTextWriterStartElement (x->content_wtr, _xml("table:table-row"));
+
+      /* Write all the columns */
+      for (c = 0 ; c < tab_nc (tab) ; ++c)
+       {
+         char *s = NULL;
+         unsigned int opts = tab->ct[tab_nc (tab) * r + c];
+         struct substring ss = tab->cc[tab_nc (tab) * r + c];
+
+         if (opts & TAB_EMPTY)
+           {
+             xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
+             xmlTextWriterEndElement (x->content_wtr);
+             continue;
+           }
+
+         if ( opts & TAB_JOIN)
+           {
+             if ( spanned_columns == 0)
+               {
+                 struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
+                 s = ss_xstrdup (j->contents);
+               }
+           }
+         else
+           s = ss_xstrdup (ss);
+
+         if ( spanned_columns == 0 )
+           {
+             xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
+             xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:value-type"), _xml("string"));
+
+             if ( opts & TAB_JOIN )
+               {
+                 struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
+                 spanned_columns = j->x2 - j->x1;
+
+                 xmlTextWriterWriteFormatAttribute (x->content_wtr,
+                                                    _xml("table:number-columns-spanned"),
+                                                    "%d", spanned_columns);
+               }
+
+             xmlTextWriterStartElement (x->content_wtr, _xml("text:p"));
+
+             if ( r < tab_t (tab) || c < tab_l (tab) )
+               xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading"));
+             else
+               xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
+
+             xmlTextWriterWriteString (x->content_wtr, _xml (s));
+         
+             xmlTextWriterEndElement (x->content_wtr); /* text:p */
+             xmlTextWriterEndElement (x->content_wtr); /* table:table-cell */
+           }
+         else
+           {
+             xmlTextWriterStartElement (x->content_wtr, _xml("table:covered-table-cell"));
+             xmlTextWriterEndElement (x->content_wtr);
+           }
+         if ( opts & TAB_JOIN )
+           spanned_columns --;
+
+         free (s);
+       }
+  
+      xmlTextWriterEndElement (x->content_wtr); /* row */
+
+      if ( tab_t (tab) > 0 && r == tab_t (tab) - 1)
+       xmlTextWriterEndElement (x->content_wtr); /* table-header-rows */
+    }
+
+  xmlTextWriterEndElement (x->content_wtr); /* table */
+}
+
+
+/* ODT driver class. */
+const struct outp_class odt_class =
+{
+  "odf",
+  1,
+
+  odt_open_driver,
+  odt_close_driver,
+
+  odt_open_page,
+  odt_close_page,
+  NULL,
+
+  odt_output_chart,
+  odt_submit,
+
+  NULL,
+  NULL,
+  NULL,
+};
index 843b0d4e544996cebcd736b25c8225e185337d1f..2f4788f9a0016fe3a5f6b74a752e11062ba4c9f5 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* FIXME? Should the output configuration format be changed to
-   drivername:classname:devicetype:options, where devicetype is zero
-   or more of screen, printer, listing? */
-
-/* FIXME: Have the reentrancy problems been solved? */
-
 /* Where the output driver name came from. */
 enum
   {
@@ -79,7 +73,7 @@ struct outp_driver_class_list
   };
 
 static struct outp_driver_class_list *outp_class_list;
-static struct outp_driver *outp_driver_list;
+static struct ll_list outp_driver_list = LL_INITIALIZER (outp_driver_list);
 
 char *outp_title;
 char *outp_subtitle;
@@ -224,18 +218,26 @@ find_defn_value (const char *key)
     return getenv (key);
 }
 
+static void
+insert_defn_value (const char *var, struct string *dst, void *aux UNUSED)
+{
+  const char *value = find_defn_value (var);
+  if (value != NULL)
+    ds_put_cstr (dst, value);
+}
+
 /* Initializes global variables. */
 void
 outp_init (void)
 {
-  extern struct outp_class ascii_class;
-  extern struct outp_class postscript_class;
-
   char def[] = "default";
 
   add_class (&html_class);
-  add_class (&postscript_class);
   add_class (&ascii_class);
+#ifdef HAVE_CAIRO
+  add_class (&cairo_class);
+#endif
+  add_class (&odt_class);
 
   add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
 }
@@ -345,13 +347,13 @@ exit:
 
   if (result)
     {
-      if (outp_driver_list == NULL)
+      if (ll_is_empty (&outp_driver_list))
         error (0, 0, _("no active output drivers"));
     }
   else
     error (0, 0, _("error reading device definition file"));
 
-  if (!result || outp_driver_list == NULL)
+  if (!result || ll_is_empty (&outp_driver_list))
     init_default_drivers ();
 }
 
@@ -413,7 +415,7 @@ outp_configure_macro (char *bp)
     ep++;
 
   ds_init_cstr (&d->value, ep);
-  fn_interp_vars (ds_ss (&d->value), find_defn_value, &d->value);
+  fn_interp_vars (ds_ss (&d->value), insert_defn_value, NULL, &d->value);
   d->next = outp_macros;
   d->prev = NULL;
   if (outp_macros)
@@ -421,29 +423,18 @@ outp_configure_macro (char *bp)
   outp_macros = d;
 }
 
-/* Destroys all the drivers in driver list *DL and sets *DL to
-   NULL. */
-static void
-destroy_list (struct outp_driver ** dl)
-{
-  struct outp_driver *d, *next;
-
-  for (d = *dl; d; d = next)
-    {
-      destroy_driver (d);
-      next = d->next;
-      free (d);
-    }
-  *dl = NULL;
-}
-
 /* Closes all the output drivers. */
 void
 outp_done (void)
 {
   struct outp_driver_class_list *n = outp_class_list ;
   outp_configure_clear ();
-  destroy_list (&outp_driver_list);
+  while (!ll_is_empty (&outp_driver_list))
+    {
+      struct outp_driver *d = ll_data (ll_head (&outp_driver_list),
+                                       struct outp_driver, node);
+      destroy_driver (d);
+    }
 
   while (n)
     {
@@ -611,10 +602,9 @@ get_option_token (struct substring *s, const char *driver_name,
 }
 
 bool
-outp_parse_options (struct substring options,
-                    bool (*callback) (struct outp_driver *, const char *key,
-                                      const struct string *value),
-                    struct outp_driver *driver)
+outp_parse_options (const char *driver_name, struct substring options,
+                    bool (*callback) (void *aux, const char *key,
+                                      const struct string *value), void *aux)
 {
   struct string key = DS_EMPTY_INITIALIZER;
   struct string value = DS_EMPTY_INITIALIZER;
@@ -627,7 +617,7 @@ outp_parse_options (struct substring options,
       if (ss_is_empty (left))
         break;
 
-      if (!get_option_token (&left, driver->name, &key))
+      if (!get_option_token (&left, driver_name, &key))
         break;
 
       ss_ltrim (&left, ss_cstr (CC_SPACES));
@@ -635,15 +625,15 @@ outp_parse_options (struct substring options,
        {
          error (0, 0, _("syntax error expecting `=' "
                          "parsing options for driver \"%s\""),
-                 driver->name);
+                 driver_name);
          break;
        }
 
       ss_ltrim (&left, ss_cstr (CC_SPACES));
-      if (!get_option_token (&left, driver->name, &value))
+      if (!get_option_token (&left, driver_name, &value))
         break;
 
-      ok = callback (driver, ds_cstr (&key), &value);
+      ok = callback (aux, ds_cstr (&key), &value);
     }
   while (ok);
 
@@ -658,8 +648,7 @@ static struct outp_driver *
 find_driver (char *name)
 {
   struct outp_driver *d;
-
-  for (d = outp_driver_list; d; d = d->next)
+  ll_for_each (d, struct outp_driver, node, &outp_driver_list)
     if (!strcmp (d->name, name))
       return d;
   return NULL;
@@ -671,11 +660,10 @@ static void
 configure_driver (struct substring driver_name, struct substring class_name,
                   struct substring device_type, struct substring options)
 {
-  struct outp_driver *d, *iter;
   struct outp_driver_class_list *c;
-
   struct substring token;
   size_t save_idx = 0;
+  char *name;
   int device;
 
   /* Find class. */
@@ -702,38 +690,67 @@ configure_driver (struct substring driver_name, struct substring class_name,
       error (0, 0, _("unknown device type `%.*s'"),
              (int) ss_length (token), ss_data (token));
 
-  /* Open the device. */
-  d = xmalloc (sizeof *d);
-  d->next = d->prev = NULL;
-  d->class = c->class;
-  d->name = ss_xstrdup (driver_name);
+  /* Open driver. */
+  name = ss_xstrdup (driver_name);
+  if (!c->class->open_driver (name, device, options))
+    error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
+           name, c->class->name);
+  free (name);
+}
+
+/* Allocates and returns a new outp_driver for a device with the
+   given NAME and CLASS and the OUTP_DEV_* type(s) in TYPES
+
+   This function is intended to be used by output drivers, not
+   by their clients. */
+struct outp_driver *
+outp_allocate_driver (const struct outp_class *class,
+                      const char *name, int types)
+{
+  struct outp_driver *d = xmalloc (sizeof *d);
+  d->class = class;
+  d->name = xstrdup (name);
   d->page_open = false;
-  d->device = device;
+  d->device = types;
   d->cp_x = d->cp_y = 0;
   d->ext = NULL;
-  d->prc = NULL;
+  return d;
+}
 
-  /* Open driver. */
-  if (!d->class->open_driver (d, options))
-    {
-      error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
-             d->name, d->class->name);
-      free (d->name);
-      free (d);
-      return;
-    }
+/* Frees driver D and the data that it owns directly.  The
+   driver's class must already have unregistered D (if it was
+   registered) and freed data private to its class.
+
+   This function is intended to be used by output drivers, not
+   by their clients. */
+void
+outp_free_driver (struct outp_driver *d)
+{
+  free (d->name);
+  free (d);
+}
+
+/* Adds D to the list of drivers that will be used for output. */
+void
+outp_register_driver (struct outp_driver *d)
+{
+  struct outp_driver *victim;
 
   /* Find like-named driver and delete. */
-  iter = find_driver (d->name);
-  if (iter != NULL)
-    destroy_driver (iter);
+  victim = find_driver (d->name);
+  if (victim != NULL)
+    destroy_driver (victim);
 
-  /* Add to list. */
-  d->next = outp_driver_list;
-  d->prev = NULL;
-  if (outp_driver_list != NULL)
-    outp_driver_list->prev = d;
-  outp_driver_list = d;
+  /* Add D to list. */
+  ll_push_tail (&outp_driver_list, &d->node);
+}
+
+/* Remove driver D from the list of drivers that will be used for
+   output. */
+void
+outp_unregister_driver (struct outp_driver *d)
+{
+  ll_remove (&d->node);
 }
 
 /* String LINE is in format:
@@ -748,7 +765,7 @@ outp_configure_driver_line (struct substring line_)
   size_t save_idx;
   size_t i;
 
-  fn_interp_vars (line_, find_defn_value, &line);
+  fn_interp_vars (line_, insert_defn_value, NULL, &line);
 
   save_idx = 0;
   for (i = 0; i < 4; i++)
@@ -772,26 +789,10 @@ static void
 destroy_driver (struct outp_driver *d)
 {
   outp_close_page (d);
-  if (d->class)
-    {
-      struct outp_driver_class_list *c;
-
-      d->class->close_driver (d);
-
-      for (c = outp_class_list; c; c = c->next)
-       if (c->class == d->class)
-         break;
-      assert (c != NULL);
-    }
-  free (d->name);
-
-  /* Remove this driver from the global driver list. */
-  if (d->prev)
-    d->prev->next = d->next;
-  if (d->next)
-    d->next->prev = d->prev;
-  if (d == outp_driver_list)
-    outp_driver_list = d->next;
+  if (d->class && d->class->close_driver)
+    d->class->close_driver (d);
+  outp_unregister_driver (d);
+  outp_free_driver (d);
 }
 
 /* Tries to match S as one of the keywords in TAB, with
@@ -1087,17 +1088,17 @@ outp_get_paper_size (const char *size, int *h, int *v)
 struct outp_driver *
 outp_drivers (struct outp_driver *d)
 {
-  for (;;)
+  do
     {
-      if (d == NULL)
-       d = outp_driver_list;
-      else
-       d = d->next;
+      struct ll *next;
+
+      next = d == NULL ? ll_head (&outp_driver_list) : ll_next (&d->node);
+      if (next == ll_null (&outp_driver_list))
+        return NULL;
 
-      if (d == NULL
-         || (d->device == 0 || (d->device & disabled_devices) != d->device))
-       break;
+      d = ll_data (next, struct outp_driver, node);
     }
+  while (d->device != 0 && (d->device & disabled_devices) == d->device);
 
   return d;
 }
index fc66874068da031cafef1743406131c486ee504d..398781f05f547fef57f429386e20af6da4993b31 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@
 #ifndef OUTPUT_OUTPUT_H
 #define OUTPUT_OUTPUT_H 1
 
+#include <libpspp/ll.h>
 #include <libpspp/str.h>
 
-
 /* Line styles.  */
 enum outp_line_style
   {
@@ -65,7 +65,8 @@ struct outp_class
     const char *name;          /* Name of this driver class. */
     int special;               /* Boolean value. */
 
-    bool (*open_driver) (struct outp_driver *, struct substring options);
+    bool (*open_driver) (const char *name, int types,
+                         struct substring options);
     bool (*close_driver) (struct outp_driver *);
 
     void (*open_page) (struct outp_driver *);
@@ -73,6 +74,8 @@ struct outp_class
 
     void (*flush) (struct outp_driver *);
 
+    void (*output_chart) (struct outp_driver *, const struct chart *);
+
     /* special != 0 only. */
     void (*submit) (struct outp_driver *, struct som_entity *);
 
@@ -83,8 +86,6 @@ struct outp_class
     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. */
@@ -99,7 +100,7 @@ enum
 /* Defines the configuration of an output driver. */
 struct outp_driver
   {
-    struct outp_driver *next, *prev; /* List of drivers. */
+    struct ll node;             /* Node in list of drivers. */
     const struct outp_class *class;    /* Driver class. */
     char *name;                        /* Name of this driver. */
     bool page_open;            /* 1=page is open, 0=page is closed. */
@@ -114,7 +115,6 @@ struct outp_driver
     int vert_line_width[OUTP_L_COUNT]; /* Width of vertical lines. */
 
     void *ext;                 /* Private extension record. */
-    void *prc;                 /* Per-procedure extension record. */
   };
 
 /* Option structure for the keyword recognizer. */
@@ -131,9 +131,15 @@ extern char *outp_title;
 extern char *outp_subtitle;
 
 void outp_init (void);
+void outp_done (void);
 void outp_read_devices (void);
 void outp_configure_driver_line (struct substring);
-void outp_done (void);
+
+struct outp_driver *outp_allocate_driver (const struct outp_class *class,
+                                          const char *name, int types);
+void outp_free_driver (struct outp_driver *);
+void outp_register_driver (struct outp_driver *);
+void outp_unregister_driver (struct outp_driver *);
 
 void outp_configure_clear (void);
 void outp_configure_add (char *);
@@ -144,10 +150,10 @@ void outp_list_classes (void);
 void outp_enable_device (bool enable, int device);
 struct outp_driver *outp_drivers (struct outp_driver *);
 
-bool outp_parse_options (struct substring options,
-                         bool (*) (struct outp_driver *, const char *key,
-                                   const struct string *value),
-                         struct outp_driver *);
+bool outp_parse_options (const char *driver_name, struct substring options,
+                         bool (*callback) (void *aux, const char *key,
+                                           const struct string *value),
+                         void *aux);
 int outp_match_keyword (const char *, const struct outp_option *, int *);
 
 int outp_evaluate_dimension (const char *);
@@ -163,4 +169,11 @@ int outp_string_width (struct outp_driver *, const char *, enum outp_font);
 /* Imported from som-frnt.c. */
 void som_destroy_driver (struct outp_driver *);
 
+/* Common drivers. */
+extern const struct outp_class ascii_class;
+#ifdef HAVE_CAIRO
+extern const struct outp_class cairo_class;
+#endif
+extern const struct outp_class odt_class;
+
 #endif /* output/output.h */
diff --git a/src/output/postscript.c b/src/output/postscript.c
deleted file mode 100644 (file)
index 11116b9..0000000
+++ /dev/null
@@ -1,1446 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <libpspp/assertion.h>
-#include <libpspp/bit-vector.h>
-#include <libpspp/compiler.h>
-#include <libpspp/freaderror.h>
-#include <libpspp/hash.h>
-#include <libpspp/misc.h>
-#include <libpspp/start-date.h>
-#include <libpspp/version.h>
-
-#include <data/file-name.h>
-
-#include "afm.h"
-#include "chart.h"
-#include "error.h"
-#include "manager.h"
-#include "output.h"
-
-#include "intprops.h"
-#include "minmax.h"
-#include "xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* PostScript driver options: (defaults listed first)
-
-   output-file="pspp.ps"
-
-   paper-size=letter (see "papersize" file)
-   orientation=portrait|landscape
-   headers=on|off
-
-   left-margin=0.5in
-   right-margin=0.5in
-   top-margin=0.5in
-   bottom-margin=0.5in
-
-   prop-font=Times-Roman
-   emph-font=Times-Italic
-   fixed-font=Courier
-   font-size=10000
-
-   line-gutter=1pt
-   line-spacing=1pt
-   line-width=0.5pt
- */
-
-/* The number of `psus' (PostScript driver UnitS) per inch. */
-#define PSUS 72000
-
-/* A PostScript font. */
-struct font
-  {
-    struct afm *metrics;        /* Metrics. */
-    char *embed_fn;             /* Name of file to embed. */
-    char *encoding_fn;          /* Name of file with encoding. */
-  };
-
-/* PostScript output driver extension record. */
-struct ps_driver_ext
-  {
-    char *file_name;            /* Output file name. */
-    FILE *file;                 /* Output file. */
-
-    bool draw_headers;          /* Draw headers at top of page? */
-    int page_number;           /* Current page number. */
-
-    bool portrait;              /* Portrait mode? */
-    int paper_width;            /* Width of paper before dropping margins. */
-    int paper_length;           /* Length of paper before dropping margins. */
-    int left_margin;           /* Left margin in psus. */
-    int right_margin;          /* Right margin in psus. */
-    int top_margin;            /* Top margin in psus. */
-    int bottom_margin;         /* Bottom margin in psus. */
-
-    int line_gutter;           /* Space around lines. */
-    int line_space;            /* Space between lines. */
-    int line_width;            /* Width of lines. */
-
-    struct font *fonts[OUTP_FONT_CNT];
-    int last_font;              /* Index of last font set with setfont. */
-  };
-
-/* Transform logical y-ordinate Y into a page ordinate. */
-#define YT(Y) (this->length - (Y))
-
-static bool handle_option (struct outp_driver *this, const char *key,
-                           const struct string *val);
-static void draw_headers (struct outp_driver *this);
-
-static void write_ps_prologue (struct outp_driver *);
-
-static char *quote_ps_name (const char *string);
-
-static struct font *load_font (const char *string);
-static void free_font (struct font *);
-static void setup_font (struct outp_driver *this, struct font *, int index);
-\f
-/* Driver initialization. */
-
-static bool
-ps_open_driver (struct outp_driver *this, struct substring options)
-{
-  struct ps_driver_ext *x;
-  size_t i;
-
-  this->width = this->length = 0;
-  this->font_height = PSUS * 10 / 72;
-
-  this->ext = x = xmalloc (sizeof *x);
-  x->file_name = xstrdup ("pspp.ps");
-  x->file = NULL;
-  x->draw_headers = true;
-  x->page_number = 0;
-  x->portrait = true;
-  outp_get_paper_size ("", &x->paper_width, &x->paper_length);
-  x->left_margin = PSUS / 2;
-  x->right_margin = PSUS / 2;
-  x->top_margin = PSUS / 2;
-  x->bottom_margin = PSUS / 2;
-  x->line_gutter = PSUS / 72;
-  x->line_space = PSUS / 72;
-  x->line_width = PSUS / 144;
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    x->fonts[i] = NULL;
-
-  outp_parse_options (options, handle_option, this);
-
-  x->file = fn_open (x->file_name, "w");
-  if (x->file == NULL)
-    {
-      error (0, errno, _("opening PostScript output file \"%s\""),
-             x->file_name);
-      goto error;
-    }
-
-  if (x->portrait)
-    {
-      this->width = x->paper_width;
-      this->length = x->paper_length;
-    }
-  else
-    {
-      this->width = x->paper_length;
-      this->length = x->paper_width;
-    }
-  this->width -= x->left_margin + x->right_margin;
-  this->length -= x->top_margin + x->bottom_margin;
-  if (x->draw_headers)
-    {
-      int header_length = 3 * this->font_height;
-      this->length -= header_length;
-      x->top_margin += header_length;
-    }
-
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    if (x->fonts[i] == NULL)
-      {
-        const char *default_fonts[OUTP_FONT_CNT];
-        default_fonts[OUTP_FIXED] = "Courier.afm";
-        default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
-        default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
-        x->fonts[i] = load_font (default_fonts[i]);
-        if (x->fonts[i] == NULL)
-          goto error;
-      }
-
-  if (this->length / this->font_height < 15)
-    {
-      error (0, 0, _("The defined PostScript page is not long "
-                     "enough to hold margins and headers, plus least 15 "
-                     "lines of the default fonts.  In fact, there's only "
-                     "room for %d lines of each font at the default size "
-                     "of %d.%03d points."),
-          this->length / this->font_height,
-          this->font_height / 1000, this->font_height % 1000);
-      goto error;
-    }
-
-  this->fixed_width =
-    afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
-    * this->font_height / 1000;
-  this->prop_em_width =
-    afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
-    * this->font_height / 1000;
-
-  this->horiz_line_width[OUTP_L_NONE] = 0;
-  this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
-  this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
-                                           + 2 * x->line_width);
-  memcpy (this->vert_line_width, this->horiz_line_width,
-          sizeof this->vert_line_width);
-
-  write_ps_prologue (this);
-
-  return true;
-
- error:
-  this->class->close_driver (this);
-  return false;
-}
-
-static bool
-ps_close_driver (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  bool ok = true;
-  size_t i;
-
-  if (x->file != NULL)
-    {
-      fprintf (x->file,
-               "%%%%Trailer\n"
-               "%%%%Pages: %d\n"
-               "%%%%EOF\n",
-               x->page_number);
-
-      ok = fn_close (x->file_name, x->file) == 0;
-      if (!ok)
-        error (0, errno, _("closing PostScript output file \"%s\""),
-               x->file_name);
-    }
-
-  free (x->file_name);
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    free_font (x->fonts[i]);
-  free (x);
-
-  return ok;
-}
-
-/* Generic option types. */
-enum
-{
-  output_file_arg,
-  paper_size_arg,
-  orientation_arg,
-  line_style_arg,
-  boolean_arg,
-  pos_int_arg,
-  dimension_arg,
-  string_arg,
-  nonneg_int_arg
-};
-
-/* All the options that the PostScript driver supports. */
-static const struct outp_option option_tab[] =
-{
-  {"output-file",              output_file_arg,0},
-  {"paper-size",               paper_size_arg, 0},
-  {"orientation",              orientation_arg,0},
-
-  {"headers",                  boolean_arg,    1},
-
-  {"prop-font",                string_arg,     OUTP_PROPORTIONAL},
-  {"emph-font",                string_arg,     OUTP_EMPHASIS},
-  {"fixed-font",               string_arg,     OUTP_FIXED},
-
-  {"left-margin",              pos_int_arg,    0},
-  {"right-margin",             pos_int_arg,    1},
-  {"top-margin",               pos_int_arg,    2},
-  {"bottom-margin",            pos_int_arg,    3},
-  {"font-size",                        pos_int_arg,    4},
-
-  {"line-width",               dimension_arg,  0},
-  {"line-gutter",              dimension_arg,  1},
-  {"line-width",               dimension_arg,  2},
-  {NULL, 0, 0},
-};
-
-static bool
-handle_option (struct outp_driver *this, const char *key,
-               const struct string *val)
-{
-  struct ps_driver_ext *x = this->ext;
-  int subcat;
-  char *value = ds_cstr (val);
-
-  switch (outp_match_keyword (key, option_tab, &subcat))
-    {
-    case -1:
-      error (0, 0,
-             _("unknown configuration parameter `%s' for PostScript device "
-               "driver"), key);
-      break;
-    case output_file_arg:
-      free (x->file_name);
-      x->file_name = xstrdup (value);
-      break;
-    case paper_size_arg:
-      outp_get_paper_size (value, &this->width, &this->length);
-      break;
-    case orientation_arg:
-      if (!strcmp (value, "portrait"))
-       x->portrait = true;
-      else if (!strcmp (value, "landscape"))
-       x->portrait = false;
-      else
-       error (0, 0, _("unknown orientation `%s' (valid orientations are "
-                       "`portrait' and `landscape')"), value);
-      break;
-    case boolean_arg:
-      if (!strcmp (value, "on") || !strcmp (value, "true")
-          || !strcmp (value, "yes") || atoi (value))
-        x->draw_headers = true;
-      else if (!strcmp (value, "off") || !strcmp (value, "false")
-               || !strcmp (value, "no") || !strcmp (value, "0"))
-        x->draw_headers = false;
-      else
-        {
-          error (0, 0, _("boolean value expected for %s"), key);
-          return false;
-        }
-      break;
-    case pos_int_arg:
-      {
-       char *tail;
-       int arg;
-
-       errno = 0;
-       arg = strtol (value, &tail, 0);
-       if (arg < 1 || errno == ERANGE || *tail)
-         {
-           error (0, 0, _("positive integer value required for `%s'"), key);
-           break;
-         }
-       if ((subcat == 4 || subcat == 5) && arg < 1000)
-         {
-           error (0, 0, _("default font size must be at least 1 point (value "
-                           "of 1000 for key `%s')"), key);
-           break;
-         }
-       switch (subcat)
-         {
-         case 0:
-           x->left_margin = arg;
-           break;
-         case 1:
-           x->right_margin = arg;
-           break;
-         case 2:
-           x->top_margin = arg;
-           break;
-         case 3:
-           x->bottom_margin = arg;
-           break;
-         case 4:
-           this->font_height = arg;
-           break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case dimension_arg:
-      {
-       int dimension = outp_evaluate_dimension (value);
-
-       if (dimension <= 0)
-          break;
-       switch (subcat)
-         {
-         case 0:
-           x->line_width = dimension;
-           break;
-         case 1:
-           x->line_gutter = dimension;
-           break;
-         case 2:
-           x->line_width = dimension;
-           break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case string_arg:
-      {
-        struct font *font = load_font (value);
-        if (font != NULL)
-          {
-            struct font **dst = &x->fonts[subcat];
-            if (*dst != NULL)
-              free_font (*dst);
-            *dst = font;
-          }
-      }
-      break;
-    default:
-      NOT_REACHED ();
-    }
-
-  return true;
-}
-
-/* Looks for a PostScript font file or config file in all the
-   appropriate places.  Returns the file name on success, NULL on
-   failure. */
-static char *
-find_ps_file (const char *name)
-{
-  if (fn_is_absolute (name))
-    return xstrdup (name);
-  else
-    {
-      char *base_name = xasprintf ("psfonts/%s", name);
-      char *file_name = fn_search_path (base_name, config_path);
-      free (base_name);
-      return file_name;
-    }
-}
-\f
-/* Basic file operations. */
-
-/* Writes the PostScript prologue to file F. */
-static void
-write_ps_prologue (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  size_t embedded_cnt, preloaded_cnt;
-  size_t i;
-
-  fputs ("%!PS-Adobe-3.0\n", x->file);
-  fputs ("%%Pages: (atend)\n", x->file);
-
-  embedded_cnt = preloaded_cnt = 0;
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    {
-      bool embed = x->fonts[i]->embed_fn != NULL;
-      embedded_cnt += embed;
-      preloaded_cnt += !embed;
-    }
-  if (preloaded_cnt > 0)
-    {
-      fputs ("%%DocumentNeededResources: font", x->file);
-      for (i = 0; i < OUTP_FONT_CNT; i++)
-        {
-          struct font *f = x->fonts[i];
-          if (f->embed_fn == NULL)
-            fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
-        }
-      fputs ("\n", x->file);
-    }
-  if (embedded_cnt > 0)
-    {
-      fputs ("%%DocumentSuppliedResources: font", x->file);
-      for (i = 0; i < OUTP_FONT_CNT; i++)
-        {
-          struct font *f = x->fonts[i];
-          if (f->embed_fn != NULL)
-            fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
-        }
-      fputs ("\n", x->file);
-    }
-  fputs ("%%Copyright: This prologue is public domain.\n", x->file);
-  fprintf (x->file, "%%%%Creator: %s\n", version);
-  fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
-           x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
-  fprintf (x->file, "%%%%Orientation: %s\n",
-           x->portrait ? "Portrait" : "Landscape");
-  fputs ("%%EndComments\n", x->file);
-  fputs ("%%BeginDefaults\n", x->file);
-  fputs ("%%PageResources: font", x->file);
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
-  fputs ("\n", x->file);
-  fputs ("%%EndDefaults\n", x->file);
-  fputs ("%%BeginProlog\n", x->file);
-  fputs ("/ED{exch def}bind def\n", x->file);
-  fputs ("/L{moveto lineto stroke}bind def\n", x->file);
-  fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
-  fputs ("/S{show}bind def\n", x->file);
-  fputs ("/GS{glyphshow}def\n", x->file);
-  fputs ("/RF{\n", x->file);
-  fputs (" exch dup maxlength 1 add dict begin\n", x->file);
-  fputs (" {\n", x->file);
-  fputs ("  1 index/FID ne{def}{pop pop}ifelse\n", x->file);
-  fputs (" }forall\n", x->file);
-  fputs (" /Encoding ED\n", x->file);
-  fputs (" currentdict end\n", x->file);
-  fputs ("}bind def\n", x->file);
-  fputs ("/F{setfont}bind def\n", x->file);
-  fputs ("/EP{\n", x->file);
-  fputs (" pg restore\n", x->file);
-  fputs (" showpage\n", x->file);
-  fputs ("}bind def\n", x->file);
-  fputs ("/GB{\n", x->file);
-  fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
-  fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
-         x->file);
-  fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
-  fputs ("}bind def\n", x->file);
-  fputs ("/K{0 rmoveto}bind def\n", x->file);
-  fputs ("%%EndProlog\n", x->file);
-  fputs ("%%BeginSetup\n", x->file);
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    setup_font (this, x->fonts[i], i);
-  fputs ("%%EndSetup\n", x->file);
-}
-
-/* Returns STRING as a Postscript name, which is just '/'
-   followed by STRING unless characters need to be quoted.
-   The caller must free the string. */
-static char *
-quote_ps_name (const char *string)
-{
-  const char *cp;
-
-  for (cp = string; *cp != '\0'; cp++)
-    {
-      unsigned char c = *cp;
-      if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
-          && (cp == string || !isdigit (c)))
-        {
-          struct string out = DS_EMPTY_INITIALIZER;
-          ds_put_char (&out, '<');
-         for (cp = string; *cp != '\0'; cp++)
-            {
-              c = *cp;
-              ds_put_format (&out, "%02x", c);
-            }
-         ds_put_cstr (&out, ">cvn");
-          return ds_cstr (&out);
-        }
-    }
-  return xasprintf ("/%s", string);
-}
-
-static void
-ps_open_page (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  /* Assure page independence. */
-  x->last_font = -1;
-
-  x->page_number++;
-
-  fprintf (x->file,
-          "%%%%Page: %d %d\n"
-          "%%%%BeginPageSetup\n"
-          "/pg save def 0.001 dup scale\n",
-          x->page_number, x->page_number);
-
-  if (!x->portrait)
-    fprintf (x->file,
-            "%d 0 translate 90 rotate\n",
-            x->paper_width);
-
-  if (x->bottom_margin != 0 || x->left_margin != 0)
-    fprintf (x->file,
-            "%d %d translate\n",
-            x->left_margin, x->bottom_margin);
-
-  fprintf (x->file,
-          "/LW %d def %d setlinewidth\n"
-          "%%%%EndPageSetup\n",
-          x->line_width, x->line_width);
-
-  if (x->draw_headers)
-    draw_headers (this);
-}
-
-static void
-ps_close_page (struct outp_driver *this)
-{
-  struct ps_driver_ext *x = this->ext;
-  fputs ("%%PageTrailer\n"
-         "EP\n",
-         x->file);
-}
-
-static void
-ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
-  switch (s->type)
-    {
-    case SOM_CHART:
-      break;
-    default:
-      NOT_REACHED ();
-    }
-}
-\f
-/* Draws a line from (x0,y0) to (x1,y1). */
-static void
-dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
-{
-  struct ps_driver_ext *ext = this->ext;
-  fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
-}
-
-/* Draws a horizontal line X0...X2 at Y if LEFT says so,
-   shortening it to X0...X1 if SHORTEN is true.
-   Draws a horizontal line X1...X3 at Y if RIGHT says so,
-   shortening it to X2...X3 if SHORTEN is true. */
-static void
-horz_line (struct outp_driver *this,
-           int x0, int x1, int x2, int x3, int y,
-           enum outp_line_style left, enum outp_line_style right,
-           bool shorten)
-{
-  if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
-    dump_line (this, x0, y, x3, y);
-  else
-    {
-      if (left != OUTP_L_NONE)
-        dump_line (this, x0, y, shorten ? x1 : x2, y);
-      if (right != OUTP_L_NONE)
-        dump_line (this, shorten ? x2 : x1, y, x3, y);
-    }
-}
-
-/* Draws a vertical line Y0...Y2 at X if TOP says so,
-   shortening it to Y0...Y1 if SHORTEN is true.
-   Draws a vertical line Y1...Y3 at X if BOTTOM says so,
-   shortening it to Y2...Y3 if SHORTEN is true. */
-static void
-vert_line (struct outp_driver *this,
-           int y0, int y1, int y2, int y3, int x,
-           enum outp_line_style top, enum outp_line_style bottom,
-           bool shorten)
-{
-  if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
-    dump_line (this, x, y0, x, y3);
-  else
-    {
-      if (top != OUTP_L_NONE)
-        dump_line (this, x, y0, x, shorten ? y1 : y2);
-      if (bottom != OUTP_L_NONE)
-        dump_line (this, x, shorten ? y2 : y1, x, y3);
-    }
-}
-
-/* Draws a generalized intersection of lines in the rectangle
-   (X0,Y0)-(X3,Y3).  The line coming from the top to the center
-   is of style TOP, from left to center of style LEFT, from
-   bottom to center of style BOTTOM, and from right to center of
-   style RIGHT. */
-static void
-ps_line (struct outp_driver *this,
-         int x0, int y0, int x3, int y3,
-         enum outp_line_style top, enum outp_line_style left,
-         enum outp_line_style bottom, enum outp_line_style right)
-{
-  /* The algorithm here is somewhat subtle, to allow it to handle
-     all the kinds of intersections that we need.
-
-     Three additional ordinates are assigned along the x axis.  The
-     first is xc, midway between x0 and x3.  The others are x1 and
-     x2; for a single vertical line these are equal to xc, and for
-     a double vertical line they are the ordinates of the left and
-     right half of the double line.
-
-     yc, y1, and y2 are assigned similarly along the y axis.
-
-     The following diagram shows the coordinate system and output
-     for double top and bottom lines, single left line, and no
-     right line:
-
-                 x0       x1 xc  x2      x3
-               y0 ________________________
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-     y1 = y2 = yc |#########     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-                  |        #     #       |
-               y3 |________#_____#_______|
-  */
-  struct ps_driver_ext *ext = this->ext;
-
-  /* Offset from center of each line in a pair of double lines. */
-  int double_line_ofs = (ext->line_space + ext->line_width) / 2;
-
-  /* Are the lines along each axis single or double?
-     (It doesn't make sense to have different kinds of line on the
-     same axis, so we don't try to gracefully handle that case.) */
-  bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
-  bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
-
-  /* When horizontal lines are doubled,
-     the left-side line along y1 normally runs from x0 to x2,
-     and the right-side line along y1 from x3 to x1.
-     If the top-side line is also doubled, we shorten the y1 lines,
-     so that the left-side line runs only to x1,
-     and the right-side line only to x2.
-     Otherwise, the horizontal line at y = y1 below would cut off
-     the intersection, which looks ugly:
-               x0       x1     x2      x3
-             y0 ________________________
-                |        #     #       |
-                |        #     #       |
-                |        #     #       |
-                |        #     #       |
-             y1 |#########     ########|
-                |                      |
-                |                      |
-             y2 |######################|
-                |                      |
-                |                      |
-             y3 |______________________|
-     It is more of a judgment call when the horizontal line is
-     single.  We actually choose to cut off the line anyhow, as
-     shown in the first diagram above.
-  */
-  bool shorten_y1_lines = top == OUTP_L_DOUBLE;
-  bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
-  bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
-  int horz_line_ofs = double_vert ? double_line_ofs : 0;
-  int xc = (x0 + x3) / 2;
-  int x1 = xc - horz_line_ofs;
-  int x2 = xc + horz_line_ofs;
-
-  bool shorten_x1_lines = left == OUTP_L_DOUBLE;
-  bool shorten_x2_lines = right == OUTP_L_DOUBLE;
-  bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
-  int vert_line_ofs = double_horz ? double_line_ofs : 0;
-  int yc = (y0 + y3) / 2;
-  int y1 = yc - vert_line_ofs;
-  int y2 = yc + vert_line_ofs;
-
-  if (!double_horz)
-    horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
-  else
-    {
-      horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
-      horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
-    }
-
-  if (!double_vert)
-    vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
-  else
-    {
-      vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
-      vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
-    }
-}
-
-/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
-   and with the given JUSTIFICATION for THIS driver. */
-static int
-draw_text (struct outp_driver *this,
-           const char *string, int x, int y, int max_width,
-           enum outp_justification justification)
-{
-  struct outp_text text;
-  int width;
-
-  text.font = OUTP_PROPORTIONAL;
-  text.justification = justification;
-  text.string = ss_cstr (string);
-  text.h = max_width;
-  text.v = this->font_height;
-  text.x = x;
-  text.y = y;
-  this->class->text_metrics (this, &text, &width, NULL);
-  this->class->text_draw (this, &text);
-  return width;
-}
-
-/* Writes LEFT left-justified and RIGHT right-justified within
-   (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
-static void
-draw_header_line (struct outp_driver *this,
-                  const char *left, const char *right,
-                  int x0, int x1, int y)
-{
-  int right_width = 0;
-  if (right != NULL)
-    right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
-                   + this->prop_em_width);
-  if (left != NULL)
-    draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
-}
-
-/* Draw top of page headers for THIS driver. */
-static void
-draw_headers (struct outp_driver *this)
-{
-  struct ps_driver_ext *ext = this->ext;
-  char *r1, *r2;
-  int x0, x1;
-  int y;
-
-  y = -3 * this->font_height;
-  x0 = this->prop_em_width;
-  x1 = this->width - this->prop_em_width;
-
-  /* Draw box. */
-  fprintf (ext->file, "%d %d %d %d GB\n",
-          0, YT (y),
-           this->width, YT (y + 2 * this->font_height + ext->line_gutter));
-  y += ext->line_width + ext->line_gutter;
-
-  r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
-  r2 = xasprintf ("%s - %s", version, host_system);
-
-  draw_header_line (this, outp_title, r1, x0, x1, y);
-  y += this->font_height;
-
-  draw_header_line (this, outp_subtitle, r2, x0, x1, y);
-
-  free (r1);
-  free (r2);
-}
-\f
-/* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
-   given FONT.
-   The characters are justified according to JUSTIFICATION in a
-   field that has WIDTH_LEFT space remaining after the characters
-   themselves are accounted for.
-   Before character I is written, its x-position is adjusted by
-   KERNS[I]. */
-static void
-write_text (struct outp_driver *this,
-            int x0, int y0,
-            enum outp_font font,
-            enum outp_justification justification,
-            const struct afm_character **chars, int *kerns, size_t char_cnt,
-            int width_left)
-{
-  struct ps_driver_ext *ext = this->ext;
-  struct afm *afm = ext->fonts[font]->metrics;
-  struct string out;
-  size_t i, j;
-
-  if (justification == OUTP_RIGHT)
-    x0 += width_left;
-  else if (justification == OUTP_CENTER)
-    x0 += width_left / 2;
-  y0 += afm_get_ascent (afm) * this->font_height / 1000;
-
-  fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
-
-  if (ext->last_font != font)
-    {
-      ext->last_font = font;
-      fprintf (ext->file, "F%d setfont\n", font);
-    }
-
-  ds_init_empty (&out);
-  for (i = 0; i < char_cnt; i = j)
-    {
-      for (j = i + 1; j < char_cnt; j++)
-        if (kerns[j] != 0)
-          break;
-
-      if (kerns[i] != 0)
-        fprintf (ext->file, "%d K", kerns[i]);
-      while (i < j)
-        {
-          size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
-          if (encoded > 0)
-            {
-              fprintf (ext->file, "%sS\n", ds_cstr (&out));
-              ds_clear (&out);
-              i += encoded;
-            }
-
-          if (i < j)
-            {
-              fprintf (ext->file, "/%s GS\n", chars[i]->name);
-              i++;
-            }
-        }
-    }
-  ds_destroy (&out);
-}
-
-/* State of a text formatting operation. */
-struct text_state
-  {
-    /* Input. */
-    const struct outp_text *text;
-    bool draw;
-
-    /* Output. */
-    const struct afm_character **glyphs;
-    int *glyph_kerns;
-
-    /* State. */
-    size_t glyph_cnt;           /* Number of glyphs output. */
-    int width_left;            /* Width left over. */
-    int height_left;            /* Height left over. */
-
-    /* State as of last space. */
-    const char *space_char;     /* Just past last space. */
-    size_t space_glyph_cnt;     /* Number of glyphs as of last space. */
-    int space_width_left;       /* Width left over as of last space. */
-
-    /* Statistics. */
-    int max_width;             /* Widest line so far. */
-  };
-
-/* Adjusts S to complete a line of text,
-   and draws the current line if appropriate. */
-static void
-finish_line (struct outp_driver *this, struct text_state *s)
-{
-  int width;
-
-  if (s->draw)
-    {
-      write_text (this,
-                  s->text->x, s->text->y + (s->text->v - s->height_left),
-                  s->text->font,
-                  s->text->justification,
-                  s->glyphs, s->glyph_kerns, s->glyph_cnt,
-                  s->width_left);
-      s->glyph_cnt = 0;
-    }
-
-  /* Update maximum width. */
-  width = s->text->h - s->width_left;
-  if (width > s->max_width)
-    s->max_width = width;
-
-  /* Move to next line. */
-  s->width_left = s->text->h;
-  s->height_left -= this->font_height;
-
-  /* No spaces on this line yet. */
-  s->space_char = NULL;
-}
-
-/* Format TEXT on THIS driver.
-   If DRAW is nonzero, draw the text.
-   The width of the widest line is stored into *WIDTH, if WIDTH
-   is nonnull.
-   The total height of the text written is stored into *HEIGHT,
-   if HEIGHT is nonnull. */
-static void
-text (struct outp_driver *this, const struct outp_text *text, bool draw,
-      int *width, int *height)
-{
-  struct ps_driver_ext *ext = this->ext;
-  struct afm *afm = ext->fonts[text->font]->metrics;
-  const char *cp;
-  size_t glyph_cap;
-  struct text_state s;
-
-  s.text = text;
-  s.draw = draw;
-
-  s.glyphs = NULL;
-  s.glyph_kerns = NULL;
-  glyph_cap = 0;
-
-  s.glyph_cnt = 0;
-  s.width_left = s.text->h;
-  s.height_left = s.text->v;
-
-  s.space_char = 0;
-
-  s.max_width = 0;
-
-  cp = ss_data (s.text->string);
-  while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
-    {
-      const struct afm_character *cur;
-      int char_width;
-      int kern_adjust;
-
-      if (*cp == '\n')
-        {
-          finish_line (this, &s);
-          cp++;
-          continue;
-        }
-
-      /* Get character and resolve ligatures. */
-      cur = afm_get_character (afm, *cp);
-      while (++cp < ss_end (s.text->string))
-        {
-          const struct afm_character *next = afm_get_character (afm, *cp);
-          const struct afm_character *ligature = afm_get_ligature (cur, next);
-          if (ligature == NULL)
-            break;
-          cur = ligature;
-        }
-      char_width = cur->width * this->font_height / 1000;
-
-      /* Get kern adjustment. */
-      if (s.glyph_cnt > 0)
-        kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
-                       * this->font_height / 1000);
-      else
-        kern_adjust = 0;
-
-      /* Record the current status if this is a space character. */
-      if (cur->code == ' ' && cp > ss_data (s.text->string))
-       {
-         s.space_char = cp;
-         s.space_glyph_cnt = s.glyph_cnt;
-          s.space_width_left = s.width_left;
-       }
-
-      /* Enough room on this line? */
-      if (char_width + kern_adjust > s.width_left)
-       {
-         if (s.space_char == NULL)
-            {
-              finish_line (this, &s);
-              kern_adjust = 0;
-            }
-          else
-            {
-              cp = s.space_char;
-              s.glyph_cnt = s.space_glyph_cnt;
-              s.width_left = s.space_width_left;
-              finish_line (this, &s);
-              continue;
-            }
-       }
-
-      if (s.glyph_cnt >= glyph_cap)
-        {
-          glyph_cap = 2 * (glyph_cap + 8);
-          s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
-          s.glyph_kerns = xnrealloc (s.glyph_kerns,
-                                     glyph_cap, sizeof *s.glyph_kerns);
-        }
-      s.glyphs[s.glyph_cnt] = cur;
-      s.glyph_kerns[s.glyph_cnt] = kern_adjust;
-      s.glyph_cnt++;
-
-      s.width_left -= char_width + kern_adjust;
-    }
-  if (s.height_left >= this->font_height && s.glyph_cnt > 0)
-    finish_line (this, &s);
-
-  if (width != NULL)
-    *width = s.max_width;
-  if (height != NULL)
-    *height = text->v - s.height_left;
-  free (s.glyphs);
-  free (s.glyph_kerns);
-}
-
-static void
-ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
-                 int *width, int *height)
-{
-  text (this, t, false, width, height);
-}
-
-static void
-ps_text_draw (struct outp_driver *this, const struct outp_text *t)
-{
-  assert (this->page_open);
-  text (this, t, true, NULL, NULL);
-}
-\f
-static void
-ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
-{
-#ifdef NO_CHARTS
-  ch->lp = NULL;
-#else
-  struct ps_driver_ext *x = this->ext;
-  char page_size[128];
-  int size;
-  int x_origin, y_origin;
-
-  ch->file = tmpfile ();
-  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;
-
-  snprintf (page_size, sizeof page_size,
-            "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
-            (double) size / PSUS, (double) size / PSUS,
-            (double) x_origin / PSUS, (double) y_origin / PSUS);
-
-  ch->pl_params = pl_newplparams ();
-  pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
-  ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
-#endif
-}
-
-static void
-ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifndef NO_CHARTS
-  struct ps_driver_ext *x = this->ext;
-  char buf[BUFSIZ];
-  static int doc_num = 0;
-
-  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))
-    continue;
-  fclose (ch->file);
-
-  fputs ("%%EndDocument\n"
-         "end\n"
-         "sp restore\n",
-         x->file);
-  outp_close_page (this);
-#endif
-}
-\f
-static void embed_font (struct outp_driver *this, struct font *font);
-static void reencode_font (struct outp_driver *this, struct font *font);
-
-/* Loads and returns the font for STRING, which has the format
-   "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
-   PFA or PFB file's name, and ENC is the encoding file's name.
-   PFA and ENC are optional.
-   Returns a null pointer if unsuccessful. */
-static struct font *
-load_font (const char *string_)
-{
-  char *string = xstrdup (string_);
-  struct font *font;
-  char *position = string;
-  char *token;
-  char *afm_file_name;
-
-  font = xmalloc (sizeof *font);
-  font->metrics = NULL;
-  font->embed_fn = NULL;
-  font->encoding_fn = NULL;
-
-  token = strsep (&position, ",");
-  if (token == NULL)
-    {
-      error (0, 0, _("\"%s\": bad font specification"), string);
-      goto error;
-    }
-
-  /* Read AFM file. */
-  afm_file_name = find_ps_file (token);
-  if (afm_file_name == NULL)
-    {
-      error (0, 0, _("could not find AFM file \"%s\""), token);
-      goto error;
-    }
-  font->metrics = afm_open (afm_file_name);
-  free (afm_file_name);
-  if (font->metrics == NULL)
-    goto error;
-
-  /* Find font file to embed. */
-  token = strsep (&position, ",");
-  if (token != NULL && *token != '\0')
-    {
-      font->embed_fn = find_ps_file (token);
-      if (font->embed_fn == NULL)
-        error (0, 0, _("could not find font \"%s\""), token);
-    }
-
-  /* Find encoding. */
-  token = strsep (&position, ",");
-  if (token != NULL && *token == '\0')
-    {
-      font->encoding_fn = find_ps_file (token);
-      if (font->encoding_fn == NULL)
-        error (0, 0, _("could not find encoding \"%s\""), token);
-    }
-
-  free (string);
-  return font;
-
- error:
-  free (string);
-  free_font (font);
-  return NULL;
-}
-
-/* Frees FONT. */
-static void
-free_font (struct font *font)
-{
-  if (font != NULL)
-    {
-      afm_close (font->metrics);
-      free (font->embed_fn);
-      free (font->encoding_fn);
-      free (font);
-    }
-}
-
-/* Emits PostScript code to embed FONT (if necessary), scale it
-   to the proper size, re-encode it (if necessary), and store the
-   resulting font as an object named F#, where INDEX is
-   substituted for #. */
-static void
-setup_font (struct outp_driver *this, struct font *font, int index)
-{
-  struct ps_driver_ext *x = this->ext;
-  char *ps_name;
-
-  if (font->embed_fn != NULL)
-    embed_font (this, font);
-  else
-    fprintf (x->file, "%%%%IncludeResource: font %s\n",
-             afm_get_findfont_name (font->metrics));
-
-  ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
-  fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
-  free (ps_name);
-
-  if (font->encoding_fn != NULL)
-    reencode_font (this, font);
-
-  fprintf (x->file, "/F%d ED\n", index);
-}
-
-/* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
-   end-of-file or on error. */
-static void
-copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
-{
-  while (copy_bytes > 0)
-    {
-      char buffer[BUFSIZ];
-      unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
-      size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
-      size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
-      if (write_bytes != chunk_bytes)
-        break;
-      copy_bytes -= chunk_bytes;
-    }
-}
-
-/* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
-   end-of-file or on error.  The bytes are translated into
-   hexadecimal during copying and broken into lines with
-   new-line characters. */
-static void
-copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
-{
-  unsigned long i;
-
-  for (i = 0; i < copy_bytes; i++)
-    {
-      int c = getc (src);
-      if (c == EOF)
-        break;
-      if (i > 0 && i % 36 == 0)
-        putc ('\n', dst);
-      fprintf (dst, "%02X", c);
-    }
-  putc ('\n', dst);
-}
-
-/* Embeds the given FONT into THIS driver's output stream. */
-static void
-embed_font (struct outp_driver *this, struct font *font)
-{
-  struct ps_driver_ext *x = this->ext;
-  FILE *file;
-  int c;
-
-  file = fopen (font->embed_fn, "rb");
-  if (file == NULL)
-    {
-      error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
-      return;
-    }
-
-  fprintf (x->file, "%%%%BeginResource: font %s\n",
-           afm_get_findfont_name (font->metrics));
-
-  c = getc (file);
-  ungetc (c, file);
-  if (c != 128)
-    {
-      /* PFA file.  Copy literally. */
-      copy_bytes_literally (file, x->file, ULONG_MAX);
-    }
-  else
-    {
-      /* PFB file.  Translate as specified in Adobe Technical
-         Note #5040. */
-      while ((c = getc (file)) == 128)
-        {
-          int type;
-          unsigned long length;
-
-          type = getc (file);
-          if (type == 3)
-            break;
-
-          length = getc (file);
-          length |= (unsigned long) getc (file) << 8;
-          length |= (unsigned long) getc (file) << 16;
-          length |= (unsigned long) getc (file) << 24;
-
-          if (type == 1)
-            copy_bytes_literally (file, x->file, length);
-          else if (type == 2)
-            copy_bytes_as_hex (file, x->file, length);
-          else
-            break;
-        }
-    }
-  if (freaderror (file))
-    error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
-  fputs ("%%EndResource\n", x->file);
-}
-
-/* Re-encodes FONT according to the specified encoding. */
-static void
-reencode_font (struct outp_driver *this, struct font *font)
-{
-  struct ps_driver_ext *x = this->ext;
-
-  struct string line;
-
-  int line_number;
-  FILE *file;
-
-  char *tab[256];
-
-  int i;
-
-  file = fopen (font->encoding_fn, "r");
-  if (file == NULL)
-    {
-      error (errno, 0, _("cannot open font encoding file \"%s\""),
-             font->encoding_fn);
-      return;
-    }
-
-  for (i = 0; i < 256; i++)
-    tab[i] = NULL;
-
-  line_number = 0;
-
-  ds_init_empty (&line);
-  while (ds_read_config_line (&line, &line_number, file))
-    {
-      char *pschar, *code;
-      char *save_ptr, *tail;
-      int code_val;
-
-      if (ds_is_empty (&line) == 0)
-        continue;
-
-      pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
-      code = strtok_r (NULL, " \t\r\n", &save_ptr);
-      if (pschar == NULL || code == NULL)
-        continue;
-
-      code_val = strtol (code, &tail, 0);
-      if (*tail)
-        {
-          error_at_line (0, 0, font->encoding_fn, line_number,
-                         _("invalid numeric format"));
-          continue;
-        }
-      if (code_val < 0 || code_val > 255)
-        continue;
-      if (tab[code_val] != 0)
-        free (tab[code_val]);
-      tab[code_val] = xstrdup (pschar);
-    }
-  ds_destroy (&line);
-
-  fputs ("[", x->file);
-  for (i = 0; i < 256; i++)
-    {
-      char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
-      fprintf (x->file, "%s\n", name);
-      free (name);
-      free (tab[i]);
-    }
-  fputs ("] RF\n", x->file);
-
-  if (freaderror (file) != 0)
-    error (errno, 0, _("closing Postscript encoding \"%s\""),
-           font->encoding_fn);
-}
-
-/* PostScript driver class. */
-const struct outp_class postscript_class =
-{
-  "postscript",
-  0,
-
-  ps_open_driver,
-  ps_close_driver,
-
-  ps_open_page,
-  ps_close_page,
-  NULL,
-
-  ps_submit,
-
-  ps_line,
-  ps_text_metrics,
-  ps_text_draw,
-
-  ps_chart_initialise,
-  ps_chart_finalise
-};
index 72edf17f08b7f6e9e0dec91ad8b56f5b9e87b88b..f8736e629c0148279ae8ed18b120c519d7de5271 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <data/settings.h>
 
+#include "error.h"
 #include "minmax.h"
 #include "xalloc.h"
 
@@ -44,7 +45,6 @@
 #define _(msgid) gettext (msgid)
 \f
 const struct som_table_class tab_table_class;
-static char *command_name;
 
 /* Returns the font to use for a cell with the given OPTIONS. */
 static enum outp_font
@@ -57,13 +57,13 @@ options_to_font (unsigned options)
 
 /* Creates a table with NC columns and NR rows. */
 struct tab_table *
-tab_create (int nc, int nr, int reallocable UNUSED)
+tab_create (int nc, int nr)
 {
   struct tab_table *t;
 
   t = pool_create_container (struct tab_table, container);
+  t->ref_cnt = 1;
   t->col_style = TAB_COL_NONE;
-  t->col_group = 0;
   t->title = NULL;
   t->flags = SOMF_NONE;
   t->nr = nr;
@@ -81,21 +81,33 @@ tab_create (int nc, int nr, int reallocable UNUSED)
   memset (t->rv, UCHAR_MAX, nr * (nc + 1));
 
   t->dim = NULL;
-  t->w = t->h = NULL;
   t->col_ofs = t->row_ofs = 0;
 
   return t;
 }
 
-/* Destroys table T. */
+/* Increases T's reference count and, if this causes T's
+   reference count to reach 0, destroys T. */
 void
 tab_destroy (struct tab_table *t)
 {
-  assert (t != NULL);
+  assert (t->ref_cnt > 0);
+  if (--t->ref_cnt > 0)
+    return;
+  if (t->dim_free != NULL)
+    t->dim_free (t->dim_aux);
   free (t->title);
   pool_destroy (t->container);
 }
 
+/* Increases T's reference count. */
+void
+tab_ref (struct tab_table *t)
+{
+  assert (t->ref_cnt > 0);
+  t->ref_cnt++;
+}
+
 /* Sets the width and height of a table, in columns and rows,
    respectively.  Use only to reduce the size of a table, since it
    does not change the amount of allocated memory. */
@@ -110,7 +122,7 @@ tab_resize (struct tab_table *t, int nc, int nr)
     }
   if (nr != -1)
     {
-      assert (nr + t->row_ofs <= t->nr);
+      assert (nr + t->row_ofs <= tab_nr (t));
       t->nr = nr + t->row_ofs;
     }
 }
@@ -133,16 +145,16 @@ tab_realloc (struct tab_table *t, int nc, int nr)
     tab_offset (t, 0, 0);
 
   if (nc == -1)
-    nc = t->nc;
+    nc = tab_nc (t);
   if (nr == -1)
-    nr = t->nr;
+    nr = tab_nr (t);
 
-  assert (nc == t->nc);
+  assert (nc == tab_nc (t));
 
   if (nc > t->cf)
     {
-      int mr1 = MIN (nr, t->nr);
-      int mc1 = MIN (nc, t->nc);
+      int mr1 = MIN (nr, tab_nr (t));
+      int mc1 = MIN (nc, tab_nc (t));
 
       struct substring *new_cc;
       unsigned char *new_ct;
@@ -152,9 +164,9 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       new_ct = pool_malloc (t->container, nr * nc);
       for (r = 0; r < mr1; r++)
        {
-         memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
-         memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
-         memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
+         memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
+         memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
+         memset (&new_ct[r * nc + tab_nc (t)], TAB_EMPTY, nc - tab_nc (t));
        }
       pool_free (t->container, t->cc);
       pool_free (t->container, t->ct);
@@ -162,7 +174,7 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       t->ct = new_ct;
       t->cf = nc;
     }
-  else if (nr != t->nr)
+  else if (nr != tab_nr (t))
     {
       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
       t->ct = pool_realloc (t->container, t->ct, nr * nc);
@@ -170,15 +182,15 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
 
-      if (nr > t->nr)
+      if (nr > tab_nr (t))
        {
-         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));
+         memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
+         memset (&t->rv[(nc + 1) * tab_nr (t)], UCHAR_MAX,
+                  (nr - tab_nr (t)) * (nc + 1));
        }
     }
 
-  memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
+  memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
 
   t->nr = nr;
   t->nc = nc;
@@ -210,14 +222,12 @@ tab_headers (struct tab_table *table, int l, int r, int t, int b)
 /* Set up table T so that, when it is an appropriate size, it will be
    displayed across the page in columns.
 
-   STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
-   as a unit. */
+   STYLE is a TAB_COL_* constant. */
 void
-tab_columns (struct tab_table *t, int style, int group)
+tab_columns (struct tab_table *t, int style)
 {
   assert (t != NULL);
   t->col_style = style;
-  t->col_group = group;
 }
 \f
 /* Rules. */
@@ -230,16 +240,16 @@ tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
   assert (t != NULL);
 
 #if DEBUGGING
-  if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
-      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+  if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
     {
       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
                "table size (%d,%d)\n"),
              x, t->col_ofs, x + t->col_ofs,
              y1, t->row_ofs, y1 + t->row_ofs,
              y2, t->row_ofs, y2 + t->row_ofs,
-             t->nc, t->nr);
+             tab_nc (t), tab_nr (t));
       return;
     }
 #endif
@@ -249,10 +259,10 @@ tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
   y2 += t->row_ofs;
 
   assert (x  > 0);
-  assert (x  < t->nc);
+  assert (x  < tab_nc (t));
   assert (y1 >= 0);
   assert (y2 >= y1);
-  assert (y2 <=  t->nr);
+  assert (y2 <=  tab_nr (t));
 
   if (style != -1)
     {
@@ -274,10 +284,10 @@ tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
   y += t->row_ofs;
 
   assert (y >= 0);
-  assert (y <= t->nr);
+  assert (y <= tab_nr (t));
   assert (x2 >= x1 );
   assert (x1 >= 0 );
-  assert (x2 < t->nc);
+  assert (x2 < tab_nc (t));
 
   if (style != -1)
     {
@@ -300,10 +310,10 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   assert (t != NULL);
 
 #if DEBUGGING
-  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
-      || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
-      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
+      || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
     {
       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
                "in table size (%d,%d)\n"),
@@ -311,7 +321,7 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
              y1, t->row_ofs, y1 + t->row_ofs,
              x2, t->col_ofs, x2 + t->col_ofs,
              y2, t->row_ofs, y2 + t->row_ofs,
-             t->nc, t->nr);
+             tab_nc (t), tab_nr (t));
       NOT_REACHED ();
     }
 #endif
@@ -325,8 +335,8 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   assert (y2 >= y1);
   assert (x1 >= 0);
   assert (y1 >= 0);
-  assert (x2 < t->nc);
-  assert (y2 < t->nr);
+  assert (x2 < tab_nc (t));
+  assert (y2 < tab_nr (t));
 
   if (f_h != -1)
     {
@@ -386,12 +396,22 @@ tab_title (struct tab_table *t, const char *title, ...)
   va_end (args);
 }
 
-/* Set DIM_FUNC as the dimension function for table T. */
+/* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
+   dimension function for table T.
+
+   DIM_FUNC must not assume that it is called from the same
+   context as tab_dim; for example, table T might be kept in
+   memory and, thus, DIM_FUNC might be called after the currently
+   running command completes.  If it is non-null, FREE_FUNC is
+   called when the table is destroyed, to allow any data
+   allocated for use by DIM_FUNC to be freed.  */
 void
-tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
+tab_dim (struct tab_table *t,
+         tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
 {
-  assert (t != NULL && t->dim == NULL);
+  assert (t->dim == NULL);
   t->dim = dim_func;
+  t->dim_free = free_func;
   t->dim_aux = aux;
 }
 
@@ -400,85 +420,76 @@ tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
    wrapping.  The width will be no larger than the page width minus
    left and right rule widths. */
 int
-tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
+tab_natural_width (const struct tab_rendering *r, int col)
 {
-  int width;
+  const struct tab_table *t = r->table;
+  int width, row, max_width;
 
-  assert (t != NULL && c >= 0 && c < t->nc);
-  {
-    int r;
+  assert (col >= 0 && col < tab_nc (t));
 
-    for (width = r = 0; r < t->nr; r++)
-      {
-       struct outp_text text;
-       unsigned char opt = t->ct[c + r * t->cf];
-        int w;
+  width = 0;
+  for (row = 0; row < tab_nr (t); row++)
+    {
+      struct outp_text text;
+      unsigned char opt = t->ct[col + row * t->cf];
+      int w;
 
-       if (opt & (TAB_JOIN | TAB_EMPTY))
-         continue;
+      if (opt & (TAB_JOIN | TAB_EMPTY))
+        continue;
 
-       text.string = t->cc[c + r * t->cf];
-       text.justification = OUTP_LEFT;
-        text.font = options_to_font (opt);
-        text.h = text.v = INT_MAX;
+      text.string = t->cc[col + row * t->cf];
+      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;
-      }
-  }
+      r->driver->class->text_metrics (r->driver, &text, &w, NULL);
+      if (w > width)
+        width = w;
+    }
 
   if (width == 0)
     {
       /* FIXME: This is an ugly kluge to compensate for the fact
          that we don't let joined cells contribute to column
          widths. */
-      width = d->prop_em_width * 8;
+      width = r->driver->prop_em_width * 8;
     }
 
-  {
-    const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
-
-    if (width > clamp)
-      width = clamp;
-  }
-
-  return width;
+  max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
+  return MIN (width, max_width);
 }
 
 /* Returns the natural height of row R in table T for driver D, that
    is, the minimum height necessary to display the information in the
    cell at the widths set for each column. */
 int
-tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
+tab_natural_height (const struct tab_rendering *r, int row)
 {
-  int height;
+  const struct tab_table *t = r->table;
+  int height, col;
 
-  assert (t != NULL && r >= 0 && r < t->nr);
+  assert (row >= 0 && row < tab_nr (t));
 
-  {
-    int c;
-
-    for (height = d->font_height, c = 0; c < t->nc; c++)
-      {
-       struct outp_text text;
-       unsigned char opt = t->ct[c + r * t->cf];
-        int h;
-
-       if (opt & (TAB_JOIN | TAB_EMPTY))
-         continue;
-
-       text.string = t->cc[c + r * t->cf];
-        text.justification = OUTP_LEFT;
-        text.font = options_to_font (opt);
-       text.h = t->w[c];
-        text.v = INT_MAX;
-       d->class->text_metrics (d, &text, NULL, &h);
-
-       if (h > height)
-         height = h;
-      }
-  }
+  height = r->driver->font_height;
+  for (col = 0; col < tab_nc (t); col++)
+    {
+      struct outp_text text;
+      unsigned char opt = t->ct[col + row * t->cf];
+      int h;
+
+      if (opt & (TAB_JOIN | TAB_EMPTY))
+        continue;
+
+      text.string = t->cc[col + row * t->cf];
+      text.justification = OUTP_LEFT;
+      text.font = options_to_font (opt);
+      text.h = r->w[col];
+      text.v = INT_MAX;
+      r->driver->class->text_metrics (r->driver, &text, NULL, &h);
+
+      if (h > height)
+        height = h;
+    }
 
   return height;
 }
@@ -486,18 +497,16 @@ tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
 /* Callback function to set all columns and rows to their natural
    dimensions.  Not really meant to be called directly.  */
 void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
-                        void *aux UNUSED)
+tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
 {
+  const struct tab_table *t = r->table;
   int i;
 
-  assert (t != NULL);
-
-  for (i = 0; i < t->nc; i++)
-    t->w[i] = tab_natural_width (t, d, i);
+  for (i = 0; i < tab_nc (t); i++)
+    r->w[i] = tab_natural_width (r, i);
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = tab_natural_height (r, i);
 }
 
 \f
@@ -515,14 +524,14 @@ tab_value (struct tab_table *table, int c, int r, unsigned char opt,
   assert (table != NULL && v != NULL && f != NULL);
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -547,22 +556,22 @@ tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
   assert (table != NULL && w <= 40);
 
   assert (c >= 0);
-  assert (c < table->nc);
+  assert (c < tab_nc (table));
   assert (r >= 0);
-  assert (r < table->nr);
+  assert (r < tab_nr (table));
 
   f = fmt_for_output (FMT_F, w, d);
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -593,9 +602,9 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
   assert (table != NULL);
 
   assert (c >= 0);
-  assert (c < table->nc);
+  assert (c < tab_nc (table));
   assert (r >= 0);
-  assert (r < table->nr);
+  assert (r < tab_nr (table));
 
   if ( fmt == NULL)
     fmt = settings_get_format ();
@@ -604,14 +613,14 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -629,21 +638,21 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
 static void
 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
 {
-  assert (c >= 0);
-  assert (r >= 0);
-  assert (c < table->nc);
-  assert (r < table->nr);
+  assert (c >= 0 );
+  assert (r >= 0 );
+  assert (c < tab_nc (table));
+  assert (r < tab_nr (table));
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -685,14 +694,14 @@ do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   assert (y1 + table->row_ofs >= 0);
   assert (y2 >= y1);
   assert (x2 >= x1);
-  assert (y2 + table->row_ofs < table->nr);
-  assert (x2 + table->col_ofs < table->nc);
+  assert (y2 + table->row_ofs < tab_nr (table));
+  assert (x2 + table->col_ofs < tab_nc (table));
 
 #if DEBUGGING
-  if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
-      || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
-      || x2 < x1 || x2 + table->col_ofs >= table->nc
-      || y2 < y2 || y2 + table->row_ofs >= table->nr)
+  if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
+      || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
+      || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
+      || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_joint_text(): bad cell "
              "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
@@ -700,7 +709,7 @@ do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
              y1, table->row_ofs, y1 + table->row_ofs,
              x2, table->col_ofs, x2 + table->col_ofs,
              y2, table->row_ofs, y2 + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -708,7 +717,6 @@ do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
 
   j = pool_alloc (table->container, sizeof *j);
-  j->hit = 0;
   j->x1 = x1 + table->col_ofs;
   j->y1 = y1 + table->row_ofs;
   j->x2 = ++x2 + table->col_ofs;
@@ -764,50 +772,25 @@ tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
                      pool_vasprintf (table->container, format, args));
   va_end (args);
 }
-
-/* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
-void
-tab_raw (struct tab_table *table, int c, int r, unsigned opt,
-        struct substring *string)
-{
-  assert (table != NULL && string != NULL);
-
-#if DEBUGGING
-  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
-    {
-      printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
-             "(%d,%d)\n",
-             c, table->col_ofs, c + table->col_ofs,
-             r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
-      return;
-    }
-#endif
-
-  table->cc[c + r * table->cf] = *string;
-  table->ct[c + r * table->cf] = opt;
-}
 \f
 /* Miscellaneous. */
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
 {
-  t->w[0] = tab_natural_width (t, d, 0);
-  t->h[0] = d->font_height;
+  r->w[0] = tab_natural_width (r, 0);
+  r->h[0] = r->driver->font_height;
 }
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+wrap_dim (struct tab_rendering *r, void *aux UNUSED)
 {
-  t->w[0] = tab_natural_width (t, d, 0);
-  t->h[0] = tab_natural_height (t, d, 0);
+  r->w[0] = tab_natural_width (r, 0);
+  r->h[0] = tab_natural_height (r, 0);
 }
 
 static void
@@ -815,7 +798,7 @@ do_tab_output_text (struct tab_table *t, int options, char *text)
 {
   do_tab_text (t, 0, 0, options, text);
   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
-  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
+  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
   tab_submit (t);
 }
 
@@ -825,7 +808,7 @@ do_tab_output_text (struct tab_table *t, int options, char *text)
 void
 tab_output_text (int options, const char *text)
 {
-  struct tab_table *table = tab_create (1, 1, 0);
+  struct tab_table *table = tab_create (1, 1);
   do_tab_output_text (table, options, pool_strdup (table->container, text));
 }
 
@@ -839,7 +822,7 @@ tab_output_text_format (int options, const char *format, ...)
   struct tab_table *table;
   va_list args;
 
-  table = tab_create (1, 1, 0);
+  table = tab_create (1, 1);
 
   va_start (args, format);
   do_tab_output_text (table, options,
@@ -880,14 +863,14 @@ tab_offset (struct tab_table *t, int col, int row)
 
   assert (t != NULL);
 #if DEBUGGING
-  if (row < -1 || row > t->nr)
+  if (row < -1 || row > tab_nr (t))
     {
-      printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
+      printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
       NOT_REACHED ();
     }
-  if (col < -1 || col > t->nc)
+  if (col < -1 || col > tab_nc (t))
     {
-      printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
+      printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
       NOT_REACHED ();
     }
 #endif
@@ -909,29 +892,46 @@ tab_next_row (struct tab_table *t)
   assert (t != NULL);
   t->cc += t->cf;
   t->ct += t->cf;
-  if (++t->row_ofs >= t->nr)
-    tab_realloc (t, -1, t->nr * 4 / 3);
+  if (++t->row_ofs >= tab_nr (t))
+    tab_realloc (t, -1, tab_nr (t) * 4 / 3);
 }
 \f
-static struct tab_table *t;
-static struct outp_driver *d;
-static int tab_hit;
+/* Return the number of columns and rows in the table into N_COLUMNS
+   and N_ROWS, respectively. */
+static void
+tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
+{
+  struct tab_table *t = t_->ext;
+  *n_columns = t->nc;
+  *n_rows = t->nr;
+}
 
-/* Set the current table to TABLE. */
+/* Return the column style for this table into STYLE. */
 static void
-tabi_table (struct som_entity *table)
+tabi_columns (struct som_entity *t_, int *style)
 {
-  assert (table != NULL);
-  assert (table->type == SOM_TABLE);
+  struct tab_table *t = t_->ext;
+  *style = t->col_style;
+}
 
-  t = table->ext;
-  tab_offset (t, 0, 0);
+/* Return the number of header rows/columns on the left, right, top,
+   and bottom sides into HL, HR, HT, and HB, respectively. */
+static void
+tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
+{
+  struct tab_table *t = t_->ext;
+  *hl = t->l;
+  *hr = t->r;
+  *ht = t->t;
+  *hb = t->b;
+}
 
-  assert (t->w == NULL && t->h == NULL);
-  t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
-  t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
-  t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
-  t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
+/* Return flags set for the current table into FLAGS. */
+static void
+tabi_flags (struct som_entity *t_, unsigned *flags)
+{
+  struct tab_table *t = t_->ext;
+  *flags = t->flags;
 }
 
 /* Returns the line style to use for spacing purposes for a rule
@@ -953,153 +953,127 @@ rule_to_spacing_type (unsigned char type)
     }
 }
 
-/* Set the current output device to DRIVER. */
-static void
-tabi_driver (struct outp_driver *driver)
+static void *
+tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
+                  int hl, int hr, int ht, int hb)
 {
-  int c, r;
+  const struct tab_table *t = t_->ext;
+  struct tab_rendering *r;
+  int col, row;
   int i;
 
-  assert (driver != NULL);
-  d = driver;
+  tab_offset (t_->ext, 0, 0);
+
+  r = xmalloc (sizeof *r);
+  r->table = t;
+  r->driver = driver;
+  r->w = xnmalloc (tab_nc (t), sizeof *r->w);
+  r->h = xnmalloc (tab_nr (t), sizeof *r->h);
+  r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
+  r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
+  r->l = hl;
+  r->r = hr;
+  r->t = ht;
+  r->b = hb;
 
   /* Figure out sizes of rules. */
-  for (r = 0; r <= t->nr; r++)
+  for (row = 0; row <= tab_nr (t); row++)
     {
       int width = 0;
-      for (c = 0; c < t->nc; c++)
+      for (col = 0; col < tab_nc (t); col++)
         {
-          unsigned char rh = t->rh[c + r * t->cf];
+          unsigned char rh = t->rh[col + row * t->cf];
           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
           if (w > width)
             width = w;
         }
-      t->hrh[r] = width;
+      r->hrh[row] = width;
     }
 
-  for (c = 0; c <= t->nc; c++)
+  for (col = 0; col <= tab_nc (t); col++)
     {
       int width = 0;
-      for (r = 0; r < t->nr; r++)
+      for (row = 0; row < tab_nr (t); row++)
         {
-          unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
+          unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
           int w;
           if (*rv == UCHAR_MAX)
-            *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
+            *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
           if (w > width)
             width = w;
         }
-      t->wrv[c] = width;
+      r->wrv[col] = width;
     }
 
-#if DEBUGGING
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = -1;
-  for (i = 0; i < t->nc; i++)
-    t->w[i] = -1;
-#endif
-
-  assert (t->dim != NULL);
-  t->dim (t, d, t->dim_aux);
+  /* Determine row heights and columns widths. */
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = -1;
+  for (i = 0; i < tab_nc (t); i++)
+    r->w[i] = -1;
 
-#if DEBUGGING
-  {
-    int error = 0;
-
-    for (i = 0; i < t->nr; i++)
-      {
-       if (t->h[i] == -1)
-         {
-           printf ("Table row %d height not initialized.\n", i);
-           error = 1;
-         }
-       assert (t->h[i] > 0);
-      }
+  t->dim (r, t->dim_aux);
 
-    for (i = 0; i < t->nc; i++)
-      {
-       if (t->w[i] == -1)
-         {
-           printf ("Table column %d width not initialized.\n", i);
-           error = 1;
-         }
-       assert (t->w[i] > 0);
-      }
-  }
-#endif
+  for (i = 0; i < tab_nr (t); i++)
+    if (r->h[i] < 0)
+      error (0, 0, "height of table row %d is %d (not initialized?)",
+             i, r->h[i]);
+  for (i = 0; i < tab_nc (t); i++)
+    if (r->w[i] < 0)
+      error (0, 0, "width of table column %d is %d (not initialized?)",
+             i, r->w[i]);
 
   /* Add up header sizes. */
-  for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
-    t->wl += t->w[i] + t->wrv[i + 1];
-  for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
-    t->ht += t->h[i] + t->hrh[i + 1];
-  for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
-    t->wr += t->w[i] + t->wrv[i + 1];
-  for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
-    t->hb += t->h[i] + t->hrh[i + 1];
+  for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
+    r->wl += r->w[i] + r->wrv[i + 1];
+  for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
+    r->ht += r->h[i] + r->hrh[i + 1];
+  for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
+    r->wr += r->w[i] + r->wrv[i + 1];
+  for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
+    r->hb += r->h[i] + r->hrh[i + 1];
 
   /* Title. */
   if (!(t->flags & SOMF_NO_TITLE))
-    t->ht += d->font_height;
+    r->ht += driver->font_height;
+
+  return r;
 }
 
-/* Return the number of columns and rows in the table into N_COLUMNS
-   and N_ROWS, respectively. */
 static void
-tabi_count (int *n_columns, int *n_rows)
+tabi_render_free (void *r_)
 {
-  assert (n_columns != NULL && n_rows != NULL);
-  *n_columns = t->nc;
-  *n_rows = t->nr;
-}
+  struct tab_rendering *r = r_;
 
-static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
+  free (r->w);
+  free (r->h);
+  free (r->hrh);
+  free (r->wrv);
+  free (r);
+}
 
 /* Return the horizontal and vertical size of the entire table,
    including headers, for the current output device, into HORIZ and
    VERT. */
 static void
-tabi_area (int *horiz, int *vert)
+tabi_area (void *r_, int *horiz, int *vert)
 {
-  assert (horiz != NULL && vert != NULL);
-
-  {
-    int w, c;
-
-    for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
-        c < t->nc - t->r; c++)
-      w += t->w[c] + t->wrv[c];
-    *horiz = w;
-  }
-
-  {
-    int h, r;
-    for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
-        r < t->nr - t->b; r++)
-      h += t->h[r] + t->hrh[r];
-    *vert = h;
-  }
-}
-
-/* Return the column style for this table into STYLE. */
-static void
-tabi_columns (int *style)
-{
-  assert (style != NULL);
-  *style = t->col_style;
-}
-
-/* Return the number of header rows/columns on the left, right, top,
-   and bottom sides into HL, HR, HT, and HB, respectively. */
-static void
-tabi_headers (int *hl, int *hr, int *ht, int *hb)
-{
-  assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
-  *hl = t->l;
-  *hr = t->r;
-  *ht = t->t;
-  *hb = t->b;
+  struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  int width, col;
+  int height, row;
+
+  width = 0;
+  for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
+       col < tab_nc (t) - r->r; col++)
+    width += r->w[col] + r->wrv[col];
+  *horiz = width;
+
+  height = 0;
+  for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
+       row < tab_nr (t) - tab_b (t); row++)
+    height += r->h[row] + r->hrh[row];
+  *vert = height;
 }
 
 /* Determines the number of rows or columns (including appropriate
@@ -1110,32 +1084,35 @@ tabi_headers (int *hl, int *hr, int *ht, int *hb)
    space the selected rows/columns (including appropriate headers)
    filled. */
 static void
-tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
+tabi_cumulate (void *r_, int cumtype, int start, int *end,
+               int max, int *actual)
 {
-  int n;
-  int *d;
-  int *r;
+  const struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  int limit;
+  int *cells, *rules;
   int total;
+  int idx;
 
   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
   if (cumtype == SOM_ROWS)
     {
-      assert (start >= 0 && start < t->nr);
-      n = t->nr - t->b;
-      d = &t->h[start];
-      r = &t->hrh[start + 1];
-      total = t->ht + t->hb;
+      assert (start >= 0 && start < tab_nr (t));
+      limit = tab_nr (t) - r->b;
+      cells = &r->h[start];
+      rules = &r->hrh[start + 1];
+      total = r->ht + r->hb;
     }
   else
     {
-      assert (start >= 0 && start < t->nc);
-      n = t->nc - t->r;
-      d = &t->w[start];
-      r = &t->wrv[start + 1];
-      total = t->wl + t->wr;
+      assert (start >= 0 && start < tab_nc (t));
+      limit = tab_nc (t) - tab_r (t);
+      cells = &r->w[start];
+      rules = &r->wrv[start + 1];
+      total = r->wl + r->wr;
     }
 
-  total += *d++;
+  total += *cells++;
   if (total > max)
     {
       if (end)
@@ -1145,180 +1122,139 @@ tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
       return;
     }
 
-  {
-    int x;
-
-    for (x = start + 1; x < n; x++)
-      {
-       int amt = *d++ + *r++;
-
-       total += amt;
-       if (total > max)
-         {
-           total -= amt;
-           break;
-         }
-      }
-
-    if (end)
-      *end = x;
-
-    if (actual)
-      *actual = total;
-  }
-}
-
-/* Return flags set for the current table into FLAGS. */
-static void
-tabi_flags (unsigned *flags)
-{
-  assert (flags != NULL);
-  *flags = t->flags;
-}
-
-/* Returns true if the table will fit in the given page WIDTH,
-   false otherwise. */
-static bool
-tabi_fits_width (int width)
-{
-  int i;
-
-  for (i = t->l; i < t->nc - t->r; i++)
-    if (t->wl + t->wr + t->w[i] > width)
-      return false;
-
-  return true;
-}
+  for (idx = start + 1; idx < limit; idx++)
+    {
+      int amt = *cells++ + *rules++;
 
-/* Returns true if the table will fit in the given page LENGTH,
-   false otherwise. */
-static bool
-tabi_fits_length (int length)
-{
-  int i;
+      total += amt;
+      if (total > max)
+        {
+          total -= amt;
+          break;
+        }
+    }
 
-  for (i = t->t; i < t->nr - t->b; i++)
-    if (t->ht + t->hb + t->h[i] > length)
-      return false;
+  if (end)
+    *end = idx;
 
-  return true;
-}
-
-/* Sets the number of header rows/columns on the left, right, top,
-   and bottom sides to HL, HR, HT, and HB, respectively. */
-static void
-tabi_set_headers (int hl, int hr, int ht, int hb)
-{
-  t->l = hl;
-  t->r = hr;
-  t->t = ht;
-  t->b = hb;
+  if (actual)
+    *actual = total;
 }
 
 /* Render title for current table, with major index X and minor index
    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
    if Y is nonzero. */
 static void
-tabi_title (int x, int y)
+tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
+            const char *command_name)
 {
-  char buf[1024];
-  char *cp;
+  const struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  struct outp_text text;
+  struct string title;
 
   if (t->flags & SOMF_NO_TITLE)
     return;
 
-  cp = spprintf (buf, "%d.%d", table_num, subtable_num);
+  ds_init_empty (&title);
+  ds_put_format (&title,"%d.%d", table_num, subtable_num);
   if (x && y)
-    cp = spprintf (cp, "(%d:%d)", x, y);
+    ds_put_format (&title, "(%d:%d)", x, y);
   else if (x)
-    cp = spprintf (cp, "(%d)", x);
+    ds_put_format (&title, "(%d)", x);
   if (command_name != NULL)
-    cp = spprintf (cp, " %s", command_name);
-  cp = stpcpy (cp, ".  ");
+    ds_put_format (&title, " %s", command_name);
+  ds_put_cstr (&title, ".  ");
   if (t->title != NULL)
-    {
-      size_t length = strlen (t->title);
-      memcpy (cp, t->title, length);
-      cp += length;
-    }
-  *cp = 0;
-
-  {
-    struct outp_text text;
-
-    text.font = OUTP_PROPORTIONAL;
-    text.justification = OUTP_LEFT;
-    text.string = ss_buffer (buf, cp - buf);
-    text.h = d->width;
-    text.v = d->font_height;
-    text.x = 0;
-    text.y = d->cp_y;
-    d->class->text_draw (d, &text);
-  }
+    ds_put_cstr (&title, t->title);
+
+  text.font = OUTP_PROPORTIONAL;
+  text.justification = OUTP_LEFT;
+  text.string = ds_ss (&title);
+  text.h = r->driver->width;
+  text.v = r->driver->font_height;
+  text.x = 0;
+  text.y = r->driver->cp_y;
+  r->driver->class->text_draw (r->driver, &text);
+
+  ds_destroy (&title);
 }
 
-static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
+static int render_strip (const struct tab_rendering *,
+                         int x, int y, int r, int c1, int c2, int r1, int r2);
 
-/* 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)
+static void
+add_range (int ranges[][2], int *np, int start, int end)
 {
-  int r;
-  for (r = r0; r < r1; r++)
+  int n = *np;
+  if (n == 0 || start > ranges[n - 1][1])
     {
-      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];
+      ranges[n][0] = start;
+      ranges[n][1] = end;
+      ++*np;
     }
-  return y;
+  else
+    ranges[n - 1][1] = end;
 }
 
 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
    current position on the current output device.  */
 static void
-tabi_render (int c0, int r0, int c1, int r1)
+tabi_render (void *r_, int c0, int r0, int c1, int r1)
 {
-  int y;
+  const struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  int rows[3][2], cols[3][2];
+  int n_row_ranges, n_col_ranges;
+  int y, i;
+
+  /* Rows to render, counting horizontal rules as rows.  */
+  n_row_ranges = 0;
+  add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
+  add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
+  add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
+             tab_nr (t) * 2 + 1);
+
+  /* Columns to render, counting vertical rules as columns. */
+  n_col_ranges = 0;
+  add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
+  add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
+  add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
+
+  y = r->driver->cp_y;
+  if (!(t->flags & SOMF_NO_TITLE))
+    y += r->driver->font_height;
+  for (i = 0; i < n_row_ranges; i++)
+    {
+      int row;
 
-  tab_hit++;
+      for (row = rows[i][0]; row < rows[i][1]; row++)
+        {
+          int x, j;
 
-  y = d->cp_y;
-  if (!(t->flags & SOMF_NO_TITLE))
-    y += d->font_height;
+          x = r->driver->cp_x;
+          for (j = 0; j < n_col_ranges; j++)
+            x = render_strip (r, x, y, row,
+                              cols[j][0], cols[j][1],
+                              rows[i][0], rows[i][1]);
 
-  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);
+          y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
+        }
+    }
 }
 
 const struct som_table_class tab_table_class =
   {
-    tabi_table,
-    tabi_driver,
-
     tabi_count,
-    tabi_area,
-    NULL,
-    NULL,
     tabi_columns,
-    NULL,
     tabi_headers,
-    NULL,
-    tabi_cumulate,
     tabi_flags,
-    tabi_fits_width,
-    tabi_fits_length,
 
-    NULL,
-    NULL,
-    tabi_set_headers,
+    tabi_render_init,
+    tabi_render_free,
 
+    tabi_area,
+    tabi_cumulate,
     tabi_title,
     tabi_render,
   };
@@ -1360,56 +1296,63 @@ rule_to_draw_type (unsigned char type)
 
 /* Returns the horizontal rule at the given column and row. */
 static int
-get_hrule (int c, int r)
+get_hrule (const struct tab_table *t, int col, int row)
 {
-  return t->rh[c + r * t->cf];
+  return t->rh[col + row * t->cf];
 }
 
 /* Returns the vertical rule at the given column and row. */
 static int
-get_vrule (int c, int r)
+get_vrule (const struct tab_table *t, int col, int row)
 {
-  return t->rv[c + r * (t->cf + 1)];
+  return t->rv[col + row * (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)
+render_horz_rule (const struct tab_rendering *r,
+                  int x, int y, int col, int row)
 {
-  enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
+  enum outp_line_style style;
+  style = rule_to_draw_type (get_hrule (r->table, col, row));
   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);
+    r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
+                            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)
+render_vert_rule (const struct tab_rendering *r,
+                  int x, int y, int col, int row)
 {
-  enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
+  enum outp_line_style style;
+  style = rule_to_draw_type (get_vrule (r->table, col, row));
   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);
+    r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
+                            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)
+render_rule_intersection (const struct tab_rendering *r,
+                          int x, int y, int col, int row)
 {
+  const struct tab_table *t = r->table;
+
   /* Bounds of intersection. */
   int x0 = x;
   int y0 = y;
-  int x1 = x + t->wrv[c];
-  int y1 = y + t->hrh[r];
+  int x1 = x + r->wrv[col];
+  int y1 = y + r->hrh[row];
 
   /* 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;
+  int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
+  int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
+  int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
+  int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
 
   /* Output style for each line. */
   enum outp_line_style o_top = rule_to_draw_type (top);
@@ -1419,36 +1362,37 @@ render_rule_intersection (int x, int y, int c, int r)
 
   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);
+    r->driver->class->line (r->driver, x0, y0, x1, y1,
+                            o_top, o_left, o_bottom, o_right);
 }
 
 /* Returns the width of columns C1...C2 exclusive,
    including interior but not exterior rules. */
 static int
-strip_width (int c1, int c2)
+strip_width (const struct tab_rendering *r, int c1, int c2)
 {
   int width = 0;
   int c;
 
   for (c = c1; c < c2; c++)
-    width += t->w[c] + t->wrv[c + 1];
+    width += r->w[c] + r->wrv[c + 1];
   if (c1 < c2)
-    width -= t->wrv[c2];
+    width -= r->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)
+strip_height (const struct tab_rendering *r, int r1, int r2)
 {
   int height = 0;
-  int r;
+  int row;
 
-  for (r = r1; r < r2; r++)
-    height += t->h[r] + t->hrh[r + 1];
+  for (row = r1; row < r2; row++)
+    height += r->h[row] + r->hrh[row + 1];
   if (r1 < r2)
-    height -= t->hrh[r2];
+    height -= r->hrh[r2];
   return height;
 }
 
@@ -1456,9 +1400,11 @@ strip_height (int r1, int r2)
    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)
+render_cell (const struct tab_rendering *r,
+             int x, int y, int col, int row, int c1, int r1)
 {
-  const int index = c + (r * t->cf);
+  const struct tab_table *t = r->table;
+  const int index = col + (row * t->cf);
   unsigned char type = t->ct[index];
   struct substring *content = &t->cc[index];
 
@@ -1470,11 +1416,11 @@ render_cell (int x, int y, int c, int r, int c1, int r1)
           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.h = r->w[col];
+          text.v = r->h[row];
           text.x = x;
           text.y = y;
-          d->class->text_draw (d, &text);
+          r->driver->class->text_draw (r->driver, &text);
         }
     }
   else
@@ -1482,63 +1428,50 @@ render_cell (int x, int y, int c, int r, int c1, int r1)
       struct tab_joined_cell *j
         = (struct tab_joined_cell *) ss_data (*content);
 
-      if (j->hit != tab_hit)
+      if (j->x1 == col && j->y1 == row)
         {
-          j->hit = tab_hit;
-
-          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);
-            }
+          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 (r, j->x1, MIN (j->x2, c1));
+          text.v = strip_height (r, j->y1, MIN (j->y2, r1));
+          r->driver->class->text_draw (r->driver, &text);
         }
     }
 }
 
 /* Render contiguous strip consisting of columns C0...C1, exclusive,
-   on row R, at (X,Y).  Returns X position after rendering.
+   on row ROW, 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. */
+   ROW 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)
+render_strip (const struct tab_rendering *r,
+              int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
 {
-  int c;
+  int col;
 
-  for (c = c0; c < c1; c++)
-    if (c & 1)
+  for (col = c0; col < c1; col++)
+    if (col & 1)
       {
-        if (r & 1)
-          render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
+        if (row & 1)
+          render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
         else
-          render_horz_rule (x, y, c / 2, r / 2);
-        x += t->w[c / 2];
+          render_horz_rule (r, x, y, col / 2, row / 2);
+        x += r->w[col / 2];
       }
     else
       {
-        if (r & 1)
-          render_vert_rule (x, y, c / 2, r / 2);
+        if (row & 1)
+          render_vert_rule (r, x, y, col / 2, row / 2);
         else
-          render_rule_intersection (x, y, c / 2, r / 2);
-        x += t->wrv[c / 2];
+          render_rule_intersection (r, x, y, col / 2, row / 2);
+        x += r->wrv[col / 2];
       }
 
   return x;
 }
-
-/* Sets COMMAND_NAME as the name of the current command,
-   for embedding in output. */
-void
-tab_set_command_name (const char *command_name_)
-{
-  free (command_name);
-  command_name = command_name_ ? xstrdup (command_name_) : NULL;
-}
index 1748c24c855e71ec8c0212dac76d00662db76f67..f4fbc786f0440ea39da04ebfa0c54070ccec7cd1 100644 (file)
@@ -61,23 +61,24 @@ struct tab_joined_cell
   {
     int x1, y1;
     int x2, y2;
-    int hit;
     struct substring contents;
   };
 
 struct outp_driver;
 struct tab_table;
-typedef void tab_dim_func (struct tab_table *, struct outp_driver *,
-                           void *aux);
+struct tab_rendering;
+
+typedef void tab_dim_func (struct tab_rendering *, void *aux);
+typedef void tab_dim_free_func (void *aux);
 
 /* A table. */
 struct tab_table
   {
     struct pool *container;
+    int ref_cnt;                /* Reference count. */
 
     /* Contents. */
     int col_style;             /* Columns: One of TAB_COL_*. */
-    int col_group;             /* Number of rows per column group. */
     char *title;                /* Table title. */
     unsigned flags;            /* SOMF_*. */
     int nc, nr;                        /* Number of columns, rows. */
@@ -87,45 +88,51 @@ struct tab_table
     unsigned char *ct;         /* Cell types; unsigned char[nr][nc]. */
     unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
     unsigned char *rv;         /* Vert rules; unsigned char[nr][nc+1]. */
+
+    /* Calculating row and column dimensions. */
     tab_dim_func *dim;         /* Calculates cell widths and heights. */
+    tab_dim_free_func *dim_free; /* Frees space allocated for dim function. */
     void *dim_aux;              /* Auxiliary data for dim function. */
 
-    /* Calculated during output. */
-    int *w;                    /* Column widths; [nc]. */
-    int *h;                    /* Row heights; [nr]. */
-    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. */
-
     /* Editing info. */
     int col_ofs, row_ofs;      /* X and Y offsets. */
   };
 
-/* Number of rows in TABLE. */
-#define tab_nr(TABLE) ((TABLE)->nr)
-
-/* Number of columns in TABLE. */
-#define tab_nc(TABLE) ((TABLE)->nc)
+/* Number of rows or columns in TABLE. */
+static inline int tab_nr (const struct tab_table *table) { return table->nr; }
+static inline int tab_nc (const struct tab_table *table) { return table->nc; }
 
-/* Number of left header columns in TABLE. */
-#define tab_l(TABLE) ((TABLE)->l)
+/* Number of left/right/top/bottom header columns/rows in TABLE. */
+static inline int tab_l (const struct tab_table *table) { return table->l; }
+static inline int tab_r (const struct tab_table *table) { return table->r; }
+static inline int tab_t (const struct tab_table *table) { return table->t; }
+static inline int tab_b (const struct tab_table *table) { return table->b; }
 
-/* Number of right header columns in TABLE. */
-#define tab_r(TABLE) ((TABLE)->r)
+struct tab_rendering
+  {
+    const struct tab_table *table;
+    struct outp_driver *driver;
 
-/* Number of top header rows in TABLE. */
-#define tab_t(TABLE) ((TABLE)->t)
+    int *w;                    /* Column widths; [nc]. */
+    int *h;                    /* Row heights; [nr]. */
+    int *hrh;                  /* Heights of horizontal rules; [nr+1]. */
+    int *wrv;                  /* Widths of vertical rules; [nc+1]. */
 
-/* Number of bottom header rows in TABLE. */
-#define tab_b(TABLE) ((TABLE)->b)
+    /* These fields would be redundant with those in struct tab_table, except
+       that a table will be rendered with fewer header rows or columns than
+       requested when we are pressed for space. */
+    int l, r, t, b;            /* Number of header rows/columns. */
+    int wl, wr, ht, hb;                /* Width/height of header rows/columns. */
+  };
 
 /* Tables. */
-struct tab_table *tab_create (int nc, int nr, int reallocable);
+struct tab_table *tab_create (int nc, int nr);
 void tab_destroy (struct tab_table *);
+void tab_ref (struct tab_table *);
 void tab_resize (struct tab_table *, int nc, int nr);
 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_columns (struct tab_table *, int style);
 void tab_title (struct tab_table *, const char *, ...)
      PRINTF_FORMAT (2, 3);
 void tab_flags (struct tab_table *, unsigned);
@@ -133,9 +140,10 @@ void tab_submit (struct tab_table *);
 
 /* Dimensioning. */
 tab_dim_func tab_natural_dimensions;
-int tab_natural_width (struct tab_table *t, struct outp_driver *d, int c);
-int tab_natural_height (struct tab_table *t, struct outp_driver *d, int r);
-void tab_dim (struct tab_table *, tab_dim_func *, void *aux);
+int tab_natural_width (const struct tab_rendering *, int c);
+int tab_natural_height (const struct tab_rendering *, int r);
+void tab_dim (struct tab_table *,
+              tab_dim_func *, tab_dim_free_func *, void *aux);
 
 /* Rules. */
 void tab_hline (struct tab_table *, int style, int x1, int x2, int y);
@@ -176,11 +184,6 @@ void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
                             unsigned opt, const char *, ...)
      PRINTF_FORMAT (7, 8);
 
-/* Cell low-level access. */
-#define tab_alloc(TABLE, AMT) pool_alloc ((TABLE)->container, (AMT))
-void tab_raw (struct tab_table *, int c, int r, unsigned opt,
-             struct substring *);
-
 /* Editing. */
 void tab_offset (struct tab_table *, int col, int row);
 void tab_next_row (struct tab_table *);
@@ -194,8 +197,5 @@ void tab_output_text (int options, const char *string);
 void tab_output_text_format (int options, const char *, ...)
      PRINTF_FORMAT (2, 3);
 
-/* Embedding the command name in the output. */
-void tab_set_command_name (const char *);
-
 #endif /* tab_h */
 
index c21b681d1f289f980e8bbcd274c2743726c73275..743d0b6dd77a85472de65b19967d79241df2cc5f 100644 (file)
@@ -1,7 +1,7 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 include $(top_srcdir)/src/ui/terminal/automake.mk
-if WITHGUI
+if HAVE_GUI
 include $(top_srcdir)/src/ui/gui/automake.mk
 endif
 
index b7d60a78edca7e250444598eb672aba525b63727..3fbf46520d2137e18d934c86f16e0ce4b1f553c4 100644 (file)
@@ -26,6 +26,7 @@ src_ui_gui_psppire_LDADD = \
        src/libpspp.la \
        src/libpspp-core.la \
        $(GTK_LIBS) \
+       $(CAIRO_LIBS) \
        $(LIBINTL)
 
 src_ui_gui_psppiredir = $(pkgdatadir)
index 9336f7ce3d4dbe318fb8ed5fbdd5a890e3be57ed..aa2fae51ce127b95cfe6c607da50c2da95fe406f 100644 (file)
@@ -104,7 +104,5 @@ execute_syntax (struct getl_interface *sss)
 
   som_flush ();
 
-  psppire_output_window_reload ();
-
   return retval;
 }
index 40e91bc0179f1f801a8d22086725735f5810af48..2319fb9f50fac2231169692fe837e395186af8a7 100644 (file)
@@ -34,6 +34,7 @@ which match particular strings */
 #include <ctype.h>
 #include <sys/types.h>
 #include <regex.h>
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 
 #include <gtk/gtk.h>
@@ -566,7 +567,8 @@ regexp_label_compare (const struct comparator *cmptr,
 static void
 regexp_destroy (struct comparator *cmptr)
 {
-  struct regexp_comparator *rec = (struct regexp_comparator *) cmptr;
+  struct regexp_comparator *rec
+    = UP_CAST (cmptr, struct regexp_comparator, parent);
 
   regfree (&rec->re);
 }
@@ -574,7 +576,8 @@ regexp_destroy (struct comparator *cmptr)
 static void
 cmptr_value_destroy (struct comparator *cmptr)
 {
-  struct value_comparator *vc = (struct value_comparator *) cmptr;
+  struct value_comparator *vc
+    = UP_CAST (cmptr, struct value_comparator, parent);
   value_destroy (&vc->pattern, var_get_width (cmptr->var));
 }
 
@@ -583,7 +586,7 @@ static struct comparator *
 value_comparator_create (const struct variable *var, const PsppireDict *dict, const char *target)
 {
   struct value_comparator *vc = xzalloc (sizeof (*vc));
-  struct comparator *cmptr = (struct comparator *) vc;
+  struct comparator *cmptr = &vc->parent;
 
   cmptr->flags = 0;
   cmptr->var = var;
@@ -602,7 +605,7 @@ string_comparator_create (const struct variable *var, const PsppireDict *dict,
                          enum string_cmp_flags flags)
 {
   struct string_comparator *ssc = xzalloc (sizeof (*ssc));
-  struct comparator *cmptr = (struct comparator *) ssc;
+  struct comparator *cmptr = &ssc->parent;
 
   cmptr->flags = flags;
   cmptr->var = var;
@@ -625,7 +628,7 @@ regexp_comparator_create (const struct variable *var, const PsppireDict *dict, c
 {
   int code;
   struct regexp_comparator *rec = xzalloc (sizeof (*rec));
-  struct comparator *cmptr = (struct comparator *) rec;
+  struct comparator *cmptr = &rec->parent;
 
   cmptr->flags = flags;
   cmptr->var = var;
index 0b709ee4cadee9fc91a8cb53bdc5a05324612169..50d92d46671a07444c4555891f5af8e0db78889a 100644 (file)
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Sat Aug 11 17:19:54 2007 by john@marilyn-->
+<?xml version="1.0"?>
 <glade-interface>
+  <!-- interface-requires gtk+ 2.16 -->
+  <!-- interface-naming-policy toplevel-contextual -->
   <widget class="GtkWindow" id="output-viewer-window">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
     <property name="default_width">600</property>
@@ -16,7 +16,6 @@
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
             <child>
               <widget class="GtkMenuItem" id="menuitem1">
-                <property name="visible">False</property>
                 <property name="sensitive">False</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="label" translatable="yes">_File</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <child>
                       <widget class="GtkImageMenuItem" id="file_save">
+                        <property name="label">gtk-save</property>
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">gtk-save</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                       </widget>
                     </child>
                     <child>
                       <widget class="GtkImageMenuItem" id="file_save-as">
+                        <property name="label">gtk-save-as</property>
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">gtk-save-as</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                       </widget>
@@ -49,7 +48,6 @@
             </child>
             <child>
               <widget class="GtkMenuItem" id="menuitem2">
-                <property name="visible">False</property>
                 <property name="sensitive">False</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="label" translatable="yes">_Edit</property>
@@ -60,9 +58,9 @@
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <child>
                       <widget class="GtkImageMenuItem" id="imagemenuitem7">
+                        <property name="label">gtk-copy</property>
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">gtk-copy</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                       </widget>
           </widget>
           <packing>
             <property name="expand">False</property>
+            <property name="position">0</property>
           </packing>
         </child>
         <child>
-          <widget class="GtkScrolledWindow" id="scrolledwindow1">
+          <widget class="GtkHPaned" id="hpaned1">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="position">112</property>
+            <property name="position_set">True</property>
             <child>
-              <widget class="GtkTextView" id="output-viewer-textview">
+              <widget class="GtkScrolledWindow" id="scrolledwindow2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <widget class="GtkTreeView" id="overview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="resize">False</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="scrolledwindow1">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="editable">False</property>
-                <property name="left_margin">5</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <widget class="GtkLayout" id="output">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                  </widget>
+                </child>
               </widget>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
             </child>
           </widget>
           <packing>
index b0eff950c7e5e6b466b70edda79e9faf40702a8b..35216d4e518502d809fd8e02965d82945fd53f86 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008  Free Software Foundation
+   Copyright (C) 2008, 2009  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include "helper.h"
 
 #include <libpspp/message.h>
+#include <output/cairo.h>
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
 #include <stdlib.h>
 
 #include "about.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-
+enum
+  {
+    COL_TITLE,                  /* Table title. */
+    COL_Y,                      /* Y position of top of title. */
+    N_COLS
+  };
 
 static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
 static void psppire_output_window_base_init     (PsppireOutputWindowClass *class);
@@ -106,13 +115,152 @@ psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
                                     gpointer class_data)
 {
 }
-
-
 \f
+/* Output driver class. */
 
 static PsppireOutputWindow *the_output_viewer = NULL;
 
+static gboolean
+expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+  struct som_entity *entity = g_object_get_data (G_OBJECT (widget), "entity");
+  GdkWindow *window = widget->window;
+  cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
+  struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
+  struct tab_table *t = entity->ext;
+  void *rendering;
+
+  rendering = entity->class->render_init (entity, driver, tab_l (t),
+                                          tab_r (t), tab_t (t), tab_b (t));
+
+  entity->class->title (rendering, 0, 0,
+                        entity->table_num, entity->subtable_num,
+                        entity->command_name);
+  entity->class->render (rendering, tab_l (t), tab_t (t),
+                         tab_nc (t) - tab_r (t),
+                         tab_nr (t) - tab_b (t));
+
+  entity->class->render_free (rendering);
+  driver->class->close_driver (driver);
+  outp_free_driver (driver);
+  return TRUE;
+}
 
+static void
+psppire_output_submit (struct outp_driver *this, struct som_entity *entity)
+{
+  if (the_output_viewer == NULL)
+    {
+      the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
+      gtk_widget_show_all (GTK_WIDGET (the_output_viewer));
+    }
+
+  if (entity->type == SOM_TABLE)
+    {
+      GdkWindow *window = GTK_WIDGET (the_output_viewer)->window;
+      cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
+      struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
+      struct tab_table *t = entity->ext;
+      GtkTreeStore *store;
+      GtkTreeIter item;
+      GtkTreePath *path;
+      GtkWidget *drawing_area;
+      void *rendering;
+      struct string title;
+      int tw, th;
+
+      tab_ref (t);
+      rendering = entity->class->render_init (entity, driver, tab_l (t),
+                                              tab_r (t), tab_t (t), tab_b (t));
+      entity->class->area (rendering, &tw, &th);
+
+      drawing_area = gtk_drawing_area_new ();
+      gtk_widget_modify_bg (GTK_WIDGET (drawing_area), GTK_STATE_NORMAL,
+                            &gtk_widget_get_style (drawing_area)->base[GTK_STATE_NORMAL]);
+      g_object_set_data (G_OBJECT (drawing_area),
+                         "entity", som_entity_clone (entity));
+      gtk_widget_set_size_request (drawing_area, tw / 1024, th / 1024);
+      gtk_layout_put (the_output_viewer->output, drawing_area,
+                      0, the_output_viewer->y);
+      gtk_widget_show (drawing_area);
+      g_signal_connect (G_OBJECT (drawing_area), "expose_event",
+                        G_CALLBACK (expose_event_callback), NULL);
+
+      entity->class->render_free (rendering);
+      driver->class->close_driver (driver);
+      outp_free_driver (driver);
+
+      store = GTK_TREE_STORE (gtk_tree_view_get_model (
+                                the_output_viewer->overview));
+
+      ds_init_empty (&title);
+      if (entity->table_num != the_output_viewer->last_table_num)
+        {
+          gtk_tree_store_append (store, &item, NULL);
+
+          ds_put_format (&title, "%d %s",
+                         entity->table_num, entity->command_name);
+          gtk_tree_store_set (store, &item,
+                              COL_TITLE, ds_cstr (&title),
+                              COL_Y, the_output_viewer->y,
+                              -1);
+
+          /* XXX shouldn't save a GtkTreeIter */
+          the_output_viewer->last_table_num = entity->table_num;
+          the_output_viewer->last_top_level = item;
+        }
+
+      gtk_tree_store_append (store, &item,
+                             &the_output_viewer->last_top_level);
+      ds_clear (&title);
+      ds_put_format (&title, "%d.%d %s",
+                     entity->table_num, entity->subtable_num,
+                     t->title ? t->title : entity->command_name);
+      gtk_tree_store_set (store, &item,
+                          COL_TITLE, ds_cstr (&title),
+                          COL_Y, the_output_viewer->y,
+                          -1);
+      ds_destroy (&title);
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
+                                      &the_output_viewer->last_top_level);
+      gtk_tree_view_expand_row (the_output_viewer->overview, path, TRUE);
+      gtk_tree_path_free (path);
+
+      if (tw / 1024 > the_output_viewer->max_width)
+        the_output_viewer->max_width = tw / 1024;
+      the_output_viewer->y += th / 1024;
+
+      gtk_layout_set_size (the_output_viewer->output,
+                           the_output_viewer->max_width, the_output_viewer->y);
+    }
+
+  gtk_window_set_urgency_hint (GTK_WINDOW (the_output_viewer), TRUE);
+}
+
+static struct outp_class psppire_output_class =
+  {
+    "PSPPIRE",                  /* name */
+    true,                       /* special */
+    NULL,                       /* open_driver */
+    NULL,                       /* close_driver */
+    NULL,                       /* open_page */
+    NULL,                       /* close_page */
+    NULL,                       /* flush */
+    NULL,                       /* output_chart */
+    psppire_output_submit,      /* submit */
+    NULL,                       /* line */
+    NULL,                       /* text_metrics */
+    NULL,                       /* text_draw */
+  };
+
+void
+psppire_output_window_setup (void)
+{
+  outp_register_driver (outp_allocate_driver (&psppire_output_class,
+                                              "PSPPIRE", 0));
+}
+\f
 int viewer_length = 16;
 int viewer_width = 59;
 
@@ -127,8 +275,6 @@ on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
 
   the_output_viewer = NULL;
 
-  unlink (output_file_name());
-
   return FALSE;
 }
 
@@ -139,99 +285,78 @@ cancel_urgency (GtkWindow *window,  gpointer data)
 {
   gtk_window_set_urgency_hint (window, FALSE);
 }
-/* Sets width and length according to the new size
-   of the output window */
+
 static void
-on_textview_resize (GtkWidget     *widget,
-                   GtkAllocation *allocation,
-                   gpointer       user_data)
+on_row_activate (GtkTreeView *overview,
+                 GtkTreePath *path,
+                 GtkTreeViewColumn *column,
+                 PsppireOutputWindow *window)
 {
-  PangoContext * context ;
-  PangoLayout *layout ;
-  PangoRectangle logical;
-  GtkStyle *style;
-  gint right_margin, left_margin;
-  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
-
-  context = gtk_widget_create_pango_context (widget);
-  layout = pango_layout_new (context);
-
-  style = gtk_widget_get_style (widget);
-
-  pango_layout_set_font_description (layout, style->font_desc);
-
-  /* Find the width of one character.  We can use any character, because
-     the textview has a monospaced font */
-  pango_layout_set_text (layout, "M", 1);
-
-  pango_layout_get_extents (layout,  NULL, &logical);
-
-  left_margin = gtk_text_view_get_left_margin (text_view);
-  right_margin = gtk_text_view_get_right_margin (text_view);
-
-  viewer_length = allocation->height / PANGO_PIXELS (logical.height);
-  viewer_width = (allocation->width - right_margin - left_margin)
-    / PANGO_PIXELS (logical.width);
-
-  g_object_unref (G_OBJECT (layout));
-  g_object_unref (G_OBJECT (context));
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  GtkAdjustment *vadj;
+  GValue value = {0};
+  double y, min, max;
+
+  model = gtk_tree_view_get_model (overview);
+  if (!gtk_tree_model_get_iter (model, &iter, path))
+    return;
+
+  gtk_tree_model_get_value (model, &iter, COL_Y, &value);
+  y = g_value_get_long (&value);
+  g_value_unset (&value);
+
+  vadj = gtk_layout_get_vadjustment (window->output);
+  min = vadj->lower;
+  max = vadj->upper - vadj->page_size;
+  if (y < min)
+    y = min;
+  else if (y > max)
+    y = max;
+  gtk_adjustment_set_value (vadj, y);
 }
 
-
 static void
 psppire_output_window_init (PsppireOutputWindow *window)
 {
-  GtkBuilder *xml = builder_new ("output-viewer.ui");
-
-  GtkWidget *box = gtk_vbox_new (FALSE, 0);
-
-  GtkWidget *sw = get_widget_assert (xml, "scrolledwindow1");
-
-  GtkWidget *menubar = get_widget_assert (xml, "menubar1");
-
-  window->textview = get_widget_assert (xml, "output-viewer-textview");
-
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *renderer;
+  GtkBuilder *xml;
 
-  gtk_container_add (GTK_CONTAINER (window), box);
+  xml = builder_new ("output-viewer.ui");
 
+  gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
 
-  g_object_ref (menubar);
-  gtk_widget_unparent (menubar);
+  window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
+  window->y = 0;
 
-  g_object_ref (sw);
-  gtk_widget_unparent (sw);
+  window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
+  gtk_tree_view_set_model (window->overview,
+                           GTK_TREE_MODEL (gtk_tree_store_new (
+                                             N_COLS,
+                                             G_TYPE_STRING, /* COL_TITLE */
+                                             G_TYPE_LONG))); /* COL_Y */
+  window->last_table_num = -1;
 
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, TRUE);
+  gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
 
-  gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
-  gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
+  g_signal_connect (GTK_TREE_VIEW (window->overview),
+                    "row-activated", G_CALLBACK (on_row_activate), window);
 
-
-  gtk_widget_show_all (box);
+  gtk_widget_modify_bg (GTK_WIDGET (window->output), GTK_STATE_NORMAL,
+                        &gtk_widget_get_style (GTK_WIDGET (window->output))->base[GTK_STATE_NORMAL]);
 
   connect_help (xml);
 
-  window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->textview));
-
   g_signal_connect (window,
                    "focus-in-event",
                    G_CALLBACK (cancel_urgency),
                    NULL);
 
-  {
-    /* Output uses ascii characters for tabular material.
-       So we need a monospaced font otherwise it'll look silly */
-    PangoFontDescription *font_desc =
-      pango_font_description_from_string ("monospace");
-
-    gtk_widget_modify_font (window->textview, font_desc);
-    pango_font_description_free (font_desc);
-  }
-
-  g_signal_connect (window->textview, "size-allocate",
-                   G_CALLBACK (on_textview_resize), NULL);
-
-  window->fp = NULL;
-
   g_signal_connect (get_action_assert (xml,"help_about"),
                    "activate",
                    G_CALLBACK (about_new),
@@ -269,108 +394,3 @@ psppire_output_window_new (void)
                                   "description", _("Output Viewer"),
                                   NULL));
 }
-
-static void reload_viewer (PsppireOutputWindow *ow);
-
-void
-psppire_output_window_reload (void)
-{
-  struct stat buf;
-
-  /* If there is no output, then don't do anything */
-  if (0 != stat (output_file_name(), &buf))
-    return ;
-
-  if ( NULL == the_output_viewer )
-    {
-      the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
-      gtk_widget_show (GTK_WIDGET (the_output_viewer));
-    }
-
-  reload_viewer (the_output_viewer);
-
-}
-
-
-static void
-reload_viewer (PsppireOutputWindow *ow)
-{
-  GtkTextIter end_iter;
-  GtkTextMark *mark ;
-
-  char *line = NULL;
-
-  gboolean chars_inserted = FALSE;
-
-  gtk_text_buffer_get_end_iter (ow->buffer, &end_iter);
-
-  line = xrealloc (line, sizeof (char) * (viewer_width + 1));
-
-  mark = gtk_text_buffer_create_mark (ow->buffer, NULL, &end_iter, TRUE);
-
-#ifdef __CYGWIN__
-  /*
-    Apparently Windoze is not capabale of writing to a file whilst
-    another (or the same) process is reading from it.   Therefore, we
-    must close the file after reading it, and clear the entire buffer
-    before writing to it.
-    This will be slower for large buffers, but should work
-    (in so far as anything ever works on windows).
-  */
-  {
-    GtkTextIter start_iter;
-    FILE *fp = fopen (output_file_name(), "r");
-    if ( !fp)
-      {
-       g_critical ("Cannot open %s\n", output_file_name());
-       return;
-      }
-
-    /* Delete all the entire buffer */
-    gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
-    gtk_text_buffer_delete (ow->buffer, &start_iter, &end_iter);
-
-
-    gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
-    /* Read in the next lot of text */
-    while (fgets (line, viewer_width + 1, fp) != NULL)
-      {
-       chars_inserted = TRUE;
-       gtk_text_buffer_insert (ow->buffer, &start_iter, line, -1);
-      }
-
-    fclose (fp);
-  }
-#else
-  {
-    if ( ow->fp == NULL)
-      {
-       ow->fp = fopen (output_file_name(), "r");
-       if ( ow->fp == NULL)
-         {
-           g_critical ("Cannot open %s\n", output_file_name());
-           return;
-         }
-      }
-
-    /* Read in the next lot of text */
-    while (fgets (line, viewer_width + 1, ow->fp) != NULL)
-      {
-       chars_inserted = TRUE;
-       gtk_text_buffer_insert (ow->buffer, &end_iter, line, -1);
-      }
-  }
-#endif
-
-  /* Scroll to where the start of this lot of text begins */
-  gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ow->textview),
-                               mark,
-                               0.1, TRUE, 0.0, 0.0);
-
-
-  if ( chars_inserted )
-    gtk_window_set_urgency_hint ( GTK_WINDOW (ow), TRUE);
-}
-
-
-
index d11eca618d84c9c18c86e5dc7bd184b6f9771a75..606a950475fc6ecc1d4833fb7eea46e9faf8ccdf 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008  Free Software Foundation
+   Copyright (C) 2008, 2009  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -31,10 +31,6 @@ extern int viewer_length;
 extern int viewer_width ;
 
 
-#define OUTPUT_FILE_NAME "psppire.txt"
-
-
-
 G_BEGIN_DECLS
 
 #define PSPPIRE_OUTPUT_WINDOW_TYPE            (psppire_output_window_get_type ())
@@ -56,9 +52,13 @@ struct _PsppireOutputWindow
   PsppireWindow parent;
 
   /* <private> */
-  GtkTextBuffer *buffer;  /* The buffer which contains the text */
-  GtkWidget *textview ;
-  FILE *fp;               /* The file it's viewing */
+  GtkLayout *output;
+  int max_width;
+  int y;
+
+  GtkTreeView *overview;
+  int last_table_num;
+  GtkTreeIter last_top_level;
 };
 
 struct _PsppireOutputWindowClass
@@ -70,9 +70,7 @@ struct _PsppireOutputWindowClass
 GType      psppire_output_window_get_type        (void);
 GtkWidget* psppire_output_window_new             (void);
 
-
-void psppire_output_window_reload (void);
-
+void psppire_output_window_setup (void);
 
 G_END_DECLS
 
index 8491851383f4405805bbf71d0a35d5e172500b29..3f6d49a65db2aec01fdef067d502954707484d8a 100644 (file)
@@ -123,25 +123,7 @@ initialize (struct command_line_processor *clp, int argc, char **argv)
 
   create_icon_factory ();
 
-  {
-    const char *filename = output_file_name ();
-
-    struct string config_string;
-
-    ds_init_empty (&config_string);
-
-    ds_put_format (&config_string,
-                  "gui:ascii:screen:squeeze=on headers=off top-margin=0 "
-                  "bottom-margin=0 paginate=off length=auto width=auto "
-                  "emphasis=none "
-                  "output-file=\"%s\" append=yes", filename);
-
-    outp_configure_driver_line (ds_ss (&config_string));
-
-    unlink (filename);
-
-    ds_destroy (&config_string);
-  }
+  psppire_output_window_setup ();
 
   journal_enable ();
   textdomain (PACKAGE);
@@ -345,16 +327,3 @@ parse_non_options (int key, char *arg, struct argp_state *state)
 
 
 const struct argp non_option_argp = {NULL, parse_non_options, 0, 0, 0, 0, 0};
-
-
-const char *
-output_file_name (void)
-{
-  const char *dir = default_output_path ();
-  static char *filename = NULL;
-
-  if ( NULL == filename )
-    filename = xasprintf ("%s%s", dir, OUTPUT_FILE_NAME);
-
-  return filename;
-}
index 214370513b7369087344f60c636f450704f3b7a7..6ec866c93a11d7997f2de2fd66583b040e5b06b3 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  Free Software Foundation
+   Copyright (C) 2006, 2009  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 
 #include <libpspp/getl.h>
 #include <libpspp/compiler.h>
+#include <libpspp/cast.h>
 #include <libpspp/str.h>
 
 #include <stdlib.h>
@@ -72,7 +73,8 @@ read_line_from_buffer (struct getl_interface *i,
   gchar *text;
   GtkTextIter next_line;
 
-  struct syntax_editor_source *ses = (struct syntax_editor_source *) i;
+  struct syntax_editor_source *ses
+    = UP_CAST (i, struct syntax_editor_source, parent);
 
   if ( gtk_text_iter_compare (&ses->i, &ses->end) >= 0)
     return false;
@@ -124,5 +126,5 @@ create_syntax_editor_source (GtkTextBuffer *buffer,
   ses->parent.location = location;
 
 
-  return (struct getl_interface *) ses;
+  return &ses->parent;
 }
index 14eacabfadddb5842e81f444855233b0e903ec03..5f43ff498f10a6c4910a53bec9fc5bfda82a87fb 100644 (file)
@@ -324,7 +324,7 @@ on_remove (GtkWidget *w, gpointer data)
   struct val_labs_dialog *dialog = data;
 
   union value value;
-  const struct val_lab *vl;
+  struct val_lab *vl;
 
   get_selected_tuple (dialog, &value, NULL);
   vl = val_labs_lookup (dialog->labs, &value);
index 9cde11c9110641e1c6f39fa1aa4de959865cb74b..81b896dc878839d44590f9e3205ebc53d5e07fb0 100644 (file)
@@ -25,6 +25,7 @@ src_ui_terminal_pspp_LDADD = \
        src/ui/libuicommon.la \
        src/libpspp.la \
        src/libpspp-core.la \
+       $(CAIRO_LIBS) \
        $(NCURSES_LIBS) \
        $(LIBICONV) \
        $(LIBINTL) $(LIBREADLINE)
index 7b23f38d2f359b58ecec1635970904d732edad5c..f85e4e76b17a2aef2e9ca0c8aac27d8296341fcd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -31,6 +31,7 @@
 #include <data/file-name.h>
 #include <data/settings.h>
 #include <language/command.h>
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 #include <libpspp/version.h>
@@ -107,8 +108,7 @@ static bool
 read_interactive (struct getl_interface *s,
                   struct string *line)
 {
-  struct readln_source *is  =
-    (struct readln_source *) s ;
+  struct readln_source *is  = UP_CAST (s, struct readln_source, parent);
 
   return is->interactive_func (line, prompt_get_style ());
 }
@@ -215,7 +215,7 @@ create_readln_source (void)
   rlns->parent.read = read_interactive;
   rlns->parent.close = readln_close;
 
-  return (struct getl_interface *) rlns;
+  return &rlns->parent;
 }
 
 
index 8a974a32f7033545d91cd075b8c879180a7c7eed..76ee1f2356b507fed397aec65d6e58f315065a1c 100644 (file)
@@ -194,6 +194,8 @@ nodist_TESTS = \
        tests/libpspp/range-set-test \
        tests/libpspp/sparse-array-test \
        tests/libpspp/str-test \
+       tests/libpspp/string-map-test \
+       tests/libpspp/string-set-test \
        tests/libpspp/tower-test
 
 TESTS = $(dist_TESTS) $(nodist_TESTS)
@@ -288,6 +290,23 @@ tests_libpspp_str_test_SOURCES = \
        tests/libpspp/str-test.c
 tests_libpspp_str_test_LDADD = src/libpspp/libpspp.la gl/libgl.la $(LIBINTL) 
 
+tests_libpspp_string_map_test_SOURCES = \
+       src/libpspp/hash-functions.c \
+       src/libpspp/hmap.c \
+       src/libpspp/string-map.c \
+       src/libpspp/string-set.c \
+       tests/libpspp/string-map-test.c
+tests_libpspp_string_map_test_LDADD = gl/libgl.la $(LIBINTL)
+tests_libpspp_string_map_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
+tests_libpspp_string_set_test_SOURCES = \
+       src/libpspp/hash-functions.c \
+       src/libpspp/hmap.c \
+       src/libpspp/string-set.c \
+       tests/libpspp/string-set-test.c
+tests_libpspp_string_set_test_LDADD = gl/libgl.la $(LIBINTL)
+tests_libpspp_string_set_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
 tests_libpspp_tower_test_SOURCES = \
        src/libpspp/abt.c \
        src/libpspp/abt.h \
index 4ca7f92fd73f48316bcc53c1f388bfab69427a11..64511b7bbf1d1c7dafb79857fc811b014d6e9af2 100644 (file)
@@ -342,6 +342,7 @@ check_hmap (struct hmap *hmap, const int data[], size_t cnt,
   size_t i, j;
   int *order;
 
+  check (hmap_is_empty (hmap) == (cnt == 0));
   check (hmap_count (hmap) == cnt);
   check (cnt <= hmap_capacity (hmap));
 
@@ -378,7 +379,6 @@ check_hmap (struct hmap *hmap, const int data[], size_t cnt,
       for (p = hmap_first (hmap), i = 0; i < cnt; p = hmap_next (hmap, p), i++)
         {
           struct element *e = hmap_node_to_element (p);
-          size_t j;
 
           check (hmap_node_hash (&e->node) == hash (e->data));
           for (j = 0; j < left; j++)
index fc08ca6161040a89d1c54574cb6f6ec2866886fb..5ad5d5723fcbc128645552ced1fb6de2a07c4dff 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -262,6 +262,7 @@ check_hmapx (struct hmapx *hmapx, const int data[], size_t cnt,
   size_t i, j;
   int *order;
 
+  check (hmapx_is_empty (hmapx) == (cnt == 0));
   check (hmapx_count (hmapx) == cnt);
   check (cnt <= hmapx_capacity (hmapx));
 
index e8cbffa5e6334408bbd69dc4c7c2a7e9dbb72829..8d6e68316834d707254b5888f92d1b41b1064bb6 100644 (file)
@@ -212,7 +212,7 @@ check_pattern (const struct range_set *rs, unsigned int pattern)
      caching. */
   for (start = 0; start <= 32; start++)
     {
-      struct range_set *nonconst_rs = (struct range_set *) rs;
+      struct range_set *nonconst_rs = CONST_CAST (struct range_set *, rs);
       nonconst_rs->cache_end = 0;
       s1 = range_set_scan (rs, start);
       s2 = next_1bit (pattern, start);
diff --git a/tests/libpspp/string-map-test.c b/tests/libpspp/string-map-test.c
new file mode 100644 (file)
index 0000000..e3f3b79
--- /dev/null
@@ -0,0 +1,906 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+/* This is a test program for the string_map_* routines defined in
+   string-map.c.  This test program aims to be as comprehensive as possible.
+   "gcov -a -b" should report almost complete coverage of lines, blocks and
+   branches in string-map.c, except that one branch caused by hash collision is
+   not exercised because our hash function has so few collisions.  "valgrind
+   --leak-check=yes --show-reachable=yes" should give a clean report. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/string-map.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/hash-functions.h>
+#include <libpspp/compiler.h>
+#include <libpspp/string-set.h>
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* Exit with a failure code.
+   (Place a breakpoint on this function while debugging.) */
+static void
+check_die (void)
+{
+  exit (EXIT_FAILURE);
+}
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line)
+{
+  if (!ok)
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      check_die ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+
+/* Prints a message about memory exhaustion and exits with a
+   failure code. */
+static void
+xalloc_die (void)
+{
+  printf ("virtual memory exhausted\n");
+  exit (EXIT_FAILURE);
+}
+
+static void *xmalloc (size_t n) MALLOC_LIKE;
+static void *xnmalloc (size_t n, size_t m) MALLOC_LIKE;
+static void *xmemdup (const void *p, size_t n) MALLOC_LIKE;
+
+/* Allocates and returns N bytes of memory. */
+static void *
+xmalloc (size_t n)
+{
+  if (n != 0)
+    {
+      void *p = malloc (n);
+      if (p == NULL)
+        xalloc_die ();
+
+      return p;
+    }
+  else
+    return NULL;
+}
+
+static void *
+xmemdup (const void *p, size_t n)
+{
+  void *q = xmalloc (n);
+  memcpy (q, p, n);
+  return q;
+}
+
+/* Clone STRING.  */
+static char *
+xstrdup (const char *string)
+{
+  return xmemdup (string, strlen (string) + 1);
+}
+
+/* Allocates and returns N * M bytes of memory. */
+static void *
+xnmalloc (size_t n, size_t m)
+{
+  if ((size_t) -1 / m <= n)
+    xalloc_die ();
+  return xmalloc (n * m);
+}
+\f
+/* Support routines. */
+
+enum {
+  IDX_BITS = 10,
+  MAX_IDX = 1 << IDX_BITS,
+  KEY_MASK = (MAX_IDX - 1),
+  KEY_SHIFT = 0,
+  VALUE_MASK = (MAX_IDX - 1) << IDX_BITS,
+  VALUE_SHIFT = IDX_BITS
+};
+
+static char *string_table[MAX_IDX];
+
+static const char *
+get_string (int idx)
+{
+  char **s;
+
+  assert (idx >= 0 && idx < MAX_IDX);
+  s = &string_table[idx];
+  if (*s == NULL)
+    {
+      *s = xmalloc (16);
+      sprintf (*s, "%d", idx);
+    }
+  return *s;
+}
+
+static void
+free_strings (void)
+{
+  int i;
+
+  for (i = 0; i < MAX_IDX; i++)
+    free (string_table[i]);
+}
+
+static const char *
+make_key (int value)
+{
+  return get_string ((value & KEY_MASK) >> KEY_SHIFT);
+}
+
+static const char *
+make_value (int value)
+{
+  return get_string ((value & VALUE_MASK) >> VALUE_SHIFT);
+}
+
+static int
+random_value (unsigned int seed, int basis)
+{
+  return hash_int (seed, basis) & VALUE_MASK;
+}
+
+/* Swaps *A and *B. */
+static void
+swap (int *a, int *b)
+{
+  int t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of the CNT integers starting at VALUES. */
+static void
+reverse (int *values, size_t cnt)
+{
+  size_t i = 0;
+  size_t j = cnt;
+
+  while (j > i)
+    swap (&values[i++], &values[--j]);
+}
+
+/* Arranges the CNT elements in VALUES into the lexicographically next greater
+   permutation.  Returns true if successful.  If VALUES is already the
+   lexicographically greatest permutation of its elements (i.e. ordered from
+   greatest to smallest), arranges them into the lexicographically least
+   permutation (i.e. ordered from smallest to largest) and returns false.
+
+   Comparisons among elements of VALUES consider only the bits in KEY_MASK. */
+static bool
+next_permutation (int *values, size_t cnt)
+{
+  if (cnt > 0)
+    {
+      size_t i = cnt - 1;
+      while (i != 0)
+        {
+          i--;
+          if ((values[i] & KEY_MASK) < (values[i + 1] & KEY_MASK))
+            {
+              size_t j;
+              for (j = cnt - 1;
+                   (values[i] & KEY_MASK) >= (values[j] & KEY_MASK);
+                   j--)
+                continue;
+              swap (values + i, values + j);
+              reverse (values + (i + 1), cnt - (i + 1));
+              return true;
+            }
+        }
+
+      reverse (values, cnt);
+    }
+
+  return false;
+}
+
+/* Returns N!. */
+static unsigned int
+factorial (unsigned int n)
+{
+  unsigned int value = 1;
+  while (n > 1)
+    value *= n--;
+  return value;
+}
+
+/* Randomly shuffles the CNT elements in ARRAY, each of which is
+   SIZE bytes in size. */
+static void
+random_shuffle (void *array_, size_t cnt, size_t size)
+{
+  char *array = array_;
+  char *tmp = xmalloc (size);
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = rand () % (cnt - i) + i;
+      if (i != j)
+        {
+          memcpy (tmp, array + j * size, size);
+          memcpy (array + j * size, array + i * size, size);
+          memcpy (array + i * size, tmp, size);
+        }
+    }
+
+  free (tmp);
+}
+
+/* Checks that MAP contains the CNT strings in DATA, that its structure is
+   correct, and that certain operations on MAP produce the expected results. */
+static void
+check_string_map (struct string_map *map, const int data[], size_t cnt)
+{
+  size_t i;
+
+  check (string_map_is_empty (map) == (cnt == 0));
+  check (string_map_count (map) == cnt);
+
+  for (i = 0; i < cnt; i++)
+    {
+      struct string_map_node *node;
+      const char *key = make_key (data[i]);
+      const char *value = make_value (data[i]);
+      const char *found_value;
+
+      check (string_map_contains (map, key));
+
+      node = string_map_find_node (map, key);
+      check (node != NULL);
+      check (!strcmp (key, string_map_node_get_key (node)));
+      check (!strcmp (value, string_map_node_get_value (node)));
+
+      check (node == string_map_insert (map, key, "abc"));
+      check (!strcmp (value, string_map_node_get_value (node)));
+
+      check (node == string_map_insert_nocopy (map, xstrdup (key),
+                                               xstrdup ("def")));
+      check (!strcmp (value, string_map_node_get_value (node)));
+
+      found_value = string_map_find (map, key);
+      check (found_value != NULL);
+      check (!strcmp (found_value, value));
+    }
+
+  check (!string_map_contains (map, "xxx"));
+  check (string_map_find (map, "z") == NULL);
+  check (string_map_find_node (map, "") == NULL);
+  check (!string_map_delete (map, "xyz"));
+
+  if (cnt == 0)
+    check (string_map_first (map) == NULL);
+  else
+    {
+      const struct string_map_node *node;
+      int *data_copy;
+      int left;
+
+      data_copy = xmemdup (data, cnt * sizeof *data);
+      left = cnt;
+      for (node = string_map_first (map), i = 0; i < cnt;
+           node = string_map_next (map, node), i++)
+        {
+          const char *key = string_map_node_get_key (node);
+          const char *value = string_map_node_get_value (node);
+          size_t j;
+
+          for (j = 0; j < left; j++)
+            if (!strcmp (key, make_key (data_copy[j]))
+                || !strcmp (value, make_value (data_copy[j])))
+              {
+                data_copy[j] = data_copy[--left];
+                goto next;
+              }
+          check_die ();
+
+        next: ;
+        }
+      check (node == NULL);
+      free (data_copy);
+    }
+}
+
+/* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a map in the
+   order specified by INSERTIONS, then deletes them in the order specified by
+   DELETIONS, checking the map's contents for correctness after each
+   operation.  */
+static void
+test_insert_delete (const int insertions[],
+                    const int deletions[],
+                    size_t cnt)
+{
+  struct string_map map;
+  size_t i;
+
+  string_map_init (&map);
+  check_string_map (&map, NULL, 0);
+  for (i = 0; i < cnt; i++)
+    {
+      check (string_map_insert (&map, make_key (insertions[i]),
+                                make_value (insertions[i])));
+      check_string_map (&map, insertions, i + 1);
+    }
+  for (i = 0; i < cnt; i++)
+    {
+      check (string_map_delete (&map, make_key (deletions[i])));
+      check_string_map (&map, deletions + i + 1, cnt - i - 1);
+    }
+  string_map_destroy (&map);
+}
+\f
+/* Inserts strings into a map in each possible order, then removes them in each
+   possible order, up to a specified maximum size. */
+static void
+test_insert_any_remove_any (void)
+{
+  const int basis = 0;
+  const int max_elems = 5;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int ins_perm_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i | random_value (i, basis);
+
+      for (ins_perm_cnt = 0;
+           ins_perm_cnt == 0 || next_permutation (insertions, cnt);
+           ins_perm_cnt++)
+        {
+          unsigned int del_perm_cnt;
+          int i;
+
+          for (i = 0; i < cnt; i++)
+            deletions[i] = i | random_value (i, basis);
+
+          for (del_perm_cnt = 0;
+               del_perm_cnt == 0 || next_permutation (deletions, cnt);
+               del_perm_cnt++)
+            test_insert_delete (insertions, deletions, cnt);
+
+          check (del_perm_cnt == factorial (cnt));
+        }
+      check (ins_perm_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+/* Inserts strings into a map in each possible order, then removes them in the
+   same order, up to a specified maximum size. */
+static void
+test_insert_any_remove_same (void)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      for (i = 0; i < cnt; i++)
+        values[i] = i | random_value (i, 1);
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        test_insert_delete (values, values, cnt);
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+    }
+}
+
+/* Inserts strings into a map in each possible order, then
+   removes them in reverse order, up to a specified maximum
+   size. */
+static void
+test_insert_any_remove_reverse (void)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int permutation_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i | random_value (i, 2);
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (insertions, cnt);
+           permutation_cnt++)
+        {
+          memcpy (deletions, insertions, sizeof *insertions * cnt);
+          reverse (deletions, cnt);
+
+          test_insert_delete (insertions, deletions, cnt);
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+/* Inserts and removes strings in a map, in random order. */
+static void
+test_random_sequence (void)
+{
+  const int basis = 3;
+  const int max_elems = 64;
+  const int max_trials = 8;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt += 2)
+    {
+      int *insertions, *deletions;
+      int trial;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i | random_value (i, basis);
+      for (i = 0; i < cnt; i++)
+        deletions[i] = i | random_value (i, basis);
+
+      for (trial = 0; trial < max_trials; trial++)
+        {
+          random_shuffle (insertions, cnt, sizeof *insertions);
+          random_shuffle (deletions, cnt, sizeof *deletions);
+
+          test_insert_delete (insertions, deletions, cnt);
+        }
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+/* Inserts strings into a map in ascending order, then delete in ascending
+   order. */
+static void
+test_insert_ordered (void)
+{
+  const int max_elems = 64;
+  int *values;
+  struct string_map map;
+  int i;
+
+  string_map_init (&map);
+  values = xnmalloc (max_elems, sizeof *values);
+  for (i = 0; i < max_elems; i++)
+    {
+      values[i] = i | random_value (i, 4);
+      string_map_insert_nocopy (&map, xstrdup (make_key (values[i])),
+                                xstrdup (make_value (values[i])));
+      check_string_map (&map, values, i + 1);
+    }
+  for (i = 0; i < max_elems; i++)
+    {
+      string_map_delete (&map, make_key (i));
+      check_string_map (&map, values + i + 1, max_elems - i - 1);
+    }
+  string_map_destroy (&map);
+  free (values);
+}
+
+/* Inserts and replaces strings in a map, in random order. */
+static void
+test_replace (void)
+{
+  const int basis = 15;
+  enum { MAX_ELEMS = 16 };
+  const int max_trials = 8;
+  int cnt;
+
+  for (cnt = 0; cnt <= MAX_ELEMS; cnt++)
+    {
+      int insertions[MAX_ELEMS];
+      int trial;
+      int i;
+
+      for (i = 0; i < cnt; i++)
+        insertions[i] = (i / 2) | random_value (i, basis);
+
+      for (trial = 0; trial < max_trials; trial++)
+        {
+          struct string_map map;
+          int data[MAX_ELEMS];
+          int n_data;
+
+          /* Insert with replacement in random order. */
+          n_data = 0;
+          string_map_init (&map);
+          random_shuffle (insertions, cnt, sizeof *insertions);
+          for (i = 0; i < cnt; i++)
+            {
+              const char *key = make_key (insertions[i]);
+              const char *value = make_value (insertions[i]);
+              int j;
+
+              for (j = 0; j < n_data; j++)
+                if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
+                  {
+                    data[j] = insertions[i];
+                    goto found;
+                  }
+              data[n_data++] = insertions[i];
+            found:
+
+              if (i % 2)
+                string_map_replace (&map, key, value);
+              else
+                string_map_replace_nocopy (&map,
+                                           xstrdup (key), xstrdup (value));
+              check_string_map (&map, data, n_data);
+            }
+
+          /* Delete in original order. */
+          for (i = 0; i < cnt; i++)
+            {
+              const char *expected_value;
+              char *value;
+              int j;
+
+              expected_value = NULL;
+              for (j = 0; j < n_data; j++)
+                if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
+                  {
+                    expected_value = make_value (data[j]);
+                    data[j] = data[--n_data];
+                    break;
+                  }
+
+              value = string_map_find_and_delete (&map,
+                                                  make_key (insertions[i]));
+              check ((value != NULL) == (expected_value != NULL));
+              check (value == NULL || !strcmp (value, expected_value));
+              free (value);
+            }
+          assert (string_map_is_empty (&map));
+
+          string_map_destroy (&map);
+        }
+    }
+}
+
+static void
+make_patterned_map (struct string_map *map, unsigned int pattern, int basis,
+                    int insertions[], int *np)
+{
+  int n;
+  int i;
+
+  string_map_init (map);
+
+  n = 0;
+  for (i = 0; pattern != 0; i++)
+    if (pattern & (1u << i))
+      {
+        pattern &= pattern - 1;
+        insertions[n] = i | random_value (i, basis);
+        check (string_map_insert (map, make_key (insertions[n]),
+                                  make_value (insertions[n])));
+        n++;
+      }
+  check_string_map (map, insertions, n);
+
+  *np = n;
+}
+
+static void
+for_each_map (void (*cb)(struct string_map *, int data[], int n),
+              int basis)
+{
+  enum { MAX_ELEMS = 5 };
+  unsigned int pattern;
+
+  for (pattern = 0; pattern < (1u << MAX_ELEMS); pattern++)
+    {
+      int data[MAX_ELEMS];
+      struct string_map map;
+      int n;
+
+      make_patterned_map (&map, pattern, basis, data, &n);
+      (*cb) (&map, data, n);
+      string_map_destroy (&map);
+    }
+}
+
+static void
+for_each_pair_of_maps (
+  void (*cb)(struct string_map *a, int a_data[], int n_a,
+             struct string_map *b, int b_data[], int n_b),
+  int a_basis, int b_basis)
+{
+  enum { MAX_ELEMS = 5 };
+  unsigned int a_pattern, b_pattern;
+
+  for (a_pattern = 0; a_pattern < (1u << MAX_ELEMS); a_pattern++)
+    for (b_pattern = 0; b_pattern < (1u << MAX_ELEMS); b_pattern++)
+      {
+        int a_data[MAX_ELEMS], b_data[MAX_ELEMS];
+        struct string_map a_map, b_map;
+        int n_a, n_b;
+
+        make_patterned_map (&a_map, a_pattern, a_basis, a_data, &n_a);
+        make_patterned_map (&b_map, b_pattern, b_basis, b_data, &n_b);
+        (*cb) (&a_map, a_data, n_a, &b_map, b_data, n_b);
+        string_map_destroy (&a_map);
+        string_map_destroy (&b_map);
+      }
+}
+
+static void
+clear_cb (struct string_map *map, int data[] UNUSED, int n UNUSED)
+{
+  string_map_clear (map);
+  check_string_map (map, NULL, 0);
+}
+
+static void
+test_clear (void)
+{
+  for_each_map (clear_cb, 5);
+}
+
+static void
+clone_cb (struct string_map *map, int data[], int n)
+{
+  struct string_map clone;
+
+  string_map_clone (&clone, map);
+  check_string_map (&clone, data, n);
+  string_map_destroy (&clone);
+}
+
+static void
+test_clone (void)
+{
+  for_each_map (clone_cb, 6);
+}
+
+static void
+node_swap_value_cb (struct string_map *map, int data[], int n)
+{
+  int i;
+
+  for (i = 0; i < n; i++)
+    {
+      const char *value = make_value (data[i]);
+      struct string_map_node *node;
+      char *old_value;
+
+      node = string_map_find_node (map, make_key (data[i]));
+      check (node != NULL);
+      check (!strcmp (string_map_node_get_value (node), value));
+      data[i] = (data[i] & KEY_MASK) | random_value (i, 15);
+      old_value = string_map_node_swap_value (node, make_value (data[i]));
+      check (old_value != NULL);
+      check (!strcmp (value, old_value));
+      free (old_value);
+    }
+}
+
+static void
+test_node_swap_value (void)
+{
+  for_each_map (node_swap_value_cb, 14);
+}
+
+static void
+swap_cb (struct string_map *a, int a_data[], int n_a,
+         struct string_map *b, int b_data[], int n_b)
+{
+  string_map_swap (a, b);
+  check_string_map (a, b_data, n_b);
+  check_string_map (b, a_data, n_a);
+}
+
+static void
+test_swap (void)
+{
+  for_each_pair_of_maps (swap_cb, 7, 8);
+}
+
+static void
+insert_map_cb (struct string_map *a, int a_data[], int n_a,
+               struct string_map *b, int b_data[], int n_b)
+{
+  int i, j;
+
+  string_map_insert_map (a, b);
+
+  for (i = 0; i < n_b; i++)
+    {
+      for (j = 0; j < n_a; j++)
+        if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
+          goto found;
+      a_data[n_a++] = b_data[i];
+    found:;
+    }
+  check_string_map (a, a_data, n_a);
+  check_string_map (b, b_data, n_b);
+}
+
+static void
+test_insert_map (void)
+{
+  for_each_pair_of_maps (insert_map_cb, 91, 10);
+}
+
+static void
+replace_map_cb (struct string_map *a, int a_data[], int n_a,
+               struct string_map *b, int b_data[], int n_b)
+{
+  int i, j;
+
+  string_map_replace_map (a, b);
+
+  for (i = 0; i < n_b; i++)
+    {
+      for (j = 0; j < n_a; j++)
+        if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
+          {
+            a_data[j] = (a_data[j] & KEY_MASK) | (b_data[i] & VALUE_MASK);
+            goto found;
+          }
+      a_data[n_a++] = b_data[i];
+    found:;
+    }
+  check_string_map (a, a_data, n_a);
+  check_string_map (b, b_data, n_b);
+}
+
+static void
+test_replace_map (void)
+{
+  for_each_pair_of_maps (replace_map_cb, 11, 12);
+}
+
+static void
+check_set (struct string_set *set, const int *data, int n_data,
+           int mask, int shift)
+{
+  int *unique;
+  int n_unique;
+  int i;
+
+  n_unique = 0;
+  unique = xmalloc (n_data * sizeof *unique);
+  for (i = 0; i < n_data; i++)
+    {
+      int idx = (data[i] & mask) >> shift;
+      int j;
+
+      for (j = 0; j < n_unique; j++)
+        if (unique[j] == idx)
+          goto found;
+      unique[n_unique++] = idx;
+    found:;
+    }
+
+  check (string_set_count (set) == n_unique);
+  for (i = 0; i < n_unique; i++)
+    check (string_set_contains (set, get_string (unique[i])));
+  string_set_destroy (set);
+  free (unique);
+}
+
+static void
+get_keys_and_values_cb (struct string_map *map, int data[], int n)
+{
+  struct string_set keys, values;
+
+  string_set_init (&keys);
+  string_set_init (&values);
+  string_map_get_keys (map, &keys);
+  string_map_get_values (map, &values);
+  check_set (&keys, data, n, KEY_MASK, KEY_SHIFT);
+  check_set (&values, data, n, VALUE_MASK, VALUE_SHIFT);
+}
+
+static void
+test_get_keys_and_values (void)
+{
+  for_each_map (get_keys_and_values_cb, 13);
+}
+
+static void
+test_destroy_null (void)
+{
+  string_map_destroy (NULL);
+}
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name)
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void)
+{
+  run_test (test_insert_any_remove_any, "insert any order, delete any order");
+  run_test (test_insert_any_remove_same,
+            "insert any order, delete same order");
+  run_test (test_insert_any_remove_reverse,
+            "insert any order, delete reverse order");
+  run_test (test_random_sequence, "insert and delete in random sequence");
+  run_test (test_replace, "insert and replace in random sequence");
+  run_test (test_insert_ordered, "insert in ascending order");
+  run_test (test_clear, "clear");
+  run_test (test_clone, "clone");
+  run_test (test_swap, "swap");
+  run_test (test_node_swap_value, "node_swap_value");
+  run_test (test_insert_map, "insert_map");
+  run_test (test_replace_map, "replace_map");
+  run_test (test_get_keys_and_values, "get keys and values");
+  run_test (test_destroy_null, "destroying null table");
+
+  putchar ('\n');
+
+  free_strings ();
+
+  return 0;
+}
diff --git a/tests/libpspp/string-set-test.c b/tests/libpspp/string-set-test.c
new file mode 100644 (file)
index 0000000..6aafef4
--- /dev/null
@@ -0,0 +1,681 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+/* This is a test program for the string_set_* routines defined in
+   string-set.c.  This test program aims to be as comprehensive as possible.
+   "gcov -a -b" should report almost complete coverage of lines, blocks and
+   branches in string-set.c, except that one branch caused by hash collision is
+   not exercised because our hash function has so few collisions.  "valgrind
+   --leak-check=yes --show-reachable=yes" should give a clean report. */
+
+#include <config.h>
+
+#include <libpspp/string-set.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/compiler.h>
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* Exit with a failure code.
+   (Place a breakpoint on this function while debugging.) */
+static void
+check_die (void)
+{
+  exit (EXIT_FAILURE);
+}
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line)
+{
+  if (!ok)
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      check_die ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+
+/* Prints a message about memory exhaustion and exits with a
+   failure code. */
+static void
+xalloc_die (void)
+{
+  printf ("virtual memory exhausted\n");
+  exit (EXIT_FAILURE);
+}
+
+static void *xmalloc (size_t n) MALLOC_LIKE;
+static void *xnmalloc (size_t n, size_t m) MALLOC_LIKE;
+static void *xmemdup (const void *p, size_t n) MALLOC_LIKE;
+
+/* Allocates and returns N bytes of memory. */
+static void *
+xmalloc (size_t n)
+{
+  if (n != 0)
+    {
+      void *p = malloc (n);
+      if (p == NULL)
+        xalloc_die ();
+
+      return p;
+    }
+  else
+    return NULL;
+}
+
+static void *
+xmemdup (const void *p, size_t n)
+{
+  void *q = xmalloc (n);
+  memcpy (q, p, n);
+  return q;
+}
+
+/* Clone STRING.  */
+static char *
+xstrdup (const char *string)
+{
+  return xmemdup (string, strlen (string) + 1);
+}
+
+/* Allocates and returns N * M bytes of memory. */
+static void *
+xnmalloc (size_t n, size_t m)
+{
+  if ((size_t) -1 / m <= n)
+    xalloc_die ();
+  return xmalloc (n * m);
+}
+\f
+/* Support routines. */
+
+enum { MAX_VALUE = 1024 };
+
+static char *string_table[MAX_VALUE];
+
+static const char *
+make_string (int value)
+{
+  char **s;
+
+  assert (value >= 0 && value < MAX_VALUE);
+  s = &string_table[value];
+  if (*s == NULL)
+    {
+      *s = xmalloc (16);
+      sprintf (*s, "%d", value);
+    }
+  return *s;
+}
+
+static void
+free_strings (void)
+{
+  int i;
+
+  for (i = 0; i < MAX_VALUE; i++)
+    free (string_table[i]);
+}
+
+/* Swaps *A and *B. */
+static void
+swap (int *a, int *b)
+{
+  int t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of the CNT integers starting at VALUES. */
+static void
+reverse (int *values, size_t cnt)
+{
+  size_t i = 0;
+  size_t j = cnt;
+
+  while (j > i)
+    swap (&values[i++], &values[--j]);
+}
+
+/* Arranges the CNT elements in VALUES into the lexicographically
+   next greater permutation.  Returns true if successful.
+   If VALUES is already the lexicographically greatest
+   permutation of its elements (i.e. ordered from greatest to
+   smallest), arranges them into the lexicographically least
+   permutation (i.e. ordered from smallest to largest) and
+   returns false. */
+static bool
+next_permutation (int *values, size_t cnt)
+{
+  if (cnt > 0)
+    {
+      size_t i = cnt - 1;
+      while (i != 0)
+        {
+          i--;
+          if (values[i] < values[i + 1])
+            {
+              size_t j;
+              for (j = cnt - 1; values[i] >= values[j]; j--)
+                continue;
+              swap (values + i, values + j);
+              reverse (values + (i + 1), cnt - (i + 1));
+              return true;
+            }
+        }
+
+      reverse (values, cnt);
+    }
+
+  return false;
+}
+
+/* Returns N!. */
+static unsigned int
+factorial (unsigned int n)
+{
+  unsigned int value = 1;
+  while (n > 1)
+    value *= n--;
+  return value;
+}
+
+/* Randomly shuffles the CNT elements in ARRAY, each of which is
+   SIZE bytes in size. */
+static void
+random_shuffle (void *array_, size_t cnt, size_t size)
+{
+  char *array = array_;
+  char *tmp = xmalloc (size);
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = rand () % (cnt - i) + i;
+      if (i != j)
+        {
+          memcpy (tmp, array + j * size, size);
+          memcpy (array + j * size, array + i * size, size);
+          memcpy (array + i * size, tmp, size);
+        }
+    }
+
+  free (tmp);
+}
+
+/* Checks that SET contains the CNT strings in DATA, that its structure is
+   correct, and that certain operations on SET produce the expected results. */
+static void
+check_string_set (struct string_set *set, const int data[], size_t cnt)
+{
+  size_t i;
+
+  check (string_set_is_empty (set) == (cnt == 0));
+  check (string_set_count (set) == cnt);
+
+  for (i = 0; i < cnt; i++)
+    {
+      struct string_set_node *node;
+      const char *s = make_string (data[i]);
+
+      check (string_set_contains (set, s));
+      check (!string_set_insert (set, s));
+      check (!string_set_insert_nocopy (set, xstrdup (s)));
+
+      node = string_set_find_node (set, s);
+      check (node != NULL);
+      check (!strcmp (s, string_set_node_get_string (node)));
+    }
+
+  check (!string_set_contains (set, "xxx"));
+  check (string_set_find_node (set, "") == NULL);
+
+  if (cnt == 0)
+    check (string_set_first (set) == NULL);
+  else
+    {
+      const struct string_set_node *node;
+      int *data_copy;
+      int left;
+
+      data_copy = xmemdup (data, cnt * sizeof *data);
+      left = cnt;
+      for (node = string_set_first (set), i = 0; i < cnt;
+           node = string_set_next (set, node), i++)
+        {
+          const char *s = string_set_node_get_string (node);
+          size_t j;
+
+          for (j = 0; j < left; j++)
+            if (!strcmp (s, make_string (data_copy[j])))
+              {
+                data_copy[j] = data_copy[--left];
+                goto next;
+              }
+          check_die ();
+
+        next: ;
+        }
+      check (node == NULL);
+      free (data_copy);
+    }
+}
+
+/* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a set in the
+   order specified by INSERTIONS, then deletes them in the order specified by
+   DELETIONS, checking the set's contents for correctness after each
+   operation.  */
+static void
+test_insert_delete (const int insertions[],
+                    const int deletions[],
+                    size_t cnt)
+{
+  struct string_set set;
+  size_t i;
+
+  string_set_init (&set);
+  check_string_set (&set, NULL, 0);
+  for (i = 0; i < cnt; i++)
+    {
+      check (string_set_insert (&set, make_string (insertions[i])));
+      check_string_set (&set, insertions, i + 1);
+    }
+  for (i = 0; i < cnt; i++)
+    {
+      check (string_set_delete (&set, make_string (deletions[i])));
+      check_string_set (&set, deletions + i + 1, cnt - i - 1);
+    }
+  string_set_destroy (&set);
+}
+\f
+/* Inserts strings into a set in each possible order, then removes them in each
+   possible order, up to a specified maximum size. */
+static void
+test_insert_any_remove_any (void)
+{
+  const int max_elems = 5;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int ins_perm_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+
+      for (ins_perm_cnt = 0;
+           ins_perm_cnt == 0 || next_permutation (insertions, cnt);
+           ins_perm_cnt++)
+        {
+          unsigned int del_perm_cnt;
+          int i;
+
+          for (i = 0; i < cnt; i++)
+            deletions[i] = i;
+
+          for (del_perm_cnt = 0;
+               del_perm_cnt == 0 || next_permutation (deletions, cnt);
+               del_perm_cnt++)
+            test_insert_delete (insertions, deletions, cnt);
+
+          check (del_perm_cnt == factorial (cnt));
+        }
+      check (ins_perm_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+/* Inserts strings into a set in each possible order, then removes them in the
+   same order, up to a specified maximum size. */
+static void
+test_insert_any_remove_same (void)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      for (i = 0; i < cnt; i++)
+        values[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        test_insert_delete (values, values, cnt);
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+    }
+}
+
+/* Inserts strings into a set in each possible order, then
+   removes them in reverse order, up to a specified maximum
+   size. */
+static void
+test_insert_any_remove_reverse (void)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int permutation_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (insertions, cnt);
+           permutation_cnt++)
+        {
+          memcpy (deletions, insertions, sizeof *insertions * cnt);
+          reverse (deletions, cnt);
+
+          test_insert_delete (insertions, deletions, cnt);
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+/* Inserts and removes strings in a set, in random order. */
+static void
+test_random_sequence (void)
+{
+  const int max_elems = 64;
+  const int max_trials = 8;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt += 2)
+    {
+      int *insertions, *deletions;
+      int trial;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+      for (i = 0; i < cnt; i++)
+        deletions[i] = i;
+
+      for (trial = 0; trial < max_trials; trial++)
+        {
+          random_shuffle (insertions, cnt, sizeof *insertions);
+          random_shuffle (deletions, cnt, sizeof *deletions);
+
+          test_insert_delete (insertions, deletions, cnt);
+        }
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+/* Inserts strings into a set in ascending order, then delete in ascending
+   order. */
+static void
+test_insert_ordered (void)
+{
+  const int max_elems = 64;
+  int *values;
+  struct string_set set;
+  int i;
+
+  string_set_init (&set);
+  values = xnmalloc (max_elems, sizeof *values);
+  for (i = 0; i < max_elems; i++)
+    {
+      values[i] = i;
+      string_set_insert_nocopy (&set, xstrdup (make_string (i)));
+      check_string_set (&set, values, i + 1);
+    }
+  for (i = 0; i < max_elems; i++)
+    {
+      string_set_delete (&set, make_string (i));
+      check_string_set (&set, values + i + 1, max_elems - i - 1);
+    }
+  string_set_destroy (&set);
+  free (values);
+}
+
+static void
+test_boolean_ops (void (*function)(struct string_set *a, struct string_set *b,
+                                   unsigned int *a_pat, unsigned int *b_pat))
+{
+  enum { MAX_STRINGS = 7 };
+  unsigned int a_pat, b_pat;
+
+  for (a_pat = 0; a_pat < (1u << MAX_STRINGS); a_pat++)
+    for (b_pat = 0; b_pat < (1u << MAX_STRINGS); b_pat++)
+      {
+        unsigned int new_a_pat = a_pat;
+        unsigned int new_b_pat = b_pat;
+        struct string_set a, b;
+        int a_strings[MAX_STRINGS], b_strings[MAX_STRINGS];
+        size_t i, n_a, n_b;
+
+        string_set_init (&a);
+        string_set_init (&b);
+        for (i = 0; i < MAX_STRINGS; i++)
+          {
+            if (a_pat & (1u << i))
+              string_set_insert (&a, make_string (i));
+            if (b_pat & (1u << i))
+              string_set_insert (&b, make_string (i));
+          }
+
+        function (&a, &b, &new_a_pat, &new_b_pat);
+
+        n_a = n_b = 0;
+        for (i = 0; i < MAX_STRINGS; i++)
+          {
+            if (new_a_pat & (1u << i))
+              a_strings[n_a++] = i;
+            if (new_b_pat & (1u << i))
+              b_strings[n_b++] = i;
+          }
+        check_string_set (&a, a_strings, n_a);
+        check_string_set (&b, b_strings, n_b);
+        string_set_destroy (&a);
+        string_set_destroy (&b);
+      }
+}
+
+static void
+union_cb (struct string_set *a, struct string_set *b,
+          unsigned int *a_pat, unsigned int *b_pat)
+{
+  string_set_union (a, b);
+  *a_pat |= *b_pat;
+}
+
+static void
+test_union (void)
+{
+  test_boolean_ops (union_cb);
+}
+
+static void
+union_and_intersection_cb (struct string_set *a, struct string_set *b,
+                           unsigned int *a_pat, unsigned int *b_pat)
+{
+  unsigned int orig_a_pat = *a_pat;
+  unsigned int orig_b_pat = *b_pat;
+
+  string_set_union_and_intersection (a, b);
+  *a_pat = orig_a_pat | orig_b_pat;
+  *b_pat = orig_a_pat & orig_b_pat;
+}
+
+static void
+test_union_and_intersection (void)
+{
+  test_boolean_ops (union_and_intersection_cb);
+}
+
+static void
+intersect_cb (struct string_set *a, struct string_set *b,
+              unsigned int *a_pat, unsigned int *b_pat)
+{
+  string_set_intersect (a, b);
+  *a_pat &= *b_pat;
+}
+
+static void
+test_intersect (void)
+{
+  test_boolean_ops (intersect_cb);
+}
+
+static void
+subtract_cb (struct string_set *a, struct string_set *b,
+              unsigned int *a_pat, unsigned int *b_pat)
+{
+  string_set_subtract (a, b);
+  *a_pat &= ~*b_pat;
+}
+
+static void
+test_subtract (void)
+{
+  test_boolean_ops (subtract_cb);
+}
+
+static void
+swap_cb (struct string_set *a, struct string_set *b,
+         unsigned int *a_pat, unsigned int *b_pat)
+{
+  unsigned int tmp;
+  string_set_swap (a, b);
+  tmp = *a_pat;
+  *a_pat = *b_pat;
+  *b_pat = tmp;
+}
+
+static void
+test_swap (void)
+{
+  test_boolean_ops (swap_cb);
+}
+
+static void
+clear_cb (struct string_set *a, struct string_set *b UNUSED,
+         unsigned int *a_pat, unsigned int *b_pat UNUSED)
+{
+  string_set_clear (a);
+  *a_pat = 0;
+}
+
+static void
+test_clear (void)
+{
+  test_boolean_ops (clear_cb);
+}
+
+static void
+clone_cb (struct string_set *a, struct string_set *b,
+         unsigned int *a_pat, unsigned int *b_pat)
+{
+  string_set_destroy (a);
+  string_set_clone (a, b);
+  *a_pat = *b_pat;
+}
+
+static void
+test_clone (void)
+{
+  test_boolean_ops (clone_cb);
+}
+
+static void
+test_destroy_null (void)
+{
+  string_set_destroy (NULL);
+}
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name)
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void)
+{
+  run_test (test_insert_any_remove_any, "insert any order, delete any order");
+  run_test (test_insert_any_remove_same,
+            "insert any order, delete same order");
+  run_test (test_insert_any_remove_reverse,
+            "insert any order, delete reverse order");
+  run_test (test_random_sequence, "insert and delete in random sequence");
+  run_test (test_insert_ordered, "insert in ascending order");
+  run_test (test_union, "union");
+  run_test (test_union_and_intersection, "union and intersection");
+  run_test (test_intersect, "intersect");
+  run_test (test_subtract, "subtract");
+  run_test (test_swap, "swap");
+  run_test (test_clear, "clear");
+  run_test (test_clone, "clone");
+  run_test (test_destroy_null, "destroying null table");
+
+  putchar ('\n');
+
+  free_strings ();
+
+  return 0;
+}