summaryrefslogtreecommitdiffstats
path: root/contrib/pdfmark/pdfmark.tmac
blob: 5f664f277083b1f293b8fb250ed06f6e7f7253b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
.ig

pdfmark.tmac

Copyright (C) 2004-2020 Free Software Foundation, Inc.
     Written by Keith Marshall (keith.d.marshall@ntlworld.com)

This file is part of groff.

groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or
(at your option) any later version.

groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Author's Note
=============

While I have written this macro package from scratch, much of my
inspiration has come from discussion on the groff mailing list
(mailto:groff@gnu.org).  I am particularly indebted to:

   Kees Zeelenberg, for an earlier macro package he posted,
   a study of which helped me to get started.

   Carlos J. G. Duarte and Werner Lemberg, whose discussion
   on computation of the bounding boxes for link "hot-spots"
   forms the basis of such computations in this package.
..
.if !\n(.g .ab These pdfmark macros require groff.
.\"
.\" Check if we have already been loaded -- do not reload
.if d pdfmark .nx
.\"
.\" ======================================================================
.\" Module PDFMARK: Insert Arbitrary PDFMARK Code in the Postscript Stream
.\" ======================================================================
.\"
.\" PDFMARK output may be disabled, by zeroing the PDFOPMODE register,
.\" ( which mimics a more generic OPMODE, if it is defined ).
.\"
.if rOPMODE .aln PDFOPMODE OPMODE
.\"
.\" but if OPMODE wasn't defined,
.\" then make the default PDFMARK mode ENABLED.
.\"
.if !rPDFOPMODE .nr PDFOPMODE 1
.\"
.\" PDFMARK output must be constrained to a maximum line length limit,
.\" for strict compliance with the Postscript DSC.  This limit is defined
.\" in register "PDFMARK.FOLDWIDTH.MAX".  This is user definable, up to a
.\" ceiling value of 255, which is also its default value;  this limit
.\" is enforced for each PDFMARK, by macro "pdf*pdfmark.limit".
.\"
.de pdf*pdfmark.limit
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdf*pdfmark.limit  REGISTER-NAME  DEFAULT-MAXIMUM-VALUE
.\" ----------------------------------------------------------------
.\"
.\" If a register named REGISTER-NAME has not been defined, then
.\" define it now, with default value = DEFAULT-MAXIMUM-VALUE.
.\"
.if !r\\$1 .nr \\$1 \\$2
.\"
.\" But when it has already been defined, ensure that its value does
.\" not exceed DEFAULT-MAXIMUM-VALUE; if value does exceed this ceiling,
.\" then redefine it, to enforce the limit.
.\"
.if (\\n[\\$1] > \\$2) .nr \\$1 \\$2
..
.\" The "pdfmark" macro is responsible for emitting the appropriate
.\" Postscript code.
.\"
.de pdfmark
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdfmark  text of pdfmark instruction
.\" Macro supplies the required opening "[" and closing "pdfmark"
.\" operator; DO NOT include them in the instruction text!
.\" ----------------------------------------------------------------
.\"
.if \\n[PDFOPMODE] \{\
.\"
.\" Strict DSC compliance forbids emission of ps:exec lines which
.\" exceed 255 characters in length.  We will allow the user to specify
.\" an alternative lesser limit ...
.\"
.   pdf*pdfmark.limit PDFMARK.FOLDWIDTH.MAX 255
.\"
.\" ... and we will also support a second lesser limit, which will be
.\" applied to literal text parenthetically embedded within the PDFMARK.
.\"
.   pdf*pdfmark.limit PDFMARK.FOLDWIDTH \\n[PDFMARK.FOLDWIDTH.MAX]
.\"
.\" We will push out the entire PDFMARK in one chunk, provided it fits
.\" within this limit.
.\"
.   length pdf:length "[\\$* pdfmark\"
.   ie !(\\n[pdf:length] > \\n[PDFMARK.FOLDWIDTH]) \{\
.   \"
.   \" This PDFMARK is suitable for single chunk output ...
.   \"
.      nop \!x X ps:exec [\\$* pdfmark
.      \}
.   el \{\
.   \" ... but, when the limit would be violated, then we must
.   \" recompose the specified PDFMARK, spreading it over as many
.   \" continuation lines as are necessary.
.   \"
.      als pdf*compose pdf*compose.first
.      while \\n(.$ \{\
.         pdf*compose \\$1
.         shift
.         \}
.   \"
.   \" Complete the PDFMARK recomposition, by appending a
.   \" "pdfmark" operator, and push it out to the intermediate
.   \" output stream, (excluding its final line break).
.   \"
.      pdf*compose pdfmark
.      pdf*pdfmark.dispatch
.   \"
.   \" And clean up when done.
.   \"
.      rm pdf*compose pdf*pdfmark.post
.      rm pdf:compose.test pdf:composed.literal
.      \}
.   rr pdf:length
.   \}
..
.\" When a PDFMARK exceeds the specified output record length limit,
.\" then we decompose it, subsequently using the dynamically overloaded
.\" macro, "pdf*compose", to reassemble it into as many continuation
.\" records as it may require.
.\"
.\" Each call to "pdf*compose" uses macro "pdf*length.increment" to
.\" keep track of the current output record length, so ensuring that
.\" the active maximum length limit is not violated.
.\"
.de pdf*length.increment
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdf*length.increment  NEXT-ADDITION
.\" ----------------------------------------------------------------
.\"
.ie d pdf:composed.line \
.   length pdf:length "\\*[pdf:composed.line] \\$*\"
.el .length pdf:length "\\$*\"
..
.\" The first call to "pdf*compose" for each PDFMARK is directed
.\" to "pdf*compose.first";  this initialises the local strings
.\" and macros used to compose the eventual PDFMARK output.
.\"
.de pdf*compose.first
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .als  pdf*compose  pdf*compose.first
.\"   .     pdf*compose  TOKEN
.\" ----------------------------------------------------------------
.\"
.\" Ensure that the output record accumulator will be initialised
.\" on posting of the first composed PDFMARK record.
.\"
.als pdf*pdfmark.post pdf*pdfmark.post.first
.\"
.\" The first token passed to "pdf*compose" should not be a
.\" literal, but be prepared to handle one, just in case.
.\"
.ds pdf:compose.test \\$1
.substring pdf:compose.test 0 0
.ie '('\\*[pdf:compose.test]' \{\
.\"
.\" We found a literal, even though we didn't expect it;
.\" if it's a single element literal, we can just handle it
.\" as if it is a regular token anyway.
.\"
.   ds pdf:compose.test "\\$\\n(.$\"
.   substring pdf:compose.test -1
.   if !')'\\*[pdf:compose.test]' \{\
.   \"
.   \" But when it is the first of a literal sequence,
.   \" then we need to set up "pdf*compose" to handle it.
.   \"
.      ds pdf:composed.literal "[\\$*\"
.      als pdf*compose pdf*compose.literal
.      \}
.   \}
.el .ds pdf:compose.test )
.if ')'\\*[pdf:compose.test]' \{\
.\"
.\" In the normal case, we start each new PDFMARK with a
.\" regular token; save it as the first in the composed output
.\" line sequence, and set up "pdf*compose" to collect
.\" the rest of the sequence.
.\"
.   ds pdf:composed.line "[\\$*\"
.   als pdf*compose pdf*compose.next
.   \}
..
.\" Subsequent calls to "pdf*compose", while collecting
.\" regular tokens, are then directed to "pdf*compose.next".
.\"
.de pdf*compose.next
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .als  pdf*compose  pdf*compose.next
.\"   .     pdf*compose  TOKEN
.\" ----------------------------------------------------------------
.\"
.\" This first checks to ensure that the supplied token really is
.\" a regular token, and not the first element in a literal.
.\"
.ds pdf:compose.test \\$1
.substring pdf:compose.test 0 0
.ie '('\\*[pdf:compose.test]' \{\
.\"
.\" The supplied token represents the first element of a literal,
.\" but it may be a single element literal, which we simply handle
.\" as a regular token anyway.
.\"
.   ds pdf:compose.test "\\$\\n(.$\"
.   substring pdf:compose.test -1
.   if !')'\\*[pdf:compose.test]' \{\
.   \"
.   \" The supplied token is the first of a sequence of elements
.   \" which collectively define a literal, so start collecting a
.   \" composite literal token, and change the "pdf*compose"
.   \" state, to collect and append the remaining elements.
.   \"
.      ds pdf:composed.literal "\\$*\"
.      als pdf*compose pdf*compose.literal
.      \}
.   \}
.el .ds pdf:compose.test )
.if ')'\\*[pdf:compose.test]' \{\
.\"
.\" The supplied token IS a regular token; add it, but ensure that
.\" the active maximum record length limit is honoured.
.\"
.   pdf*length.increment "\\$*\"
.   ie (\\n[pdf:length] > \\n[PDFMARK.FOLDWIDTH.MAX]) \{\
.   \"
.   \" Adding this token would cause the current PDFMARK record, in
.   \" groff's intermediate output file, to overflow the active record
.   \" length limit, so post the current record and start another.
.   \"
.      pdf*pdfmark.dispatch
.      ds pdf:composed.line "\\$*\"
.      \}
.   el \{\
.   \"
.   \" This token will fit in the current PDFMARK record, without
.   \" violating the active length limit, so simply add it.
.   \"
.      ie d pdf:composed.line .as pdf:composed.line " \\$*\"
.      el .ds pdf:composed.line "\\$*\"
.      \}
.   \}
..
.\" While assembling a multiple token literal sequence into a single
.\" literal token, successive calls to "pdf*compose" are directed
.\" to "pdf*compose.literal".
.\"
.de pdf*compose.literal
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .als  pdf*compose  pdf*compose.literal
.\"   .     pdf*compose  TOKEN
.\" ----------------------------------------------------------------
.\"
.\" First, check to ensure that the current token can be appended to
.\" the accumulated literal, without extending it beyond the maximum
.\" allowed literal token length.
.\"
.length pdf:length "\\*[pdf:composed.literal] \\$*\"
.ie (\\n[pdf:length] > (\\n[PDFMARK.FOLDWIDTH] - 2)) \{\
.\"
.\" If it has grown too long, then it must be folded across two
.\" physical PDFMARK output records, so check if we can accommodate
.\" the portion collected so far within the current output record.
.\"
.   pdf*length.increment "\\*[pdf:composed.literal]\"
.   if (\\n[pdf:length] > (\\n[PDFMARK.FOLDWIDTH.MAX] - 2)) \{\
.   \"
.   \" The current output record CAN'T accommodate the currently
.   \" composed portion of the literal, so flush out the current
.   \" record, to make way for the accumulated literal, and mark
.   \" the dispatch mode as "wrapped", for the fragments of the
.   \" folded literal string, which are to follow.
.   \"
.      pdf*pdfmark.dispatch
.      ds pdf*pdfmark.dispatch.wrapped
.      \}
.   ie d pdf:composed.line \{\
.   \"
.   \" If we DIDN'T need to flush the current output record,
.   \" then we can simply append the accumulated literal to it...
.   \"
.      as pdf:composed.line " \\*[pdf:composed.literal]\"
.      \}
.   el \{\
.   \"
.   \" otherwise, when the current record has been flushed, or is
.   \" empty, then we promote the accumulated literal, to make it
.   \" the next output record...
.   \"
.      rn pdf:composed.literal pdf:composed.line
.      \}
.\"
.\" Now, to complete the fold, flush out any accumulated partial
.\" output record, and continue accumulating the literal, starting
.\" with the current token.
.\"
.   pdf*pdfmark.dispatch
.   ds pdf:composed.literal "\\$*\"
.   \}
.el \{\
.\"
.\" Alternatively, when we HAVEN'T identified a need to fold the
.\" current output record, then we simply append the current token
.\" to the accumulated literal token buffer string.
.\"
.   as pdf:composed.literal " \\$*\"
.   \}
.\"
.\" Having ensured that we have sufficient space, in which to
.\" append the current token to the currently accumulated literal,
.\" we check its rightmost character, to see if is the closing
.\" parenthesis, which completes the literal.
.\"
.ds pdf:compose.test \\$\\n(.$
.substring pdf:compose.test -1
.if ')'\\*[pdf:compose.test]' \{\
.\"
.\" The literal has been completely collected, so we may now append
.\" it to the current output record, as a single literal token, but
.\" subject to the constraint that it must not extend the output
.\" record beyond the maximum permitted length.
.\"
.   pdf*length.increment "\\*[pdf:composed.literal]\"
.   ie (\\n[pdf:length] > \\n[PDFMARK.FOLDWIDTH.MAX]) \{\
.   \"
.   \" So, when the literal cannot be accommodated within the maximum
.   \" length constraint, then we flush the current record, and start
.   \" a new one, with the literal token as its first entry.
.   \"
.      pdf*pdfmark.dispatch
.      rn pdf:composed.literal pdf:composed.line
.      \}
.   el \{\
.   \"
.   \" When the literal CAN be accommodated within the maximum length
.   \" constraint, then ...
.   \"
.      ie d pdf:composed.line \{\
.      \"
.      \" When an output record has already been instantiated, we
.      \" append the literal token to it, and discard the accumulator
.      \" string, which is no longer required.
.      \"
.         as pdf:composed.line " \\*[pdf:composed.literal]\"
.         rm pdf:composed.literal
.         \}
.      el \{\
.      \"
.      \" But when no output record yet exists, then we simply
.      \" reassign the accumulated literal token, to instantiate a
.      \" new output record.
.      \"
.         rn pdf:composed.literal pdf:composed.line
.         \}
.      \}
.\"
.\" Finally, since we have completed the accumulation of the literal, we
.\" revert to the "unwrapped" mode of operation for "pdf*pdfmark.dispatch",
.\" and restore the normal "pdf*compose" action, for collection of the next
.\" token (if any).
.\"
.   rm pdf*pdfmark.dispatch.wrapped
.   als pdf*compose pdf*compose.next
.   \}
..
.\" While composing a multiple record PDFMARK, each composed record
.\" must be added to the collection, whenever the partially composed
.\" output record has been filled;  this is handled when necessary,
.\" by calling the "pdf*pdfmark.dispatch" macro.
.\"
.de pdf*pdfmark.dispatch
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdf*pdfmark.dispatch
.\" ----------------------------------------------------------------
.\"
.if d pdf:composed.line \{\
.\"
.\" This is simply a wrapper around the overloaded "pdf*pdfmark.post"
.\" macro, ensuring that an output record has actually been collected
.\" before attempting to post it; it then cleans up after posting, to
.\" ensure that each collected record is posted only once.
.\"
.   if d pdf*pdfmark.dispatch.wrapped \{\
.   \"
.   \" When dispatching an excessively long literal string, which
.   \" must be wrapped over multiple records, this mode is active
.   \" for all but the closing record; we must escape the newline
.   \" at the end of each such unclosed literal record.
.   \"
.      as pdf:composed.line " \\\\\\\\\"
.      \}
.   pdf*pdfmark.post
.   rm pdf:composed.line
.   \}
..
.\" For each PDFMARK, the first call of "pdf*pdfmark.post" is directed
.\" to the "pdf*pdfmark.post.first" macro;  this initialises the state
.\" of the "pdf:composed" macro, for assembly of a new PDFMARK.
.\"
.de pdf*pdfmark.post.first
. nop \!x X ps:exec \\*[pdf:composed.line]
.\"
.\" Subsequent calls to "pdf*pdfmark.post" are redirected to the
.\" alternative "pdf*pdfmark.post.next" macro, which simply appends
.\" additional PDFMARK records to the "pdf:composed" macro.
.\"
.als pdf*pdfmark.post pdf*pdfmark.post.next
..
.de pdf*pdfmark.post.next
. nop \!+\\*[pdf:composed.line]
..
.\" "pdf*end" is a dummy macro.  It is required to mark the end
.\" of each individual fragment which is added to "pdf:composed";
.\" other than this, it does nothing.
.\"
.de pdf*end
..
.\"
.\" Some supporting macros defer actual pdfmark output until an
.\" appropriate time for it to be written; the "pdfsync" macro
.\" provides a mechanism for flushing such deferred output;
.\" it should be called from an end macro, and at any other time
.\" when it may be deemed necessary to flush pdfmark context.
.\"
.de pdfsync
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdfsync buffer ...
.\" Arguments indicate which "buffer(s)" to flush:
.\"   O -> bookmark (outline) cache
.\"   M -> document metadata diversion
.\" If no argument, flush ALL buffers
.\" ----------------------------------------------------------------
.\"
.ie \\n(.$ \{\
.   while \\n(.$ \{\
.      if '\\$1'O' .pdf:bm.sync 1
.      if '\\$1'M' \{\
.         if dpdf:metadata .pdf:metadata
.         rm pdf:metadata
.         \}
.      shift
.      \}
.   \}
.el .pdfsync O M
..
.\"
.\" some helper functions ...
.\"
.\" "pdf:warn" and "pdf:error" write diagnostic messages to stderr
.\"
.de pdf:warn
.\" ----------------------------------------------------------
.\" Usage:
.\"   .pdf:warn text of message
.\" ----------------------------------------------------------
.\"
.tm \\n(.F:\\n(.c: macro warning: \\$*
..
.de pdf:error
.\" ----------------------------------------------------------
.\" Usage:
.\"   .pdf:error text of message
.\" ----------------------------------------------------------
.\"
.tm \\n(.F:\\n(.c: macro error: \\$*
..
.\" "pdf:pop", assisted by "pdf*pop", allows us to retrieve register,
.\" or string values, from a string masquerading as a data queue,
.\" or as a stack.
.\"
.de pdf:pop
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdf:pop <type> <to-name> <from-name>
.\"   $1 = nr for numeric register, ds for string
.\"   $2 = name of register or string to be assigned
.\"   $3 = name of string, from which data is to be retrieved
.\" ----------------------------------------------------------------
.\"
.pdf*pop \\$* \\*[\\$3]
..
.de pdf*pop
.ds pdf:stack \\$3
.\\$1 \\$2 \\$4
.shift 4
.ie \\n(.$ .ds \\*[pdf:stack] \\$*
.el .rm \\*[pdf:stack]
.rm pdf:stack
..
.\"
.\"
.\" ===========================================================
.\" Module PDFINFO: Insert MetaData Entries into a PDF Document
.\" ===========================================================
.\"
.\" N.B.
.\"   Output from the macros in this module is deferred, until
.\"   subsequent invocation of .pdfsync, or .pdfexit
.\"
.\" ."pdfinfo" provides a general purpose form of metadata entry ...
.\" it allows arbitrary text to be associated with any specified
.\" metadata field name.
.\"
.de pdfinfo
.\" -------------------------------------------------------------------
.\" Usage:
.\"   .pdfinfo /FieldName field content ...
.\" Examples:
.\"   .pdfinfo /Title   A PDF Document
.\"   .pdfinfo /Author  Keith Marshall
.\" -------------------------------------------------------------------
.\"
.ds pdf:meta.field \\$1
.shift
.da pdf:metadata
\!.pdfmark \\*[pdf:meta.field] (\\$*) /DOCINFO
.di
.rm pdf:meta.field
..
.\"
.\" Macro "pdfview" defines a special form of metadata entry ...
.\" it uses the /DOCVIEW pdfmark, to specify the initial (default) view,
.\" when the document is opened.
.\"
.de pdfview
.\" -------------------------------------------------------------------
.\" Usage:
.\"   .pdfview view parameters ...
.\" Examples:
.\"   .pdfview /PageMode /UseOutlines
.\"   .pdfview /Page 2 /View [/FitH \n(.p u]
.\" -------------------------------------------------------------------
.\"
.da pdf:metadata
\!.pdfmark \\$* /DOCVIEW
.di
..
.\"
.\"
.\" =====================================================================
.\" Module PDFNOTE: Insert "Sticky Note" Style Comments in a PDF Document
.\" =====================================================================
.\"
.\" "PDFNOTE.WIDTH" and "PDFNOTE.HEIGHT" set the preferred size for
.\" display of the "sticky note" pane, when opened.  Acrobat Reader
.\" seems not to honour these -- perhaps GhostScript doesn't encode
.\" them correctly!  Anyway, let's set some suitable default values,
.\" in case the user has a set up which does work as advertised.
.\"
.nr PDFNOTE.WIDTH  3.5i
.nr PDFNOTE.HEIGHT 2.0i
.\"
.\" "pdf:bbox" defines the expression used to set the size and location
.\" of the bounding rectangle for display of notes and link "hot-spots".
.\" This is defined, such that a note is placed at troff's current text
.\" position on the current page, with its displayed image size defined
.\" by the "PDFNOTE.WIDTH" and "PDFNOTE.HEIGHT" registers, while the
.\" bounds for a link "hot-spot" are matched to the text region which
.\" defines the "hot-spot".
.\"
.ds pdf:bbox \\n[pdf:llx] u \\n[pdf:lly] u \\n[pdf:urx] u \\n[pdf:ury] u
.\"
.\" Getting line breaks into the text of a PDFNOTE is tricky -- we need
.\" to get a "\n" into the Postscript stream, but three levels of "\" are
.\" swallowed, when we invoke "pdfnote".  The following definition of "PDFLB",
.\" (for LineBreak), is rather ugly, but does allow us to use
.\"
.\"    .pdfnote  Some text.\*[PDFLB]Some more text, on a new line.
.\"
.ds PDFLB \\\\\\\\\\\\\\\\n
.\"
.de pdfnote
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   .pdfnote [-T "Text for Title"] Text of note ...
.\" ----------------------------------------------------------------------
.\"
.if \\n[PDFOPMODE] \{\
.\"
.\" First, compute the bounding rectangle,
.\" for this PDFNOTE instance
.\"
.   mk pdf:ury
.   nr pdf:llx \\n(.k+\\n(.o+\\n[.in]
.   nr pdf:lly \\n[pdf:ury]-\\n[PDFNOTE.HEIGHT]
.   nr pdf:urx \\n[pdf:llx]+\\n[PDFNOTE.WIDTH]
.   ds pdf:note.instance /Rect [\\*[pdf:bbox]]
.\"
.\" Parse any specified (recognisable) PDFNOTE options
.\"
.   while dpdf:note\\$1 \{\
.      pdf:note\\$1 \\$@
.      shift \\n[pdf:note.argc]
.      \}
.\"
.\" Emit the note, and clean up
.\"
.   pdfmark \\*[pdf:note.instance] /Contents (\\$*) /ANN
.   rm pdf:note.instance
.   rr pdf:note.argc
.   \}
..
.de pdf:note-T
.nr pdf:note.argc 2
.as pdf:note.instance " /Title (\\$2)
..
.\"
.\"
.\" =====================================================================
.\" Module PDFBOOKMARK: Add an Outline Reference in the PDF Bookmark Pane
.\" =====================================================================
.\"
.\" "PDFBOOKMARK.VIEW" controls how the document will be displayed,
.\" when the user selects a bookmark.  This default setting will fit
.\" the page width to the viewing window, with the bookmarked entry
.\" located at the top of the viewable area.
.\"
.ds PDFBOOKMARK.VIEW /FitH \\n[PDFPAGE.Y] u
.\"
.\" "PDFOUTLINE.FOLDLEVEL" controls how the document outline will be
.\" displayed.  It is a number, defining the maximum heading level
.\" which will be visible, without outline expansion by the user, in
.\" the initial view of the document outline.  Assuming that no sane
.\" document will ever extend to 10,000 levels of nested headings,
.\" this initial default value causes outlines to be fully expanded.
.\"
.nr PDFOUTLINE.FOLDLEVEL 10000
.\"
.\" The actual job of creating an outline reference
.\" is performed by the "pdfbookmark" macro.
.\"
.de pdfbookmark
.\" ------------------------------------------------------------------
.\" Usage:
.\"   .pdfbookmark [-T tag] level "Text of Outline Entry"
.\"
.\"   $1 = nesting level for bookmark (1 is top level)
.\"   $2 = text for bookmark, (in PDF viewer bookmarks list)
.\"   $3 = suffix for PDF internal bookmark name (optional)
.\" ------------------------------------------------------------------
.\"
.ie '\\n(.z'' \{\
.\"
.\" When we are at the top diversion level, i.e. actually emitting text
.\" to the output device stream, then we compute the location of, and
.\" plant this bookmark immediately.
.\"
.   if \\n[PDFOPMODE] \{\
.   \"
.   \" Make the bookmark name "untagged" by default,
.   \" then parse any specified options, to set a "tag", if required
.   \"
.      ds pdf:href-T
.      while dpdf:href.opt\\$1 \{\
.         pdf:href.opt\\$1 \\$@
.         shift \\n[pdf:href.argc]
.         \}
.      rr pdf:href.argc
.   \"
.   \" If we found "--" to mark the end of the options, discard it
.   \"
.      if '\\$1'--' .shift
.   \"
.   \" Synchronise the bookmark cache
.   \" to the requested bookmark nesting level
.   \"
.      pdf:bm.sync \\$1
.      shift
.   \"
.   \" Increment the bookmark serialisation index
.   \" in order to generate a uniquely serialised bookmark name,
.   \" ( which we return in the string "PDFBOOKMARK.NAME" ),
.   \" and insert this bookmark into the cache
.   \"
.      pdf:href.sety
.      nr pdf:bm.nr +1
.      ds PDFBOOKMARK.NAME pdf:bm\\n[pdf:bm.nr]\\*[pdf:href-T]
.      ds pdf:bm\\n[pdf:bm.nr] /Dest /\\*[PDFBOOKMARK.NAME]
.      pdfmark \\*[pdf:bm\\n[pdf:bm.nr]] /View [\\*[PDFBOOKMARK.VIEW]] /DEST
.      as pdf:bm\\n[pdf:bm.nr] " /Title (\\$*)
.      pdf:href.options.clear
.      rr PDFPAGE.Y
.      \}
.   \}
.el \{\
.\"
.\" But when we are collecting a diversion which will be written out later,
.\" then we must defer bookmark placement, until we emit the diversion.
.\" (don't rely on $0 == pdfbookmark here; it may be a volatile alias).
.\"
.   nop \!.pdfbookmark \\$@
.   \}
..
.\"
.\" Macro "pdf:bm.sync" is called for each bookmark created,
.\" to establish a cache entry at the appropriate nesting level.
.\" It will flush ALL previous cache content, when called to
.\" add a new bookmark at level 1, or if simply called at
.\" level 1, without adding any bookmark.
.\"
.de pdf:bm.sync
.\" ------------------------------------------------------------------
.\" Usage:
.\"   .pdf:bm.sync  level
.\"   $1 = nesting level of current bookmark, or 1 to flush cache
.\" ------------------------------------------------------------------
.\"
.\" First validate the bookmark nesting level
.\" adjusting it if required
.\"
.if \\$1>\\n[pdf:bm.nl] .nr pdf:bm.nl +1
.ie \\$1>\\n[pdf:bm.nl] \{\
.   pdf:warn adjusted level \\$1 bookmark; should be <= \\n[pdf:bm.nl]
.   \}
.el .nr pdf:bm.nl \\$1
.if \\n[pdf:bm.nl]<1 \{\
.   pdf:warn bad arg (\\$1) in \\$0 \\$1; \\$0 1 forced
.   nr pdf:bm.nl 1
.   \}
.\"
.\" If reverting from a higher to a lower nesting level,
.\" cyclicly adjust cache counts for each pending higher level
.\"
.if \\n[pdf:bm.lc]>=\\n[pdf:bm.nl] \{\
.   nr pdf:bm.lc +1
.   if !rpdf:bm.c\\n[pdf:bm.lc].c .nr pdf:bm.c\\n[pdf:bm.lc].c 0
.   while \\n[pdf:bm.lc]>\\n[pdf:bm.nl] \{\
.      as pdf:bm.c\\n[pdf:bm.lc] " \\n[pdf:bm.c\\n[pdf:bm.lc].c]
.      rr pdf:bm.c\\n[pdf:bm.lc].c
.      nr pdf:bm.lc -1
.      \}
.   \}
.\"
.\" Update the cache level,
.\" flushing when we are at level 1
.\"
.nr pdf:bm.lc \\n[pdf:bm.nl]
.ie \\n[pdf:bm.nl]=1 \{\
.   while \\n[pdf:bm.ic]<\\n[pdf:bm.nr] .pdf:bm.emit 0
.   rr pdf:bm.rc
.   \}
.el .nr pdf:bm.c\\n[pdf:bm.nl].c +1
..
.\" Macro "pdf:bm.emit" is called, when the cache is at level 1.
.\" This flushes ALL pending bookmarks from the cache, i.e. the
.\" preceding level 1 bookmark, and any nested dependents,
.\" which it may have.
.\"
.de pdf:bm.emit
.\" ------------------------------------------------------------------
.\" Usage:
.\"   .pdf:bm.emit  flag
.\"   $1 = reference counting flag, used to control recursion
.\" ------------------------------------------------------------------
.\"
.\" First check for nested dependents,
.\" and append the "dependent count" to the bookmark, as required.
.\"
.nr pdf:bm.ic +1
.nr pdf:bm.lc +1
.pdf:pop nr pdf:bm.rc pdf:bm.c\\n[pdf:bm.lc]
.if \\n[pdf:bm.rc] \{\
.   ds pdf:bm.fold
.   if \\n[pdf:bm.lc]>\\n[PDFOUTLINE.FOLDLEVEL] .ds pdf:bm.fold -
.   as pdf:bm\\n[pdf:bm.ic] " /Count \\*[pdf:bm.fold]\\n[pdf:bm.rc]
.   rm pdf:bm.fold
.   \}
.pdfmark \\*[pdf:bm\\n[pdf:bm.ic]] /OUT
.rm pdf:bm\\n[pdf:bm.ic]
.\"
.\" For ALL dependents, if any,
.\" recursively flush out any higher level dependents,
.\" which they themselves may have
.\"
.while \\n[pdf:bm.rc] \{\
.   nr pdf:bm.rc -1
.   pdf:bm.emit \\n[pdf:bm.rc]
.   \}
.\"
.\" Finally,
.\" unwind the recursive call stack, until we return to the top level.
.\"
.nr pdf:bm.rc \\$1
.nr pdf:bm.lc -1
..
.nr pdf:bm.nr 0
.nr pdf:bm.nl 1
.nr pdf:bm.lc 0
.nr pdf:bm.ic 0
.\"
.\"
.\" =============================================================
.\" Module PDFHREF: Create Hypertext References in a PDF Document
.\" =============================================================
.\"
.\" "PDFHREF.VIEW" controls how the document will be displayed,
.\" when the user follows a link to a named reference.
.\"
.ds PDFHREF.VIEW     /FitH \\n[PDFPAGE.Y] u
.\"
.\" This default setting will fit the page width to the viewing
.\" window, with the bookmarked entry located close to the top
.\" of the viewable area.  "PDFHREF.VIEW.LEADING" controls the
.\" actual distance below the top of the viewing window, where
.\" the reference will be positioned; 5 points is a reasonable
.\" default offset.
.\"
.nr PDFHREF.VIEW.LEADING  5.0p
.\"
.\" Yuk!!!
.\" PDF view co-ordinates are mapped from the bottom left corner,
.\" of the page, whereas page printing co-ordinates are mapped
.\" conventionally, from top left.
.\"
.\" Macro "pdf:href.sety" transforms the vertical position of the
.\" last printed baseline, from the printing co-ordinate domain to
.\" the PDF view domain.
.\"
.de pdf:href.sety
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdf:href.sety
.\" ----------------------------------------------------------------
.\"
.\" This computation yields the vertical view co-ordinate
.\" in groff's basic units; don't forget to append grops' "u"
.\" conversion operator, when writing the pdfmark!
.\"
.nr PDFPAGE.Y \\n(.p-\\n(nl+\\n[PDFHREF.VIEW.LEADING]
..
.\" When we create a link "hot-spot" ...
.\" "PDFHREF.LEADING" sets the distance above the top of the glyph
.\" bounding boxes, in each line of link text, over which the link
.\" hot-spot will extend, while "PDFHREF.HEIGHT" sets the hot-spot
.\" height, PER LINE of text occupied by the reference.
.\"
.\" Since most fonts specify some leading space within the bounding
.\" boxes of their glyphs, a better appearance may be achieved when
.\" NEGATIVE leading is specified for link hot-spots;  indeed, when
.\" the default 10pt Times font is used, -1.0 point seems to be a
.\" reasonable default value for "PDFHREF.LEADING" -- it may be
.\" changed, if desired.
.\"
.\" "PDFHREF.HEIGHT" is initially set as one vertical spacing unit;
.\" note that it is defined as a string, so it will adapt to changes
.\" in the vertical spacing.  Changing it is NOT RECOMMENDED.
.\"
.nr PDFHREF.LEADING -1.0p
.ds PDFHREF.HEIGHT   1.0v
.\"
.\" PDF readers generally place a rectangular border around link
.\" "hot-spots".  Within text, this looks rather ugly, so we set
.\" "PDFHREF.BORDER" to suppress it -- the three zeroes represent
.\" the border parameters in the "/Border [0 0 0]" PDFMARK string,
.\" and may be changed to any valid form, as defined in Adobe's
.\" PDFMARK Reference Manual.
.\"
.ds PDFHREF.BORDER   0 0 0
.\"
.\" "PDFHREF.COLOUR" (note British spelling) defines the colour to
.\" be used for display of link "hot-spots".  This will apply both
.\" to borders, if used, and, by default to text; however, actual
.\" text colour is set by "PDFHREF.TEXT.COLOUR", which may be reset
.\" independently of "PDFHREF.COLOUR", to achieve contrasting text
.\" and border colours.
.\"
.\" "PDFHREF.COLOUR" must be set to a sequence of three values,
.\" each in the range 0.0 .. 1.0, representing the red, green, and
.\" blue components of the colour specification in the RGB colour
.\" domain, which is shared by "groff" and the PDF readers.
.\"
.ds PDFHREF.COLOUR   0.35 0.00 0.60
.defcolor pdf:href.colour rgb \*[PDFHREF.COLOUR]
.\"
.\" "PDFHREF.TEXT.COLOUR", on the other hand, is simply defined
.\" using any "groff" colour name -- this default maps it to the
.\" same colour value as "PDFHREF.COLOUR".
.\"
.ds PDFHREF.TEXT.COLOUR  pdf:href.colour
.\"
.\" Accommodate users who prefer the American spelling, COLOR, to
.\" the British spelling, COLOUR.
.\"
.als PDFHREF.COLOR       PDFHREF.COLOUR
.als PDFHREF.TEXT.COLOR  PDFHREF.TEXT.COLOUR
.\"
.\" All PDF "Hypertext" reference capabilities are accessed
.\" through the "pdfhref" macro
.\"
.de pdfhref
.\" -----------------------------------------------------------------
.\" Usage:
.\"   .pdfhref <subcommand [options ...] [parameters ...]> ...
.\" -----------------------------------------------------------------
.\"
.if \\n[PDFOPMODE] \{\
.\"
.\" Loop over all subcommands specified in the argument list
.\"
.   while \\n(.$ \{\
.   \"
.   \" Initially, assume each subcommand will complete successfully
.   \"
.      nr pdf:href.ok 1
.   \"
.   \" Initialise -E and -X flags in the OFF state
.   \"
.      nr pdf:href-E 0
.      nr pdf:href-X 0
.   \"
.   \" Handle the case where subcommand is specified as "-class",
.   \" setting up appropriate macro aliases for subcommand handlers.
.   \"
.      if dpdf*href\\$1       .als pdf*href      pdf*href\\$1
.      if dpdf*href\\$1.link  .als pdf*href.link pdf*href\\$1.link
.      if dpdf*href\\$1.file  .als pdf*href.file pdf*href\\$1.file
.   \"
.   \" Repeat macro alias setup
.   \" for the case where the subcommand is specified as "class",
.   \" (without a leading hyphen)
.   \"
.      if dpdf*href-\\$1      .als pdf*href      pdf*href-\\$1
.      if dpdf*href-\\$1.link .als pdf*href.link pdf*href-\\$1.link
.      if dpdf*href-\\$1.file .als pdf*href.file pdf*href-\\$1.file
.   \"
.   \" Process one subcommand ...
.   \"
.      ie dpdf*href \{\
.      \"
.      \" Subcommand "class" is recognised ...
.      \" discard the "class" code from the argument list,
.      \" set the initial argument count to swallow all arguments,
.      \" and invoke the selected subcommand handler.
.      \"
.         shift
.         nr pdf:argc \\n(.$
.         pdf*href \\$@
.      \"
.      \" When done,
.      \" discard all arguments actually consumed by the handler,
.      \" before proceeding to the next subcommand (if any).
.      \"
.         shift \\n[pdf:argc]
.      \}
.      el \{\
.      \"
.      \" Subcommand "class" is not recognised ...
.      \" issue a warning, and discard the entire argument list,
.      \" so aborting this "pdfhref" invocation
.      \"
.         pdf:warn \\$0: undefined reference class '\\$1' ignored
.         shift \\n(.$
.         \}
.   \"
.   \" Clean up temporary reference data,
.   \" to ensure it doesn't propagate to any future reference
.   \"
.      rm pdf*href pdf:href.link pdf:href.files
.      rr pdf:href-E pdf:href-X
.      pdf:href.options.clear
.      \}
.   rr pdf:href.ok
.   \}
..
.\"
.\" Macros "pdf:href.flag" and "pdf:href.option"
.\" provide a generic mechanism for switching on flag type options,
.\" and for decoding options with arguments, respectively
.\"
.de pdf:href.flag
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.nr pdf:href\\$1 1
.nr pdf:href.argc 1
..
.de pdf:href.option
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.ds pdf:href\\$1 \\$2
.nr pdf:href.argc 2
..
.\"
.\" Valid PDFHREF options are simply declared
.\" by aliasing option handlers to "pdf:href.option",
.\" or to "pdf:href.flag", as appropriate
.\"
.als pdf:href.opt-A pdf:href.option   \" affixed text
.als pdf:href.opt-D pdf:href.option   \" destination name
.als pdf:href.opt-E pdf:href.flag     \" echo link descriptor
.als pdf:href.opt-F pdf:href.option   \" remote file specifier
.als pdf:href.opt-N pdf:href.option   \" reference name
.als pdf:href.opt-P pdf:href.option   \" prefixed text
.als pdf:href.opt-T pdf:href.option   \" bookmark "tag"
.als pdf:href.opt-X pdf:href.flag     \" cross reference
.\"
.\" For references to another document file
.\" we also need to support OS dependent file name specifiers
.\"
.als pdf:href.opt-DF pdf:href.option  \" /DOSFile specifier
.als pdf:href.opt-MF pdf:href.option  \" /MacFile specifier
.als pdf:href.opt-UF pdf:href.option  \" /UnixFile specifier
.als pdf:href.opt-WF pdf:href.option  \" /WinFile specifier
.\"
.\" Macro "pdf:href.options.clear" ensures that ALL option
.\" argument strings are deleted, after "pdfhref" has completed
.\" all processing which depends on them
.\"
.de pdf:href.options.clear
.\" -----------------------------------------------------------------
.\" Usage:
.\"   .pdf:href.options.clear [option ...]
.\" -----------------------------------------------------------------
.\"
.\" When an option list is specified ...
.\"
.ie \\n(.$ \{\
.   \"
.   \" then loop through the list,
.   \" deleting each specified option argument string in turn
.   \"
.   while \\n(.$ \{\
.      if dpdf:href-\\$1 .rm pdf:href-\\$1
.      shift
.      \}
.   \}
.\"
.\" ... but when no list is specified,
.\" then recurse, to clear all known option argument strings
.\"
.el .pdf:href.options.clear A D F N P T DF MF UF WF
..
.\"
.\" "PDFHREF.INFO" establishes the content of the cross reference
.\" data record, which is exported via the "stderr" stream, when a
.\" cross reference anchor is created using a "pdfhref" macro request
.\" of the form
.\"
.\"    .pdfhref M -N name -X text ...
.\"
.\"    .ds PDFHREF.INFO \\*[PDFHREF.NAME] reference data ...
.\"
.ds PDFHREF.INFO page \\n% \\$*
.\"
.\" Macro "pdf*href-M" is the handler invoked by "pdfhref", when
.\" called with the "M" reference class specifier, to create a
.\" named cross reference mark, and to emit a cross reference
.\" data record, as specified by "PDFHREF.INFO".
.\"
.de pdf*href-M
.\" -----------------------------------------------------------------
.\" Usage:
.\"   .pdfhref M [-X] [-N name | -D name] [-E] descriptive text ...
.\" -----------------------------------------------------------------
.\"
.\" Initially, declare the -D and -N string options as empty,
.\" so we avoid warning messages when we try to use them, and find
.\" that they are undefined.
.\"
.ds pdf:href-D
.ds pdf:href-N
.\"
.\" Parse, interpret, and strip any specified options from the
.\" argument list.  (Note that only options with a declared handler
.\" will be processed; there is no provision for detecting invalid
.\" options -- anything which is not recognised is assumed to start
.\" the "descriptive text" component of the argument list).
.\"
.while dpdf:href.opt\\$1 \{\
.   pdf:href.opt\\$1 \\$@
.   shift \\n[pdf:href.argc]
.   \}
.\"
.\" If we found "--", to mark the end of the options,
.\" then we should discard it.
.\"
.if '\\$1'--' .shift
.\"
.\" All PDF reference markers MUST be named. The name may have been
.\" supplied using the "-N Name" option, (or the "-D Name" option);
.\" if not, deduce it from the first "word" in the "descriptive text",
.\" if any, and set the marker -- if we still can't identify the name
.\" for the destination, then this marker will not be created.
.\"
.pdf*href.set \\*[pdf:href-N] \\*[pdf:href-D] \\$1
.\"
.\" If we specified a cross reference, with the "-X" option, and the
.\" reference mark has been successfully created, then we now need to
.\" write the cross reference info to the STDERR stream
.\"
.if \\n[pdf:href-X] .pdf*href.export \\*[PDFHREF.INFO]
.\"
.\" Irrespective of whether this marker is created, or not,
.\" the descriptive text will be copied to the groff output stream,
.\" provided the "-E" option was specified
.\"
.if \\n[pdf:href-E] \&\\$*
..
.\"
.de pdf*href.set
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.pdf*href.map.init
.ie \\n(.$ \{\
.   \"
.   \" a marker name has been supplied ...
.   \" if we are formatting for immediate output,
.   \" emit PDFMARK code to establish the associated view
.   \"
.   ie '\\n(.z'' \{\
.      pdf:href.sety
.      pdfmark /Dest /\\$1 /View [\\*[PDFHREF.VIEW]] /DEST
.      ds PDFHREF.NAME \\$1
.      rr PDFPAGE.Y
.      \}
.   \"
.   \" but, when formatting a diversion ...
.   \" delay output of the PDFMARK code, until the diversion
.   \" is eventually written out
.   \"
.   el \!.\\$0 \\$@
.   \"
.   \" check if we also need to emit cross reference data
.   \" (caller will do this if "pdf:href-X" is set, but it is
.   \"  not necessary, when "pdf:href.map" already exists)
.   \"
.   if dpdf:href.map .nr pdf:href-X 0
.   \}
.el \{\
.   \" marker is unnamed ...
.   \" issue error message; do not emit reference data
.   \"
.   pdf:warn pdfhref destination marker must be named
.   nr pdf:href-X 0
.   \}
..
.de pdf*href.export
.\"
.\" Called ONLY by "pdf*href-M",
.\" this macro ensures that the emission of exported reference data
.\" is synchronised with the placement of the reference mark,
.\" especially when the mark is defined within a diversion.
.\"
.ie '\\n(.z'' .tm gropdf-info:href \\*[PDFHREF.NAME] \\$*
.el \!.\\$0 \\$@
..
.\"
.\" Macro "pdf*href-D" is invoked when "pdfhref" is called
.\" with the "D" reference class specifier; it provides a
.\" standardised mechanism for interpreting reference data
.\" exported by the "M" reference class, and may be used
.\" to directly define external reference data, without the
.\" use of "M" reference class designators in the source
.\" document.
.\"
.de pdf*href-D
.ds pdf:href-N
.\"
.\" Parse, interpret, and strip any specified options from the
.\" argument list.  (Note that only options with a declared handler
.\" will be processed; there is no provision for detecting invalid
.\" options -- anything which is not recognised is assumed to start
.\" the "descriptive text" component of the argument list).
.\"
.while dpdf:href.opt\\$1 \{\
.   pdf:href.opt\\$1 \\$@
.   shift \\n[pdf:href.argc]
.   \}
.\"
.\" If we found "--", to mark the end of the options,
.\" then we should discard it.
.\"
.if '\\$1'--' .shift
.\"
.ie '\\*[pdf:href-N]'' \{\
.   pdf:warn pdfhref defined reference requires a name
.   \}
.el \{\
.   ds pdf:href(\\*[pdf:href-N]).info \\$*
.   \}
..
.\"
.\" Macro "pdf*href-F" is invoked when "pdfhref" is called
.\" with the "F" reference class specifier; it allows the user
.\" to provide an alternative interpreter macro, which will be
.\" called when a "PDFHREF.INFO" record is retrieved to define
.\" the text of a cross reference link "hot spot".
.\"
.de pdf*href-F
.\" ----------------------------------------------------------------
.\" Usage:
.\"   .pdfhref F [macro-name]
.\" ----------------------------------------------------------------
.\"
.\" Set macro specified by "macro-name" as the format interpreter
.\" for parsing "PDFHREF.INFO" records; if "macro-name" is omitted,
.\" or is specified as the reserved name "default", then use the
.\" default format parser, "pdf*href.format", defined below.
.\"
.if '\\$1'default' .shift \\n(.$
.ie \\n(.$ .als pdf*href.format \\$1
.el .als pdf*href.format pdf*href.default
.nr pdf:argc 1
..
.\" The default reference formatting macro is defined below.
.\" It parses the "PDFHREF.INFO" record specific to each reference,
.\" recognising the keywords "file", "page" and "section", when they
.\" appear in initial key/value pairs, replacing the key/value pair
.\" with "PDFHREF.FILEREF", "PDFHREF.PAGEREF" or "PDFHREF.SECTREF"
.\" respectively; any additional data in the "PDFHREF.INFO" record
.\" is enclosed in typographic double quotes, and the parsed record
.\" is appended to "PDFHREF.PREFIX", to be returned as the formatted
.\" reference text.
.\"
.\" Default definitions for the reference strings "PDFHREF.PREFIX",
.\" "PDFHREF.FILEREF", "PDFHREF.PAGEREF" and "PDFHREF.SECTREF" are
.\" provided, in the English language.  Users may substitute any
.\" desired alternative definitions, for example, when formatting
.\" documents in other languages.  In each case, "\\$1" may be used
.\" in the substitution, to represent the "value" component of the
.\" respective key/value pair specified in the "PDFHREF.INFO" record.
.\"
.ds PDFHREF.PREFIX   see
.ds PDFHREF.PAGEREF  page \\$1,
.ds PDFHREF.SECTREF  section \\$1,
.ds PDFHREF.FILEREF  \\$1
.\"
.de pdf*href.format
.\" -----------------------------------------------------------------
.\" Usage: (to be called ONLY by "pdfhref")
.\"   .pdf*href.format cross reference data ...
.\" -----------------------------------------------------------------
.\"
.\" This macro is responsible for defining the strings "PDFHREF.TEXT"
.\" and "PDFHREF.DESC", which are used by the "pdfhref" macro, as the
.\" basis for generating the text content of a link "hot spot"; (any
.\" user specified alternate formatter MUST do likewise).
.\"
.\" Note that "PDFHREF.TEXT" defines the overall format for the "link
.\" text", while "PDFHREF.DESC" is the descriptive component thereof.
.\"
.\" This default implementation, subject to user customisation of the
.\" "internationalisation" strings defined above, formats "hot spots"
.\" of the style
.\"
.\"    see page N, section S, "descriptive text ..."
.\"
.ds PDFHREF.TEXT \\*[PDFHREF.PREFIX]
.while d\\$0.\\$1 \{\
.   \\$0.\\$1 "\\$2"
.   shift 2
.   \}
.\"
.\" Retrieve the descriptive text from the cross reference data,
.\" ONLY IF no overriding description has been set by the calling
.\" "pdfhref" macro invocation.
.\"
.if \\n(.$ .if !dPDFHREF.DESC .ds PDFHREF.DESC \\$*
.\"
.\" Augment "PDFHREF.TEXT" so the descriptive text will be included
.\" in the text of the formatted reference
.\"
.if dPDFHREF.DESC .as PDFHREF.TEXT " \(lq\\\\*[PDFHREF.DESC]\(rq
.\"
.\" Finally, suppress any leading spaces,
.\" which may have been included in the PDFHREF.TEXT definition.
.\"
.ds PDFHREF.TEXT \\*[PDFHREF.TEXT]
..
.de pdf*href.format.file
.\" ----------------------------------------------------------------------
.\" Include a file identifier in a formatted reference.
.\" This is invoked ONLY by "pdf*href.format", and ONLY IF the
.\" reference data includes an initial file identifier tuple.
.\" ----------------------------------------------------------------------
.\"
.as PDFHREF.TEXT " \\*[PDFHREF.FILEREF]
..
.de pdf*href.format.page
.\" ----------------------------------------------------------------------
.\" Include a page number in a formatted reference.
.\" This is invoked ONLY by "pdf*href.format", and ONLY IF the
.\" reference data includes an initial page number tuple.
.\" ----------------------------------------------------------------------
.\"
.as PDFHREF.TEXT " \\*[PDFHREF.PAGEREF]
..
.de pdf*href.format.section
.\" ----------------------------------------------------------------------
.\" Include a section number in a formatted reference.
.\" This is invoked ONLY by "pdf*href.format", and ONLY IF the
.\" reference data includes an initial section number tuple.
.\" ----------------------------------------------------------------------
.\"
.as PDFHREF.TEXT " \\*[PDFHREF.SECTREF]
..
.\"
.\" Make "pdf*href.format" the default cross reference formatter
.\"
.als pdf*href.default pdf*href.format
.\"
.\"
.\" Macro "pdf*href" provides a generic mechanism for placing link
.\" "hot-spots" in a PDF document.  ALL "pdfhref" class macros which
.\" create "hot-spots" are aliased to this macro; each must also have
.\" an appropriately aliased definition for "pdf*href.template".
.\"
.de pdf*href
.\" ------------------------------------------------------------------
.\" Usage:
.\"   .pdf*href class [options ...] [link text ...]
.\" ------------------------------------------------------------------
.\"
.\" First, we initialise an empty string, which will be affixed to
.\" the end of the "link text".  (This is needed to cancel the effect
.\" of a "\c" escape, which is placed at the end of the "link text"
.\" to support the "-A" option -- any text supplied by the user, when
.\" the "-A" option is specified, will replace this empty string).
.\"
.ds pdf:href-A
.\"
.\" Now we interpret, and remove any specified options from the
.\" argument list.  (Note that only options with a declared handler
.\" will be processed;  there is no provision for detecting invalid
.\" options -- anything which is not recognised is assumed to start
.\" the "link text" component of the argument list).
.\"
.while dpdf:href.opt\\$1 \{\
.   pdf:href.opt\\$1 \\$@
.   shift \\n[pdf:href.argc]
.   \}
.\"
.\" If we found "--", to mark the end of the options, then we should
.\" discard it.
.\"
.if '\\$1'--' .shift
.\"
.\" All PDF link classes REQUIRE a named destination.  This may have
.\" been supplied using the "-D Name" option, but, if not, deduce it
.\" from the first "word" in the "link text", if any -- if we still
.\" can't identify the destination, then set "pdf:href.ok" to zero,
.\" so this link will not be created.
.\"
.if !dpdf:href-D .pdf:href.option -D \\$1
.if '\\*[pdf:href-D]'' \{\
.   pdf:error pdfhref has no destination
.   nr pdf:href.ok 0
.   \}
.\"
.\" Some PDF link classes support a "/File (FilePathName)" argument.
.\"
.if dpdf*href.file \{\
.   \"
.   \" When this is supported, it may be specified by supplying
.   \" the "-F FileName" option, which is captured in "pdf:href-F".
.   \"
.   if dpdf:href-F \{\
.      \"
.      \" the /File key is present, so set up the link specification
.      \" to establish the reference to the specified file
.      \"
.      als pdf*href.link pdf*href.file
.      ds pdf:href.files /File (\\*[pdf:href-F])
.      \"
.      \" in addition to the /File key,
.      \" there may also be platform dependent alternate file names
.      \"
.      if dpdf:href-DF .as pdf:href.files " /DOSFile (\\*[pdf:href-DF])
.      if dpdf:href-MF .as pdf:href.files " /MacFile (\\*[pdf:href-MF])
.      if dpdf:href-UF .as pdf:href.files " /UnixFile (\\*[pdf:href-UF])
.      if dpdf:href-WF .as pdf:href.files " /WinFile (\\*[pdf:href-WF])
.      \}
.   \" In some cases, the "/File" key is REQUIRED.
.   \" We will know it is missing, if "pdf*href.link" is not defined.
.   \"
.   if !dpdf*href.link \{\
.   \"
.   \" When a REQUIRED "/File" key specification is not supplied,
.   \" then complain, and set "pdf:href.ok" to abort the creation
.   \" of the current reference.
.   \"
.      pdf:error pdfhref: required -F specification omitted
.      nr pdf:href.ok 0
.      \}
.   \" Now, we have no further use for "pdf*href.file".
.   \"
.   rm pdf*href.file
.   \}
.\"
.\" Now, initialise a string, defining the PDFMARK code sequence
.\" to create the reference, using the appropriate type indicators.
.\"
.ds pdf:href.link /Subtype /Link \\*[pdf*href.link]
.\"
.\" And now, we have no further use for "pdf*href.link".
.\"
.rm pdf*href.link
.\"
.\" If the user specified any "link prefix" text, (using the "-P text"
.\" option), then emit it BEFORE processing the "link text" itself.
.\"
.if dpdf:href-P \&\\*[pdf:href-P]\c
.ie \\n[pdf:href.ok] \{\
.   \"
.   \" This link is VALID (so far as we can determine) ...
.   \" Modify the "link text" argument specification, as required,
.   \" to include any pre-formatted cross reference information
.   \"
.   ie \\n(.$ \{\
.      \"
.      \" One or more "link text" argument(s) are present,
.      \" so, set the link description from the argument(s) ...
.      \"
.      ds PDFHREF.DESC \\\\$*
.      ie \\n[pdf:href-X] \{\
.         \"
.         \" ... and, when the "-X" flag is set,
.         \" also include formatted location information,
.         \" derived from the cross reference record.
.         \"
.         pdf*href.format \\*[pdf:href(\\*[pdf:href-D]).info]
.         \}
.      el \{\
.         \" ... but, when the "-X" flag is NOT set,
.         \" use only the argument(s) as the entire content
.         \" of the "link text"
.         \"
.         rn PDFHREF.DESC PDFHREF.TEXT
.         \}
.      \}
.   el \{\
.      \" No "link text" arguments are present,
.      \" so, format the cross reference record to define
.      \" the content of the "link text".
.      \"
.      pdf*href.format \\*[pdf:href(\\*[pdf:href-D]).info]
.      \}
.   \" Apply border and colour specifications to the PDFMARK string
.   \" definition, as required.
.   \"
.   if dPDFHREF.BORDER .as pdf:href.link " /Border [\\*[PDFHREF.BORDER]]
.   if dPDFHREF.COLOUR .as pdf:href.link " /Color  [\\*[PDFHREF.COLOUR]]
.   \"
.   \" Emit the "link text", in its appropriate colour, marking the
.   \" limits of its bounding box(es), as the before and after output
.   \" text positions.
.   \"
.   pdf*href.mark.begin "\\*[pdf:href.link]"
.   if dPDFHREF.COLOUR .defcolor pdf:href.colour rgb \\*[PDFHREF.COLOUR]
.   nop \&\m[\\*[PDFHREF.TEXT.COLOUR]]\\*[PDFHREF.TEXT]\m[]\c
.   pdf*href.mark.end
.   \"
.   \" Clean up the temporary registers and strings, used to
.   \" compute the "hot-spot" bounds, and format the reference,
.   \"
.   rm PDFHREF.DESC PDFHREF.TEXT
.   \}
.\"
.\" But when we identify an INVALID link ...
.\" We simply emit the "link text", with no colour change, no border,
.\" and no associated "hot-spot".
.\"
.el \&\\$*\c
.\"
.\" And then, if the user specified any affixed text, (using the
.\" "-A text" option), we tack it on at the end.
.\"
.nop \&\\*[pdf:href-A]
..
.de pdf*href.map.init
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.\"
.if dpdf:href.map-1 \{\
.   \"
.   \" We have a reference map, but we haven't started to parse it yet.
.   \" This must be the first map reference in pass 2, so we need to
.   \" "kick-start" the parsing process, by loading the first indexed
.   \" sub-map into the global map.
.   \"
.   rn pdf:href.map-1 pdf:href.map
.   als pdf:href.map.internal pdf:href.map
.   nr pdf:href.map.index 1 1
.   \}
.als pdf*href.map.init pdf*href.mark.idle
..
.\"
.\" "pdf*href-Z" is used to add link co-ordinate entries to the
.\" "pdf:href.map".  Primarily, it is used by the "pdfroff" formatter,
.\" to pass link co-ordinate data from one "groff" formatting pass to
.\" the next, and is not generally useful to the end user.
.\"
.de pdf*href-Z
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   .pdfhref Z page-index x-displacement y-displacement
.\" Where:
.\"   page-index      is the reference mark's page number
.\"   x-displacement  is its offset from the left edge of the page
.\"   y-displacement  is its offset from the top edge of the page
.\" ( both displacement values are expressed in basic groff units, )
.\" ( and measured perpendicular to their respective page edges.   )
.\" ----------------------------------------------------------------------
.\"
.ie \\n(.$=3 .ds pdf:href.map-\\n+[pdf*href-Z.index] \\$*
.el .pdf:error pdfhref Z operator expects exactly three arguments
..
.\" Initialise the auto-incrementing "pdf*href-Z.index" register,
.\" to ensure that sub-map numbering starts at 1.
.\"
.nr pdf*href-Z.index 0 1
.\"
.de pdf*href.map.read
.\" ----------------------------------------------------------------------
.\" Usage: (internal use only):
.\"   .pdf*href.map.read co-ordinate name list ...
.\" ----------------------------------------------------------------------
.\"
.\" Reads values from "pdf:href.map" to each named register, in turn
.\" Reading to "null" discards the corresponding value in "pdf:href.map"
.\"
.while \\n(.$ \{\
.   \"
.   \" Loop over all registers named in the argument list,
.   \" assigning values from "pdf:href.map" to each in turn.
.   \"
.   pdf:pop nr pdf:\\$1 pdf:href.map.internal
.   if !dpdf:href.map.internal \{\
.      \"
.      \" We ran out of map references in the current sub-map,
.      \" so move on to the next indexed sub-map, if any.
.      \"
.      if dpdf:href.map-\\n+[pdf:href.map.index] \{\
.         rn pdf:href.map-\\n[pdf:href.map.index] pdf:href.map
.         als pdf:href.map.internal pdf:href.map
.         \}
.      \}
.   \"
.   \" Proceed to the next named co-ordinate, (if any), specified
.   \" in the argument list.
.   \"
.   shift
.   \}
.\"
.\" Discard any assignments to a register named "null"
.\"
.rr pdf:null
..
.de pdf*href.mark.begin
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.pdf*href.map.init
.ie dpdf:href.map \{\
.   \"
.   \" Once we have established a document reference map,
.   \" then this, and all subsequent calls to "pdf*href.mark.begin",
.   \" may be redirected to the reference mark resolver, and the
.   \" "pdf*href.mark.end" macro has nothing further to do.
.   \"
.   pdf*href.mark.resolve \\$@
.   als pdf*href.mark.begin pdf*href.mark.resolve
.   als pdf*href.mark.end pdf*href.mark.idle
.   \}
.el \{\
.   \" Since we don't yet have a document reference map, the
.   \" reference mark resolver will not work, in this pass of the
.   \" formatter;  this, and all subsequent calls to "pdf*href.mark.begin",
.   \" may be redirected to "pdf*href.mark.end", which is responsible
.   \" for emitting the reference mark data to be incorporated into
.   \" the reference map in a subsequent formatting pass.
.   \"
.   pdf*href.mark.end
.   als pdf*href.mark.begin pdf*href.mark.end
.   \}
..
.de pdf*href.mark.resolve
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.ie '\\n(.z'' \{\
.   ds pdf:href.link \\$1
.   nr pdf:urx \\n(.o+\\n(.l
.   pdf*href.map.read spg llx ury epg urx.end lly.end
.   ie \\n[pdf:spg]=\\n[pdf:epg] \{\
.      \"
.      \" This link is entirely contained on a single page ...
.      \" emit the text, which defines the content of the link region,
.      \" then make it active.
.      \"
.      pdf*href.mark.emit 1 \\n[pdf:urx.end]
.      if \\n[pdf:lly]<\\n[pdf:lly.end] \{\
.         \"
.         \" This link spans multiple output lines; we must save its
.         \" original end co-ordinates, then define a new intermediate
.         \" end point, to create a PDFMARK "hot-spot" extending from
.         \" the start of the link to the end if its first line.
.         \"
.         nr pdf:ury +1v
.         nr pdf:llx \\n(.o+\\n[.in]
.         nr pdf:lly \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT]
.         if \\n[pdf:ury]<\\n[pdf:lly] \{\
.            nr pdf:lly +\\*[PDFHREF.HEIGHT]-1v
.            pdf*href.mark.emit 2
.            nr pdf:ury \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT]
.            \}
.         pdf*href.mark.emit 0 \\n[pdf:urx.end]
.         \}
.      pdf*href.mark.flush
.      \}
.   el \{\
.      \" This link is split across a page break, so ...
.      \" We must mark the "hot-spot" region on the current page,
.      \" BEFORE we emit the link text, as we will have moved off
.      \" this page, by the time the text has been output.
.      \"
.      \" First step: define the region from the start of the link,
.      \" to the end of its first line.
.      \"
.      pdf*href.mark.emit 1 \\n[pdf:urx]
.      \"
.      \" All additional regions MUST align with the left margin.
.      \"
.      nr pdf:llx \\n(.o+\\n[.in]
.      \"
.      \" If the current page can accommodate more than the current line,
.      \" then it will include a second active region for this link; this
.      \" will extend from just below the current line to the end of page
.      \" trap, if any, or the bottom of the page otherwise, and occupy
.      \" the full width of the page, between the margins.
.      \"
.      nr pdf:ury +1v
.      pdf*href.mark.emit 3
.      \"
.      \" We now need a page transition trap, to map the active link
.      \" region(s), which overflow on to the following page(s); (the
.      \" handler for this trap MUST have been previously installed).
.      \"
.      ie dpdf*href.mark.hook \{\
.         \"
.         \" The page transition trap handler has been installed,
.         \" so we may activate both it, and also the appropriate
.         \" termination handler, to deactivate it when done.
.         \"
.         als pdf*href.mark.hook pdf*href.mark.trap
.         \"
.         \" Now we set up "pdf:epg" to count the number of page breaks
.         \" which this link will span, and emit the link text, leaving
.         \" the page trap macro to map active regions on intervening
.         \" pages, which are included in the link.
.         \"
.         nr pdf:epg -\\n[pdf:spg] 1
.         \}
.      el \{\
.         \" There was no handler initialised for the page trap,
.         \" so we are unable to map the active regions for this link;
.         \" we may discard the remaining map data for this link,
.         \" and issue a diagnostic.
.         \"
.         pdf:error pdfhref: link dissociated at page break (trap not initialised)
.         if dPDFHREF.BROKEN.COLOR \{\
.            \"
.            \" The user may opt to have such broken links highlighted.
.            \" We use "PDFHREF.BROKEN.COLOUR" to specify this requirement,
.            \" but the user may prefer the American spelling, so we will
.            \" handle both as equivalent.
.            \"
.            als PDFHREF.BROKEN.COLOUR PDFHREF.BROKEN.COLOR
.            \}
.         if dPDFHREF.BROKEN.COLOUR \{\
.            if dPDFHREF.COLOUR .als PDFHREF.COLOUR PDFHREF.BROKEN.COLOUR
.            \}
.         \}
.      \}
.   \}
.el \!.\\$0 \\$@
..
.\"
.\" Macro "pdf*href.mark.emit" is called only by "pdf*href".  It is
.\" responsible for emitting the PDFMARK code, to establish the
.\" "hot-spot" region associated with a document or resource link.
.\"
.de pdf*href.mark.emit
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   .pdf*href.mark.emit <action> [<end-urx>]
.\"     <action> == 0 --> normal operation -- link height = 1 line
.\"     <action> == 1 --> start of link -- add leading above text
.\"     <action> == 2 --> overtall link -- set intermediate baseline
.\"     <action> == 3 --> split link -- break at bottom of page
.\" ----------------------------------------------------------------------
.\"
.if \\$1=1 \{\
.   \"
.   \" Initialising a new link region ...
.   \" Some different versions of "groff" disagree about the vertical
.   \" displacement of "opminy", as emitted by "\O1|\h'-\w"|"u'\O2\c",
.   \" relative to the current text baseline.  Therefore, recompute
.   \" the link displacement, independently of "opminy".
.   \"
.   mk pdf:ury.base
.   while \\n[pdf:ury.base]<\\n[pdf:ury] .nr pdf:ury.base +1v
.   nr pdf:ury.base -1m+\\n[PDFHREF.LEADING]
.   \"
.   \" adjust the end-point vertical displacement by the same offset,
.   \" and then relocate the link starting point to its new displacement,
.   \" as established by this base line relative computation.
.   \" 
.   nr pdf:lly.end +\\n[pdf:ury.base]-\\n[pdf:ury]+\\*[PDFHREF.HEIGHT]
.   rnn pdf:ury.base pdf:ury
.   \}
.if \\$1<2 \{\
.   \"
.   \" Link segment fits on a single line ...
.   \" Set its height and end-point horizontal displacement accordingly.
.   \"
.   nr pdf:lly \\n[pdf:ury]+\\*[PDFHREF.HEIGHT]
.   if \\n[pdf:lly]>=\\n[pdf:lly.end] .nr pdf:urx \\$2
.   \}
.ie \\$1=3 \{\
.   \"
.   \" Link segment extends beyond the next page break ...
.   \" Recompute truncated height, to just fit portion on current page,
.   \" recursing to emit it, and leaving page trap mechanism to place
.   \" continuation region(s) on following page(s).
.   \"
.   nr pdf:lly (\\n[.t]u-\\n[.V]u)/1v
.   if \\n[pdf:lly]>0 \{\
.      nr pdf:lly \\n[pdf:ury]+\\n[pdf:lly]v-1v+\\*[PDFHREF.HEIGHT]
.      pdf*href.mark.emit 2
.      \}
.   \}
.el \{\
.   \" Link region size and placement has been fully specified ...
.   \" Emit it.
.   \"
.   pdfmark \\*[pdf:href.link] /Rect [\\*[pdf:bbox]] /ANN
.   \}
..
.\"
.\" When "pdf*href" emits a link for which the "hot-spot" spans a
.\" page break, then we need to provide a "hook" in to the page break
.\" trap, so we can map the "hot-spot" regions which are to be placed
.\" on either side of the page break.
.\"
.\" Macro "pdf*href.mark.idle" is a dummy macro, which provide this
.\" "hook" for normal page breaks, where there is no link "hot-spot"
.\" crossing the break.
.\"
.de pdf*href.mark.idle
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   Called only as an internal hook, by a page trap macro.
.\"   Expects no arguments, and does nothing.
.\" ----------------------------------------------------------------------
..
.\"
.\" Macro "pdf*href.mark.trap" is the active "hook", which is substituted
.\" for "pdf*href,mark.idle" at those page breaks which are crossed by
.\" a link "hot-spot".
.\"
.de pdf*href.mark.trap
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   Called only as an internal hook, by a page trap macro.
.\"   Expects no arguments.  Maps residual link "hot-spot" regions,
.\"   which spill beyond any page break.  Not to be invoked directly
.\"   by the user, nor by any user supplied macro.
.\" ----------------------------------------------------------------------
.\"
.mk pdf:ury
.nr pdf:ury +1v-1m-\\n[PDFHREF.LEADING]
.ie \\n-[pdf:epg] \{\
.   \"
.   \" The link "hot-spot" extends across more than one page break,
.   \" so, for each page which is completely contained within the
.   \" extent of the link, simply mark the entire text area on the
.   \" page as a "hot-spot".
.   \"
.   pdf*href.mark.emit 3
.   \}
.el \{\
.   \" The link "hot-spot" ends on the page which immediately follows
.   \" the current page transition, so we may now finalise this link.
.   \"
.   nr pdf:lly \\n[pdf:ury]+\\*[PDFHREF.HEIGHT]
.   if \\n[pdf:lly.end]>\\n[pdf:lly] \{\
.      \"
.      \" The "hot-spot" extends beyond the first line of text,
.      \" on its final page; compute and emit "hot-spot" region to cover
.      \" the full with of the text area, including all but the last
.      \" line of the link text.
.      \"
.      while \\n[pdf:lly.end]>\\n[pdf:lly] .nr pdf:lly +1v
.      nr pdf:lly -1v
.      pdf*href.mark.emit 2
.      \"
.      \" Now, adjust the vertical "hot-spot" mapping reference,
.      \" to identify the correct position for the last line of
.      \" text, over which the "hot-spot" extends.
.      \"
.      nr pdf:ury \\n[pdf:lly.end]-\\*[PDFHREF.HEIGHT]
.      \}
.   \"
.   \" We now have exactly one final line of text, over which we must
.   \" emit a "hot-spot" region;  map it, terminate page trap processing
.   \" for this "hot-spot", and clean up the "hot-spot" mapping context.
.   \"
.   pdf*href.mark.emit 0 \\n[pdf:urx.end]
.   als pdf*href.mark.hook pdf*href.mark.idle
.   pdf*href.mark.flush
.   \}
..
.de pdf*href.mark.flush
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
.rr pdf:spg pdf:epg
.rr pdf:llx pdf:lly pdf:urx pdf:ury
.if dPDFHREF.COLOR .als PDFHREF.COLOUR PDFHREF.COLOR
.rr pdf:urx.end pdf:lly.end
..
.de pdf*href.mark.end
.\" ----------------------------------------------------------------------
.\" ----------------------------------------------------------------------
\O1\Z'|'\O2\c
..
.\" Macro "pdf*href-I" is used for one time initialisation of special
.\" "pdfhref" features; (currently, only the above page trap hook is
.\" supported, but it is implemented with one level of indirection, to
.\" accommodate possible future expansion).
.
.de pdf*href-I
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   .pdfhref I -<option> <optarg> [-<option> <optarg>] ...
.\" ----------------------------------------------------------------------
.\"
.\" Loop over all arguments, in pairs ...
.
.while \\n(.$ \{\
.   \"
.   \" handing them off to their respective initialisers,
.   \" when suitable initialisers exist, or complaining otherwise.
.   \"
.   ie dpdf*href\\$1.init .pdf*href\\$1.init \\$2
.   el .pdf*error pdfhref:init: unknown feature '\\$1'
.   shift 2
.   \}
..
.\" Before we can use the page break "hook", we need to initialise it
.\" as an addendum to a regular page break trap. To ensure that we don't
.\" compromise the user's page trap setup, we leave the onus for this
.\" initialisation with the user, but we provide the "pdf*href-PT.init"
.\" macro, (invoked by ".pdfhref I -PT <macro-name>"), to implement a
.\" suitable initialisation action.
.
.de pdf*href-PT.init
.\" ----------------------------------------------------------------------
.\" Usage:
.\"   .pdfhref I -PT <macro-name>
.\"     <macro-name> == name of user's page break trap macro
.\" ----------------------------------------------------------------------
.\"
.\" Initially, map the page break hook to its default, do nothing helper.
.
.als pdf*href.mark.hook pdf*href.mark.idle
.ie !\\n(.$ \{\
.   \"
.   \" Don't have enough arguments to specify a page trap macro name,
.   \" so simply plant "pdf*href.mark.hook" as a top of page trap.
.   \"
.   wh 0 pdf*href.mark.hook
.   \}
.el \{\
.   \" Page trap macro name is specified in "\\$1" ...
.   \"
.   ie d\\$1 \{\
.      \"
.      \" When this page trap macro already exists, then we simply
.      \" append a call to "pdf*href.mark.hook" to it.
.      \"
.      am \\$1 pdf*href.mark.idle
.         pdf*href.mark.hook
.         pdf*href.mark.idle
.      \}
.   el \{\
.      \" However, when the specified page trap macro does not yet
.      \" exist, then we create it, and plant it as a top of page
.      \" trap.
.      \"
.      de \\$1 pdf*href.mark.idle
.         pdf*href.mark.hook
.         pdf*href.mark.idle
.      wh 0 \\$1
.      \}
.   \}
..
.
.\" "pdf*href-L" is the generic handler for creating references to
.\" named destinations in PDF documents.  It supports both local
.\" references, to locations within the same document, through its
.\" "pdf*href-L.link" attribute, and also references to locations
.\" in any other PDF document, through "pdf*href-L.file".
.\"
.als pdf*href-L      pdf*href
.ds  pdf*href-L.link /Dest /\\\\*[pdf:href-D]
.ds  pdf*href-L.file /Action /GoToR \\\\*[pdf:href.files] \\*[pdf*href-L.link]
.\"
.\" "pdf*href-O" is the "official" handler for creating PDF
.\" document outlines.  It is simply an alias to "pdfbookmark",
.\" which may also be invoked directly, if preferred.  Neither
.\" a "pdf*href-O.link" nor a "pdf*href-O.file" attribute is
.\" required.
.\"
.als pdf*href-O      pdfbookmark
.\"
.\" "pdf*href-W" is the generic handler for creating references to
.\" web resources, (or any resource specified by a uniform resource
.\" identifier).  Such resource links are fully specified by the
.\" "pdf*href-W.link" attribute.
.\"
.als pdf*href-W      pdf*href
.ds  pdf*href-W.link /Action << /Subtype /URI /URI (\\\\*[pdf:href-D]) >>
.\"
.\" Local Variables:
.\" mode: nroff
.\" End:
.\" vim: filetype=groff:
.\" pdfmark.tmac: end of file