Add some PK bitmap font support
This commit is contained in:
parent
ad44afdb0b
commit
06d1efeb55
203
luametalatex-font-pk.lua
Normal file
203
luametalatex-font-pk.lua
Normal file
@ -0,0 +1,203 @@
|
||||
local function decode_char(buf)
|
||||
local flag = buf:byte(1)
|
||||
local form = flag & 0x07
|
||||
local tfm, dx, dy, w, h, hoff, voff, off
|
||||
if form < 4 then
|
||||
tfm, dx, w, h, hoff, voff, off = string.unpack(">I3BBBbb", buf, 4)
|
||||
dx, dy = dx * 2^16, 0
|
||||
elseif form < 7 then
|
||||
tfm, dx, w, h, hoff, voff, off = string.unpack(">I3HHHhh", buf, 5)
|
||||
dx, dy = dx * 2^16, 0
|
||||
else
|
||||
tfm, dx, dy, w, h, hoff, voff, off = string.unpack(">I4I4I4I4I4i4i4", buf, 10)
|
||||
end
|
||||
local dyn_f, state = flag >> 4, flag & 8 == 8
|
||||
local data
|
||||
local stride = w+7>>3
|
||||
if dyn_f == 14 then
|
||||
print(state)
|
||||
-- assert(not state)
|
||||
data = lua.newtable(stride*h, 0)
|
||||
local bit_offset, saved = 0
|
||||
local delta_bit_offset = 8 - w%8
|
||||
delta_bit_offset = delta_bit_offset == 8 and 0 or delta_bit_offset
|
||||
for y=0,h-1 do
|
||||
for x=1,stride do
|
||||
if bit_offset == 0 then
|
||||
saved = buf:byte(off)
|
||||
data[y*stride+x] = saved
|
||||
else
|
||||
local saved_mask = (1<<bit_offset)-1
|
||||
local current = (saved&saved_mask) << 8-bit_offset
|
||||
saved = buf:byte(off)
|
||||
data[y*stride+x] = current | (saved & ~saved_mask) >> bit_offset
|
||||
end
|
||||
off = off+1
|
||||
end
|
||||
if delta_bit_offset then
|
||||
data[(y+1)*stride] = data[(y+1)*stride] & (0x100 - (1<<delta_bit_offset))
|
||||
end
|
||||
bit_offset = bit_offset + delta_bit_offset
|
||||
if bit_offset >= 8 then
|
||||
bit_offset = bit_offset-8
|
||||
off = off-1
|
||||
saved = buf:byte(off)
|
||||
end
|
||||
end
|
||||
else
|
||||
data = {string.rep('\0', stride*h):byte(1,-1)} -- FIXME: This is probably really slow
|
||||
local nibble, repeat_row = nil, 0
|
||||
local function get_nibble()
|
||||
if nibble then
|
||||
local cur = nibble
|
||||
nibble = nil
|
||||
off = off+1
|
||||
return cur
|
||||
else
|
||||
local cur = buf:byte(off)
|
||||
nibble = cur&0xF
|
||||
return cur >> 4
|
||||
end
|
||||
end
|
||||
local function get_packed()
|
||||
local cur = get_nibble()
|
||||
if cur == 0 then
|
||||
local i = 0
|
||||
repeat
|
||||
cur = get_nibble()
|
||||
i = i+1
|
||||
until cur ~= 0
|
||||
for _=1,i do
|
||||
cur = (cur<<4) + get_nibble()
|
||||
end
|
||||
return cur - 0xF + (13-dyn_f << 4) + dyn_f
|
||||
elseif cur <= dyn_f then
|
||||
return cur
|
||||
elseif cur < 14 then
|
||||
return (cur-dyn_f-1 << 4) + get_nibble() + dyn_f + 1
|
||||
else
|
||||
repeat_row = cur == 14 and get_packed() or 1
|
||||
return get_packed()
|
||||
end
|
||||
end
|
||||
local cur_x, cur_y = 0, 0
|
||||
while cur_y < h do
|
||||
local count = get_packed()
|
||||
repeat
|
||||
local this_line = math.min(w - cur_x, count)
|
||||
count = count - this_line
|
||||
if state then
|
||||
local cur_bit_offset = cur_x % 8
|
||||
if cur_bit_offset ~= 0 then -- We are in the middle of a byte
|
||||
cur_bit_offset = 8-cur_bit_offset -- The remaining bits in the byte
|
||||
local off = cur_y*stride+(cur_x>>3)+1
|
||||
if this_line > cur_bit_offset then -- Fill byte with ones
|
||||
data[off] = data[off] + (1<<cur_bit_offset)-1
|
||||
this_line, cur_x = this_line-cur_bit_offset, cur_x+cur_bit_offset
|
||||
else
|
||||
data[off] = data[off] + (1<<cur_bit_offset)-(1<<cur_bit_offset-this_line)
|
||||
this_line, cur_x = 0, cur_x+this_line
|
||||
end
|
||||
end
|
||||
while this_line >= 8 do
|
||||
data[cur_y*stride+(cur_x>>3)+1] = 0xFF
|
||||
this_line, cur_x = this_line-8, cur_x+8
|
||||
end
|
||||
if this_line ~= 0 then
|
||||
data[cur_y*stride+(cur_x>>3)+1] = 0x100-(1<<8-this_line)
|
||||
end
|
||||
end
|
||||
cur_x = cur_x + this_line
|
||||
if cur_x == w then
|
||||
for i = 1, repeat_row do
|
||||
table.move(data, cur_y*stride+1, (cur_y+1)*stride, (cur_y+i)*stride+1) -- TODO
|
||||
end
|
||||
cur_y, cur_x, repeat_row = cur_y + 1 + repeat_row, 0, 0
|
||||
end
|
||||
until count == 0
|
||||
state = not state
|
||||
end
|
||||
end
|
||||
data = string.char(table.unpack(data))
|
||||
return {
|
||||
data = data,
|
||||
tfm = tfm,
|
||||
dx = dx,
|
||||
dy = dy,
|
||||
hoff = hoff,
|
||||
voff = voff,
|
||||
w = w,
|
||||
h = h,
|
||||
}
|
||||
end
|
||||
|
||||
local commands = {
|
||||
[240] = function(buf, off, t)
|
||||
local xxx xxx, off = string.unpack(">xs1", buf, off)
|
||||
return off
|
||||
end,
|
||||
[241] = function(buf, off, t)
|
||||
local xxx xxx, off = string.unpack(">xs2", buf, off)
|
||||
return off
|
||||
end,
|
||||
[242] = function(buf, off, t)
|
||||
local xxx xxx, off = string.unpack(">xs3", buf, off)
|
||||
return off
|
||||
end,
|
||||
[243] = function(buf, off, t)
|
||||
local xxx xxx, off = string.unpack(">xs4", buf, off)
|
||||
return off
|
||||
end,
|
||||
[244] = function(buf, off, t)
|
||||
local yyy yyy, off = string.unpack(">xI4", buf, off)
|
||||
return off
|
||||
end,
|
||||
[247] = function(buf, off, t)
|
||||
local ident
|
||||
ident, t.comment, t.designsize, t.checksum, t.hppp, t.vppp, off = string.unpack(">xBs1I4I4I4I4", buf, off)
|
||||
if ident ~= 89 then
|
||||
error[[Not a PK file]]
|
||||
end
|
||||
return off
|
||||
end,
|
||||
}
|
||||
local function parse_commands(buf, off, t)
|
||||
local cmd = buf:byte(off)
|
||||
assert(cmd == 247)
|
||||
repeat
|
||||
if cmd < 240 then
|
||||
local form = cmd & 0x07
|
||||
local chr, newoff, length
|
||||
if form < 4 then
|
||||
length, chr, newoff = string.unpack(">xBB", buf, off)
|
||||
length = length + ((form & 3)<<8)
|
||||
elseif form < 7 then
|
||||
length, chr, newoff = string.unpack(">xHB", buf, off)
|
||||
length = length + ((form & 3)<<16)
|
||||
else
|
||||
length, chr, newoff = string.unpack(">xI4I4", buf, off)
|
||||
end
|
||||
newoff = newoff + length
|
||||
t[chr] = decode_char(buf:sub(off, newoff))
|
||||
off = newoff
|
||||
else
|
||||
local handler = commands[cmd]
|
||||
if not handler then
|
||||
print([[Unknown command ]] .. cmd)
|
||||
return off-1
|
||||
end
|
||||
off = handler(buf, off, t)
|
||||
end
|
||||
cmd = buf:byte(off)
|
||||
until cmd == 245
|
||||
return off
|
||||
end
|
||||
return function(name)
|
||||
local f = assert(io.open(kpse.find_file(name, 'pk', 600), 'rb')) -- TODO configurable dpi
|
||||
local pk = f:read'a'
|
||||
f:close()
|
||||
local res = {}
|
||||
off = parse_commands(pk, off, res)
|
||||
-- assert(off == #pk+1) -- TODO: Check that only fillup bytes follow
|
||||
return res
|
||||
end
|
28
luametalatex-pdf-font-pk.lua
Normal file
28
luametalatex-pdf-font-pk.lua
Normal file
@ -0,0 +1,28 @@
|
||||
local read_pk = require'luametalatex-font-pk'
|
||||
return function(pdf, fontdir, usedcids)
|
||||
local pk = read_pk(fontdir.name)
|
||||
local designsize = pk.designsize/1044654.326 -- 1044654.326=2^20*72/72.27 -- designsize in bp
|
||||
local hscale = 65536/pk.hppp / designsize -- 65291.158=2^16*72/72.27
|
||||
local vscale = 65536/pk.vppp / designsize -- 65291.158=2^16*72/72.27
|
||||
local bbox = {0, 0, 0, 0}
|
||||
local matrix = {hscale, 0, 0, vscale, 0, 0}
|
||||
local widths = {}
|
||||
local first_cid = usedcids[1][1]-1
|
||||
local charprocs = {}
|
||||
local prev = 0
|
||||
for i=1,#usedcids do
|
||||
local used = usedcids[i]
|
||||
local glyph = pk[used[1]]
|
||||
for j=prev+1,used[1]-first_cid-1 do
|
||||
widths[j] = 0
|
||||
end
|
||||
prev = used[1]-first_cid
|
||||
widths[prev] = glyph.dx/2^16
|
||||
local lower, left, upper, right = glyph.voff - glyph.h, -glyph.hoff, glyph.voff, -glyph.hoff + glyph.w
|
||||
bbox[1], bbox[2], bbox[3], bbox[4] = math.min(bbox[1], left), math.min(bbox[2], lower), math.max(bbox[3], right), math.max(bbox[4], upper)
|
||||
charprocs[i] = string.format("/G%i %i 0 R", used[1], pdf:stream(nil, "", string.format("%i %i %i %i %i %i d1 %i 0 0 %i %i %i cm BI /W %i/H %i/IM true/BPC 1/D[1 0] ID %s EI",
|
||||
glyph.dx/2^16, glyph.dy, left, lower, right, upper, glyph.w, glyph.h, left, lower, glyph.w, glyph.h, glyph.data
|
||||
)))
|
||||
end
|
||||
return bbox, matrix, '[' .. table.concat(widths, ' ') .. ']', '<<' .. table.concat(charprocs) .. '>>'
|
||||
end
|
@ -58,6 +58,31 @@ local function fontdescriptor(pdf, basefont, fontdir, stream, kind)
|
||||
fontdir.StemV or 100, -- FIXME: How to determine StemV?
|
||||
kind, stream)
|
||||
end
|
||||
local function encodingtype3(pdf)
|
||||
if not pdf.encodingtype3 then
|
||||
pdf.encodingtype3 = string.format(" %i 0 R", pdf:indirect(nil, "\z
|
||||
<</Differences[\z
|
||||
0/G0/G1/G2/G3/G4/G5/G6/G7/G8/G9/G10/G11/G12/G13/G14/G15/G16\z
|
||||
/G17/G18/G19/G20/G21/G22/G23/G24/G25/G26/G27/G28/G29/G30/G31\z
|
||||
/G32/G33/G34/G35/G36/G37/G38/G39/G40/G41/G42/G43/G44/G45/G46\z
|
||||
/G47/G48/G49/G50/G51/G52/G53/G54/G55/G56/G57/G58/G59/G60/G61\z
|
||||
/G62/G63/G64/G65/G66/G67/G68/G69/G70/G71/G72/G73/G74/G75/G76\z
|
||||
/G77/G78/G79/G80/G81/G82/G83/G84/G85/G86/G87/G88/G89/G90/G91\z
|
||||
/G92/G93/G94/G95/G96/G97/G98/G99/G100/G101/G102/G103/G104/G105/G106\z
|
||||
/G107/G108/G109/G110/G111/G112/G113/G114/G115/G116/G117/G118/G119/G120/G121\z
|
||||
/G122/G123/G124/G125/G126/G127/G128/G129/G130/G131/G132/G133/G134/G135/G136\z
|
||||
/G137/G138/G139/G140/G141/G142/G143/G144/G145/G146/G147/G148/G149/G150/G151\z
|
||||
/G152/G153/G154/G155/G156/G157/G158/G159/G160/G161/G162/G163/G164/G165/G166\z
|
||||
/G167/G168/G169/G170/G171/G172/G173/G174/G175/G176/G177/G178/G179/G180/G181\z
|
||||
/G182/G183/G184/G185/G186/G187/G188/G189/G190/G191/G192/G193/G194/G195/G196\z
|
||||
/G197/G198/G199/G200/G201/G202/G203/G204/G205/G206/G207/G208/G209/G210/G211\z
|
||||
/G212/G213/G214/G215/G216/G217/G218/G219/G220/G221/G222/G223/G224/G225/G226\z
|
||||
/G227/G228/G229/G230/G231/G232/G233/G234/G235/G236/G237/G238/G239/G240/G241\z
|
||||
/G242/G243/G244/G245/G246/G247/G248/G249/G250/G251/G252/G253/G254/G255]>>"
|
||||
))
|
||||
end
|
||||
return pdf.encodingtype3
|
||||
end
|
||||
local function cidmap1byte(pdf)
|
||||
if not pdf.cidmap1byte then
|
||||
pdf.cidmap1byte = string.format(" %i 0 R", pdf:stream(nil, [[/Type/CMap/CMapName/Identity-8-H/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>]],
|
||||
@ -222,9 +247,29 @@ local function buildfont0(pdf, fontdir, usedcids)
|
||||
touni,
|
||||
cidfont)
|
||||
end
|
||||
local buildfontpk = require'luametalatex-pdf-font-pk'
|
||||
local function buildfont3(pdf, fontdir, usedcids)
|
||||
usedcids = usedcids or allcids(fontdir)
|
||||
table.sort(usedcids, function(a,b) return a[1]<b[1] end)
|
||||
local enc = cidmap1byte(pdf)
|
||||
local bbox, matrix, widths, charprocs = buildfontpk(pdf, fontdir, usedcids) -- TOOD
|
||||
local touni = pdf:stream(nil, "", tounicode[1](fontdir, usedcids)) -- Done late to allow for defaults set from the font file
|
||||
return string.format(
|
||||
"<</Type/Font/Subtype/Type3/FontBBox[%f %f %f %f]/FontMatrix[%f %f %f %f %f %f]/CharProcs%s/Encoding%s/FirstChar %i/LastChar %i/Widths%s/ToUnicode %i 0 R>>",
|
||||
-- "<</Type/Font/Subtype/Type3/FontBBox[%f %f %f %f]/FontMatrix[%f %f %f %f %f %f]/CharProcs%s/Encoding%s/FirstChar %i/LastChar %i/Widths[%s]/ToUnicode %i 0 R/FontDescriptor %i 0 R>>",
|
||||
bbox[1], bbox[2], bbox[3], bbox[4],
|
||||
matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6],
|
||||
charprocs,
|
||||
encodingtype3(pdf),
|
||||
usedcids[1][1],
|
||||
usedcids[#usedcids][1],
|
||||
widths,
|
||||
touni
|
||||
) -- , descriptor) -- TODO
|
||||
end
|
||||
return function(pdf, fontdir, usedcids)
|
||||
if fontdir.format == "type3" then
|
||||
error[[Currently unsupported]] -- TODO
|
||||
return buildfont3(pdf, fontdir, usedcids)
|
||||
else
|
||||
return buildfont0(pdf, fontdir, usedcids)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user