diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
commit | d318611dd6f23fcfedd50e9b9e24620b102ba96a (patch) | |
tree | 8b9eef82ca40fdd5a8deeabf07572074c236095d /contrib/pdfmark | |
parent | Initial commit. (diff) | |
download | groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.tar.xz groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.zip |
Adding upstream version 1.23.0.upstream/1.23.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/pdfmark')
-rw-r--r-- | contrib/pdfmark/ChangeLog | 683 | ||||
-rw-r--r-- | contrib/pdfmark/PROBLEMS | 32 | ||||
-rw-r--r-- | contrib/pdfmark/README | 57 | ||||
-rw-r--r-- | contrib/pdfmark/TODO | 60 | ||||
-rw-r--r-- | contrib/pdfmark/cover.ms | 70 | ||||
-rw-r--r-- | contrib/pdfmark/pdfmark.am | 99 | ||||
-rw-r--r-- | contrib/pdfmark/pdfmark.ms | 2831 | ||||
-rw-r--r-- | contrib/pdfmark/pdfmark.tmac | 1953 | ||||
-rw-r--r-- | contrib/pdfmark/pdfroff.1.man | 981 | ||||
-rw-r--r-- | contrib/pdfmark/pdfroff.sh | 682 | ||||
-rw-r--r-- | contrib/pdfmark/sanitize.tmac | 170 | ||||
-rw-r--r-- | contrib/pdfmark/spdf.tmac | 328 |
12 files changed, 7946 insertions, 0 deletions
diff --git a/contrib/pdfmark/ChangeLog b/contrib/pdfmark/ChangeLog new file mode 100644 index 0000000..1c5a18f --- /dev/null +++ b/contrib/pdfmark/ChangeLog @@ -0,0 +1,683 @@ +2023-02-18 G. Branden Robinson <g.branden.robinson@gmail.com> + + * pdfmark.am (uninstall-pdfmark-hook): Simplify uninstallation. + Try to remove the configured `pdfdocdir` in the event it is + empty, but do not fail if it isn't. (It can be a directory + shared with other groff components; we don't know in what order + the uninstall targets will serialize, but the last one run + should succeed.) + +2022-09-20 G. Branden Robinson <g.branden.robinson@gmail.com> + + * sanitize.tmac: Move comment to where escape sequences are + recognized. Problem arose in commit 058b63ce3d, 2021-09-04. + + troff:.../contrib/pdfmark/sanitize.tmac:162: warning: macro '\"' + not defined + +2022-05-20 G. Branden Robinson <g.branden.robinson@gmail.com> + + * pdfmark.am: Rename `BUILD_PDFDOC` to `USE_GROPDF`. + +2022-03-30 G. Branden Robinson <g.branden.robinson@gmail.com> + + * pdfmark.am: Eliminate `PDFMARK_TFLAG` and `PDFMARK_PFLAG` Make + macros; they were expanded in only one place. + (PDFROFF): Track rename of Make macro `TFLAG` to `MFLAG`. + +2022-03-30 G. Branden Robinson <g.branden.robinson@gmail.com> + + * cover.ms: Die horribly if `PSPIC` call fails. + +2022-03-26 G. Branden Robinson <g.branden.robinson@gmail.com> + + * pdfmark.am (PDFDOCFILES): Replace hard-coded "gnu.eps" + file name with expansion of `DOC_GNU_EPS`. + (contrib/pdfmark/pdfmark.pdf): Pass `-I` option to pdfroff(1) to + enable location of "gnu.eps" file. + +2021-10-24 Keith Marshall <keith.d.marshall@ntlworld.com> + + Adapt to accommodate global XH and XN implementations. + + cf. <https://savannah.gnu.org/bugs/?58946#comment13> + + * spdf.tmac (XH-INIT, XN-INIT, XH-UPDATE-TOC): Delete definitions; + the defaults, provided by s.tmac, are now sufficient. + (XH-REPLACEMENT, XN-REPLACEMENT): Define these, rather than... + (XH, XN): ...these, respectively. + +2021-10-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + Make a minor layout adjustment. + + * pdfmark.ms (Section 2.4.3): Add a vertical space reservation, to + avoid a widow line at the end of the paragraph explaining use of... + (PDFHREF.Y): ...this computed register, in the definition of... + (PDFBOOKMARK.VIEW): ...this. + +2021-10-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + Clarify references to use of the -Tpdf option. + + * pdfmark.ms (Section 2, Section 3.1): Add footnotes, indicating that + only "-Tps" and "-Tpdf" output formats are supported, and that "-Tpdf" + may avoid a separate step, to convert from PostScript to PDF. + +2021-10-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + Work around misplacement of link "hot-spots" in footnotes. + + * pdfmark.ms (pdfhref-nobreak): New document-local macro; used instead + of "pdfhref", this forces paragraph adjustment before placement of any + unbreakable link text, for which line-wrap may be required. Currently + observed only within footnotes, without adjustment, the "hot-spot" for + the link may be placed 1v above its associated text. + +2021-10-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + Link footnote reference marks to footnote text. + + * pdfmark.ms (FP): Redefine locally; replace s.tmac default. + (FF): Do not redefine; our FP replacement macro does not use it. + [d FS-MARK] (FS-MARK): Redefine locally; map it to... + (pdf:fn.mark): ...this locally defined macro. + [!d FS-MARK] (@FS): Rename s.tmac implementation as... + (pdf:fn.record): ...this, then redefine @FS itself, to call... + (pdf:fn.mark, pdf:fn.record): ...these, in respective order. + (groff-1.19.1, GhostScript-8.14): Update footnote reference syntax. + (Ghostscript-8.14, MSYS): Emulate sentence space after footnote mark. + (*): Replace s.tmac string definition; make it equivalent to "\c", + after renaming its original implementation as... + (pdf:fn.index): ...this; synchronize references with changes to... + (pdf:fn.index.count): ...this new locally defined register; it is + auto-incremented by one, as each footnote is placed. + +2021-10-01 Keith Marshall <keith.d.marshall@ntlworld.com> + + Incorporate user-defined TOC leader style. + + * pdfmark.ms: Make some comment tidy-up adjustments. + (TC-LEADER, TC-MARGIN): Define them, to take advantage of new + s.tmac features; cf. <https://savannah.gnu.org/bugs/?61157>. + +2021-09-18 Keith Marshall <keith.d.marshall@ntlworld.com> + + Factor a further unnecessary macro out of spdf.tmac + + * spdf.tmac (XR): Remove it; relocate it to... + * pdfmark.ms (XR): ...here. + +2021-09-13 Keith Marshall <keith.d.marshall@ntlworld.com> + + Add comments to annotate locally-defined font change macros. + + * pdfmark.ms (EM): Annotate this locally-defined emphasis macro... + (CWB, CWI, CWBI): ...these constant width font interpolation macros... + (=): ...and this locally-defined IP tag variant string. + +2021-09-13 Keith Marshall <keith.d.marshall@ntlworld.com> + + Update defunct internet URL references. + + * pdfmark.ms (pdfmark-manual): Adobe moved the document (again); + update the document reference macro, to follow the URL relocation. + (www.mingw.org): The MinGW Project has relinquished this domain; + update the URL reference, to follow web-site relocation to... + (mingw.osdn.io): ...here. + +2021-09-13 Keith Marshall <keith.d.marshall@ntlworld.com> + + Factor document-specific bloat out of spdf.tmac + + * spdf.tmac: Reorganize; add many comments. + (XN): Retained, but reimplemented, to serve as... + (XH, XN): ...both of these; add callback hooks for... + (XH-INIT, XN-INIT, XH-UPDATE-TOC): ...these; provide no-op stubs; + factor out TOC collection code, delegating to XH-UPDATE-TOC. + (opt*XN-N, opt*XN-S, opt*XN-X): Rename internal macros to... + (de spdf:XH-N, de spdf:XH-S, de spdf:XH-X): ...these, respectively. + (AN, @AN, IE, IS, LU, NN, PXREF, SAME-PAGE, XM): Delete; we do not + require these; if users do, they should define their own. + (pdf@toc): Delete internal macro; fold body into... + (TC): ...this. + + * pdfmark.ms (XH-UPDATE-TOC): Implement callback; it is based on... + (XN): ...original implementation of this, factored out of spdf.tmac, + but with significant simplification, to remove unnecessary code. + (XNVS1, XNVS2, XNVS3): Tighten vertical spacing. + +2021-09-04 Keith Marshall <keith.d.marshall@ntlworld.com> + + Reduce potential for user-space exposure of "ms" internals. + + * spdf.tmac (@NH): Append to s.tmac macro; assign... + (spdf:nh*hl): ...this new internal register; alias it to... + (.NH): ...this new public name, hence making it track... + (nh*hl): ...this s.tmac internal numeric register. + (XN): Use \n[.NH] instead of \n[nh*hl]. + +2021-09-03 Keith Marshall <keith.d.marshall@ntlworld.com> + + Sanitize text for use in PDF document outlines. + + * sanitize.tmac: New file; it implements... + (sanitize): ...this new macro; interprets its first argument as a + string name, and copies its remaining arguments to the named string, + discarding specific embedded troff escape sequences; currently... + (\F): ...only this is identified as "specifically discardable". + + * pdfmark.am (TMACFILES): Add sanitize.tmac + + * spdf.tmac (mso): Include sanitize.tmac + (xn*ref, xn*argc): Rename all occurrences... + (spdf:refname, spdf:argc): ...to these, respectively. + (XN): Stop inserting $* directly into PDF outlines; instead, use... + (spdf:bm.text): ...this new string; this is locally defined by... + (spdf:bm.define): ...this new macro; passed the original $* from + XN, this itself, is locally defined as a redirectable alias for... + (spdf:bm.basic): ...this new local macro; it simply copies $*, + passed from XN, to the string named by its first argument, (which is + always spdf:bm.text), so reproducing previous behaviour. + (opt*XN-S): New macro; defined for internal use only, it adds a "-S" + option to XN, such that, when specified, it temporarily redirects... + (spdf:bm.define): ...this macro mapping alias to... + (sanitize): ...this. + + * pdfmark.ms (XN): Add "-S" option for all headings which include... + (\F[C]...\F[]): ...this escape sequence. + +2021-08-21 Keith Marshall <keith.d.marshall@ntlworld.com> + + Define, and use registered trade mark strings. + + * pdfmark.ms (Adobe, Acrobat, Distiller, PostScript, Microsoft): + Define as strings. Each expands to its own name, followed by the + registered trademark symbol, as a superscript, and optional trailing + punctuation, below the superscript. Use each as required. + +2021-08-21 Keith Marshall <keith.d.marshall@ntlworld.com> + + Prefer "-ize" to "-ise" where etymology permits. + + * pdfmark.ms: For all verbs, and their derivative nouns, for which + British English allows either "-ise" or "-ize" as ending, prefer the + "-ize" form of verb, and "-ization" form of noun, throughout. + +2021-08-20 Keith Marshall <keith.d.marshall@ntlworld.com> + + Correct a spelling error. + + * pdfmark.ms (Section 2.5.3.1): Fix typo: s/exanple/example/. + +2021-08-20 Keith Marshall <keith.d.marshall@ntlworld.com> + + Space out section headings in pdfmark.ms source. + + * pdfmark.ms (.NH): Precede each instance by one null request, to + improve readability. + +2021-08-18 Keith Marshall <keith.d.marshall@ntlworld.com> + + Refine pdfroff "missing ghostscript" diagnostic. + + * pdfroff.sh [$GS = ":"]: Fix typo: s/connot/cannot/; refine text. + +2020-12-25 G. Branden Robinson <g.branden.robinson@gmail.com> + + * pdfmark.am (PDFROFF): Call pdfroff without + `--keep-temporary-files` option. Temporary directories are + created with mktemp(1) and files with an embedded process + identifier, which frustrates reproducible builds. + + See <https://savannah.gnu.org/bugs/?57218>. + +2018-02-28 Werner LEMBERG <wl@gnu.org> + + * pdfmark.am (pdfmark.pdf): Use $(GROFF_V). + +2018-02-28 Werner LEMBERG <wl@gnu.org> + + * pdfmark.am (pdfroff): Use $(AM_V_GEN) to silence file generation. + +2015-08-22 Bernd Warken <groff-bernd.warken-72@web.de> + + * pdfroff.1.man: Rename `pdfroff.man'. + + * pdfmark.am: Add `Last update'. Setup Emacs mode. + +2015-08-05 Bernd Warken <groff-bernd.warken-72@web.de> + + * pdfmark.am: Add `Last update'. Setup Emacs mode. + +2015-04-03 Werner LEMBERG <wl@gnu.org> + + * pdfroff.man: Make it work in compatibility mode. + +2014-10-14 Keith Marshall <keith.d.marshall@ntlworld.com> + + Deduce "--no-toc-relocation" from input stream (revisited). + + * pdfroff.sh (WRKFILE): Correct malformed sed expression. + + * spdf.tmac (TC): Prefer value of pdfroff's PHASE register to defined + state of pdf:href.map, when choosing to emit control record to... + (toc_relocation): ...enable this. + +2014-10-13 Keith Marshall <keith.d.marshall@ntlworld.com> + + Deduce "--no-toc-relocation" from input stream. + + * pdfroff.sh (WRKFILE): Scan it for "pdfroff-option:set" records; + apply settings; check for equivalent of "--no-toc-relocation" option. + + * spdf.tmac (TC): Emit "pdfroff-option:set toc-relocation=enabled". + +2014-10-12 Keith Marshall <keith.d.marshall@ntlworld.com> + + Avoid spurious user visible control messages on stderr. + + * pdfroff.sh (REFCOPY): Ensure that at least one pdfhref mark of type + 'Z' will remain in the reference map, after all references have been + resolved; this is required, to suppress writing of reference control + records to stderr during the final PDF output processing phase. + +2014-09-04 Bernd Warken <groff-bernd.warken-72@web.de> + + * all pdfmark source files: Copying (remove last updates and + replace years with package years) and Emacs setup. + +2014-03-30 Steffen Nurpmeso <sdaoden@yandex.com> + + * Makefile.sub: Put straight error-prevention prefixes for `rm'. + +2014-03-29 Steffen Nurpmeso <sdaoden@yandex.com> + + * Makefile.sub: Handle examples separately, controlled by + $(make{_,_install_,_uninstall_}examples). + +2013-01-28 Deri James <deri@chuzzlewit.myzen.co.uk> + + * pdfmark.tmac (pdfmark, pdf:composed): Use `\!' instead of `\X'. + + With the old pdfmark there are gaps between two of the lines, but + with the new version they disappear. The use of `.br' and `.in 0' + is arbitrary any request which causes an implicit break could be + used. Two breaks together only produce one line break, but if there + is an intervening `\X' then the second break finds the line buffer + not empty and generates another line break. + + Using `\!' does alter the position of the pdfmark lines in the + intermediate file sent to grops (the pdfmark lines are output + immediately rather than being serialised through the output line + processing), but this has no effect since the contents of the + pdfmark line stay the same. It is the contents which determine + where bookmarks jump to not the position of the record in the input + stream to grops. + + I initially used `.output', but hit a snag if a pdfbookmark occurs + before the document starts to output (message saying to insert an + explicit `.br'), this is quite likely for things like `.pdfinfo + /Author' which occur at the top of the document. So I'm using the + `\!' escape. + +2012-09-20 Werner LEMBERG <wl@gnu.org> + + Simplify enviroment handling. + + Suggested by Ivan Shmakov <oneingray@gmail.com>. + + * Makefile.sub (PDFROFF): Don't use export. + +2011-12-26 Mike Frysinger <vapier@gentoo.org> + + Fix parallel build race failure. + + Sometimes building in parallel will fail in the pdfmark directory: + + make[2]: Entering directory '.../contrib/pdfmark' + rm -f pdfroff + rm -f pdfmark.pdf + sed -f ... ./pdfroff.sh >pdfroff + ...; ./pdfroff ... pdfmark.ms >pdfmark.pdf + /bin/sh: ./pdfroff: Permission denied + chmod +x pdfroff + make[2]: *** [pdfmark.pdf] Error 126 + + This is because the generated pdf files use the local generated + pdfroff helper script, but they don't depend directly upon it, so + make tries to create the two in parallel and randomly falls over. + + * Makefile.sub: Have all the .pdf files explicitly depend on the + `pdfroff' helper script. + +2010-12-23 Keith Marshall <keith.d.marshall@ntlworld.com> + + Update copyright notices; pdfmark.tmac bug-fix. + + * pdfmark.tmac: Update copyright notices. + (pdf*href.mark.resolve): Avoid premature removal, by aliasing to... + (pdf*href.mark.begin): ...this, rather than renaming. + + * pdfroff.sh, pdfroff.man: Update copyright notices. + +2010-12-14 Keith Marshall <keith.d.marshall@ntlworld.com> + + Clean up handling of temporary files directory. + + * .cvsignore (pdfroff-*): Ignore sub-directories matching this. + * Makefile.sub (MOSTLCLEANDIRADD): Schedule them for removal. + +2010-12-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + Address potential temporary file security vulnerabilities. + + * pdfroff.sh (GROFF_TMPDIR): Use mktemp(1) to assign it, if possible; + fall back to ${TMPDIR}, ${TMP} or ${TEMP} if unsuccessful. + * pdfroff.man: Document it. + +2009-08-16 Colin Watson <cjwatson@debian.org> + + Make pdfroff's GhostScript invocation safer. + + * pdfroff.sh (PDFROFF_POSTPROCESSOR_COMMAND): Add `-dSAFER' option. + * pdfroff.man: Document it. + +2008-12-28 Keith Marshall <keith.d.marshall@ntlworld.com> + + Avoid phantom line wrapping in pdfhref hot-spots. + + * pdfmark.tmac (pdf*href.mark.end): Emit hot-spot end markers within + scope of `\Z', to prevent possible output line length overflow which + may occur only in the layout computation passes, but not in the final + output pass. Problem observed and identified by Nick Stoughton; it + causes some hot-spots to be displaced from their proper locations. + +2007-04-11 Keith Marshall <keith.d.marshall@ntlworld.com> + + Avoid stray newlines in folded pdfmark literal content. + + * pdfmark.tmac (pdf*pdfmark.dispatch.wrapped): New string; define it + when accumulating long literal content; make it undefined otherwise. + (PDFMARK.FOLDWIDTH, PDFMARK.FOLDWIDTH.MAX): Reserve space for two + extra characters, to accommodate a space and an escaped newline, + while accumulating literal content, in case folding is required. + (pdf*pdfmark.dispatch) [pdf*pdfmark.dispatch.wrapped]: Add them. + +2007-04-11 Keith Marshall <keith.d.marshall@ntlworld.com> + + * pdfmark.tmac (pdfbookmark): Don't evaluate within diversions; defer + placement until diversion is copied out at top level. + +2007-02-06 Eric S. Raymond <esr@snark.thyrsus.com> + + * pdfroff.man: Update .UR/.UE and .MT/.ME to latest changes in + an-ext.tmac. + +2007-01-30 Werner LEMBERG <wl@gnu.org> + + * pdfroff.man: Updated. + +2007-01-21 Werner LEMBERG <wl@gnu.org> + + * pdfroff.man: Revised, based on a patch from Eric Raymond. It now + uses the new macros from an-ext.tmac. This is the first of a series + of man patches which Eric has contributed. + +2006-07-30 Keith Marshall <keith.d.marshall@ntlworld.com> + + * pdfroff.sh (PDFROFF_KILL_NULL_PAGES): Require `%%BeginPageSetup' on + PostScript output line immediately following `%%Page:'. + +2006-07-29 Keith Marshall <keith.d.marshall@ntlworld.com> + + * pdfroff.sh (PDFROFF_KILL_NULL_PAGES): Require `sed' to match a more + explicit regular expression, for detection of redundant pages. + +2006-07-14 Keith Marshall <keith.d.marshall@ntlworld.com> + + * pdfroff.sh (PDFWRITE): Local shell variable replaced... + (PDFROFF_POSTPROCESSOR_COMMAND): by this new environment variable... + (GROFF_GHOSTSCRIPT_INTERPRETER): with this bound to it. + (PDFROFF_COLLATE, PDFROFF_KILL_NULL_PAGES): New environment variables. + (--no-kill-null-pages): New command line option; implement it, and... + (--help): Add description for it. + + * pdfroff.man (PDFROFF_POSTPROCESSOR_COMMAND): Document it. + (PDFROFF_COLLATE, PDFROFF_KILL_NULL_PAGES): Document them. + (--no-kill-null-pages): Document it. + +2006-07-14 Zvezdan Petkovic <zpetkovic@acm.org> + + * pdfroff.sh (--emit-ps): New command line option; implement it. + (--help): Add description for it. + + * pdfroff.man (--emit-ps): Document it. + +2006-06-11 Werner LEMBERG <wl@gnu.org> + + * pdfroff.man: Add `.ig' block after NAME section to make mandb + happy. + +2006-03-31 Keith Marshall <keith.d.marshall@ntlworld.com> + + Split `pdfmark' output as required, to avoid excessively long + `ps:exec' intermediate output records. + + * pdfmark.tmac (pdfmark): Macro extended to deploy ... + (pdf*pdfmark.limit): New macro; use it to define ... + (PDFMARK.FOLDWIDTH, PDFMARK.FOLDWIDTH.MAX): New registers. + (pdf*compose.first, pdf*compose.next, pdf*compose.literal): New + macros; each will be aliased as required to ... + (pdf*compose): ... this, to dynamically construct ... + (pdf:composed.line, pdf:composed.literal): ... these new strings. + (pdf:compose.test): New dynamically constructed string; use it to + detect parenthesised literals in pdfmark content, so folding can be + avoided within them, subject to honouring of `PDFMARK.FOLDWIDTH'. + (pdf*length.increment): New macro; it triggers output folding when ... + (pdf:length): ... this new register exceeds `PDFMARK.FOLDWIDTH.MAX'. + (pdf*pdfmark.post.first, pdf*pdfmark.post.next): New macros; each will + be aliased as required to ... + (pdf*pdfmark.post): ... this, and invoked by ... + (pdf*pdfmark.dispatch): ... this new macro; use it to define ... + (pdf:composed): ... this dynamically constructed macro; use ... + (pdf*end): ... this new macro to terminate it. + +2006-03-09 Keith Marshall <keith.d.marshall@ntlworld.com> + + Incorporate portability recommendations by Ralf Wildenhues + <ralf.wildenhues@gmx.de> + + * pdfroff.sh: Avoid unsafe quoting in variable substitutions of + the form "${VAR+"set"}"; remove outer quotes everywhere; prefix + with `x' on each side of comparisons. + ($NULLCMD): Define when `$ZSH_VERSION' is set, i.e. when host + has `/bin/sh -> zsh'; also... + (emulate sh): Invoke, for this case. + + Enhancement/bug fix requested by Werner LEMBERG <wl@gnu.org> + + * pdfroff.sh (--help): Direct output to `stdout', not `stderr'. + (--keep-temporary-files): New option; implement it. + + * pdfroff.man (OPTIONS): Document `--keep-temporary-files' option. + (FILES): Note names and purpose of files it affects. + + * Makefile.sub (PDFROFF): Add `--keep-temporary-files' option; + retain them in `GROFF_TMPDIR=.'. + (CLEANADD): Include temporary files matching `pdf[0-9]*'. + +2006-03-08 Werner LEMBERG <wl@gnu.org> + + * pdfmark.ms: Update URL for Adobe Reference Manual. + +2006-02-26 Claudio Fontana <claudio@gnu.org> + + * Makefile.sub: Add DESTDIR to install and uninstall targets + to support staged installations. + +2006-02-25 Werner LEMBERG <wl@gnu.org> + + * pdfmark.ms: Correct typo; reported by Thomas Klausner. + +2006-02-24 Werner LEMBERG <wl@gnu.org> + + * pdfmark.ms, pdfroff.sh: Replace legal/illegal with valid/invalid. + +2005-06-22 Keith Marshall <keith.d.marshall@ntlworld.com> + + pdfroff.sh portability enhancement. + + * pdfroff.sh (ARGLIST): Variable removed. + (GROFF_STYLE): Use it for all groff invocations. + (INPUT_FILES): Pass to all groff invocations, instead of ARGLIST. + (CS_MACRO, CE_MACRO): Initialize independently. + (CS_FILTER): Simplify quoting; it used to confuse some shells. + (Source): CVS keyword removed; replaced by... + (RCSfile, Revision): these. + +2005-06-17 Keith Marshall <keith.d.marshall@ntlworld.com> + + * pdfroff.sh (MATCH): Correct quoting. + (Source): Add terminating `$' on CVS keyword. + +2005-06-17 Zvezdan Petkovic <zpetkovic@acm.org> + + * Makefile.sub: (RM): Define as `rm -f', for `make' programs + which don't predefine it. + +2005-06-16 Bernd Warken <groff-bernd.warken-72@web.de> + + * pdfroff.sh (NULLDEV): Correct misspelled instance of NULDEV. + +2005-05-28 Werner LEMBERG <wl@gnu.org> + + * Makefile.sub (.ms.pdf): Use `--stylesheet', not `--style'. + +2005-05-26 Werner LEMBERG <wl@gnu.org> + + * Makefile.sub, pdfmark.tmac, pdfroff.sh, spdf.tmac: Update postal + address for Free Software Foundation. + +2005-05-17 Keith Marshall <keith.d.marshall@ntlworld.com> + + Improve portability of `pdfroff' shell script. + + * pdfroff.sh: Add space in shebang, conforming to portability + guidelines in `autoconf' docs. + (searchpath): New shell function; use it instead of `type' command + to locate prerequisite helper programs. + + * pdfroff.man: Document influence of `OSTYPE' and `PATH_SEPARATOR' + environment variables. + + * Makefile.sub (pdfroff): Make it depend on SH_DEPS_SED_SCRIPT, + from arch/misc/shdeps.sh; use it to customize PATH_SEPARATOR + initialization code for `searchpath' function in pdfroff.sh. + +2005-05-16 Keith Marshall <keith.d.marshall@ntlworld.com> + + Interim documentation update. + + * pdfmark.ms (GROFF-WEBSITE): New string; use it in references and + examples. + (Section 2.5): Add definitions of D and Z operators, for use with + pdfhref macro. + (Section 2.5.4): Complete description of pdfhref macro usage for + `Linking to Internet Resources'; provide examples. + +2005-05-14 Nick Stoughton <nick@usenix.org> + + * pdfmark.tmac (LB): Renamed to ... + (PDFLB): This to avoid conflicts with mm's LB macro. + +2005-05-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + Handle parsing anomalies in Cygwin's `ash', and similar, shells. + + * pdfroff.sh ($CAT, $GREP, $SED, $GROFF, $DIFF): Avoid interpreting + misdirected error messages, which `type' sends to `stdout' in some + shells, as a successful program file match. + + ($AWK, $GS): Likewise; also ensure that multiple choice match + prototypes are eval'ed as such, in case token splitting occurs + before variable expansion. + +2005-04-24 Keith Marshall <keith.d.marshall@ntlworld.com> + + Add support for folded outlines in PDF documents. + + * pdfmark.tmac (PDFOUTLINE.FOLDLEVEL): New register. + (pdf:bm.emit): Use it. + + * pdfmark.ms: Document it. + +2005-03-25 Werner LEMBERG <wl@gnu.org> + + * Makefile.in: Removed. + +2005-03-24 Werner LEMBERG <wl@gnu.org> + + * Makefile: Renamed to... + * Makefile.in: This. + +2005-03-22 Keith Marshall <keith.d.marshall@ntlworld.com> + + * pdfroff.sh: Eliminate invalid program reference to $AWK, when + invoked with `--no-reference-dictionary' option. + +2005-03-02 Keith Marshall <keith.d.marshall@ntlworld.com> + + * contrib/pdfmark/Makefile.sub (install_data): Use $(INSTALL_SCRIPT) + to install `pdfroff'. + * contrib/pdfmark/pdfroff.man (opte): New macro. + Use it to remove spurious equal signs from SYNOPSIS. + +2005-02-28 Keith Marshall <keith.d.marshall@ntlworld.com> + + Provide `pdfroff' shell script, and manpage to document it; + runs multiple groff passes, to format PDF documents. + + * pdfroff.sh: New shell script template; + * pdfroff.man: New man page to document it. + + Integrate `pdfmark' into normal groff build system; + install macro `pdfmark' packages, build and install `pdfroff', + and PDF format documentation. + + * Makefile.sub: Rewritten. + * pdfmark.tmac: Modified. + (pdfhref): New macro operators, `D' and `Z'. + (pdf*href-D, pdf*href-Z): New macros: implement them. + (pdf*href.mark.resolve, pdf*href.mark.emit, pdf*href.mark.flush): + Modified macro algorithm, to eliminate inconsistencies between + `grohtml' representations of `opminy' from differing groff versions. + (pdf*href.mark, pdf*href.mark.release, pdf*href.mark.close): + deleted (redundant macros). + (PDFHREF.LEADING): Default value changed (was 2.5p; now -1.0p). + Global comment updates. + + * TODO: Updated. + +2004-12-10 Werner LEMBERG <wl@gnu.org> + + * TODO: Updated. + +2004-12-08 Keith Marshall <keith.d.marshall@ntlworld.com> + + First import of pdfmark files. + +________________________________________________________________________ + +Copyright (C) 2004-2020 Free Software Foundation, Inc. + +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. + +Local Variables: +fill-column: 72 +mode: change-log +version-control: never +End: +vim:set autoindent textwidth=72: diff --git a/contrib/pdfmark/PROBLEMS b/contrib/pdfmark/PROBLEMS new file mode 100644 index 0000000..d31be4a --- /dev/null +++ b/contrib/pdfmark/PROBLEMS @@ -0,0 +1,32 @@ + -*- text -*- + Copyright (C) 2004-2020 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. + +Known PROBLEMS in pdfmark.tmac +============================== + +Bounding boxes for link hot-spots which straddle a page break +are not computed correctly. + +*** Resolved: 06-Dec-2004 (KDM): pdfmark.tmac.patch-20041206 *** + +-------- + +Documents including a large number of cross references may fail, +with an 'input stack limit exceeded' error. + +*** Resolved: 27-Sep-2004 (KDM): pdfmark.tmac.patch-20040927 *** + +-------- + +Links placed in diversions, such as footnotes or floating keeps, +resolve to the wrong destinations; (mapping order becomes confused +between links in diversion, and links in running text following +the diversion). + +-------- + +Annotations placed by .pdfnote cannot exceed about 200 chars. diff --git a/contrib/pdfmark/README b/contrib/pdfmark/README new file mode 100644 index 0000000..7440ffd --- /dev/null +++ b/contrib/pdfmark/README @@ -0,0 +1,57 @@ + -*- text -*- + Copyright (C) 2004-2020 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. + +README for pdfmark.tmac +======================= + +Copyright (C) 2004, 2009 Free Software Foundation Inc. +Contributed by Keith Marshall (keith.d.marshall@ntlworld.com) + +This is free software. See file COPYING, for copying permissions, +and warranty disclaimer. + +This is a preview release of a proposed pdfmark.tmac macro package, +for use with GNU troff (groff). It is not yet complete, and should +be considered as an alpha release; there are a few problems to be +resolved (see file PROBLEMS). + +Partial documentation is provided, in groff-ms format. To convert +this to PDF format, you will require a working groff installation, +a working ghostscript installation, with the gs command in your PATH, +and a GNU-compatible make. The tarball should be unpacked in the +top directory of your groff source tree, then: + + cd <groff-current>/contrib/pdfmark + make pdfmark + +where <groff-current> is the top directory of your current groff +source tree. + +Included in this package, are: + + pdfmark.tmac -- the core pdfmark macro set + spdf.tmac -- a rudimentary set of bindings for ms macros + pdfmark.ms -- preliminary documentation + cover.ms -- a template for the documentation cover sheet + gnu.eps -- the groff logo, copied from the groff distribution + Makefile -- makefile, for formatting the documentation + README -- this file + PROBLEMS -- a list of known problems + TODO -- a list of planned features, not yet implemented + +To make the pdfmark macros generally usable, copy pdfmark.tmac to the +'site-tmac' directory appropriate to your groff installation; (ms users +may also wish to copy spdf.tmac). The macros may then be accessed, by +including the '-mpdfmark' option on the groff command line; (for ms +users, '-mspdf' is equivalent to '-ms -mpdfmark', with some extra +macros 'thrown in'). + +Comments, and bug reports are welcomed. Please post to the groff +mailing list, groff@gnu.org; (you must be subscribed to this list to +post mails). To subscribe, visit + + http://lists.gnu.org/mailman/listinfo/groff diff --git a/contrib/pdfmark/TODO b/contrib/pdfmark/TODO new file mode 100644 index 0000000..219b60e --- /dev/null +++ b/contrib/pdfmark/TODO @@ -0,0 +1,60 @@ + -*- text -*- + Copyright 2004-2020 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. + +TODO items for pdfmark.tmac +=========================== + +Add copyright information to PDF documentation. + +-------- + +Add acknowledgements and trade mark ownership notifications +to PDF documentation. + +-------- + +Provide documentation in man page and texinfo formats. + +-------- + +Add comments in spdf.tmac, to clarify its operation. +Also add commentary in pdfmark.tmac, to clarify operation of +recent changes. + +-------- + +Make Makefile generic, so 'configure' can resolve target +system dependencies. + +* Comment added 2005-02-26 by Keith Marshall <keith.d.marshall@ntlworld.com> + +If this refers to contrib/pdfmark/Makefile, then it is addressed by the new +'pdfroff' script; the original Makefile may be considered redundant. Local +system dependencies are resolved by 'configure', and applied to 'pdfroff', +when it is generated from 'pdfroff.sh'. + +-------- + +Improve Makefile.sub, to integrate pdfmark.tmac installation +into a regular groff build. Add it to groff's Makefile.in. + +* Comment added 2005-02-26 by Keith Marshall <keith.d.marshall@ntlworld.com> + +Completed. + +-------- + +Provide a 'pdfmark' script (or call it 'groff2pdf'?) which +actually converts a groff input file to pdf, and which +takes care of the necessary intermediate steps to handle +PDF marks. + +* Comment added 2005-02-26 by Keith Marshall <keith.d.marshall@ntlworld.com> + +This facility now provided by 'pdfroff' script; documented in 'pdfroff.man'. +Man page still requires an additional section, to describe use of 'stylesheet' +feature. Script also requires documentation in PDF and texinfo formats. diff --git a/contrib/pdfmark/cover.ms b/contrib/pdfmark/cover.ms new file mode 100644 index 0000000..b647a29 --- /dev/null +++ b/contrib/pdfmark/cover.ms @@ -0,0 +1,70 @@ +.\" Copyright (C) 2004-2020 Free Software Foundation, Inc. +.\" +.\" Copying and distribution of this file, with or without modification, +.\" are permitted in any medium without royalty provided the copyright +.\" notice and this notice are preserved. +.\" +.am pspic*error-hook +. ab \\n[.F]:\\n[.c]: fatal error: PSPIC failed to include '\\$1' +.. +.de CS +.if !rCO .nr CO 0 +.if !rTL .nr TL 0 +.\".nr PO*SAVED \\n[PO] +.nr LL*SAVED \\n[LL] +.nr HM*SAVED \\n[HM] +.nr HM 0 +.nr PO (2.1c+\\n[CO]u) +.nr LL 17.1c +\& +.nr PS*SAVED \\n[PS] +.nr VS*SAVED \\n[VS] +.nr PS 24 +.nr VS 30 +.CD +.fam T +.sp |(5.9c+\\n[TL]u) +.als AU au@first +.. +.de au@first +.sp 1.5v +.als AU au@next +.AU \\$@ +.. +.de au@next +.DE +.nr PS 18 +.nr VS 18 +.CD +.sp 0.5v +\\$* +.. +.de AI +\H'-4z'\\$*\H'0' +.. +.de CE +.DE +.sp |17.5c +.PSPIC gnu.eps +.nr PS 19 +.CD +.fam H +.tkf HR 10z 2p 20z 4p +\H'-4z'A GNU MANUAL\H'0' +.DE +.\".nr PO \\n[PO*SAVED] +.nr LL \\n[LL*SAVED] +.nr PS \\n[PS*SAVED] +.nr VS \\n[VS*SAVED] +.nr HM \\n[HM*SAVED] +.\".rr PO*SAVED +.rr LL*SAVED +.rr PS*SAVED +.rr VS*SAVED +.rr HM*SAVED +.fam +.. +.\" Local Variables: +.\" mode: nroff +.\" End: +.\" vim: filetype=groff: diff --git a/contrib/pdfmark/pdfmark.am b/contrib/pdfmark/pdfmark.am new file mode 100644 index 0000000..15eec8c --- /dev/null +++ b/contrib/pdfmark/pdfmark.am @@ -0,0 +1,99 @@ +# Copyright (C) 2005-2021 Free Software Foundation, Inc. +# Written by Keith Marshall (keith.d.marshall@ntlworld.com) +# Automake migration by Bertrand Garrigues +# +# 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/>. + +pdfmark_srcdir = $(top_srcdir)/contrib/pdfmark +pdfmark_builddir = $(top_builddir)/contrib/pdfmark + +man1_MANS += contrib/pdfmark/pdfroff.1 + +bin_SCRIPTS += pdfroff + +# Files installed in $(tmacdir) +TMACFILES = \ + contrib/pdfmark/pdfmark.tmac \ + contrib/pdfmark/sanitize.tmac \ + contrib/pdfmark/spdf.tmac +pdfmarktmacdir = $(tmacdir) +dist_pdfmarktmac_DATA = $(TMACFILES) + +# Files installed in $(pdfdocdir) +PDFDOCFILES = \ + contrib/pdfmark/pdfmark.pdf +if USE_PDFROFF +pdfmarkpdfdocdir = $(pdfdocdir) +nodist_pdfmarkpdfdoc_DATA = $(PDFDOCFILES) +MOSTLYCLEANFILES += $(PDFDOCFILES) +else +EXTRA_DIST += $(PDFDOCFILES) +endif + +EXTRA_DIST += \ + contrib/pdfmark/cover.ms \ + contrib/pdfmark/pdfmark.ms \ + contrib/pdfmark/ChangeLog \ + contrib/pdfmark/README \ + contrib/pdfmark/PROBLEMS \ + contrib/pdfmark/TODO \ + contrib/pdfmark/pdfroff.1.man \ + contrib/pdfmark/pdfroff.sh + +PDFROFF=\ + GROFF_TMPDIR=. \ + GROFF_COMMAND_PREFIX= \ + GROFF_BIN_DIR="$(GROFF_BIN_DIR)" \ + GROFF_BIN_PATH="$(GROFF_BIN_PATH)" \ + ./pdfroff \ + $(FFLAG) $(MFLAG) -dpaper=$(PAGE) -P-p$(PAGE) -M$(pdfmark_srcdir) + +contrib/pdfmark/pdfmark.pdf: contrib/pdfmark/pdfmark.ms + $(GROFF_V)$(MKDIR_P) `dirname $@` \ + && $(PDFROFF) -I $(doc_builddir) -I $(doc_srcdir) -mspdf \ + --stylesheet=$(pdfmark_srcdir)/cover.ms \ + $(top_srcdir)/contrib/pdfmark/pdfmark.ms >$@ + +# The pdf files use the local script to generate. +$(PDFDOCFILES): pdfroff groff troff gropdf +$(PDFDOCFILES): $(dist_devpsfont_DATA) $(nodist_devpsfont_DATA) \ + $(DOC_GNU_EPS) + +pdfroff: contrib/pdfmark/pdfroff.sh $(SH_DEPS_SED_SCRIPT) + $(AM_V_GEN)sed -f $(SH_DEPS_SED_SCRIPT) \ + -e "s|[@]VERSION[@]|$(VERSION)|" \ + -e "s|[@]GROFF_AWK_INTERPRETERS[@]|$(ALT_AWK_PROGS)|" \ + -e "s|[@]GROFF_GHOSTSCRIPT_INTERPRETERS[@]|$(ALT_GHOSTSCRIPT_PROGS)|" \ + -e "s|[@]GROFF_BIN_DIR[@]|$(bindir)|" $(pdfmark_srcdir)/pdfroff.sh \ + >$@ \ + && chmod +x $@ + +mostlyclean-local: mostlyclean_pdfmark +mostlyclean_pdfmark: + rm -rf $(top_builddir)/pdfroff-* + +uninstall_groffdirs: uninstall-pdfmark-hook +uninstall-pdfmark-hook: +if USE_PDFROFF + -rmdir $(DESTDIR)$(pdfmarkpdfdocdir) +endif + + +# Local Variables: +# mode: makefile-automake +# fill-column: 72 +# End: +# vim: set autoindent filetype=automake textwidth=72: diff --git a/contrib/pdfmark/pdfmark.ms b/contrib/pdfmark/pdfmark.ms new file mode 100644 index 0000000..0d7d28c --- /dev/null +++ b/contrib/pdfmark/pdfmark.ms @@ -0,0 +1,2831 @@ +.ig +pdfmark.ms + +This file is part of groff, the GNU roff type-setting system. + +Copyright (C) 2004-2021 Free Software Foundation, Inc. +written by Keith Marshall <keith.d.marshall@ntlworld.com> + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with no Front-Cover Texts, and with no Back-Cover +Texts. + +A copy of the Free Documentation License is included as a file called +FDL in the main directory of the groff source package. +.. +. +.CS +Portable Document Format +Publishing with GNU Troff +.AU Keith Marshall +.AI <keith.d.marshall@ntlworld.com> +.CE +. +.\" Specify the Internet address for the groff web site. +.\" +.ds GROFF-WEBSITE http://www.gnu.org/software/groff +. +.\" Set the PDF default document view attribute, to ensure that the document +.\" outline is visible, each time the document is opened in Acrobat Reader. +.\" +.pdfview /PageMode /UseOutlines +.\" +.\" Initialize the outline view to show only three heading levels, +.\" with additional subordinate level headings folded. +.\" +.nr PDFOUTLINE.FOLDLEVEL 3 +. +.\" Add document identification meta-data +.\" +.pdfinfo /Title Portable Document Format Publishing with GNU Troff +.pdfinfo /Author Keith Marshall +.pdfinfo /Subject Tips and Techniques for Exploiting PDF Features with GNU Troff +.pdfinfo /Keywords groff troff PDF pdfmark +. +.\" Set the default cross reference format to indicate section numbers, +.\" rather than page numbers, when we insert a reference pointer. +.\" +.ds PDFHREF.INFO section \\*[SN-NO-DOT] \\$* +. +.\" Define a macro, to print reference links WITHOUT the usual "see" prefix. +.\" +.de XR-NO-PREFIX +.rn PDFHREF.PREFIX xx +.ds PDFHREF.PREFIX +.XR \\$@ +.rn xx PDFHREF.PREFIX +.. +. +.\" Define a string, to insert a Registered Trade Mark symbol as +.\" a superscript... +.\" +.ds rg \*{\(rg\*} +.\" +.\" ...and use it to define strings, representing frequently used +.\" registered trade marks. +.\" +.ds Adobe "Adobe\Z'\\$1'\*(rg\" +.ds Acrobat "Acrobat\Z'\\$1'\*(rg\" +.ds Distiller "Distiller\Z'\\$1'\*(rg\" +.ds PostScript "PostScript\Z'\\$1'\*(rg\" +.\" +.ds Microsoft "Microsoft\Z'\\$1'\*(rg\" +. +.\" Establish the page layout. +.\" +.nr PO 2.5c +.nr LL 17.0c +.nr LT 17.0c +.nr DI 5n +.nr HY 0 +. +.\" Within the table of contents, the width of the right-hand margin, +.\" in which space is reserved for the display of page numbers, and the +.\" appearance of the leaders which precede it, are controlled by:- +.\" +.char \[TC-LEADER] \h'0.8n'. +.nr TC-MARGIN \w'00000' +. +.\" Generate headers in larger point sizes, for NH levels < 4, +.\" with point size increasing by 1.5p, for each lesser NH level. +.\" +.nr GROWPS 4 +.nr PSINCR 1.5p +. +. +.\" Implement an interface with the FS macro (from s.tmac) to facilitate +.\" placement of footnote reference marks, with each serving as an active +.\" pdfhref link to the associated footnote itself. +.\" +.de pdf:fn.mark nr +.\" Macro to replace original duty performed by "\**"; must be invoked +.\" at point of footnote mark placement, e.g. by FS, BEFORE recording of +.\" the associated text within the footnote diversion is commenced. +.\" +.ie \\n[.$] \{\ +. pdfhref L -D pdf:fn\\$1 -- \\$2 +. pdfhref M -N pdf:fn\\$1r +. \} +.\" +.\" s.tmac does not publicly expose its auto-incrementing footnote index; +.\" to avoid a dependency on an undocumented internal feature, we create +.\" our own counter, while keeping the internal index synchronized, by +.\" interpolating a renamed "\**", each time we increment our counter. +.\" +.el .\\$0 \\n+[pdf:fn.index.count] \\*[pdf:fn.index] +.nr pdf:fn.index.count 0 1 +.rn * pdf:fn.index +.ds * \c +. +.\" For versions of s.tmac which support the FS-MARK callback hook, it +.\" is sufficient for us to answer the callback request. +.\" +.\" FIXME: in time, we may be able to unconditionally assume that this +.\" callback hook will be supported... +.\" +.ie d FS-MARK .als FS-MARK pdf:fn.mark +.el \{\ +.\" ...but in the interim, we may need to redefine s.tmac's FS macro, +.\" (actually the @FS internal macro, rather than FS itself), to gain +.\" an effect equivalent to taking control of FS-MARK, to achieve the +.\" placement of a footnote mark as an active pdfhref link. +.\" +.rn @FS pdf:fn.record +.de @FS +.pdf:fn.mark +.pdf:fn.record +.. +.\} +.\" Override s.tmac's (undocumented) footnote output hook; this emulates +.\" the default output style for \n[FF] == 3 footnotes, with the footnote +.\" number formatted as a pdfhref link back to the position at which the +.\" footnote marker appears, within the document text. +.\" +.de FP +.LP +.nr pdf:fn.tag.width (u;2*\\n[FI]) +.ds pdf:fn.tag \s'-1.5p'\\$1.\s'+1.5p' +.pdfhref M -N pdf:fn\\$1 +.in +\\n[pdf:fn.tag.width]u +.ti -\\n[pdf:fn.tag.width]u +.nr pdf:fn.tag.width -\\w'\\*[pdf:fn.tag]'u +.pdfhref L -D pdf:fn\\$1r -A \\h'\\n[pdf:fn.tag.width]u'\c -- \\*[pdf:fn.tag] +.. +.de pdfhref-nobreak +.\" FIXME: I've only noticed this anomaly when planting pdfhref links +.\" within footnotes; if the start of the link text is placed near the +.\" line length limit, and all of it is moved to the start of the next +.\" line, the "hot-spot" region is computed to be one line higher than +.\" it should be; ending the preceding input line with "\c", and then +.\" invoking pdfhref via this wrapper, works around this issue. +.\" +.ie \\n[.l]-\\n[.i]-\\n[.k]-\\w'\\$\\n[.$]' \& +.el \p +.pdfhref \\$* +.. +. +.\" Define a local macro to facilitate choice of style for emphasis; +.\" by default, make it equivalent to the ms standard "I" macro. +.\" +.de EM +.\".I "\s'+0.3'\\$1\s0" "\\$2" "\\$3" +.I \\$@ +.. +.\" Also, define variations on the ms standard "CW" macro, to add +.\" bold, italic, and both styles to constant width text; note that +.\" each of these accept two additional arguments, in comparison to +.\" standard "CW", such that \$1 specifies the text which is to be +.\" styled, \$2 and \$3 specify inner after/before bracketting, to +.\" set as regular "CW" text, while \$4 and \$5 become equivalent +.\" to \$2 and \$3 of standard "CW", acting as outer bracketting. +.\" +.de CWB +\\$5\fC\\$3\fP\f(CB\\$1\fP\fC\\$2\fP\\$4 +.. +.de CWI +\\$5\fC\\$3\fP\f(CI\\$1\fP\fC\\$2\fP\\$4 +.. +.de CWBI +\\$5\fC\\$3\fP\f[CBI]\\$1\fP\fC\\$2\fP\\$4 +.. +.\" Finally, augment this group with a variant string, which may be +.\" used to set constant width tags on "IP" paragraphs, with \$1 set +.\" as if by "CWB", followed by an optional suffix set as if by "CWBI", +.\" and with the suffix bracketted by \$3 after and \$4 before, each +.\" set in the regular "CW" style. +.\" +.ds = \f(CB\\$1\f(CR\\$4\f[CBI]\\$2\f(CR\\$3 +. +.\" Additionally, add a cross-reference convenience macro, emulating +.\" the style of the "ms" font change macros... +.\" +.\" .XR <dest-name> [<affixed> [<prefix>]] +.\" +.\" ...such that, when invoked with one, two, or three arguments, this +.\" expands to the equivalent of: +.\" +.\" .pdfhref L -D <dest-name> [-A <affixed> [-P <prefix>]] +.\" +.\" to place a pdfhref reference link, to a named destination, within +.\" the same document, using the reference text which is predefined in +.\" the reference dictionary entry associated with the destination. +.\" +.de XR +.if \\n(.$ \{\ +. if \\n[OPMODE] \{\ +. ds xr!argv -D "\\$1" +. if \\n(.$>1 .as xr!argv " -A "\\$2" +. if \\n(.$>2 .as xr!argv " -P "\\$3" +. pdfhref L \\*[xr!argv] +. rm xr!argv +. \} +. \} +.. +. +.NH 1 +.\" Conventionally, in "ms", NH precedes text which is to be set as a +.\" numbered section heading, but it makes no provision for automatic +.\" reference to that heading in a table of contents, or (in the case +.\" of PDF document production) in a document outline. Both of these +.\" limitations may be mitigated, by using the XN macro, (provided by +.\" spdf.tmac), which sets its arguments, both as text to be included +.\" in the section heading, as printed, and as an assocated document +.\" outline reference; it will also make this same text available to +.\" the user-specified callback macro, XH-UPDATE-TOC, whereby it may +.\" be used, e.g. to construct a table of contents entry. +.\" +.\" Within the table of contents, structural layout will be achieved, +.\" under the direction of the following spacing control constants: +.\" +.ds XNVS1 0.50v \" leading for top level +.ds XNVS2 0.15v \" leading at nesting level increment +.ds XNVS3 0.30v \" leading following nested group +.\" +.\" Note that one TOC related callback hook is shared by both XH and +.\" XN; its is called XH-UPDATE-TOC, regardless of whether called by +.\" XH or by XN; when called by XN, it is invoked with arguments: +.\" +.\" .XH-UPDATE-TOC <outline-level> <section-number> <text> ... +.\" +.de XH-UPDATE-TOC +.\" This implementation of XH-UPDATE-TOC utilizes the rudimentary ms +.\" mechanism for formatting a table of contents, using XS and XE to +.\" bracket individual entries. +. XS +. \" A local register, tc*hl, is used to track the outline level +. \" of each TOC entry, as it is added; it is not defined, until +. \" the first entry is recorded... +. \" +. if r tc*hl \{\ +. \" ...after which, we use it to establish indentation, +. \" to reflect changes in outline level. +. \" +. ie \\$1>1 \{\ +. \" When at any outline level greater than one, +. \" any level increment will be offset by XNVS2 +. \" units of vertical space... +. \" +. ie \\$1>\\n[tc*hl] .sp \\*[XNVS2] +. +. \" ...whereas any decrement will be offset by +. \" XNVS3 units. +. \" +. el .if \\n[tc*hl]>\\$1 .sp \\*[XNVS3] +. \} +. +. \" ...but every top-level entry, after the first, is +. \" offset by XNVS1 units. +. \" +. el .sp \\*[XNVS1] +. \} +. +. \" \$1 becomes the effective outline level for the current table +. \" of contents entry, but we must ensure that it is one or more. +. \" +. ie \\$1 .nr tc*hl \\$1 +. el .nr tc*hl 1 +. +. \" The current outline level determines the indentation at which +. \" we place the section number reference... +. \" +. nop \h'\\n[tc*hl]-1m'\\$2\c +. +. \" ...after which we discard \$1 and \$2, allowing us to append +. \" all remaining arguments, ensuring that there is at least 0.5n +. \" of following space, before the first leader dot. +. \" +. shift 2 +. nop \h'1.5n'\\$*\h'0.5n' +. XE +.. +.XN Introduction +.\" +.\" If using an old s.tmac, without the SN-NO-DOT extension, ensure +.\" that we get SOMETHING in section number references. +.\" +.if !d SN-NO-DOT .als SN-NO-DOT SN +.LP +It might appear that it is a fairly simple matter to +produce documents in \*[Adobe]\~\(lqPortable\~Document\~Format\(rq, +commonly known as PDF, using +.CW groff ) GNU\~Troff\~( +as the document formatter. +Indeed, +.CW groff 's +default output format is the native \*[Adobe]\~\*[PostScript] format, +which PDF producers such as \*[Adobe] \*[Acrobat] \*[Distiller ,] +or GhostScript, expect as their input format. +Thus, the PDF production process would seem to entail simply +formatting the document source with +.CW groff , +to produce a \*[PostScript] version of the document, +which can subsequently be processed by \*[Acrobat] \*[Distiller] +or GhostScript, to generate the final PDF document. +.LP +For many PDF production requirements, +the production cycle described above may be sufficient. +However, this is a limited PDF production method, +in which the resultant PDF document represents no more than +an on screen image of the printed form of the document, if +.CW groff 's +\*[PostScript] output were printed directly. +.LP +The Portable Document Format provides a number of features, +which significantly enhance the experience of reading a document on screen, +but which are of little or no value to a document which is merely printed. +It +.EM is +possible to exploit these PDF features, which are described in the \*[Adobe] +.de pdfmark-manual pdfmark-manual +.\" This is an example of a resource reference specified by URI ... +.\" We may need to refer often to the Adobe pdfmark Reference Manual, +.\" so we create the internet link definition using a macro, to make +.\" it reusable. +.\" +.\" Note also, that we protect the description of the reference by +.\" preceding it with "--", to avoid "invalid character in name" type +.\" error messages from groff (caused by the use of "\~"). +.\" +.pdfhref W -D https://www.adobe.com/go/acrobatsdk_pdfmark \ + -P \(lq -A \(rq\\$1 -- pdfmark\~Reference\~Manual +.pdfmark-manual , +with some refinement of the simple PDF production method, provided +appropriate \(lqfeature implementing\(rq instructions can be embedded into +.CW groff 's +\*[PostScript] rendering of the document. +This, of course, implies that the original document source, which +.CW groff +will process to generate the \*[PostScript] description of the document, +must include appropriate markup to exploit the desired PDF features. +It is this preparation of the +.CW groff +document source to exploit a number of these features, +which provides the principal focus of this document. +.LP +The markup techniques to be described have been utilized in the production of +the PDF version of this document itself. +This has been formatted using +.CW groff 's +.CW ms +macro package; +thus, usage examples may be found in the document source file, +.CW \n(.F , +to which comments have been added, +to help identify appropriate markup examples for implementing PDF features, +such as:\(en +.QS +.IP \(bu +Selecting a default document view, which defines how the document will appear +when opened in the reader application; for example, when this document is +opened in \*[Acrobat]\~Reader, it should display the top of the cover sheet, +in the document view pane, while a document outline should appear to the left, +in the \(lqBookmarks\(rq pane. +.IP \(bu +Adding document identification \(lqmeta\(hydata\(rq, +which can be accessed, in \*[Acrobat]\~Reader, +by inspecting the \(lqFile\^/\^Document\~Properties\^/\^Summary\(rq. +.IP \(bu +Creating a document outline, which will be displayed in the \(lqBookmarks\(rq +pane of \*[Acrobat]\~Reader, such that readers may quickly navigate to any +section of the document, simply by clicking on the associated heading +in the outline view. +.IP \(bu +Embedding active links in the body of the document, such that readers may +quickly navigate to related material at another location within the same +document, or in another PDF document, or even to a related Internet resource, +specified by its URI. +.IP \(bu +Adding annotations, in the form of \(lqsticky notes\(rq, at strategic +points within the PDF document. +.QE +.LP +All of the techniques described have been tested on +.EM both +GNU/Linux, and on \*[Microsoft] Windows\(tm2000 operating platforms, using +.CW groff +.CW 1.19.1 ,\** +.FS +Later versions should, and some earlier versions may, be equally suitable. +See\c +.pdfhref-nobreak W \*[GROFF-WEBSITE] +for information and availability of the latest version. +.FE +in association with +.CW AFPL +.CW GhostScript +.CW 8.14 .\** +.FS +Again, other versions may be suitable. +See\c +.pdfhref-nobreak W http://ghostscript.com +for information and availability. +.FE +\& +Other tools employed, which should be readily available on +.EM any +Unix\(tm +or GNU/Linux system, are +.CW sed , +.CW awk +and +.CW make , +together with an appropriate text editor, for creating and marking up the +.CW groff +input files. +These additional utilities are not provided, as standard, +on the \*[Microsoft] Windows\(tm platform, +but several third party implementations are available. +Some worth considering include the MKS\*(rg\~Toolkit,\** +.FS +A commercial offering; see\c +.pdfhref-nobreak W http://mkssoftware.com/products/tk/default.asp +for information. +.FE +Cygwin,\** +.FS +A +.EM free +but comprehensive +.SM +POSIX +.LG +emulation environment and +Unix\(tm +toolkit for \%32\(hybit \*[Microsoft] Windows\(tm platforms; see\c +.pdfhref-nobreak W http://cygwin.com +for information and download. +.FE +or MSYS.\** +.FS +Another free, but minimal suite of common +Unix\(tm +tools for \%32\(hybit \*[Microsoft] Windows\(tm, available for download from\c +.pdfhref-nobreak W -A ; https://mingw.osdn.io +it +.EM does +include those tools listed above, +and is the package which was actually used when performing the Windows\(tm2000 +platform tests referred to in the text. +.FE +\& +This list is by no means exhaustive, and should in no way be construed as an +endorsement of any of these packages, nor to imply that other similar packages, +which may be available, are in any way inferior to them. +.bp +. +.NH 1 +.\" We may wish a section heading to represent a named destination, +.\" so that we can create a linked reference to it, from some other +.\" part of the PDF document, (or even from another PDF document). +.\" +.\" Here we use the "-N" option of the "XN" macro, to create a named +.\" PDF link destination, at the location of the heading. Notice that +.\" we also use the "--" marker to separate the heading text from the +.\" preceding option specification; it is not strictly necessary in +.\" this case, but it does help to set off the heading text from the +.\" option specification. +.\" +.XN -N pdf-features -- Exploiting PDF Document Features +.LP +To establish a consistent framework for adding PDF features, a +.CW groff +macro package, named +.CW pdfmark.tmac , +has been provided. +Thus, to incorporate PDF features in a document, +the appropriate macro calls, as described below, may be placed in the +.CW groff +document source, which should then be processed with a +.CW groff +command of the form\** +.FS +.pdfhref M pdf-features-fn +.nr pdf-features-fn \n[fn*text-num] +Note that, +if any +.CW -T \^\c +.CWI dev +option is specified, +it should be either +.CW -T \^\c +.CW ps , +or +.CW -T \^\c +.CW pdf ; +any other explicit choice is unlikely to be compatible with +.CW -m \|\|\c +.CW pdfmark , +and will have an unpredictable +(possibly erroneous) +effect on the output. +If no +.CW -T \^\c +.CWI dev +option is specified, +(in which case +.CW -T \^\c +.CW ps +is implicitly assumed), +or if +.CW -T \^\c +.CW ps +is explicitly specified, +then the output will be produced in \*[PostScript] format, +and will require conversion to PDF, +(e.g. by using GhostScript tools); +explicit specification of +.CW -T \^\c +.CW pdf +will result in direct output in PDF format, +thus obviating the need for conversion. +.FE +.QP +.fam C +groff [-Tps\h'0.2p'|\h'0.2p'-Tpdf] [-m\F[]\|\|\FC\c +.I name ] +-m\F[]\|\|\FC\c +.B pdfmark +.I options \F[]\|\|\FC\c [- +.I file \F[]\|\|\FC\c "...] " +\&... +.LP +It may be noted that the +.CW pdfmark +macros have no dependencies on, and no known conflicts with, +any other +.CW groff +macro package; thus, users are free to use any other macro package, +of their choice, to format their documents, while also using the +.CW pdfmark +macros to add PDF features. +. +.NH 2 +.XN -S -N pdfmark-operator -- The \F[C]pdfmark\F[] Operator +.LP +All PDF features are implemented by embedding instances of the +.B \F[C]pdfmark\F[] +operator, as described in the \*[Adobe] +.pdfmark-manual , +into +.CW groff 's +\*[PostScript] output stream. +To facilitate the use of this operator, the +.CW pdfmark +macro package defines the primitive +.CW pdfmark +macro; it simply emits its argument list, +as arguments to a +.CW pdfmark +operator, in the \*[PostScript] output stream. +.LP +.pdfhref M -N pdfmark-example +To illustrate the use of the +.CW pdfmark +macro, the following is a much simplified example of how a bookmark +may be added to a PDF document outline +.QP +.CW ".pdfmark \e" +.RS 4 +.nf +.fam C +/Count 2 \e +/Title (An Example of a Bookmark with Two Children) \e +/View [/FitH \en[PDFPAGE.Y]] \e +/OUT +.RE +.LP +In general, users should rarely need to use the +.CW pdfmark +macro directly. +In particular, the above example is too simple for general use; it +.EM will +create a bookmark, but it does +.EM not +address the issues of setting the proper value for the +.CW /Count +key, nor of computing the +.CW PDFPAGE.Y +value used in the +.CW /View +key. The +.CW pdfmark +macro package includes a more robust mechanism for creating bookmarks, +.\" +.\" Here is an example of how a local reference may be planted, +.\" using the automatic formatting feature of the "pdfhref" macro. +.\" +.\" This is a forward reference to the named destination "add-outline", +.\" which is defined below, using the "XN" wrapper macro, from the +.\" "spdf.tmac" macro package. The automatically formatted reference +.\" will be enclosed in parentheses, as specified by the use of +.\" "-P" and "-A" options. +.\" +.pdfhref L -P ( -A ), -D add-outline +.\" +which addresses these issues automatically. +Nevertheless, the +.CW pdfmark +macro may be useful to users wishing to implement more advanced PDF features, +than those currently supported directly by the +.CW pdfmark +macro package. +. +.NH 2 +.XN -N docview -- Selecting an Initial Document View +.LP +By default, +when a PDF document is opened, +the first page will be displayed, +at the default magnification set for the reader, +and outline and thumbnail views will be hidden. +When using a PDF reader, +such as \*[Acrobat]\~Reader, +which supports the +.CW /DOCVIEW +class of the +.CW pdfmark +operator, +these default initial view settings may be overridden, +using the +.CW pdfview +macro. +For example +.QP +.CW ".pdfview /PageMode /UseOutlines" +.LP +will cause \*[Acrobat]\~Reader to open the document outline view, +to the left of the normal page view, +while +.QP +.CW ".pdfview /PageMode /UseThumbs" +.LP +will open the thumbnail view instead. +.LP +Note that the two +.CW /PageMode +examples, above, are mutually exclusive \(em it is not possible to have +.EM both +outline and thumbnail views open simultaneously. +However, it +.EM is +permitted to add +.CW /Page +and +.CW /View +keys, to force the document to open at a page other than the first, +or to change the magnification at which the document is initially displayed; +see the +.pdfmark-manual +for more information. +.LP +It should be noted that the view controlling meta\(hydata, defined by the +.CW pdfview +macro, is not written immediately to the \*[PostScript] output stream, +but is stored in an internal meta\(hydata \(lqcache\(rq, +(simply implemented as a +.CW groff +diversion). +This \(lqcached\(lq meta\(hydata must be written out later, by invoking the +.CW pdfsync +macro, +.\" +.\" Here is another example of how we may introduce a forward reference. +.\" This time we are using the shorter notation afforded by the "XR" macro +.\" provided by "spdf.tmac"; this example is equivalent to the native +.\" "pdfmark.tmac" form +.\" .pdfhref L -D pdfsync -P ( -A ). +.\" +.XR pdfsync ). ( +. +.NH 2 +.XN -N docinfo -- Adding Document Identification Meta-Data +.LP +In addition to the +.CW /DOCVIEW +class of meta\(hydata described above, +.XR docview ), ( +we may also wish to include document identification meta\(hydata, +which belongs to the PDF +.CW /DOCINFO +class. +.LP +To do this, we use the +.CW pdfinfo +macro. +As an example of how it is used, +the identification meta\(hydata attached to this document +was specified using a macro sequence similar to:\(en +.DS I +.CW +\&.pdfinfo /Title PDF Document Publishing with GNU Troff +\&.pdfinfo /Author Keith Marshall +\&.pdfinfo /Subject How to Exploit PDF Features with GNU Troff +\&.pdfinfo /Keywords groff troff PDF pdfmark +.DE +Notice that the +.CW pdfinfo +macro is repeated, once for each +.CW /DOCINFO +record to be placed in the document. +In each case, the first argument is the name of the applicable +.CW /DOCINFO +key, which +.EM must +be named with an initial solidus character; +all additional arguments are collected together, +to define the value to be associated with the specified key. +.LP +As is the case with the +.CW pdfview +macro, +.XR docview ), ( +the +.CW /DOCINFO +records specified with the +.CW pdfinfo +macro are not immediately written to the \*[PostScript] output stream; +they are stored in the same meta\(hydata cache as +.CW /DOCVIEW +specifications, until this cache is explicitly flushed, +by invoking the +.CW pdfsync +macro, +.XR pdfsync ). ( +. +.NH 2 +.XN -N add-outline -- Creating a Document Outline +.LP +A PDF document outline comprises a table of references, +to \(lqbookmarked\(rq locations within the document. +When the document is viewed in an \(lqoutline\~aware\(rq PDF document reader, +such as \*[Adobe] \*[Acrobat] Reader, +this table of \(lqbookmarks\(rq may be displayed in a document outline pane, +or \(lqBookmarks\(rq pane, to the left of the main document view. +Individual references in the outline view may then be selected, +by clicking with the mouse, +to jump directly to the associated marked location in the document view. +.LP +The document outline may be considered as a collection of \(lqhypertext\(rq +references to \(lqbookmarked\(rq locations within the document. +The +.CW pdfmark +macro package provides a single generalized macro, +.CW pdfhref , +for creating and linking to \(lqhypertext\(rq reference marks. +This macro will be described more comprehensively in a later section, +.XR pdfhref ); ( +the description here is restricted to its use for defining document outline entries. +. +.NH 3 +.XN -N basic-outline -- A Basic Document Outline +.LP +In its most basic form, the document outline comprises a structured list of headings, +each associated with a marked location, or \(lqbookmark\(rq, in the document text, +and a specification for how that marked location should be displayed, +when this bookmark is selected. +.LP +To create a PDF bookmark, the +.CW pdfhref +macro is used, +at the point in the document where the bookmark is to be placed, +in the form +.QP +.fam C +.B ".pdfhref O" +.I level > < +.I "descriptive text ..." +.LP +in which the reference class +.CWB O \& \& \(rq \(lq +stipulates that this is an outline reference. +.LP +Alternatively, for those users who may prefer to think of a document outline +simply as a collection of bookmarks, the +.CW pdfbookmark +macro is also provided \(em indeed, +.CW pdfhref +invokes it, when processing the +.CWB O \& \& \(rq \(lq +reference class operator. +It may be invoked directly, in the form +.QP +.fam C +.B .pdfbookmark +.I level > < +.I "descriptive text ..." +.LP +Irrespective of which of the above macro forms is employed, the +.CWI level > < +argument is required. +It is a numeric argument, defining the nesting level of the \(lqbookmark\(rq +in the outline hierarchy, with one being the topmost level. +Its function may be considered analagous to the +.EM "heading level" +of the document's section headings, +for example, as specified with the +.CW NH +macro, if using the +.CW ms +macros to format the document. +.LP +All further arguments, following the +.CWI level > < +argument, are collected together, to specify the heading text which will appear +in the document's outline view. +Thus, the outline entry for this section of this document, +which has a level three heading, +might be specified as +.QP +.CW +\&.pdfhref O 3 \*(SN A Basic Document Outline +.LP +or, in the alternative form using the +.CW pdfbookmark +macro, as +.QP +.CW +\&.pdfbookmark 3 \*(SN A Basic Document Outline +. +.NH 3 +.XN Hierarchical Structure in a Document Outline +.LP +When a document outline is created, using the +.CW pdfhref +macro as described in +.\" +.\" Here is an example of how we can temporarily modify the format of +.\" a reference link, in this case to indicate only the section number +.\" of the link target, in the form "section #", (or, if we define +.\" "SECREF.BEGIN" before the call, its content followed by the +.\" section number). +.\" +.\" We first define a macro, which will get the reference data from +.\" pdfhref, as arguments, and will return the formatted output, as we +.\" require it, the string "PDFHREF.TEXT". +.\" +.de SECREF +.while \\n(.$ \{\ +. ie '\\$1'section' \{\ +. if !dSECREF.BEGIN .ds SECREF.BEGIN \\$1 +. ds PDFHREF.TEXT \\*[SECREF.BEGIN]\~\\$2 +. rm SECREF.BEGIN +. shift \\n(.$ +. \} +. el .shift +. \} +.. +.\" We now tell "pdfhref" to use our formatting macro, in place of +.\" its builtin default formatter, before we specify the reference. +.\" +.pdfhref F SECREF +.pdfhref L -A , -D basic-outline +.\" +.\" At this point, we would normally revert the "pdfhref" formatter +.\" to use its default, built in macro. However, in this particular +.\" case, we want to use our custom format one more time, before we +.\" revert it, so we will omit the reversion step this time. +.\" +and any entry is added at a nesting level greater than one, +then a hierarchical structure is automatically defined for the outline. +However, as was noted in the simplified +.pdfhref L -D pdfmark-example -- example +in +.pdfhref L -A , -D pdfmark-operator +.\" +.\" And now, we revert to default "pdfhref" formatting behaviour, +.\" by completing the call we delayed above. +.\" +.pdfhref F +.\" +the data required by the +.CW pdfmark +operator to create the outline entry may not be fully defined, +when the outline reference is defined in the +.CW groff +document source. +Specifically, when the outline entry is created, its +.CW /Count +key must be assigned a value equal to the number of its subordinate entries, +at the next inner level of the outline hierarchy; +typically however, +these subordinate entries will be defined +.EM later +in the document source, and the appropriate +.CW /Count +value will be unknown, when defining the parent entry. +.LP +To resolve this paradox, the +.CW pdfhref +macro creates the outline entry in two distinct phases \(em +a destination marker is placed in the \*[PostScript] output stream immediately, +when the outline reference is defined, +but the actual outline entry is stored in an internal \(lqoutline cache\(rq, +until its subordinate hierarchy has been fully defined; +it can then be inserted in the output stream, with its +.CW /Count +value correctly assigned. +Effectively, to ensure integrity of the document outline structure, +this means that each top level outline entry, and +.EM all +of its subordinates, are retained in the cache, until the +.EM next +top level entry is defined. +.LP +One potential problem, which arises from the use of the \(lqoutline cache\(rq, +is that, at the end of any document formatting run, the last top level outline entry, +and any subordinates defined after it, will remain in the cache, and will +.EM not +be automatically written to the output stream. +To avoid this problem, the user should follow the guidelines given in +.\" +.\" Here is a more conventional example of how to temporarily change +.\" to the format used to display reference links. We will again use +.\" the "SECREF" format, which we defined above, but on this occasion +.\" we will immediately revert to the default format, after the link +.\" has been placed. +.\" +.pdfhref F SECREF +.pdfhref L -D pdfsync -A , +.pdfhref F +.\" +to synchronize the output state with the cache state, +.XR pdfsync ), ( +at the end of the +.CW groff +formatting run. +. +.NH 3 +.XN -N outline-view -- Associating a Document View with an Outline Reference +.LP +Each \(lqbookmark\(rq entry, in a PDF document outline, +is associated with a specific document view. +When the reader selects any outline entry, +the document view changes to display the document context +associated with that entry. +.LP +The document view specification, +to be associated with any document outline entry, +is established at the time when the outline entry is created. +However, rather than requiring that each individual use of the +.CW pdhref +macro, to create an outline entry, +should include its own view specification, +the actual specification assigned to each entry is derived from +a generalized specification defined in the string +.CW PDFBOOKMARK.VIEW , +together with the setting of the numeric register +.CW PDFHREF.VIEW.LEADING , +which determine the effective view specification as follows:\(en +.QS +.IP \*[= PDFBOOKMARK.VIEW] +Establishes the magnification at which the document will be viewed, +at the location of the \(lqbookmark\(rq; by default, it is defined by +.RS +.QP +.CW ".ds PDFBOOKMARK.VIEW /FitH \e\en[PDFPAGE.Y] u" +.RE +.IP +which displays the associated document view, +with the \(lqbookmark\(rq location positioned at the top of the display window, +and with the magnification set to fit the page width to the width of the window. +.IP \*[= PDFHREF.VIEW.LEADING] +Specifies additional spacing, +to be placed between the top of the display window +and the actual location of the \(lqbookmark\(rq on the displayed page view. +By default, it is set as +.RS +.QP +.CW ".nr PDFHREF.VIEW.LEADING 5.0p" +.RE +.IP +Note that +.CW PDFHREF.VIEW.LEADING +does not represent true \(lqleading\(rq, in the typographical sense, +since any preceding text, set in the specified display space, +will be visible at the top of the document viewing window, +when the reference is selected. +.IP +Also note that the specification of +.CW PDFHREF.VIEW.LEADING +is shared by +.EM all +reference views defined by the +.CW pdfhref +macro; whereas +.CW PDFBOOKMARK.VIEW +is applied exclusively to outline references, +there is no independent +.CW PDFBOOKMARK.VIEW.LEADING +specification. +.QE +.LP +If desired, the view specification may be changed, by redefining the string +.CW PDFBOOKMARK.VIEW , +and possibly also the numeric register +.CW PDFHREF.VIEW.LEADING . +Any alternative definition for +.CW PDFBOOKMARK.VIEW +.EM must +be specified in terms of valid view specification parameters, +as described in the \*[Adobe] +.pdfmark-manual . +.LP +Note the use of the register +.CW PDFPAGE.Y , +in the default definition of +.CW PDFBOOKMARK.VIEW +above. +This register is computed by +.CW pdfhref , +when creating an outline entry; +it specifies the vertical position of the \(lqbookmark\(rq, +in basic +.CW groff +units, relative to the +.EM bottom +edge of the document page on which it is defined, +and is followed, in the +.CW PDFBOOKMARK.VIEW +definition, by the +.CW grops +.CW u \(rq \(lq +operator, to convert it to \*[PostScript] units on output. +It may be used in any redefined specification for +.CW PDFBOOKMARK.VIEW , +(or in the analogous definition of +.CW PDFHREF.VIEW , +described in +'ne 2v +.XR-NO-PREFIX pdfhref-view ), +but +.EM not +in any other context, +since its value is undefined outside the scope of the +.CW pdfhref +macro. +.LP +Since +.CW PDFPAGE.Y +is computed relative to the +.EM bottom +of the PDF output page, +it is important to ensure that the page length specified to +.CW troff +correctly matches the size of the logical PDF page. +This is most effectively ensured, +by providing +.EM identical +page size specifications to +.CW groff , +.CW grops +and to the \*[PostScript] to PDF converter employed, +and avoiding any page length changes within the document source. +.LP +Also note that +.CW PDFPAGE.Y +is the only automatically computed \(lqbookmark\(rq location parameter; +if the user redefines +.CW PDFBOOKMARK.VIEW , +and the modified view specification requires any other positional parameters, +then the user +.EM must +ensure that these are computed +.EM before +invoking the +.CW pdfhref +macro. +. +.NH 3 +.XN -N outline-folding -- Folding the Outline to Conceal Less Significant Headings +.LP +When a document incorporates many subheadings, +at deeply nested levels, +it may be desirable to \(lqfold\(rq the outline +such that only the major heading levels are initially visible, +yet making the inferior subheadings accessible, +by allowing the reader to expand the view of any heading branch on demand. +.LP +The +.CW pdfmark +macros support this capability, +through the setting of the +.CW PDFOUTLINE.FOLDLEVEL +register. +This register should be set to the number of heading levels +which it is desired to show in expanded form, in the +.EM initial +document outline display; +all subheadings at deeper levels will still be added to the outline, +but will not become visible until the outline branch containing them is expanded. +'ne 5 +For example, the setting used in this document: +.QS +.LD +.fam C +\&.\e" Initialize the outline view to show only three heading levels, +\&.\e" with additional subordinate level headings folded. +\&.\e" +\&.nr PDFOUTLINE.FOLDLEVEL 3 +.DE +.QE +.LP +results in only the first three levels of headings being displayed +in the document outline, +.EM until +the reader chooses to expand the view, +and so reveal the lower level headings in any outline branch. +.LP +The initial default setting of +.CW PDFOUTLINE.FOLDLEVEL , +if the document author does not choose to change it, +is 10,000. +This is orders of magnitude greater than the maximum heading level +which is likely to be used in any document; +thus the default behaviour will be to show document outlines fully expanded, +to display all headings defined, +at all levels within each document. +.LP +The setting of +.CW PDFOUTLINE.FOLDLEVEL +may be changed at any time; +however, the effect of each such change may be difficult to predict, +since it is applied not only to outline entries which are defined +.EM after +the setting is changed, +but also to any entries which remain in the outline cache, +.EM at +this time. +Therefore, it is recommended that +.CW PDFOUTLINE.FOLDLEVEL +should be set +.EM once , +at the start of each document; +if it +.EM is +deemed necessary to change it at any other time, +the outline cache should be flushed, +.XR pdfsync ), ( +.EM immediately +before the change, +which should immediately preceed a level one heading. +. +.NH 3 +.XN -N multipart-outline -- Outlines for Multipart Documents +.LP +When a document outline is created, using the +.CW pdfhref +macro, each reference mark is automatically assigned a name, +composed of a fixed stem followed by a serially generated numeric qualifier. +This ensures that, for each single part document, every outline reference +has a uniquely named destination. +.LP +As the overall size of the PDF document increases, +it may become convenient to divide it into smaller, +individually formatted \*[PostScript] components, +which are then assembled, in the appropriate order, +to create a composite PDF document. +While this strategy may simplify the overall process of creating and +editing larger documents, it does introduce a problem in creating +an overall document outline, +since each individual \*[PostScript] component will be assigned +duplicated sequences of \(lqbookmark\(rq names, +with each name ultimately referring to multiple locations in the composite document. +To avoid such reference naming conflicts, the +.CW pdfhref +macro allows the user to specify a \(lqtag\(rq, +which is appended to the automatically generated \(lqbookmark\(rq name; +this may be used as a discriminating mark, to distinguish otherwise +similarly named destinations, in different sections of the composite document. +.LP +To create a \(lqtagged\(rq document outline, +the syntax for invocation of the +.CW pdfhref +macro is modified, by the inclusion of an optional \(lqtag\(rq specification, +.EM before +the nesting level argument, i.e. +.QP +.fam C +.B ".pdfhref O" +.B -T \& [ +.I tag >] < +.I level > < +.I "descriptive text ..." +.LP +The optional +.CWI tag > < +argument may be composed of any characters of the user's choice; +however, its initial character +.EM "must not" +be any decimal digit, and ideally it should be kept short +\(em one or two characters at most. +.LP +By employing a different tag in each section, +the user can ensure that \(lqbookmark\(rq names remain unique, +throughout all the sections of a composite document. +For example, when using the +.CW spdf.tmac +macro package, which adds +.CW pdfmark +capabilities to the standard +.CW ms +package, +.XR using-spdf ), ( +the table of contents is collected into a separate \*[PostScript] section +from the main body of the document. +In the \(lqbody\(rq section, the document outline is \(lquntagged\(rq, +but in the \(lqTable\~of\~Contents\(rq section, a modified version of the +.CW TC +macro adds an outline entry for the start of the \(lqTable\~of\~Contents\(rq, +invoking the +.CW pdfhref +macro as +.QP +.CW ".pdfhref O -T T 1 \e\e*[TOC]" +.LP +to tag the associated outline destination name with the single character suffix, +.CW T \(rq. \(lq +Alternatively, as in the case of the basic outline, +.XR basic-outline ), ( +this may equally well be specified as +.QP +.CW ".pdfbookmark -T T 1 \e\e*[TOC]" +. +.NH 3 +.XN Delegation of the Outline Definition +.LP +Since the most common use of a document outline +is to provide a quick method of navigating through a document, +using active \(lqhypertext\(rq links to chapter and section headings, +it may be convenient to delegate the responsibility of creating the outline +to a higher level macro, which is itself used to +define and format the section headings. +This approach has been adopted in the +.CW spdf.tmac +package, to be described later, +.XR using-spdf ). ( +.LP +When such an approach is adopted, +the user will rarely, if ever, invoke the +.CW pdfhref +macro directly, to create a document outline. +For example, the structure and content of the outline for this document +has been exclusively defined, using a combination of the +.CW NH +macro, from the +.CW ms +package, to establish the structure, and the +.CW XN +macro from +.CW spdf.tmac , +to define the content. +In this case, +the responsibility for invoking the +.CW pdfhref +macro, to create the document outline, +is delegated to the +.CW XN +macro. +. +.NH 2 +.XN -N pdfhref -- Adding Reference Marks and Links +.LP +.pdfhref F SECREF +.ds SECREF.BEGIN Section +.pdfhref L -D add-outline +.pdfhref F +has shown how the +.CW pdfhref +macro may be used to create a PDF document outline. +While this is undoubtedly a powerful capability, +it is by no means the only trick in the repertoire of this versatile macro. +.LP +The macro name, +.CW pdfhref , +which is a contraction of \(lqPDF HyperText Reference\(rq, +indicates that the general purpose of this macro is to define +.EM any +type of dynamic reference mark, within a PDF document. +Its generalized usage syntax takes the form +.QP +.fam C +.B .pdfhref +.BI class > < +.I "-options ...\&" ] [ +[--] +.I "descriptive text ...\&" ] [ +.LP +where +.CW <\f(CIclass\fP> +represents a required single character argument, +which defines the specific reference operation to be performed, +and may be selected from:\(en +.QS +.IP \*[= O] +Add an entry to the document outline. +This operation has been described earlier, +.XR add-outline ). ( +.IP \*[= M] +Place a \(lqnamed destination\(rq reference mark at the current output position, +in the current PDF document, +.XR mark-dest ). ( +.IP \*[= D] +Specify the content of a PDF document reference dictionary entry; +typically, such entries are generated automatically, +by transformation of the intermediate output resulting from the use of +.CW pdfhref +.CWB M \& \& \(rq, \(lq +with the +.CWB -X \& \& \(rq \(lq +modifier, +.XR create-map ); ( +however, it is also possible to specify such entries manually, +.XR user-format ). ( +.IP \*[= L] +Insert an active link to a named destination, +.XR link-named ), ( +at the current output position in the current PDF document, +such that when the reader clicks on the link text, +the document view changes to show the location of the named destination. +.IP \*[= W] +Insert an active link to a \(lqweb\(rq resource, +.XR add-weblink ), ( +at the current output position in the current PDF document. +This is effectively the same as using the +.CWB L \& \& \(rq \(lq +operator to establish a link to a named destination in another PDF document, +.XR link-extern ), ( +except that in this case, the destination is specified by a +\(lquniform resource identifier\(rq, or +.CW URI ; +this may represent any Internet or local resource +which can be specified in this manner. +.IP \*[= F] +Specify a user defined macro, to be called by +.CW pdfhref , +when formatting the text in the active region of a link, +.XR set-format ). ( +.IP \*[= Z] +Define the absolute position on the physical PDF output page, +where the \(lqhot\(hyspot\(rq associated with an active link is to be placed. +Invoked in pairs, marking the starting and ending PDF page co\(hyordinates +for each link \(lqhot\(hyspot\(rq, this operator is rarely, if ever, +specified directly by the user; +rather, appropriate +.CW pdfhref +.CWB Z \& \& \(rq \(lq +specifications are inserted automatically into the document reference map +during the PDF document formatting process, +.XR create-map ). ( +.IP \*[= I] +Initialize support for +.CW pdfhref +features. +The current +.CW pdfhref +implementation provides only one such feature which requires initialization +\(em a helper macro which must be attached to a user supplied page trap handler, +in order to support mapping of reference \(lqhot\(hyspots\(rq +which extend through a page transition; +.XR page-trap ). ( +.QE +. +.NH 3 +.XN -S -- Optional Features of the \F[C]pdfhref\F[] Macro +.LP +The behaviour of a number of the +.CW pdfhref +macro operations can be modified, +by including +.EM "option specifiers" \(rq \(lq +after the operation specifying argument, +but +.EM before +any other arguments normally associated with the operation. +In +.EM all +cases, an option is specified by an +.EM "option flag" \(rq, \(lq +comprising an initial hyphen, +followed by one or two option identifying characters. +Additionally, +.EM some +options require +.EM "exactly one" +option argument; +for these options, the argument +.EM must +be specified, and it +.EM must +be separated from the preceding option flag by one or more +.EM spaces , +(tabs +.EM "must not" +be used). +It may be noted that this paradigm for specifying options +is reminiscent of most +Unix\(tm +shells; however, in the case of the +.CW pdfhref +macro, omission of the space separating an option flag from its argument is +.EM never +permitted. +.LP +A list of +.EM all +general purpose options supported by the +.CW pdfhref +macro is given below. +Note that not all options are supported for all +.CW pdfhref +operations; the operations affected by each option are noted in the list. +For +.EM most +operations, if an unsupported option is specified, +it will be silently ignored; however, this behaviour should +not be relied upon. +.LP +The general purpose options, supported by the +.CW pdfhref +macro, are:\(en +.QS +.IP \*[= -N\0 name > <] +Allows the +.CWI name > < +associated with a PDF reference destination +to be defined independently from the following text, +which describes the reference. +This option affects only the +.CWB M \& \& \(rq \(lq +operation of the +.CW pdfhref +macro, +.XR mark-dest ). ( +.IP \*[= -E] +Also used exclusively with the +.CWB M \& \& \(rq \(lq +operator, the +.CWB -E +option causes any specified +.CWI descriptive \& \& \~\c +.CWI text +arguments, +.XR mark-dest ), ( +to be copied, or +.EM echoed , +in the body text of the document, +at the point where the reference mark is defined; +(without the +.CWB -E +option, such +.CWI descriptive \& \& \~\c +.CWI text +will appear +.EM only +at points where links to the reference mark are placed, +and where the standard reference display format, +.XR set-format ), ( +is used). +.IP \*[= -D\0 dest > <] +Specifies the +.CW URI , +or the destination name associated with a PDF active link, +independently of the following text, +which describes the link and demarcates the link \(lqhot\(hyspot\(rq. +This option affects the behaviour of the +.CW pdfhref +macro's +.CWB L \& \& \(rq \(lq +and +.CWB W \& \& \(rq \(lq +operations. +.IP +When used with the +.CWB L \& \& \(rq \(lq +operator, the +.CWI dest > < +argument must specify a PDF \(lqnamed destination\(rq, +as defined using +.CW pdfhref +with the +.CWB M \& \& \(rq \(lq +operator. +.IP +When used with the +.CWB W \& \& \(rq \(lq +operator, +.CWI dest > < +must specify a link destination in the form of a +\(lquniform resource identifier\(rq, or +.CW URI , +.XR add-weblink ). ( +.IP \*[= -F\0 file > <] +When used with the +.CWB L \& \& \(rq \(lq +.CW pdfhref +operator, +.CWI file > < +specifies an external PDF file in which the named destination +for the link reference is defined. +This option +.EM must +be specified with the +.CWB L \& \& \(rq \(lq +operator, +to create a link to a destination in a different PDF document; +when the +.CWB L \& \& \(rq \(lq +operator is used +.EM without +this option, the link destination is assumed to be defined +within the same document. +.IP \*[= -P\0 \(dqprefix\(hytext\(dq > <] +Specifies +.CWI \(dqprefix\(hytext\(dq > < +to be attached to the +.EM start +of the text describing an active PDF document link, +with no intervening space, but without itself being included in the +active area of the link \(lqhot\(hyspot\(rq; +it is effective with the +.CWB L \& \& \(rq \(lq +and +.CWB W \& \& \(rq \(lq +.CW pdfhref +operators. +.IP +Typically, this option would be used to insert punctuation before +the link \(lqhot\(hyspot\(rq. +Thus, there is little reason for the inclusion of spaces in +.CWI \(dqprefix\(hytext\(dq > < ; +however, if such space is required, then the enclosing double quotes +.EM must +be specified, as indicated. +.IP \*[= -A\0 \(dqaffixed\(hytext\(dq > <] +Specifies +.CWI \(dqaffixed\(hytext\(dq > < +to be attached to the +.EM end +of the text describing an active PDF document link, +with no intervening space, but without itself being included in the +active area of the link \(lqhot\(hyspot\(rq; +it is effective with the +.CWB L \& \& \(rq \(lq +and +.CWB W \& \& \(rq \(lq +.CW pdfhref +operators. +.IP +Typically, this option would be used to insert punctuation after +the link \(lqhot\(hyspot\(rq. +Thus, there is little reason for the inclusion of spaces in +.CWI \(dqaffixed\(hytext\(dq > < ; +however, if such space is required, then the enclosing double quotes +.EM must +be specified, as indicated. +.IP \*[= -T\0 tag > <] +When specified with the +.CWB O \& \& \(rq \(lq +operator, +.CWI tag > < +is appended to the \(lqbookmark\(rq name assigned to the generated outline entry. +This option is +.EM required , +to distinguish between the series of \(lqbookmark\(rq names generated in +individual passes of the +.CW groff +formatter, when the final PDF document is to be assembled +from a number of separately formatted components; +.XR multipart-outline ). ( +.IP \*[= -X] +This +.CW pdfhref +option is used with either the +.CWB M \& \& \(rq \(lq +operator, or with the +.CWB L \& \& \(rq \(lq +operator. +.IP +When used with the +.CWB M \& \& \(rq \(lq +operator, +.XR mark-dest ), ( +it ensures that a cross reference record for the marked destination +will be included in the document reference map, +.XR export-map ). ( +.IP +When used with the +.CWB L \& \& \(rq \(lq +operator, +.XR link-named ), ( +it causes the reference to be displayed in the standard cross reference format, +.XR set-format ), ( +but substituting the +.CWI descriptive \& \& \~\c +.CWI text +specified in the +.CW pdfhref \& \(lq +.CW L \(rq +argument list, +for the description specified in the document reference map. +.IP \*[= --] +Marks the end of the option specifiers. +This may be used with all +.CW pdfhref +operations which accept options, to prevent +.CW pdfhref +from interpreting any following arguments as option specifiers, +even if they would otherwise be interpreted as such. +It is also useful when the argument list to +.CW pdfhref +contains special characters \(em any special character, +which is not valid in a +.CW groff +macro name, will cause a parsing error, if +.CW pdfhref +attempts to match it as a possible option flag; +using the +.CW -- \(rq \(lq +flag prevents this, so suppressing the +.CW groff +warning message, which would otherwise ensue. +.IP +Using this flag after +.EM all +sequences of macro options is recommended, +even when it is not strictly necessary, +if only for the entirely cosmetic benefit of visually separating +the main argument list from the sequence of preceding options. +.QE +.LP +In addition to the +.CW pdfhref +options listed above, a supplementary set of two character options are defined. +These supplementary options, listed below, are intended for use with the +.CWB L \& \& \(rq \(lq +operator, in conjunction with the +.CWB -F \& \& \~\c +.CWBI file > < +option, to specify alternate file names, +in formats compatible with the file naming conventions +of alternate operating systems; +they will be silently ignored, if used in any other context. +.LP +The supported alternate file name options, +which are ignored if the +.CWB -F \& \& \~\c +.CWBI file > < +option is not specified, are:\(en +.QS +.IP \*[= -DF\0 dos\(hyfile > <] +Specifies the name of the file in which a link destination is defined, +using the file naming semantics of the +.CW MS\(hyDOS \*(rg +operating system. +When the PDF document is read on a machine +where the operating system uses the +.CW MS\(hyDOS \*(rg +file system, then +.CWI dos\(hyfile > < +is used as the name of the file containing the reference destination, +overriding the +.CWI file > < +argument specified with the +.CWB -F +option. +.IP \*[= -MF\0 mac\(hyfile > <] +Specifies the name of the file in which a link destination is defined, +using the file naming semantics of the +.CW Apple \*(rg +.CW Macintosh \*(rg +operating system. +When the PDF document is read on a machine +where the operating system uses the +.CW Macintosh \*(rg +file system, then +.CWI mac\(hyfile > < +is used as the name of the file containing the reference destination, +overriding the +.CWI file > < +argument specified with the +.CWB -F +option. +.IP \*[= -UF\0 unix\(hyfile > <] +Specifies the name of the file in which a link destination is defined, +using the file naming semantics of the +.CW Unix \(tm +operating system. +When the PDF document is read on a machine +where the operating system uses +.CW POSIX +file naming semantics, then +.CWI unix\(hyfile > < +is used as the name of the file containing the reference destination, +overriding the +.CWI file > < +argument specified with the +.CWB -F +option. +.IP \*[= -WF\0 win\(hyfile > <] +Specifies the name of the file in which a link destination is defined, +using the file naming semantics of the +.CW MS\(hyWindows \*(rg +32\(hybit operating system. +When the PDF document is read on a machine +where the operating system uses any of the +.CW MS\(hyWindows \*(rg +file systems, with long file name support, then +.CWI win\(hyfile > < +is used as the name of the file containing the reference destination, +overriding the +.CWI file > < +argument specified with the +.CWB -F +option. +.QE +. +.NH 3 +.XN -N mark-dest -- Marking a Reference Destination +.LP +The +.CW pdfhref +macro may be used to create active links to any Internet resource, +specified by its +.CW URI , +or to any \(lqnamed destination\(rq, +either within the same document, or in another PDF document. +Although the PDF specification allows link destinations to be defined +in terms of a page number, and an associated view specification, +this style of reference is not currently supported by the +.CW pdfhref +macro, because it is not possible to adequately bind the specification +for the destination with the intended reference context. +.LP +References to Internet resources are interpreted in accordance with the +.CW W3C +standard for defining a +.CW URI ; +hence the only prerequisite, for creating a link to any Internet resource, +is that the +.CW URI +be properly specified, when declaring the reference; +.XR add-weblink ). ( +In the case of references to \(lqnamed destinations\(rq in PDF documents, +however, it is necessary to provide a mechanism for creating such +\(lqnamed destinations\(rq. +This may be accomplished, by invoking the +.CW pdfhref +macro in the form +.QP +.fam C +.B ".pdfhref M" +.B -N \& [ +.I name >] < +.B -X ] [ +.B -E ] [ +.I "descriptive text ...\&" ] [ +.LP +This creates a \(lqnamed destination\(rq reference mark, with its name specified by +.CWI name > < , +or, if the +.CWB -N +option is not specified, by the first word of +.CWI descriptive \& \& \~\c +.CWI text \& \& ; +(note that this imposes the restriction that, +if the +.CWB -N +option is omitted, then +.EM "at least" +one word of +.CWI descriptive \& \& \~\c +.CWI text +.EM must +be specified). +Additionally, a reference view will be automatically defined, +and associated with the reference mark, +.XR pdfhref-view ), ( +.\" and, if any +.\" .CWI descriptive +.\" .CWI text +.\" is specified, or the +and, if the +.CWB -X +option is specified, and no document cross reference map has been imported, +.XR import-map ), ( +then a cross reference mapping record, +.XR export-map ), ( +will be written to the +.CW stdout +stream; +this may be captured, and subsequently used to generate a cross reference map +for the document, +.XR create-map ). ( +.LP +When a \(lqnamed destination\(rq reference mark is created, using the +.CW pdfhref +macro's +.CWB M \& \& \(rq \(lq +operator, there is normally no visible effect in the formatted document; any +.CWI descriptive \& \& \~\c +.CWI text +which is specified will simply be stored in the cross reference map, +for use when a link to the reference mark is created. +This default behaviour may be changed, by specifying the +.CWB -E +option, which causes any specified +.CWI descriptive \& \& \~\c +.CWI text +to be \(lqechoed\(rq in the document text, +at the point where the reference mark is placed, +in addition to its inclusion in the cross reference map. +. +.NH 4 +.XN -N export-map -- Mapping a Destination for Cross Referencing +.LP +Effective cross referencing of +.EM any +document formatted by +.CW groff +requires multiple pass formatting. +Details of how this multiple pass formatting may be accomplished, +when working with the +.CW pdfmark +macros, will be discussed later, +.XR do-xref ); ( +at this stage, the discussion will be restricted to the initial preparation, +which is required at the time when the cross reference destinations are defined. +.LP +The first stage, in the process of cross referencing a document, +is the generation of a cross reference map. +Again, the details of +.EM how +the cross reference map is generated will be discussed in +.pdfhref F SECREF L -D do-xref -A ; +.pdfhref F +however, it is important to recognize that +.EM what +content is included in the cross reference map is established +when the reference destination is defined \(em it is derived +from the reference data exported on the +.CW stderr +stream by the +.CW pdfhref +macro, when it is invoked with the +.CWB M \& \& \(rq \(lq +operator, and is controlled by whatever definition of the string +.CW PDFHREF.INFO +is in effect, when the +.CW pdfhref +macro is invoked. +.LP +The initial default setting of +.CW PDFHREF.INFO +is +.QP +.CW ".ds PDFHREF.INFO page \e\en% \e\e$*" +.LP +which ensures that the cross reference map will contain +at least a page number reference, supplemented by any +.CWI descriptive \& \& \~\c +.CWI text +which is specified for the reference mark, as defined by the +.CW pdfhref +macro, with its +.CWB M \& \& \(rq \(lq +operator; this may be redefined by the user, +to export additional cross reference information, +or to modify the default format for cross reference links, +.XR set-format ). ( +. +.NH 4 +.XN -N pdfhref-view -- Associating a Document View with a Reference Mark +.LP +In the same manner as each document outline reference, defined by the +.CW pdfhref +macro with the +.CWB O \& \& \(rq \(lq +operator, +.XR add-outline ), ( +has a specific document view associated with it, +each reference destination marked by +.CW pdfhref +with the +.CWB M \& \& \(rq \(lq +operator, requires an associated document view specification. +.LP +The mechanism whereby a document view is associated with a reference mark +is entirely analogous to that employed for outline references, +.XR outline-view ), ( +except that the +.CW PDFHREF.VIEW +string specification is used, in place of the +.CW PDFBOOKMARK.VIEW +specification. +Thus, the reference view is defined in terms of:\(en +.QS +.IP \*[= PDFHREF.VIEW] +A string, +establishing the position of the reference mark within the viewing window, +and the magnification at which the document will be viewed, +at the location of the marked reference destination; +by default, it is defined by +.RS +.QP +.CW ".ds PDFHREF.VIEW /FitH \e\en[PDFPAGE.Y] u" +.RE +.IP +which displays the reference destination at the top of the viewing window, +with the magnification set to fit the page width to the width of the window. +.IP \*[= PDFHREF.VIEW.LEADING] +A numeric register, +specifying additional spacing, to be placed between the top of the display +window and the actual position at which the location of the reference +destination appears within the window. +This register is shared with the view specification for outline references, +and thus has the same default initial setting, +.RS +.QP +.CW ".nr PDFHREF.VIEW.LEADING 5.0p" +.RE +.IP +as in the case of outline reference views. +.IP +Again, notice that +.CW PDFHREF.VIEW.LEADING +does not represent true typographic \(lqleading\(rq, +since any preceding text, set in the specified display space, +will be visible at the top of the viewing window, +when the reference is selected. +.QE +.LP +Just as the view associated with outline references may be changed, +by redefining +.CW PDFBOOKMARK.VIEW , +so the view associated with marked reference destinations may be changed, +by redefining +.CW PDFHREF.VIEW , +and, if desired, +.CW PDFHREF.VIEW.LEADING ; +such changes will become effective for all reference destinations marked +.EM after +these definitions are changed. +(Notice that, since the specification of +.CW PDFHREF.VIEW.LEADING +is shared by both outline reference views and marked reference views, +if it is changed, then the views for +.EM both +reference types are changed accordingly). +.LP +It may again be noted, that the +.CW PDFPAGE.Y +register is used in the definition of +.CW PDFHREF.VIEW , +just as it is in the definition of +.CW PDFBOOKMARK.VIEW ; +all comments in +.pdfhref F SECREF L -D outline-view +.pdfhref F +relating to its use, and indeed to page position computations in general, +apply equally to marked reference views and to outline reference views. +. +.NH 3 +.XN -N link-named -- Linking to a Marked Reference Destination +.LP +Any named destination, such as those marked by the +.CW pdfhref +macro, using it's +.CWB M \& \& \(rq \(lq +operator, may be referred to from any point in +.EM any +PDF document, using an +.EM "active link" ; +such active links are created by again using the +.CW pdfhref +macro, but in this case, with the +.CWB L \& \& \(rq \(lq +operator. +This operator provides support for two distinct cases, +depending on whether the reference destination is defined in +the same document as the link, +.XR link-intern ), ( +or is defined as a named destination in a different PDF document, +.XR link-extern ). ( +. +.NH 4 +.XN -N link-intern -- References within a Single PDF Document +.LP +The general syntactic form for invoking the +.CW pdfhref +macro, +when creating a link to a named destination within the same PDF document is +.QP +.fam C +.B .pdfhref +.B L +.B -D \& [ +.BI dest-name >] < +.B -P \& [ +.BI prefix-text >] < +.B -A \& [ +.BI affixed-text >] < +\e +.br +\0\0\0 +.B -X ] [ +.B -- ] [ +.I "descriptive text ...\&" ] [ +.LP +where +.CWI dest-name > < +specifies the name of the link destination, +as specified using the +.CW pdfhref +.CWB M \& \& \(rq \(lq +operation; (it may be defined either earlier in the document, +to create a backward reference, or later, to create a forward reference). +.\" +.\" Here's a example of how to add an iconic annotation. +.\" +.\".pdfnote -T "Internal Cross References" \ +.\" This description is rather terse, and could benefit from \ +.\" the inclusion of an example. +.LP +If any +.CWI descriptive \& \& \~\c +.CWI text +arguments are specified, then they will be inserted into the +.CW groff +output stream, to define the text appearing in the \(lqhot\(hyspot\(rq +region of the link; +this will be printed in the link colour specified by the string, +.CW PDFHREF.TEXT.COLOUR , +which is described in +.XR-NO-PREFIX set-colour . +If the +.CWB -X +option is also specified, then the +.CWI descriptive \& \& \~\c +.CWI text +will be augmented, by prefacing it with page and section number indicators, +in accordance with the reference formatting rules which are in effect, +.XR set-format ); ( +such indicators will be included within the active link region, +and will also be printed in the link colour. +.LP +Note that +.EM either +the +.CWB -D \& \& \~\c +.CWBI dest\(hyname > < +option, +.EM or +the +.CWI descriptive \& \& \~\c +.CWI text +arguments, +.EM "but not both" , +may be omitted. +If the +.CWB -D \& \& \~\c +.CWBI dest\(hyname > < +option is omitted, then the first word of +.CWI descriptive \& \& \~\c +.CWI text \& \& , +i.e.\~all text up to but not including the first space, +will be interpreted as the +.CWBI dest\(hyname > < +for the link; this text will also appear in the running text of the document, +within the active region of the link. +Alternatively, if the +.CWB -D \& \& \~\c +.CWBI dest\(hyname > < +option +.EM is +specified, and +.CWI descriptive \& \& \~\c +.CWI text +is not, +then the running text which defines the reference, +and its active region, +will be derived from the reference description which is specified +when the named destination is marked, +.XR mark-dest ), ( +and will be formatted according to the reference formatting rules +which are in effect, when the reference is placed, +.XR set-format ); ( +in this case, it is not necessary to specify the +.CWB -X +option to activate automatic formatting of the reference \(em it is implied, +by the omission of all +.CWI descriptive \& \& \~\c +.CWI text +arguments. +.LP +The +.CWB -P \& \& \~\c +.CWBI prefix\(hytext > < +and +.CWB -A \& \& \~\c +.CWBI affixed\(hytext > < +options may be used to specify additional text +which will be placed before and after the linked text respectively, +with no intervening space. +Such prefixed and affixed text will be printed in the normal text colour, +and will not be included within the active region of the link. +This feature is mostly useful for creating parenthetical references, +or for placing punctuation adjacent to, +but not included within, +the text which defines the active region of the link. +.LP +The operation of the +.CW pdfhref +macro, when used with its +.CWB L \& \& \(rq \(lq +operator to place a link to a named PDF destination, +may best be illustrated by an example. +However, since the appearance of the link will be influenced by +factors established when the named destination is marked, +.XR mark-dest ), ( +and also by the formatting rules in effect when the link is placed, +the presentation of a suitable example will be deferred, +until the formatting mechanism has been explained, +.XR set-format ). ( +. +.NH 4 +.XN -N link-extern -- References to Destinations in Other PDF Documents +.LP +The +.CW pdfhref +macro's +.CWB L \& \& \(rq \(lq +operator is not restricted to creating reference links +within a single PDF document. +When the link destination is defined in a different document, +then the syntactic form for invoking +.CW pdfhref +is modified, by the addition of options to specify the +name and location of the PDF file in which the destination is defined. +Thus, the extended +.CW pdfhref +syntactic form becomes +.QP +.fam C +.B .pdfhref +.B L +.B -F +.BI file > < +.B -D \& [ +.BI dest-name >] < +\e +.br +\0\0\0 +.B -DF \& [ +.BI dos-file >] < +.B -MF \& [ +.BI mac-file >] < +.B -UF \& [ +.BI unix-file >] < +\e +.br +\0\0\0 +.B -WF \& [ +.BI win-file >] < +.B -P \& [ +.BI prefix-text >] < +.B -A \& [ +.BI affixed-text >] < +\e +.br +\0\0\0 +.B -X ] [ +.B -- ] [ +.I "descriptive text ...\&" ] [ +.LP +where the +.CWB -F \& \& \~\c +.CWBI file > < +option serves +.EM two +purposes: it both indicates to the +.CW pdfhref +macro that the specified reference destination +is defined in an external PDF file, +and it also specifies the normal path name, +which is to be used to locate this file, +when a user selects the reference. +.LP +In addition to the +.CWB -F \& \& \~\c +.CWBI file > < +option, which +.EM must +be specified when referring to a destination in an external PDF file, +the +.CWB -DF \& \& \~\c +.CWBI dos\(hyfile > < , +.CWB -MF \& \& \~\c +.CWBI mac\(hyfile > < , +.CWB -UF \& \& \~\c +.CWBI unix\(hyfile > < +and +.CWB -WF \& \& \~\c +.CWBI win\(hyfile > < +options may be used to specify the location of the file +containing the reference destination, +in a variety of operating system dependent formats. +These options assign their arguments to the +.CW /DosFile , +.CW /MacFile , +.CW /UnixFile +and +.CW /WinFile +keys of the generated +.CW pdfmark +respectively; thus when any of these options are specified, +.EM "in addition to" +the +.CWB -F \& \& \~\c +.CWBI file > < +option, and the document is read on the appropriate operating systems, +then the path names specified by +.CWBI dos\(hyfile > < , +.CWBI mac\(hyfile > < , +.CWBI unix\(hyfile > < +and +.CWBI win\(hyfile > < +will be searched, +.EM instead +of the path name specified by +.CWBI file > < , +for each of the +.CW MS\(hyDOS \*(rg, +.CW Apple \*(rg +.CW Macintosh \*(rg, +.CW Unix \(tm +and +.CW MS\(hyWindows \*(rg +operating systems, respectively; see the +.pdfmark-manual , +for further details. +.LP +Other than the use of these additional options, +which specify that the reference destination is in an external PDF file, +the behaviour of the +.CW pdfhref +.CWB L \& \& \(rq \(lq +operator, with the +.CWB -F \& \& \~\c +.CWBI file > < +option, remains identical to its behaviour +.EM without +this option, +.XR link-intern ), ( +with respect to the interpretation of other options, +the handling of the +.CWI descriptive \& \& \~\c +.CWI text +arguments, and the formatting of the displayed reference. +.LP +Once again, since the appearance of the reference is determined by +factors specified in the document reference map, +and also by the formatting rules in effect when the reference is placed, +the presentation of an example of the placing of +a reference to an external destination will be deferred, +until the formatting mechanism has been explained, +.XR set-format ). ( +. +.NH 3 +.XN -N add-weblink -- Linking to Internet Resources +.LP +In addition to supporting the creation of cross references +to named destinations in PDF documents, the +.CW pdfhref +macro also has the capability to create active links to Internet resources, +or indeed to +.EM any +resource which may be specified by a Uniform Resource Identifier, +(which is usually abbreviated to the acronym \(lqURI\(rq, +and sometimes also referred to as a Uniform Resource Locator, +or \(lqURL\(rq). +.LP +Since the mechanism for creating a link to a URI differs somewhat +from that for creating PDF references, the +.CW pdfhref +macro is invoked with the +.CWB W \& \& \(rq \(lq +(for \(lqweb\(hylink\(rq) operator, rather than the +.CWB L \& \& \(rq \(lq +operator; nevertheless, the invocation syntax is similar, having the form +.QP +.fam C +.B .pdfhref +.B W +.B -D \& [ +.BI URI >] < +.B -P \& [ +.BI prefix-text >] < +.B -A \& [ +.BI affixed-text >] < +\e +.br +\0\0\0 +.B -- ] [ +.I "descriptive text ...\&" +.LP +where the optional +.CWB -D +.CWBI URI > < +modifier specifies the address for the target Internet resource, +in any appropriate +.EM "Uniform Resource Identifier" +format, while the +.CWI descriptive +.CWI text +argument specifies the text which is to appear in the \(lqhot\(hyspot\(rq +region, and the +.CWB -P +.CWBI prefix\(hytext > < +and +.CWB -A +.CWBI affixed\(hytext > < +options have the same effect as in the case of local document links, +.XR link-intern ). ( +.LP +Notice that it is not mandatory to include the +.CWB -D +.CWBI URI > < +in the link specification; if it +.EM is +specified, then it is not necessary for the URI to appear, +in the running text of the document \(em the +.CWI descriptive +.CWI text +argument exactly defines the text +which will appear within the \(lqhot\(hyspot\(rq region, +and this need not include the URI. +However, if the +.CWB -D \& \& \~\c +.CWBI URI > < +specification is omitted, then the +.CWI descriptive +.CWI text +argument +.EM must +be an +.EM exact +representation of the URI, which +.EM will , +therefore, appear as the entire content of the \(lqhot\(hyspot\(rq. +For example, we could introduce a reference to +.pdfhref W -D \*[GROFF-WEBSITE] -A , the groff web site +in which the actual URI is concealed, by using mark up such as:\(en +.DS I +.CW +For example, we could introduce a reference to +\&.pdfhref W -D \*[GROFF-WEBSITE] -A , the groff web site +in which the actual URI is concealed, +.DE +Alternatively, +to refer the reader to the groff web site, +making it obvious that the appropriate URI is +.pdfhref W -A , \*[GROFF-WEBSITE] +the requisite mark up might be:\(en +.DS I +.CW +to refer the reader to the groff web site, +making it obvious that the appropriate URI is +\&.pdfhref W -A , \*[GROFF-WEBSITE] +the requisite mark up might be:\e(en +.DE +. +.NH 3 +.XN -N set-format -- Establishing a Format for References +.LP +There are two principal aspects to be addressed, +when defining the format to be used when displaying references. +Firstly, it is desirable to provide a visual cue, +to indicate that the text describing the reference is imbued +with special properties \(em it is dynamically linked to the reference +destination \(em and secondly, the textual content should +describe where the link leads, and ideally, +it should also describe the content of the reference destination. +.LP +The visual cue, +that a text region defines a dynamically linked reference, +is most commonly provided by printing the text within the active +region in a distinctive colour. +This technique will be employed automatically by the +.CW pdfhref +macro \(em +.XR set-colour +\(em unless the user specifically chooses to adopt, and implement, +some alternative strategy. +. +.NH 4 +.XN -N set-colour -- Using Colour to Demarcate Link Regions +.LP +Typically, when a PDF document contains +.EM active +references to other locations, either within the same document, +or even in other documents, or on the World Wide Web, +it is usually desirable to make the regions +where these active links are placed stand out from the surrounding text. +. +.NH 4 +.XN -N user-format -- Specifying Reference Text Explicitly +. +.NH 4 +.XN -N auto-format -- Using Automatically Formatted Reference Text +. +.NH 4 +.XN -N custom-format -- Customizing Automatically Formatted Reference Text +.LP +It is incumbent on the user, +if employing automatic formatting of the displayed reference, +.XR set-format ), ( +to ensure that an appropriate reference definition +is created for the reference destination, +and is included in the reference map for the document +in which the reference will appear; +thus, it may be easiest to +.EM always +use manual formatting for external references. +. +.NH 3 +.XN Problematic Links +.LP +Irrespective of whether a +.CW pdfhref +reference is placed using the +.CWB L \& \& \(rq \(lq +operator, or the +.CWB W \& \& \(rq \(lq +operator, there may be occasions when the resulting link +does function as expected. +A number of scenarios, which are known to be troublesome, +are described below. +. +.NH 4 +.XN -N page-trap -- Links with a Page Transition in the Active Region +.LP +When a link is placed near the bottom of a page, +it is possible that its active region, or \(lqhot\(hyspot\(rq, +may extend on to the next page. +In this situation, a page trap macro is required +to intercept the page transition, and to restart the mapping of +the \(lqhot\(hyspot\(rq boundary on the new page. +.LP +The +.CW pdfmark +macro package includes a suitable page trap macro, to satisfy this requirement. +However, to avoid pre\(hyempting any other requirement the user may have for +a page transition trap, this is +.EM not +installed as an active page trap, +unless explicitly requested by the user. +.LP +To enable proper handling of page transitions, +which occur within the active regions of reference links, +the user should:\(en +.QS +.nr ITEM 0 1 +.IP \n+[ITEM]. +Define a page transition macro, to provide whatever features may be required, +when a page transition occurs \(em e.g.\& printing footnotes, +adding page footers and headers, etc. +This macro should end by setting the output position at the correct +vertical page offset, where the printing of running text is to restart, +following the page transition. +.IP \n+[ITEM]. +Plant a trap to invoke this macro, at the appropriate vertical position +marking the end of normal running text on each page. +.KS +.IP \n+[ITEM]. +Initialize the +.CW pdfhref +hook into this page transition trap, by invoking +.RS +.IP +.fam C +.B "pdfhref I -PT" +.BI macro-name > < +.LP +where +.CWBI macro-name > < +is the name of the user supplied page trap macro, +to ensure that +.CW pdfhref +will correctly restart mapping of active link regions, +at the start of each new page. +.KE +.RE +.QE +.LP +It may be observed that this initialization of the +.CW pdfhref +page transition hook is, typically, required only once +.EM before +document formatting begins. +Users of document formatting macro packages may reasonably expect that +this initialization should be performed by the macro package itself. +Thus, writers of such macro packages which include +.CW pdfmark +bindings, should provide appropriate initialization, +so relieving the end user of this responsibility. +The following example, abstracted from the sample +.CW ms +binding package, +.CW spdf.tmac , +illustrates how this may be accomplished:\(en +.DS I +.CW +\&.\e" groff "ms" provides the "pg@bottom" macro, which has already +\&.\e" been installed as a page transition trap. To ensure proper +\&.\e" mapping of "pdfhref" links which overflow the bottom of any +\&.\e" page, we need to install the "pdfhref" page transition hook, +\&.\e" as an addendum to this macro. +\&. +\&.pdfhref I -PT pg@bottom +.DE +. +.NH 2 +.XN -N add-note -- Annotating a PDF Document using Pop-Up Notes +. +.NH 2 +.XN -S -N pdfsync -- Synchronizing Output and \F[C]pdfmark\F[] Contexts +.LP +It has been noted previously, that the +.CW pdfview +macro, +.XR docview ), ( +the +.CW pdfinfo +macro, +.XR docinfo ), ( +and the +.CW pdfhref +macro, when used to create a document outline, +.XR add-outline ), ( +do not immediately write their +.CW pdfmark +output to the \*[PostScript] data stream; +instead, they cache their output, in a +.CW groff +diversion, in the case of the +.CW pdfview +and +.CW pdfinfo +macros, or in an ordered collection of strings and numeric registers, +in the case of the document outline, +until a more appropriate time for copying it out. +In the case of +.CW pdfview +and +.CW pdfinfo +\(lqmeta\(hydata\(rq, +this \(lqmore appropriate time\(rq is explicitly chosen by the user; +in the case of document outline data, +.EM some +cached data may be implicitly written out as the document outline is compiled, +but there will +.EM always +be some remaining data, which must be explicitly flushed out, before the +.CW groff +formatting process is allowed to complete. +.LP +To allow the user to choose when cached +.CW pdfmark +data is to be flushed to the output stream, the +.CW pdfmark +macro package provides the +.CW pdfsync +macro, (to synchronize the cache and output states). +In its simplest form, it is invoked without arguments, i.e. +.QP +.fam C +.B .pdfsync +.LP +This form of invocation ensures that +.EM both +the \(lqmeta\(hydata cache\(rq, containing +.CW pdfview +and +.CW pdfinfo +data, +.EM and +the \(lqoutline cache\(rq, +containing any previously uncommitted document outline data, +are flushed; ideally, this should be included in a +.CW groff +\(lqend macro\(rq, to ensure that +.EM both +caches are flushed, before +.CW groff +terminates. +.LP +Occasionally, +it may be desirable to flush either the \(lqmeta\(hydata cache\(rq, +without affecting the \(lqoutline cache\(rq, or vice\(hyversa, +at a user specified time, prior to reaching the end of the document. +This may be accomplished, by invoking the +.CW pdfsync +macro with an argument, i.e. +.QP +.fam C +.B ".pdfsync M" +.LP +to flush only the \(lqmeta\(hydata cache\(rq, or +.QP +.fam C +.B ".pdfsync O" +.LP +to flush only the \(lqoutline cache\(rq. +.LP +The \(lqmeta\(hydata cache\(rq can normally be safely flushed +in this manner, at any time +.EM after +output of the first page has started; +(it may cause formatting problems, +most notably the appearance of unwanted white space, if flushed earlier, +or indeed, if flushed immediately after a page transition, +but before the output of the content on the new page has commenced). +Caution is required, however, when explicitly flushing the +\(lqoutline cache\(rq, since if the outline is to be +subsequently extended, then the first outline entry after flushing +.EM must +be specified at level 1. +Nevertheless, such explicit flushing may occasionally be necessary; +for example, the +.CW TC +macro in the +.CW spdf.tmac +package, +.XR using-spdf ), ( +invokes +.CW ".pdfsync\ O" \(rq \(lq +to ensure that the outline for the \(lqbody\(rq section of the document +is terminated, +.EM before +it commences the formatting of the table of contents section. +.bp +. +.NH 1 +.XN -N pdf-layout -- PDF Document Layout +.LP +The +.CW pdfmark +macros described in the preceding section, +.XR pdf-features ), ( +provide no inherent document formatting capability of their own. +However, +they may be used in conjunction with any other +.CW groff +macro package of the user's choice, +to add such capability. +.LP +In preparing this document, the standard +.CW ms +macro package, supplied as a component of the GNU Troff distribution, +has been employed. +To facilitate the use of the +.CW pdfmark +macros with the +.CW ms +macros, +a binding macro package, +.CW spdf.tmac , +has been created. +The use of this binding macro package is described in the following section, +.XR using-spdf ); ( +it may also serve as an example to users of other standard +.CW groff +macro packages, +as to how the +.CW pdfmark +macros may be employed with their chosen primary macro package. +. +.NH 2 +.XN -S -N using-spdf -- Using \F[C]pdfmark\F[] Macros with the \F[C]ms\F[] Macro Package +.LP +The use of the binding macro package, +.CW spdf.tmac , +allows for the use of the +.CW pdfmark +macros in conjunction with the +.CW ms +macros, +simply by issuing a +.CW groff +command of the form\** +.FS +Once again, +.pdfhref L -D pdf-features-fn -- as noted in footnote\*{\n[pdf-features-fn]\*} +to +.XR-NO-PREFIX pdf-features , +do not specify any +.CW -T \^\c +.CWI dev +option, +other than +.CW -T \^\c +.CW ps , +or +.CW -T \^\c +.CW pdf ; +specify +.CW -T \^\c +.CW pdf , +if you wish to avoid the conversion of \*[PostScript] output to PDF, +which will be required if you specify +.CW -T \^\c +.CW ps , +or if you omit the +.CW -T \^\c +.CWI dev +option entirely. +.FE +.QP +.fam C +groff [-Tps\h'0.2p'|\h'0.2p'-Tpdf] [-m\F[]\|\|\FC\c +.B spdf +.I options \F[]\|\|\FC\c [- +.I file \F[]\|\|\FC\c "...] " +\&... +.LP +When using the +.CW spdf.tmac +package, the +.CW groff +input files may be marked up using any of the standard +.CW ms +macros to specify document formatting, +while PDF features may be added, +using any of the +.CW pdfmark +macros described previously, +.XR pdf-features ). ( +Additionally, +.CW spdf.tmac +defines a number of convenient extensions to the +.CW ms +macro set, to better accomodate the use of PDF features within the +.CW ms +formatting framework, +and to address a number of +.CW ms +document layout issues, +which require special handling when producing PDF documents. +These additional macros, +and the issues they are intended to address, +are described below. +. +.NH 3 +.XN -S -- \F[C]ms\F[] Section Headings in PDF Documents +.LP +Traditionally, +.CW ms +provides the +.CW NH +and +.CW SH +macros, to specify section headings. +However, +there is no standard mechanism for generating a +table of contents entry based on the text of the section heading; +neither is there any recognized standard method for establishing a +cross reference link to the section. +.LP +To address this +.CW ms +limitation, +.CW spdf.tmac +defines the +.CW XN +macro, +.XR xn-macro ), ( +to be used in conjunction with the +.CW NH +macro. +. +.NH 4 +.XN -S -N xn-macro -- The \F[C]XN\F[] Macro +.bp +. +.NH 1 +.XN The PDF Publishing Process +. +.NH 2 +.XN -N do-xref -- Resolving Cross References +. +.NH 3 +.XN -N create-map -- Creating a Document Reference Map +. +.NH 3 +.XN -N import-map -- Deploying a Document Reference Map +.TC +.\" Local Variables: +.\" mode: nroff +.\" End: +.\" vim: filetype=groff: diff --git a/contrib/pdfmark/pdfmark.tmac b/contrib/pdfmark/pdfmark.tmac new file mode 100644 index 0000000..5f664f2 --- /dev/null +++ b/contrib/pdfmark/pdfmark.tmac @@ -0,0 +1,1953 @@ +.ig + +pdfmark.tmac + +Copyright (C) 2004-2020 Free Software Foundation, Inc. + Written by Keith Marshall (keith.d.marshall@ntlworld.com) + +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/>. + +Author's Note +============= + +While I have written this macro package from scratch, much of my +inspiration has come from discussion on the groff mailing list +(mailto:groff@gnu.org). I am particularly indebted to: + + Kees Zeelenberg, for an earlier macro package he posted, + a study of which helped me to get started. + + Carlos J. G. Duarte and Werner Lemberg, whose discussion + on computation of the bounding boxes for link "hot-spots" + forms the basis of such computations in this package. +.. +.if !\n(.g .ab These pdfmark macros require groff. +.\" +.\" Check if we have already been loaded -- do not reload +.if d pdfmark .nx +.\" +.\" ====================================================================== +.\" Module PDFMARK: Insert Arbitrary PDFMARK Code in the Postscript Stream +.\" ====================================================================== +.\" +.\" PDFMARK output may be disabled, by zeroing the PDFOPMODE register, +.\" ( which mimics a more generic OPMODE, if it is defined ). +.\" +.if rOPMODE .aln PDFOPMODE OPMODE +.\" +.\" but if OPMODE wasn't defined, +.\" then make the default PDFMARK mode ENABLED. +.\" +.if !rPDFOPMODE .nr PDFOPMODE 1 +.\" +.\" PDFMARK output must be constrained to a maximum line length limit, +.\" for strict compliance with the Postscript DSC. This limit is defined +.\" in register "PDFMARK.FOLDWIDTH.MAX". This is user definable, up to a +.\" ceiling value of 255, which is also its default value; this limit +.\" is enforced for each PDFMARK, by macro "pdf*pdfmark.limit". +.\" +.de pdf*pdfmark.limit +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdf*pdfmark.limit REGISTER-NAME DEFAULT-MAXIMUM-VALUE +.\" ---------------------------------------------------------------- +.\" +.\" If a register named REGISTER-NAME has not been defined, then +.\" define it now, with default value = DEFAULT-MAXIMUM-VALUE. +.\" +.if !r\\$1 .nr \\$1 \\$2 +.\" +.\" But when it has already been defined, ensure that its value does +.\" not exceed DEFAULT-MAXIMUM-VALUE; if value does exceed this ceiling, +.\" then redefine it, to enforce the limit. +.\" +.if (\\n[\\$1] > \\$2) .nr \\$1 \\$2 +.. +.\" The "pdfmark" macro is responsible for emitting the appropriate +.\" Postscript code. +.\" +.de pdfmark +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdfmark text of pdfmark instruction +.\" Macro supplies the required opening "[" and closing "pdfmark" +.\" operator; DO NOT include them in the instruction text! +.\" ---------------------------------------------------------------- +.\" +.if \\n[PDFOPMODE] \{\ +.\" +.\" Strict DSC compliance forbids emission of ps:exec lines which +.\" exceed 255 characters in length. We will allow the user to specify +.\" an alternative lesser limit ... +.\" +. pdf*pdfmark.limit PDFMARK.FOLDWIDTH.MAX 255 +.\" +.\" ... and we will also support a second lesser limit, which will be +.\" applied to literal text parenthetically embedded within the PDFMARK. +.\" +. pdf*pdfmark.limit PDFMARK.FOLDWIDTH \\n[PDFMARK.FOLDWIDTH.MAX] +.\" +.\" We will push out the entire PDFMARK in one chunk, provided it fits +.\" within this limit. +.\" +. length pdf:length "[\\$* pdfmark\" +. ie !(\\n[pdf:length] > \\n[PDFMARK.FOLDWIDTH]) \{\ +. \" +. \" This PDFMARK is suitable for single chunk output ... +. \" +. nop \!x X ps:exec [\\$* pdfmark +. \} +. el \{\ +. \" ... but, when the limit would be violated, then we must +. \" recompose the specified PDFMARK, spreading it over as many +. \" continuation lines as are necessary. +. \" +. als pdf*compose pdf*compose.first +. while \\n(.$ \{\ +. pdf*compose \\$1 +. shift +. \} +. \" +. \" Complete the PDFMARK recomposition, by appending a +. \" "pdfmark" operator, and push it out to the intermediate +. \" output stream, (excluding its final line break). +. \" +. pdf*compose pdfmark +. pdf*pdfmark.dispatch +. \" +. \" And clean up when done. +. \" +. rm pdf*compose pdf*pdfmark.post +. rm pdf:compose.test pdf:composed.literal +. \} +. rr pdf:length +. \} +.. +.\" When a PDFMARK exceeds the specified output record length limit, +.\" then we decompose it, subsequently using the dynamically overloaded +.\" macro, "pdf*compose", to reassemble it into as many continuation +.\" records as it may require. +.\" +.\" Each call to "pdf*compose" uses macro "pdf*length.increment" to +.\" keep track of the current output record length, so ensuring that +.\" the active maximum length limit is not violated. +.\" +.de pdf*length.increment +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdf*length.increment NEXT-ADDITION +.\" ---------------------------------------------------------------- +.\" +.ie d pdf:composed.line \ +. length pdf:length "\\*[pdf:composed.line] \\$*\" +.el .length pdf:length "\\$*\" +.. +.\" The first call to "pdf*compose" for each PDFMARK is directed +.\" to "pdf*compose.first"; this initialises the local strings +.\" and macros used to compose the eventual PDFMARK output. +.\" +.de pdf*compose.first +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .als pdf*compose pdf*compose.first +.\" . pdf*compose TOKEN +.\" ---------------------------------------------------------------- +.\" +.\" Ensure that the output record accumulator will be initialised +.\" on posting of the first composed PDFMARK record. +.\" +.als pdf*pdfmark.post pdf*pdfmark.post.first +.\" +.\" The first token passed to "pdf*compose" should not be a +.\" literal, but be prepared to handle one, just in case. +.\" +.ds pdf:compose.test \\$1 +.substring pdf:compose.test 0 0 +.ie '('\\*[pdf:compose.test]' \{\ +.\" +.\" We found a literal, even though we didn't expect it; +.\" if it's a single element literal, we can just handle it +.\" as if it is a regular token anyway. +.\" +. ds pdf:compose.test "\\$\\n(.$\" +. substring pdf:compose.test -1 +. if !')'\\*[pdf:compose.test]' \{\ +. \" +. \" But when it is the first of a literal sequence, +. \" then we need to set up "pdf*compose" to handle it. +. \" +. ds pdf:composed.literal "[\\$*\" +. als pdf*compose pdf*compose.literal +. \} +. \} +.el .ds pdf:compose.test ) +.if ')'\\*[pdf:compose.test]' \{\ +.\" +.\" In the normal case, we start each new PDFMARK with a +.\" regular token; save it as the first in the composed output +.\" line sequence, and set up "pdf*compose" to collect +.\" the rest of the sequence. +.\" +. ds pdf:composed.line "[\\$*\" +. als pdf*compose pdf*compose.next +. \} +.. +.\" Subsequent calls to "pdf*compose", while collecting +.\" regular tokens, are then directed to "pdf*compose.next". +.\" +.de pdf*compose.next +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .als pdf*compose pdf*compose.next +.\" . pdf*compose TOKEN +.\" ---------------------------------------------------------------- +.\" +.\" This first checks to ensure that the supplied token really is +.\" a regular token, and not the first element in a literal. +.\" +.ds pdf:compose.test \\$1 +.substring pdf:compose.test 0 0 +.ie '('\\*[pdf:compose.test]' \{\ +.\" +.\" The supplied token represents the first element of a literal, +.\" but it may be a single element literal, which we simply handle +.\" as a regular token anyway. +.\" +. ds pdf:compose.test "\\$\\n(.$\" +. substring pdf:compose.test -1 +. if !')'\\*[pdf:compose.test]' \{\ +. \" +. \" The supplied token is the first of a sequence of elements +. \" which collectively define a literal, so start collecting a +. \" composite literal token, and change the "pdf*compose" +. \" state, to collect and append the remaining elements. +. \" +. ds pdf:composed.literal "\\$*\" +. als pdf*compose pdf*compose.literal +. \} +. \} +.el .ds pdf:compose.test ) +.if ')'\\*[pdf:compose.test]' \{\ +.\" +.\" The supplied token IS a regular token; add it, but ensure that +.\" the active maximum record length limit is honoured. +.\" +. pdf*length.increment "\\$*\" +. ie (\\n[pdf:length] > \\n[PDFMARK.FOLDWIDTH.MAX]) \{\ +. \" +. \" Adding this token would cause the current PDFMARK record, in +. \" groff's intermediate output file, to overflow the active record +. \" length limit, so post the current record and start another. +. \" +. pdf*pdfmark.dispatch +. ds pdf:composed.line "\\$*\" +. \} +. el \{\ +. \" +. \" This token will fit in the current PDFMARK record, without +. \" violating the active length limit, so simply add it. +. \" +. ie d pdf:composed.line .as pdf:composed.line " \\$*\" +. el .ds pdf:composed.line "\\$*\" +. \} +. \} +.. +.\" While assembling a multiple token literal sequence into a single +.\" literal token, successive calls to "pdf*compose" are directed +.\" to "pdf*compose.literal". +.\" +.de pdf*compose.literal +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .als pdf*compose pdf*compose.literal +.\" . pdf*compose TOKEN +.\" ---------------------------------------------------------------- +.\" +.\" First, check to ensure that the current token can be appended to +.\" the accumulated literal, without extending it beyond the maximum +.\" allowed literal token length. +.\" +.length pdf:length "\\*[pdf:composed.literal] \\$*\" +.ie (\\n[pdf:length] > (\\n[PDFMARK.FOLDWIDTH] - 2)) \{\ +.\" +.\" If it has grown too long, then it must be folded across two +.\" physical PDFMARK output records, so check if we can accommodate +.\" the portion collected so far within the current output record. +.\" +. pdf*length.increment "\\*[pdf:composed.literal]\" +. if (\\n[pdf:length] > (\\n[PDFMARK.FOLDWIDTH.MAX] - 2)) \{\ +. \" +. \" The current output record CAN'T accommodate the currently +. \" composed portion of the literal, so flush out the current +. \" record, to make way for the accumulated literal, and mark +. \" the dispatch mode as "wrapped", for the fragments of the +. \" folded literal string, which are to follow. +. \" +. pdf*pdfmark.dispatch +. ds pdf*pdfmark.dispatch.wrapped +. \} +. ie d pdf:composed.line \{\ +. \" +. \" If we DIDN'T need to flush the current output record, +. \" then we can simply append the accumulated literal to it... +. \" +. as pdf:composed.line " \\*[pdf:composed.literal]\" +. \} +. el \{\ +. \" +. \" otherwise, when the current record has been flushed, or is +. \" empty, then we promote the accumulated literal, to make it +. \" the next output record... +. \" +. rn pdf:composed.literal pdf:composed.line +. \} +.\" +.\" Now, to complete the fold, flush out any accumulated partial +.\" output record, and continue accumulating the literal, starting +.\" with the current token. +.\" +. pdf*pdfmark.dispatch +. ds pdf:composed.literal "\\$*\" +. \} +.el \{\ +.\" +.\" Alternatively, when we HAVEN'T identified a need to fold the +.\" current output record, then we simply append the current token +.\" to the accumulated literal token buffer string. +.\" +. as pdf:composed.literal " \\$*\" +. \} +.\" +.\" Having ensured that we have sufficient space, in which to +.\" append the current token to the currently accumulated literal, +.\" we check its rightmost character, to see if is the closing +.\" parenthesis, which completes the literal. +.\" +.ds pdf:compose.test \\$\\n(.$ +.substring pdf:compose.test -1 +.if ')'\\*[pdf:compose.test]' \{\ +.\" +.\" The literal has been completely collected, so we may now append +.\" it to the current output record, as a single literal token, but +.\" subject to the constraint that it must not extend the output +.\" record beyond the maximum permitted length. +.\" +. pdf*length.increment "\\*[pdf:composed.literal]\" +. ie (\\n[pdf:length] > \\n[PDFMARK.FOLDWIDTH.MAX]) \{\ +. \" +. \" So, when the literal cannot be accommodated within the maximum +. \" length constraint, then we flush the current record, and start +. \" a new one, with the literal token as its first entry. +. \" +. pdf*pdfmark.dispatch +. rn pdf:composed.literal pdf:composed.line +. \} +. el \{\ +. \" +. \" When the literal CAN be accommodated within the maximum length +. \" constraint, then ... +. \" +. ie d pdf:composed.line \{\ +. \" +. \" When an output record has already been instantiated, we +. \" append the literal token to it, and discard the accumulator +. \" string, which is no longer required. +. \" +. as pdf:composed.line " \\*[pdf:composed.literal]\" +. rm pdf:composed.literal +. \} +. el \{\ +. \" +. \" But when no output record yet exists, then we simply +. \" reassign the accumulated literal token, to instantiate a +. \" new output record. +. \" +. rn pdf:composed.literal pdf:composed.line +. \} +. \} +.\" +.\" Finally, since we have completed the accumulation of the literal, we +.\" revert to the "unwrapped" mode of operation for "pdf*pdfmark.dispatch", +.\" and restore the normal "pdf*compose" action, for collection of the next +.\" token (if any). +.\" +. rm pdf*pdfmark.dispatch.wrapped +. als pdf*compose pdf*compose.next +. \} +.. +.\" While composing a multiple record PDFMARK, each composed record +.\" must be added to the collection, whenever the partially composed +.\" output record has been filled; this is handled when necessary, +.\" by calling the "pdf*pdfmark.dispatch" macro. +.\" +.de pdf*pdfmark.dispatch +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdf*pdfmark.dispatch +.\" ---------------------------------------------------------------- +.\" +.if d pdf:composed.line \{\ +.\" +.\" This is simply a wrapper around the overloaded "pdf*pdfmark.post" +.\" macro, ensuring that an output record has actually been collected +.\" before attempting to post it; it then cleans up after posting, to +.\" ensure that each collected record is posted only once. +.\" +. if d pdf*pdfmark.dispatch.wrapped \{\ +. \" +. \" When dispatching an excessively long literal string, which +. \" must be wrapped over multiple records, this mode is active +. \" for all but the closing record; we must escape the newline +. \" at the end of each such unclosed literal record. +. \" +. as pdf:composed.line " \\\\\\\\\" +. \} +. pdf*pdfmark.post +. rm pdf:composed.line +. \} +.. +.\" For each PDFMARK, the first call of "pdf*pdfmark.post" is directed +.\" to the "pdf*pdfmark.post.first" macro; this initialises the state +.\" of the "pdf:composed" macro, for assembly of a new PDFMARK. +.\" +.de pdf*pdfmark.post.first +. nop \!x X ps:exec \\*[pdf:composed.line] +.\" +.\" Subsequent calls to "pdf*pdfmark.post" are redirected to the +.\" alternative "pdf*pdfmark.post.next" macro, which simply appends +.\" additional PDFMARK records to the "pdf:composed" macro. +.\" +.als pdf*pdfmark.post pdf*pdfmark.post.next +.. +.de pdf*pdfmark.post.next +. nop \!+\\*[pdf:composed.line] +.. +.\" "pdf*end" is a dummy macro. It is required to mark the end +.\" of each individual fragment which is added to "pdf:composed"; +.\" other than this, it does nothing. +.\" +.de pdf*end +.. +.\" +.\" Some supporting macros defer actual pdfmark output until an +.\" appropriate time for it to be written; the "pdfsync" macro +.\" provides a mechanism for flushing such deferred output; +.\" it should be called from an end macro, and at any other time +.\" when it may be deemed necessary to flush pdfmark context. +.\" +.de pdfsync +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdfsync buffer ... +.\" Arguments indicate which "buffer(s)" to flush: +.\" O -> bookmark (outline) cache +.\" M -> document metadata diversion +.\" If no argument, flush ALL buffers +.\" ---------------------------------------------------------------- +.\" +.ie \\n(.$ \{\ +. while \\n(.$ \{\ +. if '\\$1'O' .pdf:bm.sync 1 +. if '\\$1'M' \{\ +. if dpdf:metadata .pdf:metadata +. rm pdf:metadata +. \} +. shift +. \} +. \} +.el .pdfsync O M +.. +.\" +.\" some helper functions ... +.\" +.\" "pdf:warn" and "pdf:error" write diagnostic messages to stderr +.\" +.de pdf:warn +.\" ---------------------------------------------------------- +.\" Usage: +.\" .pdf:warn text of message +.\" ---------------------------------------------------------- +.\" +.tm \\n(.F:\\n(.c: macro warning: \\$* +.. +.de pdf:error +.\" ---------------------------------------------------------- +.\" Usage: +.\" .pdf:error text of message +.\" ---------------------------------------------------------- +.\" +.tm \\n(.F:\\n(.c: macro error: \\$* +.. +.\" "pdf:pop", assisted by "pdf*pop", allows us to retrieve register, +.\" or string values, from a string masquerading as a data queue, +.\" or as a stack. +.\" +.de pdf:pop +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdf:pop <type> <to-name> <from-name> +.\" $1 = nr for numeric register, ds for string +.\" $2 = name of register or string to be assigned +.\" $3 = name of string, from which data is to be retrieved +.\" ---------------------------------------------------------------- +.\" +.pdf*pop \\$* \\*[\\$3] +.. +.de pdf*pop +.ds pdf:stack \\$3 +.\\$1 \\$2 \\$4 +.shift 4 +.ie \\n(.$ .ds \\*[pdf:stack] \\$* +.el .rm \\*[pdf:stack] +.rm pdf:stack +.. +.\" +.\" +.\" =========================================================== +.\" Module PDFINFO: Insert MetaData Entries into a PDF Document +.\" =========================================================== +.\" +.\" N.B. +.\" Output from the macros in this module is deferred, until +.\" subsequent invocation of .pdfsync, or .pdfexit +.\" +.\" ."pdfinfo" provides a general purpose form of metadata entry ... +.\" it allows arbitrary text to be associated with any specified +.\" metadata field name. +.\" +.de pdfinfo +.\" ------------------------------------------------------------------- +.\" Usage: +.\" .pdfinfo /FieldName field content ... +.\" Examples: +.\" .pdfinfo /Title A PDF Document +.\" .pdfinfo /Author Keith Marshall +.\" ------------------------------------------------------------------- +.\" +.ds pdf:meta.field \\$1 +.shift +.da pdf:metadata +\!.pdfmark \\*[pdf:meta.field] (\\$*) /DOCINFO +.di +.rm pdf:meta.field +.. +.\" +.\" Macro "pdfview" defines a special form of metadata entry ... +.\" it uses the /DOCVIEW pdfmark, to specify the initial (default) view, +.\" when the document is opened. +.\" +.de pdfview +.\" ------------------------------------------------------------------- +.\" Usage: +.\" .pdfview view parameters ... +.\" Examples: +.\" .pdfview /PageMode /UseOutlines +.\" .pdfview /Page 2 /View [/FitH \n(.p u] +.\" ------------------------------------------------------------------- +.\" +.da pdf:metadata +\!.pdfmark \\$* /DOCVIEW +.di +.. +.\" +.\" +.\" ===================================================================== +.\" 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 ... +.\" ---------------------------------------------------------------------- +.\" +.if \\n[PDFOPMODE] \{\ +.\" +.\" 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] /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) +.\" $3 = suffix for PDF internal bookmark name (optional) +.\" ------------------------------------------------------------------ +.\" +.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. +.\" +. if \\n[PDFOPMODE] \{\ +. \" +. \" 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 +. \" +. \" Synchronise the bookmark cache +. \" to the requested bookmark nesting level +. \" +. pdf:bm.sync \\$1 +. shift +. \" +. \" Increment the bookmark serialisation index +. \" in order to generate a uniquely serialised bookmark name, +. \" ( which we return in the string "PDFBOOKMARK.NAME" ), +. \" and insert this bookmark into the cache +. \" +. pdf:href.sety +. nr pdf:bm.nr +1 +. ds PDFBOOKMARK.NAME pdf:bm\\n[pdf:bm.nr]\\*[pdf:href-T] +. ds pdf:bm\\n[pdf:bm.nr] /Dest /\\*[PDFBOOKMARK.NAME] +. pdfmark \\*[pdf:bm\\n[pdf:bm.nr]] /View [\\*[PDFBOOKMARK.VIEW]] /DEST +. as pdf:bm\\n[pdf:bm.nr] " /Title (\\$*) +. pdf:href.options.clear +. rr PDFPAGE.Y +. \} +. \} +.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 \\$@ +. \} +.. +.\" +.\" Macro "pdf:bm.sync" is called for each bookmark created, +.\" to establish a cache entry at the appropriate nesting level. +.\" It will flush ALL previous cache content, when called to +.\" add a new bookmark at level 1, or if simply called at +.\" level 1, without adding any bookmark. +.\" +.de pdf:bm.sync +.\" ------------------------------------------------------------------ +.\" Usage: +.\" .pdf:bm.sync level +.\" $1 = nesting level of current bookmark, or 1 to flush cache +.\" ------------------------------------------------------------------ +.\" +.\" First validate the bookmark nesting level +.\" adjusting it if required +.\" +.if \\$1>\\n[pdf:bm.nl] .nr pdf:bm.nl +1 +.ie \\$1>\\n[pdf:bm.nl] \{\ +. pdf:warn adjusted level \\$1 bookmark; should be <= \\n[pdf:bm.nl] +. \} +.el .nr pdf:bm.nl \\$1 +.if \\n[pdf:bm.nl]<1 \{\ +. pdf:warn bad arg (\\$1) in \\$0 \\$1; \\$0 1 forced +. nr pdf:bm.nl 1 +. \} +.\" +.\" If reverting from a higher to a lower nesting level, +.\" cyclicly adjust cache counts for each pending higher level +.\" +.if \\n[pdf:bm.lc]>=\\n[pdf:bm.nl] \{\ +. nr pdf:bm.lc +1 +. if !rpdf:bm.c\\n[pdf:bm.lc].c .nr pdf:bm.c\\n[pdf:bm.lc].c 0 +. while \\n[pdf:bm.lc]>\\n[pdf:bm.nl] \{\ +. as pdf:bm.c\\n[pdf:bm.lc] " \\n[pdf:bm.c\\n[pdf:bm.lc].c] +. rr pdf:bm.c\\n[pdf:bm.lc].c +. nr pdf:bm.lc -1 +. \} +. \} +.\" +.\" Update the cache level, +.\" flushing when we are at level 1 +.\" +.nr pdf:bm.lc \\n[pdf:bm.nl] +.ie \\n[pdf:bm.nl]=1 \{\ +. while \\n[pdf:bm.ic]<\\n[pdf:bm.nr] .pdf:bm.emit 0 +. rr pdf:bm.rc +. \} +.el .nr pdf:bm.c\\n[pdf:bm.nl].c +1 +.. +.\" Macro "pdf:bm.emit" is called, when the cache is at level 1. +.\" This flushes ALL pending bookmarks from the cache, i.e. the +.\" preceding level 1 bookmark, and any nested dependents, +.\" which it may have. +.\" +.de pdf:bm.emit +.\" ------------------------------------------------------------------ +.\" Usage: +.\" .pdf:bm.emit flag +.\" $1 = reference counting flag, used to control recursion +.\" ------------------------------------------------------------------ +.\" +.\" First check for nested dependents, +.\" and append the "dependent count" to the bookmark, as required. +.\" +.nr pdf:bm.ic +1 +.nr pdf:bm.lc +1 +.pdf:pop nr pdf:bm.rc pdf:bm.c\\n[pdf:bm.lc] +.if \\n[pdf:bm.rc] \{\ +. ds pdf:bm.fold +. if \\n[pdf:bm.lc]>\\n[PDFOUTLINE.FOLDLEVEL] .ds pdf:bm.fold - +. as pdf:bm\\n[pdf:bm.ic] " /Count \\*[pdf:bm.fold]\\n[pdf:bm.rc] +. rm pdf:bm.fold +. \} +.pdfmark \\*[pdf:bm\\n[pdf:bm.ic]] /OUT +.rm pdf:bm\\n[pdf:bm.ic] +.\" +.\" For ALL dependents, if any, +.\" recursively flush out any higher level dependents, +.\" which they themselves may have +.\" +.while \\n[pdf:bm.rc] \{\ +. nr pdf:bm.rc -1 +. pdf:bm.emit \\n[pdf:bm.rc] +. \} +.\" +.\" Finally, +.\" unwind the recursive call stack, until we return to the top level. +.\" +.nr pdf:bm.rc \\$1 +.nr pdf:bm.lc -1 +.. +.nr pdf:bm.nr 0 +.nr pdf:bm.nl 1 +.nr pdf:bm.lc 0 +.nr pdf:bm.ic 0 +.\" +.\" +.\" ============================================================= +.\" 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(.p-\\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 ...]> ... +.\" ----------------------------------------------------------------- +.\" +.if \\n[PDFOPMODE] \{\ +.\" +.\" 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 ... +. \" +. 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-X +. 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" +.als pdf:href.opt-X pdf:href.flag \" cross reference +.\" +.\" 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 +.. +.\" +.\" "PDFHREF.INFO" establishes the content of the cross reference +.\" data record, which is exported via the "stderr" stream, when a +.\" cross reference anchor is created using a "pdfhref" macro request +.\" of the form +.\" +.\" .pdfhref M -N name -X text ... +.\" +.\" .ds PDFHREF.INFO \\*[PDFHREF.NAME] reference data ... +.\" +.ds PDFHREF.INFO page \\n% \\$* +.\" +.\" 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 [-X] [-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 +.\" +.\" If we specified a cross reference, with the "-X" option, and the +.\" reference mark has been successfully created, then we now need to +.\" write the cross reference info to the STDERR stream +.\" +.if \\n[pdf:href-X] .pdf*href.export \\*[PDFHREF.INFO] +.\" +.\" 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.set +.\" ---------------------------------------------------------------------- +.\" ---------------------------------------------------------------------- +.pdf*href.map.init +.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 \\$@ +. \" +. \" check if we also need to emit cross reference data +. \" (caller will do this if "pdf:href-X" is set, but it is +. \" not necessary, when "pdf:href.map" already exists) +. \" +. if dpdf:href.map .nr pdf:href-X 0 +. \} +.el \{\ +. \" marker is unnamed ... +. \" issue error message; do not emit reference data +. \" +. pdf:warn pdfhref destination marker must be named +. nr pdf:href-X 0 +. \} +.. +.de pdf*href.export +.\" +.\" Called ONLY by "pdf*href-M", +.\" this macro ensures that the emission of exported reference data +.\" is synchronised with the placement of the reference mark, +.\" especially when the mark is defined within a diversion. +.\" +.ie '\\n(.z'' .tm gropdf-info:href \\*[PDFHREF.NAME] \\$* +.el \!.\\$0 \\$@ +.. +.\" +.\" Macro "pdf*href-D" is invoked when "pdfhref" is called +.\" with the "D" reference class specifier; it provides a +.\" standardised mechanism for interpreting reference data +.\" exported by the "M" reference class, and may be used +.\" to directly define external reference data, without the +.\" use of "M" reference class designators in the source +.\" document. +.\" +.de 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 +.\" +.ie '\\*[pdf:href-N]'' \{\ +. pdf:warn pdfhref defined reference requires a name +. \} +.el \{\ +. ds pdf:href(\\*[pdf:href-N]).info \\$* +. \} +.. +.\" +.\" Macro "pdf*href-F" is invoked when "pdfhref" is called +.\" with the "F" reference class specifier; it allows the user +.\" to provide an alternative interpreter macro, which will be +.\" called when a "PDFHREF.INFO" record is retrieved to define +.\" the text of a cross reference link "hot spot". +.\" +.de pdf*href-F +.\" ---------------------------------------------------------------- +.\" Usage: +.\" .pdfhref F [macro-name] +.\" ---------------------------------------------------------------- +.\" +.\" Set macro specified by "macro-name" as the format interpreter +.\" for parsing "PDFHREF.INFO" records; if "macro-name" is omitted, +.\" or is specified as the reserved name "default", then use the +.\" default format parser, "pdf*href.format", defined below. +.\" +.if '\\$1'default' .shift \\n(.$ +.ie \\n(.$ .als pdf*href.format \\$1 +.el .als pdf*href.format pdf*href.default +.nr pdf:argc 1 +.. +.\" The default reference formatting macro is defined below. +.\" It parses the "PDFHREF.INFO" record specific to each reference, +.\" recognising the keywords "file", "page" and "section", when they +.\" appear in initial key/value pairs, replacing the key/value pair +.\" with "PDFHREF.FILEREF", "PDFHREF.PAGEREF" or "PDFHREF.SECTREF" +.\" respectively; any additional data in the "PDFHREF.INFO" record +.\" is enclosed in typographic double quotes, and the parsed record +.\" is appended to "PDFHREF.PREFIX", to be returned as the formatted +.\" reference text. +.\" +.\" Default definitions for the reference strings "PDFHREF.PREFIX", +.\" "PDFHREF.FILEREF", "PDFHREF.PAGEREF" and "PDFHREF.SECTREF" are +.\" provided, in the English language. Users may substitute any +.\" desired alternative definitions, for example, when formatting +.\" documents in other languages. In each case, "\\$1" may be used +.\" in the substitution, to represent the "value" component of the +.\" respective key/value pair specified in the "PDFHREF.INFO" record. +.\" +.ds PDFHREF.PREFIX see +.ds PDFHREF.PAGEREF page \\$1, +.ds PDFHREF.SECTREF section \\$1, +.ds PDFHREF.FILEREF \\$1 +.\" +.de pdf*href.format +.\" ----------------------------------------------------------------- +.\" Usage: (to be called ONLY by "pdfhref") +.\" .pdf*href.format cross reference data ... +.\" ----------------------------------------------------------------- +.\" +.\" This macro is responsible for defining the strings "PDFHREF.TEXT" +.\" and "PDFHREF.DESC", which are used by the "pdfhref" macro, as the +.\" basis for generating the text content of a link "hot spot"; (any +.\" user specified alternate formatter MUST do likewise). +.\" +.\" Note that "PDFHREF.TEXT" defines the overall format for the "link +.\" text", while "PDFHREF.DESC" is the descriptive component thereof. +.\" +.\" This default implementation, subject to user customisation of the +.\" "internationalisation" strings defined above, formats "hot spots" +.\" of the style +.\" +.\" see page N, section S, "descriptive text ..." +.\" +.ds PDFHREF.TEXT \\*[PDFHREF.PREFIX] +.while d\\$0.\\$1 \{\ +. \\$0.\\$1 "\\$2" +. shift 2 +. \} +.\" +.\" Retrieve the descriptive text from the cross reference data, +.\" ONLY IF no overriding description has been set by the calling +.\" "pdfhref" macro invocation. +.\" +.if \\n(.$ .if !dPDFHREF.DESC .ds PDFHREF.DESC \\$* +.\" +.\" Augment "PDFHREF.TEXT" so the descriptive text will be included +.\" in the text of the formatted reference +.\" +.if dPDFHREF.DESC .as PDFHREF.TEXT " \(lq\\\\*[PDFHREF.DESC]\(rq +.\" +.\" Finally, suppress any leading spaces, +.\" which may have been included in the PDFHREF.TEXT definition. +.\" +.ds PDFHREF.TEXT \\*[PDFHREF.TEXT] +.. +.de pdf*href.format.file +.\" ---------------------------------------------------------------------- +.\" Include a file identifier in a formatted reference. +.\" This is invoked ONLY by "pdf*href.format", and ONLY IF the +.\" reference data includes an initial file identifier tuple. +.\" ---------------------------------------------------------------------- +.\" +.as PDFHREF.TEXT " \\*[PDFHREF.FILEREF] +.. +.de pdf*href.format.page +.\" ---------------------------------------------------------------------- +.\" Include a page number in a formatted reference. +.\" This is invoked ONLY by "pdf*href.format", and ONLY IF the +.\" reference data includes an initial page number tuple. +.\" ---------------------------------------------------------------------- +.\" +.as PDFHREF.TEXT " \\*[PDFHREF.PAGEREF] +.. +.de pdf*href.format.section +.\" ---------------------------------------------------------------------- +.\" Include a section number in a formatted reference. +.\" This is invoked ONLY by "pdf*href.format", and ONLY IF the +.\" reference data includes an initial section number tuple. +.\" ---------------------------------------------------------------------- +.\" +.as PDFHREF.TEXT " \\*[PDFHREF.SECTREF] +.. +.\" +.\" Make "pdf*href.format" the default cross reference formatter +.\" +.als pdf*href.default pdf*href.format +.\" +.\" +.\" Macro "pdf*href" provides a generic mechanism for placing link +.\" "hot-spots" in a PDF document. ALL "pdfhref" class macros which +.\" create "hot-spots" are aliased to this macro; each must also have +.\" an appropriately aliased definition for "pdf*href.template". +.\" +.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 +. \} +.\" +.\" Some PDF link classes support a "/File (FilePathName)" argument. +.\" +.if dpdf*href.file \{\ +. \" +. \" When this is supported, it may be specified by supplying +. \" the "-F FileName" option, which is captured in "pdf:href-F". +. \" +. if dpdf:href-F \{\ +. \" +. \" the /File key is present, so set up the link specification +. \" to establish the reference to the specified file +. \" +. als pdf*href.link pdf*href.file +. ds pdf:href.files /File (\\*[pdf:href-F]) +. \" +. \" in addition to the /File key, +. \" there may also be platform dependent alternate file names +. \" +. if dpdf:href-DF .as pdf:href.files " /DOSFile (\\*[pdf:href-DF]) +. if dpdf:href-MF .as pdf:href.files " /MacFile (\\*[pdf:href-MF]) +. if dpdf:href-UF .as pdf:href.files " /UnixFile (\\*[pdf:href-UF]) +. if dpdf:href-WF .as pdf:href.files " /WinFile (\\*[pdf:href-WF]) +. \} +. \" In some cases, the "/File" key is REQUIRED. +. \" We will know it is missing, if "pdf*href.link" is not defined. +. \" +. if !dpdf*href.link \{\ +. \" +. \" When a REQUIRED "/File" key specification is not supplied, +. \" then complain, and set "pdf:href.ok" to abort the creation +. \" of the current reference. +. \" +. pdf:error pdfhref: required -F specification omitted +. nr pdf:href.ok 0 +. \} +. \" Now, we have no further use for "pdf*href.file". +. \" +. rm pdf*href.file +. \} +.\" +.\" 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 \\\\$* +. ie \\n[pdf:href-X] \{\ +. \" +. \" ... and, when the "-X" flag is set, +. \" also include formatted location information, +. \" derived from the cross reference record. +. \" +. pdf*href.format \\*[pdf:href(\\*[pdf:href-D]).info] +. \} +. el \{\ +. \" ... but, when the "-X" flag is NOT set, +. \" use only the argument(s) as the entire content +. \" of the "link text" +. \" +. rn PDFHREF.DESC PDFHREF.TEXT +. \} +. \} +. el \{\ +. \" No "link text" arguments are present, +. \" so, format the cross reference record to define +. \" the content of the "link text". +. \" +. pdf*href.format \\*[pdf:href(\\*[pdf:href-D]).info] +. \} +. \" 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. +. \" +. pdf*href.mark.begin "\\*[pdf:href.link]" +. if dPDFHREF.COLOUR .defcolor pdf:href.colour rgb \\*[PDFHREF.COLOUR] +. nop \&\m[\\*[PDFHREF.TEXT.COLOUR]]\\*[PDFHREF.TEXT]\m[]\c +. pdf*href.mark.end +. \" +. \" 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] +.. +.de pdf*href.map.init +.\" ---------------------------------------------------------------------- +.\" ---------------------------------------------------------------------- +.\" +.if dpdf:href.map-1 \{\ +. \" +. \" We have a reference map, but we haven't started to parse it yet. +. \" This must be the first map reference in pass 2, so we need to +. \" "kick-start" the parsing process, by loading the first indexed +. \" sub-map into the global map. +. \" +. rn pdf:href.map-1 pdf:href.map +. als pdf:href.map.internal pdf:href.map +. nr pdf:href.map.index 1 1 +. \} +.als pdf*href.map.init pdf*href.mark.idle +.. +.\" +.\" "pdf*href-Z" is used to add link co-ordinate entries to the +.\" "pdf:href.map". Primarily, it is used by the "pdfroff" formatter, +.\" to pass link co-ordinate data from one "groff" formatting pass to +.\" the next, and is not generally useful to the end user. +.\" +.de pdf*href-Z +.\" ---------------------------------------------------------------------- +.\" Usage: +.\" .pdfhref Z page-index x-displacement y-displacement +.\" Where: +.\" page-index is the reference mark's page number +.\" x-displacement is its offset from the left edge of the page +.\" y-displacement is its offset from the top edge of the page +.\" ( both displacement values are expressed in basic groff units, ) +.\" ( and measured perpendicular to their respective page edges. ) +.\" ---------------------------------------------------------------------- +.\" +.ie \\n(.$=3 .ds pdf:href.map-\\n+[pdf*href-Z.index] \\$* +.el .pdf:error pdfhref Z operator expects exactly three arguments +.. +.\" Initialise the auto-incrementing "pdf*href-Z.index" register, +.\" to ensure that sub-map numbering starts at 1. +.\" +.nr pdf*href-Z.index 0 1 +.\" +.de pdf*href.map.read +.\" ---------------------------------------------------------------------- +.\" Usage: (internal use only): +.\" .pdf*href.map.read co-ordinate name list ... +.\" ---------------------------------------------------------------------- +.\" +.\" Reads values from "pdf:href.map" to each named register, in turn +.\" Reading to "null" discards the corresponding value in "pdf:href.map" +.\" +.while \\n(.$ \{\ +. \" +. \" Loop over all registers named in the argument list, +. \" assigning values from "pdf:href.map" to each in turn. +. \" +. pdf:pop nr pdf:\\$1 pdf:href.map.internal +. if !dpdf:href.map.internal \{\ +. \" +. \" We ran out of map references in the current sub-map, +. \" so move on to the next indexed sub-map, if any. +. \" +. if dpdf:href.map-\\n+[pdf:href.map.index] \{\ +. rn pdf:href.map-\\n[pdf:href.map.index] pdf:href.map +. als pdf:href.map.internal pdf:href.map +. \} +. \} +. \" +. \" Proceed to the next named co-ordinate, (if any), specified +. \" in the argument list. +. \" +. shift +. \} +.\" +.\" Discard any assignments to a register named "null" +.\" +.rr pdf:null +.. +.de pdf*href.mark.begin +.\" ---------------------------------------------------------------------- +.\" ---------------------------------------------------------------------- +.pdf*href.map.init +.ie dpdf:href.map \{\ +. \" +. \" Once we have established a document reference map, +. \" then this, and all subsequent calls to "pdf*href.mark.begin", +. \" may be redirected to the reference mark resolver, and the +. \" "pdf*href.mark.end" macro has nothing further to do. +. \" +. pdf*href.mark.resolve \\$@ +. als pdf*href.mark.begin pdf*href.mark.resolve +. als pdf*href.mark.end pdf*href.mark.idle +. \} +.el \{\ +. \" Since we don't yet have a document reference map, the +. \" reference mark resolver will not work, in this pass of the +. \" formatter; this, and all subsequent calls to "pdf*href.mark.begin", +. \" may be redirected to "pdf*href.mark.end", which is responsible +. \" for emitting the reference mark data to be incorporated into +. \" the reference map in a subsequent formatting pass. +. \" +. pdf*href.mark.end +. als pdf*href.mark.begin pdf*href.mark.end +. \} +.. +.de pdf*href.mark.resolve +.\" ---------------------------------------------------------------------- +.\" ---------------------------------------------------------------------- +.ie '\\n(.z'' \{\ +. ds pdf:href.link \\$1 +. nr pdf:urx \\n(.o+\\n(.l +. pdf*href.map.read spg llx ury epg urx.end lly.end +. ie \\n[pdf:spg]=\\n[pdf:epg] \{\ +. \" +. \" This link is entirely contained on a single page ... +. \" emit the text, which defines the content of the link region, +. \" then make it active. +. \" +. pdf*href.mark.emit 1 \\n[pdf:urx.end] +. if \\n[pdf:lly]<\\n[pdf:lly.end] \{\ +. \" +. \" This link spans multiple output lines; we must save its +. \" original end co-ordinates, then define a new intermediate +. \" end point, to create a PDFMARK "hot-spot" extending from +. \" the start of the link to the end if its first line. +. \" +. nr pdf:ury +1v +. nr pdf:llx \\n(.o+\\n[.in] +. nr pdf:lly \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT] +. if \\n[pdf:ury]<\\n[pdf:lly] \{\ +. nr pdf:lly +\\*[PDFHREF.HEIGHT]-1v +. pdf*href.mark.emit 2 +. nr pdf:ury \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT] +. \} +. pdf*href.mark.emit 0 \\n[pdf:urx.end] +. \} +. pdf*href.mark.flush +. \} +. el \{\ +. \" This link is split across a page break, so ... +. \" We must mark the "hot-spot" region on the current page, +. \" BEFORE we emit the link text, as we will have moved off +. \" this page, by the time the text has been output. +. \" +. \" First step: define the region from the start of the link, +. \" to the end of its first line. +. \" +. pdf*href.mark.emit 1 \\n[pdf:urx] +. \" +. \" All additional regions MUST align with the left margin. +. \" +. nr pdf:llx \\n(.o+\\n[.in] +. \" +. \" If the current page can accommodate more than the current line, +. \" then it will include a second active region for this link; this +. \" will extend from just below the current line to the end of page +. \" trap, if any, or the bottom of the page otherwise, and occupy +. \" the full width of the page, between the margins. +. \" +. nr pdf:ury +1v +. pdf*href.mark.emit 3 +. \" +. \" We now need a page transition trap, to map the active link +. \" region(s), which overflow on to the following page(s); (the +. \" handler for this trap MUST have been previously installed). +. \" +. ie dpdf*href.mark.hook \{\ +. \" +. \" The page transition trap handler has been installed, +. \" so we may activate both it, and also the appropriate +. \" termination handler, to deactivate it when done. +. \" +. als pdf*href.mark.hook pdf*href.mark.trap +. \" +. \" Now we set up "pdf:epg" to count the number of page breaks +. \" which this link will span, and emit the link text, leaving +. \" the page trap macro to map active regions on intervening +. \" pages, which are included in the link. +. \" +. nr pdf:epg -\\n[pdf:spg] 1 +. \} +. el \{\ +. \" There was no handler initialised for the page trap, +. \" so we are unable to map the active regions for this link; +. \" we may discard the remaining map data for this link, +. \" and issue a diagnostic. +. \" +. pdf:error pdfhref: link dissociated at page break (trap not initialised) +. if dPDFHREF.BROKEN.COLOR \{\ +. \" +. \" The user may opt to have such broken links highlighted. +. \" We use "PDFHREF.BROKEN.COLOUR" to specify this requirement, +. \" but the user may prefer the American spelling, so we will +. \" handle both as equivalent. +. \" +. als PDFHREF.BROKEN.COLOUR PDFHREF.BROKEN.COLOR +. \} +. if dPDFHREF.BROKEN.COLOUR \{\ +. if dPDFHREF.COLOUR .als PDFHREF.COLOUR PDFHREF.BROKEN.COLOUR +. \} +. \} +. \} +. \} +.el \!.\\$0 \\$@ +.. +.\" +.\" Macro "pdf*href.mark.emit" is called only by "pdf*href". It is +.\" responsible for emitting the PDFMARK code, to establish the +.\" "hot-spot" region associated with a document or resource link. +.\" +.de pdf*href.mark.emit +.\" ---------------------------------------------------------------------- +.\" Usage: +.\" .pdf*href.mark.emit <action> [<end-urx>] +.\" <action> == 0 --> normal operation -- link height = 1 line +.\" <action> == 1 --> start of link -- add leading above text +.\" <action> == 2 --> overtall link -- set intermediate baseline +.\" <action> == 3 --> split link -- break at bottom of page +.\" ---------------------------------------------------------------------- +.\" +.if \\$1=1 \{\ +. \" +. \" Initialising a new link region ... +. \" Some different versions of "groff" disagree about the vertical +. \" displacement of "opminy", as emitted by "\O1|\h'-\w"|"u'\O2\c", +. \" relative to the current text baseline. Therefore, recompute +. \" the link displacement, independently of "opminy". +. \" +. mk pdf:ury.base +. while \\n[pdf:ury.base]<\\n[pdf:ury] .nr pdf:ury.base +1v +. nr pdf:ury.base -1m+\\n[PDFHREF.LEADING] +. \" +. \" adjust the end-point vertical displacement by the same offset, +. \" and then relocate the link starting point to its new displacement, +. \" as established by this base line relative computation. +. \" +. nr pdf:lly.end +\\n[pdf:ury.base]-\\n[pdf:ury]+\\*[PDFHREF.HEIGHT] +. rnn pdf:ury.base pdf:ury +. \} +.if \\$1<2 \{\ +. \" +. \" Link segment fits on a single line ... +. \" Set its height and end-point horizontal displacement accordingly. +. \" +. nr pdf:lly \\n[pdf:ury]+\\*[PDFHREF.HEIGHT] +. if \\n[pdf:lly]>=\\n[pdf:lly.end] .nr pdf:urx \\$2 +. \} +.ie \\$1=3 \{\ +. \" +. \" Link segment extends beyond the next page break ... +. \" Recompute truncated height, to just fit portion on current page, +. \" recursing to emit it, and leaving page trap mechanism to place +. \" continuation region(s) on following page(s). +. \" +. nr pdf:lly (\\n[.t]u-\\n[.V]u)/1v +. if \\n[pdf:lly]>0 \{\ +. nr pdf:lly \\n[pdf:ury]+\\n[pdf:lly]v-1v+\\*[PDFHREF.HEIGHT] +. pdf*href.mark.emit 2 +. \} +. \} +.el \{\ +. \" Link region size and placement has been fully specified ... +. \" Emit it. +. \" +. pdfmark \\*[pdf:href.link] /Rect [\\*[pdf:bbox]] /ANN +. \} +.. +.\" +.\" When "pdf*href" emits a link for which the "hot-spot" spans a +.\" page break, then we need to provide a "hook" in to the page break +.\" trap, so we can map the "hot-spot" regions which are to be placed +.\" on either side of the page break. +.\" +.\" Macro "pdf*href.mark.idle" is a dummy macro, which provide this +.\" "hook" for normal page breaks, where there is no link "hot-spot" +.\" crossing the break. +.\" +.de pdf*href.mark.idle +.\" ---------------------------------------------------------------------- +.\" Usage: +.\" Called only as an internal hook, by a page trap macro. +.\" Expects no arguments, and does nothing. +.\" ---------------------------------------------------------------------- +.. +.\" +.\" Macro "pdf*href.mark.trap" is the active "hook", which is substituted +.\" for "pdf*href,mark.idle" at those page breaks which are crossed by +.\" a link "hot-spot". +.\" +.de pdf*href.mark.trap +.\" ---------------------------------------------------------------------- +.\" Usage: +.\" Called only as an internal hook, by a page trap macro. +.\" Expects no arguments. Maps residual link "hot-spot" regions, +.\" which spill beyond any page break. Not to be invoked directly +.\" by the user, nor by any user supplied macro. +.\" ---------------------------------------------------------------------- +.\" +.mk pdf:ury +.nr pdf:ury +1v-1m-\\n[PDFHREF.LEADING] +.ie \\n-[pdf:epg] \{\ +. \" +. \" The link "hot-spot" extends across more than one page break, +. \" so, for each page which is completely contained within the +. \" extent of the link, simply mark the entire text area on the +. \" page as a "hot-spot". +. \" +. pdf*href.mark.emit 3 +. \} +.el \{\ +. \" The link "hot-spot" ends on the page which immediately follows +. \" the current page transition, so we may now finalise this link. +. \" +. nr pdf:lly \\n[pdf:ury]+\\*[PDFHREF.HEIGHT] +. if \\n[pdf:lly.end]>\\n[pdf:lly] \{\ +. \" +. \" The "hot-spot" extends beyond the first line of text, +. \" on its final page; compute and emit "hot-spot" region to cover +. \" the full with of the text area, including all but the last +. \" line of the link text. +. \" +. while \\n[pdf:lly.end]>\\n[pdf:lly] .nr pdf:lly +1v +. nr pdf:lly -1v +. pdf*href.mark.emit 2 +. \" +. \" Now, adjust the vertical "hot-spot" mapping reference, +. \" to identify the correct position for the last line of +. \" text, over which the "hot-spot" extends. +. \" +. nr pdf:ury \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT] +. \} +. \" +. \" We now have exactly one final line of text, over which we must +. \" emit a "hot-spot" region; map it, terminate page trap processing +. \" for this "hot-spot", and clean up the "hot-spot" mapping context. +. \" +. pdf*href.mark.emit 0 \\n[pdf:urx.end] +. als pdf*href.mark.hook pdf*href.mark.idle +. pdf*href.mark.flush +. \} +.. +.de pdf*href.mark.flush +.\" ---------------------------------------------------------------------- +.\" ---------------------------------------------------------------------- +.rr pdf:spg pdf:epg +.rr pdf:llx pdf:lly pdf:urx pdf:ury +.if dPDFHREF.COLOR .als PDFHREF.COLOUR PDFHREF.COLOR +.rr pdf:urx.end pdf:lly.end +.. +.de pdf*href.mark.end +.\" ---------------------------------------------------------------------- +.\" ---------------------------------------------------------------------- +\O1\Z'|'\O2\c +.. +.\" 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. +. +.de pdf*href-PT.init +.\" ---------------------------------------------------------------------- +.\" Usage: +.\" .pdfhref I -PT <macro-name> +.\" <macro-name> == name of user's page break trap macro +.\" ---------------------------------------------------------------------- +.\" +.\" Initially, map the page break hook to its default, do nothing helper. +. +.als pdf*href.mark.hook pdf*href.mark.idle +.ie !\\n(.$ \{\ +. \" +. \" Don't have enough arguments to specify a page trap macro name, +. \" so simply plant "pdf*href.mark.hook" as a top of page trap. +. \" +. wh 0 pdf*href.mark.hook +. \} +.el \{\ +. \" Page trap macro name is specified in "\\$1" ... +. \" +. ie d\\$1 \{\ +. \" +. \" When this page trap macro already exists, then we simply +. \" append a call to "pdf*href.mark.hook" to it. +. \" +. am \\$1 pdf*href.mark.idle +. pdf*href.mark.hook +. pdf*href.mark.idle +. \} +. el \{\ +. \" However, when the specified page trap macro does not yet +. \" exist, then we create it, and plant it as a top of page +. \" trap. +. \" +. de \\$1 pdf*href.mark.idle +. pdf*href.mark.hook +. pdf*href.mark.idle +. wh 0 \\$1 +. \} +. \} +.. +. +.\" "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]) >> +.\" +.\" Local Variables: +.\" mode: nroff +.\" End: +.\" vim: filetype=groff: +.\" pdfmark.tmac: end of file diff --git a/contrib/pdfmark/pdfroff.1.man b/contrib/pdfmark/pdfroff.1.man new file mode 100644 index 0000000..029a1f4 --- /dev/null +++ b/contrib/pdfmark/pdfroff.1.man @@ -0,0 +1,981 @@ +.TH pdfroff @MAN1EXT@ "@MDATE@" "groff @VERSION@" +.SH Name +pdfroff \- construct files in Portable Document Format using +.I groff +. +. +.\" ==================================================================== +.\" Legal Terms +.\" ==================================================================== +.\" +.\" Copyright (C) 2005-2020 Free Software Foundation, Inc. +.\" +.\" This file is part of groff, the GNU roff type-setting system. +.\" +.\" Permission is granted to copy, distribute and/or modify this +.\" document under the terms of the GNU Free Documentation License, +.\" Version 1.3 or any later version published by the Free Software +.\" Foundation; with no Invariant Sections, with no Front-Cover Texts, +.\" and with no Back-Cover Texts. +.\" +.\" A copy of the Free Documentation License is included as a file +.\" called FDL in the main directory of the groff source package. +. +. +.\" Save and disable compatibility mode (for, e.g., Solaris 10/11). +.do nr *groff_pdfroff_1_man_C \n[.cp] +.cp 0 +. +.\" Define fallback for groff 1.23's MR macro if the system lacks it. +.nr do-fallback 0 +.if !\n(.f .nr do-fallback 1 \" mandoc +.if \n(.g .if !d MR .nr do-fallback 1 \" older groff +.if !\n(.g .nr do-fallback 1 \" non-groff *roff +.if \n[do-fallback] \{\ +. de MR +. ie \\n(.$=1 \ +. I \%\\$1 +. el \ +. IR \%\\$1 (\\$2)\\$3 +. . +.\} +.rr do-fallback +. +. +.\" ==================================================================== +.SH Synopsis +.\" ==================================================================== +. +.SY pdfroff +.RI [ groff-option ] +.RB [ \-\-emit\-ps ] +.RB [ \-\-no\-toc\-relocation ] +.RB [ \-\-no\-kill\-null\-pages ] +.RB [ \-\-stylesheet=\c +.IR name ] +.RB [ \-\-no\-pdf\-output ] +.RB [ \-\-pdf\-output=\c +.IR name ] +.RB [ \-\-no\-reference\-dictionary ] +.RB [ \-\-reference\-dictionary=\c +.IR name ] +.RB [ \-\-report\-progress ] +.RB [ \-\-keep\-temporary\-files ] +.RI [ file\~ .\|.\|.] +.YS +. +. +.SY pdfroff +.B \-h +. +.SY pdfroff +.B \-\-help +.YS +. +. +.SY pdfroff +.B \-v +.RI [ groff-option +\&.\|.\|.\&] +. +.SY pdfroff +.B \-\-version +.RI [ groff-option +\&.\|.\|.\&] +.YS +. +. +.P +.I groff-option +is any short option supported by +.MR groff @MAN1EXT@ +except for +.BR \-h , +.BR \-T , +and +.BR \-v ; +see section \[lq]Usage\[rq] below. +. +. +.\" ==================================================================== +.SH Description +.\" ==================================================================== +. +.I pdfroff +is a wrapper program for the GNU text processing system, +.IR groff . +. +It transparently handles the mechanics of multiple pass +.I groff +processing, +when applied to suitably marked up +.I groff +source files, +such that tables of contents and body text are formatted separately, +and are subsequently combined in the correct order, +for final publication as a single PDF document. +. +A further optional \[lq]style sheet\[rq] capability is provided; +this allows for the definition of content which is required to precede +the table of contents, +in the published document. +. +. +.P +For each invocation of +.IR pdfroff , +the ultimate +.I groff +output stream is post-processed by the Ghostscript +.MR gs 1 +interpreter to produce a finished PDF document. +. +. +.P +.I pdfroff +makes no assumptions about, +and imposes no restrictions on, +the use of any +.I groff +macro packages which the user may choose to employ, +in order to achieve a desired document format; +however, +it +.I does +include specific built in support for the +.I \%pdfmark +macro package, +should the user choose to employ it. +. +Specifically, +if the +.I pdfhref +macro, +defined in the +.I \%pdfmark.tmac +package, +is used to define public reference marks, +or dynamic links to such reference marks, +then +.I pdfroff +performs as many preformatting +.I groff +passes as required, +up to a maximum limit of +.IR four , +in order to compile a document reference dictionary, +to resolve +references, +and to expand the dynamically defined content of links. +. +. +.\" ==================================================================== +.SH Usage +.\" ==================================================================== +. +The command line is parsed in accordance with normal GNU conventions, +but with one exception\(emwhen specifying any short form option +(i.e., +a single character option introduced by a single hyphen), +and if that option expects an argument, +then it +.I must +be specified independently +(i.e., +it may +.I not +be appended to any group of other single character short form options). +. +. +.P +Long form option names +(i.e., +those introduced by a double hyphen) +may +be abbreviated to their minimum length unambiguous initial substring. +. +. +.P +Otherwise, +.I pdfroff +usage closely mirrors that of +.I groff +itself. +. +Indeed, +with the exception of the +.BR \-h , +.BR \-v , +and +.BI \-T \ dev +short form options, +and all long form options, +which are parsed +internally by +.IR pdfroff , +all options and file name arguments specified on the command line are +passed on to +.IR groff , +to control the formatting of the PDF document. +. +Consequently, +.I pdfroff +accepts all options and arguments, +as specified in +.MR groff @MAN1EXT@ , +which may also be considered as the definitive reference for all +standard +.I pdfroff +options and argument usage. +. +. +.\" ==================================================================== +.SH Options +.\" ==================================================================== +. +.I pdfroff +accepts all of the short form options +(i.e., +those introduced by a +single hyphen), +which are available with +.I groff +itself. +. +In most cases, +these are simply passed transparently to +.IR groff ; +the following, +however, +are handled specially by +.IR pdfroff . +. +. +.TP +.B \-h +Same as +.BR \-\-help ; +see below. +. +. +.TP +.B \-i +Process standard input, +after all other specified input files. +. +This is passed transparently to +.IR groff , +but, +if grouped with other options, +it +.I must +be the first in the group. +. +Hiding it within a group breaks standard input processing, +in the multiple-pass +.I groff +processing context of +.IR pdfroff . +. +. +.TP +.BI \-T \ dev +Only +.B \-T\ ps +is supported by +.IR pdfroff . +. +Attempting to specify any other device causes +.I pdfroff +to abort. +. +. +.TP +.B \-v +Same as +.BR \-\-version ; +see below. +. +. +.P +See +.MR groff @MAN1EXT@ +for a description of all other short form options, +which are +transparently passed through +.I pdfroff +to +.IR groff . +. +. +.P +All long form options +(i.e., +those introduced by a double hyphen) +are interpreted locally by +.IR pdfroff ; +they are +.I not +passed on to +.IR groff , +unless otherwise stated below. +. +. +.TP +.B \-\-help +Causes +.I pdfroff +to display a summary of the its usage syntax, +and supported options, +and then exit. +. +. +.TP +.B \-\-emit\-ps +Suppresses the final output conversion step, +causing +.I pdfroff +to emit PostScript output instead of PDF. +. +This may be useful to capture intermediate PostScript output when using +a specialised postprocessor, +such as +.I gpresent +for example, +in place of the default Ghostscript PDF writer. +. +. +.TP +.B \-\-keep\-temporary\-files +Suppresses the deletion of temporary files, +which normally occurs +after +.I pdfroff +has completed PDF document formatting; +this may be useful when debugging formatting problems. +. +. +.IP +See section \[lq]Files\[rq] below for a description of the temporary +files used by +.IR pdfroff . +. +. +.TP +.B \-\-no\-pdf\-output +May be used with the +.BI \%\-\-reference\-dictionary= name +option +(described below) +to eliminate the overhead of PDF formatting when running +.I pdfroff +to create a reference dictionary for use in a different document. +. +. +.TP +.B \-\-no\-reference\-dictionary +May be used to eliminate the overhead of creating a reference +dictionary, +when it is known that the target PDF document contains no public +references, +created by the +.B pdfhref +macro. +. +. +.TP +.B \-\-no\-toc\-relocation +May be used to eliminate the extra +.I groff +processing pass, +which is required to generate a table of contents, +and relocate it to the start of the PDF document, +when processing any document which lacks an automatically +generated table of contents. +. +. +.TP +.B \-\-no\-kill\-null\-pages +While preparing for simulation of the manual collation step, +which is traditionally required to relocate a +.I "table of contents" +to the start of a document, +.I pdfroff +accumulates a number of empty page descriptions +into the intermediate PostScript output stream. +. +During the final collation step, +these empty pages are normally discarded from the finished document; +this option forces +.I pdfroff +to leave them in place. +. +. +.TP +.BI \-\-pdf\-output= name +Specifies the name to be used for the resultant PDF document; +if unspecified, +the PDF output is written to standard output. +. +A future version of +.I pdfroff +may use this option, +to encode the document name in a generated reference dictionary. +. +. +.TP +.BI \-\-reference\-dictionary= name +Specifies the name to be used for the generated reference dictionary +file; +if unspecified, +the reference dictionary is created in a temporary file, +which is deleted when +.I pdfroff +completes processing of the current document. +. +This option +.I must +be specified, +if it is desired to save the reference dictionary, +for use in references placed in other PDF documents. +. +. +.TP +.B \-\-report\-progress +Causes +.I pdfroff +to display an informational message on standard error, +at the start of each +.I groff +processing pass. +. +. +.TP +.BI \-\-stylesheet= name +Specifies the name of an +.IR "input file" , +to be used as a style sheet for formatting of content, +which is to be placed +.I before +the table of contents, +in the formatted PDF document. +. +. +.TP +.B \-\-version +Causes +.I pdfroff +to display a version identification message. +. +The entire command line is then passed transparently to +.IR groff , +in a +.I one +pass operation +.IR only , +in order to display the associated +.I groff +version information, +before exiting. +. +. +.\" ==================================================================== +.SH Environment +.\" ==================================================================== +. +The following environment variables may be set, +and exported, +to modify the behaviour of +.IR pdfroff . +. +. +.TP +.I PDFROFF_COLLATE +Specifies the program to be used +for collation of the finished PDF document. +. +. +.IP +This collation step may be required to move +.I tables of contents +to the start of the finished PDF document, +when formatting with traditional macro packages, +which print them at the end. +. +However, +users should not normally need to specify +.IR \%PDFROFF_COLLATE , +(and indeed, +are not encouraged to do so). +. +If unspecified, +.I pdfroff +uses +.MR sed @MAN1EXT@ +by default, +which normally suffices. +. +. +.IP +If +.I \%PDFROFF_COLLATE +.I is +specified, +then it must act as a filter, +accepting a list of file name arguments, +and write its output to the standard output stream, +whence it is piped to the +.IR \%PDFROFF_POSTPROCESSOR_COMMAND , +to produce the finished PDF output. +. +. +.IP +When specifying +.IR \%PDFROFF_COLLATE , +it is normally necessary to also specify +.IR \%PDFROFF_KILL_NULL_PAGES . +. +. +.IP +.I \%PDFROFF_COLLATE +is ignored, +if +.I pdfroff +is invoked with the +.B \%\-\-no\-kill\-null\-pages +option. +. +. +.TP +.I PDFROFF_KILL_NULL_PAGES +Specifies options to be passed to the +.I \%PDFROFF_COLLATE +program. +. +. +.IP +It should not normally be necessary to specify +.IR \%PDFROFF_KILL_NULL_PAGES . +. +The internal default is a +.MR sed @MAN1EXT@ +script, +which is intended to remove completely blank pages +from the collated output stream, +and which should be appropriate in most applications of +.IR pdfroff . +. +However, +if any alternative to +.MR sed @MAN1EXT@ +is specified for +.IR \%PDFROFF_COLLATE , +then it is likely that a corresponding alternative specification for +.I \%PDFROFF_KILL_NULL_PAGES +is required. +. +. +.IP +As in the case of +.IR \%PDFROFF_COLLATE , +.I \%PDFROFF_KILL_NULL_PAGES +is ignored, +if +.I pdfroff +is invoked with the +.B \%\-\-no\-kill\-null\-pages +option. +. +. +.TP +.I PDFROFF_POSTPROCESSOR_COMMAND +Specifies the command to be used for the final document conversion +from PostScript intermediate output to PDF. +. +It must behave as a filter, +writing its output to the standard output stream, +and must accept an arbitrary number of +.I files .\|.\|.\& +arguments, +with the special case of +.RB \[lq] \- \[rq] +representing the standard input stream. +. +. +.IP +If unspecified, +.I \%PDFROFF_POSTPROCESSOR_COMMAND +defaults to +. +.RS 12n +.EX +gs \-dBATCH \-dQUIET \-dNOPAUSE \-dSAFER \-sDEVICE=pdfwrite \e + \-sOutputFile=\- +.EE +.RE +. +. +.TP +.I GROFF_TMPDIR +Identifies the directory in which +.I pdfroff +should create temporary files. +. +If +.I \%GROFF_TMPDIR +is +.I not +specified, +then the variables +.IR TMPDIR , +.I TMP +and +.I TEMP +are considered in turn as possible temporary file repositories. +. +If none of these are set, +then temporary files are created +in the current directory. +. +. +.TP +.I GROFF_GHOSTSCRIPT_INTERPRETER +Specifies the program to be invoked when +.I pdfroff +converts +.I groff +PostScript output to PDF. +. +If +.I \%PDFROFF_POSTPROCESSOR_COMMAND +is specified, +then the command name it specifies is +.I implicitly +assigned to +.IR \%GROFF_GHOSTSCRIPT_INTERPRETER , +overriding any explicit setting specified in the environment. +. +If +.I \%GROFF_GHOSTSCRIPT_INTERPRETER +is not specified, +then +.I pdfroff +searches the process +.IR PATH , +looking for a program with any of the well known names +for the Ghostscript interpreter; +if no Ghostscript interpreter can be found, +.I pdfroff +aborts. +. +. +.TP +.I GROFF_AWK_INTERPRETER +Specifies the program to be invoked when +.I pdfroff +is extracting reference dictionary entries from a +.I groff +intermediate message stream. +. +If +.I \%GROFF_AWK_INTERPRETER +is not specified, +then +.I pdfroff +searches the process +.IR PATH , +looking for any of the preferred programs, +.IR gawk , +.IR mawk , +.IR nawk , +and +.IR awk , +in that order; +if none of these are found, +.I pdfroff +issues a warning message, +and continue processing; +however, +in this case, +no reference dictionary is created. +. +. +.TP +.I OSTYPE +Typically defined automatically by the operating system, +.I \%OSTYPE +is used on Microsoft Win32/MS-DOS platforms +.IR only , +to infer the default +.I \%PATH_SEPARATOR +character, +which is used when parsing the process +.I PATH +to search for external helper programs. +. +. +.TP +.I PATH_SEPARATOR +If set, +.I \%PATH_SEPARATOR +overrides the default separator character, +(\[oq]:\[cq] on POSIX/Unix systems, +inferred from +.I \%OSTYPE +on Microsoft Win32/MS-DOS), +which is used when parsing the process +.I PATH +to search for external helper programs. +. +. +.TP +.I SHOW_PROGRESS +If this is set to a non-empty value, +then +.I pdfroff +always behaves as if the +.B \%\-\-report\-progress +option is specified on the command line. +. +. +.\" ==================================================================== +.SH Files +.\" ==================================================================== +. +Input and output files for +.I pdfroff +may be named according to any convention of the user's choice. +. +Typically, +input files may be named according to the choice of the principal +normatting macro package, +e.g., +.RI file .ms +might be an input file for formatting using the +.I ms +macros +.RI ( s.tmac ); +normally, +the final output file should be named +.RI file .pdf . +. +. +.P +Temporary files created by +.I pdfroff +are placed in the file system hierarchy, +in or below the directory specified by environment variables +(see section \[lq]Environment\[rq] above). +. +If +.MR mktemp @MAN1EXT@ +is available, +it is invoked to create a private subdirectory of +the nominated temporary files directory, +(with subdirectory name derived from the template +.IR pdfroff\-XXXXXXXXXX ); +if this subdirectory is successfully created, +the temporary files will be placed within it, +otherwise they will be placed directly in the directory +nominated in the environment. +. +. +.P +All temporary files themselves +are named according to the convention +.IR pdf $$ . *, +where +.I $$ +is the standard shell variable representing the process identifier of +the +.I pdfroff +process itself, +and +.I * +represents any of the extensions used by +.I pdfroff +to identify the following temporary and intermediate files. +. +. +.TP +.IR pdf $$ .tmp +A scratch pad file, +used to capture reference data emitted by +.IR groff , +during the +.I reference dictionary +compilation phase. +. +. +.TP +.IR pdf $$ .ref +The +.IR "reference dictionary" , +as compiled in the last but one pass of the +.I reference dictionary +compilation phase; +(at the start of the first pass, +this file is created empty; +in successive passes, +it contains the +.I reference dictionary +entries, +as collected in the preceding pass). +. +. +.IP +If the +.BR \%\-\-reference\-dictionary =\c +.I name +option is specified, +this intermediate file becomes permanent, +and is named +.IR name , +rather than +.IR pdf $$ .ref . +. +. +.TP +.IR pdf $$ .cmp +Used to collect +.I reference dictionary +entries during the active pass of the +.I reference dictionary +compilation phase. +. +At the end of any pass, +when the content of +.IR pdf $$ .cmp +compares as identical to +.IR pdf $$ .ref , +(or the corresponding file named by the +.BR \%\-\-reference\-dictionary =\c +.I name +option), +then +.I reference dictionary +compilation is terminated, +and the +.I document reference map +is appended to this intermediate file, +for inclusion in the final formatting passes. +. +. +.TP +.IR pdf $$ .tc +An intermediate +.I PostScript +file, +in which \[lq]Table of Contents\[rq] entries are collected, +to facilitate relocation before the body text, +on ultimate output to the +.I Ghostscript +postprocessor. +. +. +.TP +.IR pdf $$ .ps +An intermediate +.I PostScript +file, +in which the body text is collected prior to ultimate output to the +.I Ghostscript +postprocessor, +in the proper sequence, +.I after +.IR pdf $$ .tc . +. +. +.\" ==================================================================== +.SH Authors +.\" ==================================================================== +. +.I pdfroff +was written by +.MT keith\:.d\:.marshall@\:ntlworld\:.com +Keith Marshall +.ME , +who maintains it at +.UR https://\:osdn\:.net/\:users/\:keith/\:pf/\:\%groff-pdfmark/\ +\:wiki/\:\%FrontPage +his +.I groff-pdfmark +OSDN site +.UE . +. +.IR groff 's +version may be withdrawn in a future release. +. +. +.\" ==================================================================== +.SH "See also" +.\" ==================================================================== +. +.IR "Groff: The GNU Implementation of troff" , +by Trent A.\& Fisher and Werner Lemberg, +is the primary +.I groff +manual. +. +You can browse it interactively with \[lq]info groff\[rq]. +. +. +.P +Since +.I pdfroff +provides a superset of all +.I groff +capabilities, +the above manual, +or its terser reference page, +.MR groff @MAN7EXT@ +may also be considered definitive references to all +.I standard +capabilities of +.IR pdfroff , +with this document providing the reference to +.IR pdfroff 's +extended features. +. +. +.P +While +.I pdfroff +imposes neither any restriction on, +nor any requirement for, +the use of any specific +.I groff +macro package, +a number of supplied macro packages, +and in particular those associated with the package +.IR \%pdfmark.tmac , +are best suited for use with +.I pdfroff +as the preferred formatter. +. +. +.TP +.I @PDFDOCDIR@/\:\%pdfmark.pdf +\[lq]Portable Document Format Publishing with GNU +.IR Troff \[rq], +by Keith Marshall, +offers detailed documentation on the use of these packages. +. +This file, +together with its source, +.IR \%pdfmark.ms , +is part of the +.I groff +distribution. +. +. +.\" Restore compatibility mode (for, e.g., Solaris 10/11). +.cp \n[*groff_pdfroff_1_man_C] +.do rr *groff_pdfroff_1_man_C +. +. +.\" Local Variables: +.\" fill-column: 72 +.\" mode: nroff +.\" End: +.\" vim: set filetype=groff textwidth=72: diff --git a/contrib/pdfmark/pdfroff.sh b/contrib/pdfmark/pdfroff.sh new file mode 100644 index 0000000..f34c841 --- /dev/null +++ b/contrib/pdfmark/pdfroff.sh @@ -0,0 +1,682 @@ +#! /bin/sh +# ------------------------------------------------------------------------------ +# +# Function: Format PDF Output from groff Markup +# +# Copyright (C) 2005-2021 Free Software Foundation, Inc. +# Written by Keith Marshall (keith.d.marshall@ntlworld.com) +# +# 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/>. +# +# ------------------------------------------------------------------------------ +# +# Set up an identifier for the NULL device. +# In most cases "/dev/null" will be correct, but some shells on +# MS-DOS/MS-Windows systems may require us to use "NUL". +# + NULLDEV="/dev/null" + test -c $NULLDEV || NULLDEV="NUL" +# +# Set up the command name to use in diagnostic messages. +# (We can't assume we have 'basename', so use the full path if required. +# Also use the 'exec 2>...' workaround for a bug in Cygwin's 'ash'). +# + CMD=`exec 2>$NULLDEV; basename $0` || CMD=$0 +# +# To ensure that prerequisite helper programs are available, and are +# executable, a [fairly] portable method of detecting such programs is +# provided by function 'searchpath'. +# + searchpath(){ + # + # Usage: searchpath progname path + # + IFS=${PATH_SEPARATOR-":"} prog=':' + for dir in $2 + do + for ext in '' '.exe' + # + # try 'progname' with all well known extensions + # (e.g. Win32 may require 'progname.exe') + # + do + try="$dir/$1$ext" + test -f "$try" && test -x "$try" && prog="$try" && break + done + test "$prog" = ":" || break + done + echo "$prog" + } +# @PATH_SEARCH_SETUP@ +# +# If the system maps '/bin/sh' to some 'zsh' implementation, +# then we may need this hack, adapted from autoconf code. +# + test x${ZSH_VERSION+"set"} = x"set" && NULLCMD=":" \ + && (emulate sh) >$NULLDEV 2>&1 && emulate sh +# +# We need both 'grep' and 'sed' programs, to parse script options, +# and we also need 'cat', to display help and some error messages, +# so ensure they are all installed, before we continue. +# + CAT=`searchpath cat "$PATH"` + GREP=`searchpath grep "$PATH"` + SED=`searchpath sed "$PATH"` +# +# Another fundamental requirement is the 'groff' program itself; +# we MUST use a 'groff' program located in 'GROFF_BIN_DIR', if this +# is specified; if not, we will search 'GROFF_BIN_PATH', only falling +# back to a 'PATH' search, if neither of these is specified. +# + if test -n "$GROFF_BIN_DIR" + then + GPATH=GROFF_BIN_DIR + GROFF=`searchpath groff "$GROFF_BIN_DIR"` +# + elif test -n "$GROFF_BIN_PATH" + then + GPATH=GROFF_BIN_PATH + GROFF=`searchpath groff "$GROFF_BIN_PATH"` +# + else + GPATH=PATH + GROFF=`searchpath groff "$PATH"` + fi +# +# If one or more of these is missing, diagnose and bail out. +# + NO='' NOPROG="$CMD: installation problem: cannot find program" + test "$CAT" = ":" && echo >&2 "$NOPROG 'cat' in PATH" && NO="$NO 'cat'" + test "$GREP" = ":" && echo >&2 "$NOPROG 'grep' in PATH" && NO="$NO 'grep'" + test "$GROFF" = ":" && echo >&2 "$NOPROG 'groff' in $GPATH" && NO="$NO 'groff'" + test "$SED" = ":" && echo >&2 "$NOPROG 'sed' in PATH" && NO="$NO 'sed'" + if test -n "$NO" + then + set $NO + test $# -gt 1 && NO="s" IS="are" || NO='' IS="is" + while test $# -gt 0 + do + test $# -gt 2 && NO="$NO $1," + test $# -eq 2 && NO="$NO $1 and" && shift + test $# -lt 2 && NO="$NO $1" + shift + done + $CAT >&2 <<-ETX + + *** FATAL INSTALLATION ERROR *** + + The program$NO $IS required by '$CMD', + but cannot be found; '$CMD' is unable to continue. + + ETX + exit 1 + fi +# +# Identify the postprocessor command, for writing PDF output. +# (May be forced, by defining PDFROFF_POSTPROCESSOR_COMMAND in the environment; +# if this is not set, leave blank to use the built in default). +# + if test -n "${PDFROFF_POSTPROCESSOR_COMMAND}" + then + GROFF_GHOSTSCRIPT_INTERPRETER=`set command ${PDFROFF_POSTPROCESSOR_COMMAND}; + echo $2` + fi +# +# Set up temporary/intermediate file locations, with traps to +# clean them up on exit. Note that, for greater portability, we +# prefer to refer to events by number, rather than by symbolic +# names; thus, the EXIT event is trapped as event zero. +# + export TMPDIR GROFF_TMPDIR + TMPDIR=${GROFF_TMPDIR=${TMPDIR-${TMP-${TEMP-"."}}}} + if GROFF_TMPDIR=`exec 2>${NULLDEV}; mktemp -dt pdfroff-XXXXXXXXXX` + then + # + # We successfully created a private temporary directory, + # so to clean up, we may simply purge it. + # + trap "rm -rf ${GROFF_TMPDIR}" 0 + # + else + # + # Creation of a private temporary directory was unsuccessful; + # fall back to user nominated directory, (using current directory + # as default), and schedule removal of only the temporary files. + # + GROFF_TMPDIR=${TMPDIR} + trap "rm -f ${GROFF_TMPDIR}/pdf$$.*" 0 + fi + # + # In the case of abnormal termination events, we force an exit + # (with status code '1'), leaving the normal exit trap to clean + # up the temporary files, as above. Note that we again prefer + # to refer to events by number, rather than by symbolic names; + # here we trap SIGHUP, SIGINT, SIGQUIT, SIGPIPE and SIGTERM. + # + trap "exit 1" 1 2 3 13 15 +# + WRKFILE=${GROFF_TMPDIR}/pdf$$.tmp +# + REFCOPY=${GROFF_TMPDIR}/pdf$$.cmp + REFFILE=${GROFF_TMPDIR}/pdf$$.ref +# + CS_DATA="" + TC_DATA=${GROFF_TMPDIR}/pdf$$.tc + BD_DATA=${GROFF_TMPDIR}/pdf$$.ps +# +# Initialise 'groff' format control settings, +# to discriminate table of contents and document body formatting passes. +# + TOC_FORMAT="-rPHASE=1" + BODY_FORMAT="-rPHASE=2" +# + LONGOPTS=" + help reference-dictionary no-reference-dictionary + stylesheet pdf-output no-pdf-output + version report-progress no-toc-relocation + emit-ps keep-temporary-files no-kill-null-pages + " +# Parse the command line, to identify 'pdfroff' specific options. +# Collect all other parameters into new argument and file lists, +# to be passed on to 'groff', enforcing the '-Tps' option. +# + DIFF="" STREAM="" INPUT_FILES="" + SHOW_VERSION="" GROFF_STYLE="$GROFF -Tps" + while test $# -gt 0 + do + case "$1" in +# +# Long options must be processed locally ... +# + --*) +# +# First identify, matching any abbreviation to its full form. +# + MATCH="" OPTNAME=`IFS==; set dummy $1; echo $2` + for OPT in $LONGOPTS + do + MATCH="$MATCH"`echo --$OPT | $GREP "^$OPTNAME"` + done +# +# For options in the form --option=value +# capture any specified value into $OPTARG. +# + OPTARG=`echo $1 | $SED -n s?"^${OPTNAME}="??p` +# +# Perform case specific processing for matched option ... +# + case "$MATCH" in + + --help) + $CAT <<-ETX + Usage: $CMD [-option ...] [--long-option ...] [file ...] + + Options: + -h + --help + Display this usage summary, and exit. + + -v + --version + Display a version identification message and exit. + + --report-progress + Enable console messages, indicating the progress of the + PDF document formatting process. + + --emit-ps + Emit PostScript output instead of PDF; this may be useful + when the ultimate PDF output is to be generated by a more + specialised postprocessor, (e.g. gpresent), rather than + the default GhostScript PDF writer. + + --pdf-output=name + Write the PDF, (or PostScript), output stream to file + 'name'; if this option is unspecified, standard output + is used for PDF, (or PostScript), output. + + --no-pdf-output + Suppress the generation of PDF, (or PostScript), output + entirely; use this with the --reference-dictionary option, + if processing a document stream to produce only a + reference dictionary. + + --no-reference-dictionary + Suppress the generation of a '$CMD' reference dictionary + for the PDF document. Normally '$CMD' will create a + reference dictionary, at the start of document processing; + this option can accelerate processing, if it is known in + advance, that no reference dictionary is required. + + --reference-dictionary=name + Save the document reference dictionary in file 'name'. + If 'name' already exists, when processing commences, it + will be used as the base case, from which the updated + dictionary will be derived. If this option is not used, + then the reference dictionary, created during the normal + execution of '$CMD', will be deleted on completion of + document processing. + + --stylesheet=name + Use the file 'name' as a 'groff' style sheet, to control + the appearance of the document's front cover section. If + this option is not specified, then no special formatting + is applied, to create a front cover section. + + --no-toc-relocation + Suppress the multiple pass 'groff' processing, which is + normally required to position the table of contents at the + start of a PDF document. + + --no-kill-null-pages + Suppress the 'null page' elimination filter, which is used + to remove the excess blank pages produced by the collation + algorithm used for 'toc-relocation'. + + --keep-temporary-files + Suppress the normal clean up of temporary files, which is + scheduled when 'pdfroff' completes. + + ETX + exit 0 + ;; + + --version) + GROFF_STYLE="$GROFF_STYLE \"$1\"" + SHOW_VERSION="GNU pdfroff (groff) version @VERSION@" + ;; + + --report-progress) + SHOW_PROGRESS=echo + ;; + + --keep-temporary-files) + trap "" 0 + ;; + + --emit-ps) + PDFROFF_POSTPROCESSOR_COMMAND="$CAT" + ;; + + --pdf-output) + PDF_OUTPUT="$OPTARG" + ;; + + --no-pdf-output) + PDF_OUTPUT="$NULLDEV" + ;; + + --reference-dictionary) + REFFILE="$OPTARG" + ;; + + --no-reference-dictionary) + AWK=":" DIFF=":" REFFILE="$NULLDEV" REFCOPY="$NULLDEV" + ;; + + --stylesheet) + STYLESHEET="$OPTARG" CS_DATA=${GROFF_TMPDIR}/pdf$$.cs + ;; + + --no-toc-relocation) + TC_DATA="" TOC_FORMAT="" BODY_FORMAT="" + ;; + + --no-kill-null-pages) + PDFROFF_COLLATE="$CAT" PDFROFF_KILL_NULL_PAGES="" + ;; +# +# any other non-null match must have matched more than one defined case, +# so report the ambiguity, and bail out. +# + --*) + echo >&2 "$CMD: ambiguous abbreviation in option '$1'" + exit 1 + ;; +# +# while no match at all simply represents an undefined case. +# + *) + echo >&2 "$CMD: unknown option '$1'" + exit 1 + ;; + esac + ;; +# +# A solitary hyphen, as an argument, means "stream STDIN through groff", +# while the "-i" option means "append STDIN stream to specified input files", +# so set up a mechanism to achieve this, for ALL 'groff' passes. +# + - | -i*) + STREAM="$CAT ${GROFF_TMPDIR}/pdf$$.in |" + test "$1" = "-" && INPUT_FILES="$INPUT_FILES $1" \ + || GROFF_STYLE="$GROFF_STYLE $1" + ;; +# +# Those standard options which expect an argument, but are specified with +# an intervening space, between flag and argument, must be reparsed, so we +# can trap invalid use of '-T dev', or missing input files. +# + -[dfFILmMnoPrTwW]) + OPTNAME="$1" + shift; set reparse "$OPTNAME$@" + ;; +# +# Among standard options, '-Tdev' is treated as a special case. +# '-Tps' is automatically enforced, so if specified, is silently ignored. +# + -Tps) ;; +# +# No other '-Tdev' option is permitted. +# + -T*) echo >&2 "$CMD: option '$1' is incompatible with PDF output" + exit 1 + ;; +# +# '-h' and '-v' options redirect to their equivalent long forms ... +# + -h*) set redirect --help + ;; +# + -v*) shift; set redirect --version "$@" + ;; +# +# All other standard options are simply passed through to 'groff', +# with no validation beforehand. +# + -*) GROFF_STYLE="$GROFF_STYLE \"$1\"" + ;; +# +# All non-option arguments are considered as possible input file names, +# and are passed on to 'groff', unaltered. +# + *) INPUT_FILES="$INPUT_FILES \"$1\"" + ;; + esac + shift + done +# +# If the '-v' or '--version' option was specified, +# then we simply emulate the behaviour of 'groff', with this option, +# and quit. +# + if test -n "$SHOW_VERSION" + then + echo >&2 "$SHOW_VERSION" + echo >&2; eval $GROFF_STYLE $INPUT_FILES + exit $? + fi +# +# Establish how to invoke 'echo', suppressing the terminating newline. +# (Adapted from 'autoconf' code, as found in 'configure' scripts). +# + case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,*-n*) n='' c='' ;; + *c*) n='-n' c='' ;; + *) n='' c='\c' ;; + esac +# +# If STDIN is specified among the input files, +# or if no input files are specified, then we need to capture STDIN, +# so we can replay it into each 'groff' processing pass. +# + test -z "$INPUT_FILES" && STREAM="$CAT ${GROFF_TMPDIR}/pdf$$.in |" + test -n "$STREAM" && $CAT > ${GROFF_TMPDIR}/pdf$$.in +# +# Unless reference resolution is explicitly suppressed, +# we initiate it by touching the cross reference dictionary file, +# and initialise the comparator, to kickstart the reference resolver loop. +# + SAY=":" + if test -z "$DIFF" + then + >> $REFFILE + echo kickstart > $REFCOPY + test x${SHOW_PROGRESS+"set"} = x"set" && SAY=echo +# +# In order to correctly resolve 'pdfmark' references, +# we need to have both the 'awk' and 'diff' programs available. +# + NO='' + if test -n "$GROFF_AWK_INTERPRETER" + then + AWK="$GROFF_AWK_INTERPRETER" + test -f "$AWK" && test -x "$AWK" || AWK=":" + else + for prog in @GROFF_AWK_INTERPRETERS@ + do + AWK=`searchpath $prog "$PATH"` + test "$AWK" = ":" || break + done + fi + DIFF=`searchpath diff "$PATH"` + test "$AWK" = ":" && echo >&2 "$NOPROG 'awk' in PATH" && NO="$NO 'awk'" + test "$DIFF" = ":" && echo >&2 "$NOPROG 'diff' in PATH" && NO="$NO 'diff'" + if test -n "$NO" + then + set $NO + SAY=":" AWK=":" DIFF=":" + test $# -gt 1 && NO="s $1 and $2 are" || NO=" $1 is" + $CAT >&2 <<-ETX + + *** WARNING *** + + The program$NO required, but cannot be found; + consequently, '$CMD' is unable to resolve 'pdfmark' references. + + Document processing will continue, but no 'pdfmark' reference dictionary + will be compiled; if any 'pdfmark' reference appears in the resulting PDF + document, the formatting may not be correct. + + ETX + fi + fi +# +# Run the multi-pass 'pdfmark' reference resolver loop ... +# + $SAY >&2 $n Resolving references ..$c + until $DIFF $REFCOPY $REFFILE 1>$NULLDEV 2>&1 + do +# +# until all references are resolved, to yield consistent values +# in each of two consecutive passes, or until it seems that no consistent +# resolution is achievable. +# + $SAY >&2 $n .$c + PASS_INDICATOR="${PASS_INDICATOR}." + if test "$PASS_INDICATOR" = "...." + then +# +# More than three passes required indicates a probable inconsistency +# in the source document; diagnose, and bail out. +# + $SAY >&2 " failed" + $CAT >&2 <<-ETX + $CMD: unable to resolve references consistently after three passes + $CMD: the source document may exhibit instability about the reference(s) ... + ETX +# +# Report the unresolved references, as a diff between the two pass files, +# preferring 'unified' or 'context' diffs, when available +# + DIFFOPT='' + $DIFF -c0 $NULLDEV $NULLDEV 1>$NULLDEV 2>&1 && DIFFOPT='-c0' + $DIFF -u0 $NULLDEV $NULLDEV 1>$NULLDEV 2>&1 && DIFFOPT='-u0' + $DIFF >&2 $DIFFOPT $REFCOPY $REFFILE + exit 1 + fi +# +# Replace the comparison file copy from any previous pass, +# with the most recently updated copy of the reference dictionary. +# (Some versions of 'mv' may not support overwriting of an existing file, +# so remove the old comparison file first). +# + rm -f $REFCOPY + mv $REFFILE $REFCOPY +# +# Run 'groff' and 'awk', to identify reference marks in the document source, +# filtering them into the reference dictionary; discard incomplete 'groff' output +# at this stage. +# + eval $STREAM $GROFF_STYLE -Z 1>$NULLDEV 2>$WRKFILE $REFCOPY $INPUT_FILES + $AWK '/^gropdf-info:href/ {$1 = ".pdfhref D -N"; print}' $WRKFILE > $REFFILE + done + $SAY >&2 " done" +# +# To get to here ... +# We MUST have resolved all 'pdfmark' references, such that the content of the +# updated reference dictionary file EXACTLY matches the last saved copy. +# +# If PDF output has been suppressed, then there is nothing more to do. +# + test "$PDF_OUTPUT" = "$NULLDEV" && exit 0 +# +# We are now ready to start preparing the intermediate PostScript files, +# from which the PDF output will be compiled -- but before proceeding further ... +# let's make sure we have a GhostScript interpreter to convert them! +# + if test -n "$GROFF_GHOSTSCRIPT_INTERPRETER" + then + GS="$GROFF_GHOSTSCRIPT_INTERPRETER" + test -f "$GS" && test -x "$GS" || GS=":" + else + for prog in @GROFF_GHOSTSCRIPT_INTERPRETERS@ + do + GS=`searchpath $prog "$PATH"` + test "$GS" = ":" || break + done + fi +# +# If we could not find a GhostScript interpreter, then we can do no more. +# + if test "$GS" = ":" + then + echo >&2 "$CMD: installation problem: cannot find GhostScript interpreter" + $CAT >&2 <<-ETX + + *** FATAL INSTALLATION ERROR *** + + '$CMD' requires a GhostScript interpreter to convert PostScript to PDF. + You do not appear to have one installed; thus, '$CMD' cannot continue. + + ETX + exit 1 + fi +# +# We now extend the local copy of the reference dictionary file, +# to create a full 'pdfmark' reference map for the document ... +# + $AWK '/^grohtml-info/ {print ".pdfhref Z", $2, $3, $4}' $WRKFILE >> $REFCOPY +# +# ... appending a dummy map reference, to ensure that at least +# one such is always present; (this is required, to suppress any +# further intermediate output to stderr during the "press-ready" +# runs of groff, for PDF output file production). +# + echo ".pdfhref Z 0 0 0" >> $REFCOPY +# +# Evaluate any processing options which may have been specified +# as a result of parsing the document source ... +# + eval `$SED -n '/^ *pdfroff-option:set */s///p' $WRKFILE` +# +# ... (which is currently supported to enable "toc-relocation", +# only when the document actually relies on it, and if it is not +# explicitly disabled from the command line) ... +# + if test x${toc_relocation-"auto"} != xenabled + then +# +# ... thus we reproduce the effect of the "--no-toc-relocation" +# option, when no enabling request is detected in the document +# control stream. +# + TC_DATA="" TOC_FORMAT="" BODY_FORMAT="" + fi +# +# Re-enable progress reporting, if necessary ... +# (Missing 'awk' or 'diff' may have disabled it, to avoid display +# of spurious messages associated with reference resolution). +# + test x${SHOW_PROGRESS+"set"} = x"set" && SAY=echo +# +# If a document cover style sheet is specified ... +# then we run a special formatting pass, to create a cover section file. +# + if test -n "$STYLESHEET" + then + DOT='^\.[ ]*' + CS_MACRO=${CS_MACRO-"CS"} CE_MACRO=${CE_MACRO-"CE"} + $SAY >&2 $n "Formatting document ... front cover section ..$c" + CS_FILTER="$STREAM $SED -n '/${DOT}${CS_MACRO}/,/${DOT}${CE_MACRO}/p'" + eval $CS_FILTER $INPUT_FILES | eval $GROFF_STYLE $STYLESHEET - > $CS_DATA + $SAY >&2 ". done" + fi +# +# If table of contents relocation is to be performed (it is, by default), +# then we run an extra 'groff' pass, to format a TOC intermediate file. +# + if test -n "$TC_DATA" + then + $SAY >&2 $n "Formatting document ... table of contents ..$c" + eval $STREAM $GROFF_STYLE $TOC_FORMAT $REFCOPY $INPUT_FILES > $TC_DATA + $SAY >&2 ". done" + fi +# +# In all cases, a final 'groff' pass is required, to format the document body. +# + $SAY >&2 $n "Formatting document ... body section ..$c" + eval $STREAM $GROFF_STYLE $BODY_FORMAT $REFCOPY $INPUT_FILES > $BD_DATA + $SAY >&2 ". done" +# +# Finally ... +# Invoke GhostScript as a PDF writer, to bind all of the generated +# PostScript intermediate files into a single PDF output file. +# + $SAY >&2 $n "Writing PDF output ..$c" + if test -z "$PDFROFF_POSTPROCESSOR_COMMAND" + then + PDFROFF_POSTPROCESSOR_COMMAND="$GS -dQUIET -dBATCH -dNOPAUSE -dSAFER + -sDEVICE=pdfwrite -sOutputFile="${PDF_OUTPUT-"-"} + + elif test -n "$PDF_OUTPUT" + then + exec > $PDF_OUTPUT + fi +# +# (This 'sed' script is a hack, to eliminate redundant blank pages). +# + ${PDFROFF_COLLATE-"$SED"} ${PDFROFF_KILL_NULL_PAGES-' + /%%Page:/{ + N + /%%BeginPageSetup/b again + } + b + :again + /%%EndPageSetup/b finish + /%%Page:/{ + N + b again + } + b + :finish + N + /^%%Page:.*\n0 Cg EP$/d + '} $TC_DATA $BD_DATA | $PDFROFF_POSTPROCESSOR_COMMAND $CS_DATA - + $SAY >&2 ". done" +# +# ------------------------------------------------------------------------------ +# $RCSfile$ $Revision$: end of file diff --git a/contrib/pdfmark/sanitize.tmac b/contrib/pdfmark/sanitize.tmac new file mode 100644 index 0000000..49feaa9 --- /dev/null +++ b/contrib/pdfmark/sanitize.tmac @@ -0,0 +1,170 @@ +.ig + +sanitize.tmac + +Copyright (C) 2021 Free Software Foundation, Inc. + Written by Keith Marshall (keith.d.marshall@ntlworld.com) + +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/>. + +.. +.eo +.de sanitize +.\" Usage: .sanitize name text ... +.\" +.\" Remove designated formatting escape sequences from "text ..."; return +.\" the sanitized text in a string register, identified by "name". +.\" +.\" Begin by initializing the named result as an empty string, bind it to +.\" an internal reference name, and discard the "name" argument, to leave +.\" only the text which is to be sanitized, as residual arguments. +.\" +. ds \$1 +. als sanitize:result \$1 +. shift +. +.\" Initialize a working string register, which we will cyclically reduce +.\" until it becomes empty, after starting with all of the text passed as +.\" the residual arguments, and establish its initial length. +.\" +. ds sanitize:residual "\$*\" +. length sanitize:residual.length "\$*\" +. +.\" Begin the cyclic reduction loop... +.\" +. while \n[sanitize:residual.length] \{\ +. \" +. \" ...assuming, at the start of each cycle, that the next character +. \" will not be skipped, and that it will be moved from the residual, +. \" to the result, as the character-by-character scan proceeds. +. \" +. nr sanitize:skip.count 0 +. sanitize:scan.execute +. +. \" For each character scanned, we need to check if it matches the +. \" normal escape character; the check is most readily performed, if +. \" an alternative escape character is introduced, and when a match +. \" is found, we prepare to skip an escape sequence. +. \" +. ec ! +. if '!*[sanitize:scan.char]'\' .nr sanitize:skip.count 1 +. ec +. ie \n[sanitize:skip.count] \{\ +. \" +. \" When a possible escape sequence has been detected, we back it +. \" up, (in case it isn't recognized, and we need to reinstate its +. \" content into the result string), then scan ahead to check for +. \" an identifiable escape sequence... +. \" +. rn sanitize:scan.char sanitize:hold +. sanitize:scan.execute +. ie d sanitize:esc-\*[sanitize:scan.char] \ +. \" +. \" ...which we delegate to its appropriate handler, to skip... +. \" +. sanitize:esc-\*[sanitize:scan.char] +. +. \" ...but, in the case of an unrecognized escape sequence, we copy +. \" its backed-up content, followed by the character retrieved from +. \" the current scan cycle, to the result string. +. \" +. el .as sanitize:result "\*[sanitize:hold]\*[sanitize:scan.char]\" +. \} +. +. \" When the current scan cycle has retrieved a character, which isn't +. \" part of any possible escape sequence, we simply copy that character +. \" to the result string. +. \" +. el .as sanitize:result "\*[sanitize:scan.char]\" +. \} +. +.\" Clean up the register space, by deleting all of the string registers, +.\" and numeric registers, which are designated as temporary, for private +.\" use within this macro only. +.\" +. rm sanitize:hold sanitize:scan.char sanitize:residual sanitize:result +. rr sanitize:residual.length sanitize:skip.count +.. +.de sanitize:scan.execute +.\" Usage (internal): .sanitize:scan.execute +.\" +.\" Perform a single-character reduction of sanitize:residual, by copying +.\" its initial character to sanitize:scan.char, and then deleting it from +.\" sanitize:residual itself. (Note that we use arithmetic decrementation +.\" of sanitize:residual.length, rather than repeating the length request +.\" on sanitize:residual, because reduction WILL fail when there is only +.\" one character remaining). +.\" +. nr sanitize:residual.length -1 +. ds sanitize:scan.char "\*[sanitize:residual]\" +. substring sanitize:scan.char 0 0 +. substring sanitize:residual 1 +.. +.de sanitize:skip-( +.\" Usage (internal): .sanitize:skip-( +.\" +.\" For any identified escape sequence, with a two-character property name, +.\" simply skip over the next two characters in the residual string. +.\" +. nr sanitize:residual.length -2 +. substring sanitize:residual 2 +.. +.de sanitize:skip-[ +.\" Usage (internal): .sanitize:skip-[ +.\" +.\" For any identified escape sequence, with an arbitrary-length property +.\" name, skip following characters in the residual string, until we find +.\" a terminal "]" character, or we exhaust the residual. +.\" +. while \n[sanitize:skip.count] \{\ +. sanitize:scan.execute +. ie \n[sanitize:residual.length] \{\ +. \" We haven't yet exhausted the residual; if we find a nested "[" +. \" character, increment the nesting level, otherwise decrement it +. \" for each "]"; it will become zero at the terminal "]". +. \" +. ie '\*[sanitize:scan.char]'[' .nr sanitize:skip.count +1 +. el .if '\*[sanitize:scan.char]']' .nr sanitize:skip.count -1 +. \} +. \" Stop unconditionally, if we do exhaust the residual. +. \" +. el .nr sanitize:skip.count 0 +. \} +.. +.de sanitize:esc-generic +.\" Usage: .sanitize:esc-X +.\" +.\" (X represents any legitimate single-character escape sequence id). +.\" +.\" Handler for skipping "\X" sequences, in text which is to be sanitized; +.\" this will automatically detect sequences conforming to any of the forms +.\" "\Xc", "\X(cc", or "\X[...]", and will handle each appropriately. The +.\" implementation is generic, and may be aliased to handle any specific +.\" escape sequences, which exhibit similar semantics. +.\" +. sanitize:scan.execute +. if d sanitize:skip-\*[sanitize:scan.char] \ +. sanitize:skip-\*[sanitize:scan.char] +.. +.ec +.\" Map the generic handler to specific escape sequences, as required. +.\" +.als sanitize:esc-F sanitize:esc-generic +.\" Local Variables: +.\" mode: nroff +.\" End: +.\" vim: filetype=groff: +.\" sanitize.tmac: end of file diff --git a/contrib/pdfmark/spdf.tmac b/contrib/pdfmark/spdf.tmac new file mode 100644 index 0000000..130d8bd --- /dev/null +++ b/contrib/pdfmark/spdf.tmac @@ -0,0 +1,328 @@ +.ig + +spdf.tmac + +Binding macros for use of "-m pdfmark" in conjunction with "-ms". + + +Copyright (C) 2004-2021 Free Software Foundation, Inc. + Written by Keith Marshall (keith.d.marshall@ntlworld.com) + +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/>. + +.. +.\" Bindings +.\" ======== +.\" +.\" Generic output mode control flag; pdfmark.tmac will bind this to +.\" its own internal PDFOPMODE flag. +.\" +.if !r OPMODE .nr OPMODE 1 +.\" +.mso s.tmac +.mso sanitize.tmac +.mso pdfmark.tmac +. +.\" Establish a handler to clean up output context, at end of document. +.\" +.de pdf@exit em +. if \\n[OPMODE] .pdfsync +. pg@end-text +.em pdf@exit +. +. +.\" Omitted Sections +.\" ================ +.\" +.\" Define section markers, for special document sections, +.\" which are to be omitted from regular document processing. +.\" +.\" .OMIT <name1> <name2> +.\" +.\" Defines a pair of macros, <name1> and <name2>, such that execution +.\" of .<name1> marks the start of a block of troff input which should +.\" be ignored; this block ends, on execution of .<name2> +.\" +.de OMIT OMIT +. de \\$1 +. omit@begin \\$1 \\$2 +. . +. de \\$2 +. omit@end \\$1 \\$2 +. . +.\" Definition of the OMIT macro itself, ends when it is first used, +.\" to identify an omitted section marker macro pair. +.\" +.OMIT CS CE \" front cover text, processed independently +.OMIT MS ME \" menu definitions, for info documents only +. +.\" Actual omission is initiated by recording the name of the block +.\" start macro, followed by injection of an "ig" request... +.\" +.de omit@begin +. ds omit@section \\$1 +\. ig \\$2 +.. +.\" ...and terminates with verification that the end macro matches +.\" the start macro, and removal of the start macro record; (error +.\" reporting uses the "@error" macro, from s.tmac). +.\" +.de omit@end +. if !'\\*[omit@section]'\\$1' .@error \\$2 without \\$1 +. rm omit@section +.. +. +.\" Document Outlines, and Section Headings +.\" ======================================= +.\" +.\" .XH [-N <name>] [-S] [-X] <outline-level> <heading-text> ... +.\" .XN [-N <name>] [-S] [-X] <heading-text> ... +.\" +.\" Use after SH, and XN <n> respectively, to define text to be set +.\" within the section heading, as a PDF document ouline entry, and +.\" optionally, as a table of contents entry. +.\" +.\" Options: +.\" -N <name> Assigns <name> as a pdfhref destination, +.\" and associates it with the heading. +.\" +.\" -S Strip troff font-change escapes from the +.\" text copied to the document outline. +.\" +.\" -X Force pdfhref destination assignment; if +.\" -N <name> is unspecified, use first word +.\" of <heading-text> as <name>. +.\" +.\" Arguments: +.\" <outline-level> The nesting level of the heading, within +.\" the document outline, and TOC. Required +.\" for XH; inferred from NH <n>, for XN. +.\" +.\" <heading-text> Text (required) to appear within each of +.\" the section heading, document outline, +.\" and (optionally) TOC. +.\" +.\" Hooks: +.\" XH-INIT User-defined macro, called on entry to XH; +.\" used to specify initialization state which +.\" may be needed after using SH; e.g. specify +.\" a PDFHREF.INFO state without incorporation +.\" of any "section" references. +.\" +.\" XN-INIT User-defined macro, called on entry to XN; +.\" used to specify initialization state which +.\" may be needed after using NH; e.g. specify +.\" a PDFHREF.INFO state which may incorporate +.\" "section" references. +.\" +.\" XH-UPDATE-TOC User-defined macro, called by both XH, and +.\" XN; (there is no XN-UPDATE-TOC). Must be +.\" defined, if a TOC entry is to be generated +.\" by either XH, or XN; the format of such a +.\" TOC entry is determined by the definition +.\" of this macro. +.\" +.\" Our replacements for both XH, and XN macros share a common entry +.\" point; we map both to their respective replacement hooks, so that +.\" we may continue to take advantage of setup logic in s.tmac. When +.\" these are eventually invoked, they will have been renamed so that +.\" they replace XH, and XH respectively. +.\" +.de XH-REPLACEMENT als +.als XN-REPLACEMENT XH-REPLACEMENT +.\" FIXME: within s.tmac, the heading level established by the most +.\" recent prior invocation of the NH macro is tracked by the "nh*hl" +.\" private register; perhaps s.tmac could expose this more publicly, +.\" as by this ostensibly read-only alias, since we need it to keep +.\" PDF document outlines in synchronization with NH level... +.\" +.\" .aln .NH nh*hl +.\" +.\" ...but maybe a local "belt and braces" approach is better anyway, +.\" to insulate "nh*hl" from possible abuse of our ".NH" register, by +.\" any users who may be determined to shoot themselves in the foot! +.\" +.ie r nh*hl .nr spdf:nh*hl \n[nh*hl] +.el .nr spdf:nh*hl 0 +.am @NH +. nr spdf:nh*hl \\n[nh*hl] +.. +.aln .NH spdf:nh*hl +. +.\" The common entry point for both XH, and XN, handles initialization, +.\" and interpretation of any specified options, before handing over to +.\" one of two distinct formatting helper macros, which are specific to +.\" the XH, and XN implementations respectively. +.\" +.am XH-REPLACEMENT \" thus serving as implementation for both XH and XN +.\" +.\" The user is expected to furnish the XH-INIT, XH-UPDATE-TOC, and +.\" XN-INIT handlers. (Note that the XH-UPDATE-TOC hook serves both +.\" XH and XN; there is no separate XN-UPDATE-TOC). A suitable stub +.\" for each is provided by s.tmac; we have no need to replace them. +.\" +. \\$0-INIT +. rm spdf:refname +. als spdf:bm.define spdf:bm.basic +. while d spdf:XH\\$1 \{\ +. spdf:XH\\$1 \\$* +. shift \\n[spdf:argc] +. \} +. rr spdf:argc +. if '\\$1'--' .shift +. spdf:\\$0.format \\$@ +.. +.\" Interpret the "-N <name>" option... +.\" +.de spdf:XH-N +. ds spdf:refname \\$2 +. nr spdf:argc 2 +.. +.\" ...the "-X" option, and... +.\" +.de spdf:XH-X +. if !d spdf:refname .ds spdf:refname \\\\$1 +. nr spdf:argc 1 +.. +.\" ...the "-S" option. +.\" +.de spdf:XH-S +. als spdf:bm.define sanitize +. nr spdf:argc 1 +.. +.\" By default, when the "-S" option is not specified, the text +.\" incorporated into the PDF document outline will be a simple +.\" verbatim copy of the arguments. +.\" +.de spdf:bm.basic +. shift \" ignore \$1; it should always be "spdf:bm.text" +. ds spdf:bm.text "\\$*\" +.. +.\" After initialization, and interpretation of options, the XH +.\" and XN implementations diverge, into this helper macro, which +.\" is specific to the XH implementation... +.\" +.de spdf:XH.format +. XH-UPDATE-TOC \\$@ +. ds spdf:bm.argv \\$1 +. shift \" finalization doesn't want the outline level in \$1 +. spdf:XH.finalize \\$@ +.. +.\" ...and this, which is specific to XN... +.\" +.de spdf:XN.format +. ds spdf:bm.argv \\n[.NH] \\*[SN] +. XH-UPDATE-TOC \\n[.NH] \\*[SN] \\$@ +. spdf:XH.finalize \\$@ +.. +.\" ...before ultimately converging back into this finalization +.\" macro, which is once again common to both XH and XN. +.\" +.de spdf:XH.finalize +. spdf:bm.define spdf:bm.text "\\$*" +. if d spdf:refname .pdfhref M -X -N \\*[spdf:refname] -- \\$@ +. pdfhref O \\*[spdf:bm.argv] \\*[spdf:bm.text] +. rm spdf:refname spdf:bm.argv spdf:bm.text +. nop \\$* +.. +. +.\" Cross-Reference Marshalling +.\" =========================== +.\" +.\" s.tmac provides the "pg@bottom" macro, which has already +.\" been installed as a page transition trap. To ensure proper +.\" mapping of "pdfhref" links which overflow the bottom of any +.\" page, we need to install the "pdfhref" page transition hook, +.\" as an addendum to this macro. +. +.pdfhref I -PT pg@bottom +. +. +.\" Phased Output Control +.\" ===================== +.\" +.\" Segregate output of table of contents, and document body text, +.\" into two distinct output phases, to facilitate assembly of the +.\" aggregate document in the correct order, particularly when the +.\" TOC is generated at the end, by the default "ms" mechanism. +.\" +.nr PDF-TOC-ONLY 1 +.nr PDF-BODY-TEXT 2 +.\" +.\" .OP [<output-phase>] +.\" +.\" If the user-specified PHASE numeric register has been defined, +.\" and its value matches the <output-phase> argument value, (or if +.\" no <output-phase> argument is specified), then set the OPMODE +.\" control flag to one, and activate groff's "pen-down" mode. +.\" +.\" Otherwise, if <output-phase> is specified, but does NOT match +.\" PHASE, set OPMODE to zero, and select "pen-up" mode. +.\" +.\" Alternatively, if PHASE has not been defined, unconditionally +.\" set OPMODE to one, and leave groff's pen state unchanged. +.\" +.de OP +.ie r PHASE \{\ +. ie \\n(.$ \{\ +. nr op:request 0 +. while \\n(.$ \{\ +. if \\$1=\\n[PHASE] .nr op:request 1 +. shift +. \} +. \} +. el .nr op:request 1 +. if !\\n[op:request]=\\n[OPMODE] \{\ +. nr OPMODE \\n[op:request] +. nop \O[\\n[OPMODE]]\c +. \} +. \} +.el .nr OPMODE 1 +.. +. +.\" Table of Contents Generation +.\" ============================ +.\" +.\" .TC [no] +.\" +.\" Replaces (and emulates) the TC macro, from s.tmac, to ensure +.\" that the pdfmark document outline cache is flushed, before TOC +.\" generation commences, that an outline reference is added for the +.\" TOC itself, and that pdfroff TOC relocation mode is enabled, for +.\" the TOC output phase of document production. +.\" +.de TC +. pdfsync O +. P1 +. OP \n[PDF-TOC-ONLY] +. pg@begin 1 i +. if !\\n[PHASE] .tm pdfroff-option:set toc_relocation=enabled +. if \\n[OPMODE] \{\ +. pdfhref O -T T 1 "\\*[TOC]" +. pdfsync O +. \} +. PX \\$1 +.. +. +.\" Initialize the default output state, for production of "body-text". +.\" +.OP \n[PDF-BODY-TEXT] +. +.\" Local Variables: +.\" mode: nroff +.\" End: +.\" vim: filetype=groff: +.\" spdf.tmac: end of file |