grph-chk.lua /size: 10 Kb    last modification: 2024-01-16 09:02
1if not modules then modules = { } end modules ['grph-inc'] = {
2    version   = 1.001,
3    comment   = "companion to grph-inc.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 xpcall, pcall = xpcall, pcall
10
11local bpfactor          = number.dimenfactors.bp
12
13local report            = logs.reporter("graphics")
14local report_inclusion  = logs.reporter("graphics","inclusion")
15local report_bitmap     = logs.reporter("graphics","bitmap")
16local report_pdf        = logs.reporter("graphics","pdf")
17
18local trace_pdf         = false  trackers.register("graphics.pdf", function(v) trace_pdf = v end)
19
20local checkers          = figures.checkers
21local genericchecker    = checkers.generic
22
23local placeholder       = graphics.bitmaps.placeholder
24
25-- This is an experiment. The following method uses Lua to handle the embedding
26-- using the epdf library. This feature will be used when we make the transition
27-- from the pre 1.10 epdf library (using an unsuported low level poppler api) to a
28-- new (lightweight, small and statically compiled) library. More on that later.
29--
30-- The method implemented below has the same performance as the hard coded inclusion
31-- but opens up some possibilities (like merging fonts) that I will look into some
32-- day.
33
34function checkers.pdf(data)
35    local request = data.request
36    local used    = data.used
37    if request and used and not request.scanimage then
38        local image    = lpdf.epdf.image -- ok, this is a pdf specific main function
39        local openpdf  = image.open
40        local closepdf = image.close
41        local querypdf = image.query
42        local copypage = image.copy
43        local pdfdoc   = nil
44        local filename = nil
45        request.scanimage = function(t)
46            if pdfdoc then
47                if trace_pdf then
48                    report_pdf("scan image %a",filename)
49                end
50                if not filename then
51                    filename = pdfdoc.filename
52                end
53            else
54                filename = t.filename
55                if trace_pdf then
56                    report_pdf("open and scan image %a",filename)
57                end
58                pdfdoc = openpdf(filename,request.userpassword,request.ownerpassword)
59            end
60            if pdfdoc then
61                --
62                local info = querypdf(pdfdoc,request.page,request.size)
63                if info then
64                    local bbox     = info and info.boundingbox or { 0, 0, 0, 0 }
65                    local height   = bbox[4] - bbox[2]
66                    local width    = bbox[3] - bbox[1]
67                    local rotation = info.rotation or 0
68                    if rotation == 90 then
69                        rotation, height, width = 3, width, height
70                    elseif rotation == 180 then
71                        rotation = 2
72                    elseif rotation == 270 then
73                        rotation, height, width = 1, width, height
74                    elseif rotation == 1 or rotation == 3 then
75                        height, width = width, height
76                    else
77                        rotation = 0
78                    end
79                    return {
80                        filename   = filename,
81                     -- page       = 1,
82                        pages      = pdfdoc.nofpages,
83                        width      = width,
84                        height     = height,
85                        depth      = 0,
86                        colordepth = 0,
87                        xres       = 0,
88                        yres       = 0,
89                        xsize      = width,
90                        ysize      = height,
91                        rotation   = rotation,
92                        pdfdoc     = pdfdoc,
93                    }
94                end
95            end
96        end
97        request.copyimage = function(t)
98            if not pdfdoc then
99                pdfdoc = t.pdfdoc
100            end
101            if pdfdoc then
102                local page   = request.page
103                local copied = pdfdoc.nofcopied or 0
104                if not pdfdoc.copied[page] then
105                    pdfdoc.copied[page] = true
106                    copied = copied + 1
107                end
108                pdfdoc.nofcopied = copied
109                if trace_pdf then
110                    report_pdf("copy page %i from image %a, %i pages copied",page,filename,copied)
111                end
112                local result = copypage(pdfdoc,page,nil,request.compact,request.width,request.height,request.attr,request.metadata)
113                if pdfdoc.nofcopied >= pdfdoc.nofpages then
114                    if trace_pdf then
115                        report_pdf("closing image %a, %i pages copied",filename,copied)
116                    end
117                    closepdf(pdfdoc)
118                    pdfdoc = nil
119                    t.pdfdoc = nil
120                end
121                return result
122            else
123                -- message, should not happen as we always first scan so that reopens
124            end
125        end
126    end
127    return genericchecker(data)
128end
129
130local function wrappedidentify(identify,filename,filetype)
131    local wrapup    = function() report_inclusion("fatal error reading %a",filename) end
132    local _, result = xpcall(identify,wrapup,filename,filetype)
133    if result then
134        local xsize = result.xsize or 0
135        local ysize = result.ysize or 0
136        local xres  = result.xres or 0
137        local yres  = result.yres or 0
138        if xres == 0 or yres == 0 then
139            xres = 300
140            yres = 300
141        end
142        result.xsize       = xsize
143        result.ysize       = ysize
144        result.xres        = xres
145        result.yres        = yres
146        result.width       = result.width  or ((72/xres) * xsize / bpfactor)
147        result.height      = result.height or ((72/yres) * ysize / bpfactor)
148        result.depth       = result.depth  or 0
149        result.filename    = filename
150        result.colordepth  = result.colordepth or 0
151        result.colorspace  = result.colorspace or 0
152        result.rotation    = result.rotation or 0
153        result.orientation = result.orientation or 0
154        result.transform   = result.transform or 0
155        return result
156    else
157        return { error = "fatal error" }
158    end
159end
160
161function checkers.jpg(data)
162    local request = data.request
163    local used    = data.used
164    if request and used and not request.scanimage then
165        local identify = graphics.identify
166        local found    = false
167        request.scanimage = function(t)
168            local result = wrappedidentify(identify,t.filename,"jpg")
169            found = not result.error
170            return {
171                filename    = result.filename,
172                width       = result.width,
173                height      = result.height,
174                depth       = result.depth,
175                colordepth  = result.colordepth,
176                xres        = result.xres,
177                yres        = result.yres,
178                xsize       = result.xsize,
179                ysize       = result.ysize,
180                colorspace  = result.colorspace,
181                rotation    = result.rotation,
182                orientation = result.orientation,
183                transform   = result.transform,
184            }
185        end
186        request.copyimage = function(t)
187            if found then
188                found = false
189                return backends.codeinjections.jpg(t)
190            end
191        end
192    end
193    return genericchecker(data)
194end
195
196function checkers.jp2(data) -- idem as jpg
197    local request = data.request
198    local used    = data.used
199    if request and used and not request.scanimage then
200        local identify = graphics.identify
201        local found    = false
202        request.scanimage = function(t)
203            local result = wrappedidentify(identify,t.filename,"jp2")
204            found = not result.error
205            return {
206                filename    = result.filename,
207                width       = result.width,
208                height      = result.height,
209                depth       = result.depth,
210                colordepth  = result.colordepth,
211                xres        = result.xres,
212                yres        = result.yres,
213                xsize       = result.xsize,
214                ysize       = result.ysize,
215                rotation    = result.rotation,
216                colorspace  = result.colorspace,
217                orientation = result.orientation,
218                transform   = result.transform,
219            }
220        end
221        request.copyimage = function(t)
222            if found then
223                found = false
224                return backends.codeinjections.jp2(t)
225            end
226        end
227    end
228    return genericchecker(data)
229end
230
231function checkers.png(data) -- same as jpg (for now)
232    local request = data.request
233    local used    = data.used
234    if request and used and not request.scanimage then
235        local identify = graphics.identify
236        local found    = false
237        request.scanimage = function(t)
238            local result = wrappedidentify(identify,t.filename,"png")
239            found = not result.error
240            return {
241                filename    = result.filename,
242                width       = result.width,
243                height      = result.height,
244                depth       = result.depth,
245                colordepth  = result.colordepth,
246                xres        = result.xres,
247                yres        = result.yres,
248                xsize       = result.xsize,
249                ysize       = result.ysize,
250                rotation    = result.rotation,
251                colorspace  = result.colorspace,
252                tables      = result.tables,
253                interlace   = result.interlace,
254                filter      = result.filter,
255                orientation = result.orientation,
256                transform   = result.transform,
257            }
258        end
259        request.copyimage = function(t)
260            t.colorref = used.colorref -- this is a bit of a hack
261            if found then
262                found = false
263                local ok, result = pcall(backends.codeinjections.png,t)
264                if ok then
265                    return result
266                else
267                    report_inclusion("bad bitmap image")
268                    return placeholder()
269                end
270            end
271        end
272    end
273    return genericchecker(data)
274end
275