diff options
author | Hans Hagen <pragma@wxs.nl> | 2018-10-17 15:43:08 +0200 |
---|---|---|
committer | Context Git Mirror Bot <phg@phi-gamma.net> | 2018-10-17 15:43:08 +0200 |
commit | e5f189d9965a70c8f3043c07c7b07d41ef268e93 (patch) | |
tree | ea192c431e2762fbae01fdd44cb3a48278b06fc0 /tex/context/base/mkiv/lpdf-img.lua | |
parent | 9f36318b0e571bcbc93dc2112d01bbf2178ed020 (diff) | |
download | context-e5f189d9965a70c8f3043c07c7b07d41ef268e93.tar.gz |
2018-10-17 15:14:00
Diffstat (limited to 'tex/context/base/mkiv/lpdf-img.lua')
-rw-r--r-- | tex/context/base/mkiv/lpdf-img.lua | 1061 |
1 files changed, 1061 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/lpdf-img.lua b/tex/context/base/mkiv/lpdf-img.lua new file mode 100644 index 000000000..28890887a --- /dev/null +++ b/tex/context/base/mkiv/lpdf-img.lua @@ -0,0 +1,1061 @@ +if not modules then modules = { } end modules ['lpdf-img'] = { + version = 1.001, + comment = "companion to lpdf-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local concat, remove, insert = table.concat, table.remove, table.insert +local ceil = math.ceil +local char, find = string.char, string.find +local idiv = number.idiv +local band, rshift = bit32.band, bit32.rshift + +local loaddata = io.loaddata +local setmetatableindex = table.setmetatableindex + +local streams = utilities.streams +local openstring = streams.openstring +local readstring = streams.readstring +local readbytetable = streams.readbytetable + +local lpdf = lpdf or { } +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfconstant = lpdf.constant +local pdfstring = lpdf.string +local pdfflushstreamobject = lpdf.flushstreamobject +local pdfreference = lpdf.reference + +local pdfmajorversion = lpdf.majorversion +local pdfminorversion = lpdf.minorversion + +local newimage = img.new + +local trace = false + +local report_jpg = logs.reporter("graphics","jpg") +local report_jp2 = logs.reporter("graphics","jp2") +local report_png = logs.reporter("graphics","png") + +local injectors = { } +lpdf.injectors = injectors + +local chars = setmetatableindex(function(t,k) -- share this one + local v = (k <= 0 and "\000") or (k >= 255 and "\255") or char(k) + t[k] = v + return v +end) + +do + + function injectors.jpg(specification) + if specification.error then + return + end + local filename = specification.filename + if not filename then + return + end + local colorspace = specification.colorspace or jpg_gray + local attributes = specification.attr + local decodearray = nil + ----- procset = colorspace == 0 and "image b" or "image c" + if colorspace == 1 then + colorspace = "DeviceGray" + elseif colorspace == 2 then + colorspace = "DeviceRGB" + elseif colorspace == 3 then + colorspace = "DeviceCMYK" + decodearray = pdfarray { 1, 0, 1, 0, 1, 0, 1, 0 } + end + -- todo: set filename + local xsize = specification.xsize + local ysize = specification.ysize + local colordepth = specification.colordepth + local content = loaddata(filename) + local xobject = pdfdictionary { + Type = pdfconstant("XObject"), + Subtype = pdfconstant("Image"), + BBox = pdfarray { 0, 0, xsize, ysize }, + Width = xsize, + Height = ysize, + BitsPerComponent = colordepth, + Filter = pdfconstant("DCTDecode"), + ColorSpace = pdfconstant(colorspace), + Decode = decodearray, + Length = #content, -- specification.length + } + if attributes then + -- todo: add attributes to d + end + if trace then + report_jpg("%s: width %i, height %i, colordepth %i, size %i",filename,xsize,ysize,colordepth,#content) + end + return newimage { + bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate + nolength = true, + nobbox = true, + notype = true, + stream = content, + attr = xobject(), + } + end + +end + +do + + function injectors.jp2(specification) + if specification.error then + return + end + local filename = specification.filename + if not filename then + return + end + local attributes = specification.attr + -- todo: set filename + local xsize = specification.xsize + local ysize = specification.ysize + local content = loaddata(filename) + local xobject = pdfdictionary { + Type = pdfconstant("XObject"), + Subtype = pdfconstant("Image"), + BBox = pdfarray { 0, 0, xsize, ysize }, + Width = xsize, + Height = ysize, + Filter = pdfconstant("JPXDecode"), + Length = #content, -- specification.length + } + if attributes then + -- todo: add attributes to d + end + if trace then + report_jp2("%s: width %i, height %i, size %i",filename,xsize,ysize,#content) + end + return newimage { + bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate + nolength = true, + nobbox = true, + notype = true, + stream = content, + attr = xobject(), + } + end + +end + +do + + -- We don't like interlaced files. You can deinterlace them beforehand because otherwise + -- each run you add runtime. Actually, even masked images can best be converted to PDF + -- beforehand. + + -- The amount of code is larger that I like and looks somewhat redundant but we sort of + -- optimize a few combinations that happen often. + + local function convert(t,len) + if len then +local n = 0 + for i=1,#t,len do + t[i] = "" +n = n + 1 + end + end + for i=1,#t do + local ti = t[i] + if ti ~= "" then + t[i] = chars[ti] + end + end + return concat(t) + end + + local function zero(t,k) + return 0 + end + + local function bump(txt,t,xsize,ysize,bpp) + local l = xsize * bpp + 1 + print(txt,">",xsize,ysize,bpp,l) + for i=1,ysize do + local f = (i-1) * l + 1 + print(txt,i,":",concat(t," ",f,f+l-1)) + end + end + + local function decodeall(t,xsize,ysize,bpp) + local len = xsize * bpp + 1 + local n = 1 + local m = len - 1 + for i=1,ysize do + local filter = t[n] +t[n] = 0 -- not needed + if filter == 0 then + elseif filter == 1 then + for j=n+bpp+1,n+m do + t[j] = (t[j] + t[j-bpp]) % 256 + end + elseif filter == 2 then + for j=n+1,n+m do + t[j] = (t[j] + t[j-len]) % 256 + end + elseif filter == 3 then + for j=n+1,n+bpp do + t[j] = (t[j] + idiv(t[j-len],2)) % 256 + end + for j=n+bpp+1,n+m do + t[j] = (t[j] + idiv(t[j-bpp] + t[j-len],2)) % 256 + end + elseif filter == 4 then + for j=n+1,n+bpp do + local p = j - len + local b = t[p] + if b < 0 then + b = - b + end + if b > 0 then + t[j] = (t[j] + b) % 256 + end + end + for j=n+bpp+1,n+m do + local p = j - len + local a = t[j-bpp] + local b = t[p] + local c = t[p-bpp] + local pa = b - c + local pb = a - c + local pc = pa + pb + if pa < 0 then pa = - pa end + if pb < 0 then pb = - pb end + if pc < 0 then pc = - pc end + t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256 + end + end + n = n + len + end + end + + local xstart = { 0, 4, 0, 2, 0, 1, 0 } + local xstep = { 8, 8, 4, 4, 2, 2, 1 } + local ystart = { 0, 0, 4, 0, 2, 0, 1 } + local ystep = { 8, 8, 8, 4, 4, 2, 2 } + ----- xmax = { 8, 4, 4, 2, 2, 1, 1 } -- for block fill + ----- ymax = { 8, 8, 4, 4, 2, 2, 1 } -- for block fill + + local function newoutput(width,height) + local t = { } + for i=1,height*width do + t[i] = 0 + end + return t + end + + local function expand(t,xsize,ysize,parts,factor) -- we don't compact + local o = { } + local k = 0 + local l = ceil(xsize*parts/8) + 1 + local n = 1 + if factor then + if parts == 4 then + for i=1,ysize do + k = k + 1 ; o[k] = t[n] + for j=n+1,n+l do + local v = t[j] + k = k + 1 ; o[k] = band(rshift(v,4),0x0F) * 0x11 ; k = k + 1 ; o[k] = band(rshift(v,0),0x0F) * 0x11 + end + k = i * (xsize + 1) + n = n + l + end + elseif parts == 2 then + for i=1,ysize do + k = k + 1 ; o[k] = t[n] + for j=n+1,n+l do + local v = t[j] + k = k + 1 ; o[k] = band(rshift(v,6),0x03) * 0x55 ; k = k + 1 ; o[k] = band(rshift(v,4),0x03) * 0x55 + k = k + 1 ; o[k] = band(rshift(v,2),0x03) * 0x55 ; k = k + 1 ; o[k] = band(rshift(v,0),0x03) * 0x55 + end + k = i * (xsize + 1) + n = n + l + end + else + for i=1,ysize do + k = k + 1 ; o[k] = t[n] + for j=n+1,n+l do + local v = t[j] + k = k + 1 ; o[k] = band(rshift(v,7),0x01) * 0xFF ; k = k + 1 ; o[k] = band(rshift(v,6),0x01) * 0xFF + k = k + 1 ; o[k] = band(rshift(v,5),0x01) * 0xFF ; k = k + 1 ; o[k] = band(rshift(v,4),0x01) * 0xFF + k = k + 1 ; o[k] = band(rshift(v,3),0x01) * 0xFF ; k = k + 1 ; o[k] = band(rshift(v,2),0x01) * 0xFF + k = k + 1 ; o[k] = band(rshift(v,1),0x01) * 0xFF ; k = k + 1 ; o[k] = band(rshift(v,0),0x01) * 0xFF + end + k = i * (xsize + 1) + n = n + l + end + end + else + if parts == 4 then + for i=1,ysize do + k = k + 1 ; o[k] = t[n] + for j=n+1,n+l do + local v = t[j] + k = k + 1 ; o[k] = band(rshift(v,4),0x0F) ; k = k + 1 ; o[k] = band(rshift(v,0),0x0F) + end + k = i * (xsize + 1) + n = n + l + end + elseif parts == 2 then + for i=1,ysize do + k = k + 1 ; o[k] = t[n] + for j=n+1,n+l do + local v = t[j] + k = k + 1 ; o[k] = band(rshift(v,6),0x03) ; k = k + 1 ; o[k] = band(rshift(v,4),0x03) + k = k + 1 ; o[k] = band(rshift(v,2),0x03) ; k = k + 1 ; o[k] = band(rshift(v,0),0x03) + end + k = i * (xsize + 1) + n = n + l + end + else + for i=1,ysize do + k = k + 1 ; o[k] = t[n] + for j=n+1,n+l do + local v = t[j] + k = k + 1 ; o[k] = band(rshift(v,7),0x01) ; k = k + 1 ; o[k] = band(rshift(v,6),0x01) + k = k + 1 ; o[k] = band(rshift(v,5),0x01) ; k = k + 1 ; o[k] = band(rshift(v,4),0x01) + k = k + 1 ; o[k] = band(rshift(v,3),0x01) ; k = k + 1 ; o[k] = band(rshift(v,2),0x01) + k = k + 1 ; o[k] = band(rshift(v,1),0x01) ; k = k + 1 ; o[k] = band(rshift(v,0),0x01) + end + k = i * (xsize + 1) + n = n + l + end + end + end + for i=#o,k+1,-1 do + o[i] = nil + end + return o + end + + local function deinterlaceXX(s,xsize,ysize,bytes,parts,factor) + local output = newoutput(xsize*bytes,ysize) + for pass=1,7 do + local ystart = ystart[pass] + local ystep = ystep[pass] + local xstart = xstart[pass] + local xstep = xstep[pass] + local nx = idiv(xsize + xstep - xstart - 1,xstep) + local ny = idiv(ysize + ystep - ystart - 1,ystep) + if nx > 0 and ny > 0 then + local input + if parts then + local nxx = ceil(nx*parts/8) + input = readbytetable(s,ny*(nxx+1)) + setmetatableindex(input,zero) + decodeall(input,nxx,ny,bytes) + input = expand(input,nx,ny,parts,factor) + else + input = readbytetable(s,ny*(nx*bytes+1)) + setmetatableindex(input,zero) + decodeall(input,nx,ny,bytes) + end + local l = nx*bytes + 1 + for i=ny,1,-1 do + remove(input,(i-1)*l+1) + end + local xstep = xstep * bytes + local xstart = xstart * bytes + local xsize = xsize * bytes + local target = ystart * xsize + xstart + 1 + local ystep = ystep * xsize + local start = 1 + local blobs = bytes - 1 + for j=0,ny-1 do + local target = target + j * ystep + for i=1,nx do + for i=0,blobs do + output[target+i] = input[start] + start= start + 1 + end + target = target + xstep + end + end + end + end + return output + end + + local function deinterlaceYY(s,xsize,ysize,bytes,parts,factor) + local input + if parts then + local nxx = ceil(xsize*parts/8) + input = readbytetable(s,ysize*(nxx+1)) + setmetatableindex(input,zero) + decodeall(input,nxx,ysize,bytes) + input = expand(input,xsize,ysize,parts,factor) + else + input = readbytetable(s,ysize*(xsize*bytes+1)) + setmetatableindex(input,zero) + decodeall(input,xsize,ysize,bytes) + end + local l = xsize*bytes + 1 + local n = 1 + for i=1,ysize do + input[n] = "" + n = n + l + end + return input + end + + local function analyze(colordepth,colorspace,palette,mask) + local bytes, parts, factor + if palette then + if colordepth == 16 then + return 2, false, false + elseif colordepth == 8 then + return 1, false, false + elseif colordepth == 4 then + return 1, 4, false + elseif colordepth == 2 then + return 1, 2, false + elseif colordepth == 1 then + return 1, 1, false + end + elseif colorspace == "DeviceGray" then + if colordepth == 16 then + return mask and 4 or 2, false, false + elseif colordepth == 8 then + return mask and 2 or 1, false, false + elseif colordepth == 4 then + return 1, 4, true + elseif colordepth == 2 then + return 1, 2, true + elseif colordepth == 1 then + return 1, 1, true + end + else + if colordepth == 16 then + return mask and 8 or 6, false, false + elseif colordepth == 8 then + return mask and 4 or 3, false, false + elseif colordepth == 4 then + return 3, 4, true + elseif colordepth == 2 then + return 3, 2, true + elseif colordepth == 1 then + return 3, 1, true + end + end + return false, false, false + end + + local function deinterlace(content,xsize,ysize,colordepth,colorspace,palette,mask) + local bytes, parts, factor = analyze(colordepth,colorspace,palette,mask) + if bytes then + content = zlib.decompress(content) + local s = openstring(content) + local r = deinterlaceXX(s,xsize,ysize,bytes,parts,factor) + return r, parts and 8 or false + end + end + + local function decompose(content,xsize,ysize,colordepth,colorspace,palette,mask) + local bytes, parts, factor = analyze(colordepth,colorspace,palette,mask) + if bytes then + content = zlib.decompress(content) + local s = openstring(content) + local r = deinterlaceYY(s,xsize,ysize,bytes,parts,factor) + return r, parts and 8 or false + end + end + + -- 1 (palette used), 2 (color used), and 4 (alpha channel used) + + -- paeth: + -- + -- p = a + b - c + -- pa = abs(p - a) => a + b - c - a => b - c + -- pb = abs(p - b) => a + b - c - b => a - c + -- pc = abs(p - c) => a + b - c - c => a + b - c - c => a - c + b - c => pa + pb + + local function prepareimage(content,xsize,ysize,depth,colorspace,mask) + local bpp = (depth == 16 and 2 or 1) * ((colorspace == "DeviceRGB" and 3 or 1) + mask) local len = bpp * xsize + 1 + local s = openstring(content) + local t = readbytetable(s,#content) + setmetatableindex(t,zero) + return t, bpp, len + end + + local function filtermask08(xsize,ysize,t,bpp,len,n) + local mask = { } + local l = 0 + local m = len - n + for i=1,ysize do + for j=n+bpp,n+m,bpp do + l = l + 1 ; mask[l] = chars[t[j]] ; t[j] = "" + end + n = n + len + end + return concat(mask) + end + + local function filtermask16(xsize,ysize,t,bpp,len,n) + local mask = { } + local l = 0 + local m = len - n + for i=1,ysize do + for j=n+bpp-1,n+m-1,bpp do + l = l + 1 ; mask[l] = chars[t[j]] ; t[j] = "" + j = j + 1 + l = l + 1 ; mask[l] = chars[t[j]] ; t[j] = "" + end + n = n + len + end + return concat(mask) + end + + local function decodemask08(content,xsize,ysize,depth,colorspace) + local t, bpp, len = prepareimage(content,xsize,ysize,depth,colorspace,1) + local bpp2 = mask and (bpp + bpp) or bpp + local n = 1 + local m = len - 1 + for i=1,ysize do + local filter = t[n] + if filter == 0 then + elseif filter == 1 then + for j=n+bpp2,n+m,bpp do + t[j] = (t[j] + t[j-bpp]) % 256 + end + elseif filter == 2 then + for j=n+bpp,n+m,bpp do + t[j] = (t[j] + t[j-len]) % 256 + end + elseif filter == 3 then + local j = n + bpp + t[j] = (t[j] + idiv(t[j-len],2)) % 256 + for j=n+bpp2,n+m,bpp do + t[j] = (t[j] + idiv(t[j-bpp] + t[j-len],2)) % 256 + end + elseif filter == 4 then + local j = n + bpp + local p = j - len + local b = t[p] + if b < 0 then + b = - b + end + if b > 0 then + t[j] = (t[j] + b) % 256 + end + for j=n+bpp2,n+m,bpp do + local p = j - len + local a = t[j-bpp] + local b = t[p] + local c = t[p-bpp] + local pa = b - c + local pb = a - c + local pc = pa + pb + if pa < 0 then pa = - pa end + if pb < 0 then pb = - pb end + if pc < 0 then pc = - pc end + t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256 + end + end + n = n + len + end + local mask = filtermask08(xsize,ysize,t,bpp,len,1) + return convert(t), mask + end + + local function decodemask16(content,xsize,ysize,depth,colorspace) + local t, bpp, len = prepareimage(content,xsize,ysize,depth,colorspace,1) + local bpp2 = bpp + bpp + local n = 1 + local m = len - 1 + for i=1,ysize do + local filter = t[n] + if filter == 0 then + elseif filter == 1 then + for j=n+bpp2,n+m,bpp do + local k = j - 1 + t[j] = (t[j] + t[j-bpp]) % 256 + t[k] = (t[k] + t[k-bpp]) % 256 + end + elseif filter == 2 then + for j=n+bpp,n+m,bpp do + local k = j - 1 + t[j] = (t[j] + t[j-len]) % 256 + t[k] = (t[k] + t[k-len]) % 256 + end + elseif filter == 3 then + local j = n + bpp + local k = j - 1 + t[j] = (t[j] + idiv(t[j-len],2)) % 256 + t[k] = (t[k] + idiv(t[k-len],2)) % 256 + for j=n+bpp2,n+m,bpp do + local k = j - 1 + t[j] = (t[j] + idiv(t[j-bpp] + t[j-len],2)) % 256 + t[k] = (t[k] + idiv(t[k-bpp] + t[k-len],2)) % 256 + end + elseif filter == 4 then + for i=-1,0 do + local j = n + bpp + i + local p = j - len + local b = t[p] + if b < 0 then + b = - b + end + if b > 0 then + t[j] = (t[j] + b) % 256 + end + for j=n+i+bpp2,n+i+m,bpp do + local p = j - len + local a = t[j-bpp] + local b = t[p] + local c = t[p-bpp] + local pa = b - c + local pb = a - c + local pc = pa + pb + if pa < 0 then pa = - pa end + if pb < 0 then pb = - pb end + if pc < 0 then pc = - pc end + t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256 + end + end + end + n = n + len + end + local mask = filtermask16(xsize,ysize,t,bpp,len,1) + return convert(t), mask + end + + local function full(t,k) local v = "\xFF" t[k] = v return v end + + local function create(content,palette,transparent,xsize,ysize,colordepth,colorspace) + if palette then + -- + local s = openstring(transparent) + local n = #transparent + local r = { } + for i=0,n-1 do + r[i] = readstring(s,1) + end + setmetatableindex(r,full) + -- + local c = zlib.decompress(content) + local s = openstring(c) + -- + local o = { } + local len = ceil(xsize*colordepth/8) + 1 + local m = len - 1 + local u = setmetatableindex(zero) + -- + for i=1,ysize do + local t = readbytetable(s,len) + local k = (i-1) * xsize + local filter = t[1] + if filter == 0 then + elseif filter == 1 then + for j=3,len do + t[j] = (t[j] + t[j-1]) % 256 + end + elseif filter == 2 then + for j=2,len do + t[j] = (t[j] + u[j]) % 256 + end + elseif filter == 3 then + local j = 2 + t[j] = (t[j] + idiv(u[j],2)) % 256 + for j=3,len do + t[j] = (t[j] + idiv(t[j-1] + u[j],2)) % 256 + end + elseif filter == 4 then + local j = 2 + local p = j - len + local b = t[p] + if b < 0 then + b = - b + end + if b > 0 then + t[j] = (t[j] + b) % 256 + end + for j=3,len do + local p = j - len + local a = t[j-1] + local b = t[p] + local c = t[p-1] + local pa = b - c + local pb = a - c + local pc = pa + pb + if pa < 0 then pa = - pa end + if pb < 0 then pb = - pb end + if pc < 0 then pc = - pc end + t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256 + end + end + if colordepth == 8 then + for j=2,len do + local v = t[j] + k = k + 1 ; o[k] = r[v] or "\xFF" + end + elseif colordepth == 4 then + for j=2,len do + local v = t[j] + k = k + 1 ; o[k] = r[band(rshift(v,4),0x0F)] + k = k + 1 ; o[k] = r[band(rshift(v,0),0x0F)] + end + elseif colordepth == 2 then + for j=2,len do + local v = t[j] + k = k + 1 ; o[k] = r[band(rshift(v,6),0x03)] + k = k + 1 ; o[k] = r[band(rshift(v,4),0x03)] + k = k + 1 ; o[k] = r[band(rshift(v,2),0x03)] + k = k + 1 ; o[k] = r[band(rshift(v,0),0x03)] + end + else + for j=2,len do + local v = t[j] + k = k + 1 ; o[k] = r[band(rshift(v,7),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,6),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,5),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,4),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,3),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,2),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,2),0x01)] + k = k + 1 ; o[k] = r[band(rshift(v,1),0x01)] + end + end + u = t + end + return concat(o,"",1,ysize * xsize) + end + end + + local alwaysdecode = false + + directives.register("graphics.png.decode", function(v) + alwaysdecode = v + end) + + function injectors.png(specification) + if specification.error then + return + end + local filename = specification.filename + if not filename then + return + end + local colorspace = specification.colorspace + if not colorspace then + return + end + local interlace = specification.interlace or 0 + if interlace == 1 then + interlace = true + elseif interlace == 0 then + interlace = false + else + report_png("unknown interlacing %i",interlace) + return + end + local tables = specification.tables + if not tables then + return + end + local idat = tables.idat + if not idat then + return + end + local pngfile = io.open(filename,"rb") + if not pngfile then + return + end + local content = idat(pngfile,true) + tables.idat = false + -- + -- if tables.gama then + -- report_png("ignoring gamma correction") + -- end + -- + local xsize = specification.xsize + local ysize = specification.ysize + local colordepth = specification.colordepth or 8 + local mask = false + local transparent = false + local palette = false + local colors = 1 + if colorspace == 0 then -- gray | image b + colorspace = "DeviceGray" + transparent = true + elseif colorspace == 2 then -- rgb | image c + colorspace = "DeviceRGB" + colors = 3 + transparent = true + elseif colorspace == 3 then -- palette | image c+i + colorspace = "DeviceRGB" + palette = true + transparent = true + elseif colorspace == 4 then -- gray | alpha | image b + colorspace = "DeviceGray" + mask = true + elseif colorspace == 6 then -- rgb | alpha | image c + colorspace = "DeviceRGB" + colors = 3 + mask = true + else + report_png("unknown colorspace %i",colorspace) + return + end + -- + if transparent then + local trns = tables.trns + if trns then + transparent = trns(pngfile,true) + if transparent == "" then + transparent = false + end + tables.trns = false + else + transparent = false + end + end + -- + local decode = alwaysdecode + local major = pdfmajorversion() + local minor = pdfminorversion() + if major > 1 then + -- we're okay + elseif minor < 5 and colordepth == 16 then + report_png("16 bit colordepth not supported in pdf < 1.5") + return + elseif minor < 4 and (mask or transparent) then + report_png("alpha channels not supported in pdf < 1.4") + return + elseif minor < 2 then + decode = true + end + -- + -- todo: compresslevel (or delegate) + -- + if palette then + local plte = tables.plte + if plte then + palette = plte(pngfile,true) + if palette == "" then + palette = false + end + tables.plte = false + else + palette = false + end + end + -- + if interlace then + local r, p = deinterlace(content,xsize,ysize,colordepth,colorspace,palette,mask) + if not r then + return + end + if p then + colordepth = p + end + if mask then + local bpp = (colordepth == 16 and 2 or 1) * ((colorspace == "DeviceRGB" and 3 or 1) + 1) + local len = bpp * xsize -- + 1 + if colordepth == 8 then -- bpp == 1 + mask = filtermask08(xsize,ysize,r,bpp,len,0) + elseif colordepth == 16 then -- bpp == 2 + mask = filtermask16(xsize,ysize,r,bpp,len,0) + else + report_png("mask can't be split from the image") + return + end + end + decode = true + content = convert(r) + content = zlib.compress(content) + elseif mask then + local decoder + if colordepth == 8 then + decoder = decodemask08 + elseif colordepth == 16 then + decoder = decodemask16 + end + if not decoder then + report_png("mask can't be split from the image") + return + end + content = zlib.decompress(content) + content, mask = decoder(content,xsize,ysize,colordepth,colorspace) + content = zlib.compress(content) + decode = false + elseif transparent then + if palette then + mask = create(content,palette,transparent,xsize,ysize,colordepth,colorspace) + else + pallette = false + end + elseif decode then + local r, p = decompose(content,xsize,ysize,colordepth,colorspace,palette) + if not r then + return + end + if p then + colordepth = p + end + content = convert(r) + content = zlib.compress(content) + end + if palette then + palette = pdfarray { + pdfconstant("Indexed"), + pdfconstant("DeviceRGB"), + idiv(#palette,3), + pdfreference(pdfflushstreamobject(palette)), + } + end + pngfile:close() + local xobject = pdfdictionary { + Type = pdfconstant("XObject"), + Subtype = pdfconstant("Image"), + BBox = pdfarray { 0, 0, xsize, ysize }, + Width = xsize, + Height = ysize, + BitsPerComponent = colordepth, + Filter = pdfconstant("FlateDecode"), + ColorSpace = palette or pdfconstant(colorspace), + Length = #content, + } + if mask then + local d = pdfdictionary { + Type = pdfconstant("XObject"), + Subtype = pdfconstant("Image"), + Width = xsize, + Height = ysize, + BitsPerComponent = palette and 8 or colordepth, + ColorSpace = pdfconstant("DeviceGray"), + } + xobject.SMask = pdfreference(pdfflushstreamobject(mask,d())) + end + if not decode then + xobject.DecodeParms = pdfdictionary { + Colors = colors, + Columns = xsize, + BitsPerComponent = colordepth, + Predictor = 15, + } + end + if attributes then + -- todo: add attributes to d + end + if trace then + report_png("%s: width %i, height %i, colordepth: %i, size: %i, palette %l, mask: %l, transparent %l, decode %l",filename,xsize,ysize,colordepth,#content,palette,mask,transparent,decode) + end + return newimage { + bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate + nolength = true, + nobbox = true, + notype = true, + stream = content, + attr = xobject(), + } + end + +end + +do + + local function pack(specification,what) + local t = { } + local n = 0 + local s = specification.colorspace + local d = specification.data + local x = specification.xsize + local y = specification.ysize + if what == "mask" then + d = specification.mask + s = 1 + end + if s == 1 then + for i=1,y do + local r = d[i] + for j=1,x do + n = n + 1 ; t[n] = chars[r[j]] + end + end + elseif s == 2 then + for i=1,y do + local r = d[i] + for j=1,x do + local c = r[j] + n = n + 1 ; t[n] = chars[c[1]] + n = n + 1 ; t[n] = chars[c[2]] + n = n + 1 ; t[n] = chars[c[3]] + end + end + elseif s == 3 then + for i=1,y do + local r = d[i] + for j=1,x do + local c = r[j] + n = n + 1 ; t[n] = chars[c[1]] + n = n + 1 ; t[n] = chars[c[2]] + n = n + 1 ; t[n] = chars[c[3]] + n = n + 1 ; t[n] = chars[c[4]] + end + end + end + return concat(t) + end + + function injectors.bitmap(specification) + local data = specification.data + if not data then + return + end + local xsize = specification.xsize or 0 + local ysize = specification.ysize or 0 + if xsize == 0 or ysize == 0 then + return + end + local colorspace = specification.colorspace or 1 + if colorspace == 1 then + colorspace = "DeviceGray" + elseif colorspace == 2 then + colorspace = "DeviceRGB" + elseif colorspace == 3 then + colorspace = "DeviceCMYK" + end + local colordepth = (specification.colordepth or 2) == 16 or 8 + local content = pack(specification,"data") + local mask = specification.mask + local xobject = pdfdictionary { + Type = pdfconstant("XObject"), + Subtype = pdfconstant("Image"), + BBox = pdfarray { 0, 0, xsize, ysize }, + Width = xsize, + Height = ysize, + BitsPerComponent = colordepth, + ColorSpace = pdfconstant(colorspace), + Length = #content, -- specification.length + } + if mask then + local d = pdfdictionary { + Type = pdfconstant("XObject"), + Subtype = pdfconstant("Image"), + Width = xsize, + Height = ysize, + BitsPerComponent = colordepth, + ColorSpace = pdfconstant("DeviceGray"), + } + xobject.SMask = pdfreference(pdfflushstreamobject(pack(specification,"mask"),d())) + end + return newimage { + bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate + -- nolength = true, + nobbox = true, + notype = true, + stream = content, + attr = xobject(), + } + end + +end |