summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/lpdf-lmt.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/lpdf-lmt.lmt')
-rw-r--r--tex/context/base/mkiv/lpdf-lmt.lmt2840
1 files changed, 0 insertions, 2840 deletions
diff --git a/tex/context/base/mkiv/lpdf-lmt.lmt b/tex/context/base/mkiv/lpdf-lmt.lmt
deleted file mode 100644
index 2bbf5ba61..000000000
--- a/tex/context/base/mkiv/lpdf-lmt.lmt
+++ /dev/null
@@ -1,2840 +0,0 @@
-if not modules then modules = { } end modules ['lpdf-lmt'] = {
- version = 1.001,
- optimize = true,
- 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. I will rename variables.
---
--- There is no way that a lua based backend can compete with the original one
--- for relative simple text runs. And we're talking seconds here on say 500
--- pages with paragraphs alternativng between three fonts and colors. But such
--- documents are rare so in practice we are quite okay, especially because in
--- ConTeXt we can gain quite a bit elsewhere. So, when we loose 30% on such
--- simple documents, we break even on for instance the manual, and gain 30% on
--- Thomas's turture test (also for other reasons). But .. who knows what magic
--- I can cook up in due time.
-
--- 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. At that moment I will
--- also optimize and extend.
-
-local type, next, unpack, tonumber, rawget = type, next, unpack, tonumber, rawget
-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, sortedhash = table.concat, table.sortedhash
-local setmetatableindex = table.setmetatableindex
-local loaddata = io.loaddata
-
-local bpfactor = number.dimenfactors.bp
-
-local md5HEX = md5.HEX
-local osuuid = os.uuid
-local zlibcompress = (xzip or zlib).compress
-
-local nuts = nodes.nuts
-local tonut = nodes.tonut
-
-local getdata = nuts.getdata
-local getsubtype = nuts.getsubtype
-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 pdfsharedobject = lpdf.shareobjectreference
-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 descriptions = fonthashes.descriptions
-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, usedxgroups
-local getxformname, getximagename
-local boundingbox, shippingmode, objectnumber
-local tmrx, tmry, tmsx, tmsy, tmtx, tmty
-local cmrx, cmry, cmsx, cmsy, cmtx, cmty
-local tmef
-
-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.0, 1.0
- cmsx, cmsy = 0.0, 0.0
- cmtx, cmty = 0.0, 0.0
- tmrx, tmry = 1.0, 1.0
- tmsx, tmsy = 0.0, 0.0
- tmtx, tmty = 0.0, 0.0
- tmef = 1.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.0
- f_cur = 0
- f_pdf_cur = 0 -- nullfont
- f_pdf = 0 -- nullfont
- fs_cur = 0
- fs = 0
- cur_factor = 0
- cur_f = false
- cur_e = false
- tj_delta = 0.0
- cw = 0.0
- usedfonts = setmetatableindex(usefont)
- usedxforms = { }
- usedximages = { }
- -- usedxgroups = { }
- boundingbox = specification.boundingbox
-end
-
--- buffer
-
-local buffer = lua.newtable(1024,0) -- { }
-local b = 0
-
-local function reset_buffer()
- b = 0
-end
-
--- fonts
-
-local fontcharacters
-local fontdescriptions
-local fontparameters
-local fontproperties
-local usedcharacters = setmetatableindex("table")
-local pdfcharacters
-
-local horizontalmode = true
------ widefontmode = true
-local scalefactor = 1
-local threshold = 655360
-local thresfactor = 100
-local tjfactor = 100 / 65536
-
-lpdf.usedcharacters = usedcharacters
-
-local function updatefontstate(font)
- fontcharacters = characters[font]
- fontdescriptions = descriptions[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
- local fthreshold = fontproperties.threshold
- threshold = (fthreshold and (size * fthreshold / 100)) or 655360
- -- when we bolden the threshold should be smaller .. a hack .. i need to redo all this
- if (fontparameters.extendfactor or 1) == 1 then
- -- we're probably okay
- elseif fontparameters.hshift or fontparameters.vshift then
- -- we could be okay
- else
- -- some vf magic going on
- threshold = threshold / 5
- end
-end
-
--- helpers
-
-local f_cm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"]
-local f_tm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N Tm"]
-
-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] = "<"
- mode = "char"
-end
-
-local function end_charmode()
- b = b + 1 ; buffer[b] = ">"
- mode = "chararray"
-end
-
-local function calc_pdfpos(h,v)
- -- mostly char
- if mode == "page" then
- cmtx = h - pdf_h
- cmty = v - pdf_v
- return h ~= pdf_h or v ~= pdf_v
- elseif mode == "text" then
- tmtx = h - saved_text_pos_h
- tmty = v - saved_text_pos_v
- return h ~= pdf_h or v ~= pdf_v
- elseif horizontalmode then
- tmty = v - saved_text_pos_v
- tj_delta = cw - h
- return tj_delta ~= 0 or v ~= pdf_v
- else
- tmtx = h - saved_text_pos_h
- tj_delta = cw + v
- return tj_delta ~= 0 or h ~= pdf_h
- end
-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()
- 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
-
--- these dummy returns makes using them a bit faster
-
-local function pdf_end_string_nl()
- if mode == "char" then
- end_charmode()
- return end_chararray()
- elseif mode == "chararray" then
- return end_chararray()
- end
-end
-
-local function pdf_goto_textmode()
- if mode == "page" then
- pdf_reset_pos()
- return begin_text()
- elseif mode ~= "text" then
- if mode == "char" then
- end_charmode()
- return end_chararray()
- else -- if mode == "chararray" then
- return end_chararray()
- end
- end
-end
-
-local function pdf_goto_pagemode()
- if mode ~= "page" then
- if mode == "char" then
- end_charmode()
- end_chararray()
- return end_text()
- elseif mode == "chararray" then
- end_chararray()
- return end_text()
- elseif mode == "text" then
- return 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_reset_pos()
- mode = "page"
-end
-
--- characters
-
-local flushcharacter do
-
- local round = math.round
-
- -- across pages ... todo: clean up because we don't need to pass the font
- -- as fontparameters already has checked / set it we can also have a variable
- -- for it so
-
- local naturalwidth = nil
- local hshift = false
- local vshift = false
-
- -- local naturalwidths = setmetatableindex(function(t,font)
- -- local d = descriptions[font]
- -- local c = characters[font]
- -- local f = parameters[font].hfactor
- -- local v = setmetatableindex(function(t,char)
- -- local w
- -- local e = d and d[char]
- -- if e then
- -- w = e.width
- -- if w then
- -- w = w * f
- -- end
- -- end
- -- if not w then
- -- e = c[char]
- -- if e then
- -- w = e.width or 0
- -- end
- -- end
- -- if not w then
- -- w = 0
- -- end
- -- t[char] = w
- -- return w
- -- end)
- -- t[font] = v
- -- return v
- -- end)
-
- local naturalwidths = setmetatableindex(function(t,font)
- local d = descriptions[font]
- local c = characters[font]
- local f = parameters[font].hfactor
- local v = setmetatableindex(function(t,char)
- local w
- local e = c[char]
- if e then
- w = e.width or 0
- end
- if not w then
- e = d and d[char]
- if e then
- w = e.width
- if w then
- w = w * f
- end
- end
- end
- if not w then
- w = 0
- end
- t[char] = w
- return w
- end)
- t[font] = v
- return v
- end)
-
- local function setup_fontparameters(font,factor,f,e)
- local slant = fontparameters.slantfactor or 0
- local extend = fontparameters.extendfactor or 1
- local squeeze = fontparameters.squeezefactor or 1
- local expand = 1 + factor / 1000000
- local format = fontproperties.format
- if e then
- extend = extend * e
- end
- tmef = expand
- tmrx = expand * extend
- tmsy = slant
- tmry = squeeze
- need_width = fontparameters.width or 0
- need_mode = fontparameters.mode or 0
- 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
- --
- naturalwidth = naturalwidths[font]
- --
- hshift = fontparameters.hshift
- vshift = fontparameters.vshift
- end
-
- local f_width = formatters["%.6N w"]
- local f_mode = formatters["%i Tr"] -- can be hash
- local f_font = formatters["/F%i %.6N Tf"] -- can be hash
-
- local s_width = "0 w"
- local s_mode = "0 Tr"
-
- local width_factor = 72.27 / 72000.0
-
- local function set_font()
- -- if need_width and need_width ~= 0 then
- if need_width ~= 0 then
- b = b + 1 ; buffer[b] = f_width(width_factor*need_width)
- 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
- if 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)
- 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_hex_4 = formatters["%04X"]
- local f_hex_2 = formatters["%02X"]
-
- local h_hex_4 = setmetatableindex(function(t,k) -- we already have this somewhere
- if k < 256 then -- maybe 512
- -- not sparse in this range
- for i=0,255 do
- t[i] = f_hex_4(i)
- end
- return t[k]
- else
- local v = f_hex_4(k)
- t[k] = v
- return v
- end
- end)
- local h_hex_2 = setmetatableindex(function(t,k) -- we already have this somewhere
- local v = k < 256 and f_hex_2(k) or "00"
- t[k] = v
- return v
- end)
-
- -- local trace_threshold = false trackers.register("backends.pdf.threshold", function(v) trace_threshold = v end)
-
- -- local f_skip = formatters["%.2N"]
-
- -- I will redo this mess ... we no longer have the mkiv pdf generator that we used in
- -- luatex (a precursor to lmtx and also for comparison) but only in lmtx now so ...
- -- time to move on I guess.
-
- flushcharacter = function(current,pos_h,pos_v,pos_r,font,char,data,f,e,factor) -- ,naturalwidth,width)
- 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 trace_threshold then
- -- report(
- -- "font %i, char %C, factor %i, naturalwidth %p, move %l, tm %l, hpos %p, delta %p, threshold %p, cw %p",
- -- font,char,factor,naturalwidth[char],move,need_tm,pos_h,tj_delta,threshold,cw
- -- )
- -- end
-
- if move or need_tm then
- if not need_tm then
- if horizontalmode then
- 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 hshift then pos_h = pos_h + hshift end
- if vshift then pos_v = pos_v - vshift 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 = tj_delta * scalefactor
- if d <= -0.5 or d >= 0.5 then
- if mode == "char" then
- end_charmode()
- end
- b = b + 1 ; buffer[b] = round(d) -- or f_skip(d)
- end
- cw = cw - tj_delta
- end
- end
-
- if mode == "chararray" then
- begin_charmode()
- end
-
- cw = cw + naturalwidth[char] * tmef
-
- local index = data.index or char
-
- b = b + 1 ; buffer[b] = font > 0 and h_hex_4[index] or h_hex_2[index]
-
- if not pdfcharacters[index] then
- pdfcharacters[index] = true
- end
-
- end
-
- flushfontchar = function(font,char,data)
- local dummy = usedfonts[font]
- local index = data.index or char
- if not pdfcharacters[index] then
- pdfcharacters[index] = true
- end
- return dummy
- end
-
-end
-
--- literals
-
-local flushliteral do
-
- local nodeproperties = nodes.properties.data
- 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
-
- flushliteral = 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
- 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
- report("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)
- -- This only works inside objects, don't change this to flush
- -- in between. It's different from luatex but okay.
- 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 flushsave, flushrestore, flushsetmatrix do
-
- local matrices = { }
- local positions = { }
- local nofpositions = 0
- local nofmatrices = 0
-
- local f_matrix = formatters["%s 0 0 cm"]
-
- flushsave = 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
-
- flushrestore = 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 > 0 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
-
- flushsetmatrix = 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)
- -- flushsave(false,pos_h,pos_v)
- -- if orientation == 1 then
- -- flushsetmatrix("0 -1 1 0",pos_h,pos_v)
- -- elseif orientation == 2 then
- -- flushsetmatrix("-1 0 0 -1",pos_h,pos_v)
- -- elseif orientation == 3 then
- -- flushsetmatrix("0 1 -1 0",pos_h,pos_v)
- -- end
- -- end
-
- -- poporientation = function(orientation,pos_h,pos_v,pos_r)
- -- flushrestore(false,pos_h,pos_v)
- -- end
-
-end
-
--- rules
-
-local flushedxforms = { } -- actually box resources but can also be direct
-local localconverter = nil -- will be set
-
-local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup 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 f_gr = formatters["/Gp%d Do"]
-
- local s_b = "q"
- local s_e = "Q"
-
- local f_v = formatters["[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S"]
- local f_h = formatters["[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S"]
-
- local f_f = formatters["0 0 %.6N %.6N re f"]
- local f_o = formatters["[] 0 d 0 J 0 0 %.6N %.6N re S"]
- local f_w = formatters["[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S"]
-
- local f_b = formatters["%.6N w 0 %.6N %.6N %.6N re f"]
- local f_x = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S"]
-
- -- 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
- report("no box resource %S",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
- localconverter(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
- report("no box resource %S",index)
- end
- end
-
- local function getboxresourcedimensions(index)
- local l = boxresources[index]
- if l then
- return l.width, l.height, l.depth, l.margin
- else
- report("no box resource %S",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
- report("no image resource %S",index)
- end
- end
-
- updaters.register("backend.update.pdf",function()
- pdf.getximagename = getximagename
- end)
-
- -- Groups are flushed immediately but we can decide to make them into a
- -- specific whatsit ... but not now. We could hash them if needed when
- -- we use lot sof them in mp ... but not now.
-
- usedxgroups = { }
- local groups = 0
- local group = nil
-
- flushgroup = function(content,bbox)
- if not group then
- group = pdfdictionary {
- Type = pdfconstant("Group"),
- S = pdfconstant("Transparency"),
- }
- end
- local wrapper = pdfdictionary {
- Type = pdf_xobject,
- Subtype = pdf_form,
- FormType = 1,
- Group = group,
- BBox = pdfarray(bbox),
- Resources = lpdf.collectedresources { serialize = false },
- }
- local objnum = pdfflushstreamobject(content,wrapper,false)
- groups = groups + 1
- usedxgroups[groups] = objnum
- return f_gr(groups)
- end
-
- lpdf.flushgroup = flushgroup -- todo: access via driver in mlib-pps
-
- -- end of experiment
-
- 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, -- transform / orientation / rotation : it's a mess (i need to redo this)
- objnum,
- groupref = pdfincludeimage(index) -- needs to be sorted out, bad name (no longer mixed anyway)
-
- if not kind then
- report("invalid image %S",index)
- return
- end
-
- local rx, sx, sy, ry, tx, ty = 1, 0, 0, 1, 0, 0
-
- -- tricky: xsize and ysize swapped
-
- 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
-
- flushimage = 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
- -- to be sorted out
- -- local ty = pos_v - depth
- local ty = pos_v -- we assume that depth is dealt with in the caller (for now)
-
- 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).
- --
- -- These will be replaced by a new more advanced one ... some day ... or
- -- never because the next are like the other engines and compensate for
- -- small sizes which is needed for inaccurate viewers.
-
- flushrule = function(current,pos_h,pos_v,pos_r,size_h,size_v,subtype)
-
- 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
-
- flushspecialrule = function(pos_h,pos_v,pos_r,width,height,depth,line,outline)
- pdf_goto_pagemode()
-
- b = b + 1 ; buffer[b] = s_b
-
- local width = bpfactor * width
- local height = bpfactor * height
- local depth = bpfactor * depth
- local total = height + depth
- local line = bpfactor * line
- local half = line / 2
- local rule
-
- if outline then
- rule = f_x(line,half,-depth+half,width-line,total-line)
- else
- rule = f_b(line,-depth,width,total)
- end
- pdf_set_pos_temp(pos_h,pos_v)
-
- 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
- local pagetag = "unset"
-
- registerpage = function(object)
- nofpages = nofpages + 1
- local objnum = pdfpagereference(nofpages)
- pages[nofpages] = {
- page = nofpages, -- original number, only for diagnostics
- objnum = objnum,
- object = object,
- tag = pagetag,
- }
- end
-
- function lpdf.setpagetag(tag)
- pagetag = tag or "unset"
- end
-
- function lpdf.getnofpages()
- return nofpages
- end
-
- function lpdf.getpagetags()
- local list = { }
- for i=1,nofpages do
- list[i] = pages[i].tag
- end
- return list
- end
-
- function lpdf.setpageorder(mapping)
- -- mapping can be a hash so:
- local list = table.sortedkeys(mapping)
- local n = #list
- if n == nofpages then
- local done = { }
- local hash = { }
- for i=1,n do
- local order = mapping[list[i]]
- if hash[order] then
- report("invalid page order, duplicate entry %i",order)
- return
- elseif order < 1 or order > nofpages then
- report("invalid page order, no page %i",order)
- return
- else
- done[i] = pages[order]
- hash[order] = true
- end
- end
- pages = done
- else
- report("invalid page order, %i entries expected",nofpages)
- end
- end
-
- -- We can have this, but then via codeinjections etc. Later.
-
- -- function structures.pages.swapthem()
- -- local n = lpdf.getnofpages()
- -- local t = { }
- -- for i=1,n do
- -- t[i] = i
- -- end
- -- for i=2,math.odd(n) and n or (n-1),2 do
- -- t[i] = i+1
- -- t[i+1] = i
- -- end
- -- lpdf.setpageorder(t)
- -- end
-
- wrapup = function(driver)
-
- -- 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(driver,details)
- reset_variables(details)
- reset_buffer()
-end
-
--- 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 compact = false
-
-do
-
- -- This is more a convenience feature and it might even be not entirely robust.
- -- It removes redundant color directives which makes the page stream look a bit
- -- nicer (also when figuring out issues). I might add more here but there is
- -- some additional overhead involved so runtime can be impacted.
-
- local P, R, S, Cs, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.Cs, lpeg.match
-
- local p_ds = (R("09") + S(" ."))^1
- ----- p_nl = S("\n\r")^1
- local p_nl = S("\n")^1
- local p_eg = P("Q")
-
- local p_cl = p_ds * (P("rg") + P("g") + P("k")) * p_ds * (P("RG") + P("G") + P("K"))
- ----- p_cl = (p_ds * (P("rg") + P("g") + P("k") + P("RG") + P("G") + P("K")))^1
- local p_tr = P("/Tr") * p_ds * P("gs")
-
- local p_no_cl = (p_cl * p_nl) / ""
- local p_no_tr = (p_tr * p_nl) / ""
- local p_no_nl = 1 - p_nl
-
- local p_do_cl = p_cl * p_nl
- local p_do_tr = p_tr * p_nl
-
- local p_do_eg = p_eg * p_nl
-
- local pattern = Cs( (
- (p_no_cl + p_no_tr)^0 * p_do_eg -- transparencies and colors before Q
- + p_no_tr * p_no_cl * p_do_tr * p_do_cl -- transparencies and colors before others
- + p_no_cl * p_do_cl -- successive colors
- + p_no_tr * p_do_tr -- successive transparencies
- + p_no_nl^1
- + 1
- )^1 )
-
- local oldsize = 0
- local newsize = 0
-
- directives.register("pdf.compact", function(v)
- compact = v and function(s)
- oldsize = oldsize + #s
- s = lpegmatch(pattern,s) or s
- newsize = newsize + #s
- return s
- end
- end)
-
- statistics.register("pdf pagestream",function()
- if oldsize ~= newsize then
- return string.format("old size: %i, new size %i",oldsize,newsize)
- end
- end)
-
-
-end
-
-local flushdeferred -- defined later
-
-local level = 0
-
-local finalize do
-
- local f_font = formatters["F%d"]
-
- local f_form = formatters["Fm%d"]
- local f_group = formatters["Gp%d"]
- local f_image = formatters["Im%d"]
-
- finalize = function(driver,details)
-
- level = level + 1
-
- pdf_goto_pagemode() -- for now
-
- local objnum = details.objnum
- local specification = details.specification
-
- local content = concat(buffer,"\n",1,b)
-
- if compact then
- content = compact(content)
- end
-
- 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) or next(usedxgroups) 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
- for k, v in sortedhash(usedxgroups) do
- xforms[f_group(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 xorigin, yorigin, relocated = backends.codeinjections.getpageorigin() -- for now here
-
- local bbox = pdfarray {
- (boundingbox[1] + xorigin) * bpfactor,
- (boundingbox[2] + yorigin) * bpfactor,
- (boundingbox[3] + xorigin) * bpfactor,
- (boundingbox[4] + yorigin) * bpfactor,
- }
-
- if relocated then
- content = formatters["1 0 0 1 %.6N %.6N cm\n%s"](bbox[1],bbox[2],content)
- end
-
- local contentsobj = pdfflushstreamobject(content,false,false)
-
- pageattributes.Type = pdf_page
- pageattributes.Contents = pdfreference(contentsobj)
- pageattributes.Resources = pageresources
- -- pageattributes.Resources = pdfreference(pdfflushobject(pageresources))
- -- pageattributes.MediaBox = bbox
- pageattributes.MediaBox = pdfsharedobject(bbox)
- pageattributes.Parent = nil -- precalculate
- pageattributes.Group = nil -- todo
-
- -- resources can be indirect
-
- registerpage(pageattributes)
-
- lpdf.finalizepage(true)
-
- local TrimBox = pageattributes.TrimBox
- local CropBox = pageattributes.CropBox
- local BleedBox = pageattributes.BleedBox
-
- if relocated then
- if TrimBox then TrimBox = box end
- if CropBox then CropBox = box end
- if BleedBox then BleedBox = box end
- end
-
- -- Indirect objects don't work in all viewers.
-
- if TrimBox then pageattributes.TrimBox = pdfsharedobject(TrimBox ) end
- if CropBox then pageattributes.CropBox = pdfsharedobject(CropBox ) end
- if BleedBox then pageattributes.BleedBox = pdfsharedobject(BleedBox) end
-
- 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
- -- can be shared too
- 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
- localconverter(list,"xform",f[2],specification)
- end
- end
-
- pdf_h, pdf_v = 0, 0
-
- if level == 1 then
- flushdeferred()
- end
- level = level - 1
-
- end
-
-end
-
-updaters.register("backend.update.pdf",function()
- job.positions.registerhandlers {
- getpos = drivers.getpos,
- getrpos = drivers.getrpos,
- 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") (compression threshold)
-local objectstream = true
-local compress = true
-local cache = false
-local info = ""
-local catalog = ""
-local lastdeferred = false
-local majorversion = 1
-local minorversion = 7
-local trailerid = true
-
-directives.register("backend.pdf.threshold",function(v)
- if v then
- threshold = tonumber(v) or 40
- else
- threshold = -1000
- end
-end)
-
-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"]
-
------ 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"]
-
------ 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 indices = { }
-
- local maxsize = 32 * 1024 -- uncompressed
- local maxcount = 0xFF
-
- 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
- report("no data for %S",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
-
-flushdeferred = function() -- was forward defined
- 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
-
--- 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 = <string>, -- raw|stream
--- immediate = <boolean>,
--- objnum = <number>,
--- attr = <string>,
--- compresslevel = <number>,
--- objcompression = <boolean>,
--- file = <string>,
--- string = <string>,
--- nolength = <boolean>,
--- }
-
-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 = loaddata(filename) or ""
- end
- elseif kind == "raw"then
- if filename then
- data = loaddata(filename) or ""
- end
- elseif kind == "file"then
- kind = "raw"
- data = filename and loaddata(filename) or ""
- elseif kind == "streamfile" then
- kind = "stream"
- data = filename and 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 = loaddata(b)
- elseif a == "streamfile" then
- kind = "stream"
- data = 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)
-
--- In lua 5.4 the methods are now moved one metalevel deeper so we need to get them
--- from mt.__index instead. (I did get that at first.) It makes for a slightly (imo)
--- nicer interface but no real gain in speed as we don't flush that often.
-
-local openfile, closefile do
-
- -- I used to do <space><lf> but then figured out that when I open and save a file in a mode
- -- that removes trailing spaces, the xref becomes invalid. The problem was then that a
- -- reconstruction of the file by a viewer gives weird effects probably because percent symbols
- -- gets interpreted then. Thanks to Ross Moore for noticing this side effect!
-
- local f_used = formatters["%010i 00000 n\013\010"]
- local f_link = formatters["%010i 00000 f\013\010"]
- local f_first = formatters["%010i 65535 f\013\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 close = false
- local update = false
-
- -- local removefile = os.remove
-
- openfile = function(filename)
- if inmemory then
- local n = 0
- f = { }
- flush = function(f,s)
- n = n + 1 f[n] = s
- -- offset = offset + #s
- end
- close = function(f)
- f = concat(f)
- io.savedata(filename,f)
- f = false
- end
- update = function(f,s)
- f[1] = s
- end
- -- local n = 0
- -- f = {
- -- write = function(self,s)
- -- n = n + 1 f[n] = s
- -- end,
- -- close = function(self)
- -- f = concat(f)
- -- io.savedata(filename,f)
- -- f = false
- -- end,
- -- }
- else
- f = io.open(filename,"wb")
- if not f then
- -- message
- os.exit()
- end
- -- f:setvbuf("full",64*1024)
- local m = getmetatable(f)
- flush = m.write or m.__index.write
- close = m.close or m.__index.close
- update = function(f,s)
- f:seek("set",0)
- f:write(s)
- end
- 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 = offset + #v + #b
- end
-
- closefile = function(abort)
- if abort then
- close(f)
- if not environment.arguments.nodummy then
- f = io.open(abort,"wb")
- if f then
- local name = resolvers.findfile("context-lmtx-error.pdf")
- if name then
- local data = io.loaddata(name)
- if data then
- f:write(data)
- f:close()
- return
- end
- end
- f:close()
- end
- end
- os.remove(abort)
- else
- 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
- update(f,f_pdf(majorversion,minorversion))
- close(f)
- end
- io.flush()
- closefile = function() end
- 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 setprop = nodes.nuts.setprop
-
- local report_images = logs.reporter("backend","images")
-
- local lastindex = 0
- local indices = { }
-
- local bpfactor = number.dimenfactors.bp
- local imagerule_code = rulecodes.image
-
- 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)
- if specification then
- lastindex = lastindex + 1
- index = lastindex
- specification.index = index
- local xobject = pdfdictionary { }
- if not specification.notype then
- xobject.Type = pdf_xobject
- xobject.Subtype = pdf_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
- 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 = bbox[3] - xorigin -- we need the original ones, not the 'rotated' ones
- local ysize = bbox[4] - yorigin -- we need the original ones, not the 'rotated' ones
- 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()
-
- -- todo: an md5 or sha2 hash can save space
- -- todo: make a type 3 font instead
- -- todo: move to lpdf namespace
-
- local pdfimage = lpdf.epdf.image
- local newpdf = pdfimage.new
- local openpdf = pdfimage.open
- local closepdf = pdfimage.close
- local copypage = pdfimage.copy
-
- local embedimage = images.embed
-
- local nofstreams = 0
- local topdf = { }
- local toidx = { }
-
- local function storedata_s(pdf)
- local idx = toidx[pdf]
- if not idx then
- nofstreams = nofstreams + 1
- idx = nofstreams
- toidx[pdf] = nofstreams
- topdf[idx] = pdf
- end
- return idx
- end
-
- local function vfimage_s(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
- flushimage(index,wd,ht,dp,pos_h,pos_v)
- end
-
- local function storedata_n(name,page)
- local idx = toidx[pdf]
- if not idx then
- nofstreams = nofstreams + 1
- idx = nofstreams
- toidx[pdf] = nofstreams
- topdf[idx] = pdf
- end
- return idx
- end
-
- -- We need to have a way to close such a pdf ... esp for fonts.
-
- local pdfdocs = { }
-
- local function vfimage_n(name,page,wd,ht,dp,pos_h,pos_v)
- local d = pdfdocs[name]
- if not d then
- d = { doc = openpdf(name), pages = { } }
- pdfdocs[name] = d
- end
- local index = d.pages[page]
- if not index then
- local image = copypage(d.doc,page)
- local bbox = image.bbox
- image.width = bbox[3] - bbox[1]
- image.height = bbox[4] - bbox[2]
- embedimage(image)
- index = image.index
- d.pages[page] = index
- end
- flushimage(index,wd,ht,dp,pos_h,pos_v)
- end
-
- local function pdfvfimage(wd,ht,dp,data,name)
- if type(data) == "number" then
- return { "lua", function(font,char,pos_h,pos_v)
- vfimage_n(name,data,wd,ht,dp,pos_h,pos_v)
- end }
- else
- return { "lua", function(font,char,pos_h,pos_v)
- local id = storedata_s(data)
- vfimage_s(id,wd,ht,dp,pos_h,pos_v)
- end }
- end
- end
-
- lpdf.vfimage = pdfvfimage
-
-end)
-
--- The driver.
-
-do
-
- -- local addsuffix = file.addsuffix
- local texgetbox = tex.getbox
-
- local pdfname = nil
- local converter = nil
- local useddriver = nil -- a bit of a hack
-
- local function outputfilename(driver)
- return pdfname
- end
-
- -- todo: prevent twice
-
- local function prepare(driver)
- 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")
- pdfname = tex.jobname .. ".pdf"
- openfile(pdfname)
- --
- luatex.registerstopactions(1,function()
- if pdfname then
- lpdf.finalizedocument()
- closefile()
- end
- end)
- --
- luatex.registerpageactions(1,function()
- if pdfname then
- lpdf.finalizepage(true)
- end
- end)
- -- --
- lpdf.registerdocumentfinalizer(wrapup,nil,"wrapping up")
- --
- end
- --
- environment.lmtxmode = CONTEXTLMTXMODE
- --
- converter = drivers.converters.lmtx
- useddriver = driver
- end
-
- local function wrapup(driver)
- if pdfname then
- closefile()
- pdfname = nil
- end
- end
-
- local function cleanup(driver)
- if pdfname then
- closefile(pdfname)
- pdfname = nil
- end
- end
-
- local function convert(driver,boxnumber)
- converter(driver,texgetbox(boxnumber),"page")
- end
-
- localconverter = function(...)
- converter(useddriver,...)
- end
-
- drivers.install {
- name = "pdf",
- flushers = {
- character = flushcharacter,
- fontchar = flushfontchar,
- rule = flushrule,
- simplerule = flushsimplerule,
- specialrule = flushspecialrule,
- pushorientation = pushorientation,
- poporientation = poporientation,
- --
- literal = flushliteral,
- setmatrix = flushsetmatrix,
- save = flushsave,
- restore = flushrestore,
- image = flushimage,
- group = flushgroup,
- --
- updatefontstate = updatefontstate,
- },
- actions = {
- prepare = prepare,
- wrapup = wrapup,
- cleanup = cleanup,
- --
- initialize = initialize,
- convert = convert,
- finalize = finalize,
- --
- outputfilename = outputfilename,
- },
- }
-
-end