if not modules then modules = { } end modules ['back-pdf'] = {
version = 1.001,
comment = "companion to back-pdf.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- we will move code to lpdf-* files (second cleanup stage)
--[[ldx--
This module implements a couple of cleanup methods. We need these
in order to meet the specification. Watch the double
parenthesis; they are needed because otherwise we would pass more
than one argument to .
--ldx]]--
local type, next, tostring = type, next, tostring
local char, byte, format, gsub, rep, gmatch = string.char, string.byte, string.format, string.gsub, string.rep, string.gmatch
local concat = table.concat
local round = math.round
local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues
local texsprint, texwrite = tex.sprint, tex.write
local ctxcatcodes = tex.ctxcatcodes
local copy_node = node.copy
local nodeinjections = backends.pdf.nodeinjections
local codeinjections = backends.pdf.codeinjections
local registrations = backends.pdf.registrations
local pdfliteral, register = nodes.pdfliteral, nodes.register
local pdfconstant = lpdf.constant
local pdfdictionary = lpdf.dictionary
local pdfarray = lpdf.array
local pdfreference = lpdf.reference
local pdfverbose = lpdf.verbose
local pdfflushobject = lpdf.flushobject
local pdfreserveobject = lpdf.reserveobject
local pdfannotation = nodes.pdfannotation
local pdfreserveobj = pdf.reserveobj
local pdfimmediateobj = pdf.immediateobj
function nodeinjections.rgbcolor(r,g,b)
return register(pdfliteral(format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b)))
end
function nodeinjections.cmykcolor(c,m,y,k)
return register(pdfliteral(format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k)))
end
function nodeinjections.graycolor(s) -- caching 0/1 does not pay off
return register(pdfliteral(format("%s g %s G",s,s)))
end
function nodeinjections.spotcolor(n,f,d,p)
if type(p) == "string" then
p = gsub(p,","," ") -- brr misuse of spot
end
return register(pdfliteral(format("/%s cs /%s CS %s SCN %s scn",n,n,p,p)))
end
function nodeinjections.transparency(n)
return register(pdfliteral(format("/Tr%s gs",n)))
end
local effects = {
normal = 0,
inner = 0,
outer = 1,
both = 2,
hidden = 3,
}
function nodeinjections.effect(effect,stretch,rulethickness)
-- always, no zero test (removed)
rulethickness = number.dimenfactors["bp"] * rulethickness
effect = effects[effect] or effects['normal']
return register(pdfliteral(format("%s Tc %s w %s Tr",stretch,rulethickness,effect))) -- watch order
end
-- cached ..
local cache = { }
function nodeinjections.startlayer(name)
local c = cache[name]
if not c then
c = register(pdfliteral(format("/OC /%s BDC",name)))
cache[name] = c
end
return copy_node(c)
end
local stop = register(pdfliteral("EMC"))
function nodeinjections.stoplayer()
return copy_node(stop)
end
local cache = { }
function nodeinjections.switchlayer(name)
local c = cache[name]
if not c then
c = register(pdfliteral(format("EMC /OC /%s BDC",name)))
end
return copy_node(c)
end
-- code
function codeinjections.insertmovie(specification)
-- managed in figure inclusion: width, height, factor, repeat, controls, preview, label, foundname
local width = specification.width
local height = specification.height
local factor = specification.factor or number.dimenfactors.bp
local moviedict = pdfdictionary {
F = specification.foundname,
Aspect = pdfarray { factor * width, factor * height },
Poster = (specification.preview and true) or false,
}
local controldict = pdfdictionary {
ShowControls = (specification.controls and true) or false,
Mode = (specification["repeat"] and pdfconstant("Repeat")) or nil,
}
local action = pdfdictionary {
Subtype = pdfconstant("Movie"),
Border = pdfarray { 0, 0, 0 },
T = format("movie %s",specification.label),
Movie = moviedict,
A = controldict,
}
node.write(pdfannotation(width,height,0,action()))
end
function codeinjections.insertsound(specification)
-- rmanaged in interaction: repeat, label, foundname
local soundclip = interactions.soundclip(specification.label)
if soundclip then
local controldict = pdfdictionary {
Mode = (specification["repeat"] and pdfconstant("Repeat")) or nil
}
local sounddict = pdfdictionary {
F = soundclip.filename
}
local action = pdfdictionary {
Subtype = pdfconstant("Movie"),
Border = pdfarray { 0, 0, 0 },
T = format("sound %s",specification.label),
Movie = sounddict,
A = controldict,
}
node.write(pdfannotation(0,0,0,action()))
end
end
-- spot- and indexcolors
local pdf_separation = pdfconstant("Separation")
local pdf_indexed = pdfconstant("Indexed")
local pdf_device_n = pdfconstant("DeviceN")
local pdf_device_rgb = pdfconstant("DeviceRGB")
local pdf_device_cmyk = pdfconstant("DeviceCMYK")
local pdf_device_gray = pdfconstant("DeviceGray")
local pdf_extgstate = pdfconstant("ExtGState")
local pdf_rbg_range = pdfarray { 0, 1, 0, 1, 0, 1 }
local pdf_cmyk_range = pdfarray { 0, 1, 0, 1, 0, 1, 0, 1 }
local pdf_gray_range = pdfarray { 0, 1 }
local rgb_function = "dup %s mul exch dup %s mul exch %s mul"
local cmyk_function = "dup %s mul exch dup %s mul exch dup %s mul exch %s mul"
local gray_function = "%s mul"
local documentcolorspaces = pdfdictionary()
local spotcolorhash = { } -- not needed
local spotcolornames = { }
local indexcolorhash = { }
local delayedindexcolors = { }
function registrations.spotcolorname(name,e)
spotcolornames[name] = e or name
end
local function registersomespotcolor(name,noffractions,names,p,colorspace,range,funct)
noffractions = tonumber(noffractions) or 1 -- to be checked
if noffractions == 0 then
-- can't happen
elseif noffractions == 1 then
local dictionary = pdfdictionary {
FunctionType = 4,
Domain = { 0, 1 },
Range = range,
}
local n = pdfimmediateobj("stream",format("{ %s }",funct),dictionary())
local array = pdfarray {
pdf_separation,
pdfconstant(spotcolornames[name] or name),
colorspace,
pdfreference(n),
}
local m = pdfimmediateobj(tostring(array))
local mr = pdfreference(m)
spotcolorhash[name] = m
documentcolorspaces[name] = mr
lpdf.adddocumentcolorspace(name,mr)
else
local cnames = pdfarray()
local domain = pdfarray()
for n in gmatch(names,"[^,]+") do
cnames[#cnames+1] = pdfconstant(spotcolornames[n] or n)
domain[#domain+1] = 0
domain[#domain+1] = 1
end
local dictionary = pdfdictionary {
FunctionType = 4,
Domain = domain,
Range = range,
}
local n = pdfimmediateobj("stream",format("{ %s %s }",rep("pop ",noffractions),funct),dictionary())
local array = pdfarray {
pdf_device_n,
cnames,
colorspace,
pdfreference(n),
}
local m = pdfimmediateobj(tostring(array))
local mr = pdfreference(m)
spotcolorhash[name] = m
documentcolorspaces[name] = mr
lpdf.adddocumentcolorspace(name,mr)
end
end
function registersomeindexcolor(name,noffractions,names,p,colorspace,range,funct)
noffractions = tonumber(noffractions) or 1 -- to be checked
local cnames = pdfarray()
local domain = pdfarray()
if names == "" then
names = name .. ",None"
else
names = names .. ",None"
end
for n in gmatch(names,"[^,]+") do
cnames[#cnames+1] = pdfconstant(spotcolornames[n] or n)
domain[#domain+1] = 0
domain[#domain+1] = 1
end
local dictionary = pdfdictionary {
FunctionType = 4,
Domain = domain,
Range = range,
}
local n = pdfimmediateobj("stream",format("{ %s %s }",rep("exch pop ",noffractions),funct),dictionary()) -- exch pop
local a = pdfarray {
pdf_device_n,
cnames,
colorspace,
pdfreference(n),
}
if p == "" then
p = "1"
else
p = p .. ",1"
end
local pi = { }
for pp in gmatch(p,"[^,]+") do
pi[#pi+1] = tonumber(pp)
end
local vector, set, n = { }, { }, #pi
for i=255,0,-1 do
for j=1,n do
set[j] = format("%02X",round(pi[j]*i))
end
vector[#vector+1] = concat(set)
end
vector = pdfverbose { "<", concat(vector, " "), ">" }
local n = pdfimmediateobj(tostring(pdfarray{ pdf_indexed, a, 255, vector }))
lpdf.adddocumentcolorspace(format("%s_indexed",name),pdfreference(n))
return n
end
-- actually, names (parent) is the hash
local function delayindexcolor(name,names,func)
local hash = (names ~= "" and names) or name
-- logs.report("index colors","delaying '%s'",name)
delayedindexcolors[hash] = func
end
local function indexcolorref(name) -- actually, names (parent) is the hash
if not indexcolorhash[name] then
-- logs.report("index colors","registering '%s'",name)
local delayedindexcolor = delayedindexcolors[name]
if type(delayedindexcolor) == "function" then
indexcolorhash[name] = delayedindexcolor()
delayedindexcolors[name] = true
end
end
return indexcolorhash[name]
end
function registrations.rgbspotcolor(name,noffractions,names,p,r,g,b)
--~ print(name,noffractions,names,p,r,g,b)
if noffractions == 1 then
registersomespotcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rbg_range,format(rgb_function,r,g,b))
else
registersomespotcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rbg_range,format("%s %s %s",r,g,b))
end
delayindexcolor(name,names,function()
return registersomeindexcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,format(rgb_function,r,g,b))
end)
end
function registrations.cmykspotcolor(name,noffractions,names,p,c,m,y,k)
if noffractions == 1 then
registersomespotcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,format(cmyk_function,c,m,y,k))
else
registersomespotcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,format("%s %s %s %s",c,m,y,k))
end
delayindexcolor(name,names,function()
return registersomeindexcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,format(cmyk_function,c,m,y,k))
end)
end
function registrations.grayspotcolor(name,noffractions,names,p,s)
if noffractions == 1 then
registersomespotcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,format(gray_function,s))
else
registersomespotcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,s)
end
delayindexcolor(name,names,function()
return registersomeindexcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,format(gray_function,s))
end)
end
function registrations.rgbindexcolor(name,noffractions,names,p,r,g,b)
registersomeindexcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,format(rgb_function,r,g,b))
end
function registrations.cmykindexcolor(name,noffractions,names,p,c,m,y,k)
registersomeindexcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,format(cmyk_function,c,m,y,k))
end
function registrations.grayindexcolor(name,noffractions,names,p,s)
registersomeindexcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,gray_function)
end
function codeinjections.setfigurecolorspace(data,figure)
local color = data.request.color
if color then
local ref = indexcolorref(color)
if ref then
figure.colorspace = ref
data.used.color = color
end
end
end
-- transparency
local transparencies = { [0] =
pdfconstant("Normal"),
pdfconstant("Normal"),
pdfconstant("Multiply"),
pdfconstant("Screen"),
pdfconstant("Overlay"),
pdfconstant("SoftLight"),
pdfconstant("HardLight"),
pdfconstant("ColorDodge"),
pdfconstant("ColorBurn"),
pdfconstant("Darken"),
pdfconstant("Lighten"),
pdfconstant("Difference"),
pdfconstant("Exclusion"),
pdfconstant("Compatible"),
}
local documenttransparencies = { }
local transparencyhash = { } -- share objects
local done = false
function registrations.transparency(n,a,t)
if not done then
local d = pdfdictionary {
Type = pdf_extgstate,
ca = 1,
CA = 1,
BM = transparencies[1],
AIS = false,
}
local m = pdfimmediateobj(tostring(d))
local mr = pdfreference(m)
transparencyhash[0] = m
documenttransparencies[0] = mr
lpdf.adddocumentextgstate("Tr0",mr)
done = true
end
if n > 0 and not transparencyhash[n] then
local d = pdfdictionary {
Type = pdf_extgstate,
ca = tonumber(t),
CA = tonumber(t),
BM = transparencies[a] or transparencies[0],
AIS = false,
}
local m = pdfimmediateobj(tostring(d))
local mr = pdfreference(m)
transparencyhash[n] = m
documenttransparencies[n] = mr
lpdf.adddocumentextgstate(format("Tr%s",n),mr)
end
end
function codeinjections.adddocumentinfo(key,value)
lpdf.addtoinfo(key,lpdf.tosixteen(value))
end
-- graphics
function codeinjections.setfigurealternative(data,figure)
local display = data.request.display
if display and display ~= "" then
local request = data.request
figures.push {
name = request.display,
page = request.page,
size = request.size,
prefix = request.prefix,
cache = request.cache,
width = request.width,
height = request.height,
}
figures.identify()
local displayfigure = figures.check()
if displayfigure then
-- figure.aform = true
img.immediatewrite(figure)
local a = lpdf.array {
lpdf.dictionary {
Image = lpdf.reference(figure.objnum),
DefaultForPrinting = true,
}
}
local d = lpdf.dictionary {
Alternates = lpdf.reference(pdf.immediateobj(tostring(a))),
}
displayfigure.attr = d()
return displayfigure, figures.current()
end
end
end
-- eventually we need to load this runtime
--
-- backends.install((environment and environment.arguments and environment.arguments.backend) or "pdf")
--
-- but now we need to force this as we also load the pdf tex part which hooks into all kind of places
codeinjections.finalizepage = lpdf.finalizepage
codeinjections.finalizedocument = lpdf.finalizedocument
backends.install("pdf")