1if not modules then modules = { } end modules ['lpdf-img'] = {
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
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31local type = type
32local concat, move = table.concat, table.move
33local ceil, min = math.ceil, math.min
34local char, byte, find, gmatch = string.char, string.byte, string.find, string.gmatch
35
36
37
38local loaddata = io.loaddata
39local setmetatableindex = table.setmetatableindex
40local formatters = string.formatters
41
42local streams = utilities.streams
43local openstring = streams.openstring
44local readstring = streams.readstring
45local readbytetable = streams.readbytetable
46
47local newreader = io.newreader
48
49local tobytetable = string.bytetable
50
51local lpdf = lpdf or { }
52local pdfdictionary = lpdf.dictionary
53local pdfarray = lpdf.array
54local pdfconstant = lpdf.constant
55local pdfstring = lpdf.string
56local pdfreference = lpdf.reference
57local pdfverbose = lpdf.verbose
58local pdfflushstreamobject = lpdf.flushstreamobject
59local pdfmajorversion = lpdf.majorversion
60local pdfminorversion = lpdf.minorversion
61
62local codeinjections = backends.registered.pdf.codeinjections
63
64local createimage = images.create
65
66local zlibcompress = xzip.compress
67local zlibdecompress = xzip.decompress
68
69local trace = false
70local cleanvirtual = resolvers.cleaners.virtual
71
72local report_jpg = logs.reporter("graphics","jpg")
73local report_jp2 = logs.reporter("graphics","jp2")
74local report_png = logs.reporter("graphics","png")
75
76trackers.register("graphics.backend", function(v) trace = v end)
77
78directives.register("graphics.cleanvirtuals", function(v) cleanvirtual = v and resolvers.cleaners.virtual or false end)
79
80local injectors = { }
81lpdf.injectors = injectors
82
83
84
85
86
87
88
89
90
91local function loadcontent(filename,method,wipe)
92 if method == "string" then
93 return filename
94 else
95 local found, data = resolvers.loadbinfile(filename)
96 if wipe then
97 resolvers.cleanupbinfile(filename)
98 end
99 return data
100 end
101end
102
103local function newcontent(filename,method,wipe)
104 if method == "string" then
105 return newreader(filename,method)
106 else
107 local found, data = resolvers.loadbinfile(filename)
108 if wipe then
109 resolvers.cleanupbinfile(filename)
110 end
111 return newreader(data or "", "string")
112 end
113end
114
115
116
117local chars = setmetatableindex(function(t,k)
118 local v = (k <= 0 and "\000") or (k >= 255 and "\255") or char(k)
119 t[k] = v
120 return v
121end)
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137local function todecode(newranges,colors)
138 local kind = type(newranges)
139 if colors == 1 then
140 if kind == "table" then
141 local n = #newranges
142 if n == 1 then
143 return { 0, newranges[1] }
144 elseif n == 2 then
145 return { newranges[1], newranges[2] }
146 end
147 else
148 return { 0, newranges }
149 end
150 elseif colors == 3 then
151 if kind == "table" then
152 local n = #newranges
153 if n == 1 then
154 return { 0, newranges, 0, newranges, 0, newranges }
155 elseif n == 2 then
156 local n1, n2 = newranges[1], newranges[2]
157 return { n1, n2, n1, n2, n1, n2 }
158 elseif n == 6 then
159 return newranges
160 end
161 else
162 return { 0, newranges, 0, newranges, 0, newranges }
163 end
164 elseif colors == 4 then
165 if kind == "table" then
166 local n = #newranges
167 if n == 1 then
168 return { 0, newranges, 0, newranges, 0, newranges, 0, newranges }
169 elseif n == 2 then
170 local n1, n2 = newranges[1], newranges[2]
171 return { n1, n2, n1, n2, n1, n2, n1, n2 }
172 elseif n == 8 then
173 return newranges
174 end
175 else
176 return { 0, newranges, 0, newranges, 0, newranges, 0, newranges }
177 end
178 end
179end
180
181
182
183do
184
185 function injectors.jpg(specification,method)
186 if specification.error then
187 return
188 end
189 local filename = specification.filename
190 if not filename then
191 return
192 end
193 local colorspace = specification.colorspace or jpg_gray
194 local decodearray = nil
195 local colors = 1
196
197 if colorspace == 1 then
198 colorspace = "DeviceGray"
199 elseif colorspace == 2 then
200 colorspace = "DeviceRGB"
201 colors = 3
202 elseif colorspace == 3 then
203 colorspace = "DeviceCMYK"
204 colors = 4
205 decodearray = pdfarray { 1, 0, 1, 0, 1, 0, 1, 0 }
206 end
207
208 local newranges = specification.newranges
209 if newranges then
210 decodearray = newranges and pdfarray(todecode(newranges,colors)) or nil
211 end
212
213 local xsize = specification.xsize
214 local ysize = specification.ysize
215 local colordepth = specification.colordepth
216 local content = loadcontent(filename,method,true)
217 local xobject = pdfdictionary {
218 Type = pdfconstant("XObject"),
219 Subtype = pdfconstant("Image"),
220
221 Width = xsize,
222 Height = ysize,
223 BitsPerComponent = colordepth,
224 Filter = pdfconstant("DCTDecode"),
225 ColorSpace = pdfconstant(colorspace),
226 Decode = decodearray,
227 Length = #content,
228 } + specification.attr
229 if trace then
230 report_jpg("%s: width %i, height %i, colordepth %i, size %i",filename,xsize,ysize,colordepth,#content)
231 end
232 if cleanvirtual then
233 cleanvirtual(filename)
234 end
235 return createimage {
236 bbox = { 0, 0, specification.width/xsize, specification.height/ysize },
237 transform = specification.transform,
238 nolength = true,
239 nobbox = true,
240 notype = true,
241 stream = content,
242 attr = xobject(),
243 }
244 end
245
246end
247
248do
249
250 function injectors.jp2(specification,method)
251 if specification.error then
252 return
253 end
254 local filename = specification.filename
255 if not filename then
256 return
257 end
258
259 local xsize = specification.xsize
260 local ysize = specification.ysize
261 local content = loadcontent(filename,method,true)
262 local xobject = pdfdictionary {
263 Type = pdfconstant("XObject"),
264 Subtype = pdfconstant("Image"),
265 BBox = pdfarray { 0, 0, xsize, ysize },
266 Width = xsize,
267 Height = ysize,
268 Filter = pdfconstant("JPXDecode"),
269 Length = #content,
270 } + specification.attr
271 if trace then
272 report_jp2("%s: width %i, height %i, size %i",filename,xsize,ysize,#content)
273 end
274 if cleanvirtual then
275 cleanvirtual(filename)
276 end
277 return createimage {
278 bbox = { 0, 0, specification.width/xsize, specification.height/ysize },
279 transform = specification.transform,
280 nolength = true,
281 nobbox = true,
282 notype = true,
283 stream = content,
284 attr = xobject(),
285 }
286 end
287
288end
289
290do
291
292
293
294
295
296
297
298
299 local pngapplyfilter = pngdecode.applyfilter
300 local pngsplitmask = pngdecode.splitmask
301 local pnginterlace = pngdecode.interlace
302 local pngexpand = pngdecode.expand
303 local pngtocmyk = pngdecode.tocmyk
304 local pngtomask = pngdecode.tomask
305 local pngmakemask = pngdecode.makemask
306
307 local filtermask, decodemask, decodestrip, transpose, expand, tocmyk
308
309 local newindex = lua.newindex
310 local newtable = lua.newtable
311
312 local function newoutput(size)
313 if newindex then
314 return newindex(size,char(0))
315 end
316 local t = newtable and newtable(size,0) or { }
317 for i=1,size do
318 t[i] = 0
319 end
320 return t
321 end
322
323 local function convert(t)
324 if type(t) == "table" then
325 for i=1,#t do
326 local ti = t[i]
327 if ti ~= "" then
328 t[i] = chars[ti]
329 end
330 end
331 return concat(t)
332 else
333 return t
334 end
335 end
336
337 local function zero(t,k)
338 return 0
339 end
340
341 local function applyfilter(t,xsize,ysize,bpp)
342 local len = xsize * bpp + 1
343 local n = 1
344 local m = len - 1
345 for i=1,ysize do
346 local filter = t[n]
347 t[n] = ""
348 if filter == 0 then
349 elseif filter == 1 then
350 for j=n+bpp+1,n+m do
351 t[j] = (t[j] + t[j-bpp]) % 256
352 end
353 elseif filter == 2 then
354 for j=n+1,n+m do
355 t[j] = (t[j] + t[j-len]) % 256
356 end
357 elseif filter == 3 then
358 for j=n+1,n+bpp do
359 t[j] = (t[j] + (t[j-len] // 2)) % 256
360 end
361 for j=n+bpp+1,n+m do
362 t[j] = (t[j] + (t[j-bpp] + t[j-len]) // 2) % 256
363 end
364 elseif filter == 4 then
365 for j=n+1,n+bpp do
366 local p = j - len
367 local b = t[p]
368 if b > 0 then
369 t[j] = (t[j] + b) % 256
370 end
371 end
372 for j=n+bpp+1,n+m do
373 local p = j - len
374 local a = t[j-bpp]
375 local b = t[p]
376 local c = t[p-bpp]
377 local pa = b - c
378 local pb = a - c
379 local pc = pa + pb
380 if pa < 0 then pa = - pa end
381 if pb < 0 then pb = - pb end
382 if pc < 0 then pc = - pc end
383 t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256
384 end
385 end
386 n = n + len
387 end
388 return t
389 end
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481 local filtermask_c = function(content,xsize,ysize,colordepth,colorspace)
482 local bytes = colordepth == 16 and 2 or 1
483 local bpp = colorspace == "DeviceRGB" and 3 or 1
484 return pngsplitmask(content,xsize,ysize,bpp,bytes)
485 end
486
487 local decodemask_c = function(content,xsize,ysize,colordepth,colorspace)
488 local mask = true
489 local filter = false
490 local bytes = colordepth == 16 and 2 or 1
491 local bpp = colorspace == "DeviceRGB" and 3 or 1
492 local slice = bytes * (bpp + 1)
493 content = pngapplyfilter(content,xsize,ysize,slice)
494 return pngsplitmask(content,xsize,ysize,bpp,bytes,mask,filter)
495 end
496
497
498
499
500
501
502
503
504 local function decodestrip_c(s,nx,ny,slice)
505 local input = readstring(s,ny*(nx*slice+1))
506 input = pngapplyfilter(input,nx,ny,slice)
507 return input, false
508 end
509
510 local xstart = { 0, 4, 0, 2, 0, 1, 0 }
511 local ystart = { 0, 0, 4, 0, 2, 0, 1 }
512 local xstep = { 8, 8, 4, 4, 2, 2, 1 }
513 local ystep = { 8, 8, 8, 4, 4, 2, 2 }
514
515 local xblock = { 8, 4, 4, 2, 2, 1, 1 }
516 local yblock = { 8, 8, 4, 4, 2, 2, 1 }
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593 local transpose_c = pnginterlace
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717 local expand_c = pngexpand
718
719 local function analyze(colordepth,colorspace,palette,mask)
720
721 if palette then
722 if colordepth == 16 then
723 return 2, false, false
724 elseif colordepth == 8 then
725 return 1, false, false
726 elseif colordepth == 4 then
727 return 1, 4, false
728 elseif colordepth == 2 then
729 return 1, 2, false
730 elseif colordepth == 1 then
731 return 1, 1, false
732 end
733 elseif colorspace == "DeviceGray" then
734 if colordepth == 16 then
735 return mask and 4 or 2, false, false
736 elseif colordepth == 8 then
737 return mask and 2 or 1, false, false
738 elseif colordepth == 4 then
739 return 1, 4, true
740 elseif colordepth == 2 then
741 return 1, 2, true
742 elseif colordepth == 1 then
743 return 1, 1, true
744 end
745 else
746 if colordepth == 16 then
747 return mask and 8 or 6, false, false
748 elseif colordepth == 8 then
749 return mask and 4 or 3, false, false
750 elseif colordepth == 4 then
751 return 3, 4, true
752 elseif colordepth == 2 then
753 return 3, 2, true
754 elseif colordepth == 1 then
755 return 3, 1, true
756 end
757 end
758 return false, false, false
759 end
760
761
762
763
764
765
766
767
768
769
770 local function deinterlace(content,xsize,ysize,colordepth,colorspace,palette,mask)
771 local slice, parts, factor = analyze(colordepth,colorspace,palette,mask)
772 if slice then
773 content = openstring(zlibdecompress(content))
774 local filter = false
775 local output = false
776 for pass=1,7 do
777 local xstart = xstart[pass]
778 local xstep = xstep[pass]
779 local ystart = ystart[pass]
780 local ystep = ystep[pass]
781 local nx = (xsize + xstep - xstart - 1) // xstep
782 local ny = (ysize + ystep - ystart - 1) // ystep
783 if nx > 0 and ny > 0 then
784 local input, filter
785 if parts then
786 local nxx = ceil(nx*parts/8)
787 input, filter = decodestrip(content,nxx,ny,slice)
788 input, filter = expand(input,nx,ny,parts,nxx,factor,filter)
789 else
790 input, filter = decodestrip(content,nx,ny,slice)
791 end
792 output = transpose(xsize,ysize,slice,pass,input,output,filter)
793 end
794
795
796
797 end
798 return output, parts and 8 or false
799 end
800 end
801
802
803
804
805
806
807
808
809
810
811 local function full(t,k) local v = "\xFF" t[k] = v return v end
812
813 local function expandvector(transparent)
814 local s = openstring(transparent)
815 local n = #transparent
816 local r = { }
817 for i=0,n-1 do
818 r[i] = readstring(s,1)
819 end
820 setmetatableindex(r,full)
821 return r
822 end
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967 local function createmask_c(content,palette,transparent,xsize,ysize,colordepth,colorspace)
968 if palette then
969 local len = ceil(xsize*colordepth/8)
970 content = zlibdecompress(content)
971 content = pngapplyfilter(content,len,ysize,1)
972 return pngtomask(content,transparent,xsize,ysize,colordepth)
973 end
974 end
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994 local tocmyk_c = pngtocmyk
995
996 local function converttocmyk(content,colorspace,colordepth)
997 if colorspace == "DeviceRGB" and colordepth == 8 or colordepth == 16 then
998 local done = tocmyk(content,colordepth)
999 if done then
1000 content = done
1001 colorspace = "DeviceCMYK"
1002 end
1003 end
1004 return content, colorspace
1005 end
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017 filtermask = filtermask_c
1018 decodemask = decodemask_c
1019 decodestrip = decodestrip_c
1020 transpose = transpose_c
1021 expand = expand_c
1022 createmask = createmask_c
1023 tocmyk = tocmyk_c
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034 local alwaysdecode = false
1035 local compresslevel = 3
1036
1037 directives.register("graphics.png.recompress", function(v)
1038 alwaysdecode = v
1039 end)
1040
1041 directives.register("graphics.png.compresslevel", function(v)
1042 v = tonumber(v)
1043 if compresslevel >= 0 or compresslevel <= 9 then
1044 compresslevel = v
1045 end
1046 end)
1047
1048 function injectors.png(specification,method)
1049 if specification.error then
1050 return
1051 end
1052 local filename = specification.filename
1053 if not filename then
1054 return
1055 end
1056 local colorspace = specification.colorspace
1057 if not colorspace then
1058 return
1059 end
1060 local interlace = specification.interlace or 0
1061 if interlace == 1 then
1062 interlace = true
1063 elseif interlace == 0 then
1064 interlace = false
1065 else
1066 report_png("unknown interlacing %i",interlace)
1067 return
1068 end
1069 local tables = specification.tables
1070 if not tables then
1071 return
1072 end
1073 local idat = tables.idat
1074 if not idat then
1075 return
1076 end
1077 local pngfile = newcontent(filename,method,true)
1078 if not pngfile then
1079 return
1080 end
1081 local content = idat(pngfile,true)
1082 tables.idat = false
1083
1084
1085
1086
1087
1088 local xsize = specification.xsize
1089 local ysize = specification.ysize
1090 local colordepth = specification.colordepth or 8
1091 local mask = false
1092 local transparent = false
1093 local palette = false
1094 local enforcecmyk = specification.enforcecmyk
1095 local colors = 1
1096 if colorspace == 0 then
1097 colorspace = "DeviceGray"
1098 transparent = true
1099 elseif colorspace == 2 then
1100 colorspace = "DeviceRGB"
1101 colors = 3
1102 transparent = true
1103 elseif colorspace == 3 then
1104 colorspace = "DeviceRGB"
1105 palette = true
1106 transparent = true
1107 elseif colorspace == 4 then
1108 colorspace = "DeviceGray"
1109 mask = true
1110 elseif colorspace == 6 then
1111 colorspace = "DeviceRGB"
1112 colors = 3
1113 mask = true
1114 else
1115 report_png("unknown colorspace %i",colorspace)
1116 return
1117 end
1118
1119 if transparent then
1120 local trns = tables.trns
1121 if trns then
1122 transparent = trns(pngfile,true)
1123 if transparent == "" then
1124 transparent = false
1125 end
1126 tables.trns = false
1127 else
1128 transparent = false
1129 end
1130 end
1131
1132 local decode = alwaysdecode
1133 local filter = pdfconstant("FlateDecode")
1134 local major = pdfmajorversion()
1135 local minor = pdfminorversion()
1136 if major > 1 then
1137
1138 elseif minor < 5 and colordepth == 16 then
1139 report_png("16 bit colordepth not supported in pdf < 1.5")
1140 return
1141 elseif minor < 4 and (mask or transparent) then
1142 report_png("alpha channels not supported in pdf < 1.4")
1143 return
1144 elseif minor < 2 then
1145 report_png("you'd better use a version > 1.2")
1146 return
1147
1148 end
1149
1150
1151
1152 if palette then
1153 local plte = tables.plte
1154 if plte then
1155 palette = plte(pngfile,true)
1156 if palette == "" then
1157 palette = false
1158 end
1159 tables.plte = false
1160 else
1161 palette = false
1162 end
1163 end
1164
1165 local newmask = specification.newmask
1166 local newranges = specification.newranges
1167
1168 if newranges then
1169 newranges = todecode(newranges,colors)
1170 end
1171
1172 if interlace then
1173 local r, p = deinterlace(content,xsize,ysize,colordepth,colorspace,palette,mask)
1174 if not r then
1175 return
1176 end
1177 if p then
1178 colordepth = p
1179 end
1180 if mask then
1181 if not (colordepth == 8 or colordepth == 16) then
1182 report_png("mask can't be split from the image")
1183 return
1184 end
1185 content, mask = filtermask(r,xsize,ysize,colordepth,colorspace,false)
1186 else
1187 content = convert(r)
1188 end
1189 if enforcecmyk then
1190 content, colorspace = converttocmyk(content,colorspace,colordepth)
1191 end
1192 if compresslevel > 0 then
1193 content = zlibcompress(content,compresslevel)
1194 else
1195 filter = nil
1196 end
1197 decode = true
1198 elseif mask then
1199 if not (colordepth == 8 or colordepth == 16) then
1200 report_png("mask can't be split from the image")
1201 return
1202 end
1203 content = zlibdecompress(content)
1204 content, mask = decodemask(content,xsize,ysize,colordepth,colorspace)
1205 if enforcecmyk and not palette then
1206 content, colorspace = converttocmyk(content,colorspace,colordepth)
1207 end
1208 if compresslevel > 0 then
1209 content = zlibcompress(content,compresslevel)
1210 else
1211 filter = nil
1212 end
1213 decode = true
1214 elseif transparent then
1215
1216
1217 if palette then
1218 mask = createmask(content,palette,transparent,xsize,ysize,colordepth,colorspace)
1219 else
1220 pallette = false
1221 end
1222 elseif decode or (enforcecmyk and not palette) then
1223
1224 local bytes = analyze(colordepth,colorspace)
1225 if bytes then
1226 content = zlibdecompress(content)
1227 content = decodestrip(openstring(content),xsize,ysize,bytes)
1228 if enforcecmyk and not palette then
1229 content, colorspace = converttocmyk(content,colorspace,colordepth)
1230 end
1231 if compresslevel > 0 then
1232 content = zlibcompress(content,compresslevel)
1233 else
1234 filter = nil
1235 end
1236 else
1237 return
1238 end
1239 decode = true
1240 elseif newmask and colordepth == 8 and colorspace == "DeviceGray" then
1241 local bytes = analyze(colordepth,colorspace)
1242 if bytes then
1243 content = zlibdecompress(content)
1244 content = decodestrip(openstring(content),xsize,ysize,bytes)
1245 mask = pngmakemask(content,newmask)
1246 if compresslevel > 0 then
1247 content = zlibcompress(content,compresslevel)
1248 else
1249 filter = nil
1250 end
1251 else
1252 return
1253 end
1254 decode = true
1255 else
1256
1257 end
1258 if palette then
1259 local colorspace = "DeviceRGB"
1260 local nofbytes = 3
1261 if enforcecmyk then
1262 palette = converttocmyk(palette,colorspace,8)
1263 colorspace = "DeviceCMYK"
1264 nofbytes = 4
1265 end
1266 palette = pdfarray {
1267 pdfconstant("Indexed"),
1268 pdfconstant(colorspace),
1269 #palette // nofbytes,
1270 pdfreference(pdfflushstreamobject(palette)),
1271 }
1272 end
1273 pngfile:close()
1274 local xobject = pdfdictionary {
1275 Type = pdfconstant("XObject"),
1276 Subtype = pdfconstant("Image"),
1277
1278 Width = xsize,
1279 Height = ysize,
1280 BitsPerComponent = colordepth,
1281 Filter = filter,
1282 ColorSpace = palette or pdfconstant(colorspace),
1283 Length = #content,
1284 Decode = newranges and pdfarray(newranges) or nil,
1285 } + specification.attr
1286 if mask then
1287 local d = pdfdictionary {
1288 Type = pdfconstant("XObject"),
1289 Subtype = pdfconstant("Image"),
1290 Width = xsize,
1291 Height = ysize,
1292 BitsPerComponent = palette and 8 or colordepth,
1293 ColorSpace = pdfconstant("DeviceGray"),
1294 Decode = newranges and pdfarray(newranges) or nil,
1295 }
1296 xobject.SMask = pdfreference(pdfflushstreamobject(mask,d()))
1297 end
1298 if not decode then
1299
1300 xobject.DecodeParms = pdfdictionary {
1301 Colors = colors,
1302 Columns = xsize,
1303 BitsPerComponent = colordepth,
1304 Predictor = 15,
1305 }
1306 end
1307 if trace then
1308 report_png("%s: width %i, height %i, colordepth %i, size %i, palette %l, mask %l, transparent %l, decode %l",filename,xsize,ysize,colordepth,#content,palette,mask,transparent,decode)
1309 end
1310 if specification.colorref then
1311 xobject.ColorSpace = pdfreference(specification.colorref)
1312 end
1313 local width = specification.width or xsize * 65536
1314 local height = specification.height or ysize * 65536
1315 if cleanvirtual then
1316 cleanvirtual(filename)
1317 end
1318 return createimage {
1319 bbox = { 0, 0, width/xsize, height/ysize },
1320 transform = specification.transform,
1321 nolength = true,
1322 nobbox = true,
1323 notype = true,
1324 stream = content,
1325 attr = xobject(),
1326 }
1327 end
1328
1329end
1330
1331do
1332
1333 local function pack(specification,what)
1334 local t = { }
1335 local n = 0
1336 local s = specification.colorspace
1337 local d = specification.data
1338 local x = specification.xsize
1339 local y = specification.ysize
1340 if what == "mask" then
1341 d = specification.mask
1342 s = 1
1343 elseif what == "indexed" then
1344 s = 1
1345 elseif what == "index" then
1346 d = specification.index
1347 s = - s
1348 end
1349 if s > 0 then
1350
1351 return string.packrowscolumns(d)
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385 else
1386 local z = d[0] and 0 or 1
1387 if s == -1 then
1388 local f = formatters["%02X"]
1389 for i=z,#d do
1390 n = n + 1 ; t[n] = f(d[i])
1391 end
1392 elseif s == -2 then
1393 local f = formatters["%02X%02X%02X"]
1394 for i=z,#d do
1395 local c = d[i]
1396 n = n + 1 ; t[n] = f(c[1],c[2],c[3])
1397 end
1398 elseif s == -3 then
1399 local f = formatters["%02X%02X%02X%02X"]
1400 for i=z,#d do
1401 local c = d[i]
1402 n = n + 1 ; t[n] = f(c[1],c[2],c[3],c[4])
1403 end
1404 end
1405 return "<" .. concat(t," ") .. ">"
1406 end
1407 return ""
1408 end
1409
1410 function injectors.bitmap(specification)
1411 local data = specification.data
1412 if not data then
1413 return
1414 end
1415 local xsize = specification.xsize or 0
1416 local ysize = specification.ysize or 0
1417 if xsize == 0 or ysize == 0 then
1418 return
1419 end
1420 local colorspace = specification.colorspace or 1
1421 if colorspace == 1 then
1422 colorspace = "DeviceGray"
1423 elseif colorspace == 2 then
1424 colorspace = "DeviceRGB"
1425 elseif colorspace == 3 then
1426 colorspace = "DeviceCMYK"
1427 end
1428 local colordepth = (specification.colordepth or 2) == 16 or 8
1429 local index = specification.index
1430 local content = pack(specification,index and "indexed" or "data")
1431 local mask = specification.mask
1432 local colorspace = pdfconstant(colorspace)
1433 if index then
1434 colorspace = pdfarray {
1435 pdfconstant("Indexed"),
1436 colorspace,
1437 #index + (index[0] and 0 or -1),
1438 pdfverbose(pack(specification,"index"))
1439 }
1440 end
1441 local xobject = pdfdictionary {
1442 Type = pdfconstant("XObject"),
1443 Subtype = pdfconstant("Image"),
1444 BBox = pdfarray { 0, 0, xsize, ysize },
1445 Width = xsize,
1446 Height = ysize,
1447 BitsPerComponent = colordepth,
1448 ColorSpace = colorspace,
1449 Length = #content,
1450 }
1451 if mask then
1452 local d = pdfdictionary {
1453 Type = pdfconstant("XObject"),
1454 Subtype = pdfconstant("Image"),
1455 Width = xsize,
1456 Height = ysize,
1457 BitsPerComponent = colordepth,
1458 ColorSpace = pdfconstant("DeviceGray"),
1459 }
1460 xobject.SMask = pdfreference(pdfflushstreamobject(pack(specification,"mask"),d()))
1461 end
1462 local w = specification.width
1463 local h = specification.height
1464 return createimage {
1465 bbox = { 0, 0, w and (w/xsize) or xsize, h and (h/ysize) or ysize },
1466
1467 nobbox = true,
1468 notype = true,
1469 stream = content,
1470 attr = xobject(),
1471 }
1472 end
1473
1474 codeinjections.bitmap = injectors.bitmap
1475
1476end
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501codeinjections.jpg = lpdf.injectors.jpg
1502codeinjections.jp2 = lpdf.injectors.jp2
1503codeinjections.png = lpdf.injectors.png
1504 |