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 pdfstring = lpdf.string 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")