1951 lines
69 KiB
Plaintext
1951 lines
69 KiB
Plaintext
.\" -*- nroff -*-
|
|
.ig
|
|
|
|
pdfmark.tmac
|
|
|
|
Copyright (C) 2004-2014 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 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]) >>
|
|
.\"
|
|
.\" pdfmark.tmac: end of file / vim: ft=groff
|