lpdf-grp.lua /size: 9775 b    last modification: 2021-10-28 13:50
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 concat = table.concat
12local round = math.round
13
14local backends, lpdf = backends, lpdf
15
16local nodeinjections = backends.pdf.nodeinjections
17
18local colors         = attributes.colors
19local basepoints     = number.dimenfactors.bp
20
21local nodeinjections = backends.pdf.nodeinjections
22local codeinjections = backends.pdf.codeinjections
23local registrations  = backends.pdf.registrations
24
25local pdfdictionary  = lpdf.dictionary
26local pdfarray       = lpdf.array
27local pdfconstant    = lpdf.constant
28local pdfboolean     = lpdf.boolean
29local pdfreference   = lpdf.reference
30local pdfflushobject = lpdf.flushobject
31
32local createimage    = images.create
33local wrapimage      = images.wrap
34local embedimage     = images.embed
35
36-- can also be done indirectly:
37--
38-- 12 : << /AntiAlias false /ColorSpace  8 0 R /Coords [ 0.0 0.0 1.0 0.0 ] /Domain [ 0.0 1.0 ] /Extend [ true true ] /Function 22 0 R /ShadingType 2 >>
39-- 22 : << /Bounds [ ] /Domain [ 0.0 1.0 ] /Encode [ 0.0 1.0 ] /FunctionType 3 /Functions [ 31 0 R ] >>
40-- 31 : << /C0 [ 1.0 0.0 ] /C1 [ 0.0 1.0 ] /Domain [ 0.0 1.0 ] /FunctionType 2 /N 1.0 >>
41
42local function shade(stype,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
43    local func = nil
44    --
45    -- domain has to be consistently added in all dictionaries here otherwise
46    -- acrobat fails with a drawing error
47    --
48    domain = pdfarray(domain)
49    n      = tonumber(n)
50    --
51    if steps then
52        local list   = pdfarray()
53        local bounds = pdfarray()
54        local encode = pdfarray()
55        for i=1,steps do
56            if i < steps then
57                bounds[i] = fractions[i] or 1
58            end
59            encode[2*i-1] = 0
60            encode[2*i]   = 1
61            list  [i]     = pdfdictionary {
62                FunctionType = 2,
63                Domain       = domain,
64                C0           = pdfarray(color_a[i]),
65                C1           = pdfarray(color_b[i]),
66                N            = n,
67            }
68        end
69        func = pdfdictionary {
70            FunctionType = 3,
71            Bounds       = bounds,
72            Encode       = encode,
73            Functions    = list,
74            Domain       = domain,
75        }
76    else
77        func = pdfdictionary {
78            FunctionType = 2,
79            Domain       = domain,
80            C0           = pdfarray(color_a),
81            C1           = pdfarray(color_b),
82            N            = n,
83        }
84    end
85    separation = separation and registrations.getspotcolorreference(separation)
86    local s = pdfdictionary {
87        ShadingType = stype,
88        ColorSpace  = separation and pdfreference(separation) or pdfconstant(colorspace),
89        Domain      = domain,
90        Function    = pdfreference(pdfflushobject(func)),
91        Coords      = pdfarray(coordinates),
92        Extend      = pdfarray { true, true },
93        AntiAlias   = pdfboolean(true),
94    }
95    lpdf.adddocumentshade(name,pdfreference(pdfflushobject(s)))
96end
97
98function lpdf.circularshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
99    shade(3,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
100end
101
102function lpdf.linearshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
103    shade(2,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
104end
105
106-- inline bitmaps but xform'd
107--
108-- we could derive the colorspace if we strip the data
109-- and divide by x*y
110
111local template = "q BI %s ID %s > EI Q"
112local factor   = 72/300
113
114function nodeinjections.injectbitmap(t)
115    -- encoding is ascii hex, no checking here
116    local xresolution, yresolution = t.xresolution or 0, t.yresolution or 0
117    if xresolution == 0 or yresolution == 0 then
118        return -- fatal error
119    end
120    local colorspace = t.colorspace
121    if colorspace ~= "rgb" and colorspace ~= "cmyk" and colorspace ~= "gray" then
122        -- not that efficient but ok
123        local d = gsub(t.data,"[^0-9a-f]","")
124        local b = math.round(#d / (xresolution * yresolution))
125        if b == 2 then
126            colorspace = "gray"
127        elseif b == 6 then
128            colorspace = "rgb"
129        elseif b == 8 then
130            colorspace = "cmyk"
131        end
132    end
133    colorspace = lpdf.colorspaceconstants[colorspace]
134    if not colorspace then
135        return -- fatal error
136    end
137    local d = pdfdictionary {
138        W   = xresolution,
139        H   = yresolution,
140        CS  = colorspace,
141        BPC = 8,
142        F   = pdfconstant("AHx"),
143     -- CS  = nil,
144     -- BPC = 1,
145     -- IM = true,
146    }
147    -- for some reasons it only works well if we take a 1bp boundingbox
148    local urx, ury = 1/basepoints, 1/basepoints
149 -- urx = (xresolution/300)/basepoints
150 -- ury = (yresolution/300)/basepoints
151    local width, height = t.width or 0, t.height or 0
152    if width == 0 and height == 0 then
153        width  = factor * xresolution / basepoints
154        height = factor * yresolution / basepoints
155    elseif width == 0 then
156        width  = height * xresolution / yresolution
157    elseif height == 0 then
158        height = width  * yresolution / xresolution
159    end
160    local a = pdfdictionary {
161        BBox = pdfarray { 0, 0, urx * basepoints, ury * basepoints }
162    }
163    local image = createimage {
164        stream = formatters[template](d(),t.data),
165        width  = width,
166        height = height,
167        bbox   = { 0, 0, urx, ury },
168        attr   = a(),
169        nobbox = true,
170    }
171    return wrapimage(image)
172end
173
174-- general graphic helpers
175
176function codeinjections.setfigurealternative(data,figure)
177    local request = data.request
178    local display = request.display
179    if display and display ~= ""  then
180        local nested = figures.push {
181            name   = display,
182            page   = request.page,
183            size   = request.size,
184            prefix = request.prefix,
185            cache  = request.cache,
186            width  = request.width,
187            height = request.height,
188        }
189        figures.identify()
190        local displayfigure = figures.check()
191        if displayfigure then
192        --  figure.aform = true
193            embedimage(figure)
194            local a = pdfarray {
195                pdfdictionary {
196                    Image              = pdfreference(figure.objnum),
197                    DefaultForPrinting = true,
198                }
199            }
200            local d = pdfdictionary {
201                Alternates = pdfreference(pdfflushobject(a)),
202            }
203            displayfigure.attr = d()
204            figures.pop()
205            return displayfigure, nested
206        else
207            figures.pop()
208        end
209    end
210end
211
212function codeinjections.getpreviewfigure(request)
213    local figure = figures.initialize(request)
214    if not figure then
215        return
216    end
217    figure = figures.identify(figure)
218    if not (figure and figure.status and figure.status.fullname) then
219        return
220    end
221    figure = figures.check(figure)
222    if not (figure and figure.status and figure.status.fullname) then
223        return
224    end
225    local image = figure.status.private
226    if image then
227        embedimage(image)
228    end
229    return figure
230end
231
232function codeinjections.setfiguremask(data,figure) -- mark
233    local request = data.request
234    local mask    = request.mask
235    if mask and mask ~= ""  then
236        figures.push {
237            name   = mask,
238            page   = request.page,
239            size   = request.size,
240            prefix = request.prefix,
241            cache  = request.cache,
242            width  = request.width,
243            height = request.height,
244        }
245        mask = figures.identify()
246        mask = figures.check(mask)
247        if mask then
248            local image = mask.status.private
249            if image then
250                figures.include(mask)
251                embedimage(image)
252                local d = pdfdictionary {
253                    Interpolate  = false,
254                    SMask        = pdfreference(mask.status.objectnumber),
255                }
256                figure.attr = d()
257            end
258        end
259        figures.pop()
260    end
261end
262
263-- experimental (q Q is not really needed)
264
265local saveboxresource = tex.boxresources.save
266local nofpatterns     = 0
267local f_pattern       = formatters["q /Pattern cs /%s scn 0 0 %.6N %.6N re f Q"]
268
269function lpdf.registerpattern(specification)
270    nofpatterns = nofpatterns + 1
271    local d = pdfdictionary {
272        Type        = pdfconstant("Pattern"),
273        PatternType = 1,
274        PaintType   = 1,
275        TilingType  = 2,
276        XStep       = (specification.width  or 10) * basepoints,
277        YStep       = (specification.height or 10) * basepoints,
278        Matrix      = {
279            1, 0, 0, 1,
280            (specification.hoffset or 0) * basepoints,
281            (specification.voffset or 0) * basepoints,
282        },
283    }
284
285    local resources  = lpdf.collectedresources{ patterns = false }
286    local attributes = d()
287    local onlybounds = 1
288    local patternobj = saveboxresource(specification.number,attributes,resources,true,onlybounds)
289    lpdf.adddocumentpattern("Pt" .. nofpatterns,lpdf.reference(patternobj ))
290    return nofpatterns
291end
292
293function lpdf.patternstream(n,width,height)
294    return f_pattern("Pt" .. n,width*basepoints,height*basepoints)
295end
296
297backends.pdf.codeinjections.registerpattern = lpdf.registerpattern
298