1if not modules then modules = { } end modules ['m-escrito'] = {
2 version = 1.001,
3 comment = "companion to m-escrito.mkiv",
4 author = "Taco Hoekwater (BitText) and Hans Hagen (PRAGMA-ADE)",
5 license = "see below and context related readme files"
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
69local type, unpack, tonumber, tostring, next = type, unpack, tonumber, tostring, next
70
71local format = string.format
72local gmatch = string.gmatch
73local match = string.match
74local sub = string.sub
75local char = string.char
76local byte = string.byte
77
78local insert = table.insert
79local remove = table.remove
80local concat = table.concat
81local reverse = table.reverse
82
83local abs = math.abs
84local ceil = math.ceil
85local floor = math.floor
86local sin = math.sin
87local cos = math.cos
88local rad = math.rad
89local sqrt = math.sqrt
90local atan2 = math.atan2
91local tan = math.tan
92local deg = math.deg
93local pow = math.pow
94local log = math.log
95local log10 = math.log10
96local random = math.random
97local setranseed = math.randomseed
98
99local bitand = bit32.band
100local bitor = bit32.bor
101local bitxor = bit32.bxor
102local bitrshift = bit32.rshift
103local bitlshift = bit32.lshift
104
105local lpegmatch = lpeg.match
106local Ct, Cc, Cs, Cp, C, R, S, P, V = lpeg.Ct, lpeg.Cc, lpeg.Cs, lpeg.Cp, lpeg.C, lpeg.R, lpeg.S, lpeg.P, lpeg.V
107
108local formatters = string.formatters
109local setmetatableindex = table.setmetatableindex
110
111
112
113
114
115
116escrito = { }
117
118
119local initializers = { }
120local devices = { }
121local specials
122
123local DEBUG = false
124local INITDEBUG = false
125local MAX_INT = 0x7FFFFFFF
126
127initializers[#initializers+1] = function(reset)
128 if reset then
129 specials = nil
130 else
131 specials = { }
132 end
133end
134
135local devicename
136local device
137
138
139
140
141
142
143
144
145
146
147
148local VM
149
150initializers[#initializers+1] = function()
151 VM = { }
152end
153
154local directvm = false
155
156local add_VM, get_VM
157
158if directvm then
159
160 add_VM = function(a)
161 return a
162 end
163 get_VM = function(i)
164 return i
165 end
166
167else
168
169 add_VM = function(a)
170 local n = #VM + 1
171 VM[n] = a
172 return n
173 end
174
175 get_VM = function(i)
176 return VM[i]
177 end
178
179end
180
181
182
183local execstack
184local execstackptr
185local do_exec
186local next_object
187local stopped
188
189initializers[#initializers+1] = function()
190 execstack = { }
191 execstackptr = 0
192 stopped = false
193end
194
195local function pop_execstack()
196 if execstackptr > 0 then
197 local value = execstack[execstackptr]
198 execstackptr = execstackptr - 1
199 return value
200 else
201 return nil
202 end
203end
204
205local function push_execstack(v)
206 execstackptr = execstackptr + 1
207 execstack[execstackptr] = v
208end
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
263local opstack
264local opstackptr
265
266local b_true = { 'boolean', 'unlimited', 'literal', true }
267local b_false = { 'boolean', 'unlimited', 'literal', false }
268
269initializers[#initializers+1] = function()
270 opstack = { }
271 opstackptr = 0
272end
273
274local function pop_opstack()
275 if opstackptr > 0 then
276 local value = opstack[opstackptr]
277 opstackptr = opstackptr - 1
278 return value
279 else
280 return nil
281 end
282end
283
284local function push_opstack(v)
285 opstackptr = opstackptr + 1
286 opstack[opstackptr] = v
287end
288
289local function check_opstack(n)
290 return opstackptr >= n
291end
292
293local function get_opstack()
294 if opstackptr > 0 then
295 return opstack[opstackptr]
296 else
297 return nil
298 end
299end
300
301
302
303local function copy_opstack()
304 local t = { }
305 for n=1,opstackptr do
306 local sn = opstack[n]
307 t[n] = { unpack(sn) }
308 end
309 return t
310end
311
312local function set_opstack(new)
313 opstackptr = #new
314 opstack = new
315end
316
317
318
319local dictstack
320local dictstackptr
321
322initializers[#initializers+1] = function()
323 dictstack = { }
324 dictstackptr = 0
325end
326
327
328
329local function lookup(name)
330 for n=dictstackptr,1,-1 do
331 local found = get_VM(dictstack[n])
332 if found then
333 local dict = found.dict
334 if dict then
335 local d = dict[name]
336 if d then
337 return d, n
338 end
339 end
340 end
341 end
342 return nil
343end
344
345
346
347
348
349
350
351
352local gsstate
353
354initializers[#initializers+1] = function(reset)
355 if reset then
356 gsstate = nil
357 else
358 gsstate = {
359 matrix = { 1, 0, 0, 1, 0, 0 },
360 color = {
361 gray = 0,
362 hsb = { },
363 rgb = { },
364 cmyk = { },
365 type = "gray"
366 },
367 position = { },
368 path = { },
369 clip = { },
370 font = nil,
371 linewidth = 1,
372 linecap = 0,
373 linejoin = 0,
374 screen = nil,
375 transfer = nil,
376 flatness = 0,
377 miterlimit = 10,
378 dashpattern = { },
379 dashoffset = 0,
380 }
381 end
382end
383
384local function copy_gsstate()
385 local old = gsstate
386 local position = old.position
387 local matrix = old.matrix
388 local color = old.color
389 local rgb = color.rgb
390 local cmyk = color.cmyk
391 local hsb = color.hsb
392 return {
393 matrix = { matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6] },
394 color = {
395 type = color.type,
396 gray = color.gray,
397 hsb = { hsb[1], hsb[2], hsb[3] },
398 rgb = { rgb[1], rgb[2], rgb[3] },
399 cmyk = { cmyk[1], cmyk[2], cmyk[3], cmyk[4] },
400 },
401 position = { position[1], position[2] },
402 path = { unpack (old.path) },
403 clip = { unpack (old.clip) },
404 font = old.font,
405 linewidth = old.linewidth,
406 linecap = old.linecap,
407 linejoin = old.linejoin,
408 screen = old.screen,
409 transfer = nil,
410 flatness = old.flatness,
411 miterlimit = old.miterlimit,
412 dashpattern = { },
413 dashoffset = 0,
414 }
415end
416
417
418
419
420
421local gsstack
422local gsstackptr
423
424initializers[#initializers+1] = function(reset)
425 if reset then
426 gsstack = nil
427 gsstackptr = nil
428 else
429 gsstack = { }
430 gsstackptr = 0
431 end
432end
433
434local function push_gsstack(v)
435 gsstackptr = gsstackptr + 1
436 gsstack[gsstackptr] = v
437end
438
439local function pop_gsstack()
440 if gsstackptr > 0 then
441 local v = gsstack[gsstackptr]
442 gsstackptr = gsstackptr - 1
443 return v
444 end
445end
446
447
448
449local currentpage
450
451initializers[#initializers+1] = function(reset)
452 if reset then
453 currentpage = nil
454 else
455 currentpage = { }
456 end
457end
458
459
460
461
462
463
464local errordict
465local dicterror
466
467
468
469local function lookup_error(name)
470 local dict = get_VM(errordict).dict
471 return dict and dict[name]
472end
473
474
475
476local report = logs.reporter("escrito")
477
478local function ps_error(a)
479
480 return false, a
481end
482
483
484
485
486
487
488
489
490
491
492
493
494
495local operators = { }
496
497
498
499
500
501function operators.pop()
502 local a = pop_opstack()
503 if not a then
504 return ps_error('stackunderflow')
505 end
506 return true
507end
508
509function operators.exch()
510 if opstackptr < 2 then
511 return ps_error('stackunderflow')
512 end
513 local prv = opstackptr - 1
514 opstack[opstackptr], opstack[prv] = opstack[prv], opstack[opstackptr]
515 return true
516end
517
518function operators.dup()
519 if opstackptr < 1 then
520 return ps_error('stackunderflow')
521 end
522 local nxt = opstackptr + 1
523 opstack[nxt] = opstack[opstackptr]
524 opstackptr = nxt
525 return true
526end
527
528function operators.copy()
529 local a = pop_opstack()
530 if not a then
531 return ps_error('stackunderflow')
532 end
533 local ta = a[1]
534 if ta == 'integer' then
535 local va = a[4]
536 if va < 0 then
537 return ps_error('typecheck')
538 end
539 local thestack = opstackptr
540 if va > thestack then
541 return ps_error('stackunderflow')
542 end
543
544 local n = thestack - va + 1
545 while n <= thestack do
546 local b = opstack[n]
547 local tb = b[1]
548 if tb == 'array' or tb == 'string' or tb == 'dict' or tb == 'font' then
549 b = { tb, b[2], b[3], add_VM(get_VM(b[4])), b[5], b[6], b[7] }
550 end
551 push_opstack(b)
552 n = n + 1
553 end
554 elseif ta == 'dict' then
555 local b = a
556 local a = pop_opstack()
557 if not a then
558 return ps_error('stackunderflow')
559 end
560 if a[1] ~= 'dict' then
561 return ps_error('typecheck')
562 end
563 local thedict = get_VM(b[4])
564 local tobecopied = get_VM(a[4])
565 if thedict.maxsize < tobecopied.size then
566 return ps_error('rangecheck')
567 end
568 if thedict.size ~= 0 then
569 return ps_error('typecheck')
570 end
571 local access = thedict.access
572 if access == 'read-only' or access == 'noaccess' then
573 return ps_error('invalidaccess')
574 end
575 local dict = { }
576 for k, v in next, tobecopied.dict do
577 dict[k] = v
578 end
579 thedict.access = tobecopied.access
580 thedict.size = tobecopied.size
581 thedict.dict = dict
582 b = { b[1], b[2], b[3], add_VM(thedict) }
583 push_opstack(b)
584 elseif ta == 'array' then
585 local b = a
586 local a = pop_opstack()
587 if not a then
588 return ps_error('stackunderflow')
589 end
590 if a[1] ~= 'array' then
591 return ps_error('typecheck')
592 end
593 if b[6] < a[6] then
594 return ps_error('rangecheck')
595 end
596 local access = b[2]
597 if access == 'read-only' or access == 'noaccess' then
598 return ps_error('invalidaccess')
599 end
600 local array = { }
601 local thearray = get_VM(b[4])
602 local tobecopied = get_VM(a[4])
603 for k, v in next, tobecopied do
604 array[k] = v
605 end
606 b = { b[1], b[2], b[3], add_VM(array), a[5], a[6], a[7] }
607 push_opstack(b)
608 elseif ta == 'string' then
609 local b = a
610 local a = pop_opstack()
611 if not a then
612 return ps_error('stackunderflow')
613 end
614 if a[1] ~= 'string' then
615 return ps_error('typecheck')
616 end
617 if b[6] < a[6] then
618 return ps_error('rangecheck')
619 end
620 local access = b[2]
621 if access == 'read-only' or access == 'noaccess' then
622 return ps_error('invalidaccess')
623 end
624 local thestring = get_VM(b[4])
625 local repl = get_VM(a[4])
626 VM[b[4]] = repl .. sub(thestring,#repl+1,-1)
627 b = { b[1], b[2], b[3], add_VM(repl), a[5], b[6] }
628 push_opstack(b)
629 else
630 return ps_error('typecheck')
631 end
632 return true
633end
634
635function operators.index()
636 local a = pop_opstack()
637 if not a then
638 return ps_error('stackunderflow')
639 end
640 local ta = a[1]
641 if not ta == 'integer' then
642 return ps_error('typecheck')
643 end
644 local n = a[4]
645 if n < 0 then
646 return ps_error('rangecheck')
647 end
648 if n >= opstackptr then
649 return ps_error('stackunderflow')
650 end
651 push_opstack(opstack[opstackptr-n])
652 return true
653end
654
655function operators.roll()
656 local b = pop_opstack()
657 local a = pop_opstack()
658 if not a then
659 return ps_error('stackunderflow')
660 end
661 if b[1] ~= 'integer' then
662 return ps_error('typecheck')
663 end
664 if a[1] ~= 'integer' then
665 return ps_error('typecheck')
666 end
667 local stackcount = a[4]
668 if stackcount < 0 then
669 return ps_error('rangecheck')
670 end
671 if stackcount > opstackptr then
672 return ps_error('stackunderflow')
673 end
674 local rollcount = b[4]
675 if rollcount == 0 then
676 return true
677 end
678 if rollcount > 0 then
679
680 while rollcount > 0 do
681 local oldtop = opstack[opstackptr]
682 local n = 0
683 while n < stackcount do
684 opstack[opstackptr-n] = opstack[opstackptr-n-1]
685 n = n + 1
686 end
687 opstack[opstackptr-(stackcount-1)] = oldtop
688 rollcount = rollcount - 1
689 end
690 else
691
692 while rollcount < 0 do
693 local oldbot = opstack[opstackptr-stackcount+1]
694 local n = stackcount - 1
695 while n > 0 do
696 opstack[opstackptr-n] = opstack[opstackptr-n+1]
697 n = n - 1
698 end
699 opstack[opstackptr] = oldbot
700 rollcount = rollcount + 1
701 end
702 end
703 return true
704end
705
706function operators.clear()
707 opstack = { }
708 opstackptr = 0
709 return true
710end
711
712function operators.count()
713 push_opstack { 'integer', 'unlimited', 'literal', opstackptr }
714 return true
715end
716
717function operators.mark()
718 push_opstack { 'mark', 'unlimited', 'literal', null }
719end
720
721operators.beginarray = operators.mark
722
723function operators.cleartomark()
724 while opstackptr > 0 do
725 local val = pop_opstack()
726 if not val then
727 return ps_error('unmatchedmark')
728 end
729 if val[1] == 'mark' then
730 return true
731 end
732 end
733 return ps_error('unmatchedmark')
734end
735
736function operators.counttomark()
737 local v = 0
738 for n=opstackptr,1,-1 do
739 if opstack[n][1] == 'mark' then
740 push_opstack { 'integer', 'unlimited', 'literal', v }
741 return true
742 end
743 v = v + 1
744 end
745 return ps_error('unmatchedmark')
746end
747
748
749
750
751
752
753function operators.add()
754 local b = pop_opstack()
755 local a = pop_opstack()
756 if not a then
757 return ps_error('stackunderflow')
758 end
759 local ta, tb = a[1], b[1]
760 if not (tb == 'real' or tb == 'integer') then
761 return ps_error('typecheck')
762 end
763 if not (ta == 'real' or ta == 'integer') then
764 return ps_error('typecheck')
765 end
766 local c = a[4] + b[4]
767 push_opstack {
768 (ta == 'real' or tb == 'real' or c > MAX_INT) and "real" or "integer",
769 'unlimited', 'literal', c
770 }
771 return true
772end
773
774function operators.sub()
775 local b = pop_opstack()
776 local a = pop_opstack()
777 if not a then
778 return ps_error('stackunderflow')
779 end
780 local ta, tb = a[1], b[1]
781 if not (tb == 'real' or tb == 'integer') then
782 return ps_error('typecheck')
783 end
784 if not (ta == 'real' or ta == 'integer') then
785 return ps_error('typecheck')
786 end
787 local c = a[4] - b[4]
788 push_opstack {
789 (ta == 'real' or tb == 'real' or c > MAX_INT) and "real" or "integer",
790 'unlimited', 'literal', c
791 }
792 return true
793end
794
795function operators.div()
796 local b = pop_opstack()
797 local a = pop_opstack()
798 if not a then
799 return ps_error('stackunderflow')
800 end
801 local ta, tb = a[1], b[1]
802 if not (tb == 'real' or tb == 'integer') then
803 return ps_error('typecheck')
804 end
805 if not (ta == 'real' or ta == 'integer') then
806 return ps_error('typecheck')
807 end
808 local va, vb = a[4], b[4]
809 if vb == 0 then
810 return ps_error('undefinedresult')
811 end
812 push_opstack { 'real', 'unlimited', 'literal', va / vb }
813 return true
814end
815
816function operators.idiv()
817 local b = pop_opstack()
818 local a = pop_opstack()
819 if not a then
820 return ps_error('stackunderflow')
821 end
822 local ta, tb = a[1], b[1]
823 if tb ~= 'integer' then
824 return ps_error('typecheck')
825 end
826 if ta ~= 'integer' then
827 return ps_error('typecheck')
828 end
829 local va, vb = a[4], b[4]
830 if vb == 0 then
831 return ps_error('undefinedresult')
832 end
833 push_opstack { 'integer', 'unlimited', 'literal', floor(va / vb) }
834 return true
835end
836
837function operators.mod()
838 local b = pop_opstack()
839 local a = pop_opstack()
840 if not a then
841 return ps_error('stackunderflow')
842 end
843 local ta, tb = a[1], b[1]
844 if tb ~= 'integer' then
845 return ps_error('typecheck')
846 end
847 if ta ~= 'integer' then
848 return ps_error('typecheck')
849 end
850 local va, vb = a[4], b[4]
851 if vb == 0 then
852 return ps_error('undefinedresult')
853 end
854 local neg = false
855 local v
856 if va < 0 then
857 v = -va
858 neg = true
859 else
860 v = va
861 end
862 local c = v % abs(vb)
863 if neg then
864 c = -c
865 end
866 push_opstack { 'integer', 'unlimited', 'literal', c }
867 return true
868end
869
870function operators.mul()
871 local b = pop_opstack()
872 local a = pop_opstack()
873 if not a then
874 return ps_error('stackunderflow')
875 end
876 local ta, tb = a[1], b[1]
877 if not (tb == 'real' or tb == 'integer') then
878 return ps_error('typecheck')
879 end
880 if not (ta == 'real' or ta == 'integer') then
881 return ps_error('typecheck')
882 end
883 local c = a[4] * b[4]
884 push_opstack {
885 (ta == 'real' or tb == 'real' or abs(c) > MAX_INT) and 'real' or 'integer',
886 'unlimited', 'literal', c
887 }
888 return true
889end
890
891function operators.abs()
892 local a = pop_opstack()
893 if not a then
894 return ps_error('stackunderflow')
895 end
896 local ta = a[1]
897 if not (ta == 'real' or ta == 'integer') then
898 return ps_error('typecheck')
899 end
900 local v = a[4]
901 local c = abs(v)
902 push_opstack {
903 (ta == 'real' or v == -(MAX_INT+1)) and 'real' or 'integer',
904 'unlimited', 'literal', c
905 }
906 return true
907end
908
909function operators.neg()
910 local a = pop_opstack()
911 if not a then
912 return ps_error('stackunderflow')
913 end
914 local ta = a[1]
915 if not (ta == 'real' or ta == 'integer') then
916 return ps_error('typecheck')
917 end
918 local v = a[4]
919 push_opstack {
920 (ta == 'real' or v == -(MAX_INT+1)) and 'real' or 'integer',
921 'unlimited', 'literal', -v
922 }
923 return true
924end
925
926function operators.ceiling()
927 local a = pop_opstack()
928 if not a then
929 return ps_error('stackunderflow')
930 end
931 local ta = a[1]
932 if not (ta == 'real' or ta == 'integer') then
933 return ps_error('typecheck')
934 end
935 local c = ceil(a[4])
936 push_opstack { ta, 'unlimited', 'literal', c }
937 return true
938end
939
940function operators.floor()
941 local a = pop_opstack()
942 if not a then
943 return ps_error('stackunderflow')
944 end
945 local ta = a[1]
946 if not (ta == 'real' or ta == 'integer') then
947 return ps_error('typecheck')
948 end
949 local c = floor(a[4])
950 push_opstack { ta, 'unlimited', 'literal', c }
951 return true
952end
953
954function operators.round()
955 local a = pop_opstack()
956 if not a then
957 return ps_error('stackunderflow')
958 end
959 local ta = a[1]
960 if not (ta == 'real' or ta == 'integer') then
961 return ps_error('typecheck')
962 end
963 local c = floor(a[4]+0.5)
964 push_opstack { ta, 'unlimited', 'literal', c }
965 return true
966end
967
968function operators.truncate()
969 local a = pop_opstack()
970 if not a then
971 return ps_error('stackunderflow')
972 end
973 local ta = a[1]
974 if not (ta == 'real' or ta == 'integer') then
975 return ps_error('typecheck')
976 end
977 local v = a[4]
978 local c =v < 0 and -floor(-v) or floor(v)
979 push_opstack { ta, 'unlimited', 'literal', c }
980 return true
981end
982
983function operators.sqrt()
984 local a = pop_opstack()
985 if not a then
986 return ps_error('stackunderflow')
987 end
988 local ta = a[1]
989 if not (ta == 'real' or ta == 'integer') then
990 return ps_error('typecheck')
991 end
992 local v = a[4]
993 if v < 0 then
994 return ps_error('rangecheck')
995 end
996 local c = sqrt(v)
997 push_opstack { 'real', 'unlimited', 'literal', c }
998 return true
999end
1000
1001function operators.atan()
1002 local b = pop_opstack()
1003 local a = pop_opstack()
1004 if not a then
1005 return ps_error('stackunderflow')
1006 end
1007 local ta, tb = a[1], b[1]
1008 if not (tb == 'real' or tb == 'integer') then
1009 return ps_error('typecheck')
1010 end
1011 if not (ta == 'real' or ta == 'integer') then
1012 return ps_error('typecheck')
1013 end
1014 local va, vb = a[4], b[4]
1015 if va == 0 and vb == 0 then
1016 return ps_error('undefinedresult')
1017 end
1018 local c = deg(atan2(rad(va),rad(vb)))
1019 if c < 0 then
1020 c = c + 360
1021 end
1022 push_opstack { 'real', 'unlimited', 'literal', c }
1023 return true
1024end
1025
1026function operators.sin()
1027 local a = pop_opstack()
1028 if not a then
1029 return ps_error('stackunderflow')
1030 end
1031 local ta = a[1]
1032 if not (ta == 'real' or ta == 'integer') then
1033 return ps_error('typecheck')
1034 end
1035 local c = sin(rad(a[4]))
1036
1037 if abs(c) < 1.0e-16 then
1038 c = 0
1039 end
1040 push_opstack { 'real', 'unlimited', 'literal', c }
1041 return true
1042end
1043
1044function operators.cos()
1045 local a = pop_opstack()
1046 if not a then
1047 return ps_error('stackunderflow')
1048 end
1049 local ta = a[1]
1050 if not (ta == 'real' or ta == 'integer') then
1051 return ps_error('typecheck')
1052 end
1053 local c = cos(rad(a[4]))
1054
1055 if abs(c) < 1.0e-16 then
1056 c = 0
1057 end
1058 push_opstack { 'real', 'unlimited', 'literal', c }
1059 return true
1060end
1061
1062function operators.exp()
1063 local b = pop_opstack()
1064 local a = pop_opstack()
1065 if not a then
1066 return ps_error('stackunderflow')
1067 end
1068 local ta, tb = a[1], b[1]
1069 if not (ta == 'real' or ta == 'integer') then
1070 return ps_error('typecheck')
1071 end
1072 if not (tb == 'real' or tb == 'integer') then
1073 return ps_error('typecheck')
1074 end
1075 local va, vb = a[4], b[4]
1076 if va < 0 and floor(vb) ~= vb then
1077 return ps_error('undefinedresult')
1078 end
1079 local c = pow(va,vb)
1080 push_opstack { 'real', 'unlimited', 'literal', c }
1081 return true
1082end
1083
1084function operators.ln()
1085 local a = pop_opstack()
1086 if not a then
1087 return ps_error('stackunderflow')
1088 end
1089 local ta = a[1]
1090 if not (ta == 'real' or ta == 'integer') then
1091 return ps_error('typecheck')
1092 end
1093 local v = a[4]
1094 if v <= 0 then
1095 return ps_error('undefinedresult')
1096 end
1097 local c = log(v)
1098 push_opstack { 'real', 'unlimited', 'literal', c }
1099 return true
1100end
1101
1102function operators.log()
1103 local a = pop_opstack()
1104 if not a then
1105 return ps_error('stackunderflow')
1106 end
1107 local ta = a[1]
1108 if not (ta == 'real' or ta == 'integer') then
1109 return ps_error('typecheck')
1110 end
1111 local v = a[4]
1112 if v <= 0 then
1113 return ps_error('undefinedresult')
1114 end
1115 local c = log10(v)
1116 push_opstack { 'real', 'unlimited', 'literal', c }
1117 return true
1118end
1119
1120escrito.randomseed = os.time()
1121
1122
1123
1124function operators.rand()
1125 local c = random(MAX_INT) - 1
1126 push_opstack { 'integer', 'unlimited', 'literal', c }
1127 return true
1128end
1129
1130function operators.srand()
1131 local a = pop_opstack()
1132 if not a then
1133 return ps_error('stackunderflow')
1134 end
1135 local ta = a[1]
1136 if ta ~= 'integer' then
1137 return ps_error('typecheck')
1138 end
1139 escrito.randomseed = a[4]
1140 setranseed(escrito.randomseed)
1141 return true
1142end
1143
1144function operators.rrand()
1145 push_opstack { 'integer', 'unlimited', 'literal', escrito.randomseed }
1146 return true
1147end
1148
1149
1150
1151
1152
1153function operators.array()
1154 local a = pop_opstack()
1155 if not a then
1156 return ps_error('stackunderflow')
1157 end
1158 local t = a[1]
1159 local v = a[4]
1160 if t ~= 'integer' then
1161 return ps_error('typecheck')
1162 end
1163 if v < 0 then
1164 return ps_error('rangecheck')
1165 end
1166 local array = { }
1167 for i=1,v do
1168 array[n] = { 'null', 'unlimited', 'literal', true }
1169 end
1170 push_opstack { 'array', 'unlimited', 'literal', add_VM(array), 0, v, 'd'}
1171end
1172
1173function operators.endarray()
1174 local n = opstackptr
1175 while n > 0 do
1176 if opstack[n][1] == 'mark' then
1177 break
1178 end
1179 n = n - 1
1180 end
1181 if n == 0 then
1182 return ps_error('unmatchedmark')
1183 end
1184 local top = opstackptr
1185 local i = opstackptr - n
1186 local array = { }
1187 while i > 0 do
1188 array[i] = pop_opstack()
1189 i = i - 1
1190 end
1191 pop_opstack()
1192 push_opstack { 'array', 'unlimited', 'literal', add_VM(array), #array, #array, 'd' }
1193end
1194
1195function operators.length()
1196 local a = pop_opstack()
1197 if not a then
1198 return ps_error('stackunderflow')
1199 end
1200 local access = a[2]
1201 if access == "noaccess" or access == "executeonly" then
1202 return ps_error('invalidaccess')
1203 end
1204 local ta = a[1]
1205 local va = a[4]
1206 if ta == "dict" or ta == "font" then
1207 va = get_VM(va).size
1208 elseif ta == "array" or ta == "string" then
1209 va = get_VM(va)
1210 va = #va
1211 else
1212 return ps_error('typecheck')
1213 end
1214 push_opstack { 'integer', 'unlimited', 'literal', va }
1215 return true
1216end
1217
1218function operators.get()
1219 local b = pop_opstack()
1220 local a = pop_opstack()
1221 if not a then
1222 return ps_error('stackunderflow')
1223 end
1224 local access = a[2]
1225 if access == "noaccess" or access == "execute-only" then
1226 return ps_error('invalidaccess')
1227 end
1228 local ta = a[1]
1229 local va = a[4]
1230 if ta == "dict" then
1231 local dict = get_VM(va)
1232 local key = b
1233 local tb = b[1]
1234 local vb = b[4]
1235 if tb == "string" or tb == "name" then
1236 key = get_VM(vb)
1237 end
1238 local ddk = dict.dict[key]
1239 if ddk then
1240 push_opstack(ddk)
1241 else
1242 return ps_error('undefined')
1243 end
1244 elseif ta == "array" then
1245 local tb = b[1]
1246 local vb = b[4]
1247 if tb ~= 'integer' then
1248 return ps_error('typecheck')
1249 end
1250 if vb < 0 or vb >= a[6] then
1251 return ps_error('rangecheck')
1252 end
1253 local array = get_VM(va)
1254 local index = vb + 1
1255 push_opstack(array[index])
1256 elseif ta == "string" then
1257 local tb = b[1]
1258 local vb = b[4]
1259 if tb ~= 'integer' then
1260 return ps_error('typecheck')
1261 end
1262 if vb < 0 or vb >= a[6] then
1263 return ps_error('rangecheck')
1264 end
1265 local thestring = get_VM(va)
1266 local index = vb + 1
1267 local c = sub(thestring,index,index)
1268 push_opstack { 'integer', 'unlimited', 'literal', byte(c) }
1269 else
1270 return ps_error('typecheck')
1271 end
1272 return true
1273end
1274
1275function operators.put()
1276 local c = pop_opstack()
1277 local b = pop_opstack()
1278 local a = pop_opstack()
1279 if not a then
1280 return ps_error('stackunderflow')
1281 end
1282 local ta = a[1]
1283 if ta == "dict" then
1284 local dict = get_VM(a[4])
1285 if dict.access ~= 'unlimited' then
1286 return ps_error('invalidaccess')
1287 end
1288 local key = b
1289 local bt = b[1]
1290 if bt == "string" or bt == "name" then
1291 key = get_VM(b[4])
1292 end
1293 local dd = dict.dict
1294 local ds = dict.size
1295 local ddk = dd[key]
1296 if not ddk and (ds == dict.maxsize) then
1297 return ps_error('dictfull')
1298 end
1299 if c[1] == 'array' then
1300 c[7] = 'i'
1301 end
1302 if not ddk then
1303 dict.size = ds + 1
1304 end
1305 dd[key] = c
1306 elseif ta == "array" then
1307 if a[2] ~= 'unlimited' then
1308 return ps_error('invalidaccess')
1309 end
1310 if b[1] ~= 'integer' then
1311 return ps_error('typecheck')
1312 end
1313 local va, vb = a[4], b[4]
1314 if vb < 0 or vb >= a[6] then
1315 return ps_error('rangecheck')
1316 end
1317 local vm = VM[va]
1318 local vi = bv + 1
1319 if vm[vi][1] == 'null' then
1320 a[5] = a[5] + 1
1321 end
1322 vm[vi] = c
1323 elseif ta == "string" then
1324 if a[2] ~= 'unlimited' then
1325 return ps_error('invalidaccess')
1326 end
1327 if b[1] ~= 'integer' then
1328 return ps_error('typecheck')
1329 end
1330 if c[1] ~= 'integer' then
1331 return ps_error('typecheck')
1332 end
1333 local va, vb, vc = a[4], b[4], c[4]
1334 if vb < 0 or vb >= a[6] then
1335 return ps_error('rangecheck')
1336 end
1337 if vc < 0 or vc > 255 then
1338 return ps_error('rangecheck')
1339 end
1340 local thestring = get_VM(va)
1341 VM[va] = sub(thestring,1,vb) .. char(vc) .. sub(thestring,vb+2)
1342 else
1343 return ps_error('typecheck')
1344 end
1345 return true
1346end
1347
1348function operators.getinterval()
1349 local c = pop_opstack()
1350 local b = pop_opstack()
1351 local a = pop_opstack()
1352 if not a then
1353 return ps_error('stackunderflow')
1354 end
1355 local ta, tb, tc = a[1], b[1], c[1]
1356 local aa, ab, ac = a[2], b[2], c[2]
1357 local va, vb, vc = a[4], b[4], c[4]
1358 if ta ~= "array" and ta ~= 'string' then
1359 return ps_error('typecheck')
1360 end
1361 if tb ~= 'integer' or tc ~= 'integer' then
1362 return ps_error('typecheck')
1363 end
1364 if aa == "execute-only" or aa == 'noaccess' then
1365 return ps_error('invalidaccess')
1366 end
1367 if vb < 0 or vc < 0 or vb + vc >= a[6] then
1368 return ps_error('rangecheck')
1369 end
1370
1371
1372 if ta == 'array' then
1373 local array = get_VM(va)
1374 local subarray = { }
1375 local index = 1
1376 while index <= vc do
1377 subarray[index] = array[index+vb]
1378 index = index + 1
1379 end
1380 push_opstack { 'array', aa, a[3], add_VM(subarray), vc, vc, 'd' }
1381 else
1382 local thestring = get_VM(va)
1383 local newstring = sub(thestring,vb+1,vb+vc)
1384 push_opstack { 'string', aa, a[3], add_VM(newstring), vc, vc }
1385 end
1386 return true
1387end
1388
1389function operators.putinterval()
1390 local c = pop_opstack()
1391 local b = pop_opstack()
1392 local a = pop_opstack()
1393 if not a then
1394 return ps_error('stackunderflow')
1395 end
1396 local ta, tb, tc = a[1], b[1], c[1]
1397 local aa, ab, ac = a[2], b[2], c[2]
1398 local va, vb, vc = a[4], b[4], c[4]
1399 if ta ~= "array" and ta ~= 'string' then
1400 return ps_error('typecheck')
1401 end
1402 if tc ~= "array" and tc ~= 'string' then
1403 return ps_error('typecheck')
1404 end
1405 if ta ~= tc then
1406 return ps_error('typecheck')
1407 end
1408 if aa ~= "unlimited" then
1409 return ps_error('invalidaccess')
1410 end
1411 if tb ~= 'integer' then
1412 return ps_error('typecheck')
1413 end
1414 if vb < 0 or vb + c[6] >= a[6] then
1415 return ps_error('rangecheck')
1416 end
1417 if ta == 'array' then
1418 local newarr = get_VM(vc)
1419 local oldarr = get_VM(va)
1420 local index = 1
1421 local lastindex = c[6]
1422 local step = a[5]
1423 while index <= lastindex do
1424 if oldarr[vb+index][1] == 'null' then
1425 a[5] = a[5] + 1
1426
1427 end
1428 oldarr[vb+index] = newarr[index]
1429 index = index + 1
1430 end
1431 else
1432 local thestring = get_VM(va)
1433 VM[va] = sub(thestring,1,vb) .. get_VM(vc) .. sub(thestring,vb+c[6]+1)
1434 end
1435 return true
1436end
1437
1438function operators.aload()
1439 local a = pop_opstack()
1440 if not a then
1441 return ps_error('stackunderflow')
1442 end
1443 local ta, aa, va = a[1], a[2], a[4]
1444 if ta ~= "array" then
1445 return ps_error('typecheck')
1446 end
1447 if aa == "execute-only" or aa == 'noaccess' then
1448 return ps_error('invalidaccess')
1449 end
1450 local array = get_VM(va)
1451 for i=1,#array do
1452 push_opstack(array[i])
1453 end
1454 push_opstack(a)
1455 return true
1456end
1457
1458function operators.astore()
1459 local a = pop_opstack()
1460 if not a then
1461 return ps_error('stackunderflow')
1462 end
1463 local ta, aa, va = a[1], a[2], a[4]
1464 if ta ~= "array" then
1465 return ps_error('typecheck')
1466 end
1467 if aa == "execute-only" or aa == 'noaccess' then
1468 return ps_error('invalidaccess')
1469 end
1470 local array = get_VM(va)
1471 local count = a[6]
1472 for i=1,count do
1473 local v = pop_opstack()
1474 if not v then
1475 return ps_error('stackunderflow')
1476 end
1477 array[i] = v
1478 end
1479 a[5] = a[5] + count
1480 push_opstack(a)
1481 return true
1482end
1483
1484function operators.forall()
1485 local b = pop_opstack()
1486 local a = pop_opstack()
1487 if not a then
1488 return ps_error('stackunderflow')
1489 end
1490 local ta, aa, va = a[1], a[2], a[4]
1491 local tb, ab, vb = b[1], b[2], b[4]
1492 if not tb == "array" and b[3] == 'executable' then
1493 return ps_error('typecheck')
1494 end
1495 if tb == 'noaccess' then
1496 return ps_error('invalidaccess')
1497 end
1498 if not (ta == "array" or ta == 'dict' or ta == 'string' or ta == "font") then
1499 return ps_error('typecheck')
1500 end
1501 if aa == "execute-only" or aa == 'noaccess' then
1502 return ps_error('invalidaccess')
1503 end
1504 push_execstack { '.exit', 'unlimited', 'literal', false }
1505 local curstack = execstackptr
1506 if ta == 'array' then
1507 if a[6] == 0 then
1508 return true
1509 end
1510 b[7] = 'i'
1511 local thearray = get_VM(va)
1512 for i=1,#thearray do
1513 if stopped then
1514 stopped = false
1515 return false
1516 end
1517 push_opstack(thearray[i])
1518 b[5] = 1
1519 push_execstack(b)
1520 while curstack <= execstackptr do
1521 do_exec()
1522 end
1523 end
1524 local entry = execstack[execstackptr]
1525 if entry[1] == '.exit' and antry[4] == true then
1526 pop_execstack()
1527 return true
1528 end
1529 elseif ta == 'dict' or ta == 'font' then
1530 local thedict = get_VM(va)
1531 if thedict.size == 0 then
1532 return true
1533 end
1534 b[7] = 'i'
1535 local thedict = get_VM(va)
1536 for k, v in next, thedict.dict do
1537 if stopped then
1538 stopped = false
1539 return false
1540 end
1541 if type(k) == "string" then
1542 push_opstack { 'name', 'unlimited', 'literal', add_VM(k) }
1543 else
1544 push_opstack(k)
1545 end
1546 push_opstack(v)
1547 b[5] = 1
1548 push_execstack(b)
1549 while curstack < execstackptr do
1550 do_exec()
1551 end
1552 local entry = execstack[execstackptr]
1553 if entry[1] == '.exit' and antry[4] == true then
1554 pop_execstack()
1555 return true
1556 end
1557 end
1558 else
1559 if a[6] == 0 then
1560 return true
1561 end
1562 b[7] = 'i'
1563 local thestring = get_VM(va)
1564 for v in gmatch(thestring,".") do
1565 if stopped then
1566 stopped = false
1567 return false
1568 end
1569 push_opstack { 'integer', 'unlimited', 'literal', byte(v) }
1570 b[5] = 1
1571 push_execstack(b)
1572 while curstack < execstackptr do
1573 do_exec()
1574 end
1575 local entry = execstack[execstackptr]
1576 if entry[1] == '.exit' and antry[4] == true then
1577 pop_execstack()
1578 return true;
1579 end
1580 end
1581 end
1582 return true
1583end
1584
1585
1586
1587
1588
1589
1590function operators.dict()
1591 local a = pop_opstack()
1592 if not a then
1593 return ps_error('stackunderflow')
1594 end
1595 if not a[1] == 'integer' then
1596 return ps_error('typecheck')
1597 end
1598 local s = a[4]
1599 if s < 0 then
1600 return ps_error('rangecheck')
1601 end
1602 if s == 0 then
1603 s = MAX_INT
1604 end
1605 push_opstack {
1606 'dict',
1607 'unlimited',
1608 'literal',
1609 add_VM {
1610 access = 'unlimited',
1611 size = 0,
1612 maxsize = s,
1613 dict = { },
1614 }
1615 }
1616end
1617
1618function operators.maxlength()
1619 local a = pop_opstack()
1620 if not a then
1621 return ps_error('stackunderflow')
1622 end
1623 local ta, aa, va = a[1], a[2], a[4]
1624 if ta ~= 'dict' then
1625 return ps_error('typecheck')
1626 end
1627 if aa == 'execute-only' or aa == 'noaccess' then
1628 return ps_error('invalidaccess')
1629 end
1630 local thedict = get_VM(va)
1631 push_opstack { 'integer', 'unlimited', 'literal', thedict.maxsize }
1632end
1633
1634function operators.begin()
1635 local a = pop_opstack()
1636 if not a then
1637 return ps_error('stackunderflow')
1638 end
1639 if a[1] ~= 'dict' then
1640 return ps_error('typecheck')
1641 end
1642 dictstackptr = dictstackptr + 1
1643 dictstack[dictstackptr] = a[4]
1644end
1645
1646operators["end"] = function()
1647 if dictstackptr < 3 then
1648 return ps_error('dictstackunderflow')
1649 end
1650 dictstack[dictstackptr] = nil
1651 dictstackptr = dictstackptr - 1
1652end
1653
1654function operators.def()
1655 local b = pop_opstack()
1656 local a = pop_opstack()
1657 if not a then
1658 return ps_error('stackunderflow')
1659 end
1660 if not (a[1] == 'name' and a[3] == 'literal') then
1661 return ps_error('typecheck')
1662 end
1663 if b[1] == 'array' then
1664 b[7] = 'i'
1665 end
1666 local thedict = get_VM(dictstack[dictstackptr])
1667 if not thedict.dict[get_VM(a[4])] then
1668 if thedict.size == thedict.maxsize then
1669
1670 end
1671 thedict.size = thedict.size + 1
1672 end
1673 thedict.dict[get_VM(a[4])] = b
1674 return true
1675end
1676
1677
1678
1679function operators.load()
1680 local a = pop_opstack()
1681 if not a then
1682 return ps_error('stackunderflow')
1683 end
1684 local aa = a[2]
1685 if aa == 'noaccess' or aa == 'execute-only' then
1686 return ps_error('invalidaccess')
1687 end
1688 local v = lookup(get_VM(a[4]))
1689 if not v then
1690 return ps_error('undefined')
1691 end
1692 push_opstack(v)
1693end
1694
1695function operators.store()
1696 local b = pop_opstack()
1697 local a = pop_opstack()
1698 if not a then
1699 return ps_error('stackunderflow')
1700 end
1701 if not (a[1] == 'name' and a[3] == 'literal') then
1702 return ps_error('typecheck')
1703 end
1704 if b[7] == 'array' then
1705 b[7] = 'i'
1706 end
1707 local val, dictloc = lookup(a[4])
1708 if val then
1709 local thedict = get_VM(dictstack[dictloc])
1710 if thedict.access == 'execute-only' or thedict.access == 'noaccess' then
1711 return ps_error('invalidaccess')
1712 end
1713 thedict.dict[a[4]] = b
1714 else
1715 local thedict = get_VM(dictstack[dictstackptr])
1716 local access = thedict.access
1717 local size = thedict.size
1718 if access == 'execute-only' or access == 'noaccess' then
1719 return ps_error('invalidaccess')
1720 end
1721 if size == thedict.maxsize then
1722 return ps_error('dictfull')
1723 end
1724 thedict.size = size + 1
1725 thedict.dict[a[4]] = b
1726 end
1727 return true
1728end
1729
1730function operators.known()
1731 local b = pop_opstack()
1732 local a = pop_opstack()
1733 if not a then
1734 return ps_error('stackunderflow')
1735 end
1736 local ta, aa, va = a[1], a[2], a[4]
1737 local tb, vb = b[1], b[4]
1738 if ta ~= 'dict' then
1739 return ps_error('typecheck')
1740 end
1741 if not (tb == 'name' or tb == 'operator') then
1742 return ps_error('typecheck')
1743 end
1744 if aa == 'noaccess' or aa == 'execute-only' then
1745 return ps_error('invalidaccess')
1746 end
1747 local thedict = get_VM(va)
1748 push_opstack {'boolean', 'unlimited', 'literal', thedict.dict[vb] and true or false }
1749 return true
1750end
1751
1752function operators.where()
1753 local a = pop_opstack()
1754 if not a then
1755 return ps_error('stackunderflow')
1756 end
1757 if not (a[1] == 'name' and a[3] == 'literal') then
1758 return ps_error('typecheck')
1759 end
1760 local val, dictloc = lookup(get_VM(a[4]))
1761 local thedict = dictloc and get_VM(dictstack[dictloc])
1762 if val then
1763 if thedict.access == 'execute-only' or thedict.access == 'noaccess' then
1764 return ps_error('invalidaccess')
1765 end
1766 push_opstack {'dict', 'unlimited', 'literal', dictstack[dictloc]}
1767 push_opstack {'boolean', 'unlimited', 'literal', true}
1768 else
1769 push_opstack {'boolean', 'unlimited', 'literal', false}
1770 end
1771 return true
1772end
1773
1774function operators.currentdict()
1775 push_opstack { 'dict', 'unlimited', 'literal', dictstack[dictstackptr] }
1776 return true
1777end
1778
1779function operators.countdictstack()
1780 push_opstack { 'integer', 'unlimited', 'literal', dictstackptr }
1781 return true
1782end
1783
1784function operators.dictstack()
1785 local a = pop_opstack()
1786 if not a then
1787 return ps_error('stackunderflow')
1788 end
1789 if not a[1] == 'array' then
1790 return ps_error('typecheck')
1791 end
1792 if not a[2] == 'unlimited' then
1793 return ps_error('invalidaccess')
1794 end
1795 if a[6] < dictstackptr then
1796 return ps_error('rangecheck')
1797 end
1798 local thearray = get_VM(a[4])
1799 local subarray = { }
1800 for i=1,dictstackptr do
1801 thearray[n] = { 'dict', 'unlimited', 'literal', dictstack[i] }
1802 subarray[n] = thearray[i]
1803 end
1804 a[5] = a[5] + dictstackptr
1805 push_opstack { 'array', 'unlimited', 'literal', add_VM(subarray), dictstackptr, dictstackptr, '' }
1806 return true
1807end
1808
1809
1810
1811
1812
1813
1814function operators.string()
1815 local a = pop_opstack()
1816 if not a then
1817 return ps_error('stackunderflow')
1818 end
1819 local ta, va = a[1], a[4]
1820 if ta ~= 'integer' then
1821 return ps_error('typecheck')
1822 end
1823 if va < 0 then
1824 return ps_error('rangecheck')
1825 end
1826 push_opstack { 'string', 'unlimited', 'literal', add_VM(''), 1, va }
1827end
1828
1829function operators.anchorsearch()
1830 local b = pop_opstack()
1831 local a = pop_opstack()
1832 if not a then
1833 return ps_error('stackunderflow')
1834 end
1835 local ta, aa, va = a[1], a[2], a[4]
1836 local tb, ab, vb = b[1], b[2], b[4]
1837 if not ta ~= 'string' then
1838 return ps_error('typecheck')
1839 end
1840 if tb ~= 'string' then
1841 return ps_error('typecheck')
1842 end
1843 if aa == 'noaccess' or aa == 'execute-only' then
1844 return ps_error('invalidaccess')
1845 end
1846 if ab == 'noaccess' or ab == 'execute-only' then
1847 return ps_error('invalidaccess')
1848 end
1849 local thestring = get_VM(va)
1850 local thesearch = get_VM(vb)
1851 local prefix = sub(thestring,1,#thesearch)
1852 if prefix == thesearch then
1853 if aa == 'read-only' then
1854 return ps_error('invalidaccess')
1855 end
1856 local post = sub(thestring,#thesearch+1)
1857 push_opstack { 'string', 'unlimited', 'literal', add_VM(post), 1, #post }
1858 push_opstack { 'string', 'unlimited', 'literal', add_VM(prefix), 1, #prefix }
1859 push_opstack (b_true)
1860 else
1861 push_opstack(a)
1862 push_opstack (b_false)
1863 end
1864 return true
1865end
1866
1867function operators.search()
1868 local b = pop_opstack()
1869 local a = pop_opstack()
1870 if not a then
1871 return ps_error('stackunderflow')
1872 end
1873 local ta, aa, va = a[1], a[2], a[4]
1874 local tb, ab, vb = b[1], b[2], b[4]
1875 if not ta ~= 'string' then
1876 return ps_error('typecheck')
1877 end
1878 if tb ~= 'string' then
1879 return ps_error('typecheck')
1880 end
1881 if aa == 'noaccess' or aa == 'execute-only' then
1882 return ps_error('invalidaccess')
1883 end
1884 if ab == 'noaccess' or ab == 'execute-only' then
1885 return ps_error('invalidaccess')
1886 end
1887 local thestring = get_VM(a[4])
1888 local thesearch = get_VM(b[4])
1889
1890 local n = 1
1891 local match
1892 while n + #thesearch-1 <= #thestring do
1893 match = sub(thestring,n,n+#thesearch-1)
1894 if match == thesearch then
1895 break
1896 end
1897 n = n + 1
1898 end
1899 if match == thesearch then
1900 if aa == 'read-only' then
1901 return ps_error('invalidaccess')
1902 end
1903 local prefix = sub(thestring,1,n-1)
1904 local post = sub(thestring,#thesearch+n)
1905 push_opstack { 'string', 'unlimited', 'literal', add_VM(post), 1, #post }
1906 push_opstack { 'string', 'unlimited', 'literal', add_VM(thesearch), 1, #thesearch }
1907 push_opstack { 'string', 'unlimited', 'literal', add_VM(prefix), 1, #prefix }
1908 push_opstack (b_true)
1909 else
1910 push_opstack(a)
1911 push_opstack(b_false)
1912 end
1913 return true
1914end
1915
1916function operators.token()
1917 local a = pop_opstack()
1918 if not a then
1919 return ps_error('stackunderflow')
1920 end
1921 local ta, aa, va = a[1], a[2], a[4]
1922 if not (ta == 'string' or ta == 'file') then
1923 return ps_error('typecheck')
1924 end
1925 if aa ~= 'unlimited' then
1926 return ps_error('invalidaccess')
1927 end
1928
1929 if ta == 'string' then
1930 local top = execstackptr
1931 push_execstack { '.token', 'unlimited', 'literal', false }
1932 push_execstack { a[1], a[2], 'executable', va, 1, a[6] }
1933 local v, err = next_object()
1934 if not v then
1935 pop_execstack()
1936 pop_execstack()
1937 push_opstack(b_false)
1938 else
1939 local q = pop_execstack()
1940 if execstack[execstackptr][1] == '.token' then
1941 pop_execstack()
1942 end
1943 local tq, vq = q[1], q[4]
1944 if tq == 'string' and vq ~= va then
1945 push_execstack(q)
1946 end
1947 local thestring, substring
1948 if vq ~= va then
1949 thestring = ""
1950 substring = ""
1951 else
1952 thestring = get_VM(vq)
1953 substring = sub(thestring,q[5] or 0)
1954 end
1955 push_opstack { ta, aa, a[3], add_VM(substring), 1, #substring}
1956 push_opstack(v)
1957 push_opstack(b_true)
1958 end
1959 else
1960 if a[7] ~= 'r' then
1961 return ps_error('invalidaccess')
1962 end
1963 push_execstack { '.token', 'unlimited', 'literal', false }
1964 push_execstack { 'file', 'unlimited', 'executable', va, a[5], a[6], a[7], a[8] }
1965 local v, err = next_object()
1966 if not v then
1967 pop_execstack()
1968 pop_execstack()
1969 push_opstack(b_false)
1970 else
1971 local q = pop_execstack()
1972 a[5] = q[5]
1973 if execstack[execstackptr][1] == '.token' then
1974 pop_execstack()
1975 end
1976 push_opstack(v)
1977 push_opstack(b_true)
1978 end
1979 end
1980 return true
1981end
1982
1983
1984
1985
1986
1987local function both()
1988 local b = pop_opstack()
1989 local a = pop_opstack()
1990 if not a then
1991 return ps_error('stackunderflow')
1992 end
1993 local ta, aa = a[1], a[2]
1994 local tb, ab = b[1], b[2]
1995 if aa == 'noaccess' or aa == 'execute-only' then
1996 return ps_error('invalidaccess')
1997 end
1998 if ab == 'noaccess' or ab == 'execute-only' then
1999 return ps_error('invalidaccess')
2000 end
2001 if (ta == 'dict' and tb == 'dict') or (ta == 'array' and tb =='array') then
2002 return true, a[4], b[4]
2003 elseif ((ta == 'string' or ta == 'name') and (tb == 'string' or tb == 'name' )) then
2004 local astr = get_VM(a[4])
2005 local bstr = get_VM(b[4])
2006 return true, astr, bstr
2007 elseif ((ta == 'integer' or ta == 'real') and (tb == 'integer' or tb == 'real')) or (ta == tb) then
2008 return true, a[4], b[4]
2009 else
2010 return ps_error('typecheck')
2011 end
2012 return true
2013end
2014
2015function operators.eq()
2016 local ok, a, b = both()
2017 if ok then
2018 push_opstack(a == b and b_true or b_false)
2019 return true
2020 else
2021 return a
2022 end
2023end
2024
2025function operators.ne()
2026 local ok, a, b = both()
2027 if ok then
2028 push_opstack(a ~= b and b_true or b_false)
2029 return true
2030 else
2031 return a
2032 end
2033end
2034
2035local function both()
2036 local b = pop_opstack()
2037 local a = pop_opstack()
2038 if not a then
2039 return ps_error('stackunderflow')
2040 end
2041 local aa, ab = a[2], b[2]
2042 if aa == 'noaccess' or aa == 'execute-only' then
2043 return ps_error('invalidaccess')
2044 end
2045 if ab == 'noaccess' or ab == 'execute-only' then
2046 return ps_error('invalidaccess')
2047 end
2048 local ta, tb = a[1], b[1]
2049 local va, vb = a[4], b[4]
2050 if (ta == 'real' or ta == 'integer') and (tb == 'real' or tb == 'integer') then
2051 return true, va, vb
2052 elseif ta == 'string' and tb == 'string' then
2053 local va = get_VM(va)
2054 local vb = get_VM(vb)
2055 return true, va, vb
2056 else
2057 return ps_error('typecheck')
2058 end
2059end
2060
2061function operators.ge()
2062 local ok, a, b = both()
2063 if ok then
2064 push_opstack(a >= b and b_true or b_false)
2065 return true
2066 else
2067 return a
2068 end
2069end
2070
2071function operators.gt()
2072 local ok, a, b = both()
2073 if ok then
2074 push_opstack(a > b and b_true or b_false)
2075 return true
2076 else
2077 return a
2078 end
2079end
2080
2081function operators.le()
2082 local ok, a, b = both()
2083 if ok then
2084 push_opstack(a <= b and b_true or b_false)
2085 return true
2086 else
2087 return a
2088 end
2089end
2090
2091function operators.lt()
2092 local ok, a, b = both()
2093 if ok then
2094 push_opstack(a < b and b_true or b_false)
2095 return true
2096 else
2097 return a
2098 end
2099end
2100
2101local function both()
2102 local b = pop_opstack()
2103 local a = pop_opstack()
2104 if not a then
2105 return ps_error('stackunderflow')
2106 end
2107 local aa, ab = a[2], b[2]
2108 if aa == 'noaccess' or aa == 'execute-only' then
2109 return ps_error('invalidaccess')
2110 end
2111 if ab == 'noaccess' or ab == 'execute-only' then
2112 return ps_error('invalidaccess')
2113 end
2114 local ta, tb = a[1], b[1]
2115 local va, vb = a[4], b[4]
2116 if ta == 'boolean' and tb == 'boolean' then
2117 return ta, va, vb
2118 elseif ta == 'integer' and tb == 'integer' then
2119 return ta, va, vb
2120 else
2121 return ps_error('typecheck')
2122 end
2123end
2124
2125operators["and"]= function()
2126 local ok, a, b = both()
2127 if ok == 'boolean' then
2128 push_opstack((a[1] and b[1]) and b_true or b_false)
2129 return true
2130 elseif ok == 'integer' then
2131 push_opstack { 'integer', 'unlimited', 'literal', bitand(a[1],b[1]) }
2132 return true
2133 else
2134 return a
2135 end
2136end
2137
2138operators["or"] = function()
2139 local ok, a, b = both()
2140 if ok == 'boolean' then
2141 push_opstack((a[1] or b[1]) and b_true or b_false)
2142 return true
2143 elseif ok == 'integer' then
2144 push_opstack {'integer', 'unlimited', 'literal', bitor(a[1],b[1]) }
2145 return true
2146 else
2147 return a
2148 end
2149end
2150
2151function operators.xor()
2152 local ok, a, b = both()
2153 if ok == 'boolean' then
2154 push_opstack ((a[1] ~= b[1]) and b_true or b_false)
2155 return true
2156 elseif ok == 'integer' then
2157 push_opstack {'integer', 'unlimited', 'literal', bitxor(a[1],b[1]) }
2158 return true
2159 else
2160 return a
2161 end
2162end
2163
2164operators["not"] = function()
2165 local a = pop_opstack()
2166 if not a then
2167 return ps_error('stackunderflow')
2168 end
2169 local aa = a[2]
2170 local ta = a[1]
2171 if aa == 'noaccess' or aa == 'execute-only' then
2172 return ps_error('invalidaccess')
2173 end
2174 if ta == 'boolean' then
2175 push_opstack ((not a[4]) and b_true or b_false)
2176 elseif ta == 'integer' then
2177 push_opstack { 'integer', 'unlimited', 'literal', -a[4] - 1 }
2178 else
2179 return ps_error('typecheck')
2180 end
2181 return true
2182end
2183
2184function operators.bitshift()
2185 local b = pop_opstack()
2186 local a = pop_opstack()
2187 if not a then
2188 return ps_error('stackunderflow')
2189 end
2190 local aa, ab = a[2], b[2]
2191 local ta, tb = a[1], b[1]
2192 local va, vb = a[4], b[4]
2193 if aa == 'noaccess' or aa == 'execute-only' then
2194 return ps_error('invalidaccess')
2195 end
2196 if ab == 'noaccess' or ab == 'execute-only' then
2197 return ps_error('invalidaccess')
2198 end
2199 if not (ta == 'integer' and tb == 'integer') then
2200 return ps_error('typecheck')
2201 end
2202 push_opstack { 'integer', 'unlimited', 'literal', bitrshift(va,vb < 0 and -vb or vb) }
2203 return true
2204end
2205
2206
2207
2208
2209
2210
2211function operators.exec()
2212 local a = pop_opstack()
2213 if not a then
2214 return ps_error('stackunderflow')
2215 end
2216 if a[1] == 'array' then
2217 a[7] = 'i'
2218 a[5] = 1
2219 end
2220 push_execstack(a)
2221 return true
2222end
2223
2224operators["if"] = function()
2225 local b = pop_opstack()
2226 local a = pop_opstack()
2227 if not a then
2228 return ps_error('stackunderflow')
2229 end
2230 if a[1] ~= 'boolean' then
2231 return ps_error('typecheck')
2232 end
2233 if b[1] ~= 'array' then
2234 return ps_error('typecheck')
2235 end
2236 if a[4] == true then
2237 b[7] = 'i'
2238 b[5] = 1
2239 push_execstack(b)
2240 end
2241 return true
2242end
2243
2244function operators.ifelse()
2245 local c = pop_opstack()
2246 local b = pop_opstack()
2247 local a = pop_opstack()
2248 if not a then
2249 return ps_error('stackunderflow')
2250 end
2251 if a[1] ~= 'boolean' then
2252 return ps_error('typecheck')
2253 end
2254 if b[1] ~= 'array' then
2255 return ps_error('typecheck')
2256 end
2257 if c[1] ~= 'array' then
2258 return ps_error('typecheck')
2259 end
2260 if a[4] == true then
2261 b[5] = 1
2262 b[7] = 'i'
2263 push_execstack(b)
2264 else
2265 c[5] = 1
2266 c[7] = 'i'
2267 push_execstack(c)
2268 end
2269 return true
2270end
2271
2272operators["for"] = function()
2273 local d = pop_opstack()
2274 local c = pop_opstack()
2275 local b = pop_opstack()
2276 local a = pop_opstack()
2277 local ta, tb, tc, td = a[1], b[1], c[1], d[1]
2278 if not a then
2279 return ps_error('stackunderflow')
2280 end
2281 if not (ta == 'integer' or ta == 'real') then
2282 return ps_error('typecheck')
2283 end
2284 if not (tb == 'integer' or tb == 'real') then
2285 return ps_error('typecheck')
2286 end
2287 if not (tc == 'integer' or tc == 'real') then
2288 return ps_error('typecheck')
2289 end
2290 if not (td == 'array' and d[3] == 'executable') then
2291 return ps_error('typecheck')
2292 end
2293 local initial = a[4]
2294 local increment = b[4]
2295 local limit = c[4]
2296 if initial == limit then
2297 return true
2298 end
2299 push_execstack { '.exit', 'unlimited', 'literal', false }
2300 local curstack = execstackptr
2301 local tokentype = (a[1] == 'real' or b[1] == 'real' or c[1] == 'real') and 'real' or 'integer'
2302 d[7] = 'i'
2303 local first, last
2304 if increment >= 0 then
2305 first, last = initial, limit
2306 else
2307 first, last = limit, limit
2308 end
2309 for control=first,last,increment do
2310 if stopped then
2311 stopped = false
2312 return false
2313 end
2314 push_opstack { tokentype, 'unlimited', 'literal', control }
2315 d[5] = 1
2316 push_execstack(d)
2317 while curstack < execstackptr do
2318 do_exec()
2319 end
2320 local entry = execstack[execstackptr]
2321 if entry[1] == '.exit' and entry[4] == true then
2322 pop_execstack()
2323 return true;
2324 end
2325 end
2326 return true
2327end
2328
2329operators["repeat"] = function()
2330 local b = pop_opstack()
2331 local a = pop_opstack()
2332 if not a then
2333 return ps_error('stackunderflow')
2334 end
2335 if a[1] ~= 'integer' then
2336 return ps_error('typecheck')
2337 end
2338 if a[4] < 0 then
2339 return ps_error('rangecheck')
2340 end
2341 if not (b[1] == 'array' and b[3] == 'executable') then
2342 return ps_error('typecheck')
2343 end
2344 local limit = a[4]
2345 if limit == 0 then
2346 return true
2347 end
2348 push_execstack { '.exit', 'unlimited', 'literal', false }
2349 local curstack = execstackptr
2350 b[7] = 'i'
2351 local control = 0
2352 while control < limit do
2353 if stopped then
2354 stopped = false
2355 return false
2356 end
2357 b[5] = 1
2358 push_execstack(b)
2359 while curstack < execstackptr do
2360 do_exec()
2361 end
2362 local entry = execstack[execstackptr]
2363 if entry[1] == '.exit' and entry[4] == true then
2364 pop_execstack()
2365 return true;
2366 end
2367 control = control + 1
2368 end
2369 return true
2370end
2371
2372function operators.loop()
2373 local a = pop_opstack()
2374 if not a then
2375 return ps_error('stackunderflow')
2376 end
2377 if not (a[1] == 'array' and a[3] == 'executable') then
2378 return ps_error('typecheck')
2379 end
2380 push_execstack { '.exit', 'unlimited', 'literal', false }
2381 local curstack = execstackptr
2382 a[7] = 'i'
2383 while true do
2384 if stopped then
2385 stopped = false
2386 return false
2387 end
2388 a[5] = 1
2389 push_execstack(a)
2390 while curstack < execstackptr do
2391 do_exec()
2392 end
2393 if execstackptr > 0 then
2394 local entry = execstack[execstackptr]
2395 if entry[1] == '.exit' and entry[4] == true then
2396 pop_execstack()
2397 return true
2398 end
2399 end
2400 end
2401 return true
2402end
2403
2404function operators.exit()
2405 local v = pop_execstack()
2406 while v do
2407 local tv = val[1]
2408 if tv == '.exit' then
2409 push_execstack { '.exit', 'unlimited', 'literal', true }
2410 return true
2411 elseif tv == '.stopped' or tv == '.run' then
2412 push_execstack(v)
2413 return ps_error('invalidexit')
2414 end
2415 v = pop_execstack()
2416 end
2417 report("exit without context, quitting")
2418 push_execstack { 'operator', 'unlimited', 'executable', operators.quit, "quit" }
2419 return true
2420end
2421
2422function operators.stop()
2423 local v = pop_execstack()
2424 while v do
2425 if val[1] == '.stopped' then
2426 stopped = true
2427 push_opstack { 'boolean', 'unlimited', 'executable', true }
2428 return true
2429 end
2430 v = pop_execstack()
2431 end
2432 report("stop without context, quitting")
2433 push_execstack { 'operator', 'unlimited', 'executable', operators.quit, "quit" }
2434 return true
2435end
2436
2437function operators.stopped()
2438 local a = pop_opstack()
2439 if not a then
2440 return ps_error('stackunderflow')
2441 end
2442
2443 push_execstack { '.stopped', 'unlimited', 'literal', false }
2444 a[3] = 'executable'
2445 if a[1] == 'array' then
2446 a[7] = 'i'
2447 a[5] = 1
2448 end
2449 push_execstack(a)
2450 return true
2451end
2452
2453function operators.countexecstack()
2454 push_opstack { 'integer', 'unlimited', 'literal', execstackptr }
2455 return true
2456end
2457
2458function operators.execstack()
2459 local a = pop_opstack()
2460 if not a then
2461 return ps_error('stackunderflow')
2462 end
2463 if not a[1] == 'array' then
2464 return ps_error('typecheck')
2465 end
2466 if not a[2] == 'unlimited' then
2467 return ps_error('invalidaccess')
2468 end
2469 if a[6] < execstackptr then
2470 return ps_error('rangecheck')
2471 end
2472 local thearray = get_VM(a[4])
2473 local subarray = { }
2474 for n=1,execstackptr do
2475
2476
2477 local v = execstack[n]
2478 thearray[n] = v
2479 subarray[n] = v
2480 a[5] = a[5] + 1
2481 end
2482 push_opstack { 'array', 'unlimited', 'literal', add_VM(subarray), execstackptr, execstackptr, "" }
2483 return true
2484end
2485
2486
2487
2488
2489function operators.quit()
2490 while execstackptr >= 0 do
2491 execstack[execstackptr] = nil
2492 execstackptr = execstackptr - 1
2493 end
2494 return true
2495end
2496
2497
2498
2499function operators.start()
2500 return true
2501end
2502
2503
2504
2505
2506
2507
2508function operators.type()
2509 local a = pop_opstack()
2510 if not a then
2511 return ps_error('stackunderflow')
2512 end
2513 push_opstack { "name", "unlimited", "executable", add_VM(a[1] .. "type") }
2514 return true
2515end
2516
2517function operators.cvlit()
2518 local a = get_opstack()
2519 if not a then
2520 return ps_error('stackunderflow')
2521 end
2522 a[3] = 'literal'
2523 return true
2524end
2525
2526function operators.cvx()
2527 local a = get_opstack()
2528 if not a then
2529 return ps_error('stackunderflow')
2530 end
2531 a[3] = 'executable'
2532 return true
2533end
2534
2535function operators.xcheck()
2536 local a = pop_opstack()
2537 if not a then
2538 return ps_error('stackunderflow')
2539 end
2540 push_opstack((a[3] == 'executable') and b_true or b_false)
2541 return true
2542end
2543
2544function operators.executeonly()
2545 local a = pop_opstack()
2546 if not a then
2547 return ps_error('stackunderflow')
2548 end
2549 local ta = a[1]
2550 if ta == 'string' or ta == 'file' or ta == 'array' then
2551 if a[2] == 'noaccess' then
2552 return ps_error('invalidaccess')
2553 end
2554 a[2] = 'execute-only'
2555 else
2556 return ps_error('typecheck')
2557 end
2558 push_opstack(a)
2559 return true
2560end
2561
2562function operators.noaccess()
2563 local a = pop_opstack()
2564 if not a then
2565 return ps_error('stackunderflow')
2566 end
2567 local ta = a[1]
2568 if ta == 'string' or ta == 'file' or ta == 'array' then
2569 if a[2] == 'noaccess' then
2570 return ps_error('invalidaccess')
2571 end
2572 a[2] = 'noaccess'
2573 elseif ta == "dict" then
2574 local thedict = get_VM(a[4])
2575 if thedict.access == 'noaccess' then
2576 return ps_error('invalidaccess')
2577 end
2578 thedict.access = 'noaccess'
2579 else
2580 return ps_error('typecheck')
2581 end
2582 push_opstack(a)
2583 return true
2584end
2585
2586function operators.readonly()
2587 local a = pop_opstack()
2588 if not a then
2589 return ps_error('stackunderflow')
2590 end
2591 local ta = a[1]
2592 if ta == 'string' or ta == 'file' or ta == 'array' then
2593 local aa = a[2]
2594 if aa == 'noaccess' or aa == 'execute-only' then
2595 return ps_error('invalidaccess')
2596 end
2597 a[2] = 'read-only'
2598 elseif ta == 'dict' then
2599 local thedict = get_VM(a[4])
2600 local access = thedict.access
2601 if access == 'noaccess' or access == 'execute-only' then
2602 return ps_error('invalidaccess')
2603 end
2604 thedict.access = 'read-only'
2605 else
2606 return ps_error('typecheck')
2607 end
2608 push_opstack(a)
2609 return true
2610end
2611
2612function operators.rcheck()
2613 local a = pop_opstack()
2614 if not a then
2615 return ps_error('stackunderflow')
2616 end
2617 local ta = a[1]
2618 local aa
2619 if ta == 'string' or ta == 'file' or ta == 'array' then
2620 aa = a[2]
2621 elseif ta == 'dict' then
2622 aa = get_VM(a[4]).access
2623 else
2624 return ps_error('typecheck')
2625 end
2626 push_opstack((aa == 'unlimited' or aa == 'read-only') and p_true or p_false)
2627 return true
2628end
2629
2630function operators.wcheck()
2631 local a = pop_opstack()
2632 if not a then
2633 return ps_error('stackunderflow')
2634 end
2635 local ta = a[1]
2636 local aa
2637 if ta == 'string' or ta == 'file' or ta == 'array' then
2638 aa = a[2]
2639 elseif ta == 'dict' then
2640 local thedict = get_VM(a[4])
2641 aa = thedict.access
2642 else
2643 return ps_error('typecheck')
2644 end
2645 push_opstack((aa == 'unlimited') and p_true or p_false)
2646 return true
2647end
2648
2649function operators.cvi()
2650 local a = pop_opstack()
2651 if not a then
2652 return ps_error('stackunderflow')
2653 end
2654 local ta = a[1]
2655 if ta == 'string' then
2656 push_opstack(a)
2657 local ret, err = operators.token()
2658 if not ret then
2659 return ret, err
2660 end
2661 local b = pop_opstack()
2662 if b[4] == false then
2663 return ps_error('syntaxerror')
2664 end
2665 a = pop_opstack()
2666 pop_opstack()
2667 ta = a[1]
2668 end
2669 local aa = a[2]
2670 if not (aa == 'unlimited' or aa == 'read-only') then
2671 return ps_error('invalidaccess')
2672 end
2673 if ta == 'integer' then
2674 push_opstack(a)
2675 elseif ta == 'real' then
2676 local va = a[4]
2677 local c = va < 0 and -floor(-va) or floor(ava)
2678 if abs(c) > MAX_INT then
2679 return ps_error('rangecheck')
2680 end
2681 push_opstack { 'integer', 'unlimited', 'literal', c }
2682 else
2683 return ps_error('typecheck')
2684 end
2685 return true
2686end
2687
2688function operators.cvn()
2689 local a = pop_opstack()
2690 if not a then
2691 return ps_error('stackunderflow')
2692 end
2693 local ta, aa = a[1], a[2]
2694 local ta = a[1]
2695 if ta ~= 'string' then
2696 return ps_error('typecheck')
2697 end
2698 if aa == 'execute-only' or aa == 'noaccess' then
2699 return ps_error('invalidaccess')
2700 end
2701 push_opstack { 'name', aa, a[3], add_VM(get_VM(a[4])) }
2702 return true
2703end
2704
2705function operators.cvr()
2706 local a = pop_opstack()
2707 if not a then
2708 return ps_error('stackunderflow')
2709 end
2710 local ta = a[1]
2711 if ta == 'string' then
2712 push_opstack(a)
2713 local ret, err = operators.token()
2714 if not ret then
2715 return ret, err
2716 end
2717 local b = pop_opstack()
2718 if b[4] == false then
2719 return ps_error('syntaxerror')
2720 end
2721 a = pop_opstack()
2722 pop_opstack()
2723 ta = a[1]
2724 end
2725 local aa = a[2]
2726 if not (aa == 'unlimited' or aa == 'read-only') then
2727 return ps_error('invalidaccess')
2728 end
2729 if ta == 'integer' then
2730 push_opstack { 'real', 'unlimited', 'literal', a[4] }
2731 elseif ta == 'real' then
2732 push_opstack(a)
2733 else
2734 return ps_error('typecheck')
2735 end
2736 return true
2737end
2738
2739do
2740
2741 local byte0 = byte('0')
2742 local byteA = byte('A') - 10
2743
2744 function operators.cvrs()
2745 local c = pop_opstack()
2746 local b = pop_opstack()
2747 local a = pop_opstack()
2748 if not a then
2749 return ps_error('stackunderflow')
2750 end
2751 local ta, tb, tc = a[1], b[1], c[1]
2752 if not (ta == 'integer' or ta == 'real') then
2753 return ps_error('typecheck')
2754 end
2755 if not tb == 'integer' then
2756 return ps_error('typecheck')
2757 end
2758 if not tc == 'string' then
2759 return ps_error('typecheck')
2760 end
2761 if not c[2] == 'unlimited' then
2762 return ps_error('invalidaccess')
2763 end
2764 local va, vb, vc = a[4], b[4], c[4]
2765 if (vb < 2 or vb > 36) then
2766 return ps_error('rangecheck')
2767 end
2768 if ta == 'real' then
2769 push_opstack(a)
2770 local ret, err = operators.cvi()
2771 if ret then
2772 return ret, err
2773 end
2774 a = pop_opstack()
2775 end
2776
2777 local decimal = va
2778 local str = { }
2779 local n = 0
2780 while decimal > 0 do
2781 local digit = decimal % vb
2782 n = n + 1
2783 str[n] = digit < 10 and char(digit+byte0) or char(digit+byteA)
2784 decimal = floor(decimal/vb)
2785 end
2786 if n > c[6] then
2787 return ps_error('rangecheck')
2788 end
2789 str = concat(reverse(str))
2790 local thestring = get_VM(vc)
2791 VM[va] = str .. sub(thestring,n+1,-1)
2792 push_opstack { c[1], c[2], c[3], add_VM(repl), n, n }
2793 return true
2794 end
2795
2796end
2797
2798function operators.cvs()
2799 local b = pop_opstack()
2800 local a = pop_opstack()
2801 if not 4 then
2802 return ps_error('stackunderflow')
2803 end
2804 local ta, tb = a[1], b[1]
2805 local ab = b[2]
2806 if not tb == 'string' then
2807 return ps_error('typecheck')
2808 end
2809 if not ab == 'unlimited' then
2810 return ps_error('invalidaccess')
2811 end
2812 local va, vb = a[4], b[4]
2813 if ta == 'real' then
2814 if floor(va) == va then
2815 va = tostring(va) .. '.0'
2816 else
2817 va = tostring(va)
2818 end
2819 elseif ta == 'integer' then
2820 va = tostring(va)
2821 elseif ta == 'string' or ta == 'name' then
2822 va = get_VM(va)
2823 elseif ta == 'operator' then
2824 va = a[5]
2825 elseif ta == 'boolean' then
2826 va = tostring(va)
2827 else
2828 va = "--nostringval--"
2829 end
2830 local n = #va
2831 if n > b[6] then
2832 return ps_error('rangecheck')
2833 end
2834 local thestring = get_VM(vb)
2835 VM[vb] = va .. sub(thestring,n+1,-1)
2836 push_opstack { tb, ab, b[3], add_VM(va), n, n }
2837 return true
2838end
2839
2840
2841
2842
2843
2844
2845
2846function operators.file()
2847 local b = pop_opstack()
2848 local a = pop_opstack()
2849 if not a then
2850 return ps_error('stackunderflow')
2851 end
2852 if b[1] ~= 'string' then
2853 return ps_error('typecheck')
2854 end
2855 if a[1] ~= 'string' then
2856 return ps_error('typecheck')
2857 end
2858 local fmode = get_VM(b[4])
2859 local fname = get_VM(a[4])
2860
2861 if fmode ~= "r" and fmode ~= "w" and fmode ~= "a" then
2862 return ps_error('typecheck')
2863 end
2864 if fname == "%stdin" then
2865
2866 if fmode ~= "r" then
2867 return ps_error('invalidfileaccess')
2868 end
2869 push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, io.stdin }
2870 elseif fname == "%stdout" then
2871
2872 if fmode == "r" then
2873 return ps_error('invalidfileaccess')
2874 end
2875 push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, io.stdout }
2876 elseif fname == "%stderr" then
2877
2878 if fmode == "r" then
2879 return ps_error('invalidfileaccess')
2880 end
2881 push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, io.stderr }
2882 elseif fname == "%statementedit" or fname == "%lineedit"then
2883 return ps_error('invalidfileaccess')
2884 else
2885
2886 local myfile, error = io.open(fname,fmode)
2887 if not myfile then
2888 return ps_error('undefinedfilename')
2889 end
2890 if fmode == 'r' then
2891 l = myfile:read("*a")
2892 if not l then
2893 return ps_error('invalidfileaccess')
2894 end
2895
2896 push_opstack { 'file', 'unlimited', 'literal', add_VM(l), 1, #l, fmode, myfile}
2897 else
2898 push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, myfile}
2899 end
2900 end
2901 return true
2902end
2903
2904function operators.read()
2905 local a = pop_opstack()
2906 if not a then
2907 return ps_error('stackunderflow')
2908 end
2909 if a[1] ~= 'file' then
2910 return ps_error('typecheck')
2911 end
2912 if a[7] ~= 'r' then
2913 return ps_error('invalidaccess')
2914 end
2915 local b
2916 local v = a[4]
2917 local f = a[8]
2918 if v > 0 then
2919 local thestr = get_VM(v)
2920 local n = a[5]
2921 if n < a[6] then
2922 byte = sub(thestr,n,n+1)
2923
2924 end
2925 else
2926 b = f:read(1)
2927 end
2928 if b then
2929 push_opstack { 'integer', 'unlimited', 'literal', byte(b) }
2930 push_opstack (p_true)
2931 else
2932 f:close()
2933 push_opstack (p_false)
2934 end
2935 return true
2936end
2937
2938function operators.write()
2939 local b = pop_opstack()
2940 local a = pop_opstack()
2941 if not a then
2942 return ps_error('stackunderflow')
2943 end
2944 if b[1] ~= 'integer' then
2945 return ps_error('typecheck')
2946 end
2947 if a[1] ~= 'file' then
2948 return ps_error('typecheck')
2949 end
2950 if a[7] == 'r' then
2951 return ps_error('ioerror')
2952 end
2953 a[8]:write(char(b[4] % 256))
2954 return true
2955end
2956
2957function operators.writestring()
2958 local b = pop_opstack()
2959 local a = pop_opstack()
2960 if not a then
2961 return ps_error('stackunderflow')
2962 end
2963 if b[1] ~= 'string' then
2964 return ps_error('typecheck')
2965 end
2966 if a[1] ~= 'file' then
2967 return ps_error('typecheck')
2968 end
2969 if a[7] == 'r' then
2970 return ps_error('ioerror')
2971 end
2972 a[8]:write(get_VM(b[4]))
2973 return true
2974end
2975
2976function operators.writehexstring()
2977 local b = pop_opstack()
2978 local a = pop_opstack()
2979 if not a then
2980 return ps_error('stackunderflow')
2981 end
2982 if b[1] ~= 'string' then
2983 return ps_error('typecheck')
2984 end
2985 if a[1] ~= 'file' then
2986 return ps_error('typecheck')
2987 end
2988 if a[7] == 'r' then
2989 return ps_error('ioerror')
2990 end
2991 local f = a[8]
2992 local s = get_VM(b[4])
2993 for w in gmatch(s,".") do
2994 f:write(format("%x",byte(w)))
2995 end
2996 return true
2997end
2998
2999do
3000
3001 local function get_string_line(a)
3002 local str = get_VM(a[4])
3003 local start = a[5]
3004 local theend = a[6]
3005 if start == theend then
3006 return nil
3007 end
3008 str = match(str,"[\n\r]*([^\n\r]*)",start)
3009 a[5] = a[5] + #str + 1
3010 return str
3011 end
3012
3013 local function get_hexstring_line (a,b)
3014 local thestring = get_VM(a[4])
3015 local start, theend = a[5], a[6]
3016 if start == theend then
3017 return nil
3018 end
3019 local prefix, result, n = nil, { }, 0
3020 local nmax = b[6]
3021 while start < theend do
3022 local b = sub(thestring,start,start)
3023 if not b then
3024 break
3025 end
3026 local hexbyte = tonumber(b,16)
3027 if not hexbyte then
3028
3029 elseif prefix then
3030 n = n + 1
3031 result[n] = char(prefix*16+hexbyte)
3032 if n == nmax then
3033 break
3034 else
3035 prefix = nil
3036 end
3037 else
3038 prefix = hexbyte
3039 end
3040 start = start + 1
3041 end
3042 a[5] = start + 1
3043 return concat(result)
3044 end
3045
3046 function operators.readline()
3047 local b = pop_opstack()
3048 local a = pop_opstack()
3049 if not a then
3050 return ps_error('stackunderflow')
3051 end
3052 if a[1] ~= 'file' then
3053 return ps_error('typecheck')
3054 end
3055 if a[7] ~= 'r' then
3056 return ps_error('invalidaccess')
3057 end
3058 local va = a[4]
3059 if va > 0 then
3060 va = get_string_line(a)
3061 else
3062 va = a[8]:read('*l')
3063 end
3064 if not va then
3065 push_opstack { 'string', 'unlimited', 'literal', add_VM(''), 0, 0 }
3066 push_opstack (p_false)
3067 else
3068 local n = #va
3069 if n > b[6] then
3070 return ps_error('rangecheck')
3071 end
3072 local thestring = get_VM(b[4])
3073 VM[b[4]] = va .. sub(thestring,#va+1, -1)
3074 push_opstack { 'string', 'unlimited', 'literal', add_VM(va), n, n }
3075 push_opstack (p_true)
3076 end
3077 return true
3078 end
3079
3080 function operators.readhexstring()
3081 local b = pop_opstack()
3082 local a = pop_opstack()
3083 if not a then
3084 return ps_error('stackunderflow')
3085 end
3086 local ta = a[1]
3087 if not (ta == 'string' or ta == 'file') then
3088 return ps_error('typecheck')
3089 end
3090 local thefile = a[8]
3091 local va = a[4]
3092 if va > 0 then
3093 va = get_hexstring_line (a,b)
3094 else
3095 local prefix, result, n = nil, { }, 0
3096
3097 while true do
3098 local b = thefile:read(1)
3099 if not b then
3100 break
3101 end
3102 local hexbyte = tonumber(b,16)
3103 local nmax = b[6]
3104 if not hexbyte then
3105
3106 elseif prefix then
3107 n = n + 1
3108 result[n] = char(prefix*16+hexbyte)
3109 if n == nmax then
3110 break
3111 else
3112 prefix = nil
3113 end
3114 else
3115 prefix = hexbyte
3116 end
3117 end
3118 va = concat(result)
3119 end
3120 local thestring = get_VM(b[4])
3121 local n = #va
3122 VM[b[4]] = repl .. sub(thestring,n+1,-1)
3123 push_opstack { b[1], b[2], b[3], add_VM(va), n, n }
3124 push_opstack ((n == b[6]) and p_true or p_false)
3125 return true
3126 end
3127
3128end
3129
3130function operators.flush()
3131 io.flush()
3132 return true
3133end
3134
3135function operators.bytesavailable()
3136 local a = pop_opstack()
3137 if not a then
3138 return ps_error('stackunderflow')
3139 end
3140 if a[1] ~= 'file' then
3141 return ps_error('typecheck')
3142 end
3143 if a[7] ~= 'r' then
3144 return ps_error('typecheck')
3145 end
3146 local waiting = (a[4] > 0) and (a[6] - a[5] + 1) or -1
3147 push_opstack { "integer", "unlimited", "literal", waiting }
3148 return true
3149end
3150
3151
3152
3153function operators.resetfile()
3154 local a = pop_opstack()
3155 if not a then
3156 return ps_error('stackunderflow')
3157 end
3158 if a[1] ~= 'file' then
3159 return ps_error('typecheck')
3160 end
3161 return true
3162end
3163
3164function operators.flushfile()
3165 local a = pop_opstack()
3166 if not a then
3167 return ps_error('stackunderflow')
3168 end
3169 if a[1] ~= 'file' then
3170 return ps_error('typecheck')
3171 end
3172 if a[4] > 0 then
3173 a[5] = a[6]
3174 else
3175 a[8]:flush()
3176 end
3177 return true
3178end
3179
3180function operators.closefile()
3181 local a = pop_opstack()
3182 if not a then
3183 return ps_error('stackunderflow')
3184 end
3185 if a[1] ~= 'file' then
3186 return ps_error('typecheck')
3187 end
3188 if a[7] == 'r' then
3189 a[5] = a[6]
3190 else
3191 push_opstack(a)
3192 operators.flushfile()
3193 end
3194 a[8]:close()
3195 return true
3196end
3197
3198function operators.status()
3199 local a = pop_opstack()
3200 if not a then
3201 return ps_error('stackunderflow')
3202 end
3203 if a[1] ~= 'file' then
3204 return ps_error('typecheck')
3205 end
3206 local state = io.type(a[8])
3207 push_opstack { "boolean", 'unlimited', 'literal', not state or state == "closed file" }
3208 return true
3209end
3210
3211function operators.run()
3212 push_opstack { "string", "unlimited", "literal", add_VM("r"), 1, 1 }
3213 local ret, err = operators.file()
3214 if not ret then
3215 return ret, err
3216 end
3217 ret, err = operators.cvx()
3218 if not ret then
3219 return ret, err
3220 end
3221 local a = pop_opstack()
3222 push_execstack { ".run", "unlimited", "literal", false }
3223 local curstack = execstackptr
3224 local thefile = a[8]
3225 push_execstack(a)
3226 while curstack < execstackptr do
3227 do_exec()
3228 end
3229 local state = io.type(thefile)
3230 if not state or state == "closed file" then
3231
3232 else
3233 thefile:close()
3234 end
3235 if execstackptr > 0 then
3236 local entry = execstack[execstackptr]
3237 if entry[1] == '.run' and entry[4] == true then
3238 pop_execstack()
3239 end
3240 end
3241 return true
3242end
3243
3244function operators.currentfile()
3245 local n = execstackptr
3246 while n >= 0 do
3247 local entry = execstack[n]
3248 if entry[1] == 'file' and entry[7] == 'r' then
3249 push_opstack(entry)
3250 return true
3251 end
3252 n = n - 1
3253 end
3254 push_opstack { 'file', 'unlimited', 'executable', add_VM(''), 0, 0, 'r', stdin }
3255 return true
3256end
3257
3258function operators.print()
3259 local a = pop_opstack()
3260 if not a then return
3261 ps_error('stackunderflow')
3262 end
3263 if a[1] ~= 'string' then
3264 return ps_error('typecheck')
3265 end
3266 report(get_VM(a[4]))
3267end
3268
3269
3270
3271
3272
3273
3274
3275do
3276
3277 local pattern = Cs(
3278 Cc("(")
3279 * (
3280 P("\n") / "\\n"
3281 + P("\r") / "\\r"
3282 + P("(") / "\\("
3283 + P(")") / "\\)"
3284 + P("\\") / "\\\\"
3285 + P("\b") / "\\b"
3286 + P("\t") / "\\t"
3287 + P("\f") / "\\f"
3288 + R("\000\032","\127\255") / tonumber / formatters["\\%03o"]
3289 + P(1)
3290 )^0
3291 * Cc(")")
3292 )
3293
3294
3295
3296 local function do_operator_equal(a)
3297 local ta, va = a[1], a[4]
3298 if ta == 'real' then
3299 if floor(va) == va then
3300 return tostring(va .. '.0')
3301 else
3302 return tostring(va)
3303 end
3304 elseif ta == 'integer' then
3305 return tostring(va)
3306 elseif ta == 'string' then
3307 return lpegmatch(pattern,get_VM(va))
3308 elseif ta == 'boolean' then
3309 return tostring(va)
3310 elseif ta == 'operator' then
3311 return '--' .. a[5] .. '--'
3312 elseif ta == 'name' then
3313 if a[3] == 'literal' then
3314 return '/' .. get_VM(va)
3315 else
3316 return get_VM(va)
3317 end
3318 elseif ta == 'array' then
3319 va = get_VM(va)
3320 local isexec = a[3] == 'executable'
3321 local result = { isexec and "{" or "[" }
3322 local n = 1
3323 for i=1,#va do
3324 n = n + 1
3325 result[n] = do_operator_equal(va[i])
3326 end
3327 result[n+1] = isexec and "}" or "]"
3328 return concat(result," ")
3329 elseif ta == 'null' then
3330 return 'null'
3331 elseif ta == 'dict' then
3332 return '-dicttype-'
3333 elseif ta == 'save' then
3334 return '-savetype-'
3335 elseif ta == 'mark' then
3336 return '-marktype-'
3337 elseif ta == 'file' then
3338 return '-filetype-'
3339 elseif ta == 'font' then
3340 return '-fonttype-'
3341 end
3342 end
3343
3344 function operators.equal()
3345 local a = pop_opstack()
3346 if not a then
3347 return ps_error('stackunderflow')
3348 end
3349 report(do_operator_equal(a))
3350 return true
3351 end
3352
3353end
3354
3355local function commonstack(seperator)
3356 for n=1,opstackptr do
3357 push_opstack { 'string', 'unlimited', 'literal', add_VM(seperator), 1 ,1 }
3358 push_opstack(opstack[n])
3359 push_execstack { 'operator','unlimited','executable', operators.print, 'print' }
3360 push_execstack { 'operator','unlimited','executable', operators.equal, '==' }
3361 end
3362 return true
3363end
3364
3365function operators.pstack()
3366 return commonstack("\n")
3367end
3368
3369function operators.stack()
3370 return commonstack(" ")
3371end
3372
3373
3374
3375function operators.echo()
3376 local a = pop_opstack()
3377 if not a then
3378 return ps_error('stackunderflow')
3379 end
3380 if a[1] ~= 'boolean' then
3381 return ps_error('typecheck')
3382 end
3383 return true
3384end
3385
3386
3387
3388
3389
3390
3391
3392
3393local savelevel = 0
3394
3395initializers[#initializers+1] = function(reset)
3396 savelevel = 0
3397end
3398
3399function operators.save()
3400 local saved_VM = { }
3401
3402 for k1 = 1, #VM do
3403 local v1 = VM[k1]
3404 if type(v1) == "table" then
3405 local t1 = { }
3406 saved_VM[k1] = t1
3407
3408 for k2=1,#v1 do
3409 local v2 = v1[k2]
3410 if type(v2) == "table" then
3411 local t2 = { }
3412 t1[k2] = t2
3413
3414 for k3=1,#v2 do
3415 local v3 = v2[k3]
3416 t2[k3] = v3
3417 end
3418 else
3419 t1[k2] = v2
3420 end
3421 end
3422 else
3423 saved_VM[k1] = v1
3424 end
3425 end
3426 push_gsstack { 'save', copy_gsstate() }
3427 savelevel = savelevel + 1
3428 push_opstack { 'save', 'unlimited', 'executable', add_VM(saved_VM) }
3429end
3430
3431function operators.save()
3432 local saved_VM = table.copy(VM)
3433 push_gsstack { 'save', copy_gsstate() }
3434 savelevel = savelevel + 1
3435 push_opstack { 'save', 'unlimited', 'executable', add_VM(saved_VM) }
3436end
3437
3438do
3439
3440 local function validstack(stack,index,saved_VM)
3441
3442
3443 for i=index,1,-1 do
3444 local v = stack[i]
3445 if type(v) == "table" then
3446 local tv = v[1]
3447 if tv == "save" or tv == "string" or tv == "array" or tv == "dict" or tv == "name" or tv == "file" then
3448
3449 if v[4] > #saved_VM then
3450 return false
3451 end
3452 end
3453 end
3454 end
3455 return true
3456 end
3457
3458 function operators.restore()
3459 local a = pop_opstack()
3460 if not a then
3461 return ps_error('stackunderflow')
3462 end
3463 if a[1] ~= 'save' then
3464 return ps_error('typecheck')
3465 end
3466 if a[4] == 0 or savelevel == 0 then
3467 return ps_error('invalidrestore')
3468 end
3469 local saved_VM = get_VM(a[4])
3470 if directvm then
3471 else
3472 if not validstack(execstack,execstackptr,saved_VM) then
3473 return ps_error('invalidrestore')
3474 end
3475 if not validstack(dictstack,dictstackptr,saved_VM) then
3476 return ps_error('invalidrestore')
3477 end
3478 if not validstack(opstack,opstackptr,saved_VM) then
3479 return ps_error('invalidrestore')
3480 end
3481 end
3482 while gsstackptr > 0 do
3483 local g = gsstack[gsstackptr]
3484 gsstackptr = gsstackptr - 1
3485 if g[1] == "save" then
3486 gsstate = g[2]
3487 return
3488 end
3489 end
3490 a[4] = 0
3491 savelevel = savelevel - 1
3492 VM = saved_VM
3493 end
3494
3495end
3496
3497function operators.vmstatus()
3498 local n = 0
3499 push_opstack { 'integer', 'unlimited', 'literal', savelevel }
3500 push_opstack { 'integer', 'unlimited', 'literal', n }
3501 push_opstack { 'integer', 'unlimited', 'literal', n }
3502 return true
3503end
3504
3505
3506
3507
3508
3509
3510
3511local function bind()
3512 local a = pop_opstack()
3513 if not a then
3514 return true
3515 end
3516 if not a[1] == 'array' then
3517 return ps_error('typecheck')
3518 end
3519 local proc = get_VM(a[4])
3520 for i=1,#proc do
3521 local v = proc[i]
3522 local t = v[1]
3523 if t == 'name' then
3524 if v[3] == 'executable' then
3525 local op = lookup(get_VM(v[4]))
3526 if op and op[1] == 'operator' then
3527 proc[i] = op
3528 end
3529 end
3530 elseif t == 'array' then
3531 if v[2] == 'unlimited' then
3532 push_opstack(v)
3533 bind()
3534 pop_opstack()
3535 proc[i][2] = 'read-only'
3536 end
3537 end
3538 end
3539 push_opstack(a)
3540end
3541
3542operators.bind = bind
3543
3544function operators.null()
3545 push_opstack { 'null', 'unlimited', 'literal' }
3546 return true
3547end
3548
3549function operators.usertime()
3550 push_opstack { 'integer', 'unlimited', 'literal', floor(os.clock() * 1000) }
3551 return true
3552end
3553
3554function operators.version()
3555 push_opstack { 'string', 'unlimited', 'literal', add_VM('23.0') }
3556 return true
3557end
3558
3559
3560
3561
3562
3563
3564
3565
3566function operators.gsave()
3567 push_gsstack { 'gsave', copy_gsstate() }
3568 currentpage[#currentpage+1] = {
3569 type = 'gsave',
3570 }
3571 return true
3572end
3573
3574function operators.grestore()
3575 if gsstackptr > 0 then
3576 local g = gsstack[gsstackptr]
3577 if g[1] == "gsave" then
3578 gsstackptr = gsstackptr - 1
3579 gsstate = g[2]
3580 end
3581 end
3582 currentpage[#currentpage+1] = {
3583 type = 'grestore',
3584 }
3585 return true
3586end
3587
3588function operators.grestoreall()
3589 for i=gsstackptr,1,-1 do
3590 local g = gsstack[i]
3591 if g[1] == "save" then
3592 gsstate = g[2]
3593 gsstackptr = i
3594 return true
3595 end
3596 end
3597 gsstackptr = 0
3598 return true
3599end
3600
3601function operators.initgraphics()
3602 local newstate = copy_gsstate()
3603 newstate.matrix = { 1, 0, 0, 1, 0, 0 }
3604 newstate.color = { gray = 0, hsb = { }, rgb = { }, cmyk = { }, type = "gray" }
3605 newstate.position = { }
3606 newstate.path = { }
3607 newstate.linewidth = 1
3608 newstate.linecap = 0
3609 newstate.linejoin = 0
3610 newstate.miterlimit = 10
3611 newstate.dashpattern = { }
3612 newstate.dashoffset = 0
3613 gsstate = newstate
3614 device.initgraphics()
3615 operators.initclip()
3616 return true
3617end
3618
3619function operators.setlinewidth()
3620 local a = pop_opstack()
3621 if not a then
3622 return ps_error('stackunderflow')
3623 end
3624 local t = a[1]
3625 if not (t == 'integer' or t == 'real') then
3626 return ps_error('typecheck')
3627 end
3628 gsstate.linewidth = a[4]
3629 return true
3630end
3631
3632function operators.currentlinewidth()
3633 local w = gsstate.linewidth
3634 push_opstack {
3635 (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer',
3636 'unlimited',
3637 'literal',
3638 w,
3639 }
3640 return true
3641end
3642
3643function operators.setlinecap()
3644 local a = pop_opstack()
3645 if not a then
3646 return ps_error('stackunderflow')
3647 end
3648 if a[1] ~= 'integer' then
3649 return ps_error('typecheck')
3650 end
3651 local c = a[4]
3652 if c > 2 or c < 0 then
3653 return ps_error('rangecheck')
3654 end
3655 gsstate.linecap = c
3656 return true
3657end
3658
3659function operators.currentlinecap()
3660 push_opstack { 'integer', 'unlimited', 'literal', gsstate.linecap }
3661 return true
3662end
3663
3664function operators.setlinejoin()
3665 local a = pop_opstack()
3666 if not a then
3667 return ps_error('stackunderflow')
3668 end
3669 if a[1] ~= 'integer' then
3670 return ps_error('typecheck')
3671 end
3672 local j = a[4]
3673 if j > 2 or j < 0 then
3674 return ps_error('rangecheck')
3675 end
3676 gsstate.linejoin = j
3677 return true
3678end
3679
3680function operators.currentlinejoin()
3681 push_opstack { 'integer', 'unlimited', 'literal', gsstate.linejoin }
3682 return true
3683end
3684
3685function operators.setmiterlimit()
3686 local a = pop_opstack()
3687 if not a then
3688 return ps_error('stackunderflow')
3689 end
3690 local t = a[1]
3691 if not (t == 'integer' or t == 'real') then
3692 return ps_error('typecheck')
3693 end
3694 local m = a[4]
3695 if m < 1 then
3696 return ps_error('rangecheck')
3697 end
3698 gsstate.miterlimit = m
3699 return true
3700end
3701
3702function operators.currentmiterlimit()
3703 local w = gsstate.miterlimit
3704 push_opstack {
3705 (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer',
3706 'unlimited',
3707 'literal',
3708 w
3709 }
3710 return true
3711end
3712
3713function operators.setdash()
3714 local b = pop_opstack()
3715 local a = pop_opstack()
3716 if not a then
3717 return ps_error('stackunderflow')
3718 end
3719 local ta, tb = a[1], b[1]
3720 if ta ~= 'array' then
3721 return ps_error('typecheck')
3722 end
3723 if not (tb == 'integer' or tb == 'real') then
3724 return ps_error('typecheck')
3725 end
3726 local pattern = { }
3727 local total = 0
3728 local thearray = get_VM(a[4])
3729 for i=1,#thearray do
3730 local a = thearray[i]
3731 local ta, va = a[1], a[4]
3732 if ta ~= "integer" then
3733 return ps_error('typecheck')
3734 end
3735 if va < 0 then
3736 return ps_error('limitcheck')
3737 end
3738 total = total + va
3739 pattern[#pattern+1] = va
3740 end
3741 if #pattern > 0 and total == 0 then
3742 return ps_error('limitcheck')
3743 end
3744 gsstate.dashpattern = pattern
3745 gsstate.dashoffset = b[4]
3746 return true
3747end
3748
3749function operators.currentdash()
3750 local thearray = gsstate.dashpattern
3751 local pattern = { }
3752 for i=1,#thearray do
3753 pattern[i] = { 'integer', 'unlimited', 'literal', thearray[i] }
3754 end
3755 push_opstack { 'array', 'unlimited', 'literal', add_VM(pattern), #pattern, #pattern }
3756 local w = gsstate.dashoffset
3757 push_opstack {
3758 (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer', 'unlimited', 'literal', w
3759 }
3760 return true
3761end
3762
3763function operators.setflat()
3764 local a = pop_opstack()
3765 if not a then
3766 return ps_error('stackunderflow')
3767 end
3768 local ta, va = a[1], a[4]
3769 if not (ta == 'integer' or ta == 'real') then
3770 return ps_error('typecheck')
3771 end
3772 gsstate.flatness = va
3773 return true
3774end
3775
3776function operators.currentflat()
3777 local w = gsstate.flatness
3778 push_opstack {
3779 (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer', 'unlimited', 'literal', w
3780 }
3781 return true
3782end
3783
3784
3785
3786
3787
3788
3789do
3790
3791 local function rgb_to_gray (r, g, b)
3792 return 0.30 * r + 0.59 * g + 0.11 * b
3793 end
3794
3795 local function cmyk_to_gray (c, m, y, k)
3796 return 0.30 * (1.0 - min(1.0,c+k)) + 0.59 * (1.0 - min(1.0,m+k)) + 0.11 * (1.0 - min(1.0,y+k))
3797 end
3798
3799 local function cmyk_to_rgb (c, m, y, k)
3800 return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
3801 end
3802
3803 local function rgb_to_hsv(r, g, b)
3804 local offset, maximum, other_1, other_2
3805 if r >= g and r >= b then
3806 offset, maximum, other_1, other_2 = 0, r, g, b
3807 elseif g >= r and g >= b then
3808 offset, maximum, other_1, other_2 = 2, g, b, r
3809 else
3810 offset, maximum, other_1, other_2 = 4, b, r, g
3811 end
3812 if maximum == 0 then
3813 return 0, 0, 0
3814 end
3815 local minimum = other_1 < other_2 and other_1 or other_2
3816 if maximum == minimum then
3817 return 0, 0, maximum
3818 end
3819 local delta = maximum - minimum
3820 return (offset + (other_1-other_2)/delta)/6, delta/maximum, maximum
3821 end
3822
3823 local function gray_to_hsv (col)
3824 return 0, 0, col
3825 end
3826
3827 local function gray_to_rgb (col)
3828 return 1-col, 1-col, 1-col
3829 end
3830
3831 local function gray_to_cmyk (col)
3832 return 0, 0, 0, col
3833 end
3834
3835 local function hsv_to_rgb(h,s,v)
3836 local hi = floor(h * 6.0) % 6
3837 local f = (h * 6) - floor(h * 6)
3838 local p = v * (1 - s)
3839 local q = v * (1 - f * s)
3840 local t = v * (1 - (1 - f) * s)
3841 if hi == 0 then
3842 return v, t, p
3843 elseif hi == 1 then
3844 return q, v, p
3845 elseif hi == 2 then
3846 return p, v, t
3847 elseif hi == 3 then
3848 return p, q, v
3849 elseif hi == 4 then
3850 return t, p, v
3851 elseif hi == 5 then
3852 return v, p, q
3853 end
3854 end
3855
3856 local function hsv_to_gray(h,s,v)
3857 return rgb_to_gray(hsv_to_rgb(h,s,v))
3858 end
3859
3860
3861
3862 function operators.setgray()
3863 local g = pop_opstack()
3864 if not g then
3865 return ps_error('stackunderflow')
3866 end
3867 local gt = g[1]
3868 if not (gt == 'integer' or gt == 'real') then
3869 return ps_error('typecheck')
3870 end
3871 local gv = g[4]
3872 local color = gsstate.color
3873 color.type = "gray"
3874 color.gray = (gv < 0 and 0) or (gv > 1 and 1) or gv
3875 return true
3876 end
3877
3878 function operators.currentgray()
3879 local color = gsstate.color
3880 local t = color.type
3881 local s
3882 if t == "gray" then
3883 s = color.gray
3884 elseif t == "rgb" then
3885 local col = color.rgb
3886 s = rgb_to_gray(col[1],col[2],col[3])
3887 elseif t == "cmyk" then
3888 local col = cmyk
3889 s = cmyk_to_gray(col[1],col[2],col[3],col[4])
3890 else
3891 local col = color.hsb
3892 s = hsv_to_gray(col[1],col[2],col[3])
3893 end
3894 push_opstack { (s == 0 or s == 1) and 'integer' or 'real', 'unlimited', 'literal', s }
3895 return true
3896 end
3897
3898 function operators.sethsbcolor()
3899 local b = pop_opstack()
3900 local s = pop_opstack()
3901 local h = pop_opstack()
3902 if not h then
3903 return ps_error('stackunderflow')
3904 end
3905 local ht, st, bt = h[1], s[1], b[1]
3906 if not (ht == 'integer' or ht == 'real') then
3907 return ps_error('typecheck')
3908 end
3909 if not (st == 'integer' or st == 'real') then
3910 return ps_error('typecheck')
3911 end
3912 if not (bt == 'integer' or bt == 'real') then
3913 return ps_error('typecheck')
3914 end
3915 local hv, sv, bv = h[4], s[4], b[4]
3916 local color = gsstate.color
3917 color.type = "hsb"
3918 color.hsb = {
3919 (hv < 0 and 0) or (hv > 1 and 1) or hv,
3920 (sv < 0 and 0) or (sv > 1 and 1) or sv,
3921 (bv < 0 and 0) or (bv > 1 and 1) or bv,
3922 }
3923 return true
3924 end
3925
3926 function operators.currenthsbcolor()
3927 local color = gsstate.color
3928 local t = color.type
3929 local h, s, b
3930 if t == "gray" then
3931 h, s, b = gray_to_hsv(color.gray)
3932 elseif t == "rgb" then
3933 local col = color.rgb
3934 h, s, b = rgb_to_hsv(col[1],col[2],col[3])
3935 elseif t == "cmyk" then
3936 local col = color.cmyk
3937 h, s, b = cmyk_to_hsv(col[1],col[2],col[3],col[4])
3938 else
3939 local col = color.hsb
3940 h, s, b = col[1], col[2], col[3]
3941 end
3942 push_opstack { (h == 0 or h == 1) and 'integer' or 'real', 'unlimited', 'literal', h }
3943 push_opstack { (s == 0 or s == 1) and 'integer' or 'real', 'unlimited', 'literal', s }
3944 push_opstack { (b == 0 or b == 1) and 'integer' or 'real', 'unlimited', 'literal', b }
3945 return true
3946 end
3947
3948 function operators.setrgbcolor()
3949 local b = pop_opstack()
3950 local g = pop_opstack()
3951 local r = pop_opstack()
3952 if not r then
3953 return ps_error('stackunderflow')
3954 end
3955 local rt, gt, bt = r[1], g[1], b[1]
3956 if not (rt == 'integer' or rt == 'real') then
3957 return ps_error('typecheck')
3958 end
3959 if not (gt == 'integer' or gt == 'real') then
3960 return ps_error('typecheck')
3961 end
3962 if not (bt == 'integer' or bt == 'real') then
3963 return ps_error('typecheck')
3964 end
3965 local rv, gv, bv = r[4], g[4], b[4]
3966 local color = gsstate.color
3967 color.type = "rgb"
3968 color.rgb = {
3969 (rv < 0 and 0) or (rv > 1 and 1) or rv,
3970 (gv < 0 and 0) or (gv > 1 and 1) or gv,
3971 (bv < 0 and 0) or (bv > 1 and 1) or bv,
3972 }
3973 return true
3974 end
3975
3976 function operators.currentrgbcolor()
3977 local color = gsstate.color
3978 local t = color.type
3979 local r, g, b
3980 if t == "gray" then
3981 r, g, b = gray_to_rgb(color.gray)
3982 elseif t == "rgb" then
3983 local col = color.rgb
3984 r, g, b = col[1], col[2], col[3]
3985 elseif t == "cmyk" then
3986 r, g, b = cmyk_to_rgb(color.cmyk)
3987 else
3988 local col = color.hsb
3989 r, g, b = hsv_to_rgb(col[1], col[2], col[3])
3990 end
3991 push_opstack { (r == 0 or r == 1) and "integer" or "real", 'unlimited', 'literal', r }
3992 push_opstack { (g == 0 or g == 1) and "integer" or "real", 'unlimited', 'literal', g }
3993 push_opstack { (b == 0 or b == 1) and "integer" or "real", 'unlimited', 'literal', b }
3994 return true
3995 end
3996
3997 function operators.setcmykcolor()
3998 local k = pop_opstack()
3999 local y = pop_opstack()
4000 local m = pop_opstack()
4001 local c = pop_opstack()
4002 if not c then
4003 return ps_error('stackunderflow')
4004 end
4005 local ct, mt, yt, kt = c[1], m[1], y[1], k[1]
4006 if not (ct == 'integer' or ct == 'real') then
4007 return ps_error('typecheck')
4008 end
4009 if not (mt == 'integer' or mt == 'real') then
4010 return ps_error('typecheck')
4011 end
4012 if not (yt == 'integer' or yt == 'real') then
4013 return ps_error('typecheck')
4014 end
4015 if not (kt == 'integer' or kt == 'real') then
4016 return ps_error('typecheck')
4017 end
4018 local cv, mv, yv, kv = c[4], m[4], y[4], k[4]
4019 local color = gsstate.color
4020 color.type = "cmyk"
4021 color.cmyk = {
4022 (cv < 0 and 0) or (cv > 1 and 1) or cv,
4023 (mv < 0 and 0) or (mv > 1 and 1) or mv,
4024 (yv < 0 and 0) or (yv > 1 and 1) or yv,
4025 (kv < 0 and 0) or (kv > 1 and 1) or kv,
4026 }
4027 return true
4028 end
4029
4030 function operators.currentcmykcolor()
4031 local color = gsstate.color
4032 local t = color.type
4033 local c, m, y, k
4034 if t == "gray" then
4035 c, m, y, k = gray_to_cmyk(color.gray)
4036 elseif t == "rgb" then
4037 c, m, y, k = rgb_to_cmyk(color.rgb)
4038 elseif t == "cmyk" then
4039 local col = color.cmyk
4040 c, m, y, k = col[1], col[2], col[3], col[4]
4041 else
4042 local col = color.hsb
4043 c, m, y, k = hsv_to_cmyk(col[1], col[2], col[3])
4044 end
4045 push_opstack { (c == 0 or c == 1) and "integer" or "real", 'unlimited', 'literal', c }
4046 push_opstack { (m == 0 or m == 1) and "integer" or "real", 'unlimited', 'literal', m }
4047 push_opstack { (y == 0 or y == 1) and "integer" or "real", 'unlimited', 'literal', y }
4048 push_opstack { (k == 0 or k == 1) and "integer" or "real", 'unlimited', 'literal', k }
4049 return true
4050 end
4051
4052end
4053
4054function operators.setscreen()
4055 local c = pop_opstack()
4056 local b = pop_opstack()
4057 local a = pop_opstack()
4058 if not a then
4059 return ps_error('stackunderflow')
4060 end
4061 local ta, tb, tc, ac = a[1], b[1], c[1], c[3]
4062 if not (tc == 'array' and ac == 'executable') then
4063 return ps_error('typecheck')
4064 end
4065 if not (tb == 'real' or tb == 'integer') then
4066 return ps_error('typecheck')
4067 end
4068 if not (ta == 'real' or ta == 'integer') then
4069 return ps_error('typecheck')
4070 end
4071 local va, vb, vc = a[4], b[4], c[4]
4072 if vb < 0 or vb > 360 then
4073 return ps_error('rangecheck')
4074 end
4075 if va < 0 then
4076 return ps_error('rangecheck')
4077 end
4078 gsstate.screen = { va, vb, vc }
4079 return true
4080end
4081
4082function operators.currentscreen()
4083 local w
4084 if not gsstate.screen then
4085 local popper = { 'operator', 'unlimited', 'executable', operators.pop, 'pop' }
4086 push_opstack { 'integer', 'unlimited', 'literal', 1 }
4087 push_opstack { 'integer', 'unlimited', 'literal', 0 }
4088 push_opstack { 'array', 'unlimited', 'executable', add_VM{ popper }, 1, 1, 'd' }
4089 else
4090 local w1 = gsstate.screen[1]
4091 local w2 = gsstate.screen[2]
4092 local w3 = gsstate.screen[3]
4093 push_opstack {
4094 (abs(w) > MAX_INT or floor(w1) ~= w1) and 'real' or 'integer', 'unlimited', 'literal', w1
4095 }
4096 push_opstack {
4097 (abs(w) > MAX_INT or floor(w2) ~= w2) and 'real' or 'integer', 'unlimited', 'literal', w2
4098 }
4099 local thearray = get_VM(w3)
4100 push_opstack { 'array', 'unlimited', 'executable', w3, 1, #thearray, 'd' }
4101 end
4102 return true
4103end
4104
4105function operators.settransfer()
4106 local a = pop_opstack()
4107 if not a then
4108 return ps_error('stackunderflow')
4109 end
4110 if not (a[1] == 'array' and a[3] == 'executable') then
4111 return ps_error('typecheck')
4112 end
4113 local va = a[4]
4114 if va < 0 then
4115 return ps_error('rangecheck')
4116 end
4117 gsstate.transfer = va
4118 return true
4119end
4120
4121function operators.currenttransfer()
4122 local transfer = gsstate.transfer
4123 if not transfer then
4124 push_opstack { 'array', 'unlimited', 'executable', add_VM{ }, 0, 0, 'd'}
4125 else
4126 local thearray = get_VM(transfer)
4127 push_opstack { 'array', 'unlimited', 'executable', transfer, 1, #thearray, 'd' }
4128 end
4129 return true
4130end
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140function operators.matrix()
4141 local matrix = {
4142 {'real', 'unlimited', 'literal', 1},
4143 {'real', 'unlimited', 'literal', 0},
4144 {'real', 'unlimited', 'literal', 0},
4145 {'real', 'unlimited', 'literal', 1},
4146 {'real', 'unlimited', 'literal', 0},
4147 {'real', 'unlimited', 'literal', 0},
4148 }
4149 push_opstack { 'array', 'unlimited', 'literal', add_VM(matrix), 6, 6 }
4150 return true
4151end
4152
4153function operators.initmatrix()
4154 gsstate.matrix = { 1, 0, 0, 1, 0, 0 }
4155 return true
4156end
4157
4158function operators.identmatrix()
4159 local a = pop_opstack()
4160 if not a then return
4161 ps_error('stackunderflow')
4162 end
4163 if a[1] ~= 'array' then
4164 return ps_error('typecheck')
4165 end
4166 if a[6] < 6 then
4167 return ps_error('rangecheck')
4168 end
4169 local m = VM[a[4]]
4170 m[1] = { 'real', 'unlimited', 'literal', 1 }
4171 m[2] = { 'real', 'unlimited', 'literal', 0 }
4172 m[3] = { 'real', 'unlimited', 'literal', 0 }
4173 m[4] = { 'real', 'unlimited', 'literal', 1 }
4174 m[5] = { 'real', 'unlimited', 'literal', 0 }
4175 m[6] = { 'real', 'unlimited', 'literal', 0 }
4176 a[5] = 6
4177 push_opstack(a)
4178 return true
4179end
4180
4181operators.defaultmatrix = operators.identmatrix
4182
4183function operators.currentmatrix()
4184 local a = pop_opstack()
4185 if not a then
4186 return ps_error('stackunderflow')
4187 end
4188 if a[1] ~= 'array' then
4189 return ps_error('typecheck')
4190 end
4191 if a[6] < 6 then
4192 return ps_error('rangecheck')
4193 end
4194 local thearray = get_VM(a[4])
4195 local matrix = gsstate.matrix
4196 for i=1,6 do
4197 thearray[i] = {'real', 'unlimited', 'literal', matrix[i]}
4198 end
4199 push_opstack { 'array', 'unlimited', 'literal', a[4], 6, 6 }
4200 return true
4201end
4202
4203function operators.setmatrix()
4204 local a = pop_opstack()
4205 if not a then
4206 return ps_error('stackunderflow')
4207 end
4208 if a[1] ~= 'array' then
4209 return ps_error('typecheck')
4210 end
4211 if a[6] ~= 6 then
4212 return ps_error('rangecheck')
4213 end
4214 local thearray = get_VM(a[4])
4215 local matrix = gsstate.matrix
4216 for i=1,#thearray do
4217 local a = thearray[i]
4218 local ta, tv = a[1], a[4]
4219 if not (ta == 'real' or ta == 'integer') then
4220 return ps_error('typecheck')
4221 end
4222 if i > 6 then
4223 return ps_error('rangecheck')
4224 end
4225 matrix[i] = tv
4226 end
4227 return true
4228end
4229
4230local function do_transform(matrix,a,b)
4231 local x = matrix[1] * a + matrix[3] * b + matrix[5]
4232 local y = matrix[2] * a + matrix[4] * b + matrix[6]
4233 return x, y
4234end
4235
4236local function do_itransform(matrix,a,b)
4237 local m1 = matrix[1]
4238 local m4 = matrix[4]
4239 if m1 == 0 or m4 == 0 then
4240 return nil
4241 end
4242 local x = (a - matrix[5] - matrix[3] * b) / m1
4243 local y = (b - matrix[6] - matrix[2] * a) / m4
4244 return x, y
4245end
4246
4247local function do_concat (a,b)
4248 local a1, a2, a3, a4, a5, a6 = a[1], a[2], a[3], a[4], a[5], a[6]
4249 local b1, b2, b3, b4, b5, b6 = b[1], b[2], b[3], b[4], b[5], b[6]
4250 local c1 = a1 * b1 + a2 * b3
4251 local c2 = a1 * b2 + a2 * b4
4252 local c3 = a1 * b3 + a3 * b4
4253 local c4 = a3 * b2 + a4 * b4
4254 local c5 = a5 * b1 + a6 * b3 + b5
4255 local c6 = a5 * b2 + a6 * b4 + b6
4256
4257 return {
4258 abs(c1) < 1.0e-16 and 0 or c1,
4259 abs(c2) < 1.0e-16 and 0 or c2,
4260 abs(c3) < 1.0e-16 and 0 or c3,
4261 abs(c4) < 1.0e-16 and 0 or c4,
4262 abs(c5) < 1.0e-16 and 0 or c5,
4263 abs(c6) < 1.0e-16 and 0 or c6,
4264 }
4265end
4266
4267local function do_inverse (a)
4268 local a1, a2, a3, a4, a5, a6 = a[1], a[2], a[3], a[4], a[5], a[6]
4269 local det = a1 * a4 - a3 * a2
4270 if det == 0 then
4271 return nil
4272 end
4273 local c1 = a4 / det
4274 local c3 = -a3 / det
4275 local c2 = -a2 / det
4276 local c4 = a1 / det
4277 local c5 = (a3 * a6 - a5 * a4) / det
4278 local c6 = (a5 * a2 - a1 * a6) / det
4279 return {
4280 abs(c1) < 1.0e-16 and 0 or c1,
4281 abs(c2) < 1.0e-16 and 0 or c2,
4282 abs(c3) < 1.0e-16 and 0 or c3,
4283 abs(c4) < 1.0e-16 and 0 or c4,
4284 abs(c5) < 1.0e-16 and 0 or c5,
4285 abs(c6) < 1.0e-16 and 0 or c6,
4286 }
4287end
4288
4289function operators.translate()
4290 local a = pop_opstack()
4291 if not a then
4292 return ps_error('stackunderflow')
4293 end
4294 if a[1] == 'array' then
4295 if a[6] ~= 6 then
4296 return ps_error('typecheck')
4297 end
4298 local tf = a
4299 local a = pop_opstack()
4300 local b = pop_opstack()
4301 if not b then
4302 return ps_error('stackunderflow')
4303 end
4304 local ta, tb = a[1], b[1]
4305 if not (ta == 'real' or ta == 'integer') then
4306 return ps_error('typecheck')
4307 end
4308 if not (tb == 'real' or tb == 'integer') then
4309 return ps_error('typecheck')
4310 end
4311 local m = VM[tf[4]]
4312 local old = { m[1][4], m[2][4], m[3][4], m[4][4], m[5][4], m[6][4] }
4313 local c = do_concat(old,{1,0,0,1,b[4],a[4]})
4314 for i=1,6 do
4315 m[i] = { 'real', 'unlimited', 'literal', c[i] }
4316 end
4317 tf[5] = 6
4318 push_opstack(tf)
4319 else
4320 local b = pop_opstack()
4321 local ta = a[1]
4322 local tb = b[1]
4323 if not (ta == 'real' or ta == 'integer') then
4324 return ps_error('typecheck')
4325 end
4326 if not (tb == 'real' or tb == 'integer') then
4327 return ps_error('typecheck')
4328 end
4329 gsstate.matrix = do_concat(gsstate.matrix,{1,0,0,1,b[4],a[4]})
4330 end
4331 return true
4332end
4333
4334function operators.scale()
4335 local a = pop_opstack()
4336 if not a then
4337 return ps_error('stackunderflow')
4338 end
4339 local ta = a[1]
4340 if ta == 'array' then
4341 local tf = a
4342 if a[6] ~= 6 then
4343 return ps_error('typecheck')
4344 end
4345 local a = pop_opstack()
4346 local b = pop_opstack()
4347 if not b then
4348 return ps_error('stackunderflow')
4349 end
4350 local ta, tb = a[1], b[1]
4351 if not (ta == 'real' or ta == 'integer') then
4352 return ps_error('typecheck')
4353 end
4354 if not (tb == 'real' or tb == 'integer') then
4355 return ps_error('typecheck')
4356 end
4357 local v = VM[tf[4]]
4358 local c = do_concat (
4359 { v[1][4], v[2][4], v[3][4], v[4][4], v[5][4], v[6][4] },
4360 { b[4], 0, 0, a[4], 0, 0 }
4361 )
4362 for i=1,6 do
4363 v[i] = { 'real', 'unlimited', 'literal', c[i] }
4364 end
4365 tf[5] = 6
4366 push_opstack(tf)
4367 else
4368 local b = pop_opstack()
4369 if not b then
4370 return ps_error('stackunderflow')
4371 end
4372 local ta, tb = a[1], b[1]
4373 if not (ta == 'real' or ta == 'integer') then
4374 return ps_error('typecheck')
4375 end
4376 if not (tb == 'real' or tb == 'integer') then
4377 return ps_error('typecheck')
4378 end
4379 gsstate.matrix = do_concat(gsstate.matrix, { b[4], 0, 0, a[4], 0, 0 })
4380 end
4381 return true
4382end
4383
4384function operators.concat()
4385 local a = pop_opstack()
4386 if not a then
4387 return ps_error('stackunderflow')
4388 end
4389 if a[1] ~= "array" then
4390 return ps_error('typecheck')
4391 end
4392 if a[6] ~= 6 then
4393 return ps_error('typecheck')
4394 end
4395 local thearray = get_VM(a[4])
4396 local l = { }
4397 for i=1,#thearray do
4398 local v = thearray[i]
4399 local t = v[1]
4400 if not (t == 'real' or t == 'integer') then
4401 return ps_error('typecheck')
4402 end
4403 l[i] = v[4]
4404 end
4405 gsstate.matrix = do_concat(gsstate.matrix,l)
4406 return true
4407end
4408
4409function operators.concatmatrix()
4410 local tf = pop_opstack()
4411 local b = pop_opstack()
4412 local a = pop_opstack()
4413 if not a then
4414 return ps_error('stackunderflow')
4415 end
4416 if tf[1] ~= "array" then return ps_error('typecheck') end
4417 if b [1] ~= "array" then return ps_error('typecheck') end
4418 if a [1] ~= "array" then return ps_error('typecheck') end
4419 if tf[6] ~= 6 then return ps_error('typecheck') end
4420 if b [6] ~= 6 then return ps_error('typecheck') end
4421 if a [6] ~= 6 then return ps_error('typecheck') end
4422 local al = { }
4423 local thearray = get_VM(a[4])
4424 for i=1,#thearray do
4425 local v = thearray[i]
4426 local tv = v[1]
4427 if not (tv == 'real' or tv == 'integer') then
4428 return ps_error('typecheck')
4429 end
4430 al[i] = v[4]
4431 end
4432 local bl = { }
4433 local thearray = get_VM(b[4])
4434 for i=1,#thearray do
4435 local v = thearray[i]
4436 local tv = v[1]
4437 if not (tv == 'real' or tv == 'integer') then
4438 return ps_error('typecheck')
4439 end
4440 bl[i] = v[4]
4441 end
4442 local c = do_concat(al, bl)
4443 local m = VM[tf[4]]
4444 for i=1,6 do
4445 m[i] = { 'real', 'unlimited', 'literal', c[i] }
4446 end
4447 tf[5] = 6
4448 push_opstack(tf)
4449 return true
4450end
4451
4452function operators.rotate()
4453 local a = pop_opstack()
4454 if not a then
4455 return ps_error('stackunderflow')
4456 end
4457 local ta = a[1]
4458 if ta == 'array' then
4459 local tf
4460 if a[6] ~= 6 then
4461 return ps_error('typecheck')
4462 end
4463 tf = a
4464 a = pop_opstack()
4465 if not a then
4466 return ps_error('stackunderflow')
4467 end
4468 if not (a[1] == 'real' or a[1] == 'integer') then
4469 return ps_error('typecheck')
4470 end
4471 local m = VM[tf[4]]
4472 local old = { m[1][4], m[2][4], m[3][4], m[4][4], m[5][4], m[6][4] }
4473 local av = a[4]
4474 local c = do_concat (old, {cos(rad(av)),sin(rad(av)),-sin(rad(av)),cos(rad(av)), 0, 0})
4475 for i=1,6 do
4476 m[i] = { 'real', 'unlimited', 'literal', c[i] }
4477 end
4478 push_opstack(tf)
4479 elseif ta == 'real' or ta == 'integer' then
4480 local av = a[4]
4481 gsstate.matrix = do_concat(gsstate.matrix,{cos(rad(av)),sin(rad(av)),-sin(rad(av)),cos(rad(av)),0,0})
4482 else
4483 return ps_error('typecheck')
4484 end
4485 return true
4486end
4487
4488function operators.transform()
4489 local a = pop_opstack()
4490 local b = pop_opstack()
4491 if not b then
4492 ps_error('stackunderflow')
4493 end
4494 local tf
4495 if a[1] == 'array' then
4496 if a[6] ~= 6 then
4497 return ps_error('typecheck')
4498 end
4499 local thearray = get_VM(a[4])
4500 tf = { }
4501 for i=1,#thearray do
4502 local v = thearray[i]
4503 local v1 = v[1]
4504 if not (v1 == 'real' or v1 == 'integer') then
4505 return ps_error('typecheck')
4506 end
4507 tf[i] = v[4]
4508 end
4509 a = pop_opstack()
4510 if not a then
4511 return ps_error('stackunderflow')
4512 end
4513 else
4514 tf = gsstate.matrix
4515 end
4516 local a1 = a[1]
4517 local b1 = b[1]
4518 if not (a1 == 'real' or a1 == 'integer') then
4519 return ps_error('typecheck')
4520 end
4521 if not (b1 == 'real' or b1 == 'integer') then
4522 return ps_error('typecheck')
4523 end
4524 local x, y = do_transform(tf,b[4],a[4]);
4525 push_opstack { 'real', 'unlimited', 'literal', x }
4526 push_opstack { 'real', 'unlimited', 'literal', y }
4527 return true
4528end
4529
4530local function commontransform()
4531 local a = pop_opstack()
4532 if not a then
4533 return ps_error('stackunderflow')
4534 end
4535 local tf
4536 if a[1] == 'array' then
4537 if a[6] ~= 6 then
4538 return ps_error('typecheck')
4539 end
4540 tf = { }
4541 local thearray = get_VM(a[4])
4542 for i=1,#thearray do
4543 local v = thearray[i]
4544 local tv = v[1]
4545 if not (tv == 'real' or tv == 'integer') then
4546 return ps_error('typecheck')
4547 end
4548 tf[i] = v[4]
4549 end
4550 a = pop_opstack()
4551 if not a then
4552 return ps_error('stackunderflow')
4553 end
4554 else
4555 tf = gsstate.matrix
4556 end
4557 local b = pop_opstack()
4558 if not b then
4559 return ps_error('stackunderflow')
4560 end
4561 local ta = a[1]
4562 local tb = b[1]
4563 if not (ta == 'real' or ta == 'integer') then
4564 return ps_error('typecheck')
4565 end
4566 if not (tb == 'real' or tb == 'integer') then
4567 return ps_error('typecheck')
4568 end
4569 return true, tf, a, b
4570end
4571
4572function operators.dtransform()
4573 local ok, tf, a, b = commontransform()
4574 if ok then
4575 local x, y = do_transform({tf[1],tf[2],tf[3],tf[4],0,0},b[4],a[4])
4576 if not x then
4577 return ps_error('undefinedresult')
4578 end
4579 push_opstack { 'real', 'unlimited', 'literal', x }
4580 push_opstack { 'real', 'unlimited', 'literal', y }
4581 return true
4582 else
4583 return false, tf
4584 end
4585end
4586
4587function operators.itransform()
4588 local ok, tf, a, b = commontransform()
4589 if ok then
4590 local x, y = do_itransform(tf,b[4],a[4])
4591 if not x then
4592 return ps_error('undefinedresult')
4593 end
4594 push_opstack { 'real', 'unlimited', 'literal', x }
4595 push_opstack { 'real', 'unlimited', 'literal', y }
4596 return true
4597 else
4598 return false, tf
4599 end
4600end
4601
4602function operators.idtransform()
4603 local ok, tf, a, b = commontransform()
4604 if ok then
4605 local x,y = do_itransform({tf[1],tf[2],tf[3],tf[4],0,0},b[4],a[4]);
4606 if not x then
4607 return ps_error('undefinedresult')
4608 end
4609 push_opstack { 'real', 'unlimited', 'literal', x }
4610 push_opstack { 'real', 'unlimited', 'literal', y }
4611 return true
4612 else
4613 return false, tf
4614 end
4615end
4616
4617function operators.invertmatrix()
4618 local tf = pop_opstack()
4619 if not tf then
4620 return ps_error('stackunderflow')
4621 end
4622 if tf[1] ~= "array" then
4623 return ps_error('typecheck')
4624 end
4625 if tf[6] ~= 6 then
4626 return ps_error('typecheck')
4627 end
4628 local a = pop_opstack()
4629 if not a then
4630 return ps_error('stackunderflow')
4631 end
4632 if a[1] ~= "array" then
4633 return ps_error('typecheck')
4634 end
4635 if a[6] ~= 6 then
4636 return ps_error('typecheck')
4637 end
4638 local al = { }
4639 local thearray = get_VM(a[4])
4640 for i=1,#thearray do
4641 local v = thearray[i]
4642 local tv = v[1]
4643 if not (tv == 'real' or tv == 'integer') then
4644 return ps_error('typecheck')
4645 end
4646 al[i] = v[4]
4647 end
4648 local c = do_inverse(al)
4649 if not c then
4650 return ps_error('undefinedresult')
4651 end
4652 local m = VM[tf[4]]
4653 for i=1,6 do
4654 m[i] = { 'real', 'unlimited', 'literal', c[i] }
4655 end
4656 tf[5] = 6
4657 push_opstack(tf)
4658 return true
4659end
4660
4661
4662
4663
4664
4665
4666
4667function operators.newpath()
4668 gsstate.path = { }
4669 gsstate.position = { }
4670 return true
4671end
4672
4673function operators.currentpoint()
4674 local position = gsstate.position
4675 if #position == 0 then
4676 return ps_error('nocurrentpoint')
4677 end
4678 local x, y = do_itransform(gsstate.matrix, position[1], position[2])
4679 if not x then
4680 return ps_error('undefinedresult')
4681 end
4682 push_opstack { 'real', 'unlimited', 'literal', x }
4683 push_opstack { 'real', 'unlimited', 'literal', y }
4684end
4685
4686function operators.moveto()
4687 local b = pop_opstack()
4688 local a = pop_opstack()
4689 if not a then
4690 return ps_error('stackunderflow')
4691 end
4692 local b1 = b[1]
4693 local a1 = a[1]
4694 if not (b1 == 'real' or b1 == 'integer') then
4695 return ps_error('typecheck')
4696 end
4697 if not (a1 == 'real' or a1 == 'integer') then
4698 return ps_error('typecheck')
4699 end
4700 local path = gsstate.path
4701 local length = #path
4702 local x, y = do_transform(gsstate.matrix, a[4], b[4])
4703 if length > 0 and path[length][1] == "moveto" then
4704
4705 else
4706 length = length + 1
4707 end
4708 path[length] = { "moveto", x, y }
4709 gsstate.position = { x, y }
4710 return true
4711end
4712
4713function operators.rmoveto()
4714 local b = pop_opstack()
4715 local a = pop_opstack()
4716 if not a then
4717 return ps_error('stackunderflow')
4718 end
4719 local bt = b[1]
4720 local at = a[1]
4721 if not (bt == 'real' or bt == 'integer') then
4722 return ps_error('typecheck')
4723 end
4724 if not (at == 'real' or at == 'integer') then
4725 return ps_error('typecheck')
4726 end
4727 local position = gsstate.position
4728 local path = gsstate.path
4729 local length = #path
4730 if #position == 0 then
4731 return ps_error('nocurrentpoint')
4732 end
4733 local x, y = do_transform(gsstate.matrix, a[4], b[4])
4734 x = position[1] + x
4735 y = position[2] + y
4736 position[1] = x
4737 position[2] = y
4738 if length > 0 and path[length][1] == "moveto" then
4739
4740 else
4741 length = length + 1
4742 end
4743 path[length] = { "moveto", x, y }
4744 return true
4745end
4746
4747function operators.lineto()
4748 local b = pop_opstack()
4749 local a = pop_opstack()
4750 if not a then
4751 return ps_error('stackunderflow')
4752 end
4753 local at = a[1]
4754 local bt = b[1]
4755 if not (bt == 'real' or bt == 'integer') then
4756 return ps_error('typecheck')
4757 end
4758 if not (at == 'real' or at == 'integer') then
4759 return ps_error('typecheck')
4760 end
4761 local position = gsstate.position
4762 local path = gsstate.path
4763 local length = #path
4764 if #position == 0 then
4765 return ps_error('nocurrentpoint')
4766 end
4767 local x, y = do_transform(gsstate.matrix, a[4], b[4])
4768 gsstate.position = { x, y }
4769 path[length+1] = { "lineto", x, y }
4770 return true
4771end
4772
4773function operators.rlineto()
4774 local b = pop_opstack()
4775 local a = pop_opstack()
4776 if not a then
4777 return ps_error('stackunderflow')
4778 end
4779 local at = a[1]
4780 local bt = b[1]
4781 if not (bt == 'real' or bt == 'integer') then
4782 return ps_error('typecheck')
4783 end
4784 if not (at == 'real' or at == 'integer') then
4785 return ps_error('typecheck')
4786 end
4787 local position = gsstate.position
4788 local path = gsstate.path
4789 local length = #path
4790 if #position == 0 then
4791 return ps_error('nocurrentpoint')
4792 end
4793 local x, y = do_transform(gsstate.matrix, a[4], b[4])
4794 x = position[1] + x
4795 y = position[2] + y
4796 position[1] = x
4797 position[2] = y
4798 path[length+1] = { "lineto", x, y }
4799 return true
4800end
4801
4802local function arc_to_curve (x, y, r, aa, theta)
4803 local th = rad(theta/2.0)
4804 local x0 = cos(th)
4805 local y0 = sin(th)
4806 local x1 = (4.0-x0)/3.0
4807 local y1 = ((1.0-x0)*(3.0-x0))/(3.0*y0)
4808 local x2 = x1
4809 local y2 = -y1
4810
4811
4812
4813 local bezAng = rad(aa) + th
4814 local cBezAng = cos(bezAng)
4815 local sBezAng = sin(bezAng)
4816
4817 local rx0 = (cBezAng * x0) - (sBezAng * y0)
4818 local ry0 = (sBezAng * x0) + (cBezAng * y0)
4819 local rx1 = (cBezAng * x1) - (sBezAng * y1)
4820 local ry1 = (sBezAng * x1) + (cBezAng * y1)
4821 local rx2 = (cBezAng * x2) - (sBezAng * y2)
4822 local ry2 = (sBezAng * x2) + (cBezAng * y2)
4823
4824
4825
4826 local px0 = x + r*rx0
4827 local py0 = y + r*ry0
4828 local px1 = x + r*rx1
4829 local py1 = y + r*ry1
4830 local px2 = x + r*rx2
4831 local py2 = y + r*ry2
4832
4833
4834
4835 return px2, py2, px1, py1, px0, py0
4836end
4837
4838local function arc_start(x,y,r,aa)
4839 local x3 = 1
4840 local y3 = 0
4841 local bezAng = rad(aa)
4842 local cBezAng = cos(bezAng)
4843 local sBezAng = sin(bezAng)
4844 local rx3 = (cBezAng * x3) - (sBezAng * y3)
4845 local ry3 = (sBezAng * x3) + (cBezAng * y3)
4846 local px3 = x + r*rx3
4847 local py3 = y + r*ry3
4848 return px3, py3
4849end
4850
4851local function do_arc(matrix,path,x,y,r,aa,ab)
4852 local endx, endy
4853 local segments = floor((ab-aa+44.999999999)/45)
4854 if segments == 0 then
4855 return do_transform(gsstate.matrix, x,y)
4856 end
4857 local theta = (ab-aa) / segments
4858 while segments>0 do
4859 local x1, y1, x2, y2, x3, y3 = arc_to_curve(x,y,r,aa,theta)
4860 local px2, py2 = do_transform(matrix,x2,y2)
4861 local px1, py1 = do_transform(matrix,x1,y1)
4862 endx, endy = do_transform(matrix, x3,y3)
4863 path[#path+1] = { "curveto", px1, py1, px2, py2, endx, endy }
4864 segments = segments - 1
4865 aa = aa + theta
4866 end
4867 return endx, endy
4868end
4869
4870local function do_arcn(matrix,path,x,y,r,aa,ab)
4871 local endx, endy
4872 local segments = floor((aa-ab+44.999999999)/45)
4873 if segments == 0 then
4874 return do_transform(matrix, x,y)
4875 end
4876 local theta = (aa-ab) / segments
4877 while segments > 0 do
4878 local x1, y1, x2, y2, x3, y3 = arc_to_curve(x,y,r,aa,-theta)
4879 local px1, py1 = do_transform(matrix,x1,y1)
4880 local px2, py2 = do_transform(matrix,x2,y2)
4881 endx, endy = do_transform(matrix,x3,y3)
4882 path[#path+1] = { "curveto", px1 , py1 , px2 , py2 , endx , endy }
4883 segments = segments - 1
4884 aa = aa - theta
4885 end
4886 return endx, endy
4887end
4888
4889local function commonarc(action)
4890 local e = pop_opstack()
4891 local d = pop_opstack()
4892 local c = pop_opstack()
4893 local b = pop_opstack()
4894 local a = pop_opstack()
4895 if not a then
4896 return ps_error('stackunderflow')
4897 end
4898 local ta, tb, tc, td, te = a[1], b[1], c[1], d[1], e[1]
4899 if not (ta == 'real' or ta == 'integer') then return ps_error('typecheck') end
4900 if not (tb == 'real' or tb == 'integer') then return ps_error('typecheck') end
4901 if not (tc == 'real' or tc == 'integer') then return ps_error('typecheck') end
4902 if not (td == 'real' or td == 'integer') then return ps_error('typecheck') end
4903 if not (te == 'real' or te == 'integer') then return ps_error('typecheck') end
4904 local position = gsstate.position
4905 local path = gsstate.path
4906 local matrix = gsstate.matrix
4907 local vd = d[4]
4908 local ve = e[4]
4909 if vd < 0 or ve < 0 or vd > 360 or ve > 360 or (vd-ve) <= 0 then
4910 return ps_error('limitcheck')
4911 end
4912 local r = c[4]
4913 if r == 0 then
4914 ps_error('limitcheck')
4915 end
4916 local x = a[4]
4917 local y = b[4]
4918 local x0, y0 = arc_start(x,y,r,vd)
4919 local startx, starty = do_transform(matrix,x0,y0)
4920 path[#path+1] = { #position == 2 and "lineto" or "moveto", startx, starty }
4921 position[1], position[2] = action(matrix,path,x,y,r,vd,ve)
4922 return true
4923end
4924
4925function operators.arc()
4926 commonarc(do_arc)
4927end
4928
4929function operators.arcn()
4930 commonarc(do_arcn)
4931end
4932
4933local function vlength (a,b)
4934 return sqrt(a^2+b^2)
4935end
4936
4937local function vscal_ (a,b,c)
4938 return a*b, a*c
4939end
4940
4941
4942
4943local function between (dist, pa, pb)
4944 local pa1, pa2 = pa[1], pa[2]
4945 local pb1, pb2 = pb[1], pb[2]
4946 return {
4947 pa1 + dist * (pb1 - pa1),
4948 pa2 + dist * (pb2 - pa2),
4949 }
4950end
4951
4952local function sign (a)
4953 return a < 0 and -1 or 1
4954end
4955
4956local function do_arcto(x,y,r)
4957 local h = gsstate.position
4958 local tx1, tx2, ty1, ty2
4959 local c1, c2
4960 local x1, x2 = x[1], x[2]
4961 local y1, y2 = y[1], y[2]
4962 local h1, h2 = h[1], h[2]
4963 local ux, uy = x1 - h1, x2 - h2
4964 local vx, vy = y1 - x1, y2 - x2
4965 local lx, ly = vlength(ux,uy), vlength(vx,vy)
4966 local sx, sy = ux*vy - uy*vx, ux*vx + uy*vy
4967 if sx == 0 and sy == 0 then
4968 sx = r
4969 sy = 0
4970 else
4971 sx = r
4972 sy = atan2(sx,sy)
4973 end
4974 local a_arcto = sx*tan(abs(sy)/2)
4975 if sx*sy*lx*ly == 0 then
4976 tx1 = x1
4977 tx2 = x2
4978 ty1 = x1
4979 ty2 = x2
4980 c1 = x1
4981 c2 = x2
4982 else
4983 local tx = between(a_arcto/lx,x,h)
4984 local ty = between(a_arcto/ly,x,y)
4985 local cc, dd = vscal_(sign(sy)*sx/lx,-uy,ux)
4986 tx1 = tx[1]
4987 tx2 = tx[2]
4988 ty1 = ty[1]
4989 ty2 = ty[2]
4990 c1 = tx1 + cc
4991 c2 = tx2 + dd
4992 end
4993
4994
4995 local anga = deg(atan2(tx2-c2,tx1-c1))
4996 local angb = deg(atan2(ty2-c2,ty1-c1))
4997 return c1, c2, r, anga, angb, tx1, tx2, ty1, ty2
4998end
4999
5000function operators.arcto()
5001 local e = pop_opstack()
5002 local d = pop_opstack()
5003 local c = pop_opstack()
5004 local b = pop_opstack()
5005 local a = pop_opstack()
5006 if not a then
5007 return ps_error('stackunderflow')
5008 end
5009 local ta, tb, tc, td, te = a[1], b[2], c[1], d[1], e[1]
5010 if not (ta == 'real' or ta == 'integer') then
5011 return ps_error('typecheck')
5012 end
5013 if not (tb == 'real' or tb == 'integer') then
5014 return ps_error('typecheck')
5015 end
5016 if not (tc == 'real' or tc == 'integer') then
5017 return ps_error('typecheck')
5018 end
5019 if not (td == 'real' or td == 'integer') then
5020 return ps_error('typecheck')
5021 end
5022 if not (te == 'real' or te == 'integer') then
5023 return ps_error('typecheck')
5024 end
5025 local x1, y1, x2, y2, r = a[4], b[4], c[4], d[4], e[4]
5026 local position = gsstate.position
5027 local path = gsstate.path
5028 if #position == 0 then
5029 return ps_error('nocurrentpoint')
5030 end
5031 local x, y, r, anga, angb, tx1, tx2, ty1, ty2 = do_arcto({x1,y1},{x2, y2},r)
5032 local vx, vy = do_transform(gsstate.matrix,tx1,tx2)
5033 path[#path+1] = { "lineto", vx, vy }
5034 if anga == angb then
5035
5036 elseif anga > angb then
5037 position[1], position[2] = do_arcn(x,y,r,anga,angb)
5038 else
5039 position[1], position[2] = do_arc (x,y,r,anga,angb)
5040 end
5041 push_opstack { 'real', 'unlimited', 'literal', tx1 }
5042 push_opstack { 'real', 'unlimited', 'literal', tx2 }
5043 push_opstack { 'real', 'unlimited', 'literal', ty1 }
5044 push_opstack { 'real', 'unlimited', 'literal', ty2 }
5045end
5046
5047function operators.curveto()
5048 local f = pop_opstack()
5049 local e = pop_opstack()
5050 local d = pop_opstack()
5051 local c = pop_opstack()
5052 local b = pop_opstack()
5053 local a = pop_opstack()
5054 if not a then
5055 return ps_error('stackunderflow')
5056 end
5057 local f1 = f[1] if not (f1 == 'real' or f1 == 'integer') then return ps_error('typecheck') end
5058 local e1 = e[1] if not (e1 == 'real' or e1 == 'integer') then return ps_error('typecheck') end
5059 local d1 = d[1] if not (d1 == 'real' or d1 == 'integer') then return ps_error('typecheck') end
5060 local c1 = c[1] if not (c1 == 'real' or c1 == 'integer') then return ps_error('typecheck') end
5061 local b1 = b[1] if not (b1 == 'real' or b1 == 'integer') then return ps_error('typecheck') end
5062 local a1 = a[1] if not (a1 == 'real' or a1 == 'integer') then return ps_error('typecheck') end
5063
5064 if #gsstate.position == 0 then
5065 return ps_error('nocurrentpoint')
5066 end
5067
5068 local matrix = gsstate.matrix
5069 local x, y = do_transform(matrix, e[4], f[4])
5070 local ax, ay = do_transform(matrix, a[4], b[4])
5071 local bx, by = do_transform(matrix, c[4], d[4])
5072 gsstate.position = { x, y }
5073
5074 local path = gsstate.path
5075 path[#path+1] = { "curveto", ax, ay, bx, by, x, y }
5076 return true
5077end
5078
5079function operators.rcurveto()
5080 local f = pop_opstack()
5081 local e = pop_opstack()
5082 local d = pop_opstack()
5083 local c = pop_opstack()
5084 local b = pop_opstack()
5085 local a = pop_opstack()
5086 if not a then
5087 return ps_error('stackunderflow')
5088 end
5089 local ft if not (ft == 'real' or ft == 'integer') then return ps_error('typecheck') end
5090 local et if not (et == 'real' or et == 'integer') then return ps_error('typecheck') end
5091 local dt if not (dt == 'real' or dt == 'integer') then return ps_error('typecheck') end
5092 local ct if not (ct == 'real' or ct == 'integer') then return ps_error('typecheck') end
5093 local bt if not (bt == 'real' or bt == 'integer') then return ps_error('typecheck') end
5094 local at if not (at == 'real' or at == 'integer') then return ps_error('typecheck') end
5095 local position = gsstate.position
5096 local path = gsstate.path
5097 if #position == 0 then
5098 return ps_error('nocurrentpoint')
5099 end
5100 local matrix = gsstate.matrix
5101 local x, y = do_transform(matrix, e[4], f[4])
5102 local ax, ay = do_transform(matrix, a[4], b[4])
5103 local bx, by = do_transform(matrix, c[4], d[4])
5104 local px = position[1] + x
5105 local py = position[2] + y
5106 path[#path+1] = {
5107 "curveto",
5108 position[1] + ax,
5109 position[2] + ay,
5110 position[1] + bx,
5111 position[2] + by,
5112 px,
5113 py
5114 }
5115 position[1] = px
5116 position[2] = py
5117 return true
5118end
5119
5120function operators.closepath()
5121 local path = gsstate.path
5122 local length = #path
5123 if length > 0 and path[length][1] ~= 'closepath' then
5124 local m = path[1]
5125 local a = m[2]
5126 local b = m[3]
5127 local x, y = do_transform(gsstate.matrix, a, b)
5128 gsstate.position = { x, y }
5129 path[length+1] = { "closepath", x, y }
5130 end
5131 return true
5132end
5133
5134
5135
5136
5137local function bezier_at(t,x0,y0,x1,y1,x2,y2,x3,y3)
5138 local v = (1 - t)
5139 local x = (v^3)*x0 + 3*(v^2)*t*x1 + 3*v*(t^2)*x2 + (t^3)*x3
5140 local y = (v^3)*y0 + 3*(v^2)*t*y1 + 3*v*(t^2)*y2 + (t^3)*y3
5141 return x, y
5142end
5143
5144local delta = 10
5145
5146local function good_enough (flatness,c,ct1,ct2,l)
5147 local c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8]
5148 local l0x, l0y, l1x, l1y = l[1], l[2], l[3], l[4]
5149 local t = 0
5150 while t < delta do
5151 local td = t/delta
5152 local bx, by = bezier_at(ct1+(ct2-ct1)*td,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
5153 local lx, ly = (1-td)*l0x + td*l1x, (1-td)*l0y + td*l1y
5154 local dist = vlength(bx-lx,by-ly)
5155 if dist > flatness then
5156 return false
5157 end
5158 t = t + 1
5159 end
5160 return true
5161end
5162
5163
5164
5165
5166local function splitter (flatness,p,d,c,ct1,ct2,l)
5167 local c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8]
5168 d = d + 1
5169 local r = good_enough(flatness,c,ct1,ct1+ct2,l)
5170 if r or d > 10 then
5171 p[#p + 1] = { 'lineto', l[3], l[4] }
5172 else
5173 local ct22 = ct2/2
5174 local l2x, l2y = bezier_at(ct1+ct22,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
5175 local l1 = { l[1], l[2], l2x, l2y }
5176 local l2 = { l2x, l2y, l[3], l[4] }
5177 splitter(flatness,p,d,c,ct1,ct22,l1)
5178 splitter(flatness,p,d,c,ct1+ct22,ct22,l2)
5179 end
5180end
5181
5182local function flattencurve( homex, homey, curve, flatness)
5183 local p = { }
5184 local c6 = curve[6]
5185 local c7 = curve[7]
5186 local thecurve = { homex, homey, curve[2], curve[3], curve[4], curve[5], c6, c7 }
5187 local theline = { homex, homey, c6, c7 }
5188 splitter(flatness, p, 0, thecurve, 0, 1, theline)
5189 return p
5190end
5191
5192local function do_flattenpath (p, flatness)
5193 local x, y
5194 local px = { }
5195 local nx = 0
5196
5197 if flatness < 0.001 then
5198 flatness = 0.001
5199 end
5200 if p then
5201 for i=1,#p do
5202 local v = p[i]
5203 local t = v[1]
5204 if t == "curveto" then
5205 local pxl = flattencurve(x,y,v,flatness)
5206 for i=1,#pxl do
5207 nx = nx + 1 ; px[nx] = pxl[i]
5208 end
5209 x, y = v[6], v[7]
5210 elseif t == "lineto" or t == "moveto" then
5211 x, y = v[2], v[3]
5212 nx = nx + 1 ; px[nx] = v
5213 else
5214 nx = nx + 1 ; px[nx] = v
5215 end
5216 end
5217 end
5218 return px
5219end
5220
5221function operators.flattenpath()
5222 gsstate.path = do_flattenpath(gsstate.path,gsstate.flatness)
5223end
5224
5225function operators.clippath()
5226 gsstate.path = gsstate.clip
5227 return true
5228end
5229
5230function operators.initclip()
5231 device.initclip()
5232 return true
5233end
5234
5235function operators.eofill()
5236 local color = gsstate.color
5237 local thecolor = color[color.type]
5238 if type(thecolor) == "table" then
5239 thecolor = { unpack(thecolor) }
5240 end
5241 currentpage[#currentpage+1] = {
5242 type = 'eofill',
5243 path = gsstate.path,
5244 colortype = color.type,
5245 color = thecolor,
5246 }
5247 operators.newpath()
5248 return true
5249end
5250
5251
5252
5253
5254function operators.clip()
5255 currentpage[#currentpage+1] = {
5256 type = 'clip',
5257 path = gsstate.path,
5258 }
5259 return true
5260end
5261
5262
5263
5264
5265function operators.eoclip()
5266 currentpage[#currentpage+1] = {
5267 type = 'eoclip',
5268 path = gsstate.path,
5269 }
5270 return true
5271end
5272
5273
5274
5275
5276
5277
5278
5279function operators.erasepage()
5280 currentpage = { }
5281 return true
5282end
5283
5284function operators.stroke()
5285 local color = gsstate.color
5286 local ctype = color.type
5287 local thecolor = color[ctype]
5288
5289
5290
5291 currentpage[#currentpage+1] = {
5292 type = 'stroke',
5293 path = gsstate.path,
5294 colortype = ctype,
5295 color = thecolor,
5296 miterlimit = gsstate.miterlimit,
5297 linewidth = gsstate.linewidth,
5298 linecap = gsstate.linecap,
5299 linejoin = gsstate.linejoin,
5300
5301 dashpattern = gsstate.dashpattern,
5302 dashoffset = gsstate.dashoffset
5303 }
5304 operators.newpath()
5305 return true
5306end
5307
5308function operators.fill()
5309 local color = gsstate.color
5310 local ctype = color.type
5311 local thecolor = color[ctype]
5312
5313
5314
5315 currentpage[#currentpage+1] = {
5316 type = 'fill',
5317 path = gsstate.path,
5318 colortype = ctype,
5319 color = thecolor,
5320 }
5321 operators.newpath()
5322 return true
5323end
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337local calculatebox = false
5338
5339initializers[#initializers+1] = function()
5340 calculatebox = true
5341end
5342
5343local function boundingbox(page)
5344
5345 local bounding = specials.boundingbox
5346 if bounding and not calculatebox then
5347 return unpack(bounding)
5348 end
5349
5350 local minx, miny, maxx, maxy
5351 local startx, starty
5352 local linewidth
5353
5354 local function update_bbox (x,y)
5355 if not minx then
5356 minx = x
5357 miny = y
5358 maxx = x
5359 maxy = y
5360 end
5361 if linewidth then
5362 local xx = x + linewidth/2
5363 if xx > maxx then maxx = xx elseif xx < minx then minx = xx end
5364 local xx = x - linewidth/2
5365 if xx > maxx then maxx = xx elseif xx < minx then minx = xx end
5366 local yy = y + linewidth/2
5367 if yy > maxy then maxy = yy elseif yy < miny then miny = yy end
5368 local yy = y - linewidth/2
5369 if yy > maxy then maxy = yy elseif yy < miny then miny = yy end
5370 else
5371 if x > maxx then maxx = x elseif x < minx then minx = x end
5372 if y > maxy then maxy = y elseif y < miny then miny = y end
5373 end
5374 startx, starty = x, y
5375 end
5376
5377 for i=1,#page do
5378 local object = page[i]
5379 local p = do_flattenpath(object.path,0.5)
5380 linewidth = object.type == "stroke" and object.linewidth
5381 for i=1,#p do
5382 local segment = p[i]
5383 local type = segment[1]
5384 if type == "lineto" then
5385 if startx then
5386 update_bbox(startx,starty)
5387 end
5388 update_bbox(segment[2],segment[3])
5389 elseif type == "curveto" then
5390 if startx then
5391 update_bbox(startx,starty)
5392 end
5393 update_bbox(segment[6],segment[7])
5394 elseif type == "moveto" then
5395 startx, starty = segment[2], segment[3]
5396 end
5397 end
5398 end
5399 if minx then
5400 return minx, miny, maxx, maxy
5401 else
5402 return 0, 0, 0, 0
5403 end
5404end
5405
5406
5407
5408local function boundingbox (page)
5409
5410 local bounding = specials.boundingbox
5411 if bounding and not calculatebox then
5412 return unpack(bounding)
5413 end
5414
5415 local minx, miny, maxx, maxy
5416 local startx, starty
5417 local linewidth
5418
5419 local function update_bbox (x,y)
5420 if not minx then
5421 minx = x
5422 miny = y
5423 maxx = x
5424 maxy = y
5425 end
5426 if linewidth then
5427 local xx = x + linewidth/2
5428 if xx > maxx then
5429 maxx = xx
5430 elseif xx < minx then
5431 minx = xx
5432 end
5433 local xx = x - linewidth/2
5434 if xx > maxx then
5435 maxx = xx
5436 elseif xx < minx then
5437 minx = xx
5438 end
5439 local yy = y + linewidth/2
5440 if yy > maxy then
5441 maxy = yy
5442 elseif yy < miny then
5443 miny = yy
5444 end
5445 local yy = y - linewidth/2
5446 if yy > maxy then
5447 maxy = yy
5448 elseif yy < miny then
5449 miny = yy
5450 end
5451 else
5452 if x > maxx then
5453 maxx = x
5454 elseif x < minx then
5455 minx = x
5456 end
5457 if y > maxy then
5458 maxy = y
5459 elseif y < miny then
5460 miny = y
5461 end
5462 end
5463 startx, starty = x, y
5464 end
5465
5466 local delta = 10
5467
5468 local function good_enough (ct1,ct2, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l1x, l1y)
5469 local t = 0
5470 while t < delta do
5471 local td = t/delta
5472 local bx, by = bezier_at(ct1+(ct2-ct1)*td,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
5473 local lx, ly = (1-td)*l0x + td*l1x, (1-td)*l0y + td*l1y
5474 local dist = sqrt((bx-lx)^2+(by-ly)^2)
5475 if dist > 0.5 then
5476 return false
5477 end
5478 t = t + 1
5479 end
5480 return true
5481 end
5482
5483 local function splitter (d,ct1,ct2, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l1x, l1y)
5484 d = d + 1
5485 local r = good_enough(ct1,ct1+ct2, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l1x, l1y)
5486 if r or d > 10 then
5487 if startx then
5488 update_bbox(l1x, l1y)
5489 end
5490 else
5491 local ct22 = ct2/2
5492 local l2x, l2y = bezier_at(ct1+ct22,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
5493 splitter(d,ct1, ct22, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l2x, l2y)
5494 splitter(d,ct1+ct22,ct22, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l2x, l2y, l1x, l1y)
5495 end
5496 end
5497
5498 for i=1,#page do
5499 local object = page[i]
5500 local p = object.path
5501 if p then
5502 linewidth = object.type == "stroke" and object.linewidth
5503 for i=1,#p do
5504 local segment = p[i]
5505 local type = segment[1]
5506 if type == "lineto" then
5507 if startx then
5508 update_bbox(startx,starty)
5509 end
5510 update_bbox(segment[2],segment[3])
5511 elseif type == "curveto" then
5512 local c6 = segment[6]
5513 local c7 = segment[7]
5514 splitter(0, 0, 1, startx, starty, segment[2], segment[3], segment[4], segment[5], c6, c7, startx, starty, c6, c7)
5515 elseif type == "moveto" then
5516 startx, starty = segment[2], segment[3]
5517 end
5518 end
5519 end
5520 end
5521 if minx then
5522 return minx, miny, maxx, maxy
5523 else
5524 return 0, 0, 0, 0
5525 end
5526end
5527
5528
5529
5530function operators.pathbbox()
5531 print("todo: pathbbox")
5532 push_opstack { "real", 'unlimited', 'literal', 0 }
5533 push_opstack { "real", 'unlimited', 'literal', 0 }
5534 push_opstack { "real", 'unlimited', 'literal', 1 }
5535 push_opstack { "real", 'unlimited', 'literal', 1 }
5536 return true
5537end
5538
5539
5540
5541
5542
5543
5544
5545devices.null = {
5546 initgraphics = function() gsstate.matrix = { 1, 0, 0, 1, 0, 0 } end,
5547 initclip = function() gsstate.clip = { } end,
5548 showpage = function() return "" end,
5549}
5550
5551
5552
5553local pdf = {
5554 initgraphics = function() gsstate.matrix = { 1, 0, 0, 1, 0, 0 } end,
5555 initclip = function() gsstate.clip = { } end,
5556
5557
5558
5559}
5560
5561devices.pdf = pdf
5562
5563function pdf.showpage(page)
5564
5565 local startpage = pdf.startpage
5566 local stoppage = pdf.stoppage
5567 local flushpage = pdf.flushpage
5568 local showfont = pdf.showfont
5569
5570 if not flushpage then
5571 return
5572 end
5573
5574 if startpage then
5575 startpage(boundingbox(page))
5576 end
5577
5578 local t = { "q" }
5579 local n = 1
5580 local g_colortype = "notacolor"
5581 local g_color = ""
5582 local g_miterlimit = -1
5583 local g_linejoin = -1
5584 local g_linecap = -1
5585 local g_linewidth = -1
5586 local g_dashpattern = nil
5587 local g_dashoffset = -1
5588 local flush = devices.pdf.flush
5589 for i=1,#page do
5590 local object = page[i]
5591 local path = object.path
5592 local otyp = object.type
5593 if otyp == "gsave" then
5594 n = n + 1 ; t[n] = "q"
5595
5596g_colortype = "notacolor"
5597g_color = ""
5598g_miterlimit = -1
5599g_linejoin = -1
5600g_linecap = -1
5601g_linewidth = -1
5602g_dashpattern = nil
5603g_dashoffset = -1
5604 elseif otyp == "grestore" then
5605g_colortype = "notacolor"
5606g_color = ""
5607g_miterlimit = -1
5608g_linejoin = -1
5609g_linecap = -1
5610g_linewidth = -1
5611g_dashpattern = nil
5612g_dashoffset = -1
5613 n = n + 1 ; t[n] = "Q"
5614 else
5615 if otyp ~= "clip" and otyp ~= "eoclip" then
5616 local colortype = object.colortype
5617 local color = object.color
5618 if colortype == "gray" then
5619 local v = formatters["%f g %f G"](color,color)
5620 if g_color ~= v then
5621 g_colortype = "gray"
5622 g_color = v
5623 n = n + 1 ; t[n] = v
5624 end
5625 elseif colortype == "rgb" then
5626 local r, g, b = color[1], color[2], color[3]
5627 local v = formatters["%f %f %f rg %f %f %f RG"](r,g,b,r,g,b)
5628 if g_color ~= v then
5629 g_colortype = "rgb"
5630 g_color = v
5631 n = n + 1 ; t[n] = v
5632 end
5633 elseif colortype == "cmyk" then
5634 local c, m, y, k = color[1], color[2], color[3], color[4]
5635 local v = formatters["%f %f %f %f k %f %f %f %f K"](c,m,y,k,c,m,y,k)
5636 if g_color ~= v then
5637 g_colortype = "cmyk"
5638 g_color = v
5639 n = n + 1 ; t[n] = v
5640 end
5641 elseif colortype == "hsb" then
5642 local r, g, b = hsv_to_rgb(color[1],color[2],color[3])
5643 local v = formatters["%f %f %f rg %f %f %f RG"](r,g,b,r,g,b)
5644 if g_color ~= v then
5645 g_colortype = "rgb"
5646 g_color = v
5647 n = n + 1 ; t[n] = v
5648 end
5649 end
5650 end
5651 if otyp == "stroke" then
5652 local miterlimit = object.miterlimit
5653 if g_miterlimit ~= miterlimit then
5654 g_miterlimit = miterlimit
5655 n = n + 1 ; t[n] = formatters["%f M"](miterlimit)
5656 end
5657 local linejoin = object.linejoin
5658 if g_linejoin ~= linejoin then
5659 g_linejoin = linejoin
5660 n = n + 1 ; t[n] = formatters["%d j"](linejoin)
5661 end
5662 local linecap = object.linecap
5663 if g_linecap ~= linecap then
5664 g_linecap = linecap
5665 n = n + 1 ; t[n] = formatters["%d J"](linecap)
5666 end
5667 local linewidth = object.linewidth
5668 if g_linewidth ~= linewidth then
5669 g_linewidth = linewidth
5670 n = n + 1 ; t[n] = formatters["%f w"](linewidth)
5671 end
5672 local dashpattern = object.dashpattern
5673 local dashoffset = object.dashoffset
5674 if g_dashpattern ~= dashpattern or g_dashoffset ~= dashoffset then
5675 g_dashpattern = dashpattern
5676 g_dashoffset = dashoffset
5677 local l = #dashpattern
5678 if l == 0 then
5679 n = n + 1 ; t[n] = "[] 0 d"
5680 else
5681 n = n + 1 ; t[n] = formatters["[% t] %d d"](dashpattern,dashoffset)
5682 end
5683 end
5684 end
5685 if path then
5686 for i=1,#path do
5687 local segment = path[i]
5688 local styp = segment[1]
5689 if styp == "moveto" then
5690 n = n + 1 ; t[n] = formatters["%f %f m"](segment[2],segment[3])
5691 elseif styp == "lineto" then
5692 n = n + 1 ; t[n] = formatters["%f %f l"](segment[2],segment[3])
5693 elseif styp == "curveto" then
5694 n = n + 1 ; t[n] = formatters["%f %f %f %f %f %f c"](segment[2],segment[3],segment[4],segment[5],segment[6],segment[7])
5695 elseif styp == "closepath" then
5696 n = n + 1 ; t[n] = "h"
5697 else
5698 report("unknown path segment type %a",styp)
5699 end
5700 end
5701 end
5702 if otyp == "stroke" then
5703 n = n + 1 ; t[n] = "S"
5704 elseif otyp == "fill" then
5705 n = n + 1 ; t[n] = "f"
5706 elseif otyp == "eofill" then
5707 n = n + 1 ; t[n] = "f*"
5708 elseif otyp == "clip" then
5709 n = n + 1 ; t[n] = "W n"
5710 elseif otyp == "eoclip" then
5711 n = n + 1 ; t[n] = "W* n"
5712 elseif otyp == "show" then
5713 if showfont then
5714 if n > 0 then
5715 flushpage(concat(t,"\n"))
5716 n = 0 ; t = { }
5717 end
5718 showfont(object)
5719 end
5720 else
5721
5722 end
5723 end
5724 end
5725 n = n + 1 ; t[n] = "Q"
5726 flushpage(concat(t,"\n"))
5727
5728 if startpage then
5729 stoppage()
5730 end
5731end
5732
5733function operators.showpage()
5734 local copies = lookup("#copies")
5735 if copies and copies[1] == 'integer' and copies[4] >= 1 then
5736 local amount = floor(copies[4])
5737 local render = device.showpage
5738 if render then
5739 for i=1,amount do
5740 render(currentpage)
5741 end
5742 end
5743 end
5744 operators.erasepage()
5745 operators.initgraphics()
5746 return true
5747end
5748
5749function operators.copypage()
5750 local render = device.showpage
5751 if render then
5752 render(currentpage)
5753 end
5754 return true
5755end
5756
5757function operators.banddevice()
5758 local d = pop_opstack()
5759 local c = pop_opstack()
5760 local b = pop_opstack()
5761 local a = pop_opstack()
5762 if not a then
5763 return ps_error('stackunderflow')
5764 end
5765 local ta, tb, tc, td = a[1], b[1], c[1], d[1]
5766 if not (ta == 'array' and a[5] == 6) then
5767 return ps_error('typecheck')
5768 end
5769 if not (td == 'array' and d[3] == 'executable') then
5770 return ps_error('typecheck')
5771 end
5772 if not (tb == 'real' or tb == 'integer') then
5773 return ps_error('typecheck')
5774 end
5775 if not (tc == 'real' or tc == 'integer') then
5776 return ps_error('typecheck')
5777 end
5778 local dev = device.banddevice
5779 if dev then
5780 dev(a,b,c,d)
5781 else
5782 return ps_error('undefined')
5783 end
5784 return true
5785end
5786
5787function operators.renderbands()
5788 local a = pop_opstack()
5789 if not a then
5790 return ps_error('stackunderflow')
5791 end
5792 if not (a[1] == 'array' and a[3] == 'executable') then
5793 return ps_error('typecheck')
5794 end
5795 local dev = device.renderbands
5796 if dev then
5797 dev(d)
5798 else
5799 return ps_error('undefined')
5800 end
5801 return true
5802end
5803
5804function operators.framedevice()
5805 local d = pop_opstack()
5806 local c = pop_opstack()
5807 local b = pop_opstack()
5808 local a = pop_opstack()
5809 if not a then
5810 return ps_error('stackunderflow')
5811 end
5812 local ta, tb, tc, td = a[1], b[1], c[1], d[1]
5813 if not (ta == 'array' and a[5] == 6) then
5814 return ps_error('typecheck')
5815 end
5816 if not (tb == 'real' or tb == 'integer') then
5817 return ps_error('typecheck')
5818 end
5819 if not (tc == 'real' or tc == 'integer') then
5820 return ps_error('typecheck')
5821 end
5822 if not (td == 'array' and d[3] == 'executable') then
5823 return ps_error('typecheck')
5824 end
5825 local dev = device.framedevice
5826 if dev then
5827 dev(a,b,c,d)
5828 else
5829 return ps_error('undefined')
5830 end
5831 return true
5832end
5833
5834function operators.nulldevice()
5835 gsstate.device = "null"
5836 operators.initgraphics()
5837 return true
5838end
5839
5840
5841
5842
5843
5844
5845
5846
5847local FontDirectory
5848
5849initializers[#initializers+1] = function(reset)
5850 if reset then
5851 FontDirectory = nil
5852 else
5853 FontDirectory = add_VM {
5854 access = 'unlimited',
5855 size = 0,
5856 maxsize = 5000,
5857 dict = { },
5858 }
5859 end
5860end
5861
5862
5863
5864local fontmap
5865
5866initializers[#initializers+1] = function()
5867 if reset then
5868 fontmap = nil
5869 else
5870 fontmap = {
5871 ['Courier-Bold'] = 'NimbusMonL-Bold.ps',
5872 ['Courier-BoldOblique'] = 'NimbusMonL-BoldObli.ps',
5873 ['Courier'] = 'NimbusMonL-Regu.ps',
5874 ['Courier-Oblique'] = 'NimbusMonL-ReguObli.ps',
5875 ['Times-Bold'] = 'NimbusRomNo9L-Medi.ps',
5876 ['Times-BoldItalic'] = 'NimbusRomNo9L-MediItal.ps',
5877 ['Times-Roman'] = 'NimbusRomNo9L-Regu.ps',
5878 ['Times-Italic'] = 'NimbusRomNo9L-ReguItal.ps',
5879 ['Helvetica-Bold'] = 'NimbusSanL-Bold.ps',
5880 ['Helvetica-BoldOblique'] = 'NimbusSanL-BoldItal.ps',
5881 ['Helvetica'] = 'NimbusSanL-Regu.ps',
5882 ['Helvetica-Oblique'] = 'NimbusSanL-ReguItal.ps',
5883 ['Symbol'] = 'StandardSymL.ps',
5884 }
5885 end
5886end
5887
5888
5889
5890local function findfont(fontname)
5891 return fontmap[fontname]
5892end
5893
5894
5895
5896local function checkfont(f)
5897
5898 local matrix = f['FontMatrix']
5899 if not matrix or matrix[1] ~= 'array' or matrix[5] ~= 6 then
5900 return false
5901 end
5902 local thearray = get_VM(matrix[4])
5903 for i=1,#thearray do
5904 local v = thearray[i]
5905 local tv = v[1]
5906 if not (tv == 'real' or tv == 'integer') then
5907 return false
5908 end
5909 end
5910
5911 local ftype = f['FontType']
5912 if not ftype or ftype[1] ~= 'integer' then
5913 return false
5914 end
5915
5916 local bbox = f['FontBBox']
5917
5918 if not bbox or bbox[1] ~= 'array' or bbox[6] ~= 4 then
5919 return false
5920 end
5921 local thearray = get_VM(bbox[4])
5922 for i=1,#thearray do
5923 local v = thearray[i]
5924 local tv = v[1]
5925 if not (tv == 'real' or tv == 'integer') then
5926 return false
5927 end
5928 end
5929
5930 local bbox = f['Encoding']
5931 if not bbox or bbox[1] ~= 'array' or bbox[5] ~= 256 then
5932 return false
5933 end
5934 local thearray = get_VM(bbox[4])
5935 for i=1,#thearray do
5936 local v = thearray[i]
5937 local tv = v[1]
5938 if tv[1] ~= 'name' then
5939 return false
5940 end
5941 end
5942 return true
5943end
5944
5945
5946
5947function operators.definefont()
5948 local b = pop_opstack()
5949 local a = pop_opstack()
5950 if not a then
5951 return ps_error('stackunderflow')
5952 end
5953 if b[1] ~= 'dict' then
5954 return ps_error('typecheck')
5955 end
5956
5957 if a[1] ~= 'name' then
5958 return ps_error('typecheck')
5959 end
5960 local fontdict = get_VM(b[4])
5961 if not checkfont(fontdict.dict) then
5962 return ps_error('invalidfont')
5963 end
5964
5965 if fontdict.size == fontdict.maxsize then
5966 return ps_error('invalidfont')
5967 end
5968 fontdict.dict['FID'] = {'font', 'executable', 'literal', b[4]}
5969 fontdict.size = fontdict.size + 1
5970 fontdict.access = 'read-only'
5971 local dict = get_VM(FontDirectory)
5972 local key = get_VM(a[4])
5973 if not dict.dict[key] and dict.size == dict.maxsize then
5974
5975 end
5976 if not dict.dict[key] then
5977 dict.size = dict.size + 1
5978 end
5979 dict.dict[key] = fontdict.dict['FID']
5980 push_opstack(b)
5981 return true
5982end
5983
5984function operators.findfont()
5985 local a = pop_opstack()
5986 if not a then
5987 return ps_error('stackunderflow')
5988 end
5989 if a[1] ~= 'name' then
5990 return ps_error('typecheck')
5991 end
5992 local fontdict = get_VM(FontDirectory)
5993 local key = get_VM(a[4])
5994 local dict = dict.dict
5995 if not dict[key] then
5996 fname = findfont(key)
5997 if not fname then
5998 return ps_error('invalidfont')
5999 end
6000 local oldfontkeys = { }
6001 for k, v in next, dict do
6002 oldfontkeys[i] = 1
6003 end
6004 report("loading font file %a",fname)
6005 local theopstack = opstackptr
6006 local run = formatters['/eexec {pop} def (%s) run'](fname)
6007 push_execstack { '.stopped', 'unlimited', 'literal', false }
6008 local curstack = execstackptr
6009 push_execstack { 'string', 'unlimited', 'executable', add_VM(run), 1, #run }
6010 while curstack < execstackptr do
6011 do_exec()
6012 end
6013 if execstack[execstackptr][1] == '.stopped' then
6014 pop_execstack()
6015 end
6016 opstackptr = theopstack
6017 local fkey, ftab
6018 for k, v in next, dict do
6019 if not oldfontkeys[k] then
6020
6021 fkey = k
6022 ftab = v
6023 break
6024 end
6025 end
6026 if not fkey then
6027 return ps_error('invalidfont')
6028 end
6029 dict[key] = ftab
6030 end
6031 push_opstack(dict[key])
6032 return true
6033end
6034
6035local function pushscaledcopy(fontdict,matrix)
6036 local olddict = fontdict.dict
6037 if not checkfont(olddict) then
6038 return ps_error('invalidfont')
6039 end
6040 local newdict = { }
6041 local oldsize = fontdict.size
6042 local newfontdict = {
6043 dict = newdict,
6044 access = 'read-only',
6045 size = oldsize,
6046 maxsize = oldsize,
6047 }
6048 for k, v in next, olddict do
6049 if k == "FontMatrix" then
6050 local oldmatrix = get_VM(v[4])
6051 local old = {
6052 oldmatrix[1][4],
6053 oldmatrix[2][4],
6054 oldmatrix[3][4],
6055 oldmatrix[4][4],
6056 oldmatrix[5][4],
6057 oldmatrix[6][4],
6058 }
6059 local c = do_concat(old,matrix)
6060 local new = {
6061 { 'real', 'unlimited', 'literal', c[1] },
6062 { 'real', 'unlimited', 'literal', c[2] },
6063 { 'real', 'unlimited', 'literal', c[3] },
6064 { 'real', 'unlimited', 'literal', c[4] },
6065 { 'real', 'unlimited', 'literal', c[5] },
6066 { 'real', 'unlimited', 'literal', c[6] }
6067 }
6068 newdict[k] = { 'array', 'unlimited', 'literal', add_VM(new), 6, 6 }
6069 elseif k == "FID" then
6070
6071 else
6072 newfontdict.dict[k] = v
6073 end
6074 end
6075 local f = add_VM(newfontdict)
6076 newdict['FID'] = { 'font', 'read-only', 'literal', f }
6077 push_opstack { 'font', 'read-only', 'literal', f }
6078 return true
6079end
6080
6081function operators.scalefont()
6082 local s = pop_opstack()
6083 local b = pop_opstack()
6084 if not b then
6085 return ps_error('stackunderflow')
6086 end
6087 if b[1] ~= 'font' then
6088 return ps_error('typecheck')
6089 end
6090 if not (s[1] == 'integer' or s[1] == 'real') then
6091 return ps_error('typecheck')
6092 end
6093 local scals = s[4]
6094 local matrix = { scale, 0, 0, scale, 0, 0 }
6095 local fontdict = get_VM(b[4])
6096 return pushscaledcopy(fontdict,matrix)
6097end
6098
6099function operators.makefont()
6100 local s = pop_opstack()
6101 local b = pop_opstack()
6102 if not b then
6103 return ps_error('stackunderflow')
6104 end
6105 if b[1] ~= 'font' then
6106 return ps_error('typecheck')
6107 end
6108 if s[1] ~= 'array' then
6109 return ps_error('typecheck')
6110 end
6111 if s[6] ~= 6 then
6112 return ps_error('rangecheck')
6113 end
6114 local matrix = { }
6115 local array = get_VM(s[4])
6116 for i=1,#array do
6117 local v = array[i]
6118 local tv = v[1]
6119 if not (tv == 'real' or tv == 'integer') then
6120 return ps_error('typecheck')
6121 end
6122 matrix[i] = v[4]
6123 end
6124 local fontdict = get_VM(b[4])
6125 pushscaledcopy(fontdict,matrix)
6126 return true
6127end
6128
6129function operators.setfont()
6130 local b = pop_opstack()
6131 if not b then
6132 return ps_error('stackunderflow')
6133 end
6134 if b[1] ~= 'font' then
6135 return ps_error('typecheck')
6136 end
6137 gsstate.font = b[4]
6138 return true
6139end
6140
6141
6142
6143
6144function operators.currentfont()
6145 if not gsstate.font then
6146 return ps_error('invalidfont')
6147 end
6148 push_opstack {'font', 'read-only', 'literal', gsstate.font }
6149 return true
6150end
6151
6152function do_show(fontdict,s)
6153 local stringmatrix = { }
6154 local truematrix = { }
6155 local stringencoding = { }
6156
6157 local dict = fontdict.dict
6158 local fontname = get_VM(dict['FontName'][4])
6159 local fontmatrix = get_VM(dict['FontMatrix'][4])
6160 local encoding = get_VM(dict['Encoding'][4])
6161 local matrix = gsstate.matrix
6162 local position = gsstate.position
6163 local color = gsstate.color
6164 local colortype = color.type
6165 local colordata = color[colortype]
6166
6167 if fontmatrix then
6168 for i=1,#fontmatrix do
6169 stringmatrix[i] = fontmatrix[i][4]
6170 end
6171 end
6172 if matrix then
6173 for i=1,#matrix do
6174 truematrix[i] = matrix[i]
6175 end
6176 end
6177 if encoding then
6178 for i=1,#m do
6179 stringencoding[i] = get_VM(e[i][4])
6180 end
6181 end
6182 if type(colordata) == "table" then
6183 colordata = { unpack(colordata) }
6184 end
6185 currentpage[#currentpage+1] = {
6186 type = 'show',
6187 string = s,
6188 fontname = fontname,
6189 adjust = nil,
6190 x = position[1],
6191 y = position[2],
6192 encoding = stringencoding,
6193 fontmatrix = stringmatrix,
6194 matrix = truematrix,
6195 colortype = colortype,
6196 color = colordata,
6197 }
6198
6199end
6200
6201function operators.show()
6202 local s = pop_opstack()
6203 if not s then
6204 return ps_error('stackunderflow')
6205 end
6206 if s[1] ~= 'string' then
6207 return ps_error('typecheck')
6208 end
6209 if #gsstate.position == 0 then
6210 return ps_error('nocurrentpoint')
6211 end
6212 if not gsstate.font then
6213 return ps_error('invalidfont')
6214 end
6215 local fontdict = get_VM(gsstate.font)
6216 if fontdict.access == "noaccess" then
6217 return ps_error('invalidaccess')
6218 end
6219 if not checkfont(fontdict.dict) then
6220 return ps_error('invalidfont')
6221 end
6222 do_show(fontdict,get_VM(s[4]))
6223end
6224
6225
6226function operators.kshow()
6227 local a = pop_opstack()
6228 local b = pop_opstack()
6229 if not a then
6230 return ps_error('stackunderflow')
6231 end
6232 if b[1] ~= "array" and b[3] == 'executable' then
6233 return ps_error('typecheck')
6234 end
6235 if b[2] == 'noaccess' then
6236 return ps_error('invalidaccess')
6237 end
6238 if not a[1] == 'string' then
6239 return ps_error('typecheck')
6240 end
6241 if a[2] == "execute-only" or a[2] == 'noaccess' then
6242 return ps_error('invalidaccess')
6243 end
6244 local fontdict = get_VM(gsstate.font)
6245 if fontdict.access == "noaccess" then
6246 return ps_error('invalidaccess')
6247 end
6248 if #gsstate.position == 0 then
6249 return ps_error('nocurrentpoint')
6250 end
6251
6252 push_execstack { '.exit', 'unlimited', 'literal', false }
6253 local curstack = execstackptr
6254 if a[6] == 0 then
6255 return true
6256 end
6257 b[7] = 'i'
6258 local thestring = get_VM(a[4])
6259 local v = sub(thestring,1,1)
6260 thestring = sub(thestring,2,-1)
6261 do_show(fontdict,v)
6262 for w in gmatch(thestring,".") do
6263 if stopped then
6264 stopped = false
6265 return false
6266 end
6267 push_opstack { 'integer', 'unlimited', 'literal', byte(v) }
6268 push_opstack { 'integer', 'unlimited', 'literal', byte(w) }
6269 b[5] = 1
6270 push_execstack(b)
6271 while curstack < execstackptr do
6272 do_exec()
6273 end
6274 local entry = execstack[execstackptr]
6275 if entry[1] == '.exit' and entry[4] == true then
6276 pop_execstack()
6277 return true
6278 end
6279 do_show(fontdict,w)
6280 v = w
6281 end
6282 return true
6283end
6284
6285local the_standardencoding = {
6286 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6287 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6288 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6289 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6290 '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand',
6291 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma',
6292 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four',
6293 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
6294 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F',
6295 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
6296 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
6297 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b',
6298 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
6299 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar',
6300 'braceright', 'asciitilde', '.notdef', '.notdef', '.notdef',
6301 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6302 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6303 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6304 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6305 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6306 '.notdef', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen',
6307 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft',
6308 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl',
6309 '.notdef', 'endash', 'dagger', 'daggerdbl', 'periodcentered', '.notdef',
6310 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase',
6311 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', '.notdef',
6312 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron',
6313 'breve', 'dotaccent', 'dieresis', '.notdef', 'ring', 'cedilla',
6314 '.notdef', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '.notdef',
6315 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6316 '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6317 '.notdef', '.notdef', '.notdef', 'AE', '.notdef', 'ordfeminine',
6318 '.notdef', '.notdef', '.notdef', '.notdef', 'Lslash', 'Oslash', 'OE',
6319 'ordmasculine', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
6320 'ae', '.notdef', '.notdef', '.notdef', 'dotlessi', '.notdef',
6321 '.notdef', 'lslash', 'oslash', 'oe', 'germandbls', '.notdef',
6322 '.notdef', '.notdef', '.notdef'
6323}
6324
6325local function standardencoding()
6326 local a = { }
6327 for i=1,#the_standardencoding do
6328 a[i] = { 'name', 'unlimited', 'literal', add_VM(the_standardencoding[i]) }
6329 end
6330 return a
6331end
6332
6333
6334
6335
6336
6337
6338
6339local systemdict
6340local userdict
6341
6342initializers[#initializers+1] = function(reset)
6343 if reset then
6344 systemdict = nil
6345 else
6346 dictstackptr = dictstackptr + 1
6347 dictstack[dictstackptr] = add_VM {
6348 access = 'unlimited',
6349 maxsize = MAX_INTEGER,
6350 size = 0,
6351 dict = { },
6352 }
6353 if directvm then
6354 systemdict = dictstack[dictstackptr]
6355 else
6356 systemdict = dictstackptr
6357 end
6358 end
6359end
6360
6361initializers[#initializers+1] = function(reset)
6362 if reset then
6363 userdict = nil
6364 else
6365 dictstackptr = dictstackptr + 1
6366 dictstack[dictstackptr] = add_VM {
6367 access = 'unlimited',
6368 maxsize = MAX_INTEGER,
6369 size = 0,
6370 dict = { },
6371 }
6372 if directvm then
6373 userdict = dictstack[dictstackptr]
6374 else
6375 userdict = dictstackptr
6376 end
6377 end
6378end
6379
6380initializers[#initializers+1] = function(reset)
6381 if reset then
6382
6383 else
6384 local dict = {
6385 ['$error'] = { 'dict', 'unlimited', 'literal', dicterror },
6386 ['['] = { 'operator', 'unlimited', 'executable', operators.beginarray, '[' },
6387 [']'] = { 'operator', 'unlimited', 'executable', operators.endarray, ']' },
6388
6389 ['=='] = { 'operator', 'unlimited', 'executable', operators.equal, '==' },
6390 ['abs'] = { 'operator', 'unlimited', 'executable', operators.abs, 'abs' },
6391 ['add'] = { 'operator', 'unlimited', 'executable', operators.add, 'add' },
6392 ['aload'] = { 'operator', 'unlimited', 'executable', operators.aload, 'aload' },
6393 ['anchorsearch'] = { 'operator', 'unlimited', 'executable', operators.anchorsearch, 'anchorsearch' },
6394 ['and'] = { 'operator', 'unlimited', 'executable', operators["and"], 'and' },
6395 ['arc'] = { 'operator', 'unlimited', 'executable', operators.arc, 'arc' },
6396 ['arcn'] = { 'operator', 'unlimited', 'executable', operators.arcn, 'arcn' },
6397 ['arcto'] = { 'operator', 'unlimited', 'executable', operators.arcto, 'arcto' },
6398 ['array'] = { 'operator', 'unlimited', 'executable', operators.array, 'array' },
6399 ['astore'] = { 'operator', 'unlimited', 'executable', operators.astore, 'astore' },
6400 ['atan'] = { 'operator', 'unlimited', 'executable', operators.atan, 'atan' },
6401 ['banddevice'] = { 'operator', 'unlimited', 'executable', operators.banddevice, 'banddevice' },
6402 ['bind'] = { 'operator', 'unlimited', 'executable', operators.bind, 'bind' },
6403 ['bitshift'] = { 'operator', 'unlimited', 'executable', operators.bitshift, 'bitshift' },
6404 ['begin'] = { 'operator', 'unlimited', 'executable', operators.begin, 'begin' },
6405 ['bytesavailable'] = { 'operator', 'unlimited', 'executable', operators.bytesavailable, 'bytesavailable' },
6406 ['ceiling'] = { 'operator', 'unlimited', 'executable', operators.ceiling, 'ceiling' },
6407 ['clear'] = { 'operator', 'unlimited', 'executable', operators.clear, 'clear' },
6408 ['cleartomark'] = { 'operator', 'unlimited', 'executable', operators.cleartomark, 'cleartomark' },
6409 ['clip'] = { 'operator', 'unlimited', 'executable', operators.clip, 'clip' },
6410 ['clippath'] = { 'operator', 'unlimited', 'executable', operators.clippath, 'clippath' },
6411 ['pathbbox'] = { 'operator', 'unlimited', 'executable', operators.pathbbox, 'pathbbox' },
6412 ['closefile'] = { 'operator', 'unlimited', 'executable', operators.closefile, 'closefile' },
6413 ['closepath'] = { 'operator', 'unlimited', 'executable', operators.closepath, 'closepath' },
6414 ['concat'] = { 'operator', 'unlimited', 'executable', operators.concat, 'concat' },
6415 ['concatmatrix'] = { 'operator', 'unlimited', 'executable', operators.concatmatrix, 'concatmatrix' },
6416 ['copy'] = { 'operator', 'unlimited', 'executable', operators.copy, 'copy' },
6417 ['copypage'] = { 'operator', 'unlimited', 'executable', operators.copypage, 'copypage' },
6418 ['cos'] = { 'operator', 'unlimited', 'executable', operators.cos, 'cos' },
6419 ['count'] = { 'operator', 'unlimited', 'executable', operators.count, 'count' },
6420 ['countdictstack'] = { 'operator', 'unlimited', 'executable', operators.countdictstack, 'countdictstack' },
6421 ['countexecstack'] = { 'operator', 'unlimited', 'executable', operators.countexecstack, 'countexecstack' },
6422 ['counttomark'] = { 'operator', 'unlimited', 'executable', operators.counttomark, 'counttomark' },
6423 ['currentdash'] = { 'operator', 'unlimited', 'executable', operators.currentdash, 'currentdash' },
6424 ['currentdict'] = { 'operator', 'unlimited', 'executable', operators.currentdict, 'currentdict' },
6425 ['currentfile'] = { 'operator', 'unlimited', 'executable', operators.currentfile, 'currentfile' },
6426 ['currentflat'] = { 'operator', 'unlimited', 'executable', operators.currentflat, 'currentflat' },
6427 ['currentfont'] = { 'operator', 'unlimited', 'executable', operators.currentfont, 'currentfont' },
6428 ['currentgray'] = { 'operator', 'unlimited', 'executable', operators.currentgray, 'currentgray' },
6429 ['currenthsbcolor'] = { 'operator', 'unlimited', 'executable', operators.currenthsbcolor, 'currenthsbcolor' },
6430 ['currentlinecap'] = { 'operator', 'unlimited', 'executable', operators.currentlinecap, 'currentlinecap' },
6431 ['currentlinejoin'] = { 'operator', 'unlimited', 'executable', operators.currentlinejoin, 'currentlinejoin' },
6432 ['currentlinewidth'] = { 'operator', 'unlimited', 'executable', operators.currentlinewidth, 'currentlinewidth' },
6433 ['currentmatrix'] = { 'operator', 'unlimited', 'executable', operators.currentmatrix, 'currentmatrix' },
6434 ['currentmiterlimit'] = { 'operator', 'unlimited', 'executable', operators.currentmiterlimit, 'currentmiterlimit' },
6435 ['currentpoint'] = { 'operator', 'unlimited', 'executable', operators.currentpoint, 'currentpoint' },
6436 ['currentrgbcolor'] = { 'operator', 'unlimited', 'executable', operators.currentrgbcolor, 'currentrgbcolor' },
6437 ['currentcmykcolor'] = { 'operator', 'unlimited', 'executable', operators.currentcmykcolor, 'currentcmykcolor' },
6438 ['currentscreen'] = { 'operator', 'unlimited', 'executable', operators.currentscreen, 'currentscreen' },
6439 ['currenttransfer'] = { 'operator', 'unlimited', 'executable', operators.currenttransfer, 'currenttransfer' },
6440 ['curveto'] = { 'operator', 'unlimited', 'executable', operators.curveto, 'curveto' },
6441 ['cvi'] = { 'operator', 'unlimited', 'executable', operators.cvi, 'cvi' },
6442 ['cvlit'] = { 'operator', 'unlimited', 'executable', operators.cvlit, 'cvlit' },
6443 ['cvn'] = { 'operator', 'unlimited', 'executable', operators.cvn, 'cvn' },
6444 ['cvr'] = { 'operator', 'unlimited', 'executable', operators.cvr, 'cvr' },
6445 ['cvrs'] = { 'operator', 'unlimited', 'executable', operators.cvrs, 'cvrs' },
6446 ['cvs'] = { 'operator', 'unlimited', 'executable', operators.cvs, 'cvs' },
6447 ['cvx'] = { 'operator', 'unlimited', 'executable', operators.cvx, 'cvx' },
6448 ['def'] = { 'operator', 'unlimited', 'executable', operators.def, 'def' },
6449 ['definefont'] = { 'operator', 'unlimited', 'executable', operators.definefont, 'definefont' },
6450 ['dict'] = { 'operator', 'unlimited', 'executable', operators.dict, 'dict' },
6451 ['dictstack'] = { 'operator', 'unlimited', 'executable', operators.dictstack, 'dictstack' },
6452 ['div'] = { 'operator', 'unlimited', 'executable', operators.div, 'div' },
6453 ['dtransform'] = { 'operator', 'unlimited', 'executable', operators.dtransform, 'dtransform' },
6454 ['dup'] = { 'operator', 'unlimited', 'executable', operators.dup, 'dup' },
6455 ['echo'] = { 'operator', 'unlimited', 'executable', operators.echo, 'echo' },
6456 ['end'] = { 'operator', 'unlimited', 'executable', operators["end"], 'end' },
6457 ['eoclip'] = { 'operator', 'unlimited', 'executable', operators.eoclip, 'eoclip' },
6458 ['eofill'] = { 'operator', 'unlimited', 'executable', operators.eofill, 'eofill' },
6459 ['eq'] = { 'operator', 'unlimited', 'executable', operators.eq, 'eq' },
6460 ['errordict'] = { 'dict', 'unlimited', 'literal', errordict },
6461 ['exch'] = { 'operator', 'unlimited', 'executable', operators.exch, 'exch' },
6462 ['exec'] = { 'operator', 'unlimited', 'executable', operators.exec, 'exec' },
6463 ['execstack'] = { 'operator', 'unlimited', 'executable', operators.execstack, 'execstack' },
6464 ['executeonly'] = { 'operator', 'unlimited', 'executable', operators.executeonly, 'executeonly' },
6465 ['exit'] = { 'operator', 'unlimited', 'executable', operators.exit, 'exit' },
6466 ['exp'] = { 'operator', 'unlimited', 'executable', operators.exp, 'exp' },
6467 ['false'] = { 'boolean', 'unlimited', 'literal', false },
6468 ['file'] = { 'operator', 'unlimited', 'executable', operators.file, 'file' },
6469 ['fill'] = { 'operator', 'unlimited', 'executable', operators.fill, 'fill' },
6470 ['findfont'] = { 'operator', 'unlimited', 'executable', operators.findfont, 'findfont' },
6471 ['FontDirectory'] = { 'dict', 'unlimited', 'literal', escrito['FontDirectory'] },
6472 ['flattenpath'] = { 'operator', 'unlimited', 'executable', operators.flattenpath, 'flattenpath' },
6473 ['floor'] = { 'operator', 'unlimited', 'executable', operators.floor, 'floor' },
6474 ['flush'] = { 'operator', 'unlimited', 'executable', operators.flush, 'flush' },
6475 ['flushfile'] = { 'operator', 'unlimited', 'executable', operators.flushfile, 'flushfile' },
6476 ['for'] = { 'operator', 'unlimited', 'executable', operators["for"], 'for' },
6477 ['forall'] = { 'operator', 'unlimited', 'executable', operators.forall, 'forall' },
6478 ['framedevice'] = { 'operator', 'unlimited', 'executable', operators.framedevice, 'framedevice' },
6479 ['ge'] = { 'operator', 'unlimited', 'executable', operators.ge, 'ge' },
6480 ['get'] = { 'operator', 'unlimited', 'executable', operators.get, 'get' },
6481 ['getinterval'] = { 'operator', 'unlimited', 'executable', operators.getinterval, 'getinterval' },
6482 ['grestore'] = { 'operator', 'unlimited', 'executable', operators.grestore, 'grestore' },
6483 ['grestoreall'] = { 'operator', 'unlimited', 'executable', operators.grestoreall, 'grestoreall' },
6484 ['gsave'] = { 'operator', 'unlimited', 'executable', operators.gsave, 'gsave' },
6485 ['gt'] = { 'operator', 'unlimited', 'executable', operators.gt, 'gt' },
6486 ['identmatrix'] = { 'operator', 'unlimited', 'executable', operators.identmatrix, 'identmatrix' },
6487 ['idiv'] = { 'operator', 'unlimited', 'executable', operators.idiv, 'idiv' },
6488 ['if'] = { 'operator', 'unlimited', 'executable', operators["if"], 'if' },
6489 ['ifelse'] = { 'operator', 'unlimited', 'executable', operators.ifelse, 'ifelse' },
6490 ['index'] = { 'operator', 'unlimited', 'executable', operators.index, 'index' },
6491 ['initclip'] = { 'operator', 'unlimited', 'executable', operators.initclip, 'initclip' },
6492 ['initgraphics'] = { 'operator', 'unlimited', 'executable', operators.initgraphics, 'initgraphics' },
6493 ['initmatrix'] = { 'operator', 'unlimited', 'executable', operators.initmatrix, 'initmatrix' },
6494 ['invertmatrix'] = { 'operator', 'unlimited', 'executable', operators.invertmatrix, 'invertmatrix' },
6495 ['idtransform'] = { 'operator', 'unlimited', 'executable', operators.idtransform, 'idtransform' },
6496 ['itransform'] = { 'operator', 'unlimited', 'executable', operators.itransform, 'itransform' },
6497 ['known'] = { 'operator', 'unlimited', 'executable', operators.known, 'known' },
6498 ['kshow'] = { 'operator', 'unlimited', 'executable', operators.kshow, 'kshow' },
6499 ['le'] = { 'operator', 'unlimited', 'executable', operators.le, 'le' },
6500 ['length'] = { 'operator', 'unlimited', 'executable', operators.length, 'length' },
6501 ['lineto'] = { 'operator', 'unlimited', 'executable', operators.lineto, 'lineto' },
6502 ['ln'] = { 'operator', 'unlimited', 'executable', operators.ln, 'ln' },
6503 ['load'] = { 'operator', 'unlimited', 'executable', operators.load, 'load' },
6504 ['log'] = { 'operator', 'unlimited', 'executable', operators.log, 'log' },
6505 ['loop'] = { 'operator', 'unlimited', 'executable', operators.loop, 'loop' },
6506 ['lt'] = { 'operator', 'unlimited', 'executable', operators.lt, 'lt' },
6507 ['makefont'] = { 'operator', 'unlimited', 'executable', operators.makefont, 'makefont' },
6508 ['mark'] = { 'operator', 'unlimited', 'executable', operators.mark, 'mark' },
6509 ['matrix'] = { 'operator', 'unlimited', 'executable', operators.matrix, 'matrix' },
6510 ['maxlength'] = { 'operator', 'unlimited', 'executable', operators.maxlength, 'maxlength' },
6511 ['mod'] = { 'operator', 'unlimited', 'executable', operators.mod, 'mod' },
6512 ['moveto'] = { 'operator', 'unlimited', 'executable', operators.moveto, 'moveto' },
6513 ['mul'] = { 'operator', 'unlimited', 'executable', operators.mul, 'mul' },
6514 ['ne'] = { 'operator', 'unlimited', 'executable', operators.ne, 'ne' },
6515 ['neg'] = { 'operator', 'unlimited', 'executable', operators.neg, 'neg' },
6516 ['newpath'] = { 'operator', 'unlimited', 'executable', operators.newpath, 'newpath' },
6517 ['noaccess'] = { 'operator', 'unlimited', 'executable', operators.noaccess, 'noaccess' },
6518 ['not'] = { 'operator', 'unlimited', 'executable', operators["not"], 'not' },
6519 ['null'] = { 'operator', 'unlimited', 'executable', operators.null, 'null' },
6520 ['or'] = { 'operator', 'unlimited', 'executable', operators["or"], 'or' },
6521 ['pop'] = { 'operator', 'unlimited', 'executable', operators.pop, 'pop' },
6522 ['print'] = { 'operator', 'unlimited', 'executable', operators.print, 'print' },
6523 ['pstack'] = { 'operator', 'unlimited', 'executable', operators.pstack, 'pstack' },
6524 ['put'] = { 'operator', 'unlimited', 'executable', operators.put, 'put' },
6525 ['putinterval'] = { 'operator', 'unlimited', 'executable', operators.putinterval, 'putinterval' },
6526 ['quit'] = { 'operator', 'unlimited', 'executable', operators.quit, 'quit' },
6527 ['rand'] = { 'operator', 'unlimited', 'executable', operators.rand, 'rand' },
6528 ['rcheck'] = { 'operator', 'unlimited', 'executable', operators.rcheck, 'rcheck' },
6529 ['rcurveto'] = { 'operator', 'unlimited', 'executable', operators.rcurveto, 'rcurveto' },
6530 ['read'] = { 'operator', 'unlimited', 'executable', operators.read, 'read' },
6531 ['readhexstring'] = { 'operator', 'unlimited', 'executable', operators.readhexstring, 'readhexstring' },
6532 ['readline'] = { 'operator', 'unlimited', 'executable', operators.readline, 'readline' },
6533 ['readonly'] = { 'operator', 'unlimited', 'executable', operators.readonly, 'readonly' },
6534 ['renderbands'] = { 'operator', 'unlimited', 'executable', operators.renderbands, 'renderbands' },
6535 ['repeat'] = { 'operator', 'unlimited', 'executable', operators["repeat"], 'repeat' },
6536 ['resetfile'] = { 'operator', 'unlimited', 'executable', operators.resetfile, 'resetfile' },
6537 ['restore'] = { 'operator', 'unlimited', 'executable', operators.restore, 'restore' },
6538 ['rlineto'] = { 'operator', 'unlimited', 'executable', operators.rlineto, 'rlineto' },
6539 ['rmoveto'] = { 'operator', 'unlimited', 'executable', operators.rmoveto, 'rmoveto' },
6540 ['roll'] = { 'operator', 'unlimited', 'executable', operators.roll, 'roll' },
6541 ['rotate'] = { 'operator', 'unlimited', 'executable', operators.rotate, 'rotate' },
6542 ['round'] = { 'operator', 'unlimited', 'executable', operators.round, 'round' },
6543 ['rrand'] = { 'operator', 'unlimited', 'executable', operators.rrand, 'rrand' },
6544 ['run'] = { 'operator', 'unlimited', 'executable', operators.run, 'run' },
6545 ['save'] = { 'operator', 'unlimited', 'executable', operators.save, 'save' },
6546 ['scale'] = { 'operator', 'unlimited', 'executable', operators.scale, 'scale' },
6547 ['scalefont'] = { 'operator', 'unlimited', 'executable', operators.scalefont, 'scalefont' },
6548 ['search'] = { 'operator', 'unlimited', 'executable', operators.search, 'search' },
6549 ['setdash'] = { 'operator', 'unlimited', 'executable', operators.setdash, 'setdash' },
6550 ['setflat'] = { 'operator', 'unlimited', 'executable', operators.setflat, 'setflat' },
6551 ['setfont'] = { 'operator', 'unlimited', 'executable', operators.setfont, 'setfont' },
6552 ['setgray'] = { 'operator', 'unlimited', 'executable', operators.setgray, 'setgray' },
6553 ['sethsbcolor'] = { 'operator', 'unlimited', 'executable', operators.sethsbcolor, 'sethsbcolor' },
6554 ['setlinecap'] = { 'operator', 'unlimited', 'executable', operators.setlinecap, 'setlinecap' },
6555 ['setlinejoin'] = { 'operator', 'unlimited', 'executable', operators.setlinejoin, 'setlinejoin' },
6556 ['setlinewidth'] = { 'operator', 'unlimited', 'executable', operators.setlinewidth, 'setlinewidth' },
6557 ['setmatrix'] = { 'operator', 'unlimited', 'executable', operators.setmatrix, 'setmatrix' },
6558 ['setmiterlimit'] = { 'operator', 'unlimited', 'executable', operators.setmiterlimit, 'setmiterlimit' },
6559 ['setrgbcolor'] = { 'operator', 'unlimited', 'executable', operators.setrgbcolor, 'setrgbcolor' },
6560 ['setcmykcolor'] = { 'operator', 'unlimited', 'executable', operators.setcmykcolor, 'setcmykcolor' },
6561 ['setscreen'] = { 'operator', 'unlimited', 'executable', operators.setscreen, 'setscreen' },
6562 ['settransfer'] = { 'operator', 'unlimited', 'executable', operators.settransfer, 'settransfer' },
6563 ['show'] = { 'operator', 'unlimited', 'executable', operators.show, 'show' },
6564 ['showpage'] = { 'operator', 'unlimited', 'executable', operators.showpage, 'showpage' },
6565 ['sin'] = { 'operator', 'unlimited', 'executable', operators.sin, 'sin' },
6566 ['sqrt'] = { 'operator', 'unlimited', 'executable', operators.sqrt, 'sqrt' },
6567 ['srand'] = { 'operator', 'unlimited', 'executable', operators.srand, 'srand' },
6568 ['stack'] = { 'operator', 'unlimited', 'executable', operators.stack, 'stack' },
6569 ['start'] = { 'operator', 'unlimited', 'executable', operators.start, 'start' },
6570 ['StandardEncoding'] = { 'array', 'unlimited', 'literal', add_VM(standardencoding()), 256, 256 },
6571 ['status'] = { 'operator', 'unlimited', 'executable', operators.status, 'status' },
6572 ['stop'] = { 'operator', 'unlimited', 'executable', operators.stop, 'stop' },
6573 ['stopped'] = { 'operator', 'unlimited', 'executable', operators.stopped, 'stopped' },
6574 ['store'] = { 'operator', 'unlimited', 'executable', operators.store, 'store' },
6575 ['string'] = { 'operator', 'unlimited', 'executable', operators.string, 'string' },
6576 ['stroke'] = { 'operator', 'unlimited', 'executable', operators.stroke, 'stroke' },
6577 ['sub'] = { 'operator', 'unlimited', 'executable', operators.sub, 'sub' },
6578 ['systemdict'] = { 'dict', 'unlimited', 'literal', systemdict },
6579 ['token'] = { 'operator', 'unlimited', 'executable', operators.token, 'token' },
6580 ['translate'] = { 'operator', 'unlimited', 'executable', operators.translate, 'translate' },
6581 ['transform'] = { 'operator', 'unlimited', 'executable', operators.transform, 'transform' },
6582 ['true'] = { 'boolean', 'unlimited', 'literal', true },
6583 ['truncate'] = { 'operator', 'unlimited', 'executable', operators.truncate, 'truncate' },
6584 ['type'] = { 'operator', 'unlimited', 'executable', operators.type, 'type' },
6585 ['userdict'] = { 'dict', 'unlimited', 'literal', userdict },
6586 ['usertime'] = { 'operator', 'unlimited', 'executable', operators.usertime, 'usertime' },
6587 ['version'] = { 'operator', 'unlimited', 'executable', operators.version, 'version' },
6588 ['vmstatus'] = { 'operator', 'unlimited', 'executable', operators.vmstatus, 'vmstatus' },
6589 ['wcheck'] = { 'operator', 'unlimited', 'executable', operators.wcheck, 'wcheck' },
6590 ['where'] = { 'operator', 'unlimited', 'executable', operators.where, 'where' },
6591 ['write'] = { 'operator', 'unlimited', 'executable', operators.write, 'write' },
6592 ['writehexstring'] = { 'operator', 'unlimited', 'executable', operators.writehexstring, 'writehexstring' },
6593 ['writestring'] = { 'operator', 'unlimited', 'executable', operators.writestring, 'writestring' },
6594 ['xcheck'] = { 'operator', 'unlimited', 'executable', operators.xcheck, 'xcheck' },
6595 ['xor'] = { 'operator', 'unlimited', 'executable', operators.xor, 'xor' },
6596 }
6597 if directvm then
6598 systemdict.dict = dict
6599 else
6600 VM[dictstack[systemdict]].dict = dict
6601 end
6602 end
6603end
6604
6605initializers[#initializers+1] = function(reset)
6606 if reset then
6607 dicterror = nil
6608 errordict = nil
6609 else
6610 dicterror = add_VM {
6611 access = 'unlimited',
6612 size = 1,
6613 maxsize = 40,
6614 dict = {
6615 newerror = p_false
6616 },
6617 }
6618
6619 errordict = add_VM {
6620 access = 'unlimited',
6621 size = 0,
6622 maxsize = 40,
6623 dict = { },
6624 }
6625
6626 local d
6627 if directvm then
6628 d = systemdict.dict
6629 else
6630 d = VM[dictstack[systemdict]].dict
6631 end
6632
6633 d['errordict'] = { 'dict', 'unlimited', 'literal', errordict }
6634 d['systemdict'] = { 'dict', 'unlimited', 'literal', systemdict }
6635 d['userdict'] = { 'dict', 'unlimited', 'literal', userdict }
6636 d['$error'] = { 'dict', 'unlimited', 'literal', dicterror }
6637 end
6638end
6639
6640
6641
6642
6643
6644local procstack
6645local procstackptr
6646
6647initializers[#initializers+1] = function(reset)
6648 if reset then
6649 procstack = nil
6650 procstackptr = nil
6651 else
6652 procstack = { }
6653 procstackptr = 0
6654 end
6655end
6656
6657
6658
6659do
6660
6661 local function push(v)
6662 if procstackptr > 0 then
6663 local top = procstack[procstackptr]
6664 if top then
6665 top[#top+1] = v
6666 else
6667 procstack[procstackptr] = { v }
6668 end
6669 return false
6670 else
6671 push_execstack(v)
6672 return true
6673 end
6674 end
6675
6676 local function start()
6677 procstackptr = procstackptr + 1
6678 return true
6679 end
6680
6681 local function stop()
6682 local v = procstack[procstackptr]
6683 procstack[procstackptr] = { }
6684 procstackptr = procstackptr - 1
6685 if push {'array', 'unlimited', 'executable', add_VM(v), 1, #v, 'd' } then
6686 return true
6687 end
6688 end
6689
6690 local function hexify(a)
6691 return char(tonumber(a,16))
6692 end
6693
6694 local function octify(a)
6695 return char(tonumber(a,8))
6696 end
6697
6698 local function radixed(base,value)
6699 base = tonumber(base)
6700 if base > 36 or base < 2 then
6701 return nil
6702 end
6703 value = tonumber(value,base)
6704 if not value then
6705 return "error", false
6706 elseif value > MAX_INT then
6707 return "integer", value
6708 else
6709 return "real", value
6710 end
6711 end
6712
6713 local space = S(' ')
6714 local spacing = S(' \t\r\n\f')
6715 local sign = S('+-')^-1
6716 local digit = R('09')
6717 local period = P('.')
6718 local letters = R('!~') - S('[]<>{}()%/')
6719 local hexdigit = R('09','af','AF')
6720 local radixdigit = R('09','az','AZ')
6721
6722 local p_integer = (sign * digit^1 * #(1-letters)) / tonumber
6723 local p_real = ((sign * digit^0 * period * digit^0 + period * digit^1) * (S('eE') * sign * digit^1)^-1 * #(1-letters)) / tonumber
6724 local p_literal = Cs(P("/")/"" * letters^1 * letters^0)
6725 local p_symbol = C(letters^1 * letters^0)
6726
6727 local p_radixed = C(digit^1) * P("#") * C(radixdigit^1) / radixed
6728 local p_unhexed = P("<") * Cs(((C(hexdigit*hexdigit) * Cc(16))/tonumber/char+spacing/"")^0) * P(">")
6729 local p_comment = P('%') * (1 - S('\r\n'))^0 * Cc(true)
6730 local p_bounding = P('%%BoundingBox:') * Ct((space^0 * p_integer)^4) * (1 - S('\r\n'))^0
6731 local p_lbrace = C("{")
6732 local p_rbrace = C("}")
6733 local p_lbracket = C("[")
6734 local p_rbracket = C("]")
6735 local p_finish = Cc(false)
6736
6737 local p_string =
6738 P("(")
6739 * Cs( P {
6740 (
6741 (1 - S("()\\"))^1
6742 + P("\\")/"" * (
6743 (C(digit *digit * digit) * Cc(8)) / tonumber / char
6744 + P("n") / "\n" + P("r") / "\r" + P("t") / "\t"
6745 + P("b") / "\b" + P("f") / "\f" + P("\\") / "\\"
6746 + 1
6747 )
6748 + P("(") * V(1) * P(")")
6749 )^0
6750 })
6751 * P(")")
6752
6753
6754
6755
6756
6757 local p_unhexed = Cc('string') * p_unhexed
6758 local p_string = Cc('string') * p_string
6759 local p_array_start = Cc('name') * p_lbracket
6760 local p_array_stop = Cc('name') * p_rbracket
6761 local p_exec_start = Cc('start') * p_lbrace
6762 local p_exec_stop = Cc('stop') * p_rbrace
6763 local p_integer = Cc('integer') * p_integer
6764 local p_real = Cc('real') * p_real
6765 local p_radixed = p_radixed
6766 local p_symbol = Cc('name') * p_symbol
6767 local p_literal = Cc('literal') * p_literal
6768 local p_comment = Cc('comment') * p_comment
6769 local p_bounding = Cc('bounding') * p_bounding
6770 local p_finish = Cc("eof") * p_finish
6771 local p_whitespace = spacing^0
6772
6773 local tokens = p_whitespace
6774 * (
6775 p_bounding
6776 + p_comment
6777 + p_string
6778 + p_unhexed
6779 + p_array_start
6780 + p_array_stop
6781 + p_exec_start
6782 + p_exec_stop
6783 + p_real
6784 + p_radixed
6785 + p_integer
6786 + p_literal
6787 + p_symbol
6788 + p_finish
6789 )^-1
6790 * Cp()
6791
6792
6793
6794
6795 local function tokenize()
6796 local object = execstack[execstackptr]
6797 local sequence = object[4]
6798 local position = object[5]
6799 local length = object[6]
6800 local tokentype = nil
6801 local value = nil
6802 while position < length do
6803 tokentype, value, position = lpegmatch(tokens,get_VM(sequence),position)
6804 if not position then
6805 return false
6806 elseif position >= length then
6807 pop_execstack()
6808 else
6809 object[5] = position
6810 end
6811 if not value then
6812 if tokentype == "eof" then
6813
6814 return true
6815 else
6816 return false
6817 end
6818 elseif tokentype == 'integer' or tokentype == 'real' then
6819 if push { tokentype, 'unlimited', 'literal', value } then
6820 return true
6821 end
6822 elseif tokentype == 'name' then
6823 if push { 'name', 'unlimited', 'executable', add_VM(value) } then
6824 return true
6825 end
6826 elseif tokentype == 'literal' then
6827 if push { 'name', 'unlimited', 'literal', add_VM(value) } then
6828 return true
6829 end
6830 elseif tokentype == 'string' then
6831 if push { 'string', 'unlimited', 'literal', add_VM(value), 1, #value } then
6832 return true
6833 end
6834 elseif tokentype == 'start' then
6835 if start() then
6836
6837 end
6838 elseif tokentype == 'stop' then
6839 if stop() then
6840 return true
6841 end
6842 elseif tokentype == 'bounding' then
6843 specials.boundingbox = value
6844 else
6845
6846 end
6847 end
6848 return position >= length
6849 end
6850
6851
6852
6853
6854
6855
6856
6857 next_object = function()
6858 if execstackptr == 0 then
6859 return nil
6860 end
6861 local object = execstack[execstackptr]
6862 if not object then
6863 return nil
6864 end
6865 local otyp = object[1]
6866 local exec = object[3] == 'executable'
6867 if not exec then
6868 return pop_execstack()
6869 elseif otyp == 'array' then
6870 if object[7] == 'd' then
6871 return pop_execstack()
6872 else
6873 local proc = get_VM(object[4])
6874 local o = object[5]
6875 local val = proc[o]
6876 if o >= #proc then
6877 object[5] = 1
6878 pop_execstack()
6879 else
6880 object[5] = o + 1
6881 end
6882 return val
6883 end
6884 elseif otyp == 'string' then
6885 if not tokenize() then
6886 report("tokenizer failed on string")
6887 return nil
6888 else
6889 return next_object()
6890 end
6891 elseif otyp == 'file' then
6892 if object[4] == 0 then
6893 report('sorry, interactive mode is not supported')
6894 end
6895 if not tokenize() then
6896 report("tokenizer failed on file")
6897 return nil
6898 else
6899 return next_object()
6900 end
6901 else
6902 return pop_execstack()
6903 end
6904 end
6905
6906
6907
6908 local detail = false
6909
6910 local report_exec = logs.reporter("escrito","exec")
6911
6912 do_exec = function()
6913 local ret
6914 local savedopstack = detail and copy_opstack()
6915 local object = next_object()
6916 if not object then
6917 return false
6918 end
6919 local otyp = object[1]
6920 if false then
6921 if otyp == 'operator' then
6922 report_exec("%s %s %s",otyp,object[3],object[5])
6923 elseif otyp == 'dict' then
6924 local d = get_VM(object[4])
6925 report_exec("%s %s <%s:%s>",otyp,object[3],d.size or '',d.maxsize or '')
6926 elseif otyp == 'array' or otyp == 'file' or otyp == 'save' then
6927 report_exec("%s <%s:%s>",object[3],object[5] or '',object[6] or '')
6928 elseif otyp == 'string' or otyp == 'name' then
6929 report_exec("%s %s %s",otyp,object[3],get_VM(object[4]))
6930 else
6931 report_exec("%s %s %s",otyp,object[3],tostring(object[4]))
6932 end
6933 end
6934 if otyp == 'real' or otyp == 'integer' or otyp == 'boolean' or otyp == 'mark' or otyp == 'save' or otyp == 'font' then
6935 push_opstack(object)
6936 elseif otyp == '.stopped' then
6937
6938 push_opstack { 'boolean', 'unlimited', 'executable', false}
6939 elseif otyp == '.exit' then
6940
6941 elseif otyp == 'array' then
6942 if object[2] == 'noaccess' then
6943 escrito.errorname = 'noaccess'
6944 else
6945 push_opstack(object)
6946 end
6947 elseif otyp == 'string' then
6948 if object[2] == 'noaccess' then
6949 escrito.errorname = 'noaccess'
6950 else
6951 push_opstack(object)
6952 end
6953 elseif otyp == 'dict' then
6954 local dict = get_VM(object[4])
6955 if dict.access == 'noaccess' then
6956 escrito.errorname = 'noaccess'
6957 else
6958 push_opstack(object)
6959 end
6960 elseif otyp == 'file' then
6961 if object[2] == 'noaccess' then
6962 errorname = 'noaccess'
6963 else
6964 push_opstack(object)
6965 end
6966 elseif otyp == 'null' then
6967 push_opstack(object)
6968 elseif otyp == 'operator' then
6969 if object[3]=='executable' then
6970 ret, escrito.errorname = object[4]()
6971 else
6972 push_opstack(object)
6973 end
6974 elseif otyp == 'save' then
6975
6976 elseif otyp == 'name' then
6977 if object[3] == 'executable' then
6978 local v = lookup(get_VM(object[4]))
6979 if not v then
6980 if escrito.errorname then
6981
6982 error ("recursive error detected inside '" .. escrito.errorname .. "'")
6983 end
6984 escrito.errorname = 'undefined'
6985 else
6986 if DEBUG then
6987 local vt = v[1]
6988 if vt == 'operator' then
6989 print ('exec2: ' .. vt .. ' ' .. v[3] .. ' '.. v[5])
6990 elseif vt == 'dict' or vt == 'array' or vt == 'file' or vt == 'save' then
6991 print ('exec2: ' .. vt .. ' ' .. v[3] .. ' <'.. (v[5] or '') .. '>')
6992 elseif vt == 'string' or vt == 'name' then
6993 print ('exec2: ' .. vt .. ' ' .. v[3] .. ' '.. get_VM(v[4]))
6994 else
6995 print ('exec2: ' .. vt .. ' ' .. v[3] .. ' '.. tostring(v[4]))
6996 end
6997 end
6998 push_execstack(v)
6999 end
7000 else
7001 push_opstack(object)
7002 end
7003 elseif otyp == 'null' then
7004
7005 elseif otyp == 'array' then
7006 push_opstack(object)
7007 end
7008
7009 if escrito.errorname then
7010 if savedopstack then
7011 local v = lookup_error(escrito.errorname)
7012 if not v then
7013 print("unknown error handler for '" .. escrito.errorname .. "', quitting")
7014 return false
7015 else
7016 set_opstack(savedopstack)
7017 push_opstack { otyp, object[2], "literal", object[4], object[5], object[6], object[7] }
7018 push_opstack { 'string','unlimited','literal',add_VM(escrito.errorname), 1 }
7019 push_execstack(v)
7020 end
7021 escrito.errorname = nil
7022 else
7023 print("error '" .. escrito.errorname .. "', quitting")
7024
7025 end
7026 end
7027
7028 return true
7029 end
7030
7031end
7032
7033do
7034
7035
7036
7037 local errornames = {
7038 "dictfull", "dictstackoverflow", "dictstackunderflow", "execstackoverflow",
7039 "interrupt", "invalidaccess", "invalidexit", "invalidfileaccess", "invalidfont", "invalidrestore",
7040 "ioerror", "limitcheck", "nocurrentpoint", "rangecheck", "stackoverflow", "stackunderflow",
7041 "syntaxerror", "timeout", "typecheck", "undefined", "undefinedfilename", "undefinedresult",
7042 "unmatchedmark", "unregistered", "VMerror"
7043 }
7044
7045 local generic_error_proc = [[{
7046 $error /newerror true put
7047 $error exch /errorname exch put
7048 $error exch /command exch put
7049 count array astore $error /ostack 3 -1 roll put
7050 $error /dstack countdictstack array dictstack put
7051 countexecstack array execstack aload pop pop count array astore $error /estack 3 -1 roll put
7052 stop
7053 } bind ]]
7054
7055 local generic_handleerror_proc = [[{
7056 $error begin
7057 /newerror false def
7058 (%%[ Error: ) print
7059 errorname print
7060 (; OffendingCommand: ) print
7061 command ==
7062 ( ]%%\n) print flush
7063 end
7064 }]]
7065
7066 local enabled
7067
7068 local function interpret(data)
7069 if enabled then
7070 push_opstack { 'file', 'unlimited', 'executable', add_VM(data), 1, #data, 'r', stdin }
7071 push_execstack { 'operator', 'unlimited', 'executable', operators.stopped, 'stopped' }
7072 while true do
7073 if not do_exec() then
7074 local v = pop_opstack()
7075 if v and v[4] == true then
7076 local proc = {
7077 { 'name', 'unlimited', 'executable', add_VM('errordict') },
7078 { 'name', 'unlimited', 'literal', add_VM('handleerror') },
7079 { 'operator', 'unlimited', 'executable', operators.get, 'get' },
7080 { 'operator', 'unlimited', 'executable', operators.exec, 'exec' },
7081 }
7082 push_execstack { 'array', 'unlimited', 'executable', add_VM(proc), 1, #proc, 'i' }
7083 else
7084 return
7085 end
7086 end
7087 end
7088 end
7089 end
7090
7091 local function close()
7092 for i=1,#initializers do
7093 initializers[i](true)
7094 end
7095 enabled = false
7096 end
7097
7098 local function open(options)
7099 enabled = true
7100 local starttime = os.clock()
7101 local stoptime = nil
7102 for i=1,#initializers do
7103 initializers[i]()
7104 end
7105 if type(options) == "table" then
7106 devicename = options.device or "pdf"
7107 findfont = options.findfont or findfont
7108 randomseed = options.randomseed or randomseed
7109 calculatebox = options.calculatebox
7110 else
7111 devicename = "pdf"
7112 end
7113 device = devices[devicename] or devices.pdf
7114 operators.initgraphics()
7115 for i=1,#errornames do
7116 interpret(formatters["errordict /%s %s put"](errornames[i],generic_error_proc), INITDEBUG)
7117 end
7118
7119 interpret("systemdict /= { 20 string cvs print } bind put", INITDEBUG)
7120 interpret("systemdict /prompt { (PS>) print flush } bind put", INITDEBUG)
7121 interpret(format("errordict /handleerror %s bind put", generic_handleerror_proc), INITDEBUG)
7122 interpret("systemdict /handleerror {errordict /handleerror get exec } bind put", INITDEBUG)
7123
7124 interpret(format("/quit { stop } bind def"), INITDEBUG)
7125 interpret(format("userdict /#copies 1 put"), INITDEBUG)
7126 local job = {
7127 runtime = 0,
7128 interpret = interpret,
7129 boundingbox = boundingbox,
7130 close = function()
7131 close()
7132 local runtime = os.clock() - starttime
7133 job.runtime = runtime
7134 return runtime
7135 end,
7136 }
7137 return job
7138 end
7139
7140 escrito.open = open
7141
7142 if context then
7143
7144 function escrito.convert(options)
7145 if type(options) == "table" then
7146 local data = options.data
7147 if not data or data == "" then
7148 local buffer = options.buffer
7149 local filename = options.filename
7150 if buffer and buffer ~= "" then
7151 data = buffers.getcontent(buffer)
7152 elseif filename and filename ~= "" then
7153 data = io.loaddata(filename)
7154 end
7155 end
7156 if data and data ~= "" then
7157 local e = open(options)
7158
7159 e.interpret(data)
7160 return e.close()
7161 end
7162 end
7163 return 0
7164 end
7165
7166 end
7167
7168 escrito.devices = devices
7169
7170end
7171
7172return escrito
7173 |