font-tpk.lua /size: 44 Kb    last modification: 2021-10-28 13:50
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 = readcardinal1(s)
360                    yoffset = readcardinal1(s)
361                    if xoffset > 127 then
362                        xoffset = xoffset - 256
363                    end
364                    if yoffset > 127 then
365                        yoffset = yoffset - 256
366                    end
367                elseif c >= 4 and c <= 6 then
368                    length  = band(flagbyte,3) * 65536 + readcardinal1(s) * 256 + readcardinal1(s) - 4
369                    index   = readcardinal1(s)
370                    width   = readinteger3(s)
371                    pixels  = readcardinal2(s)
372                    xsize   = readcardinal2(s)
373                    ysize   = readcardinal2(s)
374                    xoffset = readcardinal2(s)
375                    yoffset = readcardinal2(s)
376                else -- 7
377                    length  = readcardinal4(s) - 9
378                    index   = readcardinal4(s)
379                    width   = readinteger4(s)
380                    pixels  = readcardinal4(s)
381                              readcardinal4(s)
382                    xsize   = readcardinal4(s)
383                    ysize   = readcardinal4(s)
384                    xoffset = readcardinal4(s)
385                    yoffset = readcardinal4(s)
386                end
387                local glyph = {
388                    index   = index,
389                    width   = width,
390                    pixels  = pixels,
391                    xsize   = xsize,
392                    ysize   = ysize,
393                    xoffset = xoffset,
394                    yoffset = yoffset,
395                }
396                if length <= 0 then
397                    data.error = "bad packet"
398                    return data
399                end
400                readbitmap(glyph,s,flagbyte)
401                glyphs[index] = glyph
402            elseif flagbyte == 240 then
403                -- k[1] x[k]
404                skipbytes(s,readcardinal1(s))
405            elseif flagbyte == 241 then
406                -- k[2] x[k]
407                skipbytes(s,readcardinal2(s)*2)
408            elseif flagbyte == 242 then
409                -- k[3] x[k]
410                skipbytes(s,readcardinal3(s)*3)
411            elseif flagbyte == 243 then
412                -- k[4] x[k]
413                skipbytes(s,readcardinal4(s)*4) -- readinteger4
414            elseif flagbyte == 244 then
415                -- y[4]
416                skipbytes(s,4)
417            elseif flagbyte == 245 then
418                break
419            elseif flagbyte == 246 then
420                -- nop
421            else
422                data.error = "unknown pk command"
423                break
424            end
425        end
426        return data
427    end
428
429end
430
431do
432
433    local leftboundary  = -1
434    local rightboundary = -2
435    local boundarychar  = 65536
436
437    function readers.loadtfm(filename)
438        local data
439        --
440        local function someerror(m)
441            if not data then
442                data = { }
443            end
444            data.error = m or "fatal error"
445            return data
446        end
447        --
448        local s = openstream(filename)
449        if not s then
450            return someerror()
451        end
452        --
453        local wide       = false
454        local header     = 0
455        local max        = 0
456        local size       = streamsize(s)
457        local glyphs     = table.setmetatableindex(function(t,k)
458            local v = {
459                -- we default because boundary chars have no dimension s
460                width  = 0,
461                height = 0,
462                depth  = 0,
463                italic = 0,
464            }
465            t[k] = v
466            return v
467        end)
468        local parameters = { }
469        local direction  = 0
470        --
471        local lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np
472        --
473        lf = readcardinal2(s)
474        if lf ~= 0 then
475            header = 6
476            max    = 255
477            wide   = false
478            lh = readcardinal2(s)
479            bc = readcardinal2(s)
480            ec = readcardinal2(s)
481            nw = readcardinal2(s)
482            nh = readcardinal2(s)
483            nd = readcardinal2(s)
484            ni = readcardinal2(s)
485            nl = readcardinal2(s)
486            nk = readcardinal2(s)
487            ne = readcardinal2(s)
488            np = readcardinal2(s)
489        else
490            header = 14
491            max    = 65535
492            wide   = readcardinal4(s) == 0
493            if not wide then
494                return someerror("invalid format")
495            end
496            lf = readcardinal4(s)
497            lh = readcardinal4(s)
498            bc = readcardinal4(s)
499            ec = readcardinal4(s)
500            nw = readcardinal4(s)
501            nh = readcardinal4(s)
502            nd = readcardinal4(s)
503            ni = readcardinal4(s)
504            nl = readcardinal4(s)
505            nk = readcardinal4(s)
506            ne = readcardinal4(s)
507            np = readcardinal4(s)
508            direction = readcardinal4(s)
509        end
510        if (bc > ec + 1) or (ec > max) then
511            return someerror("file is too small")
512        end
513        if bc > max then
514            bc, ec = 1, 0
515        end
516        local nlw  = (wide and 2 or 1) * nl
517        local neew = (wide and 2 or 1) * ne
518        local ncw  = (wide and 2 or 1) * (ec - bc + 1)
519        if lf ~= (header + lh + ncw + nw + nh + nd + ni + nlw + nk + neew + np) then
520            return someerror("file is too small")
521        end
522        if nw == 0 or nh == 0 or nd == 0 or ni == 0 then
523            return someerror("no glyphs")
524        end
525        if lf * 4 > size then
526            return someerror("file is too small")
527        end
528        local slh = lh
529        if lh < 2 then
530            return someerror("file is too small")
531        end
532        local checksum   = readcardinal4(s)
533        local designsize = readcardinal2(s)
534        designsize = designsize * 256 +        readcardinal1(s)
535        designsize = designsize *  16 + rshift(readcardinal1(s),4)
536        if designsize < 0xFFFF then
537            return someerror("weird designsize")
538        end
539        --
540        local alpha =  16
541        local z     = designsize
542        while z >= 040000000 do
543            z = rshift(z,1)
544            alpha = alpha + alpha
545        end
546        local beta = idiv(256,alpha)
547        alpha = alpha * z
548        --
549        local function readscaled()
550            local a, b, c, d = readbytes(s,4)
551            local n = idiv(rshift(rshift(d*z,8)+c*z,8)+b*z,beta)
552            if a == 0 then
553                return n
554            elseif a == 255 then
555                return n - alpha
556            else
557                return 0
558            end
559        end
560        --
561        local function readunscaled()
562            local a, b, c, d = readbytes(s,4)
563            if a > 127 then
564                a = a - 256
565            end
566            return a * 0xFFFFF + b * 0xFFF + c * 0xF + rshift(d,4)
567        end
568        --
569        while lh > 2 do -- can be one-liner
570            skipbytes(s,4)
571            lh = lh - 1
572        end
573        local saved = getposition(s)
574        setposition(s,(header + slh + ncw) * 4 + 1)
575        local widths  = { } for i=0,nw-1 do widths [i] = readscaled() end
576        local heights = { } for i=0,nh-1 do heights[i] = readscaled() end
577        local depths  = { } for i=0,nd-1 do depths [i] = readscaled() end
578        local italics = { } for i=0,ni-1 do italics[i] = readscaled() end
579        if widths[0] ~= 0 or heights[0] ~= 0 or depths[0] ~= 0 then
580            return someerror("invalid dimensions")
581        end
582        --
583        local blabel = nl
584        local bchar  = boundarychar
585        --
586        local ligatures = { }
587        if nl > 0 then
588            for i=0,nl-1 do
589                local a, b, c, d = readbytes(s,4)
590                ligatures[i] = {
591                    skip = a,
592                    nxt  = b,
593                    op   = c,
594                    rem  = d,
595                }
596                if a > 128 then
597                    if 256 * c + d >= nl then
598                        return someerror("invalid ligature table")
599                    end
600                    if a == 255 and i == 0 then
601                        bchar = b
602                    end
603                else
604                    if c < 128 then
605                       -- whatever
606                    elseif 256 * (c - 128) + d >= nk then
607                        return someerror("invalid ligature table")
608                    end
609                    if (a < 128) and (i - 0 + a + 1 >= nl) then
610                        return someerror("invalid ligature table")
611                    end
612                end
613                if a == 255 then
614                    blabel = 256 * c + d
615                end
616            end
617        end
618        local allkerns = { }
619        for i=0,nk-1 do
620            allkerns[i] = readscaled()
621        end
622        local extensibles = { }
623        for i=0,ne-1 do
624            extensibles[i] = wide and {
625                top = readcardinal2(s),
626                bot = readcardinal2(s),
627                mid = readcardinal2(s),
628                rep = readcardinal2(s),
629            } or {
630                top = readcardinal1(s),
631                bot = readcardinal1(s),
632                mid = readcardinal1(s),
633                rep = readcardinal1(s),
634            }
635        end
636        for i=1,np do
637            if i == 1 then
638                parameters[i] = readunscaled()
639            else
640                parameters[i] = readscaled()
641            end
642        end
643        for i=1,7 do
644            if not parameters[i] then
645                parameters[i] = 0
646            end
647        end
648        --
649        setposition(s,saved)
650        local extras = false
651        if blabel ~= nl then
652            local k = blabel
653            while true do
654                local l    = ligatures[k]
655                local skip = l.skip
656                if skip <= 128 then
657                 -- if l.op >= 128 then
658                 --     extras = true -- kern
659                 -- else
660                        extras = true -- ligature
661                 -- end
662                end
663                if skip == 0 then
664                    k = k + 1
665                else
666                    if skip >= 128 then
667                       break
668                    end
669                    k = k + skip + 1
670                end
671            end
672        end
673        if extras then
674            local ligas = { }
675            local kerns = { }
676            local k     = blabel
677            while true do
678                local l    = ligatures[k]
679                local skip = l.skip
680                if skip <= 128 then
681                    local nxt = l.nxt
682                    local op  = l.op
683                    local rem = l.rem
684                    if op >= 128 then
685                        kerns[nxt] = allkerns[256 * (op - 128) + rem]
686                    else
687                        ligas[nxt] = { type = op * 2 + 1, char = rem }
688                    end
689                end
690                if skip == 0 then
691                    k = k + 1
692                else
693                    if skip >= 128 then
694                        break;
695                    end
696                    k = k + skip + 1
697                end
698            end
699            if next(kerns) then
700                local glyph     = glyphs[leftboundary]
701                glyph.kerns     = kerns
702                glyph.remainder = 0
703            end
704            if next(ligas) then
705                local glyph     = glyphs[leftboundary]
706                glyph.ligatures = ligas
707                glyph.remainder = 0
708            end
709        end
710        for i=bc,ec do
711            local glyph, width, height, depth, italic, tag, remainder
712            if wide then
713                width     = readcardinal2(s)
714                height    = readcardinal1(s)
715                depth     = readcardinal1(s)
716                italic    = readcardinal1(s)
717                tag       = readcardinal1(s)
718                remainder = readcardinal2(s)
719            else
720                width     = readcardinal1(s)
721                height    = readcardinal1(s)
722                depth     = extract(height,0,4)
723                height    = extract(height,4,4)
724                italic    = readcardinal1(s)
725                tag       = extract(italic,0,2)
726                italic    = extract(italic,2,6)
727                remainder = readcardinal1(s)
728            end
729            if width == 0 then
730                -- nothing
731            else
732                if width >= nw or height >= nh or depth >= nd or italic >= ni then
733                    return someerror("invalid dimension index")
734                end
735                local extensible, nextinsize
736                if tag == 0 then
737                    -- nothing special
738                else
739                    local r = remainder
740                    if tag == 1 then
741                        if r >= nl then
742                            return someerror("invalid ligature index")
743                        end
744                    elseif tag == 2 then
745                        if r < bc or r > ec then
746                            return someerror("invalid chain index")
747                        end
748                        while r < i do
749                            local g = glyphs[r]
750                            if g.tag ~= list_tag then
751                                break
752                            end
753                            r = g.remainder
754                        end
755                        if r == i then
756                            return someerror("cycles in chain")
757                        end
758                        nextinsize = r
759                    elseif tag == 3 then
760                        if r >= ne then
761                            return someerror("bad extensible")
762                        end
763                        extensible = extensibles[r] -- remainder ?
764                        remainder  = 0
765                    end
766                end
767                glyphs[i] = {
768                    width      = widths [width],
769                    height     = heights[height],
770                    depth      = depths [depth],
771                    italic     = italics[italic],
772                    tag        = tag,
773                 -- index      = i,
774                    remainder  = remainder,
775                    extensible = extensible,
776                    next       = nextinsize,
777                }
778            end
779        end
780        for i=bc,ec do
781            local glyph  = glyphs[i]
782            if glyph.tag == 1 then
783                -- ligature
784                local k = glyph.remainder
785                local l = ligatures[k]
786                if l.skip > 128 then
787                    k = 256 * l.op + l.rem
788                end
789                local ligas = { }
790                local kerns = { }
791                while true do
792                    local l    = ligatures[k]
793                    local skip = l.skip
794                    if skip <= 128 then
795                        local nxt = l.nxt
796                        local op  = l.op
797                        local rem = l.rem
798                        if op >= 128 then
799                            local kern = allkerns[256 * (op - 128) + rem]
800                            if nxt == bchar then
801                                kerns[rightboundary] = kern
802                            end
803                            kerns[nxt] = kern
804                        else
805                            local ligature = { type = op * 2 + 1, char = rem }
806                            if nxt == bchar then
807                                ligas[rightboundary] = ligature
808                            end
809                            ligas[nxt] = ligature -- shared
810                        end
811                    end
812                    if skip == 0 then
813                       k = k + 1
814                    else
815                        if skip >= 128 then
816                            break
817                        end
818                        k = k + skip + 1
819                    end
820                end
821                if next(kerns)then
822                    glyph.kerns     = kerns
823                    glyph.remainder = 0
824                end
825                if next(ligas) then
826                    glyph.ligatures = ligas
827                    glyph.remainder = 0
828                end
829            end
830        end
831        --
832        if bchar ~= boundarychar then
833           glyphs[rightboundary] = copy(glyphs[bchar])
834        end
835        --
836     -- for k, v in next, glyphs do
837     --     v.tag       = nil
838     --     v.remainder = nil
839     -- end
840        --
841        return {
842            name           = file.nameonly(filename),
843            fontarea       = file.pathpart(filename),
844            glyphs         = glyphs,
845            parameters     = parameters,
846            designsize     = designsize,
847            size           = designsize,
848            direction      = direction,
849         -- checksum       = checksum,
850         -- embedding      = "unknown",
851         -- extend         = 1000,
852         -- slant          = 0,
853         -- squeeze        = 0,
854         -- format         = "unknown",
855         -- identity       = "unknown",
856         -- mode           = 0,
857         -- streamprovider = 0,
858         -- tounicode      = 0,
859         -- type           = "unknown",
860         -- units_per_em   = 0,
861         -- used           = false,
862         -- width          = 0,
863         -- writingmode    = "unknown",
864        }
865    end
866
867end
868
869do
870
871    local push = { "push" }
872    local push = { "pop" }
873
874    local w, x, y, z, f
875    local stack
876    local s, result, r
877    local alpha, beta, z
878
879    local function scaled1()
880        local a = readbytes(s,1)
881        if a == 0 then
882            return 0
883        elseif a == 255 then
884            return - alpha
885        else
886            return 0 -- error
887        end
888    end
889
890    local function scaled2()
891        local a, b = readbytes(s,2)
892        local sw = idiv(b*z,beta)
893        if a == 0 then
894            return sw
895        elseif a == 255 then
896            return sw - alpha
897        else
898            return 0 -- error
899        end
900    end
901
902    local function scaled3()
903        local a, b, c = readbytes(s,3)
904        local sw = idiv(rshift(c*z,8)+b*z,beta)
905        if a == 0 then
906            return sw
907        elseif a == 255 then
908            return sw - alpha
909        else
910            return 0 -- error
911        end
912    end
913
914    local function scaled4()
915        local a, b, c, d = readbytes(s,4)
916        local sw = idiv( rshift(rshift(d*z,8)+(c*z),8)+b*z,beta)
917        if a == 0 then
918            return sw
919        elseif a == 255 then
920            return sw - alpha
921        else
922            return 0 -- error
923        end
924    end
925
926    local function dummy()
927    end
928
929    local actions = {
930
931        [128] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal1(s) } p = p + 1 end,
932        [129] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal2(s) } p = p + 2 end,
933        [130] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal3(s) } p = p + 3 end,
934        [131] = function() r = r + 1 result[r] = { "slot", f or 1, readcardinal4(s) } p = p + 4 end,
935
936        [132] = function()
937            r = r + 1
938            result[r] = { "rule", scaled4(), scaled4() }
939            p = p + 8
940        end,
941
942        [133] = function()
943                    r = r + 1 result[r] = push
944                    r = r + 1 result[r] = { "slot", f or 1, readcardinal1(s) }
945                    r = r + 1 result[r] = pop
946                    p = p + 1
947                end,
948        [134] = 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 + 2
953                end,
954        [135] = function()
955                    r = r + 1 result[r] = push
956                    r = r + 1 result[r] = { "slot", f or 1, readcardinal2(s) }
957                    r = r + 1 result[r] = pop
958                    p = p + 3
959                end,
960        [136] = function()
961                    r = r + 1 result[r] = push
962                    r = r + 1 result[r] = { "slot", f or 1, readcardinal4(s) }
963                    r = r + 1 result[r] = pop
964                    p = p + 4
965                end,
966
967        [137] = function()
968                    r = r + 1 result[r] = push
969                    r = r + 1 result[r] = { "rule", scaled4(), scaled4() }
970                    r = r + 1 result[r] = pop
971                    p = p + 8
972                end,
973
974        [138] = dummy, -- nop
975        [139] = dummy, -- bop
976        [140] = dummy, -- eop
977
978        [141] = function()
979                    insert(stack, { w, x, y, z })
980                    r = r + 1
981                    result[r] = push
982                end,
983        [142] = function()
984                    local t = remove(stack)
985                    if t then
986                        w, x, y, z = t[1], t[2], t[3], t[4]
987                        r = r + 1
988                        result[r] = pop
989                    end
990                end,
991
992        [143] = function() r = r + 1 result[r] = { "right", scaled1() } p = p + 1 end,
993        [144] = function() r = r + 1 result[r] = { "right", scaled2() } p = p + 2 end,
994        [145] = function() r = r + 1 result[r] = { "right", scaled3() } p = p + 3 end,
995        [146] = function() r = r + 1 result[r] = { "right", scaled4() } p = p + 4 end,
996
997        [148] = function() w = scaled1() r = r + 1 result[r] = { "right", w } p = p + 1 end,
998        [149] = function() w = scaled2() r = r + 1 result[r] = { "right", w } p = p + 2 end,
999        [150] = function() w = scaled3() r = r + 1 result[r] = { "right", w } p = p + 3 end,
1000        [151] = function() w = scaled4() r = r + 1 result[r] = { "right", w } p = p + 4 end,
1001
1002        [153] = function() x = scaled1() r = r + 1 result[r] = { "right", x } p = p + 1 end,
1003        [154] = function() x = scaled2() r = r + 1 result[r] = { "right", x } p = p + 2 end,
1004        [155] = function() x = scaled3() r = r + 1 result[r] = { "right", x } p = p + 3 end,
1005        [156] = function() x = scaled4() r = r + 1 result[r] = { "right", x } p = p + 4 end,
1006
1007        [157] = function() r = r + 1 result[r] = { "down", scaled1() } p = p + 1 end,
1008        [158] = function() r = r + 1 result[r] = { "down", scaled2() } p = p + 2 end,
1009        [159] = function() r = r + 1 result[r] = { "down", scaled3() } p = p + 3 end,
1010        [160] = function() r = r + 1 result[r] = { "down", scaled4() } p = p + 4 end,
1011
1012        [162] = function() y = scaled1() r = r + 1 result[r] = { "down", y } p = p + 1 end,
1013        [163] = function() y = scaled2() r = r + 1 result[r] = { "down", y } p = p + 2 end,
1014        [164] = function() y = scaled3() r = r + 1 result[r] = { "down", y } p = p + 3 end,
1015        [165] = function() y = scaled3() r = r + 1 result[r] = { "down", y } p = p + 4 end,
1016
1017        [167] = function() z = scaled1() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1018        [168] = function() z = scaled2() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1019        [169] = function() z = scaled3() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1020        [170] = function() z = scaled4() r = r + 1 ; result[r] = { "down", z } p = p + 4 end,
1021
1022        [147] = function() r = r + 1 result[r] = { "right", w } end,
1023        [152] = function() r = r + 1 result[r] = { "right", x } end,
1024        [161] = function() r = r + 1 result[r] = { "down",  y } end,
1025        [166] = function() r = r + 1 result[r] = { "down",  z } end,
1026
1027        [235] = function() f = readcardinal1(s) p = p + 1 end,
1028        [236] = function() f = readcardinal2(s) p = p + 3 end,
1029        [237] = function() f = readcardinal3(s) p = p + 3 end,
1030        [238] = function() f = readcardinal4(s) p = p + 4 end,
1031
1032        [239] = function() local n = readcardinal1(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 1 + n end,
1033        [240] = function() local n = readcardinal2(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 2 + n end,
1034        [241] = function() local n = readcardinal3(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 3 + n end,
1035        [242] = function() local n = readcardinal4(s) r = r + 1 result[r] = { "special", readstring(s,n) } p = p + 4 + n end,
1036
1037        [250] = function() local n = readcardinal1(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 1 + n end,
1038        [251] = function() local n = readcardinal2(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 2 + n end,
1039        [252] = function() local n = readcardinal3(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 3 + n end,
1040        [253] = function() local n = readcardinal4(s) r = r + 1 result[r] = { "pdf", readstring(s,n) } p = p + 4 + n end,
1041
1042    }
1043
1044    table.setmetatableindex(actions,function(t,cmd)
1045        local v
1046        if cmd >= 0 and cmd <= 127 then
1047            v = function()
1048                if f == 0 then
1049                    f = 1
1050                end
1051                r = r + 1 ; result[r] = { "slot", f, cmd }
1052            end
1053        elseif cmd >= 171 and cmd <= 234 then
1054            cmd = cmd - 170
1055            v = function()
1056                r = r + 1 ; result[r] = { "font", cmd }
1057            end
1058        else
1059            v = dummy
1060        end
1061        t[cmd] = v
1062        return v
1063    end)
1064
1065    function readers.loadvf(filename,data)
1066        --
1067        local function someerror(m)
1068            if not data then
1069                data = { }
1070            end
1071            data.error = m or "fatal error"
1072            return data
1073        end
1074        --
1075        s = openstream(filename)
1076        if not s then
1077            return someerror()
1078        end
1079        --
1080        local cmd = readcardinal1(s)
1081        if cmd ~= 247 then
1082            return someerror("bad preamble")
1083        end
1084        cmd = readcardinal1(s)
1085        if cmd ~= 202 then
1086            return someerror("bad version")
1087        end
1088        local header     = readstring(s,readcardinal1(s))
1089        local checksum   = readcardinal4(s)
1090        local designsize = idiv(readcardinal4(s),16)
1091        local fonts      = data and data.fonts  or { }
1092        local glyphs     = data and data.glyphs or { }
1093        --
1094        alpha =  16
1095        z     = designsize
1096        while z >= 040000000 do
1097            z = rshift(z,1)
1098            alpha = alpha + alpha
1099        end
1100        beta  = idiv(256,alpha)
1101        alpha = alpha * z
1102        --
1103        cmd = readcardinal1(s)
1104        while true do
1105            local n
1106            if cmd == 243 then
1107                n = readcardinal1(s) + 1
1108            elseif cmd == 244 then
1109                n = readcardinal2(s) + 1
1110            elseif cmd == 245 then
1111                n = readcardinal3(s) + 1
1112            elseif cmd == 246 then
1113                n = readcardinal4(s) + 1
1114            else
1115                break
1116            end
1117            local checksum   = skipbytes(s,4)
1118            local size       = scaled4()
1119            local designsize = idiv(readcardinal4(s),16)
1120            local pathlen    = readcardinal1(s)
1121            local namelen    = readcardinal1(s)
1122            local path       = readstring(s,pathlen)
1123            local name       = readstring(s,namelen)
1124            fonts[n] = { path = path, name = name, size = size }
1125            cmd = readcardinal1(s)
1126        end
1127        local index = 0
1128        while cmd and cmd <= 242 do
1129            local width    = 0
1130            local length   = 0
1131            local checksum = 0
1132            if cmd == 242 then
1133                length   = readcardinal4(s)
1134                checksum = readcardinal4(s)
1135                width    = readcardinal4(s)
1136            else
1137                length   = cmd
1138                checksum = readcardinal1(s)
1139                width    = readcardinal3(s)
1140            end
1141            w, x, y, z, f = 0, 0, 0, 0, false
1142            stack, result, r, p = { }, { }, 0, 0
1143            while p < length do
1144                local cmd = readcardinal1(s)
1145                p = p + 1
1146                actions[cmd]()
1147            end
1148            local glyph = glyphs[index]
1149            if glyph then
1150                glyph.width    = width
1151                glyph.commands = result
1152            else
1153                glyphs[index] = {
1154                    width    = width,
1155                    commands = result,
1156                }
1157            end
1158            index = index + 1
1159            if #stack > 0 then
1160                -- error: more pushes than pops
1161            end
1162            if packet_length ~= 0 then
1163                -- error: invalid packet length
1164            end
1165            cmd = readcardinal1(s)
1166        end
1167        if readcardinal1(s) ~= 248 then
1168            -- error: no post
1169        end
1170        s, result, r = nil, nil, nil
1171        if data then
1172            data.glyphs = data.glyphs or glyphs
1173            data.fonts  = data.fonts  or fonts
1174            return data
1175        else
1176            return {
1177                name       = file.nameonly(filename),
1178                fontarea   = file.pathpart(filename),
1179                glyphs     = glyphs,
1180                designsize = designsize,
1181                header     = header,
1182                fonts      = fonts,
1183            }
1184        end
1185    end
1186
1187    -- the replacement loader (not sparse):
1188
1189    function readers.loadtfmvf(tfmname,size)
1190        local vfname  = file.addsuffix(file.nameonly(tfmfile),"vf")
1191        local tfmfile = tfmname
1192        local vffile  = resolvers.findbinfile(vfname,"ovf")
1193        if tfmfile and tfmfile ~= "" then
1194            if size < 0 then
1195                size = idiv(65536 * -size,100)
1196            end
1197            local data = readers.loadtfm(tfmfile)
1198            if data.error then
1199                return data
1200            end
1201            if vffile and vffile ~= "" then
1202                data = readers.loadvf(vffile,data)
1203                if data.error then
1204                    return data
1205                end
1206            end
1207            local designsize = data.designsize
1208            local glyphs     = data.glyphs
1209            local parameters = data.parameters
1210            local fonts      = data.fonts
1211            if size ~= designsize then
1212                local factor = size / designsize
1213                for index, glyph in next, glyphs do
1214                    if next(glyph) then
1215                        glyph.width  = round(factor*glyph.width)
1216                        glyph.height = round(factor*glyph.height)
1217                        glyph.depth  = round(factor*glyph.depth)
1218                        local italic = glyph.italic
1219                        if italic == 0 then
1220                            glyph.italic = nil
1221                        else
1222                            glyph.italic = round(factor*glyph.italic)
1223                        end
1224                        --
1225                        local kerns = glyph.kerns
1226                        if kerns then
1227                            for index, kern in next, kerns do
1228                                kerns[index] = round(factor*kern)
1229                            end
1230                        end
1231                        --
1232                        local commands = glyph.commands
1233                        if commands then
1234                            for i=1,#commands do
1235                                local c = commands[i]
1236                                local t = c[1]
1237                                if t == "down" or t == "right" then
1238                                    c[2] = round(factor*c[2])
1239                                elseif t == "rule" then
1240                                    c[2] = round(factor*c[2])
1241                                    c[3] = round(factor*c[3])
1242                                end
1243                            end
1244                        end
1245                    else
1246                        glyphs[index] = nil
1247                    end
1248                end
1249                for i=2,30 do
1250                    local p = parameters[i]
1251                    if p then
1252                        parameters[i] = round(factor*p)
1253                    else
1254                        break
1255                    end
1256                end
1257                if fonts then
1258                    for k, v in next, fonts do
1259                        v.size = round(factor*v.size)
1260                    end
1261                end
1262            else
1263                for index, glyph in next, glyphs do
1264                    if next(glyph) then
1265                        if glyph.italic == 0 then
1266                            glyph.italic = nil
1267                        end
1268                    else
1269                        glyphs[index] = nil
1270                    end
1271                end
1272            end
1273            --
1274            parameters.slant         = parameters[1]
1275            parameters.space         = parameters[2]
1276            parameters.space_stretch = parameters[3]
1277            parameters.space_shrink  = parameters[4]
1278            parameters.x_height      = parameters[5]
1279            parameters.quad          = parameters[6]
1280            parameters.extra_space   = parameters[7]
1281            --
1282            for i=1,7 do
1283                parameters[i] = nil -- so no danger for async
1284            end
1285            --
1286            data.characters = glyphs
1287            data.glyphs     = nil
1288            data.size       = size
1289            -- we assume type1 for now ... maybe the format should be unknown
1290            data.filename   = tfmfile -- file.replacesuffix(tfmfile,"pfb")
1291            data.format     = "unknown"
1292            --
1293            return data
1294        end
1295    end
1296
1297end
1298
1299-- inspect(readers.loadtfmvf(resolvers.findfile("mi-iwonari.tfm")))
1300-- inspect(readers.loadtfm(resolvers.findfile("texnansi-palatinonova-regular.tfm")))
1301-- inspect(readers.loadtfm(resolvers.findfile("cmex10.tfm")))
1302-- inspect(readers.loadtfm(resolvers.findfile("cmr10.tfm")))
1303-- local t = readers.loadtfmvf("texnansi-lte50019.tfm")
1304-- inspect(t)
1305