summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwl <wl>2011-07-26 17:11:09 +0000
committerwl <wl>2011-07-26 17:11:09 +0000
commit10f2f7c92c0a878763d523dd798c6c4be0f8e3aa (patch)
tree86ac4914be0670f0dce3fdba56264c585b75e1e9
parent8f6aeb4493fb6f1a38e83f2050d27529fc2d238b (diff)
downloadgroff-10f2f7c92c0a878763d523dd798c6c4be0f8e3aa.tar.gz
Add new output device `gropdf'.
* font/devpdf/*: New device files for gropdf. * src/devices/gropdf/*: New device. * Makefile.comm (install_dev, uninstall_dev): Handle more subdirectories. * Makefile.in (DEVDIRS, OTHERDIRS, EXTRADIRS): Add directories related to gropdf. * MANIFEST: Updated. * test-groff.in (GROFF_BIN_PATH): Updated. * tmac/Makefile.sub (NORMALFILES): Updated. * tmac/pdf.tmac: New file. * tmac/troffrc: Updated. * doc/groff.texinfo: Document it. * doc/Makefile.in, doc/Makefile.sub (groff_bin_dirs): Udpated. * doc/pic.ms: Updated. * man/groff_out.man, src/devices/grops/grops.man, src/preproc/pic/pic.man, src/roff/groff/groff.man, src/utils/afmtodit/afmtodit.man, src/utils/pfbtops/pfbtops.man: Updated. * contrib/pdfmark/pdfmark.ms: Updated.
-rw-r--r--ChangeLog29
-rw-r--r--MANIFEST4
-rw-r--r--Makefile.comm20
-rw-r--r--Makefile.in4
-rw-r--r--contrib/pdfmark/pdfmark.ms10
-rw-r--r--doc/Makefile.in3
-rw-r--r--doc/Makefile.sub3
-rw-r--r--doc/groff.texinfo122
-rw-r--r--doc/pic.ms4
-rw-r--r--font/devpdf/DESC.in11
-rw-r--r--font/devpdf/Foundry114
-rw-r--r--font/devpdf/Makefile.sub107
-rw-r--r--font/devpdf/util/BuildFoundries.pl439
-rw-r--r--font/devpdf/util/Makefile.sub38
-rw-r--r--man/groff_out.man7
-rw-r--r--src/devices/gropdf/Makefile.sub54
-rw-r--r--src/devices/gropdf/TODO31
-rw-r--r--src/devices/gropdf/gropdf.man1069
-rw-r--r--src/devices/gropdf/gropdf.pl2928
-rw-r--r--src/devices/grops/grops.man4
-rw-r--r--src/preproc/pic/pic.man16
-rw-r--r--src/roff/groff/groff.man17
-rw-r--r--src/utils/afmtodit/afmtodit.man11
-rw-r--r--src/utils/pfbtops/pfbtops.man9
-rw-r--r--test-groff.in3
-rw-r--r--tmac/Makefile.sub1
-rw-r--r--tmac/pdf.tmac738
-rw-r--r--tmac/troffrc3
28 files changed, 5754 insertions, 45 deletions
diff --git a/ChangeLog b/ChangeLog
index ddf3465a..a5bac257 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+2011-06-25 Deri James <deri@chuzzlewit.demon.co.uk>
+
+ Add new output device `gropdf'.
+
+ * font/devpdf/*: New device files for gropdf.
+ * src/devices/gropdf/*: New device.
+
+ * Makefile.comm (install_dev, uninstall_dev): Handle more
+ subdirectories.
+ * Makefile.in (DEVDIRS, OTHERDIRS, EXTRADIRS): Add directories
+ related to gropdf.
+ * MANIFEST: Updated.
+ * test-groff.in (GROFF_BIN_PATH): Updated.
+
+ * tmac/Makefile.sub (NORMALFILES): Updated.
+ * tmac/pdf.tmac: New file.
+ * tmac/troffrc: Updated.
+
+ * doc/groff.texinfo: Document it.
+ * doc/Makefile.in, doc/Makefile.sub (groff_bin_dirs): Udpated.
+ * doc/pic.ms: Updated.
+
+ * man/groff_out.man, src/devices/grops/grops.man,
+ src/preproc/pic/pic.man, src/roff/groff/groff.man,
+ src/utils/afmtodit/afmtodit.man, src/utils/pfbtops/pfbtops.man:
+ Updated.
+
+ * contrib/pdfmark/pdfmark.ms: Updated.
+
2011-07-20 George HELFFRICH <ghfbsd@gmail.com>
Improve line numbering support in tbl and with me macros.
diff --git a/MANIFEST b/MANIFEST
index ef0cc999..961053a9 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,4 +1,4 @@
- Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010
+ Copyright 2001-2006, 2009-2011
Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
@@ -69,6 +69,7 @@ the groff source distribution.
devlbp Device for Canon CAPSL laser printers.
devlj4 Device for HP Laserjet 4, PCL 5, and compatible printers.
devps PostScript device.
+ devpdf PDF device.
devutf8 Text device for Unicode output.
util Utility programs.
@@ -84,6 +85,7 @@ the groff source distribution.
grolbp Canon printers.
grolj4 HP Laserjet 4, PCL 5, and compatible printers.
grops PostScript output.
+ gropdf PDF output.
grotty Text output.
xditview A groff (pre)viewer for the X Window system.
diff --git a/Makefile.comm b/Makefile.comm
index 4587c888..67b164ae 100644
--- a/Makefile.comm
+++ b/Makefile.comm
@@ -1,4 +1,4 @@
-# Copyright (C) 1989-2000, 2002, 2003, 2004, 2006, 2007, 2009, 2010
+# Copyright (C) 1989-2000, 2002-2004, 2006-2011
# Free Software Foundation, Inc.
# Written by James Clark (jjc@jclark.com)
#
@@ -246,6 +246,18 @@ install_dev:
test -d $(DESTDIR)$(fontsubdir)/generate \
|| $(mkinstalldirs) $(DESTDIR)$(fontsubdir)/generate; \
fi
+ -if test -d $(srcdir)/enc; then \
+ test -d $(DESTDIR)$(fontsubdir)/enc \
+ || $(mkinstalldirs) $(DESTDIR)$(fontsubdir)/enc; \
+ fi
+ -if test -d $(srcdir)/map; then \
+ test -d $(DESTDIR)$(fontsubdir)/map \
+ || $(mkinstalldirs) $(DESTDIR)$(fontsubdir)/map; \
+ fi
+ -if test -d $(srcdir)/util; then \
+ test -d $(DESTDIR)$(fontsubdir)/util \
+ || $(mkinstalldirs) $(DESTDIR)$(fontsubdir)/util; \
+ fi
-if test -d $(srcdir)/old; then \
test -d $(DESTDIR)$(oldfontdir) \
|| $(mkinstalldirs) $(DESTDIR)$(oldfontdir); \
@@ -288,6 +300,12 @@ uninstall_dev:
done
-test -d $(DESTDIR)$(fontsubdir)/generate \
&& rmdir $(DESTDIR)$(fontsubdir)/generate
+ -test -d $(DESTDIR)$(fontsubdir)/enc \
+ && rmdir $(DESTDIR)$(fontsubdir)/enc
+ -test -d $(DESTDIR)$(fontsubdir)/map \
+ && rmdir $(DESTDIR)$(fontsubdir)/map
+ -test -d $(DESTDIR)$(fontsubdir)/util \
+ && rmdir $(DESTDIR)$(fontsubdir)/util
-rmdir $(DESTDIR)$(fontsubdir)
-rmdir $(DESTDIR)$(oldfontsubdir)
diff --git a/Makefile.in b/Makefile.in
index c6b89419..ca7d7e78 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -589,6 +589,7 @@ PROGDIRS=\
$(XPROGDIRS)
DEVDIRS=\
font/devps \
+ font/devpdf \
font/devdvi \
font/devhtml
ALLTTYDEVDIRS=\
@@ -613,7 +614,9 @@ OTHERDIRS=\
contrib/mom \
contrib/hdtbl \
contrib/pdfmark \
+ src/devices/gropdf \
contrib/gdiffmk
+
ALLDIRS=\
$(INCDIRS) \
$(LIBDIRS) \
@@ -627,6 +630,7 @@ EXTRADIRS=\
font/devps/generate \
font/devdvi/generate \
font/devlj4/generate \
+ font/devpdf/util \
doc
NOMAKEDIRS=\
m4 \
diff --git a/contrib/pdfmark/pdfmark.ms b/contrib/pdfmark/pdfmark.ms
index 9e5e4e75..31d2f95d 100644
--- a/contrib/pdfmark/pdfmark.ms
+++ b/contrib/pdfmark/pdfmark.ms
@@ -6,7 +6,7 @@ File position: <groff-source>/contrib/pdfmark/pdfmark.ms
This file is part of groff, the GNU roff type-setting system.
-Copyright (C) 2004, 2005, 2006, 2009 Free Software Foundation, Inc.
+Copyright (C) 2004-2006, 2009, 2011 Free Software Foundation, Inc.
written by Keith Marshall <keith.d.marshall@ntlworld.com>
Permission is granted to copy, distribute and/or modify this document
@@ -343,6 +343,10 @@ groff -Tps [-m
.I options \& [-
.I "file ..." \& "...] "
.LP
+(Or use the PDF post-processor to avoid using ghostscript,
+.CW -Tpdf\c
+).
+.LP
It may be noted that the
.CW pdfmark
macros have no dependencies on, and no known conflicts with,
@@ -2490,6 +2494,10 @@ groff -Tps
.I "-options ...\&" ] [
file(s) ...
.LP
+(Or use the PDF post-processor to avoid using ghostscript,
+.CW -Tpdf\c
+).
+.LP
When using the
.CW spdf.tmac
package, the
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 5f39e653..b8a18d78 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -1,4 +1,4 @@
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010
+# Copyright (C) 2002-2007, 2009-2011
# Free Software Foundation, Inc.
# Written by Werner Lemberg <wl@gnu.org>
#
@@ -58,6 +58,7 @@ groff_bin_dirs=\
$(top_builddir)/src/preproc/soelim \
$(top_builddir)/src/preproc/html \
$(top_builddir)/src/devices/grops \
+ $(top_builddir)/src/devices/gropdf \
$(top_builddir)/src/devices/grodvi \
$(top_builddir)/src/devices/grotty \
$(top_builddir)/src/devices/grolj4 \
diff --git a/doc/Makefile.sub b/doc/Makefile.sub
index a688d49d..a35a41c2 100644
--- a/doc/Makefile.sub
+++ b/doc/Makefile.sub
@@ -1,4 +1,4 @@
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009
+# Copyright (C) 2002-2007, 2009, 2011
# Free Software Foundation, Inc.
# Written by Werner Lemberg <wl@gnu.org>
#
@@ -32,6 +32,7 @@ groff_bin_dirs=\
$(top_builddir)/src/preproc/soelim \
$(top_builddir)/src/preproc/html \
$(top_builddir)/src/devices/grops \
+ $(top_builddir)/src/devices/gropdf \
$(top_builddir)/src/devices/grohtml
FFLAG=-F$(top_builddir)/font -F$(top_srcdir)/font
diff --git a/doc/groff.texinfo b/doc/groff.texinfo
index bc2cba72..fceddcc8 100644
--- a/doc/groff.texinfo
+++ b/doc/groff.texinfo
@@ -862,7 +862,7 @@ into a postprocessor to produce output for a particular device.
Currently, @code{groff} has postprocessors for @sc{PostScript} devices,
character terminals, X@tie{}Windows (for previewing), @TeX{} DVI format,
HP LaserJet@tie{}4 and Canon LBP printers (which use @acronym{CAPSL}),
-@acronym{HTML}, and @acronym{XHTML}.
+@acronym{HTML}, @acronym{XHTML}, and PDF.
@c =====================================================================
@@ -945,14 +945,14 @@ built). It can optionally preprocess with any of @code{gpic},
@code{geqn}, @code{gtbl}, @code{ggrn}, @code{grap}, @code{gchem},
@code{grefer}, @code{gsoelim}, or @code{preconv}.
-This section only documents options to the @code{groff} front end.
-Many of the arguments to @code{groff} are passed on to @code{gtroff},
+This section only documents options to the @code{groff} front end. Many
+of the arguments to @code{groff} are passed on to @code{gtroff},
therefore those are also included. Arguments to pre- or postprocessors
can be found in @ref{Invoking gpic}, @ref{Invoking geqn}, @ref{Invoking
-gtbl}, @ref{Invoking ggrn}, @ref{Invoking grefer}, @ref{Invoking
-gchem}, @ref{Invoking gsoelim}, @ref{Invoking preconv}, @ref{Invoking
-grotty}, @ref{Invoking grops}, @ref{Invoking grohtml}, @ref{Invoking
-grodvi}, @ref{Invoking grolj4}, @ref{Invoking grolbp}, and
+gtbl}, @ref{Invoking ggrn}, @ref{Invoking grefer}, @ref{Invoking gchem},
+@ref{Invoking gsoelim}, @ref{Invoking preconv}, @ref{Invoking grotty},
+@ref{Invoking grops}, @ref{Invoking gropdf}, @ref{Invoking grohtml},
+@ref{Invoking grodvi}, @ref{Invoking grolj4}, @ref{Invoking grolbp}, and
@ref{Invoking gxditview}.
The command line format for @code{groff} is:
@@ -1194,6 +1194,9 @@ following are the output devices currently available:
@item ps
For @sc{PostScript} printers and previewers.
+@item pdf
+For PDF viewers or printers.
+
@item dvi
For @TeX{} DVI format.
@@ -1337,7 +1340,7 @@ If this is set to@tie{}@var{X}, then @code{groff} runs
@code{tbl}, @code{pic}, @code{eqn}, @code{grn}, @code{chem},
@code{refer}, and @code{soelim}. It does not apply to @code{grops},
@code{grodvi}, @code{grotty}, @code{pre-grohtml}, @code{post-grohtml},
-@code{preconv}, @code{grolj4}, and @code{gxditview}.
+@code{preconv}, @code{grolj4}, @code{gropdf}, and @code{gxditview}.
The default command prefix is determined during the installation
process. If a non-GNU troff system is found, prefix @samp{g} is used,
@@ -2534,8 +2537,8 @@ release number (such as ``System V Release 3'').
@Defmac {UC, [@Var{version}], man}
@cindex @code{man}macros, BSD compatibility
-Alters the footer for use with @acronym{BSD} manpages. This command
-exists only for compatibility; don't use it. The argument can be:
+Alters the footer for use with BSD manpages. This command exists only
+for compatibility; don't use it. The argument can be:
@table @code
@item 3
@@ -3876,9 +3879,8 @@ equation.
@DefmacList {[, , ms}
@DefmacListEnd {], , ms}
Denotes a reference, to be processed by the @code{refer} preprocessor.
-The @acronym{GNU} @cite{refer(1)} man page provides a comprehensive
-reference to the preprocessor and the format of the bibliographic
-database.
+The GNU @cite{refer(1)} man page provides a comprehensive reference to
+the preprocessor and the format of the bibliographic database.
@endDefmac
@menu
@@ -8892,10 +8894,10 @@ the current family.
@cindex PostScript fonts
@cindex fonts, PostScript
-Currently, fonts for the devices @option{-Tps}, @option{-Tdvi},
-@option{-Tlj4}, @option{-Tlbp}, and the X11 fonts are set up to this
-mechanism. By default, @code{gtroff} uses the Times family with the
-four styles @samp{R}, @samp{I}, @samp{B}, and @samp{BI}.
+Currently, fonts for the devices @option{-Tps}, @option{-Tpdf},
+@option{-Tdvi}, @option{-Tlj4}, @option{-Tlbp}, and the X11 fonts are
+set up to this mechanism. By default, @code{gtroff} uses the Times
+family with the four styles @samp{R}, @samp{I}, @samp{B}, and @samp{BI}.
This way, it is possible to use the basic four fonts and to select a
different font family on the command line (@pxref{Groff Options}).
@@ -9804,7 +9806,8 @@ Default scaling indicator is @samp{z}.
The read-only number register @code{.height} contains the font height as
set by @code{\H}.
-Currently, only the @option{-Tps} device supports this feature.
+Currently, only the @option{-Tps} and @option{-Tpdf} devices support
+this feature.
Note that @code{\H} doesn't produce an input token in @code{gtroff}. As
a consequence, it can be used in requests like @code{mc} (which expects
@@ -9840,7 +9843,8 @@ the right. Only integer values are possible.
The read-only number register @code{.slant} contains the font slant as
set by @code{\S}.
-Currently, only the @option{-Tps} device supports this feature.
+Currently, only the @option{-Tps} and @option{-Tpdf} devices support
+this feature.
Note that @code{\S} doesn't produce an input token in @code{gtroff}. As
a consequence, it can be used in requests like @code{mc} (which expects
@@ -14913,6 +14917,7 @@ is available as an extra package from the following address:
* Special Characters::
* grotty::
* grops::
+* gropdf::
* grodvi::
* grolj4::
* grolbp::
@@ -15023,7 +15028,7 @@ ISO@tie{}6429 SGR sequences to control terminals.
@c =====================================================================
-@node grops, grodvi, grotty, Output Devices
+@node grops, gropdf, grotty, Output Devices
@section @code{grops}
@cindex @code{grops}, the program
@@ -15123,7 +15128,82 @@ This escape sequence is used internally by the macro @code{PSPIC}
@c =====================================================================
-@node grodvi, grolj4, grops, Output Devices
+@node gropdf, grodvi, grops, Output Devices
+@section @code{gropdf}
+@cindex @code{gropdf}, the program
+
+The postprocessor @command{gropdf} translates the output from GNU
+@command{troff} into a form suitable for Adobe PDF devices. It is fully
+documented on its manual page, @cite{gropdf(1)}.
+
+@menu
+* Invoking gropdf::
+* Embedding PDF::
+@end menu
+
+@c ---------------------------------------------------------------------
+
+@node Invoking gropdf, Embedding PDF, gropdf, gropdf
+@subsection Invoking @code{gropdf}
+@cindex invoking @code{gropdf}
+@cindex @code{gropdf}, invoking
+
+The postprocessor @code{gropdf} accepts the following command-line
+options:
+
+@table @option
+@item -d
+Produce uncompressed PDFs which include debugging comments.
+
+@item -e
+This forces @code{gropdf} to embed all used fonts in the PDF,
+even if they are one of the 14 base Adobe fonts.
+
+@item -F@var{dir}
+Put the directory @file{@var{dir}/dev@var{name}} in front of the
+search path for the font, prologue and device description files,
+given the target device @var{name}, usually @strong{pdf}.
+
+@item -fy@var{foundry}
+This forces the use of a different font foundry.
+
+@item -l
+Use landscape orientation.
+
+@item -p@var{papersize}
+Set the page dimensions. Overrides the commands @option{papersize},
+@option{paperlength}, and @option{paperwidth} in the @file{DESC}
+file. See the @cite{groff_font(5)} manual page for details.
+
+@item -v
+Print the version number.
+@end table
+
+@c ---------------------------------------------------------------------
+
+@node Embedding PDF, , Invoking gropdf, gropdf
+@subsection Embedding PDF
+@cindex embedding PDF
+@cindex PDF, embedding
+
+The escape sequence
+
+@code{\X'pdf: pdfpic @var{file} @var{alignment} @var{width} [@var{height}]
+ [@var{linelength}]'}
+
+@noindent
+places a rectangle of the specified @var{width} containing the PDF
+drawing from file @var{file} of desired @var{width} and @var{height} (if
+@var{height} is missing or zero then it is scaled proportionally). If
+@var{alignment} is @code{-L} the drawing is left aligned. If it is
+@code{-C} or @code{-R} a @var{linelength} greater than the width of the
+drawing is required as well. If @var{width} is specified as zero then
+the width is scaled in proportion to the height.
+
+
+@c =====================================================================
+
+@node grodvi, grolj4, gropdf, Output Devices
@section @code{grodvi}
@cindex @code{grodvi}, the program
diff --git a/doc/pic.ms b/doc/pic.ms
index 93443b77..d1bd42cb 100644
--- a/doc/pic.ms
+++ b/doc/pic.ms
@@ -30,7 +30,7 @@
.\" Eric S. Raymond <esr@thyrsus.com> in August 1995. It has been put
.\" under the GPL in March 2006.
.\"
-.\" $Id: pic.ms,v 1.48 2011/01/27 04:15:42 wl Exp $
+.\" $Id: pic.ms,v 1.49 2011/07/26 17:11:10 wl Exp $
.
.
.\" Set a proper TeX and LaTeX
@@ -862,7 +862,7 @@ In GNU \fBgpic\fR, objects can have an
.B aligned
attribute.
This only works if the postprocessor is
-\fBgrops\fP.
+\fBgrops\fP or \fBgropdf\fP.
Any text associated with an object having the
.B aligned
attribute is rotated about the center of the object
diff --git a/font/devpdf/DESC.in b/font/devpdf/DESC.in
new file mode 100644
index 00000000..5cb254f3
--- /dev/null
+++ b/font/devpdf/DESC.in
@@ -0,0 +1,11 @@
+res 72000
+hor 1
+vert 1
+sizescale 1000
+unitwidth 1000
+sizes 1000-10000000 0
+styles R I B BI
+family T
+fonts 8 0 0 0 0 0 0 S ZD
+tcommand
+postpro gropdf
diff --git a/font/devpdf/Foundry b/font/devpdf/Foundry
new file mode 100644
index 00000000..52d728cf
--- /dev/null
+++ b/font/devpdf/Foundry
@@ -0,0 +1,114 @@
+# Foundry file
+
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# Written by Deri James <deri@chuzzlewit.demon.co.uk>
+#
+# This file is part of groff.
+#
+# groff 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.
+#
+# groff 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/>.
+
+#=====================================================================
+#Foundry|Name|Searchpath
+foundry||(gs)
+
+# These are just copies of the grops fonts so MUST not have any flags etc set
+
+#Font|IsBase14|Flags|Map|Encoding|File
+AB|N||||URWGothicL-Demi
+ABI|N||||URWGothicL-DemiObli
+AI|N||||URWGothicL-BookObli
+AR|N||||URWGothicL-Book
+BMB|N||||URWBookmanL-DemiBold
+BMBI|N||||URWBookmanL-DemiBoldItal
+BMI|N||||URWBookmanL-LighItal
+BMR|N||||URWBookmanL-Ligh
+CB|Y||||NimbusMonL-Bold
+CBI|Y||||NimbusMonL-BoldObli
+CI|Y||||NimbusMonL-ReguObli
+CR|Y||||NimbusMonL-Regu
+EURO|N||||../devps/freeeuro.pfa
+HB|Y||||NimbusSanL-Bold
+HBI|Y||||NimbusSanL-BoldItal
+HI|Y||||NimbusSanL-ReguItal
+HNB|N||||NimbusSanL-BoldCond
+HNBI|N||||NimbusSanL-BoldCondItal
+HNI|N||||NimbusSanL-ReguCondItal
+HNR|N||||NimbusSanL-ReguCond
+HR|Y||||NimbusSanL-Regu
+NB|N||||CenturySchL-Bold
+NBI|N||||CenturySchL-BoldItal
+NI|N||||CenturySchL-Ital
+NR|N||||CenturySchL-Roma
+PB|N||||URWPalladioL-Bold
+PBI|N||||URWPalladioL-BoldItal
+PI|N||||URWPalladioL-Ital
+PR|N||||URWPalladioL-Roma
+S|Y||||StandardSymL
+TB|Y||||NimbusRomNo9L-Medi
+TBI|Y||||NimbusRomNo9L-MediItal
+TI|Y||||NimbusRomNo9L-ReguItal
+TR|Y||||NimbusRomNo9L-Regu
+ZCMI|N||||URWChanceryL-MediItal
+ZD|Y||||Dingbats
+
+#======================================================================
+
+#Foundry|Name|Searchpath
+foundry|U|(gs) # the URW fonts delivered with ghostscript (may be different)
+
+#Define Flags for afmtodit
+
+r=-i 0 -m
+i=-i 50
+n=-n
+s=-s
+
+#Font|IsBase14|Flags|Map|Encoding|File
+AB|N|r|textmap|text.enc|a010015l.pfb
+ABI|N|i|textmap|text.enc|a010035l.pfb
+AI|N|i|textmap|text.enc|a010033l.pfb
+AR|N|r|textmap|text.enc|a010013l.pfb
+BMB|N|r|textmap|text.enc|b018015l.pfb
+BMBI|N|i|textmap|text.enc|b018035l.pfb
+BMI|N|i|textmap|text.enc|b018032l.pfb
+BMR|N|r|textmap|text.enc|b018012l.pfb
+CB|N|nr|textmap|text.enc|n022004l.pfb
+CBI|N|ni|textmap|text.enc|n022024l.pfb
+CI|N|ni|textmap|text.enc|n022023l.pfb
+CR|N|nr|textmap|text.enc|n022003l.pfb
+HB|N|r|textmap|text.enc|n019004l.pfb
+HBI|N|i|textmap|text.enc|n019024l.pfb
+HI|N|i|textmap|text.enc|n019023l.pfb
+HNB|N|r|textmap|text.enc|n019044l.pfb
+HNBI|N|i|textmap|text.enc|n019064l.pfb
+HNI|N|i|textmap|text.enc|n019063l.pfb
+HNR|N|r|textmap|text.enc|n019043l.pfb
+HR|N|r|textmap|text.enc|n019003l.pfb
+NB|N|r|textmap|text.enc|c059016l.pfb
+NBI|N|i|textmap|text.enc|c059036l.pfb
+NI|N|i|textmap|text.enc|c059033l.pfb
+NR|N|r|textmap|text.enc|c059013l.pfb
+PB|N|r|textmap|text.enc|p052004l.pfb
+PBI|N|i|textmap|text.enc|p052024l.pfb
+PI|N|i|textmap|text.enc|p052023l.pfb
+PR|N|r|textmap|text.enc|p052003l.pfb
+S|N|sr|symbolmap||s050000l.pfb
+TB|N|r|textmap|text.enc|n021004l.pfb
+TBI|N|i|textmap|text.enc|n021024l.pfb
+TI|N|i|textmap|text.enc|n021023l.pfb
+TR|N|r|textmap|text.enc|n021003l.pfb
+ZCMI|N|i|textmap|text.enc|z003034l.pfb
+ZD|N|sr|dingbats.map||d050000l.pfb
+
+#======================================================================
diff --git a/font/devpdf/Makefile.sub b/font/devpdf/Makefile.sub
new file mode 100644
index 00000000..e61c6503
--- /dev/null
+++ b/font/devpdf/Makefile.sub
@@ -0,0 +1,107 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# Written by Deri James <deri@chuzzlewit.demon.co.uk>
+#
+# This file is part of groff.
+#
+# groff 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.
+#
+# groff 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/>.
+
+DEV=pdf
+GROFF_FONT_FILES=`cd $(DESTDIR)$(fontsubdir); \
+ grep -l internalname * | grep -v Makefile.sub`
+ENC_FILES=`cd $(DESTDIR)$(fontsubdir); ls enc`
+MAP_FILES=`cd $(DESTDIR)$(fontsubdir); ls map`
+
+UTILFILES=\
+ BuildFoundries
+
+MOSTLYCLEANADD=\
+ download \
+ DESC \
+ util/BuildFoundries \
+ $(GROFF_FONT_FILES) \
+ $(ENC_FILES) \
+ $(MAP_FILES)
+#MOSTLYCLEANDIRADD=enc map
+
+DEVFILES=\
+ Foundry
+#DEVSCRIPTS=util/BuildFoundries
+
+DISTFILES=\
+ $(DEVFILES) \
+ $(DEVSCRIPTS)
+
+
+# Some `makes' don't predefine RM...
+RM=rm -f
+
+
+all: DESC BuildFoundries
+
+DESC: DESC.in
+ -rm -f DESC
+ cat $(srcdir)/DESC.in >DESC
+ if test "$(PAGE)" = A4; then \
+ echo "papersize a4" >>DESC; \
+ else \
+ echo "papersize letter" >>DESC; \
+ fi
+
+BuildFoundries: util/BuildFoundries.pl $(SH_DEPS_SED_SCRIPT)
+ -test -d util || $(mkinstalldirs) util
+ $(RM) util/$@
+ sed -f $(SH_DEPS_SED_SCRIPT) \
+ -e "s|@VERSION@|$(version)$(revision)|" \
+ -e "s|@PERLPATH@|$(PERLPATH)|" \
+ -e "s|@GROFF_FONT_DIR@|$(fontdir)|" $(srcdir)/util/BuildFoundries.pl > util/$@
+ chmod +x util/$@
+
+install_data:
+ -test -d $(DESTDIR)$(fontsubdir) \
+ || $(mkinstalldirs) $(DESTDIR)$(fontsubdir)
+ -test -d $(DESTDIR)$(fontsubdir)/enc \
+ || $(mkinstalldirs) $(DESTDIR)$(fontsubdir)/enc
+ -test -d $(DESTDIR)$(fontsubdir)/map \
+ || $(mkinstalldirs) $(DESTDIR)$(fontsubdir)/map
+ for f in $(MOSTLYCLEANADD); do \
+ $(RM) $(DESTDIR)$(fontsubdir)/$$f; \
+ if test -f $$f; then \
+ $(INSTALL_DATA) $$f $(DESTDIR)$(fontsubdir)/$$f; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(fontsubdir)/$$f; \
+ fi; \
+ done
+ $(RM) $@
+ cp -f $(DESTDIR)$(fontdir)/devps/text.enc \
+ $(DESTDIR)$(fontsubdir)/enc
+ cp -f $(srcdir)/../devps/symbolmap \
+ $(DESTDIR)$(fontsubdir)/map
+ cp -f $(DESTDIR)$(fontdir)/devps/generate/dingbats.map \
+ $(DESTDIR)$(fontsubdir)/map
+ cp -f $(DESTDIR)$(fontdir)/devps/generate/textmap \
+ $(DESTDIR)$(fontsubdir)/map
+ cp -f $(DESTDIR)$(fontdir)/devps/generate/symbolchars \
+ $(DESTDIR)$(fontsubdir)/map
+ echo "# foundry ps name psfile" > $(DESTDIR)$(fontsubdir)/download
+ util/BuildFoundries $(DESTDIR)$(fontsubdir) \
+ > $(DESTDIR)$(fontsubdir)/download
+
+uninstall_sub:
+ for f in $(MOSTLYCLEANADD); do \
+ $(RM) $(DESTDIR)$(fontsubdir)/$$f; \
+ done
+ rmdir $(DESTDIR)$(fontsubdir)/enc
+ rmdir $(DESTDIR)$(fontsubdir)/map
+ rmdir $(DESTDIR)$(fontsubdir)/util
+ rmdir $(DESTDIR)$(fontsubdir)
diff --git a/font/devpdf/util/BuildFoundries.pl b/font/devpdf/util/BuildFoundries.pl
new file mode 100644
index 00000000..80756147
--- /dev/null
+++ b/font/devpdf/util/BuildFoundries.pl
@@ -0,0 +1,439 @@
+#!@PERLPATH@ -w
+#
+# BuildFoundries: Given a Foundry file generate groff and download files
+# Deri James: Monday 07 Feb 2011
+
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# Written by Deri James <deri@chuzzlewit.demon.co.uk>
+#
+# This file is part of groff.
+#
+# groff 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.
+#
+# groff 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/>.
+
+use strict;
+
+my $where=shift||'';
+chdir $where if $where ne '';
+my (%foundry,%flg,@downloadpreamble,%download);
+my $GSpath=FindGSpath();
+my $warn=0;
+my $lct=0;
+my $foundry=''; # the default foundry
+
+LoadDownload("download");
+LoadFoundry("Foundry");
+WriteDownload("download");
+
+exit $warn;
+
+
+sub LoadFoundry
+{
+ my $fn=shift;
+ my $foundrypath='';
+
+ open(F,"<$fn") or die "No $fn file found";
+
+ while (<F>)
+ {
+ chomp;
+ $lct++;
+ s/\r$//; # in case edited in windows
+
+ s/\s*#.*?$//; # remove comments
+
+ next if $_ eq '';
+
+ if (m/^[A-Za-z]=/)
+ {
+ my (@f)=split('=');
+ $flg{$f[0]}=$f[1];
+ next;
+ }
+
+ my (@r)=split('\|');
+
+ if (lc($r[0]) eq 'foundry')
+ {
+ $foundry=uc($r[1]);
+ $foundrypath=$r[2];
+ }
+ else
+ {
+ # 0=groff font name
+ # 1=IsBase Y/N (one of PDFs 14 base fonts)
+ # 2=afmtodit flag
+ # 3=map file
+ # 4=encoding file
+ # 5=font file
+ # 6=afm file
+
+ if (!defined($r[6]) or $r[6] eq '')
+ {
+ # if no afm file, have a guess!
+ $r[6]=substr($r[5],0,-3)."afm";
+ }
+
+ my $gfont=($foundry eq '')?$r[0]:"$foundry-$r[0]";
+
+ if ($r[2] eq '')
+ {
+ # Don't run afmtodit, just copy the grops font file
+
+ my $gotf=1;
+
+ if (-r "../devps/$r[0]")
+ {
+ my $psfont=UseGropsVersion($r[0]);
+ if (!PutDownload($psfont,LocatePF($foundrypath,$r[5]),uc($r[1])))
+ {
+ if (uc($r[1]) ne 'Y')
+ {
+ $gotf=0;
+ Msg(0,"Unable to locate font '$r[5]' on the given path(s)");
+ unlink $gfont; # Unable to find the postscript file for the font just created by afmtodit
+ }
+ }
+ print STDERR "Copied grops font $gfont...\n" if $gotf;
+ }
+ else
+ {
+ Msg(0,"Can't read grops font '$r[0]' for Foundry '$foundry'");
+ }
+ }
+ else
+ {
+ # We need to run afmtodit to create this groff font
+ my $psfont=RunAfmtodit($gfont,LocateAF($foundrypath,$r[6]),$r[2],$r[3],$r[4]);
+
+ if ($psfont)
+ {
+ if (!PutDownload($psfont,LocatePF($foundrypath,$r[5]),uc($r[1])))
+ {
+ unlink $gfont; # Unable to find the postscript file for the font just created by afmtodit
+ }
+ else
+ {
+ print STDERR "Generated $gfont...\n";
+ }
+ }
+ else
+ {
+ Msg(0,"Failed to create groff font '$gfont' by running afmtodit");
+ }
+ }
+ }
+ }
+
+ close();
+}
+
+sub RunAfmtodit
+{
+ my $gfont=shift;
+ my $afmfile=shift;
+ my $flags=shift;
+ my $map=shift||'';
+ my $enc=shift||'';
+ my $psfont='';
+
+ $enc="-e 'enc/$enc'" if $enc;
+ $map="'map/$map'" if $map;
+
+ my $cmd='afmtodit -c -dDESC';
+
+ foreach my $f (split('',$flags))
+ {
+ if (!exists($flg{$f}))
+ {
+ Msg(0,"Can't use undefined flag '$f' in calling afmtodit for groff font '$gfont'");
+ return('');
+ }
+
+ $cmd.=" $flg{$f}";
+ }
+
+ system("$cmd $enc '$afmfile' $map $gfont 2>/dev/null");
+
+ if ($?)
+ {
+ unlink $gfont;
+ return('');
+ }
+
+ if (open(GF,"<$gfont"))
+ {
+ my (@gf)=(<GF>);
+ my @ps=grep(/^internalname /,@gf);
+ if ($#ps == 0) # Just 1 match
+ {
+ (undef,$psfont)=split(' ',$ps[0],2);
+ chomp($psfont);
+ }
+ else
+ {
+ Msg(0,"Unexpected format for grops font '$gfont' for Foundry '$foundry' - ignoring");
+ }
+
+ close(GF);
+ }
+
+ return($psfont);
+}
+
+sub LocateAF
+{
+ my $path=shift;
+ my $file=shift;
+
+ return(LocateFile($path,$file,1));
+}
+
+sub LocatePF
+{
+ my $path=shift;
+ my $file=shift;
+
+ return(LocateFile($path,$file,0));
+}
+
+sub LocateFile
+{
+ my $path=shift;
+ my $file=shift;
+ my $tryafm=shift;
+
+ if ($file=~m'/')
+ {
+ # path given with file name so no need to search the paths
+
+ if (-r $file)
+ {
+ return($file);
+ }
+
+ if ($tryafm and $file=~s'type1/'afm/'i)
+ {
+ if (-r "$file")
+ {
+ return($file);
+ }
+ }
+
+ return('');
+ }
+
+ if ($path eq '(gs)')
+ {
+ $path=$GSpath;
+ }
+ elsif ($path eq '(tex)')
+ {
+ my $res=`kpsewhich $file`;
+ return '' if $?;
+ chomp($res);
+ return($res);
+ }
+
+ my (@paths)=split(':',$path);
+
+ foreach my $p (@paths)
+ {
+ $p=~s/^\s+//;
+ $p=~s/\s+$//;
+
+ next if $p=~m/^\%rom\%/; # exclude %rom% paths (from (gs))
+
+ if (-r "$p/$file")
+ {
+ return("$p/$file");
+ }
+
+ if ($tryafm and $p=~s'type1/'afm/'i)
+ {
+ if (-r "$p/$file")
+ {
+ return("$p/$file");
+ }
+ }
+ }
+
+ return('');
+}
+
+sub FindGSpath
+{
+ my (@res)=`gs -h 2>/dev/null`;
+ return '' if $?;
+ my $buildpath='';
+ my $stg=1;
+
+ foreach my $l (@res)
+ {
+ chomp($l);
+
+ if ($stg==1 and $l=~m/^Search path:/)
+ {
+ $stg=2;
+ }
+ elsif ($stg == 2)
+ {
+ if (substr($l,0,1) ne ' ')
+ {
+ $stg=3;
+ }
+ else
+ {
+ $l=~s/^\s+//;
+ $buildpath.=$l;
+ }
+ }
+ }
+
+ return($buildpath);
+}
+
+sub UseGropsVersion
+{
+ my $gfont=shift;
+ my $psfont='';
+
+ if (open(GF,"<../devps/$gfont"))
+ {
+ my (@gf)=(<GF>);
+ my @ps=grep(/^internalname /,@gf);
+ if ($#ps == 0) # Just 1 match
+ {
+ (undef,$psfont)=split(' ',$ps[0],2);
+ chomp($psfont);
+ }
+ else
+ {
+ Msg(0,"Unexpected format for grops font '$gfont' for Foundry '$foundry' - ignoring");
+ }
+
+ close(GF);
+
+ if ($psfont)
+ {
+ if (open(GF,">$gfont"))
+ {
+ local $"='';
+ print GF "@gf";
+ close(GF);
+ }
+ else
+ {
+ $psfont='';
+ Msg(0,"Failed to create new font '$gfont' for Foundry '$foundry'");
+ }
+ }
+ else
+ {
+ Msg(0,"Failed to locate postscript internalname in grops font '$gfont' for Foundry '$foundry'");
+ }
+
+ close(GF);
+ }
+ else
+ {
+ Msg(0,"Failed to open grops font '$gfont' for Foundry '$foundry'");
+ }
+
+ return($psfont);
+}
+
+sub PutDownload
+{
+ my $psfont=shift;
+ my $pffile=shift;
+ my $IsBase14=shift;
+ my $key="$foundry $psfont";
+
+ delete($download{$key}), return 0 if ($pffile eq '');
+
+ $pffile='*'.$pffile if $IsBase14 eq 'Y'; # This signals to gropdf to only edmbed if -e given
+ $download{$key}=$pffile;
+
+ return 1;
+}
+
+sub LoadDownload
+{
+ my $fn=shift;
+ my $top=1;
+
+ return if !open(F,"<$fn");
+
+ while (<F>)
+ {
+ chomp;
+ s/\r$//; # in case edited in windows
+
+ if ($top and substr($_,0,1) eq '#' or $_ eq '')
+ {
+ # Preserve comments at top of download file
+
+ push(@downloadpreamble,$_);
+ next;
+ }
+
+ $top=0;
+ s/\s*#.*?$//; # remove comments
+
+ next if $_ eq '';
+
+ my (@r)=split(/\t+/);
+ my $key=$r[1];
+ $key="$r[0] $r[1]";
+ $download{$key}=$r[2];
+ }
+
+ close(F);
+}
+
+sub WriteDownload
+{
+ my $fn=shift;
+ my $top=1;
+
+ open(F,">$fn") or die "Can't Create new file '$fn'";
+
+ print F join("\n",@downloadpreamble),"\n";
+
+ foreach my $k (sort keys %download)
+ {
+ my ($f,$ps)=split(/ /,$k);
+ print F "$f\t$ps\t$download{$k}\n";
+ }
+
+ close(F);
+}
+
+sub Msg
+{
+ my $sev=shift;
+ my $msg=shift;
+
+ if ($sev)
+ {
+ print STDERR "Error: line $lct: $msg\n";
+ exit 2;
+ }
+ else
+ {
+ print STDERR "Warning: line $lct: $msg\n";
+ $warn=1;
+ }
+}
diff --git a/font/devpdf/util/Makefile.sub b/font/devpdf/util/Makefile.sub
new file mode 100644
index 00000000..360f9803
--- /dev/null
+++ b/font/devpdf/util/Makefile.sub
@@ -0,0 +1,38 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# Written by Deri James <deri@chuzzlewit.demon.co.uk>
+#
+# This file is part of groff.
+#
+# groff 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.
+#
+# groff 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/>.
+
+DEV=pdf
+UTILFILES=\
+ BuildFoundries
+
+#MOSTLYCLEANADD=BuildFoundries
+
+# Some `makes' don't predefine RM...
+RM=rm -f
+
+
+all: BuildFoundries
+
+
+BuildFoundries: BuildFoundries.pl $(SH_DEPS_SED_SCRIPT)
+ $(RM) $@
+ sed -f $(SH_DEPS_SED_SCRIPT) \
+ -e "s|@VERSION@|$(version)$(revision)|" \
+ -e "s|@PERLPATH@|$(PERLPATH)|" \
+ -e "s|@GROFF_FONT_DIR@|$(fontdir)|" $(srcdir)/BuildFoundries.pl >$@
+ chmod +x $@
diff --git a/man/groff_out.man b/man/groff_out.man
index 7c2db26c..e54828a8 100644
--- a/man/groff_out.man
+++ b/man/groff_out.man
@@ -5,8 +5,7 @@ groff_out.5
This file is part of groff, the GNU roff type-setting system.
-Copyright (C) 1989, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009
+Copyright (C) 1989, 2001-2009, 2011
Free Software Foundation, Inc.
rewritten from scrach 2001 by Bernd Warken <bwarken@mayn.de>
@@ -1560,7 +1559,9 @@ x stop
.P
This output can be fed into the postprocessor
.BR grops (@MAN1EXT@)
-to get its representation as a PostScript file.
+to get its representation as a PostScript file, or
+.BR gropdf (@MAN1EXT@)
+to output directly to PDF.
.
.
.IP \[bu] 2m
diff --git a/src/devices/gropdf/Makefile.sub b/src/devices/gropdf/Makefile.sub
new file mode 100644
index 00000000..c876c925
--- /dev/null
+++ b/src/devices/gropdf/Makefile.sub
@@ -0,0 +1,54 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# Written by Deri James <deri@chuzzlewit.demon.co.uk>
+#
+# This file is part of groff.
+#
+# groff 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.
+#
+# groff 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/>.
+
+MAN1=\
+ gropdf.n
+
+CMDFILES=\
+ gropdf
+
+MOSTLYCLEANADD=\
+ gropdf \
+ $(MAN1)
+
+# Some `makes' don't predefine RM...
+RM=rm -f
+
+
+all: gropdf
+
+
+gropdf: gropdf.pl $(SH_DEPS_SED_SCRIPT)
+ $(RM) $@
+ sed -f $(SH_DEPS_SED_SCRIPT) \
+ -e "s|@VERSION@|$(version)$(revision)|" \
+ -e "s|@PERLPATH@|$(PERLPATH)|" \
+ -e "s|@GROFF_FONT_DIR@|$(fontdir)|" $(srcdir)/gropdf.pl >$@
+ chmod +x $@
+
+install_data:
+ -test -d $(DESTDIR)$(bindir) || $(mkinstalldirs) $(DESTDIR)$(bindir)
+ for f in $(CMDFILES); do \
+ $(RM) $(DESTDIR)$(bindir)/$$f; \
+ $(INSTALL_SCRIPT) $$f $(DESTDIR)$(bindir)/$$f; \
+ done
+
+uninstall_sub:
+ for f in $(CMDFILES); do \
+ $(RM) $(DESTDIR)$(bindir)/$$f; \
+ done
diff --git a/src/devices/gropdf/TODO b/src/devices/gropdf/TODO
new file mode 100644
index 00000000..4d6fca18
--- /dev/null
+++ b/src/devices/gropdf/TODO
@@ -0,0 +1,31 @@
+pspic.tmac
+----------
+
+Equiv for gropdf is pdfpic (which is dependant on a program
+pdfbb (to extract MediaBox (etc.) from the pdf) which is not written yet!
+Meanwhile you could use \X'pdf: pdfpic filename -L|R|C wid (hgt) (linelen)'
+(-R and -C require a linelen) Wid or hgt may be zero (in which case the same
+scaling as the other axis is used). The disadvantage of this call (over
+pdfpic macro) is that it is transparent to groff, after placing the image
+the current X/Y position remains what it was, so you need to do your own
+'motion control' (like a .sp) to "step over" the image you just placed.
+
+psfig.tmac
+----------
+
+No equiv for gropdf.
+
+psatk.tmac
+----------
+
+No equiv for gropdf.
+
+-I : search -I directory for included files
+
+-w : set line width
+
+Another \X : \X'ps: exec 0 setlinejoin'\X'ps: exec 0 setlinecap' for mom
+
+Cater for fonts with >255 glyphs (currently accessing a glyph above 255
+(i.e. \N[260]) causes a fail). This will be fixed when font subsetting is
+implemented.
diff --git a/src/devices/gropdf/gropdf.man b/src/devices/gropdf/gropdf.man
new file mode 100644
index 00000000..bdfe5727
--- /dev/null
+++ b/src/devices/gropdf/gropdf.man
@@ -0,0 +1,1069 @@
+.ig
+Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.
+.
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+. ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+. el .TP "\\$1"
+..
+.de TQ
+. br
+. ns
+. TP \$1
+..
+.
+.de FT
+. if '\\*(.T'ps' .ft \\$1
+. if '\\*(.T'pdf' .ft \\$1
+..
+.
+.
+.TH GROPDF @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.
+.
+.SH NAME
+.
+gropdf \- PDF driver for groff
+.
+.
+.SH SYNOPSIS
+.
+.SY gropdf
+.OP \-delv
+.OP \-F dir
+\#.OP \-I dir
+.OP \-p papersize
+\#.OP \-w n
+.OP \-fy foundry
+.RI [ files
+.IR .\|.\|. ]
+.YS
+.
+.LP
+It is possible to have whitespace between a command line option and its
+parameter.
+.
+.
+.SH DESCRIPTION
+.
+.B gropdf
+translates the output of GNU
+.B troff
+to PDF.
+.
+Normally
+.B gropdf
+should be invoked by using the groff command
+with a
+.B \-Tpdf
+option.
+.
+If no files are given,
+.B gropdf
+reads the standard input.
+.
+A filename of
+.B \-
+also causes
+.B grops
+to read the standard input.
+.
+PDF output is written to the standard output.
+.
+When
+.B gropdf
+is run by
+.B groff
+options can be passed to
+.B gropdf
+using
+.BR groff 's
+.B \-P
+option.
+.
+.LP
+See section
+.B FONT INSTALLATION
+below for a guide how to install fonts for
+.BR gropdf .
+.
+.
+.SH OPTIONS
+.
+.TP
+.B \-d
+Include debug information as comments within the PDF.
+Also produces an uncompressed PDF.
+.
+.TP
+.B \-e
+Force all fonts to be embedded in the PDF.
+.
+.TP
+.BI \-F dir
+Prepend directory
+.IB dir /dev name
+to the search path for font, and device description files;
+.I name
+is the name of the device, usually
+.BR pdf .
+.
+.\" .TP
+.\" .BI \-I dir
+.\" This option may be used to add a directory to the search path for
+.\" files on the command line and files named in
+.\" .B \[rs]X'ps: import'
+.\" and
+.\" .B \[rs]X'ps: file'
+.\" escapes.
+.\" .
+.\" The search path is initialized with the current directory.
+.\" .
+.\" This option may be specified more than once; the directories are then
+.\" searched in the order specified (but before the current directory).
+.\" .
+.\" If you want to make the current directory be read before other directories,
+.\" add
+.\" .B \-I.\&
+.\" at the appropriate place.
+.\" .
+.\" .IP
+.\" No directory search is performed for files with an absolute file name.
+.\" .
+.\" .TP
+.
+.TP
+.B \-l
+Print the document in landscape format.
+.
+.TP
+.BI \-p paper-size
+Set physical dimension of output medium.
+.
+This overrides the
+.BR papersize ,
+.BR paperlength ,
+and
+.B paperwidth
+commands in the
+.B DESC
+file; it accepts the same arguments as the
+.B papersize
+command.
+.
+See
+.B groff_font (@MAN5EXT@)
+for details.
+.
+.\" .TP
+.\" .BI \-w n
+.\" Lines should be drawn using a thickness of
+.\" .IR n \~\c
+.\" thousandths of an em.
+.\" .
+.\" If this option is not given, the line thickness defaults to 0.04\~em.
+.\" .
+.
+.TP
+.B \-v
+Print the version number.
+.
+.TP
+.BI \-fy foundry
+Set the foundry to use for selecting fonts of the same name.
+.
+.TP
+.B \-e
+Forces gropdf to embed ALL fonts (even the 14 base PDF fonts).
+.
+.
+.SH USAGE
+.
+The input to
+.B gropdf
+must be in the format output by
+.BR @g@troff (@MAN1EXT@).
+.
+This is described in
+.BR groff_out (@MAN5EXT@).
+.
+.LP
+In addition, the device and font description files for the device used
+must meet certain requirements:
+.
+The resolution must be an integer multiple of\~72 times the
+.BR sizescale .
+.
+The
+.B pdf
+device uses a resolution of 72000 and a sizescale of 1000.
+.
+.LP
+The device description file must contain a valid paper size; see
+.BR groff_font (@MAN5EXT@)
+for more information.
+.
+.B gropdf
+uses the same Type\~1 Adobe postscript fonts as the
+.B grops
+device driver.
+.
+Although the PDF Standard allows the use of other font types (like TrueType)
+this implementation only accepts the Type\~1 postscript font.
+.
+Fewer Type\~1 fonts are supported natively in PDF documents than
+the standard 35 fonts supported by
+.B grops
+and all postscript printers, but all the fonts are available since any
+which aren't supported natively are automatically embedded in the PDF.
+.
+.LP
+.B gropdf
+supports the concept of foundries, that is different versions of basically
+the same font.
+.
+During install a
+.B Foundry
+file controls where fonts are found and builds
+.B groff
+fonts from the files it discovers on your system.
+.
+.LP
+Each font description file must contain a command
+.
+.IP
+.BI internalname\ psname
+.
+.LP
+which says that the PostScript name of the font is
+.IR psname .
+.
+Lines starting with
+.B #
+and blank lines are ignored.
+.
+The code for each character given in the font file must correspond
+to the code in the default encoding for the font.
+.
+This code can be used with the
+.B \[rs]N
+escape sequence in
+.B troff
+to select the character,
+even if the character does not have a groff name.
+.
+Every character in the font file must exist in the PostScript font, and
+the widths given in the font file must match the widths used
+in the PostScript font.
+.
+.LP
+Note that
+.B gropdf
+is currently only able to display the first 256 glyphs in any font.
+This restriction will be lifted in a later version.
+.
+.\" .LP
+.\" Note that
+.\" .B grops
+.\" is able to display all glyphs in a PostScript font, not only 256.
+.\" .I enc_file
+.\" (or the default encoding if no encoding file specified) just defines the
+.\" order of glyphs for the first 256 characters; all other glyphs are
+.\" accessed with additional encoding vectors which
+.\" .B grops
+.\" produces on the fly.
+.
+.LP
+.B gropdf
+can automatically include the downloadable fonts necessary
+to print the document.
+.
+Fonts may be in PFA or PFB format.
+.LP
+.
+Any downloadable fonts which should, when required, be included by
+.B gropdf
+must be listed in the file
+.BR @FONTDIR@/devpdf/download ;
+this should consist of lines of the form
+.
+.IP
+.I
+foundry font filename
+.
+.LP
+where
+.I foundry
+is the foundry name or blank for the default foundry.
+.
+.I font
+is the PostScript name of the font,
+and
+.I filename
+is the name of the file containing the font;
+lines beginning with
+.B #
+and blank lines are ignored;
+fields must be separated by tabs;
+.I filename
+is searched for using the same mechanism that is used
+for groff font metric files.
+.
+The
+.B download
+file itself is also searched for using this mechanism;
+currently, only the first found file in the font path is used.
+.
+Foundry names are usually a single character (such as `U' for the URW
+Foundry) or blank for the default foundry.
+.
+This default uses the same fonts as
+.B ghostscript
+uses when it embeds fonts in a PDF file.
+.
+.LP
+In the default setup there are styles called
+.BR R ,
+.BR I ,
+.BR B ,
+and
+.B BI
+mounted at font positions 1 to\~4.
+.
+The fonts are grouped into families
+.BR A ,
+.BR BM ,
+.BR C ,
+.BR H ,
+.BR HN ,
+.BR N ,
+.BR P ,
+and\~\c
+.B T
+having members in each of these styles:
+.
+.RS
+.TP
+.B AR
+.FT AR
+AvantGarde-Book
+.FT
+.
+.TQ
+.B AI
+.FT AI
+AvantGarde-BookOblique
+.FT
+.
+.TQ
+.B AB
+.FT AB
+AvantGarde-Demi
+.FT
+.
+.TQ
+.B ABI
+.FT ABI
+AvantGarde-DemiOblique
+.FT
+.
+.TQ
+.B BMR
+.FT BMR
+Bookman-Light
+.FT
+.
+.TQ
+.B BMI
+.FT BMI
+Bookman-LightItalic
+.FT
+.
+.TQ
+.B BMB
+.FT BMB
+Bookman-Demi
+.FT
+.
+.TQ
+.B BMBI
+.FT BMBI
+Bookman-DemiItalic
+.FT
+.
+.TQ
+.B CR
+.FT CR
+Courier
+.FT
+.
+.TQ
+.B CI
+.FT CI
+Courier-Oblique
+.FT
+.
+.TQ
+.B CB
+.FT CB
+Courier-Bold
+.FT
+.
+.TQ
+.B CBI
+.FT CBI
+Courier-BoldOblique
+.FT
+.
+.TQ
+.B HR
+.FT HR
+Helvetica
+.FT
+.
+.TQ
+.B HI
+.FT HI
+Helvetica-Oblique
+.FT
+.
+.TQ
+.B HB
+.FT HB
+Helvetica-Bold
+.FT
+.
+.TQ
+.B HBI
+.FT HBI
+Helvetica-BoldOblique
+.FT
+.
+.TQ
+.B HNR
+.FT HNR
+Helvetica-Narrow
+.FT
+.
+.TQ
+.B HNI
+.FT HNI
+Helvetica-Narrow-Oblique
+.FT
+.
+.TQ
+.B HNB
+.FT HNB
+Helvetica-Narrow-Bold
+.FT
+.
+.TQ
+.B HNBI
+.FT HNBI
+Helvetica-Narrow-BoldOblique
+.FT
+.
+.TQ
+.B NR
+.FT NR
+NewCenturySchlbk-Roman
+.FT
+.
+.TQ
+.B NI
+.FT NI
+NewCenturySchlbk-Italic
+.FT
+.
+.TQ
+.B NB
+.FT NB
+NewCenturySchlbk-Bold
+.FT
+.
+.TQ
+.B NBI
+.FT NBI
+NewCenturySchlbk-BoldItalic
+.FT
+.
+.TQ
+.B PR
+.FT PR
+Palatino-Roman
+.FT
+.
+.TQ
+.B PI
+.FT PI
+Palatino-Italic
+.FT
+.
+.TQ
+.B PB
+.FT PB
+Palatino-Bold
+.FT
+.
+.TQ
+.B PBI
+.FT PBI
+Palatino-BoldItalic
+.FT
+.
+.TQ
+.B TR
+.FT TR
+Times-Roman
+.FT
+.
+.TQ
+.B TI
+.FT TI
+Times-Italic
+.FT
+.
+.TQ
+.B TB
+.FT TB
+Times-Bold
+.FT
+.
+.TQ
+.B TBI
+.FT TBI
+Times-BoldItalic
+.FT
+.RE
+.
+.LP
+There is also the following font which is not a member of a family:
+.
+.RS
+.TP
+.B ZCMI
+.FT ZCMI
+ZapfChancery-MediumItalic
+.FT
+.RE
+.
+.LP
+There are also some special fonts called
+.B S
+for the PS Symbol font. The lower case greek characters are automatically
+slanted (to match the SymbolSlanted font (SS) available to postscript).
+.
+Zapf Dingbats is available as
+.BR ZD ,
+the "hand pointing left" glyph (\[rs]lh) is available since it
+has been defined using the \[rs]X'pdf: xrev' extension which reverses the
+direction of letters within words.
+.
+.LP
+The default color for
+.B \[rs]m
+and
+.B \[rs]M
+is black; for colors defined in the `rgb' color space
+.B setrgbcolor
+is used, for `cmy' and `cmyk'
+.BR setcmykcolor ,
+and for `gray'
+.BR setgray .
+.
+Note that
+.B setcmykcolor
+is a PostScript LanguageLevel\~2 command and thus not available on some
+older printers.
+.
+.LP
+.B gropdf
+understands some of the X\~commands produced using the
+.B \[rs]X
+escape sequences supported by
+.B grops.
+Specifically it supports:-
+.
+.TP
+.B "\[rs]X'ps: invis'"
+Suppress output.
+.
+.TP
+.B "\[rs]X'ps: endinvis'"
+Stop suppressing output.
+.
+.TP
+.B "\[rs]X'ps: exec gsave currentpoint 2 copy translate \fIn\fP rotate neg exch neg exch translate'"
+where
+.I n
+is the angle of rotation.
+This is to support the
+.I align
+command in
+.BR gpic .
+.
+.TP
+.B "\[rs]X'ps: exec grestore'"
+Again used by
+.B gpic
+to restore after rotation.
+.
+.TP
+.BI "\[rs]X'ps: ... pdfmark'"
+All the
+.I pdfmark
+macros installed by using
+.I -m pdfmark
+or
+.I -m mspdf
+(see documentation in `pdfmark.pdf').
+A subset of these macros are installed automatically when you use
+.B -Tpdf
+so you should not need to use `-m pdfmark' for using most of the PDF
+functionality.
+.
+.LP
+All other
+.B ps:
+tags are silently ignored.
+.
+.LP
+One
+.B \[rs]X
+special used by the DVI driver is also recognised:
+.
+.TP
+.BI \[rs]X'papersize= paper-size '
+where the
+.I paper-size
+parameter is the same as the
+.B papersize
+command.
+.
+See
+.BR groff_font (@MAN5EXT@)
+for details.
+This means that you can alter the page size at will within the PDF file
+being created by
+.BR gropdf .
+.
+If you do want to change the paper size, it must be done before you start
+creating the page.
+.
+.LP
+In addition,
+.B gropdf
+supports its own suite of
+.B pdf:
+tags.
+.
+The following tags are supported:
+.
+.TP
+.BI "\[rs]X'pdf: pdfpic " "file alignment width height line-length" '
+Place an image of the specified
+.I width
+containing the PDF drawing from file
+.I file
+of desired
+.I width
+and
+.I height
+(if
+.I height
+is missing or zero then it is scaled proportionally).
+.
+If
+.I alignment
+is
+.B \-L
+the drawing is left aligned.
+.
+If it is
+.B \-C
+or
+.B \-R
+a
+.I linelength
+greater than the width of the drawing is required as well.
+.
+If
+.I width
+is specified as zero then the width is scaled in proportion to the height.
+.
+.\" .IP
+.\" See
+.\" .BR groff_tmac (@MAN5EXT@)
+.\" for a description of the
+.\" .B PSPIC
+.\" macro which provides a convenient high-level interface for inclusion of
+.\" PostScript graphics.
+.
+.TP
+.B \[rs]X'pdf: xrev'
+This toggles a flag which reverses the direction of printing
+.IR "letter by letter" ,
+i.e., each separate letter is reversed, not the entire word.
+.
+This is useful for reversing the direction of glyphs in the Dingbats font.
+.
+To return to normal printing repeat the command again.
+.
+.TP
+.BI "\[rs]X'pdf: markstart " "/ANN definition" '
+The macros which support PDF Bookmarks use this call internally to start the
+definition of bookmark hotspot (user will have called
+`.pdfhref\~L' with the text which will become the `hot spot' region).
+.
+Normally this is never used except from within the pdfmark macros.
+.
+.TP
+.B \[rs]X'pdf: markend'
+The macros which support PDF Bookmarks use this call internally to stop the
+definition of bookmark hotspot (user will have called
+`.pdfhref\~L' with the text which will become the `hot spot' region).
+.
+Normally this is never used except from within the pdfmark macros.
+.
+.TP
+.B \[rs]X'pdf: marksuspend'
+.TQ
+.B \[rs]X'pdf: markrestart'
+If you are using page traps to produce headings, footings, etc., you need to
+use these in case a `hot spot' crosses a page boundary, otherwise any text
+output by the heading or footing macro will be marked as part of the `hot
+spot'.
+.
+To stop this happening just place
+`.pdfmarksuspend' and `.pdfmarkrestart' at the start and end of the page trap macro,
+respectively.
+.
+(These are just convenience macros which emit the \[rs]X code.
+.
+These macros must only be used within page traps.)
+.
+.SS Importing graphics
+.
+.B gropdf
+only supports importing other PDF files as graphics.
+.
+But that PDF file may contain any of the graphic formats supported by the PDF
+standard (such as JPEG, PNG, GIF, etc.).
+.
+So any application which outputs PDF can be used as an embedded file in
+.BR gropdf .
+.
+The PDF file you wish to insert must be a single page and the drawing must
+just fit inside the media size of the PDF file.
+.
+So, in
+.BR inkscape (1)
+or
+.BR gimp (1)
+(for example) make sure the canvas size just fits the image.
+.
+.LP
+The PDF parser used in
+.B gropdf
+has not been rigorously tested with all possible applications which produce
+PDFs.
+.
+If you find a single page PDF which fails to import properly, it is worth
+running it through the
+.
+.BR pdftk (1)
+program by issuing the command:
+.
+.RS
+.LP
+.B pdftk
+.I oldfile.pdf
+.B output
+.I newfile.pdf
+.RE
+.
+.LP
+You may find that
+.I newfile.pdf
+will now load successfully.
+.
+.SS TrueType and other font formats
+.
+.B gropdf
+does not support TrueType fonts natively within the PDF files it generates
+(but does support any fonts if they are in an imported PDF).
+.
+However, TrueType fonts can be used with
+.B gropdf
+if converted first to
+.B Type\~42
+format, a special PostScript wrapper equivalent to the PFA format mentioned
+in
+.BR \%pfbtops (@MAN1EXT@).
+.
+There are several different methods to generate a Type\~42
+wrapper and most of them involve the use of a PostScript
+interpreter such as Ghostscript \[en] see
+.BR gs (1).
+.
+.LP
+Yet, the easiest method involves the use of the application
+.BR ttftot42 (1).
+.
+This program uses
+.BR freetype (3)
+(version 1.3.1) to generate Type\~42
+font wrappers and well-formed AFM files that can be fed to the
+.BR \%afmtodit (@MAN1EXT@)
+script to create appropriate metric files.
+.
+The resulting font wrappers should be added to groff's
+.B download
+file.
+.
+.B ttftot42
+source code can be downloaded from
+.UR ftp://\:www.giga.or.at/\:pub/\:nih/\:ttftot42/
+ftp://\:www.giga.or.at/\:pub/\:nih/\:ttftot42/
+.UE .
+.
+.LP
+Another solution for creating Type\~42 wrappers is to use FontForge,
+available from
+.UR http://\:fontforge.sf.net
+http://\:fontforge.sf.net
+.UE .
+.
+This font editor can convert most outline font formats.
+.
+.
+.SH FONT INSTALLATION
+.
+This section gives a summary of the above explanations; it can serve
+as a step-by-step font installation guide for
+.BR gropdf .
+.
+.ds BU \[bu]\ \ \"
+.de LI
+.IP "" 4
+\h'-\w'\*[BU]'u'\*[BU]\c
+..
+.LI
+Convert your font to something groff understands.
+.
+This is either a PostScript Type\~1 font in either PFA or PFB format or a
+PostScript Type\~42 font, together with an AFM file.
+.
+.IP
+The very first line in a PFA/PFB file contains this:
+.
+.RS
+.IP
+.B %!PS-AdobeFont-1.0:
+.RE
+.
+.IP
+A PFB file has this also in the first line, but the string is
+preceded with some binary bytes.
+.
+.IP
+The very first characters in a Type\~42 font file look like this:
+.
+.RS
+.IP
+.B %!PS-TrueTypeFont
+.RE
+.
+.IP
+This is a wrapper format for TrueType fonts.
+.
+Old PS printers might not support it (this is, they don't have a
+built-in TrueType font interpreter).
+.
+.IP
+For TrueType fonts, try
+.B ttftot42
+or
+.BR fontforge .
+For all other font formats use
+.B fontforge
+which can convert most outline font formats.
+.
+.LI
+Convert the AFM file to a groff font description file with the
+.BR \%afmtodit (@MAN1EXT@)
+program.
+.
+An example call is
+.
+.RS
+.IP
+afmtodit Foo-Bar-Bold.afm map/textmap FBB
+.RE
+.
+.IP
+which converts the metric file `Foo-Bar-Bold.afm' to the groff
+font `FBB'.
+.
+If you have a font family which comes with normal, bold, italic,
+and bold italic faces, it is recommended to use the letters
+.BR R ,
+.BR B ,
+.BR I ,
+and
+.BR BI ,
+respectively, as postfixes in the groff font names to make groff's
+`.fam' request work.
+.
+An example is groff's built-in Times-Roman font: The font family name
+is
+.BR T ,
+and the groff font names are
+.BR TR ,
+.BR TB ,
+.BR TI ,
+and
+.BR TBI .
+.
+.LI
+Install both the groff font description files and the fonts in a
+`devpdf' subdirectory of the font path which groff finds.
+.
+See the
+.B ENVIRONMENT
+section in the
+.BR troff (@MAN1EXT@)
+man page which lists the actual value of the font path.
+.
+Note that groff doesn't use the AFM files (but it is a good idea to
+store them anyway).
+.
+.LI
+Register all fonts which must be downloaded to the printer in the
+`devpdf/download' file.
+.
+Only the first occurrence of this file in the font path is read.
+.
+This means that you should copy the default `download' file to the
+first directory in your font path and add your fonts there.
+.
+To continue the above example we assume that the PS font name for
+Foo-Bar-Bold.pfa is `XY-Foo-Bar-Bold' (the PS font name is stored in the
+.B internalname
+field in the `FBB' file) and belongs to foundry `F', thus the following
+line should be added to `download':
+.
+.RS
+.IP
+.B F XY-Foo-Bar-Bold Foo-Bar-Bold.pfa
+.
+.LP
+Use a tab character to separate the fields, and the
+`foundry' field should be null for the default foundry.
+.RE
+.
+.
+.SH ENVIRONMENT
+.
+.TP
+.SM
+.B GROFF_FONT_PATH
+A list of directories in which to search for the
+.BI dev name
+directory in addition to the default ones.
+.
+If, in the `download' file, the
+font file has been specified with a full path, no directories are searched.
+.
+See
+.BR @g@troff (@MAN1EXT@)
+and
+.BR \%groff_font (@MAN5EXT@)
+for more details.
+.
+.
+.SH FILES
+.
+.Tp \w'\fB@FONTDIR@/devpdf/download'u+2n
+.B @FONTDIR@/devpdf/DESC
+Device description file.
+.
+.TP
+.BI @FONTDIR@/devpdf/ F
+Font description file for font\~\c
+.IR F .
+.
+.TP
+.BI @FONTDIR@/devpdf/ U-F
+Font description file for font\~\c
+.I F
+(using foundry\~\c
+.I U
+rather than the default foundry).
+.
+.TP
+.B @FONTDIR@/devpdf/download
+List of downloadable fonts.
+.
+.TP
+.B @FONTDIR@/devpdf/Foundry
+A Perl script used during install to locate suitable fonts.
+.
+.TP
+.B @FONTDIR@/devpdf/enc/text.enc
+Encoding used for text fonts.
+.
+.TP
+.B @MACRODIR@/pdf.tmac
+Macros for use with
+.BR gropdf ;
+automatically loaded by
+.BR troffrc .
+.
+.\" .TP
+.\" .B @MACRODIR@/pspic.tmac
+.\" Definition of
+.\" .B PSPIC
+.\" macro,
+.\" automatically loaded by
+.\" .BR ps.tmac .
+.\" .
+.
+.
+.SH "SEE ALSO"
+.
+.BR \%afmtodit (@MAN1EXT@),
+.BR groff (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR \%pfbtops (@MAN1EXT@),
+.BR \%groff_out (@MAN5EXT@),
+.BR \%groff_font (@MAN5EXT@),
+.BR \%groff_char (@MAN7EXT@),
+.BR \%groff_tmac (@MAN5EXT@)
+.
+.
+.
+.\" Local Variables:
+.\" mode: nroff
+.\" End:
diff --git a/src/devices/gropdf/gropdf.pl b/src/devices/gropdf/gropdf.pl
new file mode 100644
index 00000000..9ab54322
--- /dev/null
+++ b/src/devices/gropdf/gropdf.pl
@@ -0,0 +1,2928 @@
+#!@PERLPATH@ -w
+#
+# gropdf : PDF post processor for groff
+# Deri James : 4th May 2009
+#
+
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# Written by Deri James <deri@chuzzlewit.demon.co.uk>
+#
+# This file is part of groff.
+#
+# groff 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.
+#
+# groff 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/>.
+
+use strict;
+use Getopt::Long;
+use Compress::Zlib;
+
+my %cfg;
+
+$cfg{GROFF_VERSION}='@VERSION@';
+$cfg{GROFF_FONT_PATH}='@GROFF_FONT_DIR@';
+
+my @obj; # Array of PDF objects
+my $objct=0; # Count of Objects
+my $fct=0; # Output count
+my %fnt; # Used fonts
+my $lct=0; # Input Line Count
+my $src_name='';
+my %env; # Current environment
+my %fontlst; # Fonts Loaded
+my $rot=0; # Portrait
+my %desc; # Contents of DESC
+my %download; # Contents of downlopad file
+my $pages; # Pointer to /Pages object
+my $devnm='devpdf';
+my $cpage; # Pointer to current pages
+my $cpageno=0; # Object no of current page
+my $cat; # Pointer to catalogue
+my $dests; # Pointer to Dests
+my @mediabox=(0,0,595,842);
+my @defaultmb=(0,0,595,842);
+my $stream=''; # Current Text/Graphics stream
+my $cftsz=10; # Current font sz
+my $cft; # Current Font
+my $lwidth=1; # current linewidth
+my $textcol=''; # Current groff text
+my $fillcol=''; # Current groff fill
+my $curfill=''; # Current PDF fill
+my $strkcol='';
+my $curstrk='';
+my @lin=(); # Array holding current line of text
+my @ahead=(); # Buffer used to hol the next line
+my $mode='g'; # Graphic (g) or Text (t) mode;
+my $xpos=0; # Current X position
+my $ypos=0; # Current Y position
+my $tmxpos=0;
+my $widtbl; # Pointer to width table for current font size
+my $origwidtbl; # Pointer to width table
+my $krntbl; # Pointer to kern table
+my $matrix="1 0 0 1";
+my $whtsz; # Current width of a space
+my $poschg=0; # V/H pending
+my $fontchg=0; # font change pending
+my $tnum=2; # flatness of B-Spline curve
+my $tden=3; # flatness of B-Spline curve
+my $linewidth=40;
+my $w_flg=0;
+my $nomove=0;
+my $pendmv=0;
+my $gotT=0;
+my $suppress=0; # Suppress processing?
+my %incfil; # Included Files
+my @outlev=([0,undef,0,0]); # Structure pdfmark /OUT entries
+my $curoutlev=\@outlev;
+my $Foundry='';
+my $xrev=0; # Reverse x direction of font
+my $matrixchg=0;
+my $wt=-1;
+my $thislev=1;
+my $mark=undef;
+my $suspendmark=undef;
+my $n_flg=1;
+
+my %ppsz=( 'ledger'=>[1224,792],
+ 'legal'=>[612,1008],
+ 'letter'=>[612,792],
+ 'a0'=>[2384,3370],
+ 'a1'=>[1684,2384],
+ 'a2'=>[1191,1684],
+ 'a3'=>[842,1191],
+ 'a4'=>[595,842],
+ 'a5'=>[420,595],
+ 'a6'=>[297,420],
+ 'a7'=>[210,297],
+ 'a8'=>[148,210],
+ 'a9'=>[105,148],
+ 'a10'=>[73,105],
+ 'isob0'=>[2835,4008],
+ 'isob1'=>[2004,2835],
+ 'isob2'=>[1417,2004],
+ 'isob3'=>[1001,1417],
+ 'isob4'=>[709,1001],
+ 'isob5'=>[499,709],
+ 'isob6'=>[354,499],
+ 'c0'=>[2599,3677],
+ 'c1'=>[1837,2599],
+ 'c2'=>[1298,1837],
+ 'c3'=>[918,1298],
+ 'c4'=>[649,918],
+ 'c5'=>[459,649],
+ 'c6'=>[323,459] );
+
+
+my $fd;
+my $frot;
+my $fpsz;
+my $embedall=0;
+my $debug=0;
+my $version=0;
+
+#Load_Config();
+
+GetOptions("F=s" => \$fd, 'l' => \$frot, 'p=s' => \$fpsz, 'd!' => \$debug, 'v' => \$version, 'e' => \$embedall, 'fy=s' => \$Foundry);
+
+print "GNU gropdf (groff) version $cfg{GROFF_VERSION}\n", exit if $version;
+
+# Search for 'font directory': paths in -f opt, shell var GROFF_FONT_PATH, default paths
+
+my $fontdir=$cfg{GROFF_FONT_PATH};
+$fontdir=$ENV{GROFF_FONT_PATH}.':'.$fontdir if exists($ENV{GROFF_FONT_PATH});
+$fontdir=$fd.':'.$fontdir if defined($fd);
+
+$rot=90 if $frot;
+$matrix="0 1 -1 0" if $frot;
+
+LoadDownload();
+LoadDesc();
+
+my $unitwidth=$desc{unitwidth};
+my $papersz=$desc{papersize};
+$papersz=$fpsz if $fpsz;
+
+$env{FontHT}=0;
+$env{FontSlant}=0;
+MakeMatrix();
+
+if (substr($papersz,0,1) eq '/' and -r $papersz)
+{
+ if (open(P,"<$papersz"))
+ {
+ while (<P>)
+ {
+ chomp;
+ s/# .*//;
+ next if $_ eq '';
+ $papersz=$_;
+ last
+ }
+
+ close(P);
+ }
+}
+
+if ($papersz=~m/([\d.]+)([cipP]),([\d.]+)([cipP])/)
+{
+ @defaultmb=@mediabox=(0,0,ToPoints($1,$2),ToPoints($3,$4));
+}
+elsif (exists($ppsz{$papersz}))
+{
+ @defaultmb=@mediabox=(0,0,$ppsz{$papersz}->[0],$ppsz{$papersz}->[1]);
+}
+
+my (@dt)=localtime(time);
+my $dt=PDFDate(\@dt);
+
+my %info=('Creator' => "(groff version $cfg{GROFF_VERSION})",
+ 'Producer' => "(gropdf version $cfg{GROFF_VERSION})",
+ 'ModDate' => "($dt)",
+ 'CreationDate' => "($dt)");
+while (<>)
+{
+ chomp;
+ $lct++;
+
+ do # The ahead buffer behaves like 'ungetc'
+ {{
+ if (scalar(@ahead))
+ {
+ $_=shift(@ahead);
+ }
+
+
+ my $cmd=substr($_,0,1);
+ next if $cmd eq '#'; # just a comment
+ my $lin=substr($_,1);
+
+ while ($cmd eq 'w')
+ {
+ $cmd=substr($lin,0,1);
+ $lin=substr($lin,1);
+ $w_flg=1 if $gotT;
+ }
+
+ $lin=~s/^\s+//;
+# $lin=~s/\s#.*?$//; # remove comment
+ $stream.="\% $_\n" if $debug;
+
+ do_x($lin),next if ($cmd eq 'x');
+ next if $suppress;
+ do_p($lin),next if ($cmd eq 'p');
+ do_f($lin),next if ($cmd eq 'f');
+ do_s($lin),next if ($cmd eq 's');
+ do_m($lin),next if ($cmd eq 'm');
+ do_D($lin),next if ($cmd eq 'D');
+ do_V($lin),next if ($cmd eq 'V');
+ do_v($lin),next if ($cmd eq 'v');
+ do_t($lin),next if ($cmd eq 't');
+ do_C($lin),next if ($cmd eq 'C');
+ do_c($lin),next if ($cmd eq 'c');
+ do_N($lin),next if ($cmd eq 'N');
+ do_h($lin),next if ($cmd eq 'h');
+ do_H($lin),next if ($cmd eq 'H');
+ do_n($lin),next if ($cmd eq 'n');
+
+ my $tmp=scalar(@ahead);
+ }} until scalar(@ahead) == 0;
+
+}
+
+
+if ($cpageno > 0)
+{
+ PutObj($cpageno);
+ OutStream($cpageno+1);
+}
+
+
+PutOutlines(\@outlev);
+
+PutObj(1);
+
+my $info=BuildObj(++$objct,\%info);
+
+PutObj($objct);
+
+foreach my $o (3..$objct)
+{
+ PutObj($o) if (!exists($obj[$o]->{XREF}));
+}
+
+#my $encrypt=BuildObj(++$objct,{'Filter' => '/Standard', 'V' => 1, 'R' => 2, 'P' => 252});
+#PutObj($objct);
+PutObj(2);
+
+my $xrefct=$fct;
+
+$objct+=1;
+print "xref\n0 $objct\n0000000000 65535 f \n";
+
+foreach my $xr (@obj)
+{
+ next if !defined($xr);
+ printf("%010d 00000 n \n",$xr->{XREF});
+}
+
+print "trailer\n<<\n/Info $info\n/Root 1 0 R\n/Size $objct\n>>\nstartxref\n$fct\n\%\%EOF\n\% Pages=$pages->{Count}\n";
+
+
+sub MakeMatrix
+{
+ my $fontxrev=shift||0;
+ my @mat=($frot)?(0,1,-1,0):(1,0,0,1);
+
+ if (!$frot)
+ {
+ if ($env{FontHT} != 0)
+ {
+ $mat[3]=sprintf('%.3f',$env{FontHT}/$cftsz);
+ }
+
+ if ($env{FontSlant} != 0)
+ {
+ my $slant=$env{FontSlant};
+ $slant*=$env{FontHT}/$cftsz if $env{FontHT} != 0;
+ my $ang=rad($slant);
+
+ $mat[2]=sprintf('%.3f',sin($ang)/cos($ang));
+ }
+
+ if ($fontxrev)
+ {
+ $mat[0]=-$mat[0];
+ }
+ }
+
+ $matrix=join(' ',@mat);
+ $matrixchg=1;
+}
+
+sub PutOutlines
+{
+ my $o=shift;
+ my $outlines;
+
+ if ($#{$o} > 0)
+ {
+ # We've got Outlines to deal with
+ my $openct=$curoutlev->[0]->[2];
+
+ while ($thislev-- > 1)
+ {
+ my $nxtoutlev=$curoutlev->[0]->[1];
+ $nxtoutlev->[0]->[2]+=$openct if $curoutlev->[0]->[3]==1;
+ $openct=0 if $nxtoutlev->[0]->[3]==-1;
+ $curoutlev=$nxtoutlev;
+ }
+
+ $cat->{Outlines}=BuildObj(++$objct,{'Count' => abs($o->[0]->[0])+$o->[0]->[2]});
+ $outlines=$obj[$objct]->{DATA};
+ }
+ else
+ {
+ return;
+ }
+
+ SetOutObj($o);
+
+ $outlines->{First}=$o->[1]->[2];
+ $outlines->{Last}=$o->[$#{$o}]->[2];
+
+ LinkOutObj($o,$cat->{Outlines});
+}
+
+sub SetOutObj
+{
+ my $o=shift;
+
+ for my $j (1..$#{$o})
+ {
+ my $ono=BuildObj(++$objct,$o->[$j]->[0]);
+ $o->[$j]->[2]=$ono;
+
+ SetOutObj($o->[$j]->[1]) if $#{$o->[$j]->[1]} > -1;
+ }
+}
+
+sub LinkOutObj
+{
+ my $o=shift;
+ my $parent=shift;
+
+ for my $j (1..$#{$o})
+ {
+ my $op=GetObj($o->[$j]->[2]);
+
+ $op->{Next}=$o->[$j+1]->[2] if ($j < $#{$o});
+ $op->{Prev}=$o->[$j-1]->[2] if ($j > 1);
+ $op->{Parent}=$parent;
+
+ if ($#{$o->[$j]->[1]} > -1)
+ {
+ $op->{Count}=$o->[$j]->[1]->[0]->[2]*$o->[$j]->[1]->[0]->[3];# if exists($op->{Count}) and $op->{Count} > 0;
+ $op->{First}=$o->[$j]->[1]->[1]->[2];
+ $op->{Last}=$o->[$j]->[1]->[$#{$o->[$j]->[1]}]->[2];
+ LinkOutObj($o->[$j]->[1],$o->[$j]->[2]);
+ }
+ }
+}
+
+sub GetObj
+{
+ my $ono=shift;
+ ($ono)=split(' ',$ono);
+ return($obj[$ono]->{DATA});
+}
+
+
+
+sub PDFDate
+{
+ my $dt=shift;
+ return(sprintf("D:%04d%02d%02d%02d%02d%02d% +02d'00'",$dt->[5]+1900,$dt->[4]+1,$dt->[3],$dt->[2],$dt->[1],$dt->[0],( localtime time() + 3600*( 12 - (gmtime)[2] ) )[2] - 12));
+}
+
+sub ToPoints
+{
+ my $num=shift;
+ my $unit=shift;
+
+ if ($unit eq 'i')
+ {
+ return($num*72);
+ }
+ elsif ($unit eq 'c')
+ {
+ return int($num*72/2.54);
+ }
+ elsif ($unit eq 'm') # millimetres
+ {
+ return int($num*72/25.4);
+ }
+ elsif ($unit eq 'p')
+ {
+ return($num);
+ }
+ elsif ($unit eq 'P')
+ {
+ return($num*6);
+ }
+ else
+ {
+ Msg(1,"Unknown scaling factor '$unit'");
+ }
+}
+
+sub Load_Config
+{
+ open(CFG,"<gropdf_config") or die "Can't open config file: $!";
+
+ while (<CFG>)
+ {
+ chomp;
+ my ($key,$val)=split(/ ?= ?/);
+
+ $cfg{$key}=$val;
+ }
+
+ close(CFG);
+}
+
+sub LoadDownload
+{
+ my $f;
+
+ OpenFile(\$f,$fontdir,"download");
+ Msg(1,"Failed to open 'download'") if !defined($f);
+
+ while (<$f>)
+ {
+ chomp;
+ s/#.*$//;
+ next if $_ eq '';
+ my ($foundry,$name,$file)=split(/\t+/);
+ if (substr($file,0,1) eq '*')
+ {
+ next if !$embedall;
+ $file=substr($file,1);
+ }
+
+ $download{"$foundry $name"}=$file;
+ }
+
+ close($f);
+}
+
+sub OpenFile
+{
+ my $f=shift;
+ my $dirs=shift;
+ my $fnm=shift;
+
+ if (substr($fnm,0,1) eq '/')
+ {
+ return if -r "$fnm" and open($$f,"<$fnm");
+ }
+
+ my (@dirs)=split(':',$dirs);
+
+ foreach my $dir (@dirs)
+ {
+ last if -r "$dir/$devnm/$fnm" and open($$f,"<$dir/$devnm/$fnm");
+ }
+}
+
+sub LoadDesc
+{
+ my $f;
+
+ OpenFile(\$f,$fontdir,"DESC");
+ Msg(1,"Failed to open 'DESC'") if !defined($f);
+
+ while (<$f>)
+ {
+ chomp;
+ s/#.*$//;
+ next if $_ eq '';
+ my ($name,$prms)=split(' ',$_,2);
+ $desc{lc($name)}=$prms;
+ }
+
+ close($f);
+}
+
+sub rad { $_[0]*3.14159/180 }
+
+my $InPicRotate=0;
+
+sub do_x
+{
+ my $l=shift;
+ my ($xcmd,@xprm)=split(' ',$l);
+ $xcmd=substr($xcmd,0,1);
+
+ if ($xcmd eq 'T')
+ {
+ Msg(0,"Expecting a pdf pipe (got $xprm[0])") if $xprm[0] ne substr($devnm,3);
+ }
+ elsif ($xcmd eq 'f') # Register Font
+ {
+ $xprm[1]="${Foundry}-$xprm[1]" if $Foundry ne '';
+ LoadFont($xprm[0],$xprm[1]);
+ }
+ elsif ($xcmd eq 'F') # Source File (for errors)
+ {
+ $env{SourceFile}=$xprm[0];
+ }
+ elsif ($xcmd eq 'H') # FontHT
+ {
+ $xprm[0]/=$unitwidth;
+ $xprm[0]=0 if $xprm[0] == $cftsz;
+ $env{FontHT}=$xprm[0];
+ MakeMatrix();
+ }
+ elsif ($xcmd eq 'S') # FontSlant
+ {
+ $env{FontSlant}=$xprm[0];
+ MakeMatrix();
+ }
+ elsif ($xcmd eq 'i') # Initialise
+ {
+ $objct++;
+ @defaultmb=@mediabox;
+ BuildObj($objct,{'Pages' => BuildObj($objct+1,
+ {'Kids' => [],
+ 'Count' => 0,
+ 'Type' => '/Pages',
+ 'Rotate' => $rot,
+ 'MediaBox' => \@defaultmb,
+ 'Resources' =>
+ {'Font' => {},
+ 'ProcSet' => ['/PDF', '/Text', '/ImageB', '/ImageC', '/ImageI']}
+ }
+ ),
+ 'Type' => '/Catalog'});
+
+ $cat=$obj[$objct]->{DATA};
+ $objct++;
+ $pages=$obj[2]->{DATA};
+ Put("%PDF-1.4\n%âãÏÓ\n");
+ }
+ elsif ($xcmd eq 'X')
+ {
+ # There could be extended args
+ do
+ {{
+ LoadAhead(1);
+ if (substr($ahead[0],0,1) eq '+')
+ {
+ $l.="\n".substr($ahead[0],1);
+ shift(@ahead);
+ }
+ }} until $#ahead==0;
+
+ ($xcmd,@xprm)=split(' ',$l);
+ $xcmd=substr($xcmd,0,1);
+
+ if ($xprm[0]=~m/^(.+:)(.+)/)
+ {
+ splice(@xprm,1,0,$2);
+ $xprm[0]=$1;
+ }
+
+ my $par=join(' ',@xprm[1..$#xprm]);
+
+ if ($xprm[0] eq 'ps:')
+ {
+ if ($xprm[1] eq 'invis')
+ {
+ $suppress=1;
+ }
+ elsif ($xprm[1] eq 'endinvis')
+ {
+ $suppress=0;
+ }
+ elsif ($par=~m/exec gsave currentpoint 2 copy translate (.+) rotate neg exch neg exch translate/)
+ {
+ # This is added by gpic to rotate a single object
+
+ my $theta=-rad($1);
+
+ IsGraphic();
+ my ($curangle,$hyp)=RtoP($xpos,GraphY($ypos));
+ my ($x,$y)=PtoR($theta+$curangle,$hyp);
+ $stream.="q\n".sprintf("%.3f %.3f %.3f %.3f %.3f %.3f cm",cos($theta),sin($theta),-sin($theta),cos($theta),$xpos-$x,GraphY($ypos)-$y)."\n";
+ $InPicRotate=1;
+ }
+ elsif ($par=~m/exec grestore/ and $InPicRotate)
+ {
+ IsGraphic();
+ $stream.="Q\n";
+ $InPicRotate=0;
+ }
+ elsif ($par=~m/\[(.+) pdfmark/)
+ {
+ my $pdfmark=$1;
+ $pdfmark=~s((\d{4,6}) u)(sprintf("%.1f",$1/$desc{sizescale}))eg;
+
+ if ($pdfmark=~m/(.+) \/DOCINFO/)
+ {
+ my @xwds=split(' ',"<< $1 >>");
+ my $docinfo=ParsePDFValue(\@xwds);
+
+ foreach my $k (keys %{$docinfo})
+ {
+ $info{$k}=$docinfo->{$k} if $k ne 'Producer';
+ }
+ }
+ elsif ($pdfmark=~m/(.+) \/DOCVIEW/)
+ {
+ my @xwds=split(' ',"<< $1 >>");
+ my $docview=ParsePDFValue(\@xwds);
+
+ foreach my $k (keys %{$docview})
+ {
+ $cat->{$k}=$docview->{$k} if !exists($cat->{$k});
+ }
+ }
+ elsif ($pdfmark=~m/(.+) \/DEST/)
+ {
+ my @xwds=split(' ',"<< $1 >>");
+ my $dest=ParsePDFValue(\@xwds);
+ foreach my $v (@{$dest->{View}})
+ {
+ $v=GraphY(abs($v)) if substr($v,0,1) eq '-';
+ }
+ unshift(@{$dest->{View}},"$cpageno 0 R");
+
+ if (!defined($dests))
+ {
+ $cat->{Dests}=BuildObj(++$objct,{});
+ $dests=$obj[$objct]->{DATA};
+ }
+
+ my $k=substr($dest->{Dest},1);
+ $dests->{$k}=$dest->{View};
+ }
+ elsif ($pdfmark=~m/(.+) \/ANN/)
+ {
+ my $l=$1;
+ $l=~s/Color/C/;
+ $l=~s/Action/A/;
+ $l=~s/Title/T/;
+ $l=~s'/Subtype /URI'/S /URI';
+ my @xwds=split(' ',"<< $l >>");
+ my $annotno=BuildObj(++$objct,ParsePDFValue(\@xwds));
+ my $annot=$obj[$objct];
+ $annot->{DATA}->{Type}='/Annot';
+ FixRect($annot->{DATA}->{Rect}); # Y origin to ll
+ push(@{$cpage->{Annots}},$annotno);
+ }
+ elsif ($pdfmark=~m/(.+) \/OUT/)
+ {
+ my @xwds=split(' ',"<< $1 >>");
+ my $out=ParsePDFValue(\@xwds);
+
+ my $this=[$out,[]];
+
+ if (exists($out->{Level}))
+ {
+ my $lev=abs($out->{Level});
+ my $levsgn=sgn($out->{Level});
+ delete($out->{Level});
+
+ if ($lev > $thislev)
+ {
+ my $thisoutlev=$curoutlev->[$#{$curoutlev}]->[1];
+ $thisoutlev->[0]=[0,$curoutlev,0,$levsgn];
+ $curoutlev=$thisoutlev;
+ $thislev++;
+ }
+ elsif ($lev < $thislev)
+ {
+ my $openct=$curoutlev->[0]->[2];
+
+ while ($thislev > $lev)
+ {
+ my $nxtoutlev=$curoutlev->[0]->[1];
+ $nxtoutlev->[0]->[2]+=$openct if $curoutlev->[0]->[3]==1;
+ $openct=0 if $nxtoutlev->[0]->[3]==-1;
+ $curoutlev=$nxtoutlev;
+ $thislev--;
+ }
+ }
+
+ push(@{$curoutlev},$this);
+ $curoutlev->[0]->[2]++;
+ }
+ else
+ {
+ while ($curoutlev->[0]->[0] == 0 and defined($curoutlev->[0]->[1]))
+ {
+ $curoutlev=$curoutlev->[0]->[1];
+ }
+
+ $curoutlev->[0]->[0]--;
+ $curoutlev->[0]->[2]++;
+ push(@{$curoutlev},$this);
+
+
+ if (exists($out->{Count}) and $out->{Count} != 0)
+ {
+ push(@{$this->[1]},[abs($out->{Count}),$curoutlev,0,sgn($out->{Count})]);
+ $curoutlev=$this->[1];
+
+ if ($out->{Count} > 0)
+ {
+ my $p=$curoutlev;
+
+ while (defined($p))
+ {
+ $p->[0]->[2]+=$out->{Count};
+ $p=$p->[0]->[1];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ elsif (lc($xprm[0]) eq 'pdf:')
+ {
+ if (lc($xprm[1]) eq 'import')
+ {
+ my $fil=$xprm[2];
+ my $llx=$xprm[3];
+ my $lly=$xprm[4];
+ my $urx=$xprm[5];
+ my $ury=$xprm[6];
+ my $wid=$xprm[7];
+ my $hgt=$xprm[8]||-1;
+ my $mat=[1,0,0,1,0,0];
+
+ if (!exists($incfil{$fil}))
+ {
+ if ($fil=~m/\.pdf$/)
+ {
+ $incfil{$fil}=LoadPDF($fil,$mat,$wid,$hgt,"import");
+ }
+ elsif ($fil=~m/\.swf$/)
+ {
+ my $xscale=$wid/($urx-$llx+1);
+ my $yscale=($hgt<=0)?$xscale:($hgt/($ury-$lly+1));
+ $hgt=($ury-$lly+1)*$yscale;
+
+ if ($rot)
+ {
+ $mat->[3]=$xscale;
+ $mat->[0]=$yscale;
+ }
+ else
+ {
+ $mat->[0]=$xscale;
+ $mat->[3]=$yscale;
+ }
+
+ $incfil{$fil}=LoadSWF($fil,[$llx,$lly,$urx,$ury],$mat);
+ }
+ else
+ {
+ Msg(0,"Unknown filetype '$fil'");
+ return undef;
+ }
+ }
+
+ if (defined($incfil{$fil}))
+ {
+ IsGraphic();
+ if ($fil=~m/\.pdf$/)
+ {
+ my $bbox=$incfil{$fil}->[1];
+ my $xscale=$wid/($bbox->[2]-$bbox->[0]+1);
+ my $yscale=($hgt<=0)?$xscale:($hgt/($bbox->[3]-$bbox->[1]+1));
+ $stream.="q $xscale 0 0 $yscale ".PutXY($xpos,$ypos)." cm";
+ $stream.=" 0 1 -1 0 0 0 cm" if $rot;
+ $stream.=" /$incfil{$fil}->[0] Do Q\n";
+ }
+ elsif ($fil=~m/\.swf$/)
+ {
+ $stream.=PutXY($xpos,$ypos)." m /$incfil{$fil} Do\n";
+ }
+ }
+ }
+ elsif (lc($xprm[1]) eq 'pdfpic')
+ {
+ my $fil=$xprm[2];
+ my $flag=uc($xprm[3]);
+ my $wid=GetPoints($xprm[4]);
+ my $hgt=GetPoints($xprm[5]||-1);
+ my $ll=GetPoints($xprm[6]||0);
+ my $mat=[1,0,0,1,0,0];
+
+ if (!exists($incfil{$fil}))
+ {
+ $incfil{$fil}=LoadPDF($fil,$mat,$wid,$hgt,"pdfpic");
+ }
+
+ if (defined($incfil{$fil}))
+ {
+ IsGraphic();
+ my $bbox=$incfil{$fil}->[1];
+ my $xscale=$wid/($bbox->[2]-$bbox->[0]+1);
+ my $yscale=($hgt<=0)?$xscale:($hgt/($bbox->[3]-$bbox->[1]+1));
+ $xscale=($wid<=0)?$yscale:$xscale;
+ $xscale=$yscale if $yscale < $xscale;
+ $yscale=$xscale if $xscale < $yscale;
+ $wid=($bbox->[2]-$bbox->[0]+1)*$xscale;
+ $hgt=($bbox->[3]-$bbox->[1]+1)*$yscale;
+
+ if ($flag eq '-C' and $ll > $wid)
+ {
+ $xpos+=int(($ll-$wid)/2);
+ }
+ elsif ($flag eq '-R' and $ll > $wid)
+ {
+ $xpos+=$ll-$wid;
+ }
+
+ $ypos+=$hgt;
+ $stream.="q $xscale 0 0 $yscale ".PutXY($xpos,$ypos)." cm";
+ $stream.=" 0 1 -1 0 0 0 cm" if $rot;
+ $stream.=" /$incfil{$fil}->[0] Do Q\n";
+ }
+ }
+ elsif (lc($xprm[1]) eq 'xrev')
+ {
+ $xrev=!$xrev;
+ }
+ elsif (lc($xprm[1]) eq 'markstart')
+ {
+ $mark={'rst' => $xprm[2]/$unitwidth, 'rsb' => $xprm[3]/$unitwidth, 'xpos' => $xpos,
+ 'ypos' => $ypos, 'pdfmark' => join(' ',@xprm[4..$#xprm])};
+ }
+ elsif (lc($xprm[1]) eq 'markend')
+ {
+ PutHotSpot($xpos) if defined($mark);
+ $mark=undef;
+ }
+ elsif (lc($xprm[1]) eq 'marksuspend')
+ {
+ $suspendmark=$mark;
+ $mark=undef;
+ }
+ elsif (lc($xprm[1]) eq 'markrestart')
+ {
+ $mark=$suspendmark;
+ $suspendmark=undef;
+ }
+ }
+ elsif (lc(substr($xprm[0],0,9)) eq 'papersize')
+ {
+ my ($px,$py)=split(',',substr($xprm[0],10));
+ $px=GetPoints($px);
+ $py=GetPoints($py);
+ @mediabox=(0,0,$px,$py);
+ my @mb=@mediabox;
+ $matrixchg=1;
+ $cpage->{MediaBox}=\@mb;
+ }
+ }
+}
+
+sub PutHotSpot
+{
+ my $endx=shift;
+ my $l=$mark->{pdfmark};
+ $l=~s/Color/C/;
+ $l=~s/Action/A/;
+ $l=~s'/Subtype /URI'/S /URI';
+ my @xwds=split(' ',"<< $l >>");
+ my $annotno=BuildObj(++$objct,ParsePDFValue(\@xwds));
+ my $annot=$obj[$objct];
+ $annot->{DATA}->{Type}='/Annot';
+ $annot->{DATA}->{Rect}=[$mark->{xpos},$mark->{ypos}-$mark->{rsb},$endx,$mark->{ypos}-$mark->{rst}];
+ FixRect($annot->{DATA}->{Rect}); # Y origin to ll
+ push(@{$cpage->{Annots}},$annotno);
+}
+
+sub sgn
+{
+ return(1) if $_[0] > 0;
+ return(-1) if $_[0] < 0;
+ return(0);
+}
+
+sub FixRect
+{
+ my $rect=shift;
+
+ return if !defined($rect);
+ $rect->[1]=GraphY($rect->[1]);
+ $rect->[3]=GraphY($rect->[3]);
+}
+
+sub GetPoints
+{
+ my $val=shift;
+
+ $val=ToPoints($1,$2) if ($val=~m/(-?[\d.]+)([cipn])/);
+
+ return $val;
+}
+
+# Although the PDF reference mentions XObject/Form as a way of incorporating an external PDF page into
+# the current PDF, it seems not to work with any current PDF reader (although I am told (by Leonard Rosenthol,
+# who helped author the PDF ISO standard) that Acroread 9 does support it, empiorical observation shows otherwise!!).
+# So... do it the hard way - full PDF parser and merge required objects!!!
+
+# sub BuildRef
+# {
+# my $fil=shift;
+# my $bbox=shift;
+# my $mat=shift;
+# my $wid=($bbox->[2]-$bbox->[0])*$mat->[0];
+# my $hgt=($bbox->[3]-$bbox->[1])*$mat->[3];
+#
+# if (!open(PDF,"<$fil"))
+# {
+# Msg(0,"Failed to open '$fil'");
+# return(undef);
+# }
+#
+# my (@f)=(<PDF>);
+#
+# close(PDF);
+#
+# $objct++;
+# my $xonm="XO$objct";
+#
+# $pages->{'Resources'}->{'XObject'}->{$xonm}=BuildObj($objct,{'Type' => '/XObject',
+# 'Subtype' => '/Form',
+# 'BBox' => $bbox,
+# 'Matrix' => $mat,
+# 'Resources' => $pages->{'Resources'},
+# 'Ref' => {'Page' => '1',
+# 'F' => BuildObj($objct+1,{'Type' => '/Filespec',
+# 'F' => "($fil)",
+# 'EF' => {'F' => BuildObj($objct+2,{'Type' => '/EmbeddedFile'})}
+# })
+# }
+# });
+#
+# $obj[$objct]->{STREAM}="q 1 0 0 1 0 0 cm
+# q BT
+# 1 0 0 1 0 0 Tm
+# .5 g .5 G
+# /F5 20 Tf
+# (Proxy) Tj
+# ET Q
+# 0 0 m 72 0 l s
+# Q\n";
+#
+# # $obj[$objct]->{STREAM}=PutXY($xpos,$ypos)." m ".PutXY($xpos+$wid,$ypos)." l ".PutXY($xpos+$wid,$ypos+$hgt)." l ".PutXY($xpos,$ypos+$hgt)." l f\n";
+# $obj[$objct+2]->{STREAM}=join('',@f);
+# PutObj($objct);
+# PutObj($objct+1);
+# PutObj($objct+2);
+# $objct+=2;
+# return($xonm);
+# }
+
+sub LoadSWF
+{
+ my $fil=shift;
+ my $bbox=shift;
+ my $mat=shift;
+ my $wid=($bbox->[2]-$bbox->[0])*$mat->[0];
+ my $hgt=($bbox->[3]-$bbox->[1])*$mat->[3];
+ my (@path)=split('/',$fil);
+ my $node=pop(@path);
+
+ if (!open(PDF,"<$fil"))
+ {
+ Msg(0,"Failed to open '$fil'");
+ return(undef);
+ }
+
+ my (@f)=(<PDF>);
+
+ close(PDF);
+
+ $objct++;
+ my $xonm="XO$objct";
+
+ $pages->{'Resources'}->{'XObject'}->{$xonm}=BuildObj($objct,{'Type' => '/XObject', 'BBox' => $bbox, 'Matrix' => $mat, 'FormType' => 1, 'Subtype' => '/Form', 'Length' => 0, 'Type' => "/XObject"});
+ $obj[$objct]->{STREAM}='';
+ PutObj($objct);
+ $objct++;
+ my $asset=BuildObj($objct,{'EF' => {'F' => BuildObj($objct+1,{})},
+ 'F' => "($node)",
+ 'Type' => '/Filespec',
+ 'UF' => "($node)"});
+
+ PutObj($objct);
+ $objct++;
+ $obj[$objct]->{STREAM}=join('',@f);
+ PutObj($objct);
+ $objct++;
+ my $config=BuildObj($objct,{'Instances' => [BuildObj($objct+1,{'Params' => { 'Binding' => '/Background'}, 'Asset' => $asset})],
+ 'Subtype' => '/Flash'});
+
+ PutObj($objct);
+ $objct++;
+ PutObj($objct);
+ $objct++;
+
+ my ($x,$y)=split(' ',PutXY($xpos,$ypos));
+
+ push(@{$cpage->{Annots}},BuildObj($objct,{'RichMediaContent' => {'Subtype' => '/Flash', 'Configurations' => [$config], 'Assets' => {'Names' => [ "($node)", $asset ] }},
+ 'P' => "$cpageno 0 R",
+ 'RichMediaSettings' => { 'Deactivation' => { 'Condition' => '/PI',
+ 'Type' => '/RichMediaDeactivation'},
+ 'Activation' => { 'Condition' => '/PV',
+ 'Type' => '/RichMediaActivation'}},
+ 'F' => 68,
+ 'Subtype' => '/RichMedia',
+ 'Type' => '/Annot',
+ 'Rect' => "[ $x $y ".($x+$wid)." ".($y+$hgt)." ]",
+ 'Border' => [0,0,0]}));
+
+ PutObj($objct);
+
+ return $xonm;
+}
+
+sub LoadPDF
+{
+ my $pdfnm=shift;
+ my $wid=shift;
+ my $hgt=shift;
+ my $type=shift;
+ my $mat=[1,0,0,1,0,0];
+ my $pdf;
+ my $pdftxt='';
+ my $strmlen=0;
+ my $curobj=-1;
+ my $instream=0;
+ my $cont;
+
+ if (!open(PD,"<$pdfnm"))
+ {
+ Msg(0,"Failed to open PDF '$pdfnm'");
+ return undef;
+ }
+
+ my $hdr=<PD>;
+
+ $/="\r" if (length($hdr) > 10);
+
+ while (<PD>)
+ {
+ chomp;
+
+ s/\n//;
+
+ if (m/endstream(\s+.*)?$/)
+ {
+ $instream=0;
+ $_="endstream";
+ $_.=$1 if defined($1)
+ }
+
+ next if $instream;
+
+ if (m'/Length\s+(\d+)(\s+\d+\s+R)?')
+ {
+ if (!defined($2))
+ {
+ $strmlen=$1;
+ }
+ else
+ {
+ $strmlen=0;
+ }
+ }
+
+ if (m'^(\d+) \d+ obj')
+ {
+ $curobj=$1;
+ $pdf->[$curobj]->{OBJ}=undef;
+ }
+
+ if (m'stream\s*$' and ! m/^endstream/)
+ {
+ if ($curobj > -1)
+ {
+ $pdf->[$curobj]->{STREAMPOS}=[tell(PD),$strmlen];
+ seek(PD,$strmlen,1);
+ $instream=1;
+ }
+ else
+ {
+ Msg(0,"Parsing PDF '$pdfnm' failed");
+ return undef;
+ }
+ }
+
+ $pdftxt.=$_.' ';
+ }
+
+ close(PD);
+
+ open(PD,"<$pdfnm");
+# $pdftxt=~s/\]/ \]/g;
+ my (@pdfwds)=split(' ',$pdftxt);
+ my $wd;
+
+ while ($wd=nextwd(\@pdfwds),length($wd))
+ {
+ if ($wd=~m/\d+/ and defined($pdfwds[1]) and $pdfwds[1]=~m/^obj(.*)/)
+ {
+ $curobj=$wd;
+ shift(@pdfwds); shift(@pdfwds);
+ unshift(@pdfwds,$1) if defined($1) and length($1);
+ $pdf->[$curobj]->{OBJ}=ParsePDFObj(\@pdfwds);
+ }
+ elsif ($wd eq 'trailer' and !exists($pdf->[0]->{OBJ}))
+ {
+ $pdf->[0]->{OBJ}=ParsePDFObj(\@pdfwds);
+ }
+ else
+ {
+# print "Skip '$wd'\n";
+ }
+ }
+
+ my $catalog=${$pdf->[0]->{OBJ}->{Root}};
+ my $page=FindPage(1,$pdf);
+ my $xobj=++$objct;
+
+ # Load the streamas
+
+ foreach my $o (@{$pdf})
+ {
+ if (exists($o->{STREAMPOS}))
+ {
+ my $l;
+
+ $l=$o->{OBJ}->{Length} if exists($o->{OBJ}->{Length});
+
+ $l=$pdf->[$$l]->{OBJ} if (defined($l) && ref($l) eq 'OBJREF');
+
+ Msg(1,"Unable to determine length of stream \@$o->{STREAMPOS}->[0]") if !defined($l);
+
+ sysseek(PD,$o->{STREAMPOS}->[0],0);
+ Msg(0,'Failed to read all the stream') if $l != sysread(PD,$o->{STREAM},$l);
+
+ if (exists($o->{OBJ}->{'Filter'}) and $o->{OBJ}->{'Filter'} eq '/FlateDecode')
+ {
+ $o->{STREAM}=Compress::Zlib::uncompress($o->{STREAM});
+ delete($o->{OBJ }->{'Filter'});
+ }
+ }
+ }
+
+ close(PD);
+
+ # Find BBox
+ my $BBox;
+ my $insmap={};
+
+ foreach my $k (qw( MediaBox ArtBox TrimBox BleedBox CropBox ))
+ {
+ $BBox=FindKey($pdf,$page,$k);
+ last if $BBox;
+ }
+
+ $BBox=[0,0,595,842] if !defined($BBox);
+
+ my $xscale=$wid/($BBox->[2]-$BBox->[0]+1);
+ my $yscale=($hgt<=0)?$xscale:($hgt/($BBox->[3]-$BBox->[1]+1));
+ $hgt=($BBox->[3]-$BBox->[1]+1)*$yscale;
+
+ if ($type eq "import")
+ {
+ $mat->[0]=$xscale;
+ $mat->[3]=$yscale;
+ }
+
+ # Find Resource
+
+ my $res=FindKey($pdf,$page,'Resources');
+ my $xonm="XO$xobj";
+
+ # Map inserted objects to current PDF
+
+ MapInsValue($pdf,$page,'',$insmap,$xobj,$pdf->[$page]->{OBJ});
+#
+# Many PDFs include 'Resources' at the 'Page' level but if 'Resources' is held at a higher level (i.e 'Pages')
+# then we need to include its objects as well.
+#
+ MapInsValue($pdf,$page,'',$insmap,$xobj,$res) if !exists($pdf->[$page]->{OBJ}->{Resources});
+
+ # Copy Resources
+
+ my %incres=%{$res};
+
+ $incres{ProcSet}=['/PDF', '/Text', '/ImageB', '/ImageC', '/ImageI'];
+
+ ($mat->[4],$mat->[5])=split(' ',PutXY($xpos,$ypos));
+ $pages->{'Resources'}->{'XObject'}->{$xonm}=BuildObj($xobj,{'Type' => '/XObject', 'BBox' => $BBox, 'Name' => "/$xonm", 'FormType' => 1, 'Subtype' => '/Form', 'Length' => 0, 'Type' => "/XObject", 'Resources' => \%incres});
+
+ BuildStream($xobj,$pdf,$pdf->[$page]->{OBJ}->{Contents});
+
+ return([$xonm,$BBox] );
+}
+
+sub BuildStream
+{
+ my $xobj=shift;
+ my $pdf=shift;
+ my $val=shift;
+ my $strm='';
+ my $objs;
+ my $refval=ref($val);
+
+ if ($refval eq 'OBJREF')
+ {
+ push(@{$objs}, $val);
+ }
+ elsif ($refval eq 'ARRAY')
+ {
+ $objs=$val;
+ }
+ else
+ {
+ Msg(0,"unexpected 'Contents'");
+ }
+
+ foreach my $o (@{$objs})
+ {
+ $strm.="\n" if $strm;
+ $strm.=$pdf->[$$o]->{STREAM} if exists($pdf->[$$o]->{STREAM});
+ }
+
+ $obj[$xobj]->{STREAM}=$strm;
+}
+
+
+sub MapInsHash
+{
+ my $pdf=shift;
+ my $o=shift;
+ my $insmap=shift;
+ my $parent=shift;
+ my $val=shift;
+
+
+ foreach my $k (keys(%{$val}))
+ {
+ MapInsValue($pdf,$o,$k,$insmap,$parent,$val->{$k}) if $k ne 'Contents';
+ }
+}
+
+sub MapInsValue
+{
+ my $pdf=shift;
+ my $o=shift;
+ my $k=shift;
+ my $insmap=shift;
+ my $parent=shift;
+ my $val=shift;
+ my $refval=ref($val);
+
+ if ($refval eq 'OBJREF')
+ {
+ if ($k ne 'Parent')
+ {
+ if (!exists($insmap->{IMP}->{$$val}))
+ {
+ $objct++;
+ $insmap->{CUR}->{$objct}=$$val;
+ $insmap->{IMP}->{$$val}=$objct;
+ $obj[$objct]->{DATA}=$pdf->[$$val]->{OBJ};
+ $obj[$objct]->{STREAM}=$pdf->[$$val]->{STREAM} if exists($pdf->[$$val]->{STREAM});
+ MapInsValue($pdf,$$val,'',$insmap,$o,$pdf->[$$val]->{OBJ});
+ }
+
+ $$val=$insmap->{IMP}->{$$val};
+ }
+ else
+ {
+ $$val=$parent;
+ }
+ }
+ elsif ($refval eq 'ARRAY')
+ {
+ foreach my $v (@{$val})
+ {
+ MapInsValue($pdf,$o,'',$insmap,$parent,$v)
+ }
+ }
+ elsif ($refval eq 'HASH')
+ {
+ MapInsHash($pdf,$o,$insmap,$parent,$val);
+ }
+
+}
+
+sub FindKey
+{
+ my $pdf=shift;
+ my $page=shift;
+ my $k=shift;
+
+ if (exists($pdf->[$page]->{OBJ}->{$k}))
+ {
+ my $val=$pdf->[$page]->{OBJ}->{$k};
+ $val=$pdf->[$$val]->{OBJ} if ref($val) eq 'OBJREF';
+ return($val);
+ }
+ else
+ {
+ if (exists($pdf->[$page]->{OBJ}->{Parent}))
+ {
+ return(FindKey($pdf,${$pdf->[$page]->{OBJ}->{Parent}},$k));
+ }
+ }
+
+ return(undef);
+}
+
+sub FindPage
+{
+ my $wantpg=shift;
+ my $pdf=shift;
+ my $catalog=${$pdf->[0]->{OBJ}->{Root}};
+ my $pages=${$pdf->[$catalog]->{OBJ}->{Pages}};
+
+ return(NextPage($pdf,$pages,\$wantpg));
+}
+
+sub NextPage
+{
+ my $pdf=shift;
+ my $pages=shift;
+ my $wantpg=shift;
+ my $ret;
+
+ if ($pdf->[$pages]->{OBJ}->{Type} eq '/Pages')
+ {
+ foreach my $kid (@{$pdf->[$pages]->{OBJ}->{Kids}})
+ {
+ $ret=NextPage($pdf,$$kid,$wantpg);
+ last if $$wantpg<=0;
+ }
+ }
+ elsif ($pdf->[$pages]->{OBJ}->{Type} eq '/Page')
+ {
+ $$wantpg--;
+ $ret=$pages;
+ }
+
+ return($ret);
+}
+
+sub nextwd
+{
+ my $pdfwds=shift;
+
+ my $wd=shift(@{$pdfwds});
+
+ return('') if !defined($wd);
+
+ if ($wd=~m/^(.*?)(<<|>>|\[|\])(.*)/)
+ {
+ if (defined($1) and length($1))
+ {
+ unshift(@{$pdfwds},$3) if defined($3) and length($3);
+ unshift(@{$pdfwds},$2);
+ $wd=$1;
+ }
+ else
+ {
+ unshift(@{$pdfwds},$3) if defined($3) and length($3);
+ $wd=$2;
+ }
+ }
+
+ return($wd);
+}
+
+sub ParsePDFObj
+{
+
+ my $pdfwds=shift;
+ my $rtn;
+ my $wd;
+
+ while ($wd=nextwd($pdfwds),length($wd))
+ {
+ if ($wd eq 'stream' or $wd eq 'endstream')
+ {
+ next;
+ }
+ elsif ($wd eq 'endobj' or $wd eq 'startxref')
+ {
+ last;
+ }
+ else
+ {
+ unshift(@{$pdfwds},$wd);
+ $rtn=ParsePDFValue($pdfwds);
+ }
+ }
+
+ return($rtn);
+}
+
+sub ParsePDFHash
+{
+ my $pdfwds=shift;
+ my $rtn={};
+ my $wd;
+
+ while ($wd=nextwd($pdfwds),length($wd))
+ {
+ if ($wd eq '>>')
+ {
+ last;
+ }
+
+ my (@w)=split('/',$wd,3);
+
+ if ($w[0])
+ {
+ Msg(0,"PDF Dict Key '$wd' does not start with '/'");
+ exit 1;
+ }
+ else
+ {
+ unshift(@{$pdfwds},"/$w[2]") if $w[2];
+ $wd=$w[1];
+ (@w)=split('\(',$wd,2);
+ $wd=$w[0];
+ unshift(@{$pdfwds},"($w[1]") if defined($w[1]);
+ (@w)=split('\<',$wd,2);
+ $wd=$w[0];
+ unshift(@{$pdfwds},"<$w[1]") if defined($w[1]);
+
+ $rtn->{$wd}=ParsePDFValue($pdfwds);
+ }
+ }
+
+ return($rtn);
+}
+
+sub ParsePDFValue
+{
+ my $pdfwds=shift;
+ my $rtn;
+ my $wd=nextwd($pdfwds);
+
+ if ($wd=~m/^\d+$/ and $pdfwds->[0]=~m/^\d+$/ and $pdfwds->[1]=~m/^R(\]|\>|\/)?/)
+ {
+ shift(@{$pdfwds});
+ if (defined($1) and length($1))
+ {
+ $pdfwds->[0]=substr($pdfwds->[0],1);
+ }
+ else
+ {
+ shift(@{$pdfwds});
+ }
+ return(bless(\$wd,'OBJREF'));
+ }
+
+ if ($wd eq '<<')
+ {
+ return(ParsePDFHash($pdfwds));
+ }
+
+ if ($wd eq '[')
+ {
+ return(ParsePDFArray($pdfwds));
+ }
+
+ if ($wd=~m/(.*?)(\(.*)$/)
+ {
+ if (defined($1) and length($1))
+ {
+ unshift(@{$pdfwds},$2);
+ $wd=$1;
+ }
+ else
+ {
+ return(ParsePDFString($wd,$pdfwds));
+ }
+ }
+
+ if ($wd=~m/(.*?)(\<.*)$/)
+ {
+ if (defined($1) and length($1))
+ {
+ unshift(@{$pdfwds},$2);
+ $wd=$1;
+ }
+ else
+ {
+ return(ParsePDFHexString($wd,$pdfwds));
+ }
+ }
+
+ if ($wd=~m/(.+?)(\/.*)$/)
+ {
+ if (defined($2) and length($2))
+ {
+ unshift(@{$pdfwds},$2);
+ $wd=$1;
+ }
+ }
+
+ return($wd);
+}
+
+sub ParsePDFString
+{
+ my $wd=shift;
+ my $rtn='';
+ my $pdfwds=shift;
+ my $lev=0;
+
+ while (length($wd))
+ {
+ $rtn.=' ' if length($rtn);
+
+ while ($wd=~m/(?<!\\)\(/g) {$lev++;}
+ while ($wd=~m/(?<!\\)\)/g) {$lev--;}
+
+
+ if ($lev<=0 and $wd=~m/^(.*?\))([^)]+)$/)
+ {
+ unshift(@{$pdfwds},$2) if defined($2) and length($2);
+ $wd=$1;
+ }
+
+ $rtn.=$wd;
+
+ last if $lev <= 0;
+
+ $wd=nextwd($pdfwds);
+ }
+
+ return($rtn);
+}
+
+sub ParsePDFHexString
+{
+ my $wd=shift;
+ my $rtn='';
+ my $pdfwds=shift;
+ my $lev=0;
+
+ if ($wd=~m/^(<.+?>)(.*)/)
+ {
+ unshift(@{$pdfwds},$2) if defined($2) and length($2);
+ $rtn=$1;
+ }
+
+ return($rtn);
+}
+
+sub ParsePDFArray
+{
+ my $pdfwds=shift;
+ my $rtn=[];
+ my $wd;
+
+ while (1)
+ {
+ $wd=ParsePDFValue($pdfwds);
+ last if $wd eq ']' or length($wd)==0;
+ push(@{$rtn},$wd);
+ }
+
+ return($rtn);
+}
+
+sub Msg
+{
+ my ($lev,$msg)=@_;
+
+ print STDERR "$env{SourceFile}: " if exists($env{SourceFile});
+ print STDERR "$msg\n";
+ exit 1 if $lev;
+}
+
+sub PutXY
+{
+ my ($x,$y)=(@_);
+
+ if ($frot)
+ {
+ return("$y $x");
+ }
+ else
+ {
+ $y=$mediabox[3]-$y;
+ return("$x $y");
+ }
+}
+
+sub GraphY
+{
+ my $y=shift;
+
+ if ($frot)
+ {
+ return($y);
+ }
+ else
+ {
+ return($mediabox[3]-$y);
+ }
+}
+
+sub Put
+{
+ my $msg=shift;
+
+ print $msg;
+ $fct+=length($msg);
+}
+
+sub PutObj
+{
+ my $ono=shift;
+ my $msg="$ono 0 obj ";
+ $obj[$ono]->{XREF}=$fct;
+ if (exists($obj[$ono]->{STREAM}))
+ {
+ if (!$debug)
+ {
+ $obj[$ono]->{STREAM}=Compress::Zlib::compress($obj[$ono]->{STREAM});
+ $obj[$ono]->{DATA}->{'Filter'}=['/FlateDecode'];
+ }
+
+ $obj[$ono]->{DATA}->{'Length'}=length($obj[$ono]->{STREAM});
+ }
+ PutField(\$msg,$obj[$ono]->{DATA});
+ PutStream(\$msg,$ono) if exists($obj[$ono]->{STREAM});
+ Put($msg."endobj\n");
+}
+
+sub PutStream
+{
+ my $msg=shift;
+ my $ono=shift;
+
+ # We could 'flate' here
+ $$msg.="stream\n$obj[$ono]->{STREAM}endstream\n";
+}
+
+sub PutField
+{
+ my $pmsg=shift;
+ my $fld=shift;
+ my $term=shift||"\n";
+ my $typ=ref($fld);
+
+ if ($typ eq '')
+ {
+ $$pmsg.="$fld$term";
+ }
+ elsif ($typ eq 'ARRAY')
+ {
+ $$pmsg.='[';
+ foreach my $cell (@{$fld})
+ {
+ PutField($pmsg,$cell,' ');
+ }
+ $$pmsg.="]$term";
+ }
+ elsif ($typ eq 'HASH')
+ {
+ $$pmsg.='<< ';
+ foreach my $key (sort keys %{$fld})
+ {
+ $$pmsg.="/$key ";
+ PutField($pmsg,$fld->{$key});
+ }
+ $$pmsg.=">>$term";
+ }
+ elsif ($typ eq 'OBJREF')
+ {
+ $$pmsg.="$$fld 0 R$term";
+ }
+}
+
+sub BuildObj
+{
+ my $ono=shift;
+ my $val=shift;
+
+ $obj[$ono]->{DATA}=$val;
+
+ return("$ono 0 R ");
+}
+
+sub LoadFont
+{
+ my $fontno=shift;
+ my $fontnm=shift;
+ my $ofontnm=$fontnm;
+
+ return $fontlst{$fontno}->{OBJ} if (exists($fontlst{$fontno}));
+
+ my $f;
+ OpenFile(\$f,$fontdir,"$fontnm");
+
+ if (!defined($f) and $Foundry)
+ {
+ # Try with no foundry
+ $fontnm=~s/.*?-//;
+ OpenFile(\$f,$fontdir,$fontnm);
+ }
+
+ Msg(1,"Failed to open font '$ofontnm'") if !defined($f);
+
+ my $foundry='';
+ $foundry=$1 if $fontnm=~m/^(.*?)-/;
+ my $stg=1;
+ my %fnt;
+ my @fntbbox=(0,0,0,0);
+ my $capheight=0;
+ my $lastchr=0;
+ my $t1flags=0;
+ my $fixwid=-1;
+ my $ascent=0;
+ my $charset='';
+
+ while (<$f>)
+ {
+ chomp;
+
+ s/^ +//;
+ s/^#.*// if $stg == 1;
+ next if $_ eq '';
+
+ if ($stg == 1)
+ {
+ my ($key,$val)=split(' ',$_,2);
+
+ $key=lc($key);
+ $stg=2,next if $key eq 'kernpairs';
+ $stg=3,next if lc($_) eq 'charset';
+
+ $fnt{$key}=$val
+ }
+ elsif ($stg == 2)
+ {
+ $stg=3,next if lc($_) eq 'charset';
+
+ my ($ch1,$ch2,$k)=split;
+ $fnt{KERN}->{$ch1}->{$ch2}=$k;
+ }
+ else
+ {
+ my (@r)=split;
+ my (@p)=split(',',$r[1]);
+
+ if ($r[1] eq '"')
+ {
+ $fnt{GNM}->{$r[0]}=$lastchr;
+ next;
+ }
+
+ $r[0]='u0020' if $r[3] == 32;
+ next if $r[3] >255;
+ $fnt{GNM}->{$r[0]}=$r[3];
+ $fnt{GNO}->[$r[3]]='/'.$r[4];
+ $fnt{WID}->[$r[3]]=$p[0];
+ $lastchr=$r[3] if $r[3] > $lastchr;
+ $fixwid=$p[0] if $fixwid == -1;
+ $fixwid=-2 if $fixwid > 0 and $p[0] != $fixwid;
+
+ $fntbbox[1]=-$p[2] if defined($p[2]) and -$p[2] < $fntbbox[1];
+ $fntbbox[2]=$p[0] if $p[0] > $fntbbox[2];
+ $fntbbox[3]=$p[1] if defined($p[1]) and $p[1] > $fntbbox[3];
+ $ascent=$p[1] if defined($p[1]) and $p[1] > $ascent and $r[3] >= 32 and $r[3] < 128;
+ $charset.='/'.$r[4] if defined($r[4]);
+ $capheight=$p[1] if length($r[4]) == 1 and $r[4] ge 'A' and $r[4] le 'Z' and $p[1] > $capheight;
+ }
+ }
+
+ close($f);
+
+ unshift(@{$fnt{GNO}},0);
+
+ foreach my $glyph (@{$fnt{GNO}})
+ {
+ $glyph='/.notdef' if !defined($glyph);
+ }
+
+ foreach my $w (@{$fnt{WID}})
+ {
+ $w=0 if !defined($w);
+ }
+
+ my $fno=0;
+ my $slant=0;
+ $slant=-$fnt{'slant'} if exists($fnt{'slant'});
+ $fnt{'spacewidth'}=700 if !exists($fnt{'spacewidth'});
+
+ $t1flags|=2**0 if $fixwid > -1;
+ $t1flags|=(exists($fnt{'special'}))?2**2:2**5;
+ $t1flags|=2**6 if $slant != 0;
+ my $fontkey="$foundry $fnt{internalname}";
+
+ if (exists($download{$fontkey}))
+ {
+ # Not a Base Font
+ my ($l1,$l2,$l3,$t1stream)=GetType1($download{$fontkey});
+ Msg(0,"Incorrect font format for '$fontkey' ($l1)") if !defined($t1stream);
+ $fno=++$objct;
+ $fontlst{$fontno}->{OBJ}=BuildObj($objct,
+ {'Type' => '/Font',
+ 'Subtype' => '/Type1',
+ 'BaseFont' => '/'.$fnt{internalname},
+ 'Widths' => $fnt{WID},
+ 'FirstChar' => 0,
+ 'LastChar' => $lastchr,
+ 'Encoding' => BuildObj($objct+1,
+ {'Type' => '/Encoding',
+ 'Differences' => $fnt{GNO}
+ }
+ ),
+ 'FontDescriptor' => BuildObj($objct+2,
+ {'Type' => '/FontDescriptor',
+ 'FontName' => '/'.$fnt{internalname},
+ 'Flags' => $t1flags,
+ 'FontBBox' => \@fntbbox,
+ 'ItalicAngle' => $slant,
+ 'Ascent' => $ascent,
+ 'Descent' => $fntbbox[1],
+ 'CapHeight' => $capheight,
+ 'StemV' => 0,
+ 'CharSet' => "($charset)",
+ 'FontFile' => BuildObj($objct+3,
+ {'Length1' => $l1,
+ 'Length2' => $l2,
+ 'Length3' => $l3
+ }
+ )
+ }
+ )
+ }
+ );
+
+ $objct+=3;
+ $fontlst{$fontno}->{NM}='/F'.$fontno;
+ $pages->{'Resources'}->{'Font'}->{'F'.$fontno}=$fontlst{$fontno}->{OBJ};
+ $fontlst{$fontno}->{FNT}=\%fnt;
+ $obj[$objct]->{STREAM}=$t1stream;
+
+ }
+ else
+ {
+ $fno=++$objct;
+ $fontlst{$fontno}->{OBJ}=BuildObj($objct,
+ {'Type' => '/Font',
+ 'Subtype' => '/Type1',
+ 'BaseFont' => '/'.$fnt{internalname},
+ 'Encoding' => BuildObj($objct+1,
+ {'Type' => '/Encoding',
+ 'Differences' => $fnt{GNO}
+ }
+ )
+ }
+ );
+ $objct+=1;
+ $fontlst{$fontno}->{NM}='/F'.$fontno;
+ $pages->{'Resources'}->{'Font'}->{'F'.$fontno}=$fontlst{$fontno}->{OBJ};
+ $fontlst{$fontno}->{FNT}=\%fnt;
+ }
+
+ PutObj($fno);
+ PutObj($fno+1);
+ PutObj($fno+2) if defined($obj[$fno+2]);
+ PutObj($fno+3) if defined($obj[$fno+3]);
+}
+
+sub GetType1
+{
+ my $file=shift;
+ my ($l1,$l2,$l3); # Return lengths
+ my ($head,$body,$tail); # Font contents
+ my $f;
+
+ OpenFile(\$f,$fontdir,"$file");
+ Msg(1,"Failed to open '$file'") if !defined($f);
+
+ my $l=<$f>;
+
+ if (substr($l,0,1) eq "\x80")
+ {
+ # PFB file
+ sysseek($f,0,0);
+ my $hdr='';
+ $l1=$l2=$l3=0;
+ my $typ=0;
+ my $data='';
+ my $sl=0;
+
+ while ($typ != 3)
+ {
+ my $chk=sysread($f,$hdr,6);
+
+ if ($chk < 2)
+ {
+ # eof($f) uses buffered i/o (since file was open not sysopen)
+ # which screws up next sysread. So this will terminate loop if font
+ # has no terminating section type 3.
+ last if $l3;
+ return(5,$l2,$l3,undef);
+ }
+
+ $typ=ord(substr($hdr,1,1));
+
+ if ($chk == 6)
+ {
+ $sl=unpack('L',substr($hdr,2,4));
+ $chk=sysread($f,$data,$sl);
+ return(1,$l2,$l3,undef) if $chk != $sl;
+ }
+
+ if ($typ == 1)
+ {
+ if ($l2 == 0)
+ {
+ # First text bit(s) must be head
+ $head.=$data;
+ $l1+=$sl;
+ }
+ else
+ {
+ # A text bit after the binary sections must be tail
+ $tail.=$data;
+ $l3+=$sl;
+ }
+ }
+ elsif ($typ == 2)
+ {
+ return(2,$l2,$l3,undef) if $l3; # Found a binary bit after the tail
+ $body.=$data;
+ $l2+=$sl;
+ }
+ elsif ($typ != 3)
+ {
+ # What segment type is this!
+ return(3,$l2,$l3,undef);
+ }
+ }
+
+ close($f);
+ return($l1,$l2,$l3,"$head$body$tail");
+ }
+
+ my (@lines)=(<$f>);
+ unshift(@lines,$l);
+
+ close($f);
+
+ Msg(1,"Font file '$file' must be an Adobe type 1 font file") if $lines[0]!~m/\%\!PS.Adobe/i;
+ $head=$body=$tail='';
+
+ foreach my $line (@lines)
+ {
+ if (!defined($l1))
+ {
+ if (length($line) > 19 and $line=~s/^(currentfile eexec)//)
+ {
+ $head.=$1;
+ $l1=length($head);
+ redo;
+ }
+
+ $head.=$line;
+
+ if ($line=~m/eexec$/)
+ {
+ # chomp($head);
+ # $head.="\x0d";
+ $l1=length($head);
+ }
+ }
+ elsif (!defined($l2))
+ {
+ #$line=~s/(\0\0)0+$/&1/;
+ if ($line=~m/^0+$/)
+ {
+ $l2=length($body);
+ $tail=$line;
+ }
+ else
+ {
+ chomp($line);
+ $body.=pack('H*',$line);
+ }
+ }
+ else
+ {
+ $tail.=$line;
+ }
+ }
+
+ $l1=length($head);
+ $l2=length($body);
+ $l3=length($tail);
+
+ return($l1,$l2,$l3,"$head$body$tail");
+}
+
+
+sub OutStream
+{
+ my $ono=shift;
+
+ IsGraphic();
+ $stream.="Q\n";
+ $obj[$ono]->{STREAM}=$stream;
+ $obj[$ono]->{DATA}->{Length}=length($stream);
+ $stream='';
+ PutObj($ono);
+}
+
+sub do_p
+{
+ # Start of pages
+
+ if ($cpageno > 0)
+ {
+ PutObj($cpageno);
+ OutStream($cpageno+1);
+ }
+
+ $cpageno=++$objct;
+
+ push(@{$pages->{Kids}},BuildObj($objct,
+ {'Type' => '/Page',
+ 'Group' => {'CS' => '/DeviceRGB', 'S' => '/Transparency'},
+ 'Parent' => '2 0 R',
+ 'Contents' => [ BuildObj($objct+1,
+ {'Length' => 0}
+ ) ],
+ }
+ )
+ );
+ $objct+=1;
+ $cpage=$obj[$cpageno]->{DATA};
+ $pages->{'Count'}++;
+ $stream="q 1 0 0 1 0 0 cm\n";
+ $mode='g';
+ $curfill='';
+ @mediabox=@defaultmb;
+}
+
+sub do_f
+{
+ my $par=shift;
+
+# IsText();
+ $cft="$par";
+ $fontchg=1;
+# $stream.="/F$cft $cftsz Tf\n" if $cftsz;
+ $widtbl=CacheWid($par);
+ $origwidtbl=$fontlst{$par}->{FNT}->{WID};
+ $krntbl=$fontlst{$par}->{FNT}->{KERN};
+}
+
+sub CacheWid
+{
+ my $par=shift;
+
+ if (!defined($fontlst{$par}->{CACHE}->{$cftsz}))
+ {
+ $fontlst{$par}->{CACHE}->{$cftsz}=BuildCache($fontlst{$par}->{FNT}->{WID});
+ }
+
+ return($fontlst{$par}->{CACHE}->{$cftsz});
+}
+
+sub BuildCache
+{
+ my $wid=shift;
+ return([]);
+ my @cwid;
+
+ foreach my $w (@{$wid})
+ {
+ push(@cwid,$w*$cftsz);
+ }
+
+ return(\@cwid);
+}
+
+sub IsText
+{
+ if ($mode eq 'g')
+ {
+ $xpos+=$pendmv/$unitwidth;
+ $stream.="q BT\n$matrix ".PutXY($xpos,$ypos)." Tm\n";
+ $poschg=0;
+ $fontchg=0;
+ $pendmv=0;
+ $matrixchg=0;
+ $tmxpos=$xpos;
+ $stream.=$textcol."\n", $curfill=$textcol if $textcol ne $curfill;
+ if (defined($cft))
+ {
+ $whtsz=$fontlst{$cft}->{FNT}->{spacewidth}*$cftsz;
+ $stream.="/F$cft $cftsz Tf\n";
+ }
+ }
+
+ if ($poschg or $matrixchg)
+ {
+ PutLine(0) if $matrixchg;
+ $stream.="$matrix ".PutXY($xpos,$ypos)." Tm\n", $poschg=0;
+ $tmxpos=$xpos;
+ $matrixchg=0;
+ }
+
+ if ($fontchg)
+ {
+ PutLine(0);
+ $whtsz=$fontlst{$cft}->{FNT}->{spacewidth}*$cftsz;
+ $stream.="/F$cft $cftsz Tf\n" if $cftsz and defined($cft);
+ $fontchg=0;
+ }
+
+ $mode='t';
+}
+
+sub IsGraphic
+{
+ if ($mode eq 't')
+ {
+ PutLine();
+ $stream.="ET Q\n";
+ $xpos+=($pendmv-$nomove)/$unitwidth;
+ $pendmv=0;
+ $nomove=0;
+ $stream.=$strkcol."\n", $curstrk=$strkcol if $strkcol ne $curstrk;
+ $curfill=$fillcol;
+ }
+ $mode='g';
+}
+
+sub do_s
+{
+ my $par=shift;
+ $par/=$unitwidth;
+
+ if ($par != $cftsz and defined($cft))
+ {
+ PutLine();
+ $cftsz=$par;
+# $stream.="/F$cft $cftsz Tf\n";
+ $fontchg=1;
+ $widtbl=CacheWid($cft);
+ }
+}
+
+sub do_m
+{
+ # Groff uses /m[] for text & graphic stroke, and /M[] (DF?) for graphic fill.
+ # PDF uses G/RG/K for graphic stroke, and g/rg/k for text & graphic fill.
+ #
+ # This means that we must maintain g/rg/k state separately for text colour & graphic fill (this is
+ # probably why 'gs' maintains seperate graphic states for text & graphics when distilling PS -> PDF).
+ #
+ # To facilitate this:-
+ #
+ # $textcol = current groff stroke colour
+ # $fillcol = current groff fill colour
+ # $curfill = current PDF fill colour
+
+ my $par=shift;
+ my $mcmd=substr($par,0,1);
+
+ $par=substr($par,1);
+ $par=~s/^ +//;
+
+# IsGraphic();
+
+ $textcol=set_col($mcmd,$par,0);
+ $strkcol=set_col($mcmd,$par,1);
+
+ if ($mode eq 't')
+ {
+ PutLine();
+ $stream.=$textcol."\n";
+ $curfill=$textcol;
+ }
+ else
+ {
+ $stream.="$strkcol\n";
+ $curstrk=$strkcol;
+ }
+}
+
+sub set_col
+{
+ my $mcmd=shift;
+ my $par=shift;
+ my $upper=shift;
+ my @oper=('g','k','rg');
+
+ @oper=('G','K','RG') if $upper;
+
+ if ($mcmd eq 'd')
+ {
+ # default colour
+ return("0 $oper[0]");
+ }
+
+ my (@c)=split(' ',$par);
+
+ if ($mcmd eq 'c')
+ {
+ # Text CMY
+ return(($c[0]/65535).' '.($c[1]/65535).' '.($c[2]/65535)." 0 $oper[1]");
+ }
+ elsif ($mcmd eq 'k')
+ {
+ # Text CMYK
+ return(($c[0]/65535).' '.($c[1]/65535).' '.($c[2]/65535).' '.($c[3]/65535)." $oper[1]");
+ }
+ elsif ($mcmd eq 'g')
+ {
+ # Text Grey
+ return(($c[0]/65535)." $oper[0]");
+ }
+ elsif ($mcmd eq 'r')
+ {
+ # Text RGB0
+ return(($c[0]/65535).' '.($c[1]/65535).' '.($c[2]/65535)." $oper[2]");
+ }
+}
+
+sub do_D
+{
+ my $par=shift;
+ my $Dcmd=substr($par,0,1);
+
+ $par=substr($par,1);
+ $xpos+=$pendmv/$unitwidth;
+ $pendmv=0;
+
+ IsGraphic();
+
+ if ($Dcmd eq 'F')
+ {
+ my $mcmd=substr($par,0,1);
+
+ $par=substr($par,1);
+ $par=~s/^ +//;
+
+ $fillcol=set_col($mcmd,$par,0);
+ $stream.="$fillcol\n";
+ $curfill=$fillcol;
+ }
+ elsif ($Dcmd eq 'f')
+ {
+ my $mcmd=substr($par,0,1);
+
+ $par=substr($par,1);
+ $par=~s/^ +//;
+ ($par)=split(' ',$par);
+
+ if ($par >= 0 and $par <= 1000)
+ {
+ $fillcol=set_col('g',int((1000-$par)*65535/1000),0);
+ }
+ else
+ {
+ $fillcol=lc($textcol);
+ }
+
+ $stream.="$fillcol\n";
+ $curfill=$fillcol;
+ }
+ elsif ($Dcmd eq '~')
+ {
+ # B-Spline
+ my (@p)=split(' ',$par);
+ my ($nxpos,$nypos);
+
+ foreach my $p (@p) { $p/=$unitwidth; }
+ $stream.=PutXY($xpos,$ypos)." m\n";
+ $xpos+=($p[0]/2);
+ $ypos+=($p[1]/2);
+ $stream.=PutXY($xpos,$ypos)." l\n";
+
+ for (my $i=0; $i < $#p-1; $i+=2)
+ {
+ $nxpos=(($p[$i]*$tnum)/(2*$tden));
+ $nypos=(($p[$i+1]*$tnum)/(2*$tden));
+ $stream.=PutXY(($xpos+$nxpos),($ypos+$nypos))." ";
+ $nxpos=($p[$i]/2 + ($p[$i+2]*($tden-$tnum))/(2*$tden));
+ $nypos=($p[$i+1]/2 + ($p[$i+3]*($tden-$tnum))/(2*$tden));
+ $stream.=PutXY(($xpos+$nxpos),($ypos+$nypos))." ";
+ $nxpos=(($p[$i]-$p[$i]/2) + $p[$i+2]/2);
+ $nypos=(($p[$i+1]-$p[$i+1]/2) + $p[$i+3]/2);
+ $stream.=PutXY(($xpos+$nxpos),($ypos+$nypos))." c\n";
+ $xpos+=$nxpos;
+ $ypos+=$nypos;
+ }
+
+ $xpos+=($p[$#p-1]-$p[$#p-1]/2);
+ $ypos+=($p[$#p]-$p[$#p]/2);
+ $stream.=PutXY($xpos,$ypos)." l\nS\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'p' or $Dcmd eq 'P')
+ {
+ # B-Spline
+ my (@p)=split(' ',$par);
+ my ($nxpos,$nypos);
+
+ foreach my $p (@p) { $p/=$unitwidth; }
+ $stream.=PutXY($xpos,$ypos)." m\n";
+
+ for (my $i=0; $i < $#p; $i+=2)
+ {
+ $xpos+=($p[$i]);
+ $ypos+=($p[$i+1]);
+ $stream.=PutXY($xpos,$ypos)." l\n";
+ }
+
+ if ($Dcmd eq 'p')
+ {
+ $stream.="s\n";
+ }
+ else
+ {
+ $stream.="f\n";
+ }
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'c')
+ {
+ # Stroke circle
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+
+ DrawCircle($p[0],$p[0]);
+ $stream.="s\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'C')
+ {
+ # Fill circle
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+
+ DrawCircle($p[0],$p[0]);
+ $stream.="f\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'e')
+ {
+ # Stroke ellipse
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+
+ DrawCircle($p[0],$p[1]);
+ $stream.="s\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'E')
+ {
+ # Fill ellipse
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+
+ DrawCircle($p[0],$p[1]);
+ $stream.="f\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'l')
+ {
+ # Line To
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+
+ foreach my $p (@p) { $p/=$unitwidth; }
+ $stream.=PutXY($xpos,$ypos)." m\n";
+ $xpos+=$p[0];
+ $ypos+=$p[1];
+ $stream.=PutXY($xpos,$ypos)." l\n";
+
+ $stream.="s\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 't')
+ {
+ # Line Thickness
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+
+ foreach my $p (@p) { $p/=$unitwidth; }
+ # $xpos+=$p[0]*100; # WTF!!!
+ #int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
+ $p[0]=(($desc{res}/(72*$desc{sizescale}))*$linewidth*$cftsz)/1000 if $p[0] < 0;
+ $lwidth=$p[0];
+ $stream.="$p[0] w\n";
+ $poschg=1;
+ }
+ elsif ($Dcmd eq 'a')
+ {
+ # Arc
+ $par=substr($par,1);
+ my (@p)=split(' ',$par);
+ my $rad180=3.14159;
+ my $rad360=$rad180*2;
+ my $rad90=$rad180/2;
+
+ foreach my $p (@p) { $p/=$unitwidth; }
+
+ # Documentation is wrong. Groff does not use Dh1,Dv1 as centre of the circle!
+
+ my $centre=adjust_arc_centre(\@p);
+
+ # Using formula here : http://www.tinaja.com/glib/bezcirc2.pdf
+ # First calculate angle between start and end point
+
+ my ($startang,$r)=RtoP(-$centre->[0],$centre->[1]);
+ my ($endang,$r2)=RtoP(($p[0]+$p[2])-$centre->[0],-($p[1]+$p[3]-$centre->[1]));
+ $endang+=$rad360 if $endang < $startang;
+ my $totang=($endang-$startang)/4; # do it in 4 pieces
+
+ # Now 1 piece
+
+ my $x0=cos($totang/2);
+ my $y0=sin($totang/2);
+ my $x3=$x0;
+ my $y3=-$y0;
+ my $x1=(4-$x0)/3;
+ my $y1=((1-$x0)*(3-$x0))/(3*$y0);
+ my $x2=$x1;
+ my $y2=-$y1;
+
+ # Rotate to start position and draw 4 pieces
+
+ foreach my $j (0..3)
+ {
+ PlotArcSegment($totang/2+$startang+$j*$totang,$r,$xpos+$centre->[0],GraphY($ypos+$centre->[1]),$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3);
+ }
+
+ $xpos+=$p[0]+$p[2];
+ $ypos+=$p[1]+$p[3];
+
+ $poschg=1;
+ }
+}
+
+sub deg
+{
+ return int($_[0]*180/3.14159);
+}
+
+sub adjust_arc_centre
+{
+ # Taken from geometry.cpp
+
+ # We move the center along a line parallel to the line between
+ # the specified start point and end point so that the center
+ # is equidistant between the start and end point.
+ # It can be proved (using Lagrange multipliers) that this will
+ # give the point nearest to the specified center that is equidistant
+ # between the start and end point.
+
+ my $p=shift;
+ my @c;
+ my $x = $p->[0] + $p->[2]; # (x, y) is the end point
+ my $y = $p->[1] + $p->[3];
+ my $n = $x*$x + $y*$y;
+ if ($n != 0)
+ {
+ $c[0]= $p->[0];
+ $c[1] = $p->[1];
+ my $k = .5 - ($c[0]*$x + $c[1]*$y)/$n;
+ $c[0] += $k*$x;
+ $c[1] += $k*$y;
+ return(\@c);
+ }
+ else
+ {
+ return(undef);
+ }
+}
+
+
+sub PlotArcSegment
+{
+ my ($ang,$r,$transx,$transy,$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3)=@_;
+ my $cos=cos($ang);
+ my $sin=sin($ang);
+ my @mat=($cos,$sin,-$sin,$cos,0,0);
+ my $lw=$lwidth/$r;
+
+ $stream.="q $r 0 0 $r $transx $transy cm ".join(' ',@mat)." cm $lw w $x0 $y0 m $x1 $y1 $x2 $y2 $x3 $y3 c S Q\n";
+}
+
+sub DrawCircle
+{
+ my $hd=shift;
+ my $vd=shift;
+ my $hr=$hd/2/$unitwidth;
+ my $vr=$vd/2/$unitwidth;
+ my $kappa=0.5522847498;
+ $hd/=$unitwidth;
+ $vd/=$unitwidth;
+
+
+ $stream.=PutXY(($xpos+$hd),$ypos)." m\n";
+ $stream.=PutXY(($xpos+$hd),($ypos+$vr*$kappa))." ".PutXY(($xpos+$hr+$hr*$kappa),($ypos+$vr))." ".PutXY(($xpos+$hr),($ypos+$vr))." c\n";
+ $stream.=PutXY(($xpos+$hr-$hr*$kappa),($ypos+$vr))." ".PutXY(($xpos),($ypos+$vr*$kappa))." ".PutXY(($xpos),($ypos))." c\n";
+ $stream.=PutXY(($xpos),($ypos-$vr*$kappa))." ".PutXY(($xpos+$hr-$hr*$kappa),($ypos-$vr))." ".PutXY(($xpos+$hr),($ypos-$vr))." c\n";
+ $stream.=PutXY(($xpos+$hr+$hr*$kappa),($ypos-$vr))." ".PutXY(($xpos+$hd),($ypos-$vr*$kappa))." ".PutXY(($xpos+$hd),($ypos))." c\n";
+ $xpos+=$hd;
+
+ $poschg=1;
+}
+
+sub FindCircle
+{
+ my ($x1,$y1,$x2,$y2,$x3,$y3)=@_;
+ my ($Xo, $Yo);
+
+ my $x=$x2+$x3;
+ my $y=$y2+$y3;
+ my $n=$x**2+$y**2;
+
+ if ($n)
+ {
+ my $k=.5-($x2*$x + $y2*$y)/$n;
+ return(sqrt($n),$x2+$k*$x,$y2+$k*$y);
+ }
+ else
+ {
+ return(-1);
+ }
+
+}
+
+sub PtoR
+{
+ my ($theta,$r)=@_;
+
+ return($r*cos($theta),$r*sin($theta));
+}
+
+sub RtoP
+{
+ my ($x,$y)=@_;
+
+ return(atan2($y,$x),sqrt($x**2+$y**2));
+}
+
+sub PutLine
+{
+
+ my $f=shift;
+
+ IsText() if !defined($f);
+
+ return if (scalar(@lin) == 0) or (!defined($lin[0]->[0]) and $#lin == 0);
+
+# $stream.="% --- wht=$whtsz, pend=$pendmv, nomv=$nomove\n" if $debug;
+ $pendmv-=$nomove;
+ $lin[$#lin]->[1]=-$pendmv/$cftsz if ($pendmv != 0);
+
+ if (0)
+ {
+ if (scalar(@lin) == 1 and (!defined($lin[0]->[1]) or $lin[0]->[1] == 0))
+ {
+ $stream.="($lin[0]->[0]) Tj\n";
+ }
+ else
+ {
+ $stream.="[";
+
+ foreach my $wd (@lin)
+ {
+ $stream.="($wd->[0]) " if defined($wd->[0]);
+ $stream.="$wd->[1] " if defined($wd->[1]) and $wd->[1] != 0;
+ }
+
+ $stream.="] TJ\n";
+ }
+ }
+ else
+ {
+ if (scalar(@lin) == 1 and (!defined($lin[0]->[1]) or $lin[0]->[1] == 0))
+ {
+ $stream.="0 Tw ($lin[0]->[0]) Tj\n";
+ }
+ else
+ {
+ if ($wt>=-1 or $#lin == 0 or $lin[0]->[1]>=0)
+ {
+ $stream.="0 Tw [";
+
+ foreach my $wd (@lin)
+ {
+ $stream.="($wd->[0]) " if defined($wd->[0]);
+ $stream.="$wd->[1] " if defined($wd->[1]) and $wd->[1] != 0;
+ }
+
+ $stream.="] TJ\n";
+ }
+ else
+ {
+ # $stream.="\%dg 0 Tw [";
+ #
+ # foreach my $wd (@lin)
+ # {
+ # $stream.="($wd->[0]) " if defined($wd->[0]);
+ # $stream.="$wd->[1] " if defined($wd->[1]) and $wd->[1] != 0;
+ # }
+ #
+ # $stream.="] TJ\n";
+ #
+ # my $wt=$lin[0]->[1]||0;
+
+ # while ($wt < -$whtsz/$cftsz)
+ # {
+ # $wt+=$whtsz/$cftsz;
+ # }
+
+ $stream.=sprintf( "%.3f Tw ",-($whtsz+$wt*$cftsz)/$unitwidth );
+ $stream.="[(";
+
+ foreach my $wd (@lin)
+ {
+ my $wwt=$wd->[1]||0;
+
+ while ($wwt <= $wt+.1)
+ {
+ $wwt-=$wt;
+ $wd->[0].=' ';
+ }
+
+ if (abs($wwt) < .1 or $wwt == 0)
+ {
+ $stream.="$wd->[0]" if defined($wd->[0]);
+ }
+ else
+ {
+ $stream.="$wd->[0]) $wwt (" if defined($wd->[0]);
+ }
+ }
+ $stream.=")] TJ\n";
+ }
+ }
+ }
+
+ @lin=();
+ $xpos+=$pendmv/$unitwidth;
+ $pendmv=0;
+ $nomove=0;
+ $wt=-1;
+}
+
+sub LoadAhead
+{
+ my $no=shift;
+
+ foreach my $j (1..$no)
+ {
+ my $lin=<>;
+ chomp($lin);
+ $lct++;
+
+ push(@ahead,$lin);
+ $stream.="%% $lin\n" if $debug;
+ }
+}
+
+sub do_V
+{
+ my $par=shift;
+
+ if ($mode eq 't')
+ {
+ PutLine();
+ }
+ else
+ {
+ $xpos+=$pendmv/$unitwidth;
+ $pendmv=0;
+ }
+
+ $ypos=$par/$unitwidth;
+
+ LoadAhead(1);
+
+ if (substr($ahead[0],0,1) eq 'H')
+ {
+ $xpos=substr($ahead[0],1)/$unitwidth;
+
+ @ahead=();
+
+ }
+
+ $nomove=$pendmv=0;
+ $poschg=1;
+}
+
+sub do_v
+{
+ my $par=shift;
+
+ PutLine();
+
+ $ypos+=$par/$unitwidth;
+
+ $poschg=1;
+}
+
+sub TextWid
+{
+ my $txt=shift;
+ my $w=0;
+
+ foreach my $c (split('',$txt))
+ {
+ my $cn=ord($c);
+ $widtbl->[$cn]=$origwidtbl->[$cn]*$cftsz if !defined($widtbl->[$cn]);
+ $w+=$widtbl->[$cn];
+ }
+
+ return($w/$unitwidth);
+}
+
+sub do_t
+{
+ my $par=shift;
+ my $wid=TextWid($par);
+
+ $par=reverse(split('',$par)) if $xrev;
+ if ($n_flg and defined($mark))
+ {
+ $mark->{ypos}=$ypos;
+ $mark->{xpos}=$xpos;
+ }
+
+ $n_flg=0;
+ IsText();
+
+ $xpos+=$wid;
+ $xpos+=($pendmv-$nomove)/$unitwidth;
+
+ $stream.="% == '$par'=$wid 'xpos=$xpos\n" if $debug;
+ $par=~s/\\/\\\\/g;
+ $par=~s/\)/\\)/g;
+ $par=~s/\(/\\(/g;
+
+ # $pendmv = 'h' move since last 't'
+ # $nomove = width of char(s) added by 'C', 'N' or 'c'
+ # $w-flg = 'w' seen since last t
+
+ if ($fontchg)
+ {
+ PutLine();
+ $whtsz=$fontlst{$cft}->{FNT}->{spacewidth}*$cftsz;
+ $stream.="/F$cft $cftsz Tf\n", $fontchg=0 if $fontchg && defined($cft);
+ }
+
+ $gotT=1;
+
+ $stream.="% --- wht=$whtsz, pend=$pendmv, nomv=$nomove\n" if $debug;
+
+# if ($w_flg && $#lin > -1)
+# {
+# $lin[$#lin]->[0].=' ';
+# $pendmv-=$whtsz;
+# $dontglue=1 if $pendmv==0;
+# }
+
+ $wt=-$pendmv/$cftsz if $w_flg and $wt==-1;
+ $pendmv-=$nomove;
+ $nomove=0;
+ $w_flg=0;
+
+ if ($xrev)
+ {
+ PutLine(0) if $#lin > -1;
+ MakeMatrix(1);
+ $stream.="$matrix ".PutXY($xpos,$ypos)." Tm\n", $poschg=0;
+ $stream.="0 Tw ";
+ $stream.="($par) Tj\n";
+ MakeMatrix();
+ $stream.="$matrix ".PutXY($xpos,$ypos)." Tm\n", $poschg=0;
+ $matrixchg=0;
+ return;
+ }
+
+ if ($pendmv)
+ {
+ if ($#lin == -1)
+ {
+ push(@lin,[undef,-$pendmv/$cftsz]);
+ }
+ else
+ {
+ $lin[$#lin]->[1]=-$pendmv/$cftsz;
+ }
+
+ push(@lin,[$par,undef]);
+# $xpos+=$pendmv/$unitwidth;
+ $pendmv=0
+ }
+ else
+ {
+ if ($#lin == -1)
+ {
+ push(@lin,[$par,undef]);
+ }
+ else
+ {
+ $lin[$#lin]->[0].=$par;
+ }
+ }
+}
+
+sub do_h
+{
+ $pendmv+=shift;
+}
+
+sub do_H
+{
+ my $par=shift;
+
+ if ($mode eq 't')
+ {
+ PutLine();
+ }
+ else
+ {
+ $xpos+=$pendmv/$unitwidth;
+ $pendmv=0;
+ }
+
+ my $newx=$par/$unitwidth;
+ $stream.=sprintf("%.3f",$newx-$tmxpos)." 0 Td\n" if $mode eq 't';
+ $tmxpos=$xpos=$newx;
+ $pendmv=$nomove=0;
+}
+
+sub do_C
+{
+ my $par=shift;
+ my $nm;
+
+ ($par,$nm)=FindChar($par);
+
+ do_t($par);
+ $nomove=$nm;
+}
+
+sub FindChar
+{
+ my $chnm=shift;
+
+ if (exists($fontlst{$cft}->{FNT}->{GNM}->{$chnm}))
+ {
+ my $ch=$fontlst{$cft}->{FNT}->{GNM}->{$chnm};
+ return(chr($ch),$fontlst{$cft}->{FNT}->{WID}->[$ch]*$cftsz);
+ }
+ else
+ {
+ return(' ');
+ }
+}
+
+sub do_c
+{
+ my $par=shift;
+
+ push(@ahead,substr($par,1));
+ $par=substr($par,0,1);
+ my $ch=ord($par);
+ do_t($ch);
+ $nomove=$fontlst{$cft}->{FNT}->{WID}->[$ch]*$cftsz;
+}
+
+sub do_N
+{
+ my $par=shift;
+
+ do_t(chr($par));
+ $nomove=$fontlst{$cft}->{FNT}->{WID}->[$par]*$cftsz;
+}
+
+sub do_n
+{
+ $gotT=0;
+ PutLine();
+ $pendmv=$nomove=0;
+ $n_flg=1;
+ @lin=();
+ PutHotSpot($xpos) if defined($mark);
+}
diff --git a/src/devices/grops/grops.man b/src/devices/grops/grops.man
index 2d4ee4af..5c59ffb2 100644
--- a/src/devices/grops/grops.man
+++ b/src/devices/grops/grops.man
@@ -1,6 +1,5 @@
.ig
-Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008,
- 2009, 2010
+Copyright (C) 1989-2006, 2008-2011
Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
@@ -29,6 +28,7 @@ the original English.
.
.de FT
. if '\\*(.T'ps' .ft \\$1
+. if '\\*(.T'pdf' .ft \\$1
..
.
.
diff --git a/src/preproc/pic/pic.man b/src/preproc/pic/pic.man
index 53c593e6..e845dd12 100644
--- a/src/preproc/pic/pic.man
+++ b/src/preproc/pic/pic.man
@@ -1,5 +1,5 @@
.ig
-Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2007, 2009, 2011
+Copyright (C) 1989-2004, 2007, 2009, 2011
Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
@@ -924,7 +924,9 @@ Objects can have an
.B aligned
attribute.
This will only work if the postprocessor is
-.BR grops .
+.BR grops ,
+or
+.BR gropdf .
Any text associated with an object having the
.B aligned
attribute will be rotated about the center of the object
@@ -1033,6 +1035,16 @@ Call
for a list of the available devices.
.
.LP
+An alternative may be to use the
+.B \-Tpdf
+option to convert your picture directly into
+.B PDF
+format.
+The MediaBox of the file produced can be controlled by passing a
+.B \-P-p
+papersize to groff.
+.
+.LP
As the Encapsulated PostScript File Format
.B EPS
is getting more and more important, and the conversion wasn't regarded
diff --git a/src/roff/groff/groff.man b/src/roff/groff/groff.man
index 933e3e7b..50c2bf6a 100644
--- a/src/roff/groff/groff.man
+++ b/src/roff/groff/groff.man
@@ -456,6 +456,11 @@ is
ps
PostScript output (postprocessor is
.BR grops ).
+.
+.TP
+pdf
+Portable Document Format (PDF) output (postprocessor is
+.BR gropdf ).
.RE
.RE
.
@@ -1106,6 +1111,13 @@ PostScript output; suitable for printers and previewers like
.BR gv (1).
.
.TP
+.B pdf
+PDF files; suitable for viewing with tools such as
+.BR evince (1)
+and
+.BR okular (1).
+.
+.TP
.B utf8
Text output using the Unicode (ISO 10646) character set with \%UTF-8
encoding; see
@@ -1195,6 +1207,10 @@ for HTML and XHTML formats,
.BR grops (@MAN1EXT@)
for PostScript.
.
+.TP
+.BR gropdf (@MAN1EXT@)
+for PDF.
+.
.P
Combined with the many existing free conversion tools this should
be sufficient to convert a
@@ -1845,6 +1861,7 @@ Postprocessors for the output devices:
.BR \%grolj4 (@MAN1EXT@),
.BR \%lj4_font (@MAN5EXT@),
.BR \%grops (@MAN1EXT@),
+.BR \%gropdf (@MAN1EXT@),
.BR \%grotty (@MAN1EXT@).
.
.TP
diff --git a/src/utils/afmtodit/afmtodit.man b/src/utils/afmtodit/afmtodit.man
index 47017e19..82d6f5d3 100644
--- a/src/utils/afmtodit/afmtodit.man
+++ b/src/utils/afmtodit/afmtodit.man
@@ -1,6 +1,5 @@
.ig
-Copyright (C) 1989-2000, 2001, 2002, 2003, 2005, 2006, 2008, 2009,
- 2010
+Copyright (C) 1989-2003, 2005, 2006, 2008-2011
Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
@@ -37,7 +36,7 @@ the original English.
.
.
.SH NAME
-afmtodit \- create font files for use with groff \-Tps
+afmtodit \- create font files for use with groff \-Tps and \-Tpdf
.
.
.SH SYNOPSIS
@@ -65,8 +64,10 @@ The whitespace between a command line option and its argument is optional.
.
.SH DESCRIPTION
.B afmtodit
-creates a font file for use with groff and
-.BR grops .
+creates a font file for use with groff,
+.BR grops ,
+and
+.BR gropdf .
.
.B afmtodit
is written in perl;
diff --git a/src/utils/pfbtops/pfbtops.man b/src/utils/pfbtops/pfbtops.man
index 027732c5..eae6a461 100644
--- a/src/utils/pfbtops/pfbtops.man
+++ b/src/utils/pfbtops/pfbtops.man
@@ -1,5 +1,5 @@
.ig
-Copyright (C) 1989-1995, 2001, 2003, 2004, 2009
+Copyright (C) 1989-1995, 2001, 2003, 2004, 2009, 2011
Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
@@ -53,7 +53,9 @@ format.
.LP
The resulting ASCII format PostScript font can be used with groff.
It must first be listed in
-.BR @FONTDIR@/devps/download .
+.B @FONTDIR@/devps/download
+and
+.BR @FONTDIR@/devpdf/download .
.
.SH OPTIONS
.TP
@@ -62,7 +64,8 @@ Print the version number.
.
.
.SH "SEE ALSO"
-.BR grops (@MAN1EXT@)
+.BR grops (@MAN1EXT@),
+.BR gropdf (@MAN1EXT@)
.
.\" Local Variables:
.\" mode: nroff
diff --git a/test-groff.in b/test-groff.in
index 51e4010d..b58d8be9 100644
--- a/test-groff.in
+++ b/test-groff.in
@@ -1,6 +1,6 @@
#! /bin/sh
#
-# Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005, 2009
+# Copyright (C) 1989-2000, 2001-2005, 2009, 2011
# Free Software Foundation, Inc.
#
# This file is part of groff.
@@ -41,6 +41,7 @@ $builddir/devices/grotty$SEP\
$builddir/devices/grolj4$SEP\
$builddir/devices/grolbp$SEP\
$builddir/devices/grohtml$SEP\
+$builddir/devices/gropdf$SEP\
$builddir/devices/xditview
XENVIRONMENT=$srcdir/src/devices/xditview/GXditview.ad
diff --git a/tmac/Makefile.sub b/tmac/Makefile.sub
index ad5079cf..3179ba00 100644
--- a/tmac/Makefile.sub
+++ b/tmac/Makefile.sub
@@ -37,6 +37,7 @@ NORMALFILES=\
safer.tmac \
trace.tmac \
ps.tmac psold.tmac pspic.tmac psatk.tmac \
+ pdf.tmac \
dvi.tmac \
tty.tmac tty-char.tmac \
latin1.tmac latin2.tmac latin5.tmac latin9.tmac cp1047.tmac \
diff --git a/tmac/pdf.tmac b/tmac/pdf.tmac
new file mode 100644
index 00000000..b05ab8ca
--- /dev/null
+++ b/tmac/pdf.tmac
@@ -0,0 +1,738 @@
+.\" pdf.tmac
+.\"
+.mso ps.tmac
+.
+.de pdf:SS
+. char \\$1 \\S'16'\\$1\\S'0'
+..
+.pdf:SS \[+h]
+.pdf:SS \[ts]
+.pdf:SS \[*a]
+.pdf:SS \[*b]
+.pdf:SS \[*x]
+.pdf:SS \[*d]
+.pdf:SS \[*e]
+.pdf:SS \[*f]
+.pdf:SS \[*g]
+.pdf:SS \[*y]
+.pdf:SS \[*i]
+.pdf:SS \[+f]
+.pdf:SS \[*k]
+.pdf:SS \[*l]
+.pdf:SS \[*m]
+.pdf:SS \[*n]
+.pdf:SS \[*o]
+.pdf:SS \[*p]
+.pdf:SS \[*h]
+.pdf:SS \[*r]
+.pdf:SS \[*s]
+.pdf:SS \[*t]
+.pdf:SS \[*u]
+.pdf:SS \[+p]
+.pdf:SS \[*w]
+.pdf:SS \[*c]
+.pdf:SS \[*q]
+.pdf:SS \[*z]
+.char \[lh] \X'pdf: xrev'\[rh]\X'pdf: xrev'
+.nr pdf:bm.nl 1
+.de pdfmark
+. nop \!x X ps:exec [\\$* pdfmark
+..
+.de pdf:warn
+. tm \\n(.F:\\n(.c: macro warning: \\$*
+..
+.de pdf:error
+. tm \\n(.F:\\n(.c: macro error: \\$*
+..
+.de pdfinfo
+.\" -------------------------------------------------------------------
+.\" Usage:
+.\" .pdfinfo /FieldName field content ...
+.\" Examples:
+.\" .pdfinfo /Title A PDF Document
+.\" .pdfinfo /Author Keith Marshall
+.\" -------------------------------------------------------------------
+.\"
+.ds pdf:meta.field \\$1
+.shift
+.ie '\\n(.z'' .pdfmark \\*[pdf:meta.field] (\\$*) /DOCINFO
+.el \!.pdfmark \\*[pdf:meta.field] (\\$*) /DOCINFO
+.rm pdf:meta.field
+..
+.de pdfview
+.\" -------------------------------------------------------------------
+.\" Usage:
+.\" .pdfview view parameters ...
+.\" Examples:
+.\" .pdfview /PageMode /UseOutlines
+.\" .pdfview /Page 2 /View [/FitH \n(.p u]
+.\" -------------------------------------------------------------------
+.\"
+.ie '\\n(.z'' .pdfmark \\$* /DOCVIEW
+.el \!.pdfmark \\$* /DOCVIEW
+..
+.\" =====================================================================
+.\" Module PDFNOTE: Insert "Sticky Note" Style Comments in a PDF Document
+.\" =====================================================================
+.\"
+.\" "PDFNOTE.WIDTH" and "PDFNOTE.HEIGHT" set the preferred size for
+.\" display of the "sticky note" pane, when opened. Acrobat Reader
+.\" seems not to honour these -- perhaps GhostScript doesn't encode
+.\" them correctly! Anyway, let's set some suitable default values,
+.\" in case the user has a set up which does work as advertised.
+.\"
+.nr PDFNOTE.WIDTH 3.5i
+.nr PDFNOTE.HEIGHT 2.0i
+.\"
+.\" "pdf:bbox" defines the expression used to set the size and location
+.\" of the bounding rectangle for display of notes and link "hot-spots".
+.\" This is defined, such that a note is placed at troff's current text
+.\" position on the current page, with its displayed image size defined
+.\" by the "PDFNOTE.WIDTH" and "PDFNOTE.HEIGHT" registers, while the
+.\" bounds for a link "hot-spot" are matched to the text region which
+.\" defines the "hot-spot".
+.\"
+.ds pdf:bbox \\n[pdf:llx] u \\n[pdf:lly] u \\n[pdf:urx] u \\n[pdf:ury] u
+.\"
+.\" Getting line breaks into the text of a PDFNOTE is tricky -- we need
+.\" to get a "\n" into the Postscript stream, but three levels of "\" are
+.\" swallowed, when we invoke "pdfnote". The following definition of "PDFLB",
+.\" (for LineBreak), is rather ugly, but does allow us to use
+.\"
+.\" .pdfnote Some text.\*[PDFLB]Some more text, on a new line.
+.\"
+.ds PDFLB \\\\\\\\\\\\\\\\n
+.\"
+.de pdfnote
+.\" ----------------------------------------------------------------------
+.\" Usage:
+.\" .pdfnote [-T "Text for Title"] Text of note ...
+.\" ----------------------------------------------------------------------
+.\"
+.\" First, compute the bounding rectangle,
+.\" for this PDFNOTE instance
+.\"
+. mk pdf:ury
+. nr pdf:llx \\n(.k+\\n(.o+\\n[.in]
+. nr pdf:lly \\n[pdf:ury]-\\n[PDFNOTE.HEIGHT]
+. nr pdf:urx \\n[pdf:llx]+\\n[PDFNOTE.WIDTH]
+. ds pdf:note.instance /Rect [\\*[pdf:bbox]]
+.\"
+.\" Parse any specified (recognisable) PDFNOTE options
+.\"
+. while dpdf:note\\$1 \{\
+. pdf:note\\$1 \\$@
+. shift \\n[pdf:note.argc]
+. \}
+.\"
+.\" Emit the note, and clean up
+.\"
+. pdfmark \\*[pdf:note.instance] /Subtype /Text /Contents (\\$*) /ANN
+. rm pdf:note.instance
+. rr pdf:note.argc
+..
+.de pdf:note-T
+.nr pdf:note.argc 2
+.as pdf:note.instance " /Title (\\$2)
+..
+.\" =====================================================================
+.\" Module PDFBOOKMARK: Add an Outline Reference in the PDF Bookmark Pane
+.\" =====================================================================
+.\"
+.\" "PDFBOOKMARK.VIEW" controls how the document will be displayed,
+.\" when the user selects a bookmark. This default setting will fit
+.\" the page width to the viewing window, with the bookmarked entry
+.\" located at the top of the viewable area.
+.\"
+.ds PDFBOOKMARK.VIEW /FitH -\\n[PDFPAGE.Y] u
+.\"
+.\" "PDFOUTLINE.FOLDLEVEL" controls how the document outline will be
+.\" displayed. It is a number, defining the maximum heading level
+.\" which will be visible, without outline expansion by the user, in
+.\" the initial view of the document outline. Assuming that no sane
+.\" document will ever extend to 10,000 levels of nested headings,
+.\" this initial default value causes outlines to be fully expanded.
+.\"
+.nr PDFOUTLINE.FOLDLEVEL 10000
+.\"
+.\" The actual job of creating an outline reference
+.\" is performed by the "pdfbookmark" macro.
+.\"
+.de pdfbookmark
+.\" ------------------------------------------------------------------
+.\" Usage:
+.\" .pdfbookmark [-T tag] level "Text of Outline Entry"
+.\"
+.\" $1 = nesting level for bookmark (1 is top level)
+.\" $2 = text for bookmark, (in PDF viewer bookmarks list)
+.\" ------------------------------------------------------------------
+.\"
+.ie '\\n(.z'' \{\
+.\"
+.\" When we are at the top diversion level, i.e. actually emitting text
+.\" to the output device stream, then we compute the location of, and
+.\" plant this bookmark immediately.
+.\"
+. \" Make the bookmark name "untagged" by default,
+. \" then parse any specified options, to set a "tag", if required
+. \"
+. ds pdf:href-T
+. while dpdf:href.opt\\$1 \{\
+. pdf:href.opt\\$1 \\$@
+. shift \\n[pdf:href.argc]
+. \}
+. rr pdf:href.argc
+. \"
+. \" If we found "--" to mark the end of the options, discard it
+. \"
+. if '\\$1'--' .shift
+. \"
+. nr pdf:bm.lev 0+\\$1
+. if \\n[pdf:bm.lev]==0 .nr pdf:bm.lev 1
+. if \\n[pdf:bm.lev]>\\n[PDFOUTLINE.FOLDLEVEL] .nr pdf:bm.lev \\n[pdf:bm.lev]*-1
+. nr pdf:bm.abslev 0+\\n[pdf:bm.lev]
+. if \\n[pdf:bm.lev]<0 .nr pdf:bm.abslev 0+\\n[pdf:bm.abslev]*-1
+. if \\n[pdf:bm.abslev]>\\n[pdf:bm.nl] .nr pdf:bm.nl \\n[pdf:bm.nl]+1
+. ie \\n[pdf:bm.abslev]>\\n[pdf:bm.nl] \{\
+. pdf:warn adjusted level \\n[pdf:bm.abslev] bookmark; should be <= \\n[pdf:bm.nl]
+. nr pdf:bm.abslev 0+\\n[pdf:bm.nl]
+. \}
+. el .nr pdf:bm.nl \\n[pdf:bm.abslev]
+. if \\n[pdf:bm.lev]<0 .nr pdf:bm.abslev \\n[pdf:bm.abslev]*-1
+. nr pdf:bm.lev 0+\\n[pdf:bm.abslev]
+. rr pdf:bm.abslev
+. shift
+. \"
+. \" Increment the bookmark serialisation index
+. \" in order to generate a uniquely serialised bookmark name,
+. \" ( which we return in the string "PDFBOOKMARK.NAME" ),
+. \"
+. nr pdf:bm.nr +1
+. ie '\\*[pdf:href-T]'' .ds PDFBOOKMARK.NAME pdf:bm\\n[pdf:bm.nr]
+. el .ds PDFBOOKMARK.NAME \\*[pdf:href-T]
+. pdf:href.sety
+. ds pdf:cleaned \\$*
+. ev pdfcln
+. nf
+. box pdf:clean
+. nop \\$*
+. sp -1
+. box
+. asciify pdf:clean
+. ds pdf:cleaned \\*[pdf:clean]
+. ev
+. ds pdf:look(\\*[PDFBOOKMARK.NAME]) \\*[pdf:cleaned]
+. if dPDF.EXPORT .tm .ds pdf:look(\\*[PDFBOOKMARK.NAME]) \\*[pdf:cleaned]
+. pdfmark /Dest /\\*[PDFBOOKMARK.NAME] /View [\\*[PDFBOOKMARK.VIEW]] /DEST
+. pdfmark /Dest /\\*[PDFBOOKMARK.NAME] /Title (\\*[pdf:cleaned]) /Level \\n[pdf:bm.lev] /OUT
+. pdf:href.options.clear
+. rr PDFPAGE.Y
+. rm pdf:cleaned
+. rm pdf:clean
+. \}
+. \}
+.el \{\
+.\"
+.\" But when we are collecting a diversion which will be written out later,
+.\" then we must defer bookmark placement, until we emit the diversion.
+.\" (don't rely on $0 == pdfbookmark here; it may be a volatile alias).
+.\"
+. nop \!.pdfbookmark \\$@
+. \}
+..
+.\"
+.\" =============================================================
+.\" Module PDFHREF: Create Hypertext References in a PDF Document
+.\" =============================================================
+.\"
+.\" "PDFHREF.VIEW" controls how the document will be displayed,
+.\" when the user follows a link to a named reference.
+.\"
+.ds PDFHREF.VIEW /FitH -\\n[PDFPAGE.Y] u
+.\"
+.\" This default setting will fit the page width to the viewing
+.\" window, with the bookmarked entry located close to the top
+.\" of the viewable area. "PDFHREF.VIEW.LEADING" controls the
+.\" actual distance below the top of the viewing window, where
+.\" the reference will be positioned; 5 points is a reasonable
+.\" default offset.
+.\"
+.nr PDFHREF.VIEW.LEADING 5.0p
+.\"
+.\" Yuk!!!
+.\" PDF view co-ordinates are mapped from the bottom left corner,
+.\" of the page, whereas page printing co-ordinates are mapped
+.\" conventionally, from top left.
+.\"
+.\" Macro "pdf:href.sety" transforms the vertical position of the
+.\" last printed baseline, from the printing co-ordinate domain to
+.\" the PDF view domain.
+.\"
+.de pdf:href.sety
+.\" ----------------------------------------------------------------
+.\" Usage:
+.\" .pdf:href.sety
+.\" ----------------------------------------------------------------
+.\"
+.\" This computation yields the vertical view co-ordinate
+.\" in groff's basic units; don't forget to append grops' "u"
+.\" conversion operator, when writing the pdfmark!
+.\"
+.nr PDFPAGE.Y \\n(nl+\\n[PDFHREF.VIEW.LEADING]
+..
+.\" When we create a link "hot-spot" ...
+.\" "PDFHREF.LEADING" sets the distance above the top of the glyph
+.\" bounding boxes, in each line of link text, over which the link
+.\" hot-spot will extend, while "PDFHREF.HEIGHT" sets the hot-spot
+.\" height, PER LINE of text occupied by the reference.
+.\"
+.\" Since most fonts specify some leading space within the bounding
+.\" boxes of their glyphs, a better appearance may be achieved when
+.\" NEGATIVE leading is specified for link hot-spots; indeed, when
+.\" the default 10pt Times font is used, -1.0 point seems to be a
+.\" reasonable default value for "PDFHREF.LEADING" -- it may be
+.\" changed, if desired.
+.\"
+.\" "PDFHREF.HEIGHT" is initially set as one vertical spacing unit;
+.\" note that it is defined as a string, so it will adapt to changes
+.\" in the vertical spacing. Changing it is NOT RECOMMENDED.
+.\"
+.nr PDFHREF.LEADING -1.0p
+.ds PDFHREF.HEIGHT 1.0v
+.\"
+.\" PDF readers generally place a rectangular border around link
+.\" "hot-spots". Within text, this looks rather ugly, so we set
+.\" "PDFHREF.BORDER" to suppress it -- the three zeroes represent
+.\" the border parameters in the "/Border [0 0 0]" PDFMARK string,
+.\" and may be changed to any valid form, as defined in Adobe's
+.\" PDFMARK Reference Manual.
+.\"
+.ds PDFHREF.BORDER 0 0 0
+.\"
+.\" "PDFHREF.COLOUR" (note British spelling) defines the colour to
+.\" be used for display of link "hot-spots". This will apply both
+.\" to borders, if used, and, by default to text; however, actual
+.\" text colour is set by "PDFHREF.TEXT.COLOUR", which may be reset
+.\" independently of "PDFHREF.COLOUR", to achieve contrasting text
+.\" and border colours.
+.\"
+.\" "PDFHREF.COLOUR" must be set to a sequence of three values,
+.\" each in the range 0.0 .. 1.0, representing the red, green, and
+.\" blue components of the colour specification in the RGB colour
+.\" domain, which is shared by "groff" and the PDF readers.
+.\"
+.ds PDFHREF.COLOUR 0.35 0.00 0.60
+.defcolor pdf:href.colour rgb \*[PDFHREF.COLOUR]
+.\"
+.\" "PDFHREF.TEXT.COLOUR", on the other hand, is simply defined
+.\" using any "groff" colour name -- this default maps it to the
+.\" same colour value as "PDFHREF.COLOUR".
+.\"
+.ds PDFHREF.TEXT.COLOUR pdf:href.colour
+.\"
+.\" Accommodate users who prefer the American spelling, COLOR, to
+.\" the British spelling, COLOUR.
+.\"
+.als PDFHREF.COLOR PDFHREF.COLOUR
+.als PDFHREF.TEXT.COLOR PDFHREF.TEXT.COLOUR
+.\"
+.\" All PDF "Hypertext" reference capabilities are accessed
+.\" through the "pdfhref" macro
+.\"
+.de pdfhref
+.\" -----------------------------------------------------------------
+.\" Usage:
+.\" .pdfhref <subcommand [options ...] [parameters ...]> ...
+.\" -----------------------------------------------------------------
+.\"
+.\"
+.\" Loop over all subcommands specified in the argument list
+.\"
+. while \\n(.$ \{\
+. \"
+. \" Initially, assume each subcommand will complete successfully
+. \"
+. nr pdf:href.ok 1
+. \"
+. \" Initialise -E and -X flags in the OFF state
+. \"
+. nr pdf:href-E 0
+. nr pdf:href-X 0
+. \"
+. \" Handle the case where subcommand is specified as "-class",
+. \" setting up appropriate macro aliases for subcommand handlers.
+. \"
+. if dpdf*href\\$1 .als pdf*href pdf*href\\$1
+. if dpdf*href\\$1.link .als pdf*href.link pdf*href\\$1.link
+. if dpdf*href\\$1.file .als pdf*href.file pdf*href\\$1.file
+. \"
+. \" Repeat macro alias setup
+. \" for the case where the subcommand is specified as "class",
+. \" (without a leading hyphen)
+. \"
+. if dpdf*href-\\$1 .als pdf*href pdf*href-\\$1
+. if dpdf*href-\\$1.link .als pdf*href.link pdf*href-\\$1.link
+. if dpdf*href-\\$1.file .als pdf*href.file pdf*href-\\$1.file
+. \"
+. \" Process one subcommand ...
+. \"
+. ds pdf*href.class \\$1
+. ie dpdf*href \{\
+. \"
+. \" Subcommand "class" is recognised ...
+. \" discard the "class" code from the argument list,
+. \" set the initial argument count to swallow all arguments,
+. \" and invoke the selected subcommand handler.
+. \"
+. shift
+. nr pdf:argc \\n(.$
+. pdf*href \\$@
+. \"
+. \" When done,
+. \" discard all arguments actually consumed by the handler,
+. \" before proceeding to the next subcommand (if any).
+. \"
+. shift \\n[pdf:argc]
+. \}
+. el \{\
+. \"
+. \" Subcommand "class" is not recognised ...
+. \" issue a warning, and discard the entire argument list,
+. \" so aborting this "pdfhref" invocation
+. \"
+. pdf:warn \\$0: undefined reference class '\\$1' ignored
+. shift \\n(.$
+. \}
+. \"
+. \" Clean up temporary reference data,
+. \" to ensure it doesn't propagate to any future reference
+. \"
+. rm pdf*href pdf:href.link pdf:href.files
+. rr pdf:href-E
+. pdf:href.options.clear
+. \}
+. rr pdf:href.ok
+..
+.\"
+.\" Macros "pdf:href.flag" and "pdf:href.option"
+.\" provide a generic mechanism for switching on flag type options,
+.\" and for decoding options with arguments, respectively
+.\"
+.de pdf:href.flag
+.\" ----------------------------------------------------------------------
+.\" ----------------------------------------------------------------------
+.nr pdf:href\\$1 1
+.nr pdf:href.argc 1
+..
+.de pdf:href.option
+.\" ----------------------------------------------------------------------
+.\" ----------------------------------------------------------------------
+.ds pdf:href\\$1 \\$2
+.nr pdf:href.argc 2
+..
+.\"
+.\" Valid PDFHREF options are simply declared
+.\" by aliasing option handlers to "pdf:href.option",
+.\" or to "pdf:href.flag", as appropriate
+.\"
+.als pdf:href.opt-A pdf:href.option \" affixed text
+.als pdf:href.opt-D pdf:href.option \" destination name
+.als pdf:href.opt-E pdf:href.flag \" echo link descriptor
+.als pdf:href.opt-F pdf:href.option \" remote file specifier
+.als pdf:href.opt-N pdf:href.option \" reference name
+.als pdf:href.opt-P pdf:href.option \" prefixed text
+.als pdf:href.opt-T pdf:href.option \" bookmark "tag"
+.\"
+.\" For references to another document file
+.\" we also need to support OS dependent file name specifiers
+.\"
+.als pdf:href.opt-DF pdf:href.option \" /DOSFile specifier
+.als pdf:href.opt-MF pdf:href.option \" /MacFile specifier
+.als pdf:href.opt-UF pdf:href.option \" /UnixFile specifier
+.als pdf:href.opt-WF pdf:href.option \" /WinFile specifier
+.\"
+.\" Macro "pdf:href.options.clear" ensures that ALL option
+.\" argument strings are deleted, after "pdfhref" has completed
+.\" all processing which depends on them
+.\"
+.de pdf:href.options.clear
+.\" -----------------------------------------------------------------
+.\" Usage:
+.\" .pdf:href.options.clear [option ...]
+.\" -----------------------------------------------------------------
+.\"
+.\" When an option list is specified ...
+.\"
+.ie \\n(.$ \{\
+. \"
+. \" then loop through the list,
+. \" deleting each specified option argument string in turn
+. \"
+. while \\n(.$ \{\
+. if dpdf:href-\\$1 .rm pdf:href-\\$1
+. shift
+. \}
+. \}
+.\"
+.\" ... but when no list is specified,
+.\" then recurse, to clear all known option argument strings
+.\"
+.el .pdf:href.options.clear A D F N P T DF MF UF WF
+..
+.\"
+.\" Macro "pdf*href-M" is the handler invoked by "pdfhref", when
+.\" called with the "M" reference class specifier, to create a
+.\" named cross reference mark, and to emit a cross reference
+.\" data record, as specified by "PDFHREF.INFO".
+.\"
+.de pdf*href-M
+.\" -----------------------------------------------------------------
+.\" Usage:
+.\" .pdfhref M [-N name | -D name] [-E] descriptive text ...
+.\" -----------------------------------------------------------------
+.\"
+.\" Initially, declare the -D and -N string options as empty,
+.\" so we avoid warning messages when we try to use them, and find
+.\" that they are undefined.
+.\"
+.ds pdf:href-D
+.ds pdf:href-N
+.\"
+.\" Parse, interpret, and strip any specified options from the
+.\" argument list. (Note that only options with a declared handler
+.\" will be processed; there is no provision for detecting invalid
+.\" options -- anything which is not recognised is assumed to start
+.\" the "descriptive text" component of the argument list).
+.\"
+.while dpdf:href.opt\\$1 \{\
+. pdf:href.opt\\$1 \\$@
+. shift \\n[pdf:href.argc]
+. \}
+.\"
+.\" If we found "--", to mark the end of the options,
+.\" then we should discard it.
+.\"
+.if '\\$1'--' .shift
+.\"
+.\" All PDF reference markers MUST be named. The name may have been
+.\" supplied using the "-N Name" option, (or the "-D Name" option);
+.\" if not, deduce it from the first "word" in the "descriptive text",
+.\" if any, and set the marker -- if we still can't identify the name
+.\" for the destination, then this marker will not be created.
+.\"
+.pdf*href.set \\*[pdf:href-N] \\*[pdf:href-D] \\$1
+.\"
+.\"
+.\" Irrespective of whether this marker is created, or not,
+.\" the descriptive text will be copied to the groff output stream,
+.\" provided the "-E" option was specified
+.\"
+.if \\n[pdf:href-E] \&\\$*
+..
+.de pdf*href-F
+.\"do nothing
+..
+.\"
+.de pdf*href.set
+.\" ----------------------------------------------------------------------
+.\" ----------------------------------------------------------------------
+.ie \\n(.$ \{\
+. \"
+. \" a marker name has been supplied ...
+. \" if we are formatting for immediate output,
+. \" emit PDFMARK code to establish the associated view
+. \"
+. ie '\\n(.z'' \{\
+. pdf:href.sety
+. pdfmark /Dest /\\$1 /View [\\*[PDFHREF.VIEW]] /DEST
+. ds PDFHREF.NAME \\$1
+. rr PDFPAGE.Y
+. \}
+. \"
+. \" but, when formatting a diversion ...
+. \" delay output of the PDFMARK code, until the diversion
+. \" is eventually written out
+. \"
+. el \!.\\$0 \\$@
+. \"
+. \}
+.el \{\
+. \" marker is unnamed ...
+. \" issue error message; do not emit reference data
+. \"
+. pdf:warn pdfhref destination marker must be named
+. \}
+..
+.\"
+.de pdf*href
+.\" ------------------------------------------------------------------
+.\" Usage:
+.\" .pdf*href class [options ...] [link text ...]
+.\" ------------------------------------------------------------------
+.\"
+.\" First, we initialise an empty string, which will be affixed to
+.\" the end of the "link text". (This is needed to cancel the effect
+.\" of a "\c" escape, which is placed at the end of the "link text"
+.\" to support the "-A" option -- any text supplied by the user, when
+.\" the "-A" option is specified, will replace this empty string).
+.\"
+.ds pdf:href-A
+.\"
+.\" Now we interpret, and remove any specified options from the
+.\" argument list. (Note that only options with a declared handler
+.\" will be processed; there is no provision for detecting invalid
+.\" options -- anything which is not recognised is assumed to start
+.\" the "link text" component of the argument list).
+.\"
+.while dpdf:href.opt\\$1 \{\
+. pdf:href.opt\\$1 \\$@
+. shift \\n[pdf:href.argc]
+. \}
+.\"
+.\" If we found "--", to mark the end of the options, then we should
+.\" discard it.
+.\"
+.if '\\$1'--' .shift
+.\"
+.\" All PDF link classes REQUIRE a named destination. This may have
+.\" been supplied using the "-D Name" option, but, if not, deduce it
+.\" from the first "word" in the "link text", if any -- if we still
+.\" can't identify the destination, then set "pdf:href.ok" to zero,
+.\" so this link will not be created.
+.\"
+.if !dpdf:href-D .pdf:href.option -D \\$1
+.if '\\*[pdf:href-D]'' \{\
+. pdf:error pdfhref has no destination
+. nr pdf:href.ok 0
+. \}
+.\"
+.\" Now, initialise a string, defining the PDFMARK code sequence
+.\" to create the reference, using the appropriate type indicators.
+.\"
+.ds pdf:href.link /Subtype /Link \\*[pdf*href.link]
+.\"
+.\" And now, we have no further use for "pdf*href.link".
+.\"
+.rm pdf*href.link
+.\"
+.\" If the user specified any "link prefix" text, (using the "-P text"
+.\" option), then emit it BEFORE processing the "link text" itself.
+.\"
+.if dpdf:href-P \&\\*[pdf:href-P]\c
+.ie \\n[pdf:href.ok] \{\
+. \"
+. \" This link is VALID (so far as we can determine) ...
+. \" Modify the "link text" argument specification, as required,
+. \" to include any pre-formatted cross reference information
+. \"
+. ie \\n(.$ \{\
+. \"
+. \" One or more "link text" argument(s) are present,
+. \" so, set the link description from the argument(s) ...
+. \"
+. ds PDFHREF.DESC \\\\$*
+. \}
+. el \{\
+. ie dpdf:look(\\*[pdf:href-D]) .ds PDFHREF.DESC \\*[pdf:look(\\*[pdf:href-D])]
+. el .ds PDFHREF.DESC Unknown
+. \}
+. \" Apply border and colour specifications to the PDFMARK string
+. \" definition, as required.
+. \"
+. if dPDFHREF.BORDER .as pdf:href.link " /Border [\\*[PDFHREF.BORDER]]
+. if dPDFHREF.COLOUR .as pdf:href.link " /Color [\\*[PDFHREF.COLOUR]]
+. \"
+. \" Emit the "link text", in its appropriate colour, marking the
+. \" limits of its bounding box(es), as the before and after output
+. \" text positions.
+. \"
+. if dPDFHREF.COLOUR .defcolor pdf:href.colour rgb \\*[PDFHREF.COLOUR]
+. nr pdf:bm.width \\w'\\*[PDFHREF.DESC]'
+. nop \&\m[\\*[PDFHREF.TEXT.COLOUR]]\X'pdf: markstart \\n[rst] \\n[rsb] \\*[pdf:href.link]'\\*[PDFHREF.DESC]\X'pdf: markend'\m[]\c
+. \"
+. \" Clean up the temporary registers and strings, used to
+. \" compute the "hot-spot" bounds, and format the reference,
+. \"
+. rm PDFHREF.DESC PDFHREF.TEXT
+. \}
+.\"
+.\" But when we identify an INVALID link ...
+.\" We simply emit the "link text", with no colour change, no border,
+.\" and no associated "hot-spot".
+.\"
+.el \&\\$*\c
+.\"
+.\" And then, if the user specified any affixed text, (using the
+.\" "-A text" option), we tack it on at the end.
+.\"
+.nop \&\\*[pdf:href-A]
+..
+.\" Macro "pdf*href-I" is used for one time initialisation of special
+.\" "pdfhref" features; (currently, only the above page trap hook is
+.\" supported, but it is implemented with one level of indirection, to
+.\" accommodate possible future expansion).
+.
+.de pdf*href-I
+.\" ----------------------------------------------------------------------
+.\" Usage:
+.\" .pdfhref I -<option> <optarg> [-<option> <optarg>] ...
+.\" ----------------------------------------------------------------------
+.\"
+.\" Loop over all arguments, in pairs ...
+.
+.while \\n(.$ \{\
+. \"
+. \" handing them off to their respective initialisers,
+. \" when suitable initialisers exist, or complaining otherwise.
+. \"
+. ie dpdf*href\\$1.init .pdf*href\\$1.init \\$2
+. el .pdf*error pdfhref:init: unknown feature '\\$1'
+. shift 2
+. \}
+..
+.\" Before we can use the page break "hook", we need to initialise it
+.\" as an addendum to a regular page break trap. To ensure that we don't
+.\" compromise the user's page trap setup, we leave the onus for this
+.\" initialisation with the user, but we provide the "pdf*href-PT.init"
+.\" macro, (invoked by ".pdfhref I -PT <macro-name>"), to implement a
+.\" suitable initialisation action.
+.\"
+.\"
+.\" "pdf*href-L" is the generic handler for creating references to
+.\" named destinations in PDF documents. It supports both local
+.\" references, to locations within the same document, through its
+.\" "pdf*href-L.link" attribute, and also references to locations
+.\" in any other PDF document, through "pdf*href-L.file".
+.\"
+.als pdf*href-L pdf*href
+.ds pdf*href-L.link /Dest /\\\\*[pdf:href-D]
+.ds pdf*href-L.file /Action /GoToR \\\\*[pdf:href.files] \\*[pdf*href-L.link]
+.\"
+.\" "pdf*href-O" is the "official" handler for creating PDF
+.\" document outlines. It is simply an alias to "pdfbookmark",
+.\" which may also be invoked directly, if preferred. Neither
+.\" a "pdf*href-O.link" nor a "pdf*href-O.file" attribute is
+.\" required.
+.\"
+.als pdf*href-O pdfbookmark
+.\"
+.\" "pdf*href-W" is the generic handler for creating references to
+.\" web resources, (or any resource specified by a uniform resource
+.\" identifier). Such resource links are fully specified by the
+.\" "pdf*href-W.link" attribute.
+.\"
+.als pdf*href-W pdf*href
+.ds pdf*href-W.link /Action << /Subtype /URI /URI (\\\\*[pdf:href-D]) >>
+.nr pdf:bm.nl 0
+.\"
+.\" "pdfmarksuspend" and "pdfmarkrestart" should be used in any page trap
+.\" macros to prevent output from the page trap macro being considered part
+.\" of a 'hot spot' when it crosses a page boundary.
+.de pdfmarksuspend
+.nop \!x X pdf: marksuspend
+..
+.de pdfmarkrestart
+.nop \!x X pdf: markrestart
+..
+.\"
+.\" pdf.tmac: end of file / vim: ft=groff
diff --git a/tmac/troffrc b/tmac/troffrc
index 470f2627..553ab240 100644
--- a/tmac/troffrc
+++ b/tmac/troffrc
@@ -10,6 +10,7 @@
.\" The groff command defines the .X string if the -X option was given.
.ie r.X .do ds troffrc!ps Xps.tmac
.el .do ds troffrc!ps ps.tmac
+.do ds troffrc!pdf pdf.tmac
.do ds troffrc!dvi dvi.tmac
.do ds troffrc!X75 X.tmac
.do ds troffrc!X75-12 X.tmac
@@ -25,7 +26,7 @@
.do if d troffrc!\*[.T] \
. do mso \*[troffrc!\*[.T]]
.do rm troffrc!ps troffrc!Xps troffrc!dvi troffrc!X75 troffrc!X75-12 \
-troffrc!X100 troffrc!X100-12 troffrc!lj4 troff!lbp troffrc!html
+troffrc!X100 troffrc!X100-12 troffrc!lj4 troff!lbp troffrc!html troffrc!pdf
.
.\" Test whether we work under EBCDIC and map the no-breakable space
.\" character accordingly.