summaryrefslogtreecommitdiff
path: root/Build
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2019-02-08 22:56:29 +0000
committerKarl Berry <karl@freefriends.org>2019-02-08 22:56:29 +0000
commitab1383854b03b3c08a7575e33c84d2bafb533642 (patch)
tree14ca4245c6d8ac0010819c128b10a6303556cb54 /Build
parent3031cbd848546746dd3545c69b200dd0756c87ff (diff)
lwarp (8feb19)
git-svn-id: svn://tug.org/texlive/trunk@49976 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build')
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/epspdf/epspdf.tlu1238
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl469
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/lwarp/lwarpmk.lua59
3 files changed, 734 insertions, 1032 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.tlu b/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.tlu
index 709a2d9b691..0007ade0742 100755
--- a/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.tlu
+++ b/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.tlu
@@ -2,17 +2,22 @@
kpse.set_program_name('texlua')
--- epspdf conversion utility
+--[[
+epspdf conversion utility
--- 0.6.0: first texlua version
--- 0.6.1: allow TeX installation on path with spaces
--- 0.6.2: compatibility fix for luatex 0.81
--- 0.6.3: compatibility fixes for luatex 0.9x
+0.6.0: first texlua version
+0.6.1: allow TeX installation on path with spaces
+0.6.2: compatibility fix for luatex 0.81
+0.6.3: compatibility fixes for luatex 0.9x
+0.6.4: adaptations for newer versions of LuaTeX and ghostscript;
+ some refactoring; better handling of some corner cases
-ep_version = '0.6.3'
-ep_copyright = '2006, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016'
+Copyright (C) 2006-2019 Siep Kroonenberg
---[[
+siepo at bitmuis nl
+
+This program is free software, licensed under the GNU GPL, >=2.0.
+This software comes with absolutely NO WARRANTY. Use at your own risk!
Note.
TeX code for cropping pdfs adapted from Heiko Oberdiek's pdfcrop utility
@@ -26,14 +31,15 @@ Program structure
- other general utilities
- infrastructure: logging and temporary files
- reading and writing settings
- - gui function
+ - gui function for communicating with frontend
- boundingboxes
- manipulating [e]ps- and pdf files
- the PsPdf object:
- creator functions
- boundingbox handling
- one-step conversion methods
- - any_to_any method
+- any_to_any function.
+ this function checks options, the one-step converters check for success.
- main initialization section:
- collecting system information
- infrastructure: setting up logging and temp directory
@@ -71,6 +77,9 @@ else
path_sep = ';'
end
+infile = false
+outfile = false
+
bufsize=16000 -- for reading and writing files
-- these `declarations' are not really needed;
@@ -84,8 +93,8 @@ cwd = ''
-- Actual values determined during system-dependent initialization
is_miktex = false
is_tl_w32 = false
--- recent versions of miktex include pdftops
-auto_psp = true -- configuration of pdftops_prog not needed or not allowed
+
+-- Luatex 1.09 and later replace epdf with pdfe
-- some global file- and directory names
gs_prog = false
@@ -111,7 +120,7 @@ gray_options = false
-- logging ------------------------
-- we open and close the logfile anew for each write.
--- failure to open constitutes no error.
+-- failure to open does not constitute an error.
function print_log(s)
local f = io.open(logfile, 'a')
@@ -141,6 +150,9 @@ end
-- When run from the Tcl/Tk gui, this gui will capture error messages.
function errror(mess)
+ if infile and outfile then
+ mess = 'Failure to convert '..infile..' to '..outfile..':\n'..mess
+ end
if logfile then pcall(write_log, mess) end
-- ignore result of pcall: we can do nothing about failure
error(mess, 2)
@@ -151,38 +163,24 @@ function warn(mess)
print(mess)
end
-function dbg(mess)
- if options.debug then
- warn(mess)
- end
-end
-
---[[
-
-function dbg_opt()
- if options.debug then
- local mess = ''
- for _, k in ipairs({'bbox', 'gray', 'page'}) do
- mess = mess.. ' ' .. k .. ': ' .. tostring(options.k)
- end
- dbg(mess)
- end
-end
-
---]]
+-- function dbg(mess)
+-- if options.debug then
+-- warn(mess)
+-- end
+-- end
-- file- and path utilities ----------------
-function ep_shortname(path)
- if os.type=='unix' then
- return path
- else
- -- shortname appears not to work under miktex
- -- so return original path as a fallback
- local sp = lfs.shortname(path)
- return sp or path
- end
-end
+-- function ep_shortname(path)
+-- if os.type=='unix' then
+-- return path
+-- else
+-- -- shortname appears not to work under miktex
+-- -- so return original path as a fallback
+-- local sp = lfs.shortname(path)
+-- return sp or path
+-- end
+-- end
-- prepend or append dir to path if necessary
function maybe_add_path(dir, append)
@@ -288,41 +286,22 @@ end -- absolute_path
-- we return the original string, although we only need a yes or no answer
function find_on_path (prog)
- if os.type ~= 'unix' then
- errror('find_on_path: this is a unix-only function')
- end
- for d in string.gmatch(os.getenv('PATH'), '[^:]+') do
- if lfs.isfile(d..'/'..prog) then
- return prog
+ if os.type == 'unix' then
+ for d in string.gmatch(os.getenv('PATH'), '[^:]+') do
+ if lfs.isfile(d..'/'..prog) then
+ return prog
+ end
+ end
+ else
+ for d in string.gmatch(os.getenv('PATH'), '[^;]+') do
+ if lfs.isfile(d..'\\'..prog) then
+ return prog
+ end
end
end
return false
end -- find_on_path
--- On Windows, we do not count so much on the existing searchpath
--- so is_prog tests whether the file exists and is an exe file.
-
-function is_prog (path)
- -- 1. test for and if necessary add extension
- -- 2. test for existence
- -- 3. returns either false or absolute path
- if os.type ~= 'windows' then
- errror('is_prog: this is a Windows-only function')
- end
- if not path then
- return false
- end
- if not string.lower(string.sub(path,-4,-1))=='.exe' then
- path = path..'.exe'
- end
- path = absolute_path(path)
- if not (path and lfs.isfile(path)) then
- return false
- else
- return path
- end
-end -- is_prog
-
function dir_writable(d)
-- because directory attributes do not tell the whole story,
-- we actually try to create a file in the directory.
@@ -368,10 +347,8 @@ function system_tempdir ()
d = '/tmp'
end
end
- -- if d then dbg('system tempdir: '..d) end
-- cygwin: $TEMP=/tmp, root '/' being root of cygwin installation
if d and not dir_writable(d) then
- dbg('unfortunately, '..d..' not writable')
d = false
end
return d
@@ -424,6 +401,7 @@ function tab_combine (t)
return res
end -- tab_combine
+-- files ----------------------------------------------------
-- Copy a file in chunks, with optional length and offset.
-- Since files may be very large, we copy them piecemeal.
-- An initial chunk of size bufsize should be plenty to include
@@ -434,6 +412,22 @@ function slice_file(source, dest, len, offset, mode)
-- specifying false as value
-- Assume caller ensured parameters of correct type.
-- We do not allow negative offsets.
+
+ local copy2self = false
+ if source==dest then copy2self = true end
+ if os.type=='unix' and
+ lfs.attributes(source,'ino')==lfs.attributes(dest,'ino') then
+ copy2self = true
+ end
+ if copy2self and not len and not offset and mode~='ab' then
+ return -- nothing to do
+ elseif copy2self then
+ errror('slice_file invoked with identical source and destination '..
+ source..' and non-simple copy')
+ end
+ -- in practice any_to_any already checks for this.
+ -- in addition, the main program makes a backup if infile==outfile
+
local sz = lfs.attributes(source, 'size')
if not offset then
offset = 0
@@ -444,8 +438,6 @@ function slice_file(source, dest, len, offset, mode)
len = sz - offset
end
if not mode then mode = 'wb' end
- -- dbg('copying '..len..' bytes of '..source..' to '..dest..' from '..offset
- -- ..' in '..mode..' mode')
local buffer=''
local s=io.open(source, 'rb')
s:seek('set', offset)
@@ -474,6 +466,12 @@ function move_or_copy(source, dest)
warn('Removing old '..dest)
os.remove(dest) -- in case of failure, go ahead anyway
end
+ -- Windows: try first renaming in-place before moving to the right directory
+ if os.type == 'windows' then
+ if os.rename(source, source..'.renamed') then
+ source = source..'.renamed'
+ end
+ end
if not os.rename(source, dest) then
slice_file(source, dest) -- bails out on failure
local ok, err_mess = os.remove(source)
@@ -498,10 +496,8 @@ function mktemp(ext)
for i=0,99 do
froot = string.format('%02d.', i)
fname = froot..ext
- -- dbg('New temp file '..fname..'?')
if ext~='tex' then
if not lfs.isfile(fname) then
- -- dbg(fname..' available')
f = io.open(fname, 'wb')
if not f then
errror('Cannot create temporary file '..fname)
@@ -535,11 +531,9 @@ function mktemp(ext)
end
function waitasec()
- -- stupid windows file locking
- if os.type=='windows' and (tonumber(os.uname().version) or 0)>=6 then
+ -- stupid windows file locking; assume vista or later
+ if os.type=='windows' then
os.execute('timeout /t 1 /nobreak >nul')
- elseif os.type=='windows' then
- os.execute('ping -n 1 localhost >NUL')
-- else do nothing
end
-- error checking pointless
@@ -573,9 +567,9 @@ function cleantemp()
end
end
--- epsdevice -----------------------
+-- gs_epsdevice -----------------------
-function epsdevice()
+function gs_epsdevice()
local gh = io.popen(gs_prog..' -help')
local s = gh:read("*a")
gh:close()
@@ -599,17 +593,12 @@ function write_settings (file)
end
else -- stdout to be captured by epspdftk
f = io.output()
- if os.type=='windows' and not auto_psp then
- f:write('psp_config = yes', eol)
- end
end
for k, v in pairs(settings) do
- if k ~= 'pdftops_prog' or os.type=='windows' then
- if descriptions[k] and file then
- f:write(eol, '# ', descriptions[k], eol)
- end
- f:write(k, ' = ', tostring(v), eol)
+ if descriptions[k] and file then
+ f:write(eol, '# ', descriptions[k], eol)
end
+ f:write(k, ' = ', tostring(v), eol)
end
if file then
f:close()
@@ -634,15 +623,12 @@ function read_settings(file)
f:close()
end
if not contents or contents=='' then
- dbg('No settings read')
return
- -- else
- -- dbg(contents)
end
-- remove initial \r and \n characters
contents = string.gsub(contents, '^[\r\n]*', '');
-- gmatch chops contents into series of non-line-ending characters
- -- possibly followed by line-ending characters.
+ -- possibly followed by line-ending characters i.e. in lines
local k, v, vl, vnum
for l in string.gmatch(contents, '[^\r\n]+[\r\n]*') do
l = string.match(l,'[^\r\n]*')
@@ -660,31 +646,6 @@ function read_settings(file)
if in_list(v, pdf_versions) then
settings[k] = v
end
- --[[
- elseif k == 'ignore_hires_bb' then
- vl = string.lower(string.sub(v,1,1))
- if v == 0 or vl == 'n' or vl == 'f' then
- settings.use_hires_bb = true
- elseif v == 1 or vl == 'y' or vl == 't' then
- settings.use_hires_bb = false
- end
- elseif k == 'use_hires_bb' then
- vl = string.lower(string.sub(v,1,1))
- if v == 0 or vl == 'n' or vl == 'f' then
- settings.use_hires_bb = false
- elseif v == 1 or vl == 'y' or vl == 't' then
- settings.use_hires_bb = true
- end
- elseif k == 'bb_spread' then
- vnum = tonumber(v)
- if vnum and vnum >= 0 then
- settings[k] = math.modf(v) -- truncate to integer
- end
- --]]
- elseif k == 'pdftops_prog' then
- if is_miktex then
- settings.pdftops_prog = is_prog(v)
- end -- else ignore
elseif k == 'ignore_pdftops' then
vl = string.lower(string.sub(v,1,1))
if v == 0 or vl == 'n' or vl == 'f' then
@@ -707,13 +668,27 @@ function read_settings(file)
settings.pdf_viewer = v
elseif k == 'default_dir' then
settings.default_dir = v
- elseif k == 'default_dir' then
- settings.default_dir = v
end -- test for k
end -- not matching ^#
end -- for
end -- read settings
+function version_added()
+ for _, v in pairs(pdf_options) do
+ if string.find(v, '-dCompatibilityLevel') then
+ return true
+ end
+ end
+ return false
+end
+
+function maybe_add_version_parameter()
+ if options.type=='pdf' and not options.bbox and settings.pdf_version and
+ settings.pdf_version~='default' and not version_added() then
+ table.insert(pdf_options, '-dCompatibilityLevel#'..settings.pdf_version)
+ end
+end
+
-- gui: reading and writing settings -----------
function gui(action)
@@ -733,12 +708,19 @@ end
-- boundingboxes ---------------------------------------------------
--- [HR]Bb.coords names now same as those of epdf PDFRectangle
+-- [HR]Bb.coords names same as those of epdf PDFRectangle
+-- but the new pdfe simply uses an array
Bb = {}
Bb.coords = {'x1', 'y1', 'x2', 'y2'}
function Bb:from_rect(r)
+ -- also handle the case that r is a 4-element array:
+ if not r.x1 then r.x1 = r[1] end
+ if not r.y1 then r.y1 = r[2] end
+ if not r.x2 then r.x2 = r[3] end
+ if not r.y2 then r.y2 = r[4] end
+
for _,k in ipairs(self.coords) do
if not r[k] or type(r[k])~='number' then
errror('from_rect called with illegal parameters')
@@ -778,53 +760,6 @@ function Bb:from_comment(s)
return Bb:from_rect(b)
end
---[[ some no-longer-used bbox functions
-
-function Bb:copy ()
- local b = {}
- for _,k in ipairs(self.coords) do b[k] = self[k] end
- setmetatable(b, {__index=self})
-end
-
-function Bb:width()
- return self.x2 - self.x1
-end
-
-function Bb:height()
- return self.y2 - self.y1
-end
-
-function Bb:expand ()
- -- in-place expansion; does not return an object.
- -- any point in preserving non-negativity?
- local i = settings.bb_spread
- if i and i>0 then
- -- if x1~=0 then x1 = x1-1 end
- -- if y1~=0 then y1 = y1-1 end
- self.x1 = self.x1 - 1
- self.y1 = self.y1 - 1
- self.x2 = self.x2 + 1
- self.y2 = self.y2 + 1
- end
-end
-
--- gs handles this now
--- call this via pcall
-function Bb:wrapper()
- local fn = mktemp('ps')
- local f = io.open(fn, 'wb')
- f:write(string.format('%%%%BoundingBox: 0 0 %d %d\n',
- self:width(), self:height())
- .. string.format('<< /PageSize [%d %d] >> setpagedevice\n',
- self:width(), self:height())
- .. 'gsave\n'
- .. string.format('%d %d translate\n', -self.x1, -self.y1))
- f:close()
- return fn
-end
-
---]]
-
function Bb:nonnegative ()
return self.x1>=0 and self.y1>=0
end
@@ -842,6 +777,12 @@ HRBb = {}
setmetatable(HRBb, {__index=Bb})
function HRBb:from_rect(r)
+ -- also handle the case that r is a 4-element array:
+ if not r.x1 then r.x1 = r[1] end
+ if not r.y1 then r.y1 = r[2] end
+ if not r.x2 then r.x2 = r[3] end
+ if not r.y2 then r.y2 = r[4] end
+
for _,k in ipairs(self.coords) do
if not r[k] or type(r[k])~='number' then
errror('from_rect called with illegal parameters')
@@ -865,7 +806,6 @@ HRBb.bb_pat = '^%s*%%%%HiResBoundingBox:'
HRBb.bb_end = '^%s*%%%%HiResBoundingBox:%s*%(%s*atend%s*%)%s*$'
function HRBb:from_comment(s)
- -- dbg('hrbb from '..s)
local p = self.bb_pat..'%s*([-+.%deE]+)'..string.rep('%s+([-+.%deE]+)',3)
local b = {}
b.x1, b.y1, b.x2, b.y2 = string.match(s, p)
@@ -878,6 +818,10 @@ function HRBb:from_comment(s)
return HRBb:from_rect(b)
end
+function HRBb:nonnegative ()
+ return self.x1>=0 and self.y1>=0
+end
+
function HRBb:comment()
return string.format('%%%%HiResBoundingBox: %f %f %f %f',
self.x1, self.y1, self.x2, self.y2)
@@ -885,10 +829,6 @@ end
--[[
-function HRBb:expand ()
- errror('HRBb:expand not available')
-end
-
-- no longer used: gs handles this
-- call this one also via pcall
function HRBb:wrapper()
@@ -910,10 +850,10 @@ end
-- manipulating eps/ps/pdf files -----------------------------------
-function identify(path)
- local f = io.open(path, 'rb')
+function identify()
+ local f = io.open(infile, 'rb')
if not f then
- errror('Failure to open '..path..' for identification')
+ errror('Failure to open '..infile..' for identification')
end
local filestart= f:read(23)
f:close()
@@ -925,7 +865,7 @@ function identify(path)
return 'eps'
elseif string.match(filestart,'^%%!PS%-Adobe%-%d%.%d') then
for _, p in ipairs({'.eps', '.epi', '.epsi', '.epsf'}) do
- if string.sub(string.lower(path), -1-string.len(p),-1) == p then
+ if string.sub(string.lower(infile), -1-string.len(p),-1) == p then
return 'eps'
else
return 'ps'
@@ -940,45 +880,52 @@ function identify(path)
end -- identify
function pdf_props(path)
- local pdfdoc = epdf.open(path)
- if not pdfdoc then
- errror('epdf.open failed on '..path)
+ local pdfdoc, pgs, maver, miver
+ if pdfe then
+ pdfdoc = pdfe.open(path)
+ if pdfdoc then
+ pgs = pdfe.getnofpages(pdfdoc)
+ maver, miver = pdfe.getversion(pdfdoc)
+ end
+ if not (pdfdoc and pgs and maver and miver) then
+ errror('pdfe failed to get information about '..path)
+ end
+ pdfe.close(pdfdoc)
+ else
+ local cat
+ pdfdoc = epdf.open(path)
+ if pdfdoc then
+ cat = pdfdoc:getCatalog()
+ if cat then
+ pgs = cat:getNumPages()
+ end
+ maver = pdfdoc:getPDFMajorVersion()
+ miver = pdfdoc:getPDFMinorVersion()
+ end
+ if not (pdfdoc and pgs and maver and miver) then
+ errror('epdf failed to get information about '..path)
+ end
+ -- epdf.close(pdfdoc)
end
-- if os.type=='windows' then waitasec() end
- local cat = pdfdoc:getCatalog()
- if not cat then
- errror('Cannot open pdf catalog of '..path)
- end
- local pg = cat:getNumPages()
- if not pg then
- errror('Cannot read n. of pages of '..path)
- end
- local maver = pdfdoc:getPDFMajorVersion()
- if not maver then
- errror('Cannot read pdf major version of '..path)
- end
- local miver = pdfdoc:getPDFMinorVersion()
- if not miver then
- errror('Cannot read pdf minor version of '..path)
- end
if maver > 1 then
print(path..' has pdf major version \n'..tostring(maver)..
' which is unsupported;\n'..
'Continuing with fingers crossed...')
end
- return pg, miver, maver
-end
+ return pgs, miver, maver
+end -- pdf_props
-function info (infile)
- local intype = identify(infile)
+function info()
+ local intype = identify()
if not intype then
print(infile..' has an unsupported filetype.')
elseif intype~='pdf' then
print(infile..' has type '..intype..'.')
else
- local pg, miver, maver = pdf_props(infile)
+ local pgs, miver, maver = pdf_props(infile)
print(infile..' has type pdf, version '..tostring(maver)..
- '.'..tostring(miver)..' and has '..tostring(pg)..' pages.')
+ '.'..tostring(miver)..' and has '..tostring(pgs)..' pages.')
end
os.exit()
end
@@ -990,7 +937,6 @@ PsPdf = {}
-- creators
function PsPdf:new(ext)
- -- dbg('PsPdf:new')
local psp = {}
setmetatable(psp, {__index = self})
-- assign temp file
@@ -1013,7 +959,6 @@ function PsPdf:new(ext)
end -- PsPdf:new
function PsPdf:from_path(path)
- -- dbg('PsPdf:from_path')
local psp = {}
setmetatable(psp, {__index = self})
psp.path = path
@@ -1031,23 +976,40 @@ function PsPdf:from_path(path)
end
psp.bb = false
psp.hrbb = false
- -- calculate when needed
+ -- only calculate when needed
return psp
end -- PsPdf:from_path
---[===[ getting boundingbox property from file itself --------------
+-- do we need to downgrade the pdf to a lower version?
+-- consider [e]ps lower than any pdf version
+function PsPdf:to_downgrade()
+ if self.type~='pdf' then
+ return false
+ elseif options.type~='pdf' then
+ return true
+ elseif settings.pdf_version=='default' then
+ return false
+ elseif settings.pdf_version~='default' and
+ self.maver+0.1*self.miver-0.001 > tonumber(settings.pdf_version) then
+ -- -0.001: exact binary representation of pdf_version not guaranteed
+ return true
+ else
+ return false
+ end
+end
+
+--[[ getting boundingbox property from file itself --------------
-find_bb_simple: use only for eps PsPdf objects we generated
+get_bb_simple: use only for eps PsPdf objects we generated
ourselves, so we can assume that the bbox comments are in the header
and the hires bb lies within the lores bb.
Of course the file itself is not rewritten.
---]===]
+--]]
-function PsPdf:find_bb_simple()
- -- dbg('PsPdf:find_bb_simple')
+function PsPdf:get_bb_simple()
if self.type~='eps' then
- errror('find_bb_simple called with non-eps file '..self.path)
+ errror('get_bb_simple called with non-eps file '..self.path)
end
self.bb = false
self.hrbb = false
@@ -1074,9 +1036,7 @@ function PsPdf:find_bb_simple()
return self -- no real need for a return value
end
-function PsPdf:bb_from_gs(pg)
-
- -- dbg('bb_from_gs '..pg)
+function PsPdf:bb_from_gs()
if self.type=='ps' then
errror('bb_from_gs called with ps file '..self.path)
@@ -1086,19 +1046,25 @@ function PsPdf:bb_from_gs(pg)
end
if self.type=='eps' and not self.bb:nonnegative() then
errror('bb_from_gs called on ' .. self.path ..
- ' which has some negative boundingbox coordinates')
+ ' which has some negative boundingbox coordinates')
+ -- any_to_any should guard against such an invocation
end
- -- A pdf can also have negative ...Box coordinates, but apparently
- -- for pdf the bbox returned by gs is relative to the lower-left corner.
- -- Anyhow, with pdf it all works out even with negative coordinates.
-- Since Ghostscript writes the boundingbox comments to stderr,
-- we need a shell to intercept this output:
local bb_file = mktemp('dsc')
- local cmdline = gs_prog .. ' ' .. table.concat(gs_options,' ')
+ -- a somewhat low resolution parameter may help gs
+ -- deal with eps files with large coordinates
+ -- but this may impact the accuracy of the HRBb
+ local cmdline
+ if self.type=='eps' and (self.bb.x2 > 850 or self.bb.y2 > 850) then
+ cmdline = gs_prog .. ' -r300 ' .. table.concat(gs_options,' ')
+ else
+ cmdline = gs_prog .. ' ' .. table.concat(gs_options,' ')
+ end
if self.type=='pdf' then
- if not pg then pg=1 end
+ pg = options.page or 1
cmdline = cmdline .. ' -dFirstPage#' .. tostring(pg) ..
' -dLastPage#' .. tostring(pg)
end
@@ -1145,7 +1111,8 @@ end
function PsPdf:eps_clean()
-- return a PsPdf object referring to a new file
- -- without a preview header and with boundingbox(es) in the header
+ -- without a preview header and with boundingbox(es) in the header.
+ -- return a new file even if no changes were needed.
local function bytes2num (s, i)
-- convert substring s[i..i+3] to a number.
@@ -1158,7 +1125,6 @@ function PsPdf:eps_clean()
-- + string.byte(s,i+1)) + string.byte(s,i))
end
- -- dbg('PsPdf:eps_clean '..self.path)
if self.type~='eps' and self.type~='epsPreview' then
errror('epsclean called with non-eps file ' .. self.path)
end
@@ -1182,14 +1148,12 @@ function PsPdf:eps_clean()
if not offset then
errror('Could not read preview header of ' .. self.path)
end
- -- dbg(tostring(offset)..' '..tostring(ps_length))
end
-- create the PsPdf object which is to be returned
local psp
psp = PsPdf:new('eps')
- -- dbg(psp.path)
-- read an initial and if necessary a final chunk of the file
-- to find boundingbox comments.
@@ -1218,9 +1182,7 @@ function PsPdf:eps_clean()
-- remaining, unscanned length of input buffer slurp
local unscanned = math.min(ps_length,bufsize)
- -- dbg('bytes to be read: '..tostring(unscanned))
slurp = fin:read(unscanned)
- -- dbg('Read from '..self.path..': '..string.len(slurp)..' bytes')
-- unnecessary:
psp.bb = nil
@@ -1237,18 +1199,14 @@ function PsPdf:eps_clean()
atend = true
i_bb = i
elseif string.match(l, Bb.bb_pat) then
- -- dbg(l)
psp.bb = Bb:from_comment(l)
- -- dbg(psp.bb:comment())
-- from_comment errors out on failure; no need to check return value
i_bb = i
elseif string.match(l, HRBb.bb_end) then
hr_atend = true
i_hrbb = i
elseif string.match(l, HRBb.bb_pat) then
- -- dbg(l)
psp.hrbb = HRBb:from_comment(l)
- -- dbg(psp.hrbb:comment())
i_hrbb = i
end -- bbox line
end -- eol/non-eol
@@ -1281,14 +1239,11 @@ function PsPdf:eps_clean()
j_bb, j_hrbb, j_end = false, false, false
while unscanned>0 do
j = j - 1
- -- dbg(j)
if string.find(slurp,'[\n\r]', string.len(slurp)) then
- -- dbg('eol(s)')
slurp,l = string.match(slurp, '^(.-)([\n\r]+)$')
-- '-': non-greedy matching
else
slurp,l = string.match(slurp, '^(.-)([^\n\r]+)$')
- -- dbg(l)
if string.match(l, Bb.bb_pat) then
psp.bb = Bb:from_comment(l)
j_bb = j
@@ -1318,7 +1273,6 @@ function PsPdf:eps_clean()
post_lines[j_bb+1] = ''
end
if hr_atend and j_hrbb then
- -- dbg(psp.hrbb:comment())
-- pre_lines[i_hrbb] = post_lines[j_hrbb]
pre_lines[i_hrbb] = psp.hrbb:comment()
post_lines[j_hrbb] = ''
@@ -1340,19 +1294,14 @@ end -- eps_clean
function PsPdf:eps_crop()
- -- not a proper conversion, although
- -- we use the Ghostscript bbox device for a tight boundingbox.
+ -- conversion is not done by an external program, although
+ -- we invoke Ghostscript with a bbox device for a tight boundingbox.
-- We use both the regular and the hires boundingbox from gs.
-- The eps should already have been cleaned up by eps_clean,
-- and the current boundingbox should not contain negative coordinates,
-- otherwise the bbox output device may give incorrect results.
-- Only the boundingbox in the eps is rewritten.
- -- dbg('PsPdf:eps_crop '..self.path)
- if self.type~='eps' then
- errror('eps_crop called with non-eps file ' .. self.path)
- end
-
-- create the PsPdf object which is to be returned
local psp = PsPdf:new('eps')
@@ -1377,9 +1326,7 @@ function PsPdf:eps_crop()
-- remaining, unscanned length of input buffer slurp
local unscanned = math.min(ps_length,bufsize)
- -- dbg('bytes to be read: '..tostring(unscanned))
slurp = fin:read(unscanned)
- -- dbg('Read from '..self.path..': '..string.len(slurp)..' bytes')
i, i_bb, i_hrbb = 0, false, false
while unscanned>0 do
i = i+1
@@ -1421,17 +1368,21 @@ function PsPdf:eps_crop()
slice_file(self.path, psp.path,
lfs.attributes(self.path,'size') - offset, offset, 'ab')
options.bbox = false
- -- dbg('eps_crop from '..self.path..' to '..psp.path)
return psp
end -- eps_crop
---[===[ real conversions involving a single call of gs, pdftops or texlua
+--[[
+
+most of these conversions involve a single invocation of gs, pdftops or texlua
Each conversion fullfills all options that it can: gray, bbox and
page. gray when converting to pdf, bbox when converting from eps or
from pdf to pdf and page when converting from pdf. It then sets the
fullfilled option(s) to false.
+We make sure to do the tight boundingbox before a file format downgrade:
+rasterization of the page or graphic frustrates boundingbox calculation.
+
We like to preserve fonts as fonts. gs does this when generating
pdf, but may fail for fonts such as cid and large truetype when
generating PostScript. In such cases, pdftops may succeed. However,
@@ -1439,66 +1390,82 @@ it seems that if the page contains an element that does not cleanly
convert, pdftops simply rasterizes the entire page, and that this
choice is made per page.
---]===]
+--]]
--- TODO: multiple pages?
--- (means additional parameter checking)
+-- Converting from pdf to pdf using luatex; no grayscaling here
--- Converting from pdf to pdf using luatex; no grayscaling
+function PsPdf:getpgbox()
+ if options.page and options.page > self.pages then
+ errror('PsPdf:getpgbox called with non-existent page '.. options.page)
+ end
+ local pg = options.page or 1
+ local bb, hrbb, pgbox, pdfdoc, ppage
+ if pdfe then
+ pdfdoc = pdfe.open(self.path)
+ if pdfdoc then
+ ppage = pdfe.getpage(pdfdoc, pg)
+ if not ppage then errror('did not get page') end
+ if ppage then
+ pgbox = pdfe.getbox(ppage, 'MediaBox')
+ if not pgbox then
+ pgbox = pdfe.getbox(ppage, 'CropBox')
+ if not pgbox then
+ pgbox = pdfe.getbox(ppage, 'TrimBox')
+ if not pgbox then
+ errror('No box acquired')
+ end
+ end
+ end
+ end
+ end
+ pdfe.close(pdfdoc)
+ else
+ local cat
+ pdfdoc = epdf.open(self.path)
+ if pdfdoc then
+ cat = pdfdoc:getCatalog()
+ if cat then
+ ppage = cat:getPage(pg)
+ if ppage then
+ pgbox = ppage:getMediaBox()
+ if not pgbox then
+ pgbox = ppage:getCropBox()
+ if not pgbox then
+ pgbox = ppage:getTrimBox()
+ end
+ end
+ end
+ end
+ end
+ end
+ -- normalization and further checks, including for non-nil,
+ -- by HRBb:from_rect, which errors out on failures
+ if pgbox then
+ pgbox = HRBb:from_rect(pgbox)
+ -- this also converts numeric array indices to x1 ... y2 if necessary
+ else
+ errror('Cannot get page box from '..self.path..' page '..pg)
+ end
+ return pgbox
+end -- PsPdf:getpgbox
function PsPdf:pdf_crop()
- -- options to be fulfilled: page, boundingbox
- -- only called directly.
- -- embeds the pdf with crop parameters into a new (lua)tex document
- -- dbg('PsPdf:pdf_crop '..self.path)
- if not (options.bbox or options.page) then
- return self
- end
+ -- options to be fulfilled: page, boundingbox if possible
+ -- embeds the pdf with boundingbox parameters into a new (lua)tex document
if options.page and options.page > self.pages then
errror('PsPdf:pdf_crop called with non-existent page '.. options.page)
end
- local pg = options.page or 1
- local bb, hrbb, pgbox, pgboxname
- do
- local pdoc, pcat, ppage
- -- we already have the pages, miver and maver properties
- -- but need properties of the desired page as well
- pdoc = epdf.open(self.path)
- if not pdoc then
- errror('Epdf: cannot open '..self.path)
- end
- -- dbg('about to get catalog')
- pcat = pdoc:getCatalog()
- if not pcat then
- errror('Cannot open catalog of '..self.path)
- end
- -- dbg('got catalog')
- ppage = pcat:getPage(pg)
- if not ppage then
- errror('Epdf: cannot open page object '..tostring(pg)..' of '..self.path)
- end
- pgboxname = 'trimbox'
- pgbox = ppage:getTrimBox()
- if not pgbox then
- pgboxname = 'cropbox'
- pgbox = ppage:getCropBox()
- end
- if not pgbox then
- pgboxname = 'mediabox'
- pgbox = ppage:getMediaBox()
- end
- -- normalization and further checks, including for non-nil,
- -- by Bb:from_rect, which errors out on failures
- pgbox = HRBb:from_rect(pgbox)
- end
- if options.bbox then
- bb, hrbb = self:bb_from_gs(pg)
+ local bb, hrbb, pgbox
+ pgbox = self:getpgbox()
+ if options.bbox and pgbox:nonnegative() then
+ bb, hrbb = self:bb_from_gs()
+ options.bbox = false
else
hrbb = pgbox
end
- -- luatex on searchpath
+ -- luatex is on searchpath
local luatex_prog = 'luatex'
-- write TeX file which includes cropped pdf page
@@ -1552,12 +1519,10 @@ function PsPdf:pdf_crop()
pieces[6] = [[}%
\pdfrefximage\pdflastximage
}%
- \pdfhorigin=-#2bp\relax
+ \pdfhorigin=#2bp\relax
\pdfvorigin=#3bp\relax
\pagewidth=#4bp\relax
- \advance\pagewidth by -#2bp\relax
\pageheight=#5bp\relax
- \advance\pageheight by -#3bp\relax
\ht0=\pageheight
\shipout\box0\relax
}
@@ -1567,7 +1532,7 @@ function PsPdf:pdf_crop()
\csname @@end\endcsname
\end
]],
- options.page or 1, hrbb.x1, hrbb.y1, hrbb.x2, hrbb.y2)
+ options.page or 1, -hrbb.x1, hrbb.y1, hrbb.x2-hrbb.x1, hrbb.y2-hrbb.y1)
local textemp = mktemp('tex') -- this also took care of pdf:
local pdftemp = string.gsub(textemp, 'tex$', 'pdf')
@@ -1577,18 +1542,20 @@ function PsPdf:pdf_crop()
f:write(table.concat(pieces, ''))
f:close()
local cmd, res, psp
- if os.type=='unix' then
+ -- if os.type=='unix' then
cmd = {luatex_prog, '--safer', '--no-shell-escape', textemp}
log_cmd(cmd)
res = os.spawn(cmd)
- else
- cmd = luatex_prog..' --safer --no-shell-escape '..textemp
- log_cmd({cmd})
- -- os.execute('timeout /t 1 /nobreak >nul')
- res = os.execute(cmd)
- end
- if res and res==0 and lfs.attributes(pdftemp, 'size')>0 then
+ -- else
+ -- cmd = luatex_prog..' --safer --no-shell-escape '..textemp
+ -- log_cmd({cmd})
+ -- -- os.execute('timeout /t 1 /nobreak >nul')
+ -- res = os.execute(cmd)
+ -- end
+ if res and res==0 and lfs.attributes(pdftemp, 'size')>0 then
psp = PsPdf:from_path(pdftemp)
+ options.bbox = false
+ options.page = false
return psp
else
errror('pdf_crop failed on '..self.path)
@@ -1598,33 +1565,26 @@ end
function PsPdf:eps_to_pdf()
-- option to be fulfilled: gray
- -- set target and pdf version if applicable
- -- dbg('PsPdf:eps_to_pdf '..self.path)
+ -- set target and maybe pdf version if applicable
if self.type~='eps' then
errror('PsPdf:eps_to_pdf called for non-eps file '.. self.path)
end
local cmd
- if options.bbox and self.bb:nonnegative() then
- self = self:eps_crop() -- this sets options.bbox to false
- end
+ maybe_add_version_parameter()
cmd = tab_combine({{gs_prog}, gs_options, pdf_options})
- -- dbg(table.concat(cmd,' '))
if options.gray then
cmd = tab_combine({cmd, gray_options})
- -- dbg(table.concat(cmd,' '))
- options.gray = false
end
- table.insert(cmd, '-dEPSCrop') -- always hires bb
- -- dbg(table.concat(cmd,' '))
+ table.insert(cmd, '-dEPSCrop') -- uses existing hires bb
local psp = PsPdf:new('pdf')
table.insert(cmd, '-sOutputFile#'..psp.path)
- -- dbg(table.concat(cmd,' '))
- cmd = tab_combine({cmd, pdf_tail_options, {self.path}})
- -- dbg(table.concat(cmd,' '))
+ cmd = tab_combine({cmd, pdf_tail_options})
+ table.insert(cmd, self.path)
log_cmd(cmd)
local res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages, psp.miver, psp.maver = pdf_props(psp.path)
+ options.gray = false
return psp
else
errror('eps_to_pdf failed on '..self.path)
@@ -1632,13 +1592,14 @@ function PsPdf:eps_to_pdf()
end -- eps_to_pdf
-- Converting from pdf to pdf with grayscaling and/or page selection
+-- or just eliminating negative boundingbox
function PsPdf:pdf_to_pdf()
- -- option to be fulfilled: gray and optionally page.
+ -- option to be fulfilled: gray, optionally page
+ -- side effect: makes bbox non-negative.
-- do not call this just for page selection because
-- pdf_crop can do this in a less invasive manner
- -- dbg('PsPdf:pdf_to_pdf '..self.path)
if self.type~='pdf' then
errror('PsPdf:pdf_to_pdf called for non-pdf file '.. self.path)
end
@@ -1647,28 +1608,24 @@ function PsPdf:pdf_to_pdf()
errror('PsPdf:pdf_to_pdf called with non-existent page '.. options.page)
end
cmd = tab_combine({{gs_prog}, gs_options, pdf_options})
- -- dbg(table.concat(cmd,' '))
if options.gray then
cmd = tab_combine({cmd, gray_options})
- -- dbg(table.concat(cmd,' '))
- options.gray = false
end
if options.page then
table.insert(cmd, '-dFirstPage#'..tostring(options.page))
table.insert(cmd, '-dLastPage#'..tostring(options.page))
- -- dbg(table.concat(cmd,' '))
- options.page = false
end
+ maybe_add_version_parameter()
local psp = PsPdf:new('pdf')
table.insert(cmd, '-sOutputFile#'..psp.path)
cmd = tab_combine({cmd, pdf_tail_options})
- -- dbg(table.concat(cmd,' '))
table.insert(cmd, self.path)
- -- dbg(table.concat(cmd,' '))
log_cmd(cmd)
local res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages, psp.miver, psp.maver = pdf_props(psp.path)
+ options.gray = false
+ options.page = false
return psp
else
errror('pdf_to_pdf failed on '..self.path)
@@ -1677,34 +1634,19 @@ end -- pdf_to_pdf
function PsPdf:pdf_to_eps()
- -- options to be fulfilled: bbox and page
- -- dbg(tostring(settings.pdftops_prog))
- -- dbg('pdf_to_eps '..self.path)
+ -- options to be fulfilled: page
local psp = PsPdf:new('eps')
local cmd, res
- local page = false
- if self.pages > 1 then
- if options.page then
- page = options.page
- if page > self.pages then
- errror('PsPdf:pdf_to_eps called with non-existant page '..
- tostring(page))
- end
- else
- page = 1
- end
- page = tostring(page)
- end -- self.pages > 1
+ -- any_to_any already checked the validity and relevance of options.page
if pdftops then
- if page then
+ if options.page then
cmd = tab_combine({{pdftops}, ps_options,
- {'-f', page, '-l', page,
+ {'-f', options.page, '-l', options.page,
'-eps', self.path, psp.path}})
else
cmd = tab_combine({{pdftops}, ps_options,
{'-eps', self.path, psp.path}})
end
- options.page = false
log_cmd(cmd)
if os.type=='windows' then
-- suppress console output of 'No display font for...' messages,
@@ -1718,6 +1660,7 @@ function PsPdf:pdf_to_eps()
end
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages = 1
+ options.page = false
else
errror('pdf_to_eps failed on '..self.path)
end
@@ -1766,37 +1709,37 @@ function PsPdf:pdf_to_eps()
if not fout then errror('Cannot write new file '.. newfile) end
fout:write(table.concat(pre_lines))
fout:close()
- -- dbg('fixing '..psp.path..' to '..newfile)
slice_file(psp.path, newfile,
lfs.attributes(psp.path,'size') - offset, offset, 'ab')
psp.path = newfile
end -- needs_fixing
else -- use ghostscript
- local epsdev = epsdevice()
+ local epsdev = gs_epsdevice()
if not epsdev then
errror('Conversion to eps not supported by this ghostscript')
end
cmd = tab_combine({{gs_prog}, gs_options,
{'-sDEVICE#'..epsdev, '-dHaveTrueTypes=true', '-dLanguageLevel#3'}})
- -- the restrictions on eps files are apparently
- -- incompatible with grayscaling
+ if options.gray then
+ cmd = tab_combine({cmd, gray_options})
+ end
if options.page then
table.insert(cmd, '-dFirstPage='..options.page)
table.insert(cmd, '-dLastPage='..options.page)
end
table.insert(cmd, '-sOutputFile='..psp.path)
table.insert(cmd, self.path)
- options.page = false
log_cmd(cmd)
res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages = 1
+ options.page = false
+ options.gray = false
else
errror('pdf_to_eps failed on '..self.path)
end
end -- use ghostscript
- psp:find_bb_simple()
- if options.bbox then psp = psp:eps_crop() end
+ psp:get_bb_simple()
return psp
end -- pdf_to_eps
@@ -1804,7 +1747,6 @@ end -- pdf_to_eps
function PsPdf:ps_to_pdf()
-- options to be fulfilled: gray
- -- dbg('PsPdf:ps_to_pdf '..self.path)
if self.type~='ps' then
errror('PsPdf:ps_to_pdf called for non-ps file '.. self.path)
end
@@ -1812,7 +1754,6 @@ function PsPdf:ps_to_pdf()
cmd = tab_combine({{gs_prog}, gs_options, pdf_options})
if options.gray then
cmd = tab_combine({cmd, gray_options})
- options.gray = false
end
local psp = PsPdf:new('pdf')
table.insert(cmd, '-sOutputFile#'..psp.path)
@@ -1822,6 +1763,7 @@ function PsPdf:ps_to_pdf()
local res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages, psp.miver, psp.maver = pdf_props(psp.path)
+ options.gray = false
return psp
else
errror('ps_to_pdf failed on '..self.path)
@@ -1832,24 +1774,14 @@ end -- PsPdf:ps_to_pdf
function PsPdf:pdf_to_ps()
-- options to be fulfilled: page and, if not using pdftops, also gray
- -- dbg('PsPdf:pdf_to_ps '..self.path)
local psp = PsPdf:new('ps')
- local page = false
- if self.pages>1 then
- if options.page and options.page > self.pages then
- errror('PsPdf:pdf_to_ps called with non-existant page '.. options.page)
- elseif options.page then
- page = tostring(options.page)
- psp.pages = 1
- end
- else
- psp.pages = self.pages
- end
+ -- options.page checked by any_to_any
+ psp.pages = self.pages
local cmd, res
if pdftops then
cmd = tab_combine({{pdftops}, ps_options})
- if page then
- cmd = tab_combine({cmd, {'-f', page, '-l', page}})
+ if options.page then
+ cmd = tab_combine({cmd, {'-f', options.page, '-l', options.page}})
end
cmd = tab_combine({cmd, {'-paper', 'match', self.path, psp.path}})
else -- use ghostscript
@@ -1857,17 +1789,14 @@ function PsPdf:pdf_to_ps()
{'-sDEVICE#ps2write', '-dHaveTrueTypes=true', '-dLanguageLevel#3'}})
if options.gray then
cmd = tab_combine({cmd, gray_options})
- -- dbg(table.concat(cmd,' '))
- options.gray = false
end
- if page then
- cmd = tab_combine({cmd, {'-dFirstPage#'..page, '-dLastPage#'..page}})
+ if options.page then
+ cmd = tab_combine({cmd,
+ {'-dFirstPage#'..options.page, '-dLastPage#'..options.page}})
end
table.insert(cmd, '-sOutputFile#'..psp.path)
- -- table.insert(cmd, '-f')
table.insert(cmd, self.path)
end
- options.page = false
log_cmd(cmd)
if os.type=='windows' and pdftops then
-- suppress console output of 'No display font for...' messages,
@@ -1880,79 +1809,123 @@ function PsPdf:pdf_to_ps()
res = os.spawn(cmd)
end
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
+ options.page = false
+ -- gs will have grayscaled if requested but pdftops will not
+ if not pdftops then options.gray = false end
return psp
else
errror('pdf_to_ps failed on '..self.path)
end
end -- PsPdf:pdf_to_ps
-function PsPdf:any_to_any()
+function PsPdf:ps_to_ps()
+ -- we do not accept a page option since we do not know
+ -- which pages are available.
+ -- if no gray option then there is no point in invoking this function.
+ local psp = PsPdf:new('ps')
+ local cmd = tab_combine({{gs_prog}, gs_options,
+ {'-sDEVICE#ps2write', '-dHaveTrueTypes=true', '-dLanguageLevel#3'}})
+ if options.gray then
+ cmd = tab_combine({cmd, gray_options})
+ end
+ table.insert(cmd, '-sOutputFile#'..psp.path)
+ table.insert(cmd, self.path)
+ log_cmd(cmd)
+ local res = os.spawn(cmd)
+ if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
+ options.gray = false
+ return psp
+ else
+ errror('ps_to_ps failed on '..self.path)
+ end
+end -- PsPdf:ps_to_ps
+
+function any_to_any()
+
+ local psp = PsPdf:from_path(infile)
- -- weed out nonsense options
+ -- sanitize some options
- -- dbg('PsPdf:any_to_any '..self.path)
if options.type=='ps' then
options.bbox = false
- -- dbg('Ignoring bbox option for ps output')
end
- if options.bbox and not options.page then
+
+ if (options.bbox or options.type=='eps') and not options.page then
options.page = 1
- -- dbg('Selecting page 1 for bbox')
end
- if self.pages==1 then
+
+ if psp.type=='pdf' then
+ if options.page then
+ local pgs = pdf_props(infile)
+ if pgs<options.page then
+ errror(infile..' has only '..pgs..' page(s).')
+ elseif pgs==1 then
+ options.page = false
+ end
+ end
+ elseif psp.type=='eps' then
options.page = false
- -- dbg('dropping page selection; source is already a 1-page document')
end
- -- for _,o in ipairs({'page', 'gray', 'bbox'}) do
- -- -- if options[o] then dbg('Do option '..o) end
- -- end
+ -- psp.type=='ps': pages not yet known but maybe not needed
- -- `distiller' settings depend on whether final output is pdf
+ local do_something = false
+ if psp.type=='eps' then
+ do_something = true -- do at least cleanup
+ elseif psp.type~=options.type then
+ do_something = true
+ elseif options.page then
+ do_something = true
+ elseif options.bbox then
+ do_something = true
+ elseif options.gray then
+ do_something = true
+ elseif options.type=='pdf' then
+ if psp:to_downgrade() then
+ do_something = true
+ elseif settings.pdf_target and settings.pdf_target~=default then
+ do_something = true
+ end
+ end
+ if not do_something then
+ if infile~=outfile then
+ slice_file(infile, outfile)
+ end
+ return true
+ end
+
+ -- `distiller' settings
+ -- these depend on whether final output is pdf
-- '.setpdfwrite' is just some optimization option for ghostscript
if options.type=='pdf' then
- if settings.pdf_version~='default' then
- table.insert(pdf_options, '-dCompatibilityLevel#'..settings.pdf_version)
+ if settings.pdf_target and (settings.pdf_target~='default') then
+ table.insert(pdf_options, '-dPDFSETTINGS#/'..settings.pdf_target)
end
- -- below, consider adding <</NeverEmbed [/Times-Roman /TimesBold ...]>>
if settings.pdf_target=='screen' or settings.pdf_target=='ebook' then
- pdf_tailoptions = {'-c', '.setpdfwrite', '-f'}
+ pdf_tail_options = {'-c', '.setpdfwrite', '-f'}
-- -f ensures that the input filename is not added to the -c string
else
- pdf_tailoptions = {
- '-c', '.setpdfwrite <</NeverEmbed [ ] >> setdistillerparams', '-f'}
- end
- else
- pdf_tailoptions = {
+ pdf_tail_options = {
'-c', '.setpdfwrite <</NeverEmbed [ ] >> setdistillerparams', '-f'}
- end
-
- if options.type=='pdf' then
- table.insert(pdf_options, '-dPDFSETTINGS#/'..settings.pdf_target)
- if settings.pdf_version~='default' then
- table.insert(pdf_options, '-dCompatibilityLevel#'..settings.pdf_version)
end
- else
- table.insert(pdf_options, '-dPDFSETTINGS#/default')
end
- --[[
+ --[[ Actual conversions
each single-step conversion takes care of options it can handle
and sets those options to false.
- for boundingboxes, eps_crop is either called explicitly
- or called implicitly by another converter.
- pdf_crop is always called explicitly and always as the last step
+ Cropping a pdf is best be done before converting to postscript or
+ a low (<1.4) pdf version.
- all calls to external programs work on temporary files
+ all invocations of external programs work on temporary files
in the then-current temporary directory, with a simple generated
- filename. So no need to quote names of input- and output filenames.
+ filename. So no need to quote names of input- or output filenames.
--]]
- local psp = self
local newfile
if psp.type=='eps' or psp.type=='epsPreview' then
- -- As a side effect of eps_clean, the modified source file is copied
- -- to the temp subdirectory.
+ -- As a side effect of eps_clean, the modified or unmodified source file
+ -- is copied to our temp subdirectory.
+ -- We always create a new file.
psp = psp:eps_clean()
if options.bbox and psp.bb:nonnegative() then
psp = psp:eps_crop()
@@ -1960,99 +1933,153 @@ function PsPdf:any_to_any()
if options.type=='eps' then
if options.gray or options.bbox then
-- bbox: eps_crop was apparently not applicable: pdf roundtrip
- psp = psp:eps_to_pdf():pdf_to_eps()
+ psp = psp:eps_to_pdf()
+ if options.bbox then
+ psp = psp:pdf_crop()
+ end
+ psp = psp:pdf_to_eps()
end
elseif options.type=='pdf' then
psp = psp:eps_to_pdf()
if options.bbox then
psp = psp:pdf_crop()
end
+ if psp:to_downgrade() then
+ maybe_add_version_parameter()
+ psp = psp:pdf_to_pdf()
+ end
elseif options.type=='ps' then
+ -- often, the eps file is fine as a postscript file.
+ -- however, it may lack a showpage operator, or have a weird
+ -- boundingbox. converting back-and-forth to pdf solves both problems.
+ -- eps_to_pdf will take care of grayscaling
+ -- a tight boundingbox option is not supported for ps output.
psp = psp:eps_to_pdf():pdf_to_ps()
end
- return psp
+ slice_file(psp.path, outfile)
+ return true
elseif psp.type=='ps' then
+
-- preliminary:
-- copy infile to a file in the temp directory, needed for gs -dSAFER
newfile = mktemp(psp.type)
slice_file(psp.path, newfile)
- -- dbg(psp.path..' copied to '..newfile..' in '..lfs.currentdir())
psp.path = newfile
+ if options.type=='ps' and not options.page then
+ psp = psp:ps_to_ps()
+ slice_file(psp.path, outfile)
+ return true
+ end
+
+ -- remaining options require initial conversion to pdf
+ psp = psp:ps_to_pdf()
+ -- AFAIK, all high-level ps constructs are covered by any pdf version
+ -- so this option should not cause unnecessary loss of structure
+ maybe_add_version_parameter()
+
+ if options.page and options.page>1 then
+ -- could not check page option before
+ local pgs = pdf_props(psp.path)
+ if pgs<options.page then
+ errror(infile..' has only '..pgs..' page(s).')
+ elseif pgs==1 then
+ options.page = false
+ end
+ end
+
-- actual conversion
if options.type=='eps' then
- return psp:ps_to_pdf():pdf_to_eps()
+ if options.bbox then
+ psp = psp:pdf_crop()
+ end
+ psp = psp:pdf_to_eps()
elseif options.type=='pdf' then
if options.bbox or options.page then
- return psp:ps_to_pdf():pdf_crop()
- else
- return psp:ps_to_pdf()
+ psp = psp:pdf_crop()
+ -- else no action needed
end
elseif options.type=='ps' then
- if options.gray or options.page then
- return psp:ps_to_pdf():pdf_to_ps()
- else
- return psp -- no conversion necessary
- end
+ psp = psp:pdf_to_ps()
end -- pdf => ps
+ slice_file(psp.path, outfile)
+ return true
elseif psp.type=='pdf' then
+
+ if options.type=='pdf' and settings.pdf_target=='default' and
+ not options.gray and not options.bbox and not options.page and
+ not psp:to_downgrade() then
+ if infile~=outfile then
+ slice_file(infile, outfile)
+ end
+ return true
+ end
+
-- preliminary:
- -- copy infile to a file in the temp directory, for gs -dSAFER
+ -- copy infile to a file in the temp directory, needed for gs -dSAFER
newfile = mktemp(psp.type)
slice_file(psp.path, newfile)
- -- dbg(psp.path..' copied to '..newfile..' in '..lfs.currentdir())
- psp.path = newfile
+ psp = PsPdf:from_path(newfile)
+ local pgbox
-- actual conversion
if options.type=='eps' then
- if options.gray then
- -- one-step grayscaling available for gs/ps but not for gs/eps
- return psp:pdf_to_pdf():pdf_to_eps()
- else
- return psp:pdf_to_eps()
+ if options.bbox then
+ pgbox = psp:getpgbox() -- page n. available from options table
+ if not pgbox:nonnegative() or options.gray then
+ -- fix in extra pdf-to-pdf step
+ psp = psp:pdf_to_pdf()
+ end
+ -- we want to calculate a tight boundingbox before conversion to eps,
+ -- because this conversion may cause rasterization and baffle
+ -- gs' boundingbox calculations
+ psp = psp:pdf_crop()
+ elseif options.gray and pdftops then
+ psp = psp:pdf_to_pdf()
end
+ psp = psp:pdf_to_eps()
elseif options.type=='pdf' then
-- pdf_crop can take care of bbox and page,
- -- but not of gray and not of target use or pdf version
- do
- local need_gs = false
- -- compare actual and required versions,
- -- allowing for rounding differences
- if settings.pdf_version~='default' and
- (psp.maver+0.1*psp.miver)>tonumber(settings.pdf_version)-0.01 then
- need_gs = true
- end
- if settings.pdf_target~='default' then
- need_gs = true
- end
- if options.gray then
- need_gs = true
- end
- local need_crop = false
- if options.bbox then
- need_crop = true
- end
- if (not need_gs) and options.page then
- need_crop = true
- end
- if need_gs then
+ -- but not of gray and not of some pdf options
+ if options.bbox then
+ pgbox = psp:getpgbox()
+ if not pgbox:nonnegative() then
+ -- pdf_to_pdf fixes negative bbox parameters
+ -- and also takes care of page and gray
psp = psp:pdf_to_pdf()
end
- if need_crop or (psp.pages>1 and options.page) then
- psp = psp:pdf_crop()
- end
- return psp
+ psp = psp:pdf_crop()
+ end
+ need_gs = false
+ if psp:to_downgrade() then
+ need_gs = true
+ maybe_add_version_parameter()
+ end
+ if settings.pdf_target~='default' then
+ need_gs = true
+ end
+ if options.gray then
+ need_gs = true
+ end
+ if (not need_gs) and options.page then
+ psp = psp:pdf_crop()
+ -- less invasive that page selection by gs
+ end
+ if need_gs then
+ psp = psp:pdf_to_pdf() -- will handle page selection too
end
elseif options.type=='ps' then
if options.gray and pdftops then
- return psp:pdf_to_pdf():pdf_to_ps()
+ psp = psp:pdf_to_pdf():pdf_to_ps()
else
- return psp:pdf_to_ps()
+ psp = psp:pdf_to_ps()
end
end -- pdf => ps
- end -- psp.type=='ps'|'pdf'
+ slice_file(psp.path, outfile)
+ return true
+ end
end -- any_to_any
-- system-dependent initialization -----------------------------------
@@ -2067,11 +2094,10 @@ if os.type == 'windows' then cwd = string.gsub(cwd, '\\', '/') end
-- prepend (lua)tex directory to searchpath, if not already there
maybe_add_path(os.selfdir, false)
--- Windows: miktex, TL or neither. is_miktex, is_tl_w32, auto_psp
+-- Windows: miktex, TL or neither. is_miktex, is_tl_w32
-- no support yet for separate ghostscript
is_miktex = false
is_tl_w32 = false
-auto_psp = true -- only false for old miktex without included pdftops
if os.type == 'windows' then
if string.find (string.lower(kpse.version()), 'miktex') then
@@ -2096,14 +2122,9 @@ if os.type == 'windows' then
'/release-texlive.txt; TeX installation not supported.', 0)
end -- if isfile
end -- if not miktex
- -- recent miktex and any windows tl include pdftops
- if not lfs.isfile(string.gsub(os.selfdir, '\\', '/') .. '/pdftops.exe') then
- auto_psp = false
- end
end -- if windows
-- without Ghostscript we are dead in the water.
--- TL/w32: add to searchpath
gs_prog = false
do
local rt=''
@@ -2119,11 +2140,11 @@ do
elseif is_tl_w32 then
-- windows/TeX Live
-- grandparent of texlua.exe directory .. ...
- rt = string.gsub(os.selfdir,'[\\/][^\\/]+[\\/][^\\/]+$', '')
- ..'/tlpkg/tlgs'
- maybe_add_path(rt..'/bin', false)
- gs_prog = 'gswin32c.exe'
- --[[ problems with (at least) grayscaling
+ -- rt = string.gsub(os.selfdir,'[\\/][^\\/]+[\\/][^\\/]+$', '')
+ -- ..'/tlpkg/tlgs'
+ -- maybe_add_path(rt..'/bin', false)
+ -- -- gs_prog = 'gswin32c.exe'
+ -- ---[[ problems with (at least) grayscaling
gs_prog = 'rungs.exe'
--]]
else
@@ -2134,11 +2155,10 @@ end -- do
-- directory for configuration and log
epsdir = ''
if os.type == 'windows' then
- epsdir = fw(ep_shortname(os.getenv('APPDATA'))) .. '/epspdf'
+ epsdir = fw(os.getenv('APPDATA')) .. '/epspdf'
else
epsdir = os.getenv('HOME')..'/.epspdf'
end
--- dbg('epsdir: '..epsdir)
rcfile = epsdir .. '/config'
logfile = epsdir .. '/epspdf.log'
@@ -2166,26 +2186,29 @@ end -- if lfs...logfile
write_log('epspdf '..table.concat(arg, ' '))
---[[ some debug output
-
-dbg ('os is ' .. os.type .. ' and ' .. os.name)
-dbg ('texlua in ' .. os.selfdir)
-dbg('Ghostscript: ' .. gs_prog)
---]]
-
--[[
settings, initial values
-The values in the settings array have lowest priority - lower than
-autodetect and command-line options. We go for false rather than
-undefined, because this results in an actual settings entry.
+priority, from low to high:
+
+- built-in defaults
+- settings read from and written to the configuration file
+- command-line options, defined in the opts table
+
+The options- and settings tables are initialized from built-in
+defaults. We go for false rather than undefined, because this
+results in an actual settings- or options entry.
+
+Command-line options are copied to either options or settings.
We ignore illegal settings in the config file.
--]]
pdf_targets = {'screen', 'ebook', 'printer', 'prepress', 'default'}
-pdf_versions = {'1.2', '1.3', '1.4', 'default'}
+pdf_versions = {'1.2', '1.3', '1.4', '1.5', '1.6', '1.7', 'default'}
+-- ghostscript will substitute higher versions with
+-- the highest-supported version
settings = {}
descriptions = {}
@@ -2196,30 +2219,12 @@ descriptions.pdf_target = 'One of ' .. join(pdf_targets, ', ', ' or ')
settings.pdf_version = 'default'
descriptions.pdf_version = 'One of ' .. join(pdf_versions, ', ', ' or ')
---[[
--- is bb_spread still a useful setting?
--- look at gs options wrt boundingbox
--- settings.bb_spread = 1
--- descriptions.bb_spread = 'Safety margin in points for (low-res) boundingbox'
-
-settings.use_hires_bb = false
--- descriptions.use_hires_bb = 'Use high-resolution boundingbox if available'
--- Ignored; hires bb always used
---]]
-
--- because pdftops_prog is sometimes configurable, it is stored in settings.
--- it will not be used for TeX Live and only be read and written on Windows.
-
-settings.pdftops_prog = false
-descriptions.pdftops_prog =
- 'Full path to pdftops.exe (not used if TeX bin dir contains pdftops)'
-
settings.use_pdftops = true
descriptions.use_pdftops = 'Use pdftops if available'
-- epspdf stores ps- and pdf viewer settings on behalf of the gui interface
-- but does not use them itself.
--- They won't be used at all under osx or windows.
+-- They will not be used at all under macos or windows.
settings.ps_viewer = false
descriptions.ps_viewer =
@@ -2227,13 +2232,13 @@ descriptions.ps_viewer =
settings.pdf_viewer = false
descriptions.pdf_viewer =
- 'Epspdftk: viewer for pdf files; not used on Windows or OS X'
+ 'Epspdftk: viewer for pdf files; not used on Windows or Mac OS'
-- default_dir, which is used on all platforms, is only for the gui.
if os.type == 'windows' then
settings.default_dir =
- string.gsub(ep_shortname(os.getenv('USERPROFILE')), '\\', '/')
+ string.gsub(os.getenv('USERPROFILE'), '\\', '/')
else
settings.default_dir = os.getenv('HOME')
end
@@ -2257,6 +2262,7 @@ options.type = false -- implied via output filename on command line
-- We could make these `class attributes' for PsPdf but to what purpose?
-- For Windows shell commands, we need to substitute `#' for `='
-- when invoking Ghostscript. For simplicity, we do this across the board.
+-- -P- : do not look first in current directory
gs_options = {'-q', '-dNOPAUSE', '-dBATCH', '-P-', '-dSAFER'}
@@ -2273,25 +2279,18 @@ pdftops = false
-- gets a value only if we are going to use pdftops
ps_options = {'-level3'}
--- may add custom options later
-- `main program' inside scope-creating block ----------------------
do -- main program
- local infile = false
local in_dir = false -- directory of infile
- local outfile = false
local out_dir = false -- directory of outfile
- -- dbg('\nSettings are:\n')
- for k,v in pairs(settings) do dbg(k .. ' = ' .. tostring(v)) end
-
do -- Handle settings and command-line inside nested scope --------------
read_settings(rcfile)
- -- dbg('Defining cmdline options')
local opts = {}
opts.page = {
@@ -2345,16 +2344,6 @@ do -- main program
help = descriptions.pdf_version
}
- if os.type=='windows' and not auto_psp then
- opts.pdftops_prog = {
- type = 'string', val = nil,
- forms = {'--pdftops'},
- placeholder = 'PATH',
- negforms = nil,
- help = descriptions.pdftops_prog
- }
- end
-
opts.use_pdftops = {
type = 'boolean', val = nil,
forms = {'-U'},
@@ -2407,22 +2396,24 @@ do -- main program
-- a couple of functions only available during command-line parsing
local function show_version ()
- print('Epspdf version '..ep_version..'\nCopyright (c) '
- ..ep_copyright..' Siep Kroonenberg')
+ print('Epspdf version 0.6.4\nCopyright (c) 2006-2019 Siep Kroonenberg')
end
local function help (mess) -- requires opts array
- if mess then print(mess..eol) end
+ if mess then
+ print(mess..eol)
+ end
show_version()
-- below, string.gsub unindents its long-string parameter.
-- string.format removes the second return value of string.gsub.
- print(string.format('%s', string.gsub([[
+ print( -- string.format('%s',
+ string.gsub([[
Convert between [e]ps and pdf formats
Usage: epspdf[.tlu] [options] infile [outfile]
Default for outfile is file.pdf if infile is file.eps or file.ps
Default for outfile is file.eps if infile is file.pdf
- ]], '([\r\n]+) ', '%1')))
+ ]], '([\r\n]+) ', '%1')) --)
-- need to enforce an ordering, otherwise we could have used pairs(opts)
-- omitted below: no-op options
@@ -2431,31 +2422,28 @@ do -- main program
local intent_sp = string.rep(' ', indent_n)
local indent_fmt = '%-' .. tostring(indent_n) .. 's'
for _, o in ipairs({'page', 'gray', 'bbox', 'pdf_target', 'pdf_version',
- 'pdftops_prog', 'use_pdftops', 'save', 'info', 'debug',
- 'version', 'help'}) do
+ 'use_pdftops', 'save', 'info', 'debug', 'version', 'help'}) do
local v = opts[o]
- if v~='pdftops_prog' or not auto_psp then
- if v and v.help then
- local synt = join(v.forms, ', ')
- if v.type ~= 'boolean' then synt = synt .. ' ' .. v.placeholder end
+ if v and v.help then
+ local synt = join(v.forms, ', ')
+ if v.type ~= 'boolean' then synt = synt .. ' ' .. v.placeholder end
+ if string.len(synt)<indent_n then
+ print(string.format(indent_fmt, synt) .. v.help)
+ else
+ print(synt)
+ print(intent_sp .. v.help)
+ end
+ if v.negforms then
+ local neghelp = 'Reverses the above'
+ synt = join(v.negforms, ', ')
if string.len(synt)<indent_n then
- print(string.format(indent_fmt, synt) .. v.help)
+ print(string.format(indent_fmt, synt) .. neghelp)
else
print(synt)
- print(intent_sp .. v.help)
- end
- if v.negforms then
- local neghelp = 'Reverses the above'
- synt = join(v.negforms, ', ')
- if string.len(synt)<indent_n then
- print(string.format(indent_fmt, synt) .. neghelp)
- else
- print(synt)
- print(intent_sp .. neghelp)
- end
+ print(intent_sp .. neghelp)
end
end
- end -- ~=pdftops_prog or not auto_psp
+ end
end -- for
if mess then os.exit(1) else os.exit() end
end -- help
@@ -2471,7 +2459,6 @@ do -- main program
local i=1
while i<=#arg and string.sub(arg[i],1,1)=='-' do
- -- dbg('parse argument '..tostring(i)..': '..arg[i])
local parsed = false
local kk, vv = string.match(arg[i],'([^=]+)=(.*)$')
if kk==nil then
@@ -2480,7 +2467,6 @@ do -- main program
vv = strip_outer_spaces(vv)
end
for p, o in pairs(opts) do
- -- dbg(' try '..p)
if in_list(kk, o.forms) or in_list(kk, o.negforms) then
parsed = true
if o.type == 'boolean' then
@@ -2506,29 +2492,6 @@ do -- main program
i = i + 1
end -- while
- -- some debug output
-
- --[[
- if i<=#arg then
- dbg('non-option arguments:')
- for j=i,#arg do dbg(arg[j]) end
- dbg(eol)
- else
- dbg('no non-option arguments')
- end
-
- for i=1,#arg do dbg(arg[i]) end
-
- dbg(eol..'Options from command-line:')
- for p, o in pairs(opts) do
- if o.val==nil then
- dbg(p..': undefined')
- else
- dbg(p..': '..tostring(o.val))
- end
- end
- --]]
-
-- check and interpret opts.
-- Copy to either settings or to options table.
-- at syntax error, abort via help function.
@@ -2594,34 +2557,14 @@ do -- main program
end
end
- -- pdftops program; pdftops has already been been initialized to false
-
- -- pdftops_prog as command-line option
- if os.type=='windows' and not auto_psp and
- opts.pdftops_prog.val then
- settings.pdftops_prog = is_prog(opts.pdftops_prog.val)
- end
-
- -- pdftops should be on the path. otherwise, make it so if possible.
- if os.type=='windows' and not auto_psp then
- if settings.use_pdftops then
- pdftops = is_prog(settings.pdftops_prog)
- if pdftops then
- -- strip path and modify searchpath, to avoid paths with spaces
- maybe_add_path(string.gsub(pdftops, '[\\/][^\\/]*$', ''), 'append')
- pdftops = string.gsub(settings.pdftops_prog, '^.*[\\/]', '')
- end
- end
- elseif os.type=='windows' then
- if settings.use_pdftops then
- pdftops = 'pdftops.exe'
- end
- else
- if settings.use_pdftops then
+ -- pdftops should be on the path
+ if settings.use_pdftops then
+ if os.type=='windows' then
+ pdftops = find_on_path('pdftops.exe')
+ else
pdftops = find_on_path('pdftops')
end
end
- -- dbg('Option handling; pdftops is '..tostring(pdftops))
-- other options
@@ -2675,58 +2618,25 @@ do -- main program
-- one final quick option
if opts.info.val then
- info(infile)
- end
-
- -- add pdf_version and pdf_target to the options array,
- -- from where it will be set to false when realized
- if settings.pdf_target == 'default' then
- options.pdf_target = false
- else
- options.pdf_target = settings.pdf_target
+ info()
end
- if settings.pdf_version == 'default' then
- options.pdf_version = false
- else
- options.pdf_version = tonumber(settings.pdf_version)
- end
-
- end -- decoding command-line
- --[[
- dbg('After command-line processing\n Settings')
- -- print settings- and options array with dbg
- for k, v in pairs(settings) do
- dbg(k..': '..tostring(v))
- end
- dbg(' Options')
- for k, v in pairs(options) do
- dbg(k..': '..tostring(v))
- end
- --]]
+ end -- do (decoding command-line)
--[[
Once it becomes clear that real work needs to be done,
we shall create a temp directory.
- because of gs -dSAFER restrictions, infile must be in (a
- subdirectory of) the directory of the output file, e.g. in the
- temp directory.
-
- Also because of -dSAFER, we copy infile to the temp directory of
- it is not in the same directory as outfile.
+ because of gs -dSAFER restrictions, infile must be in (a
+ subdirectory of) the directory of the output file, e.g. in the
+ temp directory. So we copy infile to the temp directory.
--]]
- source = io.open(infile)
- if not source then
- error(infile .. ' not readable')
+ if not lfs.isfile(infile) or lfs.attributes(infile, 'size')==0 then
+ errror(infile..' does not exist or is empty.')
end
- source:close()
- -- if options.debug then
- -- warn('in: '..infile..'\nout: '..outfile..'\n\ncwd: '..cwd)
- -- end
infile, in_dir = absolute_path(infile)
outfile, out_dir = absolute_path(outfile)
if not out_dir then
@@ -2746,58 +2656,36 @@ do -- main program
tempdir = os.tmpdir()
if not tempdir then
errror('Cannot create directory for temporary files')
- else
- -- dbg('temp directory '..tempdir)
end
lfs.chdir(tempdir)
+ -- determine filetype from first few bytes of file
intype = identify(infile)
-- remaining cases: want a real conversion
if not intype then
- error(infile..' has an unsupported filetype')
+ errror(infile..' has an unsupported filetype')
end
- --sanity check on output filetype
+ -- valid output filetype?
options.type = string.match(outfile, '.*%.([^%.]+)$')
- if not options.type or (options.type~='ps'
- and options.type~='eps' and options.type~='pdf') then
+ if not options.type or (options.type~='ps' and
+ options.type~='eps' and options.type~='pdf') then
errror('Output file '..outfile..
' should have extension .eps, .ps or .pdf')
end
if outfile==infile then
- local insave = infile .. '.luasave'
- move_or_copy(infile, insave)
- infile = insave
- end
-
- -- had some trouble under msw when removing outfile later so try it now
- if lfs.isfile(outfile) then
- os.remove(outfile)
- -- if removal fails but outfile is overwritable then no real problem
+ slice_file(infile, infile..'.backup')
+ -- copy, not move, since outfile may be infile if there was nothing to do
end
- local fout = io.open(outfile, 'wb')
- if not fout then
- errror('Output file '..outfile..' not writable; aborting')
- else
- fout:close()
+ result = any_to_any(infile, outfile)
+ if not lfs.isfile(outfile) or lfs.attributes(outfile, 'size')==0 then
+ errror('Failed to generate '..outfile)
end
- source = PsPdf:from_path(infile)
- dest = source:any_to_any()
- if not lfs.isfile(dest.path) then
- errror('Failed to generate '..dest.path)
- end
- write_log('Copying or moving '..dest.path..' to '..outfile)
- move_or_copy(dest.path, outfile)
if not options.debug then
cleantemp()
end
- if lfs.isfile(outfile) and lfs.attributes(outfile, 'size')>0 then
- os.exit()
- else
- errror('Conversion failed')
- end
end
diff --git a/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl b/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl
index 7e4a7bc4c3c..26ed17efcfd 100755
--- a/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl
+++ b/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl
@@ -3,8 +3,8 @@
# epspdf conversion utility, GUI frontend
#####
-# Copyright (C) 2006, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016 Siep Kroonenberg
-# n dot s dot kroonenberg at rug dot nl
+# Copyright (C) 2006-2019 Siep Kroonenberg
+# siepo at bitmuis dot nl
#
# This program is free software, licensed under the GNU GPL, >=2.0.
# This software comes with absolutely NO WARRANTY. Use at your own risk!
@@ -12,13 +12,15 @@
package require Tk
+if {[string index $::tcl_patchLevel 2] <5} {
+ tk_messageBox -message "Tcl/Tk version >= 8.5 required;\naborting..."
+ exit
+}
+
# tired of writing out this combined test again and again
set classic_unix [expr {$::tcl_platform(platform) eq "unix" && \
$::tcl_platform(os) ne "Darwin"}]
-# combo boxes and -ignorestderr introduced in Tk8.5
-set ge_85 [expr {[string index $::tcl_patchLevel 2] > 4}]
-
# normally, epspdf.tlu should be in the same directory
# and texlua should be on the searchpath.
# However, the Windows installer version wraps this script in a starpack.
@@ -35,7 +37,7 @@ proc write_log {s} {
}
}
-### calling epspdf.tlu #########################
+### invoking epspdf.tlu #########################
# Get full path of epspdf.tlu. It should be in the same directory as
# either this script or of the starpack containing this script,
@@ -60,6 +62,13 @@ proc set_progs {} {
set eproot [file dirname [file normalize [info nameofexecutable]]]
set ::epspdf_tlu [file join $eproot "epspdf.tlu"]
}
+ if {$::tcl_platform(platform) eq "windows"} {
+ # For actual conversions, epspdf invokes third-party command-line programs.
+ # To prevent the appearing of transient black console windows for such
+ # invocations, epspdf will be invoked via a batchfile.
+ # This is not necessary for getting info about a file.
+ set ::epspdf_cmd [file join $eproot "epspdf4tk.cmd"]
+ }
if {! [file exists $::epspdf_tlu]} {
tk_messageBox -type ok -icon error -message "Epspdf.tlu not found"
exit 1
@@ -95,9 +104,6 @@ proc is_prog {x} {
# create a global empty settings array
array set ::settings [list]
-# windows: pdftops from tex distro?
-set ::psp_config 0
-
# ask epspdf.tlu for currently configured settings.
# this does not include automatically configured or transient settings.
# the availability of viewers is handled here.
@@ -107,10 +113,6 @@ proc getsettings {} {
}
# write_log "settings from epspdf.tlu:\n$set_str\n"
set l [split $set_str "\r\n"]
- if {$::tcl_platform(platform) eq "windows"} {
- set ::psp_config 0
- set settings(pdftops_prog) ""
- }
foreach e $l {
# puts "settings: $e"
# $e is either a string "var = value"
@@ -122,13 +124,8 @@ proc getsettings {} {
set val [string trim [string range $e [expr $i+1] end]]
if {$val eq "true"} {set val 1}
if {$val eq "false"} {set val 0}
- if {$para eq "psp_config"} {
- set ::psp_config 1
- write_log "TL for Windows not detected by epspdf"
- } else {
- set ::settings($para) $val
- # write_log "setting $para is $val"
- }
+ set ::settings($para) $val
+ # write_log "setting $para is $val"
}
}
@@ -151,7 +148,7 @@ proc getsettings {} {
if {$::settings(pdf_viewer) ne "" && [is_prog $::settings(pdf_viewer)]} {
lappend ::pdf_viewers $::settings(pdf_viewer)
}
- foreach v {evince okular xpdf epdfview qpdfview zathura acroread gv} {
+ foreach v {evince okular mupdf qpdfview texworks xpdf zathura} {
if {$v ne $::settings(pdf_viewer) && [is_prog $v]} {
lappend ::pdf_viewers $v
}
@@ -224,154 +221,25 @@ proc viewable {} {
### groundwork for GUI #################################
# padding for buttons and frames
-proc packb {b args} {
- eval [linsert $args 0 pack $b -padx 3 -pady 3]
- $b configure -border 2
-}
-
-proc packf {f args} {
- eval [linsert $args 0 pack $f -padx 3 -pady 3]
-}
-
-# dummy widget for vertical spacing
-set idummy -1
-proc spacing {w} {
- incr ::idummy
- pack [label $w.$::idummy -text " "]
-}
+proc ppack {b args} {pack $b {*}$args -padx 3 -pady 3}
# bold font
-font create boldfont
-# create dummy label widget to get at the default label font properties
-label .dummy
-font configure boldfont -family [font actual [.dummy cget -font] -family]
-font configure boldfont -size [font actual [.dummy cget -font] -size]
-font configure boldfont -weight bold
-destroy .dummy
-
-# bitmaps for mycombo (no combobox in core tk8.4)
-image create bitmap dwnarrow -data {
-#define dwnarrow_width 15
-#define dwnarrow_height 10
-static unsigned char dwnarrow_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, 0x03,
- 0xc0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 };
-}
-
-image create bitmap uparrow -data {
-#define uparrow_width 15
-#define uparrow_height 10
-static unsigned char uparrow_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07,
- 0xf8, 0x0f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00 };
-}
-
-# mycombo
-if {$::ge_85} {
- proc update_combo {w vls} {
- upvar $vls vs
- set new [$w get]
- if {$new ni $vs} {
- if {[is_prog $new]} {
- set vs [linsert $vs 0 $new]
- $w configure -values $vs
- } else {
- tk_messageBox -title Error -icon error -message "$vl Not a program"
- }
- }
- }
-} else {
- proc mycombo {w} {
- # entry widget and dropdown button
- frame $w
- frame $w.ef
- entry $w.ef.e -width 30 -borderwidth 1
- pack $w.ef.e -side left
- button $w.ef.b -image dwnarrow -command "toggle_list $w" -borderwidth 1
- pack $w.ef.b -side right
- pack $w.ef
- # 'drop-down' listbox; width should match entry widget above
- toplevel $w.lf -bd 0
- listbox $w.lf.l -yscrollcommand "$w.lf.s set" -height 4 -width 30 \
- -bd 1 -relief raised
- grid $w.lf.l -column 0 -row 0 -sticky news
- scrollbar $w.lf.s -command "$w.lf.l yview" -bd 1
- grid $w.lf.s -column 1 -row 0 -sticky ns
- grid columnconfigure $w.lf 0 -weight 1
- wm overrideredirect $w.lf 1
- wm transient $w.lf
- wm withdraw $w.lf
- # next two bindings:
- # final parameter: unmap/toggle listbox
- bind $w.lf.l <KeyRelease-space> {update_e %W [%W get active] 1}
- bind $w.lf.l <KeyRelease-Tab> {update_e %W [%W get active] 1}
- bind $w.lf.l <1> {update_e %W [%W index @%x,%y] 0}
- bind $w.lf.l <Double-1> {update_e %W [%W index @%x,%y] 1}
- bind $w.ef.e <Return> {update_l %W}
- bind $w.ef.e <Tab> {update_l %W}
- return $w
- }
-
- # toggle state of listbox.
- # this involves calculating the place where it should appear
- # and toggling the arrow image.
- proc toggle_list {w} {
- # $w.ef is the frame with the entry widget
- # $w.lf is the toplevel with the listbox
- # which needs to turn up right below $w.ef
- if {[wm state $w.lf] eq "withdrawn" || [wm state $w.lf] eq "iconified"} {
- set lfx [winfo rootx $w.ef]
- set lfy [expr [winfo rooty $w.ef] + [winfo height $w.ef]]
- wm geometry $w.lf [format "+%d+%d" $lfx $lfy]
- wm deiconify $w.lf
- $w.ef.b configure -image uparrow
- } else {
- wm withdraw $w.lf
- $w.ef.b configure -image dwnarrow
- }
- }
-
- # note: in tcl/tk 8.5, values of (some) widget variables can be accessed
- # directly and explicit use of upvar is unnecessary.
-
- # list => entry; optionally toggle list display
- proc update_e {v i toggle} {
- set w [winfo parent [winfo parent $v]]
- set lv [$w.lf.l cget -listvariable]
- upvar $lv l
- set tv [$w.ef.e cget -textvariable]
- upvar $tv t
- set t [lindex $l $i]
- if {$toggle} {toggle_list $w}
- }
-
- # entry => list
- proc update_l {v} {
- set w [winfo parent [winfo parent $v]]
- set tv [$w.ef.e cget -textvariable]
- upvar $tv t
- set lv [$w.lf.l cget -listvariable]
- upvar $lv l
- set found [lsearch $l $t]
- if { $found < 0} {
- set ok [$v validate]
- if {$ok} {
- lappend l $t
- set l [lsort $l]
- } else {
- tk_messageBox -message "Not a program"
- }
+font create bfont {*}[font configure TkDefaultFont]
+font configure bfont -weight bold
+
+proc update_combo {w vls} {
+ upvar $vls vs
+ set new [$w get]
+ if {$new ni $vs} {
+ if {[is_prog $new]} {
+ set vs [linsert $vs 0 $new]
+ $w configure -values $vs
+ } else {
+ tk_messageBox -title Error -icon error -message "$vl Not a program"
}
- set the_index [lsearch $l $t]
- $w.lf.l see $the_index
- $w.lf.l activate $the_index
- wm withdraw $w.lf
- $w.ef.b configure -image dwnarrow
}
}
-# end mycombo
-
### and now the actual GUI ###################################
wm title . "PostScript- and pdf conversions"
@@ -398,13 +266,14 @@ proc readhelp {} {
toplevel .help_t
wm title .help_t "EpsPdf help"
-packb [button .help_t.done -text "Close" -command {wm withdraw .help_t}] \
- -side bottom -anchor e -padx 50
+pack [ttk::frame .help_t.bg -padding 3] -fill both -expand 1
+ppack [ttk::button .help_t.done -text "Close" -command {wm withdraw .help_t}] \
+ -in .help_t.bg -side bottom -anchor e -padx 50
text .help_t.text -wrap word -width 60 -height 20 -setgrid 1 \
-yscrollcommand ".help_t.scroll set"
-scrollbar .help_t.scroll -command ".help_t.text yview"
-pack .help_t.scroll -side right -fill y
-pack .help_t.text -expand 1 -fill both
+ttk::scrollbar .help_t.scroll -command ".help_t.text yview"
+pack .help_t.scroll -in .help_t.bg -side right -fill y
+pack .help_t.text -in .help_t.bg -expand 1 -fill both
readhelp
.help_t.text configure -state disabled
wm withdraw .help_t
@@ -413,13 +282,14 @@ wm withdraw .help_t
toplevel .log_t
wm title .log_t "Epspdf log"
-packb [button .log_t.b -text "Close" -command {wm withdraw .log_t}] \
- -side bottom -anchor e -padx 50
+pack [ttk::frame .log_t.bg -padding 3] -fill both -expand 1
+ppack [ttk::button .log_t.b -text "Close" -command {wm withdraw .log_t}] \
+ -in .log_t.bg -side bottom -anchor e -padx 50
text .log_t.text -wrap word -relief flat -height 15 -width 60 \
-setgrid 1 -yscrollcommand ".log_t.scroll set"
-scrollbar .log_t.scroll -command ".log_t.text yview"
-pack .log_t.scroll -side right -fill y
-pack .log_t.text -expand 1 -fill both
+ttk::scrollbar .log_t.scroll -command ".log_t.text yview"
+pack .log_t.scroll -in .log_t.bg -side right -fill y
+pack .log_t.text -in .log_t.bg -expand 1 -fill both
.log_t.text configure -state disabled
wm withdraw .log_t
@@ -427,6 +297,7 @@ wm withdraw .log_t
toplevel .config_t
wm title .config_t "Configure epspdf"
+pack [ttk::frame .config_t.bg -padding 3] -fill both -expand 1
# The settings array is edited directly in the configuration screen:
# for most control widgets, we need a global external variable anyway,
@@ -438,120 +309,66 @@ wm title .config_t "Configure epspdf"
# viewers (not on windows or osx)
if {$::classic_unix} {
- packf [frame .config_t.viewf] -ipadx 4 -fill x
- grid [label .config_t.viewf.title -font boldfont -text "Viewers"] \
+ ppack [ttk::frame .config_t.viewf] -in .config_t.bg -ipadx 4 -fill x
+ grid [ttk::label .config_t.viewf.title -font bfont -text "Viewers"] \
-row 0 -column 0 -sticky w
- grid [label .config_t.viewf.lb_pdf -text "Pdf"] \
+ grid [ttk::label .config_t.viewf.lb_pdf -text "Pdf"] \
-row 1 -column 0 -sticky w
- grid [label .config_t.viewf.lb_ps -text "PostScript"] \
+ grid [ttk::label .config_t.viewf.lb_ps -text "PostScript"] \
-row 2 -column 0 -sticky w
- if {$::ge_85} {
- grid [ttk::combobox .config_t.viewf.pdf] -row 1 -column 1 -sticky e
- .config_t.viewf.pdf configure -values $::pdf_viewers
- .config_t.viewf.pdf configure -textvariable ::settings(pdf_viewer)
- bind .config_t.viewf.pdf <Return> {update_combo %W $::pdf_viewers}
- grid [ttk::combobox .config_t.viewf.ps] -row 2 -column 1 -sticky e
- .config_t.viewf.ps configure -values $::ps_viewers
- .config_t.viewf.ps configure -textvariable ::settings(ps_viewer)
- bind .config_t.viewf.ps <Return> {update_combo %W $::ps_viewers}
- } else {
- grid [mycombo .config_t.viewf.pdf] -row 1 -column 1 -sticky e
- .config_t.viewf.pdf.lf.l configure -listvariable ::pdf_viewers
- .config_t.viewf.pdf.ef.e configure -textvariable ::settings(pdf_viewer)
- .config_t.viewf.pdf.ef.e configure -vcmd {is_prog %P} -validate none
- grid [mycombo .config_t.viewf.ps] -row 2 -column 1 -sticky e -pady 4
- .config_t.viewf.ps.lf.l configure -listvariable ::ps_viewers
- .config_t.viewf.ps.ef.e configure -textvariable ::settings(ps_viewer)
- .config_t.viewf.ps.ef.e configure -vcmd {is_prog %P} -validate none
- }
+ grid [ttk::combobox .config_t.viewf.pdf] -row 1 -column 1 -sticky e
+ .config_t.viewf.pdf configure -values $::pdf_viewers
+ .config_t.viewf.pdf configure -textvariable ::settings(pdf_viewer)
+ bind .config_t.viewf.pdf <Return> {update_combo %W $::pdf_viewers}
+ grid [ttk::combobox .config_t.viewf.ps] -row 2 -column 1 -sticky e
+ .config_t.viewf.ps configure -values $::ps_viewers
+ .config_t.viewf.ps configure -textvariable ::settings(ps_viewer)
+ bind .config_t.viewf.ps <Return> {update_combo %W $::ps_viewers}
grid columnconfigure .config_t.viewf 1 -weight 1 -pad 2
-
- spacing .config_t
}
# settings for conversion to pdf
-packf [frame .config_t.pdff] -ipadx 4 -fill x
-pack [label .config_t.pdff.title -font boldfont -text "Conversion to pdf"] \
+ppack [ttk::frame .config_t.pdff] \
+ -in .config_t.bg -ipadx 4 -fill x -pady [list 10 0]
+pack [ttk::label .config_t.pdff.title -font bfont -text "Conversion to pdf"] \
-anchor w
-pack [label .config_t.pdff.l_target -text "Target use"] -anchor w
-pack [frame .config_t.pdff.f_targets] -fill x
+pack [ttk::label .config_t.pdff.l_target -text "Target use"] -anchor w
+pack [ttk::frame .config_t.pdff.f_targets] -fill x
foreach t {default printer prepress screen ebook} {
- pack [radiobutton .config_t.pdff.f_targets.$t \
+ pack [ttk::radiobutton .config_t.pdff.f_targets.$t \
-variable ::settings(pdf_target) \
-text $t -value $t] -side left -padx 2 -pady 4 -anchor w
}
-pack [label .config_t.pdff.l_version -text "Pdf version"] -anchor w
-pack [frame .config_t.pdff.f_version] -fill x
-foreach t {1.2 1.3 1.4 default} {
+pack [ttk::label .config_t.pdff.l_version -text "Pdf version"] -anchor w
+pack [ttk::frame .config_t.pdff.f_version] -fill x
+foreach t {1.2 1.3 1.4 1.5 1.6 1.7 default} {
regsub {\.} $t _ tp ; # replace dot in name: dots are path separators!
- pack [radiobutton .config_t.pdff.f_version.$tp \
+ pack [ttk::radiobutton .config_t.pdff.f_version.$tp \
-variable ::settings(pdf_version) \
-text $t -value $t] -side left -padx 2 -pady 4 -anchor w
}
-#pack [label .config_t.pdff.l_gs \
-# -text "Custom Ghostscript/ps2pdf parameters"] -anchor w
-#pack [entry .config_t.pdff.e_gs -border 1] -fill x -padx 2 -pady 2
-#.config_t.pdff.e_gs configure -textvariable settings(pdf_custom)
-
-spacing .config_t
-
# settings for conversion to EPS and PostScript
-packf [frame .config_t.psf] -ipadx 4 -fill x
-pack [label .config_t.psf.l_ps -text "Conversion to EPS and PostScript" \
- -font boldfont] -anchor w
-if { $::psp_config} {
- if {[string tolower [string range $::settings(pdftops_prog) end-3 end]] ne \
- ".exe"} {set ::settings(pdftops_prog) ""}
- pack [label .config_t.psf.l_pdftops -text "Find pdftops"] -anchor w
- pack [frame .config_t.psf.findf] -anchor w
- pack [entry .config_t.psf.findf.e -width 40] -side left -padx 4
- .config_t.psf.findf.e configure -textvariable ::settings(pdftops_prog)
- packb [button .config_t.psf.findf.b -text "Browse..." \
- -command find_pdftops] -side left
-}
-
-proc find_pdftops {} {
- set try [tk_getOpenFile -title "Find pdftops.exe" \
- -filetypes {{"Programs" {.exe}}} -initialdir "c:/"]
- if {$try ne ""} {
- .config_t.psf.findf.e delete 0 end
- .config_t.psf.findf.e insert 0 $try
- }
-}
+ppack [ttk::frame .config_t.psf] \
+ -in .config_t.bg -pady [list 10 0] -ipadx 4 -fill x
+pack [ttk::label .config_t.psf.l_ps -text "Conversion to EPS and PostScript" \
+ -font bfont] -anchor w
-pack [checkbutton .config_t.psf.c \
- -text "Use pdftops if available (recommended)"] -anchor w
+pack [ttk::checkbutton .config_t.psf.c \
+ -text "Use pdftops if available"] -anchor w
.config_t.psf.c configure -variable ::settings(use_pdftops) \
-onvalue 1 -offvalue 0
-spacing .config_t
-
-## hires boundingbox setting
-#
-#packf [frame .config_t.hiresf] -ipadx 4 -fill x
-#pack [label .config_t.hiresf.title -font boldfont -text "Hires BoundingBox"] \
-# -anchor w
-#pack [label .config_t.hiresf.l -text "Uncheck to prevent clipping"] \
-# -anchor w
-#
-#pack [checkbutton .config_t.hiresf.c \
-# -text "Use hires boundingbox if possible"] -anchor w
-#.config_t.hiresf.c configure \
-# -variable ::settings(ignore_hires_bb) -onvalue 0 -offvalue 1
-#
-#spacing .config_t
-
# buttons for closing the configuration screen
-pack [frame .config_t.buttonsf] -fill x
-packb [button .config_t.buttonsf.done -text "Done" -command putsettings] \
+pack [ttk::frame .config_t.buttonsf] -in .config_t.bg -pady [list 10 0] -fill x
+ppack [ttk::button .config_t.buttonsf.done -text "Done" -command putsettings] \
-side right
-packb [button .config_t.buttonsf.cancel -text "Cancel" \
+ppack [ttk::button .config_t.buttonsf.cancel -text "Cancel" \
-command cancelsettings] -side right
wm transient .config_t
@@ -566,20 +383,12 @@ proc edit_settings {} {
# store new settings
proc putsettings {} {
- if {$::classic_unix && ! $::ge_85} {
- wm withdraw .config_t.viewf.pdf.lf
- wm withdraw .config_t.viewf.ps.lf
- }
wm withdraw .config_t
grab release .config_t
write_settings
}
proc cancelsettings {} {
- if {$::classic_unix && ! $::ge_85} {
- wm withdraw .config_t.viewf.pdf.lf
- wm withdraw .config_t.viewf.ps.lf
- }
wm withdraw .config_t
grab release .config_t
# re-read config file / reg entries
@@ -593,93 +402,91 @@ proc show_w {w} {
raise $w
}
+pack [ttk::frame .bg -padding 3] -fill both -expand 1
+
# buttons to call up configure-, log- and help screens
-pack [frame .topf] -fill x
-packb [button .topf.config_t -text "Configure" \
+pack [ttk::frame .topf] -in .bg -fill x
+ppack [ttk::button .topf.config_t -text "Configure" \
-command edit_settings] -side left
-packb [button .topf.help_t -text "Help" \
+ppack [ttk::button .topf.help_t -text "Help" \
-command {show_w .help_t}] -side right
-packb [button .topf.logb -text "Show log" -command {show_w .log_t}] \
+ppack [ttk::button .topf.logb -text "Show log" -command {show_w .log_t}] \
-side right -anchor w
# file info in grid layout
-packf [frame .infof -relief sunken -border 1] -fill x
-grid [label .infof.dir_label -text "Directory" -anchor w] \
+ppack [ttk::frame .infof -relief sunken -border 1 -padding 3] -in .bg -fill x
+grid [ttk::label .infof.dir_label -text "Directory" -anchor w] \
-row 1 -column 1 -sticky w
-grid [label .infof.dir_value -textvariable ::gfile(dir) -anchor w] \
+grid [ttk::label .infof.dir_value -textvariable ::gfile(dir) -anchor w] \
-row 1 -column 2 -sticky w
-grid [label .infof.name_label -text "File" -anchor w] \
+grid [ttk::label .infof.name_label -text "File" -anchor w] \
-row 2 -column 1 -sticky w
-grid [label .infof.name_value -textvariable ::gfile(name) -anchor w] \
+grid [ttk::label .infof.name_value -textvariable ::gfile(name) -anchor w] \
-row 2 -column 2 -sticky w
-grid [label .infof.type_label -text "Type" -anchor w] \
+grid [ttk::label .infof.type_label -text "Type" -anchor w] \
-row 3 -column 1 -sticky w
-grid [label .infof.type_value -textvariable ::gfile(type) -anchor w] \
+grid [ttk::label .infof.type_value -textvariable ::gfile(type) -anchor w] \
-row 3 -column 2 -sticky w
-grid [label .infof.npages_label -text "Pages" -anchor w] \
+grid [ttk::label .infof.npages_label -text "Pages" -anchor w] \
-row 4 -column 1 -sticky w
-grid [label .infof.npages_value -textvariable ::gfile(npages) -anchor w] \
+grid [ttk::label .infof.npages_value -textvariable ::gfile(npages) -anchor w] \
-row 4 -column 2 -sticky w
grid columnconfigure .infof 1 -weight 1 -pad 2
grid columnconfigure .infof 2 -weight 3 -pad 2
-spacing .
-
# conversion options
-pack [frame .optsf] -fill x
+pack [ttk::frame .optsf] -in .bg -pady [list 10 0] -fill x
# grayscaling
-pack [frame .optsf.gray] -side left -anchor nw
-pack [label .optsf.gray.l -text "Grayscaling"] -anchor w
-pack [radiobutton .optsf.gray.off -text "No color conversion" \
+pack [ttk::frame .optsf.gray] -side left -anchor nw
+pack [ttk::label .optsf.gray.l -text "Grayscaling"] -anchor w
+pack [ttk::radiobutton .optsf.gray.off -text "No color conversion" \
-variable ::options(gray) -value "color"] -anchor w
-pack [radiobutton .optsf.gray.gray -text "Grayscale" \
+pack [ttk::radiobutton .optsf.gray.gray -text "Grayscale" \
-variable ::options(gray) -value "gray"] -anchor w
-#pack [radiobutton .optsf.gray.gRAY -text "Try harder to grayscale" \
-# -variable ::options(gray) -value "gRAY"] -anchor w
# output format
-pack [label .optsf.format] -side right -anchor ne
-pack [label .optsf.format.l -text "Output format"] -anchor w
-pack [radiobutton .optsf.format.pdf -text "pdf" -command set_widget_states \
- -variable ::options(format) -value "pdf"] -anchor w
-pack [radiobutton .optsf.format.eps -text "eps" -command set_widget_states \
- -variable ::options(format) -value "eps"] -anchor w
-pack [radiobutton .optsf.format.ps -text "ps" -command set_widget_states \
- -variable ::options(format) -value "ps"] -anchor w
-
-spacing .
+pack [ttk::label .optsf.format] -side right -anchor ne
+pack [ttk::label .optsf.format.l -text "Output format"] -anchor w
+ttk::radiobutton .optsf.format.pdf -text "pdf" \
+ -command set_widget_states -variable ::options(format) -value "pdf"
+ttk::radiobutton .optsf.format.eps -text "eps" -command set_widget_states \
+ -variable ::options(format) -value "eps"
+ttk::radiobutton .optsf.format.ps -text "ps" -command set_widget_states \
+ -variable ::options(format) -value "ps"
+pack .optsf.format.pdf -anchor w
+pack .optsf.format.eps -anchor w
+pack .optsf.format.ps -anchor w
# boundingbox
-pack [checkbutton .bbox -text "Compute tight boundingbox" \
- -variable ::options(bbox) -command set_widget_states] -anchor w
+pack [ttk::checkbutton .bbox -text "Compute tight boundingbox" \
+ -variable ::options(bbox) -command set_widget_states] \
+ -in .bg -anchor w -pady [list 10 0]
# page selection
-pack [frame .pagesf] -fill x
-pack [radiobutton .pagesf.all -text "Convert all pages" \
+pack [ttk::frame .pagesf] -in .bg -fill x
+pack [ttk::radiobutton .pagesf.all -text "Convert all pages" \
-variable ::options(pages) -value "all" -command set_widget_states] \
-side left
-pack [radiobutton .pagesf.single -text "Page:" \
+pack [ttk::radiobutton .pagesf.single -text "Page:" \
-variable ::options(pages) -value "single" -command set_widget_states] \
- -side left
+ -side left -padx [list 10 0]
pack [entry .pagesf.e -width 6 -textvariable ::options(page)] -side left
#.pagesf.e configure -vcmd {page_valid %W} -validate focusout \
# -invcmd "focusAndFlash %W [.pagesf.e cget -fg] [.pagesf.e cget -bg]"
.pagesf.e configure -vcmd {page_valid %W} -validate focusout \
-invcmd "see_red %W"
-spacing .
-
# temp files
-pack [checkbutton .clean -text "Remove temp files" \
- -variable ::options(clean)] -anchor w
+pack [ttk::checkbutton .clean -text "Remove temp files" \
+ -variable ::options(clean)] -in .bg -anchor w -pady [list 10 0]
proc focusAndFlash {w fg bg {count 9}} {
focus $w
@@ -720,17 +527,18 @@ proc page_valid {w} {
# end conversion options
-pack [label .status -justify left] -side bottom -anchor w -fill x -expand 1
+pack [ttk::label .status -justify left -text "Idle"] \
+ -in .bg -side bottom -anchor w -fill x -expand 1
# main buttons
-pack [frame .bottomf] -side bottom -fill x
-packb [button .bottomf.view -text "View" -command view] -side left
-packb [button .bottomf.open -text "Open" -command openDialog] \
+pack [ttk::frame .bottomf] -in .bg -side bottom -fill x
+ppack [ttk::button .bottomf.view -text "View" -command view] -side left
+ppack [ttk::button .bottomf.open -text "Open" -command openDialog] \
-side left -padx 2
-packb [button .bottomf.convert -text "Convert and save..." \
+ppack [ttk::button .bottomf.convert -text "Convert and save..." \
-command saveDialog] -side left -padx 2
-packb [button .bottomf.done -text "Done" -command exit] -side right
+ppack [ttk::button .bottomf.done -text "Done" -command exit] -side right
proc view {} {
if {! [viewable]} {
@@ -812,9 +620,7 @@ proc saveDialog {} {
# epspdf can read persistent options from configuration.
# only options from the options array need to be converted to parameters.
set args [list]
- if {$::options(gray) eq "gray"} {
- lappend args "-g"
- }
+ if {$::options(gray) eq "gray"} {lappend args "-g"}
if {$::options(bbox)} {lappend args "-b"}
if {! $::options(clean)} {lappend args "-d"}
if {$::options(pages) eq "single"} {
@@ -828,31 +634,36 @@ proc saveDialog {} {
}
update idletasks; # force immediate redisplay main window
- if {$::ge_85} {
- set failed [catch [linsert $args 0 \
- exec -ignorestderr $::texlua $::epspdf_tlu --gui=gui] result]
+ set cmd {} ; # necessary? if- and else blocks do not seem to define scopes
+ if {$::tcl_platform(platform) == "windows"} {
+ set cmd [linsert $args 0 exec -ignorestderr $::epspdf_cmd --gui=gui]
} else {
- set failed [catch [linsert $args 0 \
- exec $::texlua $::epspdf_tlu --gui=gui] result]
+ set cmd [linsert $args 0 exec -ignorestderr \
+ $::texlua $::epspdf_tlu --gui=gui]
}
+ set failed [catch $cmd result]
write_log $result
- if {$failed} {
+ if $failed {
tk_messageBox -icon error -type ok -message "Error; see log window"
} else {
set ::gfile(path) [file normalize $try]
set ::gfile(dir) [file dirname $::gfile(path)]
set ::gfile(type) $::options(format)
set ::gfile(name) [file tail $::gfile(path)]
- # parse result output
- regexp { is (\w+)(?: with (\d+) pages)?[\r\n]*$} \
- [string range $result [string last "File type of" $result] end] \
- mtc ::gfile(type) ::gfile(npages)
- if {$::gfile(type) eq "eps"} {set ::gfile(npages) 1}
+ if {$::gfile(type) eq "pdf"} {
+ if {! [catch {
+ exec $::texlua $::epspdf_tlu --gui=gui -i $::gfile(path)} \
+ result]} {
+ regexp {(\d+) pages\.$} $result mtc ::gfile(npages)
+ }
+ } elseif {$::gfile(type) eq "eps"} {
+ set ::gfile(npages) 1
+ }
set ::settings(default_dir) $::gfile(dir)
putsettings
set ::options(page) 1
}
- .status configure -text ""
+ .status configure -text "Idle"
foreach b {view open convert done} {
.bottomf.$b configure -state normal
}
diff --git a/Build/source/texk/texlive/linked_scripts/lwarp/lwarpmk.lua b/Build/source/texk/texlive/linked_scripts/lwarp/lwarpmk.lua
index aa7b01c66e9..33677933677 100755
--- a/Build/source/texk/texlive/linked_scripts/lwarp/lwarpmk.lua
+++ b/Build/source/texk/texlive/linked_scripts/lwarp/lwarpmk.lua
@@ -2,8 +2,8 @@
-- Copyright 2016-2018 Brian Dunn
-printversion = "v0.65"
-requiredconfversion = "1" -- also at *lwarpmk.conf
+printversion = "v0.66"
+requiredconfversion = "2" -- also at *lwarpmk.conf
function printhelp ()
print ("lwarpmk: Use lwarpmk -h or lwarpmk --help for help.") ;
@@ -103,6 +103,8 @@ end
-- Additional defaults:
confversion = "0"
opsystem = "Unix"
+imagesdirectory = "lateximages"
+imagesname = "image-"
latexmk = "false"
printlatexcmd = ""
HTMLlatexcmd = ""
@@ -164,6 +166,8 @@ elseif ( cvarname == "opsystem" ) then
elseif ( cvarname == "sourcename" ) then sourcename = cvalue
elseif ( cvarname == "homehtmlfilename" ) then homehtmlfilename = cvalue
elseif ( cvarname == "htmlfilename" ) then htmlfilename = cvalue
+elseif ( cvarname == "imagesdirectory" ) then imagesdirectory = cvalue
+elseif ( cvarname == "imagesname" ) then imagesname = cvalue
elseif ( cvarname == "latexmk" ) then latexmk = cvalue
elseif ( cvarname == "printlatexcmd" ) then printlatexcmd = cvalue
elseif ( cvarname == "HTMLlatexcmd" ) then HTMLlatexcmd = cvalue
@@ -379,34 +383,33 @@ end -- checkhtmlpdfexists
function warnlimages ()
--
--- Warning of a missing lateximages.txt file:
---
+-- Warning of a missing <sourcename>-images.txt file:
print ("lwarpmk: ===")
- print ("lwarpmk: \"lateximages.txt\" does not exist.")
+ print ("lwarpmk: \"" .. sourcename .. "-images.txt\" does not exist.")
print ("lwarpmk: Your project does not use SVG math or other lateximages,")
print ("lwarpmk: or the file has been deleted somehow.")
print ("lwarpmk: Use \"lwarpmk html\" to recompile your project")
- print ("lwarpmk: and recreate \"lateximages.txt\".")
+ print ("lwarpmk: and recreate \"" .. sourcename .. "-images.txt\".")
print ("lwarpmk: If your project does not use SVG math or other lateximages,")
- print ("lwarpmk: then \"lateximages.txt\" will never exist, and")
+ print ("lwarpmk: then \"" .. sourcename .. "-images.txt\" will never exist, and")
print ("lwarpmk: \"lwarpmk limages\" will not be necessary.")
print ("lwarpmk: ===")
end -- warnlimages
function checklimages ()
--
--- Check lateximages.txt to see if need to recompile first.
+-- Check <sourcename>.txt to see if need to recompile first.
-- If any entry has a page number of zero, then there were incorrect images.
--
-print ("lwarpmk: Checking for a valid lateximages.txt file.")
-local limagesfile = io.open("lateximages.txt", "r")
+print ("lwarpmk: Checking for a valid " .. sourcename .. "-images.txt file.")
+local limagesfile = io.open(sourcename .. "-images.txt", "r")
if ( limagesfile == nil ) then
warnlimages ()
os.exit(1)
end
-- Track warning to recompile if find a page 0
local pagezerowarning = false
--- Scan lateximages.txt
+-- Scan <sourcename>.txt
for line in limagesfile:lines() do
-- lwimgpage is the page number in the PDF which has the image
-- lwimghash is true if this filename is a hash
@@ -439,20 +442,20 @@ executecheckerror (
cmdgroupopenname ..
"pdfseparate -f " .. lwimgpage .. " -l " .. lwimgpage .. " " ..
sourcename .."_html.pdf " ..
- "lateximages" .. dirslash .."lateximagetemp-%d" .. ".pdf" ..
+ imagesdirectory .. dirslash .."lateximagetemp-%d" .. ".pdf" ..
seqname ..
-- Crop the image:
- "pdfcrop --hires lateximages" .. dirslash .. "lateximagetemp-" ..
+ "pdfcrop --hires " .. imagesdirectory .. dirslash .. "lateximagetemp-" ..
lwimgpage .. ".pdf " ..
- "lateximages" .. dirslash .. lwimgname .. ".pdf" ..
+ imagesdirectory .. dirslash .. lwimgname .. ".pdf" ..
seqname ..
-- Convert the image to svg:
- "pdftocairo -svg -noshrink lateximages" .. dirslash .. lwimgname .. ".pdf " ..
- "lateximages" .. dirslash .. lwimgname ..".svg" ..
+ "pdftocairo -svg -noshrink " .. imagesdirectory .. dirslash .. lwimgname .. ".pdf " ..
+ imagesdirectory .. dirslash .. lwimgname ..".svg" ..
seqname ..
-- Remove the temporary files:
- rmname .. " lateximages" .. dirslash .. lwimgname .. ".pdf" .. seqname ..
- rmname .. " lateximages" .. dirslash .. "lateximagetemp-" .. lwimgpage .. ".pdf" ..
+ rmname .. " " .. imagesdirectory .. dirslash .. lwimgname .. ".pdf" .. seqname ..
+ rmname .. " " .. imagesdirectory .. dirslash .. "lateximagetemp-" .. lwimgpage .. ".pdf" ..
cmdgroupclosename .. " >/dev/null " .. bgname
,
"File error trying to convert " .. lwimgfullname
@@ -495,7 +498,7 @@ end -- createwindowsimage
function createonelateximage ( line )
--
--- Given the next line of lateximages.txt, convert a single image.
+-- Given the next line of <sourcename>.txt, convert a single image.
--
-- lwimgpage is the page number in the PDF which has the image
-- lwimghash is true if this filename is a hash
@@ -508,7 +511,7 @@ if ( (i~=nil) ) then
pagezerowarning = true
else
-- Skip is this image is hashed and already exists:
- local lwimgfullname = "lateximages" .. dirslash .. lwimgname .. ".svg"
+ local lwimgfullname = imagesdirectory .. dirslash .. lwimgname .. ".svg"
if (
(lwimghash ~= "true") or
(lfs.attributes(lwimgfullname,"mode")==nil) -- file not exists
@@ -534,7 +537,7 @@ end -- createonelateximage
function createlateximages ()
--
--- Create lateximages based on lateximages.txt:
+-- Create lateximages based on <sourcename>.txt:
--
-- See if the document must be recompiled first:
checklimages ()
@@ -542,13 +545,13 @@ checklimages ()
checkhtmlpdfexists ()
-- Attempt to create the lateximages:
print ("lwarpmk: Creating lateximages.")
-local limagesfile = io.open("lateximages.txt", "r")
+local limagesfile = io.open(sourcename .. "-images.txt", "r")
if ( limagesfile == nil ) then
- warnlateximages ()
+ warnlimages ()
os.exit(1)
end
-- Create the lateximages directory, ignore error if already exists
-err = os.execute("mkdir lateximages")
+err = os.execute("mkdir " .. imagesdirectory)
-- For Windows, create lwarp_one_limage.cmd from lwarp_one_limage.txt:
if opsystem=="Windows" then
executecheckerror (
@@ -560,7 +563,7 @@ end -- create lwarp_one_limage.cmd
numimageprocesses = 0
-- Track warning to recompile if find a page 0
pagezerowarning = false
--- Scan lateximages.txt
+-- Scan <sourcename>.txt
for line in limagesfile:lines() do
createonelateximage ( line )
end -- do
@@ -758,7 +761,7 @@ os.execute(glossarycmd .. " " .. sourcename .. "_html")
updateanddone ()
-- lwarpmk limages:
--- Scan the lateximages.txt file to create lateximages.
+-- Scan the <sourcename>.txt file to create lateximages.
elseif arg[1] == "limages" then
loadconf ()
@@ -796,11 +799,11 @@ os.execute ( rmname .. " " ..
print ("lwarpmk: Done.")
-- lwarpmk cleanlimages
--- Remove images from the lateximages directory.
+-- Remove images from the imagesdirectory.
elseif arg[1] == "cleanlimages" then
loadconf ()
-os.execute ( rmname .. " lateximages/*" )
+os.execute ( rmname .. " " .. imagesdirectory .. dirslash .. "*" )
print ("lwarpmk: Done.")
-- lwarpmk epstopdf <list of file names>