More compact float representation

This commit is contained in:
Marcel Krüger 2020-07-06 15:31:15 +02:00
parent 7ffc299c16
commit 463f240670
9 changed files with 76 additions and 41 deletions

View File

@ -1,9 +1,15 @@
local pdf = pdf
local pdfvariable = pdf.variable
local writer = require'luametalatex-nodewriter'
local newpdf = require'luametalatex-pdf'
local nametree = require'luametalatex-pdf-nametree'
local build_fontdir = require'luametalatex-pdf-font'
local utils = require'luametalatex-pdf-utils'
local strip_floats = utils.strip_floats
local to_bp = utils.to_bp
local pdfname, pfile
local fontdirs = setmetatable({}, {__index=function(t, k)t[k] = pfile:getobj() return t[k] end})
local usedglyphs = {}
@ -62,7 +68,7 @@ token.luacmd("shipout", function()
local out, resources, annots = writer(pfile, list, fontdirs, usedglyphs, colorstacks)
cur_page = nil
local content = pfile:stream(nil, '', out)
pfile:indirect(page, string.format([[<</Type/Page/Parent %i 0 R/Contents %i 0 R/MediaBox[0 %i %i %i]/Resources<<%s%s>>%s%s>>]], parent, content, -math.ceil(list.depth/65781.76), math.ceil(list.width/65781.76), math.ceil(list.height/65781.76), resources, pdfvariable.pageresources, annots, pdfvariable.pageattr))
pfile:indirect(page, string.format([[<</Type/Page/Parent %i 0 R/Contents %i 0 R/MediaBox[0 %i %i %i]/Resources<<%s%s>>%s%s>>]], parent, content, -math.ceil(to_bp(list.depth)), math.ceil(to_bp(list.width)), math.ceil(to_bp(list.height)), resources, pdfvariable.pageresources, annots, pdfvariable.pageattr))
node.flush_list(list)
token.put_next(reset_deadcycles)
token.scan_token()
@ -190,9 +196,6 @@ function pdf.newcolorstack(default, mode, page)
}
return idx
end
local function sp2bp(sp)
return sp/65781.76
end
local function projected(m, x, y, w)
w = w or 1
return x*m[1] + y*m[3] + w*m[5], x*m[2] + y*m[4] + w*m[6]
@ -253,14 +256,15 @@ local function write_link(p, link)
local quadStr = {}
for i=1,#quads,8 do
local x1, y1, x4, y4, x2, y2, x3, y3 = table.unpack(quads, i, i+7)
x1, y1, x2, y2, x3, y3, x4, y4 = sp2bp(x1), sp2bp(y1), sp2bp(x2), sp2bp(y2), sp2bp(x3), sp2bp(y3), sp2bp(x4), sp2bp(y4)
x1, y1, x2, y2, x3, y3, x4, y4 = to_bp(x1), to_bp(y1), to_bp(x2), to_bp(y2), to_bp(x3), to_bp(y3), to_bp(x4), to_bp(y4)
quadStr[i//8+1] = string.format("%f %f %f %f %f %f %f %f", x1, y1, x2, y2, x3, y3, x4, y4)
minX = math.min(minX, x1, x2, x3, x4)
minY = math.min(minY, y1, y2, y3, y4)
maxX = math.max(maxX, x1, x2, x3, x4)
maxY = math.max(maxY, y1, y2, y3, y4)
end
pfile:indirect(link.objnum, string.format("<</Type/Annot/Rect[%f %f %f %f]/QuadPoints[%s]%s>>", minX-.2, minY-.2, maxX+.2, maxY+.2, table.concat(quadStr, ' '), attr))
local boxes = strip_floats(string.format("/Rect[%f %f %f %f]/QuadPoints[%s]", minX-.2, minY-.2, maxX+.2, maxY+.2, table.concat(quadStr, ' ')))
pfile:indirect(link.objnum, string.format("<</Type/Annot%s%s>>", boxes, attr))
for i=1,#quads do quads[i] = nil end
link.objnum = nil
end
@ -387,9 +391,9 @@ local dest_whatsit = declare_whatsit('pdf_dest', function(prop, p, n, x, y)
local x, y = projected(p.matrix, x, y)
local zoom = prop.xyz_zoom
if zoom then
data = string.format("[%i 0 R/XYZ %.5f %.5f %.3f]", cur_page, sp2bp(x-off), sp2bp(y+off), prop.zoom/1000)
data = string.format("[%i 0 R/XYZ %.5f %.5f %.3f]", cur_page, to_bp(x-off), to_bp(y+off), prop.zoom/1000)
else
data = string.format("[%i 0 R/XYZ %.5f %.5f null]", cur_page, sp2bp(x-off), sp2bp(y+off))
data = string.format("[%i 0 R/XYZ %.5f %.5f null]", cur_page, to_bp(x-off), to_bp(y+off))
end
elseif dest_type == "fitr" then
local m = p.matrix
@ -399,28 +403,28 @@ local dest_whatsit = declare_whatsit('pdf_dest', function(prop, p, n, x, y)
local urx, ury = projected(x+prop.width, x + prop.height)
local left, lower, right, upper = math.min(llx, lrx, ulx, urx), math.min(lly, lry, uly, ury),
math.max(llx, lrx, ulx, urx), math.max(lly, lry, uly, ury)
data = string.format("[%i 0 R/FitR %.5f %.5f %.5f %.5f]", cur_page, sp2bp(left-off), sp2bp(lower-off), sp2bp(right+off), sp2bp(upper+off))
data = string.format("[%i 0 R/FitR %.5f %.5f %.5f %.5f]", cur_page, to_bp(left-off), to_bp(lower-off), to_bp(right+off), to_bp(upper+off))
elseif dest_type == "fit" then
data = string.format("[%i 0 R/Fit]", cur_page)
elseif dest_type == "fith" then
local x, y = projected(p.matrix, x, y)
data = string.format("[%i 0 R/FitH %.5f]", cur_page, sp2bp(y+off))
data = string.format("[%i 0 R/FitH %.5f]", cur_page, to_bp(y+off))
elseif dest_type == "fitv" then
local x, y = projected(p.matrix, x, y)
data = string.format("[%i 0 R/FitV %.5f]", cur_page, sp2bp(x-off))
data = string.format("[%i 0 R/FitV %.5f]", cur_page, to_bp(x-off))
elseif dest_type == "fitb" then
data = string.format("[%i 0 R/FitB]", cur_page)
elseif dest_type == "fitbh" then
local x, y = projected(p.matrix, x, y)
data = string.format("[%i 0 R/FitBH %.5f]", cur_page, sp2bp(y+off))
data = string.format("[%i 0 R/FitBH %.5f]", cur_page, to_bp(y+off))
elseif dest_type == "fitbv" then
local x, y = projected(p.matrix, x, y)
data = string.format("[%i 0 R/FitBV %.5f]", cur_page, sp2bp(x-off))
data = string.format("[%i 0 R/FitBV %.5f]", cur_page, to_bp(x-off))
end
if pfile:written(dests[id]) then
texio.write_nl(string.format("Duplicate destination %q", id))
else
dests[id] = pfile:indirect(dests[id], data)
dests[id] = pfile:indirect(dests[id], strip_floats(data))
end
end)
local refobj_whatsit = declare_whatsit('pdf_refobj', function(prop, p, n, x, y)

View File

@ -31,6 +31,10 @@ local rangedimensions = direct.rangedimensions
local traverse_id = direct.traverse_id
local getdata = direct.getdata
local utils = require'luametalatex-pdf-utils'
local strip_floats = utils.strip_floats
local to_bp = utils.to_bp
local pdf_font_map = require'luametalatex-pdf-font-deduplicate'
local get_whatsit_handler = require'luametalatex-whatsits'.handler
@ -72,14 +76,11 @@ local whatsithandler = (function()
end)
end)()
local glyph, text, page, cm_pending = 1, 2, 3, 4
local gsub = string.gsub
local function projected_point(m, x, y, w)
w = w or 1
return x*m[1] + y*m[3] + w*m[5], x*m[2] + y*m[4] + w*m[6]
end
local function sp2bp(sp)
return sp/65781.76
end
local fontnames = setmetatable({}, {__index = function(t, k) local res = format("F%i", k) t[k] = res return res end})
local topage
local function totext(p, fid)
@ -98,7 +99,7 @@ local function totext(p, fid)
local pdf_fid = pdf_font_map[fid]
p.resources.Font[fontnames[pdf_fid]] = p.fontdirs[pdf_fid]
p.strings[#p.strings+1] = format("/F%i %f Tf 0 Tr", pdf_fid, sp2bp(f.size)) -- TODO: Setting the mode, width, etc.
p.strings[#p.strings+1] = strip_floats(format("/F%i %f Tf 0 Tr", pdf_fid, to_bp(f.size))) -- TODO: Setting the mode, width, etc.
p.font.usedglyphs = p.usedglyphs[pdf_fid]
p.font.fid = fid
@ -127,7 +128,7 @@ function topage(p)
elseif last == cm_pending then
local pending = p.pending_matrix
if pending[1] ~= 1 or pending[2] ~= 0 or pending[3] ~= 0 or pending[4] ~= 1 or pending[5] ~= 0 or pending[6] ~= 0 then
p.strings[#p.strings+1] = format("%f %f %f %f %f %f cm", pending[1], pending[2], pending[3], pending[4], sp2bp(pending[5]), sp2bp(pending[6]))
p.strings[#p.strings+1] = strip_floats(format("%f %f %f %f %f %f cm", pending[1], pending[2], pending[3], pending[4], to_bp(pending[5]), to_bp(pending[6])))
end
else
error[[Unknown mode]]
@ -147,14 +148,14 @@ local function toglyph(p, fid, x, y, exfactor)
end
if totext(p, fid) or exfactor ~= p.font.exfactor then
p.font.exfactor = exfactor
p.strings[#p.strings+1] = gsub(format("%f 0.0 %f %f %f %f Tm", p.font.extend * (1+exfactor/1000000), p.font.slant, p.font.squeeze, sp2bp(x), sp2bp(y)), '%.?0+ ', ' ')
p.strings[#p.strings+1] = strip_floats(format("%f 0.0 %f %f %f %f Tm", p.font.extend * (1+exfactor/1000000), p.font.slant, p.font.squeeze, to_bp(x), to_bp(y)))
else
-- To invert the text transformation matrix (extend 0 0;slant squeeze 0;0 0 1)
-- we have to apply (extend^-1 0 0;-slant*extend^-1*squeeze^-1 squeeze^-1 0;0 0 1). (extend has to include expansion)
-- We optimize slightly by separating some steps
local dx, dy = sp2bp((x - p.pos.lx)), sp2bp(y - p.pos.ly) / p.font.squeeze
local dx, dy = to_bp((x - p.pos.lx)), to_bp(y - p.pos.ly) / p.font.squeeze
dx = (dx-p.font.slant*dy) / (p.font.extend * (1+exfactor/1000000))
p.strings[#p.strings+1] = gsub(format("%f %f Td", dx, dy), '%.?0+ ', ' ')
p.strings[#p.strings+1] = strip_floats(format("%f %f Td", dx, dy))
end
p.pos.lx, p.pos.ly, p.pos.x, p.pos.y = x, y, x, y
p.mode = glyph
@ -287,7 +288,7 @@ function nodehandler.rule(p, n, x, y, outer)
error[[We can't handle outline rules yet]]
else
topage(p)
p.strings[#p.strings+1] = gsub(format("%f %f %f %f re f", sp2bp(x), sp2bp(y - getdepth(n)), sp2bp(getwidth(n)), sp2bp(getdepth(n) + getheight(n))), '%.?0+ ', ' ')
p.strings[#p.strings+1] = strip_floats(format("%f %f %f %f re f", to_bp(x), to_bp(y - getdepth(n)), to_bp(getwidth(n)), to_bp(getdepth(n) + getheight(n))))
end
end
end

View File

@ -13,9 +13,10 @@ local boxmap = {
art = "ArtBox",
}
-- FIXME:
local function to_sp(bp) return bp*65781.76//1 end
local function to_bp(sp) return sp/65781.76 end
local utils = require'luametalatex-pdf-utils'
local strip_floats = utils.strip_floats
local to_sp = utils.to_sp
local to_bp = utils.to_bp
local function get_box(page, box)
box = boxmap[box]
@ -74,7 +75,8 @@ function pdf_functions.write(pfile, img)
local file = open_pdfe(img)
local page = pdfe.getpage(file, img.page)
local bbox = img.bbox
local dict = string.format("/Subtype/Form/BBox[%f %f %f %f]/Resources %s", to_bp(bbox[1]), to_bp(bbox[2]), to_bp(bbox[3]), to_bp(bbox[4]), pdfe_deepcopy(file, img.filepath, pfile, pdfe.getfromdictionary(page, 'Resources')))
local dict = strip_floats(string.format("/Subtype/Form/BBox[%f %f %f %f]/Resources ", to_bp(bbox[1]), to_bp(bbox[2]), to_bp(bbox[3]), to_bp(bbox[4])))
dict = dict .. pdfe_deepcopy(file, img.filepath, pfile, pdfe.getfromdictionary(page, 'Resources'))
local content, raw = page.Contents
-- Three cases: Contents is a stream, so copy the stream (Remember to copy filter if necessary)
-- Contents is an array of streams, so append all the streams as a new stream

View File

@ -1,3 +1,5 @@
local strip_floats = require'luametalatex-pdf-utils'.strip_floats
local function ignore() end
local parse = setmetatable({
-- IHDR = below,
@ -131,11 +133,11 @@ function parse.cHRM(buf, i, after, ctxt)
local X_C, Z_C = Y_C*x_B/y_B, Y_C*((1-x_B)/y_B-1)
local X_W, Y_W, Z_W = X_A+X_B+X_C, Y_A+Y_B+Y_C, Z_A+Z_B+Z_C
ctxt.cHRM = string.format("/WhitePoint[%f %f %f]/Matrix[%f %f %f %f %f %f %f %f %f]",
ctxt.cHRM = strip_floats(string.format("/WhitePoint[%f %f %f]/Matrix[%f %f %f %f %f %f %f %f %f]",
X_W, Y_W, Z_W,
X_A, Y_A, Z_A,
X_B, Y_B, Z_B,
X_C, Y_C, Z_C)
X_C, Y_C, Z_C))
end
function parse.IDAT(buf, i, after, ctxt)
ctxt.IDAT = ctxt.IDAT or {}
@ -277,7 +279,7 @@ function png_functions.write(pfile, img)
elseif colortype & 2 == 2 then -- RGB
if t.cHRM then
local gamma = t.gAMA or 2.2
gamma = gamma and string.format("/Gamma[%f %f %f]", gamma, gamma, gamma) or ''
gamma = gamma and strip_floats(string.format("/Gamma[%f %f %f]", gamma, gamma, gamma)) or ''
colorspace = string.format("[/CalRGB<<%s%s>>]", t.cHRM, gamma)
else
if t.gAMA then

View File

@ -17,9 +17,9 @@ local imagetypes = setmetatable({}, {__index = function(t, k)
return module
end})
-- FIXME:
local function to_sp(bp) return bp*65781.76//1 end
local function to_bp(sp) return sp/65781.76 end
local utils = require'luametalatex-pdf-utils'
local strip_floats = utils.strip_floats
local to_bp = utils.to_bp
local liberal_keys = {height = true, width = true, depth = true, transform = true}
local real_images = {}
@ -171,7 +171,7 @@ local function do_img(data, p, n, x, y)
b, d, f = b*yscale, d*yscale, f*yscale
e, f = to_bp(x + e), to_bp(y - depth + f)
p.resources.XObject['Im' .. tostring(img.objnum)] = img.objnum
pdf.write('page', string.format('q %f %f %f %f %f %f cm /Im%i Do Q', a, b, c, d, e, f, img.objnum), nil, nil, p)
pdf.write('page', strip_floats(string.format('q %f %f %f %f %f %f cm /Im%i Do Q', a, b, c, d, e, f, img.objnum)), nil, nil, p)
end
local ruleid = node.id'rule'
local ruletypes = node.subtypes'rule'

View File

@ -4,7 +4,9 @@ local pdfvariable = pdf.variable
-- XForms currently have the form {width, height, depth, objnum, attributes, list, margin}
local xforms = {}
local function to_bp(sp) return sp/65781.76 end
local utils = require'luametalatex-pdf-utils'
local strip_floats = utils.strip_floats
local to_bp = utils.to_bp
local function shipout(pfile, xform, fontdirs, usedglyphs)
local list, margin = xform.list, xform.margin
@ -16,7 +18,8 @@ local function shipout(pfile, xform, fontdirs, usedglyphs)
if pdfvariable.xformattr ~= '' or pdfvariable.xformresources ~= '' then
texio.write_nl('term and log', 'WARNING (savedboxresource shipout): Ignoring unsupported PDF variables xformattr and xformresources. Specify resources and attributes for specific XForms instead.')
end
local dict = string.format('/Subtype/Form/BBox[%f %f %f %f]/Resources<<%s%s>>%s', -to_bp(margin), -to_bp(list.depth+margin), to_bp(list.width+margin), to_bp(list.height+margin), resources, xform.resources or '', xform.attributes or '')
local bbox = strip_floats(string.format('/BBox[%f %f %f %f]', -to_bp(margin), -to_bp(list.depth+margin), to_bp(list.width+margin), to_bp(list.height+margin)))
local dict = string.format('/Subtype/Form%s/Resources<<%s%s>>%s', bbox, resources, xform.resources or '', xform.attributes or '')
node.flush_list(list)
xform.list = nil
local objnum = pfile:stream(xform.objnum, dict, out)
@ -89,10 +92,10 @@ local function do_box(data, p, n, x, y)
local width, height, depth = node.direct.getwhd(n)
local xscale, yscale = width / xform.width, (height+depth) / (xform.height+xform.depth)
p.resources.XObject['Fm' .. tostring(data)] = objnum
pdf.write('page', string.format('q %f 0 0 %f %f %f cm /Fm%i Do Q',
pdf.write('page', strip_floats(string.format('q %f 0 0 %f %f %f cm /Fm%i Do Q',
xscale, yscale,
to_bp(x), to_bp(y-depth+yscale*xform.depth),
data), nil, nil, p)
data)), nil, nil, p)
end
return {

View File

@ -0,0 +1,23 @@
local l = lpeg or require'lpeg'
local trailing_zeros = l.P'0'^0 * -l.R'09'
local strip_floats_patt = l.Cs((1-l.R'09' +
(l.R'09')^1 * (l.P'.' * trailing_zeros / '' + l.P'.' * (l.R'09'-trailing_zeros)^1 * (trailing_zeros/''))^-1)^0)
local match = l.match
local function strip_floats(s)
return match(strip_floats_patt, s)
end
local function to_bp(sp)
return sp/65781.76
end
local function to_sp(bp)
return (bp*65781.76+.5)//1
end
return {
strip_floats = strip_floats,
to_bp = to_bp,
to_sp = to_sp,
}

View File

@ -1,5 +1,4 @@
local format = string.format
local gsub = string.gsub
local byte = string.byte
local pack = string.pack
local error = error

View File

@ -1,4 +1,5 @@
local format = string.format
local strip_floats = require'luametalatex-pdf-utils'.strip_floats
local pdfe = pdfe
local l = lpeg
local regularchar = 1-l.S'\0\t\n\r\f ()<>[]{}/%#'
@ -17,7 +18,7 @@ local deepcopy_lookup deepcopy_lookup = {
return format("%d", i)
end,
function(_, pdf, f) -- 4: number
return format("%f", f)
return strip_floats(format("%f", f), "%.?0+[ %]]", "")
end,
function(_, pdf, name) -- 5: name
return nameescape:match(name)