font-tpk.lua /size: 44 Kb    last modification: 2024-01-16 09:02
1if not modules then modules = { } end modules ['font-tpk'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to font-lib.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10-- The bitmap loader is more or less derived from the luatex version (taco)
11-- which is derived from pdftex (thanh) who uses code from dvips (thomas)
12-- adapted by piet ... etc. The tfm and vf readers are also derived from
13-- luatex. All do things a bit more luaish and errors are of course mine.
14
15local next = next
16local extract, band, lshift, rshift = bit32.extract, bit32.band, bit32.lshift, bit32.rshift
17local idiv = number.idiv
18local char = string.char
19local concat, insert, remove, copy = table.concat, table.insert, table.remove, table.copy
20local tobitstring = number.tobitstring
21local formatters = string.formatters
22local round = math.round
23
24local streams       = utilities.streams
25local openstream    = streams.open
26local streamsize    = streams.size
27local readcardinal1 = streams.readcardinal1
28local readcardinal2 = streams.readcardinal2
29local readcardinal3 = streams.readcardinal3
30local readcardinal4 = streams.readcardinal4
31local readinteger1  = streams.readinteger1
32local readinteger2  = streams.readinteger2
33local readinteger3  = streams.readinteger3
34local readinteger4  = streams.readinteger4
35local readbyte      = streams.readbyte
36local readbytes     = streams.readbytes
37local readstring    = streams.readstring
38local skipbytes     = streams.skipbytes
39local getposition   = streams.getposition
40local setposition   = streams.setposition
41
42if not fonts then fonts = { handlers = { tfm = { } } } end
43
44local handlers = fonts.handlers
45local tfm      = handlers.tfm or { }
46handlers.tfm   = tfm
47local readers  = tfm.readers or { }
48tfm.readers    = readers
49
50tfm.version      = 1.005
51tfm.cache        = containers.define("fonts", "tfm", tfm.version, true)
52
53-- Performance is no real issue here so I didn't optimize too much. After
54-- all, these files are small and we mostly use opentype or type1 fonts.
55
56do
57
58    local function readbitmap(glyph,s,flagbyte)
59
60        local inputbyte   = 0
61        local bitweight   = 0
62        local dynf        = 0
63        local remainder   = 0
64        local realfunc    = nil
65        local repeatcount = 0
66
67        local function getnyb() -- can be inlined
68            if bitweight == 0 then
69                bitweight = 16
70                inputbyte = readbyte(s)
71                return extract(inputbyte,4,4)
72            else
73                bitweight = 0
74                return band(inputbyte,15)
75            end
76        end
77
78        local function getbit() -- can be inlined
79            bitweight = rshift(bitweight,1)
80            if bitweight == 0 then -- actually we can check for 1
81                inputbyte = readbyte(s)
82                bitweight = 128
83            end
84            return band(inputbyte,bitweight)
85        end
86
87        local function pkpackednum()
88            local i = getnyb(s)
89            if i == 0 then
90                repeat
91                    j = getnyb()
92                    i = i + 1
93                until (j ~= 0)
94                if i > 3 then
95                    return handlehuge(i,j)
96                else
97                    for i=1,i do
98                        j = j * 16 + getnyb()
99                    end
100                    return j - 15 + (13 - dynf) * 16 + dynf
101                end
102            elseif i <= dynf then
103                return i
104            elseif i < 14 then
105                return (i - dynf - 1) * 16 + getnyb() + dynf + 1
106            elseif i == 14 then
107                repeatcount = pkpackednum()
108            else
109                repeatcount = 1
110            end
111            return realfunc()
112        end
113
114        local function rest()
115            if remainder < 0 then
116                remainder = -remainder
117                return 0
118            elseif remainder > 4000 then
119                remainder = 4000 - remainder
120                return 4000
121            elseif remainder > 0 then
122                local i = remainder
123                remainder = 0
124                realfunc  = pkpackednum
125                return i
126            else
127             -- error = "pk issue that shouldn't happen"
128                return 0
129            end
130        end
131
132        local function handlehuge(i,j)
133            while i ~= 0 do
134                j = lshift(j,4) + getnyb()
135    --             j = extract(j,8,4) + getnyb()
136                i = i - 1
137            end
138            remainder = j - 15 + (13 - dynf) * 16 + dynf
139            realfunc  = rest
140            return rest()
141        end
142
143        local gpower = { [0] =
144                0,   1,    3,    7,   15,   31,    63,   127,
145              255, 511, 1023, 2047, 4095, 8191, 16383, 32767,
146            65535
147        }
148
149        local raster = { }
150        local r      = 0
151        glyph.stream = raster
152
153        local xsize      = glyph.xsize
154        local ysize      = glyph.ysize
155        local word       = 0
156        local wordweight = 0
157        local wordwidth  = idiv(xsize + 15,16)
158        local rowsleft   = 0
159        local turnon     = band(flagbyte,8) == 8 and true or false
160        local hbit       = 0
161        local count      = 0
162        --
163        realfunc         = pkpackednum
164        dynf             = idiv(flagbyte,16)
165        --
166        if dynf == 14 then
167            bitweight = 0
168            for i=1,ysize do
169                word       = 0
170                wordweight = 32768
171                for j=1,xsize do
172                    if getbit() ~= 0 then
173                        word = word + wordweight
174                    end
175                    wordweight = rshift(wordweight,1)
176                    if wordweight == 0 then
177                        r          = r + 1
178                        raster[r]  = word
179                        word       = 0
180                        wordweight = 32768
181                    end
182                end
183                if wordweight ~= 32768 then
184                    r         = r + 1
185                    raster[r] = word
186                end
187            end
188        else
189            rowsleft    = ysize
190            hbit        = xsize
191            repeatcount = 0
192            wordweight  = 16
193            word        = 0
194            bitweight   = 0
195            while rowsleft > 0 do
196                count = realfunc()
197                while count ~= 0 do
198                    if count < wordweight and count < hbit then
199                        if turnon then
200                            word = word + gpower[wordweight] - gpower[wordweight - count]
201                        end
202                        hbit       = hbit - count
203                        wordweight = wordweight - count
204                        count      = 0
205                    elseif count >= hbit and hbit <= wordweight then
206                        if turnon then
207                            word = word + gpower[wordweight] - gpower[wordweight - hbit]
208                        end
209                        r          = r + 1
210                        raster[r]  = word
211                        for i=1,repeatcount*wordwidth do
212                            r          = r + 1
213                            raster[r]  = raster[r - wordwidth]
214                        end
215                        rowsleft    = rowsleft - repeatcount - 1
216                        repeatcount = 0
217                        word        = 0
218                        wordweight  = 16
219                        count       = count - hbit
220                        hbit        = xsize
221                    else
222                        if turnon then
223                            word = word + gpower[wordweight]
224                        end
225                        r          = r + 1
226                        raster[r]  = word
227                        word       = 0
228                        count      = count - wordweight
229                        hbit       = hbit - wordweight
230                        wordweight = 16
231                    end
232                end
233                turnon = not turnon
234            end
235            if rowsleft ~= 0 or hbit ~= xsize then
236                print("ERROR",rowsleft,hbit,xsize)
237                -- error = "error while unpacking, more bits than required"
238            end
239        end
240
241    end
242
243    function readers.showpk(glyph)
244        local xsize  = glyph.xsize
245        local ysize  = glyph.ysize
246        local stream = glyph.stream
247        local result = { }
248        local rr     = { }
249        local r      = 0
250        local s      = 0
251        local cw     = idiv(xsize+ 7, 8)
252        local rw     = idiv(xsize+15,16)
253        local extra  = 2 * rw == cw
254        local b
255        for y=1,ysize do
256            r = 0
257            for x=1,rw-1 do
258                s = s + 1 ; b = stream[s]
259                r = r + 1 ; rr[r] = tobitstring(b,16,16)
260            end
261                s = s + 1 ; b = stream[s]
262            if extra then
263                r = r + 1 ; rr[r] = tobitstring(b,16,16)
264            else
265                r = r + 1 ; rr[r] = tobitstring(extract(b,8+(8-cw),cw),cw,cw)
266            end
267            result[y] = concat(rr)
268        end
269        return concat(result,"\n")
270    end
271
272    local template = formatters [ [[
273%.3N 0 %i %i %i %i d1
274q
275%i 0 0 %i %i %i cm
276BI
277  /W %i
278  /H %i
279  /IM true
280  /BPC 1
281  /D [1 0]
282ID %t
283EI
284Q]] ]
285
286    function readers.pktopdf(glyph,data,factor)
287        local width   = data.width * factor
288        local xsize   = glyph.xsize or 0
289        local ysize   = glyph.ysize or 0
290        local xoffset = glyph.xoffset or 0
291        local yoffset = glyph.yoffset or 0
292        local stream  = glyph.stream
293
294        local dpi     = 1
295        local newdpi  = 1
296
297        local xdpi    = dpi * xsize  / newdpi
298        local ydpi    = dpi * ysize  / newdpi
299
300        local llx     = - xoffset
301        local lly     = yoffset - ysize + 1
302        local urx     = llx + xsize + 1
303        local ury     = lly + ysize
304
305        local result  = { }
306        local r       = 0
307        local s       = 0
308        local cw      = idiv(xsize+ 7, 8)
309        local rw      = idiv(xsize+15,16)
310        local extra   = 2 * rw == cw
311        local b
312        for y=1,ysize do
313            for x=1,rw-1 do
314                s = s + 1 ; b = stream[s]
315                r = r + 1 ; result[r] = char(extract(b,8,8),extract(b,0,8))
316            end
317            s = s + 1 ; b = stream[s]
318            if extra then
319                r = r + 1 ; result[r] = char(extract(b,8,8),extract(b,0,8))
320            else
321                r = r + 1 ; result[r] = char(extract(b,8,8))
322            end
323        end
324        return template(width,llx,lly,urx,ury,xdpi,ydpi,llx,lly,xsize,ysize,result), width
325    end
326
327    function readers.loadpk(filename)
328        local s          = openstream(filename)
329        local preamble   = readcardinal1(s)
330        local version    = readcardinal1(s)
331        local comment    = readstring(s,readcardinal1(s))
332        local designsize = readcardinal4(s)
333        local checksum   = readcardinal4(s)
334        local hppp       = readcardinal4(s)
335        local vppp       = readcardinal4(s)
336        if preamble ~= 247 or version ~= 89 or not vppp then
337            return { error = "invalid preamble" }
338        end
339        local glyphs = { }
340        local data   = {
341            designsize = designsize,
342            comment    = comment,
343            hppp       = hppp,
344            vppp       = vppp,
345            glyphs     = glyphs,
346        }
347        while true do
348            local flagbyte = readcardinal1(s)
349            if flagbyte < 240 then
350                local c = band(flagbyte,7)
351                local length, index, width, pixels, xsize, ysize, xoffset, yoffset
352                if c >= 0 and c <= 3 then
353                    length  = band(flagbyte,7) * 256 + readcardinal1(s) - 3
354                    index   = readcardinal1(s)
355                    width   = readinteger3(s)
356                    pixels  = readcardinal1(s)
357                    xsize   = readcardinal1(s)
358                    ysize   = readcardinal1(s)
359                    xoffset = readinteger1(s)
360                    yoffset = readinteger1(s)
361                elseif c >= 4 and c <= 6 then
362                    length  = band(flagbyte,3) * 65536 + readcardinal1(s) * 256 + readcardinal1(s) - 4
363                    index   = readcardinal1(s)
364                    width   = readinteger3(s)
365                    pixels  = readcardinal2(s)
366                    xsize   = readcardinal2(s)
367                    ysize   = readcardinal2(s)
368                    xoffset = readinteger2(s)
369                    yoffset = readinteger2(s)
370                else -- 7
371                    length  = readcardinal4(s) - 9
372                    index   = readcardinal4(s)
373                    width   = readinteger4(s)
374                    pixels  = readcardinal4(s)
375                              readcardinal4(s)
376                    xsize   = readcardinal4(s)
377                    ysize   = readcardinal4(s)
378                    xoffset = readinteger4(s)
379                    yoffset = readinteger4(s)
380                end
381                local glyph = {
382                    index   = index,
383                    width   = width,
384                    pixels  = pixels,
385                    xsize   = xsize,
386                    ysize   = ysize,
387                    xoffset = xoffset,
388                    yoffset = yoffset,
389                }
390                if length <= 0 then
391                    data.error = "bad packet"
392                    return data
393                end
394                readbitmap(glyph,s,flagbyte)
395                glyphs[index] = glyph
396            elseif flagbyte == 240 then
397                -- k[1] x[k]
398                skipbytes(s,readcardinal1(s))
399            elseif flagbyte == 241 then
400                -- k[2] x[k]
401                skipbytes(s,readcardinal2(s)*2)
402            elseif flagbyte == 242 then
403                -- k[3] x[k]
404                skipbytes(s,readcardinal3(s)*3)
405            elseif flagbyte == 243 then
406                -- k[4] x[k]
407                skipbytes(s,readcardinal4(s)*4) -- readinteger4
408            elseif flagbyte == 244 then
409                -- y[4]
410                skipbytes(s,4)
411            elseif flagbyte == 245 then
412                break
413            elseif flagbyte == 246 then
414                -- nop
415            else
416                data.error = "unknown pk command"
417                break
418            end
419        end
420        return data
421    end
422
423end
424
425do
426
427    local leftboundary  = -1
428    local rightboundary = -2
429    local boundarychar  = 65536
430
431    function readers.loadtfm(filename)
432        local data
433        --
434        local function someerror(m)
435            if not data then
436                data = { }
437            end
438            data.error = m or "fatal error"
439            return data
440        end
441        --
442        local s = openstream(filename)
443        if not s then
444            return someerror()
445        end
446        --
447        local wide       = false
448        local header     = 0
449        local max        = 0
450        local size       = streamsize(s)
451        local glyphs     = table.setmetatableindex(function(t,k)
452            local v = {
453                -- we default because boundary chars have no dimension s
454                width  = 0,
455                height = 0,
456                depth  = 0,
457                italic = 0,
458            }
459            t[k] = v
460            return v
461        end)
462        local parameters = { }
463        local direction  = 0
464        --
465        local lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np
466        --
467        lf = readcardinal2(s)
468        if lf ~= 0 then
469            header = 6
470            max    = 255
471            wide   = false
472            lh = readcardinal2(s)
473            bc = readcardinal2(s)
474            ec = readcardinal2(s)
475            nw = readcardinal2(s)
476            nh = readcardinal2(s)
477            nd = readcardinal2(s)
478            ni = readcardinal2(s)
479            nl = readcardinal2(s)
480            nk = readcardinal2(s)
481            ne = readcardinal2(s)
482            np = readcardinal2(s)
483        else
484            header = 14
485            max    = 65535
486            wide   = readcardinal4(s) == 0
487            if not wide then
488                return someerror("invalid format")
489            end
490            lf = readcardinal4(s)
491            lh = readcardinal4(s)
492            bc = readcardinal4(s)
493            ec = readcardinal4(s)
494            nw = readcardinal4(s)
495            nh = readcardinal4(s)
496            nd = readcardinal4(s)
497            ni = readcardinal4(s)
498            nl = readcardinal4(s)
499            nk = readcardinal4(s)
500            ne = readcardinal4(s)
501            np = readcardinal4(s)
502            direction = readcardinal4(s)
503        end
504        if (bc > ec + 1) or (ec > max) then
505            return someerror("file is too small")
506        end
507        if bc > max then
508            bc, ec = 1, 0
509        end
510        local nlw  = (wide and 2 or 1) * nl
511        local neew = (wide and 2 or 1) * ne
512        local ncw  = (wide and 2 or 1) * (ec - bc + 1)
513        if lf ~= (header + lh + ncw + nw + nh + nd + ni + nlw + nk + neew + np) then
514            return someerror("file is too small")
515        end
516        if nw == 0 or nh == 0 or nd == 0 or ni == 0 then
517            return someerror("no glyphs")
518        end
519        if lf * 4 > size then
520            return someerror("file is too small")
521        end
522        local slh = lh
523        if lh < 2 then
524            return someerror("file is too small")
525        end
526        local checksum   = readcardinal4(s)
527        local designsize = readcardinal2(s)
528        designsize = designsize * 256 +        readcardinal1(s)
529        designsize = designsize *  16 + rshift(readcardinal1(s),4)
530        if designsize < 0xFFFF then
531            return someerror("weird designsize")
532        end
533        --
534        local alpha =  16
535        local z     = designsize
536        while z >= 040000000 do
537            z = rshift(z,1)
538            alpha = alpha + alpha
539        end
540        local beta = idiv(256,alpha)
541        alpha = alpha * z
542        --
543        local function readscaled()
544            local a, b, c, d = readbytes(s,4)
545            local n = idiv(rshift(rshift(d*z,8)+c*z,8)+b*z,beta)
546            if a == 0 then
547                return n
548            elseif a == 255 then
549                return n - alpha
550            else
551                return 0
552            end
553        end
554        --
555        local function readunscaled()
556            local a, b, c, d = readbytes(s,4)
557            if a > 127 then
558                a = a - 256
559            end
560            return a * 0xFFFFF + b * 0xFFF + c * 0xF + rshift(d,4)
561        end
562        --
563        while lh > 2 do -- can be one-liner
564            skipbytes(s,4)
565            lh = lh - 1
566        end
567        local saved = getposition(s)
568        setposition(s,(header + slh + ncw) * 4 + 1)
569        local widths  = { } for i=0,nw-1 do widths [i] = readscaled() end
570        local heights = { } for i=0,nh-1 do heights[i] = readscaled() end
571        local depths  = { } for i=0,nd-1 do depths [i] = readscaled() end
572        local italics = { } for i=0,ni-1 do italics[i] = readscaled() end
573        if widths[0] ~= 0 or heights[0] ~= 0 or depths[0] ~= 0 then
574            return someerror("invalid dimensions")
575        end
576        --
577        local blabel = nl
578        local bchar  = boundarychar
579        --
580        local ligatures = { }
581        if nl > 0 then
582            for i=0,nl-1 do
583                local a, b, c, d = readbytes(s,4)
584                ligatures[i] = {
585                    skip = a,
586                    nxt  = b,
587                    op   = c,
588                    rem  = d,
589                }
590                if a > 128 then
591                    if 256 * c + d >= nl then
592                        return someerror("invalid ligature table")
593                    end
594                    if a == 255 and i == 0 then
595                        bchar = b
596                    end
597                else
598                    if c < 128 then
599                       -- whatever
600                    elseif 256 * (c - 128) + d >= nk then
601                        return someerror("invalid ligature table")
602                    end
603                    if (a < 128) and (i - 0 + a + 1 >= nl) then
604                        return someerror("invalid ligature table")
605                    end
606                end
607                if a == 255 then
608                    blabel = 256 * c + d
609                end
610            end
611        end
612        local allkerns = { }
613        for i=0,nk-1 do
614            allkerns[i] = readscaled()
615        end
616        local extensibles = { }
617        for i=0,ne-1 do
618            extensibles[i] = wide and {
619                top = readcardinal2(s),
620                bot = readcardinal2(s),
621                mid = readcardinal2(s),
622                rep = readcardinal2(s),
623            } or {
624                top = readcardinal1(s),
625                bot = readcardinal1(s),
626                mid = readcardinal1(s),
627                rep = readcardinal1(s),
628            }
629        end
630        for i=1,np do
631            if i == 1 then
632                parameters[i] = readunscaled()
633            else
634                parameters[i] = readscaled()
635            end
636        end
637        for i=1,7 do
638            if not parameters[i] then
639                parameters[i] = 0
640            end
641        end
642        --
643        setposition(s,saved)
644        local extras = false
645        if blabel ~= nl then
646            local k = blabel
647            while true do
648                local l    = ligatures[k]
649                local skip = l.skip
650                if skip <= 128 then
651                 -- if l.op >= 128 then
652                 --     extras = true -- kern
653                 -- else
654                        extras = true -- ligature
655                 -- end
656                end
657                if skip == 0 then
658                    k = k + 1
659                else
660                    if skip >= 128 then
661                       break
662                    end
663                    k = k + skip + 1
664                end
665            end
666        end
667        if extras then
668            local ligas = { }
669            local kerns = { }
670            local k     = blabel
671            while true do
672                local l    = ligatures[k]
673                local skip = l.skip
674                if skip <= 128 then
675                    local nxt = l.nxt
676                    local op  = l.op
677                    local rem = l.rem
678                    if op >= 128 then
679                        kerns[nxt] = allkerns[256 * (op - 128) + rem]
680                    else
681                        ligas[nxt] = { type = op * 2 + 1, char = rem }
682                    end
683                end
684                if skip == 0 then
685                    k = k + 1
686                else
687                    if skip >= 128 then
688                        break;
689                    end
690                    k = k + skip + 1
691                end
692            end
693            if next(kerns) then
694                local glyph     = glyphs[leftboundary]
695                glyph.kerns     = kerns
696                glyph.remainder = 0
697            end
698            if next(ligas) then
699                local glyph     = glyphs[leftboundary]
700                glyph.ligatures = ligas
701                glyph.remainder = 0
702            end
703        end
704        for i=bc,ec do
705            local glyph, width, height, depth, italic, tag, remainder
706            if wide then
707                width     = readcardinal2(s)
708                height    = readcardinal1(s)
709                depth     = readcardinal1(s)
710                italic    = readcardinal1(s)
711                tag       = readcardinal1(s)
712                remainder = readcardinal2(s)
713            else
714                width     = readcardinal1(s)
715                height    = readcardinal1(s)
716                depth     = extract(height,0,4)
717                height    = extract(height,4,4)
718                italic    = readcardinal1(s)
719                tag       = extract(italic,0,2)
720                italic    = extract(italic,2,6)
721                remainder = readcardinal1(s)
722            end
723            if width == 0 then
724                -- nothing
725            else
726                if width >= nw or height >= nh or depth >= nd or italic >= ni then
727                    return someerror("invalid dimension index")
728                end
729                local extensible, nextinsize
730                if tag == 0 then
731                    -- nothing special
732                else
733                    local r = remainder
734                    if tag == 1 then
735                        if r >= nl then
736                            return someerror("invalid ligature index")
737                        end
738                    elseif tag == 2 then
739                        if r < bc or r > ec then
740                            return someerror("invalid chain index")
741                        end
742                        while r < i do
743                            local g = glyphs[r]
744                            if g.tag ~= list_tag then
745                                break
746                            end
747                            r = g.remainder
748                        end
749                        if r == i then
750                            return someerror("cycles in chain")
751                        end
752                        nextinsize = r
753                    elseif tag == 3 then
754                        if r >= ne then
755                            return someerror("bad extensible")
756                        end
757                        extensible = extensibles[r] -- remainder ?
758                        remainder  = 0
759                    end
760                end
761                glyphs[i] = {
762                    width      = widths [width],
763                    height     = heights[height],
764                    depth      = depths [depth],
765                    italic     = italics[italic],
766                    tag        = tag,
767                 -- index      = i,
768                    remainder  = remainder,
769                    extensible = extensible,
770                    next       = nextinsize,
771                }
772            end
773        end
774        for i=bc,ec do
775            local glyph  = glyphs[i]
776            if glyph.tag == 1 then
777                -- ligature
778                local k = glyph.remainder
779                local l = ligatures[k]
780                if l.skip > 128 then
781                    k = 256 * l.op + l.rem
782                end
783                local ligas = { }
784                local kerns = { }
785                while true do
786                    local l    = ligatures[k]
787                    local skip = l.skip
788                    if skip <= 128 then
789                        local nxt = l.nxt
790                        local op  = l.op
791                        local rem = l.rem
792                        if op >= 128 then
793                            local kern = allkerns[256 * (op - 128) + rem]
794                            if nxt == bchar then
795                                kerns[rightboundary] = kern
796                            end
797                            kerns[nxt] = kern
798                        else
799                            local ligature = { type = op * 2 + 1, char = rem }
800                            if nxt == bchar then
801                                ligas[rightboundary] = ligature
802                            end
803                            ligas[nxt] = ligature -- shared
804                        end
805                    end
806                    if skip == 0 then
807                       k = k + 1
808                    else
809                        if skip >= 128 then
810                            break
811                        end
812                        k = k + skip + 1
813                    end
814                end
815                if next(kerns)then
816                    glyph.kerns     = kerns
817                    glyph.remainder = 0
818                end
819                if next(ligas) then
820                    glyph.ligatures = ligas
821                    glyph.remainder = 0
822                end
823            end
824        end
825        --
826        if bchar ~= boundarychar then
827           glyphs[rightboundary] = copy(glyphs[bchar])
828        end
829        --
830     -- for k, v in next, glyphs do
831     --     v.tag       = nil
832     --     v.remainder = nil
833     -- end
834        --
835        return {
836            name           = file.nameonly(filename),
837            fontarea       = file.pathpart(filename),
838            glyphs         = glyphs,
839            parameters     = parameters,
840            designsize     = designsize,
841            size           = designsize,
842            direction      = direction,
843         -- checksum       = checksum,
844         -- embedding      = "unknown",
845         -- extend         = 1000,
846         -- slant          = 0,
847         -- squeeze        = 0,
848         -- format         = "unknown",
849         -- identity       = "unknown",
850         -- mode           = 0,
851         -- streamprovider = 0,
852         -- tounicode      = 0,
853         -- type           = "unknown",
854         -- units_per_em   = 0,
855         -- used           = false,
856         -- width          = 0,
857         -- writingmode    = "unknown",
858        }
859    end
860
861end
862
863do
864
865    local push = { "push" }
866    local push = { "pop" }
867
868    local w, x, y, z, f
869    local stack
870    local s, result, r
871    local alpha, beta, z
872
873    local function scaled1()
874        local a = readbytes(s,1)
875        if a == 0 then
876            return 0
877        elseif a == 255 then
878            return - alpha
879        else
880            return 0 -- error
881        end
882    end
883
884    local function scaled2()
885        local a, b = readbytes(s,2)
886        local sw = idiv(b*z,beta)
887        if a == 0 then
888            return sw
889        elseif a == 255 then
890            return sw - alpha
891        else
892            return 0 -- error
893        end
894    end
895
896    local function scaled3()
897        local a, b, c = readbytes(s,3)
898        local sw = idiv(rshift(c*z,8)+b*z,beta)
899        if a == 0 then
900            return sw
901        elseif a == 255 then
902            return sw - alpha
903        else
904            return 0 -- error
905        end
906    end
907
908    local function scaled4()
909        local a, b, c, d = readbytes(s,4)
910        local sw = idiv( rshift(rshift(d*z,8)+(c*z),8)+b*z,beta)
911        if a == 0 then
912            return sw
913        elseif a == 255 then
914            return sw - alpha
915        else
916            return 0 -- error
917        end
918    end
919
920    local function dummy()
921    end
922
923    local actions = {
924
925        [128] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal1(s) } p = p + 1 end,
926        [129] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal2(s) } p = p + 2 end,
927        [130] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal3(s) } p = p + 3 end,
928        [131] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal4(s) } p = p + 4 end,
929
930        [132] = function()
931            r = r + 1
932            result[r] = { "rule", scaled4(), scaled4() }
933            p = p + 8
934        end,
935
936        [133] = function()
937                    r = r + 1 result[r] = push
938                    r = r + 1 result[r] = { "slot", f or 1, readcardinal1(s) }
939                    r = r + 1 result[r] = pop
940                    p = p + 1
941                end,
942        [134] = function()
943                    r = r + 1 result[r] = push
944                    r = r + 1 result[r] = { "slot", f or 1, readcardinal2(s) }
945                    r = r + 1 result[r] = pop
946                    p = p + 2
947                end,
948        [135] = function()
949                    r = r + 1 result[r] = push
950                    r = r + 1 result[r] = { "slot", f or 1, readcardinal2(s) }
951                    r = r + 1 result[r] = pop
952                    p = p + 3
953                end,
954        [136] = function()
955                    r = r + 1 result[r] = push
956                    r = r + 1 result[r] = { "slot", f or 1, readcardinal4(s) }
957                    r = r + 1 result[r] = pop
958                    p = p + 4
959                end,
960
961        [137] = function()
962                    r = r + 1 result[r] = push
963                    r = r + 1 result[r] = { "rule", scaled4(), scaled4() }
964                    r = r + 1 result[r] = pop
965                    p = p + 8
966                end,
967
968        [138] = dummy, -- nop
969        [139] = dummy, -- bop
970        [140] = dummy, -- eop
971
972        [141] = function()
973                    insert(stack, { w, x, y, z })
974                    r = r + 1
975                    result[r] = push
976                end,
977        [142] = function()
978                    local t = remove(stack)
979                    if t then
980                        w, x, y, z = t[1], t[2], t[3], t[4]
981                        r = r + 1
982                        result[r] = pop
983                    end
984                end,
985
986        [143] = function() r = r + 1 result[r] = { "right", scaled1() } p = p + 1 end,
987        [144] = function() r = r + 1 result[r] = { "right", scaled2() } p = p + 2 end,
988        [145] = function() r = r + 1 result[r] = { "right", scaled3() } p = p + 3 end,
989        [146] = function() r = r + 1 result[r] = { "right", scaled4() } p = p + 4 end,
990
991        [148] = function() w = scaled1() r = r + 1 result[r] = { "right", w } p = p + 1 end,
992        [149] = function() w = scaled2() r = r + 1 result[r] = { "right", w } p = p + 2 end,
993        [150] = function() w = scaled3() r = r + 1 result[r] = { "right", w } p = p + 3 end,
994        [151] = function() w = scaled4() r = r + 1 result[r] = { "right", w } p = p + 4 end,
995
996        [153] = function() x = scaled1() r = r + 1 result[r] = { "right", x } p = p + 1 end,
997        [154] = function() x = scaled2() r = r + 1 result[r] = { "right", x } p = p + 2 end,
998        [155] = function() x = scaled3() r = r + 1 result[r] = { "right", x } p = p + 3 end,
999        [156] = function() x = scaled4() r = r + 1 result[r] = { "right", x } p = p + 4 end,
1000
1001        [157] = function() r = r + 1 result[r] = { "down", scaled1() } p = p + 1 end,
1002        [158] = function() r = r + 1 result[r] = { "down", scaled2() } p = p + 2 end,
1003        [159] = function() r = r + 1 result[r] = { "down", scaled3() } p = p + 3 end,
1004        [160] = function() r = r + 1 result[r] = { "down", scaled4() } p = p + 4 end,
1005
1006        [162] = function() y = scaled1() r = r + 1 result[r] = { "down", y } p = p + 1 end,
1007        [163] = function() y = scaled2() r = r + 1 result[r] = { "down", y } p = p + 2 end,
1008        [164] = function() y = scaled3() r = r + 1 result[r] = { "down", y } p = p + 3 end,
1009        [165] = function() y = scaled3() r = r + 1 result[r] = { "down", y } p = p + 4 end,
1010
1011        [167] = function() z = scaled1() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1012        [168] = function() z = scaled2() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1013        [169] = function() z = scaled3() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1014        [170] = function() z = scaled4() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1015
1016        [147] = function() r = r + 1 result[r] = { "right", w } end,
1017        [152] = function() r = r + 1 result[r] = { "right", x } end,
1018        [161] = function() r = r + 1 result[r] = { "down",  y } end,
1019        [166] = function() r = r + 1 result[r] = { "down",  z } end,
1020
1021        [235] = function() f = readcardinal1(s) p = p + 1 end,
1022        [236] = function() f = readcardinal2(s) p = p + 3 end,
1023        [237] = function() f = readcardinal3(s) p = p + 3 end,
1024        [238] = function() f = readcardinal4(s) p = p + 4 end,
1025
1026        [239] = function() local n = readcardinal1(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 1 + n end,
1027        [240] = function() local n = readcardinal2(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 2 + n end,
1028        [241] = function() local n = readcardinal3(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 3 + n end,
1029        [242] = function() local n = readcardinal4(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 4 + n end,
1030
1031        [250] = function() local n = readcardinal1(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 1 + n end,
1032        [251] = function() local n = readcardinal2(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 2 + n end,
1033        [252] = function() local n = readcardinal3(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 3 + n end,
1034        [253] = function() local n = readcardinal4(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 4 + n end,
1035
1036    }
1037
1038    table.setmetatableindex(actions,function(t,cmd)
1039        local v
1040        if cmd >= 0 and cmd <= 127 then
1041            v = function()
1042                if f == 0 then
1043                    f = 1
1044                end
1045                r = r + 1 ; result[r] = { "slot", f, cmd }
1046            end
1047        elseif cmd >= 171 and cmd <= 234 then
1048            cmd = cmd - 170
1049            v = function()
1050                r = r + 1 ; result[r] = { "font", cmd }
1051            end
1052        else
1053            v = dummy
1054        end
1055        t[cmd] = v
1056        return v
1057    end)
1058
1059    function readers.loadvf(filename,data)
1060        --
1061        local function someerror(m)
1062            if not data then
1063                data = { }
1064            end
1065            data.error = m or "fatal error"
1066            return data
1067        end
1068        --
1069        s = openstream(filename)
1070        if not s then
1071            return someerror()
1072        end
1073        --
1074        local cmd = readcardinal1(s)
1075        if cmd ~= 247 then
1076            return someerror("bad preamble")
1077        end
1078        cmd = readcardinal1(s)
1079        if cmd ~= 202 then
1080            return someerror("bad version")
1081        end
1082        local header     = readstring(s,readcardinal1(s))
1083        local checksum   = readcardinal4(s)
1084        local designsize = idiv(readcardinal4(s),16)
1085        local fonts      = data and data.fonts  or { }
1086        local glyphs     = data and data.glyphs or { }
1087        --
1088        alpha =  16
1089        z     = designsize
1090        while z >= 040000000 do
1091            z = rshift(z,1)
1092            alpha = alpha + alpha
1093        end
1094        beta  = idiv(256,alpha)
1095        alpha = alpha * z
1096        --
1097        cmd = readcardinal1(s)
1098        while true do
1099            local n
1100            if cmd == 243 then
1101                n = readcardinal1(s) + 1
1102            elseif cmd == 244 then
1103                n = readcardinal2(s) + 1
1104            elseif cmd == 245 then
1105                n = readcardinal3(s) + 1
1106            elseif cmd == 246 then
1107                n = readcardinal4(s) + 1
1108            else
1109                break
1110            end
1111            local checksum   = skipbytes(s,4)
1112            local size       = scaled4()
1113            local designsize = idiv(readcardinal4(s),16)
1114            local pathlen    = readcardinal1(s)
1115            local namelen    = readcardinal1(s)
1116            local path       = readstring(s,pathlen)
1117            local name       = readstring(s,namelen)
1118            fonts[n] = { path = path, name = name, size = size }
1119            cmd = readcardinal1(s)
1120        end
1121        local index = 0
1122        while cmd and cmd <= 242 do
1123            local width    = 0
1124            local length   = 0
1125            local checksum = 0
1126            if cmd == 242 then
1127                length   = readcardinal4(s)
1128                checksum = readcardinal4(s)
1129                width    = readcardinal4(s)
1130            else
1131                length   = cmd
1132                checksum = readcardinal1(s)
1133                width    = readcardinal3(s)
1134            end
1135            w, x, y, z, f = 0, 0, 0, 0, false
1136            stack, result, r, p = { }, { }, 0, 0
1137            while p < length do
1138                local cmd = readcardinal1(s)
1139                p = p + 1
1140                actions[cmd]()
1141            end
1142            local glyph = glyphs[index]
1143            if glyph then
1144                glyph.width    = width
1145                glyph.commands = result
1146            else
1147                glyphs[index] = {
1148                    width    = width,
1149                    commands = result,
1150                }
1151            end
1152            index = index + 1
1153            if #stack > 0 then
1154                -- error: more pushes than pops
1155            end
1156            if packet_length ~= 0 then
1157                -- error: invalid packet length
1158            end
1159            cmd = readcardinal1(s)
1160        end
1161        if readcardinal1(s) ~= 248 then
1162            -- error: no post
1163        end
1164        s, result, r = nil, nil, nil
1165        if data then
1166            data.glyphs = data.glyphs or glyphs
1167            data.fonts  = data.fonts  or fonts
1168            return data
1169        else
1170            return {
1171                name       = file.nameonly(filename),
1172                fontarea   = file.pathpart(filename),
1173                glyphs     = glyphs,
1174                designsize = designsize,
1175                header     = header,
1176                fonts      = fonts,
1177            }
1178        end
1179    end
1180
1181    -- the replacement loader (not sparse):
1182
1183    function readers.loadtfmvf(tfmname,size)
1184        local vfname  = file.addsuffix(file.nameonly(tfmfile),"vf")
1185        local tfmfile = tfmname
1186        local vffile  = resolvers.findbinfile(vfname,"ovf")
1187        if tfmfile and tfmfile ~= "" then
1188            if size < 0 then
1189                size = idiv(65536 * -size,100)
1190            end
1191            local data = readers.loadtfm(tfmfile)
1192            if data.error then
1193                return data
1194            end
1195            if vffile and vffile ~= "" then
1196                data = readers.loadvf(vffile,data)
1197                if data.error then
1198                    return data
1199                end
1200            end
1201            local designsize = data.designsize
1202            local glyphs     = data.glyphs
1203            local parameters = data.parameters
1204            local fonts      = data.fonts
1205            if size ~= designsize then
1206                local factor = size / designsize
1207                for index, glyph in next, glyphs do
1208                    if next(glyph) then
1209                        glyph.width  = round(factor*glyph.width)
1210                        glyph.height = round(factor*glyph.height)
1211                        glyph.depth  = round(factor*glyph.depth)
1212                        local italic = glyph.italic
1213                        if italic == 0 then
1214                            glyph.italic = nil
1215                        else
1216                            glyph.italic = round(factor*glyph.italic)
1217                        end
1218                        --
1219                        local kerns = glyph.kerns
1220                        if kerns then
1221                            for index, kern in next, kerns do
1222                                kerns[index] = round(factor*kern)
1223                            end
1224                        end
1225                        --
1226                        local commands = glyph.commands
1227                        if commands then
1228                            for i=1,#commands do
1229                                local c = commands[i]
1230                                local t = c[1]
1231                                if t == "down" or t == "right" then
1232                                    c[2] = round(factor*c[2])
1233                                elseif t == "rule" then
1234                                    c[2] = round(factor*c[2])
1235                                    c[3] = round(factor*c[3])
1236                                end
1237                            end
1238                        end
1239                    else
1240                        glyphs[index] = nil
1241                    end
1242                end
1243                for i=2,30 do
1244                    local p = parameters[i]
1245                    if p then
1246                        parameters[i] = round(factor*p)
1247                    else
1248                        break
1249                    end
1250                end
1251                if fonts then
1252                    for k, v in next, fonts do
1253                        v.size = round(factor*v.size)
1254                    end
1255                end
1256            else
1257                for index, glyph in next, glyphs do
1258                    if next(glyph) then
1259                        if glyph.italic == 0 then
1260                            glyph.italic = nil
1261                        end
1262                    else
1263                        glyphs[index] = nil
1264                    end
1265                end
1266            end
1267            --
1268            parameters.slant         = parameters[1]
1269            parameters.space         = parameters[2]
1270            parameters.space_stretch = parameters[3]
1271            parameters.space_shrink  = parameters[4]
1272            parameters.x_height      = parameters[5]
1273            parameters.quad          = parameters[6]
1274            parameters.extra_space   = parameters[7]
1275            --
1276            for i=1,7 do
1277                parameters[i] = nil -- so no danger for async
1278            end
1279            --
1280            data.characters = glyphs
1281            data.glyphs     = nil
1282            data.size       = size
1283            -- we assume type1 for now ... maybe the format should be unknown
1284            data.filename   = tfmfile -- file.replacesuffix(tfmfile,"pfb")
1285            data.format     = "unknown"
1286            --
1287            return data
1288        end
1289    end
1290
1291end
1292
1293-- inspect(readers.loadtfmvf(resolvers.findfile("mi-iwonari.tfm")))
1294-- inspect(readers.loadtfm(resolvers.findfile("texnansi-palatinonova-regular.tfm")))
1295-- inspect(readers.loadtfm(resolvers.findfile("cmex10.tfm")))
1296-- inspect(readers.loadtfm(resolvers.findfile("cmr10.tfm")))
1297-- local t = readers.loadtfmvf("texnansi-lte50019.tfm")
1298-- inspect(t)
1299