1if not modules then modules = { } end modules ['font-phb-imp-internal'] = { 2 version = 1.000, -- 2016.10.10, 3 comment = "companion to font-txt.mkiv", 4 original = "derived from font-phb-imp-library", 5 author = "Hans Hagen", 6 copyright = "PRAGMA ADE / ConTeXt Development Team", 7 license = "see context related readme files", 8} 9 10-- The hb library comes in versions and the one I tested in 2016 was part of the inkscape 11-- suite. In principle one can have incompatibilities due to updates but that is the nature 12-- of a library. When a library ie expected one has better use the system version, if only 13-- to make sure that different programs behave the same. 14-- 15-- The main reason for testing this approach was that when Idris was working on his fonts, 16-- we wanted to know how different shapers deal with it and the hb command line program 17-- could provide uniscribe output. For the context shaper uniscribe is the reference, also 18-- because Idris started out with Volt a decade ago. 19-- 20-- We treat the lib as a black box as it should be. At some point Kai Eigner made an ffi 21-- binding and that one was adapted to the plugin approach of context. It saved me the 22-- trouble of looking at source files to figure it all out. Below is the adapted code. 23-- 24-- This is basically the ffi variant but with the hb function calls delegated to a simple 25-- runtime library. That library was a side effect of playing a day with delayed loading 26-- like ffi does in luametatex, which seems to work ok for what we call optional libraries 27-- in lmtx. I didn't really test the next code well (and will probably do that when Idris 28-- needs some comparison with uniscribe etc). There are nowadays probably other ways to do 29-- this but this is what we had and so we can keep the test code that has been around for 30-- a while (which is needed because some old articles need it.) 31-- 32-- The following setup doesn't really fit into the way we set up internal libraries 33-- but if isn't used in the same sense anyway so we stick to what we already had in 34-- the ffi variant (also because it uses helpers here and we want to keep the client 35-- variant too). We don't need to be generic as other macro packages follow a different 36-- route. 37-- 38-- Last time I checked "fiets" got no ligature with the "ot" shaper but it did get one 39-- with the "uniscribe" shaper ... somewhat puzzling .. but "effe" worked okay. Maybe 40-- there is some built-in heuristic interfering? When Idris an I tested fonts we had 41-- similar differences with arabic so maybe we miss a point here. 42-- 43-- native font plugin > hb > string : fi- 44-- font plugin > hb > text : U+00066 U+00069 U+0002D 45-- font plugin > hb > result : U+00066 U+00069 U+0002D 46-- 47-- uniscribe font plugin > hb > string : fi- 48-- font plugin > hb > text : U+00066 U+00069 U+0002D 49-- font plugin > hb > result : U+0FB01 U+0002D 50-- 51-- native font plugin > hb > string : ets 52-- font plugin > hb > text : U+00065 U+00074 U+00073 53-- font plugin > hb > result : U+00065 U+00074 U+00073 54-- 55-- uniscribe font plugin > hb > string : ets 56-- font plugin > hb > text : U+00065 U+00074 U+00073 57-- font plugin > hb > result : U+00065 U+00074 U+00073 58-- 59-- native font plugin > hb > string : fiets 60-- font plugin > hb > text : U+00066 U+00069 U+00065 U+00074 U+00073 61-- font plugin > hb > result : U+00066 U+00069 U+00065 U+00074 U+00073 62-- 63-- uniscribe font plugin > hb > string : fiets 64-- font plugin > hb > text : U+00066 U+00069 U+00065 U+00074 U+00073 65-- font plugin > hb > result : U+0FB01 U+00065 U+00074 U+00073 66 67-- In the meantime a single dll is not enough and we need some more which interferes with 68-- the idea of a simple dll in the lib path ... too many dependencies now .. and having 69-- some shared bin path is not what we want in tex so ... 70 71local report = utilities.hb.report or print 72 73local hblib = optional and (optional.hb or optional.test) 74 75if not hblib then 76 report("no hblib found, you can try the ffi variant") 77 return 78end 79 80local hb_initialize = hblib.initialize 81local hb_getversion = hblib.getversion 82local hb_getshapers = hblib.getshapers 83local hb_loadfont = hblib.loadfont 84local hb_shapestring = hblib.shapestring 85 86if not hb_initialize then 87 report("no functions in hblib found, you can try the ffi variant") 88 return 89end 90 91local loaddata = io.loaddata 92local findlib = resolvers.findlib 93local concat = table.concat 94local utfchar = utf.char 95local packtoutf8 = utilities.hb.helpers.packtoutf8 96local packtoutf32 = utilities.hb.helpers.packtoutf32 97local report = utilities.hb.report or print 98local fontdata = fonts.hashes.identifiers 99local initialized = nil 100local loaded = { } 101local shared = { } 102local libfile = os.name == "windows" and "libharfbuzz-0" or "libharfbuzz" 103 104local shapers = { 105 native = { "ot", "uniscribe", "fallback" }, 106 uniscribe = { "uniscribe", "ot", "fallback" }, 107 -- uniscribe = { "uniscribe", "fallback" }, -- stalls without fallback when no uniscribe present 108 fallback = { "fallback" }, 109} 110 111local mode = 8 -- now 32 bit crashes (random, needs checking, probably zero end of string issue) 112 113function utilities.hb.methods.internal(font,data,rlmode,text,leading,trailing) 114 if initialized == nil then 115 local filename = findlib(libfile) 116 initialized = hb_initialize(filename) 117 if initialized then 118 report("using hb library version %a, supported shapers: %,t",hb_getversion(),hb_getshapers()) 119 else 120 report("unable to locate hb library") 121 initialize = false 122 end 123 end 124 if initialized then 125 local instance = loaded[font] 126 if instance == nil then 127 local tfmdata = fontdata[font] 128 local resources = tfmdata.resources 129 local filename = resources.filename 130 instance = shared[filename] 131 if instance == nil then 132 local wholefont = loaddata(filename) 133 if wholefont then 134 instance = hb_loadfont(font,wholefont) 135 end 136 if not instance then 137 instance = false 138 end 139 shared[filename] = instance 140 end 141 loaded[font] = instance 142 end 143 if instance then 144 if mode ==32 then 145 text = packtoutf32(text,leading,trailing) 146 else 147 text = packtoutf8(text,leading,trailing) -- doesn't work ok (no time not to figure it out) 148 end 149 local result = hb_shapestring ( 150 instance, 151 data.script or "dflt", 152 data.language or "dflt", 153 rlmode < 0 and "rtl" or "ltr", 154 shapers[data.shaper] or shapers.native, 155 data.featureset or { }, 156 text, 157 rlmode < 0, 158 mode 159 ) 160 -- inspect(result) 161 return result 162 end 163 end 164end 165 |