1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
#!/usr/bin/env texlua
local options = {
pdfoutput=true,
redoall=false,
multipicfile=false,
noborder=false,
dotypesetting=true,
nopostprocess=false,
}
local usagetext = 'Usage: cachepic [options] latexfile'
local tryhelp = "Try `cachepic --help' for more information"
local helptext = [[
Options start with '-' or '--' and can be any of:
-pdf output in (e)pdf format (default)
-eps output in eps format
-all regenerate all graphics
-multi all graphics in one file (implies -pdf)
-tight no 0.5bp margin around the graphic
-notex no typesetting, only graphic postprocessing
-nopic generate no graphics, only .cachepic file
-usage display usage message
-help,-h,-? display help message]]
local msg = {
MultipleInputFiles = "cannot process multiple files:\t%s\t%s",
NoInputFile = "no input file given\n" .. tryhelp,
UnknownOption = "unknown option: %s\n" .. tryhelp,
FileOpenFailed = "could not open file: %s",
NoGhostScript = "could not locate ghostscript interpreter",
WrongOutputMode = "expected %s output but got %s",
NoPreviews = "no cachepic previews found",
MultiPicOnlyInPDF = "option '-multi' for all graphics in one file supported only in pdf mode"
}
function die(msgfmt, ...)
error(string.format(msgfmt, ...))
end
function warn(msgfmt, ...)
print('warning: ' .. string.format(msgfmt, ...))
end
local function qfile(filename)
filename = string.gsub(filename, '"', '')
if string.find(filename, "[%s%&%(%)%[%]%{%}%^%=%;%!%'%+%,%`%~]") then
filename = '"' .. filename .. '"'
end
return filename
end
local function syscall(command)
print('system: ' .. command)
local ret = os.execute(command)
if ret > 0 then os.exit(ret)
else return ret end
end
local function copyfile(src, dst)
print('copy ' .. qfile(src) .. ' ' .. qfile(dst))
local buffer, bufsize = '', 1024
local fin = io.open(src, 'rb')
if not fin then die(msg.FileOpenFailed, src) end
local fout = io.open(dst, 'wb')
if not fout then die(msg.FileOpenFailed, dst) end
while buffer do
fout:write(buffer)
buffer = fin:read(bufsize)
end
fin:close()
fout:close()
end
local isfile = lfs and lfs.isfile or function (fname)
-- if we are not running under texlua, then do it the hard way
local fh = io.open(fname)
return (fh and (fh:close() or true) or false)
end
local function whichfile(fname, path)
if isfile(fname) then return fname end
-- make the best guess if it is Windows or Unix
local dirsep, pathsep = '/', ':'
if (os.type == 'windows') or string.find(path, '\\') or string.find(path, ';') then
dirsep, pathsep = '\\', ';' -- Windows
end
-- iterate path
for dir in string.gmatch(path, '[^' .. pathsep .. ']+') do
local f = dir .. dirsep .. fname
-- print(f)
if isfile(f) then return f end
end
return nil
end
local function findgs()
local path = os.getenv('PATH')
for _, fname in ipairs{'gs', 'gswin32c.exe', 'mgs.exe'} do
if whichfile(fname, path) then return fname end
end
die(msg.NoGhostScript)
end
-- Process options --
if #arg == 0 then
print(usagetext)
print(tryhelp)
os.exit(0)
end
local filename = nil
for _, argn in ipairs(arg) do
local opt, num = string.gsub(argn, '^%-%-?', '')
if num == 0 then -- not an option
if filename then die(msg.MultipleInputFiles, filename, opt)
else filename = opt end
elseif opt == 'eps' then options.pdfoutput = false
elseif opt == 'pdf' then options.pdfoutput = true
elseif opt == 'all' then options.redoall = true
elseif opt == 'multi' then options.multipicfile = true
elseif opt == 'tight' then options.noborder = true
elseif opt == 'notex' then options.dotypesetting = false
elseif opt == 'nopic' then options.nopostprocess = true
elseif (opt == 'usage') or (opt == 'help') or (opt == 'h') or (opt == '?') then
print(usagetext)
if opt == 'usage' then os.exit(0) end
print(helptext)
os.exit(0)
else die(msg.UnknownOption, argn) end
end
if not filename then
die(msg.NoInputFile)
end
if options.multipicfile and not options.pdfoutput then
warn(msg.MultiPicOnlyInPDF)
options.multipicfile = false
end
filename = string.gsub(filename, '\\', '/')
local noextname = string.gsub(filename, '%.[^./]*$', '')
local jobname = string.match(noextname, '[^/]+$')
local jobsuffix = (options.dotypesetting and '_cptmp' or '')
local multipicfilename = noextname .. '-cachepic.pdf'
-- Typeset document --
if options.dotypesetting then
local engine = (options.pdfoutput and 'pdflatex' or 'latex')
local psfixbb = (options.pdfoutput and '' or ',psfixbb')
local noborder = (options.noborder and ' \\PreviewBorder=0pt' or '')
local redoall = (options.redoall and ' \\makeatletter\\def\\cachepic@tryincludepic#1#2{\\xdef\\cachepic@name{#1}#2}\\makeatother' or '')
local command = engine .. ' -interaction=nonstopmode -jobname=' .. jobname .. jobsuffix ..
' \\def\\jobname{' .. jobname .. '}' ..
' \\RequirePackage[active,tightpage,delayed,cachepic' .. psfixbb .. ']{preview}' ..
noborder .. redoall .. ' \\input{' .. qfile(filename) .. '}'
syscall(command)
end
-- Process log file --
local iLines = io.lines(noextname .. jobsuffix .. '.log')
local pics = {}
local pdfmode = options.pdfoutput
local trim = '0 0 0 0'
for line in iLines do
local head, c1, c2, c3, c4, c5 = string.match(line,
'^Preview:%s*([A-Za-z]+)%s*(%-?%d*)%s*(%-?%d*)%s*(%-?%d*)%s*(%-?%d*)%s*(.*)')
if head == 'PDFoutput' then
pdfmode = (tonumber(c1) ~= 0)
elseif (head == 'Tightpage') and c1 and c2 and c3 and c4 then
trim = string.format('%dsp %dsp %dsp %dsp',
-tonumber(c1), -tonumber(c2), tonumber(c3), tonumber(c4))
elseif (head == 'CachePic') and c1 and c2 and c3 and c4 and c5 then
local picname = c5
local picpage = tonumber(c1)
local baseline = tonumber(c4) or 0
local texmacro = string.format('\\CachePicDefMacro{%s}{%d}{trim=%s}{%dsp}',
picname, picpage, trim, -baseline)
pics[#pics+1] = {name=picname, page=picpage, macro=texmacro}
end
end
if #pics == 0 then
warn(msg.NoPreviews)
return
end
if pdfmode ~= options.pdfoutput then
local expectedmode = options.pdfoutput and 'pdf' or 'dvi'
local actualmode = pdfmode and 'pdf' or 'dvi'
die(msg.WrongOutputMode, expectedmode, actualmode)
end
-- Write out cachepic macros --
io.output(noextname .. '.cachepic')
for i = 1, #pics do
io.write(pics[i].macro, '\n')
end
io.close()
if options.nopostprocess then return end
-- Postprocess pics --
if options.multipicfile then
if options.pdfoutput then
-- without option -all the file size will slowly grow
-- extra run through GhostScript seems to prevent that
-- but it degrades raster graphics or makes them much larger
-- no perfect solution it seems
copyfile(noextname .. jobsuffix .. '.pdf', multipicfilename)
else die(msg.MultiPicOnlyInPDF) end -- this should never happen
else
local ext, cmdtmpl
if options.pdfoutput then
ext = '.pdf'
filename = qfile(noextname .. jobsuffix .. '.pdf')
cmdtmpl = findgs() ..
' -sDEVICE=pdfwrite -dQUIET -dBATCH -dNOPAUSE -dCompatibilityLevel=1.4' ..
' -dFirstPage=%s -dLastPage=%s -sOutputFile=%s %s'
else
ext = '.eps'
filename = qfile(noextname .. jobsuffix .. '.dvi')
cmdtmpl = 'dvips -E -p %s -l %s -o %s %s'
end
for i = 1, #pics do
local outfile = string.gsub(pics[i].name, '"', '') .. ext
if not isfile(outfile) or options.redoall then
outfile = qfile(outfile)
syscall(string.format(cmdtmpl, pics[i].page, pics[i].page, outfile, filename))
end
end
end
--[[-- GS options preventing lossy image recompression
' -dMonoImageFilter=/CCITTFaxEncode' ..
' -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode' ..
' -dAutoFilterGrayImages=false -dGrayImageFilter=/FlateEncode' ..
]]
|