summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkiv/font-ocl.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/font-ocl.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/mkiv/font-ocl.lua548
1 files changed, 548 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-ocl.lua b/Master/texmf-dist/tex/context/base/mkiv/font-ocl.lua
new file mode 100644
index 00000000000..2cb0c1eb7fb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkiv/font-ocl.lua
@@ -0,0 +1,548 @@
+if not modules then modules = { } end modules ['font-ocl'] = {
+ version = 1.001,
+ comment = "companion to font-otf.lua (context)",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo : user list of colors
+
+local tostring, next, format = tostring, next, string.format
+local round, max = math.round, math.round
+local sortedkeys, sortedhash = table.sortedkeys, table.sortedhash
+
+local formatters = string.formatters
+local tounicode = fonts.mappings.tounicode
+
+local otf = fonts.handlers.otf
+
+local f_color = formatters["pdf:direct:%f %f %f rg"]
+local f_gray = formatters["pdf:direct:%f g"]
+local s_black = "pdf:direct:0 g"
+
+if context then
+
+ local startactualtext = nil
+ local stopactualtext = nil
+
+ function otf.getactualtext(s)
+ if not startactualtext then
+ startactualtext = backends.codeinjections.startunicodetoactualtextdirect
+ stopactualtext = backends.codeinjections.stopunicodetoactualtextdirect
+ end
+ return startactualtext(s), stopactualtext()
+ end
+
+else
+
+ local tounicode = fonts.mappings.tounicode16
+
+ function otf.getactualtext(s)
+ return
+ "/Span << /ActualText <feff" .. n .. "> >> BDC",
+ "EMC"
+ end
+
+end
+
+local sharedpalettes = { }
+
+if context then
+
+ local graytorgb = attributes.colors.graytorgb
+ local cmyktorgb = attributes.colors.cmyktorgb
+
+ function otf.registerpalette(name,values)
+ sharedpalettes[name] = values
+ for i=1,#values do
+ local v = values[i]
+ local r, g, b
+ local s = v.s
+ if s then
+ r, g, b = graytorgb(s)
+ else
+ local c, m, y, k = v.c, v.m, v.y, v.k
+ if c or m or y or k then
+ r, g, b = cmyktorgb(c or 0,m or 0,y or 0,k or 0)
+ else
+ r, g, b = v.r, v.g, v.b
+ end
+ end
+ values[i] = {
+ max(r and round(r*255) or 0,255),
+ max(g and round(g*255) or 0,255),
+ max(b and round(b*255) or 0,255)
+ }
+ end
+ end
+
+else -- for generic
+
+ function otf.registerpalette(name,values)
+ sharedpalettes[name] = values
+ for i=1,#values do
+ local v = values[i]
+ values[i] = {
+ max(round((v.r or 0)*255),255),
+ max(round((v.g or 0)*255),255),
+ max(round((v.b or 0)*255),255)
+ }
+ end
+ end
+
+end
+
+local function initializecolr(tfmdata,kind,value) -- hm, always value
+ if value then
+ local palettes = tfmdata.resources.colorpalettes
+ if palettes then
+ --
+ local palette = sharedpalettes[value] or palettes[tonumber(value) or 1] or palettes[1] or { }
+ local classes = #palette
+ if classes == 0 then
+ return
+ end
+ --
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local properties = tfmdata.properties
+ local colorvalues = { }
+ --
+ properties.virtualized = true
+ tfmdata.fonts = {
+ { id = 0 }
+ }
+ --
+ for i=1,classes do
+ local p = palette[i]
+ local r, g, b = p[1], p[2], p[3]
+ if r == g and g == b then
+ colorvalues[i] = { "special", f_gray(r/255) }
+ else
+ colorvalues[i] = { "special", f_color(r/255,g/255,b/255) }
+ end
+ end
+ --
+ local getactualtext = otf.getactualtext
+ --
+ for unicode, character in next, characters do
+ local description = descriptions[unicode]
+ if description then
+ local colorlist = description.colors
+ if colorlist then
+ local b, e = getactualtext(tounicode(characters[unicode].unicode or 0xFFFD))
+ local w = character.width or 0
+ local s = #colorlist
+ local t = {
+ -- We need to force page first because otherwise the q's get outside
+ -- the font switch and as a consequence the next character has no font
+ -- set (well, it has: the preceding one). As a consequence these fonts
+ -- are somewhat inefficient as each glyph gets the font set. It's a
+ -- side effect of the fact that a font is handled when a character gets
+ -- flushed.
+ -- { "special", "pdf:page:q" },
+ -- { "special", "pdf:raw:" .. b }
+ -- This seems to be okay too:
+ { "special", "pdf:direct:q " .. b },
+ }
+ local n = #t
+ for i=1,s do
+ local entry = colorlist[i]
+ n = n + 1 t[n] = colorvalues[entry.class] or s_black
+ n = n + 1 t[n] = { "char", entry.slot }
+ if s > 1 and i < s and w ~= 0 then
+ n = n + 1 t[n] = { "right", -w }
+ end
+ end
+ -- n = n + 1 t[n] = { "special", "pdf:page:" .. e }
+ -- n = n + 1 t[n] = { "special", "pdf:raw:Q" }
+ -- This seems to be okay too:
+ n = n + 1 t[n] = { "special", "pdf:direct:" .. e .. " Q"}
+ character.commands = t
+ end
+ end
+ end
+ end
+ end
+end
+
+fonts.handlers.otf.features.register {
+ name = "colr",
+ description = "color glyphs",
+ manipulators = {
+ base = initializecolr,
+ node = initializecolr,
+ }
+}
+
+do
+
+ -- local f_setstream = formatters[ [[io.savedata("svg-glyph-%05i",%q)]] ]
+ -- local f_getstream = formatters[ [[svg-glyph-%05i]] ]
+
+ -- function otfsvg.storepdfdata(pdf)
+ -- nofstreams = nofstreams + 1
+ -- storepdfdata = function(pdf)
+ -- nofstreams = nofstreams + 1
+ -- return f_setstream(nofstreams,pdf), f_getstream(nofstreams)
+ -- end
+ -- end
+
+ local nofstreams = 0
+ local f_name = formatters[ [[pdf-glyph-%05i]] ]
+ local f_used = context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ]
+ local hashed = { }
+ local cache = { }
+
+ function otf.storepdfdata(pdf)
+ local done = hashed[pdf]
+ if not done then
+ nofstreams = nofstreams + 1
+ local o, n = epdf.openMemStream(pdf,#pdf,f_name(nofstreams))
+ cache[n] = o -- we need to keep in mem
+ done = f_used(n)
+ hashed[pdf] = done
+ end
+ return nil, done, nil
+ end
+
+ -- maybe more efficient but much slower (and we hash already)
+ --
+ -- if context then
+ --
+ -- local storepdfdata = otf.storepdfdata
+ -- local initialized = false
+ --
+ -- function otf.storepdfdata(pdf)
+ -- if not initialized then
+ -- if resolvers.setmemstream then
+ -- local f_setstream = formatters[ [[resolvers.setmemstream("pdf-glyph-%05i",%q,true)]] ]
+ -- local f_getstream = formatters[ [[memstream:///pdf-glyph-%05i]] ]
+ -- local f_nilstream = formatters[ [[resolvers.resetmemstream("pdf-glyph-%05i",true)]] ]
+ -- storepdfdata = function(pdf)
+ -- local done = hashed[pdf]
+ -- local set = nil
+ -- local reset = nil
+ -- if not done then
+ -- nofstreams = nofstreams + 1
+ -- set = f_setstream(nofstreams,pdf)
+ -- done = f_getstream(nofstreams)
+ -- reset = f_nilstream(nofstreams)
+ -- hashed[pdf] = done
+ -- end
+ -- return set, done, reset
+ -- end
+ -- otf.storepdfdata = storepdfdata
+ -- end
+ -- initialized = true
+ -- end
+ -- return storepdfdata(pdf)
+ -- end
+ --
+ -- end
+
+end
+
+local function pdftovirtual(tfmdata,pdfshapes,kind) -- kind = sbix|svg
+ if not tfmdata or not pdfshapes or not kind then
+ return
+ end
+ --
+ local characters = tfmdata.characters
+ local properties = tfmdata.properties
+ local parameters = tfmdata.parameters
+ local hfactor = parameters.hfactor
+ --
+ properties.virtualized = true
+ --
+ tfmdata.fonts = {
+ { id = 0 }
+ }
+ --
+ local getactualtext = otf.getactualtext
+ local storepdfdata = otf.storepdfdata
+ --
+ -- local nop = { "nop" }
+ --
+ for unicode, character in sortedhash(characters) do -- sort is nicer for svg
+ local index = character.index
+ if index then
+ local pdf = pdfshapes[index]
+ local typ = type(pdf)
+ local data = nil
+ local dx = nil
+ local dy = nil
+ if typ == "table" then
+ data = pdf.data
+ dx = pdf.dx or 0
+ dy = pdf.dy or 0
+ elseif typ == "string" then
+ data = pdf
+ dx = 0
+ dy = 0
+ end
+ if data then
+ local setcode, name, nilcode = storepdfdata(data)
+ if name then
+ local bt, et = getactualtext(unicode)
+ local wd = character.width or 0
+ local ht = character.height or 0
+ local dp = character.depth or 0
+ character.commands = {
+ { "special", "pdf:direct:" .. bt },
+ { "down", dp + dy * hfactor },
+ { "right", dx * hfactor },
+ -- setcode and { "lua", setcode } or nop,
+ { "image", { filename = name, width = wd, height = ht, depth = dp } },
+ -- nilcode and { "lua", nilcode } or nop,
+ { "special", "pdf:direct:" .. et },
+ }
+ character[kind] = true
+ end
+ end
+ end
+ end
+end
+
+local otfsvg = otf.svg or { }
+otf.svg = otfsvg
+otf.svgenabled = true
+
+do
+
+ local report_svg = logs.reporter("fonts","svg conversion")
+
+ local loaddata = io.loaddata
+ local savedata = io.savedata
+ local remove = os.remove
+
+ if context and xml.convert then
+
+ local xmlconvert = xml.convert
+ local xmlfirst = xml.first
+
+ function otfsvg.filterglyph(entry,index)
+ local svg = xmlconvert(entry.data)
+ local root = svg and xmlfirst(svg,"/svg[@id='glyph"..index.."']")
+ local data = root and tostring(root)
+ -- report_svg("data for glyph %04X: %s",index,data)
+ return data
+ end
+
+ else
+
+ function otfsvg.filterglyph(entry,index) -- can be overloaded
+ return entry.data
+ end
+
+ end
+
+ local runner = sandbox and sandbox.registerrunner {
+ name = "otfsvg",
+ program = "inkscape",
+ method = "pipeto",
+ template = "--shell > temp-otf-svg-shape.log",
+ reporter = report_svg,
+ }
+
+ if not runner then
+ --
+ -- poor mans variant for generic:
+ --
+ runner = function()
+ return io.open("inkscape --shell > temp-otf-svg-shape.log","w")
+ end
+ end
+
+ function otfsvg.topdf(svgshapes)
+ local pdfshapes = { }
+ local inkscape = runner()
+ if inkscape then
+ local nofshapes = #svgshapes
+ local f_svgfile = formatters["temp-otf-svg-shape-%i.svg"]
+ local f_pdffile = formatters["temp-otf-svg-shape-%i.pdf"]
+ local f_convert = formatters["%s --export-pdf=%s\n"]
+ local filterglyph = otfsvg.filterglyph
+ local nofdone = 0
+ report_svg("processing %i svg containers",nofshapes)
+ statistics.starttiming()
+ for i=1,nofshapes do
+ local entry = svgshapes[i]
+ for index=entry.first,entry.last do
+ local data = filterglyph(entry,index)
+ if data and data ~= "" then
+ local svgfile = f_svgfile(index)
+ local pdffile = f_pdffile(index)
+ savedata(svgfile,data)
+ inkscape:write(f_convert(svgfile,pdffile))
+ pdfshapes[index] = true
+ nofdone = nofdone + 1
+ if nofdone % 100 == 0 then
+ report_svg("%i shapes processed",nofdone)
+ end
+ end
+ end
+ end
+ inkscape:write("quit\n")
+ inkscape:close()
+ report_svg("processing %i pdf results",nofshapes)
+ for index in next, pdfshapes do
+ local svgfile = f_svgfile(index)
+ local pdffile = f_pdffile(index)
+ pdfshapes[index] = loaddata(pdffile)
+ remove(svgfile)
+ remove(pdffile)
+ end
+ statistics.stoptiming()
+ if statistics.elapsedseconds then
+ report_svg("svg conversion time %s",statistics.elapsedseconds() or "-")
+ end
+ end
+ return pdfshapes
+ end
+
+end
+
+local function initializesvg(tfmdata,kind,value) -- hm, always value
+ if value and otf.svgenabled then
+ local svg = tfmdata.properties.svg
+ local hash = svg and svg.hash
+ local timestamp = svg and svg.timestamp
+ if not hash then
+ return
+ end
+ local pdffile = containers.read(otf.pdfcache,hash)
+ local pdfshapes = pdffile and pdffile.pdfshapes
+ if not pdfshapes or pdffile.timestamp ~= timestamp then
+ local svgfile = containers.read(otf.svgcache,hash)
+ local svgshapes = svgfile and svgfile.svgshapes
+ pdfshapes = svgshapes and otfsvg.topdf(svgshapes) or { }
+ containers.write(otf.pdfcache, hash, {
+ pdfshapes = pdfshapes,
+ timestamp = timestamp,
+ })
+ end
+ pdftovirtual(tfmdata,pdfshapes,"svg")
+ end
+end
+
+fonts.handlers.otf.features.register {
+ name = "svg",
+ description = "svg glyphs",
+ manipulators = {
+ base = initializesvg,
+ node = initializesvg,
+ }
+}
+
+-- This can be done differently e.g. with ffi and gm and we can share code anway. Using
+-- batchmode in gm is not faster and as it accumulates we would need to flush all
+-- individual shapes.
+
+local otfsbix = otf.sbix or { }
+otf.sbix = otfsbix
+otf.sbixenabled = true
+
+do
+
+ -- for now png but also other bitmap formats
+
+ local report_sbix = logs.reporter("fonts","sbix conversion")
+
+ local loaddata = io.loaddata
+ local savedata = io.savedata
+ local remove = os.remove
+
+ local runner = sandbox and sandbox.registerrunner {
+ name = "otfsbix",
+ program = "gm",
+ template = "convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log",
+ -- reporter = report_sbix,
+ }
+
+ if not runner then
+ --
+ -- poor mans variant for generic:
+ --
+ runner = function()
+ return os.execute("gm convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log")
+ end
+ end
+
+ -- Alternatively we can create a single pdf file with -adjoin and then pick up pages from
+ -- that file but creating thousands of small files is no fun either.
+
+ function otfsbix.topdf(sbixshapes)
+ local pdfshapes = { }
+ local sbixfile = "temp-otf-sbix-shape.sbix"
+ local pdffile = "temp-otf-sbix-shape.pdf"
+ local nofdone = 0
+ local indices = sortedkeys(sbixshapes) -- can be sparse
+ local nofindices = #indices
+ report_sbix("processing %i sbix containers",nofindices)
+ statistics.starttiming()
+ for i=1,nofindices do
+ local index = indices[i]
+ local entry = sbixshapes[index]
+ local data = entry.data
+ local x = entry.x
+ local y = entry.y
+ savedata(sbixfile,data)
+ runner()
+ pdfshapes[index] = {
+ x = x ~= 0 and x or nil,
+ y = y ~= 0 and y or nil,
+ data = loaddata(pdffile),
+ }
+ nofdone = nofdone + 1
+ if nofdone % 100 == 0 then
+ report_sbix("%i shapes processed",nofdone)
+ end
+ end
+ report_sbix("processing %i pdf results",nofindices)
+ remove(sbixfile)
+ remove(pdffile)
+ statistics.stoptiming()
+ if statistics.elapsedseconds then
+ report_sbix("sbix conversion time %s",statistics.elapsedseconds() or "-")
+ end
+ return pdfshapes
+ -- end
+ end
+
+end
+
+local function initializesbix(tfmdata,kind,value) -- hm, always value
+ if value and otf.sbixenabled then
+ local sbix = tfmdata.properties.sbix
+ local hash = sbix and sbix.hash
+ local timestamp = sbix and sbix.timestamp
+ if not hash then
+ return
+ end
+ local pdffile = containers.read(otf.pdfcache,hash)
+ local pdfshapes = pdffile and pdffile.pdfshapes
+ if not pdfshapes or pdffile.timestamp ~= timestamp then
+ local sbixfile = containers.read(otf.sbixcache,hash)
+ local sbixshapes = sbixfile and sbixfile.sbixshapes
+ pdfshapes = sbixshapes and otfsbix.topdf(sbixshapes) or { }
+ containers.write(otf.pdfcache, hash, {
+ pdfshapes = pdfshapes,
+ timestamp = timestamp,
+ })
+ end
+ --
+ pdftovirtual(tfmdata,pdfshapes,"sbix")
+ end
+end
+
+fonts.handlers.otf.features.register {
+ name = "sbix",
+ description = "sbix glyphs",
+ manipulators = {
+ base = initializesbix,
+ node = initializesbix,
+ }
+}
+