font-phb-imp-binary.lmt /size: 4011 b    last modification: 2021-10-28 13:51
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
34-- output : [index=cluster@x_offset,y_offset+x_advance,y_advance|...]
35-- result : { index, cluster, x_offset, y_offset, x_advance, y_advance }
36
37local P, Ct, Cc = lpeg.P, lpeg.Ct, lpeg.Cc
38local lpegmatch = lpeg.match
39
40local zero      = Cc(0)
41local number    = lpeg.patterns.integer / tonumber + zero
42local index     = lpeg.patterns.cardinal / tonumber
43local cluster   = index
44local offset    = (P("@") * number * (P(",") * number + zero)) + zero * zero
45local advance   = (P("+") * number * (P(",") * number + zero)) + zero * zero
46local glyph     = Ct(index * P("=") * cluster * offset * advance)
47local pattern   = Ct(P("[") * (glyph * P("|")^-1)^0 * P("]"))
48
49local shapers = {
50    native    = "ot,uniscribe,fallback",
51    uniscribe = "uniscribe,ot,fallback",
52    fallback  = "fallback"
53}
54
55local runner = sandbox.registerrunner {
56    method     = "resultof",
57    name       = "harfbuzz",
58 -- program    = {
59 --     windows = "hb-shape.exe",
60 --     unix    = "hb-shape"
61 -- },
62    program    = "hb-shape",
63    checkers   = {
64        shaper    = "string",
65        features  = "string",
66        script    = "string",
67        language  = "string",
68        direction = "string",
69        textfile  = "writable",
70        fontfile  = "readable",
71    },
72    template   = string.longtostring [[
73        --shaper=%shaper%
74        --output-format=text
75        --no-glyph-names
76        --features="%features%"
77        --script=%script%
78        --language=%language%
79        --direction=%direction%
80        --text-file=%textfile%
81        --font-file=%fontfile%
82    ]],
83}
84
85local tempfile = "font-phb.tmp"
86local reported = false
87
88function utilities.hb.methods.binary(font,data,rlmode,text,leading,trailing)
89    if runner then
90        savedata(tempfile,packtoutf8(text,leading,trailing))
91        local result  = runner {
92            shaper    = shapers[data.shaper] or shapers.native,
93            features  = data.features,
94            script    = data.script or "dflt",
95            language  = data.language or "dflt",
96            direction = rlmode < 0 and "rtl" or "ltr",
97            textfile  = tempfile,
98            fontfile  = data.filename,
99        }
100        removefile(tempfile)
101        if result then
102         -- return jsontolua(result)
103            result = lpegmatch(pattern,result) -- { index cluster xo yo xa ya }
104            if rlmode < 0 then
105                return reverse(result) -- we can avoid this
106            else
107                return result
108            end
109        end
110    elseif reported then
111        report("no runner available")
112        reported = true
113    end
114end
115