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
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
|
%% OPmac -- Olsak's PlainTeX macros
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Tech-documentation, Petr Olsak, 2012
\input utf8off \clearmubyte
% This file is encoded in ISO-8859-2 because
% UTF-8 encTeX has a conflict with DocByTeX
\def\projectversion{Dec. 2012}
\def\headtitle{OPmac}
\widowpenalty=10000
\clubpenalty=10000
\emergencystretch=2em
\hbadness=2200
\showboxbreadth=1500 \showboxdepth=2
\def\csplain{\CS plain}
\def\CS{CS}
\input docby.tex
\setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace}
\noactive{\nb fontdimen}
\title OPmac -- roz¹iøující makra plain\TeX{}u
\author Petr Ol¹ák
\centerline{\ulink[http://www.olsak.net/opmac.html]%
{www.olsak.net/opmac.html}}
\def\db{\dg\nb}
\def\du#1{\api{\nb#1}}
\let\quotehook=\langleactive
\def\insdef#1 {\ifirst{docby.tex}{def\nb#1 }{^^B\cbrace}{++}}
\def\inssdef#1 {\ifirst{docby.tex}{def\nb#1}{\empty}{+-}}
\bgroup
\catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12
\gdef\obrace[{] \gdef\cbrace[}]
\egroup
\def\indexhook{%
% Kontrolní sekvence oznaèené ¹ipkou ($\succ$) jsou
% u¾ivatelskými pøíkazy. Ostatní kontrolní sekvence jsou
% v~OPmac interní.
Tuènì je oznaèena strana, kde je slovo
dokumentováno, pak následuje seznam v¹ech stran, na kterých se slovo
vyskytuje.
% U¾ivatelské pøíkazy mají v seznamu stránek podtr¾ené èíslo,
% co¾ je stránka, kde je pøíkaz vylo¾en na u¾ivatelské úrovni.
\medskip}
\def\nn#1 {\noactive{\nb#1}} \nn insert \nn undefined
\def\cnvbookmark#1{\lowercase{\lowercase{#1}}}
\def\bookmarkshook{\lo ìe \lo ¹s \lo èc \lo ør \lo ¾z \lo ýy
\lo áa \lo íi \lo ée \lo úu \lo ùu \lo óo \lo òn }
\def\lo #1#2{\lccode`#1=`#2}
\dotoc \bookmarks
\sec Úvod
%%%%%%%%%
OPmac je balík jednoduchých doplòujících maker k plain\TeX{}u umo¾òující
u¾ivatelùm základní La\TeX{}ovou funkcionalitu: zmìny velikosti písma,
automatickou tvorbu obsahu a rejstøíku, práci s {\tt bib} databázemi,
referencemi, mo¾nost prolo¾ení referencí hyperlinkovými odkazy atd.
\sec U¾ivatelská dokumentace
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
U¾ivatelská dokumentace je zatím v souboru "opmac-u.tex" a "opmac-u.pdf".
Do tohoto místa ji zahrnu pozdìji a prolinkuji ji s technickou dokumentací.
\sec Technická dokumentace
%%%%%%%%%%%%%%%%%%%%%%%%%%
Tato èást dokumentace je urèena pro tvùrce maker, kteøí se chtìjí zde
uvedenými makry inspirovat a pøípadnì je pøizpùsobit svému po¾adavku.
Pøedpokládá se znalost \TeX{}u, tj. napøíklad aspoò zbì¾ná orientace
v~\TeX{}booku naruby. Na tuto knihu je na mnoha místech odkazováno pod
zkratkou TBN.
\subsec Základní makra
%%%%%%%%%%%%%%%%%%%%%%
Na zaèátku souboru "opmac.tex" zjistíme, zda není soubor ètený podruhé. V
takovém pøípadì ètení odmítneme. Ptáme se na to, zda je definováno makro \db
OPmacversion, které vzápìtí definujeme. Je-li nìkdo pøekvapen, proè jsem
nepou¾il "\expandafter\endpinput\fi", mù¾e si prostudovat TBN, stranu~358,
heslo "\endinput".
\ifirst {opmac.tex}{OPmacversion}{\empty}{+-}
Dva pracovní registry:
\dgn \nb tmpnum
\dgn \nb tmpdim
\inext {newcount}{\empty}{+-}
OPmac nebude nikdy hlásit chyby. Èasto ale bude psát pomocí \db opwarning
na terminál varování.
\inext {warning}{\empty}{+-}
Makro \db addto "<makro>{<tokeny>}" pøidá na konec "<makra>" dané "<tokeny>".
\inext {addto}{\empty}{+-}
V OPmac budeme pracovat se seznamem \db protectlist, který bude obsahovat
makra, je¾ chceme mít tzv. robustní, tj. chceme, aby se pøi "\write" v output
rutinì neexpandovala. Ka¾dému makru v~seznamu pøedchází "\doprotect", tak¾e
seznam "\protectlist" vypadá takto:
\def\begtthook{\langleactive}
\begtt
\doprotect<makro1> \doprotect<makro2> ...
\endtt
\def\begtthook{}
Seznam budeme spou¹tìt v output rutinì s tím, ¾e "\doprotect" tam bude mít
význam makra, které zaøídí, aby jeho parametr získal význam "\relax".
Tím bude zabránìno jeho expanzi.
Naprogramujeme \db addprotect "<makro>", které zaøídí vlo¾ení "<makra>" do seznamu.
\inext {protectlist}{\empty}{+-}
Nìkterá makra budou fungovat jen v pdf\TeX{}u pøi nastaveném "\pdfoutput=1".
Pøipravíme si tedy test \db ifpdftex, který pak pou¾ijeme pøi ètení souboru
"opmac.tex". Test nikdy nebudeme vkládat do maker, tak¾e pøi ètení souboru
"opmac.tex" u¾ musí být jasné, zda bude výstup smìrován do DVI nebo PDF.
Pozdìj¹í zmìna "\pdfoutput" mù¾e zpùsobit potí¾e.
\inext {ifpdftex}{\empty}{+-}
Makra \db sdef a \db sxdef umo¾òují pohodlnì definovat kontrolní sekvence
ohranièené pomocí "\csname...\endcsname".
\inext {sdef}{\empty}{+-}
Makro \db adef umo¾ní nastavit znak na aktivní a rovnou ho definovat, co¾
normálnì uvnitø maker není jednoduché (TBN str. 25 a 26).
Vyu¾ijeme toho, ¾e "~" je aktivní znak a pomocí
"\lccode" a "\lowercase" jej pøepí¹eme na po¾adovaný znak. Dostaneme tím
aktivní token s po¾adovanou ASCII hodnotou a tento token definujeme.
Pomocí \db lccodetiezero vrátíme po pøiøazení (tj. po provedení definice)
"\lccode" vlnky na pùvodní hodnotu.
\inext {adef}{\empty}{+-}
Makrem \db isdefined "{<jméno>}\iftrue" se ptáme, zda je definovaná
"\csname<jméno>\endcsname". To závìreèné pøipojené "\iftrue" makro se¾ere,
ale u¾ivatel ho pí¹e zejména z toho dùvodu, aby mu tato konstrukce fungovala
uvnitø vnoøených "\if..\fi"
\inext {isdefined}{^^B\cbrace}{++}
Makro \db isinlist "<list>{<tokeny>}\iftrue" zjistí, zda "<tokeny>" jsou
(jako string) obsa¾eny v~makru "<list>". Pøitom se¾ere "\iftrue" ze stejných
dùvodù, jak je uvedeno pøed chvílí.
\inext {isinlist}{^^B\cbrace}{++}
Makro \db isnextchar "<znak>{<co dìlat pøi ano>}{<co dìlat pøi ne>}"
pracuje ponìkud odli¹nì od pøedchozích maker. Zjistí, zda následující znak
je "<znak>" a pokud ano, vykoná vnitøek první závorky, jinak vykoná vnitøek
druhé závorky. Pomocí "\futurelet" ulo¾í zkoumaný znak do "\next" a spustí
\db isnextcharA.
\inext {isnextchar}{\empty}{+-}
Pøedefinujeme makro \db uv z \csplain{}u. Tam je toto makro navr¾eno tak, aby
mohlo mít za svùj parametr verbatim text. Dùsledkem toho nefunguje správnì
kerning. Pova¾uji za lep¹í mít správnì kerning a pøípadné uvozování verbatim
textù øe¹it tøeba pomocí "\clqq...\crqq".
\inext {def\nb uv}{}{+-}
Knuth v souboru "plain.tex" zanechal øídicí sekvenci "\\" v provizorním stavu
(cvièení: podívejte se v jakém). Domnívám se, ¾e je lep¹í ji dát jednoznaèný
význam "\undefined". Nìkterým u¾ivatelùm toti¾ mù¾e OPmac pøipomínat
La\TeX{} a není tedy vylouèeno, ¾e je napadne psát "\\". Mìli by na to
dostat jednoznaènou odpovìï: {\tt undefined control sequence}.
\inext {undefined}{}{+-}
Do pracovního souboru urèeného k novému naètení budeme chtít vlo¾it
komentáøe za znakem procento. K tomu potøebujeme mít procento jako obyèejný
znak kategorie 12. Na tento znak se v na¹em kódu pøeklopí otazník, Tak¾e
\db percent expanduje na znak procento s kategorií 12.
\inext {percent}{}{+-}
Makro plain\TeX{}u "\,"
funguje jen v matematické sazbì. U¾ivatel bude chtít makro èasto
pou¾ít napøíklad mezi èíslem a jednotkou v textovém módu: "5\,mm", tak¾e
makro pøedefinujeme.
\inext {kern}{}{+-}
\subsec Globální parametry
%%%%%%%%%%%%%%%%%%%%%%%%%%
Zaká¾eme vdovy a sirotky a dále nastavíme registry pro listingy tiskového
materiálu na smysluplnìj¹í hodnoty, ne¾ jsou implicitní.
\inext {widowpenalty}{\empty}{+-}
Následující makra a registry ovlivní chování klíèových maker OPmac zpùsobem,
jak je popsáno v~komentáøích. Mnohé z tìchto maker a registrù byly zmínìny v
u¾ivatelské dokumentaci.
\dgn \nb iindent
\dgn \nb ttindent
\dgn \nb ttskip
\dgn \nb ttpenalty
\dgn \nb tthook
\dgn \nb intthook
\dgn \nb iiskip
\dgn \nb bibskip
\dgn \nb tabstrut
\dgn \nb tabiteml
\dgn \nb tabitemr
\dgn \nb vvkern
\dgn \nb hhkern
\dgn \nb multiskip
\dgn \nb colsep
\dgn \nb mnoteindent
\dgn \nb mnotesize
\dgn \nb picdir
\dgn \nb bibtexhook
\dgn \nb chaphook
\dgn \nb sechook
\dgn \nb secchook
\dgn \nb cnvhook
\dgn \nb pghook
\inext {newdimen}{\count=7 \empty}{+-}
\subsec Loga
%%%%%%%%%%%%
V logu \db OPmac je pomocí "\thefontscale" zvìt¹eno písmeno O. Logo \db CS je
pøepsáno beze zmìny z CS\TeX{}u. Tím snadno vytvoøíme i logo \db csplain.
\inext {def\nb OPmac}{\empty}{+-}
Troufám si tvrdit, ¾e logo \db LaTeX (aèkoli je plain\TeX{}isté asi moc
nebudou potøebovat) je v~následujícím kódu daleko lépe øe¹eno, ne¾ v
samotném La\TeX{}u. Poèítá toti¾ ve spolupráci s makrem \db slantcorr
i se sklonem písma pøi usazování zmen¹eného A.
\inext {LaTeX}{\empty}{+-}
Loga se obèas mohou vyskytnout v nadpisech. Zabezpeèíme je tedy proti
rozboøení pøi zápisu do REF souboru.
\inext {addprotect}{\empty}{+-}
\subsec Velikosti fontù, øádkování
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\csplain{} od verze "<Nov. 2012>" definuje makro
\db resizefont "<fontselector>", které zmìní
velikost fontu daného svým pøepínaèem a tento zmìnìný font si ponechá
stejný pøepínaè. Zmìna velikosti je dána obsahem makra \db sizespec.
Tam mù¾e být napøíklad napsáno "at13pt" nebo "scaled800".
Dále CSplain definuje makro
\db resizeall, které zmìní velikost
fontù s registrovanými pøepínaèi. Registrování se provádí makrem
\db regfont. Implicitnì jsou registrovány pøepínaèe
"\tenrm", "\tenit", "\tenbf", "\tenbi", a "\tentt".
Do nových velikostí tedy pùjdeme se starými názvy pøepínaèù
"\ten<nìco>" a to slovo "ten" budeme chápat jen jako historický relikt,
který nám ov¹em napoví, ¾e kontrolní sekvence je fontovým pøepínaèem.
OPmac si zjistí, zda je definovaný "\regfont". Pokud ne, upozorní na starou
verzi \csplain{}u na terminálu a potøebná makra si definuje. Je to kopie
kódu ze souboru "csfontsm.tex" z balíèku \csplain{}.
\inext {regfont}{\empty}{+-}
Implicitnì jsou zavedeny \CS{}fonty, tak¾e k nim pøidáme AMS fonty z
"ams-math.tex", které vizuálnì odpovídají. Pozdìji si mù¾e u¾ivatel zavést
jiné makro (napø. "tx-math.tex") a zavede si tøeba i jiné textové fonty. To nezmìní
vlastnosti maker v OPmac, pokud nové soubory maker správnì pøedefinují
makra "\setmathsizes[<text>/<script>/<scriptscript>]", "\normalmath" a
"\boldmath".
\inext {ams-math}{\empty}{+-}
Po naètení souboru ams-math disponujeme makry \db regtfm na registraci
rùzných metrik pro rùzné designované velikosti fontù a \db whichtfm, které
expanduje na svùj parametr nebo na metriku, která je registrována pro
velikost \db dgsize. Registrace metrik \CS{}fontù je rovnì¾ provedena
v souboru {\tt ams-math.tex}. Jak bylo øeèeno,
makro "\resizefont<fontselector>" \csplain{}u zmìní
velikost fontu "<fontselector>" podle obsahu makra "\sizespec" (viz soubor
{\tt csfontsm.tex}). Toto makro "\resizefont" volá pomocné makro \db
resizefontskipat na odstranìní \uv{sizespec} z názvu metriky. Toto pomocné
makro je v OPmac pøedefinováno s vyu¾itím "\whichtfm", tak¾e kdy¾ pøed
voláním makra "\resizefont" pøipravíme správnou "\dgsize", \TeX{} pou¾ije
metriku designovanou na po¾adovanou velikost:
\inext {resizefontskip}{\empty}{+-}
Makra "\typosize", "\fontsizex", "\textfontsize",
"\setbaselineskip" po¾adují zápis parametru bez jednotky.
Jednotkou je \db ptunit, která je nastavena na 1pt. U¾ivatel mù¾e
jednotku zmìnit (napø. "\ptunit=1mm" pøi návrhu plakátu). Dále \db fontdim
je registr, který udává aktuální velikost písma.
\inext {ptunit}{\empty}{+-}
Èasto budeme potøebovat odstranit jednotku "pt" ve výpisu "\the<dimen>".
Provedeme to pomocí "\expandafter\ignorept\the<dimen>". Proto¾e "\the"
vyrábí znaky pt s kategorií 12, je makro \db ignorept definováno trikem pøes
"\lowercase". Z otazníku vznikne p kategorie 12 a z vykøièníku vznikne t.
\inext {lccode}{\empty}{+-}
Makra \db typosize a \db typoscale zmìní velikosti a nastavují
výchozí font "\tenrm" a výchozí
matematiku "\normalmath". Nehrajeme si na OFS nebo NFSS, které se sna¾í ctít
naposledy nastavený duktus a variantu. U¾ivatel si variantu písma a tuèný
duktus pro matematiku musí nastavit a¾ po zavolání makra na zmìnu velikosti fontu.
\inext {typosize}{\empty}{+-}
Makro \db fontsizex "[<velikost>]" pøedpokládá svùj parametr bez jednotky.
Písmeno x v názvu znaèí, ¾e makro není v u¾ivatelské dokumentaci. U¾ivatel
toti¾ mù¾e pou¾ít "\typosize[<velikost>/]" a tøeba ho napadne si nìjaké
vlastní makro "\fontsize" definovat.
Je-li parametr "<velikost>" prázdný, makro "\fontsizex" neudìlá nic.
Jinak pomocí "\textfontsize" nastaví
velikost textových fontù. Dále zavolá
"\setmathsizes[\fontsize/.7\fontsize/.5\fontsize]", ov¹em v parametru musí
odstranit jednotky a parametr pøichystá pro makro "\setmathsizes"
expandovaný. Pøíkazem "\normalmath" nakonec nastaví matematické fonty do
nové velikosti.
\inext {fontsizex}{^^B\cbrace}{++}
Makro \db textfontsize "[<velikost>]" pøedpokládá svùj parametr bez
jednotky. Pøipojí jednotku "\ptunit", nastaví "\dgsize" a "\sizespec" a
zavolá "\resizeall", co¾ je makro definované v \csplain{}u, které postupnì
volá "\resizefont" na v¹echny registrované fonty.
\inext {textfontsize}{^^B\cbrace}{++}
Makro \db setbaselineskip "[<velikost>]" pøedpokládá parametr bez jednotky.
Pøipojí jednotku "\ptunit" a nastaví "\baselineskip" bez dodateèné
pru¾nosti. Nastaví dal¹í registry, které s "\baselineskip" souvisejí.
Zámìrnì není nastavena "\topskip", "\splittopskip",
"\above/belowdisplayskip". Tyto parametry (globální pro celý dokument) by si
mìl u¾ivatel nastavit sám.
\inext {setbaselineskip}{^^B\cbrace}{++}
Makro \db withoutunit "\makro<dimen>" odstraní jednotku z "<dimen>" a takto
upravené èíslo vlo¾í do parametru "\makro", které oèekává údaj bez jednotky
v~hranaté závorce.
\inext {withoutunit}{\empty}{+-}
Makra \db fontscalex "<factor>", \db textfontscale "<factor>" a
\db scalebaselineskip "<factor>"
pøepoèítají "<factor>" podle aktuálního "\fontdim" resp. "\baselineskip"
na absolutní jednotku a zavolají odpovídající makro definované pøed chvílí.
\inext {fontscalex}{\count=3 ^^B\cbrace}{++}
Makro \db thefontsize si alokuje aktuální font do sekvence \db thefont a
tento nový fontový pøepínaè podrobí zmìnì velikosti "\resizefont".
Makro \db thefontscale pøepoèítá parametr na absolutní velikost a zavolá
"\thefontsize".
\inext {thefontsize}{\count=2 ^^B\cbrace}{++}
Plain\TeX{}ový \db magstep má na konci "\relax", tak¾e nefunguje jako pouze
expandující makro. My ale "\magstep" oèekáváme v parametrech pøíkazù
"\typoscale" a podobných, proto v "\magstep" je nahrazeno "\relax" ménì
drsným "\space". To separuje èíselný parametr dostateènì.
\inext {magstep}{\empty}{+-}
Makro \db em pøepíná kontextovì do odpovídající varianty a ve spolupráci s
makrem "\additcorr" pøidává italickou korekci. Makro \db additcorr
si pomocí "\lastskip" zapamatuje poslední mezeru, pak ji odstraní, vlo¾í
italickou korekci a nakonec vrátí tu odstranìnou mezeru.
\inext {def\nb em}{\empty}{+-}
Fontová makra zabezpeèíme proti rozkladu v parametru "\write".
\inext {addprotect}{\empty}{+-}
\subsec Texty ve více jazycích
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Makro \db mtext "<znaèka>" je zkratkou za \uv{multilingual text}.
Toto makro si podle znaèky a aktuálního jazyka (dle registru "\language")
vyhledá, jaký text má vypsat.
\inext {mtext}{\empty}{+-}
Jednotlivé texty definujeme pomocí "\sdef{mt:<znaèka>:<jazyk>}" takto:
\inext {chap:en}{\empty}{+-}
Nìkteré texty jsou zapsány pomocí "\v" notace. Je lep¹í udìlat to takto
ne¾ vytvoøit soubor "opmac.tex" závislý na kódování. Aby byla tato notace
správnì interpretována, spustíme "\csaccents", co¾ je makro \csplain{}u.
Pokud nìkdo pou¾ívá OPmac s jiným formátem, ne¾ \csplain, neprovede se nic,
proto¾e konstrukce "\csname csaccents\endcsname" se v takovém pøípadì pøerodí v
"\relax". Makro "\csaccents" spustíme jen tehdy, pokud je u¾ u¾ivatel
nespustil pøed "\input opmac". To poznáme podle toho, zda je definovaná
sekvence "\r".
\inext {}{}{+-}
CSplain od verze "Nov. 2012" pøipravuje následující
makra, která konvertují èíslo "\language" na znaèku jazyka pro v¹echny
jazyky, které mají nata¾eny vzory dìlení slov.
Pro jistotu (pokud je pou¾ita star¹í verze CSplainu) tuto koverzi
\uv{nauèíme} i makro OPmac:
\inext {sdef}{\empty}{+-}
\subsec REF soubor
%%%%%%%%%%%%%%%%%%
OPmac pou¾ívá pro v¹echny potøeby (obsah, reference, citace, rejstøík,
poznámky na okraji)
jediný soubor "\jobname.ref" (tzv. REF soubor).
Navíc, pokud není potøeba, vùbec tento soubor
nezakládá. Èasto toti¾ budeme chtít dìlat s OPmac jen jednoduché vìci a je
únavné poøád na disku kvùli tomu uklízet smetí.
Je potøeba deklarovat souborové deskriptory \db reffile a \db testin:
\inext {newwrite}{\empty}{+-}
Do souboru zapisujeme makrem \db wref "\<sekvence>{<data>}", které vlo¾í do
"\reffile" øádek obsahující "\<sekvence><data>".
Implicitnì ale není "\reffile" zalo¾eno, tak¾e implicitní hodnota tohoto
makra je \db wrefrelax, tedy nedìlej nic.
\inext {wrefrelax}{\empty}{+-}
Makro \db inputref spustíme na konci ètení souboru "opmac.tex", tedy v
situaci, kdy u¾ budeme mít definovány v¹echny kontrolní sekvence, které se v
REF souboru mohou vyskytnout. Nyní si toto makro jen pøipravíme.
Makro ovìøí existenci souboru "\jobname.ref" a pokud existuje, provede
"\input \jobname.ref". V takovém pøípadì po naètení REF souboru jej otevøe
k~zápisu a pøipraví "\wref" do stavu, kdy toto
makro bude ukládat data do souboru.
\inext {inputref}{^^B\cbrace}{+-}
Makro \db openref kdekoli v dokumentu si vynutí zalo¾ení souboru
"\jobname.ref". Toto makro neprovede nic, je-li REF soubor u¾ zalo¾en. To
pozná podle toho, ¾e makro "\wref" nemá význam "\wrefrelax". Jestli¾e soubor
je¹tì není zalo¾en, makro jej zalo¾í, pøedefinuje "\wref" a vlo¾í první
øádek do souboru. Tím je zaruèeno, ¾e pøi pøí¹tím \TeX{}ování dokumentu
je soubor neprázdný, tak¾e jej OPmac rovnou pøeète a znovu zalo¾í na zaèátku
své èinnosti. Nakonec se "\openref" zasebevra¾dí, aby se nemuselo pøi
opakovaném volání obtì¾ovat vykonávat nìjakou práci. Práce u¾ je toti¾
hotova.
\inext {openref}{^^B\cbrace}{++}
Pro zápisy do REF souboru pou¾íváme tuto konvenci: první kontrolní sekvence
na øádku je v¾dy tvaru "\X<název>", tak¾e máme pøehled, která kontrolní sekvence
pochází z REF souboru.
\subsec Lejblíky a odkazy
%%%%%%%%%%%%%%%%%%%%%%%%%
K vytvoøení zpìtného odkazu provedeme tøi kroky (v tomto poøadí):
\begitems
\item* V místì "\label[<lejblík>]" si zapamatujeme "<lejblík>".
\item* V dobì vygenerování èísla (sekce, kapitoly, caption, atd.)
propojíme "<lejblík>" s tímto èíslem. Provedeme to
pomocí "\sxdef{lab:<lejblík>}{<èíslo>}".
\item* V místì "\ref[<lejblík>]" vytiskneme "\csname lab:<lejblík>\endcsname",
tedy "<èíslo>".
\enditems
To je základní idea pro zpìtné odkazy. V takovém pøípadì nepotøebujeme REF
soubor. Pokud ale chceme dopøedné odkazy, je potøeba pou¾ít REF soubor
zhruba takto:
\begitems
\item* V dobì vygenerování èísla (sekce atd.) navíc ulo¾íme informaci
"\Xlabel{<lejblík>}{<èíslo>}" do REF souboru.
\item* V dobì ètení REF souboru (tedy na zaèátku dokumentu) provede "\Xlabel"
pøiøazení pomocí "\sdef{lab:<lejblík>}{<èíslo>}".
\item* Nyní mù¾e pøijít "\ref[<lejblík>]"
se svým "\csname lab:<lejblík>\endcsname" kdekoli v dokumentu.
\enditems
Pøejdeme od idejí k implementaci.
Makro \db label "[<lejblík>]" si pouze zapamatuje "<lejblík>" do makra \db
lastlabel, aby s touto hodnotou mohlo pozdìji pracovat makro, které automaticky
generuje nìjaké èíslo.
\inext {def\nb label}{\empty}{+-}
Makro, které automaticky generuje nìjaké èíslo, má za úkol zavolat
\db wlabel "<èíslo>". Toto makro propojí "\lastlabel" a "<èíslo>" tak, ¾e
definuje sekvenci "\lab:\lastlabel" jako makro s hodnotou "<èíslo>".
Kromì toho zapí¹e expandované "\lastlabel" i "<èíslo>" do
REF souboru (jen, je-li otevøen, zpìtné reference toti¾ fungují i bez
souboru). Nakonec vrátí makru "\lastlabel" jeho pùvodní nedefinovanou
hodnotu, tj.~lejblík
u¾ byl pou¾it. Dal¹í makro automaticky generující èíslo zavolá "\wlabel",
který nyní neprovede nic (pokud tedy u¾ivatel nenapsal dal¹í
"\label[<lejblík>]").
\inext {wlabel}{^^B\cbrace}{++}
\goodbreak
Makro \db ref "[<lejblík>]" zkontroluje definovanost "\lab:<lejblík>".
Je-li to pravda, vytiskne "\lab:<lejblík>" (krz reflink, aby to bylo
pøípadnì klikací). Jinak vytiskne dva otazníky a pøedpokládá, ¾e v tomto
pøípadì jde o dopøednou referenci. Vynutí si tedy otevøení REF souboru
zavoláním "\openref".
\inext {ref}{^^B\cbrace}{++}
Makro \db pgref "[<lejblík>]" dìlá podobnou práci, jako "\ref", jen s makrem
"\""pgref:<lejblík>". Toto makro je definováno a¾ pøi znovunaètení REF
souboru makrem \db Xlabel, proto¾e ke správnému urèení èísla stránky
potøebujeme asynchronní "\write".
\inext {pgref}{\empty}{+-}
\subsec Kapitoly, sekce, podsekce
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Fonty pro titul, kapitoly a sekce \db titfont,
\db chapfont, \db secfont a \db seccfont
jsou definovány jako odpovídající zvìt¹ení a nastavení tuèného duktu.
Ten je nastaven pomocí \db bfshape jako "\bf" a navíc je ztoto¾nìn
"\tenit" s "\tenbi", tak¾e kdy¾ nyní u¾ivatel napí¹e "\it", dostane tuènou
kurzívu.
\inext{titfont}{\empty}{+-}
Pro èíslování kapitol, sekcí a podsekcí potøebujeme èítaèe:
\dgn \nb chapnum \dgn \nb secnum \dgn \nb seccnum
\inext{newcount}{\empty}{+-}
Makro pro titul \db tit poèítá s tím, ¾e bude titul na více øádcích. Sází ho
tedy jako odstavec s~pru¾nými "\leftskip" a "\rightskip". Pøíkaz "\unskip"
tìsnì za parametrem "#1" odstraní mezeru z konce øádku, která tam obvykle
je. Teprve poté je nadpis øádnì centrován.
\inext{tit}{^^B\cbrace}{++}
Makra \db chap, \db sec a \db secc nastaví odpovídající èítaèe, dále vytvoøí
èíslování pro tisk (sestávající z více èísel) v makrech \db thechapnum,
\db thesecnum a \db theseccnum a zavolají odpovídající makro "\printchap",
"\printsec" a "\printsecc".
Poté zavolá "\wlabel" na
propojení pøípadného labelu s èíslem kapitoly/sekce/podsekce a
pomocí \db wcontents ulo¾í do "\reffile" potøebná data pro obsah.\par\vskip-1pt
\inext{chap}{\empty}{+-}
Text titulu sekce a její èíslo jsou vlo¾eny do "\mark", tak¾e tyto údaje
mù¾ete pou¾ít v plovoucím záhlaví. Tato vlastnost není dokumentována v
u¾ivatelské èásti, proto¾e je ponìkud techniètìj¹ího charakteru. Makro \db
insertmark "\thesecnum{<text>}" vlo¾í do "\mark" data ve formátu
"{<secnum>} {<text>}", tak¾e je mo¾no je pou¾ít pøímo expanzí napø.
"\firstmark", nebo je oddìlit a zpracovat zvlá¹». Parametru "<text>" je
zabránìna expanze pomocí prota¾ení tohoto parametru pøes "\toks", viz TBN
str. 54 dole a strany 55--57. Pøíkaz "\mark" se toti¾ sna¾í o expanzi.
\inext{insertmark}{\empty}{+-}
Pøíklad pou¾ití plovoucího záhlaví v "\headline":
\begtt
\headline{\expandafter\domark\firstmark\hss}
\def\domark#1#2{\llap{\it\headsize #1. }\rm\headsize #2}
\def\headsize{\thefontsize[10]}
\endtt
Makro "\headsize" v této ukázce zaruèí, ¾e bude mít záhlaví v¾dy
po¾adovanou velikost. Bez toho ta záruka není, pokud tedy u¾ivatel v sazbì
dokumentu støídá velikosti písma. Output rutina toti¾ mù¾e pøijít náhle,
tøeba v okam¾iku, kdy je zapnutá jiná velikost písma.
Pokud chcete kombinovat na levých a pravých stránkách plovoucí záhlaví z
kapitol a sekcí, inspirujte se v TBN na stranách 259 a 260.
Makra \db printchap, \db printsec a \db printsecc vytisknou èíslo a titul
kapitoly, sekce resp. podsekce. Pøíkazem "\firstnoindent" dávají najevo,
¾e následující odstavec nebude mít odstavcovou zará¾ku.
\inext {printchap}{\count=3 ^^B\cbrace}{++}
Makro \db afternoindent potlaèí odstavcovou zará¾ku pomocí pøechodného
naplnìní "\everypar" kódem, který odstraní box vzniklý z "\indent" a
vyprázdní pomocí \db wipeepar registr "\everypar".
Makro \db firstnoindent je ztoto¾nìno s
"\afternoindent", ale u¾ivatel mù¾e psát "\let\firstnoindent=\relax".
Pak bude "\afternoindent" pracovat jen za verbatim výpisy.
\inext{afternoindent}{firstnoindent}{++}
Nechceme, aby se nám odstavce tvoøené z titulù kapitol a sekcí rozdìlily do
více stran. Proto místo pøíkazu "\par" je pou¾ito \db nbpar.
Koneènì u¾ivatel mù¾e odøádkovat napøíklad v titulu pomocí \db nl.
Tomuto makru pozdìji v "\output" rutinì zmìníme význam na mezeru.
\inext{nbpar}{\empty}{+-}
\subsec Popisky, rovnice
%%%%%%%%%%%%%%%%%%%%%%%%
Nejprve deklarujeme potøebné èítaèe:
\dgn \nb tnum \dgn \nb fnum \dgn \nb dnum
\inext{newcount}{\empty}{+-}
Výchozí hodnoty maker, které se vypisují v místì generovaného èísla jsou:
\inext{thetnum}{\empty}{+-}
Makro \db caption "/<typ> " zvedne èítaè "\<typ>num" o jednièku,
dále nastaví pru¾né mezery s odsazením "\iindent" a s centrováním posledního
øádku (viz TBN str.~234), propojí pomocí "\wlabel" èíslo s pøípadným
lejblíkem, vytiskne slovo Tabulka/Obrázek v závislosti na jazyku makrem
"\mtext", vytiskne èíslo "\the<typ>num" a zpracuje odstavec s nastavenými
"\leftskip", "\rightskip". Pøedefinuje "\par" tak, ¾e první výskyt prázdného
øádku ukonèí skupinu a tím se v¹echna nastavení vrátí do pùvodního stavu.
\inext{caption}{^^B\cbrace}{++}
Makro \db eqmark zvedne "\dnum" o jednièku.
V display módu pak pou¾ije primitiv "\eqno", za kterým
následuje "\thednum". V interním módu (v boxu) vytiskne jen "\thetnum".
V obou pøípadech propojí pøípadný lejblík s èíslem pomocí makra "\wlabel".
\inext{eqmark}{\empty}{+-}
\subsec Odrá¾ky
%%%%%%%%%%%%%%%
Odsazení ka¾dé dal¹í vnoøené úrovnì odrá¾ek bude o "\iindent" vìt¹í.
Jeho hodnota je nastavena na "\parindent" v dobì ètení {\tt opmac.tex}.
Jestli¾e u¾ivatel pozdìji zmìní "\parindent", mìl by odpovídajícím zpùsobem
zmìnit "\iindent". Kromì toho deklarujeme èítaè pro odrá¾ky \db itemnum.
\inext{newcount}{\empty}{+-}
Makro \db begitems vlo¾í "\iiskip", zahájí novou skupinu, pronuluje "\itemnum",
zvìt¹í odsazení o~"\iindent" a pomocí "\adef" definuje hvìzdièku jako
aktivní makro, které provede "\startitem". Makro \db enditems ukonèí
skupinu a vlo¾í "\iiskip".
\inext{begitems}{\empty}{+-}
Makro \db startitem ukonèí pøípadný pøedchozí odstavec, posune èítaè, zahájí
první odstavec jako "\noindent" a vy¹oupne doleva text definovaný v
\db printitem, který je implicitnì nastaven makrem "\begitems" na
\db normalitem.
\inext{startitem}{\empty}{+-}
Makro \db style "<znak>" pøeète "<znak>" a rozvine jen na makro
"\item:<znak>". Tato jednotlivá makra jsou definována pomocí "\sdef".
Není-li makro "\item:<znak>" definováno, pou¾ije se "\normalitem".
\inext{style}{\empty}{+-}
Ètvereèek kreslíme jako "\vrule" odpovídajících rozmìrù makrem
\db fullrectangle "{<dimen>}".
\inext{fullrectangle}{\empty}{+-}
Pro pøevod mezi numerickou hodnotou èítaèe a pøíslu¹ným písmenem a, b, c
atd. je vytvoøeno makro \db athe "<number>".
\inext{athe}{\empty}{+-}
\subsec Tvorba obsahu
%%%%%%%%%%%%%%%%%%%%%
Do \db toclist budeme ukládat data pro obsah.
Pomocí \db ifischap se budeme ptát, zda v dokumentu jsou kapitoly.
\inext{toclist}{\empty}{+-}
V dobì ètení REF souboru vlo¾íme ve¹kerá data obsahu do makra "\toclist"
tak, ¾e v tomto bufferu budeme mít za sebou sekvence "\tocline" následované pìti
parametry. Makra \db Xchap, \db Xsec a \db Xsecc
mají parametry "{<èíslo>}{<text>}{<strana>}" a jsou definovány následovnì:
\inext{Xchap}{\empty}{+-}
Makro \db tocline "{<odsazení>}{<font>}{<èíslo>}{<text>}{<strana>}" vytvoøí
øádek obsahu. Øádek tiskneme jako odstavec, proto¾e "<text>" mù¾e být tøeba
del¹í. Registr "\leftskip" nastavíme jako souèin "<odsazení>" krát "\iindent".
Pokud se v dokumentu vyskytují kapitoly, odsadíme je¹tì o dal¹í "\iindent".
Registr "\rightskip" nastavíme na 2"\iindent", aby del¹í "<text>" se zalomil
døív ne¾ v místì, kde jsou stránkové èíslice. Konec odstavce se stránkovou
èíslicí pak vytáhneme mimo tento rozsah pomocí "\hskip-2\iindent". Odstavec
pru¾í v "\tocdotfill", proto¾e tento výplnìk má vìt¹í pru¾nost ne¾
"\parfillskip". Registr "\parfillskip" má "1fil", zatímco "\tocdotfill" má
pru¾nost "1fill". Makro \db tocdotfill je implementováno pomocí "\leaders"
jako opakované teèky, které budou lícovat pod sebou.
\inext{tocline}{\empty}{+-}
Makro \db maketoc jednodu¹e spustí "\toclist", Pokud je "\toclist" prázdný,
upozorní o tom adekvátním zpùsobem na terminál.
\inext{maketoc}{\empty}{+-}
\subsec Sestavení rejstøíku
%%%%%%%%%%%%%%%%%%%%%%%%%%%
Slovo do rejstøíku vlo¾íme pomocí \db iindex "{<heslo>}". Proto¾e výskyt
slova na stránce není v dobì zpracování znám, je nutné pou¾ít REF soubor s
asynchronním "\write".
\inext{iindex}{\empty}{+-}
Nyní naprogramujeme ètení parametru makra
\db ii "<slovo>,<slovo>,...<slovo> ". Vzhledem k tomu, ¾e za
pøítomnosti zkratky "@" budeme potøebovat projít seznam slov oddìlených èárkou
v~parametru je¹tì jednou, zapamatujeme si tento seznam do "\tmp".
\inext{leavevmode}{\empty}{+-}
Makro \db iiA sejme v¾dy jedno slovo ze seznamu. Podle prázdného parametru
poznáme, ¾e jsme u konce a nedìláme nic. Pøi výskytu "<slovo>=@" (poznáme to
podle shodnosti parametru s \db iiatsign \unskip),
spustíme "\iiB", jinak vlo¾íme údaj o slovì do rejstøíku pomocí "\iindex".
Nakonec makro "\iiA" volá rekurzivnì samo sebe.
\inext{iiA}{\empty}{+-}
Makro \db iiB rovnì¾ sejme v¾dy jedno slovo ze seznamu a na konci volá
rekurzivnì samo sebe. Toto makro ov¹em za pou¾ití makra \db iiC prohodí poøadí
prvního podslova pøed prvním lomítkem se zbytkem. Není-li ve slovì lomítko,
pozná to makro "\iiC" podle toho, ¾e parametr "#2" je prázdný. V takovém pøípadì
neprovede nic, nebo» slovo je u¾ zaneseno do rejstøíku v makru "\iiA".
\inext{iiB}{\empty}{+-}
Makro \db iid "<slovo> " po¹le slovo do rejstøíku a souèasnì je zopakuje do
sazby. Pomocí "\futurelet" a \db iiD zjistí, zda následuje teèka nebo èárka. Pokud
ne, vlo¾í mezeru.
\inext{iid}{\empty}{+-}
Pøi ètení REF souboru se vykonávají makra \db Xindex "{<heslo>}{<strana>}",
která postupnì vytváøejí makra tvaru "\,<heslo>", ve kterých je shroma¾ïován
seznam stránek pro dané "<heslo>".
Kromì toho ka¾dé makro "\,<heslo>" je vlo¾eno do seznamu \db iilist.
Na konci ètení
REF souboru tedy máme v~"\iilist" seznam v¹ech hesel jako øídicí sekvence (to
zabere nejmíò místa v \TeX{}u). Ka¾dé
"\,<heslo>" na konci ètení REF souboru obsahuje dva údaje ve svorkách, první údaj
obsahuje pomocná data a druhý obsahuje seznam stránek. Tedy "\,<heslo>" je makro
s obsahem "{<pomocná-data>}{<seznam-stránek>}".
Seznam stránek není jen tupý seznam v¹ech stránek, na kterých se objevil
záznam "\ii" pro dané slovo. Nìkteré stránky se toti¾ mohou opakovat a my je
chceme mít jen jednou. Pokud stránky jsou souvisle za sebou: 13, 14, 15, 16,
chceme navíc takový seznam nahradit zápisem 13--16. Makro
"\Xindex{<heslo>}{<strana>}" je z tohoto dùvodu ponìkud sofistikovanìj¹í.
\ilabel [Xindex:first] {firstdata}
\ilabel [Xindex:second] {seconddata}
\inext{Xindex}{\empty}{+-}
Èinnost makra "\Xindex" si vysvìtlíme za chvíli podrobnìji. Nejprve ov¹em
definujeme pomocné makro \db firstdata "\,<heslo>"~"\<cs>", které expanduje na
"\<cs>"~"<první-datový-údaj-hesla>&". Je-li tøeba "\,aa" definováno jako
"{prvni}{druhy}", pak "\firstdata"~"\,aa"~"\cosi" expanduje na "\cosi"~"prvni&".
Tím máme mo¾nost vyzískat data z makra. Podobnì makro
\db seconddata "\,<heslo>"~"\<cs>" expanduje na
"\<cs>"~"<druhý-datový-údaj-hesla>&".
\inext{firstdata}{\empty}{+-}
Ne¾ se pustíme do výkladu makra "\Xindex", vysvìtlím, proè pro hesla rejstøíku
tvoøím jednu øídicí sekvenci, která je makrem se dvìma datovými údaji.
Mohl bych jednodu¹eji pracovat se dvìma rùznými øídicími sekvencemi,
napø. "\,<heslo>" a "\:<heslo>". Dùvod je prostý: ¹etøím pamì» \TeX u. Dá se toti¾
oèekávat, ¾e poèet hesel v rejstøíku mù¾eme poèítat na tisíce a je rozdíl
alokovat kvùli tomu tisíce kontrolních sekvencí nebo dvojnásobné mno¾ství
takových sekvencí.
V makru "\Xindex" èteme "\firstdata" na øádku \cite[Xindex:first]
a "\seconddata" na øádku \cite[Xindex:second].
Ètení je provedeno makry \db XindexA a \db XindexB. První úsek dat je tvaru
"<poslední-strana>/<stav>" a druhý úsek dat obsahuje rozpracovaný seznam stránek.
Podíváme-li se na definice "\XindexA" a "\XindexB", shledáme, ¾e seznam stránek
bude ulo¾en v "\tmp", dále "<poslední-strana>" bude v "\tmpa" a "<stav>" je v
"\tmpb".
\inext{XindexA}{\empty}{+-}
Rozli¹ujeme dva stavy: "<stav>=+", pokud je seznam stránek zakonèen konkrétní
stránkou. Tato konkrétní stránka je ulo¾ena v "<poslední-strana>". Druhým
stavem je "<stav>=-", kdy¾ je seznam stránek ukonèen "--" (pøesnìji obsahem
makra \db iiendash, které mù¾ete snadno pøedefinovat) a v tomto pøípadì
"<poslední-strana>" obsahuje poslední stránku, na které byl zji¹tìn výskyt
hesla. Tato strana nemusí být v~seznamu stránek explicitnì uvedena.
Makro "\Xindex{<heslo>}{<strana>}" tedy postupnì vytváøí seznam stran
zhruba takto:
\def\begtthook{\langleactive}
\begtt
if (první výskyt \,<heslo>) {
zalo¾ \,<heslo> do iilist;
<seznam-stran> = "<strana>"; <stav> = +; <posledni-strana> = <strana>;
return;
}
if (<strana> == <posledni-strana>) return;
if (<stav> == +) {
if (<strana> == <posledni-strana>+1) {
<seznam-stran> += "--";
<stav> = - ;
}
else {
<seznam-stran> += ", <strana>";
<stav> = + ;
}
else {
if (<strana> > <posledni-strana>+1) {
<seznam-stran> += "<posledni-strana>, <strana>";
<stav> = + ;
}
}
}
<poslední-strana> = <strana>;
\endtt
\def\begtthook{}
Makro \db makeindex nejprve definuje pøechodný význam rekurzivního
"\act" tak, aby byly uzavøeny seznamy stránek (tj. aby seznam nekonèil
znakem "--") a do první
datové oblasti ka¾dého makra typu "\,<heslo>" vlo¾í konverzi textu "<heslo>" do
tvaru vhodném pro abecední øazení èeských slov.
Pomocí "\expandafter \act \iilist \relax" se
po¾adovaná èinnost vykoná pro ka¾dý prvek v "\iilist".
Dále makro "\makeindex" provede seøazení "\iilist" podle abecedy
makrem "\dosorting" a nakonec provede tisk jednotlivých hesel. K tomu úèelu
znovu pøechodnì pøedefinuje "\act" a pøedlo¾í mu "\iilist".
\ilabel [mkindex:od] {firstdata}
\ilabel [mkindex:do] {after\nb act}
\ilabel [mkindex:orippx] {orippx}
\inext{makeindex}{^^B\cbrace}{++}
Makro \db printiipages sebere z "<druhého-datového-údaje>" seznam stránek a
jednodu¹e je vytiskne.
\inext{printiipages}{\empty}{+-}
Makro \uv{prepare index item} \db prepii "\,<heslo>" odstraní prostøednictvím
\db prepiiA z názvu kontrolní sekvence backslash a èárku a zbytek tiskne pomocí
"\printii". Pokud ale je "\,<heslo>" ulo¾eno v~seznamu "\iispeclist", pak se
expanduje sekvenci s názvem "\\,<heslo>", ve které je ulo¾eno, co se
má místo hesla vytisknout. Data tìchto výjimek jsou pøipravena makrem "\iis"
\inext{prepii}{\empty}{+-}
Kontrolní otázka: proè se nedotazujeme jednodu¹e na to, zda je
"\\,<heslo>" definovaná øídicí sekvence? Odpovìï: museli bychom ji
sestavit pomocí "\csname...\endcsname", ale to zalo¾í do \TeX{}ové pamìti
novou øídicí sekvenci pro ka¾dé heslo v rejstøíku. My se sna¾íme poèet
tìchto øídicích sekvencí redukovat na minimum. Poèítáme s tím, ¾e obyèejných
hesel bude tisíce a výjimek jen pár desítek.
Makro \db iis "<heslo> {<text>}" vlo¾í dal¹í údaj do slovníku výjimek pro
hesla v rejstøíku. Pøesnìji: vlo¾í "\,<heslo>" do \db iispeclist a definuje
sekvenci "\\,<heslo>" jako "<text>".
\inext{iis}{\empty}{+-}
Makro \uv{print index item} \db printii "<heslo>&" vytiskne jeden údaj do
rejstøíku. Makro projde prostøednictvím \db printiiA jednotlivá podslova
oddìlená lomítkem a pøepí¹e je do rejstøíku oddìleny mezerou. Pøitom
kontroluje, zda se podslova rovnají odpovídajícím podslovùm z pøedchozího
hesla, které je ulo¾eno v \db previi. Pokud ano, místo podslova vlo¾í \db
iiemdash, co¾ je pomlka. Na konci èinnosti se spustí makro \db printiiB,
které nastaví "\previi" na \db currii (nové slovo se pro dal¹í zpracování stává
pøedchozím) a vytiskne seznam stránek. Makrem \db everyii
(implicitnì je prázdné) dovolíme u¾ivateli vstoupit do procesu tisku hesla.
Mù¾e napøíklad psát "\def\everyii{\indent}", pokud chce.
\ilabel [printii:iindent] {iindent}
\inext{printii}{\empty}{+-}
Makro \db iiparparams nastavuje parametry sazby odstavce v rejstøíku. Vlevo
budeme mít "\leftsip" rovný "\iindent", ale první øádek posuneme o
"-\iindent" (viz øádek kódu~\cite[printii:iindent]) tak¾e první øádek je
vystrèen doleva. Vpravo máme pru¾nou mezeru, aby se seznam èísel stran mohl
rozumnì lámat, kdy¾ je moc dlouhý. Makro "\iiparparams" si musí poznaèit do
makra \db orippx pùvodní údaje mìnìných hodnot. Není mo¾né se toti¾ schovat
do skupiny, proto¾e rejstøík je obvykle ti¹tìn pomocí
"\begmulti...\endmulti" a toto makro obèas ukonèuje plnìní boxu a spou¹tí
"\flushcolumns". Kdybychom mìli "\makeindex" ve skupinì, pak by pøi
"\flushcolumns" do¹lo ke køí¾ení skupin. Na konci práce "\makeindex" na
øádku \cite[mkindex:orippx] je makro "\orippx" zavoláno a tím jsou parametry
odstavce vráceny do pùvodní podoby.
\inext{iiparparams}{\empty}{+-}
Pomocné makro \db scanprevii "<expanded-previi>&" se podívá do "\previi",
odloupne z nìj úsek pøed prvním lomítkem a tento úsek definuje jako
"\tmpa".
\inext{scanprevii}{}{+-}
Výchozí hodnota "\previi" pøed zpracováním prvního slova v rejstøíku je
prázdná.
\inext{previi}{\empty}{+-}
\subsec Abecední øazení rejstøíku
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Nejprve se zamìøíme na vytvoøení makra
"\isAleB"~"\,<heslo1>"~"\,<heslo2>", které rozhodne, zda je "<heslo1>" øazeno za
"<heslem2>" nebo ne. Výsledek zkoumání mù¾eme provìøit pomocí "\ifAleB".
Pro porovnání dvou údajù vy¾aduje norma dva prùchody. V prvním (primární
øazení) se rozli¹uje jen mezi písmeny A B C È D E F G H Ch I J K L M N O P Q
R Ø S © T U V W X Y Z ®. Pokud jsou hesla z tohoto pohledu stejná, pak se
provede druhý prùchod (sekundární øazení), ve kterém jsou øazena
neakcentovaná písmena pøed pøehlasovaná pøed èárkovaná pøed háèkovaná pøed
støí¹kovaná pøed krou¾kovaná a dále s nejni¾¹í prioritou malá písmena pøed
velká. Pøipravíme si tedy dvì sady "\lccode" dvojic: pro první prùchod a pro
druhý. Porovnávaná hesla zkonvertujeme pomocí "\lowercase" pøi nastavení
"\lccode" odpovídajícího prùchodu. Pak takto zkonvertovaná hesla teprve
zaèneme porovnávat.
Makro \db setprimarysorting pøipraví "\lccode" znakù èeské a slovenské
abecedy pro první prùchod a \db setsecondarysorting pro druhý prùchod.
Makro "\setprimarysorting" expanduje \db sortingdata
a pøedhodí pøed takto expandovaná data "\act".
Pov¹imneme si, ¾e pro první prùchod dostanou stejný "\lccode" v¹echny znaky
na spoleèném øádku makra "\sortingdata", zatímco v druhém prùchodu budou mít
v¹echny znaky z tohoto makra rozdílný "\lccode", ve vzestupném poøadí.
Je to tím, ¾e v makru "\setprimarysorting" se zvedá "\tmpnum" jen v místì
èárky, zatímco v "\setsecondarysorting" se "\tmpnum" zvedá pro ka¾dý znak.
Nejni¾¹í hodnotu má mezera vyznaèená v "\sortingdata" pomocí "{ }".
Tím je zaruèeno, ¾e krat¹í slovo je øazeno døív ne¾ del¹í slovo se stejným
zaèátkem, obsahující celé krat¹í slovo (ten~tuèòák$\prec$tento).
Je sice pravda, ¾e ASCII hodnota mezery je je¹tì men¹í, ale my musíme
mezeru nìkam ¹oupnout na jiný kód ne¾ 32, jinak by nám ji nepøeèetlo
makro s~neseparovaným parametrem. Ov¹em my budeme chtít mezeru pøeèíst.
\ilabel [sorten] {sort:en}
\ilabel [lan] {lan:}
\ilabel [chsorting] {chsorting}
\inext{sortingdata}{\count=3 ^^B\cbrace}{++}
Jedním z problémù èeského øazení je dvojhláska ch. Tu potøebujeme
promìnit v jediný znak. Pro potøeby øazení promìníme ch v "^^T", Ch v "^^U"
a CH v "^^V". Udìláme to následujícím okultním kódem, který definuje makro
\db preparesorting "\,<heslo>". Toto makro pøipraví pomocí do "\tmpb"
"<heslo>" zkonvertované krz "\lowercase",
ov¹em nejprve je dvojhláska ch nahrazena jedním znakem.
Makro \db chsorting je implicitnì nedefinované, co¾ znamená, ¾e pracujeme s
dvojhláskou ch. Na øádku \cite[sorten], \cite[lan] a \cite[chsorting] je ov¹em nastaveno
"\chsorting" jako "n", co¾ je vzkaz, ¾e dvojhlásku ch nechceme
interpretovat.
\inext{bgroup}{\empty}{+-}
Tento kód je bohu¾el obtí¾nìji èitelný, proto¾e makra "\iiscanch" a dal¹í
potøebují mít separátor "ch" ve stavu, kdy jednotlivá písmena mají "\catcode" 12.
Pracujeme toti¾ s výstupem primitivu "\string", který bohu¾el v¹e (a¾ na
mezeru) balí do tokenù s kategorií 12. Proto je celý kód obalen do
"\bgroup", "\egroup" a "\lowercase". Tam jsou znaky 4, 5, 6, 7,
které mají kategorii 12, ¹oupnuty na c, h, C, H. Po tomto de¹ifrování tedy
vidíme, ¾e makro \db iiscanch je definováno takto:
\begtt
\gdef\iiscanch #1ch#2\relax{#1\if$#2$\else^^T\iiscanch #2\relax\fi}
\iiscanch Schází hrách, který bych házel na stìnu.ch\relax
\endtt
Uvedený pøíklad expanduje postupnì na
\begtt
#1<-S
#2<-ází hrách, který bych házel na stìnu.ch
=> s^^T\iiscanch #2\relax
#1<-ází hrá
#2<-, který bych házel na stìnu.ch
=> s^^Tází hrá^^T\iiscanch #2\relax
#1<-, který by
#2<- házel na stìnu.ch
=> s^^Tází hrá^^T, který by^^T\iiscan #2\relax
#1<- házel na stìnu.
#2<-
=> s^^Tází hrá^^T, který by^^T házel na stìnu.
\endtt
a to nahradí v¹echny výskyty dvojhlásky ch znakem "^^T". Analogicky pracují
makra \db iiscanCh a \db iiscanCH. Makro \db preparesortingA nakonec zavolá
v¹echna tøi makra, tak¾e máme nahrazeny v¹echny dvojhlásky ch, Ch i CH.
V rámci optimalizace rychlosti jsou pøed algoritmem na setøídìní seznamu
v¹echna hesla jednorázovì zkonvertovaná podle pravidel prvního prùchodu
øazení a tato data jsou ulo¾ena v prvním datovém údaji hesla (ve druhém máme
seznam stránek). Není tedy nutné dìlat konverzi pøi ka¾dém porovnávání dvou
hesel. Ov¹em, pokud porovnání hesel vyjde bez rozdílu, je potøeba provést
druhý prùchod øazení (sekundární øazení). Ten nastavujeme jednotlivì jen pro
takové dvojice hesel, kde to je potøeba. Pravdìpodobnost, ¾e to je vùbec
nìkdy potøeba, je mizivá. Data pro primární øazení jsou tedy u¾ pøipravena na
øádcích \cite[mkindex:od] a¾ \cite[mkindex:do] v makru "\makeindex".
V èeském øazení se nemá pøihlí¾et na interpunkèní znaky (teèka, støedník,
otazník, atd.). Hesla máme øadit tak, jako kdyby tam tyto znaky nebyly.
Kdyby se dvì hesla podle tohoto pravidla neli¹ila, norma pøedepisuje nasadit
cca ètvrtý prùchod, ve kterém se tyto znaky rozli¹í. Ètvrtý prùchod
implementován není: hesla li¹ící se jen interpunkèními znaky, jsou v OPmac
pøi øazení nerozli¹itelná, tj. jsou øazena v poøadí, v jakém vstupují do
rejstøíku. Ignorování interpunkèních znakù je provedeno tak, ¾e v¹em
tìmto znakùm je pøidìlen makrem \db setignoredchars "\lccode" teèky a
teèka je pøi zpracování v~"\setprimarysorting" a "\setsecondarysorting" odstranìna
makrem \db removedot.
\inext{removedot}{\empty}{+-}
Pøipravíme si "\newif", kterým ohlásíme výsledek porovnání dvou hesel:
\inext{newif}{\empty}{+-}
Makro \db isAleB "\,<heslo1>"~"\,<heslo2>" spustí\hfil\break
"\testAleB"~"<zkonvertované heslo1>&\relax"~"<zkonvertované heslo2>&\relax"~%
"\,<heslo1>"~"\,<heslo2>".
\inext{isAleB}{^^B\cbrace}{++}
Idea makra \db testAleB lexikograficky porovnávající dvì slova je v tom, ¾e ze
dvou stringù v~parametru oddìlených "\relax" postupnì odlupuje v¾dy
první znak "#1" a "#3" z ka¾dého stringu a ten porovnává a samozøejmì pøi rovnosti
rekurzivnì zavolá samo sebe. Pokud jsme se dostali na konec bez rozhodnutí, co
je men¹í, narazíme na znak "&". V takovém pøípadì pøestoupíme do
sekundárního prùchodu.
\inext{testAleB}{^^B\cbrace}{++}
Makro \db testAleBsecondary "\,<heslo1>"~"\,<heslo2>" zalo¾í skupinu, v ní
nastaví "\lccode" dle sekundárního øazení a pomocí "\preparesorting"
pøipraví zkonvertovaná data do "\tmpa" a "\tmpb". Na chvosty tìchto dat
pøidám nulu a jednièku, aby porovnání v¾dy nìjak dopadlo, a spustím
\db testAleBsecondaryX, co¾ pracuje obdobnì, jako "\testAleB".
\inext{testAleBsecond}{\count=2 ^^B\cbrace}{++}
Nyní mù¾eme pomocí "\isAleB\,<heslo1>\,<heslo2>\ifAleB" rozhodnout, který
ze dvou daných parametrù má být øazen døíve. Staèí tedy u¾ jen naprogramovat
celkové øazení seznamu. Toto makro vycházející z algoritmu mergesort
vytvoøil mùj syn Miroslav. Makro bylo poprvé pou¾ito v DocBy\TeX{}u, co¾ je
nástroj, kterým je napøíklad poøízena i tato dokumentace.
Makro \db dosorting pomocí pomocného makra "\act" doplní za ka¾dý údaj v
"\iilist" èárku a dále pøedlo¾í makru "\mergesort" jako parametr obsah
"\iilist" ukonèený "\end,\end", vyprázdní "\iilist" a spustí "\mergesort".
\inext{dosorting}{\empty}{+-}
Makro \db mergesort pracuje tak, ¾e bere ze vstupní fronty v¾dy dvojici
skupin
polo¾ek, ka¾dá skupina je zatøídìná. Skupiny jsou od sebe oddìleny
èárkami. Tyto dvì skupiny spojí do jedné a zatøídí. Pak pøejde na
následující dvojici skupin polo¾ek. Jedno zatøídìní tedy vypadá
napøíklad takto: dvì skupiny: "eimn,bdkz," promìní v~jedinou
skupinu "bdeikmnz,". V tomto pøíkladì jsou polo¾ky jednotlivá písmena,
ve skuteènosti jsou to kontrolní sekvence, které obsahují celá slova.
Na poèátku jsou skupiny jednoprvkové ("\iilist" oddìluje ka¾dou
polo¾ku èárkou). Makro "\mergesort" v tomto pøípadì projde seznam a vytvoøí
seznam zatøídìných dvoupolo¾kových skupin, ulo¾ený zpìtnì v
"\iilist". V dal¹ím prùchodu znovu vyvrhne "\iilist" do vstupní
fronty,
vyprázdní ho a startuje znovu. Nyní vznikají ètyøpolo¾kové zatøídìné
skupiny. Pak osmipolo¾kové~atd. V~závìru (na øádku~\cite[konecsortu])
je první skupina celá setøídìná a druhá obsahuje "\end", tj.
v¹echny polo¾ky jsou u¾ setøídìné v první skupinì, tak¾e staèí
ji ulo¾it do "\iilist" a ukonèit èinnost. Pomocí \db gobbletoend
odstraníme druhé "\end" ze vstupního proudu.
\noactive{dvojice}
\ilabel [merge:porovnani] {isAleB}
\ilabel [merge:trojka] {mergesort p1+}
\ilabel [merge:p1] {ifx,}
\ilabel [merge:p2] {fif\nb mergesort\cbrace}
\ilabel [konecsortu] {empty\nb iilist}
\inext {def\nb mergesort }{^^B\cbrace}{++}
{\def\quotehook{\catcode`\<12}
Jádro "\mergesort" vidíme na øádcích~\cite[merge:porovnani]
a¾~\cite[merge:trojka]. Makro "\mergesort" sejme ze vstupního proudu
do "#1" první polo¾ku první skupiny, do "#2" zbytek první skupiny a do
"#3" první polo¾ku druhé skupiny. Je-li "#1<#3", je do výstupního
zatøídìného seznamu "\indexbuffer" vlo¾en "#1", ze vstupního proudu je
"#1" odebrán a "\mergesort" je zavolán znovu. V pøípadì "#3<#1"
je do "\indexbuffer" vlo¾en "#3", ze vstupního proudu je "#3" odebrán a
"\mergesort" je zavolán znovu. Øádky~\cite[merge:p1]
a¾~\cite[merge:p2] øe¹í pøípady, kdy je jedna ze skupin prázdná:
je potøeba vlo¾it do "\indexbuffer" zbytek neprázdné skupiny a pøejít
na dal¹í dvojici skupin. Ostatní øádky makra se vyrovnávají se
skuteèností, ¾e zpracování narazilo na zará¾ku "\end,\end" a je tedy
potøeba vystartovat dal¹í prùchod.
\par}
\subsec Více sloupcù
%%%%%%%%%%%%%%%%%%%%
Makro pro sazbu do více sloupcù je pøevzato z TBN, kde je podrobnì
vysvìtleno na stranách 224 a¾~245. Základní my¹lenka makra spoèívá v tom, ¾e
se naplní jeden velký "\vbox" (box6) jedním sloupcem a "\endmulti" jej
rozlomí do sloupcù po¾adované vý¹ky a strèí do sazby. Není k tomu nutno
mìnit výstupní rutinu. Makro z TBN je zde v OPmac ve dvou vìcech pøepracováno:
\begitems
\item * Dùslednìj¹í balancování sloupcù vyluèující mo¾nost ztráty sazby
a umo¾òující mít sazbu s nezlomitelnými mezerami mezi øádky.
\item * Makro mìøí kumulovanou sazbu a umo¾òuje pøi rozsáhlém mno¾ství
tiskového materiálu pøechodnì pøejít do re¾imu \uv{vyprazdòování}.
\enditems
Makra \db begmulti, \db endmulti, \db corrsize, \db makecolumns a \db
splitpart pracují zhruba tak, jak je popsáno v TBN.
\ilabel [mul:endgraf] {endgraf}
\ilabel [mul:balance] {last balancing}
\ilabel [mul:relax] {relax\cbrace}
\ilabel [mul:prevgraf] {prevdepth}
\inext{corrsize}{\count=2 \empty}{+-}
Výstup rozlomené sazby do sloupcù probíhá ve dvou re¾imech: kdy¾ je tøeba
sloupci zaplnit celou stránku, pou¾ijeme "\makecolumns". Toto makro neøe¹í
otázku, ¾e mù¾e v kumulovaném boxu~6 zbýt nìjaká sazba, proto¾e se
pøedpokládá, ¾e lámání bude pokraèovat na dal¹í stranì. Pokud ale
je na aktuální stranì vícesloupcová sazba ukonèena, pou¾ijeme
propracovanìj¹í \db balancecolumns. Toto makro si zazálohuje materiál
z~boxu~6 do boxu~7 a jme se zkou¹eti rozlomit box~6 na sloupce s
vý¹kou "\dimen0". Pokud ale po rozlomení není výchozí box~6 zcela prázdný, makro
zvìt¹í krapánek (o 0,2"\baselineskip") po¾adovanou vý¹ku, vrátí se k~zálohované
sazbì v boxu~7 a zkusí rozlomit znovu. To opakuje tak dlouho, dokud je box~6
prázdný.
\inext{balancecolumns}{^^B\cbrace}{++}
Kdy¾ je sazba plnìna do boxu~6, mù¾e ji být tak moc, ¾e tento box
pøekroèí maximální vý¹ku boxu, která je v \TeX{}u bohu¾el
omezena na cca pìt metrù
(16383~pt). Proto na øádku~\cite[mul:endgraf] je pøedefinován "\par", který
pøièítá do "\dimen0" celkovou vý¹ku postupnì kumulované sazby. Jakmile tato
vý¹ka dosáhne "0.9\maxdimen", pøedefinujeme makro
"\balancecolumns" na "\flushcolumns" a spustíme pøedèasnì "\endmulti". Toto makro
vyprázdní box pomocí opakovaného "\splitpart", ov¹em nevyprázdní ho celý.
Jen tu èást, která zaplní celé stránky. Jakmile bude chtít
"\splitpart" pøejít k~vybalancování sazby pomocí
"\balancecolumns" na øádku \cite[mul:balance], spustí se místo bì¾ného
"\balancecolumns" makro \db flushcolumns. Toto makro ignoruje zbytek èinnosti
"\endmulti" a¾ po "\relax" na øádku \cite[mul:relax], tak¾e vyskoèí z
trojitì zanoøeného "\if". Musí tedy vrátit pøíslu¹né mno¾ství "\fi" a dále
vystartuje nový "\setbox6=\vbox". Uvnitø tohoto boxu nejprve vysype zbytek
boxu~6, pomocí "\unskip\unskip" odstraní "\vfil" a "\vskip-\prevdepth",
který tam vlo¾il "\endmulti" na øádku~\cite[mul:prevgraf],
vrátí se k zálohovanému významu makra \db ibalancecolumns
a znovu definuje "\par" obdobným zpùsobem jako v~"\begmulti".
Pak pokraèuje ve ètení sazby.
\inext{flushcolumns}{\empty}{+-}
\subsec Barvy
%%%%%%%%%%%%%
Deklarujeme \db ifpgcolor, co¾ budeme potøebovat v makru "\locpgcolor".
Dále deklarujeme \db lastpage. Tento registr budeme prùbì¾nì zvìt¹ovat pøi
ètení REF souboru v makru "\Xpage" a na konci ètení REF souboru tedy bude
obsahovat èíslo poslední strany.
\inext{ifpgcolor}{\empty}{+-}
Od verze pdf\TeX{}u 1.40 nabízí tento program primitivy
"\pdfcolorstackinit" a "\pdfcolorstack". Makra v OPmac tyto
primitivy nepou¾ívají, proto¾e:
\begitems
\item * Øe¹ení v OPmac jsem vytvoøil a pou¾il podstatnì døív, ne¾ si programátoøi
pdf\TeX{}u vùbec v¹imli, ¾e existuje problém s pøecházením barev na nové
stránky.
\item * Primitivy "\pdfcolorstackinit" a "\pdfcolorstack" stále nejsou dokumentované.
\item * Barevné øe¹ení v "pdftex.def" pro La\TeX{} nepova¾uji za koncepènì
správné, proto¾e se sna¾í pomocí "\aftergroup" vytvoøit zdání, ¾e
barvy fungují lokálnì uvnitø skupiny. To je zbyteèná komplikace
navíc, která jde proti pøirozenosti nastavování barev v PDF souboru.
\enditems
Barvy se v PDF pøepínají pomocí PDF speciálù "<num> <num> <num> <num> k"
(pro text a plochy) a "<num> <num> <num> <num> K" pro linky. Pro oba typy
barev pøipravíme barevná makra \db Blue, \db Red, \db Brown, \db Green, \db
Yellow, \db Cyan, \db Magenta, \db White, \db Grey, \db LightGrey, \db Black.
U¾ivatel si mù¾e definovat dal¹í.
\inext{Blue}{\empty}{+-}
Problém barev v PDF je, ¾e od výskytu speciálu pro barvu jsou zmìnìné barvy
a¾ po jiný výskyt takového speciálu nebo po konec strany. Na ka¾dé nové
stranì zaèíná sazba v barvì èerné. Nám ov¹em nìkdy mù¾e obarvený text pøetéci
na dal¹í stranu. Pak ale musíme v "\output" rutinì (v makru "\prepage")
nastavit barvu, která pøetekla. Ov¹em jak poznáme, ¾e nìco pøeteklo do dal¹í
strany? Jedinì pomocí REF souboru a asynchronního "\write". Proto makro \db
setcmykcolor nevkládá do PDF jen po¾adovaný speciál, ale taky ukládá o sobì
informaci do REF souboru. Jakmile se pøi ètení REF souboru objeví "\Xpage",
zkontrolujeme, zda naposledy nastavená barva byla èerná. Pokud ne, dáme
pokyn makru "\prepage" prostøednictvím makra "\pg:<èíslo strany>", aby
aktivovalo hned po "\headline" barvu naposledy nastavenou. To je tedy
základní idea, nyní implementace:
\inext{ifpdftex}{\count=2 \empty}{+-}
Makro \db pdfK má implicitnì hodnotu "k" (barva pro texty a plochy) a
pøechodnì pøi pou¾ití \db linecolor má hodnotu "K". Právì nastavenou barvu
pak makro "\setcmykcolor" ulo¾í do REF souboru.
Jen tehdy, kdy¾ je explicitnì
známo, ¾e barva nepøeteèe do dal¹í strany, je mo¾né pou¾ít pøed pou¾itím
pøepínaèe barvy prefix \db locpgcolor, který potlaèí v takovém pøípadì tisk
informací do REF souboru. Následnì budou potlaèeny zápisy informací do REF souboru
v¹ech barevných pøepínaèù a¾ po "\Black" vèetnì. Dal¹í barevné pøepínaèe (po
"\Black") se chovají u¾ normálnì.
Toto opatøení je zde jen proto, aby byl REF soubor
ponìkud men¹í a pøehlednìj¹í i v pøípadì, kdy navrhujeme bohatì barevný dokument.
Makra \db Xpdfcolork a \db XpdfcolorK udr¾ují pøi ètení REF souboru
informaci o naposledy vlo¾ené barvì v makrech \db pdflastcolork a
\db pdflastcolorK (název koresponduje typu barvy).
\inext{Xpdfcolork}{\empty}{+-}
Output rutina vkládá na ka¾dé stránce do REF souboru
\db Xpage "{<èíslo strany>}".
Toto makro pøi ètení REF souboru kontroluje, zda byla naposledy pou¾ita
na pøedcházející stranì barva èerná. Pokud ne, pøidá do makra
"\pg:<èíslo strany>" pokyn pro nastavení barvy ve tvaru
\db setpgcolor "<k nebo K>{<èísla barvy>}".
Toto makro se spustí v output rutinì na stranì "<èíslo strany>"
za "\headline", ale pøed sestavením boxu s textem.
\inext{Xpage}{\empty}{+-}
Makro \db draft vlo¾í do "\headline" box nulové vý¹ky a ¹íøky "\draftbox",
který vystrèí svou ¹edou sazbu ven ze svého rozmìru a je ti¹tìn døív, ne¾
jakýkoli jiný materiál na stránce.
\inext{draft}{\empty}{+-}
V makru \db draftbox "{<text>}" je "<text>" otoèen o 55 stupòù, zvìt¹en
desetkrát a vyti¹tìn v barvì "\LightGrey". K tomu jsou
vyu¾ity PDF transformace souøadnic.
\inext{draftbox}{\empty}{+-}
\subsec Klikací odkazy
%%%%%%%%%%%%%%%%%%%%%%
Makro \db destactive "[<typ>:<lejblík>]{<vý¹ka>}" zalo¾í cíl odkazu jen tehdy, kdy¾
je "<lejblík>" neprázdný. Ve vertikálním módu se nalepí na pøedchozí box
díky "\prevdepth=-1000pt" a po vlo¾ení boxu s cílem vrátí hodnotu
"\prevdepth" do pùvodního stavu, aby následující box byl správnì øádkován.
V horizontálním módu postì vlo¾í "\destbox". Makro
\db destbox "[<typ>:<lejblík>]{<vý¹ka>}" vytvoøí box nulové vý¹ky a z~nìj
vystrèí nahoru cíl klikacího odkazu vzdálený od úèaøí o "<vý¹ka>". Internì
pou¾ije pdf\TeX{}ový primitiv "\pdfdest" s parametrem "xyz", co¾
charakterizuje obvyklou mo¾nost chování PDF prohlí¾eèe pøi odskoku na cíl.
Podrobnìji viz manuál k~pdf\TeX{}u.
\inext{dest}{\empty}{+-}
V u¾ivatelské dokumentaci je zmínìno místo "\destactive" makro \db dest
se stejnými parametry. Toto makro je implicitnì prázdné a tedy neèiné.
Teprve "\hyperlinks" je pøinutí k èinnosti.
Klikací text vytvoøí makro \db link "[<typ>:<lejblík>]{<barva>}{<text>}".
Makro pou¾ívá
pdf\TeX{}ový primitiv "\pdfstartlink", ve kterém je vymezena vý¹ka a hloubka
aktivní plochy. Nakonec pøepne na po¾adovanou "<barvu>" (pokud není èerná),
vytiskne aktivní "<text>" a pøepne zpìt na èernou barvu. Pdf\TeX{}ový primitiv
"\pdfendlink" ukonèí sazbu aktivního textu.
\inext{link}{^^B\cbrace}{++}
Makro \db urllink "[<typ>:<lejblík>]{<text>}" pracuje
analogicky jako "\link". Jen navíc pøidává nìkteré
atributy do PDF výstupu a pracuje s barvou "\urlcolor".
\inext{urllink}{^^B\cbrace}{++}
Makro \db url "{<odkaz>}" vysází "<odkaz>" strojopisem, pøièem¾ znak "/" je
na pøechodnou dobu aktivní a definován jako "\\". Toto makro pøi sestavování
externího odkazu zpìtnì expanduje na normální "/", ale pøi sazbì expanduje
na "\discretionary", aby bylo mo¾né v nìm rozdìlit sazbu do více øádkù.
\inext{url}{\empty}{+-}
Makra \db toclink, \db pglink, \db citelink, \db reflink, \db ulink, která se
specializují na urèitý typ linku, implicitnì nedìlají nic:
\inext{toclink}{\empty}{+-}
Ov¹em po pou¾ití makra \db hyperlinks "{<barva-lok>}{<barva-url>}" se
uvedená makra "\toclink", "\pglink", "\citelink" a "\reflink" probouzejí k
¾ivotu:
\inext{hyperlinks}{\empty}{+-}
Pdf\TeX{}ové primitivy pro klikací odkazy dovolují dopravit do PDF dal¹í
atributy odkazu za slovem "attr". Tam je mo¾né dát najevo, ¾e chceme vidìt
aktivní plochy ve formì rámeèkù. To zaøídí makro \db pdfborder "{<typ>}", které
expanduje na nic, pokud není kontrolní sekvence "\<typ>border" definována.
Jinak expandují na "arrt" "/C" s obsahem podle "\<typ>border".
\inext{pdfborder}{\empty}{+-}
Pokud je dokument zpracován do DVI výstupu, je vhodné vý¹e zmínìná makra
deaktivovat:
\inext{ifpdftex}{\empty}{+-}
\subsec Outlines -- obsah v zálo¾ce PDF dokumentu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hlavní problém implementace strukturovaného obsahu do zálo¾ky PDF dokumentu
spoèívá v~tom, ¾e pøi vkládání jednotlivých polo¾ek obsahu je nutno znát
poèet pøímých potomkù ka¾dé polo¾ky (v~rámci stromové struktury polo¾ek),
ov¹em tito pøímí potomci budou zaøazeni pozdìji. OPmac tento problém øe¹í
dvìma prùchody nad daty, které jsou vytvoøeny pro tisk obsahu, tj. v makru
"\toclist". V prvním prùchodu spoèítá potøebné potomky a ve druhém
prùchodu zaøadí v¹echny polo¾ky postupnì jako \uv{outlines} do zálo¾ky.
Pøipomeneme si, ¾e v "\toclist" se nachází seznam maker tvaru
"\tocline{<odsazení>}{<font>}{<èíslo>}{<text>}{<strana>}".
Makro \db outlines "{<úroveò>}" nejprve nastaví "\tocline" na hodnotu
"\outlinesA" a projde "\toclist". Pak je nastaví na hodnotu "\outlinesB" a
znovu projde "\toclist".
\inext{def\nb outlines}{^^B\cbrace}{++}
V makru \db outlinesA "{<odsazení>}{<font>}{<èíslo>}{<text>}{<strana>}"
poèítáme potomky. Makro je navr¾eno tak, aby bylo
snadno roz¹íøitelné na libovolnou úroveò hloubky stromu, nicménì pro potøeby
OPmac staèí hloubka tøi (kapitoly, sekce, podsekce). Úroveò uzlu pøeèteme v
parametru "<odsazení>". Pro kapitolu je "<odsazení>"=0, pro sekci je
"<odsazení>"=1 a pro podsekci je "<odsazení>"=2.
Pøedstavme si vedle sebe øadu counterù
"\count0:\count1:\count2". Pøi sekvenèním ètení jednotlivých uzlù stromu
si ka¾dý uzel zvìt¹í v této pomyslné øadì hodnotu svého
counteru o~jednièku. Kapitoly zvìt¹ují "\count0", sekce "\count1", podsekce
"\count2". Staèí tedy zvìt¹it "\count<odsazení>". Øada counterù pak
jednoznaènì urèuje zpracovávaný uzel.
Uzly pro kapitoly mají pøidìlenu kontrolní sekvenci "ol:\the\count0"
a uzly pro sekce mají pøidìlenu kontrolní sekvenci
"ol:\the\count0:\the\count1". Jsou to makra, jejich¾ obsahem je poèet
potomkù daného uzlu. Makrem \db addoneol "<csname>" zvìt¹íme obsah dané
kontrolní sekvence o~jednièku. Pøíkazem "\ifcase<odsazení>" øe¹íme, kterému
rodièi je tøeba zvednout tuto hodnotu. Pøi nule (kapitola) nikomu,
nebo» daný uzel nemá rodièe. Pøi "<odsazeni>"=1 zvìt¹íme o
jednièku poèet potomkù nadøazené kapitole a pøi "<odsazeni>"=2 nadøazené
sekci. Asi by bylo pøehlednìj¹í na zaèátku definovat v¹echny potøebné
sekvence "ol:<nìco>" a nastavit jim hodnotu~0. Ov¹em ¹etøíme pamìtí i èasem,
tak¾e zakládáme sekvenci "ol:<nìco>" teprve v makru "\addoneol" a to tehdy,
kdy¾ je ji poprvé potøeba zvìt¹it o~jednièku.
\inext{outlinesA}{\count=2 ^^B\cbrace}{++}
V makru \db outlinesB "{<odsazení>}{<font>}{<èíslo>}{<text>}{<strana>}"
vkládáme jednotlivou polo¾ku obsahu do zálo¾ek pomocí pdf\TeX{}ového
primitivu\hfil\break
"\pdfoutline goto name{<lejblík>} count<potomci> {<text>}".
Èíslo "<potomci>" je opatøeno znaménkem mínus právì tehdy, kdy¾ chceme, aby
polo¾ka ve výchozím stavu nezobrazovala své potomky, ale jen trojúhelníèek.
Potomci se zobrazí a¾ po kliknutí na trojúhelníèek. V makru \db outlinelevel
máme makrem "\outlines" pøipravenu úroveò rozevøení, kterou si u¾ivatel
pøeje. Nejprve pøiètením "\count<odsazení>" dostaneme øadu
"\count0:\count1:\count2" do stejného stavu, jako v pøedchozím prvním prùchodu
a máme tím jednoznaènì pøidìlen uzel stromu.
Do "\tmpnum" vlo¾íme údaj o poètu potomkù daného uzlu. K tomu je potøeba
rozvìtvit výpoèet pøíkazem "\ifcase", proto¾e pro rùznou úroveò uzlu máme
údaj v~rùznì definovaném makru. Pøíkazem "\protectlist" zastavíme expanze
pøípadných maker registrovaných pomocí "\addprotect" a definujeme vlnku jako
mezeru (v zálo¾ce vypadá líp ne¾ vlnka). Dále pomocí "\setcnvcodesA"
expandujeme "\toasciidata". Pomocí "\setlccodes\toasciidata" pøipravíme
"\lccode" znakù tak, aby "\lowercase" odstranil háèky a èárky. To
vzápìtí provedeme, ale nejprve je¹tì do toho mù¾e promluvit u¾ivatel v makru
"\cnvhook", které je implicitnì nastaveno na makro prázdné.
\ilabel [outlines:setlccodes] {setlccodes}
\inext{outlinesB}{^^B\cbrace}{++}
Makro \db setcnvcodesA zkontroluje podle definovanosti "\r", zda je zapnutý
"\csaccents" a pokud je, expanduje "\toasciidata".
Makro \db toasciidata potøebujeme expandovat, proto¾e
neobsahuje pøímý zápis znakù. Dùvod je zøejmý, nechceme, aby se
soubor {\tt opmac.tex} stal závislý na pou¾itém kódování.
\inext{setcnvcodesA}{\count=2 ^^B\cbrace}{++}
Na øádku \cite[outlines:setlccodes]
se makro \db setlccodes spustí jako "\setlccodes AAÁAÄAáa...{}{}".
Toto makro si odloupne dva parametry "xy", provede "\lccode`x=`y" a
v~rekurzivním cyklu pokraèuje v~èinnosti, dokud nenarazí na "{}{}".
\inext{setlccodes}{\empty}{+-}
Makro \db insertoutline "{<text>}" vlo¾í jedinou polo¾ku do zálo¾ky. Pro
tuto polo¾ku se pøedpokládá nulový poèet potomkù. Vyu¾ití: u¾ivatel mù¾e
takto odkázat na zaèátek nebo konec dokumentu.
\inext{insertoutline}{\empty}{+-}
Pokud je dokument zpracován do DVI výstupu, je vhodné vý¹e zmínìná makra
deaktivovat:
\inext{ifpdftex}{\empty}{+-}
\subsec Verbatim
%%%%%%%%%%%%%%%%
Verbatim výpisy budou odsazeny o "\ttindent". Je nastaven na hodnotu
"\parindent" v dobì ètení souboru a spoleènì s "\parindent" by mìl
u¾ivatel zmìnit i "\ttindent". Èítaè \db ttline èísluje øádky bì¾ného verbatim výstupu,
èítaè \db viline èísluje øádky souboru èteného pomoci "\verbinput". Souborový
deskriptor \db vifile bude pøiøazen souboru v makru "\verbinput".
\inext{newcount}{\empty}{+-}
Makra \db setverb, \db begtt "...\endtt" jsou dokumentována v TBN, str. 29.
\inext{setverb}{testparA}{++}
Makro "\begtt" oèichá na konci své èinnosti, zda se nachází pod "\endtt"
prázdný øádek (alias "\par"). K tomu slou¾í makra \db testparA (pøeskoèí
mezeru, která za "\endtt" v¾dy je), \db testparB (pøeète následující znak
pomocí "\futurelet") a \db testparC (o¹etøí, zda tento následující znak je
"\par").
\inext{testparA}{\empty}{+-}
Makro \db activettchar pracuje podobnì, jako makro "\adef". Navíc
potøebuje pou¾ít novì naètený znak ve své aktivní kategorii jako separátor
vymezující konec parametru.
\inext{activettchar}{\empty}{+-}
Makro \db verbinput si pomocí "\tmpa" ovìøí, zda minule byl èten stejný
soubor. Pokud ne, otevøe soubor "#2" ke ètení pomocí "\openin" a ulo¾í do
\db vifilename jméno naposledy otevøeného souboru. Dále zkontroluje pomocí
"\ifeof", zda je mo¾né ze souboru èíst. Pokud ne, vypí¹e se varování a
pomocí \db skiptorelax se pøeskoèí zbytek obsahu makra a¾ po "\relax",
tak¾e se neprovede nic dal¹ího. Je-li soubor úspì¹nì otevøen nebo byl-li
otevøen ji¾ minule, pustí se makro "\verbinput" do prozkoumání parametru
"#1" zapsaného v závorce pøed jménem souboru.
\inext{verbinput}{\empty}{+-}
Cílem vyhodnocení parametru v závorce makra "\verbinput" jsou dva údaje: \db
vinolines bude obsahovat poèet øádkù, které je od zaèátku souboru nutno
pøeskoèit, ne¾ se má zahájit pøepisování øádkù a \db vidolines bude
obsahovat poèet øádkù, které se mají pøepsat ze souboru do dokumentu.
Písmena "vi" na zaèátku tìchto názvù pøedstavují zkratku pro "verbinput".
Vy¹etøení parametru ukonèeného textem "+\relax" se v makru \db
viscanparameter vìtví na pøípad, kdy parametr obsahuje symbol "+" a pou¾ije
se pak \db viscanplus. Druhý pøípad, kdy u¾ivatel nenapsal symbol plus
(tak¾e parametr "#2" makra "\viscanparameter" je prázdný) je dále
vy¹etøen v makru \db viscanminus. Obì makra si oddìlí do svých parametrù
první a druhou èíslici (ka¾dá z nich mù¾e být prázdná) a nastaví podle
zdokumentovaných pravidel pro zápis parametru odpovídající interní údaje
"\vinolines" a "\vidolines". Vychází pøitom z pøedpokladu, ¾e registr
"\viline" obsahuje èíslo naposledy pøeèteného øádku (nebo nulu, jsme-li na
zaèátku souboru).
\inext{viscanparameter}{\count=3 ^^B\cbrace}{++}
Makro \db doverbinput provede samotnou práci: pøeskoèí "\vinolines" øádkù a
pøepí¹e "\vidolines" øádkù. To provede v prvním a druhém cyklu "\loop". Ne¾
se k tìmto cyklùm dostane, musí udìlat jisté pøípravné práce. Nejprve odeète
od "\vinolines" poèet u¾ pøeètených øádkù, proto¾e pøi opakovaném ètení
stejného souboru jej neotevíráme znova, jen pøeskoèíme pøíslu¹ný men¹í poèet
øádkù. Pokud ale se uká¾e, ¾e rozdíl je záporný (je potøeba se v souboru
vracet dozadu), makro znovuotevøe soubor ke ètení pomocí "\openin" a upraví
podle toho pøíslu¹né údaje o øádcích. Pak zahájí skupinu, dále pomocí
"\setverb" nastaví speciálním znakùm kategorii 12 a pomocí "\adef{ }{ }"
nastaví mezeøe aktivní kategorii (bude expandovat na neaktivní mezeru jako
"\space") a také nastaví kategorii 12 znaku, který byl deklarován pomocí
"\activettchar". Pøipraví odsazení podle "\ttindent" a spustí u¾ivatelský
"\tthook". Je-li potøeba tisknout èísla øádkù, pøipraví si na to font
"\sevenrm", který má velikost rovnu 0,7 násobku základní velikosti. A pustí se do
zmínìných dvou cyklù "\loop". V obou cyklech se mù¾e stát, ¾e narazíme
neèekanì na konec souboru. To je o¹etøeno testem "\ifeof\vifile" a následnou
úpravou èítaèe "\tmpnum" tak, abychom okam¾itì vyskoèili z cyklu. Druhý
cyklus obsahuje je¹tì jeden speciální rys: pøeje-li si u¾ivatel èíst a¾ do
konce souboru, je nastaveno "\vidolines" na nulu a pøed zahájením cyklu je
èítaè "\tmpnum" nastaven na "-1". Uvnitø cyklu je pak zaji¹tìno, ¾e v tomto
pøípadì není èítaè zvìt¹ován o jednièku. Po ukonèení práce v tìchto dvou
cyklech je ukonèena skupina, vlo¾ena mezera "\ttskip" a makrem "\testparB"
se ovìøí, zda následuje prázdný øádek.
\inext{doverbinput}{^^B\cbrace}{++}
V prvním cyklu "\loop" v tìle makra "\doverbinput" se opakovanì volá
\db vireadline, co¾ je makro, které pøeète dal¹í øádek ze souboru. V druhém
cyklu se opakovanì volá "\vireadline" následované \db viprintline. Toto
makro vytiskne pøeètený øádek do dokumentu. Pøed øádkem mù¾e být v "\llap"
vyti¹tìno èíslo øádku. Zále¾í na hodnotì "\ttline". Je to naprogramováno v
souladu s u¾ivatelskou dokumentací.
\inext{vireadline}{\empty}{++}
\subsec Jednoduchá tabulka
%%%%%%%%%%%%%%%%%%%%%%%%%%
Tabulku makrem "\table" vytvoøíme jako "\vbox", ve kterém je "\halign". Je
tedy potøeba naèíst deklaraci typu "{llc|rr}" a pøevést ji na deklaraci pro
"\halign". Tato deklarace obsahuje znak "#" a tento znak se obtí¾nì pøidává
do tìla maker. Nashromá¾díme tedy postupnì deklaraci pro "\halign" do
registru typu "\toks", který je nazvaný \db tabdata. Dále definujeme interní
\db tabstrutA, který bude obsahovat u¾ivatelùv "\tabstrut", ov¹em pøechodnì
budeme toto makro mìnit. Také deklarujeme èítaè \db colnum, ve kterém budeme
mít po pøeètení deklarace ulo¾en poèet sloupcù tabulky.
Dále bìhem skenování "<deklarace>" vytvoøíme makro \db
ddlinedata, které bude obsahovat "&\dditem &\dditem ..." (poèet tìchto dvojic
bude roven $n-1$, kde $n$ je poèet sloupcù). Pokud je v deklaraci dvojitá
svislá èára, bude v makru "\ddlinedata" na pøíslu¹ném místì je¹tì "\vvitem".
Makro "\ddlinedata" pak pou¾ijeme v "\crli" a v "\tskip",
Strýèek Pøíhoda to mù¾e pou¾ít jinde a jinak. Koneènì makro \db vvleft je
neprázdné, pokud úplnì vlevo tabulky je dvojitá èára.
\inext{newtoks}{\empty}{+-}
Makro \db table "{<deklarace>}{<data>}" vypadá takto:
\inext{table}{\empty}{+-}
Makro \db scantabdata postupnì ète znak po znaku z deklarace "\table" a podle
pøeèteného znaku ukládá do "\tabdata" odpovídající úsek skuteèné deklarace pro
"\halign". Volá pøitom "\addtabvrule" nebo "\addtabitem{\tabdeclare<znak>}".
\inext{scantabdata}{^^B\cbrace}{++}
OPmac pøeddefinuje tøi "<znaky>" pro "<deklaraci>", sice "<znaky>" "c", "l", "r"
v makrech \db tabdeclarec, \db tabdeclarel, \db tabdeclarer.
\inext{tabdeclarec}{\empty}{+-}
Makro \db unsskip vkládané na konec ka¾dé datové polo¾ky odebere mezeru,
pokud má nenulovou základní velikost. U¾ivatelé toti¾ nìkdy dávají kolem
datových polo¾ek mezery a nìkdy ne, pøitom chtìjí, aby se jim to chovalo
stejnì. Je nároèné si pamatovat, ¾e mezery pøed polo¾kou jsou
ignorovány primitivem "\halign", ale mezery za polo¾kou jsou podstatné. Tak
radìji i mezery za polo¾kou udìláme nepodstatné.
\inext{unsskip}{}{++}
Pøíklad: po deklaraci: "{|cr||cl|}" makro "\scantabdata" vytvoøí:
\begtt
tabdata: \vrule\tabiteml\hfil#\unsskip\hfil\tabitemr
&\tabiteml\hfil#\unsskip\tabitemr \vrule\kern\vvkern\vrule
&\tabiteml\hfil#\unsskip\hfil\tabitemr
&\tabiteml#\unsskip\hfil\tabitemr\vrule
ddlinedata: &\dditem &\dditem\vvitem &\dditem &\dditem
\endtt
Makra \db addtabitem, \db addtabdata a \db addtabvrule vlo¾í do "\tabdata" a
"\ddlinedata" po¾adovaný údaj. Makro "\addtabitem" pozná podle "\colnum=0",
zda vkládá data pro první sloupec (nepøidává~"&") nebo pro dal¹í sloupce
(pøidává~"&"). Makro "\addtabvrule" pozná podle "\tmpa", zda pøed
ním pøedchází dal¹í "\vrule". Pokud ano, vlo¾í dodateènou mezeru
"\kern\vvkern" a pøidá "\vvitem" do "\ddlinedata".
\inext{addtabitem}{\empty}{+-}
Ne¾ se pustíme do výkladu dal¹ích maker, pøedvedeme pøíklad, ve kterém je
definováno dal¹í písmeno "P" pro "<deklaraci>". Písmeno "P" vymezí
tabulkovou polo¾ku, je¾ má stanovenou ¹íøku a del¹í text se láme
do více øádkù. Je mo¾né si vyzkou¹et tøeba tento kód:
\begtt
\newdimen\Pwidth
\def\tabdeclareP {\enskip\vtop{\hsize=\Pwidth \rightskip=0pt plus1fil
\baselineskip=1.2em \lineskiplimit=0pt \noindent ##\tabstrut}\enskip}
\Pwidth=3cm \table{|c|P|}{\crl
aaa & Tady je del¹í textík, který se nevejde na jeden øádek. \crl
bb & A tady je taky je nìco del¹ího. \crl}
\endtt
Pus»me se nyní do rozboru maker na ukonèení øádkù.
Makro \db crl pøidá èáru pomocí "\noalign". Makro \db crll pøidá dvojitou
èáru pomocí "\noalign".
\inext{crl}{\empty}{+-}
Makro \db crli provede "\cr" a dále se vnoøí do øádku tabulky, ve kterém
klade postupnì následující
"\omit\tablinefil &\omit\tablinefil &..."
Pøitom v místì dvojité vertikální èáry naklade navíc "\tabvvline".
Makro \db tablinefil vlo¾í natahovací èáru na ¹íøku celé polo¾ky a makro
\db tabvvline vlo¾í dvì "\vrule" vzdáleny od sebe o "\vvkern". Tím vzniká
pøetrzené místo v postupnì tvoøené lince. Ke správnému nakladení uvedených
povelù pou¾ije makro "\crli" obsah makra "\ddlinedata" a vlevo pøidává
"\vvleft". Pøed spu¹tìním makra "\ddlinedata" definuje odpovídajícím
zpùsobem \db dditem a \db vvitem.
\inext{crli}{\empty}{+-}
Makro \db tskip prostøednictvím \db tskipA pøechodnì vyprázdní "\tabstrut"
pøedefinováním "\tabstrutA" a také vyprázdní "\dditem" a "\vvitem", aby
po pou¾ití "\ddlinedata" vznikl øádek tabulky s prázdnými polo¾kami.
Øádek je vypodlo¾ený strutem stanovené vý¹ky "\tmpdim".
Nakonec je potøeba vrátit "\tabstrutA" do pùvodního stavu.
\inext{tskip}{\empty}{+-}
Globální zmìna ¹íøek v¹ech linek tvoøených pomocí "\vrule" a "\hrule" je
provedena makry \db rulewidth a \db rulewidthA. My¹lenka je dokumentována v
TBN na str.~328.
\dgn \nb orivrule
\dgn \nb orihrule
\inext{orihrule}{\empty}{+-}
Makro \db frame "{<text>}" vlo¾í vnìj¹í "\vbox{\hrule..\hrule}".
V nìm se nachází dal¹í box "\hbox{\vrule\kern\vvkern..\kern\vvkern\vrule}"
a v nìm "\vbox{\kern\hhkern..\kern\hhkern}".
Nejvíce uvnitø je pak "\hbox{<text>}". To by pro sazbu rámovaného textu
staèilo, nicménì my je¹tì øe¹íme úpravu výsledného boxu tak, aby mìl úèaøí
ve stejném místì jako je úèaøí textu. Proto ulo¾íme "\hbox{<text>}" do boxu0
a zmìøíme mu hloubku. V promìnné "\tmpdim" spoèítáme celkovou hloubku
výsledného boxu. Ve výpoètu pøièítáme vý¹ku "\hrule", která nemusí být
0.4pt. Proto si její vý¹ku zmìøíme v boxu1. Ve vnìj¹ím "\vbox"u po
nakreslení spodní "\hrule" se pak vracíme pomocí "\kern-\tmpdim" na úroveò
úèaøí a zde umístíme strut hloubky "\tmpdim", aby sazba smìrem dolù
nepøeènívala, ale byla obsa¾ena v~hloubce výsledného boxu.
\inext{frame}{\empty}{+-}
\subsec Vlo¾ení obrázku
%%%%%%%%%%%%%%%%%%%%%%%
Nejprve deklarujeme \db picw.
\inext{picw}{\empty}{+-}
Makro \db inspic je zkratka za pou¾ití primitivù "\pdfximage",
"\pdfrefximage" a "\pdflastximage". Kdo si to má poøád pamatovat.
Není-li aktivován PDF výstup, napí¹eme jen varování a neprovedeme nic.
Pokud budeme pracovat se stejnými obrázky v jednom dokumentu, bude
vhodné mo¾nosti primitivù vyu¾ít naplno.
\inext{ifpdftex}{\empty}{+-}
\subsec PDF transformace
%%%%%%%%%%%%%%%%%%%%%%%%
Makro \db pdfscale "{<vodorovnì>}{<svisle>}" pracuje jednodu¹e:
\inext{pdfscale}{\empty}{+-}
Na druhé stranì makro \db pdfrotate "<úhel>" vytvoøí
"\pdfsetmatrix{"$\cos\varphi\enspace\sin\varphi\enspace
{-}\sin\varphi\enspace\cos\varphi$"}",
co¾ není jednoduché, proto¾e funkce $\cos$, $\sin$ nejsou v \TeX{}u
implementovány. Balíèek "trig.sty" nabízí vyhodnocování tìchto funkcí pomocí
Taylorových polynomù, nicménì OPmac nechce být závislý na balíècích a také
chce ukázat alternativní zpùsob implementace. Makro "\pdfrotate" pracuje
zhruba takto: je-li argument 0, neprovede nic, je-li argument 90, provede
otoèení o~90 stupòù. V ostatních pøípadech zavolá makro \db pdfrotateA,
které rozlo¾í argument na celou "#1" a zlomkovou "#2" èást. V dal¹í èásti
na øádcích~\cite[rot:360] a¾ \cite[rot:smallsin] se
zabývá jen celými stupni. Nejprve pomocí prvního a druhého "\loop" posune
argument o celé násobky 360 stupòù tak, ¾e poté je argument mezi
0 a¾ 360 stupni, a pøitom se hodnoty funkcí $\sin$ a $\cos$ nezmìnily.
Ve tøetím "\loop" postupnì sni¾uje argument o 90 stupòù a pøitom dìlá rotaci
o 90 stupòù tak dlouho, a¾ máme argument mezi nulou a devadesáti.
Je-li dále argument vìt¹í ne¾ 44 stupòù, otoèíme se o 45 a sní¾íme argument
o 45. Je-li dále argument vìt¹í ne¾ 22, otoèíme se o 22 a sní¾íme argument o
22. Nyní máme argument v mno¾inì $\{0, 1, 2, 3, \ldots, 22\}$. Pro ka¾dý
prvek z této mno¾iny argumentù máme pøedpøipraveny hodnoty funkcí
$\cos$ a $\sin$ v makrech \db smallcos a \db smallsin. Pou¾ijeme je pro
závìreènou rotaci. Tím máme sazbu otoèenou o celé stupnì.
Dal¹í èást makra na øádcích \cite[rot:174532] a¾ \cite[rot:else]
øe¹í jemné dotoèení podle zlomkové èásti argumentu. V intervalu nula a¾
jeden stupeò aproximujeme funkci $\cos$ konstantní jednièkou a funkci $\sin$
lineární funkcí $x\cdot \pi/180$. V daném rozmezí je to velmi dobrá
aproximace.
\ilabel [rot:360] {360}
\ilabel [rot:smallsin] {smallsin}
\ilabel [rot:174532] {174532}
\ilabel [rot:else] {else\space\space\space\space}
\inext{pdfrotate}{\empty}{+-}
Pro pøípad, ¾e nepracujeme s PDF výstupem, definujeme klíèové primitivy
pdf\TeX{}u jako makra, která nedìlají nic.
\inext{ifpdftex}{\empty}{+-}
\subsec Poznámky pod èarou a na okraji stránek
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Makro \db fnote pøedpokládá, ¾e správné èíslo poznámky na dané stránce je
pøipraveno v makru "\fn:<èíslo>", kde "<èíslo>" je celkové èíslo poznámky napøíè
celým dokumentem sledované globálním èítaèem \db fnotenum.
Makro ohlásí svou existenci do REF souboru záznamem "\Xfnote" (bez
parametru). Dále vytiskne znaèku pomocí "\fnmarkx" a ve skupinì pøejde na
men¹í sazbu a zavolá plain\TeX{}ové makro "\vfootnote", které vlo¾í sazbu
pomocí tzv. insertu (TBN, kapitola~6.7). Plain\TeX{}ové nastavení této
tøídy insertu není makrem OPmac nijak mìnìno.
\inext{fnotenum}{^^B\cbrace}{++}
Makro \db fnotemark pøiète lokálnì k "\fnotenum" svùj parametr a vytiskne
odpovídající znaèku. Celá práce makra probíhá ve skupinì, tak¾e po ukonèení
makra se "\fnotenum" vrátí do své pùvodní hodnoty.
\inext{fnotemark}{^^B\cbrace}{++}
Makro \db fnotetext teprve zvedne èítaè "\fnotenum" globálnì a vytiskne
poznámku pomocí plain\TeX{}ového "\vfootnote".
\inext{fnotetext}{^^B\cbrace}{++}
Makro \db fnmarkx vytiskne otazník nebo \db thefnote. Pøedpokládá se, ¾e si
u¾ivatel pøedefinuje "\thefnote" k obrazu svému. Lokální èíslo poznámky na
stránce má pøipraveno v makru \db locfnum.
\inext{fnmarkx}{\empty}{+-}
Pøi ètení REF souboru se pro ka¾dou stranu pøeète nejprve "\Xpage", co¾
je makro, které pronuluje \db fnotenumlocal.
Makru \db Xfnote tedy staèí pozvednout
"\fnotenumlocal" o jednièku a pomocí
"\sxdef" si tuto hodnotu zapamatovat v makru "\fn:<èíslo>".
\inext{Xfnote}{\empty}{+-}
Registr \db mnotenum.
globálnì èísluje okrajové poznámky a plní podobnou funkci, jako registr
"\fnotenum" pro podèárové poznámky.
\inext{mnotenum}{\empty}{+-}
Makro \db mnote ve vertikálním módu zalo¾í box nulové vý¹ky pomocí
"\mnoteA" a vycouvá na pùvodní místo sazby pomocí "\vskip-\baselineskip". V
odstavcovém módu toto makro nalepí box nulové vý¹ky pod právì vytvoøený
øádek v odstavci. Víme, ¾e "\vadjust" nalepí svùj materiál bez mezery pod
tento øádek. My ov¹em potøebujeme vycouvat nahoru na úèaøí øádku. To nejde
snadno provést, proto¾e hloubka øádku je promìnlivá. Proto do je øádku
vlo¾en "\strut" a pøedpokládá se, ¾e nyní má øádek hloubku "\dp\strutbox" a
o tento rozmìr makro vycouvá nahoru. Vlo¾í po¾adovaný box vý¹ky nula na
úrovni úèaøí a pak se vrátí na pùvodní místo.
\inext{mnote}{^^B\cbrace}{++}
Makro \db mnoteA si zjistí, zda je v makru "\mn:<èíslo>" ulo¾en primitivní
pøíkaz "\left" nebo "\right". Podle toho pozná, zda má umístit poznámku
doleva nebo doprava. Rovnì¾ dá o sobì vìdìt do REF souboru vlo¾ením
sekvence "\Xmnote" (bez parametru). Sazba musí v obou pøípadech vyprodukovat
box nulové vý¹ky i hloubky. Proto je "\vtop", uvnitø kterého je text poznámky
zpracován, vlo¾en pøechodnì do boxu0 a je mu pronulována hloubka. Nulová
vý¹ka je zaøízena pomocí "\vbox to0pt{\vss\box0}". Vlastní sazbu poznámky
zahajujeme pomocí "\noindent" s tím, ¾e je pøipraven pru¾ný "\leftskip" nebo
"\rightskip" podle toho, zda poznámku klademe vlevo nebo vpravo.
Pøi kladení vlevo musíme pou¾ít "fill", abychom pøeprali natahovací mezeru z
"\parfillskip".
\inext{mnoteA}{^^B\cbrace}{++}
Makro \db Xmnote pracuje bìhem ètení REF souboru a vyu¾ívá toho, ¾e makro
"\Xpage" nastavuje èíslo právì procesované strany do registru "\lastpge".
Tak¾e staèí pou¾ít "\sxdef" následujícím zpùsobem:
\inext{Xmnote}{\empty}{+-}
Makro \db fixmnotes "<token>" pøedefinuje v cyklu v¹echny definované
kontrolní sekvence "\mn:<èíslo>" tak, ¾e budou obsahovat "<token>".
Tím toto makro zlikviduje práci makra "\Xmnote", ale o to nám právì jde v
pøípadì, kdy¾ chceme mít poznámky na jednoznaènì dané stranì.
\inext{fixmnotes}{\empty}{+-}
\subsec Bibliografické reference
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Nejprve uvedeme deklarace deskriptoru \db auxfile a èítaèù
\db bibnum a \db lastcitenum.
\inext{auxfile}{\empty}{+-}
Makro \db cite "[<lejblík1>,<lejblík2>,...]" si prostøednictvím \db citeA
zavolá "\rcite{<lejblík>}" pro ka¾dou jednotlivou polo¾ku oddìlenou èárkou ve
svém parametru. Makro "\citeA" v sobì skrývá fintu: parametr nemá jediný,
ale dva "#1#2". Proto¾e první z nich je neseparovaný, ignorují se pøípadné
mezery za èárkou a "#1" obsahuje první písmeno "<lejblíku>". Uvnitø makra
mìníme \db citesep, co¾ je èárka a mezera, která se má mezi údaji
vytisknout. Makro \db nocite "[<lejblík1>,<lejblík2>,...]" je definováno
stejnì, jen pøipraví jiný význam makra "\docite", krz které budeme tisknout
v "\rcite" potøebný údaj. Ve¹kerá
èinnost maker "\cite" a "\nocite" probíhá uvnitø skupiny.
\inext{cite}{\empty}{+-}
Makro \db rcite "{<lejblík>}" øe¹í zhruba øeèeno následující vìci:
\begitems
\item * Zjistí, zda je definován "\csname bib:<lejblík>\endcsname". Pokud ano,
vytiskne jeho hodnotu, pokud ne, vytiskne do textu otazníky a na terminál
varování. Tato kontrolní sekvence zaène být známá po pou¾ití
"\bib[<lejblík>]" nebo "\bibitem{<lejblík>}". Tato makra ulo¾í
odpovídající informaci do REF souboru, odkud ji pøi opakovaném \TeX{}ování
vyzvedneme. Je to klasická èinnost, kterou provozujeme i u ostatních
køí¾ových referencí.
\item * Ulo¾í o sobì zprávu do bufferu "\citelist". To pou¾ijeme
v makrech "\usebibtex" nebo "\usebbl".
\enditems
Makro "\rcite" je naprogramováno zhruba takto
\def\begtthook{\langleactive}
\begtt
function rcite(<lejblík>) {
if (<lejblík> == '*') { <zapi¹ do> \citelist '*'; return; }
if (\bib:<lejblík> == nedef) {
<zapi¹ do> \citelist <lejblík>;
<na terminál:> "Warning, cite [label] unknown";
<do tiskového výstupu:> "??";
\bib:<lejblík> = empty;
return;
if (\bib:<lejblík> == empty) {
<do tiskového výstupu:> "??";
return;
}
if (\bib:<lejblík> konèí znakem '&') {
<zapi¹ do> \citelist <lejblík>;
<odstraò znak & z obsahu makra> \bib:<lejblík>;
}
<tiskni obsah makra> \bib:<lejblík>;
}
\endtt
\def\begtthook{\langleactive}
Výklad kódu: Proto¾e chceme ¹etøit pamìtí bufferu "\citelist", zapisujeme
tam ka¾dý "<lejblík>" jen jednou. Zda se nedeklarovaný "<lejblík>" vyskytl
poprvé poznáme podle nedefinované hodnoty "\bib:<lejblík>". Zda se vyskytl
pozdìji znovu poznáme podle toho, ¾e má hodnotu "empty". Zda se deklarovaný
"<lejblík>" vyskytl poprvé poznáme podle znaku "&" v jeho obsahu.
Návrh kódu v C-like notaci nyní pøevedeme do maker v \TeX{}u:
\inext{rcite}{^^B\cbrace}{++}
Asi nejzajímavìj¹í vychytávka v tomto makru se týká testu na znak "&".
Implicitnì pøi ètení REF souboru se do makra "bib:<lejblík>" ulo¾í
"\bibnn{<hodnota>}&". Pøíkaz "\if" za sebou totálnì expanduje v¹e
následující, tak¾e nejprve narazí na "&", pak se obsah "bib:<lejblík>"
expanduje prostøednictvím \db bibnn "{<hodnota>}" na nic a za tímto
\uv{nic} se zjeví druhý znak "&", který se tedy pøilepí na ten první.
Ano, je pravda, ¾e tyto dva znaky jsou stejné.
Odstranìní tohoto znaku probíhá znovu totální expanzí,
tentokrát "\bibnn" první parametr "<hodnota>" zopakuje a druhý parametr
se znakem "&" zahodí.
Ne¾ se pustíme do výkladu makra "\docite", pøipravíme si makra \db printcite
"<polo¾ka>" a \db printdashcite "<polo¾ka>". První z nich tiskne jednu
polo¾ku oddìlenou od pøípadné
dal¹í èárkou, druhé tiskne polo¾ku, pøed kterou pøedchází pomlèka
vyznaèující interval polo¾ek. Pointa makra "\printcite" je v tom, ¾e si samo
po prvním zavolání pøipraví separátor "\citesep" (který je na zaèátku
èinnosti "\cite" prázdný), tak¾e pøi opakovaném volání "\printcite" se
vytiskne i po¾adovaný separátor. Pomlèka v "\printdashcite" je schována do
"\hbox", aby nedocházelo tìsnì za ní ke zlomu øádku.
\inext{printcite}{\empty}{+-}
Makro \db docite "<polo¾ka>" vytiskne otazníky pøi prázdném parametru a jinak
vytiskne prostøednictvím "\printcite" jednu "<polo¾ku>". Kromì toho øe¹í pøi
nenulovém "\lastcitenum" sluèování po sobì následujících èísel polo¾ek do
intervalù. Naposledy vyti¹tìnou polo¾ku uchovává v registru "\lastcitenum".
Pøi pøí¹tím zavolání zvìt¹í "\lastcitenum" o jednièku a srovná ji s
"<polo¾kou>". Jsou-li si rovny, jde o následující polo¾ku v øadì a takovou
polo¾ku netiskneme, nicménì si její hodnotu uchováme v "\tmpb". Pokud je
mezi souvislou øadou polo¾ek díra, tj. "\lastcitenum" se nerovná
"<polo¾ce>", pak dovytiskneme pøedchozí interval pomocí
"\printdashcite{\the\tmpb}" a následnì vytiskneme i "<polo¾ku>".
Makro \db shortcitations jednodu¹e nastavuje "\lastcitenum" na nenulovou
hodnotu a tím probudí k ¾ivotu hlavní èást makra "\docite".
\inext{docite}{\empty}{+-}
Následuje kód makra \db bib "[<lejblík>]", které prostøednictvím
\db wbib "{<lejblík>}{<hodnota>}" vlo¾í do REF souboru propojené údaje o
tom, jaké má "<lejblík>" pøiøazeno èíslo v seznamu literatury.
Makro "\wbib" pøipojí pøed "\wref" pøíkaz "\immediate" právì tehdy, kdy¾
"\wref" je ve stavu, kdy skuteènì zapisuje do souboru REF.
Makro \db Xbib pracuje pøi ètení souboru REF a dìlá to, co jsme si øekli u¾
døíve: nastaví hodnotu makra "\bib:<lejblík>" na "\bibnn{<hodnota>}&".
\inext{bib}{\empty}{+-}
Makro \db addcitelist "{<lejblík>}" pøidá do \db citelist údaj ve tvaru
"\lcite[<lejblík>]". Hranaté závorky jsou pou¾ity proto, aby fungoval test
"\isinlist\citelist{[<lejblík>]}". Jak uvidíme za chvíli, makro
"\addcitelist" zmìní bìhem èinnosti makra "\usebibtex" svùj význam na
\db writeaux, aby pøípadné pou¾ití "\cite" a¾ za "\usebitex" rovnou
zapisovalo do AUX souboru. Podobnì makro "\addcitelist" zmìní v makru
"\usebbl" svùj význam \db writeXcite "{<lejblík>}",
aby v pøí¹tím prùchodu \TeX{}em
mìlo makro "\usebbl" pøehled i o výskytech "\cite", které jsou napsány
pozdìji, ne¾ "\usebbl".
\inext{addcitelist}{\empty}{+-}
Ne¾ se pustíme do výkladu maker "\usebibtex", "\genbbl" a "\usebbl", uvedeme
struènì popis èinnosti Bib\TeX{}u. Pøíkaz "bibtex <dokument>" zpùsobí,
¾e program {\tt bibtex} se podívá do souboru "<dokument>.aux" a tam si v¹ímá
sekvencí \db bibdata "{<bib-báze>}", \db bibstyle "{<bib-style>}" a \db
citation "{<lejblík>}". Na základì toho následnì pøeète soubor
"<bib-báze>.bib" se zdrojovými zápisy bibliografických údajù.
Pro konverzi tìchto zdrojových zápisù do výstupního souboru "<dokument>.bbl"
pou¾ije stylový soubor "<bib-style>.bst". Není-li mezi sekvencemi
"\citation" uvedeno "\citation{*}", program {\tt bibtex} zahrne do výstupu
jen ty bibliografické údaje, které mají "<lejblík>" shodný s nìkterým z
"<lejblíkù>" uvedených v parametrech sekvencí "\citation". Ka¾dá sekvence
"\citation{<lejblík>}" v souboru "<dokument>.aux" typicky odpovídá jednomu
pou¾ití pøíkazu "\cite[<lejblík>]".
Makro \db usebibtex "{<bib-báze>}{<bst-styl>}" otevøe soubor AUX
prostøednictvím \db openauxfile "{<bib-báze>}{<bst-styl>}". Napí¹e tam tedy
po¾adovaná data pro Bib\TeX{}. Dále z "\citelist" pøepí¹e do AUX souboru
lejbílky ve formátu "\citation{<lejblík>}". Nakonec se uvnitø skupiny pustí do
ètení souboru BBL prostøednictvím makra "\readbblfile".
\inext{usebibtex}{\count=2 ^^B\cbrace}{++}
Makro \db readbblfile "{<soubor>}" vyzkou¹í, zda je "<soubor>.bbl" pøipraven
ke ètení. Pokud ne, podá o tom odpovídající zprávu na terminál. Jinak
nastaví èítaè "\bibnum" na nulu a (vìdomo si toho, ¾e je spu¹tìno ve
skupinì) pustí se do lokálních re-definic La\TeX{}ových konstrukcí, které se
typicky v BBL souborech pou¾ívají. Nastaví "\leftskip" na "\iindent" a
spustí "\bibtexhook". Koneènì naète soubor BBL.
\inext{readbblfile}{^^B\cbrace}{++}
V BBL souboru se vyskytují povely \db bibitem. Za ka¾dým z nich se mo¾ná
objeví parametr v~hranaté závorce "[<znaèka>]" a následnì je uveden
"{<lejblík>}". Pak na dal¹ích øádcích jsou bibliografická data jednoho
záznamu ukonèená prázdným øádkem. Objeví-li se "[<znaèka>]", dává tím
Bib\TeX{} najevo, ¾e se má tato "<znaèka>" pou¾ít místo bì¾ného èíslování
záznamù. Následuje kód, který takové údaje pøeète, vytiskne a vlo¾í do REF
souboru o tom zprávu prostøednictvím "\wref{<lejblík>}{<hodnota>}".
Rozli¹ují se dva re¾imy tisku: není-li pøítomna "[<znaèka>]" (makro "\tmpa"
je prázdné), pak pomocí "\llap" vytiskneme "[<èíslo>]". Jinak se posuneme o
"-\iindent" a tiskneme "[<znaèku>]" následovanou mezerou. Pak se vytisknou
dal¹í údaje bibliografického záznamu.
\dgn\nb bibitemB \dgn\nb bibitemC \dgn\nb bibitemD
\inext{bibitem}{^^B\cbrace}{++}
Makro \db genbbl "{<bib-báze>}{<bst-style>}" otevøe AUX soubor a zapí¹e do
nìj údaje potøebné pro Bib\TeX{} vèetnì "\citation{*}". Poté se makro pokusí
pøeèíst výstup z Bib\TeX{}u pomocí "\readbblfile". V tomto pøípadì pracuje
"\bibitem" ve zvlá¹tním re¾imu, kdy netiskne "<hodnoty>", ale "<lejblíky>".
Z toho dùvodu je pøedefinováno makro "\bibitemC".
\inext{genbbl}{^^B\cbrace}{++}
Makro \db usebbl "/<typ> <bbl-file>" spustí jiné makro s názvem "\bbl:<typ>".
Tøi taková makra jsou definována pomocí "\sdef". První "\bbl:a" je
jednoduché: prostì projde BBL soubor a vytiskne údaje z~nìj.
Druhé makro "\bbl:b" projde BBL
soubor v re¾imu, pøi kterém jsou bibliografická data ka¾dého záznamu (a¾ po
prázdný øádek alias "\par") pøeètena do parametru "#2" makra "\bibitemC".
Celý údaj je pak vyti¹tìn jen za pøedpokladu, ¾e "[<lejblík>]" je pøítomen
v seznamu "\citelist". Tøetí makro "\bbl:c" pracuje jako druhé a¾ na to, ¾e
údaj netiskne, ale zapamatuje si ho do makra "\bb:<lejblík>". Po takovém
projití BBL souboru je¹tì projde "\citelist", kde se "\lcite[<lejblík>]"
promìní v "\bb:<lejblík>", tak¾e se záznam vytiskne. Nyní ale v poøadí, v
jakém jsou "<lejblíky>" zaøazeny do "\citelist".
\inext{usebbl}{\count=3 ^^B\cbrace}{++}
Za zmínku stojí je¹tì práce uvedených maker s "\citelist". Pøed výskytem makra
"\usebbl" se lejblíky z "\cite" a "\nocite" hromadí v "\citelist". Ov¹em
dal¹í "\cite" a "\nocite" se mohou vyskytovat za pøíkazem "\usebbl". Pokud
se tak stane, pracuje "\addcitelist" nyní ve významu "\writeXcite" a ulo¾í
potøebnou informaci do REF souboru. Pøi dal¹ím \TeX{}ování se tato informace
pøeète makrem \db Xcite "{<lejblík>}" z REF souboru takto:
\inext{Xcite}{}{++}
\noindent To tedy znamená, ¾e se ulo¾í do seznamu "\citelistB". Koneènì
makra "\bbl:b" a "\bbl:c" si dva seznamy "\citelist" a "\citelistB" pøed svou
èinností spojí do seznamu jediného nazvaného "\citelist".
\subsec Úprava output rutiny
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Místo pùvodního makra "\plainoutput" pou¾ívá OPmac makro \db opmacoutput.
\inext{opmacoutput}{}{++}
OPmac mìní output rutinu proti originální "\plainoutput" jen v nejnutnìj¹ích vìcech.
Øe¹í tyto dva problémy:
\begitems
\item * Místo pøímého "\shipout" nechá nejprve box sestavit jako "\box0",
pak provede "\protectlist" a pak provede "\shipout\box0".
\item * Mezi "\makeheadline" a "\pagebody" vkládá "\prepage".
\enditems
První úprava zabrání expanzi maker pøidaných pomocí "\addprotect" v dobì
práce pøíkazu "\shipout", tedy v dobì, kdy expandují parametry "\write".
Tìmto makrùm je pøidìlen prostøednictvím \db doprotect "<makro>"
pro tento okam¾ik význam "\relax". Je potøeba je¹tì
vysvìtlit, proè bylo nutné sestavit nejprve "\box0" a teprve poté jej poslat
ven pomocí "\shipout". Je to z toho dùvodu, ¾e v dobì sestavování "\box0"
jsou expandována "\headline" a "\footline" a pro ten pøípad je¹tì chceme,
aby v¹echna makra správnì expandovala.
\inext{opmacoutput}{\empty}{+-}
Druhá úprava vkládá makro \db prepage, které zajistí tisk stránkové èíslice
do "\reffile" (to je docela u¾iteèné) a kromì toho nastaví správnou barvu
textu strany pomocí PDF speciálu.
\inext{prepage}{\empty}{+-}
Kdy¾ budou barvy pøecházet na dal¹í stranu, jistì nechceme mít obarvenu
stránkovou èíslici. Je tedy potøeba do \db footline vlo¾it "\Black". Kdy¾
bude u¾ivatel mìnit velikost fontù v dokumentu, jistì nechce mít
stránkovou èíslici poka¾dé jinak velkou. Proto je do "\footline" vlo¾eno
"\thefontsize". Je nastaveno pevnì na 10pt. Pøedpokládáme, ¾e pokud bude
nìkdo chtít jinak velkou stránkovou èíslici, jednodu¾e si "\footline"
nastaví podle svého. Jinak je "\footline" shodná s pùvodním nastavením
v~plain\TeX{}u.
\inext{footline}{\empty}{+-}
\subsec Okraje
%%%%%%%%%%%%%%
V registrech \db pgwidth a \db pgheight budeme mít po zavolání "\setpagedimens"
¹íøku a vý¹ku strany. V~registru \db shiftoffset budeme mít pøípadný rozdíl
okrajù mezi levou a pravou stránkou.
\inext{pgwidth}{\empty}{+-}
Makro \db margins "/<typ> <formát> (<levý>,<pravý>,<horní>,<dolní>)<jednotka> "
si nastaví registry "\pgwidth" a "\pgheight" prostøednictvím "\setpagedimens" a dále v
souladu s u¾ivatelskou dokumentací nastaví potøebné okraje. V makru "\tmp"
je schována jednotka, kterou u¾ivatel taky mù¾e zapomenout napsat. V~takovém
pøípadì vypí¹eme varování a doplníme jednotku "mm". Jakmile mìníme
"\hoffset" nebo "\voffset", nastavíme je nejprve na "-1in" (tím se dostaneme
na okraj papíru) a pak budeme po¾adovanou velikost okraje k tìmto registrùm
pøidávat. Nemohu za to, ¾e Knutha napadla taková ne pøíli¹ podaøená my¹lenka
dát výchozí bod sazby kamsi doprostøed papíru umístìný pomocí ujetých
jednotek. Za zmínku stojí je¹tì dvì my¹lenky. Makro \db rbmargin
"\h(v)offset\h(v)size{<okraj>}" provede výpoèet hodnoty "\hoffset" nebo
"\voffset" v pøípadì, ¾e je dána protìj¹í hodnota okraje ne¾ je okraj pøímo
nastavitelný pomocí "\h(v)offset".
A koneènì posun okraje pøi pøechodu z pravé na levou stránku "\shiftoffset"
poèítáme jako "\pgwidth"~"-"~"\hsize"~"-"~"2*<levý>" co¾ dá stejnou hodnotu jako
"<pravý>-<levý>". Zmìna "\hoffset" o tuto hodnotu je provedena v makru
"\pghook", tedy v "\output" rutinì, schována do skupiny, tak¾e po ukonèení
"\output" rutiny se vrátí "\hoffset" na pùvodní hodnotu.
\inext{margins}{\empty}{+-}
Makro \db setpagedimens "<formát> " spustí
\db setpagedimensA "<¹íøka> <vý¹ka>/" a k tomu musí
dopøedu vyexpandovat obsah makra "\pgs:<formát>". To provedeme pomocí tøí
"\expandafter".
\inext{setpagedimens}{\empty}{+-}
Jednotlivé "<formáty>" papíru je potøeba deklarovat.
\inext{sdef}{\empty}{+-}
Makro \db magscale "[<factor>]" zvìt¹í/zmen¹í sazbu nastavením
registru "\mag" a definuje dosud prázdné makro \db trueunit hodnotou
"true", aby pozdìji pøi èinnosti makra "\setpagedimensA" zùstaly zachovány
rozmìry stránek. Pokud ale je makro "\magscale" spu¹tìno a¾ po nastavení
velikosti stránek, jsou tyto velikosti dodateènì korigovány na \uv{true}
jednotky pomocí makra \db truedimen.
\inext{trueunit}{\empty}{+-}
\subsec Závìr
%%%%%%%%%%%%%
Nakonec pomocí "\inputref" pøeèteme REF soubor (pokud existuje) a vrhneme se
na zpracování dokumentu, který nám pøipravil u¾ivatel. Pøeji dobré poøízení.
\inext{inputref}{\empty}{+-}
\doindex
\bye
|