| 12
 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' ..
]]
 |