summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/grph-inc.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/grph-inc.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/grph-inc.lua891
1 files changed, 534 insertions, 357 deletions
diff --git a/Master/texmf-dist/tex/context/base/grph-inc.lua b/Master/texmf-dist/tex/context/base/grph-inc.lua
index 508240a3bc4..7dee63eaf5f 100644
--- a/Master/texmf-dist/tex/context/base/grph-inc.lua
+++ b/Master/texmf-dist/tex/context/base/grph-inc.lua
@@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['grph-inc'] = {
license = "see context related readme files"
}
+-- figures -> managers.figures
+
-- lowercase types
-- mps tex tmp svg
-- partly qualified
@@ -14,6 +16,8 @@ if not modules then modules = { } end modules ['grph-inc'] = {
-- figures.boxnumber can go as we now can use names
+-- avoid push
+
--[[
The ConTeXt figure inclusion mechanisms are among the oldest code
in ConTeXt and evolve dinto a complex whole. One reason is that we
@@ -36,19 +40,29 @@ run TeX code from within Lua. Some more functionality will move to Lua.
]]--
local format, lower, find, match, gsub, gmatch = string.format, string.lower, string.find, string.match, string.gsub, string.gmatch
-local texsprint, texbox = tex.sprint, tex.box
+local texbox = tex.box
local contains = table.contains
-local concat = table.concat
+local concat, insert, remove = table.concat, table.insert, table.remove
local todimen = string.todimen
-local ctxcatcodes = tex.ctxcatcodes
-local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local variables = interfaces.variables
+local codeinjections = backends.codeinjections
+local nodeinjections = backends.nodeinjections
+
+local trace_figures = false trackers.register("graphics.locating", function(v) trace_figures = v end)
+local trace_bases = false trackers.register("graphics.bases", function(v) trace_bases = v end)
+local trace_programs = false trackers.register("graphics.programs", function(v) trace_programs = v end)
+local trace_conversion = false trackers.register("graphics.conversion", function(v) trace_conversion = v end)
+local trace_inclusion = false trackers.register("graphics.inclusion", function(v) trace_inclusion = v end)
-local trace_figures = false trackers.register("figures.locating", function(v) trace_figures = v end)
-local trace_bases = false trackers.register("figures.bases", function(v) trace_bases = v end)
-local trace_programs = false trackers.register("figures.programs", function(v) trace_programs = v end)
-local trace_conversion = false trackers.register("figures.conversion", function(v) trace_conversion = v end)
-local trace_inclusion = false trackers.register("figures.inclusion", function(v) trace_inclusion = v end)
+local report_inclusion = logs.reporter("graphics","inclusion")
+
+local context, img = context, img
--- some extra img functions ---
@@ -63,8 +77,12 @@ function img.totable(imgtable)
return result
end
-function img.serialize(i)
- return table.serialize(img.totable(i))
+function img.serialize(i,...)
+ return table.serialize(img.totable(i),...)
+end
+
+function img.print(i,...)
+ return table.print(img.totable(i),...)
end
function img.clone(i,data)
@@ -77,7 +95,7 @@ end
local validsizes = table.tohash(img.boxes())
local validtypes = table.tohash(img.types())
-function img.check_size(size)
+function img.checksize(size)
if size then
size = gsub(size,"box","")
return (validsizes[size] and size) or "crop"
@@ -86,56 +104,74 @@ function img.check_size(size)
end
end
----
+local indexed = { }
+
+function img.ofindex(n)
+ return indexed[n]
+end
+
+--- we can consider an grph-ini file
+
+figures = figures or { }
+local figures = figures
+
+figures.loaded = allocate()
+figures.used = allocate()
+figures.found = allocate()
+figures.suffixes = allocate()
+figures.patterns = allocate()
-figures = figures or { }
-figures.loaded = figures.loaded or { }
-figures.used = figures.used or { }
-figures.found = figures.found or { }
-figures.suffixes = figures.suffixes or { }
-figures.patterns = figures.patterns or { }
figures.boxnumber = figures.boxnumber or 0
figures.defaultsearch = true
figures.defaultwidth = 0
figures.defaultheight = 0
figures.defaultdepth = 0
-figures.n = 0
-figures.prefer_quality = true -- quality over location
+figures.nofprocessed = 0
+figures.preferquality = true -- quality over location
-figures.localpaths = {
+figures.existers = allocate() local existers = figures.existers
+figures.checkers = allocate() local checkers = figures.checkers
+figures.includers = allocate() local includers = figures.includers
+figures.converters = allocate() local converters = figures.converters
+figures.identifiers = allocate() local identifiers = figures.identifiers
+figures.programs = allocate() local programs = figures.programs
+
+figures.localpaths = allocate {
".", "..", "../.."
}
-figures.cachepaths = {
+figures.cachepaths = allocate {
prefix = "",
path = ".",
subpath = ".",
}
-figures.paths = table.copy(figures.localpaths)
+figures.paths = allocate(table.copy(figures.localpaths))
-figures.order = {
- "pdf", "mps", "jpg", "png", "jbig", "svg", "eps", "gif", "mov", "buffer", "tex",
+figures.order = allocate{
+ "pdf", "mps", "jpg", "png", "jp2", "jbig", "svg", "eps", "gif", "mov", "buffer", "tex", "cld",
}
-figures.formats = {
+figures.formats = allocate{
["pdf"] = { list = { "pdf" } },
["mps"] = { patterns = { "mps", "%d+" } },
["jpg"] = { list = { "jpg", "jpeg" } },
+ ["jp2"] = { list = { "jp2" } },
["png"] = { list = { "png" } },
["jbig"] = { list = { "jbig", "jbig2", "jb2" } },
["svg"] = { list = { "svg", "svgz" } },
["eps"] = { list = { "eps", "ai" } },
["gif"] = { list = { "gif" } },
- ["mov"] = { list = { "mov", "avi" } },
+ ["mov"] = { list = { "mov", "flv", "mp4" } }, -- "avi" is not supported
["buffer"] = { list = { "tmp", "buffer", "buf" } },
["tex"] = { list = { "tex" } },
+ ["cld"] = { list = { "cld" } },
}
function figures.setlookups()
- figures.suffixes, figures.patterns = { }, { }
+ local fs, fp = allocate(), allocate()
+ figures.suffixes, figures.patterns = fs, fp
for _, format in next, figures.order do
local data = figures.formats[format]
- local fs, fp = figures.suffixes, figures.patterns
local list = data.list
if list then
for i=1,#list do
@@ -183,7 +219,7 @@ function figures.setpaths(locationset,pathlist)
-- this function can be called each graphic so we provide this optimization
return
end
- local iv, t, h = interfaces.variables, figures.paths, locationset:tohash()
+ local iv, t, h = interfaces.variables, figures.paths, settings_to_hash(locationset)
if last_locationset ~= locationset then
-- change == reset (actually, a 'reset' would indeed reset
if h[iv["local"]] then
@@ -195,8 +231,7 @@ function figures.setpaths(locationset,pathlist)
last_locationset = locationset
end
if h[iv["global"]] then
- -- for s in gmatch(pathlist,",* *([^,]+)") do
- local list = aux.settings_to_array(pathlist)
+ local list = settings_to_array(pathlist)
for i=1,#list do
local s = list[i]
if not contains(t,s) then
@@ -206,8 +241,8 @@ function figures.setpaths(locationset,pathlist)
end
figures.paths, last_pathlist = t, pathlist
if trace_figures then
- commands.writestatus("figures","locations: %s",last_locationset)
- commands.writestatus("figures","path list: %s",concat(figures.paths, " "))
+ report_inclusion("locations: %s",last_locationset)
+ report_inclusion("path list: %s",concat(figures.paths, " "))
end
end
@@ -220,195 +255,245 @@ end
-- interfacing to tex
-do
-
- local figuredata = { }
- local callstack = { }
-
- function figures.new()
- figuredata = {
- request = {
- name = false,
- label = false,
- format = false,
- page = false,
- width = false,
- height = false,
- preview = false,
- ["repeat"] = false,
- controls = false,
- display = false,
- conversion = false,
- cache = false,
- prefix = false,
- size = false,
- },
- used = {
- fullname = false,
- format = false,
- name = false,
- path = false,
- suffix = false,
- width = false,
- height = false,
- },
- status = {
- status = 0,
- converted = false,
- cached = false,
- fullname = false,
- format = false,
- },
- }
- return figuredata
- end
+local function new() -- we could use metatables status -> used -> request but it needs testing
+ local request = {
+ name = false,
+ label = false,
+ format = false,
+ page = false,
+ width = false,
+ height = false,
+ preview = false,
+ ["repeat"] = false,
+ controls = false,
+ display = false,
+ mask = false,
+ conversion = false,
+ resolution = false,
+ cache = false,
+ prefix = false,
+ size = false,
+ }
+ local used = {
+ fullname = false,
+ format = false,
+ name = false,
+ path = false,
+ suffix = false,
+ width = false,
+ height = false,
+ }
+ local status = {
+ status = 0,
+ converted = false,
+ cached = false,
+ fullname = false,
+ format = false,
+ }
+ -- this needs checking because we might check for nil, the test case
+ -- is getfiguredimensions which then should return ~= 0
+ -- setmetatableindex(status, used)
+ -- setmetatableindex(used, request)
+ return {
+ request = request,
+ used = used,
+ status = status,
+ }
+end
- function figures.push(request)
- local ncs = #callstack + 1
- if ncs == 1 then
- statistics.starttiming(figures)
- end
- local figuredata = figures.new()
- if request then
- local iv = interfaces.variables
- -- request.width/height are strings and are only used when no natural dimensions
- -- can be determined; at some point the handlers might set them to numbers instead
- -- local w, h = tonumber(request.width), tonumber(request.height)
- request.page = math.max(tonumber(request.page) or 1,1)
- request.size = img.check_size(request.size)
- request.object = iv[request.object] == variables.yes
- request["repeat"] = iv[request["repeat"]] == variables.yes
- request.preview = iv[request.preview] == variables.yes
- request.cache = request.cache ~= "" and request.cache
- request.prefix = request.prefix ~= "" and request.prefix
- request.format = request.format ~= "" and request.format
- -- request.width = (w and w > 0) or false
- -- request.height = (h and h > 0) or false
- table.merge(figuredata.request,request)
- end
- callstack[ncs] = figuredata
- return figuredata
- end
- function figures.pop()
- local ncs = #callstack
- figuredata = callstack[ncs]
- callstack[ncs] = nil
- if ncs == 1 then
- statistics.stoptiming(figures)
- end
- end
- -- maybe move texsprint to tex
- function figures.get(category,tag,default)
- local value = figuredata[category]
- value = value and value[tag]
- if not value or value == "" or value == true then
- return default or ""
- else
- return value
- end
- end
- function figures.tprint(category,tag,default)
- texsprint(ctxcatcodes,figures.get(category,tag,default))
+ -- use table.insert|remove
+
+local lastfiguredata = nil -- will be topofstack or last so no { } (else problems with getfiguredimensions)
+local callstack = { }
+
+function figures.initialize(request)
+ local figuredata = new()
+ if request then
+ -- request.width/height are strings and are only used when no natural dimensions
+ -- can be determined; at some point the handlers might set them to numbers instead
+ -- local w, h = tonumber(request.width), tonumber(request.height)
+ request.page = math.max(tonumber(request.page) or 1,1)
+ request.size = img.checksize(request.size)
+ request.object = request.object == variables.yes
+ request["repeat"] = request["repeat"] == variables.yes
+ request.preview = request.preview == variables.yes
+ request.cache = request.cache ~= "" and request.cache
+ request.prefix = request.prefix ~= "" and request.prefix
+ request.format = request.format ~= "" and request.format
+ -- request.width = (w and w > 0) or false
+ -- request.height = (h and h > 0) or false
+ table.merge(figuredata.request,request)
end
- function figures.current()
- return callstack[#callstack]
+ return figuredata
+end
+
+function figures.push(request)
+ statistics.starttiming(figures)
+ local figuredata = figures.initialize(request)
+ insert(callstack,figuredata)
+ lastfiguredata = figuredata
+ return figuredata
+end
+
+function figures.pop()
+ lastfiguredata = remove(callstack) or lastfiguredata
+ statistics.stoptiming(figures)
+end
+
+function figures.current()
+ return callstack[#callstack] or lastfiguredata
+end
+
+function figures.get(category,tag,default)
+ local value = lastfiguredata and lastfiguredata[category]
+ value = value and value[tag]
+ if not value or value == "" or value == true then
+ return default or ""
+ else
+ return value
end
+end
+--
+
+function figures.tprint(category,tag,default)
+ context(figures.get(category,tag,default))
end
local defaultformat = "pdf"
-local defaultprefix = "m_k_v_i_"
+local defaultprefix = "m_k_i_v_"
+
+-- todo: local path or cache path
+
+local function forbiddenname(filename)
+ if not filename or filename == "" then
+ return false
+ end
+ local expandedfullname = file.collapsepath(filename,true)
+ local expandedinputname = file.collapsepath(file.addsuffix(environment.jobfilename,environment.jobfilesuffix),true)
+ if expandedfullname == expandedinputname then
+ report_inclusion("skipping graphic with same name as input filename (%s), enforce suffix",expandedinputname)
+ return true
+ end
+ local expandedoutputname = file.collapsepath(codeinjections.getoutputfilename(),true)
+ if expandedfullname == expandedoutputname then
+ report_inclusion("skipping graphic with same name as output filename (%s), enforce suffix",expandedoutputname)
+ return true
+ end
+end
local function register(askedname,specification)
if specification then
- local format = specification.format
- if format then
- local conversion = specification.conversion
- if conversion == "" then
- conversion = nil
- end
- local newformat = conversion
- if not newformat or newformat == "" then
- newformat = defaultformat
- end
- local converter = (newformat ~= format) and figures.converters[format]
- if trace_conversion then
- logs.report("figures","checking conversion of '%s': old format '%s', new format '%s', conversion '%s'",
- askedname,format,newformat,conversion or "default")
- end
- if converter then
- if converter[newformat] then
- converter = converter[newformat]
- else
+ if forbiddenname(specification.fullname) then
+ specification = { }
+ else
+ local format = specification.format
+ if format then
+ local conversion = specification.conversion
+ local resolution = specification.resolution
+ if conversion == "" then
+ conversion = nil
+ end
+ if resolution == "" then
+ resolution = nil
+ end
+ local newformat = conversion
+ if not newformat or newformat == "" then
newformat = defaultformat
+ end
+ if trace_conversion then
+ report_inclusion("checking conversion of '%s': old format '%s', new format '%s', conversion '%s', resolution '%s'",
+ askedname,format,newformat,conversion or "default",resolution or "default")
+ end
+ local converter = (newformat ~= format) and converters[format]
+ if converter then
if converter[newformat] then
converter = converter[newformat]
else
newformat = defaultformat
+ if converter[newformat] then
+ converter = converter[newformat]
+ else
+ converter = nil
+ newformat = defaultformat
+ end
end
+ elseif trace_conversion then
+ report_inclusion("no converter for '%s' -> '%s'",format,newformat)
end
- end
- if converter then
- local oldname = specification.fullname
- local newpath = file.dirname(oldname)
- local oldbase = file.basename(oldname)
- local newbase = file.replacesuffix(oldbase,newformat)
- local fc = specification.cache or figures.cachepaths.path
- if fc and fc ~= "" and fc ~= "." then
- newpath = fc
- else
- newbase = defaultprefix .. newbase
- end
- local subpath = specification.subpath or figures.cachepaths.subpath
- if subpath and subpath ~= "" and subpath ~= "." then
- newpath = newpath .. "/" .. subpath
- end
- local prefix = specification.prefix or figures.cachepaths.prefix
- if prefix and prefix ~= "" then
- newbase = prefix .. newbase
- end
- local newname = file.join(newpath,newbase)
- dir.makedirs(newpath)
- oldname = file.collapse_path(oldname)
- newname = file.collapse_path(newname)
- local oldtime = lfs.attributes(oldname,'modification') or 0
- local newtime = lfs.attributes(newname,'modification') or 0
- if oldtime > newtime then
- if trace_conversion then
- logs.report("figures","converting '%s' from '%s' to '%s'",askedname,format,newformat)
+ if converter then
+ -- local oldname = specification.fullname
+ local oldname = specification.foundname
+ local newpath = file.dirname(oldname)
+ local oldbase = file.basename(oldname)
+ local newbase = file.removesuffix(oldbase)
+ local fc = specification.cache or figures.cachepaths.path
+ if fc and fc ~= "" and fc ~= "." then
+ newpath = fc
+ else
+ newbase = defaultprefix .. newbase
end
- converter(oldname,newname)
- else
- if trace_conversion then
- logs.report("figures","no need to convert '%s' from '%s' to '%s'",askedname,format,newformat)
+ if not file.is_writable(newpath) then
+ if trace_conversion then
+ report_inclusion("[ath '%s'is not writable, forcing conversion path '.' ",newpath)
+ end
+ newpath = "."
+ end
+ local subpath = specification.subpath or figures.cachepaths.subpath
+ if subpath and subpath ~= "" and subpath ~= "." then
+ newpath = newpath .. "/" .. subpath
+ end
+ local prefix = specification.prefix or figures.cachepaths.prefix
+ if prefix and prefix ~= "" then
+ newbase = prefix .. newbase
+ end
+ if resolution and resolution ~= "" then -- the order might change
+ newbase = newbase .. "_" .. resolution
+ end
+ local newbase = file.addsuffix(newbase,newformat)
+ local newname = file.join(newpath,newbase)
+ dir.makedirs(newpath)
+ oldname = file.collapsepath(oldname)
+ newname = file.collapsepath(newname)
+ local oldtime = lfs.attributes(oldname,'modification') or 0
+ local newtime = lfs.attributes(newname,'modification') or 0
+ if newtime == 0 or oldtime > newtime then
+ if trace_conversion then
+ report_inclusion("converting '%s' from '%s' to '%s'",askedname,format,newformat)
+ end
+ converter(oldname,newname,resolution or "")
+ else
+ if trace_conversion then
+ report_inclusion("no need to convert '%s' from '%s' to '%s'",askedname,format,newformat)
+ end
+ end
+ if io.exists(newname) then
+ specification.foundname = oldname
+ specification.fullname = newname
+ specification.prefix = prefix
+ specification.subpath = subpath
+ specification.converted = true
+ format = newformat
+ elseif io.exists(oldname) then
+ specification.fullname = newname
+ specification.converted = false
end
end
- if io.exists(newname) then
- specification.foundname = oldname
- specification.fullname = newname
- specification.prefix = prefix
- specification.subpath = subpath
- specification.converted = true
- format = newformat
- elseif io.exists(oldname) then
- specification.fullname = newname
- specification.converted = false
- end
- end
- end
- local found = figures.suffixes[format] -- validtypes[format]
- if not found then
- specification.found = false
- if trace_figures then
- commands.writestatus("figures","format not supported: %s",format)
end
- else
- specification.found = true
- if trace_figures then
- if validtypes[format] then
- commands.writestatus("figures","format natively supported by backend: %s",format)
- else
- commands.writestatus("figures","format supported by output file format: %s",format)
+ local found = figures.suffixes[format] -- validtypes[format]
+ if not found then
+ specification.found = false
+ if trace_figures then
+ report_inclusion("format not supported: %s",format)
+ end
+ else
+ specification.found = true
+ if trace_figures then
+ if validtypes[format] then
+ report_inclusion("format natively supported by backend: %s",format)
+ else
+ report_inclusion("format supported by output file format: %s",format)
+ end
end
end
end
@@ -416,24 +501,31 @@ local function register(askedname,specification)
specification = { }
end
specification.foundname = specification.foundname or specification.fullname
- figures.found[askedname .. "->" .. (specification.conversion or "default")] = specification
+ figures.found[askedname .. "->" .. (specification.conversion or "default") .. "->" .. (specification.resolution or "default")] = specification
return specification
end
local resolve_too = true -- urls
local function locate(request) -- name, format, cache
- local askedname = resolvers.clean_path(request.name)
- local foundname = figures.found[askedname .. "->" .. (request.conversion or "default")]
+ local askedname = resolvers.cleanpath(request.name)
+ local foundname = figures.found[askedname .. "->" .. (request.conversion or "default") .. "->" .. (request.resolution or "default")]
if foundname then
return foundname
end
-- protocol check
local hashed = url.hashed(askedname)
- if hashed and hashed.scheme ~= "file" then
- local foundname = resolvers.findbinfile(askedname)
- if foundname then
- askedname = foundname
+ if hashed then
+ if hashed.scheme == "file" then
+ local path = hashed.path
+ if path and path ~= "" then
+ askedname = path
+ end
+ else
+ local foundname = resolvers.findbinfile(askedname)
+ if foundname then
+ askedname = foundname
+ end
end
end
-- we could use the hashed data instead
@@ -442,11 +534,12 @@ local function locate(request) -- name, format, cache
local askedformat = (request.format ~= "" and request.format ~= "unknown" and request.format) or file.extname(askedname) or ""
local askedcache = request.cache
local askedconversion = request.conversion
+ local askedresolution = request.resolution
if askedformat ~= "" then
+ askedformat = lower(askedformat)
if trace_figures then
- commands.writestatus("figures","strategy: forced format")
+ report_inclusion("strategy: forced format %s",askedformat)
end
- askedformat = lower(askedformat)
local format = figures.suffixes[askedformat]
if not format then
local figurepatterns = figures.patterns
@@ -459,27 +552,33 @@ local function locate(request) -- name, format, cache
end
end
if format then
- local foundname = figures.exists(askedname,format,resolve_too) -- not askedformat
+ local foundname, quitscanning = figures.exists(askedname,format,resolve_too) -- not askedformat
if foundname then
return register(askedname, {
- askedname = askedname,
- fullname = askedname,
- format = format,
- cache = askedcache,
- foundname = foundname,
+ askedname = askedname,
+ fullname = askedname,
+ format = format,
+ cache = askedcache,
+ foundname = foundname,
conversion = askedconversion,
+ resolution = askedresolution,
})
+ elseif quitscanning then
+ return register(askedname)
end
+ elseif trace_figures then
+ report_inclusion("strategy: unknown format %s",askedformat)
end
if askedpath then
-- path and type given, todo: strip pieces of path
if figures.exists(askedname,askedformat,resolve_too) then
return register(askedname, {
- askedname = askedname,
- fullname = askedname,
- format = askedformat,
- cache = askedcache,
+ askedname = askedname,
+ fullname = askedname,
+ format = askedformat,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
else
@@ -492,30 +591,32 @@ local function locate(request) -- name, format, cache
-- is given we don't waste much time
if figures.exists(check,askedformat,resolve_too) then
return register(check, {
- askedname = askedname,
- fullname = check,
- format = askedformat,
- cache = askedcache,
+ askedname = askedname,
+ fullname = check,
+ format = askedformat,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
end
if figures.defaultsearch then
- local check = resolvers.find_file(askedname)
+ local check = resolvers.findfile(askedname)
if check and check ~= "" then
return register(askedname, {
- askedname = askedname,
- fullname = check,
- format = askedformat,
- cache = askedcache,
+ askedname = askedname,
+ fullname = check,
+ format = askedformat,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
end
end
elseif askedpath then
if trace_figures then
- commands.writestatus("figures","strategy: rootbased path")
+ report_inclusion("strategy: rootbased path")
end
local figureorder = figures.order
for i=1,#figureorder do
@@ -526,19 +627,20 @@ local function locate(request) -- name, format, cache
local check = file.addsuffix(askedname,suffix)
if figures.exists(check,format,resolve_too) then
return register(askedname, {
- askedname = askedname,
- fullname = check,
- format = format,
- cache = askedcache,
+ askedname = askedname,
+ fullname = check,
+ format = format,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
end
end
else
- if figures.prefer_quality then
+ if figures.preferquality then
if trace_figures then
- commands.writestatus("figures","strategy: unknown format, prefer quality")
+ report_inclusion("strategy: unknown format, prefer quality")
end
local figurepaths = figures.paths
local figureorder = figures.order
@@ -555,15 +657,16 @@ local function locate(request) -- name, format, cache
local isfile = url.hashed(check).scheme == "file"
if not isfile then
if trace_figures then
- commands.writestatus("figures","warning: skipping path %s",path)
+ report_inclusion("warning: skipping path %s",path)
end
elseif figures.exists(check,format,true) then
return register(askedname, {
- askedname = askedname,
- fullname = check,
- format = format,
- cache = askedcache,
+ askedname = askedname,
+ fullname = check,
+ format = format,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
end
@@ -571,7 +674,7 @@ local function locate(request) -- name, format, cache
end
else -- 'location'
if trace_figures then
- commands.writestatus("figures","strategy: unknown format, prefer path")
+ report_inclusion("strategy: unknown format, prefer path")
end
local figurepaths = figures.paths
local figureorder = figures.order
@@ -585,11 +688,12 @@ local function locate(request) -- name, format, cache
local check = path .. "/" .. file.replacesuffix(askedbase,suffix)
if figures.exists(check,format,resolve_too) then
return register(askedname, {
- askedname = askedname,
- fullname = check,
- format = format,
- cache = askedcache,
+ askedname = askedname,
+ fullname = check,
+ format = format,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
end
@@ -598,7 +702,7 @@ local function locate(request) -- name, format, cache
end
if figures.defaultsearch then
if trace_figures then
- commands.writestatus("figures","strategy: default tex path")
+ report_inclusion("strategy: default tex path")
end
local figureorder = figures.order
for j=1,#figureorder do
@@ -606,14 +710,15 @@ local function locate(request) -- name, format, cache
local list = figures.formats[format].list or { format }
for k=1,#list do
local suffix = list[k]
- local check = resolvers.find_file(file.replacesuffix(askedname,suffix))
+ local check = resolvers.findfile(file.replacesuffix(askedname,suffix))
if check and check ~= "" then
return register(askedname, {
- askedname = askedname,
- fullname = check,
- format = format,
- cache = askedcache,
+ askedname = askedname,
+ fullname = check,
+ format = format,
+ cache = askedcache,
conversion = askedconversion,
+ resolution = askedresolution,
})
end
end
@@ -625,35 +730,24 @@ end
-- -- -- plugins -- -- --
-figures.existers = figures.existers or { }
-figures.checkers = figures.checkers or { }
-figures.includers = figures.includers or { }
-figures.converters = figures.converters or { }
-figures.identifiers = figures.identifiers or { }
-figures.programs = figures.programs or { }
-
-figures.identifiers.list = {
- figures.identifiers.default
-}
-
-function figures.identifiers.default(data)
+function identifiers.default(data)
local dr, du, ds = data.request, data.used, data.status
local l = locate(dr)
local foundname = l.foundname
- local fullname = l.fullname or foundname
+ local fullname = l.fullname or foundname
if fullname then
- du.format = l.format or false
+ du.format = l.format or false
du.fullname = fullname -- can be cached
ds.fullname = foundname -- original
- ds.format = l.format
- ds.status = (l.found and 10) or 0
+ ds.format = l.format
+ ds.status = (l.found and 10) or 0
end
return data
end
function figures.identify(data)
data = data or figures.current()
- local list = figures.identifiers.list
+ local list = identifiers.list -- defined at the end
for i=1,#list do
local identifier = list[i]
data = identifier(data)
@@ -663,25 +757,28 @@ function figures.identify(data)
end
return data
end
+
function figures.exists(askedname,format,resolve)
- return (figures.existers[format] or figures.existers.generic)(askedname,resolve)
+ return (existers[format] or existers.generic)(askedname,resolve)
end
+
function figures.check(data)
data = data or figures.current()
- local dr, du, ds = data.request, data.used, data.status
- return (figures.checkers[ds.format] or figures.checkers.generic)(data)
+ return (checkers[data.status.format] or checkers.generic)(data)
end
+
function figures.include(data)
data = data or figures.current()
- local dr, du, ds = data.request, data.used, data.status
- return (figures.includers[ds.format] or figures.includers.generic)(data)
+ return (includers[data.status.format] or includers.generic)(data)
end
+
function figures.scale(data) -- will become lua code
- texsprint(ctxcatcodes,"\\doscalefigure")
+ context.doscalefigure()
return data
end
+
function figures.done(data)
- figures.n = figures.n + 1
+ figures.nofprocessed = figures.nofprocessed + 1
data = data or figures.current()
--~ print(table.serialize(figures.current()))
local dr, du, ds, nr = data.request, data.used, data.status, figures.boxnumber
@@ -690,13 +787,14 @@ function figures.done(data)
ds.height = box.height
ds.xscale = ds.width /(du.width or 1)
ds.yscale = ds.height/(du.height or 1)
+ ds.page = ds.page or du.page or dr.page -- sort of redundant but can be limited
--~ print(table.serialize(figures.current()))
return data
end
function figures.dummy(data)
data = data or figures.current()
- local dr, du, ds, nr = data.request, data.used, data.status, figures.boxnumber
+ local dr, du, nr = data.request, data.used, figures.boxnumber
local box = node.hpack(node.new("hlist")) -- we need to set the dir (luatex 0.60 buglet)
du.width = du.width or figures.defaultwidth
du.height = du.height or figures.defaultheight
@@ -710,7 +808,7 @@ end
-- -- -- generic -- -- --
-function figures.existers.generic(askedname,resolve)
+function existers.generic(askedname,resolve)
-- not findbinfile
local result
if lfs.isfile(askedname) then
@@ -721,47 +819,66 @@ function figures.existers.generic(askedname,resolve)
end
if trace_figures then
if result then
- commands.writestatus("figures","found: %s -> %s",askedname,result)
+ report_inclusion("found: %s -> %s",askedname,result)
else
- commands.writestatus("figures","not found: %s",askedname)
+ report_inclusion("not found: %s",askedname)
end
end
return result
end
-function figures.checkers.generic(data)
+
+function checkers.generic(data)
local dr, du, ds = data.request, data.used, data.status
local name, page, size, color = du.fullname or "unknown generic", du.page or dr.page, dr.size or "crop", dr.color or "natural"
+ local mask = dr.mask or "none"
local conversion = dr.conversion
+ local resolution = dr.resolution
if not conversion or conversion == "" then
conversion = "unknown"
end
- local hash = name .. "->" .. page .. "->" .. size .. "->" .. color .. "->" .. conversion
+ if not resolution or resolution == "" then
+ resolution = "unknown"
+ end
+ local hash = name .. "->" .. page .. "->" .. size .. "->" .. color .. "->" .. conversion .. "->" .. resolution
+ .. "->" .. mask
local figure = figures.loaded[hash]
if figure == nil then
- figure = img.new { filename = name, page = page, pagebox = dr.size }
- backends.codeinjections.setfigurecolorspace(data,figure)
+ figure = img.new {
+ filename = name,
+ page = page,
+ pagebox = dr.size,
+ }
+ codeinjections.setfigurecolorspace(data,figure)
+ codeinjections.setfiguremask(data,figure)
figure = (figure and img.scan(figure)) or false
- local f, d = backends.codeinjections.setfigurealternative(data,figure)
+ local f, d = codeinjections.setfigurealternative(data,figure)
figure, data = f or figure, d or data
figures.loaded[hash] = figure
if trace_conversion then
- logs.report("figures","new graphic, hash: %s",hash)
+ report_inclusion("new graphic, hash: %s",hash)
end
else
if trace_conversion then
- logs.report("figures","existing graphic, hash: %s",hash)
+ report_inclusion("existing graphic, hash: %s",hash)
end
end
if figure then
du.width = figure.width
du.height = figure.height
du.pages = figure.pages
+ du.depth = figure.depth or 0
+ du.colordepth = figure.colordepth or 0
+ du.xresolution = figure.xres or 0
+ du.yresolution = figure.yres or 0
+ du.xsize = figure.xsize or 0
+ du.ysize = figure.ysize or 0
ds.private = figure
ds.hash = hash
end
return data
end
-function figures.includers.generic(data)
+
+function includers.generic(data)
local dr, du, ds = data.request, data.used, data.status
-- here we set the 'natural dimensions'
dr.width = du.width
@@ -780,39 +897,43 @@ function figures.includers.generic(data)
local nr = figures.boxnumber
-- it looks like we have a leak in attributes here .. todo
local box = node.hpack(img.node(figure)) -- img.node(figure) not longer valid
+ indexed[figure.index] = figure
box.width, box.height, box.depth = figure.width, figure.height, 0 -- new, hm, tricky, we need to do that in tex (yet)
texbox[nr] = box
ds.objectnumber = figure.objnum
- texsprint(ctxcatcodes,"\\relocateexternalfigure")
+ context.relocateexternalfigure()
end
return data
end
-- -- -- nongeneric -- -- --
-function figures.checkers.nongeneric(data,command)
+function checkers.nongeneric(data,command) -- todo: macros and context.*
local dr, du, ds = data.request, data.used, data.status
local name = du.fullname or "unknown nongeneric"
local hash = name
if dr.object then
- -- hm, bugged
- if not jobobjects.get("FIG::"..hash) then
- texsprint(ctxcatcodes,command)
- texsprint(ctxcatcodes,format("\\setobject{FIG}{%s}\\vbox{\\box\\foundexternalfigure}",hash))
+ -- hm, bugged ... waiting for an xform interface
+ if not job.objects.get("FIG::"..hash) then
+ if type(command) == "function" then
+ command()
+ end
+ context.dosetfigureobject(hash)
end
- texsprint(ctxcatcodes,format("\\global\\setbox\\foundexternalfigure\\vbox{\\getobject{FIG}{%s}}",hash))
- else
- texsprint(ctxcatcodes,command)
+ context.doboxfigureobject(hash)
+ elseif type(command) == "function" then
+ command()
end
return data
end
-function figures.includers.nongeneric(data)
+
+function includers.nongeneric(data)
return data
end
-- -- -- mov -- -- --
-function figures.checkers.mov(data)
+function checkers.mov(data)
local dr, du, ds = data.request, data.used, data.status
local width = todimen(dr.width or figures.defaultwidth)
local height = todimen(dr.height or figures.defaultheight)
@@ -820,12 +941,12 @@ function figures.checkers.mov(data)
dr.width, dr.height = width, height
du.width, du.height, du.foundname = width, height, foundname
if trace_inclusion then
- logs.report("figures","including movie '%s': width %s, height %s",foundname,width,height)
+ report_inclusion("including movie '%s': width %s, height %s",foundname,width,height)
end
-- we need to push the node.write in between ... we could make a shared helper for this
context.startfoundexternalfigure(width .. "sp",height .. "sp")
context(function()
- backends.codeinjections.insertmovie {
+ nodeinjections.insertmovie {
width = width,
height = height,
factor = number.dimenfactors.bp,
@@ -840,7 +961,7 @@ function figures.checkers.mov(data)
return data
end
-figures.includers.mov = figures.includers.nongeneric
+includers.mov = includers.nongeneric
-- -- -- mps -- -- --
@@ -853,49 +974,66 @@ local function internal(askedname)
end
end
-function figures.existers.mps(askedname)
+function existers.mps(askedname)
local mprun, mpnum = internal(askedname)
if mpnum then
return askedname
else
- return figures.existers.generic(askedname)
+ return existers.generic(askedname)
end
end
-function figures.checkers.mps(data)
+
+function checkers.mps(data)
local mprun, mpnum = internal(data.used.fullname)
if mpnum then
- return figures.checkers.nongeneric(data,format("\\docheckfiguremprun{%s}{%s}",mprun,mpnum))
+ return checkers.nongeneric(data,function() context.docheckfiguremprun(mprun,mpnum) end)
else
- return figures.checkers.nongeneric(data,format("\\docheckfiguremps{%s}",data.used.fullname))
+ return checkers.nongeneric(data,function() context.docheckfiguremps(data.used.fullname) end)
end
end
-figures.includers.mps = figures.includers.nongeneric
--- -- -- buffer -- -- --
+includers.mps = includers.nongeneric
-function figures.existers.buffer(askedname)
- askedname = file.nameonly(askedname)
- return buffers.exists(askedname) and askedname
+-- -- -- tex -- -- --
+
+function existers.tex(askedname)
+ askedname = resolvers.findfile(askedname)
+ return (askedname ~= "" and askedname) or false
end
-function figures.checkers.buffer(data)
- return figures.checkers.nongeneric(data,format("\\docheckfigurebuffer{%s}", file.nameonly(data.used.fullname)))
+
+function checkers.tex(data)
+ return checkers.nongeneric(data,function() context.docheckfiguretex(data.used.fullname) end)
end
-figures.includers.buffers = figures.includers.nongeneric
--- -- -- tex -- -- --
+includers.tex = includers.nongeneric
-function figures.existers.tex(askedname)
- askedname = resolvers.find_file(askedname)
- return (askedname ~= "" and askedname) or false
+-- -- -- buffer -- -- --
+
+function existers.buffer(askedname)
+ local name = file.nameonly(askedname)
+ local okay = buffers.exists(name)
+ return okay and name, true -- always quit scanning
end
-function figures.checkers.tex(data)
- return figures.checkers.nongeneric(data,format("\\docheckfiguretex{%s}", data.used.fullname))
+
+function checkers.buffer(data)
+ return checkers.nongeneric(data,function() context.docheckfigurebuffer(file.nameonly(data.used.fullname)) end)
+end
+
+includers.buffers = includers.nongeneric
+
+-- -- -- cld -- -- --
+
+existers.cld = existers.tex
+
+function checkers.cld(data)
+ return checkers.nongeneric(data,function() context.docheckfigurecld(data.used.fullname) end)
end
-figures.includers.tex = figures.includers.nongeneric
+
+includers.cld = includers.nongeneric
-- -- -- converters -- -- --
-local function makeoptions(program)
+local function makeoptions(options)
local to = type(options)
return (to == "table" and concat(options," ")) or (to == "string" and options) or ""
end
@@ -903,7 +1041,7 @@ end
local function runprogram(...)
local command = format(...)
if trace_conversion or trace_programs then
- logs.report("figures","running %s",command)
+ report_inclusion("running %s",command)
end
os.spawn(command)
end
@@ -911,22 +1049,30 @@ end
-- -- -- eps -- -- --
local epsconverter = { }
-figures.converters.eps = epsconverter
+converters.eps = epsconverter
-figures.programs.gs = {
+programs.gs = {
+ resolutions = {
+ [variables.low] = "screen",
+ [variables.medium] = "ebook",
+ [variables.high] = "prepress",
+ },
options = {
"-dAutoRotatePages=/None",
- "-dPDFSETTINGS=/prepress",
+ "-dPDFSETTINGS=/%s",
"-dEPSCrop",
},
- command = (os.type == "windows" and "gswin32") or "gs"
+ command = (os.type == "windows" and "gswin32c") or "gs"
}
-function epsconverter.pdf(oldname,newname)
- local gs = figures.programs.gs
+function epsconverter.pdf(oldname,newname,resolution) -- the resolution interface might change
+ local gs = programs.gs
runprogram (
'%s -q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH %s -sOutputFile="%s" "%s" -c quit',
- gs.command, makeoptions(gs.options), newname, oldname
+ gs.command,
+ format(makeoptions(gs.options),gs.resolutions[resolution or ""] or "prepress"),
+ newname,
+ oldname
)
end
@@ -935,12 +1081,12 @@ epsconverter.default = epsconverter.pdf
-- -- -- svg -- -- --
local svgconverter = { }
-figures.converters.svg = svgconverter
-figures.converters.svgz = svgconverter
+converters.svg = svgconverter
+converters.svgz = svgconverter
-- inkscape on windows only works with complete paths
-figures.programs.inkscape = {
+programs.inkscape = {
options = {
"--export-dpi=600"
},
@@ -948,15 +1094,17 @@ figures.programs.inkscape = {
}
function svgconverter.pdf(oldname,newname)
- local inkscape = figures.programs.inkscape
+ local inkscape = programs.inkscape
+ local oldname = dir.expandname(oldname)
+ local newname = dir.expandname(newname)
runprogram (
- '%s "%s" --export-pdf="%s" %s',
- inkscape.command, oldname, newname, makeoptions(inkscape.options)
+ '%s "%s" %s -A "%s"',
+ inkscape.command, oldname, makeoptions(inkscape.options), newname
)
end
function svgconverter.png(oldname,newname)
- local inkscape = figures.programs.inkscape
+ local inkscape = programs.inkscape
runprogram (
'%s "%s" --export-png="%s" %s',
inkscape.command, oldname, newname, makeoptions(inkscape.options)
@@ -968,16 +1116,17 @@ svgconverter.default = svgconverter.pdf
-- -- -- gif -- -- --
local gifconverter = { }
-figures.converters.gif = gifconverter
+converters.gif = gifconverter
-figures.programs.convert = {
- command = "convert" -- imagemagick
+programs.convert = {
+ command = "convert" -- imagemagick
+ -- command = "gm convert" -- graphicmagick
}
function gifconverter.pdf(oldname,newname)
- local convert = figures.programs.convert
+ local convert = programs.convert
runprogram (
- "convert %s %s",
+ "%s %s %s %s",
convert.command, makeoptions(convert.options), oldname, newname
)
end
@@ -1010,7 +1159,7 @@ function bases.use(basename)
xml.registerns("rlx","http://www.pragma-ade.com/schemas/rlx") -- we should be able to do this per xml file
end
if trace_bases then
- commands.writestatus("figures","registering base '%s'",basename)
+ report_inclusion("registering base '%s'",basename)
end
end
end
@@ -1018,7 +1167,7 @@ end
function bases.find(basename,askedlabel)
if trace_bases then
- commands.writestatus("figures","checking for '%s' in base '%s'",askedlabel,basename)
+ report_inclusion("checking for '%s' in base '%s'",askedlabel,basename)
end
basename = file.addsuffix(basename,"xml")
local t = bases.found[askedlabel]
@@ -1035,7 +1184,7 @@ function bases.find(basename,askedlabel)
base[2] = xmlfile
base[3] = xml.load(xmlfile)
if trace_bases then
- commands.writestatus("figures","base '%s' loaded",xmlfile)
+ report_inclusion("base '%s' loaded",xmlfile)
end
break
end
@@ -1054,13 +1203,13 @@ function bases.find(basename,askedlabel)
}
bases.found[askedlabel] = t
if trace_bases then
- commands.writestatus("figures","figure '%s' found in base '%s'",askedlabel,base[2])
+ report_inclusion("figure '%s' found in base '%s'",askedlabel,base[2])
end
return t
end
end
if trace_bases and not t then
- commands.writestatus("figures","figure '%s' not found in base '%s'",askedlabel,base[2])
+ report_inclusion("figure '%s' not found in base '%s'",askedlabel,base[2])
end
end
end
@@ -1081,7 +1230,7 @@ function bases.locate(askedlabel)
return false
end
-function figures.identifiers.base(data)
+function identifiers.base(data)
if bases.enabled then
local dr, du, ds = data.request, data.used, data.status
local fbl = bases.locate(dr.name or dr.label)
@@ -1098,18 +1247,46 @@ function figures.identifiers.base(data)
return data
end
-figures.identifiers.list = {
- figures.identifiers.base,
- figures.identifiers.default
+identifiers.list = {
+ identifiers.base,
+ identifiers.default
}
-- tracing
statistics.register("graphics processing time", function()
- local n = figures.n
- if n > 0 then
- return format("%s seconds including tex, n=%s", statistics.elapsedtime(figures),n)
+ local nofprocessed = figures.nofprocessed
+ if nofprocessed > 0 then
+ return format("%s seconds including tex, %s processed images", statistics.elapsedtime(figures),nofprocessed)
else
return nil
end
end)
+
+-- helper
+
+function figures.applyratio(width,height,w,h) -- width and height are strings and w and h are numbers
+ if not width or width == "" then
+ if not height or height == "" then
+ return figures.defaultwidth, figures.defaultheight
+ else
+ height = string.todimen(height)
+ if w and h then
+ return height * w/h, height
+ else
+ return figures.defaultwidth, height
+ end
+ end
+ else
+ width = string.todimen(width)
+ if not height or height == "" then
+ if w and h then
+ return width, width * h/w
+ else
+ return width, figures.defaultheight
+ end
+ else
+ return width, string.todimen(height)
+ end
+ end
+end