font-phb-imp-binary.lua /size: 4108 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['font-phb-imp-binary'] = {
2    version   = 1.000, -- 2016.10.10,
3    comment   = "companion to font-txt.mkiv",
4    author    = "Hans Hagen",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files",
7}
8
9-- The hb library comes in versions and the one I tested in 2016 was part of the inkscape
10-- suite. In principle one can have incompatibilities due to updates but that is the nature
11-- of a library. When a library ie expected one has better use the system version, if only
12-- to make sure that different programs behave the same.
13--
14-- The main reason for testing this approach was that when Idris was working on his fonts,
15-- we wanted to know how different shapers deal with it and the hb command line program
16-- could provide uniscribe output. For the context shaper uniscribe is the reference, also
17-- because Idris started out with Volt a decade ago.
18--
19-- This file uses the indirect approach by calling the executable. This file uses context
20-- features and is not generic.
21
22local next, tonumber, pcall = next, tonumber, pcall
23
24local concat      = table.concat
25local reverse     = table.reverse
26local formatters  = string.formatters
27local removefile  = os.remove
28local resultof    = os.resultof
29local savedata    = io.savedata
30
31local report      = utilities.hb.report or print
32local packtoutf8  = utilities.hb.helpers.packtoutf8
33
34if not context then
35    report("the binary runner is only supported in context")
36    return
37end
38
39-- output : [index=cluster@x_offset,y_offset+x_advance,y_advance|...]
40-- result : { index, cluster, x_offset, y_offset, x_advance, y_advance }
41
42local P, Ct, Cc = lpeg.P, lpeg.Ct, lpeg.Cc
43local lpegmatch = lpeg.match
44
45local zero      = Cc(0)
46local number    = lpeg.patterns.integer / tonumber + zero
47local index     = lpeg.patterns.cardinal / tonumber
48local cluster   = index
49local offset    = (P("@") * number * (P(",") * number + zero)) + zero * zero
50local advance   = (P("+") * number * (P(",") * number + zero)) + zero * zero
51local glyph     = Ct(index * P("=") * cluster * offset * advance)
52local pattern   = Ct(P("[") * (glyph * P("|")^-1)^0 * P("]"))
53
54local shapers = {
55    native    = "ot,uniscribe,fallback",
56    uniscribe = "uniscribe,ot,fallback",
57    fallback  = "fallback"
58}
59
60local runner = sandbox.registerrunner {
61    method     = "resultof",
62    name       = "harfbuzz",
63 -- program    = {
64 --     windows = "hb-shape.exe",
65 --     unix    = "hb-shape"
66 -- },
67    program    = "hb-shape",
68    checkers   = {
69        shaper    = "string",
70        features  = "string",
71        script    = "string",
72        language  = "string",
73        direction = "string",
74        textfile  = "writable",
75        fontfile  = "readable",
76    },
77    template   = string.longtostring [[
78        --shaper=%shaper%
79        --output-format=text
80        --no-glyph-names
81        --features="%features%"
82        --script=%script%
83        --language=%language%
84        --direction=%direction%
85        --text-file=%textfile%
86        --font-file=%fontfile%
87    ]],
88}
89
90local tempfile = "font-phb.tmp"
91local reported = false
92
93function utilities.hb.methods.binary(font,data,rlmode,text,leading,trailing)
94    if runner then
95        savedata(tempfile,packtoutf8(text,leading,trailing))
96        local result  = runner {
97            shaper    = shapers[data.shaper] or shapers.native,
98            features  = data.features,
99            script    = data.script or "dflt",
100            language  = data.language or "dflt",
101            direction = rlmode < 0 and "rtl" or "ltr",
102            textfile  = tempfile,
103            fontfile  = data.filename,
104        }
105        removefile(tempfile)
106        if result then
107         -- return jsontolua(result)
108            result = lpegmatch(pattern,result) -- { index cluster xo yo xa ya }
109            if rlmode < 0 then
110                return reverse(result) -- we can avoid this
111            else
112                return result
113            end
114        end
115    elseif reported then
116        report("no runner available")
117        reported = true
118    end
119end
120