I need to step up and help but I just wanted to say that I think this moves groff forward in a big way. Well done.
On Thu, Apr 23, 2026 at 03:52:48PM +0100, Deri via discussion of the GNU roff typesetting system and related software wrote: > Hello, > > I am hoping for some help testing some changes to the ms macros. You will > need > to be running 1.24.0 or current git version (requires Branden's numerous > fixes > to .asciify). The changes are to add automatic pdf features to any ms > document. The features are activated for "-Tpdf" by including "-rPDFFEAT=1" > on > the groff command line, otherwise the document should render exactly as it > does now. > > If you copy the attached files to a new directory and cd into it, you can do > the tests with the command:- > > groff -T pdf -M. -ms -rPDFFEAT=1 (other usual options) document.ms > > document.pdf > > So:- > > groff -Tpdf -M. -ms -pet -dpapersz=letter -rPDFFEAT=1 -P-pletter /usr/local/ > share/doc/groff-1.24.0/ms.ms > ms.pdf > > Should generate a document with an overview panel, with a title and author in > the pdf meta-data, and a clickable Table of Contents. (If you add the line > ".RP" before the ".TL" line near the top of the document to add a title page, > the TOC should appear after the title page). Setting PDFFEAT to 0, or > omitting > it, should produce a completely vanilla pdf, as it does now. If using -Tps - > PDFFEAT is ignored. > > A new macro call:- > > .XR name [after [before [hotlink-text]]] > > Allows you to insert a local link to elsewhere in the document and an > extension to:- > > .NH depth [name] > > Establishes a named destination for an XR link. The attached document "Groff- > PDF-Features.ms" illustrates the use of named destinations and documents the > above additions to ms. Sometimes you want to link to a section "later" in the > document, and since groff is a single pass system it is necessary to use a > special command. To create the Groff-PDF-Features.pdf use this command:- > > pdfmom --roff -M. -ms -msboxes -tU -P-pa4 -ww -rPDFFEAT=1 Groff-PDF- > Features.ms > Groff-PDF-Features.pdf > > If you set up a symbolic link as pdfms to pdfmom, this command can be > shortened to:- > > pdfms -M. -msboxes -petU -ww -rPDFFEAT=1 Groff-PDF-Features.ms > Groff-PDF- > Features.pdf > > NOTES > ===== > > If you use .NH with following .XN (rather than the older .NH / text / .XS / > text / .XE) the .XN must be on the line immediately following .NH. > > Only the first OUTPUT line created by the text following .NH is used in pdf > bookmarks, i.e. if the heading occupies more than 1 output line. > > If your document is already using pdf features, either with pdfmark/spdf or > your own macros, don't run with -rPDFFEAT=1, but do check the document is not > affected by this version of s.tmac. > > The changes introduced to s.tmac (when -rPDFFEAT=1) are intended to work with > any ms document which was written for postscript with no changes required to > add pdf features. > > > If you have time to test this s.tmac please let me know your results and > thoughts. > > Cheers > > Deri > .ig > > Copyright 1989-2021,2026 Free Software Foundation, Inc. > 2022-2023 G. Branden Robinson > > Written by James Clark ([email protected]) > Enhanced by: Werner Lemberg <[email protected]> > Keith Marshall <[email protected]> > G. Branden Robinson <[email protected]> > > This file is part of groff, the GNU roff typesetting system. > > 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/>. > .. > .if !\n(.g \ > . ab groff ms macros require groff extensions; aborting > . > .if \n(.C \ > . ab groff ms macros do not work in compatibility mode; aborting > . > .\" See if already loaded. > .if r GS .nx > .nr GS 1 > . > .mso devtag.tmac > .nr s@devtag-needs-end-of-heading 0 > .nr s@devtag-needs-end-of-title 0 > . > .\" Define a string for use in diagnostic messages. > .ds @s s.tmac\" > . > .nr @is-initialized 0 > . > .de @diag > . ds *file \" empty > . ds *line \" empty > . if !'\\n[.F]'' .ds *file \\n[.F]:\" > . if !'\\n[.c]'0' .ds *line \\n[.c]:\" > . tm \*[@s]:\\*[*file]\\*[*line] \\$* > . rm *file > . rm *line > .. > . > .de @error > . @diag error: \\$* > .. > . > .de @warning > . @diag warning: \\$* > .. > . > .de @nop > .. > . > .de @not-implemented > .@error sorry, .\\$0 not implemented > .als \\$0 @nop > .. > . > .\" documented Unix Version 7 ms macros that we don't implement > .als EG @not-implemented \" engineer's notes > .als IM @not-implemented \" internal memorandum > .als MF @not-implemented \" memorandum for file > .als MR @not-implemented \" memorandum for record > .als TM @not-implemented \" technical memorandum > .als TR @not-implemented \" technical report > .als AT @not-implemented \" attachments > .als CS @not-implemented \" cover sheet info for `TM` documents > .als CT @not-implemented \" copies to > .als OK @not-implemented \" "other keywords" for `TM` documents > .als SG @not-implemented \" signatures for `TM` documents > .als HO @not-implemented \" Holmdel > .als IH @not-implemented \" Naperville (Indian Hill) > .als MH @not-implemented \" Murray Hill > .als PY @not-implemented \" Piscataway > .als WH @not-implemented \" Whippany > .als UX @not-implemented \" Unix w/ footnote trademark > . > .\" 4.2BSD ms macros documented by Tuthill 1983 that we don't implement > .\"als TM @not-implemented \" thesis mode (already handled) > .\"als CT @not-implemented \" chapter title for thesis (already handled) > . > .\" Wrap the .di request to save the name of the file being processed > .\" when a diversion is started. This aids diagnostics when diversions > .\" are still open after input has been processed and \n[.F] is empty. > .als @divert di > .de di > . if \\n[.$] .ds @last-file-seen \\n[.F]\" > . @divert \\$* > .. > . > .if !r PDFFEAT .nr PDFFEAT 0 > .if !'\?\*[.T]\?'\?pdf\?' .nr PDFFEAT 0 > .if \n[PDFFEAT]=1 \{\ > .nr PDFHREF.VIEW.LEADING 10.0p > .nr nhct 0 1 > .nr shct 0 1 > .nr gottitle*pdf 0 > .ds spdf:txt_default see: + > .ds pdf:curcol default > .nr XN*first 0 > .\} > . > .de XR > .if \\n[PDFFEAT]>0 \{\ > . ds spdf!txt \\*[spdf:txt_default] > . ds spdf!opts -D "\\$1" > . if \\n(.$>1 .as spdf!opts " -A "\\$2" > . if \\n(.$>2 .as spdf!opts " -P "\\$3" > . if \\n(.$>3 .ds spdf!txt \\$4 > . ds spdf!ast \\*[spdf!txt] > . ds spdf!ast_q > . substring spdf!ast -1 -1 > . if '\\*[spdf!ast]'+' \{\ > . ds spdf!ast * > . ds spdf!ast_q "" > . \} > . pdf:lookup \\$1 > .\" . nop \c > . ie !'\\*[pdf:lookup-result]'' \{\ > . if '\\*[spdf!ast]'*' \{\ > . chop spdf!txt > . as spdf!txt \&\\*[spdf!ast_q]\\*[pdf:lookup-value]\\*[spdf!ast_q] > . \} > . \} > . el \{\ > . as spdf!txt " Unknown > . if !rspdf!unk .tm \ > [pdfms]: \\n[.F]:\\n[.c]: forward reference to '\\$1' detected (please run > using 'pdfmom --roff -ms') > . nr spdf!unk 1 > . \} > . pdfhref L \\*[spdf!opts] -- \\*[spdf!txt]\c > . rm spdf!opts > . rm spdf!txt > . rm spdf!ast > . rm spdf!ast_q > .\} > .. > .\} > .\" Wrap the `bp` request so that we disregard the enablement of > .\" no-space mode, which we enter after displays and equations to > .\" prevent multiple sources of vertical space (like the DD and PD > .\" registers) from incorrectly accumulating. Thus, if the user > .\" requests a page break, we honor it. See Savannah #62688 & #64005. > .als @break-page bp > .de bp > . nr @saved-no-space-mode \\n[.ns] > . rs > . ie \\n[.br] .@break-page \\$1 > . el '@break-page \\$1 > . if \\n[@saved-no-space-mode] .ns > . rr @saved-no-space-mode > .. > . > .\" Initialize environments `k` and `nf` immediately so that keeps can > .\" be used on a cover page. > .ev k > .evc 0 > .ev > . > .ev nf > .evc 0 > 'nf > .ev > . > .de @init > .if !rPO .nr PO \\n(.o > .\" a non-empty environment > .ev ne > \c > .ev > .nr @is-initialized 1 > .. > .ds REFERENCES References > .ds ABSTRACT ABSTRACT > .ds TOC Table of Contents > .ds MONTH1 January > .ds MONTH2 February > .ds MONTH3 March > .ds MONTH4 April > .ds MONTH5 May > .ds MONTH6 June > .ds MONTH7 July > .ds MONTH8 August > .ds MONTH9 September > .ds MONTH10 October > .ds MONTH11 November > .ds MONTH12 December > .ds MO \E*[MONTH\n[mo]] > .ds DY \n[dy] \*[MO] \n[year] > .de ND > .if \\n[.$] .ds DY "\\$* > .. > .de DA > .if \\n[.$] .ds DY "\\$* > .ds CF \\*[DY] > .. > .\" print an error message and then try to recover > .de @error-recover > .@error \\$@ (recovering) > .nr *pop-count 0 > .while !'\\n(.z'' \{\ > . \"@warning automatically terminating diversion \\n(.z > . ie d @div-end!\\n(.z .@div-end!\\n(.z > . el .*div-end-default > . nr *pop-count +1 > . \" ensure that we don't loop forever > . if \\n[*pop-count]>20 .ab \*[@s]: fatal error: recovery failed > .\} > .while !'\\n[.ev]'0' .ev > .par@reset-env > .par@reset > .. > .de *div-end-default > .ds *last-div \\n(.z > .br > .di > .ev nf > .\\*[*last-div] > .ev > .. > .\" **************************** > .\" ******** module cov ******** > .\" **************************** > .\" Cover sheet and first page. > .de cov*err-not-after-first-page > .@error .\\$0 is not allowed after the first page has started > .. > .de cov*err-not-before-tl > .@error .\\$0 is not allowed before .TL > .. > .de cov*err-not-again > .@error .\\$0 is not allowed more than once > .. > .de cov*err-not-after-ab > .@error .\\$0 is not allowed after first .AB, .LP, .PP, .IP, .SH or .NH > .. > .als AU cov*err-not-before-tl > .als AI cov*err-not-before-tl > .als AB cov*err-not-before-tl > .de cov*first-page-init > .\" Invoked by '.wh 0' trap on first page. > .\" We should not come here again, but at short page length, > .\" recursion may occur; remove trap and macro to avoid it. > .ch cov*first-page-init > .rm cov*first-page-init > .if !'\\n[.ev]'0' \{\ > . ds cov*msg must be in top-level environment, not '\\n[.ev]',\" > . as cov*msg " when first page is started\" > . @error \\*[cov*msg] > . rm cov*msg > .\} > .par@init > .als RP cov*err-not-after-first-page > .@init > .ie \\n[cov*use-rp-format] \{\ > . pg@cs-top > . als FS cov*FS > . als FE cov*FE > .\} > .el \{\ > . pg@top > . als FS @FS > . als FE @FE > .\} > .wh 0 pg@top > .CHECK-FOOTER-AND-KEEP > .. > .wh 0 cov*first-page-init > .\" This handles the case where FS occurs before TL or LP. > .de FS > .br > \\*[FS]\\ > .. > .nr cov*use-rp-format 0 > .\" If we add more cover page formats, these behaviors and names could > .\" be generalized. > .nr cov*rp-no-repeat-info 0 > .nr cov*rp-no-renumber 0 > .\" report (AT&T: "released paper") document type > .de RP > .nr cov*use-rp-format 1 > .while \\n[.$] \{\ > . if '\\$1'no' .nr cov*rp-no-repeat-info 1 > . if '\\$1'no-repeat-info' .nr cov*rp-no-repeat-info 1 > . if '\\$1'no-renumber' .nr cov*rp-no-renumber 1 > . shift > .\} > .if rPO .po \\n(POu > .if \\n[PDFFEAT]=1 \{\ > . if \\n[cov*rp-no-repeat-info]=1 .nr cov*pdf 1 > . pdfpagename title > . nr gottitle*pdf 1 > . > .\} > .. > .de TL > .br > .als TL cov*err-not-again > .rn @AB AB > .rn @AU AU > .rn @AI AI > .di cov*tl-div > .par@reset > .ft B > .ps +2 > .vs +3p > .ll (u;\\n[LL]*5/6) > .nr cov*n-au 0 > .DEVTAG-TL > .. > .de @AU > .par@reset > .if !'\\n(.z'' \{\ > . br > . di > .\} > .nr cov*n-au +1 > .di cov*au-div!\\n[cov*n-au] > .nf > .ft I > .ie (\\n[PS] >= 1000) \ > . ps (\\n[PS]z / 1000u) > .el \ > . ps \\n[PS] > .. > .de @AI > .par@reset > .if !'\\n(.z'' \{\ > . br > . di > .\} > .ie !\\n[cov*n-au] .@error .AI before .AU > .el \{\ > . di cov*ai-div!\\n[cov*n-au] > . nf > . ft R > . ie (\\n[PS] >= 1000) \ > . ps (\\n[PS]z / 1000u) > . el \ > . ps \\n[PS] > .\} > .. > . > .de LP > .if !'\\n[.z]'' \{\ > . br > . di > .\} > .br > .cov*ab-init > .cov*print > .nop \\*[\\$0]\\ > .. > . > .als IP LP > .als PP LP > .als XP LP > .als QP LP > .als RS LP > .als NH LP > .als SH LP > .als MC LP > .als RT LP > .als XS LP > . > .de cov*ab-init > .als cov*ab-init @nop > .als LP @LP > .als IP @IP > .als PP @PP > .als XP @XP > .als RT @RT > .als XS @XS > .als SH @SH > .als NH @NH > .als QP @QP > .als RS @RS > .als RE @RE > .als QS @QS > .als QE @QE > .als MC @MC > .als EQ @EQ > .als EN @EN > .als PS @PS > .als TS @TS > .als AB cov*err-not-after-ab > .als AU par@AU > .als AI par@AI > .als TL par@TL > .. > . > .de @AB > .if !'\\n(.z'' \{\ > . br > . di > .\} > .cov*ab-init > .ie '\*(.T'html' \{\ > . cov*tl-au-print > . als cov*tl-au-print @nop > .\} > .el .di cov*ab-div > .par@ab-indent > .par@reset > .if !'\\$1'no' \{\ > . if '\*(.T'html' \{\ > . nf > . sp > . \} > . ft I > . ce 1 > \\*[ABSTRACT] > . sp > . ft R > .\} > .ns > .@PP > .if '\*(.T'html' \{\ > . cov*tl-au-print > . als cov*tl-au-print @nop > . par@reset-env > . par@reset > . cov*print > .\} > .. > .de AE > .ie '\*(.T'html' \{\ > . als AE cov*err-not-again > .\} > .el \{\ > . ie '\\n(.z'cov*ab-div' \{\ > . als AE cov*err-not-again > . br > . di > .\" nr cov*ab-height \\n[dn] > . par@reset-env > . par@reset > . cov*print > . \} > . el .@error .AE without .AB > .\} > .. > .de @div-end!cov*ab-div > .AE > .. > .de cov*break-page > .ie \\n[cov*rp-no-renumber] .@break-page > .el .@break-page 1 > .. > .de cov*print > .als cov*print @nop > .ie d cov*tl-div \{\ > . ie \\n[cov*use-rp-format] .cov*rp-print > . el .cov*draft-print > .\} > .el \{\ > . if \\n[cov*use-rp-format] \{\ > . @warning .RP format but no .TL > . .cov*break-page > . als FS @FS > . als FE @FE > . CHECK-FOOTER-AND-KEEP > . \} > . br > .\} > .if \\n[PDFFEAT]=1 \{\ > . if d cov*tl-div \{\ > . asciify cov*tl-div > . chop cov*tl-div > . pdfinfo /Title \\*[cov*tl-div] > . \} > . ds pdfauthor \" > . nr cov*i 1 > . while \\n[cov*i]<=\\n[cov*n-au] \{\ > . asciify cov*au-div!\\n[cov*i] > . chop cov*au-div!\\n[cov*i] > . if \\n[cov*i]>1 .as pdfauthor ", " > . as pdfauthor \\*[cov*au-div!\\n[cov*i]] > . nr cov*i +1 > . \} > . pdfinfo /Author "\\*[pdfauthor]" > .\} > .. > .de cov*rp-print > .nr cov*page-length \\n[.p] > .pl 1000i > .cov*tl-au-print > .sp 3 > .if d cov*ab-div \{\ > . if !'\*(.T'html' .nf > . cov*ab-div > .\} > .sp 3 > .par@reset > \\*[DY] > .br > .if \\n[cov*fn-height] \{\ > . sp |(u;\\n[cov*page-length]-\\n[FM]\ > -\\n[cov*fn-height]-\\n[fn@sep-dist]>?\\n[nl]) > . fn@print-sep > . ev nf > . cov*fn-div > . ev > . ie \\n[cov*rp-no-repeat-info] .rm cov*fn-div > . el \{\ > . rn cov*fn-div fn@overflow-div > . nr fn@have-overflow 1 > . \} > .\} > .als FS @FS > .als FE @FE > .CHECK-FOOTER-AND-KEEP > .\" If anything was printed below where the footer line is normally > .\" printed, then that's an overflow. > .if -\\n[FM]/2+1v+\\n[cov*page-length]<\\n[nl] \ > . @error cover sheet overflow > .pl \\n[cov*page-length]u > .cov*break-page > .if !\\n[cov*rp-no-repeat-info] .cov*tl-au-print > .rs > .sp 1 > .. > .de cov*draft-print > .cov*tl-au-print > .if d cov*ab-div \{\ > . nf > . sp 2 > . cov*ab-div > .\} > .sp 1 > .. > .de cov*tl-au-print > .par@reset > .nf > .rs > .sp 3 > .ce \\n[.R] > .if d cov*tl-div \{\ > . cov*tl-div > . DEVTAG-EO-TL > .\} > .nr cov*i 1 > .nr cov*sp 1v > .while \\n[cov*i]<=\\n[cov*n-au] \{\ > . ie '\*(.T'html' .br > . el .sp \\n[cov*sp]u > . cov*au-div!\\n[cov*i] > . ie d cov*ai-div!\\n[cov*i] \{\ > . sp .5v > . cov*ai-div!\\n[cov*i] > . nr cov*sp 1v > . \} > . el .nr cov*sp .5v > . nr cov*i +1 > .\} > .ce 0 > .. > .nr cov*fn-height 0 > .nr cov*in-fn 0 > .\" start of footnote on cover > .de cov*FS > .if \\n[cov*in-fn] \{\ > . @error nested .FS > . FE > .\} > .nr cov*in-fn 1 > .ev fn > .par@reset-env > .da cov*fn-div > .if !\\n[cov*fn-height] .ns > .ie \\n[.$] .FP "\\$1" no > .el .@LP > .. > .de @div-end!cov*fn-div > .cov*FE > .. > .\" end of footnote on cover > .de cov*FE > .ie '\\n(.z'cov*fn-div' \{\ > . br > . ev > . di > . nr cov*in-fn 0 > . nr cov*fn-height +\\n[dn] > .\} > .el .@error .FE without matching .FS > .. > .\" *************************** > .\" ******** module pg ******** > .\" *************************** > .\" Page-level formatting. > .\" > 0 if we have a footnote on the current page > .nr pg@fn-flag 0 > .nr pg@colw 0 > .nr pg@fn-colw 0 > .nr HM 1i > .nr FM 1i > .ds LF > .ds CF > .ds RF > .ds LH > .ds CH -\En[%]-\" > .ds RH > .ds pg*OH '\E*[LH]'\E*[CH]'\E*[RH]' > .ds pg*EH '\E*[LH]'\E*[CH]'\E*[RH]' > .ds pg*OF '\E*[LF]'\E*[CF]'\E*[RF]' > .ds pg*EF '\E*[LF]'\E*[CF]'\E*[RF]' > .de OH > .ds pg*\\$0 "\\$* > .. > .als EH OH > .als OF OH > .als EF OH > .aln PN % \" Lesk 1978 documents PN. > .de PT > .\" To compare the page number to 1, we need it in Arabic format. > .ds pg*saved-page-number-format \\g%\" > .af % 0 > .nr pg*page-number-in-decimal \\n% > .af % \\*[pg*saved-page-number-format] > .fam \\*[pg@titles-font-family] > .ie \\n[pg*page-number-in-decimal]=1 .if \\n[pg*P1] .tl \\*[pg*OH] > .el \{\ > . ie o .tl \\*[pg*OH] > . el .tl \\*[pg*EH] > .\} > .rm pg*saved-page-number-format > .. > .de BT > .fam \\*[pg@titles-font-family] > .ie o .tl \\*[pg*OF] > .el .tl \\*[pg*EF] > .. > .nr pg*P1 0 > .de P1 > .nr pg*P1 1 > .. > .\" Establish traps for the bottom of the text area and the footer line. > .\" Various macros move them, but pg@top restores them at each new page. > .wh -\n[FM]u pg@bottom > .wh -\n[FM]u/2u-1v pg*footer > .nr MINGW 2n > .nr pg@ncols 1 > .de @MC > .if !'\\n(.z'' .@error-recover .MC while diversion open > .br > .ie \\n[pg@ncols]>1 .pg@super-eject > .el \{\ > . \" flush out any floating keeps > . while \\n[kp@tail]>\\n[kp@head] \{\ > . rs > . @break-page > . \} > .\} > .ie !\\n(.$ \{\ > . nr pg*gutw \\n[MINGW] > . nr pg@colw \\n[LL]-\\n[pg*gutw]/2u > . nr pg@ncols 2 > .\} > .el \{\ > . nr pg@colw (n;\\$1)<?\\n[LL] > . ie \\n[.$]<2 .nr pg*gutw \\n[MINGW] > . el .nr pg*gutw (n;\\$2) > . nr pg@ncols \\n[LL]-\\n[pg@colw]/(\\n[pg@colw]+\\n[pg*gutw])+1 > . ie \\n[pg@ncols]>1 \ > . nr pg*gutw > \\n[LL]-(\\n[pg@ncols]*\\n[pg@colw])/(\\n[pg@ncols]-1) > . el .nr pg*gutw 0 > .\} > .DEVTAG ".mc \\n[pg@ncols] \\n[pg@colw] \\n[pg*gutw]" > .nr pg*col-num 0 > .nr pg@fn-colw \\n[pg@colw]*\\*[FR] > .par@reset > .ns > .mk pg*col-top > .. > .de 2C > .MC > .. > .de 1C > .MC \\n[LL]u > .. > .de pg@top > .\" invoked by '.wh 0' trap at the top of every page > .\" > .\" At short page lengths, footers may get truncated or recursion may > .\" occur; ensure that the page length suffices to avoid these problems. > .if (u;\\n[HM]+\\n[FM]+\n[.V]>=\\n[.p]) \{\ > . @error insufficient page length; aborting\" > . pl \\n[nl]u > . ab > .\} > .ch pg*footer -\\n[FM]u/2u-1v > .nr pg*col-num 0 > .nr pg@fn-bottom-margin 0 > .po \\n[PO]u > .ev h > .par@reset > .sp (u;\\n[HM]/2) > .PT > .sp |\\n[HM]u > .if d HD .HD > .mk pg@header-bottom > .ev > .mk pg*col-top > .pg*start-col > .. > .de pg*start-col > .\" Handle footnote overflow before floating keeps, because the keep > .\" might contain an embedded footnote. > .fn@top-hook > .kp@top-hook > .tbl@top-hook > .ns > .. > .de pg@cs-top > .sp \\n[HM]u > .\" move pg@bottom and pg*footer out of the way > .ch pg@bottom \\n[.p]u*2u > .ch pg*footer \\n[.p]u*2u > .ns > .. > .de pg@bottom > .tbl@bottom-hook > .if \\n[pg@fn-flag] .fn@bottom-hook > .nr pg*col-num +1 > .ie \\n[pg*col-num]<\\n[pg@ncols] .pg*end-col > .el .pg*end-page > .. > .de pg*end-col > 'sp |\\n[pg*col-top]u > .po (u;\\n[PO]+(\\n[pg@colw]+\\n[pg*gutw]*\\n[pg*col-num])) > .\"po +(u;\\n[pg@colw]+\\n[pg*gutw]) > .pg*start-col > .. > .de pg*end-page > .po \\n[PO]u > .\" Make sure we don't exit if there are still floats or footnotes > .\" left-over. > .ie \\n[kp@head]<\\n[kp@tail]:\\n[fn@have-overflow] \{\ > . \" Switching environments ensures that we don't get an unnecessary > . \" blank line at the top of the page. > . ev ne > ' @break-page > . ev > .\} > .el \{\ > . \" If the text has ended and there are no more footnotes or > . \" keeps, exit. > . if \\n[pg@text-ended] .ex > . if r pg*next-number \{\ > . pn \\n[pg*next-number] > . rr pg*next-number > . if d pg*next-format \{\ > . af % \\*[pg*next-format] > . rm pg*next-format > . \} > . \} > ' @break-page > .\} > .. > .\" pg@begin number format > .de pg@begin > .ie \\n[.$]>0 \{\ > . nr pg*next-number (;\\$1) > . ie \\n[.$]>1 .ds pg*next-format \\$2 > . el .rm pg*next-format > .\} > .el .rr pg*next-number > .pg@super-eject > .. > .\" print the footer line > .de pg*footer > .ev h > .par@reset > .BT > .ev > .. > .\" flush out any keeps or footnotes > .de pg@super-eject > .br > .if !'\\n(.z'' \{\ > . ds @msg diversion open while ejecting page\" > . as @msg " (last file seen: \\*[@last-file-seen])\" > . @error-recover \\*[@msg] > . rm @msg > .\} > .\" Make sure we stay in the end macro while there is still footnote > .\" overflow left, or floating keeps. > .while \\n[kp@tail]>\\n[kp@head]:\\n[pg@fn-flag] \{\ > . rs > . @break-page > .\} > .@break-page > .. > .nr pg@text-ended 0 > .de pg@end-text > .br > .nr pg@text-ended 1 > .pg@super-eject > .. > .em pg@end-text > .\" *************************** > .\" ******** module fn ******** > .\" *************************** > .\" Footnotes. > .nr fn@sep-dist 8p > .ev fn > .\" Round it vertically > .vs \n[fn@sep-dist]u > .nr fn@sep-dist \n[.v] > .ev > .nr fn*text-num 0 1 > .nr fn*note-num 0 1 > .nr fn*open 0 > .\" Handle initialition tasks deferred until par module is set up. > .de fn@init > .ie t .als fn@mark-start par@sup-start > .el .ds fn@mark-start [ > .ie t .als fn@mark-end par@sup-end > .el .ds fn@mark-end ] > .ds * \E*[fn@mark-start]\En+[fn*text-num]\E*[fn@mark-end] > .. > .\" normal FS > .\" FS-MARK is a user definable hook, which may be used to perform > .\" any set-up actions, (e.g. planting an HREF link as the footnote > .\" mark, in the document text); passed the same arguments as have > .\" been passed to FS itself, unless redefined, it is a no-op. > .de @FS > .FS-MARK \\$@ > .ie \\n[.$] .fn*do-FS "\\$1" no > .el \{\ > . ie \\n[fn*text-num]>\\n[fn*note-num] .fn*do-FS \\n+[fn*note-num] > . el .fn*do-FS > .\} > .. > .\" Default no-op fallback for FS-MARK > .de FS-MARK > .. > .\" Second argument of 'no' means don't embellish the first argument. > .de fn*do-FS > .if \\n[fn*open] .@error-recover nested .FS > .nr fn*open 1 > .if \\n[.u] \{\ > . \" Ensure that the first line of the footnote is on the same page > . \" as the reference. I think this is minimal. > . ev fn > . nr fn*need 1v > . ev > . ie \\n[pg@fn-flag] .nr fn*need +\\n[fn:PD] > . el .nr fn*need +\\n[fn@sep-dist] > . ne \\n[fn*need]u+\\n[.V]u>?0 > .\} > .ev fn > .par@reset-env > .fn*start-div > .par@reset > .fam \\*[fn@font-family] > .ie \\n[.$] .FP \\$@ > .el .@LP > .. > .de @FE > .ie !\\n[fn*open] .@error .FE without .FS > .el \{\ > . nr fn*open 0 > . br > . ev > . fn*end-div > .\} > .. > .nr fn@have-overflow 0 > .\" called at the top of each column > .de fn@top-hook > .nr fn*max-width 0 > .nr fn*page-bottom-pos 0-\\n[FM]-\\n[pg@fn-bottom-margin] > .ch pg@bottom \\n[fn*page-bottom-pos]u > .if \\n[fn@have-overflow] \{\ > . nr fn@have-overflow 0 > . fn*start-div > . ev nf > . fn@overflow-div > . ev > . fn*end-div > .\} > .. > .\" This is called at the bottom of the column if pg@fn-flag is set. > .de fn@bottom-hook > .nr pg@fn-flag 0 > .nr fn@have-overflow 0 > .nr fn@bottom-pos \\n[.p]-\\n[FM]-\\n[pg@fn-bottom-margin]+\\n[.v] > .ev fn > .nr fn@bottom-pos -\\n[.v] > .ev > .ie \\n[nl]+\\n[fn@sep-dist]+\n[.V]>\\n[fn@bottom-pos] \{\ > . rn fn@div fn@overflow-div > . nr fn@have-overflow 1 > .\} > .el \{\ > . if \\n[pg@ncols]>1 \ > . if \\n[fn*max-width]>\\n[pg@fn-colw] \ > . nr pg@fn-bottom-margin \\n[.p]-\\n[FM]-\\n[nl]+1v > . wh \\n[fn@bottom-pos]u fn*catch-overflow > . fn@print-sep > . ev nf > . fn@div > . rm fn@div > . ev > . if '\\n(.z'fn@overflow-div' \{\ > . di > . nr fn@have-overflow \\n[dn]>0 > . \} > . ch fn*catch-overflow > .\} > .. > .de fn*catch-overflow > .di fn@overflow-div > .. > .nr fn*embed-count 0 > .de @div-end!fn@div > .br > .if '\\n[.ev]'fn' .ev > .fn*end-div > .nr fn*open 0 > .. > .als @div-end!fn*embed-div @div-end!fn@div > .de fn*start-div > .ie '\\n(.z'' \{\ > . da fn@div > . if !\\n[pg@fn-flag] .ns > .\} > .el .di fn*embed-div > .. > .de fn*end-div > .ie '\\n(.z'fn@div' \{\ > . di > . nr fn*page-bottom-pos -\\n[dn] > . nr fn*max-width \\n[fn*max-width]>?\\n[dl] > . if !\\n[pg@fn-flag] .nr fn*page-bottom-pos -\\n[fn@sep-dist] > . nr pg@fn-flag 1 > . nr fn*page-bottom-pos \\n[nl]-\\n[.p]+\n[.V]>?\\n[fn*page-bottom-pos] > . ch pg@bottom \\n[fn*page-bottom-pos]u > .\} > .el \{\ > . ie '\\n(.z'fn*embed-div' \{\ > . di > . rn fn*embed-div fn*embed-div!\\n[fn*embed-count] > \!. fn*embed-start \\n[fn*embed-count] > . rs > ' sp (u;\\n[dn]+\\n[fn@sep-dist]+\\n[.V]) > \!. fn*embed-end > . nr fn*embed-count +1 > . \} > . el \{\ > . ev fn > . @error-recover unclosed diversion within footnote > . \} > .\} > .. > .de fn*embed-start > .ie '\\n(.z'' \{\ > . fn*start-div > . ev nf > . fn*embed-div!\\$1 > . rm fn*embed-div!\\$1 > . ev > . fn*end-div > . di fn*null > .\} > .el \{\ > \!. fn*embed-start \\$1 > . rs > .\} > .. > .de fn*embed-end > .ie '\\n(.z'fn*null' \{\ > . di > . rm fn*null > .\} > .el \!.fn*embed-end > .. > .\" It's important that fn@print-sep use up exactly fn@sep-dist vertical > .\" space. > .de fn@print-sep > .ev fn > .in 0 > .vs \\n[fn@sep-dist]u > \D'l 1i 0' > .br > .ev > .. > .\" *************************** > .\" ******** module kp ******** > .\" *************************** > .\" Keeps. > .de KS > .br > .di kp@div > .. > .de KF > .if !'\\n(.z'' .@error-recover .KF while diversion open > .di kp@fdiv > .ev k > .if \\n[@is-initialized] \{\ > . par@reset-env > . par@reset > .\} > .. > .de KE > .nr kp*did-closure-succeed 0 > .if '\\n(.z'kp@div' .kp*end > .if '\\n(.z'kp@fdiv' .kp*fend > .if !\\n[kp*did-closure-succeed] .@error .KE without .KS or .KF > .rr kp*did-closure-succeed > .. > .de @div-end!kp@div > .kp*end > .. > .de @div-end!kp@fdiv > .kp*fend > .. > .de kp*need > .ie '\\n(.z'' .ds@need \\$1 > .el \!.kp*need \\$1 > .. > .\" end non-floating keep > .de kp*end > .br > .di > .kp*need \\n[dn] > .ev nf > .kp@div > .ev > .rm kp@div > .nr kp*did-closure-succeed 1 > .. > .\" Floating keeps. > .nr kp@head 0 > .nr kp@tail 0 > .\" end floating keep > .de kp*fend > .br > .ev > .di > .ie \\n[.t]-(\\n[.k]>0*1v)>\\n[dn] \{\ > . br > . ev nf > . kp@fdiv > . rm kp@fdiv > . ev > .\} > .el \{\ > . rn kp@fdiv kp@div!\\n[kp@tail] > . nr kp*ht!\\n[kp@tail] 0\\n[dn] > . nr kp@tail +1 > .\} > .nr kp*did-closure-succeed 1 > .. > .\" top of page processing for KF > .nr kp*doing-top 0 > .de kp@top-hook > .if !\\n[kp*doing-top] \{\ > . nr kp*doing-top 1 > . kp*do-top > . nr kp*doing-top 0 > .\} > .. > .de kp*do-top > .\" If the first keep won't fit, only force it out if we haven't had a > .\" footnote and we're at the top of the page. > .nr kp*force \\n[pg@fn-flag]=0&(\\n[nl]<=\\n[pg@header-bottom]) > .nr kp*fits 1 > .while \\n[kp@tail]>\\n[kp@head]&\\n[kp*fits] \{\ > . ie \\n[.t]>\\n[kp*ht!\\n[kp@head]]:\\n[kp*force] \{\ > . nr kp*force 0 > . \" It's important to advance kp@head before bringing > . \" back the keep, so that if the last line of the > . \" last keep springs the bottom of page trap, a new > . \" page will not be started unnecessarily. > . rn kp@div!\\n[kp@head] kp*temp > . nr kp@head +1 > . ev nf > . kp*temp > . ev > . rm kp*temp > . \} > . el .nr kp*fits 0 > .\} > .. > .\" *************************** > .\" ******** module ds ******** > .\" *************************** > .\" Displays and non-floating keeps. > .de DE > .ds*end!\\n[\\n[.ev]:ds-type] > .nr \\n[.ev]:ds-type 0 > .ns > .. > .de ds@auto-end > .if \\n[\\n[.ev]:ds-type] \{\ > . @error automatically terminating display > . DE > .\} > .. > .de @div-end!ds*div > .ie \\n[\\n[.ev]:ds-type] .DE > .el .ds*end!2 > .. > .de ds*end!0 > .@error .DE without .DS, .ID, .CD, .LD, .RD, or .BD > .. > .de LD > .br > .nr \\n[.ev]:ds-type 1 > .par@reset > .nf > .sp \\n[DD]u > .. > .de ID > .LD > .ie \\n[.$] .in +(n;\\$1) > .el .in +\\n[DI]u > .. > .de CD > .LD > .ce \\n[.R] > .. > .de RD > .LD > .rj \\n[.R] > .. > .de ds*common-end > .par@reset > .sp \\n[DD]u > .. > .als ds*end!1 ds*common-end > .de BD > .LD > .nr \\n[.ev]:ds-type 2 > .di ds*div > .. > .de ds*end!2 > .br > .ie '\\n(.z'ds*div' \{\ > . di > . nf > . in (u;\\n[.l]-\\n[dl]/2>?0) > . ds*div > . rm ds*div > . ds*common-end > .\} > .el .@error-recover mismatched .DE > .. > .de DS > .\" Break to ensure that we spring the top-of-page trap (at the start of > .\" a document, this also ensures that important registers like `PS` are > .\" set). Also, even if the drawing position is elsewhere and invalid > .\" nesting occurs, the user will still expect a break. > .br > .if '\\n(.z'ds*div' .@error-recover cannot begin display within display > .nr ds*badarg 0 > .di ds*div > .ie '\\$1'B' \{\ > . LD > . nr \\n[.ev]:ds-type 4 > .\} > .el \{\ > . ie '\\$1'L' .LD > . el \{\ > . ie '\\$1'C' .CD > . el \{\ > . ie '\\$1'R' .RD > . el \{\ > . ie '\\$1'I' .ID \\$2 > . el \{\ > . ie '\\$1'' .ID > . el .nr ds*badarg 1 > . \} > . \} > . \} > . \} > . ie \\n[ds*badarg] \{\ > . ds ds*msg unrecognized argument '\\$1' to .\\$0;\" > . as ds*msg " did you mean '.\\$0 I \\$1'?\" > . @error \\*[ds*msg] > . rm ds*msg > . di > . \} > . el .nr \\n[.ev]:ds-type 3 > .\} > .rr ds*badarg > .. > .de ds@need > .if '\\n(.z'' \{\ > . while \\n[.t]<=(\\$1)&(\\n[nl]>\\n[pg@header-bottom]) \{\ > . rs > ' sp \\n[.t]u > . \} > .\} > .. > .de ds*end!3 > .br > .ie '\\n(.z'ds*div' \{\ > . di > . ds@need \\n[dn] > . ev nf > . ds*div > . ev > . rm ds*div > . ds*common-end > .\} > .el .@error-recover mismatched .DE > .. > .de ds*end!4 > .ie '\\n(.z'ds*div' \{\ > . br > . di > . nf > . in (u;\\n[.l]-\\n[dl]/2>?0) > . ds@need \\n[dn] > . ds*div > . rm ds*div > . ds*common-end > .\} > .el .@error-recover mismatched .DE > .. > .\" **************************** > .\" ******** module par ******** > .\" **************************** > .\" Paragraph-level formatting. > .\" Load time initialization. > .de par@load-init > .\" PS and VS might have been set on the command line > .if !rPS .nr PS 10 > .if !rLL .nr LL 6.5i > .ll \\n[LL]u > .\" don't set LT so that it can be defaulted from LL > .ie rLT .lt \\n[LT]u > .el .lt \\n[LL]u > .ie (\\n[PS] >= 1000) \ > . ps (\\n[PS]z / 1000u) > .el \ > . ps \\n[PS] > .\" don't set VS so that it can be defaulted from PS > .\" Set vertical spacing defaults to 120% of type size. > .ie rVS \{\ > . ie (\\n[VS] >= 1000) \ > . par*vs "(\\n[VS]p / 1000u)" > . el \ > . par*vs \\n[VS] > .\} > .el \{\ > . ie (\\n[PS] >= 1000) \ > . par*vs "((\\n[PS]p / 1000u) * 120u / 100u)" > . el \ > . par*vs "(\\n[PS] * 120u / 100u)" > .\} > .if dFAM .fam \\*[FAM] > .if !rHY .nr HY 6 > .hy \\n[HY] > .TA > .CHECK-FOOTER-AND-KEEP > .. > .de par*vs > .\" If it's too big to be in points, treat it as units. > .ie (p;\\$1)>=40p .vs (u;\\$1) > .el .vs (p;\\$1) > .. > .de par@ab-indent > .nr 0:li (u;\\n[LL]/12) > .nr 0:ri \\n[0:li] > .. > .de par*env-init > .aln \\n[.ev]:PS PS > .aln \\n[.ev]:VS VS > .aln \\n[.ev]:LL LL > .aln \\n[.ev]:MCLL LL > .aln \\n[.ev]:LT LT > .aln \\n[.ev]:MCLT LT > .aln \\n[.ev]:PI PI > .aln \\n[.ev]:PD PD > .ad \\n[par*adj] > .par@reset-env > .. > .\" happens when the first page begins > .de par@init > .if !rLT .nr LT \\n[LL] > .if !rFL .nr FL \\n[LL]*\\*[FR] > .\" Set vertical spacing defaults to 120% of type size. > .if !rVS \{\ > . ie (\\n[PS] >= 1000) \ > . nr VS (\\n[PS]p / 1000u * 120u / 100u) > . el \ > . nr VS (\\n[PS] * 120u / 100u) > .\} > .if !rDI .nr DI .5i > .if !rFPS \{\ > . ie (\\n[PS] >= 1000) \ > . nr FPS (\\n[PS] - 2000) > . el \ > . nr FPS (\\n[PS] - 2) > .\} > .if !rFVS \{\ > . ie (\\n[FPS] >= 1000) \ > . nr FVS (\\n[FPS]p / 1000u * 120u / 100u) > . el \ > . nr FVS (\\n[FPS] * 120u / 100u) > .\} > .\" don't change environment 0 > .ev h > .ie (\\n[PS] >= 1000) \ > . ps (\\n[PS]z / 1000u) > .el \ > . ps \\n[PS] > .if !rQI .nr QI 5n > .if !rPI .nr PI 5n > .ie (\\n[VS] >= 1000) \ > . par*vs "(\\n[VS]p / 1000u)" > .el \ > . par*vs \\n[VS] > .if !rPD .nr PD .3v>?\n(.V > .if !rDD .nr DD .5v>?\n(.V > .if !rFI .nr FI 2n > .if !rFPD .nr FPD \\n[PD]/2 > .ev > .if !dFAM .ds FAM \\n[.fam] > .ds pg@titles-font-family \\*[FAM] > .ds fn@font-family \\*[FAM] > .nr par*adj \\n[.j] > .par*env-init > .ev h > .par*env-init > .ev > .ev fn > .par*env-init > .ev > .ev k > .par*env-init > .ev > .aln 0:MCLL pg@colw > .aln 0:MCLT pg@colw > .aln k:MCLL pg@colw > .aln k:MCLT pg@colw > .aln fn:PS FPS > .aln fn:VS FVS > .aln fn:LL FL > .aln fn:LT FL > .aln fn:PI FI > .aln fn:PD FPD > .aln fn:MCLL pg@fn-colw > .aln fn:MCLT pg@fn-colw > .. > .de par@reset-env > .nr \\n[.ev]:il 0 > .nr \\n[.ev]:li 0 > .nr \\n[.ev]:ri 0 > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .nr \\n[.ev]:pli 0 > .nr \\n[.ev]:pri 0 > .nr \\n[.ev]:ds-type 0 > .. > .\" par@reset > .de par@reset > .br > .if \\n[s@devtag-needs-end-of-title] .DEVTAG-EO-TL > .nr s@devtag-needs-end-of-title 0 > .if \\n[s@devtag-needs-end-of-heading] .DEVTAG-EO-H > .nr s@devtag-needs-end-of-heading 0 > .ce 0 > .rj 0 > .ul 0 > .fi > .ft 1 > .ie '\\n[.ev]'fn' .fam \\*[fn@font-family] > .el .fam \\*[FAM] > .ie (\\n[\\n[.ev]:PS] >= 1000) \ > . ps (\\n[\\n[.ev]:PS]z / 1000u) > .el \ > . ps \\n[\\n[.ev]:PS] > .ie (\\n[\\n[.ev]:VS] >= 1000) \ > . par*vs "(\\n[\\n[.ev]:VS]p / 1000u)" > .el \ > . par*vs \\n[\\n[.ev]:VS] > .ls 1 > .if !'\\$1'' .nr \\n[.ev]:pli (n;\\$1) > .if !'\\$2'' .nr \\n[.ev]:pri (n;\\$2) > .ie \\n[pg@ncols]>1 \{\ > . ll (u;\\n[\\n[.ev]:MCLL]-\\n[\\n[.ev]:ri]-\\n[\\n[.ev]:pri]) > . lt \\n[\\n[.ev]:MCLT]u > .\} > .el \{\ > . ll (u;\\n[\\n[.ev]:LL]-\\n[\\n[.ev]:ri]-\\n[\\n[.ev]:pri]) > . lt \\n[\\n[.ev]:LT]u > .\} > .in (u;\\n[\\n[.ev]:li]+\\n[\\n[.ev]:pli]) > .TA > .hy \\n[HY] > .. > .\" This can be redefined by the user. > .de TA > .ta T 5n > .. > .\" \n[PORPHANS] sets number of initial lines of any paragraph, > .\" which must be kept together, without any included page break. > .\" Initialise to reproduce original behaviour; user may adjust it. > .if !rPORPHANS .nr PORPHANS 1 > . > .de par*start > .ds@auto-end > .par@reset \\$1 \\$2 > .sp \\n[\\n[.ev]:PD]u > .ne \\n[PORPHANS]v+\\n(.Vu > .. > .de par@finish > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .nr \\n[.ev]:pli 0 > .nr \\n[.ev]:pri 0 > .par@reset 0 0 > .. > .als @RT par@finish > .\" normal LP > .de @LP > .par*start 0 0 > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .. > .de @PP > .par*start 0 0 > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .if !'\*(.T'html' .ti +\\n[\\n[.ev]:ai]u > .. > .de @QP > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .par*start \\n[QI]u \\n[QI]u > .. > .de @XP > .par*start \\n[\\n[.ev]:PI]u 0 > .ti -\\n[\\n[.ev]:PI]u > .. > .de @IP > .ie \\n[.$]>1 \{\ > .par*start \\$2 0 > .nr \\n[.ev]:ai \\n[\\n[.ev]:pli] > .\} > .el .par*start \\n[\\n[.ev]:ai]u 0 > .if !'\\$1'' \{\ > . \" Divert the label so as to freeze any spaces. > . di par*label > . par*push-tag-env > \&\\$1 > . par*pop-tag-env > . di > . chop par*label > . ti -\\n[\\n[.ev]:ai]u > . ie \\n[dl]+1n<=\\n[\\n[.ev]:ai] \{\ > . DEVTAG-COL 1 > \\*[par*label]\h'|\\n[\\n[.ev]:ai]u'\c > . DEVTAG-COL 2 > . \} > . el \{\ > . DEVTAG-COL 1 > \\*[par*label] > . DEVTAG-COL-NEXT 2 > . br > . \} > . rm par*label > .\} > .. > .\" We don't want margin characters to be attached when we divert > .\" the tag. Since there's no way to save and restore the current > .\" margin character, we have to switch to a new environment, taking > .\" what we need of the old environment with us. > .de par*push-tag-env > .nr par*saved-font \\n[.f] > .nr par*saved-size \\n[.s]z > .nr par*saved-ss \\n[.ss] > .nr par*saved-sss \\n[.sss] > .ds par*saved-fam \\n[.fam] > .ev par > .nf > .TA > .ft \\n[par*saved-font] > .ps \\n[par*saved-size]u > .ss \\n[par*saved-ss] \\n[par*saved-sss] > .fam \\*[par*saved-fam] > .. > .de par*pop-tag-env > .ev > .. > .de @RS > .br > .nr \\n[.ev]:li!\\n[\\n[.ev]:il] \\n[\\n[.ev]:li] > .nr \\n[.ev]:ri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ri] > .nr \\n[.ev]:ai!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ai] > .nr \\n[.ev]:pli!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pli] > .nr \\n[.ev]:pri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pri] > .nr \\n[.ev]:il +1 > .nr \\n[.ev]:li +\\n[\\n[.ev]:ai] > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .par@reset > .. > .de @RE > .br > .ie \\n[\\n[.ev]:il] \{\ > . nr \\n[.ev]:il -1 > . nr \\n[.ev]:ai \\n[\\n[.ev]:ai!\\n[\\n[.ev]:il]] > . nr \\n[.ev]:li \\n[\\n[.ev]:li!\\n[\\n[.ev]:il]] > . nr \\n[.ev]:ri \\n[\\n[.ev]:ri!\\n[\\n[.ev]:il]] > . nr \\n[.ev]:pli \\n[\\n[.ev]:pli!\\n[\\n[.ev]:il]] > . nr \\n[.ev]:pri \\n[\\n[.ev]:pri!\\n[\\n[.ev]:il]] > .\} > .el .@error unbalanced .\\$0 > .ie '\\$0'QE' .par*start 0 0 > .el .par@reset > .. > .de @QS > .br > .nr \\n[.ev]:li!\\n[\\n[.ev]:il] \\n[\\n[.ev]:li] > .nr \\n[.ev]:ri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ri] > .nr \\n[.ev]:ai!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ai] > .nr \\n[.ev]:pli!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pli] > .nr \\n[.ev]:pri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pri] > .nr \\n[.ev]:il +1 > .nr \\n[.ev]:li +\\n[QI] > .nr \\n[.ev]:ri +\\n[QI] > .nr \\n[.ev]:ai \\n[\\n[.ev]:PI] > .par*start 0 0 > .. > .als @QE @RE > .\" start boxed text > .de B1 > .br > .HTML-IMAGE > .di par*box-div > .nr \\n[.ev]:li +1n > .nr \\n[.ev]:ri +1n > .nr par*box-in \\n[.in] > .\" remember what 1n is, just in case the type size changes > .nr par*box-n 1n > .in +1n > .ll -1n > .lt -1n > .ti \\n[par*box-in]u+1n > .. > .de @div-end!par*box-div > .B2 > .. > .\" end boxed text > .\" Postpone the drawing of the box until we're in the top-level > .\" diversion, in case there's a footnote inside the box. > .de B2 > .ie '\\n(.z'par*box-div' \{\ > . br > . \" In nroff mode, make room for the horizontal rules taking up > . \" 1v each. (Why aren't we actually testing nroff mode? --GBR) > . if \n[.V]>.25m .sp > . di > . if \n[.V]>.25m .sp > . ds@need \\n[dn] > . par*box-mark-top > . ev nf > . par*box-div > . ev > . nr \\n[.ev]:ri -\\n[par*box-n] > . nr \\n[.ev]:li -\\n[par*box-n] > . in -\\n[par*box-n]u > . ll +\\n[par*box-n]u > . lt +\\n[par*box-n]u > . par*box-draw \\n[.i]u \\n[.l]u-(\\n[.H]u==1n*1n) > .\} > .el .@error .B2 without .B1 > .HTML-IMAGE-END > .. > .de par*box-mark-top > .ie '\\n[.z]'' \{\ > . rs > . mk par*box-top > .\} > .el \!.par*box-mark-top > .. > .de par*box-draw > .ie '\\n[.z]'' \{\ > . nr par*box-in \\n[.i] > . nr par*box-ll \\n[.l] > . nr par*box-vpt \\n[.vpt] > . nr par*box-ad \\n[.j] > . ad l > . vpt 0 > . in \\$1 > . ll \\$2 > \v'-1v+.25m'\ > \D'l (u;\\n[.l]-\\n[.i]) 0'\ > \D'l 0 |\\n[par*box-top]u'\ > \D'l -(u;\\n[.l]-\\n[.i]) 0'\ > \D'l 0 -|\\n[par*box-top]u' > . br > . sp -1 > . in \\n[par*box-in]u > . ll \\n[par*box-ll]u > . vpt \\n[par*box-vpt] > . ad \\n[par*box-ad] > .\} > .el \!.par*box-draw \\$1 \\$2 > .. > .\" \n[HORPHANS] sets how many lines of the following paragraph must be > .\" kept together, with a preceding section heading. Initialise it, > .\" to reproduce original behaviour; user may change it. > .if !rHORPHANS .nr HORPHANS 1 > . > .\" \n[GROWPS] and \n[PSINCR] cause auto-increment of heading type size. > .\" Initialise them, so they have no effect, unless explicitly set by > .\" the user. > .if !rGROWPS .nr GROWPS 0 > .if !rPSINCR .nr PSINCR 1p > . > .de SH-NO-TAG > .par@finish > .\" Keep the heading and the first few lines of the next paragraph > .\" together. (\n[HORPHANS] defines "first few" -- default = 1; user > .\" may redefine it). > .nr sh*minvs \\n[HORPHANS]v > .if \\n[sh*psincr]<0 .nr sh*psincr 0 > .ie \\n(VS<1000 .par*vs \\n(VSp+\\n[sh*psincr]u > .el .par*vs \\n(VSp/1000u+\\n[sh*psincr]u > .ne 2v+\\n[sh*minvs]u+\\n[\\n[.ev]:PD]u+\\n(.Vu > .\" Adjust type size for heading text, as specified by \n[GROWPS] and > .\" \n[PSINCR]. > .ie \\n(PS<1000 .ps \\n(PS+\\n[sh*psincr]u > .el .ps \\n(PSz/1000u+\\n[sh*psincr]u > .sp 1 > .ft B > .. > .de @SH > .\" AT&T ms implementation does not expect an argument, but groff ms > .\" allows ".SH n" to make a heading's type size match ".NH n", for same > .\" "n", when \n[GROWPS] and \n[PSINCR] are set. > . nr sh*psincr 0 > . if 0\\$1>0 .nr sh*psincr (\\n[GROWPS]-0\\$1)*\\n[PSINCR] > . SH-NO-TAG > . DEVTAG-SH 1 > . if '\*(.T'html' .nr s@devtag-needs-end-of-heading 1 > .if \\n[PDFFEAT]>0 \{\ > . ds nh*bm sh:bm\\n+[shct] > . if \\n[.$]>1 .ds nh*bm \\$2 > . box @NHpdf > . dt .1v @NH-end > .\} > .. > .\" TL, AU, and AI are aliased to these in cov*ab-init. > .de par@TL > .par@finish > .sp 1 > .ft B > .ps +2 > .vs +3p > .ce \\n[.R] > .DEVTAG-TL > .nr s@devtag-needs-end-of-title 1 > .. > .de par@AU > .par@finish > .sp 1 > .ft I > .ce \\n[.R] > .. > .de par@AI > .par@finish > .sp .5 > .ce \\n[.R] > .. > .\" In paragraph macros. > .de NL > .if \En[.$] .@warning arguments to .NL ignored > .ie (\\n[\\n[.ev]:PS] >= 1000) \ > . ps (\\n[\\n[.ev]:PS]z / 1000u) > .el \ > . ps \\n[\\n[.ev]:PS] > .. > .de SM > .if \En[.$] .@warning arguments to .SM ignored > .ps -2 > .. > .de LG > .if \En[.$] .@warning arguments to .LG ignored > .ps +2 > .. > .\" par*define-font-macro macro font apply-italic-corrections > .de par*define-font-macro > .de \\$1 > .ds par*lic \" empty > .ds par*ic \" empty > .if \\n[.$]>2 \{\ > . as par*lic \,\" > . as par*ic \/\" > .\} > .if \En[.$]>3 .@warning excess arguments to .\\$1 ignored > .ie \En[.$] \{\ > . nr par*prev-font \En[.f] > \&\E$3\E*[par*lic]\f[\\$2]\E$1\f[\En[par*prev-font]]\E*[par*ic]\E$2 > .\} > .el .ft \\$2 > \\.. > .. > .par*define-font-macro R R > .par*define-font-macro B B > .par*define-font-macro I I yes > .par*define-font-macro BI BI yes > .ie n .par*define-font-macro CW R > .el .par*define-font-macro CW CR > .\" underline a word > .de UL > .if \En[.$]>2 .@warning excess arguments to .UL ignored > . ie t .do nop \Z'\\$1'\v'.25m'\D'l \w'\\$1'u 0'\v'-.25m'\\$2 > . el \(ul\\$1\(ul\\$2 > .. > .\" box a word > .de BX > .if \En[.$]>1 .@warning excess arguments to .BX ignored > .nr par*bxw \w'\\$1' > .ie t \{\ > .nr par*bxw +.4m > \Z'\v'.25m'\ > \D'l 0 -1m'\D'l \\n[par*bxw]u 0'\D'l 0 1m'\D'l -\\n[par*bxw]u 0''\ > \Z'\h'.2m'\\$1'\h'\\n[par*bxw]u' > .\} > .el \m[black]\M[white]\Z'\\$1'\h'\\n[par*bxw]u'\m[]\M[] > .. > .\" superscript > .ds par@sup-start \v'-.9m\s'\En[.s]*7u/10u'+.7m' > .als { par@sup-start > .ds par@sup-end \v'-.7m\s0+.9m' > .als } par@sup-end > .\" subscript > .ds par@sub-start \v'+.3m\s'\En[.s]*7u/10u'-.1m' > .als < par@sub-start > .ds par@sub-end \v'+.1m\s0-.3m' > .als > par@sub-end > .\" footnote paragraphs > .fn@init > .\" FR is the ratio of footnote (horizontal) length to the line length > .ds FR 11/12 > .\" FF is the footnote format > .nr FF 0 > .\" This can be redefined. It gets a second argument of 'no' if the > .\" first argument was supplied by the user, rather than automatically. > .de FP > .br > .if !d par*fp!\\n[FF] \{\ > . @error unknown footnote format '\\n[FF]' > . nr FF 0 > .\} > .ie '\\$2'no' .par*fp!\\n[FF]-no "\\$1" > .el .par*fp!\\n[FF] "\\$1" > .. > .de par*fp!0 > .@PP > \&\\*[fn@mark-start]\\$1\\*[fn@mark-end]\ \c > .. > .de par*fp!0-no > .@PP > \&\\$1\ \c > .. > .de par*fp!1 > .@PP > \&\\$1.\ \c > .. > .de par*fp!1-no > .@PP > \&\\$1\ \c > .. > .de par*fp!2 > .@LP > \&\\$1.\ \c > .. > .de par*fp!2-no > .@LP > \&\\$1\ \c > .. > .de par*fp!3 > .@IP "\\$1." (u;\\n[\\n[.ev]:PI]*2) > .. > .de par*fp!3-no > .@IP "\\$1" (u;\\n[\\n[.ev]:PI]*2) > .. > .\" *************************** > .\" ******** module nh ******** > .\" *************************** > .\" Numbered headings. > .\" nh*hl is the level of the last heading > .nr nh*hl 0 > .\" SN-DOT and SN-NO-DOT represent the section number of > .\" the current heading, with and without a terminating dot. > .ds SN-DOT > .ds SN-NO-DOT > .\" SN-STYLE sets the statement numbering style used in headings > .\" (either SN-DOT or SN-NO-DOT); for backward compatibility with > .\" earlier ms versions, the default is SN-DOT > .als SN-STYLE SN-DOT > .\" Also for backward compatibility, let SN represent SN-DOT. > .als SN SN-DOT > .\" numbered heading > .de @NH > .ie '\\$1'S' \{\ > . shift > . nr nh*hl 0 > . while \\n[.$] \{\ > . nr nh*hl +1 > . nr H\\n[nh*hl] 0\\$1 > . shift > . \} > . if !\\n[nh*hl] \{\ > . nr H1 1 > . nr nh*hl 1 > . @error missing arguments to .NH S > . \} > .\} > .el \{\ > . nr nh*ohl \\n[nh*hl] > . ie \\n[.$] \{\ > . nr nh*hl 0\\$1 > . ie \\n[nh*hl]<=0 \{\ > . nr nh*ohl 0 > . nr nh*hl 1 > . \} > . el \{\ > . if \\n[nh*hl]-\\n[nh*ohl]>1 \{\ > . ds nh*msg .NH \\n[nh*ohl] followed by\" > . as nh*msg " .NH \\n[nh*hl]\" > . @warning \\*[nh*msg] > . rm nh*msg > . \} > . \} > . \} > . el .nr nh*hl 1 > . while \\n[nh*hl]>\\n[nh*ohl] \{\ > . nr nh*ohl +1 > . nr H\\n[nh*ohl] 0 > . \} > . nr H\\n[nh*hl] +1 > .\} > .ds SN-NO-DOT \\n(H1 > .nr nh*i 1 > .while \\n[nh*i]<\\n[nh*hl] \{\ > . nr nh*i +1 > . as SN-NO-DOT .\\n[H\\n[nh*i]] > .\} > .ds SN-DOT \\*[SN-NO-DOT]. > .nr sh*psincr (\\n[GROWPS]-\\n[nh*hl])*\\n[PSINCR] > .SH-NO-TAG > .DEVTAG-NH "\\$1" > .if '\*(.T'html' .nr s@devtag-needs-end-of-heading 1 > \\*[SN-STYLE] > .if \\n[PDFFEAT]>0 \{\ > . ds nh*bm nh:bm\\n+[nhct] > . if \\n[.$]>1 .ds nh*bm \\$2 > . box @NHpdf > . dt .1v @NH-end > .\} > .. > .de @NH-end > . fl > . box > . chop @NHpdf > \&\\*[@NHpdf] > . asciify @NHpdf > . pdfbookmark -T \\*[nh*bm] \\n[nh*hl] "\\*[@NHpdf]" > . rm @NHpdf > .. > .\" **************************** > .\" ******** module toc ******** > .\" **************************** > .\" Table of contents generation. > .de @XS > .da toc*div > .ev h > .ie \\n[.$] .XA "\\$1" > .el .XA > .. > .de @div-end!toc*div > .XE > .. > .de XA > .ie '\\n(.z'toc*div' \{\ > . if d toc*num .toc*end-entry > . ie \\n[.$] \{\ > . ie '\\$1'no' .ds toc*num \" empty > . el .ds toc*num "\\$1 > . \} > . el .ds toc*num \\n[%]\" > . br > . par@reset > . na > . ll -8n \" XXX: take TC-MARGIN into account? > . in (n;0\\$2) > . if \\n[PDFFEAT]>0 \{\ > . pdfhref L -S -D \\*[nh*bm] > . sp -1v > . \} > .\} > .el .@error .XA without .XS > .. > .de XE > .ie '\\n(.z'toc*div' \{\ > . if \\n[PDFFEAT]>0 .nop \X'pdf: markend'\m[default]\c > . if d toc*num .toc*end-entry > . ev > . di > .\} > .el .@error .XE without .XS > .. > .\" Rudimentary integration of TOC generation with SH and NH; > .\" (called by XH and XN respectively, to capture heading text > .\" for reuse as TOC entry); may be redefined, to achieve more > .\" sophisticated TOC layout effects. > .\" > .\" No-op initializers are called by XH and XN respectively, > .\" before XH-UPDATE-TOC is called; if XH-UPDATE-TOC has been > .\" redefined, then it may also be necessary to redefine either, > .\" or both of these, to perform any initialization specific > .\" to use after SH and NH respectively. > .de XH-INIT de > .de XN-INIT > .. > .de XH-UPDATE-TOC > .\" .XH-UPDATE-TOC <outline-level> <text> > .XS > .in (\\$1u - 1u * 2n) > .shift > \&\\$* > .XE > .. > .\" Rudimentary integration hook, to be called (nominally) > .\" after SH, but acceptable in any body-text context > .de XH de > .\" .XH <outline-level> <text> > .rn XH-REPLACEMENT XH > .XH \\$@ > .de XH-REPLACEMENT > .XH-INIT > .XH-UPDATE-TOC \\$@ > .shift > \&\\$* > .. > .\" Rudimentary integration hook, to be called after NH > .de XN de > .\" .XN <text> > .ie \\n[nh*hl] .toc*xn-init \\$@ > .el \{\ > . @error .XN is not allowed before .NH > . nop \&\\$* > .\} > .de toc*xn-init de > .rn XN-REPLACEMENT XN > .XN \\$@ > .rm \\$0 > .de XN-REPLACEMENT > .XN-INIT > .XH-UPDATE-TOC \\n[nh*hl] \\$@ > \&\\$* > .if \\n[PDFFEAT]>0 .sp > .. > .de toc*end-entry > .if !'\\*[toc*num]'' \\a\\t\\*[toc*num] > .br > .rm toc*num > .. > .de PX > .1C > .if !'\\$1'no' \{\ > . ce 1 > . ie (\\n[PS] >= 1000) \ > . ps ((\\n[PS]z / 1000u) + 2z) > . el \ > . ps \\n[PS]+2 > . ft B > \\*[TOC] > . ft > . ps > .\} > .nf > .if !r TC-MARGIN .nr TC-MARGIN \w'000' > .if !c \[TC-LEADER] .char \[TC-LEADER] .\h'1m' > .ta (u;\\n[.l]-\\n[.i]-\\n[TC-MARGIN]) (u;\\n[.l]-\\n[.i])R > .lc \[TC-LEADER] > .sp 2 > .toc*div > .par@reset > .. > .\" print the table of contents on page i > .de TC > . if \\n[PDFFEAT]=1 .if \\n[gottitle*pdf] .pdfswitchtopage after title > .P1 > .pg@begin 1 i > .PX \\$1 > . if \\n[PDFFEAT]=1 .pdfpagenumbering D . -0 > .. > .\" **************************** > .\" ******** module eqn ******** > .\" **************************** > .\" Eqn support. > .de EQ > .. > .de EN > .. > .de @EQ > .if \\n[tbl@within-table] \ > . @error .EQ is not allowed within a .TS/.TE table > .br > .nr eqn*type 0 > .ds eqn*num "\\$2 > .if '\\$1'L' .nr eqn*type 1 > .if '\\$1'I' .nr eqn*type 2 > .if '\\$1'C' .nr eqn*type 3 > .if !\\n[eqn*type] \{\ > . ds eqn*msg .EQ: unrecognized alignment '\\$1'; > . ie (\\n[.$] = 1) \{\ > . if !'\\$1'' \{\ > . as eqn*msg " assuming it is an equation label > . @warning \\*[eqn*msg] > . ds eqn*num "\\$1 > . \} > . \} > . el .if (\\n[.$] > 1) \{\ > . if !'\\$1'' \{\ > . as eqn*msg " centering equation > . @warning \\*[eqn*msg] > . \} > . \} > . rm eqn*msg > .\} > .di eqn*div > .in 0 > .if '\*(.T'html' \{\ > . nr eqn*ll \\n[.l] > . ll 1000n > .\} > .if \\n[eqn*type]=1 .EQN-HTML-IMAGE-LEFT > .if \\n[eqn*type]=2 \{\ > . if '\*(.T'html' .RS > .EQN-HTML-IMAGE-INLINE > .\} > .if \\n[eqn*type]=3 .EQN-HTML-IMAGE > .nf > .. > .de @div-end!eqn*div > .@EN > .. > .\" Note that geqn mark and lineup work correctly in centered equations. > .de @EN > .ie !'\\n(.z'eqn*div' .@error-recover mismatched .EN > .el \{\ > . br > . di > . nr eqn*have-num 0 > . if !'\\*[eqn*num]'' .nr eqn*have-num 1 > . ie \\n[dl]:\\n[eqn*have-num] \{\ > . sp \\n[DD]u > . ns > . par@reset > . ds eqn*tabs \\n[.tabs] > . nf > . ie \\n[dl] \{\ > .\" XXX: This really should not be necessary and > .\" indicates that there is extra space creeping > .\" into an equation when ps4html is enabled. > . ie r ps4html .ds@need \\n[dn]u-1v+\n[.V]u+1i > . el .ds@need \\n[dn]u-1v+\n[.V]u > . chop eqn*div > . ie \\n[eqn*type]=1 \{\ > . ta (u;\\n[.l]-\\n[.i])R > \\*[eqn*div]\t\\*[eqn*num] > . \} > . el \{\ > . ie \\n[eqn*type]=2 .ta \\n[DI]u \ > (u;\\n[.l]-\\n[.i])R > . el .ta (u;\\n[.l]-\\n[.i]/2)C \ > (u;\\n[.l]-\\n[.i])R > \t\\*[eqn*div]\t\\*[eqn*num] > . \} > . \} > . el \{\ > . ta (u;\\n[.l]-\\n[.i])R > \t\\*[eqn*num] > . \} > .\". if !'\*(.T'html' .sp \\n[DD]u > . sp \\n[DD]u > . ns > . ta \\*[eqn*tabs] > . \} > . el \{\ > .\" must terminate empty equations in html and ps4html as they contain > .\" the EQN-HTML-IMAGE-END suppression nodes > . if \\n[dl] .chop eqn*div > . if '\*(.T'html' \\*[eqn*div] > . if r ps4html \\*[eqn*div] > . \} > . if !'\*(.T'html' .fi > . if \\n[eqn*type]=1 .EQN-HTML-IMAGE-END > . if \\n[eqn*type]=2 \{\ > . EQN-HTML-IMAGE-END > . if '\*(.T'html' .RE > . \} > . if \\n[eqn*type]=3 .EQN-HTML-IMAGE-END > . if '\*(.T'html' \ > . ll \\n[eqn*ll]u > .\} > .. > . > .\" **************************** > .\" ******** module tbl ******** > .\" **************************** > .\" Tbl support. > .nr tbl@within-table 0 > .nr tbl*has-heading 0 > .nr tbl*was-tbl-failure-reported 0 > .de tbl*check-for-tbl > .if !r TW .if !\\n[tbl*was-tbl-failure-reported] \{\ > . ds tbl*err tbl preprocessor failed, or it or soelim was not > . as tbl*err " run; table(s) likely not rendered\" > . as tbl*err " (TE macro called with TW register undefined)\" > . @error \\*[tbl*err] > . rm tbl*err > . nr tbl*was-tbl-failure-reported 1 > .\} > .. > .\" This gets called if TS occurs before the first paragraph. > .de TS > .cov*ab-init > .\" cov*ab-init, called by LP, aliases TS to @TS. > \\*[TS]\\ > .. > .de @TS > .nr tbl@within-table 1 > .sp \\n[DD]u > .if '\\$1'H' \{\ > . ds tbl*stem .TS H table inside\" > . ie '\\n[.z]'kp@div' .@warning \\*[tbl*stem] .KS/.KE keep > . el .if '\\n[.z]'kp@fdiv' \ > . @warning \\*[tbl*stem] .KF/.KE floating keep > . rm tbl*stem > . di tbl*heading-diversion > .\} > .if '\*(.T'html' \{\ > . nr tbl*ll \\n[.l] > . ll 1000n > .\} > .HTML-IMAGE > .. > .de tbl@top-hook > .if \\n[tbl*has-heading] \{\ > . ie \\n[.t]-\\n[tbl*heading-height]-1v .tbl*print-heading > . el .sp \\n[.t]u > .\} > .. > .de tbl*print-heading > .ev nf > .tbl*heading-diversion > .ev > .mk #T > .. > .de TH > .ie '\\n[.z]'tbl*heading-diversion' \{\ > . nr T. 0 > . T# > . br > . di > . \" A table with repeating headings requires enough room for them > . \" and then at least one more vee for a row of data. > . ie \\n[dn]+1v>=(\\n[.p]-\\n[FM]-\\n[HM]) \{\ > . ds tbl*err .TH repeating table heading(s) do not fit in > . as tbl*err " page area; formatting only once > . @error \\*[tbl*err] > . rm tbl*err > . ds@need \\n[dn] > . tbl*print-heading > . \} > . el \{\ > . nr tbl*heading-height \\n[dn] > . ds@need \\n[dn]u+1v > . tbl*print-heading > . nr tbl*has-heading 1 > . \} > .\} > .el .@error-recover .TH without .TS H > .. > .de @div-end!tbl*heading-diversion > .TH > .TE > .. > .de TE > .tbl*check-for-tbl > .if !r TW .return > .ie '\\n(.z'tbl*heading-diversion' \ > . @error-recover .TS H but no .TH before .TE > .el \{\ > . nr tbl*has-heading 0 > . if !'\*(.T'html' \{\ > . sp \\n[DD]u > . ns > . \} > .\} > .HTML-IMAGE-END > .if '\*(.T'html' \ > . ll \\n[tbl*ll]u > .\" reset tabs > .TA > .nr tbl@within-table 0 > .. > .de tbl@bottom-hook > .if \\n[tbl*has-heading] \{\ > . nr T. 1 > . T# > .\} > .. > .de T& > .. > .\" **************************** > .\" ******** module pic ******** > .\" **************************** > .\" Pic support. > .\" This gets called if PS occurs before the first paragraph. > .de PS > .LP > .\" cov*ab-init, called by LP, aliases PS to @PS. > \\*[PS]\\ > .. > .\" @PS height width > .de @PS > .br > .sp \\n[DD]u > .ie \\n[.$]<2 \{\ > . ds pic*msg .PS: expected 2 arguments, got \\n[.$]\" > . as pic*msg ; not preprocessed with pic?\" > . @error \\*[pic*msg] > . rm pic*msg > .\} > .el \{\ > . ds@need (u;\\$1)+1v > . in +(u;\\n[.l]-\\n[.i]-\\$2/2>?0) > .\} > .HTML-IMAGE > .. > .de PF > .HTML-IMAGE-END > .par@reset > .. > .de PE > .PF > .sp \\n[DD]u+.5m > .ns > .. > .\" **************************** > .\" ******** module ref ******** > .\" **************************** > .\" Refer support. > .mso refer-ms.tmac > .\" **************************** > .\" ******** module acc ******** > .\" **************************** > .\" Accents and special characters. > .ds Q \(lq > .ds U \(rq > .ds - \(em > .\" Characters > .\" Accents > .de acc*over-def > .ds \\$1 \Z'\v'(u;\w'x'*0+\En[rst]-\En[.cht])'\ > \h'(u;-\En[skw]+(-\En[.w]-\w'\\$2'/2)+\En[.csk])'\\$2' > .. > .de acc*under-def > .ds \\$1 \Z'\v'\En[.cdp]u'\h'(u;-\En[.w]-\w'\\$2'/2)'\\$2' > .. > .de acc*slash-def > .ds \\$1 \Z'\h'(u;-\En[.w]-\w'\\$2'/2)'\ > \v'(u;\En[.cdp]-\En[.cht]+\En[rst]+\En[rsb]/2)'\\$2' > .. > .de acc*prefix-def > .ds \\$1 \Z'\h'(u;\w'x'-\w'\\$2'/2)'\\$2' > .. > .acc*prefix-def ' \' > .acc*prefix-def ` \` > .acc*prefix-def ^ ^ > .acc*prefix-def , \(ac > .acc*prefix-def : \(ad > .acc*prefix-def ~ ~ > .\" improved accent marks > .de AM > .acc*over-def ' \' > .acc*over-def ` \` > .acc*over-def ^ ^ > .acc*over-def ~ ~ > .acc*over-def : \(ad > .acc*over-def v \(ah > .acc*over-def _ \(a- > .acc*over-def o \(ao > .acc*under-def , \(ac > .acc*under-def . \s[\En[.s]*8u/10u]\v'.2m'.\v'-.2m'\s0 > .acc*under-def hook \(ho > .acc*slash-def / / > .char \[hooko] o\E*[hook] > .ds q \[hooko] > .\" The idea of this definition is for the top of the 3 to be at the > .\" x-height. > .if !c\[yogh] .char \[yogh] \Z'\v'\w'x'*0-\En[rst]u'\s[\En[.s]*8u/10u]\ > \v'\w'3'*0+\En[rst]u'3\s0'\h'\w'\s[\En[.s]*8u/10u]3'u' > .ds 3 \[yogh] > .ds D- \(-D\" Icelandic uppercase eth > .ds d- \(Sd\" Icelandic lowercase eth > .ds Th \(TP\" Icelandic uppercase thorn > .ds th \(Tp\" Icelandic lowercase thorn > .ds 8 \(ss\" German double s > .ds Ae \(AE\" AE ligature > .ds ae \(ae\" ae ligature > .ds Oe \(OE\" OE ligature > .ds oe \(oe\" oe ligature > .ds ? \(r?\" upside down ? > .ds ! \(r!\" upside down ! > .. > .de CHECK-FOOTER-AND-KEEP > .if '\*(.T'html' \{\ > . rm KF > . als KF KS > . > . rm FS > . de FS > . sp > . HTML-NS <cite> > \\.. > . rm FE > . de FE > . HTML-NS </cite> > . sp > \\.. > .\} > .. > .par@load-init > .\" Local Variables: > .\" mode: nroff > .\" fill-column: 72 > .\" End: > .\" vim: set filetype=groff textwidth=72: > .\" Run: pdfmom --roff -ms -msboxes -dpapersz=a4 -tU -P-pa4 > Groff-PDF-Features.ms > Groff-PDF-Features.pdf > .nr PS 11 > .nr VS 13 > .nr PO 2c > .nr LL 17c > .nr PL 29.7c > .nr HM 1.9c > .nr FM .3c > .nr QI 5n > .nr DI \n[QI] > .nr PDFOUTLINE.FOLDLEVEL 1 > .ds FR 1 > .nr TC-MARGIN \w'00' \" expect 2-digit page numbers at most > .ie t .nr PI 3.5n > .el .nr PI 4n > .ds date \*[MONTH\n[mo]] \n[year]\" > .ND \*[date] > .EH '%''\*[date]' > .EF '''' > .OH '\f[I]Groff\f[] PDF features''%' > .OF '''' > .de Cd > . ft CB > .. > .de CdX > . ft > .. > .de Qq > . nop \[lq]\\$1\[rq]\\$2 > .. > .\" Define a macro for code literals; use bold and disable hyphenation. > .de Lt > . ft CB > . nh > . nop \m[grey33]\s'-1p'\\$1\s'+1p'\m[]\c > . hy \\n[HY] > . ft > . nop \&\\$2 > .. > .de PN > . ev Pn > . evc 0 > . po .5c > . pdfnote \\$@ > . fl > . po > . ev 0 > .. > .ds = \f(CB\\$1\f(CR\\$4\f[CBI]\\$2\f(CR\\$3 > .RP no > .TL > .BI Groff > PDF features > .AU > Deri James > .AI > [email protected] > .AB no > .PDFPIC gnu.eps > .AE > .NH 1 intro > .XN Introduction > .LP > This is an attempt to draw together information gleaned from > .Lt pdf.tmac , > .Lt gropdf(1) , > and the various full service macros which manage pdf features. > .LP > Previously groff contained > .Lt pdfmark.tmac > and > .Lt spdf.tmac > (to integrate pdf features into the > .Lt ms > macros). Since these have now been dropped from groff and are now supported by > Keith Marshall at > .pdfhref W -D https://savannah.nongnu.org/projects/groff-pdfmark -A . this > site > .LP > PDF features can either be controlled at a low level, > using the macros defined in > .Lt pdf.tmac > or rely on these > macros being integrated into a full-service macro > package. Currently this is the state of play with regard > to full-service:- > .LP > .TS > allbox tab(^); > Cb Cb Cb Cb Cb Cb > Cf(CB) C C C C C. > Macro^Meta Data^Bookmarks^Internal Link^External Link^Named Destination > mom^\[OK]^\[OK]^\[OK]^\[OK]^\[OK] > man^^\[OK]^\[OK]\m[red]\**\m[]^\[OK]^^ > ms^\[OK]^\[OK]^\[OK]^\[OK]^\[OK] > me^^^^^ > mm^^^^^ > .TE > .LP > .FS > In a man page collection the MR macro can be used to link to other entries > in the collection, otherwise it is an external link. > .FE > .LP > The macros outlined below cannot be mix'n'matched between full service > macro sets, but the information given for > .Lt pdf.tmac > can be used in any document, particularly if a full macro set is missing > a particular facility. > .LP > .NH 1 meta > .XN Meta Data > .NH 2 info > .XN Document Info > .LP > A PDF document can contain meta data such as > .Qq "Created Date" , > .Qq Author , > .Qq Title , > etc.. > .NH 3 > .XN pdf.tmac > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED cornsilk3 INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > To enter pdf meta data use: > .QS > .Lt ".pdfinfo /<label>" > "\fItext\fP" > .QE > Where > .Lt /<label> > can be:- > .LP > .QS > .Lt /Title , > .Lt /Author , > .Lt /Subject , > .Lt /Keywords > .QE > And > .BI text > can be multi-line if you use:- > .QS > .Lt \[rs]*[PDFLB] > where you want the line-break. > .QE > These entries are stored in the pdf and can be seen if you run the > command:- > .QS > .Lt pdfinfo > \fIfilename\fP > .QE > Or are probably visible if you look at > .Qq "Document Properties" > in your favourite pdf viewer. > .LP > .BOXSTOP > .NH 3 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > In > .Lt mom > you can use:- > .QS > .Lt .DOCTITLE > .BI text > .LP > or more usually > .LP > .Lt .PDF_TITLE > .BI text > .QE > The text given to:- > .QS > .Lt .AUTHOR > .BI text > .QE > Will be added to the pdf meta data. > .BOXSTOP > .LP > .NH 3 > .XN ms > .BOXSTART SHADED honeydew OUTLINED green INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt ms > .BOXSTOP > .LP > .Lt .TL > and > .Lt .AU > Provide the Title and Author for the pdf. > .BOXSTOP > .LP > .NH 2 notes > .XN Annotation Notes > .LP > .PN -T "Deri James" Just an example > These are the clickable/hoverable icons which appear in the document which > can contain pop-up comments on the text. Only the > .Lt pdf.tmac > macros provide access to this facility. > .LP > .NH 3 > .XN pdf.tmac > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > .Lt .pdfnote > [ -T > .BI title > ] > .BI "text" > .LP > The popup note in the left margin was created with:- > .QS > .Lt \&.pdfnote \" > -T "Deri James" Just an example > .QE > The colour of the icon is set by setting the register > .Lt PDFNOTE.COLOUR > (or > .Lt PDFNOTE.COLOR ) > to three numbers each in the range 0.0 \[rA] 1.0 representing red, green, > blue values, so:- > .QS > \&.ds > .Lt PDFNOTE.COLOUR > 1.0 1.0 0.0 > .QE > Produces a yellow icon. The opacity of the icon is controlled by:- > .QS > \&.ds > .Lt PDFNOTE.OPACITY > 0.0 \[rA] 1.0 > .QE > Where the default value is 0.6. > .LP > Again you can use > .Lt \[rs]*[PDFLB] > in the > .BI text . > .BOXSTOP > .LP > .NH 1 bookmarks > .XN Bookmarks > .LP > For PDFs, bookmarks are the entries in the outline panel. Each bookmark > has a hierarchical level, and, optionally, can be > .Qq named > so that they can be linked to from elsewhere in the document > .XR naming ). ( > .LP > Bookmarks in the panel can be open or closed, setting:- > .QS > \&.ds > .Lt PDFOUTLINE.FOLDLEVEL > 1 > .QE > will > .Qq close > all bookmarks below the first level. The default is to open > all bookmarks. > .NH 2 > .XN pdf.tmac > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > .Lt pdf.tmac > defines this macro for bookmarks:- > .QS > .Lt .pdfbookmark > [\c > .Lt -T > .BI name ] > .BI level > .BI "descriptive text ..." > .QE > Where > .Qq level > is the mandatory nesting level for this > .Qq bookmark . > All the > .Qq "descriptive text" > form the entry in the outline panel. If the > .Lt -T > flag is given this becomes a > .Qq "named destination" > which can be linked to from elsewhere in the document > .ds here naming > .XR naming ). ( > .ds here after > .BOXSTOP > .ds here ne > .ne 5 > .ds here > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > mom uses the > .Lt .HEADING > macro to populate the outline panel:- > .QS > .Lt .HEADING > .BI level > [\c > .Lt NAMED > .BI name ] > .BI "descriptive text ..." > .QE > .Qq level , > .Qq name > and > .Qq "descriptive text" > have the same meanings as above. > .BOXSTOP > .LP > .NH 2 > .XN ms > .BOXSTART SHADED honeydew OUTLINED green INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt ms > .BOXSTOP > .LP > There are two separate ways of providing outline bookmarks in > .Lt ms . > First there is the traditional heading:- > .QS > .Lt .NH > .BI level > [\c > .BI name \m[red]\**\m[]] > .QE > .FS > If the > .Qq level > is > .Qq S > (a Berkeley extension) you cannot have a > .Qq named > bookmark. > .FE > This is then followed by input which is used as > .Qq "descriptive text" . > .LP > Although there could be several lines of input text, it is the > accumulated output line which is used as > .Qq "descriptive text" . > .LP > .pdfhref M -N hdexample ms Heading Example > So this input: > .ID > .ft CB > \&.NH 1 using > Using > \&.BI groff > with the > \&.BI ms > Macro Package > \&.XS > Using > \&.BI groff > with the > \&.BI ms > Macro Package > \&.XE > .ft > .DE > Would create a named (\c > .Qq using ) > level 1 bookmark > .Qq "Using \fIgroff\fP with the \fIms\fP Macro Package" , > and also create a > .Lt TOC > entry. > Note if the input text results in multiple output lines it is > only the first output line which is used as the bookmark text. > .LP > .pdfhref M -N XN Using .XN > The second method, which combined the > .Qq heading > with populating a > .Lt TOC , > is the > .Lt .XN > command which > .B must > immediately follow the > .Lt .NH > command:- > .QS > .Lt .XN > .BI "descriptive text" > .QE > Previously the > .Lt .NH > and following heading text had to be followed with > .Lt .XS/.XE > commands with the same text to separately populate the > .Lt TOC . > .LP > So the equivalent code to the above using > .Lt .XN > is:- > .ID > .ft CB > \&.NH 1 using > \&.XN Using \[rs]fIgroff\[rs]fP with the \[rs]fIms\[rs]fP Macro Package > .ft > .DE > In both cases the bookmark is named > .Qq using , > and clicking on the overview panel > or the hotspot link in the > .Lt TOC > will jump to the correct part of the document. > You can also use the > .XR XR "" "" ".XR command" > to jump to the section named > .Qq using :- > .QS > \&.XR using ). ( > .QE > to enter this hotlink > .Qq "(see: Using \fIgroff\fP with the \fIms\fP Macro Package)" . > .BOXSTOP > .bp > .NH 2 > .XN man > .LP > .BOXSTART SHADED linen OUTLINED maroon INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt man > .BOXSTOP > .LP > The > .QS > .Lt .TH > .BI identifier > .BI section > .QE > macro creates (via first call to > .Lt .PT ) > a named heading with name id of > .Qq identifier(section) . > It is normally given a level 1 bookmark, which can be controlled by setting > .Qq an*bookmark-base-level > on the command line to increase the level, > this can be used in man page collections where the pages > are split into separate sections. > .LP > The > .Lt .SH > and > .Lt .SS > macros create unnamed bookmarks at the appropriate level. > .BOXSTOP > .LP > .NH 1 naming > .XN Named Destination > .LP > In order to insert hotspot links which jump to other parts of the > document, the destination must be > .Qq named . > In the section on > .XR bookmarks "" "" bookmarks > it showed how they can be > .Qq named . > .LP > If you want to jump to a place in the document which has not got a bookmark, > you can name any particular place in the document, for example, a table or a > figure to which you may want to refer. See description of > .XR pdfhrefM . "" ".pdfhref M" > .NH 2 > .XN pdf.tmac > .LP > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > .pdfhref M -N pdfhrefM > This macro will plant a named destination without inserting a bookmark. > .QS > .Lt ".pdfhref M" > [\c > .Lt -N > .BI name ] > [\c > .Lt -E ] > .BI "descriptive text" ] [ > .QE > If no > .BI name > is specified, the first word of > .Qq "descriptive text" > is used as the > .Qq name , > which means that if the > .Lt -N > flag is not used then there must be at least one word in the text. > .LP > The > .Qq "descriptive text" , > if any, is saved as the > .Qq value > of the destination, and can be used when specifying a hotspot link > .XR hotspot ). ( > .LP > If the > .Lt -E > flag is used, the text is output to the document as well. > .BOXSTOP > .LP > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > The mom macro:- > .QS > .Lt .PDF_TARGET > .BI name > [\c > .BI "descriptive text" ] > .QE > You can now use > .BI name > as the destination of any hotspot link > .XR hotspot ). ( > .BOXSTOP > .LP > .bp > .NH 1 hotspot > .XN Internal Hotspot Links > .LP > An internal link is to a > .Qq "named destination" > within the document, > .XR external "" "" external > are links using a URI. > .LP > A hotspot link is a clickable piece of text which will cause the viewer > to jump to a > .XR naming . "" * > The colour of the hotspot is controlled by setting the register > .Lt PDFHREF.COLOUR > (or > .Lt PDFHREF.COLOR ) > to three numbers each in the range 0.0 \[rA] 1.0 representing red, green, > blue values, so:- > .QS > \&.ds > .Lt PDFHREF.COLOUR > 0.00 0.35 0.60 > .QE > Uses a turquoise text, which can be referenced as > .QS > .Lt \[rs]m[\[rs]*[PDFHREF.TEXT.COLOUR]] . > .QE > .ne 5 > Hotspots can have a border drawn around them, which is controlled by setting > the register > .Lt PDFHREF.BORDER > to an array of 3 numbers which represent \[en] horizontal corner radius, > vertical corner radius, and border width. A zero radius gives square (not > rounded) corners. The default is:- > .QS > \&.ds > .Lt PDFHREF.BORDER > 0 0 0 > .QE > Which produces no border around the hotspot. > .NH 2 > .XN pdf.tmac > .LP > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > To plant a hotspot link:- > .QS > .Lt ".pdfhref L " [\c > .Lt -D > .BI name ] > [\c > .Lt -P > .BI prefix-text ] > [\c > .Lt -A > .BI affixed-text ] > [\c > .Lt -- ] > [\c > .BI "hotlink text" ] > .QE > The target for the link is given by > .BI name > which should be a destination you have named > .XR naming ). ( > .LP > .pdfhref M -N prefix -- Prefix and Affix > The > .BI prefix-text > and > .BI affixed-text > are placed around the > .BI "hotlink text" . > .LP > If you used a > .BI "descriptive text" > when naming a destination, you can retrieve the value with this code:- > .ID > .ft CB > \&.pdf:lookup \c > .BI name > \&.ie !'\[rs]*[pdf:lookup-result]'' \\ > \&. ds desc-txt \&\[rs]*[pdf:lookup-value] > \&.el \\ > \&. ds desc-txt Unknown > .ft > .DE > .LP > .pdfhref M -N expandochars expando char > This technique is used by some of the full service macros to provide the > .Qq expando > characters > .Qq * > and > .Qq + \&. > When they are the final character in > .Qq "hotlink text" > the expando is replaced by > .BI desc-text , > surrounded by double quotes if the expando character is > .Qq + . > .BOXSTOP > .LP > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > mom uses this macro:- > .QS > .Lt .PDF_LINK > .BI name > [\c > .Lt PREFIX > .BI text ] > [\c > .Lt SUFFIX > .BI text ] > .BI "hotlink text" > .QE > If the > .BI "hotlink text" > is terminated with an > .XR expandochars "" "" * > it is replaced by the value of the > .BI "descriptive text" > when the target > .BI name > was created. > .BOXSTOP > .LP > .NH 2 hotspotms > .XN ms > .BOXSTART SHADED honeydew OUTLINED green INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt ms > .BOXSTOP > .LP > .pdfhref M XR > An addition to the ms macro set is the > .Lt .XR > command, originally found in some versions of Keith Marshall's > .Lt spdf.tmac , > our version now uses > .XR expandochars "" "" expandos > .QS > .Lt .XR > .BI name > [\c > .BI post > [\c > .BI pre > [\c > .BI "hotlink text" "] ] ]" > .QE > If > .BI "hotlink text" > is missing, the contents of > .Lt \[rs]*[spdf:txt_default] > is used instead. This defaults to > .Qq "see:\ +" " " > so, with the use of the terminating expando, the code > .QS > .Lt .XR > intro ), ( > .QE > would add a link > .XR intro ), ( > if the first section of this document was:- > .ID > \&.NH 1 intro > \&.XN Introduction > .DE > .BOXSTOP > .LP > .NH 2 > .XN man > .LP > .BOXSTART SHADED linen OUTLINED maroon INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt man > .BOXSTOP > .LP > The > .QS > .Lt .MR > .BI name > .BI section > .QE > macro normally creates an external link > .XR external ), ( > suitable for linking to > another man page, but if the other man page it is calling is part > of the same man page collection, it creates an internal link within > the document. > .BOXSTOP > .LP > .NH 1 external > .XN External Hotspot Links > .LP > External links are often links to resources on the internet such as website > URLs. > .NH 2 > .XN pdf.tmac > .LP > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > The macro to link to internet resources is:- > .QS > .Lt ".pdfhref W " [\c > .Lt -D > .BI URI ] > [\c > .Lt -P > .BI prefix-text ] > [\c > .Lt -A > .BI affixed-text ] > [\c > .Lt -- ] > [\c > .BI "hotlink text" ] > .QE > If a > .BI URI > is not specified the > .BI "hotlink text" > must be a valid > .BI URI > itself, and the complete > .BI URI > will be visible in the document. > If a > .BI URI > is given by specifying > .Lt -D > .BI URI > then only the > .BI "hotlink text" > will be visible, but clicking it will launch the > .BI URI . > .LP > The > .Lt -P > and > .Lt -A > flags operate in the same way as in > .Lt ".pdfhref L " > .XR prefix ). ( > .BOXSTOP > .LP > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > External links are supported by:- > .QS > .Lt .PDF_WWW_LINK > .BI URI > [\c > .Lt PREFIX > .BI text ] > [\c > .Lt SUFFIX > .BI text ] > .BI "hotlink text" > .QE > If no > .BI "hotlink text" > is given, the > .BI URI > is used as the > .BI "hotlink text" . > .XR expandochars "" "" Expandos > can be used as the last character of > .BI "hotlink text" > and will insert the > .BI URI . > .BOXSTOP > .ne 8v > .LP > .NH 2 > .XN man > .LP > .BOXSTART SHADED linen OUTLINED maroon INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt man > .BOXSTOP > .LP > The macro > .QS > .Lt .MR > .BI name > .BI section > .QE > Generates an appropriate external > .BI URI > such as:- > .QS > man:/groff(1) > .QE > except if the link is to another man page in the same collection. > .BOXSTOP > .LP > .ne 5 > .NH 1 toc > .XN Table of Contents > .LP > Some full service macro sets offer macros for building a Table of Contents, > with PDF features these TOCs have clickable entries. > .LP > .NH 2 > .XN pdf.tmac > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > .Lt pdf.tmac > has no specific macros to build Table of Contents, but it does have two macros > which can insert a TOC into the appropriate position in the document:- > .QS > .Lt .pdfpagename > .BI name > .QE > This assigns a name to the current page being rendered which can then be used > in > the second macro:- > .QS > .Lt .pdfswitchtopage > [\c > .BI when ] > .BI name > .QE > Where > .BI name > is a previously named page, and the optional > .BI when > can be > .Qq before > or > .Qq after , > default is > .Qq before . > Alternatively the positions > .Qq top > and > .Qq bottom > can be used instead of a named page. > This macro should be used before a new page is started. > .BOXSTOP > .LP > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > The macro > .QS > .Lt .AUTO_RELOCATE_TOC > .QE > (must come before > .Lt .START ) > will position the TOC appropriately in the document. > .LP > Pease see the > .pdfhref W -D http://schaffter.ca/mom/momdoc/tables-of-contents.html#top -- > mom documentation > for details of TOC generation, by default the TOC entries are clickable. > .BOXSTOP > .LP > .NH 2 > .XN ms > .BOXSTART SHADED honeydew OUTLINED green INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt ms > .BOXSTOP > .LP > The traditional way of populating a TOC was to use the > .Lt .XS/.XA/.XE > macros. > .LP > Groff 1.23.0 introduced the > .Lt .XN > macro which combined producing headings > .XR hdexample ) ( > with adding a TOC entry. > .LP > In either case the entry will be clickable. > .LP > If the document has an > .Lt .RP > entry, so a title page is produced, the TOC will be repositioned after the > Title Page. > .BOXSTOP > .LP > .ne 7v > .NH 1 slide > .XN Slideshow Presentations > .LP > The groff pdf driver understands how to create pdfs to be used for slideshow > presentations. An alternative is to use the postscript driver and gpresent > which is downloadable > .pdfhref W -D > https://ftp.openbsd.org/pub/OpenBSD/distfiles/gpresent-2.5.tar.gz here > because the original > .pdfhref W -D https://www.bob.diertens.org/corner/useful/gpresent/ website > is having difficulty downloading files larger than 32kb. > .LP > groff documents written to work with > .Lt present.tmac > .I may > work with -Tpdf with minor alteration (and no need to run > .Lt presentps > and > .Lt ps2pdf ). > .LP > .NH 2 > .XN pdf.tmac > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > There are just two macros which control the slide presentation:- > .QS > .Lt .pdftransition > .I feature > .I mode > .I duration > .I dimension > .I motion > .I direction > .I scale > .I bool > .QE > These parameters are explained in the > .pdfhref W -D man:/gropdf(1)#transition gropdf(1) > man page, many of them are not required! > .LP > There are two > .Qq events > which trigger a transition: when a slide is first drawn > (\c > .I feature > = > .Lt SLIDE ) > and when a new element is added to the slide > .I feature "" ( > = > .Lt BLOCK ). > Both can have a different transition > .I mode , > such as > .Lt "\[dq]Split | Blinds | Box | Wipe | Dissolve | Glitter | R\[dq]" > all of which are explained (and some more!) in the man page. > .LP > Whenever you want the presentation to pause (because you have added a > new element to the slide) use the macro:- > .QS > .Lt .pdfpause > .QE > and the new element will appear using the current transition set for > .Lt BLOCK . > .BOXSTOP > .LP > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > mom has full integration with pdf presentation mode, the documentation > is > .pdfhref W -D http://schaffter.ca/mom/momdoc/docprocessing.html#slides -A . > here > .LP > In addition the mom example documents include > .Lt slide-demo.mom > and its resultant pdf, so should be included in the examples/mom directory > in the groff documentation. > .BOXSTOP > .LP > .NH 2 > .XN ms > .BOXSTART SHADED honeydew OUTLINED green INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt ms > .BOXSTOP > .LP > An example of using > .Lt ms > to create a slideshow is > .pdfhref W -D http://chuzzlewit.co.uk/ms-slides.tgz -A . here > .BOXSTOP > .bp > .NH 1 boxes > .XN Boxes and PDF paper colour > .LP > PDF viewers usually show contents on a white background, the actual > background in the PDF is really transparent (so it appears white), > however it is possible to specify a colour for the page background. > .LP > In addition it is possible to specify framed coloured boxes on the page > to contain the running contents. These boxes will flow onto following > pages. > .LP > .NH 2 > .XN pdf.tmac > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt pdf.tmac > .BOXSTOP > .LP > The relevent macros are:- > .QS > .Lt .pdfbackground > .I cmd > .I left > .I top > .I right > .I bottom > .I weight > .br > .Lt ".pdfbackground off" > .br > .Lt ".pdfbackground footnote" > .I bottom > .QE > which produce a background rectangle on the page. The meanings of the > parameters are explained in the > .pdfhref W -D man:/gropdf(1)#boxes gropdf(1) > man page. > .BOXSTOP > .LP > .NH 2 > .XN ms > .BOXSTART SHADED honeydew OUTLINED green INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt ms > .BOXSTOP > .LP > If you include the macro file > .Lt -msboxes > in the > .I groff > command, you will have access to these macros:- > .QS > .Lt .BOXSTART > .Lt SHADED > .I colour > .Lt OUTLINED > .I colour > .Lt INDENT > .I size > .Lt WEIGHT > .I size > .QE > begins a box, where the argument after > .Lt SHADED > gives the fill colour and that after > .Lt OUTLINED > the border colour. Omit the former to get a borderless filled box and the > latter for a border with no fill. The specified > .Lt WEIGHT > (line thickness) is used if the box is > .Lt OUTLINED . > .LP > .Lt INDENT > precedes a value that leaves a gap between the border and the contents > inside the box. > .LP > Each colour must be a defined groff colour name, and each size a valid > .I groff > numeric expression. The keyword/value pairs can be specified in any order. > .LP > Boxes can be stacked, so you can start a box within another box; usually > the later boxes would be smaller than the containing box, but this is > not enforced. When using > .Lt BOXSTART , > the left position is the current indent minus the > .Lt INDENT > in the command, and the right position is the left position > (calculated above) plus the current line length and twice the indent. > .QS > .Lt BOXSTOP > .QE > takes no parameters. It closes the most recently started box at the > current vertical position after adding its > .Lt INDENT > spacing. > .BOXSTOP > .LP > .NH 2 > .XN mom > .BOXSTART SHADED lavenderblush OUTLINED mediumblue INDENT 2n WEIGHT 1p > .BOXSTART SHADED lightgray INDENT 2p > .ce 1 > .Lt mom > .BOXSTOP > .LP > mom has comprehensive control of boxes see the > .pdfhref W -D http://schaffter.ca/mom/momdoc/images.html#box-intro \ > -A . mom documentation > .BOXSTOP > .bp > .NH 1 developer > .XN For macro developers > .LP > If you want to add PDF features to your own macro packages here is some > information which may be helpful. > .BOXSTART SHADED cornsilk OUTLINED brown INDENT 2n WEIGHT 1p > .sp -1v > .NH 2 restart > .XN Stop/Start hotlink > .QS > .Lt .pdfmarksuspend/.pdfmarkresume > .QE > If there is a piece of > .Qq "hotlink text" > which appears right at the bottom of the page and continues across to the > start of the next page these macros can stop/restart the hotlink. > Typically they appear at the start of a page footer macro (to prevent the > footer text becoming part of the hotlink), and at the end of a page > header macro (to restart the hotlinking from the previous page). > .LP > .NH 2 pdfroff > .XN Forward References > .LP > Since groff is a single pass interpreter when it finds an > .XR hotspot "" "" * > it cannot plant a link if it has not > .Qq seen > the > .XR naming "" "" * > to which it links, and the link will appear as > \m[\*[PDFHREF.TEXT.COLOUR]]Unknown\m[] > resisting all mouse clicks - the same happens if the > .I destination > of the link is mis-typed and does not exist. The forward link problem can be > solved: instead of:- > .QS > .Lt "groff -Tpdf ..." > .QE > use:- > .QS > .Lt "pdfmom --roff ..." > .QE > The command was originally developed for satisfying forward references in > .I mom > files (hence the name), but with the > .I --roff > flag it drops its reliance on > .I mom > and you can place any macro package on the command line. > .NH 2 S > .XN Marking Hotlinks > .LP > The > .QS > .Lt .pdfhref > .I command > .QE > general purpose macro, supports these commands:- > .QS > .IP \*[= O] > (Outline) This is the command which > .XR bookmarks "" "" \fB.pdfbookmark\fP > calls. > .IP \*[= M] > (Mark) Setup a > .XR naming . "" + > .IP \*[= L] > (Local) Mark an > .XR hotspot . "" + > .IP \*[= W] > (WWW URI) Form an > .XR external . "" + > .QE > .LP > These > .Qq low-level > calls all expect > .Qq "descriptive text" > or > .Qq "hotlink text" > to be passed as part of the macro call. Sometimes this is not convenient, > consider the man macro pair > .Lt .MT/.ME > where > .ID > .ft CB > \&.MT jh@\[rs]:axis\[rs]:.se > J\[rs][o ad]rgen H\[rs][a ad]gg > \&.ME > .ft > .DE > The mail-to > .I URI > is on the > .Lt .MT > line, but the text to use for a > .I hotlink > is everything up to the following > .Lt .ME . > .LP > The > .Lt .pdfhref > macro allows an > .Lt -S > flag, not documented above, which just turns on > .I hotlinking > text. It is then up to the macro author to arrange for it to be turned off > at the appropriate point. In this example it should be > .Qq "turned off" > in the > .Lt .ME > macro. > .LP > The code to turn off the > .Qq hotlinking > and restore the text colour to what it was before the > .Qq hotlinking > started is:- > .ID > .ft CB > \[rs]X'pdf: markend'\[rs]m[\[rs]*[pdf:curcol]] > .ft > .DE > .BOXSTOP > .TC -- --- Larry McVoy Retired to fishing http://www.mcvoy.com/lm/boat
