diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/grph-inc.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/grph-inc.lua | 891 |
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 |