diff options
Diffstat (limited to 'otfl-font-ota.lua')
-rw-r--r-- | otfl-font-ota.lua | 196 |
1 files changed, 143 insertions, 53 deletions
diff --git a/otfl-font-ota.lua b/otfl-font-ota.lua index f972d28..c4663e1 100644 --- a/otfl-font-ota.lua +++ b/otfl-font-ota.lua @@ -13,50 +13,109 @@ local type, tostring, match, format, concat = type, tostring, string.match, stri if not trackers then trackers = { register = function() end } end local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end) -local trace_cjk = false trackers.register("cjk.injections", function(v) trace_cjk = v end) -trackers.register("cjk.analyzing","otf.analyzing") +local fonts, nodes, node = fonts, nodes, node -local fonts, nodes = fonts, nodes -local node = node +local allocate = utilities.storage.allocate -local otf = fonts.otf -local tfm = fonts.tfm +local otf = fonts.handlers.otf -fonts.analyzers = fonts.analyzers or { } -local analyzers = fonts.analyzers +local analyzers = fonts.analyzers +local initializers = allocate() +local methods = allocate() -analyzers.initializers = analyzers.initializers or { node = { otf = { } } } -analyzers.methods = analyzers.methods or { node = { otf = { } } } +analyzers.initializers = initializers +analyzers.methods = methods +analyzers.useunicodemarks = false -local initializers = analyzers.initializers -local methods = analyzers.methods +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph -local nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local traverse_id = node.traverse_id +local traverse_node_list = node.traverse -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local traverse_id = node.traverse_id -local traverse_node_list = node.traverse +local fontdata = fonts.hashes.identifiers +local state = attributes.private('state') +local categories = characters and characters.categories or { } -- sorry, only in context -local fontdata = fonts.identifiers -local state = attributes.private('state') -local categories = characters and characters.categories or { } -- sorry, only in context +local tracers = nodes.tracers +local colortracers = tracers and tracers.colors +local setnodecolor = colortracers and colortracers.set or function() end +local resetnodecolor = colortracers and colortracers.reset or function() end -local fontscolors = fonts.colors -local fcs = (fontscolors and fontscolors.set) or function() end -local fcr = (fontscolors and fontscolors.reset) or function() end +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register +--[[ldx-- +<p>Analyzers run per script and/or language and are needed in order to +process features right.</p> +--ldx]]-- + +-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script +-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace + +local state = attributes.private('state') + +function analyzers.setstate(head,font) + local useunicodemarks = analyzers.useunicodemarks + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean + while current do + local id = current.id + if id == glyph_code and current.font == font then + local char = current.char + local d = descriptions[char] + if d then + if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then + done = true + set_attribute(current,state,5) -- mark + elseif n == 0 then + first, last, n = current, current, 1 + set_attribute(current,state,1) -- init + else + last, n = current, n+1 + set_attribute(current,state,2) -- medi + end + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + elseif id == disc_code then + -- always in the middle + set_attribute(current,state,2) -- midi + last = current + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + current = current.next + end + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + return head, done +end -- in the future we will use language/script attributes instead of the -- font related value, but then we also need dynamic features which is -- somewhat slower; and .. we need a chain of them -local scriptandlanguage = otf.scriptandlanguage - -function fonts.initializers.node.otf.analyze(tfmdata,value,attr) - local script, language = otf.scriptandlanguage(tfmdata,attr) +local function analyzeinitializer(tfmdata,value) -- attr + local script, language = otf.scriptandlanguage(tfmdata) -- attr local action = initializers[script] if action then if type(action) == "function" then @@ -68,10 +127,9 @@ function fonts.initializers.node.otf.analyze(tfmdata,value,attr) end end end - return nil end -function fonts.methods.node.otf.analyze(head,font,attr) +local function analyzeprocessor(head,font,attr) local tfmdata = fontdata[font] local script, language = otf.scriptandlanguage(tfmdata,attr) local action = methods[script] @@ -88,14 +146,25 @@ function fonts.methods.node.otf.analyze(head,font,attr) return head, false end -otf.features.register("analyze",true) -- we always analyze -table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this +registerotffeature { + name = "analyze", + description = "analysis of (for instance) character classes", + default = true, + initializers = { + node = analyzeinitializer, + }, + processors = { + position = 1, + node = analyzeprocessor, + } +} -- latin -analyzers.methods.latn = analyzers.aux.setstate +methods.latn = analyzers.setstate --- this info eventually will go into char-def +-- this info eventually will go into char-def and we will have a state +-- table for generic then local zwnj = 0x200C local zwj = 0x200D @@ -124,6 +193,12 @@ local isol_fina = { [0x076C] = true, [0x0771] = true, [0x0773] = true, [0x0774] = true, [0x0778] = true, [0x0779] = true, [0xFEF5] = true, [0xFEF7] = true, [0xFEF9] = true, [0xFEFB] = true, + + -- syriac + + [0x0710] = true, [0x0715] = true, [0x0716] = true, [0x0717] = true, + [0x0718] = true, [0x0719] = true, [0x0728] = true, [0x072A] = true, + [0x072C] = true, [0x071E] = true, } local isol_fina_medi_init = { @@ -161,24 +236,33 @@ local isol_fina_medi_init = { [0x0772] = true, [0x0775] = true, [0x0776] = true, [0x0777] = true, [0x077A] = true, [0x077B] = true, [0x077C] = true, [0x077D] = true, [0x077E] = true, [0x077F] = true, [zwj] = true, + + -- syriac + + [0x0712] = true, [0x0713] = true, [0x0714] = true, [0x071A] = true, + [0x071B] = true, [0x071C] = true, [0x071D] = true, [0x071F] = true, + [0x0720] = true, [0x0721] = true, [0x0722] = true, [0x0723] = true, + [0x0725] = true, [0x0726] = true, [0x0727] = true, [0x0729] = true, + [0x072B] = true, [0x0724] = true, [0x0706] = true, [0x0707] = true, } local arab_warned = { } + -- todo: gref local function warning(current,what) local char = current.char if not arab_warned[char] then - log.report("analyze","arab: character %s (U+%04X) has no %s class", char, char, what) + log.report("analyze","arab: character %s (U+%05X) has no %s class", char, char, what) arab_warned[char] = true end end -function analyzers.methods.nocolor(head,font,attr) +function methods.nocolor(head,font,attr) for n in traverse_id(glyph_code,head) do if not font or n.font == font then - fcr(n) + resetnodecolor(n) end end return head, true @@ -190,22 +274,22 @@ local function finish(first,last) local fc = first.char if isol_fina_medi_init[fc] or isol_fina[fc] then set_attribute(first,state,4) -- isol - if trace_analyzing then fcs(first,"font:isol") end + if trace_analyzing then setnodecolor(first,"font:isol") end else warning(first,"isol") set_attribute(first,state,0) -- error - if trace_analyzing then fcr(first) end + if trace_analyzing then resetnodecolor(first) end end else local lc = last.char if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? -- if laststate == 1 or laststate == 2 or laststate == 4 then set_attribute(last,state,3) -- fina - if trace_analyzing then fcs(last,"font:fina") end + if trace_analyzing then setnodecolor(last,"font:fina") end else warning(last,"fina") set_attribute(last,state,0) -- error - if trace_analyzing then fcr(last) end + if trace_analyzing then resetnodecolor(last) end end end first, last = nil, nil @@ -214,21 +298,21 @@ local function finish(first,last) local fc = first.char if isol_fina_medi_init[fc] or isol_fina[fc] then set_attribute(first,state,4) -- isol - if trace_analyzing then fcs(first,"font:isol") end + if trace_analyzing then setnodecolor(first,"font:isol") end else warning(first,"isol") set_attribute(first,state,0) -- error - if trace_analyzing then fcr(first) end + if trace_analyzing then resetnodecolor(first) end end first = nil end return first, last end -function analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace +function methods.arab(head,font,attr) -- maybe make a special version with no trace local useunicodemarks = analyzers.useunicodemarks local tfmdata = fontdata[font] - local marks = tfmdata.marks + local marks = tfmdata.resources.marks local first, last, current, done = nil, nil, head, false while current do if current.id == glyph_code and current.subtype<256 and current.font == font and not has_attribute(current,state) then @@ -236,20 +320,20 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version local char = current.char if marks[char] or (useunicodemarks and categories[char] == "mn") then set_attribute(current,state,5) -- mark - if trace_analyzing then fcs(current,"font:mark") end + if trace_analyzing then setnodecolor(current,"font:mark") end elseif isol[char] then -- can be zwj or zwnj too first, last = finish(first,last) set_attribute(current,state,4) -- isol - if trace_analyzing then fcs(current,"font:isol") end + if trace_analyzing then setnodecolor(current,"font:isol") end first, last = nil, nil elseif not first then if isol_fina_medi_init[char] then set_attribute(current,state,1) -- init - if trace_analyzing then fcs(current,"font:init") end + if trace_analyzing then setnodecolor(current,"font:init") end first, last = first or current, current elseif isol_fina[char] then set_attribute(current,state,4) -- isol - if trace_analyzing then fcs(current,"font:isol") end + if trace_analyzing then setnodecolor(current,"font:isol") end first, last = nil, nil else -- no arab first, last = finish(first,last) @@ -257,18 +341,18 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version elseif isol_fina_medi_init[char] then first, last = first or current, current set_attribute(current,state,2) -- medi - if trace_analyzing then fcs(current,"font:medi") end + if trace_analyzing then setnodecolor(current,"font:medi") end elseif isol_fina[char] then if not has_attribute(last,state,1) then -- tricky, we need to check what last may be ! set_attribute(last,state,2) -- medi - if trace_analyzing then fcs(last,"font:medi") end + if trace_analyzing then setnodecolor(last,"font:medi") end end set_attribute(current,state,3) -- fina - if trace_analyzing then fcs(current,"font:fina") end + if trace_analyzing then setnodecolor(current,"font:fina") end first, last = nil, nil elseif char >= 0x0600 and char <= 0x06FF then - if trace_analyzing then fcs(current,"font:rest") end + if trace_analyzing then setnodecolor(current,"font:rest") end first, last = finish(first,last) else --no first, last = finish(first,last) @@ -281,3 +365,9 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version first, last = finish(first,last) return head, done end + +methods.syrc = methods.arab + +directives.register("otf.analyze.useunicodemarks",function(v) + analyzers.useunicodemarks = v +end) |