summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/a2ping/a2ping.pl
blob: 2f1a3bda4f8b779c526c9bd5fe37cd787a8d177b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
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
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
#! /bin/sh
eval '(exit $?0)' && eval 'PERL_BADLANG=x;export PERL_BADLANG;: \
;exec perl -x -- "$0" ${1+"$@"};#'if 0;
eval 'setenv PERL_BADLANG x;exec perl -x -- "$0" $argv:q;#'.q+
#!perl -w
package Htex::a2ping;  $0=~/(.*)/s;unshift@INC,'.';do($1);die$@if$@;__END__+if !1;
# Don't touch/remove any lines above; http://www.inf.bme.hu/~pts/justlib
#
# This program is free software, licensed under the GNU GPL, >=2.0.
# This software comes with absolutely NO WARRANTY. Use at your own risk!
#
# !! Ghostcript compute pipe too slow
#      $ a2ping.pl -v debrecen-hyph.ps debrecen-hyph.pdf
#      a2ping.pl ... -- Written by <pts@fazekas.hu> from April 2003.
#      This is free software, GNU GPL >=2.0. There is NO WARRANTY.
#      (epstopdf 2.7 Copyright 1998-2001 by Sebastian Rahtz et al.)
#      * Strongest BoundingBox comment: %%HiResBoundingBox:
#      * Doing --PaperSize unchanged
#      * Output filename: debrecen-hyph.pdf
#      * Output FileFormat: PDF
#      * Ghostscript ps2pdf command: gs -dSAFER
#      * Compression: zip
#      * Input filename: debrecen-hyph.ps
#      * Computing BBox info from non-EPS PS file
#      * Ghostscript compute pipe: gs -dSAFER -dWRITESYSTEMDICT -dNOPAUSE -sDEVICE=bbox -sFN=debrecen-hyph.ps /tmp/a2ping_pl-16977-298938572-c.tgs 2>&1
#      * Applying BoundingBox from Compute-GS T-: 71 81 539 769
#      * Applying HiResBoundingBox from Compute-GS T-H: 71.837998 81.971997 538.235984 768.113977
#      * Scanning header for BoundingBox
#      * Applying BoundingBox from ADSC T-: 0 0 596 842
#      * Final BoundingBox: 0 0 596 842
#      * Ghostscript ps2pdf pipe: gs -dSAFER -q -dBATCH -sDEVICE=pdfwrite  -sOutputFile=debrecen-hyph.pdf -
#      * Done OK, created PDF file debrecen-hyph.pdf (338451 bytes)
#
package just; BEGIN{$INC{'just.pm'}='just.pm'}
BEGIN{ $just::VERSION=2 }
sub end(){1}
sub main(){}

BEGIN{$ INC{'strict.pm'}='strict.pm'} {
package strict;
use just;
# by pts@fazekas.hu at Wed Jan 10 12:42:08 CET 2001
require 5.002;
sub bits {
  (grep{'refs'eq$_}@_ && 2)|
  (grep{'subs'eq$_}@_ && 0x200)|
  (grep{'vars'eq$_}@_ && 0x400)|
  ($@ || 0x602)
}
sub import { shift; $^H |= bits @_ }
sub unimport { shift; $^H &= ~ bits @_ }
just::end}

BEGIN{$ INC{'integer.pm'}='integer.pm'} {
package integer;
use just;
# by pts@fazekas.hu at Wed Jan 10 12:42:08 CET 2001
sub import   { $^H |= 1 }
sub unimport { $^H &= ~1 }
just::end}

BEGIN{$ INC{'Pts/string.pm'}='Pts/string.pm'} {
package Pts::string;
# by pts@fazekas.hu at Sat Dec 21 21:32:18 CET 2002
use just;
use integer;
use strict;

#** @param $_[0] a string
#** @param $_[1] index of first bit to return. Bit 128 of byte 0 is index 0.
#** @param $_[2] number of bits to return (<=32)
#** @return an integer (negative on overflow), bit at $_[1] is its MSB
sub get_bits_msb($$$) {
  # assume: use integer;
  my $loop=$_[1];
  my $count=$_[2];
  my $ret=0;
  ($ret+=$ret+(1&(vec($_[0],$loop>>3,8)>>(7-($loop&7)))), $loop++) while $count--!=0;
  $ret
}

#** @param $_[0] a string
#** @return value if $_[0] represents a floating point numeric constant
#**   in the C language (without the LU etc. modifiers) -- or undef. Returns
#**   undef for integer constants
sub c_floatval($) {
  my $S=$_[0];
  no integer; # very important; has local scope
  return 0.0+$S if $S=~/\A[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.])(?:[eE][+-]?[0-9]+)?\Z(?!\n)/;
  undef
}

#** @param $_[0] a string
#** @return value if $_[0] represents a floating point or integer numeric
#**   constant in the C language (without the LU etc. modifiers) -- or undef
sub c_numval($) {
  my $S=$_[0];
  no integer; # very important; has local scope
  return 0+$S if $S=~/\A[+-]?(?:[0-9]*\.[0-9]+(?:[eE][+-]?[0-9]+)?|[0-9]+\.?)\Z(?!\n)/;
  undef
}

#** @param $_[0] a string
#** @return the integer value of $_[0] in C -- or undef
sub c_intval($) {
  my $S=$_[0];
  my $neg=1;
  $neg=-1 if $S=~s@\A([+-])@@ and '-'eq$1;
  return $neg*hex $1 if $S=~/\A0[xX]([0-9a-fA-F]+)\Z(?!\n)/;
  return $neg*oct $1 if $S=~/\A0([0-7]+)\Z(?!\n)/;
  return $neg*$1     if $S=~/\A([0-9]+)\Z(?!\n)/;
  undef
}

sub import {
  no strict 'refs';
  my $package = (caller())[0];
  shift; # my package
  for my $p (@_ ? @_ : qw{get_bits_msb c_floatval c_numval c_intval}) { *{$package."::$p"}=\&{$p} }
}

just::end}

BEGIN{$ INC{'Htex/dimen.pm'}='Htex/dimen.pm'} {
package Htex::dimen;
# by pts@fazekas.hu at Sat Dec 21 21:26:15 CET 2002
use just;
use integer;
use strict;
use Pts::string qw(c_numval);

my %bp_mul;
{ no integer; %bp_mul=(
  'bp'=>1, # 1 bp = 1 bp (big point)
  'in'=>72, # 1 in = 72 bp (inch)
  'pt'=>72/72.27, # 1 pt = 72/72.27 bp (point)
  'pc'=>12*72/72.27, # 1 pc = 12*72/72.27 bp (pica)
  'dd'=>1238/1157*72/72.27, # 1 dd = 1238/1157*72/72.27 bp (didot point) [about 1.06601110141206 bp]
  'cc'=>12*1238/1157*72/72.27, # 1 cc = 12*1238/1157*72/72.27 bp (cicero)
  'sp'=>72/72.27/65536, # 1 sp = 72/72.27/65536 bp (scaled point)
  'cm'=>72/2.54, # 1 cm = 72/2.54 bp (centimeter)
  'mm'=>7.2/2.54, # 1 mm = 7.2/2.54 bp (millimeter)
) }

#** @param $_[0] a (real or integer) number, optionally postfixed by a
#**        TeX dimension specifier (default=bp)
#** @return the number in bp, or undef
sub dimen2bp($) {
  no integer;
  my $S=$_[0];
  my $mul;
  $mul=$bp_mul{$1} if $S=~s/\s*([a-z][a-z0-9]+)\Z(?!\n)// and exists $bp_mul{$1};
  my $val=c_numval($S);
  $val*=$mul if defined $val and defined $mul;
  $val
}

just::end}

BEGIN{$ INC{'Htex/papers.pm'}='Htex/papers.pm'} {
package Htex::papers;
# contains paper size information
# by pts@fazekas.hu at Sun Dec 22 00:30:58 CET 2002
use just;
use integer;
use strict;
use Htex::dimen;

my @papers=(
#
# paper.txt
# by pts@fazekas.hu at Tue Jan 16 18:21:59 CET 2001
# by pts@fazekas.hu at Tue Jan 16 19:13:16 CET 2001
#
# Examined: dvips, gs, libpaperg
#
# all units are measured in Big Points (bp)
# 72 bp == 1 in
# 2.54 cm == 1 in
#
# papername	width	height
qw{Comm10	297	684},
qw{Monarch	279	540},
qw{halfexecutive 378	522},

qw{Legal	612	1008},
qw{Statement	396	612},
qw{Tabloid	792	1224},
qw{Ledger	1224	792},
qw{Folio	612	936},
qw{Quarto	610	780},
qw{7x9		504	648},
qw{9x11		648	792},
qw{9x12		648	864},
qw{10x13	720	936},
qw{10x14	720	1008},
qw{Executive	540	720},

qw{ISOB0	2835	4008},
qw{ISOB1	2004	2835},
qw{ISOB2	1417	2004},
qw{ISOB3	1001	1417},
qw{ISOB4	 709	1001},
qw{ISOB5	 499	 709},
qw{ISOB6	 354	 499},
qw{ISOB7	 249	 354},
qw{ISOB8	 176	 249},
qw{ISOB9	 125	 176},
qw{ISOB10	 88	 125},
qw{jisb0	2916	4128},
qw{jisb1	2064	2916},
qw{jisb2	1458	2064},
qw{jisb3	1032	1458},
qw{jisb4	 729	1032},
qw{jisb5	 516	 729},
qw{jisb6	 363	 516},

qw{C7		230	323},
qw{DL		312	624},

qw{a3		842	1190},	# defined by Adobe
qw{a4		595	842},	# defined by Adobe; must precede a4small

# a4small should be a4 with an ImagingBBox of [25 25 570 817].},
qw{a4small	595	842},
qw{letter	612	792},	# must precede lettersmall
# lettersmall should be letter with an ImagingBBox of [25 25 587 767].
qw{lettersmall	612	792},
# note should be letter (or some other size) with the ImagingBBox
# shrunk by 25 units on all 4 sides.
qw{note		612	792},
qw{letterLand	792	612},
# End of Adobe-defined page sizes

qw{a0		2380	3368},
qw{a1		1684	2380},
qw{a2		1190	1684},
qw{a5		421	595},
qw{a6		297	421},
qw{a7		210	297},
qw{a8		148	210},
qw{a9		105	148},
qw{a10		74	105},
qw{b0		2836	4008},
qw{b1		2004	2836},
qw{b2		1418	2004},
qw{b3		1002	1418},
qw{b4		709	1002},
qw{b5		501	709}, # defined by Adobe

qw{a0Land	3368	2380},
qw{a1Land	2380	1684},
qw{a2Land	1684	1190},
qw{a3Land	1190	842},
qw{a4Land	842	595},
qw{a5Land	595	421},
qw{a6Land	421	297},
qw{a7Land	297	210},
qw{a8Land	210	148},
qw{a9Land	148	105},
qw{a10Land	105	74},
qw{b0Land	4008	2836},
qw{b1Land	2836	2004},
qw{b2Land	2004	1418},
qw{b3Land	1418	1002},
qw{b4Land	1002	709},
qw{b5Land	709	501},

qw{c0		2600	3677},
qw{c1		1837	2600},
qw{c2		1298	1837},
qw{c3		918	1298},
qw{c4		649	918},
qw{c5		459	649},
qw{c6		323	459},

# vvv U.S. CAD standard paper sizes
qw{archE	2592	3456},
qw{archD	1728	2592},
qw{archC	1296	1728},
qw{archB	864	1296},
qw{archA	648	864},

qw{flsa		612	936},	# U.S. foolscap
qw{flse		612	936},	# European foolscap
qw{halfletter	396	612},
qw{csheet	1224	1584},	# ANSI C 17x22
qw{dsheet	1584	2448},	# ANSI D 22x34
qw{esheet	2448	3168},	# ANSI E 34x44
qw{17x22	1224	1584},	# ANSI C 17x22
qw{22x34	1584	2448},	# ANSI D 22x34
qw{34x44	2448	3168},	# ANSI E 34x44
);

#** Converts a numeric paper size to a well-defined paper name. Tolerance is
#** 8.5bp
#** @param $_[0] width, in bp
#** @param $_[1] height, in bp
#** @return () or ("papername", ret.paper.width.bp, ret.paper.height.bp)
sub valid_bp($$;$$) {
  no integer;
  my ($W1,$H1)=(defined$_[2]?$_[2]:0,defined$_[3]?$_[3]:0);
  my ($WW,$HH)=(Htex::dimen::dimen2bp($_[0])-$W1, Htex::dimen::dimen2bp($_[1])-$H1);
  # Dat: 1mm == 720/254bp; 3mm =~ 8.5bp
  no integer;
  for (my $I=0; $I<@papers; $I+=3) {
    return @papers[$I,$I+1,$I+2] if abs($papers[$I+1]-$WW)<=8.5 and abs($papers[$I+2]-$HH)<=8.5;
  }
  ()
}

#** @param $_[0] (width width_unit "," height height_unit)
#** @return () or ("papername", width.bp, height.bp)
sub valid($) { # valid_papersize
  my $S=lc$_[0];
  $S=~/^\s*(\d+(\.\d+)?)\s*([a-z][a-z0-9]+)\s*,\s*(\d+(\.\d+)?)\s*([a-z][a-z0-9]+)\s*\Z(?!\n)/ ?
    valid_bp("$1$3","$4$6") : ();
}

#** @param $_[0] (width width_unit? ("," || "x") height height_unit?) || (papername)
#** @return () or ("papername"?, width.bp, height.bp)
sub any($) {
  my $S=lc$_[0];
  if ($S=~/\A[a-z]\w+\Z(?!\n)/) {
    for (my $I=0; $I<@papers; $I+=3) {
      return @papers[$I,$I+1,$I+2] if lc($papers[$I]) eq $S;
    }
  }
  return () if $S!~/^\s*(\d+(\.\d+)?)\s*((?:[a-z][a-z0-9]+)?)\s*[,xX]\s*(\d+(\.\d+)?)\s*((?:[a-z][a-z0-9]+)?)\s*\Z(?!\n)/;
  # ^^^ Dat: [xX] is xdvi-style, [,] is dvips-style spec
  my($w,$h)=($1.$3, $4.$6);
  my @L=valid_bp($w,$h);
  @L ? @L : (undef,Htex::dimen::dimen2bp($w),Htex::dimen::dimen2bp($h))
}

just::end}

BEGIN{$  INC{'Htex/a2ping.pm'}='Htex/a2ping.pm'}

package Htex::a2ping;
# a2ping.pl -- convert between PS, EPS and PDF and other page description formats
# by pts@fazekas.hu et al. at Wed Apr 16 14:54:13 CEST 2003
# a2ping.pa created at Sun Apr 20 22:25:47 2003
#
# This file contains perldoc(1) documentation. Search for `=head1'.
# See revision history at end of this file.
#

use just +1; # a JustLib application
use strict;
use integer;
use Htex::papers;
BEGIN { $Htex::a2ping::VERSION="2.83p" }

# Imp: option to ignore `%%Orientation: Portrait', which gs respects and rotates by 90 degrees if necessary
# Imp: gs(704?!) sometimes finds too small bbox, see Univers.eps
# Imp: respect bbox in METAPOST %! (not EPS), don't use Compute-GS T-
# Imp: -sPDFPassword=...
# Imp: `a2ping.pl -v jf.eps pdf1: t.pdf' PDF1: must be forced to have --below
# Imp: option to `clip' an EPS to the specified bbox -- does file size decrease?
# Imp: fix bug a2ping -v ~/a2ping_bug.ps a2ping_bug.pdf; running type1fix on
#      all fonts with dff.pl has fixed the problem
# Imp: post-process PNG etc. written by sam2p
# Imp: better help and docs
# Imp: respect full /MediaBox for a PDF -> EPS|PDF1 conversion
# Imp: --ll x,y command line option
# Imp: Htex/a2ping.pa -v ../image/tuzv.ps t.pdf  (1st and second page different)
# Imp: also save+restore /pdfmark ??
# Imp: fix /MediaBox an all PDF pages if !$ll_zero
# Imp: PDF -> PDF1 conversion with gs -sDEVICE=pdfwrite
# Imp: direct PDF to PCL5 conversion with gs
# Imp: remove %%BeginDefaults | %%PageMedia: plain | %%EndDefaults (pdftops(1))
# Imp: fix completely bogus margin and papersize handling:
#      ../justlib2/Htex/a2ping.pa --duplex=force-short -v -p:a3 -r force-unknown tuzv.ps t.pdf
# Imp: careful distinction between PDF and PDF1
# Imp: psnup support (-1 -2 -3 -4 ...)
# Imp: idempotent PS -> PS, add other header
# Imp: use convert(1) etc.
# Imp: possibly disable compute-pipe
# Imp: $header_remove_p ??
# Imp: --leftright option instead of --below
# Imp: pdfboxes.pl, get offset from gs
# Imp: detect error messages from GS, abort...
# Imp: use all pdftops + gs + acroread
# Imp: possibly accept /PageSize from %%DocumentMedia
# Imp: /DocumentMedia seems to screw up sub-pt placement in gv(1)
#
# Dat: example: a2ping.pl --extra=-c:ijg:50 -r86 nn1.eps nn1.jpg
# Dat: calling ``showpage'' is not required for -sDEVICE=pdfwrite with gs 6.50,
#      but -sDEVICE=pgmraw depends on it
# Dat: the functionality of pdfboxes.pl cannot be provided here with a shorter
#      implementation, because gs always outputs the content stream of the PDF
#      objects first
# Dat: pdftops -eps writes negative bbox correctly
# Dat: markedEPS: include pdfmarks
# Dat: gs 7.04 gdevdjet.[ch], gdevdljm.[ch]
# Dat: to be undeffed in setpagedevice: /.MarginsHWResolution /PageSize
#      /ImagingBBox /HWResolution /HWSize /.MediaSize (we undef all)

### program identification
my $program = "a2ping.pl";
my $filedate="2018-03-07";  # See also $Htex::a2ping::VERSION.
my $copyright = "Written by <pts\@fazekas.hu> from April 2003.
This is free software, GNU GPL >=2.0. There is NO WARRANTY.
(epstopdf 2.7 Copyright 1998-2001 by Sebastian Rahtz et al.)\n";
# "Contains modifications by pts\@fazekas.hu";
my $title = "$program $Htex::a2ping::VERSION, $filedate -- $copyright\n";

### ghostscript command name
my($quote,$GS)=("'","gs");
($quote,$GS) = ("\"","gswin32c") if $^O eq 'MSWin32' or $^O =~ /cygwin/i;

# --- help functions

sub fnq($) {
  my $fn=$_[0];
  return $fn if $fn!~y@-a-zA-Z0-9/.+_@@c;
  $fn=~s@'@\\'@g if $quote eq "'";
  $quote.$fn.$quote
}

sub debug {
  print STDERR "* @_\n" if $::opt_verbose;
}
sub warning {
  print STDERR "$0: warning: @_\n";
}
sub error {
  my $s=$title; $title="";
  die "$s$0: @_\n";
}

# unlink temporary files?
my $tmpunlink_p=1;
my $tmpsig=1;
my %tmpfiles;
my $tmpdir=exists $ENV{TMPDIR} ? $ENV{TMPDIR} : '/tmp';
$tmpdir="." if (!-d $tmpdir or !-w $tmpdir) and -w '.';
sub cleanup() {
  unlink keys %tmpfiles;
  exit 125;
}
END { unlink keys %tmpfiles; }
sub temp_unlink($) {
  if (defined $_[0] and exists $tmpfiles{$_[0]}) {
    unlink $_[0] if $tmpunlink_p;
    delete $tmpfiles{$_[0]};
  }
}
sub temp_prefix() {
  my $prog0=$program;
  $prog0=~y@a-zA-Z0-9@_@c;
  if ($tmpsig) {
    $tmpsig=0;
    $SIG{INT}=$SIG{TERM}=$SIG{HUP}=$SIG{QUIT}=\&cleanup;
  }
  return "$tmpdir/$prog0-$$-".int(rand(1<<30))."-"; # 30: nonnegative
}

#** @return arg rounded down to int
sub myfloor($) {
  # Dat: Perl int() rounds towards zero
  no integer;
  $_[0]==int($_[0]) ? $_[0] : $_[0] < 0 ? -int(1-$_[0]) : int($_[0])
}

#** @return arg rounded up to int
sub myceil($) {
  no integer; #### BUGFIX at Wed Nov 15 17:23:29 CET 2006
  $_[0]==int($_[0]) ? $_[0] : 1+ ($_[0] < 0 ? -int(-$_[0]) : int($_[0]));
}

just::main;

# ---

sub FL_PAGE1_STOP(){1} # is file format single-page?
sub FL_SET_PAGESIZE_OK(){2}
sub FL_PDFMARK(){4}
sub FL_NEED_SHOWPAGE(){8} # does gs -sDEVICE=... need showpage?
sub FL_SAMPLED(){16} # is it a sampled (raster, pixel-based)
sub FL_ANY_ORIGIN_OK(){32} # (llx,lly) may be anything, not just (0,0)
sub FL_HAS_ANTIALIAS(){64}
sub FL_VIA_SAM2P(){128} # sam2p(1) should convert PNM to such a format
sub FL_OK_SAM2P(){256} # sam2p(1) can convert PNM to such a format

my %fmts=( # Name=>[flags]
  'EPS'=>[FL_PAGE1_STOP],
  'markedEPS'=>[FL_PAGE1_STOP|FL_PDFMARK], # Imp: should we have FL_SET_PAGESIZE_OK?
  'PDF1'=>[FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_PDFMARK],
  'PDF'=>[FL_SET_PAGESIZE_OK|FL_PDFMARK|FL_ANY_ORIGIN_OK],
  'PS'=>[FL_SET_PAGESIZE_OK|FL_ANY_ORIGIN_OK],
  'markedPS'=>[FL_SET_PAGESIZE_OK|FL_PDFMARK|FL_ANY_ORIGIN_OK],
  'PCL5'=>[FL_SET_PAGESIZE_OK|FL_ANY_ORIGIN_OK],
  # ^^^ Dat: no FL_HAS_ANTIALIAS -- would need lj5gray, which is loonger
  # ^^^ Dat: no FL_SAMPLED, because cannot set resolution
  'PBM'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_OK_SAM2P], # Dat FL_HAS_ANTIALIAS produces obscure image
  'PGM'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_OK_SAM2P],
  'PPM'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_OK_SAM2P],
  # Now come the FileFormats supported via sam2p
  'PNG'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P],
  'XWD'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P],
  'BMP'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P],
  'TIFF'=>[FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P],
  'JPEG'=>[FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P],
  'GIF'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P], # Imp: disable antialias for few colors?
  'XPM'=> [FL_PAGE1_STOP|FL_SET_PAGESIZE_OK|FL_NEED_SHOWPAGE|FL_SAMPLED|FL_HAS_ANTIALIAS|FL_VIA_SAM2P], # Imp: disable antialias for few colors?
);
my %fmt_aliases=qw(MARKEDPS markedPS  MARKEDEPS markedEPS  PCL PCL5
  UNMARKEDPS PS  UNMARKEDEPS EPS  EPDF PDF1  MEPS markedEPS);
# Dat: .ps will be unmarked PS
# Imp: sometimes markedEPS for .eps?
my %fmt_exts=qw(eps EPS  epsi EPS  epsf EPS  eps2 EPS  ps PS  ps2  PS
  pcl PCL5  pcl5 PCL5  pbm PBM  pgm PGM  pnm PPM  ppm PPM  pdf PDF  png PNG
  xwd XWD  bmp  BMP  rle BMP  tif TIFF  tiff TIFF  jpg JPEG  jpe JPEG
  jpg JPEG  gif GIF  xpm XPM);

### usage
# vvv deprecated options:
#   --outfile=<file>: write result to <file>
#   --debug:          verbose debug informations       (default: $bool[$::opt_verbose])
#   --(no)filter:     d. read standard input           (default: false)
#   --(no)gs        d. run Ghostscript to create PDF
my $usage=
"${title}Usage:  $program [options] <inputfile> [[<outformat>:] <outputfile>]
Run with --doc to read documentation as a UNIX man(1) page.
Options: --help print this help message
--(no)compress  use compression                   (def: best)
--(no)hires     scan HiResBoundingBox             (def: yes)
--(no)exact     scan ExactBoundingBox             (def: no)
--(no)verbose   verbose debug informations        (def: no)
--(no)below     allow below+left_from baseline    (def: no)
--(no)tmpunlink unlink temporary files            (def: yes)
--(no)antialias render shades at outlines (def: scale3no) (=scale3yes =no =yes)
--(no)lossy     allow lossy image filters (EPS->PDF) (def: yes)
--(no)keepoldmediabox keep only old, [0 0]-based MediaBox in PDF (def: no)
--gs-cmd=       path to Ghostscript               (def: gs or gswin32c)
--gs-ccmd=      path to Ghostscript, 4 bbox calc  (def: gs or gswin32c)
--gsextra=      extra arg to gs
--extra=        extra arg to external prg (i.e pdftops)
--bboxfrom=     adsc|compute-gs|pagesize          (def: guess)
--papersize=    unchanged|force-unknown|600bpx5cm (def: default) (bp)
--threshold=    min color for 1 in 8->1 bit conv  (def: 128)
Possible input formats: PS EPS PDF JPEG GIF TIFF PNG PNM PCX BMP LBM XPM TGA
Possible output formats: @{[sort keys %fmts]}
Examples for producing 'test.pdf':
  * $program test.eps
  * produce postscript | $program -v - test.pdf
Example: look for HiResBoundingBox and produce corrected PostScript:
  * $program -d --nogs -hires test.ps>testcorr.ps
";
sub errorUsage {
  die "$usage\U!\E!\U!\E Error: @_\n";
}

# --- @ARGV parsing

### default option values
my @extra=();
my @gsextra=();
#** Output file format (string)
my $FileFormat=undef;
$::opt_help=0;
$::opt_verbose=0;
my %vals_compression=map{$_=>1} qw(best none flate zip);
$::opt_compression='best';
#** Prefer %%HiResBoundingBox over %%BoundingBox, but emit both
$::opt_hires=1;
$::opt_exact=0;
# $::opt_filter=0; # deprecated
# $::opt_outputfile=undef; # deprecated
$::opt_below=undef;
$::opt_keepoldmediabox=0;
$::opt_lossy=1;
$::opt_antialias=undef; # render shades at path outlines for better readability
$::opt_gs_cmd=undef;
$::opt_extra="";
$::opt_duplex="default";
$::opt_threshold=128;
my %vals_antialias=map{$_=>1} qw(no yes scale3yes scale3no);
my %vals_duplex=map{$_=>1} qw(force-unknown force-simplex force-long
  force-short unchanged default-simplex default-long default-short);
# ^^^ short: duplex printing, will bind short edge of paper (ideal for
#     duplexing psnup -2)
#** Dat: force-unknown is forced by /setpagedevice/load def
$::opt_resolution="default"; # unchanged force-unknown 600x600 (DPI)
$::opt_papersize="default"; # unchanged force-unknown 600bpx600cm (bp)
#** --bboxfrom=adsc sets %%BoundingBox from the 1st page if no ADSC comment in non-EPS ps
my %vals_bboxfrom=map{$_=>1} qw(adsc compute-gs guess pagesize);
$::opt_bboxfrom="guess";
my $InputFilename;
my $OutputFilename;

sub is_page1_stop() { 0!=($fmts{$FileFormat}[0]&FL_PAGE1_STOP) }
sub is_set_pagesize_ok() { 0!=($fmts{$FileFormat}[0]&FL_SET_PAGESIZE_OK) }
sub is_pdfmark() { 0!=($fmts{$FileFormat}[0]&FL_PDFMARK) }
sub is_need_showpage() { 0!=($fmts{$FileFormat}[0]&FL_NEED_SHOWPAGE) }
sub is_sampled() { 0!=($fmts{$FileFormat}[0]&FL_SAMPLED) }
sub is_any_origin_ok() { 0!=($fmts{$FileFormat}[0]&FL_ANY_ORIGIN_OK) }
sub is_has_antialias() { 0!=($fmts{$FileFormat}[0]&FL_HAS_ANTIALIAS) }
sub is_via_sam2p() { 0!=($fmts{$FileFormat}[0]&FL_VIA_SAM2P) }
sub is_ok_sam2p() { 0!=($fmts{$FileFormat}[0]&FL_OK_SAM2P) }

# ---

sub show_doc() {
  # run `pod2man __FILE__ | man -l -', same as `perldoc __FILE', but perldoc(1)
  # is missing from some Debian sites that have pod2man.
  my @path=split/:+/,$ENV{PATH};
  my $pod2man_='pod2man --center="a2ping: advanced PS, PDF, EPS converter" ';
  $pod2man_=q~perl -ne 'if($a>1){print}elsif($a&&/^=head1/){$a=2}else{$a=!/\S/}' ~
    if !grep { -x "$_/pod2man" } @path;
  my $pager='';
  for my $pageri ((defined $ENV{PAGER} ? $ENV{PAGER}: ''),'less','most','more','view -  ','vim -R -  ','vi -  ','joe -rdonly -asis -','pager') {
    next if $pageri!~/^(\S+)/;
    my $pagert="/$1";
    if (grep { -x $_.$pagert } @path) { $pager=$pageri; last }
  }
  $pager=q~perl -pe 's@\010_@@g;s@.\010@@gs' | ~.$pager
    if substr($pager,-2)eq'  ' or $pager=~/\A(?:view|vim?|joe|emacs|mcedit|nano|nano-tiny|ae)\b/;
    # ^^^ Dat: these cannot handle underline/bold backspace directly
  $ENV{PAGER}=$pager;
  my $man='';
  if (substr($pod2man_,0,5)ne 'perl ') {
    $man=' | man -l -'; # calls $PAGER
    if ((!grep { -x "$_/man" } @path) or qx(man -l 2>&1)=~/\binvalid option\b/) {
      $man=' | nroff -Tlatin1 -mandoc'; # Linux, no need for eqn(1), tbl(1) etc.
      if (!grep { -x "$_/nroff" } @path) { $man='' } # just write it
    }
  }
  my $cmd=$pod2man_.fnq(__FILE__).$man;
  if ($cmd!~/[|] man -l -\Z(?!\n)/) {
    if (!length $pager) {
      die unless open PIPE, "$cmd|";
      print while sysread PIPE, $_, 4096;
      die "$0: error showing doc\n" unless close PIPE;
      exit 0;
    }
    $cmd.=' | $PAGER';
  }
  ##die $cmd;
  $ENV{LESS}="" if !defined $ENV{LESS};
  $ENV{LESS}="$ENV{LESS}R"; # show ANSI escapes
  die "$0: exec ($cmd) failed: $!\n" if !exec $cmd;
}

die $usage if !@ARGV or (1==@ARGV and $ARGV[0] eq '-h' or $ARGV[0] eq '--help'
  or $ARGV[0] eq 'help');
show_doc() if 1==@ARGV and $ARGV[0] eq '--doc' or $ARGV[0] eq 'doc';

{ my($I,$optname,$optval);
  my %optmap=qw(o outputfile  outfile outputfile  r resolution  h help
    f filter  d verbose  v verbose  debug verbose  p papersize
    c compression  compress compression  h hires  b below  e exact  x extra);
  #** Options that have a mandatory argument
  my %argopt1=qw(outputfile 1 duplex 1 resolution 1 extra 1 compression 1 gs-cmd 1
    gs-ccmd 1
    papersize 1 paper 1 bboxfrom 1 antialias 1 gsextra 1 threshold 1); # 1 arg
  my %argnone=qw(help 1 verbose 1 noverbose 1 nocompress 1 noantialias 1); # 0 arg
  my %argmaybe=qw();  # 0 or 1 arg
  my %argbool=qw(hires 1 exact 1 below 1 gs 1 filter 1 tmpunlink 1
    approx 1 lossy 1 keepoldmediabox 1); # boolean arg
  # Dat: --noverbose --nocompress
  my $opts_ok=1;
  for ($I=0; $I<@ARGV; $I++) {
    if ($ARGV[$I]eq '--') {
      $OutputFilename=$InputFilename if defined $InputFilename and
        !defined $OutputFilename and $opts_ok;
      $opts_ok=0;
    } elsif ($opts_ok and $ARGV[$I]=~/\A--+(\w[\w-]*)(?:[:=](.*))?\Z(?!\n)/s) {
      $optname=lc$1; $optval=$2;
    } elsif ($opts_ok and $ARGV[$I]=~/\A-(\w)(.*)\Z(?!\n)/s) {
      $optname=lc$1;
      if (length($2)==0) { $optval=undef }
      elsif (index(":=",substr($2,0,1))>=0) { $optval=substr($2,1) }
      else { $optval=$2 }
    } elsif ($opts_ok and !defined $FileFormat and defined $InputFilename and $ARGV[$I]=~s@\A(\w+):@@) {
      my $fmtag=uc$1;
      # errorUsage "invalid FileFormat tag: $fmtag" if $fmtag!~s@:\Z(?!\n)@@;
      if (exists $fmts{$fmtag}) { $FileFormat=$fmtag }
      elsif (exists $fmt_aliases{$fmtag}) { $FileFormat=$fmt_aliases{$fmtag} }
      else { errorUsage "Unknown FileFormat tag: $fmtag" }
      if (0!=length($ARGV[$I])) {
        errorUsage "Multiple output filenames" if defined $OutputFilename;
        $OutputFilename=$ARGV[$I];
      }
      next
    } elsif (!defined $InputFilename) { $InputFilename=$ARGV[$I]; next }
    elsif (!defined $OutputFilename) { $OutputFilename=$ARGV[$I]; next }
    else { errorUsage "Too many arguments (multiple input/output files?)" }
    $optname=$optmap{$optname} if exists $optmap{$optname};
    if (exists $argopt1{$optname} and !defined $optval) {
      errorUsage "Argument expected for --$optname" if $I==@ARGV;
      $optval=$ARGV[++$I];
    }
    # Dat: $optname and $optval are now correct
    errorUsage "No argument expected for --$optname=$optval" if exists $argnone{$optname} and defined $optval;
    if (substr($optname,0,2)eq"no" and exists $argbool{substr($optname,2)}) {
      $optname=substr($optname,2);
      errorUsage "No argument expected for no --no$optname=$optval" if defined $optval;
      $optval="no";
    }
    if (exists $argbool{$optname}) {
      # same as sam2p GenBuffer::parseBool, understands:
      #   on  true  yes ja   igen oui enable  1 true  vrai? right sure allowed
      #   off false no  nein nem  non disable 0 false faux? wrong nope disallowed
      $optval=(!defined($optval) or 0==length($optval)
               or $optval=~/\Ao[nu]/i or $optval!~/\A[fndw0]/i) ? 1 : 0;
    } elsif (!exists $argopt1{$optname} and !exists $argnone{$optname} and !exists $argmaybe{$optname}) {
      errorUsage "Unknown option --$optname, see --help"
    }
    # vvv application-specific
       if ($optname eq "help") { die $usage }
    elsif ($optname eq "help") { show_doc() }
    elsif ($optname eq "noverbose") { $::opt_verbose=0 }
    elsif ($optname eq "nocompress") { $::opt_compression='none' }
    elsif ($optname eq "verbose") { $::opt_verbose++ }
    elsif ($optname eq "hires")  { $::opt_hires =$optval }
    elsif ($optname eq "exact")  { $::opt_exact =$optval }
    elsif ($optname eq "below")  { $::opt_below =$optval }
    elsif ($optname eq "keepoldmediabox")  { $::opt_keepoldmediabox=$optval }
    elsif ($optname eq "lossy")  { $::opt_lossy =$optval }
    elsif ($optname eq "approx") { $::opt_approx=$optval }
    elsif ($optname eq "threshold")  { $::opt_threshold=$optval+0 } # Imp: accept only int 0..256
    elsif ($optname eq "filter") {
      # errorUsage "Multiple input filenames" if defined $InputFilename;
      # $InputFilename='-';
      errorUsage "Multiple output filenames" if defined $OutputFilename;
      $OutputFilename='-';
    } elsif ($optname eq "tmpunlink") { $tmpunlink_p=$optval }
    elsif ($optname eq "gs")     { $FileFormat=$optval ? 'PDF1' : 'markedEPS' }
    elsif ($optname eq "compression") {
      errorUsage "--$optname expects one of: @{[keys%vals_compression]}" if !exists $vals_compression{$optval};
      $::opt_compression=$optval;
      $::opt_compression='zip' if $::opt_compression eq 'flate';
    } elsif ($optname eq "outputfile") {
      errorUsage "Multiple output filenames" if defined $OutputFilename;
      $OutputFilename=$optval;
    } elsif ($optname eq "gs-cmd") {
      errorUsage "Multiple --gs-cmd" if defined $::opt_gs_cmd;
      $::opt_gs_cmd=$optval;
    } elsif ($optname eq "gs-ccmd") {
      errorUsage "Multiple --gs-ccmd" if defined $::opt_gs_ccmd;
      $::opt_gs_ccmd=$optval;
    } elsif ($optname eq "extra") { push @extra, $optval }
    elsif ($optname eq "gsextra") { push @gsextra, $optval }
    elsif ($optname eq "duplex") {
      errorUsage "--$optname expects one of: @{[keys%vals_duplex]}" if !exists $vals_duplex{$optval};
      $::opt_duplex=$optval
    } elsif ($optname eq "bboxfrom") {
      errorUsage "--$optname expects one of: @{[keys%vals_bboxfrom]}" if !exists $vals_bboxfrom{$optval};
      $::opt_bboxfrom=$optval
    } elsif ($optname eq "noantialias") {
      $::opt_antialias='no'
    } elsif ($optname eq "antialias") {
      errorUsage "--$optname expects one of: @{[keys%vals_antialias]}" if !exists $vals_antialias{$optval};
      $::opt_antialias=$optval
    } elsif ($optname eq "resolution") {
      if ($optval eq "unchanged" or $optval eq "force-unknown") { }
      elsif ($optval=~/^(\d+(?:[.]\d+)?)\Z(?!\n)/) { $optval="$1x$1" }
      elsif ($optval=~/^(\d+(?:[.]\d+)?[x,]\d+(?:[.]\d+)?)\Z(?!\n)/) { }
      else { errorUsage "--Resultion expects unchanged | force-unknown | DPI | XDPIxYDPI" }
      $::opt_resolution=$optval
    } elsif ($optname eq "papersize" or $optname eq"paper") {
      if ($optval eq "unchanged" or $optval eq "force-unknown") { $::opt_papersize=$optval }
      else {
        my @L=Htex::papers::any($optval);
        errorUsage "invalid or unknown for --papersize" if !@L;
        $::opt_papersize="$L[1],$L[2]" # width, height
      }
    } else { die } # unreachable
  } # NEXT opt
  errorUsage "Too many arguments (multiple input/output files?)" if $I!=@ARGV;
  # splice @ARGV, 0, $I;
}

$GS=$::opt_gs_cmd if defined $::opt_gs_cmd;
my $CGS=$GS;
$CGS=$::opt_gs_ccmd if defined $::opt_gs_ccmd;
# vvv SUXX: (r) file doesn't work with gs 8.5x -DSAFER
#$GS.= " -dSAFER"; # -dWRITESYSTEMDICT
#$CGS.=" -dSAFER"; # -dWRITESYSTEMDICT

### get input and output filename
if (!defined $InputFilename and defined $OutputFilename) { # --filter
  $InputFilename='-';
} elsif (!defined $InputFilename) {
  errorUsage "Input filename missing"
} elsif (!defined $OutputFilename) {
  $FileFormat='PDF1' if !defined $FileFormat;
  if ($FileFormat eq 'PDF1' or $FileFormat eq 'PDF') {
    if (($OutputFilename=$InputFilename) ne '-') {
      $OutputFilename =~ s/\.[^\.]*$//;
      $OutputFilename .= ".pdf";
    }
  } else {
    $OutputFilename = '-'; # standard output
  }
}
print STDERR $title if $::opt_verbose;
$title="";

# Dat: no more @ARGV
errorUsage "please specify <outformat>" if
  !defined $FileFormat and ($OutputFilename!~m@[.]([^/.]+)\Z(?!\n)@ or
  !defined($FileFormat=$fmt_exts{lc$1}));
$::opt_below=is_any_origin_ok() if !defined $::opt_below;
error "--below=1 invalid for FileFormat $FileFormat" if $::opt_below and
  !is_any_origin_ok() and $FileFormat ne 'PDF1' and $FileFormat ne 'EPS' and
  $FileFormat ne 'markedEPS';
error "--below=0 invalid for FileFormat $FileFormat" if !$::opt_below and
  is_any_origin_ok();
$::opt_antialias=is_has_antialias() ?
  (is_sampled() ? 'scale3no' : 'yes') : 'no' if
  !defined $::opt_antialias;

if ($FileFormat eq 'PBM' and ($::opt_antialias eq 'scale3yes' or
  $::opt_antialias eq 'scale3no')) {
} elsif ($::opt_antialias ne 'no' and !is_has_antialias()) {
  $::opt_antialias='no';
  warning "--AntiAlias ignored for FileFormat $FileFormat"
}
if ($::opt_antialias eq 'scale3no' or $::opt_antialias eq 'scale3yes') {
  $::opt_resolution="72,72" if $::opt_resolution eq 'unchanged' or $::opt_resolution eq 'force-unknown' or $::opt_resolution eq 'default';
  # ^^^ GS raster default
  my @L=split/[,x]/,$::opt_resolution;
  @L=(@L,@L); # Imp: ..
  $L[0]*=3; $L[1]*=3;
  $::opt_resolution="$L[0],$L[0]";
}

### option compress
my $GSOPTS=join("  ",map{fnq$_}@gsextra);
# $GSOPTS.=" -r72 -sPAPERSIZE=a4 "; # default -- will be overridden by `setpagedevice'
# ^^^ Dat: default does only harm; user should specify on command line

### option BoundingBox types
#**** pts ****
# scan all of them and find the best
{ my $BBprint = "%%BoundingBox:";
  $BBprint = "%%HiResBoundingBox:" if $::opt_hires;
  $BBprint = "%%ExactBoundingBox:" if $::opt_exact;
  debug "Strongest BoundingBox comment:", $BBprint;
}
my $BBregex='%%(Hi[Rr]es|Exact|)BoundingBox:';

if (!is_set_pagesize_ok()) {
  if ($::opt_papersize ne'default' and $::opt_papersize ne'force-unknown') {
    error "Cannot set --PaperSize for FileFormat $FileFormat"
  } else { $::opt_papersize='force-unknown' }
} elsif ($::opt_papersize eq'default') { $::opt_papersize='unchanged' }

if ($::opt_resolution eq'default') { $::opt_resolution='force-unknown' }
error "Cannot set --Resolution for FileFormat $FileFormat (must be markedPS or sampled)"
  if $FileFormat ne 'markedPS' and !is_sampled()
  and $::opt_resolution ne'force-unknown';
error "Bad --Resolution=$::opt_resolution" if $::opt_resolution ne 'unchanged'
  and $::opt_resolution ne 'force-unknown' and $::opt_resolution!~/\A(\d+)+[,x](\d+)\Z(?!\n)/;

if ($FileFormat ne 'markedPS' and $FileFormat ne 'PCL5') {
  if ($::opt_duplex ne'default' and $::opt_duplex ne'force-unknown') {
    error "Cannot set --Duplex for FileFormat $FileFormat (must be markedPS or PCL5)"
  } else { $::opt_duplex='force-unknown' }
} elsif ($::opt_duplex eq'default') { $::opt_duplex='force-unknown' }

debug "Doing --PaperSize $::opt_papersize" if $::opt_papersize ne 'force-unknown';
debug "Doing --Duplex $::opt_duplex" if $::opt_duplex ne 'force-unknown';
debug "Doing --Resolution $::opt_resolution" if $::opt_resolution ne 'force-unknown';
debug "Doing --AntiAlias=$::opt_antialias" if $::opt_antialias ne 'no';

### option outfile
if ($OutputFilename eq '-') {
  debug "Output file: standard output";
} else {
  debug "Output filename: $OutputFilename";
  #error "$OutputFilename: won't overwrite input file with itself"
  #  if $OutputFilename eq $InputFilename;
}

### option gs
debug "Output FileFormat: $FileFormat";
$::opt_compression='zip' if $::opt_compression ne 'none';
if ($FileFormat eq 'PDF' or $FileFormat eq 'PDF1') {
  debug "Ghostscript ps2pdf command: $GS  $GSOPTS";
  debug "Compression: $::opt_compression";
} elsif ($FileFormat eq 'PCL5') {
  debug "Ghostscript ps2ljet command: $GS  $GSOPTS";
} elsif (is_sampled()) {
  debug "Ghostscript ps2sampled command: $GS  $GSOPTS";
}

#**** pts ****
sub read_error() { error "read $InputFilename: $!" }
my $in_mac_p=0; # 0: "\n" or "\r\n" is line terminator; 1: "\r" is line terminator
my $bytes_left=-1; # -1==unlimited
my $already_read=0;
sub dem1($){defined$_[0]?$_[0]:-1}
#** @param $_[0] number of bytes to read, or undef to read a line
#** @return the string read
sub readIN(;$) {
  my $S;
  ## return "" if $bytes_left==0;
  ## print STDERR "READ($_[0])\n";
  if (defined $_[0]) { read_error if 0>dem1 read IN, $S, $_[0] }
  else {
    $!=0; # clean up error code
    if ($in_mac_p) {
      local $/="\r";
      $S=~s@\r\Z(?!\n)@\n@ if defined($S=<IN>);
    } else { $S=<IN> }
    read_error if !defined($S) and $!;
    $S="" if !defined $S; # EOF
  }
  if ($bytes_left<0) { # unlimited
  } elsif (length($S)>=$bytes_left) {
    $S=substr($S, 0, $bytes_left);
    $bytes_left=0;
  } else { $bytes_left-=length($S) }
  $already_read+=length($S);
  $S
}

sub open_OUT() {
  error "Cannot write outfile '$OutputFilename'" unless
    open(OUT, $OutputFilename eq '-' ? ">-" : "> $OutputFilename")
}

#** @param $_[0] temp file extension (e.g ".img")
#** @param $_[1] preprint
#** @param $_[2] bool: force pipe even if seekable?
sub fix_pipe_in($$$) {
  my $c="";
  if ($_[2] or (length($c=readIN(1))!=0 and !seek IN,-1,1)) { # we cannot seek back
    # Dat: ^^^ test seekability instead of $InputFilename eq '-'
    my($ext,$preprint)=@_;
    my $tifn;
    # $ext=$1 if $InputFilename=~/[.](\w+)\Z(?!\n)/; # never true
    $tifn=temp_prefix()."M$ext";
    error "Cannot open temp input: $tifn" unless open TI, "> $tifn";
    $tmpfiles{$tifn}=1;
    die unless print TI $preprint, $c;
    print TI or die while length($_=readIN 4096);
    die unless close TI;
    $InputFilename=$tifn;
    debug "Temp input file: $InputFilename";
    die unless open IN, "< $tifn";
    die unless seek IN, length($preprint), 0;
    $already_read=length($preprint);
    $bytes_left=-1; # unlimited, since readIN() has copied only part
    # $bytes_left++ if $bytes_left>=0; # ungetc($c)
    # temp_unlink $tifn; # do it later (at END{})
  } else {
    $already_read--; $bytes_left++ if $bytes_left>=0; # BUGFIX at Fri May 14 00:21:18 CEST 2004
  }
}

my $temp_out_fn;

#** Does overwrite $temp_out_fn. Fine.
sub fix_force_out($) {
  my($ext)=$_[0];
  # $ext=$1 if $InputFilename=~/[.](\w+)\Z(?!\n)/; # never true
  $temp_out_fn=temp_prefix()."O$ext";
  # error "Cannot save output: $!" unless open SAVEOUT, ">&OUT"; # always STDOUT; maybe not open yet
  error "Cannot open temp output: $temp_out_fn" unless open OUT, "> $temp_out_fn";
  $tmpfiles{$temp_out_fn}=1;
  # $OutputFilename=$temp_out_fn;
  debug "Temp output file: $temp_out_fn";
  # temp_unlink $temp_out_fn; # do it later (at END{})
  return $temp_out_fn;
}

#** @param $_[0] temp file extension (e.g ".img")
#** @return new output filename
sub fix_pipe_out($) {
  if (!defined $temp_out_fn) {
    return $OutputFilename if $OutputFilename ne '-';
    return fix_force_out($_[0]);
  }
  $temp_out_fn
}
sub fix_close_out() {
  # error "closing filter out: $? $!" unless close OUT;
  if (defined $temp_out_fn) {
    my $buf;
    die unless open FCO, "< $temp_out_fn";
    print STDOUT $buf while sysread FCO, $buf, 4096;
    die unless close FCO;
    temp_unlink $temp_out_fn;
    undef $temp_out_fn;
  }
}

sub do_system {
  my($progname)=splice@_,0,1;
  debug "Running: $progname  @extra @_";
  error "prog $progname failed: $? $!"
    if 0!=system $progname, @extra, @_; # Dat: non-zero exit() or not found
}

sub do_exec {
  my($progname)=splice@_,0,1;
  if (scalar keys %tmpfiles) {
    # Cannot exec() right now, because we have to unlink some temporary files
    # later.
    do_system $progname, @_;
  } else {
    debug "Execing: $progname  @extra @_";
    1 if exec $progname, @extra, @_;
    # ^^^ Dat: $OutputFilename eq '-' should be OK
    error "exec failed: $!";
  }
  exit(0);
}

#sub shq($) {
#  my $S=$_[0];
#  return $S if $S!~y@A-Za-z0-9_,:./-@@c and length($S)>0;
#  $S=~s@'@'\\''@g;
#  return "'$S'"
#}

### open input file
if ($InputFilename eq '-') {
  debug "Input file: standard input";
  open(IN, "<&STDIN") or error "cannot open standard input";
} else {
  # -f $InputFilename or error "input file missing: $InputFilename"; # Imp: named pipe?
  open(IN,"< $InputFilename") or error "cannot open input file: $InputFilename";
  debug "Input filename:", $InputFilename;
  if ($InputFilename eq $OutputFilename) {
    # error "same input and output file: $InputFilename";
    my $ext=$InputFilename=~m@([.][^./]+)\Z(?!\n)@ ? $1 : "";
    binmode IN; $bytes_left=-1;
    fix_pipe_in($ext, "", 1); # Dat: defined later
  }
}
binmode IN;

#** Dat: uses $FileFormat, $InputFileName, $OutputFileName
#** @param $S prepend to pipe
sub run_sam2p($$) {
  my($approx_p,$S)=@_;
  # Imp: why isn't sam2p(1) PNG -> PNG idempotent?
  my $tfmt=$FileFormat eq'markedEPS' || $FileFormat eq 'EPS' ? 'EPS'
         : $FileFormat eq'markedPS' || $FileFormat eq 'PS' ? 'PS' # Dat: emits no /PageSize
         : $FileFormat eq'PDF1' || $FileFormat eq 'PDF' ? 'PDF'
         : undef;
  if (defined $tfmt) {}
  elsif (is_via_sam2p() or is_ok_sam2p()) {$tfmt=$FileFormat; $::opt_approx=1}
  else { error "sam2p doesn't support our FileFormat $FileFormat" }
  fix_pipe_in ".img", $S, 0;
  if ($approx_p) {
    if ($tfmt eq 'GIF') {
      # Dat: reduce palette to 8-bit if necessary
      my @args=('sam2p',@extra,"$tfmt:",'--',$InputFilename,$OutputFilename);
      debug "Running: @args";
      my $cmd=join('  ',map{fnq$_}@args)." 2>&1";
      my $res=readpipe($cmd);
      if ($res=~/\binvalid combination, no applicable OutputRule\b/) {
        # Dat: reduce palette to 8-bit
        #die "NOR";
        my $have_convert_p;
        my $have_pnmquant_p=0;
        for my $dir (split/:/,$ENV{PATH}) {
          if ((-f"$dir/pnmquant")) { $have_pnmquant_p=1 }
        }
        if (!$have_pnmquant_p) {
          $have_convert_p=0;
          for my $dir (split/:/,$ENV{PATH}) {
            if ((-f"$dir/convert")) { $have_convert_p=1 }
          }
        }
        my $cmd;
        if ($have_pnmquant_p) {
          my @args1=('sam2p','PPM:','--',$InputFilename,'-');
          my @args2=('sam2p',@extra,"$tfmt:",'--','-',$OutputFilename);
          $cmd=join('  ',map{fnq$_}@args1)." | pnmquant 256 | ".
                  join('  ',map{fnq$_}@args2);
        } elsif ($have_convert_p) {
          my @args1=('sam2p','PPM:','--',$InputFilename,'-');
          my @args2=('sam2p',@extra,"$tfmt:",'--','-',$OutputFilename);
          # vvv Dat: `convert - GIF:-' does quantize (and emits GIF)
          $cmd=join('  ',map{fnq$_}@args1)." | convert - GIF:- | ".
                  join('  ',map{fnq$_}@args2);
        }
        debug "Running pipe: $cmd";
        exec($cmd);
      } elsif ($? !=0) { die $res }
      # die $cmd;
      #debug "Running: $progname  @extra @_";
      #error "prog $progname failed: $? $!"
      #if 0!=system $progname, @extra, @_; # Dat: non-zero exit() or not found
    }
    do_exec('sam2p', ("$tfmt:", '--', $InputFilename, $OutputFilename));
  } else {
    warning "post-processing of sam2p PDF output increases file size" if $tfmt eq 'PDF';
    $tfmt='EPS' if $tfmt eq 'PDF'; # Imp: PDF1<->PDF
    close IN;
    my $tpfn=temp_prefix()."Psimg";
    error "Cannot open temp pipe dest: $tpfn" unless open TP, "> $tpfn";
    $tmpfiles{$tpfn}=1;
    die unless close TP;
    do_system('sam2p', ("$tfmt:", '--', $InputFilename, $tpfn));
    error "Cannot open temp pipe src: $tpfn" unless open IN, "< $tpfn";
    $already_read=0; $bytes_left=-1;
    $InputFilename=$tpfn; # '-'
    goto SCAN_AGAIN
  }
}

#** Force this value for %%HiResBoundingBox if a %BoundingBox is read
my $force_hiresbbox_value;

### scan first line, check for DOS EPSF (and remove DOS headers)
my $header;
{ SCAN_AGAIN:
  my $S;
  $_=$header="";
  read_error if 0>read IN, $S, 4;
  error "$InputFilename: empty file" if 0==length($S);
  $already_read+=length($S);
  ##print tell(IN)." bar=$already_read\n";
  my $iff="?"; # Input File Format
  # vvv be permissive, since we have only 4 chars
  if ($S eq "\211PNG") { $iff="PNG" }
  elsif ($S=~/\A(\377+\330)\377/) { $iff="JPEG" }
  elsif ($S eq "MM\000\052" or $S eq "II\052\000") { $iff="TIFF" }
  elsif ($S=~m@\AP([1-6])[\s#]@) { $iff="PNM" }
  elsif ($S=~/\ABM/) { $iff="BMP" }
  elsif ($S eq "GIF8") { $iff="GIF" }
  elsif ($S eq "FORM") { $iff="LBM" }
  elsif ($S eq "/* X" or $S eq "/*XP") { $iff="XPM" }
  elsif ($S=~/\A\12[\0-\005]\001[\001-\10]/) { $iff="PCX" }
  elsif ($S=~/\A[\36-\77](?:\001[\001\11]|\0[\002\12\003\13])\0/) { $iff="TGA" }
  elsif ($S eq "\305\320\323\306") { $iff="DOS-EPSF" }
  elsif ($S eq "\033%-1") { $iff="UEL" }
  elsif (substr($S,0,1)eq'%') { $iff="P" } # PS, EPS or PDF

   # PNG JPEG TIFF PNM BMP GIF LBM XPM PCX TGA
  if ($iff eq "DOS-EPSF") { # DOS EPSF header
    read_error if 30-4>read IN, $S, 30-4, 4;
    my ($eheader,$ps_ofs,$ps_len,$wmf_ofs,$wmf_len,$tif_ofs,$tif_len,$checksum)=
      unpack"A4VVVVVVv", $S;
    $already_read+=30-4;
    error "$InputFilename: bad DOS EPS" if $eheader ne "\305\320\323\306" or $ps_ofs<30;
    my($ps_end, $wmf_end, $tif_end)=($ps_ofs+$ps_len, $wmf_ofs+$wmf_len, $tif_ofs+$tif_len);
    $ps_ofs-=30;
    if (!seek IN, $ps_ofs, 1) {
      while ($ps_ofs>4096) { $ps_ofs-=4096; readIN 4096 }
      read_in $ps_ofs if $ps_ofs>0;
    }
    $bytes_left=($ps_end>$wmf_end and $ps_end>$tif_end) ? -1 : $ps_len;
    $S=readIN(1);
  } elsif ($iff eq "UEL") { # HP PJL UEL, untested
    $S.=readIN;
    $S=substr($S,1);
    error "$InputFilename: bad HP PJL UEL header: ".(~chomp($S)&&$S)
      if $S!~/\A\\e?%-12345X\@PJL ENTER LANGUAGE\s*=\s*POSTSCRIPT\s*\r?$/i;
    1 while length($S=readIN())!=0 and substr($S,0,4)ne'%!PS';
    die "$InputFilename: premature HP PJL UEL header" if length($S)==0;
  } elsif ($iff eq "P") {
    # no-op yet, see later
  } elsif ($iff eq "?") {
    error "unknown input image format: $InputFilename";
  } else { # source file is in some raster graphics format
    run_sam2p($::opt_approx,$S);
    goto SCAN_AGAIN
  }

  # now deal with PS, EPS and PDF
  if (substr($S,0,1) eq '%') {
    { my $max=128;
      my $C;
      while (length($S)<$max and defined($C=readIN(1)) and
       $C ne "\n" and $C ne "\r") { $S.=$C }
      error "couldn't find end of PS/PDF header line in $max bytes\n" if
        length($S)>=$max or !defined($C);
      $C=($C eq "\r") ? readIN(1) : "NONE";
      if (!defined$C or ($C ne "\n" and $C ne "NONE")) {
        use IO::Handle; # Dat: needed for ungetc
	IN->ungetc(ord($C)) if defined $C;
        debug "MAC \\r detected";
        $in_mac_p=1;
      } elsif ($C eq "\n") { # Dat: \r\n, DOS CRLF
        $in_mac_p=0; $S.="\r";
      } else { $in_mac_p=0 }
      $S.="\n";
    } # $S.=readIN;
    if (substr($S,0,4)eq'%PDF') {
      # error "$InputFilename: won't read a PDF file";
      if ($FileFormat eq 'PDF') { # convert PDF to PDF
        # !! PDF->PS->PDF
        open_OUT();
        $_=$S;
        debug "Doing a bit-by-bit copy";
        do {
          error "input error: $!" unless print OUT;
        } while (length($_=readIN 4096));
        close OUT; close IN;
        exit 0;
      } elsif ($FileFormat eq 'PDF1') { # Dat: remove extra pages by running pdftops and gs -sDEVICE=pdfwrite
        # !! Imp: possibly Run MetaPost output through full dvips (texc.pro) when prologues:=0
        # !! Imp: add full dvips %* font comment when prologues:=1 (design sizes missing :-()
	# Dat: no way to use $::opt_approx, because it doesn't remove extra pages
        fix_pipe_in ".pdf", $S, 0; # in case of stdin
        # Imp: option to open pdftops pipe instead of temp file
        # Dat: we rather use a temp file here for safety and early error detection
       do_input_pdftops: # come from EPS: and markedEPS:
        close IN; # after fix_pipe_in()
	my $tpfn=temp_prefix()."Peps";
        error "Cannot open temp pipe dest: $tpfn" unless open TP, "> $tpfn";
        $tmpfiles{$tpfn}=1;
        die unless close TP;
        do_system qw(pdftops -f 1 -l 1 -eps -- ), $InputFilename, $tpfn;
        error "Cannot open temp pipe src: $tpfn" unless open IN, "< $tpfn";
	$already_read=0; $bytes_left=-1;
        $InputFilename=$tpfn; # '-'
        goto SCAN_AGAIN
      } elsif ($FileFormat eq 'EPS' or $FileFormat eq 'markedEPS') { # convert PDF to EPS
        # Dat: limitation: markedEPS and EPS are treated the same
        # vvv Dat: pdftops(1) is part of the xpdf package
	# vvv Dat: pdftops(1) can emit to stdout
        fix_pipe_in ".pdf", $S, 0;
	if ($::opt_approx) {
          do_exec qw(pdftops -f 1 -l 1 -eps --), $InputFilename, $OutputFilename;
	} else { goto do_input_pdftops }
      } elsif ($FileFormat eq 'PS' or $FileFormat eq 'markedPS') {
        # Dat: limitation: markedEPS and EPS are treated the same
        fix_pipe_in ".pdf", $S, 0;
	close IN;
        # vvv we must query the BoundingBox first
        my $cmd="pdftops -f 1 -l 1 -eps ".fnq($InputFilename)." -";
        debug "pdftops bbox pipe: $cmd";
        error "pipe: $!" unless open PIPE, "$cmd |";
        my $line;
        error "expected PS document" if !defined($line=<PIPE>) or $line!~/^%!PS-Adobe-\d.*EPSF-\d/;
        my @L; # $papersize_x, $papersize_y
        while (<PIPE>) {
          last if /^%%EndComments/ or !/^%/;
          @L=($1,$2) if /^%%(?:Hires|Exact)BoundingBox:\s*\S+\s*\S+\s*(\S+)\s*(\S+)\s*$/i;
          @L=($1,$2) if /^%%BoundingBox:\s*\S+\s*\S+\s*(\S+)\s*(\S+)\s*$/i and !@L;
          # ^^^ Dat: HiRes has priority
          # ^^^ Dat: ignore llx and lly coordinates
        }
        1 while read PIPE, $line, 4096;
        error "closing PIPE: $?" unless close PIPE;
        error "BoundingBox not found in pdftops output" if !@L;
        debug "Got PaperSize: @L";

	# vvv Dat: pdftops without -eps doesn't report HiResBoundingBox,
	#     so we force it here
	# at Wed Nov 15 17:19:23 CET 2006
	$::opt_bboxfrom='adsc' if $::opt_bboxfrom eq 'guess';
	$force_hiresbbox_value="0 0 @L";
	
        #die defined $L[1];
        $L[0]=myceil $L[0]; $L[1]=myceil $L[1]; # Dat: pdftops expects integer papersize :-( )
	if ($::opt_approx) {
	  # vvv Dat: even pdftops 3.01 accepts only integer for -paperw and paperh
          do_exec 'pdftops', '-paperw', myfloor($L[0]+0.5), '-paperh', myfloor($L[1]+0.5), $InputFilename, $OutputFilename;
	} else {
	  my $tpfn=temp_prefix()."Pps";
	  error "Cannot open temp pipe dest: $tpfn" unless open TP, "> $tpfn";
	  $tmpfiles{$tpfn}=1;
	  die unless close TP;
          do_system 'pdftops', '-paperw', myfloor($L[0]+0.5), '-paperh', myfloor($L[1]+0.5), $InputFilename, $tpfn;
	  error "Cannot open temp pipe src: $tpfn" unless open IN, "< $tpfn";
	  $already_read=0; $bytes_left=-1;
	  $InputFilename=$tpfn; # '-'
	  goto SCAN_AGAIN
	}
      } elsif (is_ok_sam2p() or is_via_sam2p()) {
        # Dat: PDF to GIF conversion
        run_sam2p(1,$S);
      }

      error "cannot create from PDF: FileFormat $FileFormat";
     OK:
    }
    error "$InputFilename: EPS DSC must be %!PS-Adobe" if substr($S,0,4)ne'%!PS';
    # ^^^ Dat: mpost outputs "%!PS\n"
  } else {
    warning "$InputFilename: no PS ADSC header, BoundingBox not found\n"
  }
  $header=$S;
}

# Dat: Now we are converting from PS|EPS to EPS|markedEPS|PDF|PCL5|PGM
#      So we're converting from PS|EPS with Ghostscript

### variables and pattern for BoundingBox search
my $bbxpatt = '[0-9eE\.\-]';
my $BBValues = "\\s*($bbxpatt+)\\s+($bbxpatt+)\\s+($bbxpatt+)\\s+($bbxpatt+)"; # protect backslashes: "\\" gets '\'

my $ll_zero=0; # ($llx,$lly)==(0,0) in the output file
my $need_grestore=0; # 0 v 1
#** Applies %%*BoundingBox, %%EndComments, special setpagedevice, gsave..translate
#** @param $_[0] llx, may be undef to signify that bbox is undetected
#** @param $_[1] lly
#** @param $_[2] urx
#** @param $_[3] ury
#** @param $_[4] after_correct PostScript code (resolution, page size)
#** @return PostScript code to be printed after the header
sub CorrectBoundingBox($$$$$$$) {
  no integer;
  my $bbx="";
  my $pagedev_mark="";
  my $translate="";
  my ($llx, $lly, $urx, $ury, $after_correct, $fontsdefs, $is_restored) = @_;
  if (defined $llx) {
    my ($xoffset, $yoffset) = (0, 0);
    my $old_bbox="$llx $lly $urx $ury"; # debug "Old BoundingBox: $old_bbox";
    # my ($width, $height) = ($urx - $llx, $ury - $lly);
    ($xoffset, $yoffset) = (-$llx, -$lly);
    # $::opt_below=0 if $lly>=0; # always move to (0,0)
    # my ($urxh,$uryh)=($urx,$ury);
    # my $no_translate=$::opt_below;
    #die $no_translate;
    ($llx,$lly,$urx,$ury)=(0,0,$urx-$llx,$ury-$lly) if !$::opt_below;
    $urx=1 if $urx<=0; # Dat: GS dislikes empty image; fix also negative image
    $ury=1 if $ury<=0;
    my($px,$py)=($urx,$ury);
    ($px,$py)=($1+0,$2+0) if $after_correct=~m@/PageSize\s*\[(\S+)\s+(\S+)+\]@;
    my @paper=Htex::papers::any("$px,$py");
    $paper[0]=defined $paper[0] ? "%%DocumentPaperSizes: $paper[0]\n" : "";
    $bbx.="%%BoundingBox: ".myfloor($llx)." ".myfloor($lly)." ".
                             myceil($urx)." ". myceil($ury)."\n";
    $bbx.="%%HiResBoundingBox: $llx $lly $urx $ury\n".
          "%%ExactBoundingBox: $llx $lly $urx $ury\n" if myfloor($llx)!=$llx
      or myfloor($lly)!=$lly or myceil($urx)!=$urx or myceil($ury)!=$ury;
    $bbx.="%%DocumentMedia: plain $px $py 0 () ()\n". # like pdftops(1)
          "$paper[0]";
    # ^^^ Imp: can DocumentMedia be non-integer? As of us, it can.
    # vvv we output a second /MediaBox here, and we'll remove the first one
    #     (written by GS) later
    # vvv Dat: old version of Ghostscript insisted on an integer /CropBox (??).
    #          we do not force it now
    $pagedev_mark.="mark /CropBox [$llx $lly $urx $ury] /PAGE pdfmark\n" if is_pdfmark();
    # die "$xoffset $yoffset $::opt_below";
    if ($xoffset==0 and $yoffset==0) { #**** pts ****
      $need_grestore=0;
      $ll_zero=1; # Dat: we do not insert extra /MediaBox here, gs -sDEVICE=pdfwrite will do
    } elsif ($::opt_below) {
      # Do not translate (set (0,0) to the origin) with --below or multi-page
      # file formats.
      $need_grestore=0;
      $ll_zero=0; # fix /MediaBox because it become non-(0,0)-based
      $pagedev_mark.="mark /MediaBox [$llx $lly $urx $ury] /PAGE pdfmark\n" if
        is_pdfmark(); # $FileFormat eq 'markedEPS' or $FileFormat eq 'markedPS';
      # Dat: markedPS and contains pdfmark!
    } else {
      # debug "Offset:", $xoffset, $yoffset; # no new information, see -$llx, -$lly
      $xoffset=0 if $xoffset==0; # get rid of `-0'
      $yoffset=0 if $yoffset==0; # get rid of `-0'
      if ($is_restored) { $translate="" } # save..restore does gsave..grestore
                   else { $translate="gsave "; $need_grestore=1 }
      $ll_zero=0;
      # vvv the /MediaBox is different from what gs dumps
      # $pagedev_mark=~s@/PageSize\s*\[[^\]]*]@/PageSize [$urx $ury]@; # BUGFIX at Tue Apr 22 10:08:17 CEST 2003
      $pagedev_mark.="mark /MediaBox [$llx $lly $urx $ury] /PAGE pdfmark\n" if is_pdfmark();
      $translate.="$xoffset $yoffset translate\n"
    }
    my $new_bbox="$llx $lly $urx $ury";
    if ($old_bbox eq $new_bbox) {
      debug "Final (HiRes)BoundingBox: $new_bbox";
    } else {
      debug "Old (HiRes)BoundingBox: $old_bbox";
      debug "Final corrected (HiRes)BoundingBox: $new_bbox";
    }
    $pagedev_mark="/pdfmark where{pop}{/pdfmark/cleartomark load def}ifelse\n$pagedev_mark"
      if length($pagedev_mark)!=0;
  }
  # vvv Imp: `<<' -> `dict'
  # Dat: it is inherently impossible to tell GS that it shouldn't
  #      recompress the images already compressed in the EPS file, but keep
  #      them in their original, compressed form. So we rather instruct GS to
  #      recompress
  # !! Dat: /CompatibilityLevel 1.3 %PDF-1.2 -- Dat: 1.2 won't embed Courier
  my $markpagedevices="";
  my $imagesopts=($::opt_lossy ? "
/AutoFilterMonoImages true
/AutoFilterGrayImages true
/AutoFilterColorImages true
/MonoImageFilter /CCITTFaxEncode
/GrayImageFilter /DCTEncode
/ColorImageFilter /DCTEncode
" : "
/AutoFilterMonoImages false
/AutoFilterGrayImages false
/AutoFilterColorImages false
/MonoImageFilter /LZWEncode
/GrayImageFilter /LZWEncode
/ColorImageFilter /LZWEncode
"); # Dat: assumes new, patent-free LZW
  if (is_pdfmark()) {
    # Dat: CompatibilityLevel 1.3 is required for font embedding & all /FlateDecode
    $markpagedevices="
/CompatibilityLevel 1.3 %PDF-1.3
/EmbedAllFonts true
/Optimize true % ignored by gs-6.70
/AutoRotatePages /None
/UseFlateCompression ".($::opt_compression ne 'none'?"true":"false")."
/AutoPositionEPSFiles false
/ConvertImagesToIndexed false
/DownsampleMonoImages false
/DownsampleGrayImages false
/DownsampleColorImages false
/EncodeMonoImages true
/EncodeGrayImages true
/EncodeColorImages true
/AntiAliasMonoImages false
/AntiAliasGrayImages false
/AntiAliasColorImages false\n$imagesopts";
    $markpagedevices=(length($markpagedevices)!=0 ? "<< $markpagedevices >> setpagedevice\n" : "");
    $markpagedevices.="1 dict dup /ImagingBBox null put setpagedevice\n";
    $markpagedevices.="1 dict dup /Policies 1 dict dup /PageSize 3 put put setpagedevice\n"; # ripped from pdftops(1)
  }
  my $setpagesize="";
  # die defined $urx;
  # die is_set_pagesize_ok();
  # die $after_correct;
  if (defined $urx and is_set_pagesize_ok()
  and $::opt_papersize ne'force-unknown'
  and $after_correct!~m@/PageSize\s*\[@) { # Imp: m@/Pagesize ugly
    # Dat: true for FileFormat PGM
    # Dat: emit /PageSize even for PDF1
    # Dat: Ghostscript 6.70 rounds /PageSize down, but we need up when creating /MediaBox for PDF
    $setpagesize="2 dict dup /PageSize [".myceil($urx)." ".myceil($ury)."] put setpagedevice\n";
    # ^^^ Dat: PLRM.pdf doesn't forbid a non-integer /PageSize
  }
  my $bsetup=is_page1_stop()?"":"%%BeginSetup\n%%EndSetup\n";
  # ^^^ Dat: CUPS inserts its setpagedevice calls for /Duplex and /PageSize
  #     etc. just after the %%BeginSetup line (or, if missing, puts it in
  #     front of the first %%Page). We'd like this execution order: CUPS,
  #     ours, PStoPS (or psnup). (When we come after CUPS, we'll have a
  #     chance to override its settings.) So we emit a fake
  #     %%BeginSetup..%%EndSetup pair just before our code doing
  #     `setpagedevice'.
  # !! ?? run pstops first, and then a2ping.pl
  # !! why does a PDF -> PS conversion need $is_restored?
  # vvv Dat: `mark' is necessary, because pstops 1.17 from xpdf(1) emits lines
  #     lines leaving `false' on the stack:
  #     %%BeginResource: font SKPOPP+LMRoman12-Regular
  #     %!PS-AdobeFont-1.0: LMRoman12-Regular 0.86
  #     %%CreationDate: 4th August (Monday) 2003
  #     % Generated by MetaType1 (a MetaPost-based engine)
  #     % CM sources: copyright (C) 1997 AMS, all rights reserved; METATYPE1/Type 1 ver
  #     % ADL: 694 194 112
  #     %%EndComments
  #     FontDirectory/LMRoman12-Regular known{/LMRoman12-Regular findfont dup/UniqueID
  #     /UniqueID get 0 eq exch/FontType get 1 eq and}{pop false}ifelse
  #     {save true}{false}ifelse}{false}ifelse
  my $save=$is_restored?"save mark\n":"";
  $bbx.$fontsdefs."%%EndComments\n".$bsetup.$setpagesize.$markpagedevices.$pagedev_mark.$after_correct.$save.$translate
}

### scan header
my $to_OUT="";
my $after_code="";
my $do_fix_tumble=0;
my $is_restored=0;
if (1<length($header)) {
  my($llx,$lly,$urx,$ury);
  my($bbtype)='-'; # None
  my $allow_adsc_bb=1;
  my $after_correct="";
  my $do_bb_line=sub { # sub do_bbline($$)
    no integer;
    # Decreasing precedence of various BoundingBoxes:
    # The last valid *bbox entry has effect.
    # Active policy:
    #   normal mode : BoundingBox ExactBoundingBox HiResBoundingBox
    #   --hires mode: HiResBoundingBox BoundingBox ExactBoundingBox
    #   --exact mode: ExactBoundingBox BoundingBox HiResBoundingBox
    #   -hi -ex mode: ExactBoundingBox HiResBoundingBox BoundingBox
    # Another possible policy:
    #   normal mode : BoundingBox HiResBoundingBox==ExactBoundingBox
    #   --hires mode: HiResBoundingBox ExactBoundingBox BoundingBox
    #   --exact mode: ExactBoundingBox HiResBoundingBox BoundingBox
    #   -hi -ex mode: ExactBoundingBox==HiResBoundingBox BoundingBox
    my($S,$from)=@_;
    return if $S!~/^(?:$BBregex|set)$BBValues/oi;
    # print STDERR "($S)\n";
    my $E1=defined$1 ? $1 : "+";
    my $T=!defined($1) ? 'S' : uc substr $1,0,1; # '' || 'H' || 'E'
    # debug "Trying BoundingBox T-$bbtype: $llx $lly $urx $ury";
    if ($T eq 'S'
     or !$::opt_hires and !$::opt_exact and ($T eq '' or ($bbtype ne '' and ($T eq 'E' or $bbtype ne 'E')))
     or  $::opt_hires and !$::opt_exact and ($T eq 'H' or ($bbtype ne 'H' and ($T eq '' or $bbtype ne '')))
     or  $::opt_exact and !$::opt_hires and ($T eq 'E' or ($bbtype ne 'E' and ($T eq '' or $bbtype ne '')))
     or  $::opt_exact and  $::opt_hires and ($T eq 'E' or ($bbtype ne 'E' and ($T eq 'H' or $bbtype ne 'H')))
       ) {
      # if ($allow_bb) {
        ($bbtype,$llx,$lly,$urx,$ury)=($T,$2+0,$3+0,$4+0,$5+0);
        debug "Applying ${E1}BoundingBox$from T-$bbtype: $llx $lly $urx $ury";
      # }
    } else {
      my @L=($2+0,$3+0,$4+0,$5+0); # convert 0.00 to 0
      debug "Ignoring ${E1}BoundingBox$from T-$bbtype: @L";
    }
    # Dat: don't do $do_bb_line=sub{};# same as $allow_bb=0;
  };
  $header=~s@\r\n?\Z(?!\n)@@;
  my $after_comments=""; # after %%EndComments
  # my $res;
  { my $headEPSF;
    my $headPS="PS-Adobe-3.0";
    # vvv Imp: run this correction even w/o input EPS header
    if ($header!~s/\s+(EPSF-[.\d]+)$// or $::opt_bboxfrom ne 'guess') { # a PS not an EPS already
      # This is the compute-pipe routine.
      # To convert an [E]PS to an EPS:
      # -- find the end of the 1st page in the code, and remove everything after it
      # -- `pop' off the execution stack after the 1st page
      # -- `end' the dictionary stack after the 1st page
      # -- change the ADSC magic `%!PS-Adobe-...' to `%!PS-Adobe-3.0 EPSF-3.0'
      # -- remove the `%%Pages', `%%DocumentPaperSizes', `%%PageOrder:'
      #    (Ascending) comment from the ADSC header
      # -- possibly remove the `%%Page' ADSC comment [no]
      # -- ignore calls to = setpage setpagemargin setpageparams .setpagesize
      #    setpagedevice setpagetype setprintername setresolution a4 letter ...
      # -- surround the code by save ... pop* end* restore (implies gsave ... grestore)
      # -- possibly ignore calls to showpage [showpage is forced]
      $headEPSF=" EPSF-3.0";
      debug "Computing BBox info from non-EPS PS file";
      fix_pipe_in 'i.ps', "%!$headPS\n", 0;
      my $tfn=temp_prefix()."c.tgs";
      error "temp open $tfn: $!" unless open F, "> $tfn";
      $tmpfiles{$tfn}=1;
      ##print tell(IN)." car=$already_read\n";
      die "$0: $!\n" unless print F "% this is temporary gs command file created by $program".'
/DOCUT true def
/MAINFILE FN (r) file def
/DICTCOUNT countdictstack def
count /OPCOUNT exch def
<</BeginPage { % <PageCount> BeginPage -
    dup 1 eq {
      count OPCOUNT sub 1 sub (pop-count==) ..print ===
      countdictstack DICTCOUNT sub (end-count==) ..print ===
      DOCUT { (cut-offset==) ..print MAINFILE fileposition === flush } if
      (bbox-success\n) ..print
      quit
    } if
  }
  % Run the previous contents of /BeginPage: {--.callbeginpage--}
  [exch aload pop currentpagedevice /BeginPage get aload pop] cvx
>> setpagedevice

% vvv do these after our call to /setpagedevice
currentglobal true setglobal
systemdict begin
/..paper.redef<< >>def
/..print/print load def
/setpageparams{pop pop pop pop (\nset-called-4==/setpageparams\n) ..print flush}def
/setpage{pop pop pop (\nset-called-3==/setpage\n) ..print flush}def
/setpagesize{pop pop (\nset-called-2==/setpagesize\n) ..print flush}def
/.setpagesize{pop pop (\nset-called-2==/.setpagesize\n) ..print flush}def
/setpagemargin{pop (\nset-called-1==/setpagemargin\n) ..print flush}def
{% anti-Windows-printer-driver `%%[ ProductName:` etc.
  dup type/stringtype eq{
    dup length 3 ge{
      dup 0 3 getinterval (%%[) eq{
        (\nset-called-1==/=\n) ..print flush
        (\nset-called-1==/print\n) ..print flush
  }if}if}if
  pop
}
dup/= exch def /print exch def
/setpagedevice{pop (\nset-called-1==/setpagedevice\n) ..print flush}def
/..sdict <<
  /PageSize { % [. .] PageSize -
    dup type /arraytype eq {
      dup length 2 ge {
        dup 0 get type dup /integertype eq exch /realtype eq or {
          dup 1 get type dup /integertype eq exch /realtype eq or {
            (\npapersize-x==) ..print dup 0 get ===
            (\npapersize-y==) ..print dup 1 get === (\n) ..print
          } if
        } if
      } if
    } if
    pop
  }
  /HWResolution { % [. .] PageSize -
    dup type /arraytype eq {
      dup length 2 ge {
        dup 0 get type dup /integertype eq exch /realtype eq or {
          dup 1 get type dup /integertype eq exch /realtype eq or {
            (\nresolution-x==) ..print dup 0 get ===
            (\nresolution-y==) ..print dup 1 get === (\n) ..print
          } if
        } if
      } if
    } if
    pop
  }
  /Duplex { % [. .] PageSize -
    dup type /booleantype eq {
      (\nsides-duplex==) ..print dup === (\n) ..print
    } if
    pop
  }
  /Tumble { % [. .] PageSize -
    dup type /booleantype eq {
      (\nsides-tumble==) ..print dup === (\n) ..print
    } if
    pop
  }
>> def
%/.setpagesize{pop pop (hehehe\n) print} def
% /a4{(hehehe\n) ..print} def % doesn"t work, has to be put into userdict
/setpagedevice{
  { % <key> <val>
    exch dup ..sdict exch known { % run all keys known in ..sdict
      ..sdict exch get exec
    } {pop pop} ifelse
  } forall
  (\nset-called-1==/setpagedevice\n) ..print flush
}def
/setpagetype{pop (\nset-called-1==/setpagetype\n) ..print flush}def
/setprintername{pop (\nset-called-1==/setprintername\n) ..print flush}def
/setresolution{pop (\nset-called-1==/setresolution\n) ..print flush}def
[ % Dat: fixed at Mon May 19 14:32:31 CEST 2003
  statusdict /.pagetypenames 2 copy known {get}{pop pop{}}ifelse
  % ^^^ Dat: may be {/a4 STRICT {(%END SIZES) .skipeof} if /a5}
  { /11x17/a3/a4/a4small/b5/ledger/legal/letter % GS 7.04
    /lettersmall/note/a0/a1/a2/a5/a6/a7/a8/a9/c0/c1/c2/c3/c4/c5/c6
    /a10/b0/b1/b2/b3/b4/b5/b6
    /isob0/isob1/isob2/isob3/isob4/isob5/isob6
    /jisb0/jisb1/jisb2/jisb3/jisb4/jisb5/jisb6
    /archE/archD/archC/archB/archA/flsa/flse/halfletter
    /tabloid/csheet/dsheet/esheet/executivepage/com10envelope
    /monarchenvelope/c5envelope/dlenvelope/folio/quarto
  }
]{{
  dup type /nametype eq { dup xcheck not { % Dat: fixed
    % dup ===
    dup ..paper.redef exch known {pop} {
      dup ..paper.redef exch null put
      dup userdict exch 2 copy known { 2 copy get
        1 index userdict exch undef
        % Stack: /a4 userdict /a4 {...}
        % 3 copy pop undef
        4 2 roll exch undef
      } { exch pop pop {} } ifelse
      % Stack: /a4 {595 842 //.setpagesize --exec--}
      % Stack: /a4 {595 842 {/statusdict --.systemvar-- --begin-- .setpagesize --end--} --exec--}
      { /get exec /pop (\nset-called-0==) ..print === flush } % dump
      dup length array copy cvx % make a copy for subsequent invocations
      2 copy exch 0 exch put exch pop % change /get to {...}
      2 copy exch 2 exch put % change /pop to /a4
      def % overwrite it in systemdict
    } ifelse
    true
  } if } if
  pop
}forall} forall
end % systemdict
setglobal
systemdict readonly pop

(bbox-begin\n) ..print
MAINFILE cvx exec
(add-showpage==1\n) ..print
/DOCUT false def
showpage quit
';
      die unless close F;
      # vvv Imp: make it work on Win32 (no >&1 redirection)
      my $gs3=$CGS. # "-dPAGE1QUIT=".($FileFormat eq 'EPS' or $FileFormat eq 'markedEPS' ? 'quit' : '{}').
        " -dWRITESYSTEMDICT -dNOPAUSE -sDEVICE=bbox -sFN=".fnq($InputFilename)." ".fnq($tfn)." 2>&1";
      debug "Ghostscript compute pipe: $gs3";
      my $res=`$gs3`;
      ## die $res;
      temp_unlink $tfn;
      ## print STDERR $res;
      error $?==11 ? "segmentation fault in $GS" : "not a GS output from $GS ($?)"
        if !defined $res # BUGFIX at Sun Mar  7 18:51:34 CET 2004
        or $res!~s/\A(?:\w+ Ghostscript \d|Copyright .* artofcode ).*\n// # AFPL Ghostscript 6.50 (2000-12-02)
        or $res!~s/.*?^bbox-begin\n//m;
      if ($res!~s/\nbbox-success\n\Z(?!\n)/\n/) {
        warning # not `error', mpost(1) `prologues:=0; ... btex fonts' output won't compile
          "BBox discovery was not successful";
        # !! continue only if MetaPost output?
        goto SKIP_BBOX_DISC;
      }
      #: Copyright (C) 2000 Aladdin Enterprises, Menlo Park, CA.  All rights reserved.
      #: This software comes with NO WARRANTY: see the file PUBLIC for details.
      #: set-called-0==/a4
      #: %%BoundingBox: 56 41 539 783
      #: %%HiResBoundingBox: 56.645998 41.849999 538.811984 782.351976
      #: pop-count==0
      #: end-count==1
      #: cut-offset==81898
      my $pop_count=0;
      my $end_count=0;
      my $cut_offset=-1;
      my $papersize_x=undef; # page_width
      my $papersize_y=undef; # page_height
      my $resolution_x=undef;
      my $resolution_y=undef;
      my $duplexi=0;
      my $tumblei=0;
      my %H;
      my $undefs="";
      my $bbc=0; # required
      for my $line (split/\n/, $res) {
        if ($line=~/^$BBregex$BBValues/oi) { $do_bb_line->($line," from Compute-GS"); $bbc++ }
        elsif ($line=~m@^set-called-(\d+)==/(\S+)$@) {
          if (not exists $H{$2}) {
            $H{$2}=1;
            $undefs.="/$2".(
              $1==0 ? "{}def\n" :
              $1==1 ? "/pop load def\n" :
                      "{".("pop "x$1)."}bind def\n"
            );
          }
          # Dat: Safe, restorable, EPS-wise: /setpagedevice {pop} def
          # Dat: Smart, documentwise /a4 dup where{dup wcheck{exch{}put}{pop{}def}ifelse}{pop}ifelse
        }
        elsif ($line=~/^pop-count==(\d+)$/) { $pop_count=$1+0 }
        elsif ($line=~/^end-count==(\d+)$/) { $end_count=$1+0 }
        elsif ($line=~/^cut-offset==(\d+)$/) { $cut_offset=$1+0 }
        elsif ($line=~/^papersize-x==([-+0-9eE.]+)$/) { no integer; $papersize_x=$1+0 }
        elsif ($line=~/^papersize-y==([-+0-9eE.]+)$/) { no integer; $papersize_y=$1+0 }
        elsif ($line=~/^resolution-x==([-+0-9eE.]+)$/) { no integer; $resolution_x=$1+0 }
        elsif ($line=~/^resolution-y==([-+0-9eE.]+)$/) { no integer; $resolution_y=$1+0 }
        elsif ($line=~/^sides-duplex==true$/) { $duplexi=1 }
        elsif ($line=~/^sides-dumplex==false$/) { $duplexi=2 }
        elsif ($line=~/^sides-tumble==true$/) { $tumblei=1 }
        elsif ($line=~/^sides-tumble==false$/) { $tumblei=2 }
        elsif ($line=~/^add-showpage==\d+$/) { } # !!
        elsif (length($line)==0 or $line=~/^(?:Copyright |This software )/) {}
        elsif ($line=~/^Loading (\S+) font from.*[.][.][.]/) { debug "GS builtin font used: $1" }
        else { debug "unknown line ($line)" }
      }
      undef $papersize_y if !defined $papersize_x;
      die unless $allow_adsc_bb==1;
      # Dat: This only applies when converting fron non-EPS PS:
      #      Setting $allow_adsc_bb=0|1 now would disallow/allow the %%BoundingBox
      #      etc. ADSC comment override the bbox computed by -sDEVICE=bbox.
      #      When converting PS -> EPS, the PS file usually contains
      #      `%%BoundingBox: 0 0 paperwidth paperheight', but the figure
      #      itself is smaller.
      $bbtype='-';
      ## die "$papersize_x;;"; # PDF -sPAPERSIZE=a4
      #if ($::opt_papersize ne 'force-unknown' and $::opt_papersize ne 'unchanged') {
      #  # override bbox
      #  ($llx,$lly)=(0,0);
      #  ($urx,$ury)=split/,/,$::opt_papersize;
      #} elsif (!is_page1_stop() and defined $papersize_x and defined $papersize_y) {
      #}
      debug "PaperSize wd=${papersize_x}bp ht=${papersize_y}bp" if defined $papersize_x;
      if ($::opt_papersize eq 'force-unknown' or ($::opt_papersize eq 'unchanged' and !defined $papersize_x)) {}
      elsif ($::opt_papersize ne 'unchanged') {
        die if is_page1_stop();
        ($papersize_x,$papersize_y)=split/,/,$::opt_papersize;
        goto do_force_papersize
      } else {
        # Dat: no $do_bb_line here, because we've done it with $bbc++, and we'll also do it later
       do_force_papersize:
        die if is_page1_stop();
        # vvv ($llx,$lly,$urx,$ury)=(0,0,$papersize_x,$papersize_y);
        # $do_bb_line->("set 0 0 $papersize_x $papersize_y"," from /PageSize");
        $after_correct.="1 dict dup /PageSize [".myceil($papersize_x)." ".myceil($papersize_y)."] put setpagedevice\n";
        # ^^^ Dat: both PS and markedPS would benefit from /PaperSize
        # ^^^ Dat: will be put after CorrectBoundingBox
        # Dat: unneeded: $allow_adsc_bb=0 if $FileFormat eq 'PDF'; # force this into /CropBox (otherwise only /MediaBox)
      }
      if (defined $papersize_x and ($::opt_bboxfrom eq 'papersize' or ($::opt_bboxfrom eq 'guess' and is_set_pagesize_ok()))) {
        $allow_adsc_bb=0;
        $do_bb_line->("set 0 0 $papersize_x $papersize_y"," from /PageSize"); # does ($llx,$lly,$urx,$ury)=(0,0,$papersize_x,$papersize_y);
      }
      $allow_adsc_bb=0 if ($::opt_bboxfrom eq 'compute-gs') ? ($bbc!=0)
                        : ($::opt_bboxfrom eq 'guess') ? ($bbc!=0 and is_page1_stop()) # Imp: is is_page1_stop() OK here?
                        : 0;
      if (!is_page1_stop()) {
        $pop_count=$end_count=0; # assume PS is correct
        $cut_offset=-1;
      }
      # if ($FileFormat ne 'EPS' and $FileFormat ne 'PS') { # device-specific (marked)
      # ^^^ Dat: $::opt_resolution and $::opt_duplex are already 'force-unknown' if $FileFormat is appropriate
      {
	$::opt_resolution=$resolution_x.','.$resolution_y if
	  $::opt_resolution eq 'unchanged' and defined $resolution_x and defined $resolution_y;

        # vvv Imp: move down like $::opt_resolution
	if ($::opt_duplex eq 'force-unknown' or ($::opt_duplex eq 'unchanged' and $duplexi==0)) { $do_fix_tumble=($duplexi==1 && $tumblei==1) }
        elsif ($::opt_duplex eq 'force-simplex') { do_simplex: $after_correct.="1 dict dup /Duplex false put setpagedevice\n" }
        elsif ($::opt_duplex eq 'force-long' ) { do_long: $after_correct.="2 dict dup /Duplex true put dup /Tumble false put setpagedevice\n" }
        elsif ($::opt_duplex eq 'force-short') { do_short: $do_fix_tumble=1; $after_correct.="2 dict dup /Duplex true put dup /Tumble true put setpagedevice\n" }
        else {
          $duplexi ||= $::opt_duplex eq 'default-simplex' ? 2 : 1;
          $tumblei ||= $::opt_duplex eq 'default-short'   ? 1 : 2;
          goto do_simplex if $duplexi!=1;
          goto do_long if $tumblei!=1;
          goto do_short;
        }
      }
      # vvv Dat: save...restore is _always_ necessary to undo the changes made
      #     by the file itself (??)
      # vvv BUGFIX (only EPS) at Tue Feb  8 21:40:11 CET 2005
      # vvv Dat: now with PS output it is possible that garbage is left on
      #     the stack (see the `LMRoman' example above)
      $is_restored=1 if $FileFormat eq'EPS' or $FileFormat eq'markedEPS';
      $after_comments.=$undefs; # after our precious setpagedevice calls
      # debug "pop_count=$pop_count;";
      # debug "end_count=$end_count;";
      # debug "cut_offset=$cut_offset;";
      $after_code.=("pop\n"x$pop_count).("end\n"x$end_count);
      if ($cut_offset>=0 and ($bytes_left==-1 or $cut_offset<$bytes_left)) {
        $bytes_left=$cut_offset-$already_read;
        ##print tell(IN)." ar=$already_read\n";
        debug "Cutting after showpage at $cut_offset -> $bytes_left";
        # ^^^ Dat: cutting after `showpage' makes PS -> EPS conversion easy
        # sleep 1000;
      }
      # Dat: don't unlink $tifn yet, we'll continue scanning it
      # Imp: verify EPS created
    } else { $headEPSF=" $1" }
   SKIP_BBOX_DISC:
    if ($::opt_resolution eq 'force-unknown' or $::opt_resolution eq 'unchanged') {}
    else { $after_correct.="1 dict dup /HWResolution [@{[split/[x,]/,$::opt_resolution]}] put setpagedevice\n" }
    # ^^^ Dat: syntax already ok for $::opt_resolution
    $after_correct.="2 dict dup /TextAlphaBits 4 put dup /GraphicsAlphaBits 4 put setpagedevice\n" if
      $::opt_antialias eq 'yes' or $::opt_antialias eq 'scale3yes';

    $headEPSF="" if $FileFormat ne 'EPS' and $FileFormat ne 'markedEPS';
    $headPS=$1 if $header=~s/(PS-Adobe-[.\d]+)$//;
    $to_OUT.="%!$headPS$headEPSF\n";
  }

  debug "Scanning header for BoundingBox";
  my $do_atend=0;
  my $doing_atend=0;
  my $saved_pos;
  my $saved_bytes_left;
  my $creator_metapost_p=0; # HiResBoundingBox: after EndProlog
  my $creator_adobeps_p=0;
  my $had_pages=is_page1_stop(); # Dat: don't put `Pages:' to target EPS
  my $fontsdefs="";
  my %fontsnames;
  my @creator;
  read_again: while (length($_=readIN)) {
    #print STDERR "(($_))\n";
    ### end of header
    next unless /\S/;
    y@\r@@d; chomp;
    if (!$doing_atend) {
      if (/^%%EndComments\b/i) {
        # Dat: EPSI created by ImageMagick has BeginDefaults+EndDefaults+BeginPreview
        # Dat: PS output by ADOBEPS4.DRV has BeginDefaults+PageBoundingBox+ViewingOrientation+PageFeatures+EndDefaults
        1 while length($_=readIN) and !/\S/;
        if (!/^%%BeginDefaults\b/i) { y@\r@@d; chomp; $after_comments.="$_\n"; last }
        1 while length($_=readIN) and !/\S/;
        if (!/^%%EndDefaults\b/i) { y@\r@@d; chomp; $after_comments.="%%BeginDefaults\n$_\n"; last }
        next
      } elsif (/^%%EndDefaults\b/i) { # EPSI created by ImageMagick
      } elsif (/^%%Creator:\s*ADOBEPS/i) { # ADOBEPS4.DRV
        # Emits ``%%BoundingBox 1 1 ...' instead of `0 0'
        $creator_adobeps_p=1;
      } elsif (/^%%Creator:\s*MetaPost\b/i) {
        $creator_metapost_p=1;
      } elsif ((substr($_,0,2)ne'%%' and substr($_,0,7)ne'%*Font:' and
       substr($_,0,5)ne'%ADO_' and !/^%AI\d_/ # Dat: %ADO_DSC_..., %AI7_Thumbnail
       and substr($_,0,5)ne'%EPS ') # epsincl.mp
       or !$creator_metapost_p and substr($_,0,5)eq'%%End'
       or /^%%Begin(?:Prolog|Setup)\b/i
         ) { $after_comments.="$_\n"; last }
    }
    if (/^%%BeginPreview\b/i) { # remove EPSI preview ballast ****pts****
      while (1) {
        error "Missing EPSI %%EndPreview" if !length($_=readIN);
        last if /^%%EndPreview\b/;
        y@\r@@d; chomp
      }
    } elsif (/^%%Creator:\s*(.*)/i) {
      push @creator, $1;
      $creator[-1]=~s@\s+\Z(?!\n)@@;
      $creator[-1]=~s@, a2ping .*@@; # remove old
    } elsif (/^%%(?:DocumentPaperSizes|PageOrder|DocumentMedia):/i) {
      # silently ignore these -- will be recalculated
    } elsif (/^%%Pages:\s+(\d+)\s*/i and !$had_pages) { # Not `%%Pages: (atend)'
      # Dat: don't `$do_atend=1' only for %%Pages
      $to_OUT.="$_\n"; $had_pages=1
    } elsif (/^%%Pages:/i) {
      # silently ignore these -- not significant for EPS
    } elsif (/^$BBregex$BBValues/oi) { ### BoundingBox with values
      s@($BBregex)\s*1\s+1\s+@$1 0 0 @ if $creator_adobeps_p;
      # vvv $bbtype may be possibly already set by compute-gs
      if ($allow_adsc_bb) {
        $do_bb_line->($_," from ADSC");
	if ($force_hiresbbox_value) {
	  $do_bb_line->("%%HiResBoundingBox: $force_hiresbbox_value"," from ADSC");
	}
      }
    } elsif (/^$BBregex\s*\(atend\)/oi) {
      ### BoundingBox with (atend)
      debug "At end $1BoundingBox";
      # warning "Cannot look for BoundingBox in the trailer with option --filter" if $::opt_filter;
      # ^^^ Dat: may be seekable anyway, omit warning
      $do_atend=1
    } elsif (/^%%Page:/i and !$creator_metapost_p) { # at Thu Sep 25 15:59:52 CEST 2003
      $after_comments.="$_\n"; last
    } elsif (/^%(?:ADO_DSC_|AI\d_)/) { # Dat: example: %ADO_DSC_Encoding: MacOS Roman
      $to_OUT.="%$_\n";
    } elsif (/^%\*Font:\s+(\S+)\s+/) { # mpost(1) output
      ## debug $_;
      $fontsdefs.="$_\n"; # put in front (before `gsave ... translate')
      $fontsnames{$1}=1;
    } elsif (substr($_,0,5) eq '%EPS ') { # epsincl.mp
      $after_correct.="$_\n" if !$doing_atend; # before `gsave'
    } elsif ($doing_atend or /^%%End/) {
      # we might be in mid-line
    } else {
      $to_OUT.="$_\n"
    }
  }
  if ($doing_atend) { # already read (atend); restore file position
    seek(IN, $saved_pos, 0) or error "Input unseekable, cannot go back to line `: (atend)'";
    $bytes_left=$saved_bytes_left;
  } elsif ($do_atend) { # seek to near EOF and try again
    # error "Cannot leave line `: (atend)'" if ($saved_pos=tell IN)<0 or !seek(IN,0,1);
    fix_pipe_in "j.ps", $to_OUT, 0;
    die if ($saved_pos=tell IN)<0;
    $saved_bytes_left=$bytes_left;
    # vvv get the very last *BoundingBox entry from the last 4096 bytes
    if ($bytes_left>4096) {
      die unless seek(IN, $bytes_left-4096, 1);
      $bytes_left=4096;
    } elsif ($bytes_left<0) {
      die unless seek(IN, -4096, 2) or seek(IN, 0,0); # Dat: 0,0 for short files
    }
    $doing_atend=1; goto read_again;
  }
  # if (!$had_pages) { debug "Lying %%Pages: 1"; $to_OUT.="%%Pages: 1\n" }
  # ^^^ Dat: rather not lie !! go to (atend) if command line
  # vvv Dat: $bbtype ne'-' would be a bad idea
  warning "BoundingBox not found, try --bboxfrom=compute-gs" if !defined $llx; # !! compute-gs
  push @creator, "$program $Htex::a2ping::VERSION"; # if "@creator"!~/\ba2ping\b/;
  $to_OUT.="%%Creator: ".join(", ",@creator)."\n"; # before CorrectBoundingBox to be before `gsave' etc.
  $to_OUT.=CorrectBoundingBox $llx, $lly, $urx, $ury, $after_correct, $fontsdefs, $is_restored;
  $to_OUT.=$after_comments;
  if (%fontsnames) { # !! save .. restore
    $to_OUT.="{@{[sort keys%fontsnames]}}{\ndup where{pop pop}{cvlit dup def}ifelse}forall\n";
    $to_OUT.="/fshow where{pop}{/fshow{exch findfont exch scalefont setfont show}bind def}ifelse\n";
  }
}

### open output file or pipe (do this as late as possible)
my $do_scale3_pnm=0;
my $scale3_pnm_fn;
my @pnm2sampled_cmd;
if ($FileFormat eq 'PDF' or $FileFormat eq 'PDF1') {
  my $ofn=$ll_zero ? $OutputFilename : fix_pipe_out('.pdf');
  my $pipe = "$GS -q -dBATCH -sDEVICE=pdfwrite $GSOPTS -sOutputFile=".fnq($ofn)." -";
  debug "Ghostscript ps2pdf pipe:", $pipe;
  open(OUT, "| $pipe") or error "Cannot open Ghostscript pipe";
} elsif ($FileFormat eq 'PCL5') {
  my $ofn=$do_fix_tumble ? fix_pipe_out('.pcl5') : $OutputFilename;
  # vvv ljet4 cannot do duplex, ljet4d can
  my $pipe = "$GS -q -dBATCH -sDEVICE=ljet4d $GSOPTS -sOutputFile=".fnq($ofn)." -";
  debug "Ghostscript ps2ljet pipe:", $pipe;
  open(OUT, "| $pipe") or error "Cannot open Ghostscript pipe";
} elsif ($FileFormat eq 'PBM' or $FileFormat eq 'PGM' or $FileFormat eq 'PPM'
  or is_via_sam2p()) {
  $do_scale3_pnm=($::opt_antialias eq'scale3no' or $::opt_antialias eq 'scale3yes');
  my $device=$FileFormat eq 'PPM' || is_via_sam2p() ? 'ppmraw' :
    $FileFormat eq 'PBM' && $::opt_antialias eq 'no' ? 'pbmraw' : 'pgmraw';
  # Dat: -sDEVICE=pgm is ASCII, pgmraw is binary
  my $pipe ="$GS -q -dBATCH -sDEVICE=$device ";
  # $pipe.="-dTextAlphaBits=4 -dGraphicsAlphaBits=4 " if $::opt_antialias; # Dat: already done
  my $ofn=$OutputFilename;
  if (is_via_sam2p()) {
    # Dat: fortunately the following file format names are commin in sam2p(1)
    #      and a2ping.pl: PNG XWD BMP TIFF JPEG GIF XPM
    @pnm2sampled_cmd=('sam2p',"$FileFormat:",'--'); # Imp: use convert(1) if no sam2p(1)
    $ofn=fix_force_out('SS.pnm');
    push @pnm2sampled_cmd, $ofn, $OutputFilename;
  }
  if ($do_scale3_pnm) {
    $scale3_pnm_fn=$ofn;
    $ofn=fix_force_out('S3.pnm') if $do_scale3_pnm;
    # ^^^ Dat: this must be the last call to fix_force_out()
  }
  $pipe.="$GSOPTS -sOutputFile=".fnq($ofn)." -";
  debug "Ghostscript ps2raster pipe:", $pipe;
  open(OUT, "| $pipe") or error "Cannot open Ghostscript pipe";
} else { open_OUT() }

my $unlink_OutputFilename;
END { unlink $unlink_OutputFilename if defined $unlink_OutputFilename }
$unlink_OutputFilename=$OutputFilename;

die unless binmode OUT;
#die $to_OUT;
# !! remove setpagedevice from EPS->EPS, but keep EPS->PGM
# vvv needed by EPS->PGM conversion !!
if (is_need_showpage() and is_page1_stop()) {
  $to_OUT.="/showpage{showpage quit}bind def\n"; # don't call showpage twice
  $after_code.="showpage\n";
}
error "write OUT" if !print OUT $to_OUT;
$to_OUT="";

### print rest of file
sub OVERLAP_LENGTH(){4096} # `%%TrailerLength: 1162' by ADOBEPS4.DRV
my $extra_trailer="%%Trailer\n";
##undef $unlink_OutputFilename; die;
{ my $overlap=""; # keeps OVERLAP_LENGTH chars
  my $S;
  # print OUT while length($_=readIN 4096);
  while (length($S=readIN 4096)) { # Dat: 4096>=OVERLAP_LENGTH
    #if (length($S)<OVERLAP_LENGTH) { $S="$overlap$S"; $overlap=""; }
    #die if length($S)<OVERLAP_LENGTH; # happens sometimes
    #print OUT $overlap, substr($S,0,length($S)-OVERLAP_LENGTH());
    #$overlap=substr($S,-OVERLAP_LENGTH(),OVERLAP_LENGTH);
    next if length($overlap.=$S)<OVERLAP_LENGTH;
    print OUT substr($overlap,0,length($overlap)-OVERLAP_LENGTH());
    $overlap=substr($overlap,-OVERLAP_LENGTH());
  }
  if ($overlap=~s@\r?\n%%Trailer\r?\n(.*?)\Z(?!\n)@\n%%Trailer\n@s) {
    my $S=$1;
    $S=~y@\r@@d;
    $S=~s@\n%%TrailerLength:.*$@@m; # ADOBEPS4.DRV
    $overlap.=$S;
    $extra_trailer="";
  }
  $overlap=~s@(?:[\n\r\0\f]+%%EOF)?[\n\r\0\f]*\Z(?!\n)@@;
  # vvv Dat: would move %%Trailer after dvips output `end userdict /end-hook known{end-hook}if'
  # $overlap=~s@(?:[\n\r\0\f]+%%Trailer)?(?:[\n\r\0\f]+%%EOF)?[\n\r\0\f]+\Z(?!\n)@@;
  print OUT $overlap;
}

### close files
error "closing IN: $?" unless close IN;
# ^^^ SUXX: gs always exit(0), if exists
# vvv Dat: $after_code is pop+end
print OUT "\n$extra_trailer$after_code",
  ("grestore\n"x$need_grestore),
  ("cleartomark restore\n"x$is_restored),
  "%%EOF\n";
error "closing gs filter: $? $!" unless close OUT;

# --- PNM scaling routines for --antialias=scale3*

sub pnm_gettok($) {
  my ($fh,$S,$C)=($_[0],"");
  while (1) {
    die "unexpected EOF" if !defined($C=getc($fh));
    if ($C eq'#') { <$fh> } # ignore rest of line
    elsif ($C=~y@ \n\r\t@@) { last if length($S)!=0 }
    else { $S.=$C }
  }
  $S
}

my @div9=(0,0,0,0,0,map { $_, $_, $_, $_, $_, $_, $_, $_, $_ } 1..255, 255);

#** @param $_[0] length always divisible by 3
sub p5_avg_lines($$$$) {
  use integer;
  my $len=length($_[0]);
  my $olen=$len/3;
  while ($len>0) {
    vec($_[3],--$olen,8)=$div9[
      vec($_[0],$len-1,8)+vec($_[0],$len-2,8)+vec($_[0],$len-3,8)+
      vec($_[1],$len-1,8)+vec($_[1],$len-2,8)+vec($_[1],$len-3,8)+
      vec($_[2],$len-1,8)+vec($_[2],$len-2,8)+vec($_[2],$len-3,8)];
    $len-=3;
  }
}

#** @param $_[0] length always divisible by 9
sub p6_avg_lines($$$$) {
  # Imp: why is it lighter than: convert -scale '33.3333%' a3.pbm a3r.pgm
  use integer;
  my $len=length($_[0]);
  my $olen=$len/3;
  while ($len>0) {
    vec($_[3],--$olen,8)=$div9[
      vec($_[0],$len-1,8)+vec($_[0],$len-4,8)+vec($_[0],$len-7,8)+
      vec($_[1],$len-1,8)+vec($_[1],$len-4,8)+vec($_[1],$len-7,8)+
      vec($_[2],$len-1,8)+vec($_[2],$len-4,8)+vec($_[2],$len-7,8)];
    $len-=6 if 0==--$len%3;
  }
}

# ---

if (!$ll_zero and ($FileFormat eq 'PDF' or $FileFormat eq 'PDF1')) { # correct /MediaBox if not (0,0)-based
  ### ****pts**** remove incorrect /MediaBox produced by gs
  my $tfn=temp_prefix()."p.tgs";
  error "temp open $tfn: $!" unless open F, "> $tfn";
  $tmpfiles{$tfn}=1;
  # vvv Dat: doesn't work with gs 8.53: Error: /undefined in readxrefentry
  die unless print F "% this is temporary gs command file created by $program".'
  GS_PDF_ProcSet begin
  pdfdict begin
  FN (r) file pdfopen begin
  % vvv keep file offsets, because `pdffindpageref` overrides it with contents
  /OFT Objects 0 get dup length array copy def
  % vvv Dat: the generation number is assumed to be 0
  % vvv Dat: modifies Objects[0]
  1 pdffindpageref 0 get
  Objects 0 OFT put
  %===
  %print_xref
  { readxrefentry } stopped { Objects exch lget } if
  ===
  currentdict pdfclose end end end
  ';
  die unless close F;

  my $gs2="$GS -dNODISPLAY -dBATCH -sFN=".fnq(fix_pipe_out(undef))." -q ".fnq($tfn);
  debug "Ghostscript dup pipe: $gs2";
  my $offset=`$gs2`;
  #die $offset;
  chomp $offset;
  temp_unlink $tfn;
  if ($offset=~/\A\d+\Z(?!\n)/) {
    # Dat: now $offsets is a file position containing our /Page object
    die unless open F, "+< ".fix_pipe_out(undef);
    die unless binmode F;
    die unless seek F, $offset+=0, 0;
    my $pageobj;
    die unless 32<read F, $pageobj, 4096;
    if ($::opt_keepoldmediabox) {
      if ($pageobj=~m@\A(.*?/Type\s*/Page\b.*?/MediaBox\s*\[0 0 [^\]]*\].*?)((?:/CropBox\s*\[[^\]]+\]\s*)?/MediaBox\s*\[[^\]]+\])@s) {
	substr($pageobj, length($1), length($2))=" "x length($2);
	# ^^^ overwrite first buggy /MediaBox definition with spaces
	die unless seek F, $offset, 0;
	die unless print F $pageobj;
	debug "new /MediaBox destroyed.";
      } else {
	debug "warning: double /MediaBox not found at $offset";
      }
    } else {
      if ($pageobj=~m@\A(.*?/Type\s*/Page\b.*?)(/MediaBox\s*\[0 0 [^\]]*\]).*?/MediaBox\b@s) {
	substr($pageobj, length($1), length($2))=" "x length($2);
	# ^^^ overwrite first buggy /MediaBox definition with spaces
	die unless seek F, $offset, 0;
	die unless print F $pageobj;
	debug "old /MediaBox destroyed.";
      } else {
	debug "warning: double /MediaBox not found at $offset";
      }
    }
    die unless close F;
  } else {
    debug "warning: gs failed to locate double /MediaBox";
  }
}
if ($FileFormat eq 'PCL5' and $do_fix_tumble) {
  # stupid Ghostscript ignores /Tumble true with -sDEVICE=ljet4
  #  2 dict dup /Duplex true  /Tumble false put setpagedevice % long     HP PCL5e "\033&l1S"
  #  2 dict dup /Duplex true  /Tumble true  put setpagedevice % short    HP PCL5e "\033&l2S"
  #  2 dict dup /Duplex false /Tumble false put setpagedevice % simplex  HP PCL5e "\033&l0S"
  # HP PCL5e gs header "\033E\033&l2A\033&l1S\033&l0o0l0E\033&l-180u36Z"
  die unless open F, "+< ".fix_pipe_out(undef);
  die unless binmode F;
  my $pageobj;
  die unless 32<read F, $pageobj, 4096;
  if ($pageobj=~s@\033&l1S.*@\033&l2S@s) {
    die unless seek F, 0, 0;
    die unless print F $pageobj;
    debug "fixed /Tumble true (short).";
  } elsif ($pageobj=~m@\033&l0S@) {
    debug "no need to fix to /Tumble.";
  } else {
    debug "warning: /Duplex /Tumble settings not found"
  }
  die unless close F;
}
if ($do_scale3_pnm) {
  # Imp: scale down the file in place, ovoid early overwrite
  # Imp: possibly call an external C program that is faster
  debug "Scaling down PNM by 3x3";
  die unless open F, "> $scale3_pnm_fn";
  die unless binmode F;
  die unless open FIN, "< ".fix_pipe_out(undef);
  my $hd;
  die "PNMraw expected\n" if read(FIN,$hd,2)!=2 or $hd!~/\AP[456]/;
  my $wd=pnm_gettok(*FIN); die "width expected\n"  if $wd!~/\A(\d+)\Z(?!\n)/;
  my $wd3=$hd eq 'P4' ? ($wd+7)>>3 : $hd eq 'P5' ? $wd : $wd*3; # bw/grayscale/RGB
  my $ht=pnm_gettok(*FIN); die "height expected\n" if $ht!~/\A(\d+)\Z(?!\n)/;
  if ($hd ne 'P4') {
    my $mx=pnm_gettok(*FIN); die "max==255 expected, got: $mx\n" if $mx ne '255';
  }
  $wd+=0; $ht+=0;
  { use integer;
    my $phd=($hd eq 'P5' and $FileFormat eq 'PBM') ? "P4 ".(($wd+2)/3)." ".(($ht+2)/3)."\n"
      : ($hd eq 'P6' ? 'P6' : 'P5')."\n# reduced-3x3\n".
      (($wd+2)/3)." ".(($ht+2)/3)." 255\n";
    die if !print F $phd;
  }

  my($l1,$l2,$l3);
  my $ret="";
  if ($hd eq 'P4') {
    while ($ht>0) {
      die "full row expected1\n" if $wd3!=read FIN, $l1, $wd3;
      if (--$ht==0) { $l2=$l1 }
      else {
	die "full row expected2\n" if $wd3!=read FIN, $l2, $wd3;
	if (--$ht==0) { $l3=$l2 } # Imp: adjust 2/3 weight
	else { $ht--;
	  die "full row expected3\n" if $wd3!=read FIN, $l3, $wd3;
	}
      }
      $l1=unpack("B$wd",$l1); $l1=~y@10@\000\377@;  $l1.=substr($l1,-3+length($l1)%3) if length($l1)%3!=0;
      $l2=unpack("B$wd",$l2); $l2=~y@10@\000\377@;  $l2.=substr($l2,-3+length($l2)%3) if length($l2)%3!=0;
      $l3=unpack("B$wd",$l3); $l3=~y@10@\000\377@;  $l3.=substr($l3,-3+length($l3)%3) if length($l3)%3!=0;
      p5_avg_lines($l1, $l2, $l3, $ret);
      die if !print F $ret;
    }
  } elsif ($hd eq 'P5') {
    while ($ht>0) {
      die "full row expected1\n" if $wd3!=read FIN, $l1, $wd3;
      $l1.=substr($l1,-3+length($l1)%3) if length($l1)%3!=0;
      if (--$ht==0) { $l2=$l1 }
      else {
	die "full row expected2\n" if $wd3!=read FIN, $l2, $wd3;
	$l2.=substr($l2,-3+length($l2)%3) if length($l2)%3!=0;
	if (--$ht==0) { $l3=$l2 } # Imp: adjust 2/3 weight
	else { $ht--;
	  die "full row expected3\n" if $wd3!=read FIN, $l3, $wd3;
	  $l3.=substr($l3,-3+length($l3)%3) if length($l3)%3!=0;
	}
      }
      p5_avg_lines($l1, $l2, $l3, $ret);
      if ($FileFormat eq 'PBM') {
        my $I=length($ret);
	while ($I--) { vec($ret,$I,8)=vec($ret,$I,8)<$::opt_threshold } # [\0\1]
	# ^^^ grayscale>=$::opt_threshold will be white
        $ret=pack"B".length($ret),$ret;
      }
      die if !print F $ret;
    }
  } elsif ($hd eq 'P6') {
    while ($ht>0) {
      die "full row expected1\n" if $wd3!=read FIN, $l1, $wd3;
      $l1.=substr($l1,-9+length($l1)%9) if length($l1)%9!=0;
      if (--$ht==0) { $l2=$l1 }
      else {
	die "full row expected2\n" if $wd3!=read FIN, $l2, $wd3;
	$l2.=substr($l2,-9+length($l2)%9) if length($l2)%9!=0;
	if (--$ht==0) { $l3=$l2 } # Imp: adjust 2/3 weight
	else { $ht--;
	  die "full row expected3\n" if $wd3!=read FIN, $l3, $wd3;
	  $l3.=substr($l3,-9+length($l3)%9) if length($l3)%9!=0;
	}
      }
      p6_avg_lines($l1, $l2, $l3, $ret);
      die if !print F $ret;
    }
  }
  die unless close F;
  temp_unlink $temp_out_fn;
  undef $temp_out_fn;
}
if (@pnm2sampled_cmd) { # $scale3_pnm_fn -> $OutputFilename
  do_system @pnm2sampled_cmd; # Dat: uses @extra -- really share that?
  temp_unlink $scale3_pnm_fn;
} else { # BUGFIX for `a2ping.pl -v --antialias=no negyzet.eps negyzet.png' at Wed Jul 20 21:34:29 CEST 2005
  fix_close_out();
}
undef $unlink_OutputFilename;
if ($OutputFilename eq '-') {
  debug "Done OK, stdout is $FileFormat"
} elsif (-f $OutputFilename) {
  debug "Done OK, created $FileFormat file $OutputFilename (".(-s _)." bytes)";
} else {
  error "missing $OutputFilename"
}
just::end __END__

Dat: `=item * foo' is wrong, puts big space between `*' and `foo'

=encoding latin1

=begin man

.ds pts-dev \*[.T]
.do if '\*[.T]'ascii'  .ds pts-dev tty
.do if '\*[.T]'ascii8' .ds pts-dev tty
.do if '\*[.T]'latin1' .ds pts-dev tty
.do if '\*[.T]'nippon' .ds pts-dev tty
.do if '\*[.T]'utf8'   .ds pts-dev tty
.do if '\*[.T]'cp1047' .ds pts-dev tty
.do if '\*[pts-dev]'tty' \{\
.ll 79
.pl 33333v
.nr IN 2n
.\}
.ad n

=end man

=head1 NAME

a2ping.pl -- convert between PS, EPS and PDF and other page description
formats

=head1 SYNOPSIS

Z<> B<a2ping.pl> [B<-->]B<help>
 B<a2ping.pl> [B<-->]B<doc>
 B<a2ping.pl> [I<options>] <I<inputfile>> [[I<outformat>:] I<outputfile>]

=head1 DESCRIPTION

B<a2ping> is a UNIX command line utility written in Perl that
converts many raster image and vector graphics formats to EPS or PDF and
other page description formats. Accepted input file formats are: PS
(PostScript), EPS, PDF, PNG, JPEG, TIFF, PNM, BMP, GIF, LBM, XPM, PCX,
TGA. Accepted output formats are: EPS, PCL5, PDF, PDF1, PBM, PGM, PPM,
PS, markedEPS, markedPS, PNG, XWD, BMP, TIFF, JPEG, GIF, XPM.
B<a2ping> delegates the low-level work to
Ghostscript (GS), B<pdftops> and B<sam2p>. B<a2ping> fixes many glitches
during the EPS to EPS conversion, so its output is often more compatible
and better embeddable than its input.

Without the C<--below> option, it is guarenteed to start at the 0,0
coordinate. C<--below>, C<--hires> and C<-v> are recommended options.

The page size is set exactly corresponding to the BoundingBox.
This means that when Ghostscript renders it, the result needs no
cropping, and the PDF MediaBox is correct.

If the bounding box is not right, of course, you have problems. If you
feed crap in, you get crap. But you can supply the
B<--bboxfrom=compute-gs> option to make GS recompute the bounding box.

The name of the input file doesn't matter -- B<a2ping> detects the file
format based on the first few bytes of the file. The name of the output
file matters if I<outformat> is missing from the command line: then the
extension of the output file determines the FileFormat (I<outformat>).

=head1 EXTERNAL PROGRAMS

The internal file format of B<a2ping.pl> is PS/EPS. Everything read is
first converted to PS or EPS, then processed by B<a2ping.pl>, then
converted to the output format.

To analyse the bounding box and other properties of non-EPS PS files
(and EPS files with option B<--bboxfrom> other than B<=guess>), GS is
used. Converting PS to EPS involves this analysis.

To write PDF files, GS is used.

To read PDF files, B<pdftops> from the B<xpdf> package is used.

Sampled input formats are PNG, JPEG, TIFF, PNM, BMP, GIF, LBM, XPM, PCX
and TGA. To read sampled input formats, B<sam2p> is used. B<sam2p> is
a raster image converter written in C++ by the author of B<a2ping.pl>.

Extra output formats are PNG, XWD, BMP, TIFF, JPEG, GIF and XPM. To
write extra output formats, B<sam2p> and GS are used.

PNM output formats are PGM, PGM and PPM. To write PNM output formats, GS
is used.

=head1 OPTIONS

=head2 General Options

=over 2

=item B<-h>, B<--help>

Show a summary of the usage

=item B<--doc>

Show the man page

=item B<-v>, B<--(no)verbose>

Show progress and debug messages (default: no)

=back

=head2 Options for the Bounding box

=over 2

=item B<--(no)hires>

Use HiResBoundingBox in the input file, if present (default: yes)

=item B<--(no)exact>

Use ExactBoundingBox in the input file, if present (default: no)

=item B<--(no)keepoldmediabox>

keep only old, [0 0]-based MediaBox in PDF (default: no)

=item B<--bboxfrom=>I<adsc|compute-gs|guess|pagesize>

Method for determining the BoundingBox  (default: guess)

=item B<--(no)below>

Allow page content below and left of the origin (default: no)

=back

=head2 Options for graphics and fonts

=over 2

=item B<--(no)compress>

use compression                   (default: best)

=item B<--(no)antialias>

render shades at outlines.  Possible values: (=I<scale3yes> =I<no> =I<yes>)  (default: scale3no)

=item B<--(no)lossy>

allow lossy image filters (EPS->PDF) (default: yes)

=item B<--papersize=>I<unchanged|force-unknown|600bpx5cm>

(default: default) (bp)

=item B<--threshold=>

min color for 1 in 8->1 bit conv  (default: 128)

=back

=head2 Options for debugging and changing internals

=over 2

=item B<--(no)tmpunlink>

Unlink temporary files (default: yes).  Use B<--notmpunklink> if you
want to inspect intermediate files.

=item B<--gs-cmd=>I<path>

path to Ghostscript program (default: gs or gswin32c)

=item B<--gs-ccmd=>I<path>

path to Ghostscript for BoundingBox calculation (default: gs or gswin32c)

=item B<--gsextra=>I<GS_ARGS>

Pass extra arguments to gs

=item B<--extra=>

Pass extra arguments to external program (i.e pdftops)

=back

=head1 TIPS AND TRICKS

=over 2

=item *

If your EPS contains a wrong bounding box, you can fix it by running
C<a2ping.pl -v --bboxfrom=compute-gs thefile.eps -->

=item *

You can specify B<-> as I<inputfile> to get stdin and as I<outputfile>
to get stdout. This works even for PDF files (which must be seekable),
because B<a2ping> copies them to a temporary file automatically.

=item *

If I<inputfile> and I<outputfile> are the same, B<a2ping> copies the
I<inputfile> to a temporary location first. However, this usage is
recommended only if there is a backup of the file to be restored in case
B<a2ping> doesn't produce the desired result.

=item *

If you specify B<--> as I<outputfile>, it will be the same as I<inputfile>.

=item *

B<a2ping> respects B<--Duplex> for FileFormat PCL5, even though GS doesn't.

=item *

If you have an incompatible PS that GS can read but your printer cannot print,
just run C<a2ping.pl foo.ps PDF: - | a2ping.pl - PS: foo.ps>

=item *

If you have a PS coming from Win32 (often with extension C<.prn>), run
it through B<a2ping>. It will remove the resolution changes and the
progress text printed to the terminal (which confuses gv(1) and makes
some filters in the print queue emit incorrect output).

=item *

B<a2ping> does antialiasing (B<--antialias=scale3no>) of glyphs and
curves when emitting a sampled image (FileFormats such as PGM and PPM).
This improves readability of the glyphs. B<=yes> instructs GS to do
internal antialiasing, but it usually doesn't improve much. B<=scale3no>
turns off GS internal antialiasing, but makes it render everything 3x3
as big, and then scales it back down. B<=scale3no> turns on both 3x3
scaling and GS internal antialiasing, which results in thicker lines and
worse quality in general.

=item *

When creating a PBM file, antialiasing usually doesn't improve the
quality, so it is switched off by default. But if you set
B<--antialias=scale3no> or B<--antialias=scale3yes>, GS will render a PGM file,
and the value of B<--threshold> determines the minimum intensity for white in
the final PBM.

=item *

If you need a bigger sampled output file, specify a larger
B<--Resolution>. The default is B<--Resolution=72>. If your sampled output file
is going to be really big, you should specify B<--AntiAlias=yes> instead of
the default B<--AntiAlias=scale3no> to speed up conversion.

=item *

To make sure fonts are included in a PDF file generated from eps, use
B<--gsextra='-dEmbedAllFonts=true -dPDFSETTINGS=/printer'>.

=back


=head1 MISC

=over 2

=item *

Doesn't depend on the filename or extension of the input file.

=item *

Conversion from EPS to PDF: fixes glitches etc., calls gs
-sDEVICE=pdfwrite

=item *

Conversion from EPS to EPS: fixes various glitches, moves (llx,lly) to
(0,0), removes binary junk from the beginning of the EPS etc.

=item *

Conversion from PDF to PDF: keeps the file intact

=item *

Conversion from PDF to EPS: calls pdftops -eps (of the xpdf package)

=item *

Conversion from PS to EPS: keeps 1st page only, removes setpagedevice etc.

=back

=head1 AUTHORS

The author of B<a2ping> is Péter Szabó <F<pts@fazekas.hu>>.

B<a2ping> is inspired by and historically based on the B<epstopdf> Perl
script modified by Thomas Esser, Sept. 1998, but his modifications have
been removed from B<a2ping>, and also B<a2ping> and B<epstopdf> do not
share common code anymore.  B<epstopdf> is written by Sebastian Rahtz,
for Elsevier Science. B<epstopdf> contained extra tricks from Hans Hagen's
texutil.

B<a2ping> contains contributions from several people, see the file
F<HISTORY.txt> for details. Thank you all for contributing!