diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt | 639 |
1 files changed, 639 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt new file mode 100644 index 00000000000..afb149d8af7 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt @@ -0,0 +1,639 @@ +if not modules then modules = { } end modules ['lpdf-mis'] = { + version = 1.001, + comment = "companion to lpdf-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- Although we moved most pdf handling to the lua end, we didn't change +-- the overall approach. For instance we share all resources i.e. we +-- don't make subsets for each xform or page. The current approach is +-- quite efficient. A big difference between MkII and MkIV is that we +-- now use forward references. In this respect the MkII code shows that +-- it evolved over a long period, when backends didn't provide forward +-- referencing and references had to be tracked in multiple passes. Of +-- course there are a couple of more changes. + +local next, tostring, type = next, tostring, type +local format, gsub, formatters = string.format, string.gsub, string.formatters +local concat, flattened = table.concat, table.flattened +local settings_to_array = utilities.parsers.settings_to_array + +local backends, lpdf, nodes = backends, lpdf, nodes + +local nodeinjections = backends.pdf.nodeinjections +local codeinjections = backends.pdf.codeinjections +local registrations = backends.pdf.registrations + +local getpagedimensions = layouts.getpagedimensions +local getcanvas = layouts.getcanvas + +local nuts = nodes.nuts +local copy_node = nuts.copy + +local nodepool = nuts.pool +local setstate = nodepool.setstate +local register = nodepool.register + +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfconstant = lpdf.constant +local pdfreference = lpdf.reference +local pdfunicode = lpdf.unicode +local pdfverbose = lpdf.verbose +local pdfstring = lpdf.string +local pdfaction = lpdf.action + +local pdfflushobject +local pdfflushstreamobject +local pdfminorversion + +updaters.register("backend.update.lpdf",function() + pdfflushobject = lpdf.flushobject + pdfflushstreamobject = lpdf.flushstreamobject + pdfminorversion = lpdf.minorversion +end) + +local formattedtimestamp = lpdf.pdftimestamp +local adddocumentextgstate = lpdf.adddocumentextgstate +local addtocatalog = lpdf.addtocatalog +local addtoinfo = lpdf.addtoinfo +local addtopageattributes = lpdf.addtopageattributes +local addtonames = lpdf.addtonames + +local pdfgetmetadata = lpdf.getmetadata + +local texset = tex.set + +local variables = interfaces.variables + +local v_stop = variables.stop +local v_none = variables.none +local v_max = variables.max +local v_bookmark = variables.bookmark +local v_fit = variables.fit +local v_doublesided = variables.doublesided +local v_singlesided = variables.singlesided +local v_default = variables.default +local v_auto = variables.auto +local v_fixed = variables.fixed +local v_landscape = variables.landscape +local v_portrait = variables.portrait +local v_page = variables.page +local v_paper = variables.paper +local v_attachment = variables.attachment +local v_layer = variables.layer +local v_lefttoright = variables.lefttoright +local v_righttoleft = variables.righttoleft +local v_title = variables.title +local v_nomenubar = variables.nomenubar + +local positive = register(setstate("/GSpositive gs")) +local negative = register(setstate("/GSnegative gs")) +local overprint = register(setstate("/GSoverprint gs")) +local knockout = register(setstate("/GSknockout gs")) + +local omitextraboxes = false + +directives.register("backend.omitextraboxes", function(v) omitextraboxes = v end) + +local function initializenegative() + local a = pdfarray { 0, 1 } + local g = pdfconstant("ExtGState") + local d = pdfdictionary { + FunctionType = 4, + Range = a, + Domain = a, + } + local negative = pdfdictionary { Type = g, TR = pdfreference(pdfflushstreamobject("{ 1 exch sub }",d)) } -- can be shared + local positive = pdfdictionary { Type = g, TR = pdfconstant("Identity") } + adddocumentextgstate("GSnegative", pdfreference(pdfflushobject(negative))) + adddocumentextgstate("GSpositive", pdfreference(pdfflushobject(positive))) + initializenegative = nil +end + +local function initializeoverprint() + local g = pdfconstant("ExtGState") + local knockout = pdfdictionary { Type = g, OP = false, OPM = 0 } + local overprint = pdfdictionary { Type = g, OP = true, OPM = 1 } + adddocumentextgstate("GSknockout", pdfreference(pdfflushobject(knockout))) + adddocumentextgstate("GSoverprint", pdfreference(pdfflushobject(overprint))) + initializeoverprint = nil +end + +function nodeinjections.overprint() + if initializeoverprint then initializeoverprint() end + return copy_node(overprint) +end +function nodeinjections.knockout () + if initializeoverprint then initializeoverprint() end + return copy_node(knockout) +end + +function nodeinjections.positive() + if initializenegative then initializenegative() end + return copy_node(positive) +end +function nodeinjections.negative() + if initializenegative then initializenegative() end + return copy_node(negative) +end + +-- function codeinjections.addtransparencygroup() +-- -- png: /CS /DeviceRGB /I true +-- local d = pdfdictionary { +-- S = pdfconstant("Transparency"), +-- I = true, +-- K = true, +-- } +-- lpdf.registerpagefinalizer(function() addtopageattributes("Group",d) end) -- hm +-- end + +-- actions (todo: store and update when changed) + +local openpage, closepage, opendocument, closedocument + +function codeinjections.registerdocumentopenaction(open) + opendocument = open +end + +function codeinjections.registerdocumentcloseaction(close) + closedocument = close +end + +function codeinjections.registerpageopenaction(open) + openpage = open +end + +function codeinjections.registerpagecloseaction(close) + closepage = close +end + +local function flushdocumentactions() + if opendocument then + addtocatalog("OpenAction",pdfaction(opendocument)) + end + if closedocument then + addtocatalog("CloseAction",pdfaction(closedocument)) + end +end + +local function flushpageactions() + if openpage or closepage then + local d = pdfdictionary() + if openpage then + d.O = pdfaction(openpage) + end + if closepage then + d.C = pdfaction(closepage) + end + addtopageattributes("AA",d) + end +end + +lpdf.registerpagefinalizer (flushpageactions, "page actions") +lpdf.registerdocumentfinalizer(flushdocumentactions,"document actions") + +--- info : this can change and move elsewhere + +local identity = { } + +function codeinjections.setupidentity(specification) + for k, v in next, specification do + if v ~= "" then + identity[k] = v + end + end +end + +function codeinjections.getidentityvariable(name) + return identity[name] +end + +local done = false -- using "setupidentity = function() end" fails as the meaning is frozen in register + +local function setupidentity() + if not done then + local metadata = pdfgetmetadata() + local creator = metadata.creator + local version = metadata.contextversion + local time = metadata.time + local jobname = environment.jobname or tex.jobname or "unknown" + -- + local title = identity.title + if not title or title == "" then + title = tex.jobname + end + addtoinfo("Title", pdfunicode(title), title) + local subtitle = identity.subtitle or "" + if subtitle ~= "" then + addtoinfo("Subject", pdfunicode(subtitle), subtitle) + end + local author = identity.author or "" + if author ~= "" then + addtoinfo("Author", pdfunicode(author), author) -- '/Author' in /Info, 'Creator' in XMP + end + addtoinfo("Creator", pdfunicode(creator), creator) + addtoinfo("CreationDate", pdfstring(formattedtimestamp(time))) + local date = identity.date or "" + local pdfdate = date and formattedtimestamp(date) + if pdfdate then + addtoinfo("ModDate", pdfstring(pdfdate), date) + else + -- users should enter the date in 2010-01-19T23:27:50+01:00 format + -- and if not provided that way we use the creation time instead + addtoinfo("ModDate", pdfstring(formattedtimestamp(time)),time) + end + local keywords = identity.keywords or "" + if keywords ~= "" then + keywords = concat(settings_to_array(keywords), " ") + addtoinfo("Keywords", pdfunicode(keywords), keywords) + end + local id = lpdf.id() + addtoinfo("ID", pdfstring(id), id) -- needed for pdf/x + -- + addtoinfo("ConTeXt.Version",version) + addtoinfo("ConTeXt.Time",os.date("%Y-%m-%d %H:%M")) + addtoinfo("ConTeXt.Jobname",jobname) + addtoinfo("ConTeXt.Url","www.pragma-ade.com") + addtoinfo("ConTeXt.Support","contextgarden.net") + addtoinfo("TeX.Support","tug.org") + -- + done = true + else + -- no need for a message + end +end + +lpdf.registerpagefinalizer(setupidentity,"identity") + +-- or when we want to be able to set things after pag e1: +-- +-- lpdf.registerdocumentfinalizer(setupidentity,1,"identity") + +local function flushjavascripts() + local t = interactions.javascripts.flushpreambles() + if #t > 0 then + local a = pdfarray() + local pdf_javascript = pdfconstant("JavaScript") + for i=1,#t do + local ti = t[i] + local name = ti[1] + local script = ti[2] + local j = pdfdictionary { + S = pdf_javascript, + JS = pdfreference(pdfflushstreamobject(script)), + } + a[#a+1] = pdfstring(name) + a[#a+1] = pdfreference(pdfflushobject(j)) + end + addtonames("JavaScript",pdfreference(pdfflushobject(pdfdictionary{ Names = a }))) + end +end + +lpdf.registerdocumentfinalizer(flushjavascripts,"javascripts") + +-- -- -- + +local plusspecs = { + [v_max] = { + mode = "FullScreen", + }, + [v_bookmark] = { + mode = "UseOutlines", + }, + [v_attachment] = { + mode = "UseAttachments", + }, + [v_layer] = { + mode = "UseOC", + }, + [v_fit] = { + fit = true, + }, + [v_doublesided] = { + layout = "TwoColumnRight", + }, + [v_fixed] = { + fixed = true, + }, + [v_landscape] = { + duplex = "DuplexFlipShortEdge", + }, + [v_portrait] = { + duplex = "DuplexFlipLongEdge", + }, + [v_page] = { + duplex = "Simplex" , + }, + [v_paper] = { + paper = true, + }, + [v_title] ={ + title = true, + }, + [v_lefttoright] ={ + direction = "L2R", + }, + [v_righttoleft] ={ + direction = "R2L", + }, + [v_nomenubar] ={ + nomenubar = true, + }, +} + +local pagespecs = { + -- + [v_max] = plusspecs[v_max], + [v_bookmark] = plusspecs[v_bookmark], + [v_attachment] = plusspecs[v_attachment], + [v_layer] = plusspecs[v_layer], + [v_lefttoright] = plusspecs[v_lefttoright], + [v_righttoleft] = plusspecs[v_righttoleft], + [v_title] = plusspecs[v_title], + -- + [v_none] = { + }, + [v_fit] = { + mode = "UseNone", + fit = true, + }, + [v_doublesided] = { + mode = "UseNone", + layout = "TwoColumnRight", + fit = true, + }, + [v_singlesided] = { + mode = "UseNone" + }, + [v_default] = { + mode = "UseNone", + layout = "auto", + }, + [v_auto] = { + mode = "UseNone", + layout = "auto", + }, + [v_fixed] = { + mode = "UseNone", + layout = "auto", + fixed = true, -- noscale + }, + [v_landscape] = { + mode = "UseNone", + layout = "auto", + fixed = true, + duplex = "DuplexFlipShortEdge", + }, + [v_portrait] = { + mode = "UseNone", + layout = "auto", + fixed = true, + duplex = "DuplexFlipLongEdge", + }, + [v_page] = { + mode = "UseNone", + layout = "auto", + fixed = true, + duplex = "Simplex", + }, + [v_paper] = { + mode = "UseNone", + layout = "auto", + fixed = true, + duplex = "Simplex", + paper = true, + }, + [v_nomenubar] = { + mode = "UseNone", + layout = "auto", + nomenubar = true, + }, +} + +local function documentspecification() + local canvas = getcanvas() + + -- move this to layo-ini ? + + local pagespec = canvas.pagespec + if not pagespec or pagespec == "" then + pagespec = v_default + end + local settings = settings_to_array(pagespec) + -- so the first one detemines the defaults + local first = settings[1] + local defaults = pagespecs[first] + local spec = defaults or pagespecs[v_default] + -- successive keys can modify this + if spec.layout == "auto" then + if canvas.doublesided then + local s = pagespecs[v_doublesided] -- to be checked voor interfaces + for k, v in next, s do + spec[k] = v + end + else + spec.layout = false + end + end + -- we start at 2 when we have a valid first default set + for i=defaults and 2 or 1,#settings do + local s = plusspecs[settings[i]] + if s then + for k, v in next, s do + spec[k] = v + end + end + end + + -- maybe interfaces.variables + local layout = spec.layout + local mode = spec.mode + local fit = spec.fit + local fixed = spec.fixed + local duplex = spec.duplex + local paper = spec.paper + local title = spec.title + local direction = spec.direction + local nomenubar = spec.nomenubar + if layout then + addtocatalog("PageLayout",pdfconstant(layout)) + end + if mode then + addtocatalog("PageMode",pdfconstant(mode)) + end + local prints = nil + local marked = canvas.marked + local copies = canvas.copies + if marked then + local pages = structures.pages + local marked = pages.allmarked(marked) + local nofmarked = marked and #marked or 0 + if nofmarked > 0 then + -- the spec is wrong in saying that numbering starts at 1 which of course makes + -- sense as most real documents start with page 0 .. sigh + for i=1,#marked do marked[i] = marked[i] - 1 end + prints = pdfarray(flattened(pages.toranges(marked))) + end + end + if fit or fixed or duplex or copies or paper or prints or title or direction or nomenubar then + addtocatalog("ViewerPreferences",pdfdictionary { + FitWindow = fit and true or nil, + PrintScaling = fixed and pdfconstant("None") or nil, + Duplex = duplex and pdfconstant(duplex) or nil, + NumCopies = copies and copies or nil, + PickTrayByPDFSize = paper and true or nil, + PrintPageRange = prints or nil, + DisplayDocTitle = title and true or nil, + Direction = direction and pdfconstant(direction) or nil, + HideMenubar = nomenubar and true or nil, + }) + end + addtoinfo ("Trapped", pdfconstant("False")) -- '/Trapped' in /Info, 'Trapped' in XMP + addtocatalog("Version", pdfconstant(format("1.%s",pdfminorversion()))) + addtocatalog("Lang", pdfstring(tokens.getters.macro("currentmainlanguage"))) +end + +-- temp hack: the mediabox is not under our control and has a precision of 5 digits + +local factor = number.dimenfactors.bp +local f_value = formatters["%.6N"] + +local function boxvalue(n) -- we could share them + return pdfverbose(f_value(factor * n)) +end + +local function pagespecification() + local canvas = getcanvas() + local paperwidth = canvas.paperwidth + local paperheight = canvas.paperheight + local leftoffset = canvas.leftoffset + local topoffset = canvas.topoffset + -- + local llx = leftoffset + local urx = canvas.width - leftoffset + local lly = paperheight + topoffset - canvas.height + local ury = paperheight - topoffset + -- boxes can be cached + local function extrabox(WhatBox,offset,always) + if offset ~= 0 or always then + addtopageattributes(WhatBox, pdfarray { + boxvalue(llx + offset), + boxvalue(lly + offset), + boxvalue(urx - offset), + boxvalue(ury - offset), + }) + end + end + if omitextraboxes then + -- only useful for testing / comparing + else + extrabox("CropBox",canvas.cropoffset,true) -- mandate for rendering + extrabox("TrimBox",canvas.trimoffset,true) -- mandate for pdf/x + extrabox("BleedBox",canvas.bleedoffset) -- optional + -- extrabox("ArtBox",canvas.artoffset) -- optional .. unclear what this is meant to do + end +end + +lpdf.registerpagefinalizer(pagespecification,"page specification") +lpdf.registerdocumentfinalizer(documentspecification,"document specification") + +-- Page Label support ... +-- +-- In principle we can also support /P (prefix) as we can just use the verbose form +-- and we can then forget about the /St (start) as we don't care about those few +-- extra bytes due to lack of collapsing. Anyhow, for that we need a stupid prefix +-- variant and that's not on the agenda now. + +local map = { + numbers = "D", + Romannumerals = "R", + romannumerals = "r", + Characters = "A", + characters = "a", +} + +-- local function featurecreep() +-- local pages, lastconversion, list = structures.pages.tobesaved, nil, pdfarray() +-- local getstructureset = structures.sets.get +-- for i=1,#pages do +-- local p = pages[i] +-- if not p then +-- return -- fatal error +-- else +-- local numberdata = p.numberdata +-- if numberdata then +-- local conversionset = numberdata.conversionset +-- if conversionset then +-- local conversion = getstructureset("structure:conversions",p.block,conversionset,1,"numbers") +-- if conversion ~= lastconversion then +-- lastconversion = conversion +-- list[#list+1] = i - 1 -- pdf starts numbering at 0 +-- list[#list+1] = pdfdictionary { S = pdfconstant(map[conversion] or map.numbers) } +-- end +-- end +-- end +-- if not lastconversion then +-- lastconversion = "numbers" +-- list[#list+1] = i - 1 -- pdf starts numbering at 0 +-- list[#list+1] = pdfdictionary { S = pdfconstant(map.numbers) } +-- end +-- end +-- end +-- addtocatalog("PageLabels", pdfdictionary { Nums = list }) +-- end + +local function featurecreep() + local pages = structures.pages.tobesaved + local list = pdfarray() + local getset = structures.sets.get + local stopped = false + local oldlabel = nil + local olconversion = nil + for i=1,#pages do + local p = pages[i] + if not p then + return -- fatal error + end + local label = p.viewerprefix or "" + if p.status == v_stop then + if not stopped then + list[#list+1] = i - 1 -- pdf starts numbering at 0 + list[#list+1] = pdfdictionary { + P = pdfunicode(label), + } + stopped = true + end + oldlabel = nil + oldconversion = nil + stopped = false + else + local numberdata = p.numberdata + local conversion = nil + local number = p.number + if numberdata then + local conversionset = numberdata.conversionset + if conversionset then + conversion = getset("structure:conversions",p.block,conversionset,1,"numbers") + end + end + conversion = conversion and map[conversion] or map.numbers + if number == 1 or oldlabel ~= label or oldconversion ~= conversion then + list[#list+1] = i - 1 -- pdf starts numbering at 0 + list[#list+1] = pdfdictionary { + S = pdfconstant(conversion), + St = number, + P = label ~= "" and pdfunicode(label) or nil, + } + end + oldlabel = label + oldconversion = conversion + stopped = false + end + end + addtocatalog("PageLabels", pdfdictionary { Nums = list }) +end + +lpdf.registerdocumentfinalizer(featurecreep,"featurecreep") |