mtx-fixpdf.lua /size: 7552 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['mtx-fixpdf'] = {
2    version   = 1.002,
3    comment   = "companion to mtxrun.lua",
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 find, lower = string.find, string.lower
10local formatters = string.formatters
11local nameonly, suffix = file.nameonly, file.suffix
12
13local helpinfo = [[
14<?xml version="1.0"?>
15<application>
16 <metadata>
17  <entry name="name">mtx-tools</entry>
18  <entry name="detail">Some File Related Goodies</entry>
19  <entry name="version">1.01</entry>
20 </metadata>
21 <flags>
22  <category name="basic">
23   <subcategory>
24    <flag name="uncompress"><short>uncompress using qpdf</short></flag>
25    <flag name="validate"><short>validate using verapdf</short></flag>
26    <flag name="check"><short>check verification result </short></flag>
27    <flag name="convert"><short>convert using context</short></flag>
28    <flag name="compare"><short>compare result with original</short></flag>
29    <flag name="compactor"><short>use given compactor</short></flag>
30    <flag name="standard"><short>use given standard</short></flag>
31   </subcategory>
32   <subcategory>
33    <flag name="pattern"><short>processs files according to pattern</short></flag>
34    <flag name="silent"><short>suppress messages</short></flag>
35    <flag name="once"><short>run only once</short></flag>
36    <flag name="resolution"><short>use this resolution when comparing</short></flag>
37   </subcategory>
38  </category>
39 </flags>
40</application>
41]]
42
43local application = logs.application {
44    name     = "mtx-fixpdfs",
45    banner   = "Making PDF files more compliant",
46    helpinfo = helpinfo,
47}
48
49local report  = application.report
50local writeln = (logs and logs.writer) or (texio and texio.write_nl) or print
51
52scripts        = scripts        or { }
53scripts.fixpdf = scripts.fixpdf or { }
54
55local function getfiles()
56    local pattern = environment.arguments.pattern
57    if pattern then
58        return dir.glob(pattern) or { }
59    else
60        return environment.files
61    end
62end
63
64local function validfile(filename)
65    name = nameonly(filename)
66    return suffix(filename) == "pdf" and not find(name,"%-out$") and not find(name,"%-uncompressed$") and name or false
67end
68
69function scripts.fixpdf.uncompress()
70    local files = environment.files
71    for i=1,#files do
72        local name = validfile(files[i])
73        if name then
74            local command = formatters["qpdf --stream-data=uncompress --object-streams=disable %s.pdf %s-uncompressed.pdf"](name,name)
75            report("uncompressing %s.pdf -> %s-uncompressed.pdf",name,name)
76            os.execute(command)
77        end
78    end
79end
80
81function scripts.fixpdf.validate()
82    local files = getfiles()
83    for i=1,#files do
84        local name = validfile(files[i])
85        if name then
86            local command = formatters["verapdf %s-out.pdf > %s-out.txt"](name,name)
87            report("validating %s-out.pdf -> %s-out.txt",name,name)
88            os.execute(command)
89        end
90    end
91end
92
93function scripts.fixpdf.check()
94    local files = getfiles()
95    for i=1,#files do
96        local name = validfile(files[i])
97        if name then
98            local data = io.loaddata(formatters["%s-out.txt"](name))
99            local atad = data and lower(data)
100            local t = { }
101            local compliant = atad and find(atad,"pdf file is compliant with validation profile requirements")
102            local hasmasks  = atad and find(atad,"smask")
103            local fontissue = atad and find(atad,"errormessage.*font")
104            if compliant then
105                t = { "okay" }
106            else
107                if fontissue then
108                    t[#t+1] = "font"
109                end
110                if hasmasks then
111                    t[#t+1] = "mask"
112                end
113            end
114            if #t == 0 then
115                t = { "issue" }
116            end
117            report("checking %s-out.pdf : % t",name,t)
118        end
119    end
120end
121
122function scripts.fixpdf.convert()
123    local files         = getfiles()
124    local compactor     = environment.arguments.compactor
125    local standard      = environment.arguments.standard
126    local silent        = environment.arguments.silent and "--batch --silent" or ""
127    local once          = environment.arguments.silent and "--once " or ""
128    local nocompression = environment.arguments.nocompression and "--nocompression" or ""
129    local extrastyle    = environment.arguments.extrastyle or ""
130    if type(compactor) ~= "string" then
131        compactor = "yes"
132    end
133    if type(standard) ~= "string" then
134        standard = "PDF/A-1a:2005"
135    end
136    for i=1,#files do
137        local name = validfile(files[i])
138        if name then
139            -- "--batch --silent --once" can speed up if needed
140            local command = formatters["context --global s-pdf-fix.mkxl --compactor=%s --extrastyle=%s --standard=%s %s %s %s --pdffile=%s.pdf --result=%s-out"](compactor,extrastyle,standard,once,silent,nocompression,name,name)
141            report("converting %s.pdf -> %s-out.pdf",name,name)
142            os.execute(command)
143        end
144    end
145end
146
147function scripts.fixpdf.compare()
148    local files      = getfiles()
149    local resolution = tonumber(environment.arguments.resolution) or 72
150    local tempdir    = "temp-compare"
151    local workdir = lfs.currentdir()
152    dir.makedirs(tempdir)
153    if lfs.chdir(tempdir) then
154        for i=1,#files do
155            local name = validfile(files[i])
156            if name then
157                local files = dir.glob("*.png")
158                for i=1,#files do
159                    os.remove(files[i])
160                end
161                print(oldname)
162                local command = string.format('mutool draw -c gray -o temp-old-%%d.png -r %i -A 0/0 -N ../%s.pdf',resolution,name)
163                print(command)
164                os.execute(command)
165                print(newname)
166                local command = string.format('mutool draw -c gray -o temp-new-%%d.png -r %i -A 0/0 -N ../%s-out.pdf',resolution,name)
167                print(command)
168                os.execute(command)
169                local files = dir.glob(string.format("temp-new-*.png",newpngname))
170                for i=1,#files do
171                    local command = string.format('gm compare -colorspace gray -highlight-color red -file temp-diff-%i.png temp-old-%i.png temp-new-%i.png',i,i,i)
172                    print(command)
173                    os.execute(command)
174                end
175                os.remove("context-extra.pdf")
176                command = string.format('context --extra=convert --pattern="temp-diff-*.png" --once --purgeall --result=%s-compare',name)
177                print(command)
178                os.execute(command)
179                local files = dir.glob("*.png")
180                for i=1,#files do
181                    os.remove(files[i])
182                end
183                -- we keep the file on temp-compare
184            end
185        end
186    end
187end
188
189if environment.argument("uncompress") then
190    scripts.fixpdf.uncompress()
191elseif environment.argument("validate") then
192    scripts.fixpdf.validate()
193elseif environment.argument("check") then
194    scripts.fixpdf.check()
195elseif environment.argument("convert") then
196    scripts.fixpdf.convert()
197elseif environment.argument("compare") then
198    scripts.fixpdf.compare()
199elseif environment.argument("exporthelp") then
200    application.export(environment.argument("exporthelp"),environment.files[1])
201else
202    application.help()
203end
204