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?
|
fontdir.StemV or 100, -- FIXME: How to determine StemV?
|
||||||
kind, stream)
|
kind, stream)
|
||||||
end
|
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)
|
local function cidmap1byte(pdf)
|
||||||
if not pdf.cidmap1byte then
|
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>>]],
|
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,
|
touni,
|
||||||
cidfont)
|
cidfont)
|
||||||
end
|
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)
|
return function(pdf, fontdir, usedcids)
|
||||||
if fontdir.format == "type3" then
|
if fontdir.format == "type3" then
|
||||||
error[[Currently unsupported]] -- TODO
|
return buildfont3(pdf, fontdir, usedcids)
|
||||||
else
|
else
|
||||||
return buildfont0(pdf, fontdir, usedcids)
|
return buildfont0(pdf, fontdir, usedcids)
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user