summaryrefslogtreecommitdiffstats
path: root/contrib/pdfmark/pdfmark.tmac
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/pdfmark/pdfmark.tmac')
-rw-r--r--contrib/pdfmark/pdfmark.tmac1953
1 files changed, 1953 insertions, 0 deletions
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