PNG inclusion support
This commit is contained in:
parent
552bbb258c
commit
50e97985cf
@ -257,6 +257,10 @@ local user_rule = rulesubtypes.user
|
||||
local empty_rule = rulesubtypes.empty
|
||||
local outline_rule = rulesubtypes.outline
|
||||
local ship_img = require'luametalatex-pdf-image'.ship
|
||||
-- print(require'inspect'(node.subtypes('glue')))
|
||||
-- print(require'inspect'(node.fields('glue')))
|
||||
-- print(require'inspect'(node.fields('rule')))
|
||||
-- print(require'inspect'(node.fields('whatsit')))
|
||||
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
|
||||
|
106
luametalatex-pdf-image-pdf.lua
Normal file
106
luametalatex-pdf-image-pdf.lua
Normal file
@ -0,0 +1,106 @@
|
||||
local box_fallback = {
|
||||
BleedBox = "CropBox",
|
||||
TrimBox = "CropBox",
|
||||
ArtBox = "CropBox",
|
||||
CropBox = "MediaBox",
|
||||
}
|
||||
|
||||
local boxmap = {
|
||||
media = "MediaBox",
|
||||
crop = "CropBox",
|
||||
bleed = "BleedBox",
|
||||
trim = "TrimBox",
|
||||
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 function get_box(page, box)
|
||||
box = boxmap[box]
|
||||
while box do
|
||||
local found = pdfe.getbox(page, box)
|
||||
if found then
|
||||
return {to_sp(found[1]), to_sp(found[2]), to_sp(found[3]), to_sp(found[4])}
|
||||
end
|
||||
box = box_fallback[box]
|
||||
end
|
||||
end
|
||||
|
||||
local pdf_functions = {}
|
||||
|
||||
local function open_pdfe(img)
|
||||
local file = pdfe.open(img.filepath)
|
||||
do
|
||||
local userpassword = img.userpassword
|
||||
local ownerpassword = img.ownerpassword
|
||||
if userpassword or ownerpassword then
|
||||
pdfe.unencrypt(file, userpassword, ownerpassword)
|
||||
end
|
||||
end
|
||||
local status = pdfe.getstatus(file)
|
||||
if status >= 0 then
|
||||
return file
|
||||
elseif status == -1 then
|
||||
error[[PDF image is encrypted. Please provide the decryption key.]]
|
||||
elseif status == -2 then
|
||||
error[[PDF image could not be opened.]]
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
function pdf_functions.scan(img)
|
||||
local file = open_pdfe(img)
|
||||
img.pages = pdfe.getnofpages(file)
|
||||
img.page = img.page or 1
|
||||
if img.page > img.pages then
|
||||
error[[Not enough pages in PDF image]]
|
||||
end
|
||||
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 = (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]
|
||||
img.xres, img.yres = nil, nil
|
||||
end
|
||||
|
||||
local pdfe_deepcopy = require'luametalatex-pdfe-deepcopy'
|
||||
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", bbox[1], bbox[2], bbox[3], bbox[4], 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
|
||||
-- Contents is missing. Then create an empty stream.
|
||||
local type = pdfe.type(content)
|
||||
if type == 'pdfe.stream' then
|
||||
raw = true
|
||||
for i=1,#content do
|
||||
local key, type, value, detail = pdfe.getfromstream(content, i)
|
||||
dict = dict .. pdfe_deepcopy(file, img.filepath, pfile, 5, key) .. ' ' .. pdfe_deepcopy(file, img.filepath, pfile, type, value, detail)
|
||||
end
|
||||
content = content(false)
|
||||
elseif type == 'pdfe.array' then
|
||||
local array = content
|
||||
content = ''
|
||||
for i=1,#array do
|
||||
content = content .. array[i](true)
|
||||
end
|
||||
else
|
||||
content = ''
|
||||
end
|
||||
local attr = img.attr
|
||||
if attr then
|
||||
dict = dict .. attr
|
||||
end
|
||||
pfile:stream(img.objnum, dict, content, nil, raw)
|
||||
end
|
||||
|
||||
return pdf_functions
|
285
luametalatex-pdf-image-png.lua
Normal file
285
luametalatex-pdf-image-png.lua
Normal file
@ -0,0 +1,285 @@
|
||||
local function ignore() end
|
||||
local parse = setmetatable({
|
||||
-- IHDR = below,
|
||||
-- PLTE = below,
|
||||
-- IDAT = below,
|
||||
-- IEND = below,
|
||||
-- I'm not yet sure what to do about the following four color management chunks:
|
||||
-- These two will probably be ignored (if you care about this stuff, you probably
|
||||
-- prefer an ICC profile anyway. Also especially cHRM requires some weird computations.)
|
||||
-- cHRM = TODO, -- ignore?
|
||||
-- gAMA = TODO, -- ignore?
|
||||
-- iCCP is implemented, but profiles are not cached, so it might include the
|
||||
-- same profile many times
|
||||
-- iCCP = below,
|
||||
-- I would expect sRGB to be the most common, but it is a bit complicated because
|
||||
-- PDF seems to require us to ship an actual ICC profile to support sRGB. Maybe later.
|
||||
-- sRGB = TODO,
|
||||
sBIT = ignore,
|
||||
bKGD = ignore, -- Background color. Ignored since we support transparency
|
||||
hIST = ignore, -- Color histogram
|
||||
-- tRNS = below,
|
||||
-- pHYs = below, -- resolution information
|
||||
sPLT = ignore, -- Suggested palette but we support full truetype
|
||||
tIME = ignore, -- The following only store metadata
|
||||
iTXt = ignore,
|
||||
tEXt = ignore,
|
||||
zTXt = ignore,
|
||||
}, {
|
||||
__index = function(_, n)
|
||||
print("Table " .. n .. " unsupported") -- FIXME: Handle extensions by detecing if they are critical etc.
|
||||
return ignore
|
||||
end
|
||||
})
|
||||
function parse.IHDR(buf, i, after, ctxt)
|
||||
if next(ctxt) then
|
||||
error[[The header should come first]]
|
||||
end
|
||||
local compression, filter
|
||||
ctxt.width, ctxt.height,
|
||||
ctxt.bitdepth, ctxt.colortype,
|
||||
compression, filter,
|
||||
ctxt.interlace, i = string.unpack(">I4I4I1I1I1I1I1", buf, i)
|
||||
if i ~= after then
|
||||
return [[Invalid header size]]
|
||||
end
|
||||
if compression ~= 0 then
|
||||
error [[Unsupported compression mode]]
|
||||
end
|
||||
if filter ~= 0 then
|
||||
error [[Unsupported filter mode]]
|
||||
end
|
||||
end
|
||||
function parse.PLTE(buf, i, after, ctxt)
|
||||
if ctxt.PLTE then
|
||||
error[[Multiple palettes detected]]
|
||||
end
|
||||
if (after-i)%3 ~= 0 then
|
||||
error[[Invalid palette lenght]]
|
||||
end
|
||||
ctxt.PLTE_len = (after-i) // 3
|
||||
ctxt.PLTE = string.sub(buf, i, after-1)
|
||||
end
|
||||
function parse.tRNS(buf, i, after, ctxt)
|
||||
if ctxt.colortype == 3 then
|
||||
local count = assert(ctxt.PLTE_len)
|
||||
local patt = lpeg.P(1) * lpeg.Cc'\xff'
|
||||
for j=0,after-i-1 do
|
||||
local off = i+j
|
||||
patt = lpeg.P(string.char(j)) * lpeg.Cc(buf:sub(off, off)) + patt
|
||||
end
|
||||
ctxt.tRNS = lpeg.Cs(lpeg.Cg(patt)^0)
|
||||
elseif ctxt.colortype == 0 then
|
||||
local color
|
||||
color, i = string.unpack(">I2", buf, i)
|
||||
assert(i == after)
|
||||
ctxt.tRNS = string.format('%i %i', color, color)
|
||||
elseif ctxt.colortype == 2 then
|
||||
local r, g, b
|
||||
r, g, b, i = string.unpack(">I2I2I2", buf, i)
|
||||
assert(i == after)
|
||||
ctxt.tRNS = string.format('%i %i %i %i %i %i', r, r, g, g, b, b)
|
||||
end
|
||||
end
|
||||
local meterperinch = 0.0254
|
||||
function parse.pHYs(buf, i, after, ctxt)
|
||||
local xres, yres, unit
|
||||
xres, yres, unit, i = string.unpack('>I4I4I1', buf, i)
|
||||
if unit == 0 then
|
||||
if xres > yres then
|
||||
ctxt.xres, ctxt.yres = xres/yres, 0
|
||||
elseif xres < yres then
|
||||
ctxt.xres, ctxt.yres = 0, yres/xres
|
||||
end
|
||||
elseif unit == 1 then
|
||||
ctxt.xres, ctxt.yres = xres * meterperinch, yres * meterperinch
|
||||
else
|
||||
error[[Invalid unit]]
|
||||
end
|
||||
assert(i == after)
|
||||
end
|
||||
function parse.iCCP(buf, i, after, ctxt)
|
||||
local j = buf:find('\0', i, true)
|
||||
assert(j+1<after)
|
||||
local name = buf:sub(i, j-1)
|
||||
print('ICC Profile name: ' .. name)
|
||||
assert(buf:byte(j+1) == 0) -- The only known compression mode
|
||||
ctxt.iCCP = xzip.decompress(buf:sub(j+2, after-1))
|
||||
end
|
||||
function parse.IDAT(buf, i, after, ctxt)
|
||||
ctxt.IDAT = ctxt.IDAT or {}
|
||||
table.insert(ctxt.IDAT, buf:sub(i, after-1))
|
||||
end
|
||||
function parse.IEND(buf, i, after)
|
||||
if i ~= after then
|
||||
error[[Unexpected data in end chunk]]
|
||||
end
|
||||
end
|
||||
|
||||
local function run(buf, i, len, limit)
|
||||
i = i or 1
|
||||
len = i+(len or #buf)
|
||||
if buf:sub(i,i+7) ~= "\x89PNG\x0D\x0A\x1A\x0A" then
|
||||
error[[You lied. This isn't a PNG file.]]
|
||||
end
|
||||
i = i+8
|
||||
local chunks = {}
|
||||
while i < len do
|
||||
local length, tp, off = string.unpack(">I4c4", buf, i)
|
||||
if tp == limit then break end
|
||||
parse[tp](buf, off, off + length, chunks)
|
||||
i = off + length + 4
|
||||
end
|
||||
return chunks, i
|
||||
end
|
||||
local function passes(buf, width, height, bitdepth, colortype)
|
||||
local stride = (bitdepth == 16 and 2 or 1) * (1 + (colortype&3 == 2 and 2 or 0) + (colortype&4)/4)
|
||||
local passes = {
|
||||
{(width+7)//8, (height+7)//8},
|
||||
{(width+3)//8, (height+7)//8},
|
||||
{(width+3)//4, (height+3)//8},
|
||||
{(width+1)//4, (height+3)//4},
|
||||
{(width+1)//2, (height+1)//4},
|
||||
{ width //2, (height+1)//2},
|
||||
{ width , height //2},
|
||||
}
|
||||
local off = 1
|
||||
local result
|
||||
for i=1,#passes do
|
||||
local xsize, ysize = passes[i][1], passes[i][2]
|
||||
if xsize ~= 0 and ysize ~= 0 then
|
||||
if bitdepth < 8 then
|
||||
xsize = (xsize * bitdepth + 7) // 8
|
||||
end
|
||||
local after = off + (xsize+1) * stride * ysize
|
||||
local pass = pngdecode.applyfilter(
|
||||
buf:sub(off, after-1),
|
||||
xsize,
|
||||
ysize,
|
||||
stride)
|
||||
if bitdepth < 8 then
|
||||
pass = pngdecode.expand(pass, passes[i][1], ysize, bitdepth, xsize)
|
||||
end
|
||||
result = pngdecode.interlace(width, height, stride, i, pass, result)
|
||||
off = after
|
||||
end
|
||||
end
|
||||
assert(off == #buf+1)
|
||||
return result
|
||||
end
|
||||
|
||||
local png_functions = {}
|
||||
|
||||
function png_functions.scan(img)
|
||||
local file = io.open(img.filepath)
|
||||
if not file then
|
||||
error[[PDF image could not be opened.]]
|
||||
end
|
||||
local buf = file:read'a'
|
||||
file:close()
|
||||
local t = run(buf, 1, #buf, 'IDAT')
|
||||
img.pages = 1
|
||||
img.page = 1
|
||||
img.rotation = 0
|
||||
img.xsize, img.ysize = t.width, t.height
|
||||
img.xres, img.yres = t.xres or 0, t.yres or 0
|
||||
img.colordepth = t.bitdepth
|
||||
end
|
||||
|
||||
local pdf_escape = require'luametalatex-pdf-escape'.escape_bytes
|
||||
|
||||
local function rawimage(t, content)
|
||||
content = xzip.decompress(content)
|
||||
if t.interlace == 1 then
|
||||
content = passes(content, t.width, t.height, t.bitdepth, t.colortype)
|
||||
else
|
||||
local xsize = t.width
|
||||
if t.bitdepth < 8 then
|
||||
xsize = (xsize * t.bitdepth + 7) // 8
|
||||
end
|
||||
local colortype = t.colortype
|
||||
content = pngdecode.applyfilter(
|
||||
content,
|
||||
xsize,
|
||||
t.height,
|
||||
(t.bitdepth == 16 and 2 or 1) * (1 + (colortype&3 == 2 and 2 or 0) + (colortype&4)/4))
|
||||
end
|
||||
return content
|
||||
end
|
||||
|
||||
function png_functions.write(pfile, img)
|
||||
local file = io.open(img.filepath)
|
||||
if not file then
|
||||
error[[PDF image could not be opened.]]
|
||||
end
|
||||
local buf = file:read'a'
|
||||
file:close()
|
||||
local t = run(buf, 1, #buf, 'IEND')
|
||||
local colorspace
|
||||
local colortype = t.colortype
|
||||
if img.colorspace then
|
||||
colorspace = string.format(' %i 0 R', img.colorspace)
|
||||
elseif t.iCCP then
|
||||
local icc_ref = pfile:stream(nil, '/N ' .. tostring(colortype & 2 == 2 and '3' or '1'), t.iCCP)
|
||||
colorspace = string.format('[/ICCBased %i 0 R]', icc_ref)
|
||||
elseif colortype & 2 == 2 then -- RGB
|
||||
colorspace = '/DeviceRGB'
|
||||
else -- Gray
|
||||
colorspace = '/DeviceGray'
|
||||
end
|
||||
if colortype & 1 == 1 then -- Indexed
|
||||
colorspace = string.format('[/Indexed%s %i%s]', colorspace, t.PLTE_len-1, pdf_escape(t.PLTE))
|
||||
end
|
||||
local colordepth = t.interlace == 1 and 8 or img.colordepth
|
||||
local dict = string.format("/Subtype/Image/Width %i/Height %i/BitsPerComponent %i/ColorSpace%s", img.xsize, img.ysize, colordepth, colorspace)
|
||||
|
||||
local content = table.concat(t.IDAT)
|
||||
local copy -- = true
|
||||
if copy and (t.interlace == 1 or colortype & 4 == 4) then -- TODO: Add additional conditions
|
||||
copy = false
|
||||
end
|
||||
|
||||
if copy then
|
||||
-- In this case we never have to deal with an alpha component
|
||||
dict = string.format(
|
||||
'%s/Filter/FlateDecode/DecodeParms<</Colors %i/Columns %i/BitsPerComponent %i/Predictor 10>>',
|
||||
dict, colortype == 2 and 3 or 1, img.xsize, colordepth)
|
||||
else
|
||||
content = rawimage(t, content)
|
||||
if colortype & 4 == 4 then -- Alpha channel present
|
||||
local mask
|
||||
content, mask = pngdecode.splitmask(
|
||||
content,
|
||||
img.xsize,
|
||||
img.ysize,
|
||||
1 + (colortype&2),
|
||||
colordepth//8) -- colordepth must be 8 or 16 if alpha is present
|
||||
local mask_dict = string.format("/Subtype/Image/Width %i/Height %i/BitsPerComponent %i/ColorSpace/DeviceGray", img.xsize, img.ysize, colordepth)
|
||||
local objnum = pfile:stream(nil, mask_dict, mask)
|
||||
dict = string.format('%s/SMask %i 0 R', dict, objnum)
|
||||
end
|
||||
end
|
||||
|
||||
if t.tRNS then
|
||||
if colortype == 3 then
|
||||
local unpacked = copy and rawimage(t, content) or content
|
||||
if colordepth ~= 8 then
|
||||
unpacked = pngdecode.expand(unpacked, img.xsize, img.ysize, colordepth, (img.xsize*colordepth+7)//8)
|
||||
end
|
||||
unpacked = t.tRNS:match(unpacked)
|
||||
local mask_dict = string.format("/Subtype/Image/Width %i/Height %i/BitsPerComponent 8/ColorSpace/DeviceGray", img.xsize, img.ysize)
|
||||
local objnum = pfile:stream(nil, mask_dict, unpacked)
|
||||
dict = string.format('%s/SMask %i 0 R', dict, objnum)
|
||||
else
|
||||
dict = string.format('%s/Mask[%s]', dict, t.tRNS)
|
||||
end
|
||||
end
|
||||
|
||||
local attr = img.attr
|
||||
if attr then
|
||||
dict = dict .. attr
|
||||
end
|
||||
pfile:stream(img.objnum, dict, content, nil, copy)
|
||||
end
|
||||
|
||||
return png_functions
|
@ -6,109 +6,21 @@ local setwhd = node.direct.setwhd
|
||||
local tonode = node.direct.tonode
|
||||
local nodewrite = node.write
|
||||
|
||||
local box_fallback = {
|
||||
BleedBox = "CropBox",
|
||||
TrimBox = "CropBox",
|
||||
ArtBox = "CropBox",
|
||||
CropBox = "MediaBox",
|
||||
}
|
||||
|
||||
local boxmap = {
|
||||
media = "MediaBox",
|
||||
crop = "CropBox",
|
||||
bleed = "BleedBox",
|
||||
trim = "TrimBox",
|
||||
art = "ArtBox",
|
||||
-- Mapping extensions to canonical type names if necessary
|
||||
local imagetype_map = {
|
||||
-- pdf1 = 'pdf',
|
||||
}
|
||||
local imagetypes = setmetatable({}, {__index = function(t, k)
|
||||
local remapped = imagetype_map[k]
|
||||
local module = remapped and t[remapped] or require('luametalatex-pdf-image-' .. k)
|
||||
t[k] = module
|
||||
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 function get_box(page, box)
|
||||
box = boxmap[box]
|
||||
while box do
|
||||
local found = pdfe.getbox(page, box)
|
||||
if found then
|
||||
return {to_sp(found[1]), to_sp(found[2]), to_sp(found[3]), to_sp(found[4])}
|
||||
end
|
||||
box = box_fallback[box]
|
||||
end
|
||||
end
|
||||
|
||||
local function open_pdfe(img)
|
||||
local file = pdfe.open(img.filepath)
|
||||
do
|
||||
local userpassword = img.userpassword
|
||||
local ownerpassword = img.ownerpassword
|
||||
if userpassword or ownerpassword then
|
||||
pdfe.unencrypt(file, userpassword, ownerpassword)
|
||||
end
|
||||
end
|
||||
local status = pdfe.getstatus(file)
|
||||
if status >= 0 then
|
||||
return file
|
||||
elseif status == -1 then
|
||||
error[[PDF image is encrypted. Please provide the decryption key.]]
|
||||
elseif status == -2 then
|
||||
error[[PDF image could not be opened.]]
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
local function scan_pdf(img)
|
||||
local file = open_pdfe(img)
|
||||
img.imagetype = 'pdf'
|
||||
img.pages = pdfe.getnofpages(file)
|
||||
img.page = img.page or 1
|
||||
if img.page > img.pages then
|
||||
error[[Not enough pages in PDF image]]
|
||||
end
|
||||
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 = (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'
|
||||
local function write_pdf(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", bbox[1], bbox[2], bbox[3], bbox[4], 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
|
||||
-- Contents is missing. Then create an empty stream.
|
||||
local type = pdfe.type(content)
|
||||
if type == 'pdfe.stream' then
|
||||
raw = true
|
||||
for i=1,#content do
|
||||
local key, type, value, detail = pdfe.getfromstream(content, i)
|
||||
dict = dict .. pdfe_deepcopy(file, img.filepath, pfile, 5, key) .. ' ' .. pdfe_deepcopy(file, img.filepath, pfile, type, value, detail)
|
||||
end
|
||||
content = content(false)
|
||||
elseif type == 'pdfe.array' then
|
||||
local array = content
|
||||
content = ''
|
||||
for i=1,#array do
|
||||
content = content .. array[i](true)
|
||||
end
|
||||
else
|
||||
content = ''
|
||||
end
|
||||
local attr = img.attr
|
||||
if attr then
|
||||
dict = dict .. attr
|
||||
end
|
||||
pfile:stream(img.objnum, dict, content, nil, raw)
|
||||
end
|
||||
|
||||
local liberal_keys = {height = true, width = true, depth = true, transform = true}
|
||||
local real_images = {}
|
||||
local function relaxed_newindex(t, k, v)
|
||||
@ -150,9 +62,13 @@ local function scan(img)
|
||||
real = real_images[img]
|
||||
if real.stream then error[[stream images are not yet supported]] end
|
||||
assert(real.filename)
|
||||
if not real.filename:match'%.pdf$' then error[[Currently only PDF images are supported]] end
|
||||
-- TODO: At some point we should just take the lowercased extension
|
||||
local imagetype = real.filename:match'%.pdf$' and 'pdf'
|
||||
or real.filename:match'%.png$' and 'png'
|
||||
or error'Unsupported image format'
|
||||
real.filepath = assert(kpse.find_file(real.filename), "Image not found")
|
||||
scan_pdf(real)
|
||||
real.imagetype = imagetype
|
||||
imagetypes[imagetype].scan(real)
|
||||
setmetatable(img, restricted_meta)
|
||||
end
|
||||
img.transform = img.transform or 0
|
||||
@ -163,13 +79,27 @@ local function scan(img)
|
||||
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 xsize, ysize = real.xsize, real.ysize
|
||||
if not real.bbox then
|
||||
local xres, yres = img.xres, img.yres
|
||||
-- TODO: \pdfvariable Parameters
|
||||
if xres == 0 then
|
||||
xres = 72
|
||||
yres = xres * ((not yres or yres == 0) and 1 or yres)
|
||||
elseif yres == 0 then
|
||||
yres = 72
|
||||
xres = yres * ((not xres or xres == 0) and 1 or xres)
|
||||
end
|
||||
local xscale, yscale = 4736286.72/xres, 4736286.72/yres
|
||||
xsize, ysize = xsize*xscale//1, ysize*yscale//1
|
||||
end
|
||||
local total_y
|
||||
if flipped then
|
||||
img.width = real.ysize
|
||||
total_y = real.xsize
|
||||
img.width = ysize
|
||||
total_y = xsize
|
||||
else
|
||||
img.width = real.xsize
|
||||
total_y = real.ysize
|
||||
img.width = xsize
|
||||
total_y = ysize
|
||||
end
|
||||
if img.height then
|
||||
img.depth = total_y - img.height
|
||||
@ -177,7 +107,7 @@ local function scan(img)
|
||||
img.height = total_y - img.depth
|
||||
end
|
||||
else
|
||||
local ratio = flipped and real.xsize / real.ysize or real.ysize / real.xsize
|
||||
local ratio = flipped and xsize / ysize or ysize / xsize
|
||||
if img.width then
|
||||
if img.depth then
|
||||
img.height = (ratio * img.width - img.depth) // 1
|
||||
@ -210,7 +140,7 @@ local function write_img(pfile, img)
|
||||
local objnum = reserve(pfile, img)
|
||||
if not img.written then
|
||||
img.written = true
|
||||
write_pdf(pfile, img)
|
||||
imagetypes[img.imagetype].write(pfile, img)
|
||||
end
|
||||
end
|
||||
local function do_img(data, p, n, x, y)
|
||||
@ -222,7 +152,13 @@ local function do_img(data, p, n, x, y)
|
||||
height = height + depth
|
||||
local bbox = img.bbox
|
||||
local xsize, ysize = img.xsize, img.ysize
|
||||
local a, b, c, d, e, f = 1, 0, 0, 1, -bbox[1], -bbox[2]
|
||||
local a, b, c, d, e, f = 1, 0, 0, 1
|
||||
if bbox then
|
||||
e, f = -bbox[1], -bbox[2]
|
||||
else
|
||||
e, f = 0, 0
|
||||
xsize, ysize = 65781.76, 65781.76
|
||||
end
|
||||
if mirror then
|
||||
a, e = -a, -e+xsize
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user