From 8b678f473da7b81842dbc13b50c6303991570521 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Sat, 25 May 2019 11:48:47 +0200 Subject: 2019-05-25 10:53:00 --- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 5 +- tex/context/base/mkii/mult-de.mkii | 1 + tex/context/base/mkiv/anch-pos.lua | 11 +- tex/context/base/mkiv/anch-pos.mkiv | 6 + tex/context/base/mkiv/back-ini.lua | 145 +- tex/context/base/mkiv/back-ini.mkiv | 129 +- tex/context/base/mkiv/back-lpd.lua | 2477 -------------------- tex/context/base/mkiv/back-lua.mkiv | 18 + tex/context/base/mkiv/back-mps.mkiv | 18 + tex/context/base/mkiv/back-out.lua | 2 +- tex/context/base/mkiv/back-out.mkiv | 2 + tex/context/base/mkiv/back-pdf.lua | 31 +- tex/context/base/mkiv/back-pdf.mkiv | 468 +--- tex/context/base/mkiv/back-pdp.lua | 212 +- tex/context/base/mkiv/back-res.lua | 167 +- tex/context/base/mkiv/back-res.mkiv | 32 + tex/context/base/mkiv/back-trf.lua | 155 ++ tex/context/base/mkiv/back-trf.mkiv | 79 + tex/context/base/mkiv/buff-imp-xml.lua | 4 +- tex/context/base/mkiv/char-fio.lua | 6 +- tex/context/base/mkiv/cont-log.mkiv | 2 + tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/context.mkiv | 79 +- tex/context/base/mkiv/core-sys.mkiv | 12 - tex/context/base/mkiv/data-env.lua | 2 +- tex/context/base/mkiv/driv-shp.lua | 4 +- tex/context/base/mkiv/driv-shp.mkiv | 12 +- tex/context/base/mkiv/font-ocl.lua | 44 +- tex/context/base/mkiv/font-off.lua | 230 -- tex/context/base/mkiv/font-syn.lua | 3 - tex/context/base/mkiv/grph-bmp.lua | 44 +- tex/context/base/mkiv/grph-img.lua | 24 +- tex/context/base/mkiv/grph-inc.lua | 73 +- tex/context/base/mkiv/grph-inc.mkiv | 9 +- tex/context/base/mkiv/grph-mem.lua | 168 +- tex/context/base/mkiv/grph-rul.lua | 237 +- tex/context/base/mkiv/lpdf-eng.lua | 106 + tex/context/base/mkiv/lpdf-epa.lua | 1 - tex/context/base/mkiv/lpdf-epd.lua | 1313 ----------- tex/context/base/mkiv/lpdf-img.lua | 4 +- tex/context/base/mkiv/lpdf-ini.lua | 2 - tex/context/base/mkiv/lpdf-lmt.lua | 2460 +++++++++++++++++++ tex/context/base/mkiv/lpdf-pde.lua | 49 +- tex/context/base/mkiv/luat-cnf.lua | 3 +- tex/context/base/mkiv/luat-mac.lua | 20 +- tex/context/base/mkiv/meta-ini.mkiv | 42 +- tex/context/base/mkiv/mlib-ctx.lua | 13 +- tex/context/base/mkiv/mult-aux.mkiv | 5 +- tex/context/base/mkiv/node-syn.lua | 2 + tex/context/base/mkiv/pack-rul.mkiv | 25 +- tex/context/base/mkiv/publ-imp-apa.mkvi | 10 +- tex/context/base/mkiv/publ-imp-chicago.lua | 560 +++++ tex/context/base/mkiv/publ-imp-chicago.mkvi | 1686 +++++++++++++ tex/context/base/mkiv/publ-imp-cite.mkvi | 6 + tex/context/base/mkiv/publ-ini.lua | 30 +- tex/context/base/mkiv/publ-ini.mkiv | 2 +- tex/context/base/mkiv/status-files.pdf | Bin 26596 -> 26631 bytes tex/context/base/mkiv/status-lua.pdf | Bin 269171 -> 266539 bytes tex/context/base/mkiv/syst-aux.mkiv | 24 +- tex/context/base/mkiv/toks-scn.lua | 20 + tex/context/base/mkiv/util-sbx.lua | 11 +- tex/context/base/mkiv/util-sci.lua | 29 +- tex/context/fonts/mkiv/type-imp-ebgaramond.mkiv | 40 +- tex/context/interface/mkii/keys-de.xml | 1 + tex/context/interface/mkiv/i-context.pdf | Bin 865366 -> 865512 bytes tex/context/interface/mkiv/i-readme.pdf | Bin 61165 -> 61165 bytes tex/generic/context/luatex/luatex-fonts-merged.lua | 39 +- 68 files changed, 6046 insertions(+), 5372 deletions(-) delete mode 100644 tex/context/base/mkiv/back-lpd.lua create mode 100644 tex/context/base/mkiv/back-lua.mkiv create mode 100644 tex/context/base/mkiv/back-mps.mkiv create mode 100644 tex/context/base/mkiv/back-res.mkiv create mode 100644 tex/context/base/mkiv/back-trf.lua create mode 100644 tex/context/base/mkiv/back-trf.mkiv delete mode 100644 tex/context/base/mkiv/font-off.lua create mode 100644 tex/context/base/mkiv/lpdf-eng.lua delete mode 100644 tex/context/base/mkiv/lpdf-epd.lua create mode 100644 tex/context/base/mkiv/lpdf-lmt.lua create mode 100644 tex/context/base/mkiv/publ-imp-chicago.lua create mode 100644 tex/context/base/mkiv/publ-imp-chicago.mkvi (limited to 'tex') diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 9a66b4b07..c6ff7e991 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2019.05.18 10:42} +\newcontextversion{2019.05.25 10:45} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index e54769554..e5c386698 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2019.05.18 10:42} +\edef\contextversion{2019.05.25 10:45} %D For those who want to use this: @@ -34,10 +34,7 @@ \ifx\normalinput\undefined \let\normalinput\input \fi -\def\loadcorefile#1{\normalinput#1\relax} \def\loadmarkfile#1{\normalinput#1.\mksuffix\relax} -\def\loadmkiifile#1{\normalinput#1\relax} -\def\loadmkivfile#1{} %D First we load the system modules. These implement a lot of %D manipulation macros. The first one loads \PLAIN\ \TEX, as diff --git a/tex/context/base/mkii/mult-de.mkii b/tex/context/base/mkii/mult-de.mkii index 46a239304..5557eb7b8 100644 --- a/tex/context/base/mkii/mult-de.mkii +++ b/tex/context/base/mkii/mult-de.mkii @@ -373,6 +373,7 @@ \setinterfacevariable{nohz}{nohz} \setinterfacevariable{noline}{noline} \setinterfacevariable{nomarking}{keinebeschriftung} +\setinterfacevariable{nomenubar}{nomenubar} \setinterfacevariable{none}{kein} \setinterfacevariable{nonumber}{nonumber} \setinterfacevariable{norepeat}{norepeat} diff --git a/tex/context/base/mkiv/anch-pos.lua b/tex/context/base/mkiv/anch-pos.lua index 56250e16d..e53c3c514 100644 --- a/tex/context/base/mkiv/anch-pos.lua +++ b/tex/context/base/mkiv/anch-pos.lua @@ -54,8 +54,6 @@ local texget = tex.get local texsp = tex.sp ----- texsp = string.todimen -- because we cache this is much faster but no rounding -local pdf = pdf -- h and v are variables - local setmetatableindex = table.setmetatableindex local setmetatablenewindex = table.setmetatablenewindex @@ -1449,3 +1447,12 @@ statistics.register("positions", function() return nil end end) + +-- We support the low level positional commands too: + +local newsavepos = nodes.pool.savepos +local implement = interfaces.implement + +implement { name = "savepos", actions = function() context(newsavepos()) end } +implement { name = "lastxpos", actions = function() context(gethpos()) end } +implement { name = "lastypos", actions = function() context(getvpos()) end } diff --git a/tex/context/base/mkiv/anch-pos.mkiv b/tex/context/base/mkiv/anch-pos.mkiv index fbebb5f17..68cb2f723 100644 --- a/tex/context/base/mkiv/anch-pos.mkiv +++ b/tex/context/base/mkiv/anch-pos.mkiv @@ -497,4 +497,10 @@ \let\doifpositionsusedelse\doifelsepositionsused +%D Moved here: + +\unexpanded\def\savepos {\clf_savepos} +\unexpanded\def\lastxpos{\numexpr\clf_lastxpos\relax} +\unexpanded\def\lastypos{\numexpr\clf_lastypos\relax} + \protect \endinput diff --git a/tex/context/base/mkiv/back-ini.lua b/tex/context/base/mkiv/back-ini.lua index b7ecce00b..bc4a67bc0 100644 --- a/tex/context/base/mkiv/back-ini.lua +++ b/tex/context/base/mkiv/back-ini.lua @@ -8,31 +8,21 @@ if not modules then modules = { } end modules ['back-ini'] = { local next, type = next, type local format = string.format -local sind, cosd, abs = math.sind, math.cosd, math.abs -local insert, remove = table.insert, table.remove -local unpack = unpack backends = backends or { } local backends = backends -local trace_backend = false trackers.register("backend.initializers", function(v) trace_finalizers = v end) +local context = context +local trace = false trackers.register("backend", function(v) trace = v end) local report = logs.reporter("backend") -local report_backend = logs.reporter("backend","initializing") local allocate = utilities.storage.allocate local setmetatableindex = table.setmetatableindex local setaction = nodes.tasks.setaction -local scanners = tokens.scanners -local scannumber = scanners.number -local scankeyword = scanners.keyword -local scancount = scanners.count -local scanstring = scanners.string - -local scanners = interfaces.scanners - local implement = interfaces.implement +local variables = interfaces.variables local texset = tex.set @@ -87,11 +77,11 @@ function backends.install(what) if type(what) == "string" then local backend = backends[what] if backend then - if trace_backend then + if trace then if backend.comment then - report_backend("initializing backend %a, %a",what,backend.comment) + report("initializing backend %a, %a",what,backend.comment) else - report_backend("initializing backend %a",what) + report("initializing backend %a",what) end end backends.current = what @@ -101,8 +91,8 @@ function backends.install(what) setmetatableindex(plugin, default) setmetatableindex(target, plugin) end - elseif trace_backend then - report_backend("no backend named %a",what) + elseif trace then + report("no backend named %a",what) end end end @@ -139,7 +129,7 @@ interfaces.implement { name = "setrealspaces", arguments = "string", actions = function(v) - setaction("shipouts","nodes.handlers.accessibility",v == interfaces.variables.yes) + setaction("shipouts","nodes.handlers.accessibility",v == variables.yes) end } @@ -197,120 +187,3 @@ implement { function backends.noflatelua() return status.late_callbacks or 0 end - --- - -local stack = { } -local restore = true -- false - -local nodepool = nodes.pool -local savenode = nodepool.save -local restorenode = nodepool.restore -local setmatrixnode = nodepool.setmatrix - -updaters.register("backend.update",function() - savenode = nodepool.save - restorenode = nodepool.restore - setmatrixnode = nodepool.setmatrix -end) - -local function stopsomething() - local top = remove(stack) - if top == false then - -- not wrapped - elseif top == true then - context(restorenode()) - elseif top then - context(setmatrixnode(unpack(top))) -- not really needed anymore - context(restorenode()) - else - -- nesting error - end -end - -local function startrotation() - local a = scannumber() - if a == 0 then - insert(stack,false) - else - local s, c = sind(a), cosd(a) - if abs(s) < 0.000001 then - s = 0 -- otherwise funny -0.00000 - end - if abs(c) < 0.000001 then - c = 0 -- otherwise funny -0.00000 - end - context(savenode()) - context(setmatrixnode(c,s,-s,c)) - insert(stack,restore and { c, -s, s, c } or true) - end -end - -implement { name = "startrotation", actions = startrotation } -implement { name = "stoprotation", actions = stopsomething } - -local function startscaling() -- at the tex end we use sx and sy instead of rx and ry - local rx, ry = 1, 1 - while true do - if scankeyword("rx") then - rx = scannumber() - elseif scankeyword("ry") then - ry = scannumber() - -- elseif scankeyword("revert") then - -- local top = stack[#stack] - -- if top then - -- rx = top[1] - -- ry = top[4] - -- else - -- rx = 1 - -- ry = 1 - -- end - else - break - end - end - if rx == 1 and ry == 1 then - insert(stack,false) - else - if rx == 0 then - rx = 0.0001 - end - if ry == 0 then - ry = 0.0001 - end - context(savenode()) - context(setmatrixnode(rx,0,0,ry)) - insert(stack,restore and { 1/rx, 0, 0, 1/ry } or true) - end -end - -implement { name = "startscaling", actions = startscaling } -implement { name = "stopscaling", actions = stopsomething } - -local function startmatrix() -- rx sx sy ry -- tx, ty - local rx, sx, sy, ry = 1, 0, 0, 1 - while true do - if scankeyword("rx") then rx = scannumber() - elseif scankeyword("ry") then ry = scannumber() - elseif scankeyword("sx") then sx = scannumber() - elseif scankeyword("sy") then sy = scannumber() - else break end - end - if rx == 1 and sx == 0 and sy == 0 and ry == 1 then - insert(stack,false) - else - context(savenode()) - context(setmatrixnode(rx,sx,sy,ry)) - insert(stack,store and { -rx, -sx, -sy, -ry } or true) - end -end - -implement { name = "startmatrix", actions = startmatrix } -implement { name = "stopmatrix", actions = stopsomething } - -local function startmirroring() - context(setmatrixnode(-1,0,0,1)) -end - -implement { name = "startmirroring", actions = startmirroring } -implement { name = "stopmirroring", actions = startmirroring } -- not: stopsomething diff --git a/tex/context/base/mkiv/back-ini.mkiv b/tex/context/base/mkiv/back-ini.mkiv index 8729403f8..ff19d0229 100644 --- a/tex/context/base/mkiv/back-ini.mkiv +++ b/tex/context/base/mkiv/back-ini.mkiv @@ -11,35 +11,12 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -%D Most will go away here as it is replaced by \LUA\ calls to -%D backend functions. - \writestatus{loading}{ConTeXt Backend Macros / Initialization} -%D The exact page model depends on the backend so we just define some -%D variables that are used. A helper at the \LUA\ end will synchronize -%D with the internal variables. We store these in the format. - -% \newdimen\backendpageheight -% \newdimen\backendpagewidth -% \newdimen\backendinchoffset \backendinchoffset=1in - -%D Now we load the \LUA\ code: - \registerctxluafile{back-ini}{} -\registerctxluafile{back-res}{} - -\doifelsefileexists{back-out.mkiv}{\loadmarkfile{back-out}}{} - -%D We currently have a curious mix between tex and lua backend handling but -%D eventually most will move to \LUA. \unprotect -\ifdefined\everybackendshipout \else \newtoks\everybackendshipout \fi -\ifdefined\everylastbackendshipout \else \newtoks\everylastbackendshipout \fi -\ifdefined\everybackendlastinshipout \else \newtoks\everybackendlastinshipout \fi % e.g. finalize via latelua - %D Right from the start \CONTEXT\ had a backend system based on runtime pluggable %D code. As most backend issues involved specials and since postprocessors had not %D that much in common, we ended up with a system where we could switch backend as @@ -52,104 +29,30 @@ %D At this moment \DVI\ is no longer used for advanced document output and we %D therefore dropped support for this format. Future versions might support more %D backends again, but this has a low priority. -%D -%D Not everything here makes sense and the content of this file will definitely -%D change (or even go away). - -% rotation - -\unexpanded\def\dostartrotation#1% - {\forcecolorhack - \clf_startrotation#1\relax} % todo: implement without Q q - -\unexpanded\def\dostoprotation - {\clf_stoprotation - \forcecolorhack} - -% scaling - -\unexpanded\def\dostartscaling#1#2% - {\forcecolorhack - \clf_startscaling rx #1 ry #2\relax} - -\unexpanded\def\dostopscaling - {\clf_stopscaling - \forcecolorhack} - -% mirroring - -\unexpanded\def\dostartmirroring - {\clf_startmirroring} - -\unexpanded\def\dostopmirroring - {\clf_stopmirroring} - -% transform - -\unexpanded\def\dotransformnextbox#1#2#3#4#5#6% - {\dowithnextbox{\dodotransformnextbox{#1}{#2}{#3}{#4}{#5}{#6}}} -\unexpanded\def\dodotransformnextbox#1#2#3#4#5#6% - {\hpack - {\kern #5\onebasepoint - \raise#6\onebasepoint - \hpack - {\clf_startmatrix rx #1 sx #2 sy #3 ry #4\relax - \box\nextbox - \clf_stopmatrix}}} +\ifdefined\outputmode + \outputmode\plusone + \let\outputmode\relax + \newcount\outputmode + \outputmode\plusone + \let\normaloutputmode\outputmode +\fi -%D \macros -%D {back_ovalbox} -%D -%D When we look at the implementation, this is a complicated one. There are seven -%D arguments. -%D -%D \starttyping -%D \back_ovalbox {w} {h} {d} {linewidth} {radius} {stroke} {fill} {variant} -%D \stoptyping -%D -%D This command has to return a \type {\vbox} which can be used to lay over another -%D one (with text). The radius is in degrees, the stroke and fill are~\type {1} -%D (true) of~\type {0} (false). - -\let\back_ovalbox \gobbleeightarguments - -%D \macros -%D {dostartclipping,dostopclipping} -%D -%D Clipping is implemented in such a way that an arbitrary code can be fed. -%D -%D \starttyping -%D \dostartclipping {pathname} {width} {height} -%D \dostopclipping -%D \stoptyping +%D The exact page model depends on the backend so we just define some variables that +%D are used. A helper at the \LUA\ end will synchronize with the internal variables. +%D We store these in the format. -\let \dostartclipping \gobblethreearguments -\let \dostopclipping \donothing - -%D \macros -%D {jobsuffix} -%D -%D By default, \TEX\ produces \DVI\ files which can be converted to other filetypes. -%D Sometimes it is handy to know what the target file will be. In other driver -%D modules we wil set \type {\jobsuffix} to \type {pdf}. - -%D Backend configuration: +\ifdefined\everybackendshipout \else \newtoks\everybackendshipout \fi +\ifdefined\everylastbackendshipout \else \newtoks\everylastbackendshipout \fi +\ifdefined\everybackendlastinshipout \else \newtoks\everybackendlastinshipout \fi \installcorenamespace{backend} \installsetuponlycommandhandler \??backend {backend} -\let\jobsuffix\empty - -\unexpanded\def\back_job_set_suffix#1% % checking could happen in mode - {\ifx\jobsuffix\empty\else\resetsystemmode\jobsuffix\fi - \edef\jobsuffix{#1}% - \ifx\jobsuffix\empty\else\setsystemmode \jobsuffix\fi} - -\back_job_set_suffix{pdf} % default - -% \setupbackend[space=yes] % replace spacing in (pdf) file +%D \starttyping +%D \setupbackend[space=yes] +%D \stoptyping \appendtoks \clf_setrealspaces{\backendparameter\c!space}% diff --git a/tex/context/base/mkiv/back-lpd.lua b/tex/context/base/mkiv/back-lpd.lua deleted file mode 100644 index 9a52130b8..000000000 --- a/tex/context/base/mkiv/back-lpd.lua +++ /dev/null @@ -1,2477 +0,0 @@ -if not modules then modules = { } end modules ['back-lpd'] = { - 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" -} - --- If you consider this complex, watch: --- --- https://www.youtube.com/watch?v=6H-cAzfB2qo --- --- or in distactionmode: --- --- https://www.youtube.com/watch?v=TYuTE_1jvvE --- https://www.youtube.com/watch?v=nnicGKX3lvM --- --- For the moment we have to support the built in backend as well as the alternative. So the --- next interface is suboptimal and will change at some time. - -local type, next, unpack, tonumber = type, next, unpack, tonumber -local char, rep, find = string.char, string.rep, string.find -local formatters, splitstring = string.formatters, string.split -local band, extract = bit32.band, bit32.extract -local concat, keys, sortedhash = table.concat, table.keys, table.sortedhash -local setmetatableindex = table.setmetatableindex - -local bpfactor = number.dimenfactors.bp - -local md5HEX = md5.HEX -local osuuid = os.uuid -local zlibcompress = flate.zip_compress or zlib.compress - -local starttiming = statistics.starttiming -local stoptiming = statistics.stoptiming - -local nuts = nodes.nuts -local tonut = nodes.tonut - -local getdata = nuts.getdata -local getsubtype = nuts.getsubtype -local getfield = nuts.getfield -local getwhd = nuts.getwhd -local flushlist = nuts.flush_list - -local pdfincludeimage = lpdf.includeimage -local pdfgetfontname = lpdf.getfontname -local pdfgetfontobjnumber = lpdf.getfontobjnumber - -local pdfreserveobject = lpdf.reserveobject -local pdfpagereference = lpdf.pagereference -local pdfflushobject = lpdf.flushobject -local pdfreference = lpdf.reference -local pdfdictionary = lpdf.dictionary -local pdfarray = lpdf.array -local pdfconstant = lpdf.constant -local pdfflushstreamobject = lpdf.flushstreamobject -local pdfliteral = lpdf.literal -- not to be confused with a whatsit! - -local pdf_pages = pdfconstant("Pages") -local pdf_page = pdfconstant("Page") -local pdf_xobject = pdfconstant("XObject") -local pdf_form = pdfconstant("Form") - -local fonthashes = fonts.hashes -local characters = fonthashes.characters -local parameters = fonthashes.parameters -local properties = fonthashes.properties - -local report = logs.reporter("backend") - -do - - local dummy = function() end - - local function unavailable(t,k) - report("calling unavailable pdf.%s function",k) - t[k] = dummy - return dummy - end - - updaters.register("backend.update.pdf",function() - setmetatableindex(pdf,unavailable) - end) - - updaters.register("backend.update",function() - pdf = setmetatableindex(unavailable) - end) - -end - --- used variables - -local pdf_h, pdf_v -local need_tm, need_tf, cur_tmrx, cur_factor, cur_f, cur_e -local need_width, need_mode, done_width, done_mode -local mode -local f_pdf_cur, f_pdf, fs_cur, fs, f_cur -local tj_delta, cw -local usedfonts, usedxforms, usedximages -local getxformname, getximagename -local boundingbox, shippingmode, objectnumber -local tmrx, tmry, tmsx, tmsy, tmtx, tmty -local cmrx, cmry, cmsx, cmsy, cmtx, cmty - -local function usefont(t,k) -- a bit redundant hash - local v = pdfgetfontname(k) - t[k] = v - return v -end - -local function reset_variables(specification) - pdf_h, pdf_v = 0, 0 - cmrx, cmry = 1, 1 - cmsx, cmsy = 0, 0 - cmtx, cmty = 0, 0 - tmrx, tmry = 1, 1 - tmsx, tmsy = 0, 0 - tmtx, tmty = 0, 0 - need_tm = false - need_tf = false - need_width = 0 - need_mode = 0 - done_width = false - done_mode = false - mode = "page" - shippingmode = specification.shippingmode - objectnumber = specification.objectnumber - cur_tmrx = 0 - f_cur = 0 - f_pdf_cur = 0 -- nullfont - f_pdf = 0 -- nullfont - fs_cur = 0 - fs = 0 - tj_delta = 0 - cur_factor = 0 - cur_f = false - cur_e = false - cw = 0 - usedfonts = setmetatableindex(usefont) - usedxforms = { } - usedximages = { } - boundingbox = specification.boundingbox - -- --- if overload then --- overload() --- end -end - --- buffer - -local buffer = { } -local b = 0 - -local function reset_buffer() - -- buffer = { } - b = 0 -end - -local function flush_buffer() - b = 0 -end - -local function show_buffer() - print(concat(buffer,"\n",1,b)) -end - --- fonts - -local fontcharacters -local fontparameters -local fontproperties -local usedcharacters = setmetatableindex("table") -local pdfcharacters -local horizontalmode = true ------ widefontmode = true -local scalefactor = 1 ------ threshold = 655360 / (10 * 5) -- default is 5 -local threshold = 655360 * 50 / 100 -local tjfactor = 100 / 65536 - -lpdf.usedcharacters = usedcharacters - -local function updatefontstate(font) - fontcharacters = characters[font] - fontparameters = parameters[font] - fontproperties = properties[font] - local size = fontparameters.size -- or bad news - local designsize = fontparameters.designsize or size - pdfcharacters = usedcharacters[font] - horizontalmode = fontparameters.writingmode ~= "vertical" - -- widefontmode = fontproperties.encodingbytes == 2 - scalefactor = (designsize/size) * tjfactor - -- threshold = designsize / (10 * (fontproperties.threshold or 5)) - threshold = designsize * (fontproperties.threshold or 50) / 100 -end - --- helpers - -local f_cm = formatters["%.6F %.6F %.6F %.6F %.6F %.6F cm"] -local f_tm = formatters["%.6F %.6F %.6F %.6F %.6F %.6F Tm"] - -directives.register("pdf.stripzeros",function() - f_cm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"] - f_tm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N Tm"] -end) - -local saved_text_pos_v = 0 -local saved_text_pos_h = 0 - -local function begin_text() - saved_text_pos_h = pdf_h - saved_text_pos_v = pdf_v - b = b + 1 ; buffer[b] = "BT" - need_tf = true - need_width = 0 - need_mode = 0 - mode = "text" -end - -local function end_text() - if done_width then - b = b + 1 ; buffer[b] = "0 w" - done_width = false - end - if done_mode then - b = b + 1 ; buffer[b] = "0 Tr" - done_mode = false - end - b = b + 1 ; buffer[b] = "ET" - pdf_h = saved_text_pos_h - pdf_v = saved_text_pos_v - mode = "page" -end - -local saved_chararray_pos_h -local saved_chararray_pos_v - -local saved_b = 0 - -local function begin_chararray() - saved_chararray_pos_h = pdf_h - saved_chararray_pos_v = pdf_v - cw = horizontalmode and saved_chararray_pos_h or - saved_chararray_pos_v - tj_delta = 0 - saved_b = b - b = b + 1 ; buffer[b] = " [" - mode = "chararray" -end - -local function end_chararray() - b = b + 1 ; buffer[b] = "] TJ" - buffer[saved_b] = concat(buffer,"",saved_b,b) - b = saved_b - pdf_h = saved_chararray_pos_h - pdf_v = saved_chararray_pos_v - mode = "text" -end - -local function begin_charmode() - -- b = b + 1 ; buffer[b] = widefontmode and "<" or "(" - b = b + 1 ; buffer[b] = "<" - mode = "char" -end - -local function end_charmode() - -- b = b + 1 ; buffer[b] = widefontmode and ">" or ")" - b = b + 1 ; buffer[b] = ">" - mode = "chararray" -end - -local function calc_pdfpos(h,v) - if mode == "page" then - cmtx = h - pdf_h - cmty = v - pdf_v - if h ~= pdf_h or v ~= pdf_v then - return true - end - elseif mode == "text" then - tmtx = h - saved_text_pos_h - tmty = v - saved_text_pos_v - if h ~= pdf_h or v ~= pdf_v then - return true - end - elseif horizontalmode then - tmty = v - saved_text_pos_v - tj_delta = cw - h -- + saved_chararray_pos_h - if tj_delta ~= 0 or v ~= pdf_v then - return true - end - else - tmtx = h - saved_text_pos_h - tj_delta = cw + v -- - saved_chararray_pos_v - if tj_delta ~= 0 or h ~= pdf_h then - return true - end - end - return false -end - -local function pdf_set_pos(h,v) - local move = calc_pdfpos(h,v) - if move then - b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor) - pdf_h = pdf_h + cmtx - pdf_v = pdf_v + cmty - end -end - -local function pdf_reset_pos() -- pdf_set_pos(0,0) - if mode == "page" then - cmtx = - pdf_h - cmty = - pdf_v - if pdf_h == 0 and pdf_v == 0 then - return - end - elseif mode == "text" then - tmtx = - saved_text_pos_h - tmty = - saved_text_pos_v - if pdf_h == 0 and pdf_v == 0 then - return - end - elseif horizontalmode then - tmty = - saved_text_pos_v - tj_delta = cw - if tj_delta == 0 and pdf_v == 0 then - return - end - else - tmtx = - saved_text_pos_h - tj_delta = cw - if tj_delta == 0 and pdf_h == 0 then - return - end - end - b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor) - pdf_h = pdf_h + cmtx - pdf_v = pdf_v + cmty -end - -local function pdf_set_pos_temp(h,v) - local move = calc_pdfpos(h,v) - if move then - b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor) - end -end - -local function pdf_end_string_nl() - if mode == "char" then - end_charmode() - end_chararray() - elseif mode == "chararray" then - end_chararray() - end -end - -local function pdf_goto_textmode() - if mode == "page" then - -- pdf_set_pos(0,0) - pdf_reset_pos() - begin_text() - elseif mode ~= "text" then - if mode == "char" then - end_charmode() - end_chararray() - else -- if mode == "chararray" then - end_chararray() - end - end -end - -local function pdf_goto_pagemode() - if mode ~= "page" then - if mode == "char" then - end_charmode() - end_chararray() - end_text() - elseif mode == "chararray" then - end_chararray() - end_text() - elseif mode == "text" then - end_text() - end - end -end - -local function pdf_goto_fontmode() - if mode == "char" then - end_charmode() - end_chararray() - end_text() - elseif mode == "chararray" then - end_chararray() - end_text() - elseif mode == "text" then - end_text() - end - -- pdf_set_pos(0,0) - pdf_reset_pos() - mode = "page" -end - --- characters - -local flushcharacter do - - local round = math.round - - local function setup_fontparameters(font,factor,f,e) - local slant = (fontparameters.slantfactor or 0) / 1000 - local extend = (fontparameters.extendfactor or 1000) / 1000 - local squeeze = (fontparameters.squeezefactor or 1000) / 1000 - local expand = 1 + factor / 1000000 - local format = fontproperties.format - if e then - extend = extend * e - end - tmrx = expand * extend - tmsy = slant - tmry = squeeze - need_width = fontparameters.width - need_mode = fontparameters.mode - f_cur = font - f_pdf = usedfonts[font] -- cache - cur_factor = factor - cur_f = f - cur_e = e - tj_delta = 0 - fs = fontparameters.size * bpfactor - if f then - fs = fs * f - end - -- kind of special: - if format == "opentype" or format == "type1" then - fs = fs * 1000 / fontparameters.units -- can we avoid this ? - end - end - - local f_width = formatters["%.6F w"] - local f_mode = formatters["%i Tr"] - local f_font = formatters["/F%i %.6F Tf"] - local s_width = "0 w" - local s_mode = "0 Tr" - - directives.register("pdf.stripzeros",function() - f_width = formatters["%.6N w"] - f_font = formatters["/F%i %.6N Tf"] - end) - - local function set_font() - if need_width and need_width ~= 0 then - b = b + 1 ; buffer[b] = f_width(bpfactor * need_width / 1000) - done_width = true - elseif done_width then - b = b + 1 ; buffer[b] = s_width - done_width = false - end - if need_mode and need_mode ~= 0 then - b = b + 1 ; buffer[b] = f_mode(need_mode) - done_mode = true - elseif done_mode then - b = b + 1 ; buffer[b] = s_mode - done_mode = false - end - b = b + 1 ; buffer[b] = f_font(f_pdf,fs) - f_pdf_cur = f_pdf - fs_cur = fs - need_tf = false - need_tm = true - end - - local function set_textmatrix(h,v) - local move = calc_pdfpos(h,v) -- duplicate - if need_tm or move then - b = b + 1 ; buffer[b] = f_tm(tmrx, tmsx, tmsy, tmry, tmtx*bpfactor, tmty*bpfactor) - pdf_h = saved_text_pos_h + tmtx - pdf_v = saved_text_pos_v + tmty - need_tm = false - end - cur_tmrx = tmrx - end - - local f_skip = formatters["%.1f"] - -- local f_octal = formatters["\\%o"] - -- local f_char = formatters["%c"] - local f_hex = formatters["%04X"] - - local h_hex = setmetatableindex(function(t,k) - local v = f_hex(k) - t[k] = v - return v - end) - - flushcharacter = function(current,pos_h,pos_v,pos_r,font,char,data,factor,width,f,e) - if need_tf or font ~= f_cur or f_pdf ~= f_pdf_cur or fs ~= fs_cur or mode == "page" then - pdf_goto_textmode() - setup_fontparameters(font,factor,f,e) - set_font() - elseif cur_tmrx ~= tmrx or cur_factor ~= factor or cur_f ~= f or cur_e ~= e then - setup_fontparameters(font,factor,f,e) - need_tm = true - end - local move = calc_pdfpos(pos_h,pos_v) - if move or need_tm then - if not need_tm then - if horizontalmode then --- print(tj_delta,threshold) - if (saved_text_pos_v + tmty) ~= pdf_v then - need_tm = true - elseif tj_delta >= threshold or tj_delta <= -threshold then - need_tm = true - end - else - if (saved_text_pos_h + tmtx) ~= pdf_h then - need_tm = true - elseif tj_delta >= threshold or tj_delta <= -threshold then - need_tm = true - end - end - end - if need_tm then - pdf_goto_textmode() - set_textmatrix(pos_h,pos_v) - begin_chararray() - move = calc_pdfpos(pos_h,pos_v) - end - if move then - -- local d = round(tj_delta * scalefactor) - -- if d ~= 0 then - local d = tj_delta * scalefactor - if d <= -0.5 or d >= 0.5 then - if mode == "char" then - end_charmode() - end - -- b = b + 1 ; buffer[b] = f_skip(d) - -- b = b + 1 ; buffer[b] = d - b = b + 1 ; buffer[b] = round(d) - end --- if d <= -0.25 or d >= 0.25 then --- if mode == "char" then --- end_charmode() --- end --- b = b + 1 ; buffer[b] = f_skip(d) --- end - cw = cw - tj_delta - end - end - if mode == "chararray" then - begin_charmode(font) - end - - local index = data.index or char - - b = b + 1 - -- if widefontmode then - -- buffer[b] = h_hex[data.index or 0] - buffer[b] = h_hex[index] - -- elseif char > 255 then - -- buffer[b] = f_octal(32) - -- elseif char <= 32 or char == 92 or char == 40 or char == 41 or char > 127 then -- \ ( ) - -- buffer[b] = f_octal(char) - -- else - -- buffer[b] = f_char(char) - -- end - - cw = cw + width - - -- pdfcharacters[fontcharacters[char].index or char] = true - pdfcharacters[index] = true - - end - -end - --- literals - -local flushpdfliteral do - - local literalvalues = nodes.literalvalues - - local originliteral_code = literalvalues.origin - local pageliteral_code = literalvalues.page - local alwaysliteral_code = literalvalues.always - local rawliteral_code = literalvalues.raw - local textliteral_code = literalvalues.text - local fontliteral_code = literalvalues.font - - local nodeproperties = nodes.properties.data - - flushpdfliteral = function(current,pos_h,pos_v,mode,str) - if mode then - if not str then - mode, str = originliteral_code, mode - elseif mode == "mode" then - mode = literalvalues[str] - if mode == originliteral_code then - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - elseif mode == pageliteral_code then - pdf_goto_pagemode() - elseif mode == textliteral_code then - pdf_goto_textmode() - elseif mode == fontliteral_code then - pdf_goto_fontmode() - elseif mode == alwaysliteral_code then - pdf_end_string_nl() - need_tm = true - elseif mode == rawliteral_code then - pdf_end_string_nl() - end - return - else - mode = literalvalues[mode] - end - else - -- str, mode = getdata(current) - local p = nodeproperties[current] -if p then - str = p.data - mode = p.mode -else - str, mode = getdata(current) -end - end - if str and str ~= "" then - if mode == originliteral_code then - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - elseif mode == pageliteral_code then - pdf_goto_pagemode() - elseif mode == textliteral_code then - pdf_goto_textmode() - elseif mode == fontliteral_code then - pdf_goto_fontmode() - elseif mode == alwaysliteral_code then - pdf_end_string_nl() - need_tm = true - elseif mode == rawliteral_code then - pdf_end_string_nl() - else - print("check literal") - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - end - b = b + 1 ; buffer[b] = str - end - end - - updaters.register("backend.update.pdf",function() - function pdf.print(mode,str) - if str then - mode = literalvalues[mode] - else - mode, str = originliteral_code, mode - end - if str and str ~= "" then - if mode == originliteral_code then - pdf_goto_pagemode() - -- pdf_set_pos(pdf_h,pdf_v) - elseif mode == pageliteral_code then - pdf_goto_pagemode() - elseif mode == textliteral_code then - pdf_goto_textmode() - elseif mode == fontliteral_code then - pdf_goto_fontmode() - elseif mode == alwaysliteral_code then - pdf_end_string_nl() - need_tm = true - elseif mode == rawliteral_code then - pdf_end_string_nl() - else - pdf_goto_pagemode() - -- pdf_set_pos(pdf_h,pdf_v) - end - b = b + 1 ; buffer[b] = str - end - end - end) - -end - --- grouping & orientation - -local flushpdfsave, flushpdfrestore, flushpdfsetmatrix do - - local matrices = { } - local positions = { } - local nofpositions = 0 - local nofmatrices = 0 - - local f_matrix = formatters["%s 0 0 cm"] - - flushpdfsave = function(current,pos_h,pos_v) - nofpositions = nofpositions + 1 - positions[nofpositions] = { pos_h, pos_v, nofmatrices } - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - b = b + 1 ; buffer[b] = "q" - end - - flushpdfrestore = function(current,pos_h,pos_v) - if nofpositions < 1 then - return - end - local t = positions[nofpositions] - -- local h = pos_h - t[1] - -- local v = pos_v - t[2] - if shippingmode == "page" then - nofmatrices = t[3] - end - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - b = b + 1 ; buffer[b] = "Q" - nofpositions = nofpositions - 1 - end - - local function pdf_set_matrix(str,pos_h,pos_v) - if shippingmode == "page" then - local rx, sx, sy, ry = splitstring(str," ") - if rx and ry and sx and ry then - rx, sx, sy, ry = tonumber(rx), tonumber(ry), tonumber(sx), tonumber(sy) - local tx = pos_h * (1 - rx) - pos_v * sy - local ty = pos_v * (1 - ry) - pos_h * sx - if nofmatrices > 1 then - local t = matrices[nofmatrices] - local r_x, s_x, s_y, r_y, te, tf = t[1], t[2], t[3], t[4], t[5], t[6] - rx, sx = rx * r_x + sx * s_y, rx * s_x + sx * r_y - sy, ry = sy * r_x + ry * s_y, sy * s_x + ry * r_y - tx, ty = tx * r_x + ty * s_y + t_x, tx * s_x + ty * r_y + t_y - end - nofmatrices = nofmatrices + 1 - matrices[nofmatrices] = { rx, sx, sy, ry, tx, ty } - end - end - end - - local nodeproperties = nodes.properties.data - - flushpdfsetmatrix = function(current,pos_h,pos_v) - local str - if type(current) == "string" then - str = current - else - local p = nodeproperties[current] - if p then - str = p.matrix - else - str = getdata(current) -- for the moment - end - end - if str and str ~= "" then - pdf_set_matrix(str,pos_h,pos_v) - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - b = b + 1 ; buffer[b] = f_matrix(str) - end - end - - do - - local function hasmatrix() - return nofmatrices > 0 - end - - local function getmatrix() - if nofmatrices > 0 then - return unpack(matrices[nofmatrices]) - else - return 0, 0, 0, 0, 0, 0 -- or 1 0 0 1 0 0 - end - end - - updaters.register("backend.update.pdf",function() - pdf.hasmatrix = hasmatrix - pdf.getmatrix = getmatrix - end) - - end - - pushorientation = function(orientation,pos_h,pos_v,pos_r) - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - b = b + 1 ; buffer[b] = "q" - if orientation == 1 then - b = b + 1 ; buffer[b] = "0 -1 1 0 0 0 cm" -- 90 - elseif orientation == 2 then - b = b + 1 ; buffer[b] = "-1 0 0 -1 0 0 cm" -- 180 - elseif orientation == 3 then - b = b + 1 ; buffer[b] = "0 1 -1 0 0 0 cm" -- 270 - end - end - - poporientation = function(orientation,pos_h,pos_v,pos_r) - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - b = b + 1 ; buffer[b] = "Q" - end - --- pushorientation = function(orientation,pos_h,pos_v,pos_r) --- flushpdfsave(false,pos_h,pos_v) --- if orientation == 1 then --- flushpdfsetmatrix("0 -1 1 0",pos_h,pos_v) --- elseif orientation == 2 then --- flushpdfsetmatrix("-1 0 0 -1",pos_h,pos_v) --- elseif orientation == 3 then --- flushpdfsetmatrix("0 1 -1 0",pos_h,pos_v) --- end --- end - --- poporientation = function(orientation,pos_h,pos_v,pos_r) --- flushpdfrestore(false,pos_h,pos_v) --- end - -end - --- rules - -local flushedxforms = { } -- actually box resources but can also be direct - -local flushrule, flushsimplerule do - - local rulecodes = nodes.rulecodes - local newrule = nodes.pool.rule - - local setprop = nuts.setprop - local getprop = nuts.getprop - - local normalrule_code = rulecodes.normal - local boxrule_code = rulecodes.box - local imagerule_code = rulecodes.image - local emptyrule_code = rulecodes.empty - local userrule_code = rulecodes.user - local overrule_code = rulecodes.over - local underrule_code = rulecodes.under - local fractionrule_code = rulecodes.fraction - local radicalrule_code = rulecodes.radical - local outlinerule_code = rulecodes.outline - - local rule_callback = callbacks.functions.process_rule - - local f_fm = formatters["/Fm%d Do"] - local f_im = formatters["/Im%d Do"] - - local s_b = "q" - local s_e = "Q" - - local f_v = formatters["[] 0 d 0 J %.6F w 0 0 m %.6F 0 l S"] - local f_h = formatters["[] 0 d 0 J %.6F w 0 0 m 0 %.6F l S"] - - local f_f = formatters["0 0 %.6F %.6F re f"] - local f_o = formatters["[] 0 d 0 J 0 0 %.6F %.6F re S"] - local f_w = formatters["[] 0 d 0 J %.6F w 0 0 %.6F %.6F re S"] - - directives.register("pdf.stripzeros",function() - f_v = formatters["[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S"] - f_h = formatters["[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S"] - f_f = formatters["0 0 %.6N %.6N re f"] - f_o = formatters["[] 0 d 0 J 0 0 %.6N %.6N re S"] - f_w = formatters["[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S"] - end) - - -- Historically the index is an object which is kind of bad. - - local boxresources, n = { }, 0 - - getxformname = function(index) - local l = boxresources[index] - if l then - return l.name - else - print("no box resource",index) - end - end - - updaters.register("backend.update.pdf",function() - pdf.getxformname = getxformname - end) - - local function saveboxresource(box,attributes,resources,immediate,kind,margin) - n = n + 1 - local immediate = true - local margin = margin or 0 -- or dimension - local objnum = pdfreserveobject() - local list = tonut(type(box) == "number" and tex.takebox(box) or box) - -- - local width, height, depth = getwhd(list) - -- - local l = { - width = width, - height = height, - depth = depth, - margin = margin, - attributes = attributes, - resources = resources, - list = nil, - type = kind, - name = n, - index = objnum, - objnum = objnum, - } - boxresources[objnum] = l - if immediate then - lpdf.convert(list,"xform",objnum,l) - flushedxforms[objnum] = { true , objnum } - flushlist(list) - else - l.list = list - end - return objnum - end - - local function useboxresource(index,wd,ht,dp) - local l = boxresources[index] - if l then - if wd or ht or dp then - wd, ht, dp = wd or 0, ht or 0, dp or 0 - else - wd, ht, dp = l.width, l.height, l.depth - end - local rule = newrule(wd,ht,dp) -- newboxrule - rule.subtype = boxrule_code - setprop(tonut(rule),"index",index) - return rule, wd, ht, dp - else - print("no box resource",index) - end - end - - local function getboxresourcedimensions(index) - local l = boxresources[index] - if l then - return l.width, l.height, l.depth, l.margin - else - print("no box resource",index) - end - end - - local function getboxresourcebox(index) - local l = boxresources[index] - if l then - return l.list - end - end - - updaters.register("backend.update.tex",function() - tex.saveboxresource = saveboxresource - tex.useboxresource = useboxresource - tex.getboxresourcedimensions = getboxresourcedimensions - tex.getboxresourcebox = getboxresourcebox - end) - - -- a bit of a mess: index is now objnum but that has to change to a proper index - -- ... an engine inheritance - - local function flushpdfxform(current,pos_h,pos_v,pos_r,size_h,size_v) - -- object properties - local objnum = getprop(current,"index") - local name = getxformname(objnum) - local info = flushedxforms[objnum] - local r = boxresources[objnum] - if not info then - info = { false , objnum } - flushedxforms[objnum] = info - end - local wd, ht, dp = getboxresourcedimensions(objnum) - -- or: wd, ht, dp = r.width, r.height, r.depth - -- sanity check - local htdp = ht + dp - if wd == 0 or size_h == 0 or htdp == 0 or size_v == 0 then - return - end - -- calculate scale - local rx, ry = 1, 1 - if wd ~= size_h or htdp ~= size_v then - rx = size_h / wd - ry = size_v / htdp - end - -- flush the reference - usedxforms[objnum] = true - pdf_goto_pagemode() - calc_pdfpos(pos_h,pos_v) - tx = cmtx * bpfactor - ty = cmty * bpfactor - b = b + 1 ; buffer[b] = s_b - b = b + 1 ; buffer[b] = f_cm(rx,0,0,ry,tx,ty) - b = b + 1 ; buffer[b] = f_fm(name) - b = b + 1 ; buffer[b] = s_e - end - - -- place image also used in vf but we can use a different one if - -- we need it - - local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream memstream - local img_none = imagetypes.none - local img_pdf = imagetypes.pdf - local img_stream = imagetypes.stream - local img_memstream = imagetypes.memstream - - local one_bp = 65536 * bpfactor - - local imageresources, n = { }, 0 - - getximagename = function(index) - local l = imageresources[index] - if l then - return l.name - else - print("no image resource",index) - end - end - - updaters.register("backend.update.pdf",function() - pdf.getximagename = getximagename - end) - - local function flushpdfximage(current,pos_h,pos_v,pos_r,size_h,size_v) - - local width, - height, - depth = getwhd(current) - local total = height + depth - local transform = getprop(current,"transform") or 0 -- we never set it ... so just use rotation then - local index = getprop(current,"index") or 0 - local kind, - xorigin, - yorigin, - xsize, - ysize, - rotation, - objnum, - groupref = pdfincludeimage(index) -- needs to be sorted out, bad name (no longer mixed anyway) - - if not kind then - print("invalid image",index) - return - end - - local rx, sx, sy, ry, tx, ty = 1, 0, 0, 1, 0, 0 - - if kind == img_pdf or kind == img_stream or kind == img_memstream then - rx, ry, tx, ty = 1/xsize, 1/ysize, xorigin/xsize, yorigin/ysize - else - -- if kind == img_png then - -- -- if groupref > 0 and img_page_group_val == 0 then - -- -- img_page_group_val = groupref - -- -- end - -- end - rx, ry = bpfactor, bpfactor - end - - if band(transform,7) > 3 then - -- mirror - rx, tx = -rx, -tx - end - local t = band(transform + rotation,3) - if t == 0 then - -- nothing - elseif t == 1 then - -- rotation over 90 degrees (counterclockwise) - rx, sx, sy, ry, tx, ty = 0, rx, -ry, 0, -ty, tx - elseif t == 2 then - -- rotation over 180 degrees (counterclockwise) - rx, ry, tx, ty = -rx, -ry, -tx, -ty - elseif t == 3 then - -- rotation over 270 degrees (counterclockwise) - rx, sx, sy, ry, tx, ty = 0, -rx, ry, 0, ty, -tx - end - - rx = rx * width - sx = sx * total - sy = sy * width - ry = ry * total - tx = pos_h - tx * width - ty = pos_v - ty * total - - local t = transform + rotation - - if band(transform,7) > 3 then - t = t + 1 - end - - t = band(t,3) - - if t == 0 then - -- no transform - elseif t == 1 then - -- rotation over 90 degrees (counterclockwise) - tx = tx + width - elseif t == 2 then - -- rotation over 180 degrees (counterclockwise) - tx = tx + width - ty = ty + total - elseif t == 3 then - -- rotation over 270 degrees (counterclockwise) - ty = ty + total - end - - -- a flaw in original, can go: - -- - -- if img_page_group_val == 0 then - -- img_page_group_val = group_ref - -- end - - usedximages[index] = objnum -- hm - - pdf_goto_pagemode() - - calc_pdfpos(tx,ty) - - tx = cmtx * bpfactor - ty = cmty * bpfactor - - b = b + 1 ; buffer[b] = s_b - b = b + 1 ; buffer[b] = f_cm(rx,sx,sy,ry,tx,ty) - b = b + 1 ; buffer[b] = f_im(index) - b = b + 1 ; buffer[b] = s_e - end - - flushpdfimage = function(index,width,height,depth,pos_h,pos_v) - - -- used in vf characters - - local total = height + depth - local kind, - xorigin, yorigin, - xsize, ysize, - rotation, - objnum, - groupref = pdfincludeimage(index) - - local rx = width / xsize - local sx = 0 - local sy = 0 - local ry = total / ysize - local tx = pos_h - local ty = pos_v - 2 * depth -- to be sorted out - - usedximages[index] = objnum - - pdf_goto_pagemode() - - calc_pdfpos(tx,ty) - - tx = cmtx * bpfactor - ty = cmty * bpfactor - - b = b + 1 ; buffer[b] = s_b - b = b + 1 ; buffer[b] = f_cm(rx,sx,sy,ry,tx,ty) - b = b + 1 ; buffer[b] = f_im(index) - b = b + 1 ; buffer[b] = s_e - end - - -- For the moment we need this hack because the engine checks the 'image' - -- command in virtual fonts (so we use lua instead). - - pdf.flushpdfimage = flushpdfimage - - flushrule = function(current,pos_h,pos_v,pos_r,size_h,size_v) - - local subtype = getsubtype(current) - if subtype == emptyrule_code then - return - elseif subtype == boxrule_code then - return flushpdfxform(current,pos_h,pos_v,pos_r,size_h,size_v) - elseif subtype == imagerule_code then - return flushpdfximage(current,pos_h,pos_v,pos_r,size_h,size_v) - end - if subtype == userrule_code or subtype >= overrule_code and subtype <= radicalrule_code then - pdf_goto_pagemode() - b = b + 1 ; buffer[b] = s_b - pdf_set_pos_temp(pos_h,pos_v) - rule_callback(current,size_h,size_v,pos_r) -- so we pass direction - b = b + 1 ; buffer[b] = s_e - return - end - - pdf_goto_pagemode() - - -- local saved_b = b - - b = b + 1 ; buffer[b] = s_b - - local dim_h = size_h * bpfactor - local dim_v = size_v * bpfactor - local rule - - if dim_v <= one_bp then - pdf_set_pos_temp(pos_h,pos_v + 0.5 * size_v) - rule = f_v(dim_v,dim_h) - elseif dim_h <= one_bp then - pdf_set_pos_temp(pos_h + 0.5 * size_h,pos_v) - rule = f_h(dim_h,dim_v) - else - pdf_set_pos_temp(pos_h,pos_v) - if subtype == outlinerule_code then - local linewidth = getdata(current) - if linewidth > 0 then - rule = f_w(linewidth * bpfactor,dim_h,dim_v) - else - rule = f_o(dim_h,dim_v) - end - else - rule = f_f(dim_h,dim_v) - end - end - - b = b + 1 ; buffer[b] = rule - b = b + 1 ; buffer[b] = s_e - - -- buffer[saved_b] = concat(buffer," ",saved_b,b) - -- b = saved_b - - end - - flushsimplerule = function(pos_h,pos_v,pos_r,size_h,size_v) - pdf_goto_pagemode() - - b = b + 1 ; buffer[b] = s_b - - local dim_h = size_h * bpfactor - local dim_v = size_v * bpfactor - local rule - - if dim_v <= one_bp then - pdf_set_pos_temp(pos_h,pos_v + 0.5 * size_v) - rule = f_v(dim_v,dim_h) - elseif dim_h <= one_bp then - pdf_set_pos_temp(pos_h + 0.5 * size_h,pos_v) - rule = f_h(dim_h,dim_v) - else - pdf_set_pos_temp(pos_h,pos_v) - rule = f_f(dim_h,dim_v) - end - - b = b + 1 ; buffer[b] = rule - b = b + 1 ; buffer[b] = s_e - end - -end - ---- basics - -local wrapup, registerpage do - - local pages = { } - local maxkids = 10 - local nofpages = 0 - - registerpage = function(object) - nofpages = nofpages + 1 - local objnum = pdfpagereference(nofpages) - pages[nofpages] = { - objnum = objnum, - object = object, - } - end - - wrapup = function() - - -- local includechar = lpdf.includecharlist - -- - -- for font, list in next, usedcharacters do - -- includechar(font,keys(list)) - -- end - - -- hook (to reshuffle pages) - local pagetree = { } - local parent = nil - local minimum = 0 - local maximum = 0 - local current = 0 - if #pages > 1.5 * maxkids then - repeat - local plist, pnode - if current == 0 then - plist, minimum = pages, 1 - elseif current == 1 then - plist, minimum = pagetree, 1 - else - plist, minimum = pagetree, maximum + 1 - end - maximum = #plist - if maximum > minimum then - local kids - for i=minimum,maximum do - local p = plist[i] - if not pnode or #kids == maxkids then - kids = pdfarray() - parent = pdfreserveobject() - pnode = pdfdictionary { - objnum = parent, - Type = pdf_pages, - Kids = kids, - Count = 0, - } - pagetree[#pagetree+1] = pnode - end - kids[#kids+1] = pdfreference(p.objnum) - pnode.Count = pnode.Count + (p.Count or 1) - p.Parent = pdfreference(parent) - end - end - current = current + 1 - until maximum == minimum - -- flush page tree - for i=1,#pagetree do - local entry = pagetree[i] - local objnum = entry.objnum - entry.objnum = nil - pdfflushobject(objnum,entry) - end - else - -- ugly - local kids = pdfarray() - local list = pdfdictionary { - Type = pdf_pages, - Kids = kids, - Count = nofpages, - } - parent = pdfreserveobject() - for i=1,nofpages do - local page = pages[i] - kids[#kids+1] = pdfreference(page.objnum) - page.Parent = pdfreference(parent) - end - pdfflushobject(parent,list) - end - for i=1,nofpages do - local page = pages[i] - local object = page.object - object.Parent = page.Parent - pdfflushobject(page.objnum,object) - end - lpdf.addtocatalog("Pages",pdfreference(parent)) - - end - -end - -pdf_h, pdf_v = 0, 0 - -local function initialize(specification) - reset_variables(specification) - reset_buffer() -end - -local f_font = formatters["F%d"] -local f_form = formatters["Fm%d"] -local f_image = formatters["Im%d"] - --- This will all move and be merged and become less messy. - --- todo: more clever resource management: a bit tricky as we can inject --- stuff in the page stream - -local pushmode, popmode - -local function finalize(objnum,specification) - - pushmode() - - pdf_goto_pagemode() -- for now - - local content = concat(buffer,"\n",1,b) - - local fonts = nil - local xforms = nil - - if next(usedfonts) then - fonts = pdfdictionary { } - for k, v in next, usedfonts do - fonts[f_font(v)] = pdfreference(pdfgetfontobjnumber(k)) -- we can overload for testing - end - end - - -- messy: use real indexes for both ... so we need to change some in the - -- full luatex part - - if next(usedxforms) or next(usedximages) then - xforms = pdfdictionary { } - for k in sortedhash(usedxforms) do - -- xforms[f_form(k)] = pdfreference(k) - xforms[f_form(getxformname(k))] = pdfreference(k) - end - for k, v in sortedhash(usedximages) do - xforms[f_image(k)] = pdfreference(v) - end - end - - reset_buffer() - - -- finish_pdfpage_callback(shippingmode == "page") - - if shippingmode == "page" then - - local pageproperties = lpdf.getpageproperties() - - local pageresources = pageproperties.pageresources - local pageattributes = pageproperties.pageattributes - local pagesattributes = pageproperties.pagesattributes - - pageresources.Font = fonts - pageresources.XObject = xforms - pageresources.ProcSet = lpdf.procset() - - local contentsobj = pdfflushstreamobject(content,false,false) - - pageattributes.Type = pdf_page - pageattributes.Contents = pdfreference(contentsobj) - pageattributes.Resources = pageresources - -- pageattributes.Resources = pdfreference(pdfflushobject(pageresources)) - pageattributes.MediaBox = pdfarray { - boundingbox[1] * bpfactor, - boundingbox[2] * bpfactor, - boundingbox[3] * bpfactor, - boundingbox[4] * bpfactor, - } - pageattributes.Parent = nil -- precalculate - pageattributes.Group = nil -- todo - - -- resources can be indirect - - registerpage(pageattributes) - - lpdf.finalizepage(true) - - else - - local xformtype = specification.type or 0 - local margin = specification.margin or 0 - local attributes = specification.attributes or "" - local resources = specification.resources or "" - local wrapper = nil - if xformtype == 0 then - wrapper = pdfdictionary { - Type = pdf_xobject, - Subtype = pdf_form, - FormType = 1, - BBox = nil, - Matrix = nil, - Resources = nil, - } - else - wrapper = pdfdictionary { - BBox = nil, - Matrix = nil, - Resources = nil, - } - end - if xformtype == 0 or xformtype == 1 or xformtype == 3 then - wrapper.BBox = pdfarray { - -margin * bpfactor, - -margin * bpfactor, - (boundingbox[3] + margin) * bpfactor, - (boundingbox[4] + margin) * bpfactor, - } - end - if xformtype == 0 or xformtype == 2 or xformtype == 3 then - wrapper.Matrix = pdfarray { 1, 0, 0, 1, 0, 0 } - end - - -- todo: additional = resources - - local boxresources = lpdf.collectedresources { serialize = false } - boxresources.Font = fonts - boxresources.XObject = xforms - - -- todo: maybe share them - -- wrapper.Resources = pdfreference(pdfflushobject(boxresources)) - - if resources ~= "" then - boxresources = boxresources + resources - end - if attributes ~= "" then - wrapper = wrapper + attributes - end - - wrapper.Resources = next(boxresources) and boxresources or nil - wrapper.ProcSet = lpdf.procset() - - -- pdfflushstreamobject(content,wrapper,false,objectnumber) - pdfflushstreamobject(content,wrapper,false,specification.objnum) - - end - - for objnum in sortedhash(usedxforms) do - local f = flushedxforms[objnum] - if f[1] == false then - f[1] = true - local objnum = f[2] -- specification.objnum - local specification = boxresources[objnum] - local list = specification.list - lpdf.convert(list,"xform",f[2],specification) - end - end - - pdf_h, pdf_v = 0, 0 - - popmode() - -end - -updaters.register("backend.update.pdf",function() - job.positions.registerhandlers { - getpos = drivers.getpos, - gethpos = drivers.gethpos, - getvpos = drivers.getvpos, - } -end) - -updaters.register("backend.update",function() - local saveboxresource = tex.boxresources.save - -- - -- also in lpdf-res .. brrr .. needs fixing - -- - backends.codeinjections.registerboxresource = function(n,offset) - local r = saveboxresource(n,nil,nil,false,0,offset or 0) - return r - end -end) - --- now comes the pdf file handling - -local objects = { } -local streams = { } -- maybe just parallel to objects (no holes) -local nofobjects = 0 -local offset = 0 -local f = false -local flush = false -local threshold = 40 -- also #("/Filter /FlateDecode") -local objectstream = true -local compress = true -local cache = false -local info = "" -local catalog = "" -local level = 0 -local lastdeferred = false -local enginepdf = pdf -local majorversion = 1 -local minorversion = 7 -local trailerid = true - -local f_object = formatters["%i 0 obj\010%s\010endobj\010"] -local f_stream_n_u = formatters["%i 0 obj\010<< /Length %i >>\010stream\010%s\010endstream\010endobj\010"] -local f_stream_n_c = formatters["%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010"] -local f_stream_d_u = formatters["%i 0 obj\010<< %s /Length %i >>\010stream\010%s\010endstream\010endobj\010"] -local f_stream_d_c = formatters["%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010"] -local f_stream_d_r = formatters["%i 0 obj\010<< %s >>\010stream\010%s\010endstream\010endobj\010"] - -local f_object_b = formatters["%i 0 obj\010"] -local f_stream_b_n_u = formatters["%i 0 obj\010<< /Length %i >>\010stream\010"] -local f_stream_b_n_c = formatters["%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010"] -local f_stream_b_d_u = formatters["%i 0 obj\010<< %s /Length %i >>\010stream\010"] -local f_stream_b_d_c = formatters["%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010"] -local f_stream_b_d_r = formatters["%i 0 obj\010<< %s >>\010stream\010"] - -local s_object_e = "\010endobj\010" -local s_stream_e = "\010endstream\010endobj\010" - -do - - local function setinfo() end -- we get it - local function setcatalog() end -- we get it - - local function settrailerid(v) - trailerid = v or false - end - - local function setmajorversion(v) majorversion = tonumber(v) or majorversion end - local function setminorversion(v) minorversion = tonumber(v) or minorversion end - - local function getmajorversion(v) return majorversion end - local function getminorversion(v) return minorversion end - - local function setcompresslevel (v) compress = v and v ~= 0 and true or false end - local function setobjcompresslevel(v) objectstream = v and v ~= 0 and true or false end - - local function getcompresslevel (v) return compress and 3 or 0 end - local function getobjcompresslevel(v) return objectstream and 1 or 0 end - - local function setpageresources () end -- needs to be sorted out - local function setpageattributes () end - local function setpagesattributes() end - - updaters.register("backend.update.pdf",function() - pdf.setinfo = setinfo - pdf.setcatalog = setcatalog - pdf.settrailerid = settrailerid - pdf.setmajorversion = setmajorversion - pdf.setminorversion = setminorversion - pdf.getmajorversion = getmajorversion - pdf.getminorversion = getminorversion - pdf.setcompresslevel = setcompresslevel - pdf.setobjcompresslevel = setobjcompresslevel - pdf.getcompresslevel = getcompresslevel - pdf.getobjcompresslevel = getobjcompresslevel - pdf.setpageresources = setpageresources - pdf.setpageattributes = setpageattributes - pdf.setpagesattributes = setpagesattributes - end) - -end - -local function pdfreserveobj() - nofobjects = nofobjects + 1 - objects[nofobjects] = false - return nofobjects -end - -local addtocache, flushcache, cache do - - local data, d = { }, 0 - local list, l = { }, 0 - local coffset = 0 - local maxsize = 32 * 1024 -- uncompressed - local maxcount = 0xFF - local indices = { } - - addtocache = function(n,str) - local size = #str - if size == 0 then - -- todo: message - return - end - if coffset + size > maxsize or d == maxcount then - flushcache() - end - if d == 0 then - nofobjects = nofobjects + 1 - objects[nofobjects] = false - streams[nofobjects] = indices - cache = nofobjects - end - objects[n] = - cache - indices[n] = d - d = d + 1 - -- can have a comment n 0 obj as in luatex - data[d] = str - l = l + 1 ; list[l] = n - l = l + 1 ; list[l] = coffset - coffset = coffset + size + 1 - end - - local p_ObjStm = pdfconstant("ObjStm") - - flushcache = function() -- references cannot be stored - if l > 0 then - list = concat(list," ") - data[0] = list - data = concat(data,"\010",0,d) - local strobj = pdfdictionary { - Type = p_ObjStm, - N = d, - First = #list + 1, - } - objects[cache] = offset - local b = nil - local e = s_stream_e - if compress then - local comp = zlibcompress(data,3) - if comp and #comp < #data then - data = comp - b = f_stream_b_d_c(cache,strobj(),#data) - else - b = f_stream_b_d_u(cache,strobj(),#data) - end - else - b = f_stream_b_d_u(cache,strobj(),#data) - end - flush(f,b) - flush(f,data) - flush(f,e) - offset = offset + #b + #data + #e - data, d = { }, 0 - list, l = { }, 0 - coffset = 0 - indices = { } - end - end - -end - -local pages = table.setmetatableindex(function(t,k) - local v = pdfreserveobj() - t[k] = v - return v -end) - -local function getpageref(n) - return pages[n] -end - -local function refobj() - -- not needed, as we have auto-delay -end - -local function flushnormalobj(data,n) - if not n then - nofobjects = nofobjects + 1 - n = nofobjects - end - data = f_object(n,data) - if level == 0 then - objects[n] = offset - offset = offset + #data - flush(f,data) - else - if not lastdeferred then - lastdeferred = n - elseif n < lastdeferred then - lastdeferred = n - end - objects[n] = data - end - return n -end - -local function flushstreamobj(data,n,dict,comp,nolength) - if not data then - print("no data for",dict) - return - end - if not n then - nofobjects = nofobjects + 1 - n = nofobjects - end - local size = #data - if level == 0 then - local b = nil - local e = s_stream_e - if nolength then - b = f_stream_b_d_r(n,dict) - elseif comp ~= false and compress and size > threshold then - local compdata = zlibcompress(data,3) - if compdata then - local compsize = #compdata - if compsize > size - threshold then - b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size) - else - data = compdata - b = dict and f_stream_b_d_c(n,dict,compsize) or f_stream_b_n_c(n,compsize) - end - else - b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size) - end - else - b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size) - end - flush(f,b) - flush(f,data) - flush(f,e) - objects[n] = offset - offset = offset + #b + #data + #e - else - if nolength then - data = f_stream_d_r(n,dict,data) - elseif comp ~= false and compress and size > threshold then - local compdata = zlibcompress(data,3) - if compdata then - local compsize = #compdata - if compsize > size - threshold then - data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data) - else - data = dict and f_stream_d_c(n,dict,compsize,compdata) or f_stream_n_c(n,compsize,compdata) - end - else - data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data) - end - else - data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data) - end - if not lastdeferred then - lastdeferred = n - elseif n < lastdeferred then - lastdeferred = n - end - objects[n] = data - end - return n -end - -local function flushdeferred() - if lastdeferred then - for n=lastdeferred,nofobjects do - local o = objects[n] - if type(o) == "string" then - objects[n] = offset - offset = offset + #o - flush(f,o) - end - end - lastdeferred = false - end -end - --- These are already used above, so we define them now: - -pushmode = function() - level = level + 1 -end - -popmode = function() - if level == 1 then - flushdeferred() - end - level = level - 1 -end - --- n = pdf.obj([n,] objtext) --- n = pdf.obj([n,] "file", filename) --- n = pdf.obj([n,] "stream", streamtext [, attrtext]) --- n = pdf.obj([n,] "streamfile", filename [, attrtext]) --- --- n = pdf.obj { --- type = , -- raw|stream --- immediate = , --- objnum = , --- attr = , --- compresslevel = , --- objcompression = , --- file = , --- string = , --- nolength = , --- } - -local function obj(a,b,c,d) - local kind --, immediate - local objnum, data, attr, filename - local compresslevel, objcompression, nolength - local argtype = type(a) - if argtype == "table" then - kind = a.type -- raw | stream - -- immediate = a.immediate - objnum = a.objnum - attr = a.attr - compresslevel = a.compresslevel - objcompression = a.objcompression - filename = a.file - data = a.string or a.stream or "" - nolength = a.nolength - if kind == "stream" then - if filename then - data = io.loaddata(filename) or "" - end - elseif kind == "raw"then - if filename then - data = io.loaddata(filename) or "" - end - elseif kind == "file"then - kind = "raw" - data = filename and io.loaddata(filename) or "" - elseif kind == "streamfile" then - kind = "stream" - data = filename and io.loaddata(filename) or "" - end - else - if argtype == "number" then - objnum = a - a, b, c = b, c, d - else - nofobjects = nofobjects + 1 - objnum = nofobjects - end - if b then - if a == "stream" then - kind = "stream" - data = b - elseif a == "file" then - -- kind = "raw" - data = io.loaddata(b) - elseif a == "streamfile" then - kind = "stream" - data = io.loaddata(b) - else - data = "" -- invalid object - end - attr = c - else - -- kind = "raw" - data = a - end - end - if not objnum then - nofobjects = nofobjects + 1 - objnum = nofobjects - end - -- todo: immediate - if kind == "stream" then - flushstreamobj(data,objnum,attr,compresslevel and compresslevel > 0 or nil,nolength) - elseif objectstream and objcompression ~= false then - addtocache(objnum,data) - else - flushnormalobj(data,objnum) - end - return objnum -end - -updaters.register("backend.update.pdf",function() - pdf.reserveobj = pdfreserveobj - pdf.getpageref = getpageref - pdf.refobj = refobj - pdf.flushstreamobj = flushstreamobj - pdf.flushnormalobj = flushnormalobj - pdf.obj = obj - pdf.immediateobj = obj -end) - -local openfile, closefile do - - local f_used = formatters["%010i 00000 n \010"] - local f_link = formatters["%010i 00000 f \010"] - local f_first = formatters["%010i 65535 f \010"] - local f_pdf = formatters["%%PDF-%i.%i\010"] - local f_xref = formatters["xref\0100 %i\010"] - local f_trailer_id = formatters["trailer\010<< %s /ID [ <%s> <%s> ] >>\010startxref\010%i\010%%%%EOF"] - local f_trailer_no = formatters["trailer\010<< %s >>\010startxref\010%i\010%%%%EOF"] - local f_startxref = formatters["startxref\010%i\010%%%%EOF"] - - local inmemory = false - -- local inmemory = environment.arguments.inmemory - local close = nil - - openfile = function(filename) - if inmemory then - f = { } - local n = 0 - flush = function(f,s) - n = n + 1 f[n] = s - end - close = function(f) - f = concat(f) - io.savedata(filename,f) - f = false - end - else - f = io.open(filename,"wb") - if not f then - -- message - os.exit() - end - flush = getmetatable(f).write - close = getmetatable(f).close - end - local v = f_pdf(majorversion,minorversion) - -- local b = "%\xCC\xD5\xC1\xD4\xC5\xD8\xD0\xC4\xC6\010" -- LUATEXPDF (+128) - local b = "%\xC3\xCF\xCE\xD4\xC5\xD8\xD4\xD0\xC4\xC6\010" -- CONTEXTPDF (+128) - flush(f,v) - flush(f,b) - offset = #v + #b - end - - closefile = function(abort) - if not abort then - local xrefoffset = offset - local lastfree = 0 - local noffree = 0 - local catalog = lpdf.getcatalog() - local info = lpdf.getinfo() - if trailerid == true then - trailerid = md5HEX(osuuid()) - elseif trailerid and #trailerid > 32 then - trailerid = md5HEX(trailerid) - else - trailerid = false - end - if objectstream then - flushdeferred() - flushcache() - -- - xrefoffset = offset - -- - nofobjects = nofobjects + 1 - objects[nofobjects] = offset -- + 1 - -- - -- combine these three in one doesn't really give less code so - -- we go for the efficient ones - -- - local nofbytes = 4 - local c1, c2, c3, c4 - if offset <= 0xFFFF then - nofbytes = 2 - for i=1,nofobjects do - local o = objects[i] - if not o then - noffree = noffree + 1 - else - local strm = o < 0 - if strm then - o = -o - end - c1 = extract(o,8,8) - c2 = extract(o,0,8) - if strm then - objects[i] = char(2,c1,c2,streams[o][i]) - else - objects[i] = char(1,c1,c2,0) - end - end - end - if noffree > 0 then - for i=nofobjects,1,-1 do - local o = objects[i] - if not o then - local f1 = extract(lastfree,8,8) - local f2 = extract(lastfree,0,8) - objects[i] = char(0,f1,f2,0) - lastfree = i - end - end - end - elseif offset <= 0xFFFFFF then - nofbytes = 3 - for i=1,nofobjects do - local o = objects[i] - if not o then - noffree = noffree + 1 - else - local strm = o < 0 - if strm then - o = -o - end - c1 = extract(o,16,8) - c2 = extract(o, 8,8) - c3 = extract(o, 0,8) - if strm then - objects[i] = char(2,c1,c2,c3,streams[o][i]) - else - objects[i] = char(1,c1,c2,c3,0) - end - end - end - if noffree > 0 then - for i=nofobjects,1,-1 do - local o = objects[i] - if not o then - local f1 = extract(lastfree,16,8) - local f2 = extract(lastfree, 8,8) - local f3 = extract(lastfree, 0,8) - objects[i] = char(0,f1,f2,f3,0) - lastfree = i - end - end - end - else - nofbytes = 4 - for i=1,nofobjects do - local o = objects[i] - if not o then - noffree = noffree + 1 - else - local strm = o < 0 - if strm then - o = -o - end - c1 = extract(o,24,8) - c2 = extract(o,16,8) - c3 = extract(o, 8,8) - c4 = extract(o, 0,8) - if strm then - objects[i] = char(2,c1,c2,c3,c4,streams[o][i]) - else - objects[i] = char(1,c1,c2,c3,c4,0) - end - end - end - if noffree > 0 then - for i=nofobjects,1,-1 do - local o = objects[i] - if not o then - local f1 = extract(lastfree,24,8) - local f2 = extract(lastfree,16,8) - local f3 = extract(lastfree, 8,8) - local f4 = extract(lastfree, 0,8) - objects[i] = char(0,f1,f2,f3,f4,0) - lastfree = i - end - end - end - end - objects[0] = rep("\0",1+nofbytes+1) - local data = concat(objects,"",0,nofobjects) - local xref = pdfdictionary { - Type = pdfconstant("XRef"), - Size = nofobjects + 1, - W = pdfarray { 1, nofbytes, 1 }, - Root = catalog, - Info = info, - ID = trailerid and pdfarray { pdfliteral(trailerid,true), pdfliteral(trailerid,true) } or nil, - } - if compress then - local comp = zlibcompress(data,3) - if comp then - data = comp - flush(f,f_stream_b_d_c(nofobjects,xref(),#data)) - else - flush(f,f_stream_b_d_u(nofobjects,xref(),#data)) - end - else - flush(f,f_stream_b_d_u(nofobjects,xref(),#data)) - end - flush(f,data) - flush(f,s_stream_e) - flush(f,f_startxref(xrefoffset)) - else - flushdeferred() - xrefoffset = offset - flush(f,f_xref(nofobjects+1)) - local trailer = pdfdictionary { - Size = nofobjects+1, - Root = catalog, - Info = info, - } - for i=1,nofobjects do - local o = objects[i] - if o then - objects[i] = f_used(o) - end - end - for i=nofobjects,1,-1 do - local o = objects[i] - if not o then - objects[i] = f_link(lastfree) - lastfree = i - end - end - objects[0] = f_first(lastfree) - flush(f,concat(objects,"",0,nofobjects)) - trailer.Size = nofobjects + 1 - if trailerid then - flush(f,f_trailer_id(trailer(),trailerid,trailerid,xrefoffset)) - else - flush(f,f_trailer_no(trailer(),xrefoffset)) - end - end - end - close(f) - io.flush() - end - -end - --- For the moment we overload it here, although back-fil.lua eventually will --- be merged with back-pdf as it's pdf specific, or maybe back-imp-pdf or so. - -updaters.register("backend.update.pdf",function() - - -- We overload img but at some point it will even go away, so we just - -- reimplement what we need in context. This will change completely i.e. - -- we will drop the low level interface! - - local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream memstream - local img_none = imagetypes.none - - local rulecodes = nodes.rulecodes - local imagerule_code = rulecodes.image - - local setprop = nodes.nuts.setprop - - local report_images = logs.reporter("backend","images") - - local lastindex = 0 - local indices = { } - - local bpfactor = number.dimenfactors.bp - - local function new_img(specification) - return specification - end - - local function copy_img(original) - return setmetatableindex(original) - end - - local function scan_img(specification) - return specification - end - - local function embed_img(specification) - lastindex = lastindex + 1 - index = lastindex - specification.index = index - local xobject = pdfdictionary { } - if not specification.notype then - xobject.Type = pdfconstant("XObject") - xobject.Subtype = pdfconstant("Form") - xobject.FormType = 1 - end - local bbox = specification.bbox - if bbox and not specification.nobbox then - xobject.BBox = pdfarray { - bbox[1] * bpfactor, - bbox[2] * bpfactor, - bbox[3] * bpfactor, - bbox[4] * bpfactor, - } - end - xobject = xobject + specification.attr - if bbox and not specification.width then - specification.width = bbox[3] - end - if bbox and not specification.height then - specification.height = bbox[4] - end - local dict = xobject() - -- - nofobjects = nofobjects + 1 - local objnum = nofobjects - local nolength = specification.nolength - local stream = specification.stream or specification.string - -- - -- We cannot set type in native img so we need this hack or - -- otherwise we need to patch too much. Better that i write - -- a wrapper then. Anyway, it has to be done better: a key that - -- tells either or not to scale by xsize/ysize when flushing. - -- - if not specification.type then - local kind = specification.kind - if kind then - -- take that one - elseif attr and find(attr,"BBox") then - kind = img_stream - else - -- hack: a bitmap - kind = img_none - end - specification.type = kind - specification.kind = kind - end - local compress = compresslevel and compresslevel > 0 or nil - flushstreamobj(stream,objnum,dict,compress,nolength) - specification.objnum = objnum - specification.rotation = specification.rotation or 0 - specification.orientation = specification.orientation or 0 - specification.transform = specification.transform or 0 - specification.stream = nil - specification.attr = nil - specification.type = specification.kind or specification.type or img_none - indices[index] = specification -- better create a real specification - return specification - end - - local function wrap_img(specification) - -- - local index = specification.index - if not index then - embed_img(specification) - end - -- - local width = specification.width or 0 - local height = specification.height or 0 - local depth = specification.depth or 0 - -- newimagerule - local n = nodes.pool.rule(width,height,depth) - n.subtype = imagerule_code - setprop(tonut(n),"index",specification.index) - return n - end - - -- plugged into the old stuff - - local img = images.__img__ - img.new = new_img - img.copy = copy_img - img.scan = scan_img - img.embed = embed_img - img.wrap = wrap_img - - function pdf.includeimage(index) - local specification = indices[index] - if specification then - local bbox = specification.bbox - local xorigin = bbox[1] - local yorigin = bbox[2] - local xsize = specification.width -- should equal to: bbox[3] - xorigin - local ysize = specification.height -- should equal to: bbox[4] - yorigin - local transform = specification.transform or 0 - local objnum = specification.objnum or pdfreserveobj() - local groupref = nil - local kind = specification.kind or specification.type or img_none -- determines scaling type - return - kind, - xorigin, yorigin, - xsize, ysize, - transform, - objnum, - groupref - end - end - -end) - -updaters.register("backend.update.lpdf",function() - - -- for the moment here, todo: an md5 or sha2 hash can save space - - local pdfimage = lpdf.epdf.image - local newpdf = pdfimage.new - local closepdf = pdfimage.close - local copypage = pdfimage.copy - - local embedimage = images.embed - - local nofstreams = 0 - local topdf = { } - local toidx = { } - - local function storedata(pdf) - local idx = toidx[pdf] - if not idx then - nofstreams = nofstreams + 1 - idx = nofstreams - toidx[pdf] = nofstreams - topdf[idx] = pdf - end - return idx - end - - -- todo: make a type 3 font instead - - -- move to lpdf namespace - - local function vfimage(id,wd,ht,dp,pos_h,pos_v) - local index = topdf[id] - if type(index) == "string" then - local pdfdoc = newpdf(index,#index) - local image = copypage(pdfdoc) - local bbox = image.bbox - image.width = bbox[3] - bbox[1] - image.height = bbox[4] - bbox[2] - embedimage(image) - index = image.index - topdf[id] = index - end - -- pdf.print or pdf.literal - pdf.flushpdfimage(index,wd,ht,dp,pos_h,pos_v) - end - - local function pdfvfimage(wd,ht,dp,data,name) - return { "lua", function(font,char,pos_h,pos_v) - local id = storedata(data) - vfimage(id,wd,ht,dp,pos_h,pos_v) - end } - end - - lpdf.vfimage = pdfvfimage - -end) - --- The driver. - -do - - local isfile = lfs.isfile - local removefile = os.remove - local renamefile = os.rename - -- local copyfile = file.copy - -- local addsuffix = file.addsuffix - - local pdfname = nil - local tmpname = nil - - local function outputfilename() - return pdfname - end - - local function prepare() - if not environment.initex then - -- install new functions in pdf namespace - updaters.apply("backend.update.pdf") - -- install new functions in lpdf namespace - updaters.apply("backend.update.lpdf") - -- adapt existing shortcuts to lpdf namespace - updaters.apply("backend.update.tex") - -- adapt existing shortcuts to tex namespace - updaters.apply("backend.update") - -- - updaters.apply("backend.update.img") - -- - directives.enable("graphics.uselua") - -- - -- if rawget(pdf,"setforcefile") then - -- pdf.setforcefile(false) -- default anyway - -- end - -- - pdfname = file.addsuffix(tex.jobname,"pdf") - tmpname = "l_m_t_x_" .. pdfname - removefile(tmpname) - if isfile(tmpname) then - report("file %a can't be opened, aborting",tmpname) - os.exit() - end - openfile(tmpname) - -- - luatex.registerstopactions(1,function() - lpdf.finalizedocument() - closefile() - end) - -- - luatex.registerpageactions(1,function() - lpdf.finalizepage(true) - end) - -- - fonts.constructors.autocleanup = false - -- - lpdf.registerdocumentfinalizer(wrapup,nil,"wrapping up") - end - -- - environment.lmtxmode = CONTEXTLMTXMODE - end - - local function wrapup() - if pdfname then - local ok = true - if isfile(pdfname) then - removefile(pdfname) - end - if isfile(pdfname) then - ok = false - file.copy(tmpname,pdfname) - else - renamefile(tmpname,pdfname) - if isfile(tmpname) then - ok = false - end - end - if not ok then - print(formatters["\nerror in renaming %a to %a\n"](tmpname,pdfname)) - end - pdfname = nil - tmpname = nil - end - end - - local function cleanup() - if tmpname then - closefile(true) - if isfile(tmpname) then - removefile(tmpname) - end - pdfname = nil - tmpname = nil - end - end - - local function convert(boxnumber) - lpdf.convert(tex.box[boxnumber],"page") - end - - drivers.install { - name = "pdf", - actions = { - prepare = prepare, - wrapup = wrapup, - convert = convert, - cleanup = cleanup, - -- - initialize = initialize, - finalize = finalize, - updatefontstate = updatefontstate, - outputfilename = outputfilename, - }, - flushers = { - character = flushcharacter, - rule = flushrule, - simplerule = flushsimplerule, - pushorientation = pushorientation, - poporientation = poporientation, - -- - pdfliteral = flushpdfliteral, - pdfsetmatrix = flushpdfsetmatrix, - pdfsave = flushpdfsave, - pdfrestore = flushpdfrestore, - pdfimage = flushpdfimage, - }, - } - -end diff --git a/tex/context/base/mkiv/back-lua.mkiv b/tex/context/base/mkiv/back-lua.mkiv new file mode 100644 index 000000000..82ba8f9f1 --- /dev/null +++ b/tex/context/base/mkiv/back-lua.mkiv @@ -0,0 +1,18 @@ +%D \module +%D [ file=back-lua, +%D version=2018.07.26, +%D title=\CONTEXT\ Backend Macros, +%D subtitle=\LUA, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\ifcase\contextlmtxmode \else + \registerctxluafile{back-lua}{optimize} +\fi + +\endinput diff --git a/tex/context/base/mkiv/back-mps.mkiv b/tex/context/base/mkiv/back-mps.mkiv new file mode 100644 index 000000000..0a6640614 --- /dev/null +++ b/tex/context/base/mkiv/back-mps.mkiv @@ -0,0 +1,18 @@ +%D \module +%D [ file=back-mps, +%D version=2018.07.26, +%D title=\CONTEXT\ Backend Macros, +%D subtitle=\METAPOST, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\ifcase\contextlmtxmode \else + \registerctxluafile{back-mps}{optimize} +\fi + +\endinput diff --git a/tex/context/base/mkiv/back-out.lua b/tex/context/base/mkiv/back-out.lua index 176a71f78..8fc2a048f 100644 --- a/tex/context/base/mkiv/back-out.lua +++ b/tex/context/base/mkiv/back-out.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['back-out'] = { version = 1.001, - comment = "companion to lpdf-ini.mkiv", + comment = "companion to back-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" diff --git a/tex/context/base/mkiv/back-out.mkiv b/tex/context/base/mkiv/back-out.mkiv index 26548a0a4..492af2463 100644 --- a/tex/context/base/mkiv/back-out.mkiv +++ b/tex/context/base/mkiv/back-out.mkiv @@ -11,6 +11,8 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +\ifcase\contextlmtxmode\endinput\fi + \registerctxluafile{back-out}{} \let\normalimmediate\immediate diff --git a/tex/context/base/mkiv/back-pdf.lua b/tex/context/base/mkiv/back-pdf.lua index 8886967e0..5f845fdcd 100644 --- a/tex/context/base/mkiv/back-pdf.lua +++ b/tex/context/base/mkiv/back-pdf.lua @@ -10,17 +10,38 @@ if not modules then modules = { } end modules ['back-pdf'] = { -- the way we manage resources, info, etc. Users should use the \type {lpdf} -- interface instead. If needed I will provide replacement functionality. +local setmetatableindex = table.setmetatableindex + interfaces.implement { name = "setpdfcompression", arguments = { "integer", "integer" }, actions = lpdf.setcompression, } -if CONTEXTLMTXMODE == 0 then - updaters.apply("backend.update.pdf") - updaters.apply("backend.update.lpdf") - updaters.apply("backend.update.tex") - updaters.apply("backend.update") +do + + local dummy = function() end + local report = logs.reporter("backend") + + local function unavailable(t,k) + report("calling unavailable pdf.%s function",k) + t[k] = dummy + return dummy + end + + updaters.register("backend.update",function() + -- + -- For now we keep this for tikz. If really needed some more can be made + -- accessible but it has to happen in a controlled way then, for instance + -- by first loading or enabling some compatibility layer so that we can + -- trace possible interferences. + -- + pdf = { + immediateobj = pdf.immediateobj + } + setmetatableindex(pdf,unavailable) + end) + end backends.install("pdf") diff --git a/tex/context/base/mkiv/back-pdf.mkiv b/tex/context/base/mkiv/back-pdf.mkiv index 776581413..80130577c 100644 --- a/tex/context/base/mkiv/back-pdf.mkiv +++ b/tex/context/base/mkiv/back-pdf.mkiv @@ -11,14 +11,23 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -%D The less there is here, the better. +%D The less there is here, the better. After a decade it is time to remove the \type +%D {\pdf*} ones completely. For the moment I keep them commented but even that will +%D go away. \writestatus{loading}{ConTeXt Backend Macros / PDF} \registerctxluafile{lpdf-ini}{optimize} \ifcase\contextlmtxmode\relax - \registerctxluafile{lpdf-nod}{} + % \doifelsefile {lpdf-nod-test.lua} { + % \registerctxluafile{lpdf-nod-test}{} + % } { + \registerctxluafile{lpdf-nod}{} + % } + \registerctxluafile{lpdf-eng}{optimize} +\else + \registerctxluafile{lpdf-lmt}{optimize} \fi \registerctxluafile{lpdf-col}{} @@ -47,10 +56,9 @@ \registerctxluafile{lpdf-emb}{optimize} \fi -\registerctxluafile{back-pdp}{} - \registerctxluafile{lpdf-fnt}{} +\registerctxluafile{back-pdp}{} \registerctxluafile{back-pdf}{} % some code will move to lpdf-* \loadmarkfile{back-u3d} % this will become a module @@ -61,167 +69,37 @@ %D We will minimize the number of calls to \PDF\ specific primitives and delegate %D all management and injection of code to the backend. %D -%D Here we initialize some internal quantities. We also protect them. - -\ifdefined\outputmode - \outputmode\plusone - \let\outputmode\relax - \newcount\outputmode - \outputmode\plusone - \let\normaloutputmode\outputmode -\fi - %D Because we do a lot in \LUA\ and don't want interferences, we nil most of the %D \PDFTEX\ primitives. Of course one can always use the \type {\pdfvariable}, %D \type {\pdfextension} and \type {\pdffeedback} primitives but it will probably %D have bad side effects. -%D For the moment we put these here as they are pdf related but they might move to a -%D better place. We overload the primitives with our own but use a bit of -%D indirection for the purpose of tracing. Some of these are needes for packages like -%D tikz. - -\unexpanded\def\saveboxresource {\clf_saveboxresource} -\unexpanded\def\lastsavedboxresourceindex {\numexpr\clf_lastsavedboxresourceindex\relax} -\unexpanded\def\useboxresource {\clf_useboxresource} - -\unexpanded\def\saveimageresource {\clf_saveimageresource} -\unexpanded\def\lastsavedimageresourceindex{\numexpr\clf_lastsavedimageresourceindex\relax} -\unexpanded\def\lastsavedimageresourcepages{\numexpr\clf_lastsavedimageresourcepages\relax} -\unexpanded\def\useimageresource {\clf_useimageresource} - -%D Maybe these are used: - -\unexpanded\def\savepos {\clf_savepos} - \def\lastxpos{\clf_lastxpos} - \def\lastypos{\clf_lastypos} - -%D We do some interception: - \unexpanded\def\pdfextension{\clf_pdfextension} \def\pdffeedback {\clf_pdffeedback} -%D These are no-ops and don't even intercept what comes next. We used to use -%D token registers for some so that they kept working but now we just abort the -%D run. - -\unexpanded\def\pdfannot {\clf_pdfannot} -\unexpanded\def\pdfcolorstack {\unsupportedcs\pdfcolorstack} -\unexpanded\def\pdfcolorstackinit{\unsupportedcs\pdfcolorstackinit} -\unexpanded\def\pdfdest {\clf_pdfdest} -\unexpanded\def\pdfendlink {\unsupportedcs\pdfendlink} -\unexpanded\def\pdfendthread {\unsupportedcs\pdfendthread} -\unexpanded\def\pdffontattr {\unsupportedcs\pdffontattr} -\unexpanded\def\pdfglyphtounicode{\unsupportedcs\pdfglyphtounicode} -\unexpanded\def\pdfoutline {\unsupportedcs\pdfoutline} -\unexpanded\def\pdfstartlink {\unsupportedcs\pdfstartlink} -\unexpanded\def\pdfstartthread {\unsupportedcs\pdfstartthread} -\unexpanded\def\pdfthread {\unsupportedcs\pdfthread} - -\unexpanded\def\pdfcatalog {\unsupportedcs\pdfcatalog} -\unexpanded\def\pdfinfo {\unsupportedcs\pdfinfo} -\unexpanded\def\pdfnames {\unsupportedcs\pdfnames} -\unexpanded\def\pdftrailer {\unsupportedcs\pdftrailer} -\unexpanded\def\pdfpageresources {\unsupportedcs\pdfpageresources} -\unexpanded\def\pdfpageattr {\unsupportedcs\pdfpageattr} -\unexpanded\def\pdfpagesattr {\unsupportedcs\pdfpagesattr} -\unexpanded\def\pdfxformattr {\unsupportedcs\pdfxformattr} -\unexpanded\def\pdfxformresources{\unsupportedcs\pdfxformresources} - -%D There is no need for these: - -\unexpanded\def\pdfmapfile {\unsupportedcs\pdfmapfile} -\unexpanded\def\pdfmapline {\unsupportedcs\pdfmapline} - -%D These have been no-ops for a long time so now we just abort on their usage. - -\unexpanded\def\pdfcompresslevel {\unsupportedcs\pdfcompresslevel} -\unexpanded\def\pdfobjcompresslevel{\unsupportedcs\pdfobjcompresslevel} - -%D But we still provide: - -\unexpanded\def\nopdfcompression {\clf_setpdfcompression\zerocount\zerocount} -\unexpanded\def\onlypdfobjectcompression{\clf_setpdfcompression\zerocount\plusthree} -\unexpanded\def\maximumpdfcompression {\clf_setpdfcompression\plusnine \plusnine } -\unexpanded\def\normalpdfcompression {\clf_setpdfcompression\plusthree\plusthree} - -%D We don't support these directives, at least not this way. If they are needed -%D by third party modules we can provide some interface. - -% pdfcreationdate -\def\pdfdecimaldigits {\unsupportedcs\pdfdecimaldigits} -\def\pdfdestmargin {\unsupportedcs\pdfdestmargin} -% pdffontname -% pdffontobjnum -\def\pdffontsize {\unsupportedcs\pdffontsize} -\def\pdfgamma {\unsupportedcs\pdfgamma} -\def\pdfgentounicode {\unsupportedcs\pdfgentounicode} -\def\pdfhorigin {\unsupportedcs\pdfhorigin} -\def\pdfignoreunknownimages {\unsupportedcs\pdfignoreunknownimages} -\def\pdfimageaddfilename {\unsupportedcs\pdfimageaddfilename} -\def\pdfimageapplygamma {\unsupportedcs\pdfimageapplygamma} -\def\pdfimagegamma {\unsupportedcs\pdfimagegamma} -\def\pdfimagehicolor {\unsupportedcs\pdfimagehicolor} -\def\pdfimageresolution {\unsupportedcs\pdfimageresolution} -\def\pdfincludechars {\unsupportedcs\pdfincludechars} -\def\pdfinclusioncopyfonts {\unsupportedcs\pdfinclusioncopyfonts} -\def\pdfinclusionerrorlevel {\unsupportedcs\pdfinclusionerrorlevel} -\def\pdfinfoomitdate {\unsupportedcs\pdfinfoomitdate} -% pdflastannot -% pdflastlink -\def\pdflinkmargin {\unsupportedcs\pdflinkmargin} -\def\pdfmajorversion {\unsupportedcs\pdfmajorversion} -\def\pdfminorversion {\unsupportedcs\pdfminorversion} -\def\pdfpagebox {\unsupportedcs\pdfpagebox} -% pdfpageref -\def\pdfpkfixeddpi {\unsupportedcs\pdfpkfixeddpi} -\def\pdfpkmode {\unsupportedcs\pdfpkmode} -\def\pdfpkresolution {\unsupportedcs\pdfpkresolution} -% pdfretval -\def\pdfsuppressoptionalinfo {\unsupportedcs\pdfsuppressoptionalinfo} -\def\pdfsuppressptexinfo {\unsupportedcs\pdfsuppressptexinfo} -% pdftexrevision -% pdftexversion -\def\pdfthreadmargin {\unsupportedcs\pdfthreadmargin} -\def\pdftrailerid {\unsupportedcs\pdftrailerid} -\def\pdfuniqueresname {\unsupportedcs\pdfuniqueresname} -\def\pdfvorigin {\unsupportedcs\pdfvorigin} -\def\pdfxformmargin {\unsupportedcs\pdfxformmargin} -% \pdfxformname - -%D These are still accepted but are normally not needed. - -\let\pdfxform \saveboxresource -\let\pdfximage \saveimageresource - -\let\pdflastxform \lastsavedboxresourceindex -\let\pdflastximage \lastsavedimageresourceindex - -\let\pdfrefxform \useboxresource -\let\pdfrefximage \useimageresource - -\let\pdflastximagepages \lastsavedimageresourcepages - -\let\pdfsavepos \savepos -\let\pdflastxpos \lastxpos -\let\pdflastypos \lastypos - -%D For the moment we keep these as they are but they will become \LUA\ calls -%D eventually, after which we will nil the three \type {\pdf} interface primitives. - -\unexpanded\def\pdfliteral {\clf_pdfliteral}% +%D For the moment we keep this for tikz but hopefully it will at some point use +%D the proper ones. Consider them obsolete: +\unexpanded\def\pdfliteral {\clf_pdfliteral} \unexpanded\def\pdfobj {\clf_pdfobj}% -\unexpanded\def\pdflastobj {\numexpr\clf_pdflastobj\relax}% +\unexpanded\def\pdflastobj {\numexpr\clf_pdflastobj\relax} +\unexpanded\def\pdfrefobj {\clf_pdfrefobj } -\unexpanded\def\pdfrefobj {\pdfextension refobj } \unexpanded\def\pdfrestore {\clf_restore} \unexpanded\def\pdfsave {\clf_save} \unexpanded\def\pdfsetmatrix{\clf_setmatrix} -%D This one can be consulted by users although the suffix is also a system mode. + \let\pdfxform \saveboxresource + \let\pdflastxform \lastsavedboxresourceindex + \let\pdfrefxform \useboxresource -\back_job_set_suffix{pdf} +%D Here are quick and dirty compression flippers, mostly used when testing something +%D as one can best stick to the defaults that also adapt to specific standards. + +\unexpanded\def\nopdfcompression {\clf_setpdfcompression\zerocount\zerocount} +\unexpanded\def\onlypdfobjectcompression{\clf_setpdfcompression\zerocount\plusthree} +\unexpanded\def\maximumpdfcompression {\clf_setpdfcompression\plusnine \plusnine } +\unexpanded\def\normalpdfcompression {\clf_setpdfcompression\plusthree\plusthree} %D PDF/X (maybe combine the two lua calls) @@ -229,11 +107,13 @@ [xmpfile=] \appendtoks - \doifsomething{\backendparameter{xmpfile}} - {\clf_setxmpfile{\backendparameter{xmpfile}}}% + \edef\p_file{\backendparameter{xmpfile}}% + \ifx\p_file\empty\else + \clf_setxmpfile{\p_file}% + \fi \to \everysetupbackend -% \doifsomething{\backendparameter\c!format} .. at the lua end +%D This will change: \appendtoks \clf_setformat @@ -246,281 +126,35 @@ \relax \to \everysetupbackend -%D For the moment we keep these. - -%newtoks \pdfbackendeveryximage -\newtoks \pdfbackendeveryxform +%D These are the only official methods to add stuff to the resources. If more is +%D needed for third party low level code, it can be added. -%D These are the only official methods to add stuff to the resources. +\unexpanded\def\pdfbackendsetcatalog #1#2{\clf_lpdf_addtocatalog{#1}{#2}} +\unexpanded\def\pdfbackendsetinfo #1#2{\clf_lpdf_addtoinfo{#1}{#2}} +\unexpanded\def\pdfbackendsetname #1#2{\clf_lpdf_addtonames{#1}{#2}} -\unexpanded\def\pdfbackendsetcatalog #1#2{\clf_lpdf_addtocatalog {#1}{#2}} -\unexpanded\def\pdfbackendsetinfo #1#2{\clf_lpdf_addtoinfo {#1}{#2}} -\unexpanded\def\pdfbackendsetname #1#2{\clf_lpdf_addtonames {#1}{#2}} +\unexpanded\def\pdfbackendsetpageattribute #1#2{\clf_lpdf_addtopageattributes{#1}{#2}} +\unexpanded\def\pdfbackendsetpagesattribute#1#2{\clf_lpdf_addtopagesattributes{#1}{#2}} +\unexpanded\def\pdfbackendsetpageresource #1#2{\clf_lpdf_addtopageresources{#1}{#2}} -\unexpanded\def\pdfbackendsetpageattribute #1#2{\clf_lpdf_addtopageattributes {#1}{#2}} -\unexpanded\def\pdfbackendsetpagesattribute#1#2{\clf_lpdf_addtopagesattributes {#1}{#2}} -\unexpanded\def\pdfbackendsetpageresource #1#2{\clf_lpdf_addtopageresources {#1}{#2}} - -\unexpanded\def\pdfbackendsetextgstate #1#2{\clf_lpdf_adddocumentextgstate {#1}{#2}} +\unexpanded\def\pdfbackendsetextgstate #1#2{\clf_lpdf_adddocumentextgstate{#1}{#2}} \unexpanded\def\pdfbackendsetcolorspace #1#2{\clf_lpdf_adddocumentcolorspace{#1}{#2}} -\unexpanded\def\pdfbackendsetpattern #1#2{\clf_lpdf_adddocumentpattern {#1}{#2}} -\unexpanded\def\pdfbackendsetshade #1#2{\clf_lpdf_adddocumentshade {#1}{#2}} +\unexpanded\def\pdfbackendsetpattern #1#2{\clf_lpdf_adddocumentpattern{#1}{#2}} +\unexpanded\def\pdfbackendsetshade #1#2{\clf_lpdf_adddocumentshade{#1}{#2}} \def\pdfbackendcurrentresources {\clf_lpdf_collectedresources} \def\pdfcolor #1{\clf_lpdf_color\numexpr\thecolorattribute{#1}\relax} - \let\PDFcolor\pdfcolor - -% clipping - -\unexpanded\def\dostartclipping#1#2#3% we can move this to lua and only set a box here - {\forcecolorhack - \edef\width {\tobigpoints#2}% - \edef\height{\tobigpoints#3}% - \meta_grab_clip_path{#1}\width\height{% - 0 0 m % - \width\space 0 l % - \width\space \height\space l % - 0 \height\space l% - }% - \pdfliteral{q 0 w \MPclippath\space W n}} - -\unexpanded\def\dostopclipping - {\pdfliteral{Q}} -%D Temporary hack, will be removed or improved or default. +%D This is a temporary hack mthat will be removed, improved or somehow can become +%D default. -% attr {/Group << /S /Transparency /I false /K true >>} - -%def\TransparencyHack{\ctxlua{backends.codeinjections.addtransparencygroup()}} \def\TransparencyHack{\setupcolors[\c!pagecolormodel=\v!auto]} -% Still here: - -%D \macros -%D {back_ovalbox} +%D Just in case one needs this \unknown: %D -%D Drawing frames with round corners is inherited from the main module. For drawing -%D ovals we use quite raw \PDF\ code. The next implementation does not differ that -%D much from the one implemented in the \POSTSCRIPT\ driver. This code is somewhat -%D obsolete as we now have metapost embedded. - -\unexpanded\def\back_ovalbox#1#2#3#4#5#6#7#8% - {\forcecolorhack - \bgroup - \edef\stroke{\tobigpoints\dimexpr#4\relax\space}% - \edef\radius{\tobigpoints\dimexpr#5\relax\space}% - \scratchdimen\dimexpr#4/\plustwo\relax - \edef\xmin {\tobigpoints \scratchdimen \space}% - \edef\xmax {\tobigpoints\dimexpr #1-\scratchdimen\relax\space}% - \edef\ymax {\tobigpoints\dimexpr #2-\scratchdimen\relax\space}% - \edef\ymin {\tobigpoints\dimexpr-#3+\scratchdimen\relax\space}% - \advance\scratchdimen by #5\relax - \edef\xxmin{\tobigpoints \scratchdimen \space}% - \edef\xxmax{\tobigpoints\dimexpr #1-\scratchdimen\relax\space}% - \edef\yymax{\tobigpoints\dimexpr #2-\scratchdimen\relax\space}% - \edef\yymin{\tobigpoints\dimexpr-#3+\scratchdimen\relax\space}% - % - \edef\dostroke{\number#6}% - \edef\dofill {\number#7}% - \scratchcounter#8\relax - \setbox\scratchbox\naturalhpack - {\ifnum\dostroke\dofill>\zerocount - \pdfliteral - {q - \stroke w - \ifcase\scratchcounter - \xxmin \ymin m - \xxmax \ymin l - \xmax \ymin \xmax \yymin y - \xmax \yymax l - \xmax \ymax \xxmax \ymax y - \xxmin \ymax l - \xmin \ymax \xmin \yymax y - \xmin \yymin l - \xmin \ymin \xxmin \ymin y - h - \or % 1 - \xxmin \ymin m - \xxmax \ymin l - \xmax \ymin \xmax \yymin y - \xmax \ymax l - \xmin \ymax l - \xmin \yymin l - \xmin \ymin \xxmin \ymin y - h - \or % 2 - \xxmin \ymin m - \xmax \ymin l - \xmax \ymax l - \xxmin \ymax l - \xmin \ymax \xmin \yymax y - \xmin \yymin l - \xmin \ymin \xxmin \ymin y - h - \or % 3 - \xmin \ymin m - \xmax \ymin l - \xmax \yymax l - \xmax \ymax \xxmax \ymax y - \xxmin \ymax l - \xmin \ymax \xmin \yymax y - \xmin \ymin l - h - \or % 4 - \xmin \ymin m - \xxmax \ymin l - \xmax \ymin \xmax \yymin y - \xmax \yymax l - \xmax \ymax \xxmax \ymax y - \xmin \ymax l - \xmin \ymin l - h - \or % 5 - \xmin \ymin m - \xmax \ymin l - \xmax \yymax l - \xmax \ymax \xxmax \ymax y - \xmin \ymax l - \xmin \ymin l - h - \or % 6 - \xmin \ymin m - \xxmax \ymin l - \xmax \ymin \xmax \yymin y - \xmax \ymax l - \xmin \ymax l - \xmin \ymin l - h - \or % 7 - \xxmin \ymin m - \xmax \ymin l - \xmax \ymax l - \xmin \ymax l - \xmin \yymin l - \xmin \ymin \xxmin \ymin y - h - \or % 8 - \xmin \ymin m - \xmax \ymin l - \xmax \ymax l - \xxmin \ymax l - \xmin \ymax \xmin \yymax y - \xmin \ymin l - h - \or % 9 top open - \xmin \ymax m - \xmin \yymin l - \xmin \ymin \xxmin \ymin y - \xxmax \ymin l - \xmax \ymin \xmax \yymin y - \xmax \ymax l - \or % 10 right open - \xmax \ymax m - \xxmin \ymax l - \xmin \ymax \xmin \yymax y - \xmin \yymin l - \xmin \ymin \xxmin \ymin y - \xmax \ymin l - \or % 11 bottom open - \xmax \ymin m - \xmax \yymax l - \xmax \ymax \xxmax \ymax y - \xxmin \ymax l - \xmin \ymax \xmin \yymax y - \xmin \ymin l - \or % 12 left open - \xmin \ymax m - \xxmax \ymax l - \xmax \ymax \xmax \yymax y - \xmax \yymin l - \xmax \ymin \xxmax \ymin y - \xmin \ymin l - \or % 13 - \xmin \ymax m - \xxmax \ymax l - \xmax \ymax \xmax \yymax y - \xmax \ymin l - \or % 14 - \xmax \ymax m - \xmax \yymin l - \xmax \ymin \xxmax \ymin y - \xmin \ymin l - \or % 15 - \xmax \ymin m - \xxmin \ymin l - \xmin \ymin \xmin \yymin y - \xmin \ymax l - \or % 16 - \xmin \ymin m - \xmin \yymax l - \xmin \ymax \xxmin \ymax y - \xmax \ymax l - \or % 17 - \xxmax \ymax m - \xmax \ymax \xmax \yymax y - \or % 18 - \xmax \yymin m - \xmax \ymin \xxmax \ymin y - \or % 19 - \xxmin \ymin m - \xmin \ymin \xmin \yymin y - \or % 20 - \xmin \yymax m - \xmin \ymax \xxmin \ymax y - \or % 21 - \xxmax \ymax m - \xmax \ymax \xmax \yymax y - \xmin \yymax m - \xmin \ymax \xxmin \ymax y - \or % 22 - \xxmax \ymax m - \xmax \ymax \xmax \yymax y - \xmax \yymin m - \xmax \ymin \xxmax \ymin y - \or % 23 - \xmax \yymin m - \xmax \ymin \xxmax \ymin y - \xxmin \ymin m - \xmin \ymin \xmin \yymin y - \or % 24 - \xxmin \ymin m - \xmin \ymin \xmin \yymin y - \xmin \yymax m - \xmin \ymax \xxmin \ymax y - \or % 25 - \xxmax \ymax m - \xmax \ymax \xmax \yymax y - \xmax \yymin m - \xmax \ymin \xxmax \ymin y - \xxmin \ymin m - \xmin \ymin \xmin \yymin y - \xmin \yymax m - \xmin \ymax \xxmin \ymax y - \or % 26 - \xmax \yymin m - \xmax \ymin \xxmax \ymin y - \xmin \yymax m - \xmin \ymax \xxmin \ymax y - \or % 27 - \xxmax \ymax m - \xmax \ymax \xmax \yymax y - \xxmin \ymin m - \xmin \ymin \xmin \yymin y - \or % 28 - \fi - \ifnum\scratchcounter>\pluseight - S - \else - \ifnum\dostroke=\plusone S \fi - \ifnum\dofill =\plusone f \fi - \fi - Q}% - \fi}% - \wd\scratchbox#1% - \ht\scratchbox#2% - \dp\scratchbox#3% - \box\scratchbox - \egroup} +%D \starttyping +%D text \pdfbackendactualtext{Meier}{Müller} text +%D \stoptyping \unexpanded\def\pdfbackendactualtext#1#2% not interfaced {\clf_startactualtext{#2}% @@ -529,8 +163,4 @@ \let\pdfactualtext\pdfbackendactualtext -% \starttext -% text \pdfbackendactualtext{Meier}{Müller} text -% \stoptext - \protect \endinput diff --git a/tex/context/base/mkiv/back-pdp.lua b/tex/context/base/mkiv/back-pdp.lua index d7033608c..6360dea47 100644 --- a/tex/context/base/mkiv/back-pdp.lua +++ b/tex/context/base/mkiv/back-pdp.lua @@ -9,7 +9,8 @@ if not modules then modules = { } end modules ['back-pdp'] = { -- This is temporary ... awaiting a better test .. basically we can -- always use this: pdf primitives. -local context = context +local context = context +local lpdf = lpdf local lpdfreserveobject = lpdf.reserveobject local lpdfcompresslevel = lpdf.compresslevel @@ -17,22 +18,17 @@ local lpdfobj = lpdf.obj local lpdfpagereference = lpdf.pagereference local lpdfxformname = lpdf.xformname -local jobpositions = job.positions -local gethpos = jobpositions.gethpos -local getvpos = jobpositions.getvpos - local tokenscanners = tokens.scanners local scanword = tokenscanners.word local scankeyword = tokenscanners.keyword local scanstring = tokenscanners.string local scaninteger = tokenscanners.integer -local scandimension = tokenscanners.dimension +local scanwhd = tokenscanners.whd -local trace = false trackers.register("commands", function(v) trace = v end) -local report = logs.reporter("command") +local trace = false trackers.register("backend", function(v) trace = v end) +local report = logs.reporter("backend") local nodepool = nodes.pool -local newsavepos = nodepool.savepos local newliteral = nodepool.literal local newsave = nodepool.save local newrestore = nodepool.restore @@ -42,183 +38,12 @@ local implement = interfaces.implement local constants = interfaces.constants local variables = interfaces.variables --- helper - -local function scanwhd() - local width, height, depth - while true do - if scankeyword("width") then - width = scandimension() - elseif scankeyword("height") then - height = scandimension() - elseif scankeyword("depth") then - depth = scandimension() - else - break - end - end - if width or height or depth then - return width or 0, height or 0, depth or 0 - else - -- we inherit - end -end - --- positions - -local function savepos() - context(newsavepos()) -end - -local function lastxpos() - context(gethpos()) -end - -local function lastypos() - context(getvpos()) -end - -implement { name = "savepos", actions = savepos } -implement { name = "lastxpos", actions = lastxpos } -implement { name = "lastypos", actions = lastypos } - -- literals local function pdfliteral() context(newliteral(scanword() or "origin",scanstring())) end -implement { name = "pdfliteral", actions = pdfliteral } - --- box resources - -local boxresources = tex.boxresources -local savebox = boxresources.save -local usebox = boxresources.use - -local lastindex = 0 - -local function saveboxresource() - local immediate = true - local kind = scankeyword("type") and scaninteger() or 0 - local attributes = scankeyword("attr") and scanstring() or nil - local resources = scankeyword("resources") and scanstring() or nil - local margin = scankeyword("margin") and scandimension() or 0 -- register - local boxnumber = scaninteger() - -- - lastindex = savebox(boxnumber,attributes,resources,immediate,kind,margin) - if trace then - report("\\saveboxresource: index %i",lastindex) - end -end - -local function lastsavedboxresourceindex() - if trace then - report("\\lastsaveboxresource: index %i",lastindex) - end - context("%i",lastindex) -end - -local function useboxresource() - local width, height, depth = scanwhd() - local index = scaninteger() - local node = usebox(index,width,height,depth) - if trace then - report("\\useboxresource: index %i",index) - end - context(node) -end - -implement { name = "saveboxresource", actions = saveboxresource } -implement { name = "lastsavedboxresourceindex", actions = lastsavedboxresourceindex } -implement { name = "useboxresource", actions = useboxresource } - --- image resources (messy: will move) - -local imageresources = { } -local lastindex = 0 -local lastpages = 1 - -local function saveimageresource() - local width, height, depth = scanwhd() - local page = 1 - local immediate = true - local margin = 0 -- or dimension - local attributes = scankeyword("attr") and scanstring() or nil - if scankeyword("named") then - scanstring() -- ignored - elseif scankeyword("page") then - page = scaninteger() - end - local userpassword = scankeyword("userpassword") and scanstring() or nil - local ownerpassword = scankeyword("ownerpassword") and scanstring() or nil - local visiblefilename = scankeyword("visiblefilename") and scanstring() or nil - local colorspace = scankeyword("colorspace") and scaninteger() or nil - local pagebox = scanword() or nil - local filename = scanstring() --- pcall - context.getfiguredimensions( { filename }, { - [constants.userpassword] = userpassword, - [constants.ownerpassword] = ownerpassword, - [constants.page] = page or 1, - [constants.size] = pagebox, - }) - context.relax() - lastindex = lastindex + 1 - lastpages = 1 - imageresources[lastindex] = { - filename = filename, - page = page or 1, - size = pagebox, - width = width, - height = height, - depth = depth, - attr = attributes, - -- margin = margin, - } -end - -local function lastsavedimageresourceindex() - context("%i",lastindex or 0) -end - -local function lastsavedimageresourcepages() - context("%i",lastpages or 0) -- todo -end - -local function useimageresource() - local width, height, depth = scanwhd() - if scankeyword("keepopen") then - -- ignored - end - local index = scaninteger() - local l = imageresources[index] - if l then - if not (width or height or depth) then - width = l.width - height = l.height - depth = l.depth - end --- pcall - context.externalfigure( { l.filename }, { - [constants.userpassword] = l.userpassword, - [constants.ownerpassword] = l.ownerpassword, - [constants.width] = width and (width .. "sp") or nil, - [constants.height] = height and (height .. "sp") or nil, - [constants.page] = l.page or 1, - [constants.size] = pagebox, - }) - context.relax() - else - print("no image resource",index) - end -end - -implement { name = "saveimageresource", actions = saveimageresource } -implement { name = "lastsavedimageresourceindex", actions = lastsavedimageresourceindex } -implement { name = "lastsavedimageresourcepages", actions = lastsavedimageresourcepages } -implement { name = "useimageresource", actions = useimageresource } - -- objects local lastobjnum = 0 @@ -271,10 +96,6 @@ local function pdfrefobj() end end -implement { name = "pdfobj", actions = pdfobj } -implement { name = "pdflastobj", actions = pdflastobj } -implement { name = "pdfrefobj", actions = pdfrefobj } - -- annotations local lastobjnum = 0 @@ -306,8 +127,6 @@ local function pdfannot() end end -implement { name = "pdfannot", actions = pdfannot } - local function pdfdest() local name = false local zoom = false @@ -345,8 +164,6 @@ local function pdfdest() context(backends.nodeinjections.destination(width or 0,height or 0,depth or 0,{ name or "" },view or "fit")) end -implement { name = "pdfdest", actions = pdfdest } - -- management local function pdfsave() @@ -398,8 +215,6 @@ local function pdfextension() end end -implement { name = "pdfextension", actions = pdfextension } - -- feedbacks: colorstackinit creationdate fontname fontobjnum fontsize lastannot -- lastlink lastobj pageref retval revision version xformname @@ -421,8 +236,6 @@ local function pdffeedback() end end -implement { name = "pdffeedback", actions = pdffeedback } - -- variables: (integers:) compresslevel decimaldigits gamma gentounicode -- ignoreunknownimages imageaddfilename imageapplygamma imagegamma imagehicolor -- imageresolution inclusioncopyfonts inclusionerrorlevel majorversion minorversion @@ -448,4 +261,17 @@ implement { name = "pdffeedback", actions = pdffeedback } -- end -- end ---------- { name = "pdfvariable", actions = pdfvariable } +-- kept: + +implement { name = "pdfextension", actions = pdfextension } +implement { name = "pdffeedback", actions = pdffeedback } +--------- { name = "pdfvariable", actions = pdfvariable } + +-- for the moment (tikz) + +implement { name = "pdfliteral", actions = pdfliteral } +implement { name = "pdfobj", actions = pdfobj } +implement { name = "pdflastobj", actions = pdflastobj } +implement { name = "pdfrefobj", actions = pdfrefobj } +--------- { name = "pdfannot", actions = pdfannot } +--------- { name = "pdfdest", actions = pdfdest } diff --git a/tex/context/base/mkiv/back-res.lua b/tex/context/base/mkiv/back-res.lua index be92c74a6..2ca4a5ac8 100644 --- a/tex/context/base/mkiv/back-res.lua +++ b/tex/context/base/mkiv/back-res.lua @@ -1,15 +1,32 @@ if not modules then modules = { } end modules ['back-res'] = { version = 1.001, - comment = "companion to lpdf-ini.mkiv", + comment = "companion to back-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } --- A box resource has an index. This happens to be an object number --- due to the pdf backend but in fact it's an abstraction. This is why --- we have explicit fetchers. The internal number (as in \Fm123) is yet --- another number. +local context = context + +local trace = false trackers.register("backend", function(v) trace = v end) +local report = logs.reporter("backend") + +local scanners = tokens.scanners +local scankeyword = scanners.keyword +local scaninteger = scanners.integer +local scanstring = scanners.string +local scandimension = scanners.dimension +local scanword = scanners.word +local scanwhd = scanners.whd + +local scanners = interfaces.scanners +local implement = interfaces.implement +local constants = interfaces.constants +local variables = interfaces.variables + +-- A box resource has an index. This happens to be an object number due to the pdf +-- backend but in fact it's an abstraction. This is why we have explicit fetchers. +-- The internal number (as in \Fm123) is yet another number. local tex_saveboxresource = tex.saveboxresource local tex_useboxresource = tex.useboxresource @@ -23,13 +40,20 @@ updaters.register("backend.update",function() tex_getboxresourcedimensions = tex.getboxresourcedimensions end) -tex.boxresources = { - save = function(...) return tex_saveboxresource(...) end, - use = function(...) return tex_useboxresource(...) end, - getbox = function(...) return tex_getboxresourcebox(...) end, - getdimensions = function(...) return tex_getboxresourcedimensions(...) end, +local savebox = function(...) return tex_saveboxresource(...) end +local usebox = function(...) return tex_useboxresource(...) end +local getbox = function(...) return tex_getboxresourcebox(...) end +local getwhd = function(...) return tex_getboxresourcedimensions(...) end + +local boxresources = { + save = savebox, + use = usebox, + getbox = getbox, + getdimensions = getwhd, } +tex.boxresources = boxresources + -- local tex_saveimageresource = tex.saveimageresource -- local tex_useimageresource = tex.useimageresource -- @@ -42,3 +66,126 @@ tex.boxresources = { -- save = function(...) return tex_saveimageresource(...) end, -- use = function(...) return tex_useimageresource(...) end, -- } + +local lastindex = 0 + +local function saveboxresource() + local immediate = true + local kind = scankeyword("type") and scaninteger() or 0 + local attributes = scankeyword("attr") and scanstring() or nil + local resources = scankeyword("resources") and scanstring() or nil + local margin = scankeyword("margin") and scandimension() or 0 -- register + local boxnumber = scaninteger() + -- + lastindex = savebox(boxnumber,attributes,resources,immediate,kind,margin) + if trace then + report("\\saveboxresource: index %i",lastindex) + end +end + +local function lastsavedboxresourceindex() + if trace then + report("\\lastsaveboxresource: index %i",lastindex) + end + context("%i",lastindex) +end + +local function useboxresource() + local width, height, depth = scanwhd() + local index = scaninteger() + local node = usebox(index,width,height,depth) + if trace then + report("\\useboxresource: index %i",index) + end + context(node) +end + +implement { name = "saveboxresource", actions = saveboxresource } +implement { name = "lastsavedboxresourceindex", actions = lastsavedboxresourceindex } +implement { name = "useboxresource", actions = useboxresource } + +-- image resources + +local imageresources = { } +local lastindex = 0 +local lastpages = 1 + +local function saveimageresource() + local width, height, depth = scanwhd() + local page = 1 + local immediate = true + local margin = 0 -- or dimension + local attributes = scankeyword("attr") and scanstring() or nil + if scankeyword("named") then + scanstring() -- ignored + elseif scankeyword("page") then + page = scaninteger() + end + local userpassword = scankeyword("userpassword") and scanstring() or nil + local ownerpassword = scankeyword("ownerpassword") and scanstring() or nil + local visiblefilename = scankeyword("visiblefilename") and scanstring() or nil + local colorspace = scankeyword("colorspace") and scaninteger() or nil + local pagebox = scanword() or nil + local filename = scanstring() +-- pcall + context.getfiguredimensions( { filename }, { + [constants.userpassword] = userpassword, + [constants.ownerpassword] = ownerpassword, + [constants.page] = page or 1, + [constants.size] = pagebox, + }) + context.relax() + lastindex = lastindex + 1 + lastpages = 1 + imageresources[lastindex] = { + filename = filename, + page = page or 1, + size = pagebox, + width = width, + height = height, + depth = depth, + attr = attributes, + -- margin = margin, + } +end + +local function lastsavedimageresourceindex() + context("%i",lastindex or 0) +end + +local function lastsavedimageresourcepages() + context("%i",lastpages or 0) -- todo +end + +local function useimageresource() + local width, height, depth = scanwhd() + if scankeyword("keepopen") then + -- ignored + end + local index = scaninteger() + local l = imageresources[index] + if l then + if not (width or height or depth) then + width = l.width + height = l.height + depth = l.depth + end + -- pcall / we could use a dedicated call instead: + context.externalfigure( { l.filename }, { + [constants.userpassword] = l.userpassword, + [constants.ownerpassword] = l.ownerpassword, + [constants.width] = width and (width .. "sp") or nil, + [constants.height] = height and (height .. "sp") or nil, + [constants.page] = l.page or 1, + [constants.size] = pagebox, + }) + context.relax() + else + report("no valid image resource %a",index) + end +end + +implement { name = "saveimageresource", actions = saveimageresource } +implement { name = "lastsavedimageresourceindex", actions = lastsavedimageresourceindex } +implement { name = "lastsavedimageresourcepages", actions = lastsavedimageresourcepages } +implement { name = "useimageresource", actions = useimageresource } diff --git a/tex/context/base/mkiv/back-res.mkiv b/tex/context/base/mkiv/back-res.mkiv new file mode 100644 index 000000000..fd7b208c9 --- /dev/null +++ b/tex/context/base/mkiv/back-res.mkiv @@ -0,0 +1,32 @@ +%D \module +%D [ file=back-res, +%D version=2019.05.23, % 2009.04.15, +%D title=\CONTEXT\ Backend Macros, +%D subtitle=Resources, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Backend Macros / Resources} + +\registerctxluafile{back-res}{} + +\unprotect + +%D We overload the primitives with our own but use a bit of indirection for the +%D purpose of tracing. Some of these are needes for packages like tikz. + +\unexpanded\def\saveboxresource {\clf_saveboxresource} +\unexpanded\def\lastsavedboxresourceindex {\numexpr\clf_lastsavedboxresourceindex\relax} +\unexpanded\def\useboxresource {\clf_useboxresource} + +\unexpanded\def\saveimageresource {\clf_saveimageresource} +\unexpanded\def\lastsavedimageresourceindex{\numexpr\clf_lastsavedimageresourceindex\relax} +\unexpanded\def\lastsavedimageresourcepages{\numexpr\clf_lastsavedimageresourcepages\relax} +\unexpanded\def\useimageresource {\clf_useimageresource} + +\protect \endinput diff --git a/tex/context/base/mkiv/back-trf.lua b/tex/context/base/mkiv/back-trf.lua new file mode 100644 index 000000000..721c856a9 --- /dev/null +++ b/tex/context/base/mkiv/back-trf.lua @@ -0,0 +1,155 @@ +if not modules then modules = { } end modules ['back-trf'] = { + version = 1.001, + comment = "companion to back-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local sind, cosd, abs = math.sind, math.cosd, math.abs +local insert, remove = table.insert, table.remove +local unpack = unpack + +local context = context + +local formatters = string.formatters + +local scanners = tokens.scanners +local scankeyword = scanners.keyword +local scaninteger = scanners.integer +local scannumber = scanners.number +local scanstring = scanners.string + +local implement = interfaces.implement + +local nodepool = nodes.pool +local savenode = nodepool.save +local restorenode = nodepool.restore +local setmatrixnode = nodepool.setmatrix +local literalnode = nodepool.literal -- has to become some nodeinjection + +local stack = { } +local restore = true -- false + +updaters.register("backend.update",function() + savenode = nodepool.save + restorenode = nodepool.restore + setmatrixnode = nodepool.setmatrix + literalnode = nodepool.literal -- has to become some nodeinjection +end) + +local function stopsomething() + local top = remove(stack) + if top == false then + -- not wrapped + elseif top == true then + context(restorenode()) + elseif top then + context(setmatrixnode(unpack(top))) -- not really needed anymore + context(restorenode()) + else + -- nesting error + end +end + +local function startrotation() + local a = scannumber() + if a == 0 then + insert(stack,false) + else + local s, c = sind(a), cosd(a) + if abs(s) < 0.000001 then + s = 0 -- otherwise funny -0.00000 + end + if abs(c) < 0.000001 then + c = 0 -- otherwise funny -0.00000 + end + context(savenode()) + context(setmatrixnode(c,s,-s,c)) + insert(stack,restore and { c, -s, s, c } or true) + end +end + +implement { name = "startrotation", actions = startrotation } +implement { name = "stoprotation", actions = stopsomething } + +local function startscaling() -- at the tex end we use sx and sy instead of rx and ry + local rx, ry = 1, 1 + while true do + if scankeyword("rx") then + rx = scannumber() + elseif scankeyword("ry") then + ry = scannumber() + -- elseif scankeyword("revert") then + -- local top = stack[#stack] + -- if top then + -- rx = top[1] + -- ry = top[4] + -- else + -- rx = 1 + -- ry = 1 + -- end + else + break + end + end + if rx == 1 and ry == 1 then + insert(stack,false) + else + if rx == 0 then + rx = 0.0001 + end + if ry == 0 then + ry = 0.0001 + end + context(savenode()) + context(setmatrixnode(rx,0,0,ry)) + insert(stack,restore and { 1/rx, 0, 0, 1/ry } or true) + end +end + +implement { name = "startscaling", actions = startscaling } +implement { name = "stopscaling", actions = stopsomething } + +local function startmatrix() -- rx sx sy ry -- tx, ty + local rx, sx, sy, ry = 1, 0, 0, 1 + while true do + if scankeyword("rx") then rx = scannumber() + elseif scankeyword("ry") then ry = scannumber() + elseif scankeyword("sx") then sx = scannumber() + elseif scankeyword("sy") then sy = scannumber() + else break end + end + if rx == 1 and sx == 0 and sy == 0 and ry == 1 then + insert(stack,false) + else + context(savenode()) + context(setmatrixnode(rx,sx,sy,ry)) + insert(stack,store and { -rx, -sx, -sy, -ry } or true) + end +end + +implement { name = "startmatrix", actions = startmatrix } +implement { name = "stopmatrix", actions = stopsomething } + +local function startmirroring() + context(setmatrixnode(-1,0,0,1)) +end + +implement { name = "startmirroring", actions = startmirroring } +implement { name = "stopmirroring", actions = startmirroring } -- not: stopsomething + +-- this could also run on top of pack-rul ... todo + +local function startclipping() + -- context(savenode()) + context(literalnode("origin",formatters["q 0 w %s W n"](scanstring()))) +end + +local function stopclipping() + -- context(restorenode()) + context(literalnode("Q")) +end + +implement { name = "startclipping", actions = startclipping } +implement { name = "stopclipping", actions = stopclipping } diff --git a/tex/context/base/mkiv/back-trf.mkiv b/tex/context/base/mkiv/back-trf.mkiv new file mode 100644 index 000000000..3d8477a82 --- /dev/null +++ b/tex/context/base/mkiv/back-trf.mkiv @@ -0,0 +1,79 @@ +%D \module +%D [ file=back-trf, +%D version=2019.02.08, % 2009.04.15, +%D title=\CONTEXT\ Backend Macros, +%D subtitle=Transformations, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +\registerctxluafile{back-trf}{} + +% rotation + +\unexpanded\def\dostartrotation#1% + {\forcecolorhack + \clf_startrotation#1\relax} % todo: implement without Q q + +\unexpanded\def\dostoprotation + {\clf_stoprotation + \forcecolorhack} + +% scaling + +\unexpanded\def\dostartscaling#1#2% + {\forcecolorhack + \clf_startscaling rx #1 ry #2\relax} + +\unexpanded\def\dostopscaling + {\clf_stopscaling + \forcecolorhack} + +% mirroring + +\unexpanded\def\dostartmirroring + {\clf_startmirroring} + +\unexpanded\def\dostopmirroring + {\clf_stopmirroring} + +% transform + +\unexpanded\def\dotransformnextbox#1#2#3#4#5#6% + {\dowithnextbox{\dodotransformnextbox{#1}{#2}{#3}{#4}{#5}{#6}}} + +\unexpanded\def\dodotransformnextbox#1#2#3#4#5#6% + {\hpack + {\kern #5\onebasepoint + \raise#6\onebasepoint + \hpack + {\clf_startmatrix rx #1 sx #2 sy #3 ry #4\relax + \box\nextbox + \clf_stopmatrix}}} + +%D \macros +%D {dostartclipping,dostopclipping} +%D +%D Clipping is implemented in such a way that an arbitrary code can be fed. +%D +%D \starttyping +%D \dostartclipping {pathname} {width} {height} +%D \dostopclipping +%D \stoptyping + +\unexpanded\def\dostartclipping#1#2#3% we can move this to lua and only set a box here + {\forcecolorhack + \meta_grab_clip_path{#1}{#2}{#3}% + \clf_startclipping{\MPclippath}% + \glet\MPclippath\empty} + +\unexpanded\def\dostopclipping + {\clf_stopclipping} + +\protect \endinput diff --git a/tex/context/base/mkiv/buff-imp-xml.lua b/tex/context/base/mkiv/buff-imp-xml.lua index 7bac868d7..b7fa85818 100644 --- a/tex/context/base/mkiv/buff-imp-xml.lua +++ b/tex/context/base/mkiv/buff-imp-xml.lua @@ -59,11 +59,11 @@ local closecdata = P("]]>") local grammar = visualizers.newgrammar("default", { "visualizer", sstring = makepattern(handler,"string",patterns.dquote) - * (V("whitespace") + makepattern(handler,"default",1-patterns.dquote))^0 + * (V("whitespace") + makepattern(handler,"default",(1-patterns.dquote)^0)) * makepattern(handler,"string",patterns.dquote), dstring = makepattern(handler,"string",patterns.squote) - * (V("whitespace") + makepattern(handler,"default",1-patterns.squote))^0 + * (V("whitespace") + makepattern(handler,"default",(1-patterns.squote)^0)) * makepattern(handler,"string",patterns.squote), entity = makepattern(handler,"entity",entity), diff --git a/tex/context/base/mkiv/char-fio.lua b/tex/context/base/mkiv/char-fio.lua index 9939bf041..ad61fee65 100644 --- a/tex/context/base/mkiv/char-fio.lua +++ b/tex/context/base/mkiv/char-fio.lua @@ -92,7 +92,11 @@ directives.register("filters.utf.reorder", function(v) configure("characters.f directives.register("filters.utf.collapse", function(v) configure("characters.filters.utf.collapse", v) end) directives.register("filters.utf.decompose", function(v) configure("characters.filters.utf.decompose",v) end) -utffilters.setskippable { "mkiv", "mkvi", "mkix", "mkxi" } +utffilters.setskippable { + "mkiv", "mkvi", + "mkix", "mkxi", + "mkil", "mkli", +} interfaces.implement { name = "enableutf", diff --git a/tex/context/base/mkiv/cont-log.mkiv b/tex/context/base/mkiv/cont-log.mkiv index 8a256a7a9..352e53ec9 100644 --- a/tex/context/base/mkiv/cont-log.mkiv +++ b/tex/context/base/mkiv/cont-log.mkiv @@ -278,6 +278,8 @@ \unexpanded\def\MKII{MkII} \unexpanded\def\MKIV{MkIV} \unexpanded\def\MKVI{MkVI} +\unexpanded\def\MKIL{MkIL} +\unexpanded\def\MKLI{MkLI} \unexpanded\def\MKIX{MkIX} \unexpanded\def\MKXI{MkXI} diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index fa1a405b7..0cd33a4eb 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2019.05.18 10:42} +\newcontextversion{2019.05.25 10:45} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index f05092fe9..2550292e1 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -18,9 +18,9 @@ % 0.2 sec : format dumped % 0.2 sec : char-def/char-ini (no bytecode) % -% So a format still saves some 1.5 seconds (with luajjitex) startup and -% on network shares cq. when no files are cached by the os it's of course -% much worse. A zero run is .27 sec with luajittex. +%D So a format still saves some 1.5 seconds (with luajjitex) startup and on network +%D shares cq. when no files are cached by the os it's of course much worse. A zero +%D run is .27 sec with luajittex. In luametatex it's faster anyway (2.4 sec). % http://build.contextgarden.net/waterfall?tag=c/luatex @@ -29,20 +29,19 @@ \catcode`\{=1 \catcode`\}=2 \catcode`\#=6 -%D From the next string (which is set by the script that assembles the -%D distribution) later on we will calculate a number that can be used -%D by use modules to identify the feature level. Starting with version -%D 2004.8.30 the low level interface is english. Watch out and adapt -%D your styles an modules. +%D From the next string (which is set by the script that assembles the distribution) +%D later on we will calculate a number that can be used by use modules to identify +%D the feature level. Starting with version 2004.8.30 the low level interface is +%D english. Watch out and adapt your styles an modules. % \everypar{\writestatus{!!!!}{some spurious input in line \the\inputlineno}\wait} -%D The order of loading will change when all modules have been cleaned -%D up and the dependencies are more consistent. beware, the version number -%D has to match \type {YYYY.MM.DD HH:MM} format. +%D The order of loading will change when all modules have been cleaned up and the +%D dependencies are more consistent. Beware, the version number has to match \type +%D {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2019.05.18 10:42} +\edef\contextversion{2019.05.25 10:45} \edef\contextkind {beta} %D Kind of special: @@ -61,31 +60,30 @@ \ifx\normalinput\undefined \let\normalinput\input \fi -\def\loadcorefile#1{\normalinput#1\relax} -\def\loadmarkfile#1{\normalinput#1.\mksuffix\relax} -\def\loadmkiifile#1{} -\def\loadmkivfile#1{\normalinput#1.mkiv\relax} -\def\loadmkvifile#1{\normalinput#1.mkvi\relax} +\def\loadmarkfile#1{\normalinput{#1.\mksuffix}} +\def\loadmkivfile#1{\normalinput{#1.mkiv}} +\def\loadmkvifile#1{\normalinput{#1.mkvi}} +%def\loadmkilfile#1{\normalinput{#1.mkil}} +%def\loadmklifile#1{\normalinput{#1.mkli}} -%D First we load the system modules. These implement a lot of -%D manipulation macros. We start with setting up some basic \TEX\ -%D machinery. +%D First we load the system modules. These implement a lot of manipulation macros. +%D We start with setting up some basic \TEX\ machinery. \loadmarkfile{syst-ini} +%D We just quit if new functionality is expected. + +\ifnum\luatexversion<110 % also change message + \writestatus{!!!!}{Your luatex binary is too old, you need at least version 1.10!} + \expandafter\end +\fi + %D Some checking (more primitives are now defined): \ifdefined\defaultinterface \else \def\defaultinterface {english} \fi %ifdefined\messageinterface \else \let\messageinterface \defaultinterface \fi \ifdefined\defaultlanguagetag \else \def\defaultlanguagetag{en} \fi -%D We just quit if new functionality is expected. - -\ifnum\luatexversion<109 % also change message - \writestatus{!!!!}{Your luatex binary is too old, you need at least version 1.09!} - \expandafter\end -\fi - %D There is only this way to pass the version info to \LUA\ (currently). Hm, we could %D now put it into the environment. @@ -116,8 +114,8 @@ % \tracecatcodetables -% From here on we have \unexpanded being \normalprotected, as we already had -% \unexpanded long before etex came around. +%D From here on we have \unexpanded being \normalprotected, as we already had +%D \type {\unexpanded} long before etex came around. \loadmarkfile{luat-ini} \loadmarkfile{toks-scn} @@ -135,13 +133,13 @@ \loadmkvifile{file-res} \loadmkvifile{file-lib} -\loadmarkfile{core-lmt} +\loadmarkfile{core-lmt} % lmtx -% needs more checking for clashes: -% -% no need to register, just execute once, slightly faster -% -% \doifelsefileexists{l-macro-imp-codes.lua}{\registerctxluafile{l-macro-imp-codes}{}}{} +%D This needs more checking for clashes: +%D +%D \starttyping +%D \doifelsefileexists{l-macro-imp-codes.lua}{\registerctxluafile{l-macro-imp-codes}{}}{} +%D \stoptyping \loadmarkfile{supp-dir} @@ -154,7 +152,6 @@ \loadmarkfile{mult-aux} \loadmarkfile{mult-def} \loadmarkfile{mult-chk} -%loadmarkfile{mult-aux} % moved up \loadmkvifile{mult-dim} \loadmarkfile{cldf-int} % interface @@ -182,9 +179,12 @@ \loadmarkfile{node-mig} %loadmarkfile{node-pag} -\loadmarkfile{driv-ini} +\loadmarkfile{driv-ini} % lmtx \loadmarkfile{back-ini} +\loadmarkfile{back-res} +\loadmarkfile{back-trf} +\loadmarkfile{back-out} \loadmarkfile{attr-col} \loadmarkfile{attr-lay} @@ -580,8 +580,9 @@ % now we hook in backend code (needs checking) \loadmarkfile{back-exp} - -\loadmarkfile{back-pdf} % actually, this one should load the next three using document.arguments.backend +\loadmarkfile{back-pdf} +\loadmarkfile{back-mps} +\loadmarkfile{back-lua} \loadmarkfile{mlib-pdf} \loadmarkfile{mlib-pps} diff --git a/tex/context/base/mkiv/core-sys.mkiv b/tex/context/base/mkiv/core-sys.mkiv index 4d99d8647..7a04df840 100644 --- a/tex/context/base/mkiv/core-sys.mkiv +++ b/tex/context/base/mkiv/core-sys.mkiv @@ -78,18 +78,6 @@ \fi \to \everysetupsystem -\let\m_system_job_suffix\s!unknown - -\appendtoks - \resetsystemmode{suffix-\m_system_job_suffix}% - \edef\m_system_job_suffix{\jobsuffix}% - \setsystemmode{suffix-\m_system_job_suffix}% -\to \everysetupsystem - -% \appendtoks -% \ctxcommand{updatefilenames("\jobame","\inputfilename","\outputfilename")}% -% \to \everysetupsystem - \newconditional\prerollrun % when true it means that we have a forced number of runs % Some mechanisms (see x-res-01) use either \jobfilename or diff --git a/tex/context/base/mkiv/data-env.lua b/tex/context/base/mkiv/data-env.lua index 1b31caab6..611504db4 100644 --- a/tex/context/base/mkiv/data-env.lua +++ b/tex/context/base/mkiv/data-env.lua @@ -100,7 +100,7 @@ local relations = allocate { -- todo: handlers also here tex = { names = { "tex" }, variable = 'TEXINPUTS', - suffixes = { "tex", "mkvi", "mkiv", "mkii", "cld", "lfg", "xml" }, -- known suffixes have less lookups + suffixes = { "tex", "mkvi", "mkiv", "mkli", "mkil", "mkii", "cld", "lfg", "xml" }, -- known suffixes have less lookups usertype = true, }, icc = { diff --git a/tex/context/base/mkiv/driv-shp.lua b/tex/context/base/mkiv/driv-shp.lua index 4dffa947e..26bb06b39 100644 --- a/tex/context/base/mkiv/driv-shp.lua +++ b/tex/context/base/mkiv/driv-shp.lua @@ -150,7 +150,7 @@ local flushpdfsetmatrix local flushpdfsave local flushpdfrestore local flushspecial -local flushpdfimage +----- flushpdfimage -- make local @@ -1182,7 +1182,7 @@ function lpdf.convert(box,smode,objnum,specification) -- temp name flushpdfsetmatrix = flushers.pdfsetmatrix flushpdfsave = flushers.pdfsave flushpdfrestore = flushers.pdfrestore - flushpdfimage = flushers.pdfimage + -- flushpdfimage = flushers.pdfimage reset_dir_stack() reset_state() diff --git a/tex/context/base/mkiv/driv-shp.mkiv b/tex/context/base/mkiv/driv-shp.mkiv index 031d0265f..f92649a48 100644 --- a/tex/context/base/mkiv/driv-shp.mkiv +++ b/tex/context/base/mkiv/driv-shp.mkiv @@ -11,18 +11,14 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\ifcase\contextlmtxmode\expandafter\endinput\fi +\ifcase\contextlmtxmode \else + \registerctxluafile{driv-shp}{optimize} +\fi \unprotect -\registerctxluafile{driv-shp}{optimize} - -\registerctxluafile{back-lpd}{optimize} -\registerctxluafile{back-lua}{optimize} -\registerctxluafile{back-mps}{optimize} - \appendtoks - \clf_enabledriver{pdf}% for now this way + \clf_enabledriver{pdf}% \to \everyjob \protect \endinput diff --git a/tex/context/base/mkiv/font-ocl.lua b/tex/context/base/mkiv/font-ocl.lua index 0976cdb21..d79ed657e 100644 --- a/tex/context/base/mkiv/font-ocl.lua +++ b/tex/context/base/mkiv/font-ocl.lua @@ -330,39 +330,19 @@ do local hashed = { } local cache = { } - if epdf then - - local openpdf = epdf.openMemStream - - function otf.storepdfdata(pdf) - local done = hashed[pdf] - if not done then - nofstreams = nofstreams + 1 - local o, n = openpdf(pdf,#pdf,f_name(nofstreams)) - cache[n] = o -- we need to keep in mem - done = f_used(n) - hashed[pdf] = done - end - return done + local openpdf = pdfe.new + ----- prefix = "data:application/pdf," + + function otf.storepdfdata(pdf) + local done = hashed[pdf] + if not done then + nofstreams = nofstreams + 1 + local f = f_name(nofstreams) + local n = openpdf(pdf,#pdf,f) + done = f_used(n) + hashed[pdf] = done end - - else - - local openpdf = pdfe.new - ----- prefix = "data:application/pdf," - - function otf.storepdfdata(pdf) - local done = hashed[pdf] - if not done then - nofstreams = nofstreams + 1 - local f = f_name(nofstreams) - local n = openpdf(pdf,#pdf,f) - done = f_used(n) - hashed[pdf] = done - end - return done - end - + return done end end diff --git a/tex/context/base/mkiv/font-off.lua b/tex/context/base/mkiv/font-off.lua deleted file mode 100644 index 7e509c2c3..000000000 --- a/tex/context/base/mkiv/font-off.lua +++ /dev/null @@ -1,230 +0,0 @@ -if not modules then modules = { } end modules ['font-off'] = { - version = 1.001, - comment = "companion to font-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local tonumber = tonumber -local lower = string.lower -local round = math.round -local setmetatableindex = table.setmetatableindex - -local fontloader = fontloader ------ font_to_table = fontloader.to_table -local open_font = fontloader.open ------ get_font_info = fontloader.info -local close_font = fontloader.close ------ font_fields = fontloader.fields - --- table={ --- ["familyname"]="TeXGyrePagella", --- ["fontname"]="TeXGyrePagella-Regular", --- ["fullname"]="TeXGyrePagella-Regular", --- ["italicangle"]=0, --- ["names"]={ --- { --- ["lang"]="English (US)", --- ["names"]={ --- ["copyright"]="Copyright 2006, 2009 for TeX Gyre extensions by B. Jackowski and J.M. Nowacki (on behalf of TeX users groups). This work is released under the GUST Font License -- see http://tug.org/fonts/licenses/GUST-FONT-LICENSE.txt for details.", --- ["family"]="TeXGyrePagella", --- ["fullname"]="TeXGyrePagella-Regular", --- ["postscriptname"]="TeXGyrePagella-Regular", --- ["preffamilyname"]="TeX Gyre Pagella", --- ["subfamily"]="Regular", --- ["trademark"]="Please refer to the Copyright section for the font trademark attribution notices.", --- ["uniqueid"]="2.004;UKWN;TeXGyrePagella-Regular", --- ["version"]="Version 2.004;PS 2.004;hotconv 1.0.49;makeotf.lib2.0.14853", --- }, --- }, --- }, --- ["pfminfo"]={ --- ["avgwidth"]=528, --- ["codepages"]={ 536871315, 0 }, --- ["firstchar"]=32, --- ["fstype"]=12, --- ["hhead_ascent"]=1098, --- ["hhead_descent"]=-283, --- ["hheadascent_add"]=0, --- ["hheaddescent_add"]=0, --- ["hheadset"]=1, --- ["lastchar"]=64260, --- ["linegap"]=0, --- ["os2_breakchar"]=32, --- ["os2_capheight"]=692, --- ["os2_defaultchar"]=0, --- ["os2_family_class"]=0, --- ["os2_strikeypos"]=269, --- ["os2_strikeysize"]=50, --- ["os2_subxoff"]=0, --- ["os2_subxsize"]=650, --- ["os2_subyoff"]=75, --- ["os2_subysize"]=600, --- ["os2_supxoff"]=0, --- ["os2_supxsize"]=650, --- ["os2_supyoff"]=350, --- ["os2_supysize"]=600, --- ["os2_typoascent"]=726, --- ["os2_typodescent"]=-274, --- ["os2_typolinegap"]=200, --- ["os2_vendor"]="UKWN", --- ["os2_winascent"]=1098, --- ["os2_windescent"]=283, --- ["os2_xheight"]=449, --- ["panose"]={ --- ["armstyle"]="Any", --- ["contrast"]="Any", --- ["familytype"]="Any", --- ["letterform"]="Any", --- ["midline"]="Any", --- ["proportion"]="Any", --- ["serifstyle"]="Any", --- ["strokevariation"]="Any", --- ["weight"]="Book", --- ["xheight"]="Any", --- }, --- ["panose_set"]=1, --- ["pfmfamily"]=81, --- ["pfmset"]=1, --- ["subsuper_set"]=1, --- ["typoascent_add"]=0, --- ["typodescent_add"]=0, --- ["unicoderanges"]={ 536871047, 0, 0, 0 }, --- ["vheadset"]=0, --- ["vlinegap"]=0, --- ["weight"]=400, --- ["width"]=5, --- ["winascent_add"]=0, --- ["windescent_add"]=0, --- }, --- ["units_per_em"]=1000, --- ["version"]="2.004;PS 2.004;hotconv 1.0.49;makeotf.lib2.0.14853", --- ["weight"]="Book", --- } - --- We had this as temporary solution because we needed a bit more info but in the --- meantime it got an interesting side effect: currently luatex delays loading of e.g. --- glyphs so here we first load and then discard which is a waste. In the past it did --- free memory because a full load was done. One of these things that goes unnoticed. --- --- local function get_full_info(...) -- check with taco what we get / could get --- local ff = open_font(...) --- if ff then --- local d = ff -- and font_to_table(ff) --- d.glyphs, d.subfonts, d.gpos, d.gsub, d.lookups = nil, nil, nil, nil, nil --- close_font(ff) --- return d --- else --- return nil, "error in loading font" --- end --- end - --- Phillip suggested this faster variant but it's still a hack as fontloader.info should --- return these keys/values (and maybe some more) but at least we close the loader which --- might save some memory in the end. - --- local function get_full_info(name) --- local ff = open_font(name) --- if ff then --- local fields = table.tohash(font_fields(ff),true) -- isn't that one stable --- local d = { --- names = fields.names and ff.names, --- familyname = fields.familyname and ff.familyname, --- fullname = fields.fullname and ff.fullname, --- fontname = fields.fontname and ff.fontname, --- weight = fields.weight and ff.weight, --- italicangle = fields.italicangle and ff.italicangle, --- units = fields.units_per_em and ff.units_per_em, --- designsize = fields.design_size and ff.design_size, --- minsize = fields.design_range_bottom and ff.design_range_bottom, --- maxsize = fields.design_range_top and ff.design_range_top, --- italicangle = fields.italicangle and ff.italicangle, --- pfmweight = pfminfo and pfminfo.weight or 400, --- pfmwidth = pfminfo and pfminfo.width or 5, --- } --- -- setmetatableindex(d,function(t,k) --- -- report_names("warning, trying to access field %a in font table of %a",k,name) --- -- end) --- close_font(ff) --- return d --- else --- return nil, "error in loading font" --- end --- end - --- more efficient: - -local fields = nil - -local function check_names(names) - if names then - for i=1,#names do - local name = names[i] - if lower(name.lang) == "english (us)" then -- lower added - return name.names - end - end - end -end - -local function get_full_info(name) - local ff = open_font(name) - if ff then - -- unfortunately luatex aborts when a field is not available but we just make - -- sure that we only access a few known ones - local pfminfo = ff.pfminfo or { } - local names = check_names(ff.names) or { } - local weight = names.weight or ff.weight - local width = names.width -- no: ff.width - local d = { - fontname = ff.fontname, - fullname = names.fullname or ff.fullname, - family = names.family, - subfamily = names.subfamily, - familyname = names.preffamilyname or names.family or ff.familyname, - subfamilyname = names.prefmodifiers or names.subfamily, - weight = weight and lower(weight), - width = width and lower(width), - italicangle = round(1000*(tonumber(ff.italicangle) or 0))/1000 or 0, - units = ff.units_per_em, - designsize = ff.design_size, - minsize = ff.design_range_bottom, - maxsize = ff.design_range_top, - pfmweight = pfminfo.weight or 400, - pfmwidth = pfminfo.width or 5, - monospaced = pfminfo.panose and pfminfo.panose.proportion == "Monospaced", - } - close_font(ff) - return d - else - return nil, "error in loading font" - end -end - --- As we have lazy loading anyway, this one still is full and with less code than --- the previous one. But this depends on the garbage collector to kick in and in the --- current version that somehow happens not that often (on my machine I end up with --- some 3 GB extra before that happens). - --- local function get_full_info(...) --- local ff = open_font(...) --- if ff then --- local d = { } -- ff is userdata so [1] or # fails on it --- setmetatableindex(d,ff) --- return d -- garbage collection will do the close_font(ff) --- else --- return nil, "error in loading font" --- end --- end - -fonts = fonts or { } -local handlers = fonts.handlers or { } -fonts.handlers = handlers -local otf = handlers.otf or { } -handlers.otf = otf -local readers = otf.readers or { } -otf.readers = readers - -fontloader.fullinfo = get_full_info -readers.getinfo = readers.getinfo or get_full_info diff --git a/tex/context/base/mkiv/font-syn.lua b/tex/context/base/mkiv/font-syn.lua index 18c25fbfd..a68276504 100644 --- a/tex/context/base/mkiv/font-syn.lua +++ b/tex/context/base/mkiv/font-syn.lua @@ -46,9 +46,6 @@ local findfile = resolvers.findfile local cleanpath = resolvers.cleanpath local resolveprefix = resolvers.resolve ------ fontloader = fontloader -- still needed for pfb (now) ------ get_font_info = fontloader.info - local settings_to_hash = utilities.parsers.settings_to_hash_tolerant local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end) diff --git a/tex/context/base/mkiv/grph-bmp.lua b/tex/context/base/mkiv/grph-bmp.lua index 256b64309..d863b1a98 100644 --- a/tex/context/base/mkiv/grph-bmp.lua +++ b/tex/context/base/mkiv/grph-bmp.lua @@ -38,29 +38,39 @@ function bitmaps.new(xsize,ysize,colorspace,colordepth,mask) } end +-- function backends.codeinjections.bitmap(bitmap) +-- return lpdf.injectors.bitmap(bitmap) +-- end + local function flush(bitmap) - return wrapimage(lpdf.injectors.bitmap(bitmap)) + local specification = backends.codeinjections.bitmap(bitmap) + if specification then + return wrapimage(specification) + end end bitmaps.flush = flush function bitmaps.tocontext(bitmap,width,height) - if type(width) == "number" then - width = width .. "sp" - end - if type(height) == "number" then - height = height .. "sp" - end - if width or height then - context.scale ( - { - width = width, - height = height, - }, - flush(bitmap) - ) - else - context(flush(bitmap)) + local bmp = flush(bitmap) + if bmp then + if type(width) == "number" then + width = width .. "sp" + end + if type(height) == "number" then + height = height .. "sp" + end + if width or height then + context.scale ( + { + width = width, + height = height, + }, + bmp + ) + else + context(bmp) + end end end diff --git a/tex/context/base/mkiv/grph-img.lua b/tex/context/base/mkiv/grph-img.lua index 9acd1a5de..55ae5a3a0 100644 --- a/tex/context/base/mkiv/grph-img.lua +++ b/tex/context/base/mkiv/grph-img.lua @@ -32,9 +32,9 @@ local getposition = files.getposition local setmetatableindex = table.setmetatableindex local setmetatablecall = table.setmetatablecall -local lpdf = lpdf or { } -local pdfmajorversion = lpdf.majorversion -local pdfminorversion = lpdf.minorversion +----- lpdf = lpdf or { } +----- pdfmajorversion = lpdf.majorversion +----- pdfminorversion = lpdf.minorversion local graphics = graphics or { } local identifiers = { } @@ -254,8 +254,8 @@ do local orientation = 1 local okay = false local filesize = getsize(f) -- seek end - local majorversion = pdfmajorversion and pdfmajorversion() or 2 - local minorversion = pdfminorversion and pdfminorversion() or 2 + -- local majorversion = pdfmajorversion and pdfmajorversion() or 1 + -- local minorversion = pdfminorversion and pdfminorversion() or 7 while getposition(f) < filesize do local b = readbyte(f) if not b then @@ -280,10 +280,14 @@ do end local name = tagdata.name if name == "SOF0" or name == "SOF1" or name == "SOF2" then - if majorversion == 1 and minorversion <= 2 then - specification.error = "no progressive DCT in PDF <= 1.2" - break - end + -- + -- It makes no sense to support pdf < 1.3 so we now just omit this + -- test. There is no need to polute the code with useless tests. + -- + -- if majorversion == 1 and minorversion <= 2 then + -- specification.error = "no progressive DCT in PDF <= 1.2" + -- break + -- end length = readcardinal2(f) specification.colordepth = readcardinal(f) specification.ysize = readcardinal2(f) @@ -477,8 +481,6 @@ do local orientation = 1 local okay = false local filesize = getsize(f) -- seek end - local majorversion = pdfmajorversion and pdfmajorversion() or 2 - local minorversion = pdfminorversion and pdfminorversion() or 2 -- local pos = 0 -- signature diff --git a/tex/context/base/mkiv/grph-inc.lua b/tex/context/base/mkiv/grph-inc.lua index 0856e0de9..fdac5f1a9 100644 --- a/tex/context/base/mkiv/grph-inc.lua +++ b/tex/context/base/mkiv/grph-inc.lua @@ -157,36 +157,11 @@ function checkimage(figure) end end ---- begin of mapping / this will become graphics & code|nodeinjections but not this year +-- This is a bit of abstraction. Fro a while we will follow the luatex image +-- model. -local __img__ = type(img) == "table" and img or { } -images.__img__ =__img__ - -local imgnew = __img__.new -local imgscan = __img__.scan -local imgcopy = __img__.copy -local imgwrap = __img__.node -local imgembed = __img__.immediatewrite - -if imgnew then - -- catch (actually we should be less picky in img) - local __img__new__ = imgnew - imgnew = function(t) - t.kind = nil - return __img__new__(t) - end -end - -updaters.register("backend.update.img",function() - local img = images.__img__ - imgnew = img.new - imgscan = img.scan - imgcopy = img.copy - imgwrap = img.wrap - imgembed = img.embed -end) - -local imagekeys = { -- only relevant ones +local imagekeys = { + -- only relevant ones (permitted in luatex) "width", "height", "depth", "bbox", "colordepth", "colorspace", "filename", "filepath", "visiblefilename", @@ -214,28 +189,26 @@ images.keys = imagekeys images.types = imagetypes images.sizes = imagesizes --- new interface - local function createimage(specification) - return imgnew(specification) + return backends.codeinjections.newimage(specification) end local function copyimage(specification) - return imgcopy(specification) + return backends.codeinjections.copyimage(specification) end local function scanimage(specification) - return imgscan(specification) + return backends.codeinjections.scanimage(specification) end local function embedimage(specification) -- write the image to file - return imgembed(specification) + return backends.codeinjections.embedimage(specification) end local function wrapimage(specification) -- create an image rule - return imgwrap(specification) + return backends.codeinjections.wrapimage(specification) end images.create = createimage @@ -244,20 +217,20 @@ images.copy = copyimage images.wrap = wrapimage images.embed = embedimage --- now we reimplement img: - -img = { - new = createimage, - scan = scanimage, - copy = copyimage, - node = wrapimage, - write = function(specification) context(wrapimage(specification)) end, - immediatewrite = embedimage, - immediatewriteobject = function() end, -- not upported, experimental anyway - boxes = function() return sortedkeys(imagesizes) end, - fields = function() return imagekeys end, - types = function() return { unpack(imagetypes,0,#imagetypes) } end, -} +-- If really needed we can provide: +-- +-- img = { +-- new = createimage, +-- scan = scanimage, +-- copy = copyimage, +-- node = wrapimage, +-- write = function(specification) context(wrapimage(specification)) end, +-- immediatewrite = embedimage, +-- immediatewriteobject = function() end, -- not upported, experimental anyway +-- boxes = function() return sortedkeys(imagesizes) end, +-- fields = function() return imagekeys end, +-- types = function() return { unpack(imagetypes,0,#imagetypes) } end, +-- } -- end of copies / mapping diff --git a/tex/context/base/mkiv/grph-inc.mkiv b/tex/context/base/mkiv/grph-inc.mkiv index f2f626b28..abf9d709a 100644 --- a/tex/context/base/mkiv/grph-inc.mkiv +++ b/tex/context/base/mkiv/grph-inc.mkiv @@ -20,11 +20,10 @@ \writestatus{loading}{ConTeXt Graphic Macros / Figure Inclusion} -\ifcase\contextlmtxmode - \registerctxluafile{grph-inc}{} -\else - \registerctxluafile{grph-img}{} - \registerctxluafile{grph-inc}{} +\registerctxluafile{grph-img}{} +\registerctxluafile{grph-inc}{} + +\ifcase\contextlmtxmode \else \registerctxluafile{grph-bmp}{} \registerctxluafile{grph-chk}{} \fi diff --git a/tex/context/base/mkiv/grph-mem.lua b/tex/context/base/mkiv/grph-mem.lua index ff3548350..bde216826 100644 --- a/tex/context/base/mkiv/grph-mem.lua +++ b/tex/context/base/mkiv/grph-mem.lua @@ -21,142 +21,74 @@ local report = logs.reporter("memstream") local trace = false trackers.register ("graphics.memstreams", function(v) trace = v end) local data = { } -if pdfe then - - local function setmemstream(name,stream,once) - if once and data[name] then - if trace then - report("not overloading %a",name) -- - end - return data[name] - end - local kind = figures.guessfromstring(stream) - local identifier = false - if kind == "pdf" then - identifier = pdfe.new(stream,#stream,name) - if not identifier then - report("no valid pdf stream %a",name) - elseif trace then - report("setting %a with identifier %a",name,identifier) - end - else - identifier = "m_k_i_v_memstream_" .. name .. "." .. kind - io.savedata(identifier,stream) - end - if not identifier then - identifier = "invalid-memstream" - end - data[name] = identifier - return identifier - end - - resolvers.setmemstream = setmemstream - - function resolvers.finders.memstream(specification) - local name = specification.path - local identifier = data[name] - if identifier then - if trace then - report("reusing %a with identifier %a",name,identifier) - end - return identifier - end - local stream = io.loaddata(name) - if stream and stream ~= "" then - return setmemstream(name,stream) - end +local function setmemstream(name,stream,once) + if once and data[name] then if trace then - report("no valid memstream %a",name) + report("not overloading %a",name) -- end - return resolvers.finders.notfound() + return data[name] end - - local flush = { } - - function resolvers.resetmemstream(name,afterpage) - if afterpage then - flush[#flush+1] = name - end - end - - luatex.registerpageactions(function() - if #flush > 0 then - for i=1,#flush do - local identifier = data[name] - if identifier then - os.remove(identifier) - data[name] = nil - end - end - flush = { } - end - end) - -else - - local opened = { } - - local function setmemstream(name,stream,once) - if once and data[name] then - if trace then - report("not overloading %a",name) -- - end - return data[name] - end - local memstream, identifier = epdf.openMemStream(stream,#stream,name) + local kind = figures.guessfromstring(stream) + local identifier = false + if kind == "pdf" then + identifier = pdfe.new(stream,#stream,name) if not identifier then - report("no valid stream %a",name) - identifier = "invalid-memstream" + report("no valid pdf stream %a",name) elseif trace then report("setting %a with identifier %a",name,identifier) end - data [name] = identifier - opened[name] = memstream - return identifier + else + identifier = "m_k_i_v_memstream_" .. name .. "." .. kind + io.savedata(identifier,stream) end + if not identifier then + identifier = "invalid-memstream" + end + data[name] = identifier + return identifier +end - resolvers.setmemstream = setmemstream +resolvers.setmemstream = setmemstream - function resolvers.finders.memstream(specification) - local name = specification.path - local identifier = data[name] - if identifier then - if trace then - report("reusing %a with identifier %a",name,identifier) - end - return identifier - end - local stream = io.loaddata(name) - if not stream or stream == "" then - if trace then - report("no valid file %a",name) - end - return resolvers.finders.notfound() - else - return setmemstream(name,stream) +function resolvers.finders.memstream(specification) + local name = specification.path + local identifier = data[name] + if identifier then + if trace then + report("reusing %a with identifier %a",name,identifier) end + return identifier + end + local stream = io.loaddata(name) + if stream and stream ~= "" then + return setmemstream(name,stream) end + if trace then + report("no valid memstream %a",name) + end + return resolvers.finders.notfound() +end - local flush = { } +local flush = { } - function resolvers.resetmemstream(name,afterpage) - if afterpage then - flush[#flush+1] = name - else - opened[name] = nil - end +function resolvers.resetmemstream(name,afterpage) + if afterpage then + flush[#flush+1] = name end +end - luatex.registerpageactions(function() - if #flush > 0 then - for i=1,#flush do - opened[flush[i]] = nil -- we keep of course data[name] because of reuse +luatex.registerpageactions(function() + if #flush > 0 then + for i=1,#flush do + local identifier = data[name] + if identifier then + os.remove(identifier) + data[name] = nil end - flush = { } end - end) - -end + flush = { } + end +end) figures.identifiers.list[#figures.identifiers.list+1] = function(specification) local name = specification.request.name diff --git a/tex/context/base/mkiv/grph-rul.lua b/tex/context/base/mkiv/grph-rul.lua index 08edade1f..194823161 100644 --- a/tex/context/base/mkiv/grph-rul.lua +++ b/tex/context/base/mkiv/grph-rul.lua @@ -7,6 +7,7 @@ if not modules then modules = { } end modules ['grph-rul'] = { } local tonumber, next, type = tonumber, next, type +local concat = table.concat local attributes = attributes local nodes = nodes @@ -15,6 +16,7 @@ local context = context local bpfactor = number.dimenfactors.bp local nuts = nodes.nuts +local nodepool = nuts.pool local userrule = nuts.rules.userrule local outlinerule = nuts.pool.outlinerule local ruleactions = nuts.rules.ruleactions @@ -24,6 +26,8 @@ local setattr = nuts.setattr local tonode = nuts.tonode local getattribute = tex.getattribute +local getwhd = nuts.getwhd +local setwhd = nuts.setwhd local lefttoright_code = nodes.dirvalues.lefttoright @@ -143,6 +147,184 @@ end do + -- This is the old oval method that we keep it for compatibility reasons. Of + -- course one can use mp instead. It could be improved but at the cost of more + -- code than I'm willing to add for something hardly used. + + local function round(p,kind) + local method = tonumber(p.corner) or 0 + if method < 0 or method > 27 then + method = 0 + end + local width = p.width or 0 + local height = p.height or 0 + local depth = p.depth or 0 + local total = height + depth + local radius = p.radius or 655360 + local line = p.line or 65536 + local how = (method > 8 or kind ~= "fill") and "S" or "f" + local half = line / 2 + local xmin = half * bpfactor + local xmax = ( width - half) * bpfactor + local ymax = ( height - half) * bpfactor + local ymin = (-depth + half) * bpfactor + local full = ( radius + half) + local xxmin = full * bpfactor + local xxmax = ( width - full) * bpfactor + local yymax = ( height - full) * bpfactor + local yymin = (-depth + full) * bpfactor + line = line * bpfactor + if xxmin <= xxmax and yymin <= yymax then + local list = nil + if method == 0 then + list = { + "q", line, "w", xxmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", xxmin, ymax, "l", xmin, ymax, + xmin, yymax, "y", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", "h", how, "Q", + } + elseif method == 1 then + list = { + "q", line, "w", xxmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, ymax, "l", xmin, ymax, "l", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", + "h", how, "Q", + } + elseif method == 2 then + list = { + "q", line, "w", xxmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xxmin, ymax, + "l", xmin, ymax, xmin, yymax, "y", xmin, yymin, "l", xmin, ymin, xxmin, ymin, + "y", "h", how, "Q", + } + elseif method == 3 then + list = { + "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, yymax, "l", xmax, ymax, + xxmax, ymax, "y", xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, + "l", "h", how, "Q", + } + + elseif method == 4 then + list = { + "q", line, "w", xmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", xmin, ymax, "l", xmin, ymin, "l", + "h", how, "Q", + } + elseif method == 5 then + list = { + "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, yymax, "l", xmax, ymax, + xxmax, ymax, "y", xmin, ymax, "l", xmin, ymin, "l", "h", how, "Q", + } + elseif method == 6 then + list = { + "q", line, "w", xmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, ymax, "l", xmin, ymax, "l", xmin, ymin, "l", "h", how, "Q", + } + elseif method == 7 then + list = { + "q", line, "w", xxmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xmin, ymax, + "l", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", "h", how, "Q", + } + elseif method == 8 then + list = { + "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xxmin, ymax, + "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, "l", "h", how, "Q", + } + elseif method == 9 then + list = { + "q", line, "w", xmin, ymax, "m", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", + xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", xmax, ymax, "l", how, "Q", + } + elseif method == 10 then + list = { + "q", line, "w", xmax, ymax, "m", xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", + xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", xmax, ymin, "l", how, "Q", + } + elseif method == 11 then + list = { + "q", line, "w", xmax, ymin, "m", xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", + xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, "l", how, "Q", + } + elseif method == 12 then + list = { + "q", line, "w", xmin, ymax, "m", xxmax, ymax, "l", xmax, ymax, xmax, yymax, "y", + xmax, yymin, "l", xmax, ymin, xxmax, ymin, "y", xmin, ymin, "l", how, "Q", + } + elseif method == 13 then + list = { + "q", line, "w", xmin, ymax, "m", xxmax, ymax, "l", xmax, ymax, xmax, yymax, "y", + xmax, ymin, "l", how, "Q", + } + elseif method == 14 then + list = { + "q", line, "w", xmax, ymax, "m", xmax, yymin, "l", xmax, ymin, xxmax, ymin, "y", + xmin, ymin, "l", how, "Q", + } + elseif method == 15 then + list = { + "q", line, "w", xmax, ymin, "m", xxmin, ymin, "l", xmin, ymin, xmin, yymin, "y", + xmin, ymax, "l", how, "Q", + } + elseif method == 16 then + list = { + "q", line, "w", xmin, ymin, "m", xmin, yymax, "l", xmin, ymax, xxmin, ymax, "y", + xmax, ymax, "l", how, "Q", + } + elseif method == 17 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", how, "Q", + } + elseif method == 18 then + list = { + "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", how, "Q", + } + elseif method == 19 then + list = { + "q", line, "w", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", how, "Q", + } + elseif method == 20 then + list = { + "q", line, "w", xmin, yymax, "m", xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 21 then + list = { + "q", line, "w", xxmax, ymax, m, xmax, ymax, xmax, yymax, "y", xmin, yymax, "m", + xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 22 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmax, yymin, "m", + xmax, ymin, xxmax, ymin, "y", how, "Q", + } + elseif method == 23 then + list = { + "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", xxmin, ymin, "m", + xmin, ymin, xmin, yymin, "y", how, "Q", + } + elseif method == 24 then + list = { + "q", line, "w", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", xmin, yymax, "m", + xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 25 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmax, yymin, "m", + xmax, ymin, xxmax, ymin, "y", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", + xmin, yymax, "m", xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 26 then + list = { + "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", xmin, yymax, "m", + xmin, ymax, xxmin, ymax, "y", how, "Q", + } + + elseif method == 27 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xxmin, ymin, "m", + xmin, ymin, xmin, yymin, "y", how, "Q", + } + end + pdfprint("direct",concat(list," ")) + end + end + -- maybe %.6F local f_rectangle = formatters["%.6F w %.6F %.6F %.6F %.6F re %s"] @@ -172,30 +354,32 @@ h %s]] end) ruleactions.fill = function(p,h,v,i,n) - local l = (p.line or 65536)*bpfactor - local r = p and (p.radius or 0)*bpfactor or 0 - local w = h * bpfactor - local h = v * bpfactor - local m = nil - local t = i == "fill" and "f" or "s" - local o = l / 2 - if r > 0 then - w = w - o - h = h - o - m = f_radtangle(l, r,o, w-r,o, w,o,w,r, w,h-r, w,h,w-r,h, r,h, o,h,o,h-r, o,r, o,o,r,o, t) + if p.corner then + return round(p,i) else - w = w - l - h = h - l - m = f_rectangle(l,o,o,w,h,t) + local l = (p.line or 65536)*bpfactor + local r = p and (p.radius or 0)*bpfactor or 0 + local w = h * bpfactor + local h = v * bpfactor + local m = nil + local t = i == "fill" and "f" or "s" + local o = l / 2 + if r > 0 then + w = w - o + h = h - o + m = f_radtangle(l, r,o, w-r,o, w,o,w,r, w,h-r, w,h,w-r,h, r,h, o,h,o,h-r, o,r, o,o,r,o, t) + else + w = w - l + h = h - l + m = f_rectangle(l,o,o,w,h,t) + end + pdfprint("direct",m) end - pdfprint("direct",m) end ruleactions.draw = ruleactions.fill ruleactions.stroke = ruleactions.fill - local getwhd = nodes.nuts.getwhd - ruleactions.box = function(p,h,v,i,n) local w, h, d = getwhd(n) local line = p.line or 65536 @@ -231,22 +415,17 @@ interfaces.implement { { "type", "string" }, { "data", "string" }, { "name", "string" }, + { "radius", "dimension" }, + { "corner", "string" }, } } , actions = function(t) - -- no nuts ! local rule = userrule(t) - local ma = getattribute(a_colormodel) or 1 - local ca = getattribute(a_color) - local ta = getattribute(a_transparency) - setattrlist(rule,true) if t.type == "mp" then - t.ma = ma - t.ca = ca - t.ta = ta + t.ma = getattribute(a_colormodel) or 1 + t.ca = getattribute(a_color) + t.ta = getattribute(a_transparency) else - setattr(rule,a_colormodel,ma) - setattr(rule,a_color,ca) - setattr(rule,a_transparency,ta) + setattrlist(rule,true) end context(tonode(rule)) -- will become context.nodes.flush end @@ -306,5 +485,3 @@ interfaces.implement { context(tonode(rule)) end } - - diff --git a/tex/context/base/mkiv/lpdf-eng.lua b/tex/context/base/mkiv/lpdf-eng.lua new file mode 100644 index 000000000..6f2e75c46 --- /dev/null +++ b/tex/context/base/mkiv/lpdf-eng.lua @@ -0,0 +1,106 @@ +if not modules then modules = { } end modules ['lpdf-eng'] = { + 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" +} + +if CONTEXTLMTXMODE > 0 then + return +end + +-- Here we plug in the regular luatex image handler. The low level module itself +-- is hidden from the user. + +local codeinjections = backends.pdf.codeinjections +local imgnew = img.new + +function codeinjections.newimage(specification) + if type(specification) == "table" then + specification.kind = nil + end + return imgnew(specification) +end + +codeinjections.copyimage = img.copy +codeinjections.scanimage = img.scan +codeinjections.embedimage = img.immediatewrite +codeinjections.wrapimage = img.node + +-- We cannot nil the img table because the backend code explicitly accesses the +-- new field when dealing with virtual characters. I should patch luatex for that +-- and maybe I will. So no: +-- +-- img = nil +-- +-- We keep the low level img.new function but make the rest kind of unseen. At some +-- point the other ones will be gone and one has to use the images.* wrappers. + +local unpack = unpack +local sortedkeys = table.sortedkeys +local context = context + +img = table.setmetatableindex ( + { + new = images.create, + }, + { + -- new = images.create, + scan = images.scan, + copy = images.copy, + node = images.wrap, + write = function(specification) context(images.wrap(specification)) end, + immediatewrite = images.embed, + immediatewriteobject = function() end, -- not upported, experimental anyway + boxes = function() return sortedkeys(images.sizes) end, + fields = function() return images.keys end, + types = function() return { unpack(images.types,0,#images.types) } end, + } +) + +-- + +do + + local function prepare() + if not environment.initex then + -- install new functions in pdf namespace + updaters.apply("backend.update.pdf") + -- install new functions in lpdf namespace + updaters.apply("backend.update.lpdf") + -- adapt existing shortcuts to lpdf namespace + updaters.apply("backend.update.tex") + -- adapt existing shortcuts to tex namespace + updaters.apply("backend.update") + -- + end + end + + local function outputfilename() + if not filename then + filename = addsuffix(tex.jobname,"pdf") + end + return filename + end + + function lpdf.flushers() + return { } + end + + function lpdf.actions() + return { + convert = tex.shipout, + outputfilename = outputfilename, + prepare = prepare, + } + end + + drivers.install { + name = "pdf", + flushers = lpdf.flushers(), + actions = lpdf.actions(), + } + +end + diff --git a/tex/context/base/mkiv/lpdf-epa.lua b/tex/context/base/mkiv/lpdf-epa.lua index 01c4e237f..7ec331554 100644 --- a/tex/context/base/mkiv/lpdf-epa.lua +++ b/tex/context/base/mkiv/lpdf-epa.lua @@ -31,7 +31,6 @@ local report_field = logs.reporter("backend","field") local report_outline = logs.reporter("backend","outline") local lpdf = lpdf -local epdf = epdf local backends = backends local context = context diff --git a/tex/context/base/mkiv/lpdf-epd.lua b/tex/context/base/mkiv/lpdf-epd.lua deleted file mode 100644 index 86aa6f294..000000000 --- a/tex/context/base/mkiv/lpdf-epd.lua +++ /dev/null @@ -1,1313 +0,0 @@ -if not modules then modules = { } end modules ['lpdf-epd'] = { - version = 1.001, - comment = "companion to lpdf-epa.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- This is an experimental layer around the epdf library. The reason for this layer is that --- I want to be independent of the library (which implements a selection of what a file --- provides) and also because I want an interface closer to Lua's table model while the API --- stays close to the original xpdf library. Of course, after prototyping a solution, we can --- optimize it using the low level epdf accessors. However, not all are accessible (this will --- be fixed). --- --- It will be handy when we have a __length and __next that can trigger the resolve till then --- we will provide .n as #; maybe in Lua 5.3 or later. --- --- As there can be references to the parent we cannot expand a tree. I played with some --- expansion variants but it does not pay off; adding extra checks is not worth the trouble. --- --- The document stays open. In order to free memory one has to explicitly unload the loaded --- document. --- --- We have much more checking then needed in the prepare functions because occasionally --- we run into bugs in poppler or the epdf interface. It took us a while to realize that --- there was a long standing gc issue the on long runs with including many pages could --- crash the analyzer. --- --- Normally a value is fetched by key, as in foo.Title but as it can be in pdfdoc encoding --- a safer bet is foo("Title") which will return a decoded string (or the original if it --- already was unicode). - -local setmetatable, rawset, rawget, type, next = setmetatable, rawset, rawget, type, next -local tostring, tonumber, unpack = tostring, tonumber, unpack -local lower, match, char, byte, find = string.lower, string.match, string.char, string.byte, string.find -local abs = math.abs -local concat = table.concat -local toutf, toeight, utfchar = string.toutf, utf.toeight, utf.char -local setmetatableindex = table.setmetatableindex - -local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns -local P, C, S, R, Ct, Cc, V, Carg, Cs, Cf, Cg = lpeg.P, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cc, lpeg.V, lpeg.Carg, lpeg.Cs, lpeg.Cf, lpeg.Cg - -local epdf = epdf - lpdf = lpdf or { } -local lpdf = lpdf -local lpdf_epdf = { } -lpdf.epdf = lpdf_epdf - --- local getDict, getArray, getReal, getNum, getString, getBool, getName, getRef, getRefNum --- local getType, getTypeName --- local dictGetLength, dictGetVal, dictGetValNF, dictGetKey --- local arrayGetLength, arrayGetNF, arrayGet --- local streamReset, streamGetDict, streamGetChar - --- We use as little as possible and also not an object interface. After all, we --- don't know how the library (and its api) evolves so we better can be prepared --- for wrappers. - -local registry = debug.getregistry() - -local object = registry["epdf.Object"] -local dictionary = registry["epdf.Dict"] -local array = registry["epdf.Array"] -local xref = registry["epdf.XRef"] -local catalog = registry["epdf.Catalog"] -local pdfdoc = registry["epdf.PDFDoc"] - -if not (object and dictionary and array and xref and catalog and pdfdoc) then - logs.report("fatal error","invalid pdf inclusion library (%s)",1) - os.exit() -end - -local openPDF = epdf.open - -local getMajorVersion = pdfdoc.getPDFMajorVersion -local getMinorVersion = pdfdoc.getPDFMinorVersion -local getXRef = pdfdoc.getXRef -local getRawCatalog = pdfdoc.getCatalog - -if not (openPDF and getMajorVersion and getMinorVersion and getXRef and getRawCatalog) then - logs.report("fatal error","invalid pdf inclusion library (%s)",2) - os.exit() -end - -local getDict = object.getDict -local getArray = object.getArray -local getReal = object.getReal -local getInt = object.getInt -local getString = object.getString -local getBool = object.getBool -local getName = object.getName -local getRef = object.getRef -local getRefNum = object.getRefNum - -local getType = object.getType - -if not (getDict and getArray and getReal and getInt and getString and getBool and getName and getRef and getRefNum and getType) then - logs.report("fatal error","invalid pdf inclusion library (%s)",3) - os.exit() -end - -local streamReset = object.streamReset -local streamGetDict = object.streamGetDict -local streamGetChar = object.streamGetChar -local streamGetAll = object.streamGetAll - -if not (streamReset and streamGetDict and streamGetChar) then - logs.report("fatal error","invalid pdf inclusion library (%s)",3) - os.exit() -end - -local dictGetLength = dictionary.getLength -local dictGetVal = dictionary.getVal -local dictGetValNF = dictionary.getValNF -local dictGetKey = dictionary.getKey - -if not (dictGetLength and dictGetVal and dictGetValNF and dictGetKey) then - logs.report("fatal error","invalid pdf inclusion library (%s)",4) - os.exit() -end - -local arrayGetLength = array.getLength -local arrayGetNF = array.getNF -local arrayGet = array.get - -if not (arrayGetLength and arrayGetNF and arrayGet) then - logs.report("fatal error","invalid pdf inclusion library (%s)",5) - os.exit() -end - --- these are kind of weird as they can't be accessed by (root) object - -local getNumPages = catalog.getNumPages -local getPageRef = catalog.getPageRef - -local fetch = xref.fetch -local getCatalog = xref.getCatalog -local getDocInfo = xref.getDocInfo - -if not (getNumPages and getPageRef and fetch and getCatalog and getDocInfo) then - logs.report("fatal error","invalid pdf inclusion library (%s)",6) - os.exit() -end - --- we're done with library shortcuts - -local typenames = { [0] = - "boolean", - "integer", - "real", - "string", - "name", - "null", - "array", - "dictionary", - "stream", - "ref", - "cmd", - "error", - "eof", - "none", - "integer64", -} - -local typenumbers = table.swapped(typenames) - -local null_object_code = typenumbers.null -local ref_object_code = typenumbers.ref - -local report_epdf = logs.reporter("epdf") - -local function fatal_error(...) - report_epdf(...) - report_epdf("aborting job in order to avoid crash") - os.exit() -end - --- epdf is the built-in library - -function epdf.type(o) - local t = lower(match(tostring(o),"[^ :]+")) - return t or "?" -end - -local checked_access - --- dictionaries (can be optimized: ... resolve and redefine when all locals set) - -local frompdfdoc = lpdf.frompdfdoc - -local get_flagged - -if lpdf.dictionary then - - local pdfdictionary = lpdf.dictionary - local pdfarray = lpdf.array - local pdfconstant = lpdf.constant - local pdfstring = lpdf.string - local pdfunicode = lpdf.unicode - - get_flagged = function(t,f,k) - local tk = t[k] -- triggers resolve - local fk = f[k] - if not fk then - return tk - elseif fk == "name" then - return pdfconstant(tk) - elseif fk == "array" then - return pdfarray(tk) - elseif fk == "dictionary" then - return pdfarray(tk) - elseif fk == "rawtext" then - return pdfstring(tk) - elseif fk == "unicode" then - return pdfunicode(tk) - else - return tk - end - end - -else - - get_flagged = function(t,f,k) - local tk = t[k] -- triggers resolve - local fk = f[k] - if not fk then - return tk - elseif fk == "rawtext" then - return frompdfdoc(tk) - else - return tk - end - end - -end - -local function prepare(document,d,t,n,k,mt,flags) - for i=1,n do - local v = dictGetVal(d,i) - if v then - local r = dictGetValNF(d,i) - local kind = getType(v) - if kind == null_object_code then - -- ignore - elseif kind then - local key = dictGetKey(d,i) - if r and getType(r) == ref_object_code then - local objnum = getRefNum(r) - local cached = document.__cache__[objnum] - if not cached then - cached = checked_access[kind](v,document,objnum,mt) - if cached then - document.__cache__[objnum] = cached - document.__xrefs__[cached] = objnum - end - end - t[key] = cached - else - local v, flag = checked_access[kind](v,document) - t[key] = v - if flag and flags then - flags[key] = flag -- flags - end - end - else - report_epdf("warning: nil value for key %a in dictionary",key) - end - else - fatal_error("error: invalid value at index %a in dictionary of %a",i,document.filename) - end - end - if mt then - setmetatable(t,mt) - else - getmetatable(t).__index = nil - end - return t[k] -end - --- local function prepare(document,d,t,n,k,mt,flags) --- for i=1,n do --- local v = dictGetValNF(d,i) --- if v then --- local key = dictGetKey(d,i) --- local kind = getType(v) --- if kind == ref_object_code then --- local objnum = getRefNum(v) --- local cached = document.__cache__[objnum] --- if not cached then --- local v = dictGetVal(d,i) --- local kind = getType(v) --- cached = checked_access[kind](v,document,objnum,mt) --- if cached then --- document.__cache__[objnum] = cached --- document.__xrefs__[cached] = objnum --- end --- end --- t[key] = cached --- else --- local v, flag = checked_access[kind](v,document) --- t[key] = v --- if flag and flags then --- flags[key] = flag -- flags --- end --- end --- end --- end --- if mt then --- setmetatable(t,mt) --- else --- getmetatable(t).__index = nil --- end --- return t[k] --- end - -local function some_dictionary(d,document) - local n = d and dictGetLength(d) or 0 - if n > 0 then - local t = { } - local f = { } - setmetatable(t, { - __index = function(t,k) - return prepare(document,d,t,n,k,_,_,f) - end, - __call = function(t,k) - return get_flagged(t,f,k) - end, - -- __kind = function(k) - -- return f[k] or type(t[k]) - -- end, - } ) - return t, "dictionary" - end -end - -local function get_dictionary(object,document,r,mt) - local d = getDict(object) - local n = d and dictGetLength(d) or 0 - if n > 0 then - local t = { } - local f = { } - setmetatable(t, { - __index = function(t,k) - return prepare(document,d,t,n,k,mt,f) - end, - __call = function(t,k) - return get_flagged(t,f,k) - end, - -- __kind = function(k) - -- return f[k] or type(t[k]) - -- end, - } ) - return t, "dictionary" - end -end - --- arrays (can be optimized: ... resolve and redefine when all locals set) - -local function prepare(document,a,t,n,k) - for i=1,n do - local v = arrayGet(a,i) - if v then - local kind = getType(v) - if kind == null_object_code then - -- ignore - elseif kind then - local r = arrayGetNF(a,i) - if r and getType(r) == ref_object_code then - local objnum = getRefNum(r) - local cached = document.__cache__[objnum] - if not cached then - cached = checked_access[kind](v,document,objnum) - document.__cache__[objnum] = cached - document.__xrefs__[cached] = objnum - end - t[i] = cached - else - t[i] = checked_access[kind](v,document) - end - else - report_epdf("warning: nil value for index %a in array",i) - end - else - fatal_error("error: invalid value at index %a in array of %a",i,document.filename) - end - end - local m = getmetatable(t) - if m then - m.__index = nil - m.__len = nil - end - if k then - return t[k] - end -end - --- local function prepare(document,a,t,n,k) --- for i=1,n do --- local v = arrayGetNF(a,i) --- if v then --- local kind = getType(v) --- if kind == ref_object_code then --- local objnum = getRefNum(v) --- local cached = document.__cache__[objnum] --- if not cached then --- local v = arrayGet(a,i) --- local kind = getType(v) --- cached = checked_access[kind](v,document,objnum) --- document.__cache__[objnum] = cached --- document.__xrefs__[cached] = objnum --- end --- t[i] = cached --- else --- t[i] = checked_access[kind](v,document) --- end --- end --- end --- local m = getmetatable(t) --- if m then --- m.__index = nil --- m.__len = nil --- end --- if k then --- return t[k] --- end --- end - -local function some_array(a,document) - local n = a and arrayGetLength(a) or 0 - if n > 0 then - local t = { n = n } - setmetatable(t, { - __index = function(t,k) - return prepare(document,a,t,n,k,_,_,f) - end, - __len = function(t) - prepare(document,a,t,n,_,_,f) - return n - end, - __call = function(t,k) - return get_flagged(t,f,k) - end, - -- __kind = function(k) - -- return f[k] or type(t[k]) - -- end, - } ) - return t, "array" - end -end - -local function get_array(object,document) - local a = getArray(object) - local n = a and arrayGetLength(a) or 0 - if n > 0 then - local t = { n = n } - local f = { } - setmetatable(t, { - __index = function(t,k) - return prepare(document,a,t,n,k,_,_,f) - end, - __len = function(t) - prepare(document,a,t,n,_,_,f) - return n - end, - __call = function(t,k) - return get_flagged(t,f,k) - end, - -- __kind = function(k) - -- return f[k] or type(t[k]) - -- end, - } ) - return t, "array" - end -end - --- todo: collect chunks - --- local function streamaccess(s,_,what) --- if not what or what == "all" or what == "*all" then --- local t, n = { }, 0 --- streamReset(s) --- while true do --- local c = streamGetChar(s) --- if c < 0 then --- break --- else --- n = n + 1 --- t[n] = char(c) --- end --- end --- return concat(t,"",1,n) --- end --- end - -local function getstream(s) - streamReset(s) - if streamGetAll then - return streamGetAll(s) - else - local t, b, n = { }, { }, 0 - while true do - local c = streamGetChar(s) - if c < 0 then - break - else - n = n + 1 - b[n] = c - end - if n == 2000 then - t[#t+1] = char(unpack(b,1,n)) - n = 1 - end - end - t[#t+1] = char(unpack(b,1,n)) - return concat(t) - end -end - -local function streamaccess(s,_,what) - if not what or what == "all" or what == "*all" then - return getstream(s) - end -end - -local function get_stream(d,document) - if d then - streamReset(d) - local s = some_dictionary(streamGetDict(d),document) - getmetatable(s).__call = function(...) return streamaccess(d,...) end - return s - end -end - --- We need to convert the string from utf16 although there is no way to --- check if we have a regular string starting with a bom. So, we have --- na dilemma here: a pdf doc encoded string can be invalid utf. - --- : implicit 0 appended if odd --- (byte encoded) : \( \) \\ escaped --- --- : utf16be --- --- \r \r \t \b \f \( \) \\ \NNN and \ : append next line --- --- the getString function gives back bytes so we don't need to worry about --- the hex aspect. - -local u_pattern = lpeg.patterns.utfbom_16_be * lpeg.patterns.utf16_to_utf8_be ------ b_pattern = lpeg.patterns.hextobytes - -local function get_string(v) - -- the toutf function only converts a utf16 string and leaves the original - -- untouched otherwise; one might want to apply lpdf.frompdfdoc to a - -- non-unicode string - local s = getString(v) - if not s or s == "" then - return "" - end - local u = lpegmatch(u_pattern,s) - if u then - return u, "unicode" - end - -- this is too tricky and fails on e.g. reload of url www.pragma-ade.com) - -- local b = lpegmatch(b_pattern,s) - -- if b then - -- return b, "rawtext" - -- end - return s, "rawtext" -end - -local function get_name(v) - return getName(v), "name" -end - -local function get_null() - return nil -end - --- we have dual access: by typenumber and by typename - -local function invalidaccess(k,document) - local fullname = type(document) == "table" and document.fullname - if fullname then - fatal_error("error, asking for key %a in checker of %a",k,fullname) - else - fatal_error("error, asking for key %a in checker",k) - end -end - -checked_access = setmetatableindex(function(t,k) - return function(v,document) - invalidaccess(k,document) - end -end) - -checked_access[typenumbers.boolean] = getBool -checked_access[typenumbers.integer] = getInt -checked_access[typenumbers.real] = getReal -checked_access[typenumbers.string] = get_string -- getString -checked_access[typenumbers.name] = get_name -checked_access[typenumbers.null] = get_null -checked_access[typenumbers.array] = get_array -- d,document,r -checked_access[typenumbers.dictionary] = get_dictionary -- d,document,r -checked_access[typenumbers.stream] = get_stream -checked_access[typenumbers.ref] = getRef - -for i=0,#typenames do - local checker = checked_access[i] - if not checker then - checker = function() - return function(v,document) - invalidaccess(i,document) - end - end - checked_access[i] = checker - end - checked_access[typenames[i]] = checker -end - -local function getnames(document,n,target) -- direct - if n then - local Names = n.Names - if Names then - if not target then - target = { } - end - for i=1,Names.n,2 do - target[Names[i]] = Names[i+1] - end - else - local Kids = n.Kids - if Kids then - for i=1,Kids.n do - target = getnames(document,Kids[i],target) - end - end - end - return target - end -end - -local function getkids(document,n,target) -- direct - if n then - local Kids = n.Kids - if Kids then - for i=1,Kids.n do - target = getkids(document,Kids[i],target) - end - elseif target then - target[#target+1] = n - else - target = { n } - end - return target - end -end - --- /OCProperties << --- /OCGs [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ] --- /D << --- /Order [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ] --- /ON [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ] --- /OFF [ ] --- >> --- >> - -local function getlayers(document) - local properties = document.Catalog.OCProperties - if properties then - local layers = properties.OCGs - if layers then - local t = { } - local n = layers.n - for i=1,n do - local layer = layers[i] - t[i] = layer.Name - end - t.n = n - return t - end - end -end - -local function getstructure(document) - -- this might become a tree - return document.Catalog.StructTreeRoot -end - --- This is the only messy helper. We can't access the root as any object (it seems) --- so we need a few low level acessors. It's anyway sort of simple enough to deal --- with but it won't win a beauty contest. - -local function getpages(document,Catalog) - local __data__ = document.__data__ - local __xrefs__ = document.__xrefs__ - local __cache__ = document.__cache__ - local __xref__ = document.__xref__ - -- - local rawcatalog = getRawCatalog(__data__) - local nofpages = getNumPages(rawcatalog) - -- - local majorversion = getMajorVersion(__data__) - local minorversion = getMinorVersion(__data__) - -- - local pages = { } - local metatable = { __index = Catalog.Pages } -- somewhat empty - -- - for pagenumber=1,nofpages do - local pagereference = getPageRef(rawcatalog,pagenumber).num - local pageobject = fetch(__xref__,pagereference,0) - local pagedata = get_dictionary(pageobject,document,pagereference,metatable) - if pagedata then - -- rawset(pagedata,"number",pagenumber) - pagedata.number = pagenumber - pagedata.object = pageobject - pages[pagenumber] = pagedata - __xrefs__[pagedata] = pagereference - __cache__[pagereference] = pagedata - else - report_epdf("missing pagedata at slot %i",i) - end - end - -- - pages.n = nofpages - -- - document.pages = pages - document.majorversion = majorversion - document.minorversion = minorversion - -- - return pages -end - -local function resolve(document,k) - local entry = nil - local Catalog = document.Catalog - local Names = Catalog.Names - if k == "pages" then - entry = getpages(document,Catalog) - elseif k == "destinations" then - entry = getnames(document,Names and Names.Dests) - elseif k == "javascripts" then - entry = getnames(document,Names and Names.JS) - elseif k == "widgets" then - entry = getnames(document,Names and Names.AcroForm) - elseif k == "embeddedfiles" then - entry = getnames(document,Names and Names.EmbeddedFiles) - elseif k == "layers" then - entry = getlayers(document) - elseif k == "structure" then - entry = getstructure(document) - end - document[k] = entry - return entry -end - -local loaded = { } - -function lpdf_epdf.load(filename) - local document = loaded[filename] - if not document then - statistics.starttiming(lpdf_epdf) - local __data__ = openPDF(filename) -- maybe resolvers.find_file - if __data__ then - local __xref__ = getXRef(__data__) - document = { - filename = filename, - __cache__ = { }, - __xrefs__ = { }, - __fonts__ = { }, - __data__ = __data__, - __xref__ = __xref__ - } - document.Catalog = some_dictionary(getDict(getCatalog(__xref__)),document) - document.Info = some_dictionary(getDict(getDocInfo(__xref__)),document) - setmetatableindex(document,resolve) - -- - document.nofpages = getNumPages(getRawCatalog(__data__)) - else - document = false - end - loaded[filename] = document - loaded[document] = document - statistics.stoptiming(lpdf_epdf) - -- print(statistics.elapsedtime(lpdf_epdf)) - end - return document or nil -end - -function lpdf_epdf.unload(filename) - local document = loaded[filename] - if document then - loaded[document] = nil - loaded[filename] = nil - end -end - --- for k, v in next, expand(t) do - -local function expand(t) - if type(t) == "table" then - local dummy = t.dummy - end - return t -end - --- for k, v in expanded(t) do - -local function expanded(t) - if type(t) == "table" then - local dummy = t.dummy - end - return next, t -end - -lpdf_epdf.expand = expand -lpdf_epdf.expanded = expanded - --- we could resolve the text stream in one pass if we directly handle the --- font but why should we complicate things - -local hexdigit = R("09","AF") -local numchar = ( P("\\") * ( (R("09")^3/tonumber) + C(1) ) ) + C(1) -local number = lpegpatterns.number / tonumber -local spaces = lpegpatterns.whitespace^1 -local optspaces = lpegpatterns.whitespace^0 -local keyword = P("/") * C(R("AZ","az","09")^1) -local operator = C((R("AZ","az")+P("'")+P('"'))^1) - -local grammar = P { "start", - start = (keyword + number + V("dictionary") + V("unicode") + V("string") + V("unicode")+ V("array") + spaces)^1, - -- keyvalue = (keyword * spaces * V("start") + spaces)^1, - keyvalue = optspaces * Cf(Ct("") * Cg(keyword * optspaces * V("start") * optspaces)^1,rawset), - array = P("[") * Ct(V("start")^1) * P("]"), - dictionary = P("<<") * V("keyvalue") * P(">>"), - unicode = P("<") * Ct(Cc("hex") * C((1-P(">"))^1)) * P(">"), - string = P("(") * Ct(Cc("dec") * C((V("string")+numchar)^1)) * P(")"), -- untested -} - -local operation = Ct(grammar^1 * operator) -local parser = Ct((operation + P(1))^1) - --- beginbfrange : --- [ ] --- beginbfchar : - -local fromsixteen = lpdf.fromsixteen -- maybe inline the lpeg ... but not worth it - -local function f_bfchar(t,a,b) - t[tonumber(a,16)] = fromsixteen(b) -end - -local function f_bfrange_1(t,a,b,c) - print("todo 1",a,b,c) - -- c is string - -- todo t[tonumber(a,16)] = fromsixteen(b) -end - -local function f_bfrange_2(t,a,b,c) - print("todo 2",a,b,c) - -- c is table - -- todo t[tonumber(a,16)] = fromsixteen(b) -end - -local optionals = spaces^0 -local hexstring = optionals * P("<") * C((1-P(">"))^1) * P(">") -local bfchar = Carg(1) * hexstring * hexstring / f_bfchar -local bfrange = Carg(1) * hexstring * hexstring * hexstring / f_bfrange_1 - + Carg(1) * hexstring * hexstring * optionals * P("[") * Ct(hexstring^1) * optionals * P("]") / f_bfrange_2 -local fromunicode = ( - P("beginbfchar" ) * bfchar ^1 * optionals * P("endbfchar" ) + - P("beginbfrange") * bfrange^1 * optionals * P("endbfrange") + - spaces + - P(1) -)^1 * Carg(1) - -local function analyzefonts(document,resources) -- unfinished, see mtx-pdf for better code - local fonts = document.__fonts__ - if resources then - local fontlist = resources.Font - if fontlist then - for id, data in expanded(fontlist) do - if not fonts[id] then - -- a quick hack ... I will look into it more detail if I find a real - -- -application for it - local tounicode = data.ToUnicode() - if tounicode then - tounicode = lpegmatch(fromunicode,tounicode,1,{}) - end - fonts[id] = { - tounicode = type(tounicode) == "table" and tounicode or { } - } - setmetatableindex(fonts[id],"self") - end - end - end - end - return fonts -end - -local more = 0 -local unic = nil -- cheaper than passing each time as Carg(1) - -local p_hex_to_utf = C(4) / function(s) -- needs checking ! - local now = tonumber(s,16) - if more > 0 then - now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong - more = 0 - return unic[now] or utfchar(now) - elseif now >= 0xD800 and now <= 0xDBFF then - more = now - -- return "" - else - return unic[now] or utfchar(now) - end -end - -local p_dec_to_utf = C(1) / function(s) -- needs checking ! - local now = byte(s) - return unic[now] or utfchar(now) -end - -local p_hex_to_utf = P(true) / function() more = 0 end * Cs(p_hex_to_utf^1) -local p_dec_to_utf = P(true) / function() more = 0 end * Cs(p_dec_to_utf^1) - -function lpdf_epdf.getpagecontent(document,pagenumber) - - local page = document.pages[pagenumber] - - if not page then - return - end - - local fonts = analyzefonts(document,page.Resources) - - local content = page.Contents() or "" - local list = lpegmatch(parser,content) - local font = nil - -- local unic = nil - - for i=1,#list do - local entry = list[i] - local size = #entry - local operator = entry[size] - if operator == "Tf" then - font = fonts[entry[1]] - unic = font.tounicode - elseif operator == "TJ" then -- { array, TJ } - local list = entry[1] - for i=1,#list do - local li = list[i] - if type(li) == "table" then - if li[1] == "hex" then - list[i] = lpegmatch(p_hex_to_utf,li[2]) - else - list[i] = lpegmatch(p_dec_to_utf,li[2]) - end - else - -- kern - end - end - elseif operator == "Tj" or operator == "'" or operator == '"' then -- { string, Tj } { string, ' } { n, m, string, " } - local list = entry[size-1] - if list[1] == "hex" then - list[2] = lpegmatch(p_hex_to_utf,li[2]) - else - list[2] = lpegmatch(p_dec_to_utf,li[2]) - end - end - end - - unic = nil -- can be collected - - return list - -end - --- This is also an experiment. When I really need it I can improve it, for instance --- with proper position calculating. It might be usefull for some search or so. - -local softhyphen = utfchar(0xAD) .. "$" -local linefactor = 1.3 - -function lpdf_epdf.contenttotext(document,list) -- maybe signal fonts - local last_y = 0 - local last_f = 0 - local text = { } - local last = 0 - - for i=1,#list do - local entry = list[i] - local size = #entry - local operator = entry[size] - if operator == "Tf" then - last_f = entry[2] - elseif operator == "TJ" then - local list = entry[1] - for i=1,#list do - local li = list[i] - if type(li) == "string" then - last = last + 1 - text[last] = li - elseif li < -50 then - last = last + 1 - text[last] = " " - end - end - line = concat(list) - elseif operator == "Tj" then - last = last + 1 - text[last] = entry[size-1] - elseif operator == "cm" or operator == "Tm" then - local ty = entry[6] - local dy = abs(last_y - ty) - if dy > linefactor*last_f then - if last > 0 then - if find(text[last],softhyphen,1,true) then - -- ignore - else - last = last + 1 - text[last] = "\n" - end - end - end - last_y = ty - end - end - - return concat(text) -end - -function lpdf_epdf.getstructure(document,list) -- just a test - local depth = 0 - for i=1,#list do - local entry = list[i] - local size = #entry - local operator = entry[size] - if operator == "BDC" then - report_epdf("%w%s : %s",depth,entry[1] or "?",entry[2].MCID or "?") - depth = depth + 1 - elseif operator == "EMC" then - depth = depth - 1 - elseif operator == "TJ" then - local list = entry[1] - for i=1,#list do - local li = list[i] - if type(li) == "string" then - report_epdf("%w > %s",depth,li) - elseif li < -50 then - report_epdf("%w >",depth,li) - end - end - elseif operator == "Tj" then - report_epdf("%w > %s",depth,entry[size-1]) - end - end -end - --- document.Catalog.StructTreeRoot.ParentTree.Nums[2][1].A.P[1]) - --- helpers - --- function lpdf_epdf.getdestinationpage(document,name) --- local destination = document.__data__:findDest(name) --- return destination and destination.number --- end - --- This is experimental code that we need for testing the transition from --- poppler to a new lightweight library. Don't rely on this code to remain --- as it is now. Interesting is that performance of this variant is the same --- as the natural page includer. - -if img then do - - local copydictionary = nil - local copyarray = nil - - local ref_object_code = typenumbers.ref - local boolean_object_code = typenumbers.boolean - local integer_object_code = typenumbers.integer - local real_object_code = typenumbers.real - local string_object_code = typenumbers.string - local name_object_code = typenumbers.name - local null_object_code = typenumbers.null - local array_object_code = typenumbers.array - local dictionary_object_code = typenumbers.dictionary - local stream_object_code = typenumbers.stream - local cmd_object_code = typenumbers.cmd - - local pdfreserveobject = lpdf.reserveobject - local pdfflushobject = lpdf.flushobject - local pdfflushstreamobject = lpdf.flushstreamobject - local pdfreference = lpdf.reference - local pdfconstant = lpdf.constant - local pdfarray = lpdf.array - local pdfdictionary = lpdf.dictionary - local pdfunicode = lpdf.unicode - local pdfstring = lpdf.string - local pdfnull = lpdf.null - - local report = logs.reporter("backend","xobjects") - - local factor = 65536 / (7200/7227) -- 1/number.dimenfactors.bp - - local createimage = images.create - - local function scaledbbox(b) - return { b[1]*factor, b[2]*factor, b[3]*factor, b[4]*factor } - end - - local function copyobject(xref,copied,kind,r,v) - if kind == null_object_code then - return pdfnull() - elseif r and getType(r) == ref_object_code then - local objnum = getRefNum(r) - local r = copied[objnum] - if r then - -- report("%s object %i is reused",kind,objnum) - else - local o - r = pdfreserveobject() - copied[objnum] = r - if kind == array_object_code then - local a = copyarray(xref,copied,fetch(xref,objnum,0)) - pdfflushobject(r,tostring(a)) - elseif kind == dictionary_object_code then - local d = copydictionary(xref,copied,fetch(xref,objnum,0)) - pdfflushobject(r,tostring(d)) - elseif kind == stream_object_code then - local f = fetch(xref,objnum,0) - local d = copydictionary(xref,copied,false,streamGetDict(f)) - local s = getstream(f) - -- - d.Filter = nil - d.Length = nil - d.DecodeParms = nil - d.DL = nil - -- - pdfflushstreamobject(s,d,true,r) - else - report("reference not done: %s", kind) - end - end - return pdfreference(r) - elseif kind == array_object_code then - return copyarray(xref,copied,v) - elseif kind == dictionary_object_code then - return copydictionary(xref,copied,v) - elseif kind == integer_object_code then - return getInt(v) - elseif kind == real_object_code then - return getReal(v) - elseif kind == name_object_code then - return pdfconstant(getName(v)) - elseif kind == string_object_code then - local s = getString(v) - if not s or s == "" then - return "" - end - local u = lpegmatch(u_pattern,s) - if u then - return pdfunicode(s) - end - return pdfstring(s) - elseif kind == boolean_object_code then - return getBool(v) - elseif kind == stream_object_code then - -- hm ... - return getStream(v) - else - report("object not done: %s", kind) - end - end - - copyarray = function (xref,copied,object) - local a = getArray(object) - local n = a and arrayGetLength(a) or 0 - if n > 0 then - local target = pdfarray() - for i=1,n do - local v = arrayGet(a,i) - if v then - local kind = getType(v) - local r = arrayGetNF(a,i) - target[i] = copyobject(xref,copied,kind,r,v) - end - end - return target - end - end - - copydictionary = function (xref,copied,object,d) - local d = d or getDict(object) - local n = d and dictGetLength(d) or 0 - if n > 0 then - local target = pdfdictionary() - for i=1,n do - local v = dictGetVal(d,i) - if v then - local kind = getType(v) - local key = dictGetKey(d,i) - local r = dictGetValNF(d,i) - target[key] = copyobject(xref,copied,kind,r,v) - end - end - return target - end - end - - local function copy_resources(pdfdoc,xref,copied,pagedata) - local object = pagedata.object - if object then - local d = getDict(object) - local n = d and dictGetLength(d) or 0 - for i=1,n do - local k = dictGetKey(d,i) - if v and k == "Resources" then - local v = dictGetVal(d,i) - local kind = getType(v) - local r = dictGetValNF(d,i) - return copyobject(xref,copied,kind,r,v) - end - end - end - end - - local function openpdf(filename) - local pdfdoc = lpdf_epdf.load(filename) - if pdfdoc then - pdfdoc.__copied__ = pdfdoc.__copied__ or { } - pdfdoc.filename = filename - return pdfdoc - end - end - - local function closepdf(pdfdoc) - if pdfdoc then - lpdf_epdf.unload(pdfdoc.filename) - end - end - - local function querypdf(pdfdoc,pagenumber) - if pdfdoc then - if not pagenumber then - pagenumber = 1 - end - local root = pdfdoc.Catalog - local page = pdfdoc.pages[pagenumber] - if page then - local mediabox = page.MediaBox or { 0, 0, 0, 0 } - local cropbox = page.CropBox or mediabox - return { - filename = pdfdoc.filename, - pagenumber = pagenumber, - nofpages = pdfdoc.nofpages, - boundingbox = scaledbbox(cropbox), - cropbox = cropbox, - mediabox = mediabox, - bleedbox = page.BleedBox or cropbox, - trimbox = page.TrimBox or cropbox, - artbox = page.ArtBox or cropbox, - } - end - end - end - - local function copypage(pdfdoc,pagenumber,attributes) - if pdfdoc then - local root = pdfdoc.Catalog - local page = pdfdoc.pages[pagenumber or 1] - local pageinfo = querypdf(pdfdoc,pagenumber) - local contents = page.Contents - local xref = pdfdoc.__xref__ - local copied = pdfdoc.__copied__ - -- - local xobject = pdfdictionary { - Type = pdfconstant("XObject"), - Subtype = pdfconstant("Form"), - -- image attributes - FormType = 1, - BBox = pageinfo.cropbox, - -- Metadata = copy(xref,copied,root,"Metadata"), - -- Group = copy(xref,copied,page,"Group"), - -- LastModified = copy(xref,copied,page,"LastModified"), - -- Metadata = copy(xref,copied,page,"Metadata"), - -- PieceInfo = copy(xref,copied,page,"PieceInfo"), - Resources = copy_resources(pdfdoc,xref,copied,page), - -- SeparationInfo = copy(xref,copied,page,"SeparationInfo"), - } - if attributes then - for k, v in next, expand(attributes) do - page[k] = v -- maybe nested - end - end - return createimage { - bbox = pageinfo.boundingbox, - stream = contents(), - attr = xobject(), - } - end - end - - -- todo: codeinjections - - lpdf_epdf.image = { - open = openpdf, - close = closepdf, - query = querypdf, - copy = copypage, - } - -end end diff --git a/tex/context/base/mkiv/lpdf-img.lua b/tex/context/base/mkiv/lpdf-img.lua index ff89ad322..8f9c87ddd 100644 --- a/tex/context/base/mkiv/lpdf-img.lua +++ b/tex/context/base/mkiv/lpdf-img.lua @@ -1065,7 +1065,7 @@ do } end if trace then - report_png("%s: width %i, height %i, colordepth: %i, size: %i, palette %l, mask: %l, transparent %l, decode %l",filename,xsize,ysize,colordepth,#content,palette,mask,transparent,decode) + report_png("%s: width %i, height %i, colordepth %i, size %i, palette %l, mask %l, transparent %l, decode %l",filename,xsize,ysize,colordepth,#content,palette,mask,transparent,decode) end if specification.colorref then xobject.ColorSpace = pdfreference(specification.colorref) @@ -1180,6 +1180,8 @@ do } end + backends.pdf.codeinjections.bitmap = injectors.bitmap + end -- local function validcompression(data) diff --git a/tex/context/base/mkiv/lpdf-ini.lua b/tex/context/base/mkiv/lpdf-ini.lua index 050b1fa84..e39548ca6 100644 --- a/tex/context/base/mkiv/lpdf-ini.lua +++ b/tex/context/base/mkiv/lpdf-ini.lua @@ -205,8 +205,6 @@ do return pdfprint(...) end - pdfbackend.codeinjections.print = lpdf.print -- will go - -- local function transform(llx,lly,urx,ury,rx,sx,sy,ry) -- local x1 = llx * rx + lly * sy -- local y1 = llx * sx + lly * ry diff --git a/tex/context/base/mkiv/lpdf-lmt.lua b/tex/context/base/mkiv/lpdf-lmt.lua new file mode 100644 index 000000000..156d3486f --- /dev/null +++ b/tex/context/base/mkiv/lpdf-lmt.lua @@ -0,0 +1,2460 @@ +if not modules then modules = { } end modules ['lpdf-lmt'] = { + 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" +} + +-- The code below was originally in back-lpd.lua but it makes more sense in +-- this namespace. + +if CONTEXTLMTXMODE == 0 then + return +end + +-- If you consider this complex, watch: +-- +-- https://www.youtube.com/watch?v=6H-cAzfB2qo +-- +-- or in distractionmode: +-- +-- https://www.youtube.com/watch?v=TYuTE_1jvvE +-- https://www.youtube.com/watch?v=nnicGKX3lvM +-- +-- For the moment we have to support the built in backend as well as the alternative. So the +-- next interface is suboptimal and will change at some time. + +local type, next, unpack, tonumber = type, next, unpack, tonumber +local char, rep, find = string.char, string.rep, string.find +local formatters, splitupstring = string.formatters, string.splitup +local band, extract = bit32.band, bit32.extract +local concat, keys, sortedhash = table.concat, table.keys, table.sortedhash +local setmetatableindex = table.setmetatableindex + +local bpfactor = number.dimenfactors.bp + +local md5HEX = md5.HEX +local osuuid = os.uuid +local zlibcompress = flate.zip_compress or zlib.compress + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming + +local nuts = nodes.nuts +local tonut = nodes.tonut + +local getdata = nuts.getdata +local getsubtype = nuts.getsubtype +local getfield = nuts.getfield +local getwhd = nuts.getwhd +local flushlist = nuts.flush_list + +local pdfincludeimage = lpdf.includeimage +local pdfgetfontname = lpdf.getfontname +local pdfgetfontobjnumber = lpdf.getfontobjnumber + +local pdfreserveobject = lpdf.reserveobject +local pdfpagereference = lpdf.pagereference +local pdfflushobject = lpdf.flushobject +local pdfreference = lpdf.reference +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfconstant = lpdf.constant +local pdfflushstreamobject = lpdf.flushstreamobject +local pdfliteral = lpdf.literal -- not to be confused with a whatsit! + +local pdf_pages = pdfconstant("Pages") +local pdf_page = pdfconstant("Page") +local pdf_xobject = pdfconstant("XObject") +local pdf_form = pdfconstant("Form") + +local fonthashes = fonts.hashes +local characters = fonthashes.characters +local parameters = fonthashes.parameters +local properties = fonthashes.properties + +local report = logs.reporter("backend") + +-- used variables + +local pdf_h, pdf_v +local need_tm, need_tf, cur_tmrx, cur_factor, cur_f, cur_e +local need_width, need_mode, done_width, done_mode +local mode +local f_pdf_cur, f_pdf, fs_cur, fs, f_cur +local tj_delta, cw +local usedfonts, usedxforms, usedximages +local getxformname, getximagename +local boundingbox, shippingmode, objectnumber +local tmrx, tmry, tmsx, tmsy, tmtx, tmty +local cmrx, cmry, cmsx, cmsy, cmtx, cmty + +local function usefont(t,k) -- a bit redundant hash + local v = pdfgetfontname(k) + t[k] = v + return v +end + +local function reset_variables(specification) + pdf_h, pdf_v = 0, 0 + cmrx, cmry = 1, 1 + cmsx, cmsy = 0, 0 + cmtx, cmty = 0, 0 + tmrx, tmry = 1, 1 + tmsx, tmsy = 0, 0 + tmtx, tmty = 0, 0 + need_tm = false + need_tf = false + need_width = 0 + need_mode = 0 + done_width = false + done_mode = false + mode = "page" + shippingmode = specification.shippingmode + objectnumber = specification.objectnumber + cur_tmrx = 0 + f_cur = 0 + f_pdf_cur = 0 -- nullfont + f_pdf = 0 -- nullfont + fs_cur = 0 + fs = 0 + tj_delta = 0 + cur_factor = 0 + cur_f = false + cur_e = false + cw = 0 + usedfonts = setmetatableindex(usefont) + usedxforms = { } + usedximages = { } + boundingbox = specification.boundingbox + -- +-- if overload then +-- overload() +-- end +end + +-- buffer + +local buffer = { } +local b = 0 + +local function reset_buffer() + -- buffer = { } + b = 0 +end + +local function flush_buffer() + b = 0 +end + +local function show_buffer() + print(concat(buffer,"\n",1,b)) +end + +-- fonts + +local fontcharacters +local fontparameters +local fontproperties +local usedcharacters = setmetatableindex("table") +local pdfcharacters +local horizontalmode = true +----- widefontmode = true +local scalefactor = 1 +----- threshold = 655360 / (10 * 5) -- default is 5 +local threshold = 655360 * 50 / 100 +local tjfactor = 100 / 65536 + +lpdf.usedcharacters = usedcharacters + +local function updatefontstate(font) + fontcharacters = characters[font] + fontparameters = parameters[font] + fontproperties = properties[font] + local size = fontparameters.size -- or bad news + local designsize = fontparameters.designsize or size + pdfcharacters = usedcharacters[font] + horizontalmode = fontparameters.writingmode ~= "vertical" + -- widefontmode = fontproperties.encodingbytes == 2 + scalefactor = (designsize/size) * tjfactor + -- threshold = designsize / (10 * (fontproperties.threshold or 5)) + threshold = designsize * (fontproperties.threshold or 50) / 100 +end + +-- helpers + +local f_cm = formatters["%.6F %.6F %.6F %.6F %.6F %.6F cm"] +local f_tm = formatters["%.6F %.6F %.6F %.6F %.6F %.6F Tm"] + +directives.register("pdf.stripzeros",function() + f_cm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"] + f_tm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N Tm"] +end) + +local saved_text_pos_v = 0 +local saved_text_pos_h = 0 + +local function begin_text() + saved_text_pos_h = pdf_h + saved_text_pos_v = pdf_v + b = b + 1 ; buffer[b] = "BT" + need_tf = true + need_width = 0 + need_mode = 0 + mode = "text" +end + +local function end_text() + if done_width then + b = b + 1 ; buffer[b] = "0 w" + done_width = false + end + if done_mode then + b = b + 1 ; buffer[b] = "0 Tr" + done_mode = false + end + b = b + 1 ; buffer[b] = "ET" + pdf_h = saved_text_pos_h + pdf_v = saved_text_pos_v + mode = "page" +end + +local saved_chararray_pos_h +local saved_chararray_pos_v + +local saved_b = 0 + +local function begin_chararray() + saved_chararray_pos_h = pdf_h + saved_chararray_pos_v = pdf_v + cw = horizontalmode and saved_chararray_pos_h or - saved_chararray_pos_v + tj_delta = 0 + saved_b = b + b = b + 1 ; buffer[b] = " [" + mode = "chararray" +end + +local function end_chararray() + b = b + 1 ; buffer[b] = "] TJ" + buffer[saved_b] = concat(buffer,"",saved_b,b) + b = saved_b + pdf_h = saved_chararray_pos_h + pdf_v = saved_chararray_pos_v + mode = "text" +end + +local function begin_charmode() + -- b = b + 1 ; buffer[b] = widefontmode and "<" or "(" + b = b + 1 ; buffer[b] = "<" + mode = "char" +end + +local function end_charmode() + -- b = b + 1 ; buffer[b] = widefontmode and ">" or ")" + b = b + 1 ; buffer[b] = ">" + mode = "chararray" +end + +local function calc_pdfpos(h,v) + if mode == "page" then + cmtx = h - pdf_h + cmty = v - pdf_v + if h ~= pdf_h or v ~= pdf_v then + return true + end + elseif mode == "text" then + tmtx = h - saved_text_pos_h + tmty = v - saved_text_pos_v + if h ~= pdf_h or v ~= pdf_v then + return true + end + elseif horizontalmode then + tmty = v - saved_text_pos_v + tj_delta = cw - h -- + saved_chararray_pos_h + if tj_delta ~= 0 or v ~= pdf_v then + return true + end + else + tmtx = h - saved_text_pos_h + tj_delta = cw + v -- - saved_chararray_pos_v + if tj_delta ~= 0 or h ~= pdf_h then + return true + end + end + return false +end + +local function pdf_set_pos(h,v) + local move = calc_pdfpos(h,v) + if move then + b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor) + pdf_h = pdf_h + cmtx + pdf_v = pdf_v + cmty + end +end + +local function pdf_reset_pos() -- pdf_set_pos(0,0) + if mode == "page" then + cmtx = - pdf_h + cmty = - pdf_v + if pdf_h == 0 and pdf_v == 0 then + return + end + elseif mode == "text" then + tmtx = - saved_text_pos_h + tmty = - saved_text_pos_v + if pdf_h == 0 and pdf_v == 0 then + return + end + elseif horizontalmode then + tmty = - saved_text_pos_v + tj_delta = cw + if tj_delta == 0 and pdf_v == 0 then + return + end + else + tmtx = - saved_text_pos_h + tj_delta = cw + if tj_delta == 0 and pdf_h == 0 then + return + end + end + b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor) + pdf_h = pdf_h + cmtx + pdf_v = pdf_v + cmty +end + +local function pdf_set_pos_temp(h,v) + local move = calc_pdfpos(h,v) + if move then + b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor) + end +end + +local function pdf_end_string_nl() + if mode == "char" then + end_charmode() + end_chararray() + elseif mode == "chararray" then + end_chararray() + end +end + +local function pdf_goto_textmode() + if mode == "page" then + -- pdf_set_pos(0,0) + pdf_reset_pos() + begin_text() + elseif mode ~= "text" then + if mode == "char" then + end_charmode() + end_chararray() + else -- if mode == "chararray" then + end_chararray() + end + end +end + +local function pdf_goto_pagemode() + if mode ~= "page" then + if mode == "char" then + end_charmode() + end_chararray() + end_text() + elseif mode == "chararray" then + end_chararray() + end_text() + elseif mode == "text" then + end_text() + end + end +end + +local function pdf_goto_fontmode() + if mode == "char" then + end_charmode() + end_chararray() + end_text() + elseif mode == "chararray" then + end_chararray() + end_text() + elseif mode == "text" then + end_text() + end + -- pdf_set_pos(0,0) + pdf_reset_pos() + mode = "page" +end + +-- characters + +local flushcharacter do + + local round = math.round + + local function setup_fontparameters(font,factor,f,e) + local slant = (fontparameters.slantfactor or 0) / 1000 + local extend = (fontparameters.extendfactor or 1000) / 1000 + local squeeze = (fontparameters.squeezefactor or 1000) / 1000 + local expand = 1 + factor / 1000000 + local format = fontproperties.format + if e then + extend = extend * e + end + tmrx = expand * extend + tmsy = slant + tmry = squeeze + need_width = fontparameters.width + need_mode = fontparameters.mode + f_cur = font + f_pdf = usedfonts[font] -- cache + cur_factor = factor + cur_f = f + cur_e = e + tj_delta = 0 + fs = fontparameters.size * bpfactor + if f then + fs = fs * f + end + -- kind of special: + if format == "opentype" or format == "type1" then + fs = fs * 1000 / fontparameters.units -- can we avoid this ? + end + end + + local f_width = formatters["%.6F w"] + local f_mode = formatters["%i Tr"] + local f_font = formatters["/F%i %.6F Tf"] + local s_width = "0 w" + local s_mode = "0 Tr" + + directives.register("pdf.stripzeros",function() + f_width = formatters["%.6N w"] + f_font = formatters["/F%i %.6N Tf"] + end) + + local function set_font() + if need_width and need_width ~= 0 then + b = b + 1 ; buffer[b] = f_width(bpfactor * need_width / 1000) + done_width = true + elseif done_width then + b = b + 1 ; buffer[b] = s_width + done_width = false + end + if need_mode and need_mode ~= 0 then + b = b + 1 ; buffer[b] = f_mode(need_mode) + done_mode = true + elseif done_mode then + b = b + 1 ; buffer[b] = s_mode + done_mode = false + end + b = b + 1 ; buffer[b] = f_font(f_pdf,fs) + f_pdf_cur = f_pdf + fs_cur = fs + need_tf = false + need_tm = true + end + + local function set_textmatrix(h,v) + local move = calc_pdfpos(h,v) -- duplicate + if need_tm or move then + b = b + 1 ; buffer[b] = f_tm(tmrx, tmsx, tmsy, tmry, tmtx*bpfactor, tmty*bpfactor) + pdf_h = saved_text_pos_h + tmtx + pdf_v = saved_text_pos_v + tmty + need_tm = false + end + cur_tmrx = tmrx + end + + local f_skip = formatters["%.1f"] + -- local f_octal = formatters["\\%o"] + -- local f_char = formatters["%c"] + local f_hex = formatters["%04X"] + + local h_hex = setmetatableindex(function(t,k) + local v = f_hex(k) + t[k] = v + return v + end) + + flushcharacter = function(current,pos_h,pos_v,pos_r,font,char,data,factor,width,f,e) + if need_tf or font ~= f_cur or f_pdf ~= f_pdf_cur or fs ~= fs_cur or mode == "page" then + pdf_goto_textmode() + setup_fontparameters(font,factor,f,e) + set_font() + elseif cur_tmrx ~= tmrx or cur_factor ~= factor or cur_f ~= f or cur_e ~= e then + setup_fontparameters(font,factor,f,e) + need_tm = true + end + local move = calc_pdfpos(pos_h,pos_v) + if move or need_tm then + if not need_tm then + if horizontalmode then +-- print(tj_delta,threshold) + if (saved_text_pos_v + tmty) ~= pdf_v then + need_tm = true + elseif tj_delta >= threshold or tj_delta <= -threshold then + need_tm = true + end + else + if (saved_text_pos_h + tmtx) ~= pdf_h then + need_tm = true + elseif tj_delta >= threshold or tj_delta <= -threshold then + need_tm = true + end + end + end + if need_tm then + pdf_goto_textmode() + set_textmatrix(pos_h,pos_v) + begin_chararray() + move = calc_pdfpos(pos_h,pos_v) + end + if move then + -- local d = round(tj_delta * scalefactor) + -- if d ~= 0 then + local d = tj_delta * scalefactor + if d <= -0.5 or d >= 0.5 then + if mode == "char" then + end_charmode() + end + -- b = b + 1 ; buffer[b] = f_skip(d) + -- b = b + 1 ; buffer[b] = d + b = b + 1 ; buffer[b] = round(d) + end +-- if d <= -0.25 or d >= 0.25 then +-- if mode == "char" then +-- end_charmode() +-- end +-- b = b + 1 ; buffer[b] = f_skip(d) +-- end + cw = cw - tj_delta + end + end + if mode == "chararray" then + begin_charmode(font) + end + + local index = data.index or char + + b = b + 1 + -- if widefontmode then + -- buffer[b] = h_hex[data.index or 0] + buffer[b] = h_hex[index] + -- elseif char > 255 then + -- buffer[b] = f_octal(32) + -- elseif char <= 32 or char == 92 or char == 40 or char == 41 or char > 127 then -- \ ( ) + -- buffer[b] = f_octal(char) + -- else + -- buffer[b] = f_char(char) + -- end + + cw = cw + width + + -- pdfcharacters[fontcharacters[char].index or char] = true + pdfcharacters[index] = true + + end + +end + +-- literals + +local flushpdfliteral do + + local literalvalues = nodes.literalvalues + + local originliteral_code = literalvalues.origin + local pageliteral_code = literalvalues.page + local alwaysliteral_code = literalvalues.always + local rawliteral_code = literalvalues.raw + local textliteral_code = literalvalues.text + local fontliteral_code = literalvalues.font + + local nodeproperties = nodes.properties.data + + flushpdfliteral = function(current,pos_h,pos_v,mode,str) + if mode then + if not str then + mode, str = originliteral_code, mode + elseif mode == "mode" then + mode = literalvalues[str] + if mode == originliteral_code then + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + elseif mode == pageliteral_code then + pdf_goto_pagemode() + elseif mode == textliteral_code then + pdf_goto_textmode() + elseif mode == fontliteral_code then + pdf_goto_fontmode() + elseif mode == alwaysliteral_code then + pdf_end_string_nl() + need_tm = true + elseif mode == rawliteral_code then + pdf_end_string_nl() + end + return + else + mode = literalvalues[mode] + end + else + -- str, mode = getdata(current) + local p = nodeproperties[current] +if p then + str = p.data + mode = p.mode +else + str, mode = getdata(current) +end + end + if str and str ~= "" then + if mode == originliteral_code then + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + elseif mode == pageliteral_code then + pdf_goto_pagemode() + elseif mode == textliteral_code then + pdf_goto_textmode() + elseif mode == fontliteral_code then + pdf_goto_fontmode() + elseif mode == alwaysliteral_code then + pdf_end_string_nl() + need_tm = true + elseif mode == rawliteral_code then + pdf_end_string_nl() + else + print("check literal") + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + end + b = b + 1 ; buffer[b] = str + end + end + + updaters.register("backend.update.pdf",function() + function pdf.print(mode,str) + if str then + mode = literalvalues[mode] + else + mode, str = originliteral_code, mode + end + if str and str ~= "" then + if mode == originliteral_code then + pdf_goto_pagemode() + -- pdf_set_pos(pdf_h,pdf_v) + elseif mode == pageliteral_code then + pdf_goto_pagemode() + elseif mode == textliteral_code then + pdf_goto_textmode() + elseif mode == fontliteral_code then + pdf_goto_fontmode() + elseif mode == alwaysliteral_code then + pdf_end_string_nl() + need_tm = true + elseif mode == rawliteral_code then + pdf_end_string_nl() + else + pdf_goto_pagemode() + -- pdf_set_pos(pdf_h,pdf_v) + end + b = b + 1 ; buffer[b] = str + end + end + end) + +end + +-- grouping & orientation + +local flushpdfsave, flushpdfrestore, flushpdfsetmatrix do + + local matrices = { } + local positions = { } + local nofpositions = 0 + local nofmatrices = 0 + + local f_matrix = formatters["%s 0 0 cm"] + + flushpdfsave = function(current,pos_h,pos_v) + nofpositions = nofpositions + 1 + positions[nofpositions] = { pos_h, pos_v, nofmatrices } + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + b = b + 1 ; buffer[b] = "q" + end + + flushpdfrestore = function(current,pos_h,pos_v) + if nofpositions < 1 then + return + end + local t = positions[nofpositions] + -- local h = pos_h - t[1] + -- local v = pos_v - t[2] + if shippingmode == "page" then + nofmatrices = t[3] + end + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + b = b + 1 ; buffer[b] = "Q" + nofpositions = nofpositions - 1 + end + + local function pdf_set_matrix(str,pos_h,pos_v) + if shippingmode == "page" then + local rx, sx, sy, ry = splitupstring(str," ") + if rx and ry and sx and ry then + rx, sx, sy, ry = tonumber(rx), tonumber(sx), tonumber(sy), tonumber(ry) + local tx = pos_h * (1 - rx) - pos_v * sy + local ty = pos_v * (1 - ry) - pos_h * sx + if nofmatrices > 1 then + local t = matrices[nofmatrices] + local r_x, s_x, s_y, r_y, te, tf = t[1], t[2], t[3], t[4], t[5], t[6] + rx, sx = rx * r_x + sx * s_y, rx * s_x + sx * r_y + sy, ry = sy * r_x + ry * s_y, sy * s_x + ry * r_y + tx, ty = tx * r_x + ty * s_y, tx * s_x + ty * r_y + end + nofmatrices = nofmatrices + 1 + matrices[nofmatrices] = { rx, sx, sy, ry, tx, ty } + end + end + end + + local nodeproperties = nodes.properties.data + + flushpdfsetmatrix = function(current,pos_h,pos_v) + local str + if type(current) == "string" then + str = current + else + local p = nodeproperties[current] + if p then + str = p.matrix + else + str = getdata(current) -- for the moment + end + end + if str and str ~= "" then + pdf_set_matrix(str,pos_h,pos_v) + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + b = b + 1 ; buffer[b] = f_matrix(str) + end + end + + do + + local function hasmatrix() + return nofmatrices > 0 + end + + local function getmatrix() + if nofmatrices > 0 then + return unpack(matrices[nofmatrices]) + else + return 1, 0, 0, 1, 0, 0 + end + end + + updaters.register("backend.update.pdf",function() + pdf.hasmatrix = hasmatrix + pdf.getmatrix = getmatrix + end) + + end + + pushorientation = function(orientation,pos_h,pos_v,pos_r) + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + b = b + 1 ; buffer[b] = "q" + if orientation == 1 then + b = b + 1 ; buffer[b] = "0 -1 1 0 0 0 cm" -- 90 + elseif orientation == 2 then + b = b + 1 ; buffer[b] = "-1 0 0 -1 0 0 cm" -- 180 + elseif orientation == 3 then + b = b + 1 ; buffer[b] = "0 1 -1 0 0 0 cm" -- 270 + end + end + + poporientation = function(orientation,pos_h,pos_v,pos_r) + pdf_goto_pagemode() + pdf_set_pos(pos_h,pos_v) + b = b + 1 ; buffer[b] = "Q" + end + +-- pushorientation = function(orientation,pos_h,pos_v,pos_r) +-- flushpdfsave(false,pos_h,pos_v) +-- if orientation == 1 then +-- flushpdfsetmatrix("0 -1 1 0",pos_h,pos_v) +-- elseif orientation == 2 then +-- flushpdfsetmatrix("-1 0 0 -1",pos_h,pos_v) +-- elseif orientation == 3 then +-- flushpdfsetmatrix("0 1 -1 0",pos_h,pos_v) +-- end +-- end + +-- poporientation = function(orientation,pos_h,pos_v,pos_r) +-- flushpdfrestore(false,pos_h,pos_v) +-- end + +end + +-- rules + +local flushedxforms = { } -- actually box resources but can also be direct + +local flushrule, flushsimplerule, flushpdfimage do + + local rulecodes = nodes.rulecodes + local newrule = nodes.pool.rule + + local setprop = nuts.setprop + local getprop = nuts.getprop + + local normalrule_code = rulecodes.normal + local boxrule_code = rulecodes.box + local imagerule_code = rulecodes.image + local emptyrule_code = rulecodes.empty + local userrule_code = rulecodes.user + local overrule_code = rulecodes.over + local underrule_code = rulecodes.under + local fractionrule_code = rulecodes.fraction + local radicalrule_code = rulecodes.radical + local outlinerule_code = rulecodes.outline + + local rule_callback = callbacks.functions.process_rule + + local f_fm = formatters["/Fm%d Do"] + local f_im = formatters["/Im%d Do"] + + local s_b = "q" + local s_e = "Q" + + local f_v = formatters["[] 0 d 0 J %.6F w 0 0 m %.6F 0 l S"] + local f_h = formatters["[] 0 d 0 J %.6F w 0 0 m 0 %.6F l S"] + + local f_f = formatters["0 0 %.6F %.6F re f"] + local f_o = formatters["[] 0 d 0 J 0 0 %.6F %.6F re S"] + local f_w = formatters["[] 0 d 0 J %.6F w 0 0 %.6F %.6F re S"] + + directives.register("pdf.stripzeros",function() + f_v = formatters["[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S"] + f_h = formatters["[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S"] + f_f = formatters["0 0 %.6N %.6N re f"] + f_o = formatters["[] 0 d 0 J 0 0 %.6N %.6N re S"] + f_w = formatters["[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S"] + end) + + -- Historically the index is an object which is kind of bad. + + local boxresources, n = { }, 0 + + getxformname = function(index) + local l = boxresources[index] + if l then + return l.name + else + print("no box resource",index) + end + end + + updaters.register("backend.update.pdf",function() + pdf.getxformname = getxformname + end) + + local function saveboxresource(box,attributes,resources,immediate,kind,margin) + n = n + 1 + local immediate = true + local margin = margin or 0 -- or dimension + local objnum = pdfreserveobject() + local list = tonut(type(box) == "number" and tex.takebox(box) or box) + -- + local width, height, depth = getwhd(list) + -- + local l = { + width = width, + height = height, + depth = depth, + margin = margin, + attributes = attributes, + resources = resources, + list = nil, + type = kind, + name = n, + index = objnum, + objnum = objnum, + } + boxresources[objnum] = l + if immediate then + lpdf.convert(list,"xform",objnum,l) + flushedxforms[objnum] = { true , objnum } + flushlist(list) + else + l.list = list + end + return objnum + end + + local function useboxresource(index,wd,ht,dp) + local l = boxresources[index] + if l then + if wd or ht or dp then + wd, ht, dp = wd or 0, ht or 0, dp or 0 + else + wd, ht, dp = l.width, l.height, l.depth + end + local rule = newrule(wd,ht,dp) -- newboxrule + rule.subtype = boxrule_code + setprop(tonut(rule),"index",index) + return rule, wd, ht, dp + else + print("no box resource",index) + end + end + + local function getboxresourcedimensions(index) + local l = boxresources[index] + if l then + return l.width, l.height, l.depth, l.margin + else + print("no box resource",index) + end + end + + local function getboxresourcebox(index) + local l = boxresources[index] + if l then + return l.list + end + end + + updaters.register("backend.update.tex",function() + tex.saveboxresource = saveboxresource + tex.useboxresource = useboxresource + tex.getboxresourcedimensions = getboxresourcedimensions + tex.getboxresourcebox = getboxresourcebox + end) + + -- a bit of a mess: index is now objnum but that has to change to a proper index + -- ... an engine inheritance + + local function flushpdfxform(current,pos_h,pos_v,pos_r,size_h,size_v) + -- object properties + local objnum = getprop(current,"index") + local name = getxformname(objnum) + local info = flushedxforms[objnum] + local r = boxresources[objnum] + if not info then + info = { false , objnum } + flushedxforms[objnum] = info + end + local wd, ht, dp = getboxresourcedimensions(objnum) + -- or: wd, ht, dp = r.width, r.height, r.depth + -- sanity check + local htdp = ht + dp + if wd == 0 or size_h == 0 or htdp == 0 or size_v == 0 then + return + end + -- calculate scale + local rx, ry = 1, 1 + if wd ~= size_h or htdp ~= size_v then + rx = size_h / wd + ry = size_v / htdp + end + -- flush the reference + usedxforms[objnum] = true + pdf_goto_pagemode() + calc_pdfpos(pos_h,pos_v) + tx = cmtx * bpfactor + ty = cmty * bpfactor + b = b + 1 ; buffer[b] = s_b + b = b + 1 ; buffer[b] = f_cm(rx,0,0,ry,tx,ty) + b = b + 1 ; buffer[b] = f_fm(name) + b = b + 1 ; buffer[b] = s_e + end + + -- place image also used in vf but we can use a different one if + -- we need it + + local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream memstream + local img_none = imagetypes.none + local img_pdf = imagetypes.pdf + local img_stream = imagetypes.stream + local img_memstream = imagetypes.memstream + + local one_bp = 65536 * bpfactor + + local imageresources, n = { }, 0 + + getximagename = function(index) + local l = imageresources[index] + if l then + return l.name + else + print("no image resource",index) + end + end + + updaters.register("backend.update.pdf",function() + pdf.getximagename = getximagename + end) + + local function flushpdfximage(current,pos_h,pos_v,pos_r,size_h,size_v) + + local width, + height, + depth = getwhd(current) + local total = height + depth + local transform = getprop(current,"transform") or 0 -- we never set it ... so just use rotation then + local index = getprop(current,"index") or 0 + local kind, + xorigin, + yorigin, + xsize, + ysize, + rotation, + objnum, + groupref = pdfincludeimage(index) -- needs to be sorted out, bad name (no longer mixed anyway) + + if not kind then + print("invalid image",index) + return + end + + local rx, sx, sy, ry, tx, ty = 1, 0, 0, 1, 0, 0 + + if kind == img_pdf or kind == img_stream or kind == img_memstream then + rx, ry, tx, ty = 1/xsize, 1/ysize, xorigin/xsize, yorigin/ysize + else + -- if kind == img_png then + -- -- if groupref > 0 and img_page_group_val == 0 then + -- -- img_page_group_val = groupref + -- -- end + -- end + rx, ry = bpfactor, bpfactor + end + + if band(transform,7) > 3 then + -- mirror + rx, tx = -rx, -tx + end + local t = band(transform + rotation,3) + if t == 0 then + -- nothing + elseif t == 1 then + -- rotation over 90 degrees (counterclockwise) + rx, sx, sy, ry, tx, ty = 0, rx, -ry, 0, -ty, tx + elseif t == 2 then + -- rotation over 180 degrees (counterclockwise) + rx, ry, tx, ty = -rx, -ry, -tx, -ty + elseif t == 3 then + -- rotation over 270 degrees (counterclockwise) + rx, sx, sy, ry, tx, ty = 0, -rx, ry, 0, ty, -tx + end + + rx = rx * width + sx = sx * total + sy = sy * width + ry = ry * total + tx = pos_h - tx * width + ty = pos_v - ty * total + + local t = transform + rotation + + if band(transform,7) > 3 then + t = t + 1 + end + + t = band(t,3) + + if t == 0 then + -- no transform + elseif t == 1 then + -- rotation over 90 degrees (counterclockwise) + tx = tx + width + elseif t == 2 then + -- rotation over 180 degrees (counterclockwise) + tx = tx + width + ty = ty + total + elseif t == 3 then + -- rotation over 270 degrees (counterclockwise) + ty = ty + total + end + + -- a flaw in original, can go: + -- + -- if img_page_group_val == 0 then + -- img_page_group_val = group_ref + -- end + + usedximages[index] = objnum -- hm + + pdf_goto_pagemode() + + calc_pdfpos(tx,ty) + + tx = cmtx * bpfactor + ty = cmty * bpfactor + + b = b + 1 ; buffer[b] = s_b + b = b + 1 ; buffer[b] = f_cm(rx,sx,sy,ry,tx,ty) + b = b + 1 ; buffer[b] = f_im(index) + b = b + 1 ; buffer[b] = s_e + end + + flushpdfimage = function(index,width,height,depth,pos_h,pos_v) + + -- used in vf characters + + local total = height + depth + local kind, + xorigin, yorigin, + xsize, ysize, + rotation, + objnum, + groupref = pdfincludeimage(index) + + local rx = width / xsize + local sx = 0 + local sy = 0 + local ry = total / ysize + local tx = pos_h + local ty = pos_v - 2 * depth -- to be sorted out + + usedximages[index] = objnum + + pdf_goto_pagemode() + + calc_pdfpos(tx,ty) + + tx = cmtx * bpfactor + ty = cmty * bpfactor + + b = b + 1 ; buffer[b] = s_b + b = b + 1 ; buffer[b] = f_cm(rx,sx,sy,ry,tx,ty) + b = b + 1 ; buffer[b] = f_im(index) + b = b + 1 ; buffer[b] = s_e + end + + -- For the moment we need this hack because the engine checks the 'image' + -- command in virtual fonts (so we use lua instead). + + flushrule = function(current,pos_h,pos_v,pos_r,size_h,size_v) + + local subtype = getsubtype(current) + if subtype == emptyrule_code then + return + elseif subtype == boxrule_code then + return flushpdfxform(current,pos_h,pos_v,pos_r,size_h,size_v) + elseif subtype == imagerule_code then + return flushpdfximage(current,pos_h,pos_v,pos_r,size_h,size_v) + end + if subtype == userrule_code or subtype >= overrule_code and subtype <= radicalrule_code then + pdf_goto_pagemode() + b = b + 1 ; buffer[b] = s_b + pdf_set_pos_temp(pos_h,pos_v) + rule_callback(current,size_h,size_v,pos_r) -- so we pass direction + b = b + 1 ; buffer[b] = s_e + return + end + + pdf_goto_pagemode() + + -- local saved_b = b + + b = b + 1 ; buffer[b] = s_b + + local dim_h = size_h * bpfactor + local dim_v = size_v * bpfactor + local rule + + if dim_v <= one_bp then + pdf_set_pos_temp(pos_h,pos_v + 0.5 * size_v) + rule = f_v(dim_v,dim_h) + elseif dim_h <= one_bp then + pdf_set_pos_temp(pos_h + 0.5 * size_h,pos_v) + rule = f_h(dim_h,dim_v) + else + pdf_set_pos_temp(pos_h,pos_v) + if subtype == outlinerule_code then + local linewidth = getdata(current) + if linewidth > 0 then + rule = f_w(linewidth * bpfactor,dim_h,dim_v) + else + rule = f_o(dim_h,dim_v) + end + else + rule = f_f(dim_h,dim_v) + end + end + + b = b + 1 ; buffer[b] = rule + b = b + 1 ; buffer[b] = s_e + + -- buffer[saved_b] = concat(buffer," ",saved_b,b) + -- b = saved_b + + end + + flushsimplerule = function(pos_h,pos_v,pos_r,size_h,size_v) + pdf_goto_pagemode() + + b = b + 1 ; buffer[b] = s_b + + local dim_h = size_h * bpfactor + local dim_v = size_v * bpfactor + local rule + + if dim_v <= one_bp then + pdf_set_pos_temp(pos_h,pos_v + 0.5 * size_v) + rule = f_v(dim_v,dim_h) + elseif dim_h <= one_bp then + pdf_set_pos_temp(pos_h + 0.5 * size_h,pos_v) + rule = f_h(dim_h,dim_v) + else + pdf_set_pos_temp(pos_h,pos_v) + rule = f_f(dim_h,dim_v) + end + + b = b + 1 ; buffer[b] = rule + b = b + 1 ; buffer[b] = s_e + end + +end + +--- basics + +local wrapup, registerpage do + + local pages = { } + local maxkids = 10 + local nofpages = 0 + + registerpage = function(object) + nofpages = nofpages + 1 + local objnum = pdfpagereference(nofpages) + pages[nofpages] = { + objnum = objnum, + object = object, + } + end + + wrapup = function() + + -- local includechar = lpdf.includecharlist + -- + -- for font, list in next, usedcharacters do + -- includechar(font,keys(list)) + -- end + + -- hook (to reshuffle pages) + local pagetree = { } + local parent = nil + local minimum = 0 + local maximum = 0 + local current = 0 + if #pages > 1.5 * maxkids then + repeat + local plist, pnode + if current == 0 then + plist, minimum = pages, 1 + elseif current == 1 then + plist, minimum = pagetree, 1 + else + plist, minimum = pagetree, maximum + 1 + end + maximum = #plist + if maximum > minimum then + local kids + for i=minimum,maximum do + local p = plist[i] + if not pnode or #kids == maxkids then + kids = pdfarray() + parent = pdfreserveobject() + pnode = pdfdictionary { + objnum = parent, + Type = pdf_pages, + Kids = kids, + Count = 0, + } + pagetree[#pagetree+1] = pnode + end + kids[#kids+1] = pdfreference(p.objnum) + pnode.Count = pnode.Count + (p.Count or 1) + p.Parent = pdfreference(parent) + end + end + current = current + 1 + until maximum == minimum + -- flush page tree + for i=1,#pagetree do + local entry = pagetree[i] + local objnum = entry.objnum + entry.objnum = nil + pdfflushobject(objnum,entry) + end + else + -- ugly + local kids = pdfarray() + local list = pdfdictionary { + Type = pdf_pages, + Kids = kids, + Count = nofpages, + } + parent = pdfreserveobject() + for i=1,nofpages do + local page = pages[i] + kids[#kids+1] = pdfreference(page.objnum) + page.Parent = pdfreference(parent) + end + pdfflushobject(parent,list) + end + for i=1,nofpages do + local page = pages[i] + local object = page.object + object.Parent = page.Parent + pdfflushobject(page.objnum,object) + end + lpdf.addtocatalog("Pages",pdfreference(parent)) + + end + +end + +pdf_h, pdf_v = 0, 0 + +local function initialize(specification) + reset_variables(specification) + reset_buffer() +end + +local f_font = formatters["F%d"] +local f_form = formatters["Fm%d"] +local f_image = formatters["Im%d"] + +-- This will all move and be merged and become less messy. + +-- todo: more clever resource management: a bit tricky as we can inject +-- stuff in the page stream + +local pushmode, popmode + +local function finalize(objnum,specification) + + pushmode() + + pdf_goto_pagemode() -- for now + + local content = concat(buffer,"\n",1,b) + + local fonts = nil + local xforms = nil + + if next(usedfonts) then + fonts = pdfdictionary { } + for k, v in next, usedfonts do + fonts[f_font(v)] = pdfreference(pdfgetfontobjnumber(k)) -- we can overload for testing + end + end + + -- messy: use real indexes for both ... so we need to change some in the + -- full luatex part + + if next(usedxforms) or next(usedximages) then + xforms = pdfdictionary { } + for k in sortedhash(usedxforms) do + -- xforms[f_form(k)] = pdfreference(k) + xforms[f_form(getxformname(k))] = pdfreference(k) + end + for k, v in sortedhash(usedximages) do + xforms[f_image(k)] = pdfreference(v) + end + end + + reset_buffer() + + -- finish_pdfpage_callback(shippingmode == "page") + + if shippingmode == "page" then + + local pageproperties = lpdf.getpageproperties() + + local pageresources = pageproperties.pageresources + local pageattributes = pageproperties.pageattributes + local pagesattributes = pageproperties.pagesattributes + + pageresources.Font = fonts + pageresources.XObject = xforms + pageresources.ProcSet = lpdf.procset() + + local contentsobj = pdfflushstreamobject(content,false,false) + + pageattributes.Type = pdf_page + pageattributes.Contents = pdfreference(contentsobj) + pageattributes.Resources = pageresources + -- pageattributes.Resources = pdfreference(pdfflushobject(pageresources)) + pageattributes.MediaBox = pdfarray { + boundingbox[1] * bpfactor, + boundingbox[2] * bpfactor, + boundingbox[3] * bpfactor, + boundingbox[4] * bpfactor, + } + pageattributes.Parent = nil -- precalculate + pageattributes.Group = nil -- todo + + -- resources can be indirect + + registerpage(pageattributes) + + lpdf.finalizepage(true) + + else + + local xformtype = specification.type or 0 + local margin = specification.margin or 0 + local attributes = specification.attributes or "" + local resources = specification.resources or "" + local wrapper = nil + if xformtype == 0 then + wrapper = pdfdictionary { + Type = pdf_xobject, + Subtype = pdf_form, + FormType = 1, + BBox = nil, + Matrix = nil, + Resources = nil, + } + else + wrapper = pdfdictionary { + BBox = nil, + Matrix = nil, + Resources = nil, + } + end + if xformtype == 0 or xformtype == 1 or xformtype == 3 then + wrapper.BBox = pdfarray { + -margin * bpfactor, + -margin * bpfactor, + (boundingbox[3] + margin) * bpfactor, + (boundingbox[4] + margin) * bpfactor, + } + end + if xformtype == 0 or xformtype == 2 or xformtype == 3 then + wrapper.Matrix = pdfarray { 1, 0, 0, 1, 0, 0 } + end + + -- todo: additional = resources + + local boxresources = lpdf.collectedresources { serialize = false } + boxresources.Font = fonts + boxresources.XObject = xforms + + -- todo: maybe share them + -- wrapper.Resources = pdfreference(pdfflushobject(boxresources)) + + if resources ~= "" then + boxresources = boxresources + resources + end + if attributes ~= "" then + wrapper = wrapper + attributes + end + + wrapper.Resources = next(boxresources) and boxresources or nil + wrapper.ProcSet = lpdf.procset() + + -- pdfflushstreamobject(content,wrapper,false,objectnumber) + pdfflushstreamobject(content,wrapper,false,specification.objnum) + + end + + for objnum in sortedhash(usedxforms) do + local f = flushedxforms[objnum] + if f[1] == false then + f[1] = true + local objnum = f[2] -- specification.objnum + local specification = boxresources[objnum] + local list = specification.list + lpdf.convert(list,"xform",f[2],specification) + end + end + + pdf_h, pdf_v = 0, 0 + + popmode() + +end + +updaters.register("backend.update.pdf",function() + job.positions.registerhandlers { + getpos = drivers.getpos, + gethpos = drivers.gethpos, + getvpos = drivers.getvpos, + } +end) + +updaters.register("backend.update",function() + local saveboxresource = tex.boxresources.save + -- + -- also in lpdf-res .. brrr .. needs fixing + -- + backends.codeinjections.registerboxresource = function(n,offset) + local r = saveboxresource(n,nil,nil,false,0,offset or 0) + return r + end +end) + +-- now comes the pdf file handling + +local objects = { } +local streams = { } -- maybe just parallel to objects (no holes) +local nofobjects = 0 +local offset = 0 +local f = false +local flush = false +local threshold = 40 -- also #("/Filter /FlateDecode") +local objectstream = true +local compress = true +local cache = false +local info = "" +local catalog = "" +local level = 0 +local lastdeferred = false +local majorversion = 1 +local minorversion = 7 +local trailerid = true + +local f_object = formatters["%i 0 obj\010%s\010endobj\010"] +local f_stream_n_u = formatters["%i 0 obj\010<< /Length %i >>\010stream\010%s\010endstream\010endobj\010"] +local f_stream_n_c = formatters["%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010"] +local f_stream_d_u = formatters["%i 0 obj\010<< %s /Length %i >>\010stream\010%s\010endstream\010endobj\010"] +local f_stream_d_c = formatters["%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010"] +local f_stream_d_r = formatters["%i 0 obj\010<< %s >>\010stream\010%s\010endstream\010endobj\010"] + +local f_object_b = formatters["%i 0 obj\010"] +local f_stream_b_n_u = formatters["%i 0 obj\010<< /Length %i >>\010stream\010"] +local f_stream_b_n_c = formatters["%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010"] +local f_stream_b_d_u = formatters["%i 0 obj\010<< %s /Length %i >>\010stream\010"] +local f_stream_b_d_c = formatters["%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010"] +local f_stream_b_d_r = formatters["%i 0 obj\010<< %s >>\010stream\010"] + +local s_object_e = "\010endobj\010" +local s_stream_e = "\010endstream\010endobj\010" + +do + + local function setinfo() end -- we get it + local function setcatalog() end -- we get it + + local function settrailerid(v) + trailerid = v or false + end + + local function setmajorversion(v) majorversion = tonumber(v) or majorversion end + local function setminorversion(v) minorversion = tonumber(v) or minorversion end + + local function getmajorversion(v) return majorversion end + local function getminorversion(v) return minorversion end + + local function setcompresslevel (v) compress = v and v ~= 0 and true or false end + local function setobjcompresslevel(v) objectstream = v and v ~= 0 and true or false end + + local function getcompresslevel (v) return compress and 3 or 0 end + local function getobjcompresslevel(v) return objectstream and 1 or 0 end + + local function setpageresources () end -- needs to be sorted out + local function setpageattributes () end + local function setpagesattributes() end + + updaters.register("backend.update.pdf",function() + pdf.setinfo = setinfo + pdf.setcatalog = setcatalog + pdf.settrailerid = settrailerid + pdf.setmajorversion = setmajorversion + pdf.setminorversion = setminorversion + pdf.getmajorversion = getmajorversion + pdf.getminorversion = getminorversion + pdf.setcompresslevel = setcompresslevel + pdf.setobjcompresslevel = setobjcompresslevel + pdf.getcompresslevel = getcompresslevel + pdf.getobjcompresslevel = getobjcompresslevel + pdf.setpageresources = setpageresources + pdf.setpageattributes = setpageattributes + pdf.setpagesattributes = setpagesattributes + end) + +end + +local addtocache, flushcache, cache do + + local data, d = { }, 0 + local list, l = { }, 0 + local coffset = 0 + local maxsize = 32 * 1024 -- uncompressed + local maxcount = 0xFF + local indices = { } + + addtocache = function(n,str) + local size = #str + if size == 0 then + -- todo: message + return + end + if coffset + size > maxsize or d == maxcount then + flushcache() + end + if d == 0 then + nofobjects = nofobjects + 1 + objects[nofobjects] = false + streams[nofobjects] = indices + cache = nofobjects + end + objects[n] = - cache + indices[n] = d + d = d + 1 + -- can have a comment n 0 obj as in luatex + data[d] = str + l = l + 1 ; list[l] = n + l = l + 1 ; list[l] = coffset + coffset = coffset + size + 1 + end + + local p_ObjStm = pdfconstant("ObjStm") + + flushcache = function() -- references cannot be stored + if l > 0 then + list = concat(list," ") + data[0] = list + data = concat(data,"\010",0,d) + local strobj = pdfdictionary { + Type = p_ObjStm, + N = d, + First = #list + 1, + } + objects[cache] = offset + local b = nil + local e = s_stream_e + if compress then + local comp = zlibcompress(data,3) + if comp and #comp < #data then + data = comp + b = f_stream_b_d_c(cache,strobj(),#data) + else + b = f_stream_b_d_u(cache,strobj(),#data) + end + else + b = f_stream_b_d_u(cache,strobj(),#data) + end + flush(f,b) + flush(f,data) + flush(f,e) + offset = offset + #b + #data + #e + data, d = { }, 0 + list, l = { }, 0 + coffset = 0 + indices = { } + end + end + +end + +local function pdfreserveobj() + nofobjects = nofobjects + 1 + objects[nofobjects] = false + return nofobjects +end + +local pages = table.setmetatableindex(function(t,k) + local v = pdfreserveobj() + t[k] = v + return v +end) + +local function getpageref(n) + return pages[n] +end + +local function refobj() + -- not needed, as we have auto-delay +end + +local function flushnormalobj(data,n) + if not n then + nofobjects = nofobjects + 1 + n = nofobjects + end + data = f_object(n,data) + if level == 0 then + objects[n] = offset + offset = offset + #data + flush(f,data) + else + if not lastdeferred then + lastdeferred = n + elseif n < lastdeferred then + lastdeferred = n + end + objects[n] = data + end + return n +end + +local function flushstreamobj(data,n,dict,comp,nolength) + if not data then + print("no data for",dict) + return + end + if not n then + nofobjects = nofobjects + 1 + n = nofobjects + end + local size = #data + if level == 0 then + local b = nil + local e = s_stream_e + if nolength then + b = f_stream_b_d_r(n,dict) + elseif comp ~= false and compress and size > threshold then + local compdata = zlibcompress(data,3) + if compdata then + local compsize = #compdata + if compsize > size - threshold then + b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size) + else + data = compdata + b = dict and f_stream_b_d_c(n,dict,compsize) or f_stream_b_n_c(n,compsize) + end + else + b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size) + end + else + b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size) + end + flush(f,b) + flush(f,data) + flush(f,e) + objects[n] = offset + offset = offset + #b + #data + #e + else + if nolength then + data = f_stream_d_r(n,dict,data) + elseif comp ~= false and compress and size > threshold then + local compdata = zlibcompress(data,3) + if compdata then + local compsize = #compdata + if compsize > size - threshold then + data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data) + else + data = dict and f_stream_d_c(n,dict,compsize,compdata) or f_stream_n_c(n,compsize,compdata) + end + else + data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data) + end + else + data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data) + end + if not lastdeferred then + lastdeferred = n + elseif n < lastdeferred then + lastdeferred = n + end + objects[n] = data + end + return n +end + +local function flushdeferred() + if lastdeferred then + for n=lastdeferred,nofobjects do + local o = objects[n] + if type(o) == "string" then + objects[n] = offset + offset = offset + #o + flush(f,o) + end + end + lastdeferred = false + end +end + +-- These are already used above, so we define them now: + +pushmode = function() + level = level + 1 +end + +popmode = function() + if level == 1 then + flushdeferred() + end + level = level - 1 +end + +-- n = pdf.obj([n,] objtext) +-- n = pdf.obj([n,] "file", filename) +-- n = pdf.obj([n,] "stream", streamtext [, attrtext]) +-- n = pdf.obj([n,] "streamfile", filename [, attrtext]) +-- +-- n = pdf.obj { +-- type = , -- raw|stream +-- immediate = , +-- objnum = , +-- attr = , +-- compresslevel = , +-- objcompression = , +-- file = , +-- string = , +-- nolength = , +-- } + +local function obj(a,b,c,d) + local kind --, immediate + local objnum, data, attr, filename + local compresslevel, objcompression, nolength + local argtype = type(a) + if argtype == "table" then + kind = a.type -- raw | stream + -- immediate = a.immediate + objnum = a.objnum + attr = a.attr + compresslevel = a.compresslevel + objcompression = a.objcompression + filename = a.file + data = a.string or a.stream or "" + nolength = a.nolength + if kind == "stream" then + if filename then + data = io.loaddata(filename) or "" + end + elseif kind == "raw"then + if filename then + data = io.loaddata(filename) or "" + end + elseif kind == "file"then + kind = "raw" + data = filename and io.loaddata(filename) or "" + elseif kind == "streamfile" then + kind = "stream" + data = filename and io.loaddata(filename) or "" + end + else + if argtype == "number" then + objnum = a + a, b, c = b, c, d + else + nofobjects = nofobjects + 1 + objnum = nofobjects + end + if b then + if a == "stream" then + kind = "stream" + data = b + elseif a == "file" then + -- kind = "raw" + data = io.loaddata(b) + elseif a == "streamfile" then + kind = "stream" + data = io.loaddata(b) + else + data = "" -- invalid object + end + attr = c + else + -- kind = "raw" + data = a + end + end + if not objnum then + nofobjects = nofobjects + 1 + objnum = nofobjects + end + -- todo: immediate + if kind == "stream" then + flushstreamobj(data,objnum,attr,compresslevel and compresslevel > 0 or nil,nolength) + elseif objectstream and objcompression ~= false then + addtocache(objnum,data) + else + flushnormalobj(data,objnum) + end + return objnum +end + +updaters.register("backend.update.pdf",function() + pdf.reserveobj = pdfreserveobj + pdf.getpageref = getpageref + pdf.refobj = refobj + pdf.flushstreamobj = flushstreamobj + pdf.flushnormalobj = flushnormalobj + pdf.obj = obj + pdf.immediateobj = obj +end) + +local openfile, closefile do + + local f_used = formatters["%010i 00000 n \010"] + local f_link = formatters["%010i 00000 f \010"] + local f_first = formatters["%010i 65535 f \010"] + local f_pdf = formatters["%%PDF-%i.%i\010"] + local f_xref = formatters["xref\0100 %i\010"] + local f_trailer_id = formatters["trailer\010<< %s /ID [ <%s> <%s> ] >>\010startxref\010%i\010%%%%EOF"] + local f_trailer_no = formatters["trailer\010<< %s >>\010startxref\010%i\010%%%%EOF"] + local f_startxref = formatters["startxref\010%i\010%%%%EOF"] + + local inmemory = false + -- local inmemory = environment.arguments.inmemory + local close = nil + + openfile = function(filename) + if inmemory then + f = { } + local n = 0 + flush = function(f,s) + n = n + 1 f[n] = s + end + close = function(f) + f = concat(f) + io.savedata(filename,f) + f = false + end + else + f = io.open(filename,"wb") + if not f then + -- message + os.exit() + end + flush = getmetatable(f).write + close = getmetatable(f).close + end + local v = f_pdf(majorversion,minorversion) + -- local b = "%\xCC\xD5\xC1\xD4\xC5\xD8\xD0\xC4\xC6\010" -- LUATEXPDF (+128) + local b = "%\xC3\xCF\xCE\xD4\xC5\xD8\xD4\xD0\xC4\xC6\010" -- CONTEXTPDF (+128) + flush(f,v) + flush(f,b) + offset = #v + #b + end + + closefile = function(abort) + if not abort then + local xrefoffset = offset + local lastfree = 0 + local noffree = 0 + local catalog = lpdf.getcatalog() + local info = lpdf.getinfo() + if trailerid == true then + trailerid = md5HEX(osuuid()) + elseif trailerid and #trailerid > 32 then + trailerid = md5HEX(trailerid) + else + trailerid = false + end + if objectstream then + flushdeferred() + flushcache() + -- + xrefoffset = offset + -- + nofobjects = nofobjects + 1 + objects[nofobjects] = offset -- + 1 + -- + -- combine these three in one doesn't really give less code so + -- we go for the efficient ones + -- + local nofbytes = 4 + local c1, c2, c3, c4 + if offset <= 0xFFFF then + nofbytes = 2 + for i=1,nofobjects do + local o = objects[i] + if not o then + noffree = noffree + 1 + else + local strm = o < 0 + if strm then + o = -o + end + c1 = extract(o,8,8) + c2 = extract(o,0,8) + if strm then + objects[i] = char(2,c1,c2,streams[o][i]) + else + objects[i] = char(1,c1,c2,0) + end + end + end + if noffree > 0 then + for i=nofobjects,1,-1 do + local o = objects[i] + if not o then + local f1 = extract(lastfree,8,8) + local f2 = extract(lastfree,0,8) + objects[i] = char(0,f1,f2,0) + lastfree = i + end + end + end + elseif offset <= 0xFFFFFF then + nofbytes = 3 + for i=1,nofobjects do + local o = objects[i] + if not o then + noffree = noffree + 1 + else + local strm = o < 0 + if strm then + o = -o + end + c1 = extract(o,16,8) + c2 = extract(o, 8,8) + c3 = extract(o, 0,8) + if strm then + objects[i] = char(2,c1,c2,c3,streams[o][i]) + else + objects[i] = char(1,c1,c2,c3,0) + end + end + end + if noffree > 0 then + for i=nofobjects,1,-1 do + local o = objects[i] + if not o then + local f1 = extract(lastfree,16,8) + local f2 = extract(lastfree, 8,8) + local f3 = extract(lastfree, 0,8) + objects[i] = char(0,f1,f2,f3,0) + lastfree = i + end + end + end + else + nofbytes = 4 + for i=1,nofobjects do + local o = objects[i] + if not o then + noffree = noffree + 1 + else + local strm = o < 0 + if strm then + o = -o + end + c1 = extract(o,24,8) + c2 = extract(o,16,8) + c3 = extract(o, 8,8) + c4 = extract(o, 0,8) + if strm then + objects[i] = char(2,c1,c2,c3,c4,streams[o][i]) + else + objects[i] = char(1,c1,c2,c3,c4,0) + end + end + end + if noffree > 0 then + for i=nofobjects,1,-1 do + local o = objects[i] + if not o then + local f1 = extract(lastfree,24,8) + local f2 = extract(lastfree,16,8) + local f3 = extract(lastfree, 8,8) + local f4 = extract(lastfree, 0,8) + objects[i] = char(0,f1,f2,f3,f4,0) + lastfree = i + end + end + end + end + objects[0] = rep("\0",1+nofbytes+1) + local data = concat(objects,"",0,nofobjects) + local xref = pdfdictionary { + Type = pdfconstant("XRef"), + Size = nofobjects + 1, + W = pdfarray { 1, nofbytes, 1 }, + Root = catalog, + Info = info, + ID = trailerid and pdfarray { pdfliteral(trailerid,true), pdfliteral(trailerid,true) } or nil, + } + if compress then + local comp = zlibcompress(data,3) + if comp then + data = comp + flush(f,f_stream_b_d_c(nofobjects,xref(),#data)) + else + flush(f,f_stream_b_d_u(nofobjects,xref(),#data)) + end + else + flush(f,f_stream_b_d_u(nofobjects,xref(),#data)) + end + flush(f,data) + flush(f,s_stream_e) + flush(f,f_startxref(xrefoffset)) + else + flushdeferred() + xrefoffset = offset + flush(f,f_xref(nofobjects+1)) + local trailer = pdfdictionary { + Size = nofobjects+1, + Root = catalog, + Info = info, + } + for i=1,nofobjects do + local o = objects[i] + if o then + objects[i] = f_used(o) + end + end + for i=nofobjects,1,-1 do + local o = objects[i] + if not o then + objects[i] = f_link(lastfree) + lastfree = i + end + end + objects[0] = f_first(lastfree) + flush(f,concat(objects,"",0,nofobjects)) + trailer.Size = nofobjects + 1 + if trailerid then + flush(f,f_trailer_id(trailer(),trailerid,trailerid,xrefoffset)) + else + flush(f,f_trailer_no(trailer(),xrefoffset)) + end + end + end + close(f) + io.flush() + end + +end + +-- For the moment we overload it here, although back-fil.lua eventually will +-- be merged with back-pdf as it's pdf specific, or maybe back-imp-pdf or so. + +updaters.register("backend.update.pdf",function() + + -- We overload img but at some point it will even go away, so we just + -- reimplement what we need in context. This will change completely i.e. + -- we will drop the low level interface! + + local codeinjections = backends.pdf.codeinjections + + local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream memstream + local img_none = imagetypes.none + + local rulecodes = nodes.rulecodes + local imagerule_code = rulecodes.image + + local setprop = nodes.nuts.setprop + + local report_images = logs.reporter("backend","images") + + local lastindex = 0 + local indices = { } + + local bpfactor = number.dimenfactors.bp + + function codeinjections.newimage(specification) + return specification + end + + function codeinjections.copyimage(original) + return setmetatableindex(original) + end + + function codeinjections.scanimgage(specification) + return specification + end + + local function embedimage(specification) + lastindex = lastindex + 1 + index = lastindex + specification.index = index + local xobject = pdfdictionary { } + if not specification.notype then + xobject.Type = pdfconstant("XObject") + xobject.Subtype = pdfconstant("Form") + xobject.FormType = 1 + end + local bbox = specification.bbox + if bbox and not specification.nobbox then + xobject.BBox = pdfarray { + bbox[1] * bpfactor, + bbox[2] * bpfactor, + bbox[3] * bpfactor, + bbox[4] * bpfactor, + } + end + xobject = xobject + specification.attr + if bbox and not specification.width then + specification.width = bbox[3] + end + if bbox and not specification.height then + specification.height = bbox[4] + end + local dict = xobject() + -- + nofobjects = nofobjects + 1 + local objnum = nofobjects + local nolength = specification.nolength + local stream = specification.stream or specification.string + -- + -- We cannot set type in native img so we need this hack or + -- otherwise we need to patch too much. Better that i write + -- a wrapper then. Anyway, it has to be done better: a key that + -- tells either or not to scale by xsize/ysize when flushing. + -- + if not specification.type then + local kind = specification.kind + if kind then + -- take that one + elseif attr and find(attr,"BBox") then + kind = img_stream + else + -- hack: a bitmap + kind = img_none + end + specification.type = kind + specification.kind = kind + end + local compress = compresslevel and compresslevel > 0 or nil + flushstreamobj(stream,objnum,dict,compress,nolength) + specification.objnum = objnum + specification.rotation = specification.rotation or 0 + specification.orientation = specification.orientation or 0 + specification.transform = specification.transform or 0 + specification.stream = nil + specification.attr = nil + specification.type = specification.kind or specification.type or img_none + indices[index] = specification -- better create a real specification + return specification + end + + codeinjections.embedimage = embedimage + + function codeinjections.wrapimage(specification) + -- + local index = specification.index + if not index then + embedimage(specification) + end + -- + local width = specification.width or 0 + local height = specification.height or 0 + local depth = specification.depth or 0 + -- newimagerule + local n = nodes.pool.rule(width,height,depth) + n.subtype = imagerule_code + setprop(tonut(n),"index",specification.index) + return n + end + + function pdf.includeimage(index) + local specification = indices[index] + if specification then + local bbox = specification.bbox + local xorigin = bbox[1] + local yorigin = bbox[2] + local xsize = specification.width -- should equal to: bbox[3] - xorigin + local ysize = specification.height -- should equal to: bbox[4] - yorigin + local transform = specification.transform or 0 + local objnum = specification.objnum or pdfreserveobj() + local groupref = nil + local kind = specification.kind or specification.type or img_none -- determines scaling type + return + kind, + xorigin, yorigin, + xsize, ysize, + transform, + objnum, + groupref + end + end + +end) + +updaters.register("backend.update.lpdf",function() + + -- for the moment here, todo: an md5 or sha2 hash can save space + + local pdfimage = lpdf.epdf.image + local newpdf = pdfimage.new + local closepdf = pdfimage.close + local copypage = pdfimage.copy + + local embedimage = images.embed + + local nofstreams = 0 + local topdf = { } + local toidx = { } + + local function storedata(pdf) + local idx = toidx[pdf] + if not idx then + nofstreams = nofstreams + 1 + idx = nofstreams + toidx[pdf] = nofstreams + topdf[idx] = pdf + end + return idx + end + + -- todo: make a type 3 font instead + + -- move to lpdf namespace + + local function vfimage(id,wd,ht,dp,pos_h,pos_v) + local index = topdf[id] + if type(index) == "string" then + local pdfdoc = newpdf(index,#index) + local image = copypage(pdfdoc) + local bbox = image.bbox + image.width = bbox[3] - bbox[1] + image.height = bbox[4] - bbox[2] + embedimage(image) + index = image.index + topdf[id] = index + end + -- pdf.print or pdf.literal + flushpdfimage(index,wd,ht,dp,pos_h,pos_v) + end + + local function pdfvfimage(wd,ht,dp,data,name) + return { "lua", function(font,char,pos_h,pos_v) + local id = storedata(data) + vfimage(id,wd,ht,dp,pos_h,pos_v) + end } + end + + lpdf.vfimage = pdfvfimage + +end) + +-- The driver. + +do + + local isfile = lfs.isfile + local removefile = os.remove + local renamefile = os.rename + -- local copyfile = file.copy + -- local addsuffix = file.addsuffix + + local pdfname = nil + local tmpname = nil + + local function outputfilename() + return pdfname + end + + local function prepare() + if not environment.initex then + -- install new functions in pdf namespace + updaters.apply("backend.update.pdf") + -- install new functions in lpdf namespace + updaters.apply("backend.update.lpdf") + -- adapt existing shortcuts to lpdf namespace + updaters.apply("backend.update.tex") + -- adapt existing shortcuts to tex namespace + updaters.apply("backend.update") + -- + -- if rawget(pdf,"setforcefile") then + -- pdf.setforcefile(false) -- default anyway + -- end + -- + pdfname = file.addsuffix(tex.jobname,"pdf") + tmpname = "l_m_t_x_" .. pdfname + removefile(tmpname) + if isfile(tmpname) then + report("file %a can't be opened, aborting",tmpname) + os.exit() + end + openfile(tmpname) + -- + luatex.registerstopactions(1,function() + lpdf.finalizedocument() + closefile() + end) + -- + luatex.registerpageactions(1,function() + lpdf.finalizepage(true) + end) + -- + fonts.constructors.autocleanup = false + -- + lpdf.registerdocumentfinalizer(wrapup,nil,"wrapping up") + end + -- + environment.lmtxmode = CONTEXTLMTXMODE + end + + local function wrapup() + if pdfname then + local ok = true + if isfile(pdfname) then + removefile(pdfname) + end + if isfile(pdfname) then + ok = false + file.copy(tmpname,pdfname) + else + renamefile(tmpname,pdfname) + if isfile(tmpname) then + ok = false + end + end + if not ok then + print(formatters["\nerror in renaming %a to %a\n"](tmpname,pdfname)) + end + pdfname = nil + tmpname = nil + end + end + + local function cleanup() + if tmpname then + closefile(true) + if isfile(tmpname) then + removefile(tmpname) + end + pdfname = nil + tmpname = nil + end + end + + local function convert(boxnumber) + lpdf.convert(tex.box[boxnumber],"page") + end + + function lpdf.flushers() + return { + character = flushcharacter, + rule = flushrule, + simplerule = flushsimplerule, + pushorientation = pushorientation, + poporientation = poporientation, + -- + pdfliteral = flushpdfliteral, + pdfsetmatrix = flushpdfsetmatrix, + pdfsave = flushpdfsave, + pdfrestore = flushpdfrestore, + pdfimage = flushpdfimage, + } + end + + function lpdf.actions() + return { + prepare = prepare, + wrapup = wrapup, + convert = convert, + cleanup = cleanup, + -- + initialize = initialize, + finalize = finalize, + updatefontstate = updatefontstate, + outputfilename = outputfilename, + } + end + + drivers.install { + name = "pdf", + flushers = lpdf.flushers(), + actions = lpdf.actions(), + } + +end diff --git a/tex/context/base/mkiv/lpdf-pde.lua b/tex/context/base/mkiv/lpdf-pde.lua index c1680137d..cb6c112e7 100644 --- a/tex/context/base/mkiv/lpdf-pde.lua +++ b/tex/context/base/mkiv/lpdf-pde.lua @@ -60,29 +60,29 @@ if not (number and number.dimenfactors) then require("util-dim") end -local epdf = pdfe +local pdfe = pdfe lpdf = lpdf or { } local lpdf = lpdf local lpdf_epdf = { } -lpdf.epdf = lpdf_epdf - -local openPDF = epdf.open -local newPDF = epdf.new -local closePDF = epdf.close - -local getcatalog = epdf.getcatalog -local getinfo = epdf.getinfo -local gettrailer = epdf.gettrailer -local getnofpages = epdf.getnofpages -local getversion = epdf.getversion -local getbox = epdf.getbox -local getstatus = epdf.getstatus -local unencrypt = epdf.unencrypt - -local dictionarytotable = epdf.dictionarytotable -local arraytotable = epdf.arraytotable -local pagestotable = epdf.pagestotable -local readwholestream = epdf.readwholestream + lpdf.epdf = lpdf_epdf + +local pdfopen = pdfe.open +local pdfnew = pdfe.new +local pdfclose = pdfe.close + +local getcatalog = pdfe.getcatalog +local getinfo = pdfe.getinfo +local gettrailer = pdfe.gettrailer +local getnofpages = pdfe.getnofpages +local getversion = pdfe.getversion +local getbox = pdfe.getbox +local getstatus = pdfe.getstatus +local unencrypt = pdfe.unencrypt + +local dictionarytotable = pdfe.dictionarytotable +local arraytotable = pdfe.arraytotable +local pagestotable = pdfe.pagestotable +local readwholestream = pdfe.readwholestream local getfromreference = pdfe.getfromreference @@ -437,9 +437,9 @@ function lpdf_epdf.load(filename,userpassword,ownerpassword,fromstring) statistics.starttiming(lpdf_epdf) local __data__ if fromstring then - __data__ = newPDF(filename,#filename) + __data__ = pdfnew(filename,#filename) else - __data__ = openPDF(filename) + __data__ = pdfopen(filename) end if __data__ then if userpassword and getstatus(__data__) < 0 then @@ -491,8 +491,7 @@ function lpdf_epdf.unload(filename) if type(filename) == "string" then local document = loaded[filename] if document then --- report_epdf("%04i closed: %s",nofloaded,filename) --- nofloaded = nofloaded - 1 + -- pdfclose(document) loaded[document] = nil loaded[filename] = nil end @@ -793,7 +792,7 @@ function lpdf_epdf.getstructure(document,list) -- just a test end end -if img then do +if images then do -- This can be made a bit faster (just get raw data and pass it) but I will -- do that later. In the end the benefit is probably neglectable. diff --git a/tex/context/base/mkiv/luat-cnf.lua b/tex/context/base/mkiv/luat-cnf.lua index cec4c414a..979ca36a0 100644 --- a/tex/context/base/mkiv/luat-cnf.lua +++ b/tex/context/base/mkiv/luat-cnf.lua @@ -70,9 +70,10 @@ function texconfig.init() "gzip", "zip", "zlib", "lfs", "ltn12", "mime", "socket", "md5", "fio", "unicode", "utf", }, extratex = { - "epdf", "kpse", "mplib", -- "fontloader", + "pdfe", "kpse", "mplib", }, obsolete = { + "epdf", "fontloader", -- can be filled by luat-log "kpse", }, diff --git a/tex/context/base/mkiv/luat-mac.lua b/tex/context/base/mkiv/luat-mac.lua index 44630b194..d13b69c42 100644 --- a/tex/context/base/mkiv/luat-mac.lua +++ b/tex/context/base/mkiv/luat-mac.lua @@ -268,6 +268,9 @@ function processors.mkxi(str,filename) return str end +processors.mkli = processors.mkvi +processors.mkil = processors.mkiv + function macros.processmk(str,filename) if filename then local suffix = filesuffix(filename) @@ -279,8 +282,18 @@ function macros.processmk(str,filename) return str end +local function validvi(filename,str) + local suffix = filesuffix(filename) + if suffix == "mkvi" or suffix == "mkli" then + return true + else + local check = lpegmatch(checker,str) + return check == "mkvi" or check == "mkli" + end +end + function macros.processmkvi(str,filename) - if filename and filesuffix(filename) == "mkvi" or lpegmatch(checker,str) == "mkvi" then + if filename and filename ~= "" and validvi(filename,str) then local oldsize = #str str = lpegmatch(parser,str,1,true) or str pushtarget("logfile") @@ -290,6 +303,8 @@ function macros.processmkvi(str,filename) return str end +macros.processmkli = macros.processmkvi + local sequencers = utilities.sequencers if sequencers then @@ -308,7 +323,7 @@ if resolvers.schemes then local path = hashed.path if path and path ~= "" then local str = resolvers.loadtexfile(path) - if filesuffix(path) == "mkvi" or lpegmatch(checker,str) == "mkvi" then + if validvi(path,str) then -- already done automatically io.savedata(cachename,str) else @@ -323,6 +338,7 @@ if resolvers.schemes then end resolvers.schemes.install('mkvi',handler,1) -- this will cache ! + resolvers.schemes.install('mkli',handler,1) -- bonus, best use just mkvi end diff --git a/tex/context/base/mkiv/meta-ini.mkiv b/tex/context/base/mkiv/meta-ini.mkiv index cf2dc883c..09d7b2c06 100644 --- a/tex/context/base/mkiv/meta-ini.mkiv +++ b/tex/context/base/mkiv/meta-ini.mkiv @@ -408,36 +408,41 @@ \let\stopMPclip\relax -\unexpanded\def\meta_grab_clip_path#1#2#3#4% #4 is alternative (called in backend code) +\unexpanded\def\meta_grab_clip_path#1#2#3% {\begingroup - \edef\width {#2}% \let\overlaywidth \width - \edef\height{#3}% \let\overlayheight\height - \d_overlay_width #2\onebasepoint - \d_overlay_height#3\onebasepoint + \d_overlay_width #2\relax + \d_overlay_height#3\relax + \edef\width {\the\d_overlay_width \space}% + \edef\height{\the\d_overlay_height\space}% \edef\currentMPclip{#1}% \ifcsname\??mpclip\currentMPclip\endcsname - \meta_grab_clip_path_indeed - \ifx\MPclippath\empty - \xdef\MPclippath{#4}% - \fi + \meta_grab_clip_path_yes \else - \xdef\MPclippath{#4}% + \meta_grab_clip_path_nop \fi \endgroup} -\def\meta_grab_clip_path_indeed +\def\meta_grab_clip_path_yes {\meta_start_current_graphic \normalexpanded{\noexpand\clf_mpsetclippath - instance {\currentMPinstance}% - format {\currentMPformat}% - data {\csname\??mpclip\currentMPclip\endcsname}% - initializations {\meta_flush_current_initializations}% - useextensions {\MPinstanceparameter\s!extensions}% - inclusions {\meta_flush_current_inclusions}% - method {\MPinstanceparameter\c!method}% + instance {\currentMPinstance}% + format {\currentMPformat}% + data {\csname\??mpclip\currentMPclip\endcsname}% + initializations {\meta_flush_current_initializations}% + useextensions {\MPinstanceparameter\s!extensions}% + inclusions {\meta_flush_current_inclusions}% + method {\MPinstanceparameter\c!method}% + width \d_overlay_width + height \d_overlay_height \relax}% \meta_stop_current_graphic} +\def\meta_grab_clip_path_nop + {\clf_mpsetclippath + width \d_overlay_width + height \d_overlay_height + \relax} + %D Since we want lables to follow the document settings, we also set the font %D related variables. @@ -574,7 +579,6 @@ \def\meta_prepare_variable_dimension {\expandafter\edef\csname\m_meta_current_variable_template\endcsname{\the\dimexpr\m_meta_current_variable}} - \startmkivmode \def\meta_prepare_variable_yes diff --git a/tex/context/base/mkiv/mlib-ctx.lua b/tex/context/base/mkiv/mlib-ctx.lua index 89f2fa0ee..e19f111b4 100644 --- a/tex/context/base/mkiv/mlib-ctx.lua +++ b/tex/context/base/mkiv/mlib-ctx.lua @@ -245,6 +245,8 @@ implement { arguments = "string", } +-- this has to become a codeinjection + function metapost.getclippath(specification) -- why not a special instance for this local mpx = metapost.pushformat(specification) local data = specification.data or "" @@ -304,7 +306,14 @@ end implement { name = "mpsetclippath", actions = function(specification) - setmacro("MPclippath",metapost.theclippath(specification),"global") + local p = specification.data and metapost.theclippath(specification) + if not p or p == "" then + local b = number.dimenfactors.bp + local w = b * (specification.width or 0) + local h = b * (specification.height or 0) + p = formatters["0 0 m %.6N 0 l %.6N %.6N l 0 %.6N l"](w,w,h,h) + end + setmacro("MPclippath",p,"global") end, arguments = { { @@ -316,6 +325,8 @@ implement { { "inclusions" }, { "method" }, { "namespace" }, + { "width", "dimension" }, + { "height", "dimension" }, }, } } diff --git a/tex/context/base/mkiv/mult-aux.mkiv b/tex/context/base/mkiv/mult-aux.mkiv index 6a2fd9fef..d77ec82a1 100644 --- a/tex/context/base/mkiv/mult-aux.mkiv +++ b/tex/context/base/mkiv/mult-aux.mkiv @@ -29,7 +29,7 @@ \unprotect -\edef\??empty{\Uchar25} \letvalue{\Uchar25}\empty % cancel: dec:24 hex:18 +\edef\??empty{\Uchar25} \letvalue{\Uchar25}\empty % hex 19 % \edef\s!parent{\Uchar29} % inlining  is ugly, a tiny bit faster, but neglectable on a run @@ -227,7 +227,8 @@ \def#3##1{\csname\ifcsname#1#2:##1\endcsname#1#2:##1\else\expandafter#5\csname#1#2:\s!parent\endcsname{##1}\fi\endcsname}% \def#4##1##2{\ifcsname##1:##2\endcsname##1:##2\else\expandafter#5\csname##1:\s!parent\endcsname{##2}\fi}% %\def#5##1##2{\ifx##1\relax\??empty\else#4{##1}{##2}\fi}% is {} needed around ##1 ? - \def#5##1##2{\ifx##1\relax\??empty\else#4##1{##2}\fi}% is {} needed around ##1 ? + %\def#5##1##2{\ifx##1\relax\??empty\else#4##1{##2}\fi}% is {} needed around ##1 ? + \def#5##1##2{\ifx##1\relax^^^^0019\else#4##1{##2}\fi}% is {} needed around ##1 ? \def#6##1##2{\csname\ifcsname#1##1:##2\endcsname#1##1:##2\else\expandafter#5\csname#1##1:\s!parent\endcsname{##2}\fi\endcsname}% \def#7##1{\detokenize\expandafter\expandafter\expandafter{\csname#1#2:##1\endcsname}}% always root, no backtrack \def#8##1{\begincsname#1#2:##1\endcsname}} diff --git a/tex/context/base/mkiv/node-syn.lua b/tex/context/base/mkiv/node-syn.lua index 35fb8fc7c..bb2eba780 100644 --- a/tex/context/base/mkiv/node-syn.lua +++ b/tex/context/base/mkiv/node-syn.lua @@ -225,6 +225,8 @@ local blockedsuffixes = { mkii = true, mkiv = true, mkvi = true, + mkil = true, + mkli = true, mkix = true, mkxi = true, -- lfg = true, diff --git a/tex/context/base/mkiv/pack-rul.mkiv b/tex/context/base/mkiv/pack-rul.mkiv index 5fb5c4853..34db6eec1 100644 --- a/tex/context/base/mkiv/pack-rul.mkiv +++ b/tex/context/base/mkiv/pack-rul.mkiv @@ -272,10 +272,15 @@ \fi} \def\pack_framed_filled_box_round - {\back_ovalbox - \d_framed_target_wd \d_framed_target_ht \d_framed_target_dp - \d_framed_linewidth \p_framed_backgroundradius - \zerocount \plusone {\ifx\p_framed_backgroundcorner\v!round0\else\number\p_framed_backgroundcorner\fi}} + {\frule + type fill + width \d_framed_target_wd + height \d_framed_target_ht + depth \d_framed_target_dp + line \d_framed_linewidth + radius \p_framed_backgroundradius\space + corner {\p_framed_backgroundcorner} + \relax} \def\pack_framed_stroked_box {\edef\p_framed_framecorner{\framedparameter\c!framecorner}% @@ -296,10 +301,14 @@ % \pack_framed_stroked_box_normal % later \def\pack_framed_stroked_box_round - {\back_ovalbox - \d_framed_target_wd \d_framed_target_ht \d_framed_target_dp - \d_framed_linewidth \p_framed_frameradius - \plusone \zerocount {\ifx\p_framed_framecorner\v!round0\else\number\p_framed_framecorner\fi}} + {\frule + width \d_framed_target_wd + height \d_framed_target_ht + depth \d_framed_target_dp + line \d_framed_linewidth + radius \p_framed_frameradius\space + corner {\p_framed_backgroundcorner} + \relax} % a lot of weird corners % diff --git a/tex/context/base/mkiv/publ-imp-apa.mkvi b/tex/context/base/mkiv/publ-imp-apa.mkvi index 36e4882d2..5603b79a5 100644 --- a/tex/context/base/mkiv/publ-imp-apa.mkvi +++ b/tex/context/base/mkiv/publ-imp-apa.mkvi @@ -703,20 +703,20 @@ apa:edition={Utgåva}, apa:Editor=Redaktör, apa:Editors=Redaktörer, - apa:Volume=Band, + apa:Volume=Band, apa:Volumes=Band, apa:nd={u.å.}, % utan årtal apa:supplement=Bilaga, % Supplement apa:MotionPicture=Spelfilm, % ? apa:Writer={Manusförfattare}, % Assuming for a movie - apa:Writers={Manusförfattare}, % + apa:Writers={Manusförfattare}, % apa:Producer=Producent, % Assuming for a movie - apa:Producers=Producenter, % + apa:Producers=Producenter, % apa:Director={Regissör}, % Assuming for a movie - apa:Directors={Regissörer}, % + apa:Directors={Regissörer}, % apa:Recordedby={Inspelad av}, % Assuming for a movie apa:Author={Författare}, - apa:Translator={Översättare}, + apa:Translator={Översättare}, apa:Advanced={Avancerad onlinepublikation}, % ? apa:Retrieved={Hämtad från}] diff --git a/tex/context/base/mkiv/publ-imp-chicago.lua b/tex/context/base/mkiv/publ-imp-chicago.lua new file mode 100644 index 000000000..cda76f52a --- /dev/null +++ b/tex/context/base/mkiv/publ-imp-chicago.lua @@ -0,0 +1,560 @@ +local specification = { + -- + -- metadata + -- + name = "chicago", + version = "1.00", + comment = "Chicago Manual of Style specification", + author = "Alan Braslau and Hans Hagen", + copyright = "ConTeXt development team", + -- + -- derived (combinations of) fields (all share the same default set) + -- + virtual = { + "authoryear", + "authoryears", + "authornum", + "num", + "suffix", + }, + -- + -- special datatypes + -- + types = { + -- + -- list of fields that are interpreted as names: "NAME [and NAME]" where + -- NAME is one of the following: + -- + -- First vons Last + -- vons Last, First + -- vons Last, Jrs, First + -- Vons, Last, Jrs, First + -- + author = "author", -- interpreted as name(s) + withauthor = "author", + editor = "author", + translator = "author", + artist = "author", + composer = "author", + producer = "author", + director = "author", + doi = "url", -- an external link + url = "url", + page = "pagenumber", -- number or range: f--t + pages = "pagenumber", + volume = "range", + number = "range", + keywords = "keyword", -- comma|-|separated list + year = "number", + }, + -- + -- categories with their specific fields + -- + categories = { + -- + -- categories are added below + -- + }, +} + +local generic = { + -- + -- A set returns the first field (in order of position below) that is found + -- present in an entry. A set having the same name as a field conditionally + -- allows the substitution of an alternate field. + -- + -- note that anything can get assigned a doi or be available online. + doi = { "doi", "url" }, + editionset = { "edition", "volume", "number", "pages" }, +} + +-- Definition of recognized categories and the fields that they contain. +-- Required fields should be present; optional fields may also be rendered; +-- all other fields will be ignored. + +-- Sets contain either/or in order of precedence. +-- +-- For a category *not* defined here yet present in the dataset, *all* fields +-- are taken as optional. This allows for flexibility in the addition of new +-- categories. + +local categories = specification.categories + +-- an article from a journal + +categories.article = { + sets = { + author = { "author", "organization", "editor", "title" }, + doi = generic.doi, + }, + required = { + "author", -- a set + }, + optional = { + "withauthor", "translator", + "year", + "subtitle", "type", "file", + "journal", "volume", "number", "pages", + "doi", "note", + -- APA ignores this: + -- + -- "month", + -- + -- fields defined in jabref but presently ignored: + -- + -- "issn", + }, +} + +-- an article from a magazine + +categories.magazine = { + sets = categories.article.sets, + required = { + "author", + "year", + "journal", + }, + optional = { + "withauthor", "translator", + "subtitle", "type", "file", + "number", + "month", "day", + "doi", "note", + }, +} + +categories.newspaper = categories.magazine + +-- (from jabref) to be identified and setup ... + +categories.periodical = { + sets = { + author = { "editor", "publisher", "organization", }, + doi = generic.doi, + }, + required = { + "title", + "year", + }, + optional = { + "author", "withauthor", "translator", + "subtitle", "file", + "series", "volume", "number", "month", + "organization", + "doi", "note", + }, +} + +-- (from jabref) to be identified and setup ... + +categories.standard = { + sets = { + author = { "author", "institution", "organization" }, + doi = generic.doi, + }, + required = { + "author", + "year", + "title", "subtitle", + "doi", "note", + }, + optional = { + "withauthor", "translator", + }, +} + +-- a book with an explicit publisher. + +categories.book = { + sets = { + author = { "author", "editor", "publisher", "title" }, + ineditor = { "editor" }, + editionset = generic.editionset, + doi = generic.doi, + }, + required = { "author" }, + optional = { + "ineditor", + "withauthor", "translator", + "year", "month", "day", + "subtitle", "type", "file", + "editionset", "series", + "address", + "doi", "note", + "abstract", + }, +} + +-- a part of a book, which may be a chapter (or section or whatever) and/or a range of pages. + +categories.inbook = { + sets = { + author = { "author", "organization", "editor", "publisher", "title", }, + ineditor = { "editor" }, + editionset = generic.editionset, + doi = generic.doi, + }, + required = { + "author", + "year" , + }, + optional = { + "ineditor", + "withauthor", "translator", + "subtitle", "type", "file", + "booktitle", "subbooktitle", + -- APA ignores this: "chapter", + "editionset", "series", + "month", + "address", + "doi", "note", + }, +} + +-- a book having its own title as part of a collection. +-- (like inbook, but we here make booktitle required) + +categories.incollection = { + sets = { + author = { "author", "editor", "publisher", "title", }, + ineditor = { "editor" }, + editionset = generic.editionset, + doi = generic.doi, + }, + required = { + "author", + "booktitle", + "year", + }, + optional = { + "ineditor", + "withauthor", "translator", + "subtitle", "type", "file", + "subbooktitle", + "editionset", "series", + -- APA ignores this: "chapter", + "month", + "address", + "doi", "note", + }, +} + +-- a work that is printed and bound, but without a named publisher or sponsoring institution. + +categories.booklet = { + sets = { + author = { "author", "title", }, + publisher = { "howpublished" }, -- no "publisher"! + doi = generic.doi, + }, + required = { + "author" + }, + optional = { + "withauthor", "translator", + "publisher", + "year", "month", + "subtitle", "type", "file", + "address", + "doi", "note", + }, +} + +-- the proceedings of a conference. + +categories.proceedings = { + sets = { + author = { "editor", "organization", "publisher", "title" }, -- no "author"! + publisher = { "publisher", "organization" }, + editionset = generic.editionset, + doi = generic.doi, + }, + required = { + "author", + "year" + }, + optional = { + "withauthor", "translator", + "publisher", + "subtitle", "file", + "editionset", "series", + "month", + "address", + "doi", "note", + }, +} + +-- an article in a conference proceedings. + +categories.inproceedings = { + sets = categories.incollection.sets, + required = categories.incollection.required, + optional = { + "withauthor", "translator", + "subtitle", "type", "file", + "month", + "edition", "series", + "address", "organization", + "doi", "note", + }, +} + +categories.conference = categories.inproceedings + +-- a thesis (of course). + +categories.thesis = { + sets = { + doi = generic.doi, + }, + required = { + "author", + "title", + "school", + "year", + "type" + }, + optional = { + "withauthor", "translator", + "subtitle", "file", + "month", + "address", + "doi", "note", + }, +} + +categories.mastersthesis = { + sets = categories.thesis.sets, + required = { + "author", + "title", + "school", + "year" + }, + optional = { + "withauthor", "translator", + "type", + "subtitle", "file", + "month", + "address", + "doi", "note", + }, +} +categories.phdthesis = categories.mastersthesis + +-- a report published by a school or other institution, usually numbered within a series. + +categories.techreport = { + sets = { + author = { "author", "institution", "publisher", "title" }, + publisher = { "publisher", "institution", }, + editionset = { "type", "volume", "number", "pages" }, -- no "edition"! + doi = generic.doi, + }, + required = { + "author", + "title", + "institution", + "year" + }, + optional = { + "withauthor", "translator", + "publisher", + "address", + "subtitle", "file", + "editionset", + "month", + "doi", "note", + }, +} + +-- technical documentation. + +categories.manual = { + sets = { + author = { "author", "organization", "publisher", "title" }, + publisher = { "publisher", "organization", }, + editionset = generic.editionset, + doi = generic.doi, + }, + required = { + "title" + }, + optional = { + "author", "publisher", + "withauthor", "translator", + "address", + "subtitle", "file", + "editionset", "month", "year", + "doi", "note", +-- "abstract", + }, +} + +-- a patent (of course). + +categories.patent = { + sets = { + author = { "author", "assignee", }, + publisher = { "publisher", "assignee", }, + year = { "year", "yearfiled", }, + month = { "month", "monthfiled", }, + day = { "day", "dayfiled", }, + doi = generic.doi, + }, + required = { + "nationality", + "number", + "year", + }, + optional = { + "type", + --check this: "language", + "author", "publisher", + "withauthor", "translator", + "title", "subtitle", "file", + "address", + "day", "month", + "doi", "note" + }, +} + +-- a document having an author and title, but not formally published. + +categories.unpublished = { + sets = { + doi = generic.doi, + }, + required = { + "author", + "title", + "note" + }, + optional = { + "withauthor", "translator", + "subtitle", "file", + "year", "month", + "doi" + }, +} + +-- like misc below but includes organization. + +categories.electronic = { + sets = { + doi = generic.doi, + author = { "author", "organization", }, + }, + required = { + "title" + }, + optional = { + "subtitle", "type", "file", + "year", "month", + "author", "withauthor", "translator", + "address", + "organization", + "howpublished", + "doi", "note" + }, +} + +-- not bibtex categories... + +categories.film = { + sets = { + doi = generic.doi, + author = { "author", "producer", "director", }, + }, + required = { + "author", + "title", + "year", + "address", "publisher", -- aka studio + }, + optional = { + "withauthor", "translator", + "type", + "note", + "doi", + }, +} + +categories.music = { + sets = { + doi = generic.doi, + author = { "composer", "artist", "title", "album" }, + title = { "title", "album", }, + }, + required = { + "author", + "title", + "year", + "address", "publisher", -- aka label + }, + optional = { + "withauthor", "translator", + "type", + "note", + "doi", + }, +} + +-- use this type when nothing else fits. + +categories.misc = { + sets = { + doi = generic.doi, + }, + required = { + -- nothing is really important here + }, + optional = { + "author", "withauthor", "translator", + "title", "subtitle", "file", + "year", "month", + "howpublished", + "doi", "note", + }, +} + +-- other (whatever jabref does not know!) + +categories.other = { + sets = { + doi = generic.doi, + }, + required = { + "author", + "title", + "year" + }, + optional = { + "withauthor", "translator", + "subtitle", "file", + "doi", "note", + }, +} + +-- if all else fails to match: + +categories.literal = { + sets = { + author = { "key" }, + doi = generic.doi, + }, + required = { + "author", + "text" + }, + optional = { + "withauthor", "translator", + "doi", "note" + }, + virtual = false, +} + +-- done + +return specification diff --git a/tex/context/base/mkiv/publ-imp-chicago.mkvi b/tex/context/base/mkiv/publ-imp-chicago.mkvi new file mode 100644 index 000000000..867535ca6 --- /dev/null +++ b/tex/context/base/mkiv/publ-imp-chicago.mkvi @@ -0,0 +1,1686 @@ +%D \module +%D [ file=publ-imp-chicago, +%D version=2019.05.20, +%D title=Chicago bibliography style, +%D subtitle=Publications, +%D author=Alan Braslau and Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is therefore copyrighted +%D by \PRAGMA. See mreadme.pdf for details. + +\startbtxrenderingdefinitions[chicago] + +\ifdefined\c!translate \else \def\c!translate{translate} \fi + +%D Reference: +%D \startTEX +%D @Book{Chicago2010, +%D title ={The Chicago Manual of Style}, +%D subtitle ={The Essential Guide for Writers, Editors, and Publishers}, +%D year ={2010}, +%D edition ={Sixteenth}, +%D address ={Chicago and London}, +%D publisher={The University of Chicago Press}, +%D Xpages ={1026}, +%D url ={https://www.chicagomanualofstyle.org/}, +%D } +%D \stopTEX + +% set ALL specific Chicago compliant values + +\definebtx + [chicago] + [\c!default=default, + \c!specification=chicago, + \c!otherstext={\btxspace\btxlabeltext{others}}, + %\c!journalconversion=\v!normal, + \c!monthconversion=\v!month, + \c!stopper:initials={. }, % with a (breakable) space + \c!separator:names:2={\btxcomma}, % aka namesep - in this namespace + \c!separator:names:3={\btxcomma\btxlabeltext{and}\space}, % comma separated list + \c!separator:names:4={\btxspace\btxlabeltext{and}\space}] % last of two, no comma! + +% First, define list and rendering parameters + +% The Chicago style sorts the unnumbered rendered list by authoryear + +\definebtxrendering + [chicago] + [\c!specification=chicago, + \c!sorttype=authoryear, + \c!numbering=\v!no] + +\setupbtxlist + [chicago] + [\c!alternative=\v!paragraph, + \c!align={normal,verytolerant,stretch}, + %\c!width=\v!fit, + %\c!distance=.5\emwidth, + \c!margin=3.5\emwidth] + +\definebtx + [chicago:\s!list] + [chicago] + %[\c!otherstext={\btxcomma\btxnobreakspace\textellipsis\space}, + % \c!etallimit=7, + % \c!etaldisplay=6, + % \c!etaloption=last, + [\c!authorconversion=inverted] + +% First, we define a namespace for a few special fields + +\definebtx + [chicago:\s!list:author] + [chicago:\s!list] + +\definebtx + [chicago:\s!list:withauthor] + [chicago:\s!list:author] + +\definebtx + [chicago:\s!list:editor] + [chicago:\s!list:author] + +\definebtx + [chicago:\s!list:ineditor] + [chicago:\s!list:editor] + [\c!authorconversion=normalshort] + +\definebtx + [chicago:\s!list:translator] + [chicago:\s!list:author] + [\c!authorconversion=normalshort] + +\definebtx + [chicago:\s!list:director] + [chicago:\s!list:author] + +\definebtx + [chicago:\s!list:producer] + [chicago:\s!list:author] + +\definebtx + [chicago:\s!list:suffix] + [chicago:\s!list] + +\definebtx + [chicago:\s!list:url] + [chicago:\s!list] + +\definebtx + [chicago:\s!list:doi] + [chicago:\s!list] + +\definebtx + [chicago:\s!list:\s!page] + [chicago:\s!list] + %[\c!separator:2={\btxcomma}, + % \c!separator:3={\btxcomma\btxlabeltext{and}\space}, + % \c!separator:4={\btxspace\btxlabeltext{and}\space}, + [\c!left={\btxleftparenthesis}, + \c!right={\btxrightparenthesis}, + \c!command={\wordright}] + +\definebtx + [chicago:\s!list:numbering] + [chicago:\s!list] + [\c!right={\btxspace}] + +\definebtx + [chicago:\s!list:numbering:default] + [chicago:\s!list:numbering] + +\definebtx + [chicago:\s!list:numbering:num] + [chicago:\s!list:numbering] + [\c!stopper={.}] + +\definebtx + [chicago:\s!list:numbering:short] + [chicago:\s!list:numbering] + +\definebtx + [chicago:\s!list:numbering:tag] + [chicago:\s!list:numbering] + +\definebtx + [chicago:\s!list:numbering:index] + [chicago:\s!list:numbering] + +% Next, we define a namespace for each category + +%D In order to be able to get journals expanded (or normalized or abbreviated) you need +%D to load a list: +%D +%D \starttyping +%D \btxloadjournallist[journals.txt] % the jabref list +%D \stoptyping + +\definebtx + [chicago:\s!list:journal] + [chicago:\s!list] + [\c!style=\v!italic] + %command=\btxexpandedjournal] % btxabbreviatedjournal + +\definebtx + [chicago:\s!list:volume] + [chicago:\s!list] + [\c!style=\v!italic] + +\definebtx + [chicago:\s!list:title] + [chicago:\s!list] + [\c!style=\v!italic, + \c!command=\Word, + \c!translate=\v!yes] + +\definebtx + [chicago:\s!list:title:article] + [chicago:\s!list:title] + [\c!style=] % journal is set in italics + +\definebtx + [chicago:\s!list:title:magazine] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:newspaper] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:periodical] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:standard] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:book] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:booktitle:inbook] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:inbook] + [chicago:\s!list:title] + [\c!style=] % booktitle is set in italics + +\definebtx + [chicago:\s!list:booktitle:incollection] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:incollection] + [chicago:\s!list:title] + [\c!style=] % booktitle is set in italics + +\definebtx + [chicago:\s!list:title:proceedings] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:booktitle:inproceedings] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:inproceedings] + [chicago:\s!list:title] + [\c!style=] % booktitle is set in italics + +\definebtx + [chicago:\s!list:booktitle:conference] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:conference] + [chicago:\s!list:title] + [\c!style=] % booktitle is set in italics + +\definebtx + [chicago:\s!list:title:thesis] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:phdthesis] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:mastersthesis] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:booklet] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:manual] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:techreport] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:unpublished] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:patent] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:electronic] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:music] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:film] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:other] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:misc] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:title:literal] + [chicago:\s!list:title] + +\definebtx + [chicago:\s!list:type] + [\c!command=\Word] + +% Then define and set cite parameters. + +\definebtx + [chicago:\s!cite] + [chicago] + [\c!alternative=authoryear, + \c!otherstext={\btxcomma\btxlabeltext{others}}, + \c!etallimit=1, + \c!etaldisplay=1, + \c!authorconversion=\v!name, + \c!sorttype=normal, % \v!normal ? + \c!style=, + \c!compress=\v!yes] % note that cite sorts only work with compress=yes. + +\definebtx + [chicago:\s!cite:name] + [chicago:\s!cite] + [\c!authorconversion=\v!name] + +\definebtx + [chicago:\s!cite:inverted] + [chicago:\s!cite] + [\c!authorconversion=\v!invertedshort] + +\definebtx + [chicago:\s!cite:invertedshort] + [chicago:\s!cite] + [\c!authorconversion=\v!invertedshort] + +\definebtx + [chicago:\s!cite:normalshort] + [chicago:\s!cite] + [\c!authorconversion=\v!normalshort] + +\definebtx + [chicago:\s!cite:normal] + [chicago:\s!cite] + [\c!authorconversion=\v!normal] + +\definebtx + [chicago:\s!cite:author] + [chicago:\s!cite] + +\definebtx + [chicago:\s!cite:editor] + [chicago:\s!cite:author] + +\definebtx + [chicago:\s!cite:translator] + [chicago:\s!cite:author] + +\definebtx + [chicago:\s!cite:organization] + [chicago:\s!cite] + +\definebtx + [chicago:\s!cite:authoryear] + [chicago:\s!cite:author] + [\c!left={(}, + \c!right={)}, + \c!inbetween={\btxspace}] + +\definebtx + [chicago:\s!cite:default] + [chicago:\s!cite:authoryear] + +\definebtx + [chicago:\s!cite:authoryears] + [chicago:\s!cite:author] + [\c!left=, % these two settings are perhaps redundant? + \c!right=, + \c!inbetween={\btxspace}] + +\definebtx + [chicago:\s!cite:authornum] + [chicago:\s!cite:author] + [\c!left={(}, + \c!right={)}] + +\definebtx + [chicago:\s!cite:author:num] % todo + [chicago:\s!cite:authornum] + [\c!left={\btxspace[}, + \c!right={]}] + +\definebtx + [chicago:\s!cite:author:year] % todo + [chicago:\s!cite] + +\definebtx + [chicago:\s!cite:author:years] % todo + [chicago:\s!cite:authoryears] + [\c!inbetween=, + \c!left={\btxspace(}, + \c!right={)}] + +\definebtx + [chicago:\s!cite:lefttext] + [chicago:\s!cite] + [\c!left=, + \c!right={\btxspace}] + +\definebtx + [chicago:\s!cite:righttext] + [chicago:\s!cite] + [\c!left={\btxcomma}, + \c!right=] + +\definebtx + [chicago:\s!cite:year] + [chicago:\s!cite] + [\c!separator:2={\btxcomma}, % :0 and :1 - between items of a list + \c!separator:3={\btxcomma\btxlabeltext{and}\space}, + \c!separator:4={\btxspace\btxlabeltext{and}\space}] + +\definebtx + [chicago:\s!cite:title] + [chicago:\s!cite] + [\c!separator:2={\btxcomma}, % :0 and :1 - between items of a list + \c!separator:3={\btxcomma\btxlabeltext{and}\space}, + \c!separator:4={\btxspace\btxlabeltext{and}\space}, + \c!command={\language[\currentbtxlanguage]}, % BAH + \c!sorttype=none, + \c!style=\v!italic] + +\definebtx + [chicago:\s!cite:subtitle] + [chicago:\s!cite:title] + +\definebtx + [chicago:\s!cite:booktitle] + [chicago:\s!cite:title] + +\definebtx + [chicago:\s!cite:subbooktitle] + [chicago:\s!cite:booktitle] + +% Will these get used? + +\definebtx + [chicago:\s!cite:title:inbook] + [chicago:\s!cite:title] + [\c!style=] % not italic + +\definebtx + [chicago:\s!cite:title:incollection] + [chicago:\s!cite:title:inbook] + +\definebtx + [chicago:\s!cite:title:inproceedings] + [chicago:\s!cite:title:inbook] + +\definebtx + [chicago:\s!cite:subtitle:inbook] + [chicago:\s!cite:title:inbook] + +\definebtx + [chicago:\s!cite:subtitle:incollection] + [chicago:\s!cite:title:incollection] + +\definebtx + [chicago:\s!cite:subtitle:inproceedings] + [chicago:\s!cite:title:inproceedings] + + +\definebtx + [chicago:\s!cite:tag] + [chicago:\s!cite] + [\c!left={[}, + \c!right={]}] + +\definebtx + [chicago:\s!cite:index] + [chicago:\s!cite] + [\c!left={[}, + \c!right={]}] + +\definebtx + [chicago:\s!cite:page] + [chicago:\s!cite] + [\c!left=, + \c!right=, + \c!separator:2={\btxcomma}, % :0 and :1 - between items of a list + \c!separator:3={\btxcomma\btxlabeltext{and}\space}, + \c!separator:4={\btxspace\btxlabeltext{and}\space}] + +\definebtx + [chicago:\s!cite:pages] + [chicago:\s!cite:page] + +\definebtx + [chicago:\s!cite:keywords] + [chicago:\s!cite] + +\definebtx + [chicago:\s!cite:short] + [chicago:\s!cite] + [\c!left={[}, + \c!right={]}] + +\definebtx + [chicago:\s!cite:category] + [chicago:\s!cite] + +\definebtx + [chicago:\s!cite:url] + [chicago:\s!cite] + [\c!left={(}, + \c!right={)}] + +\definebtx + [chicago:\s!cite:doi] + [chicago:\s!cite:url] + +\definebtx + [chicago:\s!cite:num] + [chicago:\s!cite] + [\c!left={[}, + \c!right={]}, + \c!separator:2={,}, % no space + \c!separator:3=\btxparameter{\c!separator:2}, + \c!separator:4=\btxparameter{\c!separator:2}] + +\definebtx + [chicago:\s!cite:textnum] + [chicago:\s!cite:num] + [\c!left={Ref.\nbsp}, + \c!right=, + \c!separator:2={\btxcomma}, + \c!separator:3={\btxspace\btxlabeltext{and}\space}, + \c!separator:4={\btxspace\btxlabeltext{and}\space}] + +\definebtx + [chicago:\s!cite:entry] + [chicago:\s!cite] + [\c!left=, + \c!right=, + \c!inbetween={\btxspace}, + \c!separator:2={\btxsemicolon}, + \c!separator:3=\btxparameter{\c!separator:2}, + \c!separator:4=\btxparameter{\c!separator:2}] + +\definebtx + [chicago:\s!cite:footnote] + [chicago:\s!cite:entry] + +% Now we setup for the details of the renderings + +%D Sometimes we have verbose injections in an entry and these can be language +%D dependent, so we use labels. +%D +%D Because we want to mix rendering (in the manual) we need a namespace in label +%D texts: + +\setupbtxlabeltext + [en] + [chicago:number={No.}, + chicago:edition={ed.}, + chicago:Editor={Ed.}, + chicago:Editors={Eds.}, + chicago:Volume={Vol.}, + chicago:Volumes={Vols.}, + chicago:nd={n.d.}, % no date + chicago:supplement={Suppl.}, % Supplement (not used?) + chicago:MotionPicture={Motion picture}, + chicago:Writer=Writer, + chicago:Writers=Writers, + chicago:Producer=Producer, + chicago:Producers=Producers, + chicago:Director=Director, + chicago:Directors=Directors, + chicago:Recordedby={Recorded by}, + chicago:Author=Author, + chicago:Translator={Trans.}, % Translator(s) + chicago:Advanced={Advanced online publication}, + chicago:Retrieved={Retrieved from}] % {Available from}] + +\setupbtxlabeltext + [nl] + [chicago:number={Nr.}, + chicago:edition={ed.}, % editie + chicago:Editor=Editor, % Ed./Eds. + chicago:Editors=Editors, + chicago:Volume={Vol.}, + chicago:Volumes={Vols.}, + chicago:nd={g.d.} % geen datum + chicago:supplement=Supplement, + chicago:MotionPicture=Film, % ? + chicago:Writer=Scenarioschrijver, % ? + chicago:Writers=Schrijvers, % ? + chicago:Producer=Producent, % ? + chicago:Producers=Producents, % ? + chicago:Director=Directeur, + chicago:Directors=Directeurs, + chicago:Recordedby={Opgenomen door}, % ? + chicago:Author=Auteur, + chicago:Translator=Vertaler, + chicago:Advanced={Geavanceerde online publicatie}, + chicago:Retrieved={Ontvangen van}] % {Beschikbaar vanaf}] + +\setupbtxlabeltext + [fr] + [chicago:number={N\high{o}}, + chicago:edition={édition}, + chicago:Editor=Éditeur, + chicago:Editors=Éditeurs, + chicago:Volume=Volume, + chicago:Volumes=Volumes, + chicago:nd={s.d.}, % sans date + chicago:supplement=Supplément, + chicago:MotionPicture={Film cinématographique}, + chicago:Writer=Scénariste, + chicago:Writers=Scénaristes, + chicago:Producer=Producteur, + chicago:Producers=Producteurs, + chicago:Director=Réalisateur, + chicago:Directors=Réalisateurs, + chicago:Recordedby={Enregistré par}, + chicago:Author=Auteur, + chicago:Translator=Traducteur, + chicago:Advanced={Publication en ligne anticipée}, + chicago:Retrieved={Téléchargé de}] % {Disponible à}] + +\setupbtxlabeltext + [de] + [chicago:number={Nr.}, + chicago:edition=Auf\/lage, + chicago:Editor=Herausgeber, % Hrsg./Hg. + chicago:Editors=Herausgeber, + chicago:Volume=Band, % Bd. + chicago:Volumes={Bände}, + chicago:nd={o.D.}, % ohne Datum (mostly: o.J. / ohne Jahr) + chicago:supplement={Beilage}, % Supplement + chicago:MotionPicture=Kinofilm, % ? + chicago:Writer=Drehbuchautor, % ? + chicago:Writers=Schriftsteller, % ? + chicago:Producer=Producer, % ? + chicago:Producers=Produzenten, % ? + chicago:Director=Director, % ? + chicago:Directors=Directors, % ? + chicago:Recordedby={per Einschreiben}, % ? + chicago:Author=Autor, + chicago:Translator={Übersetzer}, % Übers. + chicago:Advanced={Erweiterte Online-Publikation}, + chicago:Retrieved={heruntergeladen von}] + +% thanks: Andrea Valle + +\setupbtxlabeltext + [it] + [chicago:number={Nº}, + chicago:edition={ed.}, % edizione + chicago:Editor={A cura di}, + chicago:Editors={A cura di}, + chicago:Volume={Vol.}, % Volume + chicago:Volumes={Vol.}, % Volumi + chicago:nd={s.d.}, % senza data + chicago:supplement={Supplemento}, + chicago:MotionPicture=Film, % ? + chicago:Writer=Sceneggiatore, % ? + chicago:Writers=Scrittori, % ? + chicago:Producer=Produttore, + chicago:Producers=Produttori, + chicago:Director=Direttore, + chicago:Directors=Direttori, + chicago:Recordedby={Registrato da}, + chicago:Author=Autore, + chicago:Translator={Trad.}, % Translator(s) + chicago:Advanced={Pre-pubblicazione on line}, + chicago:Retrieved={Accessible online}] + +\setupbtxlabeltext + [es] + [chicago:number={Nº}, + chicago:edition={ed.}, % edición + chicago:Editor=Editor, % Ed./Eds. + chicago:Editors=Editores, + chicago:Volume={Vol.}, % Volumen + chicago:Volumes={Vols.}, % Volúmenes + chicago:nd={s.f.}, % sin fecha + chicago:supplement=Suplemento, + chicago:MotionPicture=Cinematográfica, + chicago:Writer=Guionista, % ? + chicago:Writers=Escritores, % ? + chicago:Producer=Productor, + chicago:Producers=Productores, + chicago:Director=Director, + chicago:Directors=Directores, + chicago:Recordedby={Grabada por}, + chicago:Author=Autor, + chicago:Translator=Traductor, + chicago:Advanced={Publicación en línea avanzada}, + chicago:Retrieved={Obtenido de}] % {Disponible desde}] + + +\setupbtxlabeltext + [sv] + [chicago:number={nr.}, + chicago:edition={Utgåva}, + chicago:Editor=Redaktör, + chicago:Editors=Redaktörer, + chicago:Volume=Band, + chicago:Volumes=Band, + chicago:nd={u.å.}, % utan årtal + chicago:supplement=Bilaga, % Supplement + chicago:MotionPicture=Spelfilm, % ? + chicago:Writer={Manusförfattare}, % Assuming for a movie + chicago:Writers={Manusförfattare}, % + chicago:Producer=Producent, % Assuming for a movie + chicago:Producers=Producenter, % + chicago:Director={Regissör}, % Assuming for a movie + chicago:Directors={Regissörer}, % + chicago:Recordedby={Inspelad av}, % Assuming for a movie + chicago:Author={Författare}, + chicago:Translator={Översättare}, + chicago:Advanced={Avancerad onlinepublikation}, % ? + chicago:Retrieved={Hämtad från}] + +% cite setups + +% The following differs from the default returning n.d. if year is empty + +\startsetups btx:chicago:cite:author:year + \ifx\currentbtxfirst\empty + \def\currentbtxfirst{\fastsetup{\s!btx:chicago:nd}} + \fi + \fastsetup{\s!btx:\s!cite:author:year} +\stopsetups + +\startsetups btx:chicago:cite:author:years + \ifx\currentbtxfirst\empty + \def\currentbtxfirst{\fastsetup{\s!btx:chicago:nd}} + \fi + \fastsetup{\s!btx:\s!cite:author:years} +\stopsetups + +% these setups need to be explicitly defined in order to get cite rendering + +\startsetups \s!btx:chicago:\s!cite:organization + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:subtitle + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:booktitle + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:subbooktitle + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +% are these needed? + +\startsetups \s!btx:chicago:\s!cite:title:inbook + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:title:incollection + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:title:inproceedings + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:subtitle:inbook + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:subtitle:incollection + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +\startsetups \s!btx:chicago:\s!cite:subtitle:inproceedings + \fastsetup{\s!btx:\s!cite:normal} +\stopsetups + +% used in publ-imp-page.mkvi + +\startsetups btx:chicago:list:page-or-pages + \ifx\currentbtxlastpage\empty + \btxlabeltext{p} + \else + \btxlabeltext{pp} + \fi + \btxnbsp +\stopsetups + +% The sameauthor feature may not be Chicago compliant +% (there is nothing in the manual cited above). +% It can be removed using the command: +% \resetsetups [chicago:list:sameauthor] + +% :rule, :empty or :ditto ... + +\startsetups chicago:list:sameauthor + \fastsetup{chicago:list:sameauthor:rule} +\stopsetups + +\startsetups chicago:list:sameauthor:rule + \blackrule + [\c!width=3em, + \c!height=1.5\linewidth]% \linewidth is just too thin with respect to font strokes... +\stopsetups + +\startsetups [chicago:list:sameauthor:\v!empty] + \kern\dimexpr\listparameter\c!margin-\interwordspace\relax +\stopsetups + +% horrible ! + +\startsetups chicago:list:sameauthor:ditto + \inframed + [\c!width=\dimexpr\listparameter\c!margin-\interwordspace\relax, + \c!frame=\v!off, + \c!align=\v!middle] + {\doubleprime} +\stopsetups + +%D Instead of texdefinitions without arguments, we could have used setups but in my +%D editor (hh, scite) the commands stand out better. It also saves an additional +%D component in the name (e.g. common:) because commands and setups have a different +%D namespace, so similar calls don't clash. Performance of definitions is somewhat +%D better. + +%D We use "texdefinitions" (with eventual arguments) for helpers that are used +%D in the rendering "setups" defined for each category below. + +%D Note that \btxdoif... and \btxflush rely on the definitions in +%D publ-imp-chicago.lua: fields that are not listed as required nor optional are +%D IGNORED. We also make heavy use of the notion of sets - comma-separated lists +%D of alternative fields to be used in hierarchal order. For example: +%D author = { "author", "editor", "publisher", "title" }, will return the +%D author field if it exists; if not, the editor field will be returned, if it +%D exists; if not, the publisher field will be returned, if it exists; if not, +%D the title field will be returned, it it exists; if not, nothing will be +%D returned. In lua syntax, it can be understood as +%D author or editor or publisher or title or "" + +% #title can be title or booktitle + +\starttexdefinition unexpanded btx:chicago:translated-title #title + \ifx\currentbtxlanguage\empty + % no need for an extra + \else\ifx\mainbtxlanguage\currentbtxlanguage + % no need for an extra + \else + \btxdoif {#title:\mainbtxlanguage} { + \begingroup + \language[\mainbtxlanguage] + \btxleftbracket + \btxusecommand [chicago:\s!list:#title:\currentbtxcategory] { + \btxflush{#title:\mainbtxlanguage} + } + \btxrightbracket + \endgroup + } + \fi\fi +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:composed-title #title + \btxstartstyleandcolor[chicago:\s!list:#title:\currentbtxcategory] + \begingroup + \language[\currentbtxlanguage] + \btxusecommand [chicago:\s!list:#title:\currentbtxcategory] { + \btxflush{#title} + \btxdoif {sub#title} { + \btxcolon + \btxflush{sub#title} + } + } + \endgroup + % which namespace? + %\doif{\btxparameter{translate}}\v!yes { + \texdefinition{btx:chicago:translated-title}{#title} + %} + \btxstopstyleandcolor +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:title + \setmode{btx:chicago:title-placed} + % we make the title active, opening "file" + \btxdoifelse {file} { + \texdefinition{btx:format:inject} + {url(file:\btxflush{file})} + { + \texdefinition{btx:chicago:composed-title}{title} + } + } { + \texdefinition{btx:chicago:composed-title}{title} + } + \btxdoif {title} { + % A book might have an editor AND an author + \doif {\currentbtxcategory} {book} { + \doifnot {\btxfoundname{author}} {editor} { + \btxdoif {ineditor} { % ineditor specific authorconversion + \btxleftparenthesis + \btxflush{ineditor} + \btxcomma + \btxsingularorplural {ineditor} { + \btxlabeltext{chicago:Editor} + } { + \btxlabeltext{chicago:Editors} + } + \btxrightparenthesis + } + } + } + \btxdoif {translator} { + \btxleftparenthesis + \btxflush{translator} + \btxcomma + \btxlabeltext{chicago:Translator} + \btxrightparenthesis + } + \btxperiod + % TODO: this period may NOT be wanted, as in: Title (2nd ed.). + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:title-if-not-placed + \doifelsemode {btx:chicago:title-placed} { + \resetmode{btx:chicago:title-placed} + } { + \btxdoif {title} { + \btxspace + \texdefinition {btx:chicago:title} + } + } +\stoptexdefinition + +\startsetups btx:chicago:nd + \btxlabeltext{chicago:nd} +\stopsetups + +\starttexdefinition unexpanded btx:chicago:suffixedyear + \btxdoifelse {year} { + \btxflush{year} + \btxflushsuffix + \btxdoif {month} { % month and day optional in publ-imp-chicago.lua + \btxcomma + \btxflush{month} + \btxdoif {day} { + \btxspace + \btxflush{day} + } + } + } { + \fastsetup{btx:chicago:nd} + % Hans: the following won't work but should. + \btxdoif {suffix} { + \btxspace + \btxflushsuffix + % Hans: similarly, why can't \btxflush{suffix} be made to work? + } + } +\stoptexdefinition + +% #author may be author(set) or editor + +\starttexdefinition unexpanded btx:chicago:author-or-editor #author + \btxdoifelse {#author} { + \btxstartstyleandcolor[chicago:\s!list:#author] + \btxusecommand[chicago:\s!list:#author]{ + \btxflush{#author} + } + \btxstopstyleandcolor + % use \processaction [] [] here? + \doifelse {\btxfoundname{#author}} {editor} { + \btxleftparenthesis + \btxsingularorplural {editor} { + \btxlabeltext{chicago:Editor} + } { + \btxlabeltext{chicago:Editors} + } + \btxrightparenthesis + } {\doif {\btxfoundname{#author}} {ineditor} { + \btxleftparenthesis + \btxsingularorplural {ineditor} { + \btxlabeltext{chicago:Editor} + } { + \btxlabeltext{chicago:Editors} + } + \btxrightparenthesis + } } + \doif {\currentbtxcategory} {film} { + \btxleftparenthesis + \doifelse {\btxfoundname{#author}} {director} { + \btxsingularorplural {director} { + \btxlabeltext{chicago:Director} + } { + \btxlabeltext{chicago:Directors} + } + } { + \doif {\btxfoundname{#author}} {author} { + \btxsingularorplural {author} { + \btxlabeltext{chicago:Writer} + } { + \btxlabeltext{chicago:Writers} + } + } + \doif {\btxfoundname{#author}} {producer} { + \btxsingularorplural {producer} { + \btxlabeltext{chicago:Producer} + } { + \btxlabeltext{chicago:Producers} + } + } + \btxdoif {director} { + \btxrightparenthesis + \removeunwantedspaces + \btxparameter{\c!separator:names:3} + \btxstartstyleandcolor[chicago:\s!list:director] + \btxusecommand[chicago:\s!list:director]{ + \btxflush{director} + } + \btxstopstyleandcolor + \btxleftparenthesis + \btxsingularorplural {director} { + \btxlabeltext{chicago:Director} + } { + \btxlabeltext{chicago:Directors} + } + } + } + \btxrightparenthesis + } + \btxdoif {withauthor} { + \btxleftparenthesis + \btxlabeltext{with} + \btxspace + \btxstartstyleandcolor[chicago:\s!list:withauthor] + \btxusecommand[chicago:\s!list:withauthor]{ + \btxflush{withauthor} + } + \btxstopstyleandcolor + \btxrightparenthesis + } + } { + \texdefinition{btx:chicago:title} + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:authoryear + % we make the authoryear active, pointing to the citation + \texdefinition{btx:format:inject} + {internal(\currentbtxinternal)} + { + \doifelsesetups{chicago:list:sameauthor} { + \btxdoifelsesameasprevious {author} { + \fastsetup{chicago:list:sameauthor} + } { + \texdefinition{btx:chicago:author-or-editor} {author} + } + } { + \texdefinition{btx:chicago:author-or-editor} {author} + } + \texdefinition{btx:chicago:suffixedyear} + } + % outside of interaction + \btxperiod + \doif {\btxfoundname{author}} {title} { + \setmode{btx:chicago:title-placed} + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:editor-in + \btxdoif {booktitle} { + \btxlabeltext{In} + \btxspace + \doifnot {\btxfoundname{author}} {editor} { + \texdefinition{btx:chicago:author-or-editor} {ineditor} + \btxcomma + } + \texdefinition{btx:chicago:composed-title}{booktitle} + \btxperiod + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:organization-if-not-author + \btxdoif {organization} { + \doifnot {\btxfoundname{author}} {organization} { + \btxspace + \btxflush{organization} + \btxcomma + } + } +\stoptexdefinition + +% TODO: The title is terminated with period. However, +% we probably don't want this before the parenthesis. + +\starttexdefinition unexpanded btx:chicago:leftparenthesis-or-comma + \doifelsemode {btx:chicago:editionset-is-empty} { + \btxleftparenthesis + \resetmode{btx:chicago:editionset-is-empty} + } { + \btxcomma + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:editionset + \setmode{btx:chicago:editionset-is-empty} + \doif {\currentbtxcategory} {techreport} { + \texdefinition{btx:chicago:leftparenthesis-or-comma} + \btxdoifelse {type} { + \btxusecommand [chicago:\s!list:type] { + \btxflush{type} + } + } { + \btxlabeltext{technicalreport} + } + } + \btxdoif {volume} { + \texdefinition{btx:chicago:leftparenthesis-or-comma} + \btxoneorrange {volume} { + \btxlabeltext{chicago:Volume} + } { + \btxlabeltext{chicago:Volumes} + } + \btxspace + \btxflush{volume} + } + \btxdoif {number} { + \texdefinition{btx:chicago:leftparenthesis-or-comma} + \btxlabeltext{chicago:number} + \btxspace + \btxflush{number} + } + \btxdoif {edition} { + \texdefinition{btx:chicago:leftparenthesis-or-comma} + \btxflush{edition} + \btxspace + \btxlabeltext{chicago:edition} + } + \btxdoif {pages} { + \texdefinition{btx:chicago:leftparenthesis-or-comma} + \btxoneorrange {pages} { + \btxlabeltext{p} + } { + \btxlabeltext{pp} + } + \btxnbsp + \btxflush{pages} + } + \doifnotmode {btx:chicago:editionset-is-empty} { + \btxrightparenthesisperiod + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:journal + \btxstartstyleandcolor[chicago:\s!list:journal] + \btxusecommand [chicago:\s!list:journal] { + \btxflush{journal} + } + \btxstopstyleandcolor +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:volume + \btxstartstyleandcolor[chicago:\s!list:volume] + \btxflush{volume} + \btxstopstyleandcolor +\stoptexdefinition + + % this could be simplified! + +\starttexdefinition unexpanded btx:chicago:journal-volume-number-pages + \btxdoif {journal} { + \btxspace + \texdefinition{btx:chicago:journal} + \btxdoifelse {volume} { + \btxcomma + \texdefinition{btx:chicago:volume} + \btxdoif {number} { + %\btxleftparenthesis + (\btxflush{number} + \btxrightparenthesis + } + } { + \btxdoif {number} { + \btxcomma + \btxleftparenthesis + \btxflush{number} + \btxrightparenthesis + } + } + \btxdoif {pages} { + \btxcomma + \doif {\currentbtxcategory} {newspaper} { + \btxoneorrange {pages} { + \btxlabeltext{p} + } { + \btxlabeltext{pp} + } + \btxnbsp + } + \btxflush{pages} + } + \btxperiod + \doifnot {\currentbtxcategory} {newspaper} { + \btxdoifnot {volume} { + \btxdoifnot {number} { + \btxdoifnot {pages} { + \btxdoif {doi} {%set: doi or url + \btxspace + \btxlabeltext{chicago:Advanced} + \btxperiod + } + } + } + } + } + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:wherefrom-publisher + \btxdoifelse {address} { + \btxflush{address} + \btxdoif {country} { + \btxcomma + \btxflush{country} + } + \btxcolon + } { + \btxdoif {country} { + \btxflush{country} + \btxcolon + } + } + \doifelse {\btxfoundname{author}} {\btxfoundname{publisher}} { + \btxlabeltext{chicago:Author} + } { + \btxdoifelse {publisher} { + \btxflush{publisher} + } { + \btxlabeltext{chicago:Author} + } + } + \btxperiod +\stoptexdefinition + +\definebreakpoints[doi] +\definebreakpoint [doi][:][nleft=3,type=1] +\definebreakpoint [doi][/][nleft=3,type=1] +\definebreakpoint [doi][-][nleft=3,type=1] +\definebreakpoint [doi][.][nleft=3,type=1] + +% use \btxentry here? + +\starttexdefinition unexpanded btx:chicago:url + \begingroup + \setbreakpoints[doi] + \ifconditional\btxinteractive + \goto { + \hyphenatedurl{\btxflush{url}} + } [ + url(\btxflush{url}) + ] + \else + \hyphenatedurl{\btxflush{url}} + \fi + \endgroup +\stoptexdefinition + +% use \btxentry here? + +\starttexdefinition unexpanded btx:chicago:doi + \begingroup + \setbreakpoints[doi] + \ifconditional\btxinteractive + \goto { + \hyphenatedurl{doi:\btxflush{doi}} + } [ + url(http://dx.doi.org/\btxflush{doi}) + ] + \else + \hyphenatedurl{doi:\btxflush{doi}} + \fi + \endgroup +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:note + \btxdoif {note} { + \btxleftparenthesis + \btxflush{note} + \btxrightparenthesis + } +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:url-doi-note + \doif {\btxfoundname{doi}} {url} { + \btxspace + \btxlabeltext{chicago:Retrieved} + \btxspace + \texdefinition{btx:chicago:url} + } + \doif {\btxfoundname{doi}} {doi} { + \btxspace + \texdefinition{btx:chicago:doi} + } + \texdefinition{btx:chicago:note} + \removeunwantedspaces +\stoptexdefinition + +\starttexdefinition unexpanded btx:chicago:type + \btxdoif {type} { + \btxleftbracket + \btxflush{type} + \btxrightbracketperiod + } +\stoptexdefinition + +% Then setups, by category + +% An article from a journal +% Required fields: author or organization or editor or title, journal, (year). +% Optional fields: volume, number, pages, type, doi, url, note. +% Note that bibtex (and tools) do not include editor (e.g. special issue or section) + +\startsetups btx:chicago:list:article + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \texdefinition{btx:chicago:type} + \texdefinition{btx:chicago:journal-volume-number-pages} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% An article from a magazine. +% Required fields: author or title, journal, (year). +% Optional fields: number, pages, type, month, day, doi, url, note. + +\startsetups btx:chicago:list:magazine + \fastsetup{btx:chicago:list:article} +\stopsetups + +% An article from a newspaper. +% Required fields: author or title, journal, (year). +% Optional fields: volume, number, pages, type, month, day, doi, url, note. + +\startsetups btx:chicago:list:newspaper + \fastsetup{btx:chicago:list:article} +\stopsetups + +% A complete issue of a periodical, such as a special issue of a journal. +% Required fields: title, year +% Optional fields: editor, publisher, subtitle, series, volume, number, month, organization, doi, url, issn, note + +% needs to be tuned... + +\startsetups btx:chicago:list:periodical + \fastsetup{btx:chicago:list:article} +\stopsetups + +% National and international standards issued by a standards body +% Required fields: author, institution, or organization, year, title +% Optional fields: subtitle, doi, url, note + +\startsetups btx:chicago:list:standard + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% A book with an explicit publisher. +% Required fields: author or editor or publisher, title, (year). +% Optional fields: volume or number, series, address, edition, month, day, note. +% Chicago ignores: month, day + +% todo: series? + +\startsetups btx:chicago:list:book + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \texdefinition{btx:chicago:type} + \texdefinition{btx:chicago:editionset} + \texdefinition{btx:chicago:wherefrom-publisher} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% There is some debate about how inbook should differ from incollection + +% A part of a book, which may be a chapter (or section or whatever) and/or a range of pages. +% (note that inbook is handled differently by bibtex and biblatex) +% Required fields: author or editor, title, chapter and/or pages, publisher, year. +% Optional fields: volume or number, series, type, address, edition, month, note. +% We add optional: booktitle. +% Chicago ignores: chapter, month + +\startsetups btx:chicago:list:inbook + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \texdefinition{btx:chicago:type} + \texdefinition{btx:chicago:editor-in} + \texdefinition{btx:chicago:editionset} + \texdefinition{btx:chicago:wherefrom-publisher} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% A part of a book having its own title. +% Required fields: author, title, booktitle, publisher, year. +% Optional fields: editor, volume or number, series, type, chapter, pages, address, edition, month, note. +% Chicago ignores: chapter, month + +\startsetups btx:chicago:list:incollection + \fastsetup{btx:chicago:list:inbook} +\stopsetups + +% The proceedings of a conference. +% Required fields: title, year. +% Optional fields: editor, volume or number, series, address, month, organization, publisher, note. +% todo: series? + +\startsetups btx:chicago:list:proceedings + \fastsetup{btx:chicago:list:book} +\stopsetups + +% An article in a conference proceedings. +% Required fields: author, title, booktitle, year. +% Optional fields: editor, volume or number, series, pages, address, month, organization, publisher, note. + +\startsetups btx:chicago:list:inproceedings + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \texdefinition{btx:chicago:editor-in} + \texdefinition{btx:chicago:editionset} + \texdefinition{btx:chicago:organization-if-not-author} + \texdefinition{btx:chicago:wherefrom-publisher} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +\startsetups btx:chicago:list:conference + \fastsetup{btx:chicago:list:inproceedings} +\stopsetups + +% A thesis. +% Required fields: author, title, school, year. +% Optional fields: type, address, month, note. + +\startsetups btx:chicago:list:thesis + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \btxleftparenthesis + \btxdoifelse {type} { + \btxusecommand [chicago:\s!list:type] { + \btxflush{type} + } + } { + \btxlabeltext{\currentbtxcategory} + } + \btxrightparenthesis + \btxdoif {school} { + \btxperiod + \btxflush{school} + } + \btxdoif {address} { + \btxdoifelse {school} { + \btxcomma + } { + \btxperiod + } + \btxflush{address} + \btxdoif {country} { + \btxcomma + \btxflush{country} + } + } + \btxperiod + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +\startsetups btx:chicago:list:phdthesis + \fastsetup{btx:chicago:list:thesis} +\stopsetups + +\startsetups btx:chicago:list:mastersthesis + \fastsetup{btx:chicago:list:thesis} +\stopsetups + +% A work that is printed and bound, but without a named publisher or sponsoring institution. +% Required field: title. +% Optional fields: author, howpublished, address, month, year, note. + +\startsetups btx:chicago:list:booklet + \fastsetup{btx:chicago:list:book} +\stopsetups + +% Technical documentation. +% Required field: title. +% Optional fields: author, organization, address, edition, month, year, note. + +\startsetups btx:chicago:list:manual + \fastsetup{btx:chicago:list:book} +\stopsetups + +% A report published by a school or other institution, usually numbered within a series. +% Required fields: author, title, institution, year. +% Optional fields: type, number, address, month, note. + +\startsetups btx:chicago:list:techreport + \fastsetup{btx:chicago:list:book} +\stopsetups + +% A document having an author and title, but not formally published. +% Required fields: author, title, note. +% Optional fields: month, year. + +\startsetups btx:chicago:list:unpublished + \fastsetup{btx:chicago:list:book} +\stopsetups + +% A patent. Note that this category was not defined with BIBTEX. Below from JabRef: +% Required fields: nationality, number, year, yearfiled +% Optional fields: author, title, assignee, address, type, number, day, dayfiled, month, monthfiled, note, url +% Also optional: publisher + +% todo: yearfiled, monthfiled, dayfiled + +\startsetups btx:chicago:list:patent + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \begingroup + \it + \btxdoif {nationality} { + \btxspace + \btxflush{nationality} + } + \btxspace + \btxdoifelse{type}{ + \btxflush{type} + }{ + \btxlabeltext{patent} + } + \btxdoif {number} { + \btxspace + \btxlabeltext{chicago:number} + \btxspace + \btxflush{number} + } + \btxperiod + \italiccorrection + \endgroup + \texdefinition{btx:chicago:wherefrom-publisher} + \texdefinition{btx:chicago:url} + \texdefinition{btx:chicago:note} +\stopsetups + +% Electronic. Note that this category was not defined with BIBTEX. Below from JabRef: +% Required fields: title +% Optional fields: address, author, howpublished, month, note, organization, url, year, doi +% Also optional: type + +% Like Misc below but includes organization. + +\startsetups btx:chicago:list:electronic + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \texdefinition{btx:chicago:type} + \texdefinition{btx:chicago:organization-if-not-author} + \btxdoif {howpublished} { + \btxspace + \btxflush{howpublished} + \btxperiod + } + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% Film. Note that this category was not defined with BIBTEX. +% Required fields: producer, director, title, year, address, publisher +% Optional fields: subtitle, type, note, url, doi + +\startsetups btx:chicago:list:film + \texdefinition{btx:chicago:authoryear} + \texdefinition {btx:chicago:title} + \btxleftbracket + \btxdoifelse {type} { + \btxflush{type} + } { + \btxlabeltext{chicago:MotionPicture} + } + \btxrightbracketperiod + \texdefinition{btx:chicago:wherefrom-publisher} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% Music. Note that this category was not defined with BIBTEX. +% Required fields: composer, artist, title, album, year, address, publisher +% Optional fields: subtitle, type, note, url, doi + +\startsetups btx:chicago:list:music + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \doifnot {\btxfoundname{author}} {artist} { + \btxdoif {artist} { + \btxleftbracket + \btxlabeltext{chicago:Recordedby} + \btxspace + \btxflush{artist} + \btxrightbracketperiod + } + } + \doifnot {\btxfoundname{title}} {album} { + \btxdoif {album} { + \btxlabeltext{In} + \btxspace + \btxflush{album} + \btxperiod + } + } + \texdefinition{btx:chicago:type} + \texdefinition{btx:chicago:wherefrom-publisher} + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% Other. Note that this category was not defined with BIBTEX. Below from JabRef: +% Required fields: author or title, year +% Optional fields: note, doi, url + +\startsetups btx:chicago:list:other + \fastsetup{btx:chicago:list:book} +\stopsetups + +% Use this category when nothing else fits. +% Required fields: none. +% Optional fields: author, title, howpublished, month, year, note. + +\startsetups btx:chicago:list:misc + \texdefinition{btx:chicago:authoryear} + \texdefinition{btx:chicago:title-if-not-placed} + \btxdoif {howpublished} { + \btxspace + \btxflush{howpublished} + \btxperiod + } + \texdefinition{btx:chicago:url-doi-note} +\stopsetups + +% If all else fails to match: + +\startsetups btx:chicago:list:literal + %\btxleftparenthesis + \removeunwantedspaces( + \btxflush{tag} + \btxrightparenthesis + \btxdoif {text} { + \btxflush{text} + } +\stopsetups + +% HH: an example of setting up translations using a sub rendering. Keep it here +% till we find another spot as otherwise I forget about it and I don't want to +% waste hours reinventing a wheel when something like this is needed. +% +% \definebtx +% [chicago:cite:title:translated] +% [chicago:cite:title] +% [left=\btxleftbracket, +% right=\btxrightbracket, +% style=\v!bolditalic] +% +% \startsetups btx:chicago:cite:title +% % need to add concat, etc. +% \btxcitereference +% \currentbtxfirst +% \doifmode {btx:chicago:translatedtitles} { +% \ifx\currentbtxlanguage\empty +% % no need for an extra +% \else\ifx\mainbtxlanguage\currentbtxlanguage +% % no need for an extra +% \else +% \btxdoif {title:\mainbtxlanguage} { +% \btxstartciterendering[title:translated] +% \language[\mainbtxlanguage] +% \btxflush{title:\mainbtxlanguage} +% \btxstopciterendering +% } +% \fi\fi +% } +% \stopsetups + +\stopbtxrenderingdefinitions diff --git a/tex/context/base/mkiv/publ-imp-cite.mkvi b/tex/context/base/mkiv/publ-imp-cite.mkvi index 56af83a1b..3a01b219a 100644 --- a/tex/context/base/mkiv/publ-imp-cite.mkvi +++ b/tex/context/base/mkiv/publ-imp-cite.mkvi @@ -168,6 +168,12 @@ \fastsetup{\s!btx:\s!cite:righttext} \stopsetups +\startsetups \s!btx:\s!cite:footnote + \startfootnote + \fastsetup{btx:cite:entry} + \stopfootnote +\stopsetups + % these three are goodies to get something but are not set up as it makes no % sense to have something root for combinations like this (esp not because one % gets default anyway diff --git a/tex/context/base/mkiv/publ-ini.lua b/tex/context/base/mkiv/publ-ini.lua index f62352f07..aa55b40e9 100644 --- a/tex/context/base/mkiv/publ-ini.lua +++ b/tex/context/base/mkiv/publ-ini.lua @@ -502,13 +502,18 @@ local findallused do local luadata = current.luadata local details = current.details local ordered = current.ordered - if allused then + if allused then -- always true local registered = { } local function register(tag) local entry = forcethem and luadata[tag] if entry then if registered[tag] then + if trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"already cited (1)") + end return + elseif trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"okay") end okay[#okay+1] = entry -- todo[tag] = true @@ -516,6 +521,9 @@ local findallused do return tag end entry = allused[tag] + if trace_cite then + report_cite("dataset: %s, tag: %s, used: % t",dataset,tag,table.sortedkeys(allused)) + end if not entry then local parent = details[tag].parent if parent then @@ -524,9 +532,16 @@ local findallused do if entry then report("using reference of parent %a for %a",parent,tag) tag = parent + elseif trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"not used") end + elseif trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"used") end if registered[tag] then + if trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"already cited (2)") + end return end if entry then @@ -564,7 +579,17 @@ local findallused do entry = entry[1] end end + if not entry then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"no entry (1)") + elseif trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"new entry") + end okay[#okay+1] = entry + elseif not entry then + if trace_cite then + report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"no entry (2)") + end + -- okay[#okay+1] = luadata[tag] end todo[tag] = true registered[tag] = true @@ -1712,7 +1737,6 @@ do end end - -- tag | listindex | reference | userdata | dataindex local methods = { } @@ -1794,7 +1818,7 @@ do if traced then local l = traced[tag] if l then - l[#l+1] = u.btxint + l[#l+1] = u.btxint -- tonumber ? else local data = luadata[tag] local l = { tag, listindex, 0, u, data and data.index or 0 } diff --git a/tex/context/base/mkiv/publ-ini.mkiv b/tex/context/base/mkiv/publ-ini.mkiv index ead46929d..8e70ec5a7 100644 --- a/tex/context/base/mkiv/publ-ini.mkiv +++ b/tex/context/base/mkiv/publ-ini.mkiv @@ -1080,7 +1080,7 @@ \relax} \unexpanded\def\btx_reference_checked - {\dontleavehmode\hbox\bgroup + {\dontleavehmode\hbox\bgroup % \hpack \btx_reference_indeed \egroup} diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf index 0547dc574..328354ee5 100644 Binary files a/tex/context/base/mkiv/status-files.pdf and b/tex/context/base/mkiv/status-files.pdf differ diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf index 63e611ca1..94132234a 100644 Binary files a/tex/context/base/mkiv/status-lua.pdf and b/tex/context/base/mkiv/status-lua.pdf differ diff --git a/tex/context/base/mkiv/syst-aux.mkiv b/tex/context/base/mkiv/syst-aux.mkiv index 4fbcbd0e6..d6f54f7d0 100644 --- a/tex/context/base/mkiv/syst-aux.mkiv +++ b/tex/context/base/mkiv/syst-aux.mkiv @@ -4231,8 +4231,8 @@ \stoplmtxmode -%D These macros can explictly take care of spaces, which means -%D that the next definition and calls are valid: +%D These macros can explictly take care of spaces, which means that the next +%D definition and calls are valid: %D %D \starttyping %D \def\test#1#2#3{[#1#2#3]} @@ -4673,8 +4673,7 @@ \unexpanded\def\newcounter#1% {\dodoglobal\let#1\zerocountervalue} -%D Nowadays we don't mind a few more tokens if we can gain a -%D bit of speed. +%D Nowadays we don't mind a few more tokens if we can gain a bit of speed. \def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\plusone \relax}} \def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}} @@ -4870,24 +4869,22 @@ #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}% \fi} -%D As we can see here, the simple command \type{\dorecurse} is -%D a special case of the more general: +%D As we can see here, the simple command \type{\dorecurse} is a special case of the +%D more general: %D %D \starttyping %D \dostepwiserecurse {from} {to} {step} {action} %D \stoptyping %D -%D This commands accepts positive and negative steps. Illegal -%D values are handles as good as possible and the macro accepts -%D numbers and \COUNTERS. +%D This commands accepts positive and negative steps. Illegal values are handles as +%D good as possible and the macro accepts numbers and \COUNTERS. %D %D \starttyping %D \dostepwiserecurse {1} {10} {2} {...} %D \dostepwiserecurse {10} {1} {-2} {...} %D \stoptyping %D -%D Because the simple case is used often, we implement it -%D more efficiently: +%D Because the simple case is used often, we implement it more efficiently: \unexpanded\def\dorecurse#1% {\ifcase#1\relax @@ -7952,9 +7949,8 @@ %D \freezedimensionwithunit\SomeDepth{\dp\strutbox} %D \stoptyping %D -%D As an alternative for the next macro we can use a global -%D assignment inside a box. The \type{\empty}'s permits -%D gobbling while preventing spurious \type{\relax}'s. +%D As an alternative for the next macro we can use a global assignment inside a box. +%D The \type {\empty}'s permits gobbling while preventing spurious \type {\relax}'s. \unexpanded\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty} diff --git a/tex/context/base/mkiv/toks-scn.lua b/tex/context/base/mkiv/toks-scn.lua index 297ef7121..f259bcee5 100644 --- a/tex/context/base/mkiv/toks-scn.lua +++ b/tex/context/base/mkiv/toks-scn.lua @@ -144,6 +144,26 @@ scanners.list = scanlist scanners.table = scantable scanners.conditional = scanconditional +function scanners.whd() + local width, height, depth + while true do + if scankeyword("width") then + width = scandimen() + elseif scankeyword("height") then + height = scandimen() + elseif scankeyword("depth") then + depth = scandimen() + else + break + end + end + if width or height or depth then + return width or 0, height or 0, depth or 0 + else + -- we inherit + end +end + local shortcuts = { tokens = tokens, bits = tokenbits, diff --git a/tex/context/base/mkiv/util-sbx.lua b/tex/context/base/mkiv/util-sbx.lua index 57c576870..d5cf691ce 100644 --- a/tex/context/base/mkiv/util-sbx.lua +++ b/tex/context/base/mkiv/util-sbx.lua @@ -637,16 +637,7 @@ end -- these are used later on if zip then - zip.open = register(zip.open, filehandlerone,"zip.open") -end - -if fontloader then - fontloader.open = register(fontloader.open,filehandlerone,"fontloader.open") - fontloader.info = register(fontloader.info,filehandlerone,"fontloader.info") -end - -if epdf then - epdf.open = register(epdf.open, filehandlerone,"epdf.open") + zip.open = register(zip.open, filehandlerone,"zip.open") end sandbox.registerroot = registerroot diff --git a/tex/context/base/mkiv/util-sci.lua b/tex/context/base/mkiv/util-sci.lua index fe28635a3..4565e089d 100644 --- a/tex/context/base/mkiv/util-sci.lua +++ b/tex/context/base/mkiv/util-sci.lua @@ -33,14 +33,27 @@ do end local knownlexers = { - tex = "tex", mkiv = "tex", mkvi = "tex", mkxi = "tex", mkix = "tex", mkii = "tex", cld = "tex", - lua = "lua", lfg = "lua", lus = "lua", - mp = "mps", mpiv = "mps", mpii = "mps", - w = "web", ww = "web", - c = "cpp", h = "cpp", cpp = "cpp", hpp = "cpp", cxx = "cpp", hxx = "cpp", - xml = "xml", lmx = "xml", ctx = "xml", xsl = "xml", xsd = "xml", rlx = "xml", css = "xml", dtd = "xml", - bib = "bibtex", - rme = "txt", + tex = "tex", + mkiv = "tex", mkvi = "tex", + mkil = "tex", mkli = "tex", + mkxi = "tex", mkix = "tex", + mkii = "tex", + bib = "bibtex", + cld = "tex", + lua = "lua", + lfg = "lua", lus = "lua", luv = "lua", + mp = "mps", + mpiv = "mps", + mpii = "mps", + w = "web", ww = "web", + c = "cpp", h = "cpp", + cpp = "cpp", hpp = "cpp", + cxx = "cpp", hxx = "cpp", + xml = "xml", xsl = "xml", xsd = "xml", dtd = "xml", + lmx = "xml", ctx = "xml", rlx = "xml", + css = "xml", + rme = "txt", + txt = "txt", -- todo: pat/hyp ori } diff --git a/tex/context/fonts/mkiv/type-imp-ebgaramond.mkiv b/tex/context/fonts/mkiv/type-imp-ebgaramond.mkiv index bba07f41d..5cafe503f 100644 --- a/tex/context/fonts/mkiv/type-imp-ebgaramond.mkiv +++ b/tex/context/fonts/mkiv/type-imp-ebgaramond.mkiv @@ -13,6 +13,19 @@ % Why so many features ... dead slow too +% renewed: +% +% Regular +% Italic +% Medium +% MediumItalic +% SemiBold +% SemiBoldItalic +% Bold +% BoldItalic +% ExtraBold +% ExtraBoldItalic + \definefontfeature [eb-garamond-normal] [default] @@ -29,17 +42,32 @@ \starttypescriptcollection[ebgaramond] + % \starttypescript [serif] [ebgaramond] + % \loadfontgoodies[ebgaramond] + % \setups[font:fallback:serif] + % \definefontsynonym [Serif] [file:ebgaramond-regular] [features=eb-garamond-normal] + % \definefontsynonym [SerifItalic] [file:ebgaramond-italic] [features=eb-garamond-normal] + % \definefontsynonym [SerifBold] [file:ebgaramond-regular] [features=eb-garamond-normal] % there is no bold + % \definefontsynonym [SerifCaps] [Serif] [features=eb-garamond-smallcaps] + % \stoptypescript + + % \starttypescript[ebgaramond] + % \definetypeface [ebgaramond] [rm] [serif] [ebgaramond] [default] [designsize=auto] + % \definetypeface [ebgaramond] [tt] [mono] [dejavu] [default] [rscale=0.8] % rather arbitrary but seldom mixed anyway + % \definetypeface [ebgaramond] [mm] [math] [bonum] [default] [rscale=0.8] % rather arbitrary but seldom mixed anyway + % \stoptypescript + \starttypescript [serif] [ebgaramond] - \loadfontgoodies[ebgaramond] \setups[font:fallback:serif] - \definefontsynonym [Serif] [file:ebgaramond-regular] [features=eb-garamond-normal] - \definefontsynonym [SerifItalic] [file:ebgaramond-italic] [features=eb-garamond-normal] - \definefontsynonym [SerifBold] [file:ebgaramond-regular] [features=eb-garamond-normal] % there is no bold - \definefontsynonym [SerifCaps] [Serif] [features=eb-garamond-smallcaps] + \definefontsynonym [Serif] [file:ebgaramond-regular] [features=eb-garamond-normal] + \definefontsynonym [SerifItalic] [file:ebgaramond-italic] [features=eb-garamond-normal] + \definefontsynonym [SerifBold] [file:ebgaramond-bold] [features=eb-garamond-normal] + \definefontsynonym [SerifBoldItalic][file:ebgaramond-bolditalic][features=eb-garamond-normal] + \definefontsynonym [SerifCaps] [Serif] [features=eb-garamond-smallcaps] \stoptypescript \starttypescript[ebgaramond] - \definetypeface [ebgaramond] [rm] [serif] [ebgaramond] [default] [designsize=auto] + \definetypeface [ebgaramond] [rm] [serif] [ebgaramond] [default] \definetypeface [ebgaramond] [tt] [mono] [dejavu] [default] [rscale=0.8] % rather arbitrary but seldom mixed anyway \definetypeface [ebgaramond] [mm] [math] [bonum] [default] [rscale=0.8] % rather arbitrary but seldom mixed anyway \stoptypescript diff --git a/tex/context/interface/mkii/keys-de.xml b/tex/context/interface/mkii/keys-de.xml index 701dc246b..e76ea9340 100644 --- a/tex/context/interface/mkii/keys-de.xml +++ b/tex/context/interface/mkii/keys-de.xml @@ -376,6 +376,7 @@ + diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf index 3d5205666..e1f854a34 100644 Binary files a/tex/context/interface/mkiv/i-context.pdf and b/tex/context/interface/mkiv/i-context.pdf differ diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf index 7b6a077ef..007fc0e61 100644 Binary files a/tex/context/interface/mkiv/i-readme.pdf and b/tex/context/interface/mkiv/i-readme.pdf differ diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 8a940cc22..4379ead13 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 05/18/19 10:42:24 +-- merge date : 05/25/19 10:45:25 do -- begin closure to overcome local limits and interference @@ -31241,32 +31241,17 @@ do local f_used=context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ] local hashed={} local cache={} - if epdf then - local openpdf=epdf.openMemStream - function otf.storepdfdata(pdf) - local done=hashed[pdf] - if not done then - nofstreams=nofstreams+1 - local o,n=openpdf(pdf,#pdf,f_name(nofstreams)) - cache[n]=o - done=f_used(n) - hashed[pdf]=done - end - return done - end - else - local openpdf=pdfe.new - function otf.storepdfdata(pdf) - local done=hashed[pdf] - if not done then - nofstreams=nofstreams+1 - local f=f_name(nofstreams) - local n=openpdf(pdf,#pdf,f) - done=f_used(n) - hashed[pdf]=done - end - return done - end + local openpdf=pdfe.new + function otf.storepdfdata(pdf) + local done=hashed[pdf] + if not done then + nofstreams=nofstreams+1 + local f=f_name(nofstreams) + local n=openpdf(pdf,#pdf,f) + done=f_used(n) + hashed[pdf]=done + end + return done end end local function pdftovirtual(tfmdata,pdfshapes,kind) -- cgit v1.2.3