1if not modules then modules = { } end modules ['lpdf-ini'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to lpdf-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10
11
12
13
14
15local setmetatable, getmetatable, type, next, tostring, tonumber, rawset = setmetatable, getmetatable, type, next, tostring, tonumber, rawset
16local char, byte, format, gsub, concat, match, sub, gmatch = string.char, string.byte, string.format, string.gsub, table.concat, string.match, string.sub, string.gmatch
17local utfchar, utfbyte, utfvalues = utf.char, utf.byte, utf.values
18local sind, cosd, max, min = math.sind, math.cosd, math.max, math.min
19local sort, sortedhash = table.sort, table.sortedhash
20local P, C, R, S, Cc, Cs, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs, lpeg.V
21local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
22local formatters = string.formatters
23local isboolean = string.is_boolean
24local rshift = bit32.rshift
25local osdate, ostime = os.date, os.time
26
27local report_objects = logs.reporter("backend","objects")
28local report_finalizing = logs.reporter("backend","finalizing")
29local report_blocked = logs.reporter("backend","blocked")
30
31local implement = interfaces.implement
32
33local context = context
34
35
36
37
38
39pdf = type(pdf) == "table" and pdf or { }
40local factor = number.dimenfactors.bp
41
42local codeinjections = { }
43local nodeinjections = { }
44
45local backends = backends
46
47local pdfbackend = {
48 comment = "backend for directly generating pdf output",
49 nodeinjections = nodeinjections,
50 codeinjections = codeinjections,
51 registrations = { },
52 tables = { },
53}
54
55backends.pdf = pdfbackend
56
57lpdf = lpdf or { }
58local lpdf = lpdf
59lpdf.flags = lpdf.flags or { }
60
61local trace_finalizers = false trackers.register("backend.finalizers", function(v) trace_finalizers = v end)
62local trace_resources = false trackers.register("backend.resources", function(v) trace_resources = v end)
63local trace_objects = false trackers.register("backend.objects", function(v) trace_objects = v end)
64local trace_details = false trackers.register("backend.details", function(v) trace_details = v end)
65
66do
67
68 local pdfsetmajorversion, pdfsetminorversion, pdfgetmajorversion, pdfgetminorversion
69 local pdfsetcompresslevel, pdfsetobjectcompresslevel, pdfgetcompresslevel, pdfgetobjectcompresslevel
70 local pdfsetsuppressoptionalinfo, pdfsetomitcidset, pdfsetomitcharset
71
72 updaters.register("backend.update.lpdf",function()
73 pdfsetmajorversion = pdf.setmajorversion
74 pdfsetminorversion = pdf.setminorversion
75 pdfgetmajorversion = pdf.getmajorversion
76 pdfgetminorversion = pdf.getminorversion
77
78 pdfsetcompresslevel = pdf.setcompresslevel
79 pdfsetobjectcompresslevel = pdf.setobjcompresslevel
80 pdfgetcompresslevel = pdf.getcompresslevel
81 pdfgetobjectcompresslevel = pdf.getobjcompresslevel
82
83 pdfsetsuppressoptionalinfo = pdf.setsuppressoptionalinfo
84 pdfsetomitcidset = pdf.setomitcidset
85 pdfsetomitcharset = pdf.setomitcharset
86 end)
87
88 function lpdf.setversion(major,minor)
89 pdfsetmajorversion(major or 1)
90 pdfsetminorversion(minor or 7)
91 end
92
93 function lpdf.getversion(major,minor)
94 return pdfgetmajorversion(), pdfgetminorversion()
95 end
96
97 function lpdf.majorversion() return pdfgetmajorversion() end
98 function lpdf.minorversion() return pdfgetminorversion() end
99
100 local frozen = false
101 local clevel = 3
102 local olevel = 1
103
104 function lpdf.setcompression(level,objectlevel,freeze)
105 if not frozen then
106 if pdfsetcompresslevel then
107 pdfsetcompresslevel(level or 3)
108 pdfsetobjectcompresslevel(objectlevel or level or 3)
109 else
110 clevel = level
111 olevel = objectlevel
112 end
113 frozen = freeze
114 end
115 end
116
117 function lpdf.getcompression()
118 if pdfgetcompresslevel then
119 return pdfgetcompresslevel(), pdfgetobjectcompresslevel()
120 else
121 return clevel, olevel
122 end
123 end
124
125 function lpdf.compresslevel()
126 if pdfgetcompresslevel then
127 return pdfgetcompresslevel()
128 else
129 return clevel
130 end
131 end
132
133 function lpdf.objectcompresslevel()
134 if pdfgetobjectcompresslevel then
135 return pdfgetobjectcompresslevel()
136 else
137 return olevel
138 end
139 end
140
141 function lpdf.setsuppressoptionalinfo(n)
142 if pdfsetsuppressoptionalinfo then
143 pdfsetsuppressoptionalinfo(n)
144 end
145 end
146
147 function lpdf.setomitcidset(v)
148 return pdfsetomitcidset(v)
149 end
150
151 function lpdf.setomitcharset(v)
152 return pdfsetomitcharset(v)
153 end
154
155end
156
157do
158
159 local pdfgetxformname, pdfincludeimage
160
161 updaters.register("backend.update.lpdf",function()
162 pdfgetxformname = pdf.getxformname
163 pdfincludeimage = pdf.includeimage
164 end)
165
166 function lpdf.getxformname(id) return pdfgetxformname(id) end
167 function lpdf.includeimage(id) return pdfincludeimage(id) end
168
169end
170
171 local pdfsetpageresources, pdfsetpageattributes, pdfsetpagesattributes
172 local pdfreserveobject, pdfimmediateobject, pdfdeferredobject, pdfreferenceobject
173 local pdfgetpagereference
174
175 updaters.register("backend.update.lpdf",function()
176 pdfreserveobject = pdf.reserveobj
177 pdfimmediateobject = pdf.immediateobj
178 pdfdeferredobject = pdf.obj
179 pdfreferenceobject = pdf.refobj
180
181 pdfgetpagereference = pdf.getpageref
182
183 pdfsetpageresources = pdf.setpageresources
184 pdfsetpageattributes = pdf.setpageattributes
185 pdfsetpagesattributes = pdf.setpagesattributes
186 end)
187
188local jobpositions = job.positions
189local getpos = jobpositions.getpos
190local getrpos = jobpositions.getrpos
191
192jobpositions.registerhandlers {
193 getpos = pdf.getpos,
194
195 gethpos = pdf.gethpos,
196 getvpos = pdf.getvpos,
197}
198
199do
200
201 local pdfgetmatrix, pdfhasmatrix, pdfprint
202
203 updaters.register("backend.update.lpdf",function()
204 pdfgetmatrix = pdf.getmatrix
205 pdfhasmatrix = pdf.hasmatrix
206 pdfprint = pdf.print
207 end)
208
209 function lpdf.print(...)
210 return pdfprint(...)
211 end
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 function lpdf.rectangle(width,height,depth,offset)
243 local tx, ty = getpos()
244 if offset then
245 tx = tx - offset
246 ty = ty + offset
247 width = width + 2*offset
248 height = height + offset
249 depth = depth + offset
250 end
251 if pdfhasmatrix() then
252 local rx, sx, sy, ry = pdfgetmatrix()
253 return
254 factor * tx,
255 factor * (ty - ry*depth + sx*width),
256 factor * (tx + rx*width - sy*height),
257 factor * (ty + ry*height - sx*width)
258 else
259 return
260 factor * tx,
261 factor * (ty - depth),
262 factor * (tx + width),
263 factor * (ty + height)
264 end
265 end
266
267end
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290local tosixteen, fromsixteen, topdfdoc, frompdfdoc, toeight, fromeight
291
292do
293
294 local escaped = Cs(Cc("(") * (S("\\()\n\r\t\b\f")/"\\%0" + P(1))^0 * Cc(")"))
295
296 local cache = table.setmetatableindex(function(t,k)
297 local v = utfbyte(k)
298 if v < 0x10000 then
299 v = format("%04x",v)
300 else
301 v = v - 0x10000
302 v = format("%04x%04x",rshift(v,10)+0xD800,v%1024+0xDC00)
303
304 end
305 t[k] = v
306 return v
307 end)
308
309 local unified = Cs(Cc("<feff") * (lpeg.patterns.utf8character/cache)^1 * Cc(">"))
310
311 tosixteen = function(str)
312 if not str or str == "" then
313 return "<feff>"
314 else
315 return lpegmatch(unified,str)
316 end
317 end
318
319 local more = 0
320
321 local pattern = C(4) / function(s)
322 local now = tonumber(s,16)
323 if more > 0 then
324 now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000
325 more = 0
326 return utfchar(now)
327 elseif now >= 0xD800 and now <= 0xDBFF then
328 more = now
329 return ""
330 else
331 return utfchar(now)
332 end
333 end
334
335 local pattern = P(true) / function() more = 0 end * Cs(pattern^0)
336
337 fromsixteen = function(str)
338 if not str or str == "" then
339 return ""
340 else
341 return lpegmatch(pattern,str)
342 end
343 end
344
345 local toregime = regimes.toregime
346 local fromregime = regimes.fromregime
347
348 topdfdoc = function(str,default)
349 if not str or str == "" then
350 return ""
351 else
352 return lpegmatch(escaped,toregime("pdfdoc",str,default))
353 end
354 end
355
356 frompdfdoc = function(str)
357 if not str or str == "" then
358 return ""
359 else
360 return fromregime("pdfdoc",str)
361 end
362 end
363
364 if not toregime then topdfdoc = function(s) return s end end
365 if not fromregime then frompdfdoc = function(s) return s end end
366
367 toeight = function(str)
368 if not str or str == "" then
369 return "()"
370 else
371 return lpegmatch(escaped,str)
372 end
373 end
374
375 local b_pattern = Cs((P("\\")/"" * (
376 S("()")
377 + S("nrtbf") / { n = "\n", r = "\r", t = "\t", b = "\b", f = "\f" }
378 + lpegpatterns.octdigit^-3 / function(s) return char(tonumber(s,8)) end)
379 + P(1))^0)
380
381 fromeight = function(str)
382 if not str or str == "" then
383 return ""
384 else
385 return lpegmatch(unescape,str)
386 end
387 end
388
389 local u_pattern = lpegpatterns.utfbom_16_be * lpegpatterns.utf16_to_utf8_be
390 + lpegpatterns.utfbom_16_le * lpegpatterns.utf16_to_utf8_le
391
392 local h_pattern = lpegpatterns.hextobytes
393
394 local zero = S(" \n\r\t") + P("\\ ")
395 local one = C(4)
396 local two = P("d") * R("89","af") * C(2) * C(4)
397
398 local x_pattern = P { "start",
399 start = V("wrapped") + V("unwrapped") + V("original"),
400 original = Cs(P(1)^0),
401 wrapped = P("<") * V("unwrapped") * P(">") * P(-1),
402 unwrapped = P("feff")
403 * Cs( (
404 zero / ""
405 + two / function(a,b)
406 a = (tonumber(a,16) - 0xD800) * 1024
407 b = (tonumber(b,16) - 0xDC00)
408 return utfchar(a+b)
409 end
410 + one / function(a)
411 return utfchar(tonumber(a,16))
412 end
413 )^1 ) * P(-1)
414 }
415
416 function lpdf.frombytes(s,hex)
417 if not s or s == "" then
418 return ""
419 end
420 if hex then
421 local x = lpegmatch(x_pattern,s)
422 if x then
423 return x
424 end
425 local h = lpegmatch(h_pattern,s)
426 if h then
427 return h
428 end
429 else
430 local u = lpegmatch(u_pattern,s)
431 if u then
432 return u
433 end
434 end
435 return lpegmatch(b_pattern,s)
436 end
437
438 lpdf.tosixteen = tosixteen
439 lpdf.toeight = toeight
440 lpdf.topdfdoc = topdfdoc
441 lpdf.fromsixteen = fromsixteen
442 lpdf.fromeight = fromeight
443 lpdf.frompdfdoc = frompdfdoc
444
445end
446
447local tostring_a, tostring_d
448
449do
450
451 local f_key_null = formatters["/%s null"]
452 local f_key_value = formatters["/%s %s"]
453
454
455 local f_key_dictionary = formatters["/%s << %s >>"]
456 local f_dictionary = formatters["<< %s >>"]
457
458
459 local f_key_array = formatters["/%s [ %s ]"]
460 local f_array = formatters["[ %s ]"]
461 local f_key_number = formatters["/%s %N"]
462 local f_tonumber = formatters["%N"]
463
464 tostring_d = function(t,contentonly,key)
465 if next(t) then
466 local r = { }
467 local n = 0
468 local e
469 for k, v in next, t do
470 if k == "__extra__" then
471 e = v
472 elseif k == "__stream__" then
473
474 else
475 n = n + 1
476 r[n] = k
477 end
478 end
479 if n > 1 then
480 sort(r)
481 end
482 for i=1,n do
483 local k = r[i]
484 local v = t[k]
485 local tv = type(v)
486
487 if tv == "table" then
488
489
490 if v.__lpdftype__ then
491
492
493
494
495 r[i] = f_key_value(k,tostring(v))
496
497 elseif v[1] then
498 r[i] = f_key_value(k,tostring_a(v))
499 else
500 r[i] = f_key_value(k,tostring_d(v))
501 end
502 elseif tv == "string" then
503 r[i] = f_key_value(k,toeight(v))
504 elseif tv == "number" then
505 r[i] = f_key_number(k,v)
506 else
507 r[i] = f_key_value(k,tostring(v))
508 end
509 end
510 if e then
511 r[n+1] = e
512 end
513 r = concat(r," ")
514 if contentonly then
515 return r
516 elseif key then
517 return f_key_dictionary(key,r)
518 else
519 return f_dictionary(r)
520 end
521 elseif contentonly then
522 return ""
523 else
524 return "<< >>"
525 end
526 end
527
528 tostring_a = function(t,contentonly,key)
529 local tn = #t
530 if tn ~= 0 then
531 local r = { }
532 for k=1,tn do
533 local v = t[k]
534 local tv = type(v)
535
536 if tv == "number" then
537 r[k] = f_tonumber(v)
538 elseif tv == "table" then
539
540
541 if v.__lpdftype__ then
542
543
544
545
546 r[k] = tostring(v)
547
548 elseif v[1] then
549 r[k] = tostring_a(v)
550 else
551 r[k] = tostring_d(v)
552 end
553 elseif tv == "string" then
554 r[k] = toeight(v)
555 else
556 r[k] = tostring(v)
557 end
558 end
559 local e = t.__extra__
560 if e then
561 r[tn+1] = e
562 end
563 r = concat(r," ")
564 if contentonly then
565 return r
566 elseif key then
567 return f_key_array(key,r)
568 else
569 return f_array(r)
570 end
571 elseif contentonly then
572 return ""
573 else
574 return "[ ]"
575 end
576 end
577
578end
579
580local f_tonumber = formatters["%N"]
581
582local tostring_x = function(t) return concat(t," ") end
583local tostring_s = function(t) return toeight(t[1]) end
584local tostring_p = function(t) return topdfdoc(t[1],t[2]) end
585local tostring_u = function(t) return tosixteen(t[1]) end
586
587local tostring_n = function(t) return f_tonumber(t[1]) end
588local tostring_c = function(t) return t[1] end
589local tostring_z = function() return "null" end
590local tostring_t = function() return "true" end
591local tostring_f = function() return "false" end
592local tostring_r = function(t) local n = t[1] return n and n > 0 and (n .. " 0 R") or "null" end
593
594local tostring_v = function(t)
595 local s = t[1]
596 if type(s) == "table" then
597 return concat(s)
598 else
599 return s
600 end
601end
602
603local tostring_l = function(t)
604 local s = t[1]
605 if not s or s == "" then
606 return "()"
607 elseif t[2] then
608 return "<" .. s .. ">"
609 else
610 return "(" .. s .. ")"
611 end
612end
613
614local function value_x(t) return t end
615local function value_s(t) return t[1] end
616local function value_p(t) return t[1] end
617local function value_u(t) return t[1] end
618local function value_n(t) return t[1] end
619local function value_c(t) return sub(t[1],2) end
620local function value_d(t) return tostring_d(t,true) end
621local function value_a(t) return tostring_a(t,true) end
622local function value_z() return nil end
623local function value_t(t) return t.value or true end
624local function value_f(t) return t.value or false end
625local function value_r(t) return t[1] or 0 end
626local function value_v(t) return t[1] end
627local function value_l(t) return t[1] end
628
629local function add_to_d(t,v)
630 local k = type(v)
631 if k == "string" then
632 if t.__extra__ then
633 t.__extra__ = t.__extra__ .. " " .. v
634 else
635 t.__extra__ = v
636 end
637 elseif k == "table" then
638 for k, v in next, v do
639 t[k] = v
640 end
641 end
642 return t
643end
644
645local function add_to_a(t,v)
646 local k = type(v)
647 if k == "string" then
648 if t.__extra__ then
649 t.__extra__ = t.__extra__ .. " " .. v
650 else
651 t.__extra__ = v
652 end
653 elseif k == "table" then
654 local n = #t
655 for i=1,#v do
656 n = n + 1
657 t[n] = v[i]
658 end
659 end
660 return t
661end
662
663local function add_x(t,k,v) rawset(t,k,tostring(v)) end
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680local mt_x = { __index = { __lpdftype__ = "stream" }, __tostring = tostring_x, __call = value_x, __newindex = add_x }
681local mt_d = { __index = { __lpdftype__ = "dictionary" }, __tostring = tostring_d, __call = value_d, __add = add_to_d }
682local mt_a = { __index = { __lpdftype__ = "array" }, __tostring = tostring_a, __call = value_a, __add = add_to_a }
683local mt_u = { __index = { __lpdftype__ = "unicode" }, __tostring = tostring_u, __call = value_u }
684local mt_s = { __index = { __lpdftype__ = "string" }, __tostring = tostring_s, __call = value_s }
685local mt_p = { __index = { __lpdftype__ = "docstring" }, __tostring = tostring_p, __call = value_p }
686local mt_n = { __index = { __lpdftype__ = "number" }, __tostring = tostring_n, __call = value_n }
687local mt_c = { __index = { __lpdftype__ = "constant" }, __tostring = tostring_c, __call = value_c }
688local mt_z = { __index = { __lpdftype__ = "null" }, __tostring = tostring_z, __call = value_z }
689local mt_t = { __index = { __lpdftype__ = "true" }, __tostring = tostring_t, __call = value_t }
690local mt_f = { __index = { __lpdftype__ = "false" }, __tostring = tostring_f, __call = value_f }
691local mt_r = { __index = { __lpdftype__ = "reference" }, __tostring = tostring_r, __call = value_r }
692local mt_v = { __index = { __lpdftype__ = "verbose" }, __tostring = tostring_v, __call = value_v }
693local mt_l = { __index = { __lpdftype__ = "literal" }, __tostring = tostring_l, __call = value_l }
694
695local function pdfstream(t)
696 if t then
697 local tt = type(t)
698 if tt == "table" then
699 for i=1,#t do
700 t[i] = tostring(t[i])
701 end
702 elseif tt == "string" then
703 t = { t }
704 else
705 t = { tostring(t) }
706 end
707 end
708 return setmetatable(t or { },mt_x)
709end
710
711local function pdfdictionary(t)
712 return setmetatable(t or { },mt_d)
713end
714
715local function pdfarray(t)
716 if type(t) == "string" then
717 return setmetatable({ t },mt_a)
718 else
719 return setmetatable(t or { },mt_a)
720 end
721end
722
723local function pdfstring(str,default)
724 return setmetatable({ str or default or "" },mt_s)
725end
726
727local function pdfdocstring(str,default,defaultchar)
728 return setmetatable({ str or default or "", defaultchar or " " },mt_p)
729end
730
731local function pdfunicode(str,default)
732 return setmetatable({ str or default or "" },mt_u)
733end
734
735local function pdfliteral(str,hex)
736 return setmetatable({ str, hex },mt_l)
737end
738
739local pdfnumber, pdfconstant
740
741do
742
743 local cache = { }
744
745 pdfnumber = function(n,default)
746 if not n then
747 n = default
748 end
749 local c = cache[n]
750 if not c then
751 c = setmetatable({ n },mt_n)
752
753 end
754 return c
755 end
756
757 for i=-1,9 do cache[i] = pdfnumber(i) end
758
759 local replacer = S("\0\t\n\r\f ()[]{}/%%#\\") / {
760 ["\00"]="#00",
761 ["\09"]="#09",
762 ["\10"]="#0a",
763 ["\12"]="#0c",
764 ["\13"]="#0d",
765 [ " " ]="#20",
766 [ "#" ]="#23",
767 [ "%" ]="#25",
768 [ "(" ]="#28",
769 [ ")" ]="#29",
770 [ "/" ]="#2f",
771 [ "[" ]="#5b",
772 [ "\\"]="#5c",
773 [ "]" ]="#5d",
774 [ "{" ]="#7b",
775 [ "}" ]="#7d",
776 } + P(1)
777
778 local escaped = Cs(Cc("/") * replacer^0)
779
780 local cache = table.setmetatableindex(function(t,k)
781 local v = setmetatable({ lpegmatch(escaped,k) }, mt_c)
782 t[k] = v
783 return v
784 end)
785
786 pdfconstant = function(str,default)
787 if not str then
788 str = default or "none"
789 end
790 return cache[str]
791 end
792
793 local escaped = Cs(replacer^0)
794
795 function lpdf.escaped(str)
796 return lpegmatch(escaped,str) or str
797 end
798
799end
800
801local pdfnull, pdfboolean, pdfreference, pdfverbose
802
803do
804
805 local p_null = { } setmetatable(p_null, mt_z)
806 local p_true = { } setmetatable(p_true, mt_t)
807 local p_false = { } setmetatable(p_false,mt_f)
808
809 pdfnull = function()
810 return p_null
811 end
812
813 pdfboolean = function(b,default)
814 if type(b) == "boolean" then
815 return b and p_true or p_false
816 else
817 return default and p_true or p_false
818 end
819 end
820
821
822
823
824
825 local r_zero = setmetatable({ 0 },mt_r)
826
827 pdfreference = function(r)
828 if r and r ~= 0 then
829 return setmetatable({ r },mt_r)
830 else
831 return r_zero
832 end
833 end
834
835 local v_zero = setmetatable({ 0 },mt_v)
836 local v_empty = setmetatable({ "" },mt_v)
837
838 pdfverbose = function(t)
839 if t == 0 then
840 return v_zero
841 elseif t == "" then
842 return v_empty
843 else
844 return setmetatable({ t },mt_v)
845 end
846 end
847
848end
849
850lpdf.stream = pdfstream
851lpdf.dictionary = pdfdictionary
852lpdf.array = pdfarray
853lpdf.docstring = pdfdocstring
854lpdf.string = pdfstring
855lpdf.unicode = pdfunicode
856lpdf.number = pdfnumber
857lpdf.constant = pdfconstant
858lpdf.null = pdfnull
859lpdf.boolean = pdfboolean
860lpdf.reference = pdfreference
861lpdf.verbose = pdfverbose
862lpdf.literal = pdfliteral
863
864local names, cache = { }, { }
865
866function lpdf.reserveobject(name)
867 local r = pdfreserveobject()
868 if name then
869 names[name] = r
870 if trace_objects then
871 report_objects("reserving number %a under name %a",r,name)
872 end
873 elseif trace_objects then
874 report_objects("reserving number %a",r)
875 end
876 return r
877end
878
879local nofpages = 0
880
881local texgetcount = tex.getcount
882
883function lpdf.pagereference(n,complete)
884 if nofpages == 0 then
885 nofpages = structures.pages.nofpages
886 if nofpages == 0 then
887 nofpages = 1
888 end
889 end
890 if n == true or not n then
891 complete = n
892 n = texgetcount("realpageno")
893 end
894 local r = n > nofpages and pdfgetpagereference(nofpages) or pdfgetpagereference(n)
895 return complete and pdfreference(r) or r
896end
897
898function lpdf.nofpages()
899 return structures.pages.nofpages
900end
901
902function lpdf.obj(...)
903 pdfdeferredobject(...)
904end
905
906function lpdf.immediateobj(...)
907 pdfimmediateobject(...)
908end
909
910function lpdf.delayedobject(data,n)
911 if n then
912 pdfdeferredobject(n,data)
913 else
914 n = pdfdeferredobject(data)
915 end
916 pdfreferenceobject(n)
917 return n
918end
919
920function lpdf.flushobject(name,data)
921 if data then
922 local named = names[name]
923 if named then
924 if not trace_objects then
925 elseif trace_details then
926 report_objects("flushing data to reserved object with name %a, data: %S",name,data)
927 else
928 report_objects("flushing data to reserved object with name %a",name)
929 end
930 return pdfimmediateobject(named,tostring(data))
931 else
932 if not trace_objects then
933 elseif trace_details then
934 report_objects("flushing data to reserved object with number %s, data: %S",name,data)
935 else
936 report_objects("flushing data to reserved object with number %s",name)
937 end
938 return pdfimmediateobject(name,tostring(data))
939 end
940 else
941 if trace_objects and trace_details then
942 report_objects("flushing data: %S",name)
943 end
944 return pdfimmediateobject(tostring(name))
945 end
946end
947
948function lpdf.flushstreamobject(data,dict,compressed,objnum)
949 if trace_objects then
950 report_objects("flushing stream object of %s bytes",#data)
951 end
952 local dtype = type(dict)
953 local kind = compressed == "raw" and "raw" or "stream"
954 local nolength = nil
955 if compressed == "raw" then
956 compressed = nil
957 nolength = true
958
959 end
960 return pdfdeferredobject {
961 objnum = objnum,
962 immediate = true,
963 nolength = nolength,
964 compresslevel = compressed == false and 0 or nil,
965 type = "stream",
966 string = data,
967 attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil,
968 }
969end
970
971function lpdf.flushstreamfileobject(filename,dict,compressed,objnum)
972 if trace_objects then
973 report_objects("flushing stream file object %a",filename)
974 end
975 local dtype = type(dict)
976 return pdfdeferredobject {
977 objnum = objnum,
978 immediate = true,
979 compresslevel = compressed == false and 0 or nil,
980 type = "stream",
981 file = filename,
982 attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil,
983 }
984end
985
986local shareobjectcache, shareobjectreferencecache = { }, { }
987
988function lpdf.shareobject(content)
989 if content == nil then
990
991 else
992 content = tostring(content)
993 local o = shareobjectcache[content]
994 if not o then
995 o = pdfimmediateobject(content)
996 shareobjectcache[content] = o
997 end
998 return o
999 end
1000end
1001
1002function lpdf.shareobjectreference(content)
1003 if content == nil then
1004
1005 else
1006 content = tostring(content)
1007 local r = shareobjectreferencecache[content]
1008 if not r then
1009 local o = shareobjectcache[content]
1010 if not o then
1011 o = pdfimmediateobject(content)
1012 shareobjectcache[content] = o
1013 end
1014 r = pdfreference(o)
1015 shareobjectreferencecache[content] = r
1016 end
1017 return r
1018 end
1019end
1020
1021
1022
1023local pagefinalizers = { { }, { }, { } }
1024local documentfinalizers = { { }, { }, { } }
1025
1026local pageresources, pageattributes, pagesattributes
1027
1028local function resetpageproperties()
1029 pageresources = pdfdictionary()
1030 pageattributes = pdfdictionary()
1031 pagesattributes = pdfdictionary()
1032end
1033
1034function lpdf.getpageproperties()
1035 return {
1036 pageresources = pageresources,
1037 pageattributes = pageattributes,
1038 pagesattributes = pagesattributes,
1039 }
1040end
1041
1042resetpageproperties()
1043
1044local function setpageproperties()
1045 pdfsetpageresources (pageresources ())
1046 pdfsetpageattributes (pageattributes ())
1047 pdfsetpagesattributes(pagesattributes())
1048end
1049
1050local function addtopageresources (k,v) pageresources [k] = v end
1051local function addtopageattributes (k,v) pageattributes [k] = v end
1052local function addtopagesattributes(k,v) pagesattributes[k] = v end
1053
1054lpdf.addtopageresources = addtopageresources
1055lpdf.addtopageattributes = addtopageattributes
1056lpdf.addtopagesattributes = addtopagesattributes
1057
1058local function set(where,what,f,when,comment)
1059 if type(when) == "string" then
1060 when, comment = 2, when
1061 elseif not when then
1062 when = 2
1063 end
1064 local w = where[when]
1065 w[#w+1] = { f, comment }
1066 if trace_finalizers then
1067 report_finalizing("%s set: [%s,%s]",what,when,#w)
1068 end
1069end
1070
1071local function run(where,what)
1072 if trace_finalizers then
1073 report_finalizing("start backend, category %a, n %a",what,#where)
1074 end
1075 for i=1,#where do
1076 local w = where[i]
1077 for j=1,#w do
1078 local wj = w[j]
1079 if trace_finalizers then
1080 report_finalizing("%s finalizer: [%s,%s] %s",what,i,j,wj[2] or "")
1081 end
1082 wj[1]()
1083 end
1084 end
1085 if trace_finalizers then
1086 report_finalizing("stop finalizing")
1087 end
1088end
1089
1090local function registerpagefinalizer(f,when,comment)
1091 set(pagefinalizers,"page",f,when,comment)
1092end
1093
1094local function registerdocumentfinalizer(f,when,comment)
1095 set(documentfinalizers,"document",f,when,comment)
1096end
1097
1098lpdf.registerpagefinalizer = registerpagefinalizer
1099lpdf.registerdocumentfinalizer = registerdocumentfinalizer
1100
1101function lpdf.finalizepage(shipout)
1102 if shipout and not environment.initex then
1103
1104 run(pagefinalizers,"page")
1105 setpageproperties()
1106 resetpageproperties()
1107 end
1108end
1109
1110function lpdf.finalizedocument()
1111 if not environment.initex then
1112 run(documentfinalizers,"document")
1113 function lpdf.finalizedocument()
1114
1115 function lpdf.finalizedocument() end
1116 end
1117 end
1118end
1119
1120callbacks.register("finish_pdfpage", lpdf.finalizepage)
1121callbacks.register("finish_pdffile", lpdf.finalizedocument)
1122
1123do
1124
1125 local pdfsetinfo, pdfsetcatalog, pdfsettrailerid
1126
1127 updaters.register("backend.update.lpdf",function()
1128 pdfsetinfo = pdf.setinfo
1129 pdfsetcatalog = pdf.setcatalog
1130 pdfsettrailerid = pdf.settrailerid
1131 end)
1132
1133 function lpdf.settrailerid(id)
1134 pdfsettrailerid(id)
1135 end
1136
1137
1138
1139 local function trace_set(what,key)
1140 if trace_resources then
1141 report_finalizing("setting key %a in %a",key,what)
1142 end
1143 end
1144
1145 local function trace_flush(what)
1146 if trace_resources then
1147 report_finalizing("flushing %a",what)
1148 end
1149 end
1150
1151 lpdf.protectresources = true
1152
1153 local catalog = pdfdictionary { Type = pdfconstant("Catalog") }
1154 local info = pdfdictionary { Type = pdfconstant("Info") }
1155
1156
1157 local function checkcatalog()
1158 if not environment.initex then
1159 trace_flush("catalog")
1160 return true
1161 end
1162 end
1163
1164 local function checkinfo()
1165 if not environment.initex then
1166 trace_flush("info")
1167 if lpdf.majorversion() > 1 then
1168 for k, v in next, info do
1169 if k == "CreationDate" or k == "ModDate" then
1170
1171 else
1172 info[k] = nil
1173 end
1174 end
1175 end
1176 return true
1177 end
1178 end
1179
1180 local function flushcatalog()
1181 if checkcatalog() then
1182 catalog.Type = nil
1183 pdfsetcatalog(catalog())
1184 end
1185 end
1186
1187 local function flushinfo()
1188 if checkinfo() then
1189 info.Type = nil
1190 pdfsetinfo(info())
1191 end
1192 end
1193
1194 function lpdf.getcatalog()
1195 if checkcatalog() then
1196 catalog.Type = pdfconstant("Catalog")
1197 return pdfreference(pdfimmediateobject(tostring(catalog)))
1198 end
1199 end
1200
1201 function lpdf.getinfo()
1202 if checkinfo() then
1203 return pdfreference(pdfimmediateobject(tostring(info)))
1204 end
1205 end
1206
1207 function lpdf.addtocatalog(k,v)
1208 if not (lpdf.protectresources and catalog[k]) then
1209 trace_set("catalog",k)
1210 catalog[k] = v
1211 end
1212 end
1213
1214 function lpdf.addtoinfo(k,v)
1215 if not (lpdf.protectresources and info[k]) then
1216 trace_set("info",k)
1217 info[k] = v
1218 end
1219 end
1220
1221
1222
1223
1224
1225
1226
1227
1228 local names = pdfdictionary {
1229
1230 }
1231
1232 local function flushnames()
1233 if next(names) and not environment.initex then
1234 names.Type = pdfconstant("Names")
1235 trace_flush("names")
1236 lpdf.addtocatalog("Names",pdfreference(pdfimmediateobject(tostring(names))))
1237 end
1238 end
1239
1240 function lpdf.addtonames(k,v)
1241 if not (lpdf.protectresources and names[k]) then
1242 trace_set("names", k)
1243 names [k] = v
1244 end
1245 end
1246
1247 local r_extgstates, r_colorspaces, r_patterns, r_shades
1248 local d_extgstates, d_colorspaces, d_patterns, d_shades
1249 local p_extgstates, p_colorspaces, p_patterns, p_shades
1250
1251 local function checkextgstates () if d_extgstates then addtopageresources("ExtGState", p_extgstates ) end end
1252 local function checkcolorspaces() if d_colorspaces then addtopageresources("ColorSpace",p_colorspaces) end end
1253 local function checkpatterns () if d_patterns then addtopageresources("Pattern", p_patterns ) end end
1254 local function checkshades () if d_shades then addtopageresources("Shading", p_shades ) end end
1255
1256 local function flushextgstates () if d_extgstates then trace_flush("extgstates") pdfimmediateobject(r_extgstates, tostring(d_extgstates )) end end
1257 local function flushcolorspaces() if d_colorspaces then trace_flush("colorspaces") pdfimmediateobject(r_colorspaces,tostring(d_colorspaces)) end end
1258 local function flushpatterns () if d_patterns then trace_flush("patterns") pdfimmediateobject(r_patterns, tostring(d_patterns )) end end
1259 local function flushshades () if d_shades then trace_flush("shades") pdfimmediateobject(r_shades, tostring(d_shades )) end end
1260
1261
1262
1263
1264
1265
1266
1267 local f_font = formatters["%s%d"]
1268
1269 function lpdf.collectedresources(options)
1270 local ExtGState = d_extgstates and next(d_extgstates ) and p_extgstates
1271 local ColorSpace = d_colorspaces and next(d_colorspaces) and p_colorspaces
1272 local Pattern = d_patterns and next(d_patterns ) and p_patterns
1273 local Shading = d_shades and next(d_shades ) and p_shades
1274 local Font
1275 if options and options.patterns == false then
1276 Pattern = nil
1277 end
1278 local fonts = options and options.fonts
1279 if fonts and next(fonts) then
1280 local pdfgetfontobjnumber = lpdf.getfontobjnumber
1281 if pdfgetfontobjnumber then
1282 local prefix = options.fontprefix or "F"
1283 Font = pdfdictionary { }
1284 for k, v in sortedhash(fonts) do
1285 Font[f_font(prefix,v)] = pdfreference(pdfgetfontobjnumber(k))
1286 end
1287 end
1288 end
1289 if ExtGState or ColorSpace or Pattern or Shading or Font then
1290 local collected = pdfdictionary {
1291 ExtGState = ExtGState,
1292 ColorSpace = ColorSpace,
1293 Pattern = Pattern,
1294 Shading = Shading,
1295 Font = Font,
1296 }
1297 if options and options.serialize == false then
1298 return collected
1299 else
1300 return collected()
1301 end
1302 elseif options and options.notempty then
1303 return nil
1304 elseif options and options.serialize == false then
1305 return pdfdictionary { }
1306 else
1307 return ""
1308 end
1309 end
1310
1311 function lpdf.adddocumentextgstate (k,v)
1312 if not d_extgstates then
1313 r_extgstates = pdfreserveobject()
1314 d_extgstates = pdfdictionary()
1315 p_extgstates = pdfreference(r_extgstates)
1316 end
1317 d_extgstates[k] = v
1318 end
1319
1320 function lpdf.adddocumentcolorspace(k,v)
1321 if not d_colorspaces then
1322 r_colorspaces = pdfreserveobject()
1323 d_colorspaces = pdfdictionary()
1324 p_colorspaces = pdfreference(r_colorspaces)
1325 end
1326 d_colorspaces[k] = v
1327 end
1328
1329 function lpdf.adddocumentpattern(k,v)
1330 if not d_patterns then
1331 r_patterns = pdfreserveobject()
1332 d_patterns = pdfdictionary()
1333 p_patterns = pdfreference(r_patterns)
1334 end
1335 d_patterns[k] = v
1336 end
1337
1338 function lpdf.adddocumentshade(k,v)
1339 if not d_shades then
1340 r_shades = pdfreserveobject()
1341 d_shades = pdfdictionary()
1342 p_shades = pdfreference(r_shades)
1343 end
1344 d_shades[k] = v
1345 end
1346
1347 registerdocumentfinalizer(flushextgstates,3,"extended graphic states")
1348 registerdocumentfinalizer(flushcolorspaces,3,"color spaces")
1349 registerdocumentfinalizer(flushpatterns,3,"patterns")
1350 registerdocumentfinalizer(flushshades,3,"shades")
1351
1352 registerdocumentfinalizer(flushnames,3,"names")
1353 registerdocumentfinalizer(flushcatalog,3,"catalog")
1354 registerdocumentfinalizer(flushinfo,3,"info")
1355
1356 registerpagefinalizer(checkextgstates,3,"extended graphic states")
1357 registerpagefinalizer(checkcolorspaces,3,"color spaces")
1358 registerpagefinalizer(checkpatterns,3,"patterns")
1359 registerpagefinalizer(checkshades,3,"shades")
1360
1361end
1362
1363
1364
1365function lpdf.rotationcm(a)
1366 local s = sind(a)
1367 local c = cosd(a)
1368 return format("%.6F %.6F %.6F %.6F 0 0 cm",c,s,-s,c)
1369end
1370
1371
1372
1373do
1374
1375
1376
1377 local metadata = nil
1378 local timestamp = backends.timestamp()
1379
1380 function lpdf.getmetadata()
1381 if not metadata then
1382 local contextversion = environment.version
1383 local luatexversion = format("%1.2f",LUATEXVERSION)
1384 local luatexfunctionality = tostring(LUATEXFUNCTIONALITY)
1385 metadata = {
1386 producer = format("LuaTeX-%s",luatexversion),
1387 creator = format("LuaTeX %s %s + ConTeXt MkIV %s",luatexversion,luatexfunctionality,contextversion),
1388 luatexversion = luatexversion,
1389 contextversion = contextversion,
1390 luatexfunctionality = luatexfunctionality,
1391 luaversion = tostring(LUAVERSION),
1392 platform = os.platform,
1393 time = timestamp,
1394 }
1395 end
1396 return metadata
1397 end
1398
1399 function lpdf.settime(n)
1400 if n then
1401 n = converters.totime(n)
1402 if n then
1403 converters.settime(n)
1404 timestamp = backends.timestamp()
1405 end
1406 end
1407 if metadata then
1408 metadata.time = timestamp
1409 end
1410 return timestamp
1411 end
1412
1413
1414 lpdf.settime(tonumber(resolvers.variable("starttime")))
1415
1416 function lpdf.pdftimestamp(str)
1417 local t = type(str)
1418 if t == "string" then
1419 local Y, M, D, h, m, s, Zs, Zh, Zm = match(str,"^(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$")
1420 return Y and format("D:%s%s%s%s%s%s%s%s'%s",Y,M,D,h,m,s,Zs,Zh,Zm)
1421 else
1422 return osdate("D:%Y%m%d%H%M%S",t == "number" and str or ostime())
1423 end
1424 end
1425
1426 function lpdf.id(date)
1427 local banner = environment.jobname or tex.jobname or "unknown"
1428 if not date then
1429 return banner
1430 else
1431 return format("%s | %s",banner,timestamp)
1432 end
1433 end
1434
1435end
1436
1437
1438
1439function lpdf.checkedkey(t,key,variant)
1440 local pn = t and t[key]
1441 if pn ~= nil then
1442 local tn = type(pn)
1443 if tn == variant then
1444 if variant == "string" then
1445 if pn ~= "" then
1446 return pn
1447 end
1448 elseif variant == "table" then
1449 if next(pn) then
1450 return pn
1451 end
1452 else
1453 return pn
1454 end
1455 elseif tn == "string" then
1456 if variant == "number" then
1457 return tonumber(pn)
1458 elseif variant == "boolean" then
1459 return isboolean(pn,nil,true)
1460 end
1461 end
1462 end
1463
1464end
1465
1466function lpdf.checkedvalue(value,variant)
1467 if value ~= nil then
1468 local tv = type(value)
1469 if tv == variant then
1470 if variant == "string" then
1471 if value ~= "" then
1472 return value
1473 end
1474 elseif variant == "table" then
1475 if next(value) then
1476 return value
1477 end
1478 else
1479 return value
1480 end
1481 elseif tv == "string" then
1482 if variant == "number" then
1483 return tonumber(value)
1484 elseif variant == "boolean" then
1485 return isboolean(value,nil,true)
1486 end
1487 end
1488 end
1489end
1490
1491function lpdf.limited(n,min,max,default)
1492 if not n then
1493 return default
1494 else
1495 n = tonumber(n)
1496 if not n then
1497 return default
1498 elseif n > max then
1499 return max
1500 elseif n < min then
1501 return min
1502 else
1503 return n
1504 end
1505 end
1506end
1507
1508
1509
1510
1511
1512
1513
1514
1515do
1516
1517 local f_actual_text_p = formatters["BT /Span << /ActualText <feff%s> >> BDC %s EMC ET"]
1518 local f_actual_text_b = formatters["BT /Span << /ActualText <feff%s> >> BDC"]
1519 local s_actual_text_e = "EMC ET"
1520 local f_actual_text_b_not = formatters["/Span << /ActualText <feff%s> >> BDC"]
1521 local s_actual_text_e_not = "EMC"
1522 local f_actual_text = formatters["/Span <</ActualText %s >> BDC"]
1523
1524 local context = context
1525 local pdfdirect = nodes.pool.directliteral
1526 local tounicode = fonts.mappings.tounicode
1527
1528 function codeinjections.unicodetoactualtext(unicode,pdfcode)
1529 return f_actual_text_p(type(unicode) == "string" and unicode or tounicode(unicode),pdfcode)
1530 end
1531
1532 function codeinjections.startunicodetoactualtext(unicode)
1533 return f_actual_text_b(type(unicode) == "string" and unicode or tounicode(unicode))
1534 end
1535
1536 function codeinjections.stopunicodetoactualtext()
1537 return s_actual_text_e
1538 end
1539
1540 function codeinjections.startunicodetoactualtextdirect(unicode)
1541 return f_actual_text_b_not(type(unicode) == "string" and unicode or tounicode(unicode))
1542 end
1543
1544 function codeinjections.stopunicodetoactualtextdirect()
1545 return s_actual_text_e_not
1546 end
1547
1548 implement {
1549 name = "startactualtext",
1550 arguments = "string",
1551 actions = function(str)
1552 context(pdfdirect(f_actual_text(tosixteen(str))))
1553 end
1554 }
1555
1556 implement {
1557 name = "stopactualtext",
1558 actions = function()
1559 context(pdfdirect("EMC"))
1560 end
1561 }
1562
1563end
1564
1565
1566
1567implement { name = "lpdf_collectedresources", actions = { lpdf.collectedresources, context } }
1568implement { name = "lpdf_addtocatalog", arguments = "2 strings", actions = lpdf.addtocatalog }
1569implement { name = "lpdf_addtoinfo", arguments = "2 strings", actions = function(a,b,c) lpdf.addtoinfo(a,b,c) end }
1570implement { name = "lpdf_addtonames", arguments = "2 strings", actions = lpdf.addtonames }
1571implement { name = "lpdf_addtopageattributes", arguments = "2 strings", actions = lpdf.addtopageattributes }
1572implement { name = "lpdf_addtopagesattributes", arguments = "2 strings", actions = lpdf.addtopagesattributes }
1573implement { name = "lpdf_addtopageresources", arguments = "2 strings", actions = lpdf.addtopageresources }
1574implement { name = "lpdf_adddocumentextgstate", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentextgstate (a,pdfverbose(b)) end }
1575implement { name = "lpdf_adddocumentcolorspace", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentcolorspace(a,pdfverbose(b)) end }
1576implement { name = "lpdf_adddocumentpattern", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentpattern (a,pdfverbose(b)) end }
1577implement { name = "lpdf_adddocumentshade", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentshade (a,pdfverbose(b)) end }
1578
1579
1580
1581function lpdf.copyconstant(v)
1582 if v ~= nil then
1583 return pdfconstant(v)
1584 end
1585end
1586
1587function lpdf.copyboolean(v)
1588 if v ~= nil then
1589 return pdfboolean(v)
1590 end
1591end
1592
1593function lpdf.copyunicode(v)
1594 if v then
1595 return pdfunicode(v)
1596 end
1597end
1598
1599function lpdf.copyarray(a)
1600 if a then
1601 local t = pdfarray()
1602 for i=1,#a do
1603 t[i] = a(i)
1604 end
1605 return t
1606 end
1607end
1608
1609function lpdf.copydictionary(d)
1610 if d then
1611 local t = pdfdictionary()
1612 for k, v in next, d do
1613 t[k] = d(k)
1614 end
1615 return t
1616 end
1617end
1618
1619function lpdf.copynumber(v)
1620 return v
1621end
1622
1623function lpdf.copyinteger(v)
1624 return v
1625end
1626
1627function lpdf.copyfloat(v)
1628 return v
1629end
1630
1631function lpdf.copystring(v)
1632 if v then
1633 return pdfstring(v)
1634 end
1635end
1636
1637do
1638
1639 local pdfincludechar, pdfincludecharlist, pdfincludefont
1640 local pdfgetfontname, pdfgetfontobjnum
1641 local pdfsetmapfile, pdfsetmapline
1642
1643 updaters.register("backend.update.lpdf",function()
1644 pdfincludechar = pdf.includechar
1645 pdfincludefont = pdf.includefont
1646 pdfincludecharlist = pdf.includecharlist
1647 pdfgetfontname = pdf.getfontname
1648 pdfgetfontobjnum = pdf.getfontobjnum
1649 pdfsetmapfile = pdf.mapfile
1650 pdfsetmapline = pdf.mapline
1651 end)
1652
1653 function lpdf.includechar(f,c) pdfincludechar(f,c) end
1654 function lpdf.includefont(...) pdfincludefont(...) end
1655
1656 function lpdf.includecharlist(f,c) pdfincludecharlist(f,c) end
1657
1658 function lpdf.getfontname (id) return pdfgetfontname (id) end
1659 function lpdf.getfontobjnumber(id) return pdfgetfontobjnum(id) end
1660
1661 function lpdf.setmapfile(...) pdfsetmapfile(...) end
1662 function lpdf.setmapline(...) pdfsetmapline(...) end
1663
1664end
1665
1666do
1667
1668
1669
1670
1671 local a_procset, d_procset
1672
1673 function lpdf.procset(dict)
1674 if not a_procset then
1675 a_procset = pdfarray {
1676 pdfconstant("PDF"),
1677 pdfconstant("Text"),
1678 pdfconstant("ImageB"),
1679 pdfconstant("ImageC"),
1680 pdfconstant("ImageI"),
1681 }
1682 a_procset = pdfreference(pdfimmediateobject(tostring(a_procset)))
1683 end
1684 if dict then
1685 if not d_procset then
1686 d_procset = pdfdictionary {
1687 ProcSet = a_procset
1688 }
1689 d_procset = pdfreference(pdfimmediateobject(tostring(d_procset)))
1690 end
1691 return d_procset
1692 else
1693 return a_procset
1694 end
1695 end
1696
1697end
1698
1699
1700
1701if environment.arguments.nocompression then
1702 lpdf.setcompression(0,0,true)
1703end
1704 |