grph-chk.lua /size: 10 Kb    last modification: 2025-02-21 11:03
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 page  = request.page
63                local label = request.pagelabel
64                local info  = querypdf(pdfdoc,page,request.size,label)
65                if info then
66                    -- in case we have resolved a page label
67                    local foundpage = info.pagenumber
68                    if foundpage and foundpage ~= page then
69                        if trace_pdf then
70                            report_pdf("page label %a resolved to page %i in image %a",label,foundpage,filename)
71                        end
72                    else
73                        foundpage = page
74                    end
75                    request.page = foundpage
76                    --
77                    local bbox     = info and info.boundingbox or { 0, 0, 0, 0 }
78                    local height   = bbox[4] - bbox[2]
79                    local width    = bbox[3] - bbox[1]
80                    local rotation = info.rotation or 0
81                    if rotation == 90 then
82                        rotation, height, width = 3, width, height
83                    elseif rotation == 180 then
84                        rotation = 2
85                    elseif rotation == 270 then
86                        rotation, height, width = 1, width, height
87                    elseif rotation == 1 or rotation == 3 then
88                        height, width = width, height
89                    else
90                        rotation = 0
91                    end
92                    return {
93                        filename   = filename,
94                     -- page       = 1,
95                        page       = foundpage,
96                        pages      = pdfdoc.nofpages,
97                        width      = width,
98                        height     = height,
99                        depth      = 0,
100                        colordepth = 0,
101                        xres       = 0,
102                        yres       = 0,
103                        xsize      = width,
104                        ysize      = height,
105                        rotation   = rotation,
106                        pdfdoc     = pdfdoc,
107                    }
108                end
109            end
110        end
111        request.copyimage = function(t)
112            if not pdfdoc then
113                pdfdoc = t.pdfdoc
114            end
115            if pdfdoc then
116                local page   = request.page
117                local copied = pdfdoc.nofcopied or 0
118                if not pdfdoc.copied[page] then
119                    pdfdoc.copied[page] = true
120                    copied = copied + 1
121                end
122                pdfdoc.nofcopied = copied
123                if trace_pdf then
124                    report_pdf("copy page %i from image %a, %i pages copied",page,filename,copied)
125                end
126                local result = copypage(pdfdoc,page,nil,request.compact,request.width,request.height,request.attr,request.metadata)
127                if pdfdoc.nofcopied >= pdfdoc.nofpages then
128                    if trace_pdf then
129                        report_pdf("closing image %a, %i pages copied",filename,copied)
130                    end
131                    closepdf(pdfdoc)
132                    pdfdoc = nil
133                    t.pdfdoc = nil
134                end
135                return result
136            else
137                -- message, should not happen as we always first scan so that reopens
138            end
139        end
140    end
141    return genericchecker(data)
142end
143
144local function wrappedidentify(identify,filename,filetype)
145    local wrapup    = function() report_inclusion("fatal error reading %a",filename) end
146    local _, result = xpcall(identify,wrapup,filename,filetype)
147    if result then
148        local xsize = result.xsize or 0
149        local ysize = result.ysize or 0
150        local xres  = result.xres or 0
151        local yres  = result.yres or 0
152        if xres == 0 or yres == 0 then
153            xres = 300
154            yres = 300
155        end
156        result.xsize       = xsize
157        result.ysize       = ysize
158        result.xres        = xres
159        result.yres        = yres
160        result.width       = result.width  or ((72/xres) * xsize / bpfactor)
161        result.height      = result.height or ((72/yres) * ysize / bpfactor)
162        result.depth       = result.depth  or 0
163        result.filename    = filename
164        result.colordepth  = result.colordepth or 0
165        result.colorspace  = result.colorspace or 0
166        result.rotation    = result.rotation or 0
167        result.orientation = result.orientation or 0
168        result.transform   = result.transform or 0
169        return result
170    else
171        return { error = "fatal error" }
172    end
173end
174
175function checkers.jpg(data)
176    local request = data.request
177    local used    = data.used
178    if request and used and not request.scanimage then
179        local identify = graphics.identify
180        local found    = false
181        request.scanimage = function(t)
182            local result = wrappedidentify(identify,t.filename,"jpg")
183            found = not result.error
184            return {
185                filename    = result.filename,
186                width       = result.width,
187                height      = result.height,
188                depth       = result.depth,
189                colordepth  = result.colordepth,
190                xres        = result.xres,
191                yres        = result.yres,
192                xsize       = result.xsize,
193                ysize       = result.ysize,
194                colorspace  = result.colorspace,
195                rotation    = result.rotation,
196                orientation = result.orientation,
197                transform   = result.transform,
198            }
199        end
200        request.copyimage = function(t)
201            if found then
202                found = false
203                return backends.codeinjections.jpg(t)
204            end
205        end
206    end
207    return genericchecker(data)
208end
209
210function checkers.jp2(data) -- idem as jpg
211    local request = data.request
212    local used    = data.used
213    if request and used and not request.scanimage then
214        local identify = graphics.identify
215        local found    = false
216        request.scanimage = function(t)
217            local result = wrappedidentify(identify,t.filename,"jp2")
218            found = not result.error
219            return {
220                filename    = result.filename,
221                width       = result.width,
222                height      = result.height,
223                depth       = result.depth,
224                colordepth  = result.colordepth,
225                xres        = result.xres,
226                yres        = result.yres,
227                xsize       = result.xsize,
228                ysize       = result.ysize,
229                rotation    = result.rotation,
230                colorspace  = result.colorspace,
231                orientation = result.orientation,
232                transform   = result.transform,
233            }
234        end
235        request.copyimage = function(t)
236            if found then
237                found = false
238                return backends.codeinjections.jp2(t)
239            end
240        end
241    end
242    return genericchecker(data)
243end
244
245function checkers.png(data) -- same as jpg (for now)
246    local request = data.request
247    local used    = data.used
248    if request and used and not request.scanimage then
249        local identify = graphics.identify
250        local found    = false
251        request.scanimage = function(t)
252            local result = wrappedidentify(identify,t.filename,"png")
253            found = not result.error
254            return {
255                filename    = result.filename,
256                width       = result.width,
257                height      = result.height,
258                depth       = result.depth,
259                colordepth  = result.colordepth,
260                xres        = result.xres,
261                yres        = result.yres,
262                xsize       = result.xsize,
263                ysize       = result.ysize,
264                rotation    = result.rotation,
265                colorspace  = result.colorspace,
266                tables      = result.tables,
267                interlace   = result.interlace,
268                filter      = result.filter,
269                orientation = result.orientation,
270                transform   = result.transform,
271            }
272        end
273        request.copyimage = function(t)
274            t.colorref = used.colorref -- this is a bit of a hack
275            if found then
276                found = false
277                local ok, result = pcall(backends.codeinjections.png,t)
278                if ok then
279                    return result
280                else
281                    report_inclusion("bad bitmap image")
282                    return placeholder()
283                end
284            end
285        end
286    end
287    return genericchecker(data)
288end
289