Provide `pdfroff' shell script, and manpage to document it;
[s-roff.git] / contrib / pdfmark / pdfmark.tmac
blob84f400cc8673fd2958056178274eda728fe9f650
1 .\" -*- nroff -*-
2 .ig
4 pdfmark.tmac
6 Copyright (C) 2004
7   Free Software Foundation, Inc.
8      Written by Keith Marshall (keith.d.marshall@ntlworld.com)
10 This file is part of groff.
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 for more details.
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING.  If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 Author's Note
27 =============
29 While I have written this macro package from scratch, much of my
30 inspiration has come from discussion on the groff mailing list
31 (mailto:groff@gnu.org).  I am particularly indebted to:
33    Kees Zeelenberg, for an earlier macro package he posted,
34    a study of which helped me to get started.
36    Carlos J. G. Duarte and Werner Lemberg, whose discussion
37    on computation of the bounding boxes for link "hot-spots"
38    forms the basis of such computations in this package.
40 .if !\n(.g .ab These pdfmark macros require groff.
41 .\"
42 .\" Check if we have already been loaded -- do not reload
43 .if d pdfmark .nx
44 .\"
45 .\" ======================================================================
46 .\" Module PDFMARK: Insert Arbitrary PDFMARK Code in the PostScript Stream
47 .\" ======================================================================
48 .\"
49 .\" PDFMARK output may be disabled, by zeroing the PDFOPMODE register,
50 .\" ( which mimics a more generic OPMODE, if it is defined ).
51 .\"
52 .if rOPMODE .aln PDFOPMODE OPMODE
53 .\"
54 .\" but if OPMODE wasn't defined,
55 .\" then make the default PDFMARK mode ENABLED.
56 .\"
57 .if !rPDFOPMODE .nr PDFOPMODE 1
58 .\"
59 .\" The "pdfmark" macro is responsible for emitting the appropriate
60 .\" PostScript code.
61 .\"
62 .de pdfmark
63 .\" ----------------------------------------------------------------
64 .\" Usage:
65 .\"   .pdfmark  text of pdfmark instruction
66 .\" Macro supplies the required opening "[" and closing "pdfmark"
67 .\" operator; DO NOT include them in the instruction text!
68 .\" ----------------------------------------------------------------
69 .\"
70 .if \\n[PDFOPMODE] \X'ps:exec [\\$* pdfmark'\c
72 .\"
73 .\" Some supporting macros defer actual pdfmark output until an
74 .\" appropriate time for it to be written; the "pdfsync" macro
75 .\" provides a mechanism for flushing such deferred output;
76 .\" it should be called from an end macro, and at any other time
77 .\" when it may be deemed necessary to flush pdfmark context.
78 .\"
79 .de pdfsync
80 .\" ----------------------------------------------------------------
81 .\" Usage:
82 .\"   .pdfsync buffer ...
83 .\" Arguments indicate which "buffer(s)" to flush:
84 .\"   O -> bookmark (outline) cache
85 .\"   M -> document metadata diversion
86 .\" If no argument, flush ALL buffers
87 .\" ----------------------------------------------------------------
88 .\"
89 .ie \\n(.$ \{\
90 .   while \\n(.$ \{\
91 .      if '\\$1'O' .pdf:bm.sync 1
92 .      if '\\$1'M' \{\
93 .         if dpdf:metadata .pdf:metadata
94 .         rm pdf:metadata
95 .         \}
96 .      shift
97 .      \}
98 .   \}
99 .el .pdfsync O M
102 .\" some helper functions ...
104 .\" "pdf:warn" and "pdf:error" write diagnostic messages to stderr
106 .de pdf:warn
107 .\" ----------------------------------------------------------
108 .\" Usage:
109 .\"   .pdf:warn text of message
110 .\" ----------------------------------------------------------
112 .tm \\n(.F:\\n(.c: macro warning: \\$*
114 .de pdf:error
115 .\" ----------------------------------------------------------
116 .\" Usage:
117 .\"   .pdf:error text of message
118 .\" ----------------------------------------------------------
120 .tm \\n(.F:\\n(.c: macro error: \\$*
122 .\" "pdf:pop", assisted by "pdf*pop", allows us to retrieve register,
123 .\" or string values, from a string masquerading as a data queue,
124 .\" or as a stack.
126 .de pdf:pop
127 .\" ----------------------------------------------------------------
128 .\" Usage:
129 .\"   .pdf:pop <type> <to-name> <from-name>
130 .\"   $1 = nr for numeric register, ds for string
131 .\"   $2 = name of register or string to be assigned
132 .\"   $3 = name of string, from which data is to be retrieved
133 .\" ----------------------------------------------------------------
135 .pdf*pop \\$* \\*[\\$3]
137 .de pdf*pop
138 .ds pdf:stack \\$3
139 .\\$1 \\$2 \\$4
140 .shift 4
141 .ie \\n(.$ .ds \\*[pdf:stack] \\$*
142 .el .rm \\*[pdf:stack]
143 .rm pdf:stack
147 .\" ===========================================================
148 .\" Module PDFINFO: Insert MetaData Entries into a PDF Document
149 .\" ===========================================================
151 .\" N.B.
152 .\"   Output from the macros in this module is deferred, until
153 .\"   subsequent invocation of .pdfsync, or .pdfexit
155 .\" ."pdfinfo" provides a general purpose form of metadata entry ...
156 .\" it allows arbitrary text to be associated with any specified
157 .\" metadata field name.
159 .de pdfinfo
160 .\" -------------------------------------------------------------------
161 .\" Usage:
162 .\"   .pdfinfo /FieldName field content ...
163 .\" Examples:
164 .\"   .pdfinfo /Title   A PDF Document
165 .\"   .pdfinfo /Author  Keith Marshall
166 .\" -------------------------------------------------------------------
168 .ds pdf:meta.field \\$1
169 .shift
170 .da pdf:metadata
171 \!.pdfmark \\*[pdf:meta.field] (\\$*) /DOCINFO
173 .rm pdf:meta.field
176 .\" Macro "pdfview" defines a special form of metadata entry ...
177 .\" it uses the /DOCVIEW pdfmark, to specify the initial (default) view,
178 .\" when the document is opened.
180 .de pdfview
181 .\" -------------------------------------------------------------------
182 .\" Usage:
183 .\"   .pdfview view parameters ...
184 .\" Examples:
185 .\"   .pdfview /PageMode /UseOutlines
186 .\"   .pdfview /Page 2 /View [/FitH \n(.p u]
187 .\" -------------------------------------------------------------------
189 .da pdf:metadata
190 \!.pdfmark \\$* /DOCVIEW
195 .\" =====================================================================
196 .\" Module PDFNOTE: Insert "Sticky Note" Style Comments in a PDF Document
197 .\" =====================================================================
199 .\" "PDFNOTE.WIDTH" and "PDFNOTE.HEIGHT" set the preferred size for
200 .\" display of the "sticky note" pane, when opened.  Acrobat Reader
201 .\" seems not to honour these -- perhaps GhostScript doesn't encode
202 .\" them correctly!  Anyway, let's set some suitable default values,
203 .\" in case the user has a set up which does work as advertised.
205 .nr PDFNOTE.WIDTH  3.5i
206 .nr PDFNOTE.HEIGHT 2.0i
208 .\" "pdf:bbox" defines the expression used to set the size and location
209 .\" of the bounding rectangle for display of notes and link "hot-spots".
210 .\" This is defined, such that a note is placed at troff's current text
211 .\" position on the current page, with its displayed image size defined
212 .\" by the "PDFNOTE.WIDTH" and "PDFNOTE.HEIGHT" registers, while the
213 .\" bounds for a link "hot-spot" are matched to the text region which
214 .\" defines the "hot-spot".
216 .ds pdf:bbox \\n[pdf:llx] u \\n[pdf:lly] u \\n[pdf:urx] u \\n[pdf:ury] u
218 .\" Getting line breaks into the text of a PDFNOTE is tricky -- we need
219 .\" to get a "\n" into the PostScript stream, but three levels of "\" are
220 .\" swallowed, when we invoke "pdfnote".  The following definition of "LB",
221 .\" (for LineBreak), is rather ugly, but does allow us to use
223 .\"    .pdfnote  Some text.\*[LB]Some more text, on a new line.
225 .ds LB \\\\\\\\\\\\\\\\n
227 .de pdfnote
228 .\" ----------------------------------------------------------------------
229 .\" Usage:
230 .\"   .pdfnote [-T "Text for Title"] Text of note ...
231 .\" ----------------------------------------------------------------------
233 .if \\n[PDFOPMODE] \{\
235 .\" First, compute the bounding rectangle,
236 .\" for this PDFNOTE instance
238 .   mk pdf:ury
239 .   nr pdf:llx \\n(.k+\\n(.o+\\n[.in]
240 .   nr pdf:lly \\n[pdf:ury]-\\n[PDFNOTE.HEIGHT]
241 .   nr pdf:urx \\n[pdf:llx]+\\n[PDFNOTE.WIDTH]
242 .   ds pdf:note.instance /Rect [\\*[pdf:bbox]]
244 .\" Parse any specified (recognisable) PDFNOTE options
246 .   while dpdf:note\\$1 \{\
247 .      pdf:note\\$1 \\$@
248 .      shift \\n[pdf:note.argc]
249 .      \}
251 .\" Emit the note, and clean up
253 .   pdfmark \\*[pdf:note.instance] /Contents (\\$*) /ANN
254 .   rm pdf:note.instance
255 .   rr pdf:note.argc
256 .   \}
258 .de pdf:note-T
259 .nr pdf:note.argc 2
260 .as pdf:note.instance " /Title (\\$2)
264 .\" =====================================================================
265 .\" Module PDFBOOKMARK: Add an Outline Reference in the PDF Bookmark Pane
266 .\" =====================================================================
268 .\" "PDFBOOKMARK.VIEW" controls how the document will be displayed,
269 .\" when the user selects a bookmark.  This default setting will fit
270 .\" the page width to the viewing window, with the bookmarked entry
271 .\" located at the top of the viewable area.
273 .ds PDFBOOKMARK.VIEW /FitH \\n[PDFPAGE.Y] u
275 .\" The actual job of creating an outline reference
276 .\" is performed by the "pdfbookmark" macro.
278 .de pdfbookmark
279 .\" ------------------------------------------------------------------
280 .\" Usage:
281 .\"   .pdfbookmark [-T tag] level "Text of Outline Entry"
283 .\"   $1 = nesting level for bookmark (1 is top level)
284 .\"   $2 = text for bookmark, (in PDF viewer bookmarks list)
285 .\"   $3 = suffix for PDF internal bookmark name (optional)
286 .\" ------------------------------------------------------------------
288 .if \\n[PDFOPMODE] \{\
290 .\" Make the bookmark name "untagged" by default,
291 .\" then parse any specified options, to set a "tag", if required
293 .   ds pdf:href-T
294 .   while dpdf:href.opt\\$1 \{\
295 .      pdf:href.opt\\$1 \\$@
296 .      shift \\n[pdf:href.argc]
297 .      \}
298 .   rr pdf:href.argc
300 .\" If we found "--" to mark the end of the options, discard it
302 .   if '\\$1'--' .shift
304 .\" Synchronise the bookmark cache
305 .\" to the requested bookmark nesting level
307 .   pdf:bm.sync \\$1
308 .   shift
310 .\" Increment the bookmark serialisation index
311 .\" in order to generate a uniquely serialised bookmark name,
312 .\" ( which we return in the string "PDFBOOKMARK.NAME" ),
313 .\" and insert this bookmark into the cache
315 .   pdf:href.sety
316 .   nr pdf:bm.nr +1
317 .   ds PDFBOOKMARK.NAME pdf:bm\\n[pdf:bm.nr]\\*[pdf:href-T]
318 .   ds pdf:bm\\n[pdf:bm.nr] /Dest /\\*[PDFBOOKMARK.NAME]
319 .   pdfmark \\*[pdf:bm\\n[pdf:bm.nr]] /View [\\*[PDFBOOKMARK.VIEW]] /DEST
320 .   as pdf:bm\\n[pdf:bm.nr] " /Title (\\$*)
321 .   pdf:href.options.clear
322 .   rr PDFPAGE.Y
323 .   \}
326 .\" Macro "pdf:bm.sync" is called for each bookmark created,
327 .\" to establish a cache entry at the appropriate nesting level.
328 .\" It will flush ALL previous cache content, when called to
329 .\" add a new bookmark at level 1, or if simply called at
330 .\" level 1, without adding any bookmark.
332 .de pdf:bm.sync
333 .\" ------------------------------------------------------------------
334 .\" Usage:
335 .\"   .pdf:bm.sync  level
336 .\"   $1 = nesting level of current bookmark, or 1 to flush cache
337 .\" ------------------------------------------------------------------
339 .\" First validate the bookmark nesting level
340 .\" adjusting it if required
342 .if \\$1>\\n[pdf:bm.nl] .nr pdf:bm.nl +1
343 .ie \\$1>\\n[pdf:bm.nl] \{\
344 .   pdf:warn adjusted level \\$1 bookmark; should be <= \\n[pdf:bm.nl]
345 .   \}
346 .el .nr pdf:bm.nl \\$1
347 .if \\n[pdf:bm.nl]<1 \{\
348 .   pdf:warn bad arg (\\$1) in \\$0 \\$1; \\$0 1 forced
349 .   nr pdf:bm.nl 1
350 .   \}
352 .\" If reverting from a higher to a lower nesting level,
353 .\" cyclicly adjust cache counts for each pending higher level
355 .if \\n[pdf:bm.lc]>=\\n[pdf:bm.nl] \{\
356 .   nr pdf:bm.lc +1
357 .   if !rpdf:bm.c\\n[pdf:bm.lc].c .nr pdf:bm.c\\n[pdf:bm.lc].c 0
358 .   while \\n[pdf:bm.lc]>\\n[pdf:bm.nl] \{\
359 .      as pdf:bm.c\\n[pdf:bm.lc] " \\n[pdf:bm.c\\n[pdf:bm.lc].c]
360 .      rr pdf:bm.c\\n[pdf:bm.lc].c
361 .      nr pdf:bm.lc -1
362 .      \}
363 .   \}
365 .\" Update the cache level,
366 .\" flushing when we are at level 1
368 .nr pdf:bm.lc \\n[pdf:bm.nl]
369 .ie \\n[pdf:bm.nl]=1 \{\
370 .   while \\n[pdf:bm.ic]<\\n[pdf:bm.nr] .pdf:bm.emit 0
371 .   rr pdf:bm.rc
372 .   \}
373 .el .nr pdf:bm.c\\n[pdf:bm.nl].c +1
375 .\" Macro "pdf:bm.emit" is called, when the cache is at level 1.
376 .\" This flushes ALL pending bookmarks from the cache, i.e. the
377 .\" preceding level 1 bookmark, and any nested dependents,
378 .\" which it may have.
380 .de pdf:bm.emit
381 .\" ------------------------------------------------------------------
382 .\" Usage:
383 .\"   .pdf:bm.emit  flag
384 .\"   $1 = reference counting flag, used to control recursion
385 .\" ------------------------------------------------------------------
387 .\" First check for nested dependents,
388 .\" and append the "dependent count" to the bookmark, as required.
390 .nr pdf:bm.ic +1
391 .nr pdf:bm.lc +1
392 .pdf:pop nr pdf:bm.rc pdf:bm.c\\n[pdf:bm.lc]
393 .if \\n[pdf:bm.rc] .as pdf:bm\\n[pdf:bm.ic] " /Count \\n[pdf:bm.rc]
394 .pdfmark \\*[pdf:bm\\n[pdf:bm.ic]] /OUT
395 .rm pdf:bm\\n[pdf:bm.ic]
397 .\" For ALL dependents, if any,
398 .\" recursively flush out any higher level dependents,
399 .\" which they themselves may have
401 .while \\n[pdf:bm.rc] \{\
402 .   nr pdf:bm.rc -1
403 .   pdf:bm.emit \\n[pdf:bm.rc]
404 .   \}
406 .\" Finally,
407 .\" unwind the recursive call stack, until we return to the top level.
409 .nr pdf:bm.rc \\$1
410 .nr pdf:bm.lc -1
412 .nr pdf:bm.nr 0
413 .nr pdf:bm.nl 1
414 .nr pdf:bm.lc 0
415 .nr pdf:bm.ic 0
418 .\" =============================================================
419 .\" Module PDFHREF: Create Hypertext References in a PDF Document
420 .\" =============================================================
422 .\" "PDFHREF.VIEW" controls how the document will be displayed,
423 .\" when the user follows a link to a named reference.
425 .ds PDFHREF.VIEW     /FitH \\n[PDFPAGE.Y] u
427 .\" This default setting will fit the page width to the viewing
428 .\" window, with the bookmarked entry located close to the top
429 .\" of the viewable area.  "PDFHREF.VIEW.LEADING" controls the
430 .\" actual distance below the top of the viewing window, where
431 .\" the reference will be positioned; 5 points is a reasonable
432 .\" default offset.
434 .nr PDFHREF.VIEW.LEADING  5.0p
436 .\" Yuk!!!
437 .\" PDF view co-ordinates are mapped from the bottom left corner,
438 .\" of the page, whereas page printing co-ordinates are mapped
439 .\" conventionally, from top left.
441 .\" Macro "pdf:href.sety" transforms the vertical position of the
442 .\" last printed baseline, from the printing co-ordinate domain to
443 .\" the PDF view domain.
445 .de pdf:href.sety
446 .\" ----------------------------------------------------------------
447 .\" Usage:
448 .\"   .pdf:href.sety
449 .\" ----------------------------------------------------------------
451 .\" This computation yields the vertical view co-ordinate
452 .\" in groff's basic units; don't forget to append grops' "u"
453 .\" conversion operator, when writing the pdfmark!
455 .nr PDFPAGE.Y \\n(.p-\\n(nl+\\n[PDFHREF.VIEW.LEADING]
457 .\" When we create a link "hot-spot" ...
458 .\" "PDFHREF.LEADING" sets the distance above the top of the glyph
459 .\" bounding boxes, in each line of link text, over which the link
460 .\" hot-spot will extend, while "PDFHREF.HEIGHT" sets the hot-spot
461 .\" height, PER LINE of text occupied by the reference.
463 .\" Since most fonts specify some leading space within the bounding
464 .\" boxes of their glyphs, a better appearance may be achieved when
465 .\" NEGATIVE leading is specified for link hot-spots;  indeed, when
466 .\" the default 10pt Times font is used, -1.0 point seems to be a
467 .\" reasonable default value for "PDFHREF.LEADING" -- it may be
468 .\" changed, if desired.
470 .\" "PDFHREF.HEIGHT" is initially set as one vertical spacing unit;
471 .\" note that it is defined as a string, so it will adapt to changes
472 .\" in the vertical spacing.  Changing it is NOT RECOMMENDED.
474 .nr PDFHREF.LEADING -1.0p
475 .ds PDFHREF.HEIGHT   1.0v
477 .\" PDF readers generally place a rectangular border around link
478 .\" "hot-spots".  Within text, this looks rather ugly, so we set
479 .\" "PDFHREF.BORDER" to suppress it -- the three zeroes represent
480 .\" the border parameters in the "/Border [0 0 0]" PDFMARK string,
481 .\" and may be changed to any valid form, as defined in Adobe's
482 .\" PDFMARK Reference Manual.
484 .ds PDFHREF.BORDER   0 0 0
486 .\" "PDFHREF.COLOUR" (note British spelling) defines the colour to
487 .\" be used for display of link "hot-spots".  This will apply both
488 .\" to borders, if used, and, by default to text; however, actual
489 .\" text colour is set by "PDFHREF.TEXT.COLOUR", which may be reset
490 .\" independently of "PDFHREF.COLOUR", to achieve contrasting text
491 .\" and border colours.
493 .\" "PDFHREF.COLOUR" must be set to a sequence of three values,
494 .\" each in the range 0.0 .. 1.0, representing the red, green, and
495 .\" blue components of the colour specification in the RGB colour
496 .\" domain, which is shared by "groff" and the PDF readers.
498 .ds PDFHREF.COLOUR   0.35 0.00 0.60
499 .defcolor pdf:href.colour rgb \*[PDFHREF.COLOUR]
501 .\" "PDFHREF.TEXT.COLOUR", on the other hand, is simply defined
502 .\" using any "groff" colour name -- this default maps it to the
503 .\" same colour value as "PDFHREF.COLOUR".
505 .ds PDFHREF.TEXT.COLOUR  pdf:href.colour
507 .\" Accommodate users who prefer the American spelling, COLOR, to
508 .\" the British spelling, COLOUR.
510 .als PDFHREF.COLOR       PDFHREF.COLOUR
511 .als PDFHREF.TEXT.COLOR  PDFHREF.TEXT.COLOUR
513 .\" All PDF "Hypertext" reference capabilities are accessed
514 .\" through the "pdfhref" macro
516 .de pdfhref
517 .\" -----------------------------------------------------------------
518 .\" Usage:
519 .\"   .pdfhref <subcommand [options ...] [parameters ...]> ...
520 .\" -----------------------------------------------------------------
522 .if \\n[PDFOPMODE] \{\
524 .\" Loop over all subcommands specified in the argument list
526 .   while \\n(.$ \{\
527 .   \"
528 .   \" Initially, assume each subcommand will complete successfully
529 .   \"
530 .      nr pdf:href.ok 1
531 .   \"
532 .   \" Initialise -E and -X flags in the OFF state
533 .   \"
534 .      nr pdf:href-E 0
535 .      nr pdf:href-X 0
536 .   \"
537 .   \" Handle the case where subcommand is specified as "-class",
538 .   \" setting up appropriate macro aliases for subcommand handlers.
539 .   \"
540 .      if dpdf*href\\$1       .als pdf*href      pdf*href\\$1
541 .      if dpdf*href\\$1.link  .als pdf*href.link pdf*href\\$1.link
542 .      if dpdf*href\\$1.file  .als pdf*href.file pdf*href\\$1.file
543 .   \"
544 .   \" Repeat macro alias setup
545 .   \" for the case where the subcommand is specified as "class",
546 .   \" (without a leading hyphen)
547 .   \"
548 .      if dpdf*href-\\$1      .als pdf*href      pdf*href-\\$1
549 .      if dpdf*href-\\$1.link .als pdf*href.link pdf*href-\\$1.link
550 .      if dpdf*href-\\$1.file .als pdf*href.file pdf*href-\\$1.file
551 .   \"
552 .   \" Process one subcommand ...
553 .   \"
554 .      ie dpdf*href \{\
555 .      \"
556 .      \" Subcommand "class" is recognised ...
557 .      \" discard the "class" code from the argument list,
558 .      \" set the initial argument count to swallow all arguments,
559 .      \" and invoke the selected subcommand handler.
560 .      \"
561 .         shift
562 .         nr pdf:argc \\n(.$
563 .         pdf*href \\$@
564 .      \"
565 .      \" When done,
566 .      \" discard all arguments actually consumed by the handler,
567 .      \" before proceeding to the next subcommand (if any).
568 .      \"
569 .         shift \\n[pdf:argc]
570 .      \}
571 .      el \{\
572 .      \"
573 .      \" Subcommand "class" is not recognised ...
574 .      \" issue a warning, and discard the entire argument list,
575 .      \" so aborting this "pdfhref" invocation
576 .      \"
577 .         pdf:warn \\$0: undefined reference class '\\$1' ignored
578 .         shift \\n(.$
579 .         \}
580 .   \"
581 .   \" Clean up temporary reference data,
582 .   \" to ensure it doesn't propagate to any future reference
583 .   \"
584 .      rm pdf*href pdf:href.link pdf:href.files
585 .      rr pdf:href-E pdf:href-X
586 .      pdf:href.options.clear
587 .      \}
588 .   rr pdf:href.ok
589 .   \}
592 .\" Macros "pdf:href.flag" and "pdf:href.option"
593 .\" provide a generic mechanism for switching on flag type options,
594 .\" and for decoding options with arguments, respectively
596 .de pdf:href.flag
597 .\" ----------------------------------------------------------------------
598 .\" ----------------------------------------------------------------------
599 .nr pdf:href\\$1 1
600 .nr pdf:href.argc 1
602 .de pdf:href.option
603 .\" ----------------------------------------------------------------------
604 .\" ----------------------------------------------------------------------
605 .ds pdf:href\\$1 \\$2
606 .nr pdf:href.argc 2
609 .\" Valid PDFHREF options are simply declared
610 .\" by aliasing option handlers to "pdf:href.option",
611 .\" or to "pdf:href.flag", as appropriate
613 .als pdf:href.opt-A pdf:href.option   \" affixed text
614 .als pdf:href.opt-D pdf:href.option   \" destination name
615 .als pdf:href.opt-E pdf:href.flag     \" echo link descriptor
616 .als pdf:href.opt-F pdf:href.option   \" remote file specifier
617 .als pdf:href.opt-N pdf:href.option   \" reference name
618 .als pdf:href.opt-P pdf:href.option   \" prefixed text
619 .als pdf:href.opt-T pdf:href.option   \" bookmark "tag"
620 .als pdf:href.opt-X pdf:href.flag     \" cross reference
622 .\" For references to another document file
623 .\" we also need to support OS dependent file name specifiers
625 .als pdf:href.opt-DF pdf:href.option  \" /DOSFile specifier
626 .als pdf:href.opt-MF pdf:href.option  \" /MacFile specifier
627 .als pdf:href.opt-UF pdf:href.option  \" /UnixFile specifier
628 .als pdf:href.opt-WF pdf:href.option  \" /WinFile specifier
630 .\" Macro "pdf:href.options.clear" ensures that ALL option
631 .\" argument strings are deleted, after "pdfhref" has completed
632 .\" all processing which depends on them
634 .de pdf:href.options.clear
635 .\" -----------------------------------------------------------------
636 .\" Usage:
637 .\"   .pdf:href.options.clear [option ...]
638 .\" -----------------------------------------------------------------
640 .\" When an option list is specified ...
642 .ie \\n(.$ \{\
643 .   \"
644 .   \" then loop through the list,
645 .   \" deleting each specified option argument string in turn
646 .   \"
647 .   while \\n(.$ \{\
648 .      if dpdf:href-\\$1 .rm pdf:href-\\$1
649 .      shift
650 .      \}
651 .   \}
653 .\" ... but when no list is specified,
654 .\" then recurse, to clear all known option argument strings
656 .el .pdf:href.options.clear A D F N P T DF MF UF WF
659 .\" "PDFHREF.INFO" establishes the content of the cross reference
660 .\" data record, which is exported via the "stderr" stream, when a
661 .\" cross reference anchor is created using a "pdfhref" macro request
662 .\" of the form
664 .\"    .pdfhref M -N name -X text ...
666 .\"    .ds PDFHREF.INFO \\*[PDFHREF.NAME] reference data ...
668 .ds PDFHREF.INFO page \\n% \\$*
670 .\" Macro "pdf*href-M" is the handler invoked by "pdfhref", when
671 .\" called with the "M" reference class specifier, to create a
672 .\" named cross reference mark, and to emit a cross reference
673 .\" data record, as specified by "PDFHREF.INFO".
675 .de pdf*href-M
676 .\" -----------------------------------------------------------------
677 .\" Usage:
678 .\"   .pdfhref M [-X] [-N name | -D name] [-E] descriptive text ...
679 .\" -----------------------------------------------------------------
681 .\" Initially, declare the -D and -N string options as empty,
682 .\" so we avoid warning messages when we try to use them, and find
683 .\" that they are undefined.
685 .ds pdf:href-D
686 .ds pdf:href-N
688 .\" Parse, interpret, and strip any specified options from the
689 .\" argument list.  (Note that only options with a declared handler
690 .\" will be processed; there is no provision for detecting invalid
691 .\" options -- anything which is not recognised is assumed to start
692 .\" the "descriptive text" component of the argument list).
694 .while dpdf:href.opt\\$1 \{\
695 .   pdf:href.opt\\$1 \\$@
696 .   shift \\n[pdf:href.argc]
697 .   \}
699 .\" If we found "--", to mark the end of the options,
700 .\" then we should discard it.
702 .if '\\$1'--' .shift
704 .\" All PDF reference markers MUST be named. The name may have been
705 .\" supplied using the "-N Name" option, (or the "-D Name" option);
706 .\" if not, deduce it from the first "word" in the "descriptive text",
707 .\" if any, and set the marker -- if we still can't identify the name
708 .\" for the destination, then this marker will not be created.
710 .pdf*href.set \\*[pdf:href-N] \\*[pdf:href-D] \\$1
712 .\" If we specified a cross reference, with the "-X" option, and the
713 .\" reference mark has been sucessfully created, then we now need to
714 .\" write the cross reference info to the STDERR stream
716 .if \\n[pdf:href-X] .pdf*href.export \\*[PDFHREF.INFO]
718 .\" Irrespective of whether this marker is created, or not,
719 .\" the descriptive text will be copied to the groff output stream,
720 .\" provided the "-E" option was specified
722 .if \\n[pdf:href-E] \&\\$*
725 .de pdf*href.set
726 .\" ----------------------------------------------------------------------
727 .\" ----------------------------------------------------------------------
728 .pdf*href.map.init
729 .ie \\n(.$ \{\
730 .   \"
731 .   \" a marker name has been supplied ...
732 .   \" if we are formatting for immediate output,
733 .   \" emit PDFMARK code to establish the associated view
734 .   \"
735 .   ie '\\n(.z'' \{\
736 .      pdf:href.sety
737 .      pdfmark /Dest /\\$1 /View [\\*[PDFHREF.VIEW]] /DEST
738 .      ds PDFHREF.NAME \\$1
739 .      rr PDFPAGE.Y
740 .      \}
741 .   \"
742 .   \" but, when formatting a diversion ...
743 .   \" delay output of the PDFMARK code, until the diversion
744 .   \" is eventually written out
745 .   \"
746 .   el \!.\\$0 \\$@
747 .   \"
748 .   \" check if we also need to emit cross reference data
749 .   \" (caller will do this if "pdf:href-X" is set, but it is
750 .   \"  not necessary, when "pdf:href.map" already exists)
751 .   \"
752 .   if dpdf:href.map .nr pdf:href-X 0
753 .   \}
754 .el \{\
755 .   \" marker is unnamed ...
756 .   \" issue error message; do not emit reference data
757 .   \"
758 .   pdf:warn pdfhref destination marker must be named
759 .   nr pdf:href-X 0
760 .   \}
762 .de pdf*href.export
764 .\" Called ONLY by "pdf*href-M",
765 .\" this macro ensures that the emission of exported reference data
766 .\" is synchronised with the placement of the reference mark,
767 .\" especially when the mark is defined within a diversion.
769 .ie '\\n(.z'' .tm gropdf-info:href \\*[PDFHREF.NAME] \\$*
770 .el \!.\\$0 \\$@
773 .\" Macro "pdf*href-D" is invoked when "pdfhref" is called
774 .\" with the "D" reference class specifier; it provides a
775 .\" standardised mechanism for interpreting reference data
776 .\" exported by the "M" reference class, and may be used
777 .\" to directly define external reference data, without the
778 .\" use of "M" reference class designators in the source
779 .\" document.
781 .de pdf*href-D
782 .ds pdf:href-N
784 .\" Parse, interpret, and strip any specified options from the
785 .\" argument list.  (Note that only options with a declared handler
786 .\" will be processed; there is no provision for detecting invalid
787 .\" options -- anything which is not recognised is assumed to start
788 .\" the "descriptive text" component of the argument list).
790 .while dpdf:href.opt\\$1 \{\
791 .   pdf:href.opt\\$1 \\$@
792 .   shift \\n[pdf:href.argc]
793 .   \}
795 .\" If we found "--", to mark the end of the options,
796 .\" then we should discard it.
798 .if '\\$1'--' .shift
800 .ie '\\*[pdf:href-N]'' \{\
801 .   pdf:warn pdfhref defined reference requires a name
802 .   \}
803 .el \{\
804 .   ds pdf:href(\\*[pdf:href-N]).info \\$*
805 .   \}
808 .\" Macro "pdf*href-F" is invoked when "pdfhref" is called
809 .\" with the "F" reference class specifier; it allows the user
810 .\" to provide an alternative interpreter macro, which will be
811 .\" called when a "PDFHREF.INFO" record is retrieved to define
812 .\" the text of a cross reference link "hot spot".
814 .de pdf*href-F
815 .\" ----------------------------------------------------------------
816 .\" Usage:
817 .\"   .pdfhref F [macro-name]
818 .\" ----------------------------------------------------------------
820 .\" Set macro specified by "macro-name" as the format interpreter
821 .\" for parsing "PDFHREF.INFO" records; if "macro-name" is omitted,
822 .\" or is specified as the reserved name "default", then use the
823 .\" default format parser, "pdf*href.format", defined below.
825 .if '\\$1'default' .shift \\n(.$
826 .ie \\n(.$ .als pdf*href.format \\$1
827 .el .als pdf*href.format pdf*href.default
828 .nr pdf:argc 1
830 .\" The default reference formatting macro is defined below.
831 .\" It parses the "PDFHREF.INFO" record specific to each reference,
832 .\" recognising the keywords "file", "page" and "section", when they
833 .\" appear in initial key/value pairs, replacing the key/value pair
834 .\" with "PDFHREF.FILEREF", "PDFHREF.PAGEREF" or "PDFHREF.SECTREF"
835 .\" respectively; any additional data in the "PDFHREF.INFO" record
836 .\" is enclosed in typographic double quotes, and the parsed record
837 .\" is appended to "PDFHREF.PREFIX", to be returned as the formatted
838 .\" reference text.
840 .\" Default definitions for the reference strings "PDFHREF.PREFIX",
841 .\" "PDFHREF.FILEREF", "PDFHREF.PAGEREF" and "PDFHREF.SECTREF" are
842 .\" provided, in the English language.  Users may substitute any
843 .\" desired alternative definitions, for example, when formatting
844 .\" documents in other languages.  In each case, "\\$1" may be used
845 .\" in the substitution, to represent the "value" component of the
846 .\" respective key/value pair specified in the "PDFHREF.INFO" record.
848 .ds PDFHREF.PREFIX   see
849 .ds PDFHREF.PAGEREF  page \\$1,
850 .ds PDFHREF.SECTREF  section \\$1,
851 .ds PDFHREF.FILEREF  \\$1
853 .de pdf*href.format
854 .\" -----------------------------------------------------------------
855 .\" Usage: (to be called ONLY by "pdfhref")
856 .\"   .pdf*href.format cross reference data ...
857 .\" -----------------------------------------------------------------
859 .\" This macro is responsible for defining the strings "PDFHREF.TEXT"
860 .\" and "PDFHREF.DESC", which are used by the "pdfhref" macro, as the
861 .\" basis for generating the text content of a link "hot spot"; (any
862 .\" user specified alternate formatter MUST do likewise).
864 .\" Note that "PDFHREF.TEXT" defines the overall format for the "link
865 .\" text", while "PDFHREF.DESC" is the descriptive component thereof.
867 .\" This default implementation, subject to user customisation of the
868 .\" "internationalisation" strings defined above, formats "hot spots"
869 .\" of the style
871 .\"    see page N, section S, "descriptive text ..."
873 .ds PDFHREF.TEXT \\*[PDFHREF.PREFIX]
874 .while d\\$0.\\$1 \{\
875 .   \\$0.\\$1 "\\$2"
876 .   shift 2
877 .   \}
879 .\" Retrieve the descriptive text from the cross reference data,
880 .\" ONLY IF no overriding description has been set by the calling
881 .\" "pdfhref" macro invocation.
883 .if \\n(.$ .if !dPDFHREF.DESC .ds PDFHREF.DESC \\$*
885 .\" Augment "PDFHREF.TEXT" so the descriptive text will be included
886 .\" in the text of the formatted reference
888 .if dPDFHREF.DESC .as PDFHREF.TEXT " \(lq\\\\*[PDFHREF.DESC]\(rq
890 .\" Finally, suppress any leading spaces,
891 .\" which may have been included in the PDFHREF.TEXT definition.
893 .ds PDFHREF.TEXT \\*[PDFHREF.TEXT]
895 .de pdf*href.format.file
896 .\" ----------------------------------------------------------------------
897 .\" Include a file identifier in a formatted reference.
898 .\" This is invoked ONLY by "pdf*href.format", and ONLY IF the
899 .\" reference data includes an initial file identifier tuple.
900 .\" ----------------------------------------------------------------------
902 .as PDFHREF.TEXT " \\*[PDFHREF.FILEREF]
904 .de pdf*href.format.page
905 .\" ----------------------------------------------------------------------
906 .\" Include a page number in a formatted reference.
907 .\" This is invoked ONLY by "pdf*href.format", and ONLY IF the
908 .\" reference data includes an initial page number tuple.
909 .\" ----------------------------------------------------------------------
911 .as PDFHREF.TEXT " \\*[PDFHREF.PAGEREF]
913 .de pdf*href.format.section
914 .\" ----------------------------------------------------------------------
915 .\" Include a section number in a formatted reference.
916 .\" This is invoked ONLY by "pdf*href.format", and ONLY IF the
917 .\" reference data includes an initial section number tuple.
918 .\" ----------------------------------------------------------------------
920 .as PDFHREF.TEXT " \\*[PDFHREF.SECTREF]
923 .\" Make "pdf*href.format" the default cross reference formatter
925 .als pdf*href.default pdf*href.format
928 .\" Macro "pdf*href" provides a generic mechanism for placing link
929 .\" "hot-spots" in a PDF document.  ALL "pdfhref" class macros which
930 .\" create "hot-spots" are aliased to this macro; each must also have
931 .\" an appropriately aliased definition for "pdf*href.template".
933 .de pdf*href
934 .\" ------------------------------------------------------------------
935 .\" Usage:
936 .\"   .pdf*href class [options ...] [link text ...]
937 .\" ------------------------------------------------------------------
939 .\" First, we initialise an empty string, which will be affixed to
940 .\" the end of the "link text".  (This is needed to cancel the effect
941 .\" of a "\c" escape, which is placed at the end of the "link text"
942 .\" to support the "-A" option -- any text supplied by the user, when
943 .\" the "-A" option is specified, will replace this empty string).
945 .ds pdf:href-A
947 .\" Now we interpret, and remove any specified options from the
948 .\" argument list.  (Note that only options with a declared handler
949 .\" will be processed;  there is no provision for detecting invalid
950 .\" options -- anything which is not recognised is assumed to start
951 .\" the "link text" component of the argument list).
953 .while dpdf:href.opt\\$1 \{\
954 .   pdf:href.opt\\$1 \\$@
955 .   shift \\n[pdf:href.argc]
956 .   \}
958 .\" If we found "--", to mark the end of the options, then we should
959 .\" discard it.
961 .if '\\$1'--' .shift
963 .\" All PDF link classes REQUIRE a named destination.  This may have
964 .\" been supplied using the "-D Name" option, but, if not, deduce it
965 .\" from the first "word" in the "link text", if any -- if we still
966 .\" can't identify the destination, then set "pdf:href.ok" to zero,
967 .\" so this link will not be created.
969 .if !dpdf:href-D .pdf:href.option -D \\$1
970 .if '\\*[pdf:href-D]'' \{\
971 .   pdf:error pdfhref has no destination
972 .   nr pdf:href.ok 0
973 .   \}
975 .\" Some PDF link classes support a "/File (FilePathName)" argument.
977 .if dpdf*href.file \{\
978 .   \"
979 .   \" When this is supported, it may be specified by supplying
980 .   \" the "-F FileName" option, which is captured in "pdf:href-F".
981 .   \"
982 .   if dpdf:href-F \{\
983 .      \"
984 .      \" the /File key is present, so set up the link specification
985 .      \" to establish the reference to the specified file
986 .      \"
987 .      als pdf*href.link pdf*href.file
988 .      ds pdf:href.files /File (\\*[pdf:href-F])
989 .      \"
990 .      \" in addition to the /File key,
991 .      \" there may also be platform dependent alternate file names
992 .      \"
993 .      if dpdf:href-DF .as pdf:href.files " /DOSFile (\\*[pdf:href-DF])
994 .      if dpdf:href-MF .as pdf:href.files " /MacFile (\\*[pdf:href-MF])
995 .      if dpdf:href-UF .as pdf:href.files " /UnixFile (\\*[pdf:href-UF])
996 .      if dpdf:href-WF .as pdf:href.files " /WinFile (\\*[pdf:href-WF])
997 .      \}
998 .   \" In some cases, the "/File" key is REQUIRED.
999 .   \" We will know it is missing, if "pdf*href.link" is not defined.
1000 .   \"
1001 .   if !dpdf*href.link \{\
1002 .   \"
1003 .   \" When a REQUIRED "/File" key specification is not supplied,
1004 .   \" then complain, and set "pdf:href.ok" to abort the creation
1005 .   \" of the current reference.
1006 .   \"
1007 .      pdf:error pdfhref: required -F specification omitted
1008 .      nr pdf:href.ok 0
1009 .      \}
1010 .   \" Now, we have no further use for "pdf*href.file".
1011 .   \"
1012 .   rm pdf*href.file
1013 .   \}
1015 .\" Now, initialise a string, defining the PDFMARK code sequence
1016 .\" to create the reference, using the appropriate type indicators.
1018 .ds pdf:href.link /Subtype /Link \\*[pdf*href.link]
1020 .\" And now, we have no further use for "pdf*href.link".
1022 .rm pdf*href.link
1024 .\" If the user specified any "link prefix" text, (using the "-P text"
1025 .\" option), then emit it BEFORE processing the "link text" itself.
1027 .if dpdf:href-P \&\\*[pdf:href-P]\c
1028 .ie \\n[pdf:href.ok] \{\
1029 .   \"
1030 .   \" This link is VALID (so far as we can determine) ...
1031 .   \" Modify the "link text" argument specification, as required,
1032 .   \" to include any pre-formatted cross reference information
1033 .   \"
1034 .   ie \\n(.$ \{\
1035 .      \"
1036 .      \" One or more "link text" argument(s) are present,
1037 .      \" so, set the link description from the argument(s) ...
1038 .      \"
1039 .      ds PDFHREF.DESC \\\\$*
1040 .      ie \\n[pdf:href-X] \{\
1041 .         \"
1042 .         \" ... and, when the "-X" flag is set,
1043 .         \" also include formatted location information,
1044 .         \" derived from the cross reference record.
1045 .         \"
1046 .         pdf*href.format \\*[pdf:href(\\*[pdf:href-D]).info]
1047 .         \}
1048 .      el \{\
1049 .         \" ... but, when the "-X" flag is NOT set,
1050 .         \" use only the argument(s) as the entire content
1051 .         \" of the "link text"
1052 .         \"
1053 .         rn PDFHREF.DESC PDFHREF.TEXT
1054 .         \}
1055 .      \}
1056 .   el \{\
1057 .      \" No "link text" arguments are present,
1058 .      \" so, format the cross reference record to define
1059 .      \" the content of the "link text".
1060 .      \"
1061 .      pdf*href.format \\*[pdf:href(\\*[pdf:href-D]).info]
1062 .      \}
1063 .   \" Apply border and colour specifications to the PDFMARK string
1064 .   \" definition, as required.
1065 .   \"
1066 .   if dPDFHREF.BORDER .as pdf:href.link " /Border [\\*[PDFHREF.BORDER]]
1067 .   if dPDFHREF.COLOUR .as pdf:href.link " /Color  [\\*[PDFHREF.COLOUR]]
1068 .   \"
1069 .   \" Emit the "link text", in its appropriate colour, marking the
1070 .   \" limits of its bounding box(es), as the before and after output
1071 .   \" text positions.
1072 .   \"
1073 .   pdf*href.mark.begin "\\*[pdf:href.link]"
1074 .   if dPDFHREF.COLOUR .defcolor pdf:href.colour rgb \\*[PDFHREF.COLOUR]
1075 .   nop \&\m[\\*[PDFHREF.TEXT.COLOUR]]\\*[PDFHREF.TEXT]\m[]\c
1076 .   pdf*href.mark.end
1077 .   \"
1078 .   \" Clean up the temporary registers and strings, used to
1079 .   \" compute the "hot-spot" bounds, and format the reference,
1080 .   \"
1081 .   rm PDFHREF.DESC PDFHREF.TEXT
1082 .   \}
1084 .\" But when we identify an INVALID link ...
1085 .\" We simply emit the "link text", with no colour change, no border,
1086 .\" and no associated "hot-spot".
1088 .el \&\\$*\c
1090 .\" And then, if the user specified any affixed text, (using the
1091 .\" "-A text" option), we tack it on at the end.
1093 .nop \&\\*[pdf:href-A]
1095 .de pdf*href.map.init
1096 .\" ----------------------------------------------------------------------
1097 .\" ----------------------------------------------------------------------
1099 .if dpdf:href.map-1 \{\
1100 .   \"
1101 .   \" We have a reference map, but we haven't started to parse it yet.
1102 .   \" This must be the first map reference in pass 2, so we need to
1103 .   \" "kick-start" the parsing process, by loading the first indexed
1104 .   \" sub-map into the global map.
1105 .   \"
1106 .   rn pdf:href.map-1 pdf:href.map
1107 .   als pdf:href.map.internal pdf:href.map
1108 .   nr pdf:href.map.index 1 1
1109 .   \}
1110 .als pdf*href.map.init pdf*href.mark.idle
1113 .\" "pdf*href-Z" is used to add link co-ordinate entries to the
1114 .\" "pdf:href.map".  Primarily, it is used by the "pdfroff" formatter,
1115 .\" to pass link co-ordinate data from one "groff" formatting pass to
1116 .\" the next, and is not generally useful to the end user.
1118 .de pdf*href-Z
1119 .\" ----------------------------------------------------------------------
1120 .\" Usage:
1121 .\"   .pdfhref Z page-index x-displacement y-displacement
1122 .\" Where:
1123 .\"   page-index      is the reference mark's page number
1124 .\"   x-displacement  is its offset from the left edge of the page
1125 .\"   y-displacement  is its offset from the top edge of the page
1126 .\" ( both displacement values are expressed in basic groff units, )
1127 .\" ( and measured perpendicular to their respective page edges.   )
1128 .\" ----------------------------------------------------------------------
1130 .ie \\n(.$=3 .ds pdf:href.map-\\n+[pdf*href-Z.index] \\$*
1131 .el .pdf:error pdfhref Z operator expects exactly three arguments
1133 .\" Initialise the auto-incrementing "pdf*href-Z.index" register,
1134 .\" to ensure that sub-map numbering starts at 1.
1136 .nr pdf*href-Z.index 0 1
1138 .de pdf*href.map.read
1139 .\" ----------------------------------------------------------------------
1140 .\" Usage: (internal use only):
1141 .\"   .pdf*href.map.read co-ordinate name list ...
1142 .\" ----------------------------------------------------------------------
1144 .\" Reads values from "pdf:href.map" to each named register, in turn
1145 .\" Reading to "null" discards the corresponding value in "pdf:href.map"
1147 .while \\n(.$ \{\
1148 .   \"
1149 .   \" Loop over all registers named in the argument list,
1150 .   \" assigning values from "pdf:href.map" to each in turn.
1151 .   \"
1152 .   pdf:pop nr pdf:\\$1 pdf:href.map.internal
1153 .   if !dpdf:href.map.internal \{\
1154 .      \"
1155 .      \" We ran out of map references in the current sub-map,
1156 .      \" so move on to the next indexed sub-map, if any.
1157 .      \"
1158 .      if dpdf:href.map-\\n+[pdf:href.map.index] \{\
1159 .         rn pdf:href.map-\\n[pdf:href.map.index] pdf:href.map
1160 .         als pdf:href.map.internal pdf:href.map
1161 .         \}
1162 .      \}
1163 .   \"
1164 .   \" Proceed to the next named co-ordinate, (if any), specified
1165 .   \" in the argument list.
1166 .   \"
1167 .   shift
1168 .   \}
1170 .\" Discard any assignments to a register named "null"
1172 .rr pdf:null
1174 .de pdf*href.mark.begin
1175 .\" ----------------------------------------------------------------------
1176 .\" ----------------------------------------------------------------------
1177 .pdf*href.map.init
1178 .ie dpdf:href.map \{\
1179 .   \"
1180 .   \" Once we have established a document reference map,
1181 .   \" then this, and all subsequent calls to "pdf*href.mark.begin",
1182 .   \" may be redirected to the reference mark resolver, and the
1183 .   \" "pdf*href.mark.end" macro has nothing further to do.
1184 .   \"
1185 .   pdf*href.mark.resolve \\$@
1186 .   rn pdf*href.mark.resolve pdf*href.mark.begin
1187 .   als pdf*href.mark.end pdf*href.mark.idle
1188 .   \}
1189 .el \{\
1190 .   \" Since we don't yet have a document reference map, the
1191 .   \" reference mark resolver will not work, in this pass of the
1192 .   \" formatter;  this, and all subsequent calls to "pdf*href.mark.begin",
1193 .   \" may be redirected to "pdf*href.mark.end", which is responsible
1194 .   \" for emitting the reference mark data to be incorporated into
1195 .   \" the reference map in a subsequent formatting pass.
1196 .   \"
1197 .   pdf*href.mark.end
1198 .   als pdf*href.mark.begin pdf*href.mark.end
1199 .   \}
1201 .de pdf*href.mark.resolve
1202 .\" ----------------------------------------------------------------------
1203 .\" ----------------------------------------------------------------------
1204 .ie '\\n(.z'' \{\
1205 .   ds pdf:href.link \\$1
1206 .   nr pdf:urx \\n(.o+\\n(.l
1207 .   pdf*href.map.read spg llx ury epg urx.end lly.end
1208 .   ie \\n[pdf:spg]=\\n[pdf:epg] \{\
1209 .      \"
1210 .      \" This link is entirely contained on a single page ...
1211 .      \" emit the text, which defines the content of the link region,
1212 .      \" then make it active.
1213 .      \"
1214 .      pdf*href.mark.emit 1 \\n[pdf:urx.end]
1215 .      if \\n[pdf:lly]<\\n[pdf:lly.end] \{\
1216 .         \"
1217 .         \" This link spans multiple output lines; we must save its
1218 .         \" original end co-ordinates, then define a new intermediate
1219 .         \" end point, to create a PDFMARK "hot-spot" extending from
1220 .         \" the start of the link to the end if its first line.
1221 .         \"
1222 .         nr pdf:ury +1v
1223 .         nr pdf:llx \\n(.o+\\n[.in]
1224 .         nr pdf:lly \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT]
1225 .         if \\n[pdf:ury]<\\n[pdf:lly] \{\
1226 .            nr pdf:lly +\\*[PDFHREF.HEIGHT]-1v
1227 .            pdf*href.mark.emit 2
1228 .            nr pdf:ury \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT]
1229 .            \}
1230 .         pdf*href.mark.emit 0 \\n[pdf:urx.end]
1231 .         \}
1232 .      pdf*href.mark.flush
1233 .      \}
1234 .   el \{\
1235 .      \" This link is split across a page break, so ...
1236 .      \" We must mark the "hot-spot" region on the current page,
1237 .      \" BEFORE we emit the link text, as we will have moved off
1238 .      \" this page, by the time the text has been output.
1239 .      \"
1240 .      \" First step: define the region from the start of the link,
1241 .      \" to the end of its first line.
1242 .      \"
1243 .      pdf*href.mark.emit 1 \\n[pdf:urx]
1244 .      \"
1245 .      \" All additional regions MUST align with the left margin.
1246 .      \"
1247 .      nr pdf:llx \\n(.o+\\n[.in]
1248 .      \"
1249 .      \" If the current page can accomodate more than the current line,
1250 .      \" then it will include a second active region for this link; this
1251 .      \" will extend from just below the current line to the end of page
1252 .      \" trap, if any, or the bottom of the page otherwise, and occupy
1253 .      \" the full width of the page, between the margins.
1254 .      \"
1255 .      nr pdf:ury +1v
1256 .      pdf*href.mark.emit 3
1257 .      \"
1258 .      \" We now need a page transition trap, to map the active link
1259 .      \" region(s), which overflow on to the following page(s); (the
1260 .      \" handler for this trap MUST have been previously installed).
1261 .      \"
1262 .      ie dpdf*href.mark.hook \{\
1263 .         \"
1264 .         \" The page transition trap handler has been installed,
1265 .         \" so we may activate both it, and also the appropriate
1266 .         \" termination handler, to deactivate it when done.
1267 .         \"
1268 .         als pdf*href.mark.hook pdf*href.mark.trap
1269 .         \"
1270 .         \" Now we set up "pdf:epg" to count the number of page breaks
1271 .         \" which this link will span, and emit the link text, leaving
1272 .         \" the page trap macro to map active regions on intervening
1273 .         \" pages, which are included in the link.
1274 .         \"
1275 .         nr pdf:epg -\\n[pdf:spg] 1
1276 .         \}
1277 .      el \{\
1278 .         \" There was no handler initialised for the page trap,
1279 .         \" so we are unable to map the active regions for this link;
1280 .         \" we may discard the remaining map data for this link,
1281 .         \" and issue a diagnostic.
1282 .         \"
1283 .         pdf:error pdfhref: link dissociated at page break (trap not initialised)
1284 .         if dPDFHREF.BROKEN.COLOR \{\
1285 .            \"
1286 .            \" The user may opt to have such broken links highlighted.
1287 .            \" We use "PDFHREF.BROKEN.COLOUR" to specify this requirement,
1288 .            \" but the user may prefer the American spelling, so we will
1289 .            \" handle both as equivalent.
1290 .            \"
1291 .            als PDFHREF.BROKEN.COLOUR PDFHREF.BROKEN.COLOR
1292 .            \}
1293 .         if dPDFHREF.BROKEN.COLOUR \{\
1294 .            if dPDFHREF.COLOUR .als PDFHREF.COLOUR PDFHREF.BROKEN.COLOUR
1295 .            \}
1296 .         \}
1297 .      \}
1298 .   \}
1299 .el \!.\\$0 \\$@
1302 .\" Macro "pdf*href.mark.emit" is called only by "pdf*href".  It is
1303 .\" responsible for emitting the PDFMARK code, to establish the
1304 .\" "hot-spot" region associated with a document or resource link.
1306 .de pdf*href.mark.emit
1307 .\" ----------------------------------------------------------------------
1308 .\" Usage:
1309 .\"   .pdf*href.mark.emit <action> [<end-urx>]
1310 .\"     <action> == 0 --> normal operation -- link height = 1 line
1311 .\"     <action> == 1 --> start of link -- add leading above text
1312 .\"     <action> == 2 --> overtall link -- set intermediate baseline
1313 .\"     <action> == 3 --> split link -- break at bottom of page
1314 .\" ----------------------------------------------------------------------
1316 .if \\$1=1 \{\
1317 .   \"
1318 .   \" Initialising a new link region ...
1319 .   \" Some different versions of "groff" disagree about the vertical
1320 .   \" displacement of "opminy", as emitted by "\O1|\h'-\w"|"u'\O2\c",
1321 .   \" relative to the current text baseline.  Therefore, recompute
1322 .   \" the link displacement, independently of "opminy".
1323 .   \"
1324 .   mk pdf:ury.base
1325 .   while \\n[pdf:ury.base]<\\n[pdf:ury] .nr pdf:ury.base +1v
1326 .   nr pdf:ury.base -1m+\\n[PDFHREF.LEADING]
1327 .   \"
1328 .   \" adjust the end-point vertical displacement by the same offset,
1329 .   \" and then relocate the link starting point to its new displacement,
1330 .   \" as established by this base line relative computation.
1331 .   \" 
1332 .   nr pdf:lly.end +\\n[pdf:ury.base]-\\n[pdf:ury]+\\*[PDFHREF.HEIGHT]
1333 .   rnn pdf:ury.base pdf:ury
1334 .   \}
1335 .if \\$1<2 \{\
1336 .   \"
1337 .   \" Link segment fits on a single line ...
1338 .   \" Set its height and end-point horizontal displacement accordingly.
1339 .   \"
1340 .   nr pdf:lly \\n[pdf:ury]+\\*[PDFHREF.HEIGHT]
1341 .   if \\n[pdf:lly]>=\\n[pdf:lly.end] .nr pdf:urx \\$2
1342 .   \}
1343 .ie \\$1=3 \{\
1344 .   \"
1345 .   \" Link segment extends beyond the next page break ...
1346 .   \" Recompute truncated height, to just fit portion on current page,
1347 .   \" recursing to emit it, and leaving page trap mechanism to place
1348 .   \" continuation region(s) on following page(s).
1349 .   \"
1350 .   nr pdf:lly (\\n[.t]u-\\n[.V]u)/1v
1351 .   if \\n[pdf:lly]>0 \{\
1352 .      nr pdf:lly \\n[pdf:ury]+\\n[pdf:lly]v-1v+\\*[PDFHREF.HEIGHT]
1353 .      pdf*href.mark.emit 2
1354 .      \}
1355 .   \}
1356 .el \{\
1357 .   \" Link region size and placement has been fully specified ...
1358 .   \" Emit it.
1359 .   \"
1360 .   pdfmark \\*[pdf:href.link] /Rect [\\*[pdf:bbox]] /ANN
1361 .   \}
1364 .\" When "pdf*href" emits a link for which the "hot-spot" spans a
1365 .\" page break, then we need to provide a "hook" in to the page break
1366 .\" trap, so we can map the "hot-spot" regions which are to be placed
1367 .\" on either side of the page break.
1369 .\" Macro "pdf*href.mark.idle" is a dummy macro, which provide this
1370 .\" "hook" for normal page breaks, where there is no link "hot-spot"
1371 .\" crossing the break.
1373 .de pdf*href.mark.idle
1374 .\" ----------------------------------------------------------------------
1375 .\" Usage:
1376 .\"   Called only as an internal hook, by a page trap macro.
1377 .\"   Expects no arguments, and does nothing.
1378 .\" ----------------------------------------------------------------------
1381 .\" Macro "pdf*href.mark.trap" is the active "hook", which is substituted
1382 .\" for "pdf*href,mark.idle" at those page breaks which are crossed by
1383 .\" a link "hot-spot".
1385 .de pdf*href.mark.trap
1386 .\" ----------------------------------------------------------------------
1387 .\" Usage:
1388 .\"   Called only as an internal hook, by a page trap macro.
1389 .\"   Expects no arguments.  Maps residual link "hot-spot" regions,
1390 .\"   which spill beyond any page break.  Not to be invoked directly
1391 .\"   by the user, nor by any user supplied macro.
1392 .\" ----------------------------------------------------------------------
1394 .mk pdf:ury
1395 .nr pdf:ury +1v-1m-\\n[PDFHREF.LEADING]
1396 .ie \\n-[pdf:epg] \{\
1397 .   \"
1398 .   \" The link "hot-spot" extends across more than one page break,
1399 .   \" so, for each page which is completely contained within the
1400 .   \" extent of the link, simply mark the entire text area on the
1401 .   \" page as a "hot-spot".
1402 .   \"
1403 .   pdf*href.mark.emit 3
1404 .   \}
1405 .el \{\
1406 .   \" The link "hot-spot" ends on the page which immediately follows
1407 .   \" the current page transition, so we may now finalise this link.
1408 .   \"
1409 .   nr pdf:lly \\n[pdf:ury]+\\*[PDFHREF.HEIGHT]
1410 .   if \\n[pdf:lly.end]>\\n[pdf:lly] \{\
1411 .      \"
1412 .      \" The "hot-spot" extends beyond the first line of text,
1413 .      \" on its final page; compute and emit "hot-spot" region to cover
1414 .      \" the full with of the text area, including all but the last
1415 .      \" line of the link text.
1416 .      \"
1417 .      while \\n[pdf:lly.end]>\\n[pdf:lly] .nr pdf:lly +1v
1418 .      nr pdf:lly -1v
1419 .      pdf*href.mark.emit 2
1420 .      \"
1421 .      \" Now, adjust the vertical "hot-spot" mapping reference,
1422 .      \" to identify the correct position for the the last line of
1423 .      \" text, over which the "hot-spot" extends.
1424 .      \"
1425 .      nr pdf:ury \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT]
1426 .      \}
1427 .   \"
1428 .   \" We now have exactly one final line of text, over which we must
1429 .   \" emit a "hot-spot" region;  map it, terminate page trap processing
1430 .   \" for this "hot-spot", and clean up the "hot-spot" mapping context.
1431 .   \"
1432 .   pdf*href.mark.emit 0 \\n[pdf:urx.end]
1433 .   als pdf*href.mark.hook pdf*href.mark.idle
1434 .   pdf*href.mark.flush
1435 .   \}
1437 .de pdf*href.mark.flush
1438 .\" ----------------------------------------------------------------------
1439 .\" ----------------------------------------------------------------------
1440 .rr pdf:spg pdf:epg
1441 .rr pdf:llx pdf:lly pdf:urx pdf:ury
1442 .if dPDFHREF.COLOR .als PDFHREF.COLOUR PDFHREF.COLOR
1443 .rr pdf:urx.end pdf:lly.end
1445 .de pdf*href.mark.end
1446 .\" ----------------------------------------------------------------------
1447 .\" ----------------------------------------------------------------------
1448 \O1|\h'-\w"|"u'\O2\c
1450 .\" Macro "pdf*href-I" is used for one time initialisation of special
1451 .\" "pdfhref" features; (currently, only the above page trap hook is
1452 .\" supported, but it is implemented with one level of indirection, to
1453 .\" accommodate possible future expansion).
1455 .de pdf*href-I
1456 .\" ----------------------------------------------------------------------
1457 .\" Usage:
1458 .\"   .pdfhref I -<option> <optarg> [-<option> <optarg>] ...
1459 .\" ----------------------------------------------------------------------
1461 .\" Loop over all arguments, in pairs ...
1463 .while \\n(.$ \{\
1464 .   \"
1465 .   \" handing them off to their respective initialisers,
1466 .   \" when suitable initialisers exist, or complaining otherwise.
1467 .   \"
1468 .   ie dpdf*href\\$1.init .pdf*href\\$1.init \\$2
1469 .   el .pdf*error pdfhref:init: unknown feature '\\$1'
1470 .   shift 2
1471 .   \}
1473 .\" Before we can use the page break "hook", we need to initialise it
1474 .\" as an addendum to a regular page break trap. To ensure that we don't
1475 .\" compromise the user's page trap setup, we leave the onus for this
1476 .\" initialisation with the user, but we provide the "pdf*href-PT.init"
1477 .\" macro, (invoked by ".pdfhref I -PT <macro-name>"), to implement a
1478 .\" suitable initialisation action.
1480 .de pdf*href-PT.init
1481 .\" ----------------------------------------------------------------------
1482 .\" Usage:
1483 .\"   .pdfhref I -PT <macro-name>
1484 .\"     <macro-name> == name of user's page break trap macro
1485 .\" ----------------------------------------------------------------------
1487 .\" Initially, map the page break hook to its default, do nothing helper.
1489 .als pdf*href.mark.hook pdf*href.mark.idle
1490 .ie !\\n(.$ \{\
1491 .   \"
1492 .   \" Don't have enough arguments to specify a page trap macro name,
1493 .   \" so simply plant "pdf*href.mark.hook" as a top of page trap.
1494 .   \"
1495 .   wh 0 pdf*href.mark.hook
1496 .   \}
1497 .el \{\
1498 .   \" Page trap macro name is specified in "\\$1" ...
1499 .   \"
1500 .   ie d\\$1 \{\
1501 .      \"
1502 .      \" When this page trap macro already exists, then we simply
1503 .      \" append a call to "pdf*href.mark.hook" to it.
1504 .      \"
1505 .      am \\$1 pdf*href.mark.idle
1506 .         pdf*href.mark.hook
1507 .         pdf*href.mark.idle
1508 .      \}
1509 .   el \{\
1510 .      \" However, when the specified page trap macro does not yet
1511 .      \" exist, then we create it, and plant it as a top of page
1512 .      \" trap.
1513 .      \"
1514 .      de \\$1 pdf*href.mark.idle
1515 .         pdf*href.mark.hook
1516 .         pdf*href.mark.idle
1517 .      wh 0 \\$1
1518 .      \}
1519 .   \}
1522 .\" "pdf*href-L" is the generic handler for creating references to
1523 .\" named destinations in PDF documents.  It supports both local
1524 .\" references, to locations within the same document, through its
1525 .\" "pdf*href-L.link" attribute, and also references to locations
1526 .\" in any other PDF document, through "pdf*href-L.file".
1528 .als pdf*href-L      pdf*href
1529 .ds  pdf*href-L.link /Dest /\\\\*[pdf:href-D]
1530 .ds  pdf*href-L.file /Action /GoToR \\\\*[pdf:href.files] \\*[pdf*href-L.link]
1532 .\" "pdf*href-O" is the "official" handler for creating PDF
1533 .\" document outlines.  It is simply an alias to "pdfbookmark",
1534 .\" which may also be invoked directly, if preferred.  Neither
1535 .\" a "pdf*href-O.link" nor a "pdf*href-O.file" attribute is
1536 .\" required.
1538 .als pdf*href-O      pdfbookmark
1540 .\" "pdf*href-W" is the generic handler for creating references to
1541 .\" web resources, (or any resource specified by a uniform resource
1542 .\" identifier).  Such resource links are fully specified by the
1543 .\" "pdf*href-W.link" attribute.
1545 .als pdf*href-W      pdf*href
1546 .ds  pdf*href-W.link /Action << /Subtype /URI /URI (\\\\*[pdf:href-D]) >>
1548 .\" pdfmark.tmac: end of file / vim: ft=groff