1if not modules then modules = { } end modules ['lpdf-grp'] = {
2 version = 1.001,
3 comment = "companion to lpdf-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local type, tonumber = type, tonumber
10local formatters, gsub = string.formatters, string.gsub
11local sort = table.sort
12local round = math.round
13
14local colors = attributes.colors
15local basepoints = number.dimenfactors.bp
16
17local pdfbackend = backends.registered.pdf
18local nodeinjections = pdfbackend.nodeinjections
19local codeinjections = pdfbackend.codeinjections
20local registrations = pdfbackend.registrations
21
22local lpdf = lpdf
23local pdfdictionary = lpdf.dictionary
24local pdfarray = lpdf.array
25local pdfconstant = lpdf.constant
26local pdfboolean = lpdf.boolean
27local pdfreference = lpdf.reference
28local pdfflushobject = lpdf.flushobject
29
30local createimage = images.create
31local wrapimage = images.wrap
32local embedimage = images.embed
33
34
35
36
37
38
39
40local function hasopacities(opacities)
41 if opacities then
42 for i=1,#opacities do
43 if type(opacities[i]) ~= "number" then
44 return false
45 end
46 end
47 return true
48 end
49 return false
50end
51
52local function shade(stype,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions,opacities)
53 local func = nil
54
55
56
57
58 domain = pdfarray(domain)
59 n = tonumber(n)
60
61 if steps then
62 local list = pdfarray()
63 local bounds = pdfarray()
64 local encode = pdfarray()
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 local tmp = { }
90 for i=1,steps do
91 tmp[i] = { fractions[i], color_a[i], color_b[i] }
92 end
93 sort(tmp, function(a,b)
94 return a[1] < b[1]
95 end)
96 for i=1,steps do
97 local t = tmp[i]
98 fractions[i] = t[1]
99 color_a [i] = t[2]
100 color_b [i] = t[3]
101 end
102
103 for i=1,steps do
104 if i < steps then
105 bounds[i] = fractions[i] or 1
106 end
107 encode[2*i-1] = 0
108 encode[2*i] = 1
109 list [i] = pdfdictionary {
110 FunctionType = 2,
111 Domain = domain,
112 C0 = pdfarray(color_a[i]),
113 C1 = pdfarray(color_b[i]),
114 N = n,
115 }
116 end
117 func = pdfdictionary {
118 FunctionType = 3,
119 Bounds = bounds,
120 Encode = encode,
121 Functions = list,
122 Domain = domain,
123 }
124 else
125 func = pdfdictionary {
126 FunctionType = 2,
127 Domain = domain,
128 C0 = pdfarray(color_a),
129 C1 = pdfarray(color_b),
130 N = n,
131 }
132 end
133 separation = separation and registrations.getspotcolorreference(separation)
134 local s = pdfdictionary {
135 ShadingType = stype,
136 ColorSpace = separation and pdfreference(separation) or pdfconstant(colorspace),
137 Domain = domain,
138 Function = pdfreference(pdfflushobject(func)),
139 Coords = pdfarray(coordinates),
140 Extend = pdfarray { true, true },
141 AntiAlias = pdfboolean(true),
142 }
143 lpdf.adddocumentshade(name,pdfreference(pdfflushobject(s)))
144end
145
146function lpdf.circularshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions,opacities)
147 shade(3,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions,opacities)
148end
149
150function lpdf.linearshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions,opacities)
151 shade(2,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions,opacities)
152end
153
154
155
156
157
158
159
160
161
162do
163
164 local template = "q BI %s ID %s > EI Q"
165 local factor = 72/300
166
167 local methods = { }
168
169 methods.hex = function(t)
170
171 local xresolution = t.xresolution or 0
172 local yresolution = t.yresolution or 0
173 if xresolution == 0 or yresolution == 0 then
174 return
175 end
176 local colorspace = t.colorspace
177 if colorspace ~= "rgb" and colorspace ~= "cmyk" and colorspace ~= "gray" then
178
179 local d = gsub(t.data,"[^0-9a-f]","")
180 local b = round(#d / (xresolution * yresolution))
181 if b == 2 then
182 colorspace = "gray"
183 elseif b == 6 then
184 colorspace = "rgb"
185 elseif b == 8 then
186 colorspace = "cmyk"
187 end
188 end
189 colorspace = lpdf.colorspaceconstants[colorspace]
190 if not colorspace then
191 return
192 end
193
194 local d = pdfdictionary {
195 W = xresolution,
196 H = yresolution,
197 CS = colorspace,
198 BPC = 8,
199 F = pdfconstant("AHx"),
200
201
202
203 }
204
205 local urx, ury = 1/basepoints, 1/basepoints
206
207
208 local width, height = t.width or 0, t.height or 0
209 if width == 0 and height == 0 then
210 width = factor * xresolution / basepoints
211 height = factor * yresolution / basepoints
212 elseif width == 0 then
213 width = height * xresolution / yresolution
214 elseif height == 0 then
215 height = width * yresolution / xresolution
216 end
217 local a = pdfdictionary {
218 BBox = pdfarray { 0, 0, round(urx * basepoints), round(ury * basepoints) }
219 }
220 local image = createimage {
221 stream = formatters[template](d(),t.data),
222 width = width,
223 height = height,
224 bbox = { 0, 0, round(urx), round(ury) },
225 attr = a(),
226 nobbox = true,
227 }
228 return wrapimage(image)
229 end
230
231
232
233
234 local zlibcompress = xzip.compress
235 local hextocharacters = string.hextocharacters
236 local compresslevel = 3
237
238 methods.png = function(t)
239
240 local xresolution = t.xresolution or 0
241 local yresolution = t.yresolution or 0
242 local data = t.data or ""
243 if xresolution == 0 or yresolution == 0 or data == "" then
244 return
245 end
246 data = hextocharacters(data)
247 if not data then
248 return
249 end
250 local colorspace = t.colorspace
251 local colordepth = 8
252 local colors = 1
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 if colorspace ~= "rgb" and colorspace ~= "gray" then
268 local b = round(#data / (xresolution * yresolution))
269 if b == 1 then
270 colorspace = "gray"
271 colors = 1
272 elseif b == 3 then
273 colorspace = "rgb"
274 colors = 3
275 elseif b == 4 then
276 return
277 end
278 end
279 colorspace = lpdf.colorspaceconstants[colorspace]
280 if not colorspace then
281 return
282 end
283 local width = t.width
284 local height = t.height
285 if width == 0 and height == 0 then
286 width = factor * xresolution / basepoints
287 height = factor * yresolution / basepoints
288 elseif width == 0 then
289 width = height * xresolution / yresolution
290 elseif height == 0 then
291 height = width * yresolution / xresolution
292 end
293
294 data = zlibcompress(data,compresslevel)
295 local xobject = pdfdictionary {
296 Type = pdfconstant("XObject"),
297 Subtype = pdfconstant("Image"),
298 Width = xresolution,
299 Height = yresolution,
300 BitsPerComponent = 8,
301 ColorSpace = colorspace,
302 Length = #data,
303 Filter = pdfconstant("FlateDecode"),
304 }
305 local image = createimage {
306
307 bbox = { 0, 0, round(width), round(height) },
308 width = round(width),
309 height = round(height),
310 nolength = true,
311 nobbox = true,
312 notype = true,
313 stream = data,
314 attr = xobject(),
315 }
316 return wrapimage(image)
317 end
318
319 function nodeinjections.injectbitmap(t)
320 if t.colorspace == "cmyk" then
321 return methods.hex(t)
322 else
323 return (methods[t.format or "hex"] or methods.hex)(t)
324 end
325 end
326
327end
328
329function codeinjections.setfigurealternative(data,figure)
330 local request = data.request
331 local display = request.display
332 if display and display ~= "" then
333 local nested = figures.push {
334 name = display,
335 page = request.page,
336 size = request.size,
337 prefix = request.prefix,
338 cache = request.cache,
339 width = request.width,
340 height = request.height,
341 }
342 figures.identify()
343 local displayfigure = figures.check()
344 if displayfigure then
345
346 embedimage(figure)
347 local a = pdfarray {
348 pdfdictionary {
349 Image = pdfreference(figure.objnum),
350 DefaultForPrinting = true,
351 }
352 }
353 local d = pdfdictionary {
354 Alternates = pdfreference(pdfflushobject(a)),
355 }
356 displayfigure.attr = d()
357 figures.pop()
358 return displayfigure, nested
359 else
360 figures.pop()
361 end
362 end
363end
364
365function codeinjections.getpreviewfigure(request)
366 local figure = figures.initialize(request)
367 if not figure then
368 return
369 end
370 figure = figures.identify(figure)
371 if not (figure and figure.status and figure.status.fullname) then
372 return
373 end
374 figure = figures.check(figure)
375 if not (figure and figure.status and figure.status.fullname) then
376 return
377 end
378 local image = figure.status.private
379 if image then
380 embedimage(image)
381 end
382 return figure
383end
384
385local masks = {
386 demomask = {
387 { 0, 63, 0 },
388 { 64, 127, 127 },
389 { 128, 195, 195 },
390 { 196, 255, 255 },
391 }
392}
393
394local ranges = {
395
396
397
398}
399
400function codeinjections.registerfiguremask(name,specification)
401 masks[name] = specification
402end
403
404function codeinjections.registerfigurerange(name,specification)
405 ranges[name] = specification
406end
407
408function codeinjections.setfiguremask(data,figure)
409 local request = data.request
410 local mask = request.mask
411 local range = request.range
412 if mask and mask ~= "" then
413 local m = masks[mask]
414 if m then
415 if type(m) == "function" then
416 m = m()
417 end
418 figure.newmask = m
419 else
420 figures.push {
421 name = mask,
422 page = request.page,
423 size = request.size,
424 prefix = request.prefix,
425 cache = request.cache,
426 width = request.width,
427 height = request.height,
428 }
429 mask = figures.identify()
430 mask = figures.check(mask)
431 if mask then
432 local image = mask.status.private
433 if image then
434 figures.include(mask)
435 embedimage(image)
436 local d = pdfdictionary {
437 Interpolate = false,
438 SMask = pdfreference(mask.status.objectnumber),
439 }
440 figure.attr = d()
441 end
442 end
443 figures.pop()
444 end
445 end
446 if range and range ~= "" then
447 local r = ranges[range]
448 if not r then
449 r = tonumber(range)
450 end
451 if type(r) == "function" then
452 r = r()
453 end
454 figure.newranges = r
455 end
456end
457
458
459
460local saveboxresource = tex.boxresources.save
461local nofpatterns = 0
462local f_pattern = formatters["q /Pattern cs /%s scn 0 0 %.6N %.6N re f Q"]
463
464function lpdf.registerpattern(specification)
465 nofpatterns = nofpatterns + 1
466 local d = pdfdictionary {
467 Type = pdfconstant("Pattern"),
468 PatternType = 1,
469 PaintType = 1,
470 TilingType = 2,
471 XStep = (specification.width or 10) * basepoints,
472 YStep = (specification.height or 10) * basepoints,
473 Matrix = {
474 1, 0, 0, 1,
475 (specification.hoffset or 0) * basepoints,
476 (specification.voffset or 0) * basepoints,
477 },
478 }
479
480
481 local resources = lpdf.collectedresources{ patterns = false, serialize = false }
482 local attributes = d
483 local onlybounds = 1
484 local patternobj = saveboxresource(specification.number,attributes,resources,true,onlybounds)
485 lpdf.adddocumentpattern("Pt" .. nofpatterns,lpdf.reference(patternobj ))
486 return nofpatterns
487end
488
489function lpdf.patternstream(n,width,height)
490 return f_pattern("Pt" .. n,width*basepoints,height*basepoints)
491end
492
493codeinjections.registerpattern = lpdf.registerpattern
494 |