From 5c45cbc2e327631f42a37d12730ef261bb09ac8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Fabian=20Kr=C3=BCger?= Date: Thu, 11 Jun 2020 01:16:42 +0200 Subject: [PATCH] Significantly improved image support Except for te TeX interface, image inclusions are working (for PDF image at least, but everything else can be converted) --- ltexpl.ltx | 65 ------------------------ luametalatex-dir-registers.lua | 1 - luametalatex-font-tfm.lua | 1 + luametalatex-init.lua | 38 +++++++++----- luametalatex-nodewriter.lua | 28 +++++++--- luametalatex-pdf-image.lua | 93 ++++++++++++++++++++++++---------- 6 files changed, 112 insertions(+), 114 deletions(-) delete mode 100644 ltexpl.ltx diff --git a/ltexpl.ltx b/ltexpl.ltx deleted file mode 100644 index 245addb..0000000 --- a/ltexpl.ltx +++ /dev/null @@ -1,65 +0,0 @@ -%% -%% This is file `ltexpl.ltx', -%% generated with the docstrip utility. -%% -%% The original source files were: -%% -%% ltexpl.dtx (with options: `2ekernel') -%% -%% This is a generated file. -%% -%% The source is maintained by the LaTeX Project team and bug -%% reports for it can be opened at https://latex-project.org/bugs.html -%% (but please observe conditions on bug reports sent to that address!) -%% -%% -%% Copyright (C) 1993-2019 -%% The LaTeX3 Project and any individual authors listed elsewhere -%% in this file. -%% -%% This file was generated from file(s) of the LaTeX base system. -%% -------------------------------------------------------------- -%% -%% It may be distributed and/or modified under the -%% conditions of the LaTeX Project Public License, either version 1.3c -%% of this license or (at your option) any later version. -%% The latest version of this license is in -%% https://www.latex-project.org/lppl.txt -%% and version 1.3c or later is part of all distributions of LaTeX -%% version 2008 or later. -%% -%% This file has the LPPL maintenance status "maintained". -%% -%% This file may only be distributed together with a copy of the LaTeX -%% base system. You may however distribute the LaTeX base system without -%% such generated files. -%% -%% The list of all files belonging to the LaTeX base distribution is -%% given in the file `manifest.txt'. See also `legal.txt' for additional -%% information. -%% -%% The list of derived (unpacked) files belonging to the distribution -%% and covered by LPPL is defined by the unpacking scripts (with -%% extension .ins) which are part of the distribution. -%%% From File: ltexpl.dtx -\input luametalatex-baseregisters -\IfFileExists{expl3.ltx} - {% - \ifnum0% - \ifdefined\pdffilesize 1\fi - \ifdefined\filesize 1\fi - \ifdefined\luatexversion\ifnum\luatexversion>94 1\fi\fi - >0 % - \else - \message{Skipping expl3-dependent extensions} - \expandafter\endinput - \fi - } - {% - \message{Skipping expl3-dependent extensions}% - \endinput - }% -\input{expl3.ltx} -\endinput -%% -%% End of file `ltexpl.ltx'. diff --git a/luametalatex-dir-registers.lua b/luametalatex-dir-registers.lua index fdc876a..f4b671d 100644 --- a/luametalatex-dir-registers.lua +++ b/luametalatex-dir-registers.lua @@ -37,7 +37,6 @@ return function(name) local setter = tex["set" .. name] assert(getter and setter, "direction parameter undefined") local idx = token.luacmd(name, set_xdir, "protected", "global", "value") - -- names[idx] = name getters[idx] = getter setters[idx] = setter return idx diff --git a/luametalatex-font-tfm.lua b/luametalatex-font-tfm.lua index fd0161c..2f82a1b 100644 --- a/luametalatex-font-tfm.lua +++ b/luametalatex-font-tfm.lua @@ -136,6 +136,7 @@ end local basename = ((1-lpeg.S'\\/')^0*lpeg.S'\\/')^0*lpeg.C((1-lpeg.P'.tfm'*-1)^0) return function(name, size) local filename = kpse.find_file(name, 'tfm', true) + if not filename then return end local f = io.open(filename) if not f then return end local buf = f:read'*a' diff --git a/luametalatex-init.lua b/luametalatex-init.lua index 3af0d43..fad4b1c 100644 --- a/luametalatex-init.lua +++ b/luametalatex-init.lua @@ -1,5 +1,5 @@ do - local ourpath = arg[0]:match('^%-%-lua=(.*)luametalatex%-init%.lua$') + local ourpath = arg[0]:match('^%-%-lua=(.*[/\\])[^/\\]*%.lua$') kpse = assert(package.loadlib(ourpath .. 'kpse.so', 'luaopen_kpse'))() end do @@ -35,6 +35,7 @@ local read_tfm = require'luametalatex-font-tfm' local read_vf = require'luametalatex-font-vf' font.read_tfm = read_tfm font.read_vf = read_vf +local callback_register = callback.register require'module' font.fonts = {} function font.getfont(id) @@ -51,7 +52,7 @@ function font.define(f) font.fonts[i] = f return i end -callback.register('define_font', function(name, size) +callback_register('define_font', function(name, size) local f = read_tfm(name, size) local id = font.define(f) if status.ini_version then @@ -59,12 +60,26 @@ callback.register('define_font', function(name, size) end return id end) -callback.register('find_log_file', function(name) return name end) --- callback.register('find_read_file', function(i, name) return kpse.find_file(name, 'tex', true) end) -callback.register('find_data_file', function(name, ...) return kpse.find_file(name, 'tex', true) end) -callback.register('read_data_file', function(name) error[[TODO]]return kpse.find_file(name, 'tex', true) end) +callback_register('find_log_file', function(name) return name end) +do + local function normal_find_data_file(name) + return kpse.find_file(name, 'tex', true) + end + if status.ini_version then + callback_register('find_data_file', function(name) + if name == 'ltexpl.ltx' then + callback_register('find_data_file', normal_find_data_file) + name = 'luametalatex-ltexpl-hook' + end + return normal_find_data_file(name) + end) + else + callback_register('find_data_file', normal_find_data_file) + end +end +callback_register('read_data_file', function(name) error[[TODO]]return kpse.find_file(name, 'tex', true) end) -- local file_meta = {\ -callback.register('open_data_file', function(name) +callback_register('open_data_file', function(name) local f = io.open(name) return setmetatable({ reader = function() return f:read() end, @@ -73,13 +88,13 @@ callback.register('open_data_file', function(name) __gc = function()f:close()end, }) end) -callback.register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end) -callback.register('handle_error_hook', function() +callback_register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end) +callback_register('handle_error_hook', function() repeat texio.write_nl'? ' local line = io.read() if not line then - error[[TODO: Handle EOL]] + tex.fatalerror'End of line encountered on terminal' end if line == "" then return 3 end local first = line:sub(1,1):upper() @@ -102,10 +117,9 @@ callback.register('handle_error_hook', function() \z H for help, X to quit.' end until false - -- print('handle') return 3 end) -callback.register('pre_dump', function() +callback_register('pre_dump', function() lua.prepared_code[1] = string.format("fixupluafunctions(%i)", fixupluafunctions()) lua.bytecode[1] = assert(load(table.concat(lua.prepared_code, ' '))) end) diff --git a/luametalatex-nodewriter.lua b/luametalatex-nodewriter.lua index 5e6a331..7d8d4cc 100644 --- a/luametalatex-nodewriter.lua +++ b/luametalatex-nodewriter.lua @@ -29,6 +29,7 @@ local getexpansion = direct.getexpansion local getchar = direct.getchar local rangedimensions = direct.rangedimensions local traverse_id = direct.traverse_id +local getdata = direct.getdata local dir_id = node.id'dir' @@ -97,8 +98,6 @@ local function totext(p, fid) p.font.font = f return false -- Return true if we need a new textmatrix end -local inspect = require'inspect' -local function show(t) print(inspect(t)) end function topage(p) local last = p.mode if last == page then return end @@ -218,19 +217,31 @@ function nodehandler.vlist(p, list, x, y0, outerlist, origin, level) y = y - (d or 0) end end +do +local rulesubtypes = {} +for i, n in next, node.subtypes'rule' do + rulesubtypes[n] = i +end +local box_rule = rulesubtypes.box +local image_rule = rulesubtypes.image +local user_rule = rulesubtypes.user +local empty_rule = rulesubtypes.empty +local outline_rule = rulesubtypes.outline +local ship_img = require'luametalatex-pdf-image'.ship function nodehandler.rule(p, n, x, y, outer) if getwidth(n) == -1073741824 then setwidth(n, getwidth(outer)) end if getheight(n) == -1073741824 then setheight(n, getheight(outer)) end if getdepth(n) == -1073741824 then setdepth(n, getdepth(outer)) end local sub = getsubtype(n) - if sub == 1 then + if sub == box_rule then error[[We can't handle boxes yet]] - elseif sub == 2 then - error[[We can't handle images yet]] - elseif sub == 3 then - elseif sub == 4 then + elseif sub == image_rule then + if getwidth(n) <= 0 or getdepth(n) + getheight(n) <= 0 then return end + ship_img(getdata(n), p, n, x, y) + elseif sub == empty_rule then + elseif sub == user_rule then error[[We can't handle user rules yet]] - elseif sub == 9 then + elseif sub == outline_rule then error[[We can't handle outline rules yet]] else if getwidth(n) <= 0 or getdepth(n) + getheight(n) <= 0 then return end @@ -238,6 +249,7 @@ function nodehandler.rule(p, n, x, y, outer) 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+ ', ' ') end end +end function nodehandler.boundary() end function nodehandler.disc(p, n, x, y, list, ...) -- FIXME: I am not sure why this can happen, let's assume we can use .replace for n in traverse(getreplace(n)) do diff --git a/luametalatex-pdf-image.lua b/luametalatex-pdf-image.lua index af4d99c..edabea4 100644 --- a/luametalatex-pdf-image.lua +++ b/luametalatex-pdf-image.lua @@ -1,4 +1,9 @@ local rawset = rawset +local setdata = node.direct.setdata +local nodenew = node.direct.new +local getwhd = node.direct.getwhd +local setwhd = node.direct.setwhd +local tonode = node.direct.tonode local reserve @@ -18,8 +23,8 @@ local boxmap = { } -- FIXME: -local function to_sp(bp) return bp*65536//1 end -local function to_bp(sp) return sp/65536 end +local function to_sp(bp) return bp*65781.76//1 end +local function to_bp(sp) return sp/65781.76 end local function get_box(page, box) box = boxmap[box] @@ -63,16 +68,12 @@ local function scan_pdf(img) local page = pdfe.getpage(file, img.page) local bbox = img.bbox or get_box(page, img.pagebox or 'crop') or {0, 0, 0, 0} img.bbox = bbox - img.rotation = (page.Rotation or 0) % 360 - if img.rotation < 0 then img.rotation = img.rotation + 360 end - if img.rotation % 2 == 0 then - img.xsize = bbox[3] - bbox[1] - img.ysize = bbox[4] - bbox[2] - else - img.xsize = bbox[4] - bbox[2] - img.ysize = bbox[3] - bbox[1] - end - img.transform = img.transform or 0 + img.rotation = (360 - (page.Rotate or 0)) % 360 + assert(img.rotation % 90 == 0, "Invalid /Rotate") + img.rotation = img.rotation / 90 + if img.rotation < 0 then img.rotation = img.rotation + 4 end + img.xsize = bbox[3] - bbox[1] + img.ysize = bbox[4] - bbox[2] end local pdfe_deepcopy = require'luametalatex-pdfe-deepcopy' @@ -151,11 +152,12 @@ local function scan(img) scan_pdf(real) setmetatable(img, restricted_meta) end + img.transform = img.transform or 0 -- (Re)Set dimensions if img.depth and img.height and img.width then return img end - local flipped = img.transform % 2 == 1 + local flipped = (img.transform + real.rotation) % 2 == 1 if not (img.depth or img.height) then img.depth = 0 end if not img.width and not (img.height and img.depth) then local total_y @@ -186,42 +188,76 @@ local function scan(img) return img end +local img_by_objnum = {} +local function img_from_objnum(objnum, img) + img = img or {} + real_images[img] = assert(img_by_objnum[objnum]) + return setmetatable(img, restricted_meta) +end + -- Noop if already reserved function reserve(img, pfile) local real = assert(real_images[img]) local obj = real.objnum or pfile:getobj() real.objnum = obj - return img, obj + img_by_objnum[obj] = real + return obj end local function write_img(pfile, img) - local _, objnum = reserve(img, pfile) + local objnum = reserve(img, pfile) local real = real_images[img] if not real.written then real.written = true write_pdf(real, pfile) end end -local function do_img(prop, p, n, x, y, outer) - local img = prop.img - img.height, img.depth, img.width = prop.height, prop.depth, prop.width - scan(img) +local function do_img(data, p, n, x, y) + local img = {} + img_from_objnum(data >> 3, img) + -- scan(img) write_img(p.file, img) local real = real_images[img] - local total_height = img.height + img.depth + local mirror = data & 4 == 4 + local rotate = (data + img.rotation) % 8 + local width, height, depth = getwhd(n) + height = height + depth local bbox = real.bbox - x, y = to_bp(x - bbox[1]), to_bp(y - img.depth + bbox[2]) + local xsize, ysize = img.xsize, img.ysize + local a, b, c, d, e, f = 1, 0, 0, 1, -bbox[1], -bbox[2] + if mirror then + a, e = -a, -e+xsize + end + print(img.rotation, rotate, data, a, b, c, d, e, f) + for i=1,rotate do + a, b, c, d, e, f = -b, a, -d, c, -f+ysize, e + xsize, ysize = ysize, xsize + end + print(a, b, c, d, e, f) + local xscale, yscale = width / xsize, height / ysize + a, c, e = a*xscale, c*xscale, e*xscale + 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(real.objnum)] = real.objnum - pdf.write('page', string.format('q 1 0 0 1 %f %f cm /Im%i Do Q', x, y, real.objnum), nil, nil, p) + pdf.write('page', string.format('q %f %f %f %f %f %f cm /Im%i Do Q', a, b, c, d, e, f, real.objnum), nil, nil, p) end +local ruleid = node.id'rule' +local ruletypes = node.subtypes'rule' +local imagerule +for n, name in next, ruletypes do + if name == 'image' then + imagerule = n + break + end +end +assert(imagerule) local function node(img, pfile) pfile = pfile or pdf.__get_pfile() - local n = _ENV.node.new('whatsit', 42) -- image - _ENV.node.setproperty(n, { - handle = do_img, - img = img, - }) - return n + scan(img) + local n = nodenew(ruleid, imagerule) -- image + setdata(n, (reserve(img, pfile) << 3) | ((img.transform or 0) & 7)) + setwhd(n, img.width or -0x40000000, img.height or -0x40000000, img.depth or -0x40000000) + return tonode(n) end --[[ @@ -237,4 +273,5 @@ return { scan = scan, write = write, node = node, + ship = do_img, }