diff options
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | NEWS | 5 | ||||
| -rw-r--r-- | luaotfload-legacy-attributes.lua | 27 | ||||
| -rw-r--r-- | luaotfload-legacy-database.lua | 724 | ||||
| -rw-r--r-- | luaotfload-legacy-merged.lua | 8157 | ||||
| -rwxr-xr-x | luaotfload-legacy-tool.lua | 105 | ||||
| -rw-r--r-- | luaotfload-legacy.lua | 266 | ||||
| -rwxr-xr-x | luaotfload-tool.lua | 15 | ||||
| -rw-r--r-- | luaotfload.dtx | 10 | 
9 files changed, 9310 insertions, 6 deletions
@@ -12,6 +12,7 @@ SCRIPTNAME   = luaotfload-tool  SCRIPT       = $(SCRIPTNAME).lua  MANSOURCE	 = $(SCRIPTNAME).rst  MANPAGE   	 = $(SCRIPTNAME).1 +OLDSCRIPT    = luaotfload-legacy-tool.lua  GRAPH  		 = filegraph  DOTPDF 		 = $(GRAPH).pdf @@ -24,7 +25,7 @@ MAN			= $(MANPAGE)  COMPILED    = $(DOC)  UNPACKED    = luaotfload.sty luaotfload.lua  GENERATED   = $(GRAPHED) $(UNPACKED) $(COMPILED) $(GLYPHS) $(MAN) -SOURCE 		= $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(SCRIPT) $(GLYPHSCRIPT) +SOURCE 		= $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(SCRIPT) $(OLDSCRIPT) $(GLYPHSCRIPT)  # test files  TESTDIR 		= tests @@ -33,7 +34,7 @@ TESTFILES_SYS 	= $(TESTDIR)/systemfonts.tex $(TESTDIR)/fontconfig_conf_reading.t  TESTFILES_TL 	= $(filter-out $(TESTFILES_SYS), $(TESTFILES))  # Files grouped by installation location -SCRIPTFILES = $(SCRIPT) $(GLYPHSCRIPT) +SCRIPTFILES = $(SCRIPT) $(OLDSCRIPT) $(GLYPHSCRIPT)  RUNFILES    = $(UNPACKED) $(OTFL)  DOCFILES    = $(DOC) $(DOTPDF) README NEWS  MANFILES	= $(MANPAGE) @@ -56,7 +57,7 @@ CTAN_ZIP = $(NAME).zip  TDS_ZIP  = $(NAME).tds.zip  ZIPS 	 = $(CTAN_ZIP) $(TDS_ZIP) -DO_TEX 		  	= tex --interaction=batchmode $< >/dev/null +DO_TEX 		  	= luatex --interaction=batchmode $< >/dev/null  # (with the next version of latexmk: -pdf -pdflatex=lualatex)  DO_LATEX 	  	= latexmk -pdf -e '$$pdflatex = q(lualatex %O %S)' -silent $< >/dev/null  DO_GRAPHVIZ 	= dot -Tpdf -o $@ $< > /dev/null @@ -1,7 +1,7 @@  Change History  -------------- -2013/05/06, luaotfload v2.2: +2013/05/11, luaotfload v2.2:      * There is now a central, non-personal dev repo on github:        https://github.com/lualatex/luaotfload      * Synchronisation with ConTeXt from TeXLive 2013, inducing @@ -27,6 +27,9 @@ Change History        allocation, custom merged package name etc.)      * Auxiliary functions for package authors      * Man page for luaotfload-tool +    * Unsupported v1.3 codebase is included in the luaotfload-*lua files. Crude, +      but effective way to support older Luatex binaries +    * implemented basic cache control/stats option for luaotfload-tool  2013/04/27, luaotfload v1.3:      *  blacklisting lingoes.ttf (segfaults) diff --git a/luaotfload-legacy-attributes.lua b/luaotfload-legacy-attributes.lua new file mode 100644 index 0000000..c6130b4 --- /dev/null +++ b/luaotfload-legacy-attributes.lua @@ -0,0 +1,27 @@ +----------------------------------------------------------------------- +--         FILE:  otfl-luat-att.lua +--        USAGE:  with old luaotfload +--  DESCRIPTION:  setting attributes abide luatexbase rules +-- REQUIREMENTS:  some old luatex +--       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com> +--      CREATED:  2013-05-10 20:37:19+0200 +----------------------------------------------------------------------- +-- + +if not modules then modules = { } end modules ['otfl-luat-att'] = { +    version   = math.pi/42, +    comment   = "companion to luaotfload.lua", +    author    = "Philipp Gesang", +    copyright = "Luaotfload Development Team", +    license   = "GNU GPL v2" +} + +function attributes.private(name) +    local attr   = "otfl@" .. name +    local number = luatexbase.attributes[attr] +    if not number then +        number = luatexbase.new_attribute(attr) +    end +    return number +end + diff --git a/luaotfload-legacy-database.lua b/luaotfload-legacy-database.lua new file mode 100644 index 0000000..4af05f5 --- /dev/null +++ b/luaotfload-legacy-database.lua @@ -0,0 +1,724 @@ +if not modules then modules = { } end modules ['font-nms'] = { +    version   = "old", +    comment   = "companion to luaotfload.lua", +    author    = "Khaled Hosny and Elie Roux", +    copyright = "Luaotfload Development Team", +    license   = "GNU GPL v2" +} + +fonts                = fonts       or { } +fonts.names          = fonts.names or { } + +local names          = fonts.names +local names_dir      = "luatex-cache/generic/names" +names.version        = "old" -- not the same as in context +names.data           = nil +names.path           = { +    basename  = "otfl-names.lua", --- different from current +    localdir  = file.join(kpse.expand_var("$TEXMFVAR"),    names_dir), +    systemdir = file.join(kpse.expand_var("$TEXMFSYSVAR"), names_dir), +} + + +local splitpath, expandpath = file.split_path, kpse.expand_path +local glob, basename        = dir.glob, file.basename +local upper, lower, format  = string.upper, string.lower, string.format +local gsub, match, rpadd    = string.gsub, string.match, string.rpadd +local gmatch, sub, find     = string.gmatch, string.sub, string.find +local utfgsub               = unicode.utf8.gsub + +local trace_short    = false --tracing adapted to rebuilding of the database inside a document +local trace_search   = false --trackers.register("names.search",   function(v) trace_search   = v end) +local trace_loading  = false --trackers.register("names.loading",  function(v) trace_loading  = v end) + +local function sanitize(str) +    if str then +        return utfgsub(lower(str), "[^%a%d]", "") +    else +        return str -- nil +    end +end + +local function fontnames_init() +    return { +        mappings  = { }, +        status    = { }, +        version   = names.version, +    } +end + +local function load_names() +    local localpath  = file.join(names.path.localdir, names.path.basename) +    local systempath = file.join(names.path.systemdir, names.path.basename) +    local kpsefound  = kpse.find_file(names.path.basename) +    local foundname +    local data +    if kpsefound and file.isreadable(kpsefound) then +        data = dofile(kpsefound) +	foundname = kpsefound +    elseif file.isreadable(localpath)  then +        data = dofile(localpath) +	foundname = localpath +    elseif file.isreadable(systempath) then +        data = dofile(systempath) +	foundname = systempath +    end +    if data then +        logs.info("Font names database loaded: " .. foundname) +    else +        logs.info([[Font names database not found, generating new one. +             This can take several minutes; please be patient.]]) +        data = names.update(fontnames_init()) +        names.save(data) +    end +    return data +end + +local synonyms = { +    regular    = { "normal", "roman", "plain", "book", "medium" }, +    -- boldregular was for old versions of Linux Libertine, is it still useful? +    -- semibold is in new versions of Linux Libertine, but there is also a bold, +    -- not sure it's useful here... +    bold       = { "demi", "demibold", "semibold", "boldregular" }, +    italic     = { "regularitalic", "normalitalic", "oblique", "slanted" }, +    bolditalic = { "boldoblique", "boldslanted", "demiitalic", "demioblique", "demislanted", "demibolditalic", "semibolditalic" }, +} + +local loaded   = false +local reloaded = false + +function names.resolve(specification) +    local name  = sanitize(specification.name) +    local style = sanitize(specification.style) or "regular" + +    local size +    if specification.optsize then +        size = tonumber(specification.optsize) +    elseif specification.size then +        size = specification.size / 65536 +    end + + +    if not loaded then +        names.data = names.load() +        loaded     = true +    end + +    local data = names.data +    if type(data) == "table" and data.version == names.version then +        if data.mappings then +            local found = { } +            for _,face in next, data.mappings do +                local family, subfamily, fullname, psname +                local optsize, dsnsize, maxsize, minsize + +                if face.names then +                    family    = sanitize(face.names.family) +                    subfamily = sanitize(face.names.subfamily) +                    fullname  = sanitize(face.names.fullname) +                    psname    = sanitize(face.names.psname) +                end +                local fontname  = sanitize(face.fontname) +                local pfullname = sanitize(face.fullname) +                if #face.size > 0 then +                    optsize = face.size +                    dsnsize = optsize[1] and optsize[1] / 10 +                    -- can be nil +                    maxsize = optsize[2] and optsize[2] / 10 or dsnsize +                    minsize = optsize[3] and optsize[3] / 10 or dsnsize +                end +                if name == family then +                    if subfamily == style then +                        if optsize then +                            if dsnsize == size +                            or (size > minsize and size <= maxsize) then +                                found[1] = face +                                break +                            else +                                found[#found+1] = face +                            end +                        else +                            found[1] = face +                            break +                        end +                    elseif synonyms[style] and +                           table.contains(synonyms[style], subfamily) then +                        if optsize then +                            if dsnsize == size +                            or (size > minsize and size <= maxsize) then +                                found[1] = face +                                break +                            else +                                found[#found+1] = face +                            end +                        else +                            found[1] = face +                            break +                        end +                    elseif subfamily == "regular" or +                           table.contains(synonyms.regular, subfamily) then +                        found.fallback = face +                    elseif name == fullname +                    or name == pfullname +                    or name == fontname +                    or name == psname then +                        if optsize then +                            if dsnsize == size +                            or (size > minsize and size <= maxsize) then +                                found[1] = face +                                break +                            else +                                found[#found+1] = face +                            end +                        else +                            found[1] = face +                            break +                        end +                    end +                else +                    if name == fullname +                    or name == pfullname +                    or name == fontname +                    or name == psname then +                        if optsize then +                            if dsnsize == size +                            or (size > minsize and size <= maxsize) then +                                found[1] = face +                                break +                            else +                                found[#found+1] = face +                            end +                        else +                            found[1] = face +                            break +                        end +                    end +                end +            end +            if #found == 1 then +                if kpse.lookup(found[1].filename[1]) then +                    logs.report("load font", +                                "font family='%s', subfamily='%s' found: %s", +                                name, style, found[1].filename[1]) +                    return found[1].filename[1], found[1].filename[2] +                elseif lfs.isfile(found[1].found_at) then +                    logs.report("load font", +                                "font family='%s', subfamily='%s' found: %s", +                                name, style, found[1].found_at) +                    return found[1].found_at, found[1].filename[2] +                end +            elseif #found > 1 then +                -- we found matching font(s) but not in the requested optical +                -- sizes, so we loop through the matches to find the one with +                -- least difference from the requested size. +                local closest +                local least = math.huge -- initial value is infinity +                for i,face in next, found do +                    local dsnsize    = face.size[1]/10 +                    local difference = math.abs(dsnsize-size) +                    if difference < least then +                        closest = face +                        least   = difference +                    end +                end +                if kpse.lookup(closest.filename[1]) then +                    logs.report("load font", +                                "font family='%s', subfamily='%s' found: %s", +                                name, style, closest.filename[1]) +                    return closest.filename[1], closest.filename[2] +                elseif lfs.isfile(closest.found_at) then +                    logs.report("load font", +                                "font family='%s', subfamily='%s' found: %s", +                                name, style, closest.found_at) +                    return closest.found_at, closest.filename[2] +                end +            elseif found.fallback then +                return found.fallback.filename[1], found.fallback.filename[2] +            end +            -- no font found so far +            if not reloaded then +                -- try reloading the database +                names.data = names.update(names.data) +                names.save(names.data) +                reloaded   = true +                return names.resolve(specification) +            else +                -- else, fallback to filename +                return specification.name, false +            end +        end +    else +        if not reloaded then +            names.data = names.update() +            names.save(names.data) +            reloaded   = true +            return names.resolve(specification) +        else +            return specification.name, false +        end +    end +end + +names.resolvespec = names.resolve + +function names.set_log_level(level) +    if level == 2 then +        trace_loading = true +    elseif level >= 3 then +        trace_loading = true +        trace_search = true +    end +end + +local lastislog = 0 + +local function log(fmt, ...) +    lastislog = 1 +    texio.write_nl(format("luaotfload | %s", format(fmt,...))) +    io.flush() +end + +logs        = logs or { } +logs.report = logs.report or log +logs.info   = logs.info or log + +local function font_fullinfo(filename, subfont, texmf) +    local found_at = filename +    local t = { } +    local f = fontloader.open(filename, subfont) +    if not f then +	    if trace_loading then +        	logs.report("error: failed to open %s", filename) +	    end +        return +    end +    local m = fontloader.to_table(f) +    fontloader.close(f) +    collectgarbage('collect') +    -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size +    if m.fontstyle_name then +        for _,v in next, m.fontstyle_name do +            if v.lang == 1033 then +                t.fontstyle_name = v.name +            end +        end +    end +    if m.names then +        for _,v in next, m.names do +            if v.lang == "English (US)" then +                t.names = { +                    -- see +                    -- http://developer.apple.com/textfonts/ +                    -- TTRefMan/RM06/Chap6name.html +                    fullname = v.names.compatfull     or v.names.fullname, +                    family   = v.names.preffamilyname or v.names.family, +                    subfamily= t.fontstyle_name       or v.names.prefmodifiers  or v.names.subfamily, +                    psname   = v.names.postscriptname +                } +            end +        end +    else +        -- no names table, propably a broken font +        if trace_loading then +            logs.report("broken font rejected: %s", basefile) +        end +        return +    end +    t.fontname    = m.fontname +    t.fullname    = m.fullname +    t.familyname  = m.familyname +    t.filename    = { texmf and basename(filename) or filename, subfont } +    t.weight      = m.pfminfo.weight +    t.width       = m.pfminfo.width +    t.slant       = m.italicangle +    -- don't waste the space with zero values +    t.size = { +        m.design_size         ~= 0 and m.design_size         or nil, +        m.design_range_top    ~= 0 and m.design_range_top    or nil, +        m.design_range_bottom ~= 0 and m.design_range_bottom or nil, +    } +    -- rather, waste space on paths +    t.found_at = found_at +    return t +end + +local function load_font(filename, fontnames, newfontnames, texmf) +    local newmappings = newfontnames.mappings +    local newstatus   = newfontnames.status +    local mappings    = fontnames.mappings +    local status      = fontnames.status +    local basefile    = texmf and basename(filename) or filename +    if filename then +        if table.contains(names.blacklist, filename) or +           table.contains(names.blacklist, basename(filename)) then +            if trace_search then +                logs.report("ignoring font '%s'", filename) +            end +            return +        end +        local timestamp, db_timestamp +        db_timestamp        = status[basefile] and status[basefile].timestamp +        timestamp           = lfs.attributes(filename, "modification") + +        local index_status = newstatus[basefile] or (not texmf and newstatus[basename(filename)]) +        if index_status and index_status.timestamp == timestamp then +            -- already indexed this run +            return +        end + +        newstatus[basefile] = newstatus[basefile] or { } +        newstatus[basefile].timestamp = timestamp +        newstatus[basefile].index     = newstatus[basefile].index or { } + +        if db_timestamp == timestamp and not newstatus[basefile].index[1] then +            for _,v in next, status[basefile].index do +                local index = #newstatus[basefile].index +                newmappings[#newmappings+1]        = mappings[v] +                newstatus[basefile].index[index+1] = #newmappings +            end +            if trace_loading then +                logs.report("font already indexed: %s", basefile) +            end +            return +        end +        local info = fontloader.info(filename) +        if info then +            if type(info) == "table" and #info > 1 then +                for i in next, info do +                    local fullinfo = font_fullinfo(filename, i-1, texmf) +                    if not fullinfo then +                        return +                    end +                    local index = newstatus[basefile].index[i] +                    if not index then +                        index = #newmappings+1 +                    end +                    newmappings[index]           = fullinfo +                    newstatus[basefile].index[i] = index +                end +            else +                local fullinfo = font_fullinfo(filename, false, texmf) +                if not fullinfo then +                    return +                end +                local index = newstatus[basefile].index[1] +                if not index then +                    index = #newmappings+1 +                end +                newmappings[index]           = fullinfo +                newstatus[basefile].index[1] = index +            end +        else +            if trace_loading then +               logs.report("failed to load %s", basefile) +            end +        end +    end +end + +local function path_normalize(path) +    --[[ +        path normalization: +        - a\b\c  -> a/b/c +        - a/../b -> b +        - /cygdrive/a/b -> a:/b +        - reading symlinks under non-Win32 +        - using kpse.readable_file on Win32 +    ]] +    if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then +        path = path:gsub('\\', '/') +        path = path:lower() +        path = path:gsub('^/cygdrive/(%a)/', '%1:/') +    end +    if os.type ~= "windows" and os.type ~= "msdos" then +        local dest = lfs.readlink(path) +        if dest then +            if kpse.readable_file(dest) then +                path = dest +            elseif kpse.readable_file(file.join(file.dirname(path), dest)) then +                path = file.join(file.dirname(path), dest) +            else +                -- broken symlink? +            end +        end +    end +    path = file.collapse_path(path) +    return path +end + +fonts.path_normalize = path_normalize + +names.blacklist = { } + +local function read_blacklist() +    local files = { +        kpse.lookup("otfl-blacklist.cnf", {all=true, format="tex"}) +    } +    local blacklist = names.blacklist + +    if files and type(files) == "table" then +        for _,v in next, files do +            for line in io.lines(v) do +                line = line:strip() -- to get rid of lines like " % foo" +                if line:find("^%%") or line:is_empty() then +                    -- comment or empty line +                else +                    line = line:split("%")[1] +                    line = line:strip() +                    if trace_search then +                        logs.report("blacklisted file: %s", line) +                    end +                    blacklist[#blacklist+1] = line +                end +            end +        end +    end +end + +local font_extensions = { "otf", "ttf", "ttc", "dfont" } + +local function scan_dir(dirname, fontnames, newfontnames, texmf) +    --[[ +    This function scans a directory and populates the list of fonts +    with all the fonts it finds. +    - dirname is the name of the directory to scan +    - names is the font database to fill +    - texmf is a boolean saying if we are scanning a texmf directory +    ]] +    local list, found = { }, { } +    local nbfound = 0 +    if trace_search then +        logs.report("scanning '%s'", dirname) +    end +    for _,i in next, font_extensions do +        for _,ext in next, { i, upper(i) } do +            found = glob(format("%s/**.%s$", dirname, ext)) +            -- note that glob fails silently on broken symlinks, which happens +            -- sometimes in TeX Live. +            if trace_search then +                logs.report("%s '%s' fonts found", #found, ext) +            end +            nbfound = nbfound + #found +            table.append(list, found) +        end +    end +    if trace_search then +        logs.report("%d fonts found in '%s'", nbfound, dirname) +    end + +    for _,file in next, list do +        file = path_normalize(file) +        if trace_loading then +            logs.report("loading font: %s", file) +        end +        load_font(file, fontnames, newfontnames, texmf) +    end +end + +local function scan_texmf_fonts(fontnames, newfontnames) +    --[[ +    This function scans all fonts in the texmf tree, through kpathsea +    variables OPENTYPEFONTS and TTFONTS of texmf.cnf +    ]] +    if expandpath("$OSFONTDIR"):is_empty() then +        logs.info("Scanning TEXMF fonts...") +    else +        logs.info("Scanning TEXMF and OS fonts...") +    end +    local fontdirs = expandpath("$OPENTYPEFONTS"):gsub("^%.", "") +    fontdirs = fontdirs .. expandpath("$TTFONTS"):gsub("^%.", "") +    if not fontdirs:is_empty() then +        for _,d in next, splitpath(fontdirs) do +            scan_dir(d, fontnames, newfontnames, true) +        end +    end +end + +--[[ +  For the OS fonts, there are several options: +   - if OSFONTDIR is set (which is the case under windows by default but +     not on the other OSs), it scans it at the same time as the texmf tree, +     in the scan_texmf_fonts. +   - in addition: +     - under Windows and Mac OSX, we take a look at some hardcoded directories +     - under Unix, we read /etc/fonts/fonts.conf and read the directories in it + +  This means that if you have fonts in fancy directories, you need to set them +  in OSFONTDIR if they cannot be found by fontconfig. +]] + +local function read_fonts_conf(path, results, passed_paths) +    --[[ +    This function parses /etc/fonts/fonts.conf and returns all the dir it finds. +    The code is minimal, please report any error it may generate. +    ]] +    local f = io.open(path) +    table.insert(passed_paths, path) +    if not f then +        logs.info("Warning: unable to read "..path.. ", skipping...") +        return results +    end +    local incomments = false +    for line in f:lines() do +        while line and line ~= "" do +            -- spaghetti code... hmmm... +            if incomments then +                local tmp = find(line, '-->') +                if tmp then +                    incomments = false +                    line = sub(line, tmp+3) +                else +                    line = nil +                end +            else +                local tmp = find(line, '<!--') +                local newline = line +                if tmp then +                    -- for the analysis, we take everything that is before the +                    -- comment sign +                    newline = sub(line, 1, tmp-1) +                    -- and we loop again with the comment +                    incomments = true +                    line = sub(line, tmp+4) +                else +                    -- if there is no comment start, the block after that will +                    -- end the analysis, we exit the while loop +                    line = nil +                end +                for dir in gmatch(newline, '<dir>([^<]+)</dir>') do +                    -- now we need to replace ~ by kpse.expand_path('~') +                    if sub(dir, 1, 1) == '~' then +                        dir = file.join(kpse.expand_path('~'), sub(dir, 2)) +                    end +                    -- we exclude paths with texmf in them, as they should be +                    -- found anyway +                    if not find(dir, 'texmf') then +                        results[#results+1] = dir +                    end +                end +                for include in gmatch(newline, '<include[^<]*>([^<]+)</include>') do +                    -- include here can be four things: a directory or a file, +                    -- in absolute or relative path. +                    if sub(include, 1, 1) == '~' then +                        include = file.join(kpse.expand_path('~'),sub(include, 2)) +                        -- First if the path is relative, we make it absolute: +                    elseif not lfs.isfile(include) and not lfs.isdir(include) then +                        include = file.join(file.dirname(path), include) +                    end +                    if lfs.isfile(include) and kpse.readable_file(include) and not table.contains(passed_paths, include) then +                        -- we exclude path with texmf in them, as they should +                        -- be found otherwise +                        read_fonts_conf(include, results, passed_paths) +                    elseif lfs.isdir(include) then +                        for _,f in next, glob(file.join(include, "*.conf")) do +                            if not table.contains(passed_paths, f) then +                                read_fonts_conf(f, results, passed_paths) +                            end +                        end +                    end +                end +            end +        end +    end +    f:close() +    return results +end + +-- for testing purpose +names.read_fonts_conf = read_fonts_conf + +local function get_os_dirs() +    if os.name == 'macosx' then +        return { +            file.join(kpse.expand_path('~'), "Library/Fonts"), +            "/Library/Fonts", +            "/System/Library/Fonts", +            "/Network/Library/Fonts", +        } +    elseif os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then +        local windir = os.getenv("WINDIR") +        return { file.join(windir, 'Fonts') } +    else +        return read_fonts_conf("/etc/fonts/fonts.conf", {}, {}) +    end +end + +local function scan_os_fonts(fontnames, newfontnames) +    --[[ +    This function scans the OS fonts through +      - fontcache for Unix (reads the fonts.conf file and scans the directories) +      - a static set of directories for Windows and MacOSX +    ]] +    logs.info("Scanning OS fonts...") +    if trace_search then +        logs.info("Searching in static system directories...") +    end +    for _,d in next, get_os_dirs() do +        scan_dir(d, fontnames, newfontnames, false) +    end +end + +local function update_names(fontnames, force) +    --[[ +    The main function, scans everything +    - fontnames is the final table to return +    - force is whether we rebuild it from scratch or not +    ]] +    logs.info("Updating the font names database:") + +    if force then +        fontnames = fontnames_init() +    else +        if not fontnames then +            fontnames = names.load() +        end +        if fontnames.version ~= names.version then +            fontnames = fontnames_init() +            if trace_search then +                logs.report("No font names database or old one found; " +                          .."generating new one") +            end +        end +    end +    local newfontnames = fontnames_init() +    read_blacklist() +    scan_texmf_fonts(fontnames, newfontnames) +    scan_os_fonts(fontnames, newfontnames) +    return newfontnames +end + +local function save_names(fontnames) +    local savepath  = names.path.localdir +    if not lfs.isdir(savepath) then +        dir.mkdirs(savepath) +    end +    savepath = file.join(savepath, names.path.basename) +    if file.iswritable(savepath) then +        table.tofile(savepath, fontnames, true) +        logs.info("Font names database saved: %s \n", savepath) +        return savepath +    else +        logs.info("Failed to save names database\n") +        return nil +    end +end + +local function scan_external_dir(dir) +    local old_names, new_names +    if loaded then +        old_names = names.data +    else +        old_names = names.load() +        loaded    = true +    end +    new_names = table.copy(old_names) +    scan_dir(dir, old_names, new_names) +    names.data = new_names +end + +names.scan   = scan_external_dir +names.load   = load_names +names.update = update_names +names.save   = save_names + +-- vim:ft=lua:sw=4:ts=4:expandtab diff --git a/luaotfload-legacy-merged.lua b/luaotfload-legacy-merged.lua new file mode 100644 index 0000000..9bec298 --- /dev/null +++ b/luaotfload-legacy-merged.lua @@ -0,0 +1,8157 @@ +-- merged file : luaotfload-legacy-merged.lua +-- parent file : luaotfload-legacy.lua +-- merge date  : Fri May 10 20:57:35 2013 + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luat-dum']={ +  version=1.100, +  comment="companion to luatex-*.tex", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local dummyfunction=function() end +statistics={ +  register=dummyfunction, +  starttiming=dummyfunction, +  stoptiming=dummyfunction, +} +directives={ +  register=dummyfunction, +  enable=dummyfunction, +  disable=dummyfunction, +} +trackers={ +  register=dummyfunction, +  enable=dummyfunction, +  disable=dummyfunction, +} +experiments={ +  register=dummyfunction, +  enable=dummyfunction, +  disable=dummyfunction, +} +storage={ +  register=dummyfunction, +  shared={}, +} +logs={ +  report=dummyfunction, +  simple=dummyfunction, +} +tasks={ +  new=dummyfunction, +  actions=dummyfunction, +  appendaction=dummyfunction, +  prependaction=dummyfunction, +} +callbacks={ +  register=function(n,f) return callback.register(n,f) end, +} +texconfig.kpse_init=true +resolvers=resolvers or {}  +local remapper={ +  otf="opentype fonts", +  ttf="truetype fonts", +  ttc="truetype fonts", +  dfont="truetype fonts", +  cid="cid maps", +  fea="font feature files", +} +function resolvers.find_file(name,kind) +  name=string.gsub(name,"\\","/") +  kind=string.lower(kind) +  return kpse.find_file(name,(kind and kind~="" and (remapper[kind] or kind)) or file.extname(name,"tex")) +end +function resolvers.findbinfile(name,kind) +  if not kind or kind=="" then +    kind=file.extname(name)  +  end +  return resolvers.find_file(name,(kind and remapper[kind]) or kind) +end +caches={} +local writable,readables=nil,{} +if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then +  caches.namespace='generic' +end +do +  local cachepaths +  if kpse.expand_var('$TEXMFCACHE')~='$TEXMFCACHE' then +    cachepaths=kpse.expand_var('$TEXMFCACHE') +  elseif kpse.expand_var('$TEXMFVAR')~='$TEXMFVAR' then +    cachepaths=kpse.expand_var('$TEXMFVAR') +  end +  if not cachepaths then +    cachepaths="." +  end +  cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":") +  for i=1,#cachepaths do +    local done +    writable=file.join(cachepaths[i],"luatex-cache") +    writable=file.join(writable,caches.namespace) +    writable,done=dir.mkdirs(writable) +    if done then +      break +    end +  end +  for i=1,#cachepaths do +    if file.isreadable(cachepaths[i]) then +      readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace) +    end +  end +  if not writable then +    texio.write_nl("quiting: fix your writable cache path\n") +    os.exit() +  elseif #readables==0 then +    texio.write_nl("quiting: fix your readable cache path\n") +    os.exit() +  elseif #readables==1 and readables[1]==writable then +    texio.write(string.format("(using cache: %s)",writable)) +  else +    texio.write(string.format("(using write cache: %s)",writable)) +    texio.write(string.format("(using read cache: %s)",table.concat(readables," "))) +  end +end +function caches.getwritablepath(category,subcategory) +  local path=file.join(writable,category) +  lfs.mkdir(path) +  path=file.join(path,subcategory) +  lfs.mkdir(path) +  return path +end +function caches.getreadablepaths(category,subcategory) +  local t={} +  for i=1,#readables do +    t[i]=file.join(readables[i],category,subcategory) +  end +  return t +end +local function makefullname(path,name) +  if path and path~="" then +    name="temp-"..name  +    return file.addsuffix(file.join(path,name),"lua") +  end +end +function caches.iswritable(path,name) +  local fullname=makefullname(path,name) +  return fullname and file.iswritable(fullname) +end +function caches.loaddata(paths,name) +  for i=1,#paths do +    local fullname=makefullname(paths[i],name) +    if fullname then +      texio.write(string.format("(load: %s)",fullname)) +      local data=loadfile(fullname) +      return data and data() +    end +  end +end +function caches.savedata(path,name,data) +  local fullname=makefullname(path,name) +  if fullname then +    texio.write(string.format("(save: %s)",fullname)) +    table.tofile(fullname,data,'return',false,true,false) +  end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luat-ovr']={ +  version=1.001, +  comment="companion to luatex-*.tex", +  author="Khaled Hosny and Elie Roux", +  copyright="Luaotfload Development Team", +  license="GNU GPL v2" +} +local write_nl,format,name=texio.write_nl,string.format,"luaotfload" +local dummyfunction=function() end +callbacks={ +  register=dummyfunction, +} +function logs.report(category,fmt,...) +  if fmt then +    write_nl('log',format("%s | %s: %s",name,category,format(fmt,...))) +  elseif category then +    write_nl('log',format("%s | %s",name,category)) +  else +    write_nl('log',format("%s |",name)) +  end +end +function logs.info(category,fmt,...) +  if fmt then +    write_nl(format("%s | %s: %s",name,category,format(fmt,...))) +  elseif category then +    write_nl(format("%s | %s",name,category)) +  else +    write_nl(format("%s |",name)) +  end +  io.flush() +end +function logs.simple(fmt,...) +  if fmt then +    write_nl('log',format("%s | %s",name,format(fmt,...))) +  else +    write_nl('log',format("%s |",name)) +  end +end +tex.attribute[0]=0 +tex.ctxcatcodes=luatexbase.catcodetables.latex + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['data-con']={ +  version=1.100, +  comment="companion to luat-lib.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local format,lower,gsub=string.format,string.lower,string.gsub +local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end) +local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end) +local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end) +containers=containers or {} +containers.usecache=true +local function report(container,tag,name) +  if trace_cache or trace_containers then +    logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid') +  end +end +local allocated={} +local mt={ +  __index=function(t,k) +    if k=="writable" then +      local writable=caches.getwritablepath(t.category,t.subcategory) or { "." } +      t.writable=writable +      return writable +    elseif k=="readables" then +      local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." } +      t.readables=readables +      return readables +    end +  end +} +function containers.define(category,subcategory,version,enabled) +  if category and subcategory then +    local c=allocated[category] +    if not c then +      c={} +      allocated[category]=c +    end +    local s=c[subcategory] +    if not s then +      s={ +        category=category, +        subcategory=subcategory, +        storage={}, +        enabled=enabled, +        version=version or math.pi, +        trace=false, +      } +      setmetatable(s,mt) +      c[subcategory]=s +    end +    return s +  end +end +function containers.is_usable(container,name) +  return container.enabled and caches and caches.iswritable(container.writable,name) +end +function containers.is_valid(container,name) +  if name and name~="" then +    local storage=container.storage[name] +    return storage and storage.cache_version==container.version +  else +    return false +  end +end +function containers.read(container,name) +  local storage=container.storage +  local stored=storage[name] +  if not stored and container.enabled and caches and containers.usecache then +    stored=caches.loaddata(container.readables,name) +    if stored and stored.cache_version==container.version then +      report(container,"loaded",name) +    else +      stored=nil +    end +    storage[name]=stored +  elseif stored then +    report(container,"reusing",name) +  end +  return stored +end +function containers.write(container,name,data) +  if data then +    data.cache_version=container.version +    if container.enabled and caches then +      local unique,shared=data.unique,data.shared +      data.unique,data.shared=nil,nil +      caches.savedata(container.writable,name,data) +      report(container,"saved",name) +      data.unique,data.shared=unique,shared +    end +    report(container,"stored",name) +    container.storage[name]=data +  end +  return data +end +function containers.content(container,name) +  return container.storage[name] +end +function containers.cleanname(name) +  return (gsub(lower(name),"[^%w%d]+","-")) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-ini']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local utf=unicode.utf8 +local format,serialize=string.format,table.serialize +local write_nl=texio.write_nl +local lower=string.lower +if not fontloader then fontloader=fontforge end +fontloader.totable=fontloader.to_table +fonts=fonts   or {} +fonts.ids=fonts.ids or {} fonts.identifiers=fonts.ids  +fonts.chr=fonts.chr or {} fonts.characters=fonts.chr  +fonts.qua=fonts.qua or {} fonts.quads=fonts.qua  +fonts.tfm=fonts.tfm or {} +fonts.mode='base' +fonts.private=0xF0000  +fonts.verbose=false  +fonts.ids[0]={  +  characters={}, +  descriptions={}, +  name="nullfont", +} +fonts.chr[0]={} +fonts.methods=fonts.methods or { +  base={ tfm={},afm={},otf={},vtf={},fix={} }, +  node={ tfm={},afm={},otf={},vtf={},fix={} }, +} +fonts.initializers=fonts.initializers or { +  base={ tfm={},afm={},otf={},vtf={},fix={} }, +  node={ tfm={},afm={},otf={},vtf={},fix={} } +} +fonts.triggers=fonts.triggers or { +  'mode', +  'language', +  'script', +  'strategy', +} +fonts.processors=fonts.processors or {} +fonts.manipulators=fonts.manipulators or {} +fonts.define=fonts.define         or {} +fonts.define.specify=fonts.define.specify     or {} +fonts.define.specify.synonyms=fonts.define.specify.synonyms or {} +if not fonts.color then +  fonts.color={ +    set=function() end, +    reset=function() end, +  } +end +fonts.formats={} +function fonts.fontformat(filename,default) +  local extname=lower(file.extname(filename)) +  local format=fonts.formats[extname] +  if format then +    return format +  else +    logs.report("fonts define","unable to determine font format for '%s'",filename) +    return default +  end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['node-dum']={ +  version=1.001, +  comment="companion to luatex-*.tex", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +nodes=nodes   or {} +fonts=fonts   or {} +attributes=attributes or {} +local traverse_id=node.traverse_id +local free_node=node.free +local remove_node=node.remove +local new_node=node.new +local glyph=node.id('glyph') +local fontdata=fonts.ids or {} +function nodes.simple_font_handler(head) +  head=nodes.process_characters(head) +  nodes.inject_kerns(head) +  nodes.protect_glyphs(head) +  head=node.ligaturing(head) +  head=node.kerning(head) +  return head +end +if tex.attribute[0]~=0 then +  texio.write_nl("log","!") +  texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") +  texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") +  texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.") +  texio.write_nl("log","!") +  tex.attribute[0]=0  +end +nodes.protect_glyphs=node.protect_glyphs +nodes.unprotect_glyphs=node.unprotect_glyphs +function nodes.process_characters(head) +  local usedfonts,done,prevfont={},false,nil +  for n in traverse_id(glyph,head) do +    local font=n.font +    if font~=prevfont then +      prevfont=font +      local used=usedfonts[font] +      if not used then +        local tfmdata=fontdata[font] +        if tfmdata then +          local shared=tfmdata.shared  +          if shared then +            local processors=shared.processes +            if processors and #processors>0 then +              usedfonts[font]=processors +              done=true +            end +          end +        end +      end +    end +  end +  if done then +    for font,processors in next,usedfonts do +      for i=1,#processors do +        local h,d=processors[i](head,font,0) +        head,done=h or head,done or d +      end +    end +  end +  return head,true +end +function nodes.kern(k) +  local n=new_node("kern",1) +  n.kern=k +  return n +end +function nodes.remove(head,current,free_too) +  local t=current +  head,current=remove_node(head,current) +  if t then +    if free_too then +      free_node(t) +      t=nil +    else +      t.next,t.prev=nil,nil +    end +  end +  return head,current,t +end +function nodes.delete(head,current) +  return nodes.remove(head,current,true) +end +nodes.before=node.insert_before +nodes.after=node.insert_after +attributes.unsetvalue=-0x7FFFFFFF +local numbers,last={},127 +function attributes.private(name) +  local number=numbers[name] +  if not number then +    if last<255 then +      last=last+1 +    end +    number=last +    numbers[name]=number +  end +  return number +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['node-inj']={ +  version=1.001, +  comment="companion to node-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local next=next +local trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end) +fonts=fonts   or {} +fonts.tfm=fonts.tfm or {} +fonts.ids=fonts.ids or {} +local fontdata=fonts.ids +local glyph=node.id('glyph') +local kern=node.id('kern') +local traverse_id=node.traverse_id +local unset_attribute=node.unset_attribute +local has_attribute=node.has_attribute +local set_attribute=node.set_attribute +local insert_node_before=node.insert_before +local insert_node_after=node.insert_after +local newkern=nodes.kern +local markbase=attributes.private('markbase') +local markmark=attributes.private('markmark') +local markdone=attributes.private('markdone') +local cursbase=attributes.private('cursbase') +local curscurs=attributes.private('curscurs') +local cursdone=attributes.private('cursdone') +local kernpair=attributes.private('kernpair') +local cursives={} +local marks={} +local kerns={} +function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) +  local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2]) +  local ws,wn=tfmstart.width,tfmnext.width +  local bound=#cursives+1 +  set_attribute(start,cursbase,bound) +  set_attribute(nxt,curscurs,bound) +  cursives[bound]={ rlmode,dx,dy,ws,wn } +  return dx,dy,bound +end +function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr) +  local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] +  if x~=0 or w~=0 or y~=0 or h~=0 then +    local bound=has_attribute(current,kernpair) +    if bound then +      local kb=kerns[bound] +      kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h +    else +      bound=#kerns+1 +      set_attribute(current,kernpair,bound) +      kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width } +    end +    return x,y,w,h,bound +  end +  return x,y,w,h  +end +function nodes.set_kern(current,factor,rlmode,x,tfmchr) +  local dx=factor*x +  if dx~=0 then +    local bound=#kerns+1 +    set_attribute(current,kernpair,bound) +    kerns[bound]={ rlmode,dx } +    return dx,bound +  else +    return 0,0 +  end +end +function nodes.set_mark(start,base,factor,rlmode,ba,ma,index)  +  local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) +  local bound=has_attribute(base,markbase) +  if bound then +    local mb=marks[bound] +    if mb then +      if not index then index=#mb+1 end +      mb[index]={ dx,dy } +      set_attribute(start,markmark,bound) +      set_attribute(start,markdone,index) +      return dx,dy,bound +    else +      logs.report("nodes mark","possible problem, U+%04X is base without data (id: %s)",base.char,bound) +    end +  end +  index=index or 1 +  bound=#marks+1 +  set_attribute(base,markbase,bound) +  set_attribute(start,markmark,bound) +  set_attribute(start,markdone,index) +  marks[bound]={ [index]={ dx,dy,rlmode } } +  return dx,dy,bound +end +function nodes.trace_injection(head) +  local function dir(n) +    return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset") +  end +  local function report(...) +    logs.report("nodes finisher",...) +  end +  report("begin run") +  for n in traverse_id(glyph,head) do +    if n.subtype<256 then +      local kp=has_attribute(n,kernpair) +      local mb=has_attribute(n,markbase) +      local mm=has_attribute(n,markmark) +      local md=has_attribute(n,markdone) +      local cb=has_attribute(n,cursbase) +      local cc=has_attribute(n,curscurs) +      report("char U+%05X, font=%s",n.char,n.font) +      if kp then +        local k=kerns[kp] +        if k[3] then +          report("  pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") +        else +          report("  kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") +        end +      end +      if mb then +        report("  markbase: bound=%s",mb) +      end +      if mm then +        local m=marks[mm] +        if mb then +          local m=m[mb] +          if m then +            report("  markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") +          else +            report("  markmark: bound=%s, missing index",mm) +          end +        else +          m=m[1] +          report("  markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") +        end +      end +      if cb then +        report("  cursbase: bound=%s",cb) +      end +      if cc then +        local c=cursives[cc] +        report("  curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") +      end +    end +  end +  report("end run") +end +function nodes.inject_kerns(head,where,keep) +  local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns) +  if has_marks or has_cursives then +    if trace_injections then +      nodes.trace_injection(head) +    end +    local done,ky,rl,valid,cx,wx,mk=false,{},{},{},{},{},{} +    if has_kerns then  +      local nf,tm=nil,nil +      for n in traverse_id(glyph,head) do +        if n.subtype<256 then +          valid[#valid+1]=n +          if n.font~=nf then +            nf=n.font +            tm=fontdata[nf].marks +          end +          mk[n]=tm[n.char] +          local k=has_attribute(n,kernpair) +          if k then +            local kk=kerns[k] +            if kk then +              local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0 +              local dy=y-h +              if dy~=0 then +                ky[n]=dy +              end +              if w~=0 or x~=0 then +                wx[n]=kk +              end +              rl[n]=kk[1]  +            end +          end +        end +      end +    else +      local nf,tm=nil,nil +      for n in traverse_id(glyph,head) do +        if n.subtype<256 then +          valid[#valid+1]=n +          if n.font~=nf then +            nf=n.font +            tm=fontdata[nf].marks +          end +          mk[n]=tm[n.char] +        end +      end +    end +    if #valid>0 then +      local cx={} +      if has_kerns and next(ky) then +        for n,k in next,ky do +          n.yoffset=k +        end +      end +      if has_cursives then +        local p_cursbase,p=nil,nil +        local t,d,maxt={},{},0 +        for i=1,#valid do  +          local n=valid[i] +          if not mk[n] then +            local n_cursbase=has_attribute(n,cursbase) +            if p_cursbase then +              local n_curscurs=has_attribute(n,curscurs) +              if p_cursbase==n_curscurs then +                local c=cursives[n_curscurs] +                if c then +                  local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5] +                  if rlmode>=0 then +                    dx=dx-ws +                  else +                    dx=dx+wn +                  end +                  if dx~=0 then +                    cx[n]=dx +                    rl[n]=rlmode +                  end +                    dy=-dy +                  maxt=maxt+1 +                  t[maxt]=p +                  d[maxt]=dy +                else +                  maxt=0 +                end +              end +            elseif maxt>0 then +              local ny=n.yoffset +              for i=maxt,1,-1 do +                ny=ny+d[i] +                local ti=t[i] +                ti.yoffset=ti.yoffset+ny +              end +              maxt=0 +            end +            if not n_cursbase and maxt>0 then +              local ny=n.yoffset +              for i=maxt,1,-1 do +                ny=ny+d[i] +                local ti=t[i] +                ti.yoffset=ny +              end +              maxt=0 +            end +            p_cursbase,p=n_cursbase,n +          end +        end +        if maxt>0 then +          local ny=n.yoffset +          for i=maxt,1,-1 do +            ny=ny+d[i] +            local ti=t[i] +            ti.yoffset=ny +          end +          maxt=0 +        end +        if not keep then +          cursives={} +        end +      end +      if has_marks then +        for i=1,#valid do +          local p=valid[i] +          local p_markbase=has_attribute(p,markbase) +          if p_markbase then +            local mrks=marks[p_markbase] +            for n in traverse_id(glyph,p.next) do +              local n_markmark=has_attribute(n,markmark) +              if p_markbase==n_markmark then +                local index=has_attribute(n,markdone) or 1 +                local d=mrks[index] +                if d then +                  local rlmode=d[3] +                  if rlmode and rlmode>0 then +                    local k=wx[p] +                    if k then  +                      n.xoffset=p.xoffset-(p.width-d[1])-k[2] +                    else +                      n.xoffset=p.xoffset-(p.width-d[1]) +                    end +                  else +                    local k=wx[p] +                    if k then +                      n.xoffset=p.xoffset-d[1]-k[2] +                    else +                      n.xoffset=p.xoffset-d[1] +                    end +                  end +                  if mk[p] then +                    n.yoffset=p.yoffset+d[2] +                  else +                    n.yoffset=n.yoffset+p.yoffset+d[2] +                  end +                end +              else +                break +              end +            end +          end +        end +        if not keep then +          marks={} +        end +      end +      if next(wx) then +        for n,k in next,wx do +          local rl,x,w,r2l=k[1],k[2] or 0,k[4] or 0,k[6] +          local wx=w-x +          if r2l then +            if wx~=0 then +              insert_node_before(head,n,newkern(wx)) +            end +            if x~=0 then +              insert_node_after (head,n,newkern(x)) +            end +          else +            if x~=0 then +              insert_node_before(head,n,newkern(x)) +            end +            if wx~=0 then +              insert_node_after(head,n,newkern(wx)) +            end +          end +        end +      end +      if next(cx) then +        for n,k in next,cx do +          if k~=0 then +            local rln=rl[n] +            if rln and rln<0 then +              insert_node_before(head,n,newkern(-k)) +            else +              insert_node_before(head,n,newkern(k)) +            end +          end +        end +      end +      if not keep then +        kerns={} +      end +      return head,true +    elseif not keep then +      kerns,cursives,marks={},{},{} +    end +  elseif has_kerns then +    if trace_injections then +      nodes.trace_injection(head) +    end +    for n in traverse_id(glyph,head) do +      if n.subtype<256 then +        local k=has_attribute(n,kernpair) +        if k then +          local kk=kerns[k] +          if kk then +            local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4] +            if y and y~=0 then +              n.yoffset=y  +            end +            if w then +              local r2l=kk[6] +              local wx=w-x +              if r2l then +                if wx~=0 then +                  insert_node_before(head,n,newkern(wx)) +                end +                if x~=0 then +                  insert_node_after (head,n,newkern(x)) +                end +              else +                if x~=0 then +                  insert_node_before(head,n,newkern(x)) +                end +                if wx~=0 then +                  insert_node_after(head,n,newkern(wx)) +                end +              end +            else +              if x~=0 then +                insert_node_before(head,n,newkern(x)) +              end +            end +          end +        end +      end +    end +    if not keep then +      kerns={} +    end +    return head,true +  else +  end +  return head,false +end + +end -- closure + +do -- begin closure to overcome local limits and interference + + +if not modules then modules={} end modules ['otfl-luat-att']={ +  version=math.pi/42, +  comment="companion to luaotfload.lua", +  author="Philipp Gesang", +  copyright="Luaotfload Development Team", +  license="GNU GPL v2" +} +function attributes.private(name) +  local attr="otfl@"..name +  local number=luatexbase.attributes[attr] +  if not number then +    number=luatexbase.new_attribute(attr) +  end +  return number +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-tfm']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local utf=unicode.utf8 +local next,format,match,lower,gsub=next,string.format,string.match,string.lower,string.gsub +local concat,sortedkeys,utfbyte,serialize=table.concat,table.sortedkeys,utf.byte,table.serialize +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) +fonts=fonts   or {} +fonts.tfm=fonts.tfm or {} +fonts.ids=fonts.ids or {} +local tfm=fonts.tfm +fonts.loaded=fonts.loaded  or {} +fonts.dontembed=fonts.dontembed or {} +fonts.triggers=fonts.triggers or {}  +fonts.initializers=fonts.initializers    or {} +fonts.initializers.common=fonts.initializers.common or {} +local fontdata=fonts.ids +local disc=node.id('disc') +local glyph=node.id('glyph') +local set_attribute=node.set_attribute +tfm.resolve_vf=true  +tfm.share_base_kerns=false  +tfm.mathactions={} +tfm.fontname_mode="fullpath" +tfm.enhance=tfm.enhance or function() end +fonts.formats.tfm="type1"  +function tfm.read_from_tfm(specification) +  local fname,tfmdata=specification.filename or "",nil +  if fname~="" then +    if trace_defining then +      logs.report("define font","loading tfm file %s at size %s",fname,specification.size) +    end +    tfmdata=font.read_tfm(fname,specification.size)  +    if tfmdata then +      tfmdata.descriptions=tfmdata.descriptions or {} +      if tfm.resolve_vf then +        fonts.logger.save(tfmdata,file.extname(fname),specification)  +        fname=resolvers.findbinfile(specification.name,'ovf') +        if fname and fname~="" then +          local vfdata=font.read_vf(fname,specification.size)  +          if vfdata then +            local chars=tfmdata.characters +            for k,v in next,vfdata.characters do +              chars[k].commands=v.commands +            end +            tfmdata.type='virtual' +            tfmdata.fonts=vfdata.fonts +          end +        end +      end +      tfm.enhance(tfmdata,specification) +    end +  elseif trace_defining then +    logs.report("define font","loading tfm with name %s fails",specification.name) +  end +  return tfmdata +end +local factors={ +  pt=65536.0, +  bp=65781.8, +} +function tfm.setfactor(f) +  tfm.factor=factors[f or 'pt'] or factors.pt +end +tfm.setfactor() +function tfm.scaled(scaledpoints,designsize)  +  if scaledpoints<0 then +    if designsize then +      if designsize>tfm.factor then  +        return (- scaledpoints/1000)*designsize  +      else +        return (- scaledpoints/1000)*designsize*tfm.factor +      end +    else +      return (- scaledpoints/1000)*10*tfm.factor +    end +  else +    return scaledpoints +  end +end +function tfm.get_virtual_id(tfmdata) +  if not tfmdata.fonts then +    tfmdata.type="virtual" +    tfmdata.fonts={ { id=0 } } +    return 1 +  else +    tfmdata.fonts[#tfmdata.fonts+1]={ id=0 } +    return #tfmdata.fonts +  end +end +function tfm.check_virtual_id(tfmdata,id) +  if tfmdata and tfmdata.type=="virtual" then +    if not tfmdata.fonts or #tfmdata.fonts==0 then +      tfmdata.type,tfmdata.fonts="real",nil +    else +      local vfonts=tfmdata.fonts +      for f=1,#vfonts do +        local fnt=vfonts[f] +        if fnt.id and fnt.id==0 then +          fnt.id=id +        end +      end +    end +  end +end +fonts.trace_scaling=false +local charactercache={} +function tfm.calculate_scale(tfmtable,scaledpoints,relativeid) +  if scaledpoints<0 then +    scaledpoints=(- scaledpoints/1000)*tfmtable.designsize  +  end +  local units=tfmtable.units or 1000 +  local delta=scaledpoints/units  +  return scaledpoints,delta,units +end +function tfm.do_scale(tfmtable,scaledpoints,relativeid) +  local t={}  +  local scaledpoints,delta,units=tfm.calculate_scale(tfmtable,scaledpoints,relativeid) +  t.units_per_em=units or 1000 +  local hdelta,vdelta=delta,delta +  for k,v in next,tfmtable do +    if type(v)=="table" then +    else +      t[k]=v +    end +  end +  local extend_factor=tfmtable.extend_factor or 0 +  if extend_factor~=0 and extend_factor~=1 then +    hdelta=hdelta*extend_factor +    t.extend=extend_factor*1000 +  else +    t.extend=1000 +  end +  local slant_factor=tfmtable.slant_factor or 0 +  if slant_factor~=0 then +    t.slant=slant_factor*1000 +  else +    t.slant=0 +  end +  local isvirtual=tfmtable.type=="virtual" or tfmtable.virtualized +  local hasmath=(tfmtable.math_parameters~=nil and next(tfmtable.math_parameters)~=nil) or (tfmtable.MathConstants~=nil and next(tfmtable.MathConstants)~=nil) +  local nodemode=tfmtable.mode=="node" +  local hasquality=tfmtable.auto_expand or tfmtable.auto_protrude +  local hasitalic=tfmtable.has_italic +  t.parameters={} +  t.characters={} +  t.MathConstants={} +  local descriptions=tfmtable.descriptions or {} +  t.unicodes=tfmtable.unicodes +  t.indices=tfmtable.indices +  t.marks=tfmtable.marks +t.goodies=tfmtable.goodies +t.colorscheme=tfmtable.colorscheme +  t.descriptions=descriptions +  if tfmtable.fonts then +    t.fonts=table.fastcopy(tfmtable.fonts)  +  end +  local tp=t.parameters +  local mp=t.math_parameters +  local tfmp=tfmtable.parameters +  tp.slant=(tfmp.slant     or tfmp[1] or 0) +  tp.space=(tfmp.space     or tfmp[2] or 0)*hdelta +  tp.space_stretch=(tfmp.space_stretch or tfmp[3] or 0)*hdelta +  tp.space_shrink=(tfmp.space_shrink or tfmp[4] or 0)*hdelta +  tp.x_height=(tfmp.x_height   or tfmp[5] or 0)*vdelta +  tp.quad=(tfmp.quad     or tfmp[6] or 0)*hdelta +  tp.extra_space=(tfmp.extra_space  or tfmp[7] or 0)*hdelta +  local protrusionfactor=(tp.quad~=0 and 1000/tp.quad) or 0 +  local tc=t.characters +  local characters=tfmtable.characters +  local nameneeded=not tfmtable.shared.otfdata  +  local changed=tfmtable.changed or {}  +  local ischanged=changed and next(changed) +  local indices=tfmtable.indices +  local luatex=tfmtable.luatex +  local tounicode=luatex and luatex.tounicode +  local defaultwidth=luatex and luatex.defaultwidth or 0 +  local defaultheight=luatex and luatex.defaultheight or 0 +  local defaultdepth=luatex and luatex.defaultdepth or 0 +  local scaledwidth=defaultwidth*hdelta +  local scaledheight=defaultheight*vdelta +  local scaleddepth=defaultdepth*vdelta +  local stackmath=tfmtable.ignore_stack_math~=true +  local private=fonts.private +  local sharedkerns={} +  for k,v in next,characters do +    local chr,description,index +    if ischanged then +      local c=changed[k] +      if c then +        description=descriptions[c] or v +        v=characters[c] or v +        index=(indices and indices[c]) or c +      else +        description=descriptions[k] or v +        index=(indices and indices[k]) or k +      end +    else +      description=descriptions[k] or v +      index=(indices and indices[k]) or k +    end +    local width=description.width +    local height=description.height +    local depth=description.depth +    if width then width=hdelta*width else width=scaledwidth end +    if height then height=vdelta*height else height=scaledheight end +    if depth and depth~=0 then +      depth=delta*depth +      if nameneeded then +        chr={ +          name=description.name, +          index=index, +          height=height, +          depth=depth, +          width=width, +        } +      else +        chr={ +          index=index, +          height=height, +          depth=depth, +          width=width, +        } +      end +    else +      if nameneeded then +        chr={ +          name=description.name, +          index=index, +          height=height, +          width=width, +        } +      else +        chr={ +          index=index, +          height=height, +          width=width, +        } +      end +    end +    if tounicode then +      local tu=tounicode[index]  +      if tu then +        chr.tounicode=tu +      end +    end +    if hasquality then +      local ve=v.expansion_factor +      if ve then +        chr.expansion_factor=ve*1000  +      end +      local vl=v.left_protruding +      if vl then +        chr.left_protruding=protrusionfactor*width*vl +      end +      local vr=v.right_protruding +      if vr then +        chr.right_protruding=protrusionfactor*width*vr +      end +    end +    if hasitalic then +      local vi=description.italic or v.italic +      if vi and vi~=0 then +        chr.italic=vi*hdelta +      end +    end +    if hasmath then +      local vn=v.next +      if vn then +        chr.next=vn +      else +        local vv=v.vert_variants +        if vv then +          local t={} +          for i=1,#vv do +            local vvi=vv[i] +            t[i]={ +              ["start"]=(vvi["start"]  or 0)*vdelta, +              ["end"]=(vvi["end"]   or 0)*vdelta, +              ["advance"]=(vvi["advance"] or 0)*vdelta, +              ["extender"]=vvi["extender"], +              ["glyph"]=vvi["glyph"], +            } +          end +          chr.vert_variants=t +        else +          local hv=v.horiz_variants +          if hv then +            local t={} +            for i=1,#hv do +              local hvi=hv[i] +              t[i]={ +                ["start"]=(hvi["start"]  or 0)*hdelta, +                ["end"]=(hvi["end"]   or 0)*hdelta, +                ["advance"]=(hvi["advance"] or 0)*hdelta, +                ["extender"]=hvi["extender"], +                ["glyph"]=hvi["glyph"], +              } +            end +            chr.horiz_variants=t +          end +        end +      end +      local vt=description.top_accent +      if vt then +        chr.top_accent=vdelta*vt +      end +      if stackmath then +        local mk=v.mathkerns +        if mk then +          local kerns={} +          local v=mk.top_right  if v then local k={} for i=1,#v do local vi=v[i] +            k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } +          end   kerns.top_right=k end +          local v=mk.top_left   if v then local k={} for i=1,#v do local vi=v[i] +            k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } +          end   kerns.top_left=k end +          local v=mk.bottom_left if v then local k={} for i=1,#v do local vi=v[i] +            k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } +          end   kerns.bottom_left=k end +          local v=mk.bottom_right if v then local k={} for i=1,#v do local vi=v[i] +            k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } +          end   kerns.bottom_right=k end +          chr.mathkern=kerns  +        end +      end +    end +    if not nodemode then +      local vk=v.kerns +      if vk then +        local s=sharedkerns[vk] +        if not s then +          s={} +          for k,v in next,vk do s[k]=v*hdelta end +          sharedkerns[vk]=s +        end +        chr.kerns=s +      end +      local vl=v.ligatures +      if vl then +        if true then +          chr.ligatures=vl  +        else +          local tt={} +          for i,l in next,vl do +            tt[i]=l +          end +          chr.ligatures=tt +        end +      end +    end +    if isvirtual then +      local vc=v.commands +      if vc then +        local ok=false +        for i=1,#vc do +          local key=vc[i][1] +          if key=="right" or key=="down" then +            ok=true +            break +          end +        end +        if ok then +          local tt={} +          for i=1,#vc do +            local ivc=vc[i] +            local key=ivc[1] +            if key=="right" then +              tt[#tt+1]={ key,ivc[2]*hdelta } +            elseif key=="down" then +              tt[#tt+1]={ key,ivc[2]*vdelta } +            elseif key=="rule" then +              tt[#tt+1]={ key,ivc[2]*vdelta,ivc[3]*hdelta } +            else  +              tt[#tt+1]=ivc  +            end +          end +          chr.commands=tt +        else +          chr.commands=vc +        end +      end +    end +    tc[k]=chr +  end +  t.size=scaledpoints +  t.factor=delta +  t.hfactor=hdelta +  t.vfactor=vdelta +  if t.fonts then +    t.fonts=table.fastcopy(t.fonts)  +  end +  if hasmath then +    local ma=tfm.mathactions +    for i=1,#ma do +      ma[i](t,tfmtable,delta,hdelta,vdelta)  +    end +  end +  local tpx=tp.x_height +  if hasmath then +    if not tp[13] then tp[13]=.86*tpx end  +    if not tp[14] then tp[14]=.86*tpx end  +    if not tp[15] then tp[15]=.86*tpx end  +    if not tp[16] then tp[16]=.48*tpx end  +    if not tp[17] then tp[17]=.48*tpx end  +    if not tp[22] then tp[22]=0   end  +    if t.MathConstants then t.MathConstants.AccentBaseHeight=nil end  +  end +  t.tounicode=1 +  t.cidinfo=tfmtable.cidinfo +  if hasmath then +    if trace_defining then +      logs.report("define font","math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") +    end +  else +    if trace_defining then +      logs.report("define font","math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") +    end +    t.nomath,t.MathConstants=true,nil +  end +  if not t.psname then +    t.psname=t.fontname or (t.fullname and fonts.names.cleanname(t.fullname)) +  end +  if trace_defining then +    logs.report("define font","used for accesing subfont: '%s'",t.psname or "nopsname") +    logs.report("define font","used for subsetting: '%s'",t.fontname or "nofontname") +  end +  return t,delta +end +tfm.auto_cleanup=true +local lastfont=nil +function tfm.cleanup_table(tfmdata)  +  if tfm.auto_cleanup then  +    if tfmdata.type=='virtual' or tfmdata.virtualized then +      for k,v in next,tfmdata.characters do +        if v.commands then v.commands=nil end +      end +    else +    end +  end +end +function tfm.cleanup(tfmdata)  +end +function tfm.scale(tfmtable,scaledpoints,relativeid) +  local t,factor=tfm.do_scale(tfmtable,scaledpoints,relativeid) +  t.factor=factor +  t.ascender=factor*(tfmtable.ascender or 0) +  t.descender=factor*(tfmtable.descender or 0) +  t.shared=tfmtable.shared or {} +  t.unique=table.fastcopy(tfmtable.unique or {}) +  tfm.cleanup(t) +  return t +end +fonts.analyzers=fonts.analyzers       or {} +fonts.analyzers.aux=fonts.analyzers.aux     or {} +fonts.analyzers.methods=fonts.analyzers.methods   or {} +fonts.analyzers.initializers=fonts.analyzers.initializers or {} +local state=attributes.private('state') +function fonts.analyzers.aux.setstate(head,font) +  local tfmdata=fontdata[font] +  local characters=tfmdata.characters +  local descriptions=tfmdata.descriptions +  local first,last,current,n,done=nil,nil,head,0,false  +  while current do +    local id=current.id +    if id==glyph and current.font==font then +      local d=descriptions[current.char] +      if d then +        if d.class=="mark" then +          done=true +          set_attribute(current,state,5)  +        elseif n==0 then +          first,last,n=current,current,1 +          set_attribute(current,state,1)  +        else +          last,n=current,n+1 +          set_attribute(current,state,2)  +        end +      else  +        if first and first==last then +          set_attribute(last,state,4)  +        elseif last then +          set_attribute(last,state,3)  +        end +        first,last,n=nil,nil,0 +      end +    elseif id==disc then +      set_attribute(current,state,2)  +      last=current +    else  +      if first and first==last then +        set_attribute(last,state,4)  +      elseif last then +        set_attribute(last,state,3)  +      end +      first,last,n=nil,nil,0 +    end +    current=current.next +  end +  if first and first==last then +    set_attribute(last,state,4)  +  elseif last then +    set_attribute(last,state,3)  +  end +  return head,done +end +function tfm.replacements(tfm,value) +  tfm.characters[0x0027]=tfm.characters[0x2019] +end +function tfm.checked_filename(metadata,whatever) +  local foundfilename=metadata.foundfilename +  if not foundfilename then +    local askedfilename=metadata.filename or "" +    if askedfilename~="" then +      foundfilename=resolvers.findbinfile(askedfilename,"") or "" +      if foundfilename=="" then +        logs.report("fonts","source file '%s' is not found",askedfilename) +        foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or "" +        if foundfilename~="" then +          logs.report("fonts","using source file '%s' (cache mismatch)",foundfilename) +        end +      end +    elseif whatever then +      logs.report("fonts","no source file for '%s'",whatever) +      foundfilename="" +    end +    metadata.foundfilename=foundfilename +  end +  return foundfilename +end +statistics.register("fonts load time",function() +  return statistics.elapsedseconds(fonts) +end) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-cid']={ +  version=1.001, +  comment="companion to font-otf.lua (cidmaps)", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local format,match,lower=string.format,string.match,string.lower +local tonumber=tonumber +local lpegmatch=lpeg.match +local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) +fonts=fonts     or {} +fonts.cid=fonts.cid   or {} +fonts.cid.map=fonts.cid.map or {} +fonts.cid.max=fonts.cid.max or 10 +local number=lpeg.C(lpeg.R("09","af","AF")^1) +local space=lpeg.S(" \n\r\t") +local spaces=space^0 +local period=lpeg.P(".") +local periods=period*period +local name=lpeg.P("/")*lpeg.C((1-space)^1) +local unicodes,names={},{} +local function do_one(a,b) +  unicodes[tonumber(a)]=tonumber(b,16) +end +local function do_range(a,b,c) +  c=tonumber(c,16) +  for i=tonumber(a),tonumber(b) do +    unicodes[i]=c +    c=c+1 +  end +end +local function do_name(a,b) +  names[tonumber(a)]=b +end +local grammar=lpeg.P { "start", +  start=number*spaces*number*lpeg.V("series"), +  series=(spaces*(lpeg.V("one")+lpeg.V("range")+lpeg.V("named")) )^1, +  one=(number*spaces*number)/do_one, +  range=(number*periods*number*spaces*number)/do_range, +  named=(number*spaces*name)/do_name +} +function fonts.cid.load(filename) +  local data=io.loaddata(filename) +  if data then +    unicodes,names={},{} +    lpegmatch(grammar,data) +    local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$") +    return { +      supplement=supplement, +      registry=registry, +      ordering=ordering, +      filename=filename, +      unicodes=unicodes, +      names=names +    } +  else +    return nil +  end +end +local template="%s-%s-%s.cidmap" +local function locate(registry,ordering,supplement) +  local filename=format(template,registry,ordering,supplement) +  local hashname=lower(filename) +  local cidmap=fonts.cid.map[hashname] +  if not cidmap then +    if trace_loading then +      logs.report("load otf","checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) +    end +    local fullname=resolvers.find_file(filename,'cid') or "" +    if fullname~="" then +      cidmap=fonts.cid.load(fullname) +      if cidmap then +        if trace_loading then +          logs.report("load otf","using cidmap file %s",filename) +        end +        fonts.cid.map[hashname]=cidmap +        cidmap.usedname=file.basename(filename) +        return cidmap +      end +    end +  end +  return cidmap +end +function fonts.cid.getmap(registry,ordering,supplement) +  local supplement=tonumber(supplement) +  if trace_loading then +    logs.report("load otf","needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) +  end +  local cidmap=locate(registry,ordering,supplement) +  if not cidmap then +    local cidnum=nil +    if supplement<fonts.cid.max then +      for supplement=supplement+1,fonts.cid.max do +        local c=locate(registry,ordering,supplement) +        if c then +          cidmap,cidnum=c,supplement +          break +        end +      end +    end +    if not cidmap and supplement>0 then +      for supplement=supplement-1,0,-1 do +        local c=locate(registry,ordering,supplement) +        if c then +          cidmap,cidnum=c,supplement +          break +        end +      end +    end +    if cidmap and cidnum>0 then +      for s=0,cidnum-1 do +        filename=format(template,registry,ordering,s) +        if not fonts.cid.map[filename] then +          fonts.cid.map[filename]=cidmap  +        end +      end +    end +  end +  return cidmap +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otf']={ +  version=1.001, +  comment="companion to font-otf.lua (tables)", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local type,next,tonumber,tostring=type,next,tonumber,tostring +local gsub,lower=string.gsub,string.lower +fonts=fonts   or {} +fonts.otf=fonts.otf or {} +local otf=fonts.otf +otf.tables=otf.tables  or {} +otf.meanings=otf.meanings or {} +otf.tables.scripts={ +  ['dflt']='Default', +  ['arab']='Arabic', +  ['armn']='Armenian', +  ['bali']='Balinese', +  ['beng']='Bengali', +  ['bopo']='Bopomofo', +  ['brai']='Braille', +  ['bugi']='Buginese', +  ['buhd']='Buhid', +  ['byzm']='Byzantine Music', +  ['cans']='Canadian Syllabics', +  ['cher']='Cherokee', +  ['copt']='Coptic', +  ['cprt']='Cypriot Syllabary', +  ['cyrl']='Cyrillic', +  ['deva']='Devanagari', +  ['dsrt']='Deseret', +  ['ethi']='Ethiopic', +  ['geor']='Georgian', +  ['glag']='Glagolitic', +  ['goth']='Gothic', +  ['grek']='Greek', +  ['gujr']='Gujarati', +  ['guru']='Gurmukhi', +  ['hang']='Hangul', +  ['hani']='CJK Ideographic', +  ['hano']='Hanunoo', +  ['hebr']='Hebrew', +  ['ital']='Old Italic', +  ['jamo']='Hangul Jamo', +  ['java']='Javanese', +  ['kana']='Hiragana and Katakana', +  ['khar']='Kharosthi', +  ['khmr']='Khmer', +  ['knda']='Kannada', +  ['lao' ]='Lao', +  ['latn']='Latin', +  ['limb']='Limbu', +  ['linb']='Linear B', +  ['math']='Mathematical Alphanumeric Symbols', +  ['mlym']='Malayalam', +  ['mong']='Mongolian', +  ['musc']='Musical Symbols', +  ['mymr']='Myanmar', +  ['nko' ]="N'ko", +  ['ogam']='Ogham', +  ['orya']='Oriya', +  ['osma']='Osmanya', +  ['phag']='Phags-pa', +  ['phnx']='Phoenician', +  ['runr']='Runic', +  ['shaw']='Shavian', +  ['sinh']='Sinhala', +  ['sylo']='Syloti Nagri', +  ['syrc']='Syriac', +  ['tagb']='Tagbanwa', +  ['tale']='Tai Le', +  ['talu']='Tai Lu', +  ['taml']='Tamil', +  ['telu']='Telugu', +  ['tfng']='Tifinagh', +  ['tglg']='Tagalog', +  ['thaa']='Thaana', +  ['thai']='Thai', +  ['tibt']='Tibetan', +  ['ugar']='Ugaritic Cuneiform', +  ['xpeo']='Old Persian Cuneiform', +  ['xsux']='Sumero-Akkadian Cuneiform', +  ['yi' ]='Yi', +} +otf.tables.languages={ +  ['dflt']='Default', +  ['aba']='Abaza', +  ['abk']='Abkhazian', +  ['ady']='Adyghe', +  ['afk']='Afrikaans', +  ['afr']='Afar', +  ['agw']='Agaw', +  ['als']='Alsatian', +  ['alt']='Altai', +  ['amh']='Amharic', +  ['ara']='Arabic', +  ['ari']='Aari', +  ['ark']='Arakanese', +  ['asm']='Assamese', +  ['ath']='Athapaskan', +  ['avr']='Avar', +  ['awa']='Awadhi', +  ['aym']='Aymara', +  ['aze']='Azeri', +  ['bad']='Badaga', +  ['bag']='Baghelkhandi', +  ['bal']='Balkar', +  ['bau']='Baule', +  ['bbr']='Berber', +  ['bch']='Bench', +  ['bcr']='Bible Cree', +  ['bel']='Belarussian', +  ['bem']='Bemba', +  ['ben']='Bengali', +  ['bgr']='Bulgarian', +  ['bhi']='Bhili', +  ['bho']='Bhojpuri', +  ['bik']='Bikol', +  ['bil']='Bilen', +  ['bkf']='Blackfoot', +  ['bli']='Balochi', +  ['bln']='Balante', +  ['blt']='Balti', +  ['bmb']='Bambara', +  ['bml']='Bamileke', +  ['bos']='Bosnian', +  ['bre']='Breton', +  ['brh']='Brahui', +  ['bri']='Braj Bhasha', +  ['brm']='Burmese', +  ['bsh']='Bashkir', +  ['bti']='Beti', +  ['cat']='Catalan', +  ['ceb']='Cebuano', +  ['che']='Chechen', +  ['chg']='Chaha Gurage', +  ['chh']='Chattisgarhi', +  ['chi']='Chichewa', +  ['chk']='Chukchi', +  ['chp']='Chipewyan', +  ['chr']='Cherokee', +  ['chu']='Chuvash', +  ['cmr']='Comorian', +  ['cop']='Coptic', +  ['cos']='Corsican', +  ['cre']='Cree', +  ['crr']='Carrier', +  ['crt']='Crimean Tatar', +  ['csl']='Church Slavonic', +  ['csy']='Czech', +  ['dan']='Danish', +  ['dar']='Dargwa', +  ['dcr']='Woods Cree', +  ['deu']='German', +  ['dgr']='Dogri', +  ['div']='Divehi', +  ['djr']='Djerma', +  ['dng']='Dangme', +  ['dnk']='Dinka', +  ['dri']='Dari', +  ['dun']='Dungan', +  ['dzn']='Dzongkha', +  ['ebi']='Ebira', +  ['ecr']='Eastern Cree', +  ['edo']='Edo', +  ['efi']='Efik', +  ['ell']='Greek', +  ['eng']='English', +  ['erz']='Erzya', +  ['esp']='Spanish', +  ['eti']='Estonian', +  ['euq']='Basque', +  ['evk']='Evenki', +  ['evn']='Even', +  ['ewe']='Ewe', +  ['fan']='French Antillean', +  ['far']='Farsi', +  ['fin']='Finnish', +  ['fji']='Fijian', +  ['fle']='Flemish', +  ['fne']='Forest Nenets', +  ['fon']='Fon', +  ['fos']='Faroese', +  ['fra']='French', +  ['fri']='Frisian', +  ['frl']='Friulian', +  ['fta']='Futa', +  ['ful']='Fulani', +  ['gad']='Ga', +  ['gae']='Gaelic', +  ['gag']='Gagauz', +  ['gal']='Galician', +  ['gar']='Garshuni', +  ['gaw']='Garhwali', +  ['gez']="Ge'ez", +  ['gil']='Gilyak', +  ['gmz']='Gumuz', +  ['gon']='Gondi', +  ['grn']='Greenlandic', +  ['gro']='Garo', +  ['gua']='Guarani', +  ['guj']='Gujarati', +  ['hai']='Haitian', +  ['hal']='Halam', +  ['har']='Harauti', +  ['hau']='Hausa', +  ['haw']='Hawaiin', +  ['hbn']='Hammer-Banna', +  ['hil']='Hiligaynon', +  ['hin']='Hindi', +  ['hma']='High Mari', +  ['hnd']='Hindko', +  ['ho']='Ho', +  ['hri']='Harari', +  ['hrv']='Croatian', +  ['hun']='Hungarian', +  ['hye']='Armenian', +  ['ibo']='Igbo', +  ['ijo']='Ijo', +  ['ilo']='Ilokano', +  ['ind']='Indonesian', +  ['ing']='Ingush', +  ['inu']='Inuktitut', +  ['iri']='Irish', +  ['irt']='Irish Traditional', +  ['isl']='Icelandic', +  ['ism']='Inari Sami', +  ['ita']='Italian', +  ['iwr']='Hebrew', +  ['jan']='Japanese', +  ['jav']='Javanese', +  ['jii']='Yiddish', +  ['jud']='Judezmo', +  ['jul']='Jula', +  ['kab']='Kabardian', +  ['kac']='Kachchi', +  ['kal']='Kalenjin', +  ['kan']='Kannada', +  ['kar']='Karachay', +  ['kat']='Georgian', +  ['kaz']='Kazakh', +  ['keb']='Kebena', +  ['kge']='Khutsuri Georgian', +  ['kha']='Khakass', +  ['khk']='Khanty-Kazim', +  ['khm']='Khmer', +  ['khs']='Khanty-Shurishkar', +  ['khv']='Khanty-Vakhi', +  ['khw']='Khowar', +  ['kik']='Kikuyu', +  ['kir']='Kirghiz', +  ['kis']='Kisii', +  ['kkn']='Kokni', +  ['klm']='Kalmyk', +  ['kmb']='Kamba', +  ['kmn']='Kumaoni', +  ['kmo']='Komo', +  ['kms']='Komso', +  ['knr']='Kanuri', +  ['kod']='Kodagu', +  ['koh']='Korean Old Hangul', +  ['kok']='Konkani', +  ['kon']='Kikongo', +  ['kop']='Komi-Permyak', +  ['kor']='Korean', +  ['koz']='Komi-Zyrian', +  ['kpl']='Kpelle', +  ['kri']='Krio', +  ['krk']='Karakalpak', +  ['krl']='Karelian', +  ['krm']='Karaim', +  ['krn']='Karen', +  ['krt']='Koorete', +  ['ksh']='Kashmiri', +  ['ksi']='Khasi', +  ['ksm']='Kildin Sami', +  ['kui']='Kui', +  ['kul']='Kulvi', +  ['kum']='Kumyk', +  ['kur']='Kurdish', +  ['kuu']='Kurukh', +  ['kuy']='Kuy', +  ['kyk']='Koryak', +  ['lad']='Ladin', +  ['lah']='Lahuli', +  ['lak']='Lak', +  ['lam']='Lambani', +  ['lao']='Lao', +  ['lat']='Latin', +  ['laz']='Laz', +  ['lcr']='L-Cree', +  ['ldk']='Ladakhi', +  ['lez']='Lezgi', +  ['lin']='Lingala', +  ['lma']='Low Mari', +  ['lmb']='Limbu', +  ['lmw']='Lomwe', +  ['lsb']='Lower Sorbian', +  ['lsm']='Lule Sami', +  ['lth']='Lithuanian', +  ['ltz']='Luxembourgish', +  ['lub']='Luba', +  ['lug']='Luganda', +  ['luh']='Luhya', +  ['luo']='Luo', +  ['lvi']='Latvian', +  ['maj']='Majang', +  ['mak']='Makua', +  ['mal']='Malayalam Traditional', +  ['man']='Mansi', +  ['map']='Mapudungun', +  ['mar']='Marathi', +  ['maw']='Marwari', +  ['mbn']='Mbundu', +  ['mch']='Manchu', +  ['mcr']='Moose Cree', +  ['mde']='Mende', +  ['men']="Me'en", +  ['miz']='Mizo', +  ['mkd']='Macedonian', +  ['mle']='Male', +  ['mlg']='Malagasy', +  ['mln']='Malinke', +  ['mlr']='Malayalam Reformed', +  ['mly']='Malay', +  ['mnd']='Mandinka', +  ['mng']='Mongolian', +  ['mni']='Manipuri', +  ['mnk']='Maninka', +  ['mnx']='Manx Gaelic', +  ['moh']='Mohawk', +  ['mok']='Moksha', +  ['mol']='Moldavian', +  ['mon']='Mon', +  ['mor']='Moroccan', +  ['mri']='Maori', +  ['mth']='Maithili', +  ['mts']='Maltese', +  ['mun']='Mundari', +  ['nag']='Naga-Assamese', +  ['nan']='Nanai', +  ['nas']='Naskapi', +  ['ncr']='N-Cree', +  ['ndb']='Ndebele', +  ['ndg']='Ndonga', +  ['nep']='Nepali', +  ['new']='Newari', +  ['ngr']='Nagari', +  ['nhc']='Norway House Cree', +  ['nis']='Nisi', +  ['niu']='Niuean', +  ['nkl']='Nkole', +  ['nko']="N'ko", +  ['nld']='Dutch', +  ['nog']='Nogai', +  ['nor']='Norwegian', +  ['nsm']='Northern Sami', +  ['nta']='Northern Tai', +  ['nto']='Esperanto', +  ['nyn']='Nynorsk', +  ['oci']='Occitan', +  ['ocr']='Oji-Cree', +  ['ojb']='Ojibway', +  ['ori']='Oriya', +  ['oro']='Oromo', +  ['oss']='Ossetian', +  ['paa']='Palestinian Aramaic', +  ['pal']='Pali', +  ['pan']='Punjabi', +  ['pap']='Palpa', +  ['pas']='Pashto', +  ['pgr']='Polytonic Greek', +  ['pil']='Pilipino', +  ['plg']='Palaung', +  ['plk']='Polish', +  ['pro']='Provencal', +  ['ptg']='Portuguese', +  ['qin']='Chin', +  ['raj']='Rajasthani', +  ['rbu']='Russian Buriat', +  ['rcr']='R-Cree', +  ['ria']='Riang', +  ['rms']='Rhaeto-Romanic', +  ['rom']='Romanian', +  ['roy']='Romany', +  ['rsy']='Rusyn', +  ['rua']='Ruanda', +  ['rus']='Russian', +  ['sad']='Sadri', +  ['san']='Sanskrit', +  ['sat']='Santali', +  ['say']='Sayisi', +  ['sek']='Sekota', +  ['sel']='Selkup', +  ['sgo']='Sango', +  ['shn']='Shan', +  ['sib']='Sibe', +  ['sid']='Sidamo', +  ['sig']='Silte Gurage', +  ['sks']='Skolt Sami', +  ['sky']='Slovak', +  ['sla']='Slavey', +  ['slv']='Slovenian', +  ['sml']='Somali', +  ['smo']='Samoan', +  ['sna']='Sena', +  ['snd']='Sindhi', +  ['snh']='Sinhalese', +  ['snk']='Soninke', +  ['sog']='Sodo Gurage', +  ['sot']='Sotho', +  ['sqi']='Albanian', +  ['srb']='Serbian', +  ['srk']='Saraiki', +  ['srr']='Serer', +  ['ssl']='South Slavey', +  ['ssm']='Southern Sami', +  ['sur']='Suri', +  ['sva']='Svan', +  ['sve']='Swedish', +  ['swa']='Swadaya Aramaic', +  ['swk']='Swahili', +  ['swz']='Swazi', +  ['sxt']='Sutu', +  ['syr']='Syriac', +  ['tab']='Tabasaran', +  ['taj']='Tajiki', +  ['tam']='Tamil', +  ['tat']='Tatar', +  ['tcr']='TH-Cree', +  ['tel']='Telugu', +  ['tgn']='Tongan', +  ['tgr']='Tigre', +  ['tgy']='Tigrinya', +  ['tha']='Thai', +  ['tht']='Tahitian', +  ['tib']='Tibetan', +  ['tkm']='Turkmen', +  ['tmn']='Temne', +  ['tna']='Tswana', +  ['tne']='Tundra Nenets', +  ['tng']='Tonga', +  ['tod']='Todo', +  ['trk']='Turkish', +  ['tsg']='Tsonga', +  ['tua']='Turoyo Aramaic', +  ['tul']='Tulu', +  ['tuv']='Tuvin', +  ['twi']='Twi', +  ['udm']='Udmurt', +  ['ukr']='Ukrainian', +  ['urd']='Urdu', +  ['usb']='Upper Sorbian', +  ['uyg']='Uyghur', +  ['uzb']='Uzbek', +  ['ven']='Venda', +  ['vit']='Vietnamese', +  ['wa' ]='Wa', +  ['wag']='Wagdi', +  ['wcr']='West-Cree', +  ['wel']='Welsh', +  ['wlf']='Wolof', +  ['xbd']='Tai Lue', +  ['xhs']='Xhosa', +  ['yak']='Yakut', +  ['yba']='Yoruba', +  ['ycr']='Y-Cree', +  ['yic']='Yi Classic', +  ['yim']='Yi Modern', +  ['zhh']='Chinese Hong Kong', +  ['zhp']='Chinese Phonetic', +  ['zhs']='Chinese Simplified', +  ['zht']='Chinese Traditional', +  ['znd']='Zande', +  ['zul']='Zulu' +} +otf.tables.features={ +  ['aalt']='Access All Alternates', +  ['abvf']='Above-Base Forms', +  ['abvm']='Above-Base Mark Positioning', +  ['abvs']='Above-Base Substitutions', +  ['afrc']='Alternative Fractions', +  ['akhn']='Akhands', +  ['blwf']='Below-Base Forms', +  ['blwm']='Below-Base Mark Positioning', +  ['blws']='Below-Base Substitutions', +  ['c2pc']='Petite Capitals From Capitals', +  ['c2sc']='Small Capitals From Capitals', +  ['calt']='Contextual Alternates', +  ['case']='Case-Sensitive Forms', +  ['ccmp']='Glyph Composition/Decomposition', +  ['cjct']='Conjunct Forms', +  ['clig']='Contextual Ligatures', +  ['cpsp']='Capital Spacing', +  ['cswh']='Contextual Swash', +  ['curs']='Cursive Positioning', +  ['dflt']='Default Processing', +  ['dist']='Distances', +  ['dlig']='Discretionary Ligatures', +  ['dnom']='Denominators', +  ['dtls']='Dotless Forms', +  ['expt']='Expert Forms', +  ['falt']='Final glyph Alternates', +  ['fin2']='Terminal Forms #2', +  ['fin3']='Terminal Forms #3', +  ['fina']='Terminal Forms', +  ['flac']='Flattened Accents Over Capitals', +  ['frac']='Fractions', +  ['fwid']='Full Width', +  ['half']='Half Forms', +  ['haln']='Halant Forms', +  ['halt']='Alternate Half Width', +  ['hist']='Historical Forms', +  ['hkna']='Horizontal Kana Alternates', +  ['hlig']='Historical Ligatures', +  ['hngl']='Hangul', +  ['hojo']='Hojo Kanji Forms', +  ['hwid']='Half Width', +  ['init']='Initial Forms', +  ['isol']='Isolated Forms', +  ['ital']='Italics', +  ['jalt']='Justification Alternatives', +  ['jp04']='JIS2004 Forms', +  ['jp78']='JIS78 Forms', +  ['jp83']='JIS83 Forms', +  ['jp90']='JIS90 Forms', +  ['kern']='Kerning', +  ['lfbd']='Left Bounds', +  ['liga']='Standard Ligatures', +  ['ljmo']='Leading Jamo Forms', +  ['lnum']='Lining Figures', +  ['locl']='Localized Forms', +  ['mark']='Mark Positioning', +  ['med2']='Medial Forms #2', +  ['medi']='Medial Forms', +  ['mgrk']='Mathematical Greek', +  ['mkmk']='Mark to Mark Positioning', +  ['mset']='Mark Positioning via Substitution', +  ['nalt']='Alternate Annotation Forms', +  ['nlck']='NLC Kanji Forms', +  ['nukt']='Nukta Forms', +  ['numr']='Numerators', +  ['onum']='Old Style Figures', +  ['opbd']='Optical Bounds', +  ['ordn']='Ordinals', +  ['ornm']='Ornaments', +  ['palt']='Proportional Alternate Width', +  ['pcap']='Petite Capitals', +  ['pnum']='Proportional Figures', +  ['pref']='Pre-base Forms', +  ['pres']='Pre-base Substitutions', +  ['pstf']='Post-base Forms', +  ['psts']='Post-base Substitutions', +  ['pwid']='Proportional Widths', +  ['qwid']='Quarter Widths', +  ['rand']='Randomize', +  ['rkrf']='Rakar Forms', +  ['rlig']='Required Ligatures', +  ['rphf']='Reph Form', +  ['rtbd']='Right Bounds', +  ['rtla']='Right-To-Left Alternates', +  ['rtlm']='Right To Left Math', +  ['ruby']='Ruby Notation Forms', +  ['salt']='Stylistic Alternates', +  ['sinf']='Scientific Inferiors', +  ['size']='Optical Size', +  ['smcp']='Small Capitals', +  ['smpl']='Simplified Forms', +  ['ss01']='Stylistic Set 1', +  ['ss02']='Stylistic Set 2', +  ['ss03']='Stylistic Set 3', +  ['ss04']='Stylistic Set 4', +  ['ss05']='Stylistic Set 5', +  ['ss06']='Stylistic Set 6', +  ['ss07']='Stylistic Set 7', +  ['ss08']='Stylistic Set 8', +  ['ss09']='Stylistic Set 9', +  ['ss10']='Stylistic Set 10', +  ['ss11']='Stylistic Set 11', +  ['ss12']='Stylistic Set 12', +  ['ss13']='Stylistic Set 13', +  ['ss14']='Stylistic Set 14', +  ['ss15']='Stylistic Set 15', +  ['ss16']='Stylistic Set 16', +  ['ss17']='Stylistic Set 17', +  ['ss18']='Stylistic Set 18', +  ['ss19']='Stylistic Set 19', +  ['ss20']='Stylistic Set 20', +  ['ssty']='Script Style', +  ['subs']='Subscript', +  ['sups']='Superscript', +  ['swsh']='Swash', +  ['titl']='Titling', +  ['tjmo']='Trailing Jamo Forms', +  ['tnam']='Traditional Name Forms', +  ['tnum']='Tabular Figures', +  ['trad']='Traditional Forms', +  ['twid']='Third Widths', +  ['unic']='Unicase', +  ['valt']='Alternate Vertical Metrics', +  ['vatu']='Vattu Variants', +  ['vert']='Vertical Writing', +  ['vhal']='Alternate Vertical Half Metrics', +  ['vjmo']='Vowel Jamo Forms', +  ['vkna']='Vertical Kana Alternates', +  ['vkrn']='Vertical Kerning', +  ['vpal']='Proportional Alternate Vertical Metrics', +  ['vrt2']='Vertical Rotation', +  ['zero']='Slashed Zero', +  ['trep']='Traditional TeX Replacements', +  ['tlig']='Traditional TeX Ligatures', +} +otf.tables.baselines={ +  ['hang']='Hanging baseline', +  ['icfb']='Ideographic character face bottom edge baseline', +  ['icft']='Ideographic character face tope edige baseline', +  ['ideo']='Ideographic em-box bottom edge baseline', +  ['idtp']='Ideographic em-box top edge baseline', +  ['math']='Mathmatical centered baseline', +  ['romn']='Roman baseline' +} +function otf.tables.to_tag(id) +  return stringformat("%4s",lower(id)) +end +local function resolve(tab,id) +  if tab and id then +    id=lower(id) +    return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' +  else +    return "unknown" +  end +end +function otf.meanings.script(id) +  return resolve(otf.tables.scripts,id) +end +function otf.meanings.language(id) +  return resolve(otf.tables.languages,id) +end +function otf.meanings.feature(id) +  return resolve(otf.tables.features,id) +end +function otf.meanings.baseline(id) +  return resolve(otf.tables.baselines,id) +end +otf.tables.to_scripts=table.reverse_hash(otf.tables.scripts ) +otf.tables.to_languages=table.reverse_hash(otf.tables.languages) +otf.tables.to_features=table.reverse_hash(otf.tables.features ) +local scripts=otf.tables.scripts +local languages=otf.tables.languages +local features=otf.tables.features +local to_scripts=otf.tables.to_scripts +local to_languages=otf.tables.to_languages +local to_features=otf.tables.to_features +for k,v in next,to_features do +  local stripped=gsub(k,"%-"," ") +  to_features[stripped]=v +  local stripped=gsub(k,"[^a-zA-Z0-9]","") +  to_features[stripped]=v +end +for k,v in next,to_features do +  to_features[lower(k)]=v +end +otf.meanings.checkers={ +  rand=function(v) +    return v and "random" +  end +} +local checkers=otf.meanings.checkers +function otf.meanings.normalize(features) +  local h={} +  for k,v in next,features do +    k=lower(k) +    if k=="language" or k=="lang" then +      v=gsub(lower(v),"[^a-z0-9%-]","") +      if not languages[v] then +        h.language=to_languages[v] or "dflt" +      else +        h.language=v +      end +    elseif k=="script" then +      v=gsub(lower(v),"[^a-z0-9%-]","") +      if not scripts[v] then +        h.script=to_scripts[v] or "dflt" +      else +        h.script=v +      end +    else +      if type(v)=="string" then +        local b=v:is_boolean() +        if type(b)=="nil" then +          v=tonumber(v) or lower(v) +        else +          v=b +        end +      end +      k=to_features[k] or k +      local c=checkers[k] +      h[k]=c and c(v) or v +    end +  end +  return h +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-map']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local utf=unicode.utf8 +local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower +local lpegmatch=lpeg.match +local utfbyte=utf.byte +local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) +local trace_unimapping=false trackers.register("otf.unimapping",function(v) trace_unimapping=v end) +local ctxcatcodes=tex and tex.ctxcatcodes +fonts=fonts   or {} +fonts.map=fonts.map or {} +local function load_lum_table(filename)  +  local lumname=file.replacesuffix(file.basename(filename),"lum") +  local lumfile=resolvers.find_file(lumname,"map") or "" +  if lumfile~="" and lfs.isfile(lumfile) then +    if trace_loading or trace_unimapping then +      logs.report("load otf","enhance: loading %s ",lumfile) +    end +    lumunic=dofile(lumfile) +    return lumunic,lumfile +  end +end +local hex=lpeg.R("AF","09") +local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end +local hexsix=(hex^1)/function(s) return tonumber(s,16) end +local dec=(lpeg.R("09")^1)/tonumber +local period=lpeg.P(".") +local unicode=lpeg.P("uni")*(hexfour*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexfour^1)*lpeg.Cc(true)) +local ucode=lpeg.P("u")*(hexsix*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexsix^1)*lpeg.Cc(true)) +local index=lpeg.P("index")*dec*lpeg.Cc(false) +local parser=unicode+ucode+index +local parsers={} +local function make_name_parser(str) +  if not str or str=="" then +    return parser +  else +    local p=parsers[str] +    if not p then +      p=lpeg.P(str)*period*dec*lpeg.Cc(false) +      parsers[str]=p +    end +    return p +  end +end +local function tounicode16(unicode) +  if unicode<0x10000 then +    return format("%04X",unicode) +  else +    return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) +  end +end +local function tounicode16sequence(unicodes) +  local t={} +  for l=1,#unicodes do +    local unicode=unicodes[l] +    if unicode<0x10000 then +      t[l]=format("%04X",unicode) +    else +      t[l]=format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) +    end +  end +  return concat(t) +end +fonts.map.load_lum_table=load_lum_table +fonts.map.make_name_parser=make_name_parser +fonts.map.tounicode16=tounicode16 +fonts.map.tounicode16sequence=tounicode16sequence +local separator=lpeg.S("_.") +local other=lpeg.C((1-separator)^1) +local ligsplitter=lpeg.Ct(other*(separator*other)^0) +fonts.map.add_to_unicode=function(data,filename) +  local unicodes=data.luatex and data.luatex.unicodes +  if not unicodes then +    return +  end +  unicodes['space']=unicodes['space'] or 32 +  unicodes['hyphen']=unicodes['hyphen'] or 45 +  unicodes['zwj']=unicodes['zwj']  or 0x200D +  unicodes['zwnj']=unicodes['zwnj']  or 0x200C +  local tounicode,originals,ns,nl,private,unknown={},{},0,0,fonts.private,format("%04X",utfbyte("?")) +  data.luatex.tounicode,data.luatex.originals=tounicode,originals +  local lumunic,uparser,oparser +  if false then  +    lumunic=load_lum_table(filename) +    lumunic=lumunic and lumunic.tounicode +  end +  local cidinfo,cidnames,cidcodes=data.cidinfo +  local usedmap=cidinfo and cidinfo.usedname +  usedmap=usedmap and lower(usedmap) +  usedmap=usedmap and fonts.cid.map[usedmap] +  if usedmap then +    oparser=usedmap and make_name_parser(cidinfo.ordering) +    cidnames=usedmap.names +    cidcodes=usedmap.unicodes +  end +  uparser=make_name_parser() +  local aglmap=fonts.map and fonts.map.agl_to_unicode +  for index,glyph in next,data.glyphs do +    local name,unic=glyph.name,glyph.unicode or -1  +    if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then +      local unicode=(lumunic and lumunic[name]) or (aglmap and aglmap[name]) +      if unicode then +        originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 +      end +      if (not unicode) and usedmap then +        local foundindex=lpegmatch(oparser,name) +        if foundindex then +          unicode=cidcodes[foundindex]  +          if unicode then +            originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 +          else +            local reference=cidnames[foundindex]  +            if reference then +              local foundindex=lpegmatch(oparser,reference) +              if foundindex then +                unicode=cidcodes[foundindex] +                if unicode then +                  originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 +                end +              end +              if not unicode then +                local foundcodes,multiple=lpegmatch(uparser,reference) +                if foundcodes then +                  if multiple then +                    originals[index],tounicode[index],nl,unicode=foundcodes,tounicode16sequence(foundcodes),nl+1,true +                  else +                    originals[index],tounicode[index],ns,unicode=foundcodes,tounicode16(foundcodes),ns+1,foundcodes +                  end +                end +              end +            end +          end +        end +      end +      if not unicode then +        local split=lpegmatch(ligsplitter,name) +        local nplit=(split and #split) or 0 +        if nplit==0 then +        elseif nplit==1 then +          local base=split[1] +          unicode=unicodes[base] or (aglmap and aglmap[base]) +          if unicode then +            if type(unicode)=="table" then +              unicode=unicode[1] +            end +            originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 +          end +        else +          local t={} +          for l=1,nplit do +            local base=split[l] +            local u=unicodes[base] or (aglmap and aglmap[base]) +            if not u then +              break +            elseif type(u)=="table" then +              t[#t+1]=u[1] +            else +              t[#t+1]=u +            end +          end +          if #t>0 then  +            originals[index],tounicode[index],nl,unicode=t,tounicode16sequence(t),nl+1,true +          end +        end +      end +      if not unicode then +        local foundcodes,multiple=lpegmatch(uparser,name) +        if foundcodes then +          if multiple then +            originals[index],tounicode[index],nl,unicode=foundcodes,tounicode16sequence(foundcodes),nl+1,true +          else +            originals[index],tounicode[index],ns,unicode=foundcodes,tounicode16(foundcodes),ns+1,foundcodes +          end +        end +      end +      if not unicode then +        originals[index],tounicode[index]=0xFFFD,"FFFD" +      end +    end +  end +  if trace_unimapping then +    for index,glyph in table.sortedhash(data.glyphs) do +      local toun,name,unic=tounicode[index],glyph.name,glyph.unicode or -1  +      if toun then +        logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) +      else +        logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) +      end +    end +  end +  if trace_loading and (ns>0 or nl>0) then +    logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns,ns) +  end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otf']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local utf=unicode.utf8 +local concat,utfbyte=table.concat,utf.byte +local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local type,next,tonumber,tostring=type,next,tonumber,tostring +local abs=math.abs +local getn=table.getn +local lpegmatch=lpeg.match +local trace_private=false trackers.register("otf.private",function(v) trace_private=v end) +local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) +local trace_features=false trackers.register("otf.features",function(v) trace_features=v end) +local trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end) +local trace_sequences=false trackers.register("otf.sequences",function(v) trace_sequences=v end) +local trace_math=false trackers.register("otf.math",function(v) trace_math=v end) +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +fonts=fonts   or {} +fonts.otf=fonts.otf or {} +fonts.tfm=fonts.tfm or {} +local otf=fonts.otf +local tfm=fonts.tfm +local fontdata=fonts.ids +otf.tables=otf.tables      or {}  +otf.meanings=otf.meanings     or {}  +otf.tables.features=otf.tables.features or {}  +otf.tables.languages=otf.tables.languages or {}  +otf.tables.scripts=otf.tables.scripts  or {}  +otf.features=otf.features     or {} +otf.features.list=otf.features.list  or {} +otf.features.default=otf.features.default or {} +otf.enhancers=otf.enhancers    or {} +otf.glists={ "gsub","gpos" } +otf.version=2.653  +otf.pack=true  +otf.syncspace=true +otf.notdef=false +otf.cache=containers.define("fonts","otf",otf.version,true) +otf.cleanup_aat=false  +local wildcard="*" +local default="dflt" +otf.tables.global_fields=table.tohash { +  "lookups", +  "glyphs", +  "subfonts", +  "luatex", +  "pfminfo", +  "cidinfo", +  "tables", +  "names", +  "unicodes", +  "names", +  "anchor_classes", +  "kern_classes", +  "gpos", +  "gsub" +} +otf.tables.valid_fields={ +  "anchor_classes", +  "ascent", +  "cache_version", +  "cidinfo", +  "copyright", +  "creationtime", +  "descent", +  "design_range_bottom", +  "design_range_top", +  "design_size", +  "encodingchanged", +  "extrema_bound", +  "familyname", +  "fontname", +  "fontstyle_id", +  "fontstyle_name", +  "fullname", +  "glyphs", +  "hasvmetrics", +  "head_optimized_for_cleartype", +  "horiz_base", +  "issans", +  "isserif", +  "italicangle", +  "kerns", +  "lookups", +  "macstyle", +  "modificationtime", +  "onlybitmaps", +  "origname", +  "os2_version", +  "pfminfo", +  "private", +  "serifcheck", +  "sfd_version", +  "strokedfont", +  "strokewidth", +  "subfonts", +  "table_version", +  "ttf_tables", +  "uni_interp", +  "uniqueid", +  "units_per_em", +  "upos", +  "use_typo_metrics", +  "uwidth", +  "validation_state", +  "verbose", +  "version", +  "vert_base", +  "weight", +  "weight_width_slope_only", +  "xuid", +} +local function load_featurefile(ff,featurefile) +  if featurefile then +    featurefile=resolvers.find_file(file.addsuffix(featurefile,'fea'),'fea') +    if featurefile and featurefile~="" then +      if trace_loading then +        logs.report("load otf","featurefile: %s",featurefile) +      end +      fontloader.apply_featurefile(ff,featurefile) +    end +  end +end +function otf.enhance(name,data,filename,verbose) +  local enhancer=otf.enhancers[name] +  if enhancer then +    if (verbose~=nil and verbose) or trace_loading then +      logs.report("load otf","enhance: %s (%s)",name,filename) +    end +    enhancer(data,filename) +  end +end +local enhancers={ +  "patch bugs", +  "merge cid fonts","prepare unicode","cleanup ttf tables","compact glyphs","reverse coverage", +  "cleanup aat","enrich with features","add some missing characters", +  "reorganize mark classes", +  "reorganize kerns", +  "flatten glyph lookups","flatten anchor tables","flatten feature tables", +  "simplify glyph lookups", +  "prepare luatex tables", +  "analyse features","rehash features", +  "analyse anchors","analyse marks","analyse unicodes","analyse subtables", +  "check italic correction","check math", +  "share widths", +  "strip not needed data", +  "migrate metadata", +  "check math parameters", +} +function otf.load(filename,format,sub,featurefile) +  local name=file.basename(file.removesuffix(filename)) +  local attr=lfs.attributes(filename) +  local size,time=attr.size or 0,attr.modification or 0 +  if featurefile then +    local fattr=lfs.attributes(featurefile) +    local fsize,ftime=fattr and fattr.size or 0,fattr and fattr.modification or 0 +    name=name.."@"..file.removesuffix(file.basename(featurefile))..ftime..fsize +  end +  if sub=="" then sub=false end +  local hash=name +  if sub then +    hash=hash.."-"..sub +  end +  hash=containers.cleanname(hash) +  local data=containers.read(otf.cache,hash) +  if not data or data.verbose~=fonts.verbose or data.size~=size or data.time~=time then +    logs.report("load otf","loading: %s (hash: %s)",filename,hash) +    local ff,messages +    if sub then +      ff,messages=fontloader.open(filename,sub) +    else +      ff,messages=fontloader.open(filename) +    end +    if trace_loading and messages and #messages>0 then +      if type(messages)=="string" then +        logs.report("load otf","warning: %s",messages) +      else +        for m=1,#messages do +          logs.report("load otf","warning: %s",tostring(messages[m])) +        end +      end +    else +      logs.report("load otf","font loaded okay") +    end +    if ff then +      load_featurefile(ff,featurefile) +      data=fontloader.to_table(ff) +      fontloader.close(ff) +      if data then +        logs.report("load otf","file size: %s",size) +        logs.report("load otf","enhancing ...") +        for e=1,#enhancers do +          otf.enhance(enhancers[e],data,filename) +          io.flush()  +        end +        if otf.pack and not fonts.verbose then +          otf.enhance("pack",data,filename) +        end +        data.size=size +        data.time=time +        data.verbose=fonts.verbose +        logs.report("load otf","saving in cache: %s",filename) +        data=containers.write(otf.cache,hash,data) +        collectgarbage("collect") +        data=containers.read(otf.cache,hash)  +        collectgarbage("collect") +      else +        logs.report("load otf","loading failed (table conversion error)") +      end +    else +      logs.report("load otf","loading failed (file read error)") +    end +  end +  if data then +    if trace_defining then +      logs.report("define font","loading from cache: %s",hash) +    end +    otf.enhance("unpack",data,filename,false)  +    otf.add_dimensions(data) +    if trace_sequences then +      otf.show_feature_order(data,filename) +    end +  end +  return data +end +function otf.add_dimensions(data) +  if data then +    local force=otf.notdef +    local luatex=data.luatex +    local defaultwidth=luatex.defaultwidth or 0 +    local defaultheight=luatex.defaultheight or 0 +    local defaultdepth=luatex.defaultdepth or 0 +    for _,d in next,data.glyphs do +      local bb,wd=d.boundingbox,d.width +      if not wd then +        d.width=defaultwidth +      elseif wd~=0 and d.class=="mark" then +        d.width=-wd +      end +      if force and not d.name then +        d.name=".notdef" +      end +      if bb then +        local ht,dp=bb[4],-bb[2] +        if ht==0 or ht<0 then +        else +          d.height=ht +        end +        if dp==0 or dp<0 then +        else +          d.depth=dp +        end +      end +    end +  end +end +function otf.show_feature_order(otfdata,filename) +  local sequences=otfdata.luatex.sequences +  if sequences and #sequences>0 then +    if trace_loading then +      logs.report("otf check","font %s has %s sequences",filename,#sequences) +      logs.report("otf check"," ") +    end +    for nos=1,#sequences do +      local sequence=sequences[nos] +      local typ=sequence.type or "no-type" +      local name=sequence.name or "no-name" +      local subtables=sequence.subtables or { "no-subtables" } +      local features=sequence.features +      if trace_loading then +        logs.report("otf check","%3i  %-15s  %-20s  [%s]",nos,name,typ,concat(subtables,",")) +      end +      if features then +        for feature,scripts in next,features do +          local tt={} +          for script,languages in next,scripts do +            local ttt={} +            for language,_ in next,languages do +              ttt[#ttt+1]=language +            end +            tt[#tt+1]=format("[%s: %s]",script,concat(ttt," ")) +          end +          if trace_loading then +            logs.report("otf check","       %s: %s",feature,concat(tt," ")) +          end +        end +      end +    end +    if trace_loading then +      logs.report("otf check","\n") +    end +  elseif trace_loading then +    logs.report("otf check","font %s has no sequences",filename) +  end +end +otf.enhancers["reorganize mark classes"]=function(data,filename) +  if data.mark_classes then +    local unicodes=data.luatex.unicodes +    local reverse={} +    for name,class in next,data.mark_classes do +      local t={} +      for s in gmatch(class,"[^ ]+") do +        local us=unicodes[s] +        if type(us)=="table" then +          for u=1,#us do +            t[us[u]]=true +          end +        else +          t[us]=true +        end +      end +      reverse[name]=t +    end +    data.luatex.markclasses=reverse +    data.mark_classes=nil +  end +end +otf.enhancers["prepare luatex tables"]=function(data,filename) +  data.luatex=data.luatex or {} +  local luatex=data.luatex +  luatex.filename=filename +  luatex.version=otf.version +  luatex.creator="context mkiv" +end +otf.enhancers["cleanup aat"]=function(data,filename) +  if otf.cleanup_aat then +  end +end +local function analyze_features(g,features) +  if g then +    local t,done={},{} +    for k=1,#g do +      local f=features or g[k].features +      if f then +        for k=1,#f do +          local tag=f[k].tag +          if not done[tag] then +            t[#t+1]=tag +            done[tag]=true +          end +        end +      end +    end +    if #t>0 then +      return t +    end +  end +  return nil +end +otf.enhancers["analyse features"]=function(data,filename) +end +otf.enhancers["rehash features"]=function(data,filename) +  local features={} +  data.luatex.features=features +  for k,what in next,otf.glists do +    local dw=data[what] +    if dw then +      local f={} +      features[what]=f +      for i=1,#dw do +        local d=dw[i] +        local dfeatures=d.features +        if dfeatures then +          for i=1,#dfeatures do +            local df=dfeatures[i] +            local tag=strip(lower(df.tag)) +            local ft=f[tag] if not ft then ft={} f[tag]=ft end +            local dscripts=df.scripts +            for script,languages in next,dscripts do +              script=strip(lower(script)) +              local fts=ft[script] if not fts then fts={} ft[script]=fts end +              for i=1,#languages do +                fts[strip(lower(languages[i]))]=true +              end +            end +          end +        end +      end +    end +  end +end +otf.enhancers["analyse anchors"]=function(data,filename) +  local classes=data.anchor_classes +  local luatex=data.luatex +  local anchor_to_lookup,lookup_to_anchor={},{} +  luatex.anchor_to_lookup,luatex.lookup_to_anchor=anchor_to_lookup,lookup_to_anchor +  if classes then +    for c=1,#classes do +      local class=classes[c] +      local anchor=class.name +      local lookups=class.lookup +      if type(lookups)~="table" then +        lookups={ lookups } +      end +      local a=anchor_to_lookup[anchor] +      if not a then a={} anchor_to_lookup[anchor]=a end +      for l=1,#lookups do +        local lookup=lookups[l] +        local l=lookup_to_anchor[lookup] +        if not l then l={} lookup_to_anchor[lookup]=l end +        l[anchor]=true +        a[lookup]=true +      end +    end +  end +end +otf.enhancers["analyse marks"]=function(data,filename) +  local glyphs=data.glyphs +  local marks={} +  data.luatex.marks=marks +  for unicode,index in next,data.luatex.indices do +    local glyph=glyphs[index] +    if glyph.class=="mark" then +      marks[unicode]=true +    end +  end +end +otf.enhancers["analyse unicodes"]=fonts.map.add_to_unicode +otf.enhancers["analyse subtables"]=function(data,filename) +  data.luatex=data.luatex or {} +  local luatex=data.luatex +  local sequences={} +  local lookups={} +  luatex.sequences=sequences +  luatex.lookups=lookups +  for _,g in next,{ data.gsub,data.gpos } do +    for k=1,#g do +      local gk=g[k] +      local typ=gk.type +      if typ=="gsub_contextchain" or typ=="gpos_contextchain" then +        gk.chain=1 +      elseif typ=="gsub_reversecontextchain" or typ=="gpos_reversecontextchain" then +        gk.chain=-1 +      else +        gk.chain=0 +      end +      local features=gk.features +      if features then +        sequences[#sequences+1]=gk +        local t={} +        for f=1,#features do +          local feature=features[f] +          local hash={} +          for s,languages in next,feature.scripts do +            s=lower(s) +            local h=hash[s] +            if not h then h={} hash[s]=h end +            for l=1,#languages do +              h[strip(lower(languages[l]))]=true +            end +          end +          t[feature.tag]=hash +        end +        gk.features=t +      else +        lookups[gk.name]=gk +        gk.name=nil +      end +      local subtables=gk.subtables +      if subtables then +        local t={} +        for s=1,#subtables do +          local subtable=subtables[s] +          local name=subtable.name +          t[#t+1]=name +        end +        gk.subtables=t +      end +      local flags=gk.flags +      if flags then +        gk.flags={  +          (flags.ignorecombiningmarks and "mark")   or false, +          (flags.ignoreligatures   and "ligature") or false, +          (flags.ignorebaseglyphs   and "base")   or false, +           flags.r2l                 or false, +        } +        if flags.mark_class then +          gk.markclass=luatex.markclasses[flags.mark_class] +        end +      end +    end +  end +end +otf.enhancers["merge cid fonts"]=function(data,filename) +  if data.subfonts then +    if data.glyphs and next(data.glyphs) then +      logs.report("load otf","replacing existing glyph table due to subfonts") +    end +    local cidinfo=data.cidinfo +    local verbose=fonts.verbose +    if cidinfo.registry then +      local cidmap,cidname=fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) +      if cidmap then +        cidinfo.usedname=cidmap.usedname +        local glyphs,uni_to_int,int_to_uni,nofnames,nofunicodes={},{},{},0,0 +        local unicodes,names=cidmap.unicodes,cidmap.names +        for n,subfont in next,data.subfonts do +          for index,g in next,subfont.glyphs do +            if not next(g) then +            else +              local unicode,name=unicodes[index],names[index] +              g.cidindex=n +              g.boundingbox=g.boundingbox  +              g.name=g.name or name or "unknown" +              if unicode then +                uni_to_int[unicode]=index +                int_to_uni[index]=unicode +                nofunicodes=nofunicodes+1 +                g.unicode=unicode +              elseif name then +                nofnames=nofnames+1 +                g.unicode=-1 +              end +              glyphs[index]=g +            end +          end +          subfont.glyphs=nil +        end +        if trace_loading then +          logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames) +        end +        data.glyphs=glyphs +        data.map=data.map or {} +        data.map.map=uni_to_int +        data.map.backmap=int_to_uni +      elseif trace_loading then +        logs.report("load otf","unable to remap cid font, missing cid file for %s",filename) +      end +    elseif trace_loading then +      logs.report("load otf","font %s has no glyphs",filename) +    end +  end +end +otf.enhancers["prepare unicode"]=function(data,filename) +  local luatex=data.luatex +  if not luatex then luatex={} data.luatex=luatex end +  local indices,unicodes,multiples,internals={},{},{},{} +  local glyphs=data.glyphs +  local mapmap=data.map +  if not mapmap then +    logs.report("load otf","no map in %s",filename) +    mapmap={} +    data.map={ map=mapmap } +  elseif not mapmap.map then +    logs.report("load otf","no unicode map in %s",filename) +    mapmap={} +    data.map.map=mapmap +  else +    mapmap=mapmap.map +  end +  local criterium=fonts.private +  local private=fonts.private +  for index,glyph in next,glyphs do +    if index>0 then +      local name=glyph.name +      if name then +        local unicode=glyph.unicode +        if unicode==-1 or unicode>=criterium then +          glyph.unicode=private +          indices[private]=index +          unicodes[name]=private +          internals[index]=true +          if trace_private then +            logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) +          end +          private=private+1 +        else +          indices[unicode]=index +          unicodes[name]=unicode +        end +      end +    end +  end +  for unicode,index in next,mapmap do +    if not internals[index] then +      local name=glyphs[index].name +      if name then +        local un=unicodes[name] +        if not un then +          unicodes[name]=unicode  +        elseif type(un)=="number" then +          if un~=unicode then +            multiples[#multiples+1]=name +            unicodes[name]={ un,unicode } +            indices[unicode]=index +          end +        else +          local ok=false +          for u=1,#un do +            if un[u]==unicode then +              ok=true +              break +            end +          end +          if not ok then +            multiples[#multiples+1]=name +            un[#un+1]=unicode +            indices[unicode]=index +          end +        end +      end +    end +  end +  if trace_loading then +    if #multiples>0 then +      logs.report("load otf","%s glyph are reused: %s",#multiples,concat(multiples," ")) +    else +      logs.report("load otf","no glyph are reused") +    end +  end +  luatex.indices=indices +  luatex.unicodes=unicodes +  luatex.private=private +end +otf.enhancers["cleanup ttf tables"]=function(data,filename) +  local ttf_tables=data.ttf_tables +  if ttf_tables then +    for k=1,#ttf_tables do +      if ttf_tables[k].data then ttf_tables[k].data="deleted" end +    end +  end +  data.ttf_tab_saved=nil +end +otf.enhancers["compact glyphs"]=function(data,filename) +  table.compact(data.glyphs)  +  if data.subfonts then +    for _,subfont in next,data.subfonts do +      table.compact(subfont.glyphs)  +    end +  end +end +otf.enhancers["reverse coverage"]=function(data,filename) +  if data.lookups then +    for _,v in next,data.lookups do +      if v.rules then +        for _,vv in next,v.rules do +          local c=vv.coverage +          if c and c.before then +            c.before=table.reverse(c.before) +          end +        end +      end +    end +  end +end +otf.enhancers["check italic correction"]=function(data,filename) +  local glyphs=data.glyphs +  local ok=false +  for index,glyph in next,glyphs do +    local ic=glyph.italic_correction +    if ic then +      if ic~=0 then +        glyph.italic=ic +      end +      glyph.italic_correction=nil +      ok=true +    end +  end +  otf.tables.valid_fields[#otf.tables.valid_fields+1]="has_italic" +  data.has_italic=true +end +otf.enhancers["check math"]=function(data,filename) +  if data.math then +    local glyphs=data.glyphs +    local unicodes=data.luatex.unicodes +    for index,glyph in next,glyphs do +      local mk=glyph.mathkern +      local hv=glyph.horiz_variants +      local vv=glyph.vert_variants +      if mk or hv or vv then +        local math={} +        glyph.math=math +        if mk then +          for k,v in next,mk do +            if not next(v) then +              mk[k]=nil +            end +          end +          math.kerns=mk +          glyph.mathkern=nil +        end +        if hv then +          math.horiz_variants=hv.variants +          local p=hv.parts +          if p and #p>0 then +            for i=1,#p do +              local pi=p[i] +              pi.glyph=unicodes[pi.component] or 0 +            end +            math.horiz_parts=p +          end +          local ic=hv.italic_correction +          if ic and ic~=0 then +            math.horiz_italic_correction=ic +          end +          glyph.horiz_variants=nil +        end +        if vv then +          local uc=unicodes[index] +          math.vert_variants=vv.variants +          local p=vv.parts +          if p and #p>0 then +            for i=1,#p do +              local pi=p[i] +              pi.glyph=unicodes[pi.component] or 0 +            end +            math.vert_parts=p +          end +          local ic=vv.italic_correction +          if ic and ic~=0 then +            math.vert_italic_correction=ic +          end +          glyph.vert_variants=nil +        end +        local ic=glyph.italic_correction +        if ic then +          if ic~=0 then +            math.italic_correction=ic +          end +          glyph.italic_correction=nil +        end +      end +    end +  end +end +otf.enhancers["share widths"]=function(data,filename) +  local glyphs=data.glyphs +  local widths={} +  for index,glyph in next,glyphs do +    local width=glyph.width +    widths[width]=(widths[width] or 0)+1 +  end +  local wd,most=0,1 +  for k,v in next,widths do +    if v>most then +      wd,most=k,v +    end +  end +  if most>1000 then +    if trace_loading then +      logs.report("load otf","most common width: %s (%s times), sharing (cjk font)",wd,most) +    end +    for k,v in next,glyphs do +      if v.width==wd then +        v.width=nil +      end +    end +    data.luatex.defaultwidth=wd +  end +end +otf.enhancers["reorganize kerns"]=function(data,filename) +  local glyphs,mapmap,unicodes=data.glyphs,data.luatex.indices,data.luatex.unicodes +  local mkdone=false +  local function do_it(lookup,first_unicode,kerns) +    local glyph=glyphs[mapmap[first_unicode]] +    if glyph then +      local mykerns=glyph.mykerns +      if not mykerns then +        mykerns={}  +        glyph.mykerns=mykerns +      end +      local lookupkerns=mykerns[lookup] +      if not lookupkerns then +        lookupkerns={} +        mykerns[lookup]=lookupkerns +      end +      for second_unicode,kern in next,kerns do +        lookupkerns[second_unicode]=kern +      end +    elseif trace_loading then +      logs.report("load otf","no glyph data for U+%04X",first_unicode) +    end +  end +  for index,glyph in next,glyphs do +    if glyph.kerns then +      local mykerns={} +      for k,v in next,glyph.kerns do +        local vc,vo,vl=v.char,v.off,v.lookup +        if vc and vo and vl then  +          local uvc=unicodes[vc] +          if not uvc then +            if trace_loading then +              logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) +            end +          else +            if type(vl)~="table" then +              vl={ vl } +            end +            for l=1,#vl do +              local vll=vl[l] +              local mkl=mykerns[vll] +              if not mkl then +                mkl={} +                mykerns[vll]=mkl +              end +              if type(uvc)=="table" then +                for u=1,#uvc do +                  mkl[uvc[u]]=vo +                end +              else +                mkl[uvc]=vo +              end +            end +          end +        end +      end +      glyph.mykerns=mykerns +      glyph.kerns=nil  +      mkdone=true +    end +  end +  if trace_loading and mkdone then +    logs.report("load otf","replacing 'kerns' tables by 'mykerns' tables") +  end +  if data.kerns then +    if trace_loading then +      logs.report("load otf","removing global 'kern' table") +    end +    data.kerns=nil +  end +  local dgpos=data.gpos +  if dgpos then +    local separator=lpeg.P(" ") +    local other=((1-separator)^0)/unicodes +    local splitter=lpeg.Ct(other*(separator*other)^0) +    for gp=1,#dgpos do +      local gpos=dgpos[gp] +      local subtables=gpos.subtables +      if subtables then +        for s=1,#subtables do +          local subtable=subtables[s] +          local kernclass=subtable.kernclass  +          if kernclass then  +            local split={}  +            for k=1,#kernclass do +              local kcl=kernclass[k] +              local firsts,seconds,offsets,lookups=kcl.firsts,kcl.seconds,kcl.offsets,kcl.lookup  +              if type(lookups)~="table" then +                lookups={ lookups } +              end +              local maxfirsts,maxseconds=#firsts,#seconds +              for _,s in next,firsts do +                split[s]=split[s] or lpegmatch(splitter,s) +              end +              for _,s in next,seconds do +                split[s]=split[s] or lpegmatch(splitter,s) +              end +              for l=1,#lookups do +                local lookup=lookups[l] +                for fk=1,#firsts do +                  local fv=firsts[fk] +                  local splt=split[fv] +                  if splt then +                    local kerns,baseoffset={},(fk-1)*maxseconds +                    for sk=2,maxseconds do +                      local sv=seconds[sk] +                      local splt=split[sv] +                      if splt then +                        local offset=offsets[baseoffset+sk] +                        if offset then +                          for i=1,#splt do +                            local second_unicode=splt[i] +                            if tonumber(second_unicode) then +                              kerns[second_unicode]=offset +                            else for s=1,#second_unicode do +                              kerns[second_unicode[s]]=offset +                            end end +                          end +                        end +                      end +                    end +                    for i=1,#splt do +                      local first_unicode=splt[i] +                      if tonumber(first_unicode) then +                        do_it(lookup,first_unicode,kerns) +                      else for f=1,#first_unicode do +                        do_it(lookup,first_unicode[f],kerns) +                      end end +                    end +                  end +                end +              end +            end +            subtable.comment="The kernclass table is merged into mykerns in the indexed glyph tables." +            subtable.kernclass={} +          end +        end +      end +    end +  end +end +otf.enhancers["strip not needed data"]=function(data,filename) +  local verbose=fonts.verbose +  local int_to_uni=data.luatex.unicodes +  for k,v in next,data.glyphs do +    local d=v.dependents +    if d then v.dependents=nil end +    local a=v.altuni +    if a then v.altuni=nil end +    if verbose then +      local code=int_to_uni[k] +      if code then +        local vu=v.unicode +        if not vu then +          v.unicode=code +        elseif type(vu)=="table" then +          if vu[#vu]==code then +          else +            vu[#vu+1]=code +          end +        elseif vu~=code then +          v.unicode={ vu,code } +        end +      end +    else +      v.unicode=nil +      v.index=nil +    end +  end +  data.luatex.comment="Glyph tables have their original index. When present, mykern tables are indexed by unicode." +  data.map=nil +  data.names=nil  +  data.glyphcnt=nil +  data.glyphmax=nil +  if true then +    data.gpos=nil +    data.gsub=nil +    data.anchor_classes=nil +  end +end +otf.enhancers["migrate metadata"]=function(data,filename) +  local global_fields=otf.tables.global_fields +  local metadata={} +  for k,v in next,data do +    if not global_fields[k] then +      metadata[k]=v +      data[k]=nil +    end +  end +  data.metadata=metadata +  local pfminfo=data.pfminfo +  metadata.isfixedpitch=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"]=="Monospaced") +  metadata.charwidth=pfminfo and pfminfo.avgwidth +end +local private_math_parameters={ +  "FractionDelimiterSize", +  "FractionDelimiterDisplayStyleSize", +} +otf.enhancers["check math parameters"]=function(data,filename) +  local mathdata=data.metadata.math +  if mathdata then +    for m=1,#private_math_parameters do +      local pmp=private_math_parameters[m] +      if not mathdata[pmp] then +        if trace_loading then +          logs.report("load otf","setting math parameter '%s' to 0",pmp) +        end +        mathdata[pmp]=0 +      end +    end +  end +end +otf.enhancers["flatten glyph lookups"]=function(data,filename) +  for k,v in next,data.glyphs do +    local lookups=v.lookups +    if lookups then +      for kk,vv in next,lookups do +        for kkk=1,#vv do +          local vvv=vv[kkk] +          local s=vvv.specification +          if s then +            local t=vvv.type +            if t=="ligature" then +              vv[kkk]={ "ligature",s.components,s.char } +            elseif t=="alternate" then +              vv[kkk]={ "alternate",s.components } +            elseif t=="substitution" then +              vv[kkk]={ "substitution",s.variant } +            elseif t=="multiple" then +              vv[kkk]={ "multiple",s.components } +            elseif t=="position" then +              vv[kkk]={ "position",{ s.x or 0,s.y or 0,s.h or 0,s.v or 0 } } +            elseif t=="pair" then +              local one,two,paired=s.offsets[1],s.offsets[2],s.paired or "" +              if one then +                if two then +                  vv[kkk]={ "pair",paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } } +                else +                  vv[kkk]={ "pair",paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } } +                end +              else +                if two then +                  vv[kkk]={ "pair",paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} }  +                else +                  vv[kkk]={ "pair",paired } +                end +              end +            else +              if trace_loading then +                logs.report("load otf","flattening needed, report to context list") +              end +              for a,b in next,s do +                if trace_loading and vvv[a] then +                  logs.report("load otf","flattening conflict, report to context list") +                end +                vvv[a]=b +              end +              vvv.specification=nil +            end +          end +        end +      end +    end +  end +end +otf.enhancers["simplify glyph lookups"]=function(data,filename) +  for k,v in next,data.glyphs do +    local lookups=v.lookups +    if lookups then +      local slookups,mlookups +      for kk,vv in next,lookups do +        if #vv==1 then +          if not slookups then +            slookups={} +            v.slookups=slookups +          end +          slookups[kk]=vv[1] +        else +          if not mlookups then +            mlookups={} +            v.mlookups=mlookups +          end +          mlookups[kk]=vv +        end +      end +      v.lookups=nil +    end +  end +end +otf.enhancers["flatten anchor tables"]=function(data,filename) +  for k,v in next,data.glyphs do +    if v.anchors then +      for kk,vv in next,v.anchors do +        for kkk,vvv in next,vv do +          if vvv.x or vvv.y then +            vv[kkk]={ vvv.x or 0,vvv.y or 0 } +          else +            for kkkk=1,#vvv do +              local vvvv=vvv[kkkk] +              vvv[kkkk]={ vvvv.x or 0,vvvv.y or 0 } +            end +          end +        end +      end +    end +  end +end +otf.enhancers["flatten feature tables"]=function(data,filename) +  for _,tag in next,otf.glists do +    if data[tag] then +      if trace_loading then +        logs.report("load otf","flattening %s table",tag) +      end +      for k,v in next,data[tag] do +        local features=v.features +        if features then +          for kk=1,#features do +            local vv=features[kk] +            local t={} +            local scripts=vv.scripts +            for kkk=1,#scripts do +              local vvv=scripts[kkk] +              t[vvv.script]=vvv.langs +            end +            vv.scripts=t +          end +        end +      end +    end +  end +end +otf.enhancers.patches=otf.enhancers.patches or {} +otf.enhancers["patch bugs"]=function(data,filename) +  local basename=file.basename(lower(filename)) +  for pattern,action in next,otf.enhancers.patches do +    if find(basename,pattern) then +      action(data,filename) +    end +  end +end +fonts.otf.enhancers["enrich with features"]=function(data,filename) +end +function otf.features.register(name,default) +  otf.features.list[#otf.features.list+1]=name +  otf.features.default[name]=default +end +function otf.set_features(tfmdata,features) +  local processes={} +  if features and next(features) then +    local lists={  +      fonts.triggers, +      fonts.processors, +      fonts.manipulators, +    } +    local mode=tfmdata.mode or fonts.mode  +    local initializers=fonts.initializers +    local fi=initializers[mode] +    if fi then +      local fiotf=fi.otf +      if fiotf then +        local done={} +        for l=1,4 do +          local list=lists[l] +          if list then +            for i=1,#list do +              local f=list[i] +              local value=features[f] +              if value and fiotf[f] then  +                if not done[f] then  +                  if trace_features then +                    logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.fullname or 'unknown') +                  end +                  fiotf[f](tfmdata,value)  +                  mode=tfmdata.mode or fonts.mode  +                  local im=initializers[mode] +                  if im then +                    fiotf=initializers[mode].otf +                  end +                  done[f]=true +                end +              end +            end +          end +        end +      end +    end +    local fm=fonts.methods[mode]  +    if fm then +      local fmotf=fm.otf +      if fmotf then +        for l=1,4 do +          local list=lists[l] +          if list then +            for i=1,#list do +              local f=list[i] +              if fmotf[f] then  +                if trace_features then +                  logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown',tfmdata.fullname or 'unknown') +                end +                processes[#processes+1]=fmotf[f] +              end +            end +          end +        end +      end +    else +    end +  end +  return processes,features +end +function otf.otf_to_tfm(specification) +  local name=specification.name +  local sub=specification.sub +  local filename=specification.filename +  local format=specification.format +  local features=specification.features.normal +  local cache_id=specification.hash +  local tfmdata=containers.read(tfm.cache,cache_id) +  if not tfmdata then +    local otfdata=otf.load(filename,format,sub,features and features.featurefile) +    if otfdata and next(otfdata) then +      otfdata.shared=otfdata.shared or { +        featuredata={}, +        anchorhash={}, +        initialized=false, +      } +      tfmdata=otf.copy_to_tfm(otfdata,cache_id) +      if tfmdata and next(tfmdata) then +        tfmdata.unique=tfmdata.unique or {} +        tfmdata.shared=tfmdata.shared or {}  +        local shared=tfmdata.shared +        shared.otfdata=otfdata +        shared.features=features  +        shared.dynamics={} +        shared.processes={} +        shared.set_dynamics=otf.set_dynamics +        tfmdata.luatex=otfdata.luatex +        tfmdata.indices=otfdata.luatex.indices +        tfmdata.unicodes=otfdata.luatex.unicodes +        tfmdata.marks=otfdata.luatex.marks +        tfmdata.originals=otfdata.luatex.originals +        tfmdata.changed={} +        tfmdata.has_italic=otfdata.metadata.has_italic +        if not tfmdata.language then tfmdata.language='dflt' end +        if not tfmdata.script  then tfmdata.script='dflt' end +        shared.processes,shared.features=otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) +      end +    end +    containers.write(tfm.cache,cache_id,tfmdata) +  end +  return tfmdata +end +fonts.formats.dfont="truetype" +fonts.formats.ttc="truetype" +fonts.formats.ttf="truetype" +fonts.formats.otf="opentype" +function otf.copy_to_tfm(data,cache_id)  +  if data then +    local glyphs,pfminfo,metadata=data.glyphs or {},data.pfminfo or {},data.metadata or {} +    local luatex=data.luatex +    local unicodes=luatex.unicodes  +    local indices=luatex.indices +    local characters,parameters,math_parameters,descriptions={},{},{},{} +    local designsize=metadata.designsize or metadata.design_size or 100 +    if designsize==0 then +      designsize=100 +    end +    local spaceunits,spacer=500,"space" +    for u,i in next,indices do +      characters[u]={}  +      descriptions[u]=glyphs[i] +    end +    if metadata.math then +      for name,value in next,metadata.math do +        math_parameters[name]=value +      end +      for u,char in next,characters do +        local d=descriptions[u] +        local m=d.math +        if m then +          local variants,parts,c,uc=m.horiz_variants,m.horiz_parts,char,u +          if variants then +            for n in gmatch(variants,"[^ ]+") do +              local un=unicodes[n] +              if un and uc~=un then +                c.next=un +                c=characters[un] +				uc=un +              end +            end +            c.horiz_variants=parts +          elseif parts then +            c.horiz_variants=parts +          end +          local variants,parts,c,uc=m.vert_variants,m.vert_parts,char,u +          if variants then +            for n in gmatch(variants,"[^ ]+") do +              local un=unicodes[n] +              if un and uc~=un then +                c.next=un +                c=characters[un] +				uc=un +              end +            end  +            c.vert_variants=parts +          elseif parts then +            c.vert_variants=parts +          end +          local italic_correction=m.vert_italic_correction +          if italic_correction then +            c.vert_italic_correction=italic_correction +          end +          local kerns=m.kerns +          if kerns then +            char.mathkerns=kerns +          end +        end +      end +    end +    local endash,emdash,space=0x20,0x2014,"space"  +    if metadata.isfixedpitch then +      if descriptions[endash] then +        spaceunits,spacer=descriptions[endash].width,"space" +      end +      if not spaceunits and descriptions[emdash] then +        spaceunits,spacer=descriptions[emdash].width,"emdash" +      end +      if not spaceunits and metadata.charwidth then +        spaceunits,spacer=metadata.charwidth,"charwidth" +      end +    else +      if descriptions[endash] then +        spaceunits,spacer=descriptions[endash].width,"space" +      end +      if not spaceunits and descriptions[emdash] then +        spaceunits,spacer=descriptions[emdash].width/2,"emdash/2" +      end +      if not spaceunits and metadata.charwidth then +        spaceunits,spacer=metadata.charwidth,"charwidth" +      end +    end +    spaceunits=tonumber(spaceunits) or tfm.units/2 +    local filename=fonts.tfm.checked_filename(luatex) +    local fontname=metadata.fontname +    local fullname=metadata.fullname or fontname +    local cidinfo=data.cidinfo +    local units=metadata.units_per_em or 1000 +    cidinfo.registry=cidinfo and cidinfo.registry or "" +    parameters.slant=0 +    parameters.space=spaceunits      +    parameters.space_stretch=units/2   +    parameters.space_shrink=1*units/3  +    parameters.x_height=2*units/5  +    parameters.quad=units    +    if spaceunits<2*units/5 then +    end +    local italicangle=metadata.italicangle +    if italicangle then  +      parameters.slant=parameters.slant-math.round(math.tan(italicangle*math.pi/180)) +    end +    if metadata.isfixedpitch then +      parameters.space_stretch=0 +      parameters.space_shrink=0 +    elseif otf.syncspace then  +      parameters.space_stretch=spaceunits/2 +      parameters.space_shrink=spaceunits/3 +    end +    parameters.extra_space=parameters.space_shrink  +    if pfminfo.os2_xheight and pfminfo.os2_xheight>0 then +      parameters.x_height=pfminfo.os2_xheight +    else +      local x=0x78  +      if x then +        local x=descriptions[x] +        if x then +          parameters.x_height=x.height +        end +      end +    end +    return { +      characters=characters, +      parameters=parameters, +      math_parameters=math_parameters, +      descriptions=descriptions, +      indices=indices, +      unicodes=unicodes, +      type="real", +      direction=0, +      boundarychar_label=0, +      boundarychar=65536, +      designsize=(designsize/10)*65536, +      spacer="500 units", +      encodingbytes=2, +      filename=filename, +      fontname=fontname, +      fullname=fullname, +      psname=fontname or fullname, +      name=filename or fullname, +      units=units, +      format=fonts.fontformat(filename,"opentype"), +      cidinfo=cidinfo, +      ascender=abs(metadata.ascent or 0), +      descender=abs(metadata.descent or 0), +      spacer=spacer, +      italicangle=italicangle, +    } +  else +    return nil +  end +end +otf.features.register('mathsize') +function tfm.read_from_open_type(specification) +  local tfmtable=otf.otf_to_tfm(specification) +  if tfmtable then +    local otfdata=tfmtable.shared.otfdata +    tfmtable.name=specification.name +    tfmtable.sub=specification.sub +    local s=specification.size +    local m=otfdata.metadata.math +    if m then +      local f=specification.features +      if f then +        local f=f.normal +        if f and f.mathsize then +          local mathsize=specification.mathsize or 0 +          if mathsize==2 then +            local p=m.ScriptPercentScaleDown +            if p then +              local ps=p*specification.textsize/100 +              if trace_math then +                logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) +              end +              s=ps +            end +          elseif mathsize==3 then +            local p=m.ScriptScriptPercentScaleDown +            if p then +              local ps=p*specification.textsize/100 +              if trace_math then +                logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) +              end +              s=ps +            end +          end +        end +      end +    end +    tfmtable=tfm.scale(tfmtable,s,specification.relativeid) +    if tfm.fontname_mode=="specification" then +      local specname=specification.specification +      if specname then +        tfmtable.name=specname +        if trace_defining then +          logs.report("define font","overloaded fontname: '%s'",specname) +        end +      end +    end +    fonts.logger.save(tfmtable,file.extname(specification.filename),specification) +  end +  return tfmtable +end +function otf.collect_lookups(otfdata,kind,script,language) +  local sequences=otfdata.luatex.sequences +  if sequences then +    local featuremap,featurelist={},{} +    for s=1,#sequences do +      local sequence=sequences[s] +      local features=sequence.features +      features=features and features[kind] +      features=features and (features[script]  or features[default] or features[wildcard]) +      features=features and (features[language] or features[default] or features[wildcard]) +      if features then +        local subtables=sequence.subtables +        if subtables then +          for s=1,#subtables do +            local ss=subtables[s] +            if not featuremap[s] then +              featuremap[ss]=true +              featurelist[#featurelist+1]=ss +            end +          end +        end +      end +    end +    if #featurelist>0 then +      return featuremap,featurelist +    end +  end +  return nil,nil +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otd']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end) +fonts=fonts   or {} +fonts.otf=fonts.otf or {} +local otf=fonts.otf +local fontdata=fonts.ids +otf.features=otf.features     or {} +otf.features.default=otf.features.default or {} +local context_setups=fonts.define.specify.context_setups +local context_numbers=fonts.define.specify.context_numbers +local a_to_script={} otf.a_to_script=a_to_script +local a_to_language={} otf.a_to_language=a_to_language +function otf.set_dynamics(font,dynamics,attribute) +  local features=context_setups[context_numbers[attribute]]  +  if features then +    local script=features.script  or 'dflt' +    local language=features.language or 'dflt' +    local ds=dynamics[script] +    if not ds then +      ds={} +      dynamics[script]=ds +    end +    local dsl=ds[language] +    if not dsl then +      dsl={} +      ds[language]=dsl +    end +    local dsla=dsl[attribute] +    if dsla then +      return dsla +    else +      local tfmdata=fontdata[font] +      a_to_script [attribute]=script +      a_to_language[attribute]=language +      local saved={ +        script=tfmdata.script, +        language=tfmdata.language, +        mode=tfmdata.mode, +        features=tfmdata.shared.features +      } +      tfmdata.mode="node" +      tfmdata.language=language +      tfmdata.script=script +      tfmdata.shared.features={} +      local set=fonts.define.check(features,otf.features.default) +      dsla=otf.set_features(tfmdata,set) +      if trace_dynamics then +        logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s, set: %s",context_numbers[attribute],attribute,script,language,table.sequenced(set)) +      end +      tfmdata.script=saved.script +      tfmdata.language=saved.language +      tfmdata.mode=saved.mode +      tfmdata.shared.features=saved.features +      dynamics[script][language][attribute]=dsla  +      return dsla +    end +  end +  return nil  +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-oti']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local lower=string.lower +local otf=fonts.otf +otf.default_language='latn' +otf.default_script='dflt' +local languages=otf.tables.languages +local scripts=otf.tables.scripts +function otf.features.language(tfmdata,value) +  if value then +    value=lower(value) +    if languages[value] then +      tfmdata.language=value +    end +  end +end +function otf.features.script(tfmdata,value) +  if value then +    value=lower(value) +    if scripts[value] then +      tfmdata.script=value +    end +  end +end +function otf.features.mode(tfmdata,value) +  if value then +    tfmdata.mode=lower(value) +  end +end +fonts.initializers.base.otf.language=otf.features.language +fonts.initializers.base.otf.script=otf.features.script +fonts.initializers.base.otf.mode=otf.features.mode +fonts.initializers.base.otf.method=otf.features.mode +fonts.initializers.node.otf.language=otf.features.language +fonts.initializers.node.otf.script=otf.features.script +fonts.initializers.node.otf.mode=otf.features.mode +fonts.initializers.node.otf.method=otf.features.mode +otf.features.register("features",true)    +table.insert(fonts.processors,"features")  + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otb']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local concat=table.concat +local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local type,next,tonumber,tostring=type,next,tonumber,tostring +local lpegmatch=lpeg.match +local otf=fonts.otf +local tfm=fonts.tfm +local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end) +local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end) +local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end) +local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end) +local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end) +local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) +local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) +local wildcard="*" +local default="dflt" +local split_at_space=lpeg.Ct(lpeg.splitat(" "))  +local pcache,fcache={},{}  +local function gref(descriptions,n) +  if type(n)=="number" then +    local name=descriptions[n].name +    if name then +      return format("U+%04X (%s)",n,name) +    else +      return format("U+%04X") +    end +  elseif n then +    local num,nam={},{} +    for i=1,#n do +      local ni=n[i] +      num[i]=format("U+%04X",ni) +      nam[i]=descriptions[ni].name or "?" +    end +    return format("%s (%s)",concat(num," "),concat(nam," ")) +  else +    return "?" +  end +end +local function cref(kind,lookupname) +  if lookupname then +    return format("feature %s, lookup %s",kind,lookupname) +  else +    return format("feature %s",kind) +  end +end +local function resolve_ligatures(tfmdata,ligatures,kind) +  kind=kind or "unknown" +  local unicodes=tfmdata.unicodes +  local characters=tfmdata.characters +  local descriptions=tfmdata.descriptions +  local changed=tfmdata.changed +  local done={} +  while true do +    local ok=false +    for k,v in next,ligatures do +      local lig=v[1] +      if not done[lig] then +        local ligs=lpegmatch(split_at_space,lig) +        if #ligs==2 then +          local uc=v[2] +          local c,f,s=characters[uc],ligs[1],ligs[2] +          local uft,ust=unicodes[f] or 0,unicodes[s] or 0 +          if not uft or not ust then +            logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) +          else +            if type(uft)=="number" then uft={ uft } end +            if type(ust)=="number" then ust={ ust } end +            for ufi=1,#uft do +              local uf=uft[ufi] +              for usi=1,#ust do +                local us=ust[usi] +                if changed[uf] or changed[us] then +                  if trace_baseinit and trace_ligatures then +                    logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) +                  end +                else +                  local first,second=characters[uf],us +                  if first and second then +                    local t=first.ligatures +                    if not t then +                      t={} +                      first.ligatures=t +                    end +                    if type(uc)=="number" then +                      t[second]={ type=0,char=uc } +                    else +                      t[second]={ type=0,char=uc[1] }  +                    end +                    if trace_baseinit and trace_ligatures then +                      logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) +                    end +                  end +                end +              end +            end +          end +          ok,done[lig]=true,descriptions[uc].name +        end +      end +    end +    if ok then +      for d,n in next,done do +        local pattern=pcache[d] if not pattern then pattern="^("..d..") "       pcache[d]=pattern end +        local fnc=fcache[n] if not fnc   then fnc=function() return n.." " end fcache[n]=fnc   end +        for k,v in next,ligatures do +          v[1]=gsub(v[1],pattern,fnc) +        end +      end +    else +      break +    end +  end +end +local splitter=lpeg.splitat(" ") +local function prepare_base_substitutions(tfmdata,kind,value)  +  if value then +    local otfdata=tfmdata.shared.otfdata +    local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) +    if validlookups then +      local ligatures={} +      local unicodes=tfmdata.unicodes  +      local indices=tfmdata.indices +      local characters=tfmdata.characters +      local descriptions=tfmdata.descriptions +      local changed=tfmdata.changed +      local actions={ +        substitution=function(p,lookup,k,glyph,unicode) +          local pv=p[2]  +          if pv then +            local upv=unicodes[pv] +            if upv then +              if type(upv)=="table" then +                upv=upv[1] +              end +              if characters[upv] then +                if trace_baseinit and trace_singles then +                  logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) +                end +                changed[k]=upv +              end +            end +          end +        end, +        alternate=function(p,lookup,k,glyph,unicode) +          local pc=p[2]  +          if pc then +            if value==1 then +              pc=lpegmatch(splitter,pc) +            elseif value==2 then +              local a,b=lpegmatch(splitter,pc) +              pc=b or a +            else +              pc={ lpegmatch(splitter,pc) } +              pc=pc[value] or pc[#pc] +            end +            if pc then +              local upc=unicodes[pc] +              if upc then +                if type(upc)=="table" then +                  upc=upc[1] +                end +                if characters[upc] then +                  if trace_baseinit and trace_alternatives then +                    logs.report("define otf","%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) +                  end +                  changed[k]=upc +                end +              end +            end +          end +        end, +        ligature=function(p,lookup,k,glyph,unicode) +          local pc=p[2] +          if pc then +            if trace_baseinit and trace_ligatures then +              local upc={ lpegmatch(splitter,pc) } +              for i=1,#upc do upc[i]=unicodes[upc[i]] end +              logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) +            end +            ligatures[#ligatures+1]={ pc,k } +          end +        end, +      } +      for k,c in next,characters do +        local glyph=descriptions[k] +        local lookups=glyph.slookups +        if lookups then +          for l=1,#lookuplist do +            local lookup=lookuplist[l] +            local p=lookups[lookup] +            if p then +              local a=actions[p[1]] +              if a then +                a(p,lookup,k,glyph,unicode) +              end +            end +          end +        end +        local lookups=glyph.mlookups +        if lookups then +          for l=1,#lookuplist do +            local lookup=lookuplist[l] +            local ps=lookups[lookup] +            if ps then +              for i=1,#ps do +                local p=ps[i] +                local a=actions[p[1]] +                if a then +                  a(p,lookup,k,glyph,unicode) +                end +              end +            end +          end +        end +      end +      resolve_ligatures(tfmdata,ligatures,kind) +    end +  else +    tfmdata.ligatures=tfmdata.ligatures or {}  +  end +end +local function prepare_base_kerns(tfmdata,kind,value)  +  if value then +    local otfdata=tfmdata.shared.otfdata +    local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) +    if validlookups then +      local unicodes=tfmdata.unicodes  +      local indices=tfmdata.indices +      local characters=tfmdata.characters +      local descriptions=tfmdata.descriptions +      local sharedkerns={} +      for u,chr in next,characters do +        local d=descriptions[u] +        if d then +          local dk=d.mykerns  +          if dk then +            local s=sharedkerns[dk] +            if s==false then +            elseif s then +              chr.kerns=s +            else +              local t,done=chr.kerns or {},false +              for l=1,#lookuplist do +                local lookup=lookuplist[l] +                local kerns=dk[lookup] +                if kerns then +                  for k,v in next,kerns do +                    if v~=0 and not t[k] then  +                      t[k],done=v,true +                      if trace_baseinit and trace_kerns then +                        logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) +                      end +                    end +                  end +                end +              end +              if done then +                sharedkerns[dk]=t +                chr.kerns=t  +              else +                sharedkerns[dk]=false +              end +            end +          end +        end +      end +    end +  end +end +local supported_gsub={ +  'liga','dlig','rlig','hlig', +  'pnum','onum','tnum','lnum', +  'zero', +  'smcp','cpsp','c2sc','ornm','aalt', +  'hwid','fwid', +  'ssty','rtlm', +} +local supported_gpos={ +  'kern' +} +function otf.features.register_base_substitution(tag) +  supported_gsub[#supported_gsub+1]=tag +end +function otf.features.register_base_kern(tag) +  supported_gsub[#supported_gpos+1]=tag +end +local basehash,basehashes={},1 +function fonts.initializers.base.otf.features(tfmdata,value) +  if true then +    local t=trace_preparing and os.clock() +    local features=tfmdata.shared.features +    if features then +      local h={} +      for f=1,#supported_gsub do +        local feature=supported_gsub[f] +        local value=features[feature] +        prepare_base_substitutions(tfmdata,feature,value) +        if value then +          h[#h+1]=feature.."="..tostring(value) +        end +      end +      for f=1,#supported_gpos do +        local feature=supported_gpos[f] +        local value=features[feature] +        prepare_base_kerns(tfmdata,feature,features[feature]) +        if value then +          h[#h+1]=feature.."="..tostring(value) +        end +      end +      local hash=concat(h," ") +      local base=basehash[hash] +      if not base then +        basehashes=basehashes+1 +        base=basehashes +        basehash[hash]=base +      end +      tfmdata.fullname=tfmdata.fullname.."-"..base +    end +    if trace_preparing then +      logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") +    end +  end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otn']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local concat,insert,remove=table.concat,table.insert,table.remove +local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local type,next,tonumber,tostring=type,next,tonumber,tostring +local lpegmatch=lpeg.match +local otf=fonts.otf +local tfm=fonts.tfm +local trace_lookups=false trackers.register("otf.lookups",function(v) trace_lookups=v end) +local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end) +local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end) +local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end) +local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end) +local trace_contexts=false trackers.register("otf.contexts",function(v) trace_contexts=v end) +local trace_marks=false trackers.register("otf.marks",function(v) trace_marks=v end) +local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) +local trace_cursive=false trackers.register("otf.cursive",function(v) trace_cursive=v end) +local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) +local trace_bugs=false trackers.register("otf.bugs",function(v) trace_bugs=v end) +local trace_details=false trackers.register("otf.details",function(v) trace_details=v end) +local trace_applied=false trackers.register("otf.applied",function(v) trace_applied=v end) +local trace_steps=false trackers.register("otf.steps",function(v) trace_steps=v end) +local trace_skips=false trackers.register("otf.skips",function(v) trace_skips=v end) +local trace_directions=false trackers.register("otf.directions",function(v) trace_directions=v end) +trackers.register("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end) +trackers.register("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end) +trackers.register("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures") +trackers.register("otf.positions","otf.marks,otf.kerns,otf.cursive") +trackers.register("otf.actions","otf.replacements,otf.positions") +trackers.register("otf.injections","nodes.injections") +trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing") +local insert_node_after=node.insert_after +local delete_node=nodes.delete +local copy_node=node.copy +local find_node_tail=node.tail or node.slide +local set_attribute=node.set_attribute +local has_attribute=node.has_attribute +local zwnj=0x200C +local zwj=0x200D +local wildcard="*" +local default="dflt" +local split_at_space=lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" "))  +local glyph=node.id('glyph') +local glue=node.id('glue') +local kern=node.id('kern') +local disc=node.id('disc') +local whatsit=node.id('whatsit') +local state=attributes.private('state') +local markbase=attributes.private('markbase') +local markmark=attributes.private('markmark') +local markdone=attributes.private('markdone') +local cursbase=attributes.private('cursbase') +local curscurs=attributes.private('curscurs') +local cursdone=attributes.private('cursdone') +local kernpair=attributes.private('kernpair') +local set_mark=nodes.set_mark +local set_cursive=nodes.set_cursive +local set_kern=nodes.set_kern +local set_pair=nodes.set_pair +local markonce=true +local cursonce=true +local kernonce=true +local fontdata=fonts.ids +otf.features.process={} +local tfmdata=false +local otfdata=false +local characters=false +local descriptions=false +local marks=false +local indices=false +local unicodes=false +local currentfont=false +local lookuptable=false +local anchorlookups=false +local handlers={} +local rlmode=0 +local featurevalue=false +local context_setups=fonts.define.specify.context_setups +local context_numbers=fonts.define.specify.context_numbers +local context_merged=fonts.define.specify.context_merged +local special_attributes={ +  init=1, +  medi=2, +  fina=3, +  isol=4 +} +local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check)  or function() end +local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end +local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end +local function logprocess(...) +  if trace_steps then +    registermessage(...) +  end +  logs.report("otf direct",...) +end +local function logwarning(...) +  logs.report("otf direct",...) +end +local function gref(n) +  if type(n)=="number" then +    local description=descriptions[n] +    local name=description and description.name +    if name then +      return format("U+%04X (%s)",n,name) +    else +      return format("U+%04X",n) +    end +  elseif not n then +    return "<error in tracing>" +  else +    local num,nam={},{} +    for i=1,#n do +      local ni=n[i] +      num[#num+1]=format("U+%04X",ni) +      local dni=descriptions[ni] +      nam[#num]=(dni and dni.name) or "?" +    end +    return format("%s (%s)",concat(num," "),concat(nam," ")) +  end +end +local function cref(kind,chainname,chainlookupname,lookupname,index) +  if index then +    return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index) +  elseif lookupname then +    return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname) +  elseif chainlookupname then +    return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname) +  elseif chainname then +    return format("feature %s, chain %s",kind,chainname) +  else +    return format("feature %s",kind) +  end +end +local function pref(kind,lookupname) +  return format("feature %s, lookup %s",kind,lookupname) +end +local function markstoligature(kind,lookupname,start,stop,char) +  local n=copy_node(start) +  local keep=start +  local current +  current,start=insert_node_after(start,start,n) +  local snext=stop.next +  current.next=snext +  if snext then +    snext.prev=current +  end +  start.prev,stop.next=nil,nil +  current.char,current.subtype,current.components=char,2,start +  return keep +end +local function toligature(kind,lookupname,start,stop,char,markflag,discfound)  +  if start~=stop then +    if discfound then +      local lignode=copy_node(start) +      lignode.font,lignode.char,lignode.subtype=start.font,char,2 +      local next,prev=stop.next,start.prev +      stop.next=nil +      lignode=node.do_ligature_n(start,stop,lignode) +      prev.next=lignode +      if next then +        next.prev=lignode +      end +      lignode.next,lignode.prev=next,prev +      start=lignode +    else  +      local deletemarks=markflag~="mark" +      local n=copy_node(start) +      local current +      current,start=insert_node_after(start,start,n) +      local snext=stop.next +      current.next=snext +      if snext then +        snext.prev=current +      end +      start.prev,stop.next=nil,nil +      current.char,current.subtype,current.components=char,2,start +      local head=current +      if deletemarks then +        if trace_marks then +          while start do +            if marks[start.char] then +              logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char)) +            end +            start=start.next +          end +        end +      else +        local i=0 +        while start do +          if marks[start.char] then +            set_attribute(start,markdone,i) +            if trace_marks then +              logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) +            end +            head,current=insert_node_after(head,current,copy_node(start)) +          else +            i=i+1 +          end +          start=start.next +        end +        start=current.next +        while start and start.id==glyph do +          if marks[start.char] then +            set_attribute(start,markdone,i) +            if trace_marks then +              logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) +            end +          else +            break +          end +          start=start.next +        end +      end +      return head +    end +  else +    start.char=char +  end +  return start +end +function handlers.gsub_single(start,kind,lookupname,replacement) +  if trace_singles then +    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) +  end +  start.char=replacement +  return start,true +end +local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname)  +  local value,choice,n=featurevalue or tfmdata.shared.features[kind],nil,#alternatives  +  if value=="random" then +    local r=math.random(1,n) +    value,choice=format("random, choice %s",r),alternatives[r] +  elseif value=="first" then +    value,choice=format("first, choice %s",1),alternatives[1] +  elseif value=="last" then +    value,choice=format("last, choice %s",n),alternatives[n] +  else +    value=tonumber(value) +    if type(value)~="number" then +      value,choice="default, choice 1",alternatives[1] +    elseif value>n then +      value,choice=format("no %s variants, taking %s",value,n),alternatives[n] +    elseif value==0 then +      value,choice=format("choice %s (no change)",value),start.char +    elseif value<1 then +      value,choice=format("no %s variants, taking %s",value,1),alternatives[1] +    else +      value,choice=format("choice %s",value),alternatives[value] +    end +  end +  if not choice then +    logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(start.char)) +    choice,value=start.char,format("no replacement instead of %s",value) +  end +  return choice,value +end +function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) +  local choice,index=alternative_glyph(start,alternative,kind,lookupname) +  if trace_alternatives then +    logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(start.char),gref(choice),index) +  end +  start.char=choice +  return start,true +end +function handlers.gsub_multiple(start,kind,lookupname,multiple) +  if trace_multiples then +    logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) +  end +  start.char=multiple[1] +  if #multiple>1 then +    for k=2,#multiple do +      local n=copy_node(start) +      n.char=multiple[k] +      local sn=start.next +      n.next=sn +      n.prev=start +      if sn then +        sn.prev=n +      end +      start.next=n +      start=n +    end +  end +  return start,true +end +function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence)  +  local s,stop,discfound=start.next,nil,false +  local startchar=start.char +  if marks[startchar] then +    while s do +      local id=s.id +      if id==glyph and s.subtype<256 then +        if s.font==currentfont then +          local char=s.char +          local lg=ligature[1][char] +          if not lg then +            break +          else +            stop=s +            ligature=lg +            s=s.next +          end +        else +          break +        end +      else +        break +      end +    end +    if stop and ligature[2] then +      if trace_ligatures then +        local stopchar=stop.char +        start=markstoligature(kind,lookupname,start,stop,ligature[2]) +        logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) +      else +        start=markstoligature(kind,lookupname,start,stop,ligature[2]) +      end +      return start,true +    end +  else +    local skipmark=sequence.flags[1] +    while s do +      local id=s.id +      if id==glyph and s.subtype<256 then +        if s.font==currentfont then +          local char=s.char +          if skipmark and marks[char] then +            s=s.next +          else +            local lg=ligature[1][char] +            if not lg then +              break +            else +              stop=s +              ligature=lg +              s=s.next +            end +          end +        else +          break +        end +      elseif id==disc then +        discfound=true +        s=s.next +      else +        break +      end +    end +    if stop and ligature[2] then +      if trace_ligatures then +        local stopchar=stop.char +        start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) +        logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) +      else +        start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) +      end +      return start,true +    end +  end +  return start,false +end +function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) +  local markchar=start.char +  if marks[markchar] then +    local base=start.prev  +    if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +      local basechar=base.char +      if marks[basechar] then +        while true do +          base=base.prev +          if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +            basechar=base.char +            if not marks[basechar] then +              break +            end +          else +            if trace_bugs then +              logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) +            end +            return start,false +          end +        end +      end +      local baseanchors=descriptions[basechar] +      if baseanchors then +        baseanchors=baseanchors.anchors +      end +      if baseanchors then +        local baseanchors=baseanchors['basechar'] +        if baseanchors then +          local al=anchorlookups[lookupname] +          for anchor,ba in next,baseanchors do +            if al[anchor] then +              local ma=markanchors[anchor] +              if ma then +                local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) +                if trace_marks then +                  logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", +                    pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                end +                return start,true +              end +            end +          end +          if trace_bugs then +            logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) +          end +        end +      else +        fonts.register_message(currentfont,basechar,"no base anchors") +      end +    elseif trace_bugs then +      logwarning("%s: prev node is no char",pref(kind,lookupname)) +    end +  elseif trace_bugs then +    logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) +  end +  return start,false +end +function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) +  local markchar=start.char +  if marks[markchar] then +    local base=start.prev  +    local index=1 +    if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +      local basechar=base.char +      if marks[basechar] then +        index=index+1 +        while true do +          base=base.prev +          if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +            basechar=base.char +            if marks[basechar] then +              index=index+1 +            else +              break +            end +          else +            if trace_bugs then +              logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) +            end +            return start,false +          end +        end +      end +      local i=has_attribute(start,markdone) +      if i then index=i end +      local baseanchors=descriptions[basechar] +      if baseanchors then +        baseanchors=baseanchors.anchors +        if baseanchors then +          local baseanchors=baseanchors['baselig'] +          if baseanchors then +            local al=anchorlookups[lookupname] +            for anchor,ba in next,baseanchors do +              if al[anchor] then +                local ma=markanchors[anchor] +                if ma then +                  ba=ba[index] +                  if ba then +                    local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) +                    if trace_marks then +                      logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", +                        pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) +                    end +                    return start,true +                  end +                end +              end +            end +            if trace_bugs then +              logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) +            end +          end +        end +      else +        fonts.register_message(currentfont,basechar,"no base anchors") +      end +    elseif trace_bugs then +      logwarning("%s: prev node is no char",pref(kind,lookupname)) +    end +  elseif trace_bugs then +    logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) +  end +  return start,false +end +function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) +  local markchar=start.char +  if marks[markchar] then +      local base=start.prev  +      if base and base.id==glyph and base.subtype<256 and base.font==currentfont then  +        local basechar=base.char +        local baseanchors=descriptions[basechar] +        if baseanchors then +          baseanchors=baseanchors.anchors +          if baseanchors then +            baseanchors=baseanchors['basemark'] +            if baseanchors then +              local al=anchorlookups[lookupname] +              for anchor,ba in next,baseanchors do +                if al[anchor] then +                  local ma=markanchors[anchor] +                  if ma then +                    local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) +                    if trace_marks then +                      logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", +                        pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                    end +                    return start,true +                  end +                end +              end +              if trace_bugs then +                logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) +              end +            end +          end +        else +          fonts.register_message(currentfont,basechar,"no base anchors") +        end +      elseif trace_bugs then +        logwarning("%s: prev node is no mark",pref(kind,lookupname)) +      end +  elseif trace_bugs then +    logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) +  end +  return start,false +end +function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence)  +  local alreadydone=cursonce and has_attribute(start,cursbase) +  if not alreadydone then +    local done=false +    local startchar=start.char +    if marks[startchar] then +      if trace_cursive then +        logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) +      end +    else +      local nxt=start.next +      while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do +        local nextchar=nxt.char +        if marks[nextchar] then +          nxt=nxt.next +        else +          local entryanchors=descriptions[nextchar] +          if entryanchors then +            entryanchors=entryanchors.anchors +            if entryanchors then +              entryanchors=entryanchors['centry'] +              if entryanchors then +                local al=anchorlookups[lookupname] +                for anchor,entry in next,entryanchors do +                  if al[anchor] then +                    local exit=exitanchors[anchor] +                    if exit then +                      local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) +                      if trace_cursive then +                        logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) +                      end +                      done=true +                      break +                    end +                  end +                end +              end +            end +          else +            fonts.register_message(currentfont,startchar,"no entry anchors") +          end +          break +        end +      end +    end +    return start,done +  else +    if trace_cursive and trace_details then +      logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) +    end +    return start,false +  end +end +function handlers.gpos_single(start,kind,lookupname,kerns,sequence) +  local startchar=start.char +  local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) +  if trace_kerns then +    logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) +  end +  return start,false +end +function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) +  local snext=start.next +  if not snext then +    return start,false +  else +    local prev,done=start,false +    local factor=tfmdata.factor +    while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do +      local nextchar=snext.char +local krn=kerns[nextchar] +      if not krn and marks[nextchar] then +        prev=snext +        snext=snext.next +      else +        local krn=kerns[nextchar] +        if not krn then +        elseif type(krn)=="table" then +          if krn[1]=="pair" then +            local a,b=krn[3],krn[4] +            if a and #a>0 then +              local startchar=start.char +              local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) +              if trace_kerns then +                logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +              end +            end +            if b and #b>0 then +              local startchar=start.char +              local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) +              if trace_kerns then +                logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +              end +            end +          else +            logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname)) +            local a,b=krn[3],krn[7] +            if a and a~=0 then +              local k=set_kern(snext,factor,rlmode,a) +              if trace_kerns then +                logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) +              end +            end +            if b and b~=0 then +              logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) +            end +          end +          done=true +        elseif krn~=0 then +          local k=set_kern(snext,factor,rlmode,krn) +          if trace_kerns then +            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) +          end +          done=true +        end +        break +      end +    end +    return start,done +  end +end +local chainmores={} +local chainprocs={} +local function logprocess(...) +  if trace_steps then +    registermessage(...) +  end +  logs.report("otf subchain",...) +end +local function logwarning(...) +  logs.report("otf subchain",...) +end +function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n) +  logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) +  return start,false +end +function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +  logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) +  return start,false +end +function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +  logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) +  return start,false +end +local function logprocess(...) +  if trace_steps then +    registermessage(...) +  end +  logs.report("otf chain",...) +end +local function logwarning(...) +  logs.report("otf chain",...) +end +function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname) +  logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) +  return start,false +end +function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements) +  local char=start.char +  local replacement=replacements[char] +  if replacement then +    if trace_singles then +      logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) +    end +    start.char=replacement +    return start,true +  else +    return start,false +  end +end +local function delete_till_stop(start,stop,ignoremarks) +  if start~=stop then +    local done=false +    while not done do +      done=start==stop +      delete_node(start,start.next) +    end +  end +end +function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) +  if not chainindex then +    delete_till_stop(start,stop)  +  end +  local current=start +  local subtables=currentlookup.subtables +  while current do +    if current.id==glyph then +      local currentchar=current.char +      local lookupname=subtables[1] +      local replacement=cache.gsub_single[lookupname] +      if not replacement then +        if trace_bugs then +          logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) +        end +      else +        replacement=replacement[currentchar] +        if not replacement then +          if trace_bugs then +            logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) +          end +        else +          if trace_singles then +            logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) +          end +          current.char=replacement +        end +      end +      return start,true +    elseif current==stop then +      break +    else +      current=current.next +    end +  end +  return start,false +end +chainmores.gsub_single=chainprocs.gsub_single +function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +  delete_till_stop(start,stop) +  local startchar=start.char +  local subtables=currentlookup.subtables +  local lookupname=subtables[1] +  local replacements=cache.gsub_multiple[lookupname] +  if not replacements then +    if trace_bugs then +      logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) +    end +  else +    replacements=replacements[startchar] +    if not replacements then +      if trace_bugs then +        logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) +      end +    else +      if trace_multiples then +        logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) +      end +      local sn=start.next +      for k=1,#replacements do +        if k==1 then +          start.char=replacements[k] +        else +          local n=copy_node(start)  +          n.char=replacements[k] +          n.next,n.prev=sn,start +          if sn then +            sn.prev=n +          end +          start.next,start=n,n +        end +      end +      return start,true +    end +  end +  return start,false +end +function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +  delete_till_stop(start,stop) +  local current=start +  local subtables=currentlookup.subtables +  while current do +    if current.id==glyph then +      local currentchar=current.char +      local lookupname=subtables[1] +      local alternatives=cache.gsub_alternate[lookupname] +      if not alternatives then +        if trace_bugs then +          logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) +        end +      else +        alternatives=alternatives[currentchar] +        if not alternatives then +          if trace_bugs then +            logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) +          end +        else +          local choice,index=alternative_glyph(current,alternatives,kind,chainname,chainlookupname,lookupname) +          current.char=choice +          if trace_alternatives then +            logprocess("%s: replacing single %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),index,gref(currentchar),gref(choice),index) +          end +        end +      end +      return start,true +    elseif current==stop then +      break +    else +      current=current.next +    end +  end +  return start,false +end +function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) +  local startchar=start.char +  local subtables=currentlookup.subtables +  local lookupname=subtables[1] +  local ligatures=cache.gsub_ligature[lookupname] +  if not ligatures then +    if trace_bugs then +      logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) +    end +  else +    ligatures=ligatures[startchar] +    if not ligatures then +      if trace_bugs then +        logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) +      end +    else +      local s,discfound,last,nofreplacements=start.next,false,stop,0 +      while s do +        local id=s.id +        if id==disc then +          s=s.next +          discfound=true +        else +          local schar=s.char +          if marks[schar] then  +            s=s.next +          else +            local lg=ligatures[1][schar] +            if not lg then +              break +            else +              ligatures,last,nofreplacements=lg,s,nofreplacements+1 +              if s==stop then +                break +              else +                s=s.next +              end +            end +          end +        end +      end +      local l2=ligatures[2] +      if l2 then +        if chainindex then +          stop=last +        end +        if trace_ligatures then +          if start==stop then +            logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) +          else +            logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) +          end +        end +        start=toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound) +        return start,true,nofreplacements +      elseif trace_bugs then +        if start==stop then +          logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) +        else +          logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) +        end +      end +    end +  end +  return start,false,0 +end +chainmores.gsub_ligature=chainprocs.gsub_ligature +function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +  local markchar=start.char +  if marks[markchar] then +    local subtables=currentlookup.subtables +    local lookupname=subtables[1] +    local markanchors=cache.gpos_mark2base[lookupname] +    if markanchors then +      markanchors=markanchors[markchar] +    end +    if markanchors then +      local base=start.prev  +      if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +        local basechar=base.char +        if marks[basechar] then +          while true do +            base=base.prev +            if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +              basechar=base.char +              if not marks[basechar] then +                break +              end +            else +              if trace_bugs then +                logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) +              end +              return start,false +            end +          end +        end +        local baseanchors=descriptions[basechar].anchors +        if baseanchors then +          local baseanchors=baseanchors['basechar'] +          if baseanchors then +            local al=anchorlookups[lookupname] +            for anchor,ba in next,baseanchors do +              if al[anchor] then +                local ma=markanchors[anchor] +                if ma then +                  local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) +                  if trace_marks then +                    logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", +                      cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                  end +                  return start,true +                end +              end +            end +            if trace_bugs then +              logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) +            end +          end +        end +      elseif trace_bugs then +        logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) +      end +    elseif trace_bugs then +      logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) +    end +  elseif trace_bugs then +    logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) +  end +  return start,false +end +function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +  local markchar=start.char +  if marks[markchar] then +    local subtables=currentlookup.subtables +    local lookupname=subtables[1] +    local markanchors=cache.gpos_mark2ligature[lookupname] +    if markanchors then +      markanchors=markanchors[markchar] +    end +    if markanchors then +      local base=start.prev  +      local index=1 +      if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +        local basechar=base.char +        if marks[basechar] then +          index=index+1 +          while true do +            base=base.prev +            if base and base.id==glyph and base.subtype<256 and base.font==currentfont then +              basechar=base.char +              if marks[basechar] then +                index=index+1 +              else +                break +              end +            else +              if trace_bugs then +                logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) +              end +              return start,false +            end +          end +        end +        local i=has_attribute(start,markdone) +        if i then index=i end +        local baseanchors=descriptions[basechar].anchors +        if baseanchors then +          local baseanchors=baseanchors['baselig'] +          if baseanchors then +            local al=anchorlookups[lookupname] +            for anchor,ba in next,baseanchors do +              if al[anchor] then +                local ma=markanchors[anchor] +                if ma then +                  ba=ba[index] +                  if ba then +                    local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) +                    if trace_marks then +                      logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", +                        cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) +                    end +                    return start,true +                  end +                end +              end +            end +            if trace_bugs then +              logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) +            end +          end +        end +      elseif trace_bugs then +        logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) +      end +    elseif trace_bugs then +      logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) +    end +  elseif trace_bugs then +    logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) +  end +  return start,false +end +function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +  local markchar=start.char +  if marks[markchar] then +      local subtables=currentlookup.subtables +      local lookupname=subtables[1] +      local markanchors=cache.gpos_mark2mark[lookupname] +      if markanchors then +        markanchors=markanchors[markchar] +      end +      if markanchors then +        local base=start.prev  +        if base and base.id==glyph and base.subtype<256 and base.font==currentfont then  +          local basechar=base.char +          local baseanchors=descriptions[basechar].anchors +          if baseanchors then +            baseanchors=baseanchors['basemark'] +            if baseanchors then +              local al=anchorlookups[lookupname] +              for anchor,ba in next,baseanchors do +                if al[anchor] then +                  local ma=markanchors[anchor] +                  if ma then +                    local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) +                    if trace_marks then +                      logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", +                        cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                    end +                    return start,true +                  end +                end +              end +              if trace_bugs then +                logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) +              end +            end +          end +        elseif trace_bugs then +          logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) +        end +      elseif trace_bugs then +        logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) +      end +  elseif trace_bugs then +    logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) +  end +  return start,false +end +function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +  local alreadydone=cursonce and has_attribute(start,cursbase) +  if not alreadydone then +    local startchar=start.char +    local subtables=currentlookup.subtables +    local lookupname=subtables[1] +    local exitanchors=cache.gpos_cursive[lookupname] +    if exitanchors then +      exitanchors=exitanchors[startchar] +    end +    if exitanchors then +      local done=false +      if marks[startchar] then +        if trace_cursive then +          logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) +        end +      else +        local nxt=start.next +        while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do +          local nextchar=nxt.char +          if marks[nextchar] then +            nxt=nxt.next +          else +            local entryanchors=descriptions[nextchar] +            if entryanchors then +              entryanchors=entryanchors.anchors +              if entryanchors then +                entryanchors=entryanchors['centry'] +                if entryanchors then +                  local al=anchorlookups[lookupname] +                  for anchor,entry in next,entryanchors do +                    if al[anchor] then +                      local exit=exitanchors[anchor] +                      if exit then +                        local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) +                        if trace_cursive then +                          logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) +                        end +                        done=true +                        break +                      end +                    end +                  end +                end +              end +            else +              fonts.register_message(currentfont,startchar,"no entry anchors") +            end +            break +          end +        end +      end +      return start,done +    else +      if trace_cursive and trace_details then +        logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) +      end +      return start,false +    end +  end +  return start,false +end +function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) +  local startchar=start.char +  local subtables=currentlookup.subtables +  local lookupname=subtables[1] +  local kerns=cache.gpos_single[lookupname] +  if kerns then +    kerns=kerns[startchar] +    if kerns then +      local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) +      if trace_kerns then +        logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +      end +    end +  end +  return start,false +end +function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) +  local snext=start.next +  if snext then +    local startchar=start.char +    local subtables=currentlookup.subtables +    local lookupname=subtables[1] +    local kerns=cache.gpos_pair[lookupname] +    if kerns then +      kerns=kerns[startchar] +      if kerns then +        local prev,done=start,false +        local factor=tfmdata.factor +        while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do +          local nextchar=snext.char +          local krn=kerns[nextchar] +          if not krn and marks[nextchar] then +            prev=snext +            snext=snext.next +          else +            if not krn then +            elseif type(krn)=="table" then +              if krn[1]=="pair" then +                local a,b=krn[3],krn[4] +                if a and #a>0 then +                  local startchar=start.char +                  local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) +                  if trace_kerns then +                    logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                  end +                end +                if b and #b>0 then +                  local startchar=start.char +                  local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) +                  if trace_kerns then +                    logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                  end +                end +              else +                logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) +                local a,b=krn[3],krn[7] +                if a and a~=0 then +                  local k=set_kern(snext,factor,rlmode,a) +                  if trace_kerns then +                    logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) +                  end +                end +                if b and b~=0 then +                  logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) +                end +              end +              done=true +            elseif krn~=0 then +              local k=set_kern(snext,factor,rlmode,krn) +              if trace_kerns then +                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) +              end +              done=true +            end +            break +          end +        end +        return start,done +      end +    end +  end +  return start,false +end +local function show_skip(kind,chainname,char,ck,class) +  if ck[9] then +    logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) +  else +    logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) +  end +end +local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) +  local flags,done=sequence.flags,false +  local skipmark,skipligature,skipbase=flags[1],flags[2],flags[3] +  local someskip=skipmark or skipligature or skipbase  +  local markclass=sequence.markclass  +  local skipped=false +  for k=1,#contexts do +    local match,current,last=true,start,start +    local ck=contexts[k] +    local seq=ck[3] +    local s=#seq +    if s==1 then +      match=current.id==glyph and current.subtype<256 and current.font==currentfont and seq[1][current.char] +    else +      local f,l=ck[4],ck[5] +      if f==l then +        match=true +      else +        local n=f+1 +        last=last.next +          while n<=l do +            if last then +              local id=last.id +              if id==glyph then +                if last.subtype<256 and last.font==currentfont then +                  local char=last.char +                  local ccd=descriptions[char] +                  if ccd then +                    local class=ccd.class +                    if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then +                      skipped=true +                      if trace_skips then +                        show_skip(kind,chainname,char,ck,class) +                      end +                      last=last.next +                    elseif seq[n][char] then +                      if n<l then +                        last=last.next +                      end +                      n=n+1 +                    else +                      match=false break +                    end +                  else +                    match=false break +                  end +                else +                  match=false break +                end +              elseif id==disc then  +                last=last.next +              else +                match=false break +              end +            else +              match=false break +            end +          end +      end +      if match and f>1 then +        local prev=start.prev +        if prev then +          local n=f-1 +          while n>=1 do +            if prev then +              local id=prev.id +              if id==glyph then +                if prev.subtype<256 and prev.font==currentfont then  +                  local char=prev.char +                  local ccd=descriptions[char] +                  if ccd then +                    local class=ccd.class +                    if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then +                      skipped=true +                      if trace_skips then +                        show_skip(kind,chainname,char,ck,class) +                      end +                    elseif seq[n][char] then +                      n=n -1 +                    else +                      match=false break +                    end +                  else +                    match=false break +                  end +                else +                  match=false break +                end +              elseif id==disc then +              elseif seq[n][32] then +                n=n -1 +              else +                match=false break +              end +              prev=prev.prev +            elseif seq[n][32] then +              n=n -1 +            else +              match=false break +            end +          end +        elseif f==2 then +          match=seq[1][32] +        else +          for n=f-1,1 do +            if not seq[n][32] then +              match=false break +            end +          end +        end +      end +      if match and s>l then +        local current=last.next +        if current then +          local n=l+1 +          while n<=s do +            if current then +              local id=current.id +              if id==glyph then +                if current.subtype<256 and current.font==currentfont then  +                  local char=current.char +                  local ccd=descriptions[char] +                  if ccd then +                    local class=ccd.class +                    if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then +                      skipped=true +                      if trace_skips then +                        show_skip(kind,chainname,char,ck,class) +                      end +                    elseif seq[n][char] then +                      n=n+1 +                    else +                      match=false break +                    end +                  else +                    match=false break +                  end +                else +                  match=false break +                end +              elseif id==disc then +              elseif seq[n][32] then  +                n=n+1 +              else +                match=false break +              end +              current=current.next +            elseif seq[n][32] then +              n=n+1 +            else +              match=false break +            end +          end +        elseif s-l==1 then +          match=seq[s][32] +        else +          for n=l+1,s do +            if not seq[n][32] then +              match=false break +            end +          end +        end +      end +    end +    if match then +      if trace_contexts then +        local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5] +        local char=start.char +        if ck[9] then +          logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) +        else +          logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) +        end +      end +      local chainlookups=ck[6] +      if chainlookups then +        local nofchainlookups=#chainlookups +        if nofchainlookups==1 then +          local chainlookupname=chainlookups[1] +          local chainlookup=lookuptable[chainlookupname] +          local cp=chainprocs[chainlookup.type] +          if cp then +            start,done=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence) +          else +            logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) +          end +         else +          local i=1 +          repeat +if skipped then +  while true do +    local char=start.char +    local ccd=descriptions[char] +    if ccd then +      local class=ccd.class +      if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then +        start=start.next +      else +        break +      end +    else +      break +    end +  end +end +            local chainlookupname=chainlookups[i] +            local chainlookup=lookuptable[chainlookupname] +            local cp=chainmores[chainlookup.type] +            if cp then +              local ok,n +              start,ok,n=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) +              if ok then +                done=true +                i=i+(n or 1) +              else +                i=i+1 +              end +            else +              logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) +              i=i+1 +            end +            start=start.next +          until i>nofchainlookups +        end +      else +        local replacements=ck[7] +        if replacements then +          start,done=chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements)  +        else +          done=true  +          if trace_contexts then +            logprocess("%s: skipping match",cref(kind,chainname)) +          end +        end +      end +    end +  end +  return start,done +end +local verbose_handle_contextchain=function(font,...) +  logwarning("no verbose handler installed, reverting to 'normal'") +  otf.setcontextchain() +  return normal_handle_contextchain(...) +end +otf.chainhandlers={ +  normal=normal_handle_contextchain, +  verbose=verbose_handle_contextchain, +} +function otf.setcontextchain(method) +  if not method or method=="normal" or not otf.chainhandlers[method] then +    if handlers.contextchain then  +      logwarning("installing normal contextchain handler") +    end +    handlers.contextchain=normal_handle_contextchain +  else +    logwarning("installing contextchain handler '%s'",method) +    local handler=otf.chainhandlers[method] +    handlers.contextchain=function(...) +      return handler(currentfont,...)  +    end +  end +  handlers.gsub_context=handlers.contextchain +  handlers.gsub_contextchain=handlers.contextchain +  handlers.gsub_reversecontextchain=handlers.contextchain +  handlers.gpos_contextchain=handlers.contextchain +  handlers.gpos_context=handlers.contextchain +end +otf.setcontextchain() +local missing={}  +local function logprocess(...) +  if trace_steps then +    registermessage(...) +  end +  logs.report("otf process",...) +end +local function logwarning(...) +  logs.report("otf process",...) +end +local function report_missing_cache(typ,lookup) +  local f=missing[currentfont] if not f then f={} missing[currentfont]=f end +  local t=f[typ]        if not t then t={} f[typ]=t end +  if not t[lookup] then +    t[lookup]=true +    logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname) +  end +end +local resolved={} +function fonts.methods.node.otf.features(head,font,attr) +  if trace_steps then +    checkstep(head) +  end +  tfmdata=fontdata[font] +  local shared=tfmdata.shared +  otfdata=shared.otfdata +  local luatex=otfdata.luatex +  descriptions=tfmdata.descriptions +  characters=tfmdata.characters +  indices=tfmdata.indices +  unicodes=tfmdata.unicodes +  marks=tfmdata.marks +  anchorlookups=luatex.lookup_to_anchor +  currentfont=font +  rlmode=0 +  local featuredata=otfdata.shared.featuredata  +  local sequences=luatex.sequences +  lookuptable=luatex.lookups +  local done=false +  local script,language,s_enabled,a_enabled,dyn +  local attribute_driven=attr and attr~=0 +  if attribute_driven then +    local features=context_setups[context_numbers[attr]]  +    dyn=context_merged[attr] or 0 +    language,script=features.language or "dflt",features.script or "dflt" +    a_enabled=features  +    if dyn==2 or dyn==-2 then +      s_enabled=shared.features +    end +  else +    language,script=tfmdata.language or "dflt",tfmdata.script or "dflt" +    s_enabled=shared.features  +    dyn=0 +  end +  local res=resolved[font]   if not res  then res={} resolved[font]=res end +  local rs=res   [script]  if not rs  then rs={} res   [script]=rs end +  local rl=rs   [language] if not rl  then rl={} rs   [language]=rl end +  local ra=rl   [attr]   if ra==nil then ra={} rl   [attr]=ra end +  for s=1,#sequences do +    local pardir,txtdir,success=0,{},false +    local sequence=sequences[s] +    local r=ra[s]  +    if r==nil then +      local chain=sequence.chain or 0 +      local features=sequence.features +      if not features then +        r=false  +      else +        local valid,attribute,kind,what=false,false +        for k,v in next,features do +          local s_e=s_enabled and s_enabled[k] +          local a_e=a_enabled and a_enabled[k] +          if s_e or a_e then +            local l=v[script] or v[wildcard] +            if l then +              if l[language] then +                valid,what=s_e or a_e,language +              elseif l[wildcard] then +                valid,what=s_e or a_e,wildcard +              end +              if valid then +                kind,attribute=k,special_attributes[k] or false +                if a_e and dyn<0 then +                  valid=false +                end +                if trace_applied then +                  local typ,action=match(sequence.type,"(.*)_(.*)") +                  logs.report("otf node mode", +                    "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", +                    (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) +                end +                break +              end +            end +          end +        end +        if valid then +          r={ valid,attribute,chain,kind } +        else +          r=false  +        end +      end +      ra[s]=r +    end +    featurevalue=r and r[1]  +    if featurevalue then +      local attribute,chain,typ,subtables=r[2],r[3],sequence.type,sequence.subtables +      if chain<0 then +        local handler=handlers[typ] +        local thecache=featuredata[typ] or {} +        local start=find_node_tail(head)  +        while start do +          local id=start.id +          if id==glyph then +            if start.subtype<256 and start.font==font then +              local a=has_attribute(start,0) +              if a then +                a=a==attr +              else +                a=true +              end +              if a then +                for i=1,#subtables do +                  local lookupname=subtables[i] +                  local lookupcache=thecache[lookupname] +                  if lookupcache then +                    local lookupmatch=lookupcache[start.char] +                    if lookupmatch then +                      start,success=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) +                      if success then +                        break +                      end +                    end +                  else +                    report_missing_cache(typ,lookupname) +                  end +                end +                if start then start=start.prev end +              else +                start=start.prev +              end +            else +              start=start.prev +            end +          else +            start=start.prev +          end +        end +      else +        local handler=handlers[typ] +        local ns=#subtables +        local thecache=featuredata[typ] or {} +        local start=head  +        rlmode=0  +        if ns==1 then +          local lookupname=subtables[1] +          local lookupcache=thecache[lookupname] +          if not lookupcache then +            report_missing_cache(typ,lookupname) +          else +            while start do +              local id=start.id +              if id==glyph then +                if start.subtype<256 and start.font==font then +                  local a=has_attribute(start,0) +                  if a then +                    a=(a==attr) and (not attribute or has_attribute(start,state,attribute)) +                  else +                    a=not attribute or has_attribute(start,state,attribute) +                  end +                  if a then +                    local lookupmatch=lookupcache[start.char] +                    if lookupmatch then +                      local ok +                      start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) +                      if ok then +                        success=true +                      end +                    end +                    if start then start=start.next end +                  else +                    start=start.next +                  end +                else +                  start=start.next +                end +              elseif id==whatsit then +                local subtype=start.subtype +                if subtype==7 then +                  local dir=start.dir +                  if   dir=="+TRT" or dir=="+TLT" then +                    insert(txtdir,dir) +                  elseif dir=="-TRT" or dir=="-TLT" then +                    remove(txtdir) +                  end +                  local d=txtdir[#txtdir] +                  if d=="+TRT" then +                    rlmode=-1 +                  elseif d=="+TLT" then +                    rlmode=1 +                  else +                    rlmode=pardir +                  end +                  if trace_directions then +                    logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) +                  end +                elseif subtype==6 then +                  local dir=start.dir +                  if dir=="TRT" then +                    pardir=-1 +                  elseif dir=="TLT" then +                    pardir=1 +                  else +                    pardir=0 +                  end +                  rlmode=pardir +                  if trace_directions then +                    logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) +                  end +                end +                start=start.next +              else +                start=start.next +              end +            end +          end +        else +          while start do +            local id=start.id +            if id==glyph then +              if start.subtype<256 and start.font==font then +                local a=has_attribute(start,0) +                if a then +                  a=(a==attr) and (not attribute or has_attribute(start,state,attribute)) +                else +                  a=not attribute or has_attribute(start,state,attribute) +                end +                if a then +                  for i=1,ns do +                    local lookupname=subtables[i] +                    local lookupcache=thecache[lookupname] +                    if lookupcache then +                      local lookupmatch=lookupcache[start.char] +                      if lookupmatch then +                        local ok +                        start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) +                        if ok then +                          success=true +                          break +                        end +                      end +                    else +                      report_missing_cache(typ,lookupname) +                    end +                  end +                  if start then start=start.next end +                else +                  start=start.next +                end +              else +                start=start.next +              end +            elseif id==whatsit then +              local subtype=start.subtype +              if subtype==7 then +                local dir=start.dir +                if   dir=="+TRT" or dir=="+TLT" then +                  insert(txtdir,dir) +                elseif dir=="-TRT" or dir=="-TLT" then +                  remove(txtdir) +                end +                local d=txtdir[#txtdir] +                if d=="+TRT" then +                  rlmode=-1 +                elseif d=="+TLT" then +                  rlmode=1 +                else +                  rlmode=pardir +                end +                if trace_directions then +                  logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) +                end +              elseif subtype==6 then +                local dir=start.dir +                if dir=="TRT" then +                  pardir=-1 +                elseif dir=="TLT" then +                  pardir=1 +                else +                  pardir=0 +                end +                rlmode=pardir +                if trace_directions then +                  logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) +                end +              end +              start=start.next +            else +              start=start.next +            end +          end +        end +      end +      if success then +        done=true +      end +      if trace_steps then  +        registerstep(head) +      end +    end +  end +  return head,done +end +otf.features.prepare={} +local function split(replacement,original,cache,unicodes) +  local o,t,n={},{},0 +  for s in gmatch(original,"[^ ]+") do +    local us=unicodes[s] +    if type(us)=="number" then  +      o[#o+1]=us +    else +      o[#o+1]=us[1] +    end +  end +  for s in gmatch(replacement,"[^ ]+") do +    n=n+1 +    local us=unicodes[s] +    if type(us)=="number" then  +      t[o[n]]=us +    else +      t[o[n]]=us[1] +    end +  end +  return t +end +local function uncover(covers,result,cache,unicodes) +  for n=1,#covers do +    local c=covers[n] +    local cc=cache[c] +    if not cc then +      local t={} +      for s in gmatch(c,"[^ ]+") do +        local us=unicodes[s] +        if type(us)=="number" then +          t[us]=true +        else +          for i=1,#us do +            t[us[i]]=true +          end +        end +      end +      cache[c]=t +      result[#result+1]=t +    else +      result[#result+1]=cc +    end +  end +end +local function prepare_lookups(tfmdata) +  local otfdata=tfmdata.shared.otfdata +  local featuredata=otfdata.shared.featuredata +  local anchor_to_lookup=otfdata.luatex.anchor_to_lookup +  local lookup_to_anchor=otfdata.luatex.lookup_to_anchor +  local multiple=featuredata.gsub_multiple +  local alternate=featuredata.gsub_alternate +  local single=featuredata.gsub_single +  local ligature=featuredata.gsub_ligature +  local pair=featuredata.gpos_pair +  local position=featuredata.gpos_single +  local kerns=featuredata.gpos_pair +  local mark=featuredata.gpos_mark2mark +  local cursive=featuredata.gpos_cursive +  local unicodes=tfmdata.unicodes  +  local indices=tfmdata.indices +  local descriptions=tfmdata.descriptions +  local action={ +    substitution=function(p,lookup,glyph,unicode) +      local old,new=unicode,unicodes[p[2]] +      if type(new)=="table" then +        new=new[1] +      end +      local s=single[lookup] +      if not s then s={} single[lookup]=s end +      s[old]=new +    end, +    multiple=function (p,lookup,glyph,unicode) +      local old,new=unicode,{} +      local m=multiple[lookup] +      if not m then m={} multiple[lookup]=m end +      m[old]=new +      for pc in gmatch(p[2],"[^ ]+") do +        local upc=unicodes[pc] +        if type(upc)=="number" then +          new[#new+1]=upc +        else +          new[#new+1]=upc[1] +        end +      end +    end, +    alternate=function(p,lookup,glyph,unicode) +      local old,new=unicode,{} +      local a=alternate[lookup] +      if not a then a={} alternate[lookup]=a end +      a[old]=new +      for pc in gmatch(p[2],"[^ ]+") do +        local upc=unicodes[pc] +        if type(upc)=="number" then +          new[#new+1]=upc +        else +          new[#new+1]=upc[1] +        end +      end +    end, +    ligature=function (p,lookup,glyph,unicode) +      local first=true +      local t=ligature[lookup] +      if not t then t={} ligature[lookup]=t end +      for s in gmatch(p[2],"[^ ]+") do +        if first then +          local u=unicodes[s] +          if not u then +            logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) +            break +          elseif type(u)=="number" then +            if not t[u] then +              t[u]={ {} } +            end +            t=t[u] +          else +            local tt=t +            local tu +            for i=1,#u do +              local u=u[i] +              if i==1 then +                if not t[u] then +                  t[u]={ {} } +                end +                tu=t[u] +                t=tu +              else +                if not t[u] then +                  tt[u]=tu +                end +              end +            end +          end +          first=false +        else +          s=unicodes[s] +          local t1=t[1] +          if not t1[s] then +            t1[s]={ {} } +          end +          t=t1[s] +        end +      end +      t[2]=unicode +    end, +    position=function(p,lookup,glyph,unicode) +      local s=position[lookup] +      if not s then s={} position[lookup]=s end +      s[unicode]=p[2]  +    end, +    pair=function(p,lookup,glyph,unicode) +      local s=pair[lookup] +      if not s then s={} pair[lookup]=s end +      local others=s[unicode] +      if not others then others={} s[unicode]=others end +      local two=p[2] +      local upc=unicodes[two] +      if not upc then +        for pc in gmatch(two,"[^ ]+") do +          local upc=unicodes[pc] +          if type(upc)=="number" then +            others[upc]=p  +          else +            for i=1,#upc do +              others[upc[i]]=p  +            end +          end +        end +      elseif type(upc)=="number" then +        others[upc]=p  +      else +        for i=1,#upc do +          others[upc[i]]=p  +        end +      end +    end, +  } +  for unicode,glyph in next,descriptions do +    local lookups=glyph.slookups +    if lookups then +      for lookup,p in next,lookups do +        action[p[1]](p,lookup,glyph,unicode) +      end +    end +    local lookups=glyph.mlookups +    if lookups then +      for lookup,whatever in next,lookups do +        for i=1,#whatever do  +          local p=whatever[i] +          action[p[1]](p,lookup,glyph,unicode) +        end +      end +    end +    local list=glyph.mykerns +    if list then +      for lookup,krn in next,list do +        local k=kerns[lookup] +        if not k then k={} kerns[lookup]=k end +        k[unicode]=krn +      end +    end +    local oanchor=glyph.anchors +    if oanchor then +      for typ,anchors in next,oanchor do  +        if typ=="mark" then +          for name,anchor in next,anchors do +            local lookups=anchor_to_lookup[name] +            if lookups then +              for lookup,_ in next,lookups do +                local f=mark[lookup] +                if not f then f={} mark[lookup]=f end +                f[unicode]=anchors +              end +            end +          end +        elseif typ=="cexit" then  +          for name,anchor in next,anchors do +            local lookups=anchor_to_lookup[name] +            if lookups then +              for lookup,_ in next,lookups do +                local f=cursive[lookup] +                if not f then f={} cursive[lookup]=f end +                f[unicode]=anchors +              end +            end +          end +        end +      end +    end +  end +end +luatex=luatex or {}  +local function prepare_contextchains(tfmdata) +  local otfdata=tfmdata.shared.otfdata +  local lookups=otfdata.lookups +  if lookups then +    local featuredata=otfdata.shared.featuredata +    local contextchain=featuredata.gsub_contextchain  +    local reversecontextchain=featuredata.gsub_reversecontextchain  +    local characters=tfmdata.characters +    local unicodes=tfmdata.unicodes +    local indices=tfmdata.indices +    local cache=luatex.covers +    if not cache then +      cache={} +      luatex.covers=cache +    end +    for lookupname,lookupdata in next,otfdata.lookups do +      local lookuptype=lookupdata.type +      if not lookuptype then +        logs.report("otf process","missing lookuptype for %s",lookupname) +      else +        local rules=lookupdata.rules +        if rules then +          local fmt=lookupdata.format +          if fmt=="coverage" then +            if lookuptype~="chainsub" and lookuptype~="chainpos" then +              logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) +            else +              local contexts=contextchain[lookupname] +              if not contexts then +                contexts={} +                contextchain[lookupname]=contexts +              end +              local t={} +              for nofrules=1,#rules do  +                local rule=rules[nofrules] +                local coverage=rule.coverage +                if coverage and coverage.current then +                  local current,before,after,sequence=coverage.current,coverage.before,coverage.after,{} +                  if before then +                    uncover(before,sequence,cache,unicodes) +                  end +                  local start=#sequence+1 +                  uncover(current,sequence,cache,unicodes) +                  local stop=#sequence +                  if after then +                    uncover(after,sequence,cache,unicodes) +                  end +                  if sequence[1] then +                    t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups } +                    for unic,_ in next,sequence[start] do +                      local cu=contexts[unic] +                      if not cu then +                        contexts[unic]=t +                      end +                    end +                  end +                end +              end +            end +          elseif fmt=="reversecoverage" then +            if lookuptype~="reversesub" then +              logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname) +            else +              local contexts=reversecontextchain[lookupname] +              if not contexts then +                contexts={} +                reversecontextchain[lookupname]=contexts +              end +              local t={} +              for nofrules=1,#rules do +                local rule=rules[nofrules] +                local reversecoverage=rule.reversecoverage +                if reversecoverage and reversecoverage.current then +                  local current,before,after,replacements,sequence=reversecoverage.current,reversecoverage.before,reversecoverage.after,reversecoverage.replacements,{} +                  if before then +                    uncover(before,sequence,cache,unicodes) +                  end +                  local start=#sequence+1 +                  uncover(current,sequence,cache,unicodes) +                  local stop=#sequence +                  if after then +                    uncover(after,sequence,cache,unicodes) +                  end +                  if replacements then +                    replacements=split(replacements,current[1],cache,unicodes) +                  end +                  if sequence[1] then +                    t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements } +                    for unic,_ in next,sequence[start] do +                      local cu=contexts[unic] +                      if not cu then +                        contexts[unic]=t +                      end +                    end +                  end +                end +              end +            end +          elseif fmt=="glyphs" then +            if lookuptype~="chainsub" and lookuptype~="chainpos" then +              logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) +            else +              local contexts=contextchain[lookupname] +              if not contexts then +                contexts={} +                contextchain[lookupname]=contexts +              end +              local t={} +              for nofrules=1,#rules do +                local rule=rules[nofrules] +                local glyphs=rule.glyphs +                if glyphs and glyphs.names then +                  local fore,back,names,sequence=glyphs.fore,glyphs.back,glyphs.names,{} +                  if fore and fore~="" then +                    fore=lpegmatch(split_at_space,fore) +                    uncover(fore,sequence,cache,unicodes) +                  end +                  local start=#sequence+1 +                  names=lpegmatch(split_at_space,names) +                  uncover(names,sequence,cache,unicodes) +                  local stop=#sequence +                  if back and back~="" then +                    back=lpegmatch(split_at_space,back) +                    uncover(back,sequence,cache,unicodes) +                  end +                  if sequence[1] then +                    t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups } +                    for unic,_ in next,sequence[start] do +                      local cu=contexts[unic] +                      if not cu then +                        contexts[unic]=t +                      end +                    end +                  end +                end +              end +            end +          end +        end +      end +    end +  end +end +function fonts.initializers.node.otf.features(tfmdata,value) +  if true then  +    if not tfmdata.shared.otfdata.shared.initialized then +      local t=trace_preparing and os.clock() +      local otfdata=tfmdata.shared.otfdata +      local featuredata=otfdata.shared.featuredata +      featuredata.gsub_multiple={} +      featuredata.gsub_alternate={} +      featuredata.gsub_single={} +      featuredata.gsub_ligature={} +      featuredata.gsub_contextchain={} +      featuredata.gsub_reversecontextchain={} +      featuredata.gpos_pair={} +      featuredata.gpos_single={} +      featuredata.gpos_mark2base={} +      featuredata.gpos_mark2ligature=featuredata.gpos_mark2base +      featuredata.gpos_mark2mark=featuredata.gpos_mark2base +      featuredata.gpos_cursive={} +      featuredata.gpos_contextchain=featuredata.gsub_contextchain +      featuredata.gpos_reversecontextchain=featuredata.gsub_reversecontextchain +      prepare_contextchains(tfmdata) +      prepare_lookups(tfmdata) +      otfdata.shared.initialized=true +      if trace_preparing then +        logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") +      end +    end +  end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-ota']={ +  version=1.001, +  comment="companion to font-otf.lua (analysing)", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local type,tostring,match,format,concat=type,tostring,string.match,string.format,table.concat +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") +fonts=fonts            or {} +fonts.analyzers=fonts.analyzers       or {} +fonts.analyzers.initializers=fonts.analyzers.initializers or { node={ otf={} } } +fonts.analyzers.methods=fonts.analyzers.methods   or { node={ otf={} } } +local otf=fonts.otf +local tfm=fonts.tfm +local initializers=fonts.analyzers.initializers +local methods=fonts.analyzers.methods +local glyph=node.id('glyph') +local glue=node.id('glue') +local penalty=node.id('penalty') +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.ids +local state=attributes.private('state') +local fcs=(fonts.color and fonts.color.set)  or function() end +local fcr=(fonts.color and fonts.color.reset) or function() end +local a_to_script=otf.a_to_script +local a_to_language=otf.a_to_language +function fonts.initializers.node.otf.analyze(tfmdata,value,attr) +  local script,language +  if attr and attr>0 then +    script,language=a_to_script[attr],a_to_language[attr] +  else +    script,language=tfmdata.script,tfmdata.language +  end +  local action=initializers[script] +  if action then +    if type(action)=="function" then +      return action(tfmdata,value) +    else +      local action=action[language] +      if action then +        return action(tfmdata,value) +      end +    end +  end +  return nil +end +function fonts.methods.node.otf.analyze(head,font,attr) +  local tfmdata=fontdata[font] +  local script,language +  if attr and attr>0 then +    script,language=a_to_script[attr],a_to_language[attr] +  else +    script,language=tfmdata.script,tfmdata.language +  end +  local action=methods[script] +  if action then +    if type(action)=="function" then +      return action(head,font,attr) +    else +      action=action[language] +      if action then +        return action(head,font,attr) +      end +    end +  end +  return head,false +end +otf.features.register("analyze",true)   +table.insert(fonts.triggers,"analyze") +fonts.analyzers.methods.latn=fonts.analyzers.aux.setstate +local zwnj=0x200C +local zwj=0x200D +local isol={ +  [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true, +  [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true, +  [0x06DD]=true,[zwnj]=true, +} +local isol_fina={ +  [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true, +  [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true, +  [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true, +  [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true, +  [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true, +  [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true, +  [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true, +  [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true, +  [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true, +  [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true, +  [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true, +  [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true, +  [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true, +  [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true, +  [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true, +	[0x0778]=true,[0x0779]=true,[0xFEF5]=true,[0xFEF7]=true, +	[0xFEF9]=true,[0xFEFB]=true, +} +local isol_fina_medi_init={ +  [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true, +  [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true, +  [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true, +  [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true, +  [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true, +  [0x0640]=true,[0x0641]=true,[0x0642]=true,[0x0643]=true, +  [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true, +  [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true, +  [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true, +  [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true, +  [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true, +  [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true, +  [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true, +  [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true, +  [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true, +  [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true, +  [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true, +  [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true, +  [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true, +  [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true, +  [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true, +  [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true, +  [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true, +  [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true, +  [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true, +  [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true, +  [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true, +  [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true, +  [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true, +  [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true, +  [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true, +  [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true, +  [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true, +  [0x077E]=true,[0x077F]=true,[zwj]=true, +} +local arab_warned={} +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) +    arab_warned[char]=true +  end +end +function fonts.analyzers.methods.nocolor(head,font,attr) +  for n in traverse_node_list(head,glyph) do +    if not font or n.font==font then +      fcr(n) +    end +  end +  return head,true +end +local function finish(first,last) +  if last then +    if first==last then +      local fc=first.char +      if isol_fina_medi_init[fc] or isol_fina[fc] then +        set_attribute(first,state,4)  +        if trace_analyzing then fcs(first,"font:isol") end +      else +        warning(first,"isol") +        set_attribute(first,state,0)  +        if trace_analyzing then fcr(first) end +      end +    else +      local lc=last.char +      if isol_fina_medi_init[lc] or isol_fina[lc] then +        set_attribute(last,state,3)  +        if trace_analyzing then fcs(last,"font:fina") end +      else +        warning(last,"fina") +        set_attribute(last,state,0)  +        if trace_analyzing then fcr(last) end +      end +    end +    first,last=nil,nil +  elseif first then +    local fc=first.char +    if isol_fina_medi_init[fc] or isol_fina[fc] then +      set_attribute(first,state,4)  +      if trace_analyzing then fcs(first,"font:isol") end +    else +      warning(first,"isol") +      set_attribute(first,state,0)  +      if trace_analyzing then fcr(first) end +    end +    first=nil +  end +  return first,last +end +function fonts.analyzers.methods.arab(head,font,attr)  +  local tfmdata=fontdata[font] +  local marks=tfmdata.marks +  local first,last,current,done=nil,nil,head,false +  while current do +    if current.id==glyph and current.subtype<256 and current.font==font and not has_attribute(current,state) then +      done=true +      local char=current.char +      if marks[char] then +        set_attribute(current,state,5)  +        if trace_analyzing then fcs(current,"font:mark") end +      elseif isol[char] then  +        first,last=finish(first,last) +        set_attribute(current,state,4)  +        if trace_analyzing then fcs(current,"font:isol") end +        first,last=nil,nil +      elseif not first then +        if isol_fina_medi_init[char] then +          set_attribute(current,state,1)  +          if trace_analyzing then fcs(current,"font:init") end +          first,last=first or current,current +        elseif isol_fina[char] then +          set_attribute(current,state,4)  +          if trace_analyzing then fcs(current,"font:isol") end +          first,last=nil,nil +        else  +          first,last=finish(first,last) +        end +      elseif isol_fina_medi_init[char] then +        first,last=first or current,current +        set_attribute(current,state,2)  +        if trace_analyzing then fcs(current,"font:medi") end +      elseif isol_fina[char] then +        if not has_attribute(last,state,1) then +          set_attribute(last,state,2)  +          if trace_analyzing then fcs(last,"font:medi") end +        end +        set_attribute(current,state,3)  +        if trace_analyzing then fcs(current,"font:fina") end +        first,last=nil,nil +      elseif char>=0x0600 and char<=0x06FF then +        if trace_analyzing then fcs(current,"font:rest") end +        first,last=finish(first,last) +      else  +        first,last=finish(first,last) +      end +    else +      first,last=finish(first,last) +    end +    current=current.next +  end +  first,last=finish(first,last) +  return head,done +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otc']={ +  version=1.001, +  comment="companion to font-otf.lua (context)", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local format,insert=string.format,table.insert +local type,next=type,next +local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) +local otf=fonts.otf +local tfm=fonts.tfm +local extra_lists={ +  tlig={ +    { +      endash="hyphen hyphen", +      emdash="hyphen hyphen hyphen", +      quotedblleft="quoteleft quoteleft", +      quotedblright="quoteright quoteright", +      quotedblleft="grave grave", +      quotedblright="quotesingle quotesingle", +      quotedblbase="comma comma", +      exclamdown="exclam grave", +      questiondown="question grave", +      guillemotleft="less less", +      guillemotright="greater greater", +    }, +  }, +  trep={ +    { +      [0x0022]=0x201D, +      [0x0027]=0x2019, +      [0x0060]=0x2018, +    }, +  }, +  anum={ +    {  +      [0x0030]=0x0660, +      [0x0031]=0x0661, +      [0x0032]=0x0662, +      [0x0033]=0x0663, +      [0x0034]=0x0664, +      [0x0035]=0x0665, +      [0x0036]=0x0666, +      [0x0037]=0x0667, +      [0x0038]=0x0668, +      [0x0039]=0x0669, +    }, +    {  +      [0x0030]=0x06F0, +      [0x0031]=0x06F1, +      [0x0032]=0x06F2, +      [0x0033]=0x06F3, +      [0x0034]=0x06F4, +      [0x0035]=0x06F5, +      [0x0036]=0x06F6, +      [0x0037]=0x06F7, +      [0x0038]=0x06F8, +      [0x0039]=0x06F9, +    }, +  }, +} +local extra_features={  +  tlig={ +    { +      features={ { scripts={ { script="*",langs={ "*" },} },tag="tlig",comment="added bij mkiv" },}, +      name="ctx_tlig_1", +      subtables={ { name="ctx_tlig_1_s" } }, +      type="gsub_ligature", +      flags={}, +    }, +  }, +  trep={ +    { +      features={ { scripts={ { script="*",langs={ "*" },} },tag="trep",comment="added bij mkiv" },}, +      name="ctx_trep_1", +      subtables={ { name="ctx_trep_1_s" } }, +      type="gsub_single", +      flags={}, +    }, +  }, +  anum={ +    { +      features={ { scripts={ { script="arab",langs={ "dflt","ARA" },} },tag="anum",comment="added bij mkiv" },}, +      name="ctx_anum_1", +      subtables={ { name="ctx_anum_1_s" } }, +      type="gsub_single", +      flags={}, +    }, +    { +      features={ { scripts={ { script="arab",langs={ "FAR" },} },tag="anum",comment="added bij mkiv" },}, +      name="ctx_anum_2", +      subtables={ { name="ctx_anum_2_s" } }, +      type="gsub_single", +      flags={}, +    }, +  }, +} +fonts.otf.enhancers["add some missing characters"]=function(data,filename) +end +fonts.otf.enhancers["enrich with features"]=function(data,filename) +  local used={} +  for i=1,#otf.glists do +    local g=data[otf.glists[i]] +    if g then +      for i=1,#g do +        local f=g[i].features +        if f then +          for i=1,#f do +            local t=f[i].tag +            if t then used[t]=true end +          end +        end +      end +    end +  end +  local glyphs=data.glyphs +  local indices=data.map.map +  data.gsub=data.gsub or {} +  for kind,specifications in next,extra_features do +    if not used[kind] then +      local done=0 +      for s=1,#specifications do +        local added=false +        local specification=specifications[s] +        local list=extra_lists[kind][s] +        local name=specification.name.."_s" +        if specification.type=="gsub_ligature" then +          for unicode,index in next,indices do +            local glyph=glyphs[index] +            local ligature=list[glyph.name] +            if ligature then +              local o=glyph.lookups or {} +              o[name]={ +                { +                  ["type"]="ligature", +                  ["specification"]={ +                    char=glyph.name, +                    components=ligature, +                  } +                } +              } +              glyph.lookups,done,added=o,done+1,true +            end +          end +        elseif specification.type=="gsub_single" then +          for unicode,index in next,indices do +            local glyph=glyphs[index] +            local r=list[unicode] +            if r then +              local replacement=indices[r] +              if replacement and glyphs[replacement] then +                local o=glyph.lookups or {} +                o[name]={ +                  { +                    ["type"]="substitution", +                    ["specification"]={ +                      variant=glyphs[replacement].name, +                    } +                  } +                } +                glyph.lookups,done,added=o,done+1,true +              end +            end +          end +        end +        if added then +          insert(data.gsub,s,table.fastcopy(specification))  +        end +      end +      if done>0 then +        if trace_loading then +          logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done) +        end +      end +    end +  end +end +otf.tables.features['tlig']='TeX Ligatures' +otf.tables.features['trep']='TeX Replacements' +otf.tables.features['anum']='Arabic Digits' +otf.features.register_base_substitution('tlig') +otf.features.register_base_substitution('trep') +otf.features.register_base_substitution('anum') +fonts.initializers.base.otf.equaldigits=fonts.initializers.common.equaldigits +fonts.initializers.node.otf.equaldigits=fonts.initializers.common.equaldigits +fonts.initializers.base.otf.lineheight=fonts.initializers.common.lineheight +fonts.initializers.node.otf.lineheight=fonts.initializers.common.lineheight +fonts.initializers.base.otf.compose=fonts.initializers.common.compose +fonts.initializers.node.otf.compose=fonts.initializers.common.compose + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-def']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local format,concat,gmatch,match,find,lower=string.format,table.concat,string.gmatch,string.match,string.find,string.lower +local tostring,next=tostring,next +local lpegmatch=lpeg.match +local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) +local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) +trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") +trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*") +fonts=fonts    or {} +fonts.define=fonts.define or {} +fonts.tfm=fonts.tfm  or {} +fonts.ids=fonts.ids  or {} +fonts.vf=fonts.vf   or {} +fonts.used=fonts.used  or {} +local tfm=fonts.tfm +local vf=fonts.vf +local define=fonts.define +tfm.version=1.01 +tfm.cache=containers.define("fonts","tfm",tfm.version,false)  +define.method="afm or tfm"  +define.specify=fonts.define.specify or {} +define.methods=fonts.define.methods or {} +tfm.fonts=tfm.fonts    or {} +tfm.readers=tfm.readers   or {} +tfm.internalized=tfm.internalized or {}  +tfm.readers.sequence={ 'otf','ttf','afm','tfm' } +tfm.auto_afm=true +local readers=tfm.readers +local sequence=readers.sequence +fonts.version=1.05 +fonts.cache=containers.define("fonts","def",fonts.version,false) +local splitter,specifiers=nil,"" +local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc +local left=P("(") +local right=P(")") +local colon=P(":") +local space=P(" ") +define.defaultlookup="file" +local prefixpattern=P(false) +function define.add_specifier(symbol) +  specifiers=specifiers..symbol +  local method=S(specifiers) +  local lookup=C(prefixpattern)*colon +  local sub=left*C(P(1-left-right-method)^1)*right +  local specification=C(method)*C(P(1)^1) +  local name=C((1-sub-specification)^1) +  splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc(""))) +end +function define.add_lookup(str,default) +  prefixpattern=prefixpattern+P(str) +end +define.add_lookup("file") +define.add_lookup("name") +define.add_lookup("spec") +function define.get_specification(str) +  return lpegmatch(splitter,str) +end +function define.register_split(symbol,action) +  define.add_specifier(symbol) +  define.specify[symbol]=action +end +function define.makespecification(specification,lookup,name,sub,method,detail,size) +  size=size or 655360 +  if trace_defining then +    logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", +      specification,(lookup~="" and lookup) or "[file]",(name~="" and name) or "-", +      (sub~="" and sub) or "-",(method~="" and method) or "-",(detail~="" and detail) or "-") +  end +  if not lookup or lookup=="" then +    lookup=define.defaultlookup +  end +  local t={ +    lookup=lookup, +    specification=specification, +    size=size, +    name=name, +    sub=sub, +    method=method, +    detail=detail, +    resolved="", +    forced="", +    features={}, +  } +  return t +end +function define.analyze(specification,size) +  local lookup,name,sub,method,detail=define.get_specification(specification or "") +  return define.makespecification(specification,lookup,name,sub,method,detail,size) +end +local sortedhashkeys=table.sortedhashkeys +function tfm.hash_features(specification) +  local features=specification.features +  if features then +    local t={} +    local normal=features.normal +    if normal and next(normal) then +      local f=sortedhashkeys(normal) +      for i=1,#f do +        local v=f[i] +        if v~="number" and v~="features" then  +          t[#t+1]=v..'='..tostring(normal[v]) +        end +      end +    end +    local vtf=features.vtf +    if vtf and next(vtf) then +      local f=sortedhashkeys(vtf) +      for i=1,#f do +        local v=f[i] +        t[#t+1]=v..'='..tostring(vtf[v]) +      end +    end +    if #t>0 then +      return concat(t,"+") +    end +  end +  return "unknown" +end +fonts.designsizes={} +function tfm.hash_instance(specification,force) +  local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks +  if force or not hash then +    hash=tfm.hash_features(specification) +    specification.hash=hash +  end +  if size<1000 and fonts.designsizes[hash] then +    size=math.round(tfm.scaled(size,fonts.designsizes[hash])) +    specification.size=size +  end +    if fallbacks then +      return hash..' @ '..tostring(size)..' @ '..fallbacks +    else +      return hash..' @ '..tostring(size) +    end +end +define.resolvers=resolvers +function define.resolvers.file(specification) +  local suffix=file.suffix(specification.name) +  if fonts.formats[suffix] then +    specification.forced=suffix +    specification.name=file.removesuffix(specification.name) +  end +end +function define.resolvers.name(specification) +  local resolve=fonts.names.resolve +  if resolve then +    local resolved,sub=fonts.names.resolve(specification) +    specification.resolved,specification.sub=resolved,sub +    if resolved then +      local suffix=file.suffix(resolved) +      if fonts.formats[suffix] then +        specification.forced=suffix +        specification.name=file.removesuffix(resolved) +      else +        specification.name=resolved +      end +    end +  else +    define.resolvers.file(specification) +  end +end +function define.resolvers.spec(specification) +  local resolvespec=fonts.names.resolvespec +  if resolvespec then +    specification.resolved,specification.sub=fonts.names.resolvespec(specification) +    if specification.resolved then +      specification.forced=file.extname(specification.resolved) +      specification.name=file.removesuffix(specification.resolved) +    end +  else +    define.resolvers.name(specification) +  end +end +function define.resolve(specification) +  if not specification.resolved or specification.resolved=="" then  +    local r=define.resolvers[specification.lookup] +    if r then +      r(specification) +    end +  end +  if specification.forced=="" then +    specification.forced=nil +  else +    specification.forced=specification.forced +  end +  specification.hash=lower(specification.name..' @ '..tfm.hash_features(specification)) +  if specification.sub and specification.sub~="" then +    specification.hash=specification.sub..' @ '..specification.hash +  end +  return specification +end +function tfm.read(specification) +  local hash=tfm.hash_instance(specification) +  local tfmtable=tfm.fonts[hash]  +  if not tfmtable then +    local forced=specification.forced or "" +    if forced~="" then +      tfmtable=readers[lower(forced)](specification) +      if not tfmtable then +        logs.report("define font","forced type %s of %s not found",forced,specification.name) +      end +    else +      for s=1,#sequence do  +        local reader=sequence[s] +        if readers[reader] then  +          if trace_defining then +            logs.report("define font","trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") +          end +          tfmtable=readers[reader](specification) +          if tfmtable then +            break +          else +            specification.filename=nil +          end +        end +      end +    end +    if tfmtable then +      if directive_embedall then +        tfmtable.embedding="full" +      elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then +        tfmtable.embedding="no" +      else +        tfmtable.embedding="subset" +      end +      tfm.fonts[hash]=tfmtable +      fonts.designsizes[specification.hash]=tfmtable.designsize +    end +  end +  if not tfmtable then +    logs.report("define font","font with name %s is not found",specification.name) +  end +  return tfmtable +end +function tfm.read_and_define(name,size)  +  local specification=define.analyze(name,size) +  local method=specification.method +  if method and define.specify[method] then +    specification=define.specify[method](specification) +  end +  specification=define.resolve(specification) +  local hash=tfm.hash_instance(specification) +  local id=define.registered(hash) +  if not id then +    local fontdata=tfm.read(specification) +    if fontdata then +      fontdata.hash=hash +      id=font.define(fontdata) +      define.register(fontdata,id) +      tfm.cleanup_table(fontdata) +    else +      id=0  +    end +  end +  return fonts.ids[id],id +end +local function check_tfm(specification,fullname) +  local foundname=resolvers.findbinfile(fullname,'tfm') or ""  +  if foundname=="" then +    foundname=resolvers.findbinfile(fullname,'ofm') or ""  +  end +  if foundname~="" then +    specification.filename,specification.format=foundname,"ofm" +    return tfm.read_from_tfm(specification) +  end +end +local function check_afm(specification,fullname) +  local foundname=resolvers.findbinfile(fullname,'afm') or ""  +  if foundname=="" and tfm.auto_afm then +    local encoding,shortname=match(fullname,"^(.-)%-(.*)$")  +    if encoding and shortname and fonts.enc.known[encoding] then +      shortname=resolvers.findbinfile(shortname,'afm') or ""  +      if shortname~="" then +        foundname=shortname +        if trace_loading then +          logs.report("load afm","stripping encoding prefix from filename %s",afmname) +        end +      end +    end +  end +  if foundname~="" then +    specification.filename,specification.format=foundname,"afm" +    return tfm.read_from_afm(specification) +  end +end +function readers.tfm(specification) +  local fullname,tfmtable=specification.filename or "",nil +  if fullname=="" then +    local forced=specification.forced or "" +    if forced~="" then +      tfmtable=check_tfm(specification,specification.name.."."..forced) +    end +    if not tfmtable then +      tfmtable=check_tfm(specification,specification.name) +    end +  else +    tfmtable=check_tfm(specification,fullname) +  end +  return tfmtable +end +function readers.afm(specification,method) +  local fullname,tfmtable=specification.filename or "",nil +  if fullname=="" then +    local forced=specification.forced or "" +    if forced~="" then +      tfmtable=check_afm(specification,specification.name.."."..forced) +    end +    if not tfmtable then +      method=method or define.method or "afm or tfm" +      if method=="tfm" then +        tfmtable=check_tfm(specification,specification.name) +      elseif method=="afm" then +        tfmtable=check_afm(specification,specification.name) +      elseif method=="tfm or afm" then +        tfmtable=check_tfm(specification,specification.name) or check_afm(specification,specification.name) +      else  +        tfmtable=check_afm(specification,specification.name) or check_tfm(specification,specification.name) +      end +    end +  else +    tfmtable=check_afm(specification,fullname) +  end +  return tfmtable +end +local function check_otf(forced,specification,suffix,what) +  local name=specification.name +  if forced then +    name=file.addsuffix(name,suffix,true) +  end +  local fullname,tfmtable=resolvers.findbinfile(name,suffix) or "",nil  +  if fullname=="" then +    local fb=fonts.names.old_to_new[name] +    if fb then +      fullname=resolvers.findbinfile(fb,suffix) or "" +    end +  end +  if fullname=="" then +    local fb=fonts.names.new_to_old[name] +    if fb then +      fullname=resolvers.findbinfile(fb,suffix) or "" +    end +  end +  if fullname~="" then +    specification.filename,specification.format=fullname,what  +    tfmtable=tfm.read_from_open_type(specification)        +  end +  return tfmtable +end +function readers.opentype(specification,suffix,what) +  local forced=specification.forced or "" +  if forced=="otf" then +    return check_otf(true,specification,forced,"opentype") +  elseif forced=="ttf" or forced=="ttc" or forced=="dfont" then +    return check_otf(true,specification,forced,"truetype") +  else +    return check_otf(false,specification,suffix,what) +  end +end +function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end +function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end +function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end  +function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end +function define.check(features,defaults)  +  local done=false +  if features and next(features) then +    for k,v in next,defaults do +      if features[k]==nil then +        features[k],done=v,true +      end +    end +  else +    features,done=table.fastcopy(defaults),true +  end +  return features,done  +end +define.last=nil +function define.register(fontdata,id) +  if fontdata and id then +    local hash=fontdata.hash +    if not tfm.internalized[hash] then +      if trace_defining then +        logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?") +      end +      fonts.identifiers[id]=fontdata +      fonts.characters [id]=fontdata.characters +      fonts.quads   [id]=fontdata.parameters.quad +      tfm.internalized[hash]=id +    end +  end +end +function define.registered(hash) +  local id=tfm.internalized[hash] +  return id,id and fonts.ids[id] +end +local cache_them=false +function tfm.make(specification) +  local fvm=define.methods[specification.features.vtf.preset] +  if fvm then +    return fvm(specification) +  else +    return nil +  end +end +function define.read(specification,size,id)  +  statistics.starttiming(fonts) +  if type(specification)=="string" then +    specification=define.analyze(specification,size) +  end +  local method=specification.method +  if method and define.specify[method] then +    specification=define.specify[method](specification) +  end +  specification=define.resolve(specification) +  local hash=tfm.hash_instance(specification) +  if cache_them then +    local fontdata=containers.read(fonts.cache,hash)  +  end +  local fontdata=define.registered(hash)  +  if not fontdata then +    if specification.features.vtf and specification.features.vtf.preset then +      fontdata=tfm.make(specification) +    else +      fontdata=tfm.read(specification) +      if fontdata then +        tfm.check_virtual_id(fontdata) +      end +    end +    if cache_them then +      fontdata=containers.write(fonts.cache,hash,fontdata)  +    end +    if fontdata then +      fontdata.hash=hash +      fontdata.cache="no" +      if id then +        define.register(fontdata,id) +      end +    end +  end +  define.last=fontdata or id  +  if not fontdata then +    logs.report("define font","unknown font %s, loading aborted",specification.name) +  elseif trace_defining and type(fontdata)=="table" then +    logs.report("define font","using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", +      fontdata.type     or "unknown", +      id           or "?", +      fontdata.name     or "?", +      fontdata.size     or "default", +      fontdata.encodingbytes or "?", +      fontdata.encodingname or "unicode", +      fontdata.fullname   or "?", +      file.basename(fontdata.filename or "?")) +  end +  statistics.stoptiming(fonts) +  return fontdata +end +function vf.find(name) +  name=file.removesuffix(file.basename(name)) +  if tfm.resolve_vf then +    local format=fonts.logger.format(name) +    if format=='tfm' or format=='ofm' then +      if trace_defining then +        logs.report("define font","locating vf for %s",name) +      end +      return resolvers.findbinfile(name,"ovf") +    else +      if trace_defining then +        logs.report("define font","vf for %s is already taken care of",name) +      end +      return nil  +    end +  else +    if trace_defining then +      logs.report("define font","locating vf for %s",name) +    end +    return resolvers.findbinfile(name,"ovf") +  end +end +callbacks.register('define_font',define.read,"definition of fonts (tfmtable preparation)") +callbacks.register('find_vf_file',vf.find,"locating virtual fonts, insofar needed")  + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-xtx']={ +  version=1.001, +  comment="companion to font-ini.mkiv", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +local texsprint,count=tex.sprint,tex.count +local format,concat,gmatch,match,find,lower=string.format,table.concat,string.gmatch,string.match,string.find,string.lower +local tostring,next=tostring,next +local lpegmatch=lpeg.match +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +local list={} +fonts.define.specify.colonized_default_lookup="file" +local function isstyle(s) +  local style=string.lower(s):split("/") +  for _,v in ipairs(style) do +    if v=="b" then +      list.style="bold" +    elseif v=="i" then +      list.style="italic" +    elseif v=="bi" or v=="ib" then +      list.style="bolditalic" +    elseif v:find("^s=") then +      list.optsize=v:split("=")[2] +    elseif v=="aat" or v=="icu" or v=="gr" then +      logs.report("load font","unsupported font option: %s",v) +    elseif not v:is_empty() then +      list.style=v:gsub("[^%a%d]","") +    end +  end +end +fonts=fonts   or {} +fonts.otf=fonts.otf or {} +local otf=fonts.otf +otf.tables=otf.tables or {} +otf.tables.defaults={ +  dflt={ +    "ccmp","locl","rlig","liga","clig", +    "kern","mark","mkmk","itlc", +  }, +  arab={ +    "ccmp","locl","isol","fina","fin2", +    "fin3","medi","med2","init","rlig", +    "calt","liga","cswh","mset","curs", +    "kern","mark","mkmk", +  }, +  deva={ +    "ccmp","locl","init","nukt","akhn", +    "rphf","blwf","half","pstf","vatu", +    "pres","blws","abvs","psts","haln", +    "calt","blwm","abvm","dist","kern", +    "mark","mkmk", +  }, +  khmr={ +    "ccmp","locl","pref","blwf","abvf", +    "pstf","pres","blws","abvs","psts", +    "clig","calt","blwm","abvm","dist", +    "kern","mark","mkmk", +  }, +  thai={ +    "ccmp","locl","liga","kern","mark", +    "mkmk", +  }, +  hang={ +    "ccmp","ljmo","vjmo","tjmo", +  }, +} +otf.tables.defaults.beng=otf.tables.defaults.deva +otf.tables.defaults.guru=otf.tables.defaults.deva +otf.tables.defaults.gujr=otf.tables.defaults.deva +otf.tables.defaults.orya=otf.tables.defaults.deva +otf.tables.defaults.taml=otf.tables.defaults.deva +otf.tables.defaults.telu=otf.tables.defaults.deva +otf.tables.defaults.knda=otf.tables.defaults.deva +otf.tables.defaults.mlym=otf.tables.defaults.deva +otf.tables.defaults.sinh=otf.tables.defaults.deva +otf.tables.defaults.syrc=otf.tables.defaults.arab +otf.tables.defaults.mong=otf.tables.defaults.arab +otf.tables.defaults.nko=otf.tables.defaults.arab +otf.tables.defaults.tibt=otf.tables.defaults.khmr +otf.tables.defaults.lao=otf.tables.defaults.thai +local function parse_script(script) +  if otf.tables.scripts[script] then +    local dflt +    if otf.tables.defaults[script] then +      logs.report("load font","auto-selecting default features for script: %s",script) +      dflt=otf.tables.defaults[script] +    else +      logs.report("load font","auto-selecting default features for script: dflt (was %s)",script) +      dflt=otf.tables.defaults["dflt"] +    end +    for _,v in next,dflt do +      list[v]="yes" +    end +  else +    logs.report("load font","unknown script: %s",script) +  end +end +local function issome ()  list.lookup=fonts.define.specify.colonized_default_lookup end +local function isfile ()  list.lookup='file' end +local function isname ()  list.lookup='name' end +local function thename(s)  list.name=s end +local function issub (v)  list.sub=v end +local function iskey (k,v) +  if k=="script" then +    parse_script(v) +  end +  list[k]=v +end +local function istrue (s)  list[s]=true end +local function isfalse(s)  list[s]=false end +local spaces=lpeg.P(" ")^0 +local namespec=(1-lpeg.S("/:("))^0  +local filespec=(lpeg.R("az","AZ")*lpeg.P(":"))^-1*(1-lpeg.S(":("))^1 +local crapspec=spaces*lpeg.P("/")*(((1-lpeg.P(":"))^0)/isstyle)*spaces +local filename=(lpeg.P("file:")/isfile*(filespec/thename))+(lpeg.P("[")*lpeg.P(true)/isfile*(((1-lpeg.P("]"))^0)/thename)*lpeg.P("]")) +local fontname=(lpeg.P("name:")/isname*(namespec/thename))+lpeg.P(true)/issome*(namespec/thename) +local sometext=(lpeg.R("az","AZ","09")+lpeg.S("+-."))^1 +local truevalue=lpeg.P("+")*spaces*(sometext/istrue) +local falsevalue=lpeg.P("-")*spaces*(sometext/isfalse) +local keyvalue=lpeg.P("+")+(lpeg.C(sometext)*spaces*lpeg.P("=")*spaces*lpeg.C(sometext))/iskey +local somevalue=sometext/istrue +local subvalue=lpeg.P("(")*(lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub)*lpeg.P(")")  +local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces +local options=lpeg.P(":")*spaces*(lpeg.P(";")^0*option)^0 +local pattern=(filename+fontname)*subvalue^0*crapspec^0*options^0 +local normalize_meanings=fonts.otf.meanings.normalize +function fonts.define.specify.colonized(specification)  +  list={} +  lpegmatch(pattern,specification.specification) +  if list.style then +    specification.style=list.style +    list.style=nil +  end +  if list.optsize then +    specification.optsize=list.optsize +    list.optsize=nil +  end +  if list.name then +    if resolvers.find_file(list.name,"tfm") then +      list.lookup="file" +      list.name=file.addsuffix(list.name,"tfm") +    elseif resolvers.find_file(list.name,"ofm") then +      list.lookup="file" +      list.name=file.addsuffix(list.name,"ofm") +    end +    specification.name=list.name +    list.name=nil +  end +  if list.lookup then +    specification.lookup=list.lookup +    list.lookup=nil +  end +  if list.sub then +    specification.sub=list.sub +    list.sub=nil +  end +  specification.features.normal=normalize_meanings(list) +  return specification +end +fonts.define.register_split(":",fonts.define.specify.colonized) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-dum']={ +  version=1.001, +  comment="companion to luatex-*.tex", +  author="Hans Hagen, PRAGMA-ADE, Hasselt NL", +  copyright="PRAGMA ADE / ConTeXt Development Team", +  license="see context related readme files" +} +fonts=fonts or {} +fonts.otf.pack=false +fonts.tfm.resolve_vf=false  +fonts.tfm.fontname_mode="specification" +fonts.tfm.readers=fonts.tfm.readers or {} +fonts.tfm.readers.sequence={ 'otf','ttf','tfm' } +fonts.tfm.readers.afm=nil +fonts.define=fonts.define or {} +fonts.define.specify.colonized_default_lookup="name" +function fonts.define.get_specification(str) +  return "",str,"",":",str +end +fonts.logger=fonts.logger or {} +function fonts.logger.save() +end +fonts.names=fonts.names or {} +fonts.names.version=1.001  +fonts.names.basename="luatex-fonts-names.lua" +fonts.names.new_to_old={} +fonts.names.old_to_new={} +local data,loaded=nil,false +local fileformats={ "lua","tex","other text files" } +function fonts.names.resolve(name,sub) +  if not loaded then +    local basename=fonts.names.basename +    if basename and basename~="" then +      for i=1,#fileformats do +        local format=fileformats[i] +        local foundname=resolvers.find_file(basename,format) or "" +        if foundname~="" then +          data=dofile(foundname) +          break +        end +      end +    end +    loaded=true +  end +  if type(data)=="table" and data.version==fonts.names.version then +    local condensed=string.gsub(string.lower(name),"[^%a%d]","") +    local found=data.mappings and data.mappings[condensed] +    if found then +      local fontname,filename,subfont=found[1],found[2],found[3] +      if subfont then +        return filename,fontname +      else +        return filename,false +      end +    else +      return name,false  +    end +  end +end +fonts.names.resolvespec=fonts.names.resolve +table.insert(fonts.triggers,"itlc") +local function itlc(tfmdata,value) +  if value then +    local metadata=tfmdata.shared.otfdata.metadata +    if metadata then +      local italicangle=metadata.italicangle +      if italicangle and italicangle~=0 then +        local uwidth=(metadata.uwidth or 40)/2 +        for unicode,d in next,tfmdata.descriptions do +          local it=d.boundingbox[3]-d.width+uwidth +          if it~=0 then +            d.italic=it +          end +        end +        tfmdata.has_italic=true +      end +    end +  end +end +fonts.initializers.base.otf.itlc=itlc +fonts.initializers.node.otf.itlc=itlc +function fonts.initializers.common.slant(tfmdata,value) +  value=tonumber(value) +  if not value then +    value=0 +  elseif value>1 then +    value=1 +  elseif value<-1 then +    value=-1 +  end +  tfmdata.slant_factor=value +end +function fonts.initializers.common.extend(tfmdata,value) +  value=tonumber(value) +  if not value then +    value=0 +  elseif value>10 then +    value=10 +  elseif value<-10 then +    value=-10 +  end +  tfmdata.extend_factor=value +end +table.insert(fonts.triggers,"slant") +table.insert(fonts.triggers,"extend") +fonts.initializers.base.otf.slant=fonts.initializers.common.slant +fonts.initializers.node.otf.slant=fonts.initializers.common.slant +fonts.initializers.base.otf.extend=fonts.initializers.common.extend +fonts.initializers.node.otf.extend=fonts.initializers.common.extend +fonts.protrusions=fonts.protrusions    or {} +fonts.protrusions.setups=fonts.protrusions.setups or {} +local setups=fonts.protrusions.setups +local function map_opbd_onto_protrusion(tfmdata,value,opbd) +  local characters,descriptions=tfmdata.characters,tfmdata.descriptions +  local otfdata=tfmdata.shared.otfdata +  local singles=otfdata.shared.featuredata.gpos_single +  local script,language=tfmdata.script,tfmdata.language +  local done,factor,left,right=false,1,1,1 +  local setup=setups[value] +  if setup then +    factor=setup.factor or 1 +    left=setup.left  or 1 +    right=setup.right or 1 +  else +    factor=tonumber(value) or 1 +  end +  if opbd~="right" then +    local validlookups,lookuplist=fonts.otf.collect_lookups(otfdata,"lfbd",script,language) +    if validlookups then +      for i=1,#lookuplist do +        local lookup=lookuplist[i] +        local data=singles[lookup] +        if data then +          if trace_protrusion then +            logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup) +          end +          for k,v in next,data do +            local p=- (v[1]/1000)*factor*left +            characters[k].left_protruding=p +            if trace_protrusion then +              logs.report("opbd","lfbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) +            end +          end +          done=true +        end +      end +    end +  end +  if opbd~="left" then +    local validlookups,lookuplist=fonts.otf.collect_lookups(otfdata,"rtbd",script,language) +    if validlookups then +      for i=1,#lookuplist do +        local lookup=lookuplist[i] +        local data=singles[lookup] +        if data then +          if trace_protrusion then +            logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup) +          end +          for k,v in next,data do +            local p=(v[1]/1000)*factor*right +            characters[k].right_protruding=p +            if trace_protrusion then +              logs.report("opbd","rtbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) +            end +          end +        end +        done=true +      end +    end +  end +  tfmdata.auto_protrude=done +end +function fonts.initializers.common.protrusion(tfmdata,value) +  if value then +    local opbd=tfmdata.shared.features.opbd +    if opbd then +      map_opbd_onto_protrusion(tfmdata,value,opbd) +    elseif value then +      local setup=setups[value] +      if setup then +        local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1 +        local emwidth=tfmdata.parameters.quad +        tfmdata.auto_protrude=true +        for i,chr in next,tfmdata.characters do +          local v,pl,pr=setup[i],nil,nil +          if v then +            pl,pr=v[1],v[2] +          end +          if pl and pl~=0 then chr.left_protruding=left*pl*factor end +          if pr and pr~=0 then chr.right_protruding=right*pr*factor end +        end +      end +    end +  end +end +fonts.expansions=fonts.expansions    or {} +fonts.expansions.setups=fonts.expansions.setups or {} +local setups=fonts.expansions.setups +function fonts.initializers.common.expansion(tfmdata,value) +  if value then +    local setup=setups[value] +    if setup then +      local stretch,shrink,step,factor=setup.stretch or 0,setup.shrink or 0,setup.step or 0,setup.factor or 1 +      tfmdata.stretch,tfmdata.shrink,tfmdata.step,tfmdata.auto_expand=stretch*10,shrink*10,step*10,true +      for i,chr in next,tfmdata.characters do +        local v=setup[i] +        if v and v~=0 then +          chr.expansion_factor=v*factor +        else  +          chr.expansion_factor=factor +        end +      end +    end +  end +end +table.insert(fonts.manipulators,"protrusion") +table.insert(fonts.manipulators,"expansion") +fonts.initializers.base.otf.protrusion=fonts.initializers.common.protrusion +fonts.initializers.node.otf.protrusion=fonts.initializers.common.protrusion +fonts.initializers.base.otf.expansion=fonts.initializers.common.expansion +fonts.initializers.node.otf.expansion=fonts.initializers.common.expansion +function fonts.register_message() +end +local byte=string.byte +fonts.expansions.setups['default']={ +  stretch=2,shrink=2,step=.5,factor=1, +  [byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7, +  [byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7, +  [byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7, +  [byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7, +  [byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7, +  [byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7, +  [byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7, +  [byte('w')]=0.7,[byte('z')]=0.7, +  [byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7, +} +fonts.protrusions.setups['default']={ +  factor=1,left=1,right=1, +  [0x002C]={ 0,1  }, +  [0x002E]={ 0,1  }, +  [0x003A]={ 0,1  }, +  [0x003B]={ 0,1  }, +  [0x002D]={ 0,1  }, +  [0x2013]={ 0,0.50 }, +  [0x2014]={ 0,0.33 }, +  [0x3001]={ 0,1  }, +  [0x3002]={ 0,1  }, +  [0x060C]={ 0,1  }, +  [0x061B]={ 0,1  }, +  [0x06D4]={ 0,1  }, +} +fonts.otf.meanings=fonts.otf.meanings or {} +fonts.otf.meanings.normalize=fonts.otf.meanings.normalize or function(t) +  if t.rand then +    t.rand="random" +  end +end +function fonts.otf.name_to_slot(name) +  local tfmdata=fonts.ids[font.current()] +  if tfmdata and tfmdata.shared then +    local otfdata=tfmdata.shared.otfdata +    local unicode=otfdata.luatex.unicodes[name] +    return unicode and (type(unicode)=="number" and unicode or unicode[1]) +  end +end +function fonts.otf.char(n) +  if type(n)=="string" then +    n=fonts.otf.name_to_slot(n) +  end +  if type(n)=="number" then +    tex.sprint("\\char"..n) +  end +end +fonts.strippables=table.tohash { +  0x000AD,0x017B4,0x017B5,0x0200B,0x0200C,0x0200D,0x0200E,0x0200F,0x0202A,0x0202B, +  0x0202C,0x0202D,0x0202E,0x02060,0x02061,0x02062,0x02063,0x0206A,0x0206B,0x0206C, +  0x0206D,0x0206E,0x0206F,0x0FEFF,0x1D173,0x1D174,0x1D175,0x1D176,0x1D177,0x1D178, +  0x1D179,0x1D17A,0xE0001,0xE0020,0xE0021,0xE0022,0xE0023,0xE0024,0xE0025,0xE0026, +  0xE0027,0xE0028,0xE0029,0xE002A,0xE002B,0xE002C,0xE002D,0xE002E,0xE002F,0xE0030, +  0xE0031,0xE0032,0xE0033,0xE0034,0xE0035,0xE0036,0xE0037,0xE0038,0xE0039,0xE003A, +  0xE003B,0xE003C,0xE003D,0xE003E,0xE003F,0xE0040,0xE0041,0xE0042,0xE0043,0xE0044, +  0xE0045,0xE0046,0xE0047,0xE0048,0xE0049,0xE004A,0xE004B,0xE004C,0xE004D,0xE004E, +  0xE004F,0xE0050,0xE0051,0xE0052,0xE0053,0xE0054,0xE0055,0xE0056,0xE0057,0xE0058, +  0xE0059,0xE005A,0xE005B,0xE005C,0xE005D,0xE005E,0xE005F,0xE0060,0xE0061,0xE0062, +  0xE0063,0xE0064,0xE0065,0xE0066,0xE0067,0xE0068,0xE0069,0xE006A,0xE006B,0xE006C, +  0xE006D,0xE006E,0xE006F,0xE0070,0xE0071,0xE0072,0xE0073,0xE0074,0xE0075,0xE0076, +  0xE0077,0xE0078,0xE0079,0xE007A,0xE007B,0xE007C,0xE007D,0xE007E,0xE007F, +} + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-clr']={ +  version=1.001, +  comment="companion to font-otf.lua (font color)", +  author="Khaled Hosny and Elie Roux", +  copyright="Luaotfload Development Team", +  license="GPL" +} +fonts.triggers=fonts.triggers      or {} +fonts.initializers=fonts.initializers    or {} +fonts.initializers.common=fonts.initializers.common or {} +local initializers,format=fonts.initializers,string.format +table.insert(fonts.triggers,"color") +function initializers.common.color(tfmdata,value) +  local sanitized +  if value then +    value=tostring(value) +    if #value==6 or #value==8 then +      sanitized=value +    elseif #value==7 then +      _,_,sanitized=value:find("(......)") +    elseif #value>8 then +      _,_,sanitized=value:find("(........)") +    else +    end +  end +  if sanitized then +    tfmdata.color=sanitized +    add_color_callback() +  end +end +initializers.base.otf.color=initializers.common.color +initializers.node.otf.color=initializers.common.color +local function hex2dec(hex,one) +  if one then +    return format("%.1g",tonumber(hex,16)/255) +  else +    return format("%.3g",tonumber(hex,16)/255) +  end +end +local res +local function pageresources(a) +  local res2 +  if not res then +    res="/TransGs1<</ca 1/CA 1>>" +  end +  res2=format("/TransGs%s<</ca %s/CA %s>>",a,a,a) +  res=format("%s%s",res,res:find(res2) and "" or res2) +end +local function hex_to_rgba(hex) +  local r,g,b,a,push,pop,res3 +  if hex then +    if #hex==6 then +      _,_,r,g,b=hex:find('(..)(..)(..)') +    elseif #hex==8 then +      _,_,r,g,b,a=hex:find('(..)(..)(..)(..)') +      a=hex2dec(a,true) +      pageresources(a) +    end +  else +    return nil +  end +  r=hex2dec(r) +  g=hex2dec(g) +  b=hex2dec(b) +  if a then +    push=format('/TransGs%g gs %s %s %s rg',a,r,g,b) +    pop='0 g /TransGs1 gs' +  else +    push=format('%s %s %s rg',r,g,b) +    pop='0 g' +  end +  return push,pop +end +local glyph=node.id('glyph') +local hlist=node.id('hlist') +local vlist=node.id('vlist') +local whatsit=node.id('whatsit') +local pgi=node.id('page_insert') +local sbox=node.id('sub_box') +local function lookup_next_color(head) +  for n in node.traverse(head) do +    if n.id==glyph then +      if fonts.ids[n.font] and fonts.ids[n.font].color then +        return fonts.ids[n.font].color +      else +        return -1 +      end +    elseif n.id==vlist or n.id==hlist or n.id==sbox then +      local r=lookup_next_color(n.list) +      if r==-1 then +        return -1 +      elseif r then +        return r +      end +    elseif n.id==whatsit or n.id==pgi then +      return -1 +    end +  end +  return nil +end +local function node_colorize(head,current_color,next_color) +  for n in node.traverse(head) do +    if n.id==hlist or n.id==vlist or n.id==sbox then +      local next_color_in=lookup_next_color(n.next) or next_color +      n.list,current_color=node_colorize(n.list,current_color,next_color_in) +    elseif n.id==glyph then +      local tfmdata=fonts.ids[n.font] +      if tfmdata and tfmdata.color then +        if tfmdata.color~=current_color then +          local pushcolor=hex_to_rgba(tfmdata.color) +          local push=node.new(whatsit,8) +          push.mode=1 +          push.data=pushcolor +          head=node.insert_before(head,n,push) +          current_color=tfmdata.color +        end +        local next_color_in=lookup_next_color (n.next) or next_color +        if next_color_in~=tfmdata.color then +          local _,popcolor=hex_to_rgba(tfmdata.color) +          local pop=node.new(whatsit,8) +          pop.mode=1 +          pop.data=popcolor +          head=node.insert_after(head,n,pop) +          current_color=nil +        end +      end +    end +  end +  return head,current_color +end +local function font_colorize(head) +  if res then +   local r="/ExtGState<<"..res..">>" +   tex.pdfpageresources=tex.pdfpageresources:gsub(r,"") +  end +  local h=node_colorize(head,nil,nil) +  if res and res:find("%S") then  +   local r="/ExtGState<<"..res..">>" +   tex.pdfpageresources=tex.pdfpageresources..r +  end +  return h +end +local color_callback_activated=0 +function add_color_callback() +  if color_callback_activated==0 then +    luatexbase.add_to_callback("pre_output_filter",font_colorize,"loaotfload.colorize") +    color_callback_activated=1 +  end +end + +end -- closure diff --git a/luaotfload-legacy-tool.lua b/luaotfload-legacy-tool.lua new file mode 100755 index 0000000..30ab13b --- /dev/null +++ b/luaotfload-legacy-tool.lua @@ -0,0 +1,105 @@ +#!/usr/bin/env texlua +--[[ +This file is copyright 2010 Elie Roux and Khaled Hosny and is under CC0 +license (see http://creativecommons.org/publicdomain/zero/1.0/legalcode). + +This file is a wrapper for the luaotfload's font names module. It is part of the +luaotfload bundle, please see the luaotfload documentation for more info. +--]] + +kpse.set_program_name("luatex") + +require("lualibs")                    ---> current +require("luaotfload-legacy-database") ---> old +require("alt_getopt")                 ---> ? + +local name      = "mkluatexfontdb" +local version   = "1.31 (legacy)" + +local names    = fonts.names + +local function help_msg() +    texio.write(string.format([[ +Usage: %s [OPTION]... + +================================================================================ +                        please update your luatex binary +             this version is unsupported and likely to break things +================================================================================ +     +Rebuild the LuaTeX font database. + +Valid options: +  -f --force                   force re-indexing all fonts +  -q --quiet                   don't output anything +  -v --verbose=LEVEL           be more verbose (print the searched directories) +  -vv                          print the loaded fonts +  -vvv                         print all steps of directory searching +  -V --version                 print version and exit +  -h --help                    print this message + +The output database file is named otfl-names.lua and is placed under: + +   %s + +contact: https://github.com/lualatex/luaotfload + +]], name, names.path.localdir)) +end + +local function version_msg() +    texio.write(string.format( +        "%s version %s, database version %s.\n", name, version, names.version)) +end + +--[[ +Command-line processing. +Here we fill cmdargs with the good values, and then analyze it. +--]] + +local long_opts = { +    force            = "f", +    quiet            = "q", +    help             = "h", +    verbose          = 1  , +    version          = "V", +} + +local short_opts = "fqpvVh" + +local force_reload = nil + +local function process_cmdline() +    local opts, optind, optarg = alt_getopt.get_ordered_opts (arg, short_opts, long_opts) +    local log_level = 1 +    for i,v in ipairs(opts) do +        if     v == "q" then +            log_level = 0 +        elseif v == "v" then +            if log_level > 0 then +                log_level = log_level + 1 +            else +                log_level = 2 +            end +        elseif v == "V" then +            version_msg() +            os.exit(0) +        elseif v == "h" then +            help_msg() +            os.exit(0) +        elseif v == "f" then +            force_reload = 1 +        end +    end +    names.set_log_level(log_level) +end + +local function generate(force) +    local fontnames, saved +    fontnames = names.update(fontnames, force) +    logs.report("%s fonts in the database", #fontnames.mappings) +    saved = names.save(fontnames) +end + +process_cmdline() +generate(force_reload) diff --git a/luaotfload-legacy.lua b/luaotfload-legacy.lua new file mode 100644 index 0000000..e0c2230 --- /dev/null +++ b/luaotfload-legacy.lua @@ -0,0 +1,266 @@ +module("luaotfload", package.seeall) + +luaotfload.module = { +    name          = "luaotfload-legacy", +    version       = 1.31, +    date          = "2013/04/25", +    description   = "Unsupported Luaotfload", +    author        = "Elie Roux & Hans Hagen", +    copyright     = "Elie Roux", +    license       = "GPL v2" +} + +local error, warning, info, log = luatexbase.provides_module(luaotfload.module) + +--[[doc-- + +   This used to be a necessary initalization in order not to rebuild an +   existing font.  Maybe 600 should be replaced by |\pdfpkresolution| +   or |texconfig.pk_dpi| (and it should be replaced dynamically), but +   we don't have access (yet) to the |texconfig| table, so we let it be +   600. Anyway, it does still work fine even if |\pdfpkresolution| is +   changed. + +--doc]]-- + +kpse.init_prog("", 600, "/") + +--[[doc-- + +   The minimal required \luatex version. +   We are tolerant folks. + +--doc]]-- + +local luatex_version = 60 +if tex.luatexversion < luatex_version then +    warning("LuaTeX v%.2f is old, v%.2f is required, v0.76 recommended.", +             tex.luatexversion/100, +             luatex_version   /100) +end + +--[[doc-- + +    \subsection{Module loading} +    We load the outdated \context files with this function. It +    automatically adds the |otfl-| prefix to it, so that we call it with +    the actual \context name. + +--doc]]-- + +function luaotfload.loadmodule(tofind) +    local found = kpse.find_file(tofind,"tex") +    if found then +        log("loading file %s.", found) +        dofile(found) +    else +        error("file %s not found.", tofind) +    end +end +local loadmodule = luaotfload.loadmodule + +--[[doc-- + +    Keep away from these lines! + +--doc]]-- +loadmodule"luaotfload-legacy-merged.lua" + +if not fonts then +  loadmodule("otfl-luat-dum.lua") -- not used in context at all +  loadmodule("otfl-luat-ovr.lua") -- override some luat-dum functions +  loadmodule("otfl-data-con.lua") -- maybe some day we don't need this one +  loadmodule("otfl-font-ini.lua") +  loadmodule("otfl-node-dum.lua") +  loadmodule("otfl-node-inj.lua") +--[[doc-- +   By default \context takes some private attributes for internal use. To +   avoide attribute clashes with other packages, we override the function +   that allocates new attributes, making it a wraper around +   |luatexbase.new_attribute()|. We also prefix attributes with |otfl@| to +   avoid possiple name clashes. +--doc]]-- +  loadmodule("luaotfload-legacy-attributes.lua") -- patch attributes +--[[doc-- +   Font handling modules. +--doc]]-- +  loadmodule("otfl-font-tfm.lua") +  loadmodule("otfl-font-cid.lua") +  loadmodule("otfl-font-ott.lua") +  loadmodule("otfl-font-map.lua") +  loadmodule("otfl-font-otf.lua") +  loadmodule("otfl-font-otd.lua") +  loadmodule("otfl-font-oti.lua") +  loadmodule("otfl-font-otb.lua") +  loadmodule("otfl-font-otn.lua") +  loadmodule("otfl-font-ota.lua") +  loadmodule("otfl-font-otc.lua") +  loadmodule("otfl-font-def.lua") +--[[doc-- +   \textsf{old luaotfload} specific modules. +--doc]]-- +  loadmodule("otfl-font-xtx.lua") +  loadmodule("otfl-font-dum.lua") +  loadmodule("otfl-font-clr.lua") +end +loadmodule"luaotfload-legacy-database.lua" --- unmerged coz needed in db script + +--[[doc-- + +    This is a patch for |otfl-font-def.lua|, that defines a reader for ofm +    fonts, this is necessary if we set the forced field of the specification +    to |ofm|. + +--doc]]-- + +if fonts and fonts.tfm and fonts.tfm.readers then +    fonts.tfm.readers.ofm = fonts.tfm.readers.tfm +end + +--[[doc-- + +    \subsection{Post-processing TFM table} +    Here we do some final touches to the loaded TFM table before passing it +    to the \tex end. +    First we create a callback for patching fonts on the fly, to be used by +    other packages. + +--doc]]-- + +luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) + +--[[doc-- + +    then define a function where font manipulation will take place. + +--doc]]-- + +local function def_font(...) +    local fontdata = fonts.define.read(...) +    if type(fontdata) == "table" and fontdata.shared then +--[[doc-- + +    Then we populate |MathConstants| table, which is required for +    OpenType math. + +    Note: actually it isn’t, but you’re asking for it by using outdated +    code. + +--doc]]-- +        local otfdata = fontdata.shared.otfdata +        if otfdata.metadata.math then +            local mc = { } +            for k,v in next, otfdata.metadata.math do +                if k:find("Percent") then +                    -- keep percent values as is +                    mc[k] = v +                else +                    mc[k] = v / fontdata.units * fontdata.size +                end +            end +            -- for \overwithdelims +            mc.FractionDelimiterSize             = 1.01 * fontdata.size +            mc.FractionDelimiterDisplayStyleSize = 2.39 * fontdata.size + +            fontdata.MathConstants = mc +        end +--[[doc-- + +    Execute any registered font patching callbacks. + +--doc]]-- +        luatexbase.call_callback("luaotfload.patch_font", fontdata) +    end +    return fontdata +end + +--[[doc-- +\subsection{\context override} + +    We have a unified function for both file and name resolver. This +    line is commented as it makes database reload too often. This means +    that in some cases, a font in the database will not be found if +    it's not in the texmf tree. A similar thing will reappear in next +    version. + +--doc]]-- + +--fonts.define.resolvers.file = fonts.define.resolvers.name + +--[[doc-- + +    Overriding some defaults set in \context code. + +--doc]]-- + +fonts.mode = "node" + +--[[doc-- + +    The following features are useful in math (e.g. in XITS Math font), +    but \textsf{luaotfload} does not recognize them in |base| mode. + +--doc]]-- + +local register_base_sub = fonts.otf.features.register_base_substitution +local gsubs = { +    "ss01", "ss02", "ss03", "ss04", "ss05", +    "ss06", "ss07", "ss08", "ss09", "ss10", +    "ss11", "ss12", "ss13", "ss14", "ss15", +    "ss16", "ss17", "ss18", "ss19", "ss20", +} + +for _,v in next, gsubs do +    register_base_sub(v) +end + +--[[doc-- + +    Finally we register the callbacks + +--doc]]-- + +luatexbase.add_to_callback("pre_linebreak_filter", +                            nodes.simple_font_handler, +                           "luaotfload.pre_linebreak_filter") +luatexbase.add_to_callback("hpack_filter", +                            nodes.simple_font_handler, +                           "luaotfload.hpack_filter") +luatexbase.reset_callback("define_font") +luatexbase.add_to_callback("define_font", +                            def_font, +                           "luaotfload.define_font", 1) +luatexbase.add_to_callback("find_vf_file", +                            fonts.vf.find, +                           "luaotfload.find_vf_file") +--[[doc-- + +    XXX: see https://github.com/wspr/unicode-math/issues/185 +    \luatex does not provide interface to accessing +    |(Script)ScriptPercentScaleDown| math constants, so we +    emulate \xetex behaviour by setting |\fontdimen10| and +    |\fontdimen11|. + +    Note: actually, it does now, but not unless you update. + +--doc]]-- + +local function set_sscale_diments(fontdata) +    local mc = fontdata.MathConstants +    if mc then +        if mc["ScriptPercentScaleDown"] then +            fontdata.parameters[10] = mc.ScriptPercentScaleDown +        else -- resort to plain TeX default +            fontdata.parameters[10] = 70 +        end +        if mc["ScriptScriptPercentScaleDown"] then +            fontdata.parameters[11] = mc.ScriptScriptPercentScaleDown +        else -- resort to plain TeX default +            fontdata.parameters[11] = 50 +        end +    end +end + +luatexbase.add_to_callback("luaotfload.patch_font", set_sscale_diments, "unicodemath.set_sscale_diments") + +-- vim:ts=2:sw=2:expandtab:ft=lua diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 882a296..352697c 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -26,8 +26,23 @@ see the luaotfload documentation for more info. Report bugs to  --doc]]-- +--[[doc-- + +    We test for Lua 5.1 by means of capability detection to see if +    we’re running an outdated Luatex.  If so, we hand over control to +    the legacy db runner. + +    \url{http://lua-users.org/wiki/LuaVersionCompatibility} + +--doc]]-- +  kpse.set_program_name"luatex" +if _G.getfenv then +    local oldscript = kpse.find_file"luaotfload-legacy-tool.lua" +    return require(oldscript) +end +  local stringformat    = string.format  local texiowrite_nl   = texio.write_nl  local stringlower     = string.lower diff --git a/luaotfload.dtx b/luaotfload.dtx index 785a879..7ccd6e8 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1792,10 +1792,16 @@ loadmodule"auxiliary.lua"   --- additionaly high-level functionality (new)  \else    \NeedsTeXFormat{LaTeX2e}    \ProvidesPackage{luaotfload}% -    [2013/04/16 v2.2 OpenType layout system] +    [2013/05/10 v2.2 OpenType layout system]    \RequirePackage{luatexbase}  \fi -\RequireLuaModule{luaotfload} +\ifnum\luatexversion<76 +  %% here some deprecation warning would be in order +  \RequireLuaModule{lualibs} +  \RequireLuaModule{luaotfload-legacy} +\else +  \RequireLuaModule{luaotfload} +\fi  \endinput  %    \end{macrocode}  % \iffalse  | 
