diff options
author | Mojca Miklavec <mojca.miklavec@gmail.com> | 2012-05-14 17:38:55 +0000 |
---|---|---|
committer | Mojca Miklavec <mojca.miklavec@gmail.com> | 2012-05-14 17:38:55 +0000 |
commit | 15995e10bfc68edf79970c4ea4fbb6678566c46e (patch) | |
tree | 2de7ca2a83f2d37ef043ad7429a5cb945bb79ddb /Master/texmf-dist/tex/generic | |
parent | c9a39f716f1e5ec820ed3aab2c9aef25c5a9d730 (diff) |
ConTeXt 2012.05.14 16:00
git-svn-id: svn://tug.org/texlive/trunk@26371 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/generic')
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-basics-gen.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-basics-gen.lua) | 65 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-basics-nod.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex (renamed from Master/texmf-dist/tex/generic/context/luatex-basics.tex) | 2 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-cbk.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-cbk.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-def.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-def.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-demo-vf-1.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-enc.lua) | 2 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-ext.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-ext.lua) | 6 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-lua.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-lua.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-merged.lua) | 3750 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-syn.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-syn.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-tfm.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts-tfm.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts.lua) | 19 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-fonts.tex (renamed from Master/texmf-dist/tex/generic/context/luatex-fonts.tex) | 2 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-mplib.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.tex (renamed from Master/texmf-dist/tex/generic/context/luatex-mplib.tex) | 2 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-plain.tex | 25 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor-test.tex (renamed from Master/texmf-dist/tex/generic/context/luatex-preprocessor-test.tex) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor.lua (renamed from Master/texmf-dist/tex/generic/context/luatex-preprocessor.lua) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor.tex (renamed from Master/texmf-dist/tex/generic/context/luatex-preprocessor.tex) | 2 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/luatex/luatex-test.tex (renamed from Master/texmf-dist/tex/generic/context/luatex-test.tex) | 4 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/m-metapo.tex | 89 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/mptopdf/mptopdf.tex (renamed from Master/texmf-dist/tex/generic/context/mptopdf.tex) | 2 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/ppchtex/m-ch-de.tex (renamed from Master/texmf-dist/tex/generic/context/m-ch-de.tex) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/ppchtex/m-ch-en.tex (renamed from Master/texmf-dist/tex/generic/context/m-ch-en.tex) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/ppchtex/m-ch-nl.tex (renamed from Master/texmf-dist/tex/generic/context/m-ch-nl.tex) | 0 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/context/ppchtex/ppchtex.noc (renamed from Master/texmf-dist/tex/generic/context/ppchtex.noc) | 0 |
27 files changed, 2407 insertions, 1563 deletions
diff --git a/Master/texmf-dist/tex/generic/context/luatex-basics-gen.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-gen.lua index 602601d5cc7..bdbc3cf5188 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-basics-gen.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-gen.lua @@ -80,6 +80,8 @@ local remapper = { dfont = "truetype fonts", -- "truetype dictionary", cid = "cid maps", fea = "font feature files", + pfa = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! + pfb = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! } function resolvers.findfile(name,fileformat) @@ -191,7 +193,7 @@ end local function makefullname(path,name) if path and path ~= "" then name = "temp-" .. name -- clash prevention - return file.addsuffix(file.join(path,name),"lua") + return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),"luc") end end @@ -202,25 +204,66 @@ 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() + local data = false + local luaname, lucname = makefullname(paths[i],name) + if lucname and lfs.isfile(lucname) then + texio.write(string.format("(load: %s)",lucname)) + data = loadfile(lucname) end + if not data and luaname and lfs.isfile(luaname) then + texio.write(string.format("(load: %s)",luaname)) + data = loadfile(luaname) + end + return data and data() 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,true,{ reduce = true }) + local luaname, lucname = makefullname(path,name) + if luaname then + texio.write(string.format("(save: %s)",luaname)) + table.tofile(luaname,data,true,{ reduce = true }) + if lucname and type(caches.compile) == "function" then + os.remove(lucname) -- better be safe + texio.write(string.format("(save: %s)",lucname)) + caches.compile(data,luaname,lucname) + end + end +end + +-- According to KH os.execute is not permitted in plain/latex so there is +-- no reason to use the normal context way. So the method here is slightly +-- different from the one we have in context. We also use different suffixes +-- as we don't want any clashes (sharing cache files is not that handy as +-- context moves on faster.) +-- +-- Beware: serialization might fail on large files (so maybe we should pcall +-- this) in which case one should limit the method to luac and enable support +-- for execution. + +caches.compilemethod = "luac" -- luac dump both + +function caches.compile(data,luaname,lucname) + local done = false + if caches.compilemethod == "luac" or caches.compilemethod == "both" then + local command = "-o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname) + done = os.spawn("texluac " .. command) == 0 + end + if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then + local d = table.serialize(data,true) + if d and d ~= "" then + local f = io.open(lucname,'w') + if f then + local s = loadstring(d) + f:write(string.dump(s)) + f:close() + end + end end end -- -local table.setmetatableindex(t,f) +function table.setmetatableindex(t,f) setmetatable(t,{ __index = f }) end diff --git a/Master/texmf-dist/tex/generic/context/luatex-basics-nod.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua index 151d98a8f31..151d98a8f31 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-basics-nod.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-basics.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex index 8308204d572..bb34587ff6c 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-basics.tex +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex @@ -5,7 +5,7 @@ %D subtitle=Attribute Allocation, %D author=Hans Hagen, %D date=\currentdate, -%D copyright=public domain] +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %D As soon as we feel the need this file will file will contain an extension %D to the standard plain register allocation. For the moment we stick to a diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-cbk.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-cbk.lua index 9db94f65e48..9db94f65e48 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-cbk.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-cbk.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-def.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-def.lua index 0c2f0dbd583..0c2f0dbd583 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-def.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-def.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-demo-vf-1.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua index 3878ae64805..3878ae64805 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-demo-vf-1.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-enc.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua index ac736f2a6e8..e20c3a03b54 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-enc.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua @@ -18,7 +18,7 @@ fonts.encodings.agl = { } setmetatable(fonts.encodings.agl, { __index = function(t,k) if k == "unicodes" then texio.write(" <loading (extended) adobe glyph list>") - local unicodes = dofile(resolvers.findfile("font-agl.lua")) + local unicodes = dofile(resolvers.findfile("font-age.lua")) fonts.encodings.agl = { unicodes = unicodes } return unicodes else diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-ext.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-ext.lua index 951afcc7e0b..d8884ccc74c 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-ext.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-ext.lua @@ -29,7 +29,7 @@ local function initializeitlc(tfmdata,value) d.italic = it end end - tfmdata.properties.italic_correction = true + tfmdata.properties.hasitalics = true end end end @@ -54,7 +54,7 @@ local function initializeslant(tfmdata,value) elseif value < -1 then value = -1 end - tfmdata.parameters.slant_factor = value + tfmdata.parameters.slantfactor = value end otffeatures.register { @@ -75,7 +75,7 @@ local function initializeextend(tfmdata,value) elseif value < -10 then value = -10 end - tfmdata.parameters.extend_factor = value + tfmdata.parameters.extendfactor = value end otffeatures.register { diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-lua.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-lua.lua index ec3fe38be3e..ec3fe38be3e 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-lua.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-lua.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-merged.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua index 7e954182ad7..cdef91b38fd 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-merged.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/18/11 18:04:18 +-- merge date : 05/14/12 16:00:55 do -- begin closure to overcome local limits and interference @@ -127,650 +127,6 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['l-lpeg'] = { - version = 1.001, - 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 lpeg = require("lpeg") - -local type = type - --- Beware, we predefine a bunch of patterns here and one reason for doing so --- is that we get consistent behaviour in some of the visualizers. - -lpeg.patterns = lpeg.patterns or { } -- so that we can share -local patterns = lpeg.patterns - -local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match -local Ct, C, Cs, Cc = lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc -local lpegtype = lpeg.type - -local utfcharacters = string.utfcharacters -local utfgmatch = unicode and unicode.utf8.gmatch - -local anything = P(1) -local endofstring = P(-1) -local alwaysmatched = P(true) - -patterns.anything = anything -patterns.endofstring = endofstring -patterns.beginofstring = alwaysmatched -patterns.alwaysmatched = alwaysmatched - -local digit, sign = R('09'), S('+-') -local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local newline = crlf + cr + lf -local escaped = P("\\") * anything -local squote = P("'") -local dquote = P('"') -local space = P(" ") - -local utfbom_32_be = P('\000\000\254\255') -local utfbom_32_le = P('\255\254\000\000') -local utfbom_16_be = P('\255\254') -local utfbom_16_le = P('\254\255') -local utfbom_8 = P('\239\187\191') -local utfbom = utfbom_32_be + utfbom_32_le - + utfbom_16_be + utfbom_16_le - + utfbom_8 -local utftype = utfbom_32_be / "utf-32-be" + utfbom_32_le / "utf-32-le" - + utfbom_16_be / "utf-16-be" + utfbom_16_le / "utf-16-le" - + utfbom_8 / "utf-8" + alwaysmatched / "unknown" - -local utf8next = R("\128\191") - -patterns.utf8one = R("\000\127") -patterns.utf8two = R("\194\223") * utf8next -patterns.utf8three = R("\224\239") * utf8next * utf8next -patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next -patterns.utfbom = utfbom -patterns.utftype = utftype - -local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four -local validutf8char = utf8char^0 * endofstring * Cc(true) + Cc(false) - -patterns.utf8 = utf8char -patterns.utf8char = utf8char -patterns.validutf8 = validutf8char -patterns.validutf8char = validutf8char - -patterns.digit = digit -patterns.sign = sign -patterns.cardinal = sign^0 * digit^1 -patterns.integer = sign^0 * digit^1 -patterns.float = sign^0 * digit^0 * P('.') * digit^1 -patterns.cfloat = sign^0 * digit^0 * P(',') * digit^1 -patterns.number = patterns.float + patterns.integer -patterns.cnumber = patterns.cfloat + patterns.integer -patterns.oct = P("0") * R("07")^1 -patterns.octal = patterns.oct -patterns.HEX = P("0x") * R("09","AF")^1 -patterns.hex = P("0x") * R("09","af")^1 -patterns.hexadecimal = P("0x") * R("09","AF","af")^1 -patterns.lowercase = R("az") -patterns.uppercase = R("AZ") -patterns.letter = patterns.lowercase + patterns.uppercase -patterns.space = space -patterns.tab = P("\t") -patterns.spaceortab = patterns.space + patterns.tab -patterns.eol = S("\n\r") -patterns.spacer = S(" \t\f\v") -- + string.char(0xc2, 0xa0) if we want utf (cf mail roberto) -patterns.newline = newline -patterns.emptyline = newline^1 -patterns.nonspacer = 1 - patterns.spacer -patterns.whitespace = patterns.eol + patterns.spacer -patterns.nonwhitespace = 1 - patterns.whitespace -patterns.equal = P("=") -patterns.comma = P(",") -patterns.commaspacer = P(",") * patterns.spacer^0 -patterns.period = P(".") -patterns.colon = P(":") -patterns.semicolon = P(";") -patterns.underscore = P("_") -patterns.escaped = escaped -patterns.squote = squote -patterns.dquote = dquote -patterns.nosquote = (escaped + (1-squote))^0 -patterns.nodquote = (escaped + (1-dquote))^0 -patterns.unsingle = (squote/"") * patterns.nosquote * (squote/"") -patterns.undouble = (dquote/"") * patterns.nodquote * (dquote/"") -patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble -patterns.unspacer = ((patterns.spacer^1)/"")^0 - -patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1 -patterns.beginline = #(1-newline) - -local unquoted = Cs(patterns.unquoted * endofstring) -- not C - -function string.unquoted(str) - return match(unquoted,str) or str -end - ---~ print(string.unquoted("test")) ---~ print(string.unquoted([["t\"est"]])) ---~ print(string.unquoted([["t\"est"x]])) ---~ print(string.unquoted("\'test\'")) - -function lpeg.anywhere(pattern) --slightly adapted from website - return P { P(pattern) + 1 * V(1) } -- why so complex? -end - -function lpeg.splitter(pattern, action) - return (((1-P(pattern))^1)/action+1)^0 -end - -local splitters_s, splitters_m = { }, { } - -local function splitat(separator,single) - local splitter = (single and splitters_s[separator]) or splitters_m[separator] - if not splitter then - separator = P(separator) - local other = C((1 - separator)^0) - if single then - local any = anything - splitter = other * (separator * C(any^0) + "") -- ? - splitters_s[separator] = splitter - else - splitter = other * (separator * other)^0 - splitters_m[separator] = splitter - end - end - return splitter -end - -lpeg.splitat = splitat - ---~ local p = splitat("->",false) print(match(p,"oeps->what->more")) -- oeps what more ---~ local p = splitat("->",true) print(match(p,"oeps->what->more")) -- oeps what->more ---~ local p = splitat("->",false) print(match(p,"oeps")) -- oeps ---~ local p = splitat("->",true) print(match(p,"oeps")) -- oeps - -local cache = { } - -function lpeg.split(separator,str) - local c = cache[separator] - if not c then - c = Ct(splitat(separator)) - cache[separator] = c - end - return match(c,str) -end - -function string.split(str,separator) - local c = cache[separator] - if not c then - c = Ct(splitat(separator)) - cache[separator] = c - end - return match(c,str) -end - -local spacing = patterns.spacer^0 * newline -- sort of strip -local empty = spacing * Cc("") -local nonempty = Cs((1-spacing)^1) * spacing^-1 -local content = (empty + nonempty)^1 - -patterns.textline = content - ---~ local linesplitter = Ct(content^0) ---~ ---~ function string.splitlines(str) ---~ return match(linesplitter,str) ---~ end - -local linesplitter = Ct(splitat(newline)) - -patterns.linesplitter = linesplitter - -function string.splitlines(str) - return match(linesplitter,str) -end - -local utflinesplitter = utfbom^-1 * Ct(splitat(newline)) - -patterns.utflinesplitter = utflinesplitter - -function string.utfsplitlines(str) - return match(utflinesplitter,str) -end - ---~ lpeg.splitters = cache -- no longer public - -local cache = { } - -function lpeg.checkedsplit(separator,str) - local c = cache[separator] - if not c then - separator = P(separator) - local other = C((1 - separator)^1) - c = Ct(separator^0 * other * (separator^1 * other)^0) - cache[separator] = c - end - return match(c,str) -end - -function string.checkedsplit(str,separator) - local c = cache[separator] - if not c then - separator = P(separator) - local other = C((1 - separator)^1) - c = Ct(separator^0 * other * (separator^1 * other)^0) - cache[separator] = c - end - return match(c,str) -end - ---~ from roberto's site: - -local f1 = string.byte - -local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 - 12416 end -local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end -local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end - -local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 - -patterns.utf8byte = utf8byte - ---~ local str = " a b c d " - ---~ local s = lpeg.stripper(lpeg.R("az")) print("["..lpeg.match(s,str).."]") ---~ local s = lpeg.keeper(lpeg.R("az")) print("["..lpeg.match(s,str).."]") ---~ local s = lpeg.stripper("ab") print("["..lpeg.match(s,str).."]") ---~ local s = lpeg.keeper("ab") print("["..lpeg.match(s,str).."]") - -local cache = { } - -function lpeg.stripper(str) - if type(str) == "string" then - local s = cache[str] - if not s then - s = Cs(((S(str)^1)/"" + 1)^0) - cache[str] = s - end - return s - else - return Cs(((str^1)/"" + 1)^0) - end -end - -local cache = { } - -function lpeg.keeper(str) - if type(str) == "string" then - local s = cache[str] - if not s then - s = Cs((((1-S(str))^1)/"" + 1)^0) - cache[str] = s - end - return s - else - return Cs((((1-str)^1)/"" + 1)^0) - end -end - -function lpeg.frontstripper(str) -- or pattern (yet undocumented) - return (P(str) + P(true)) * Cs(P(1)^0) -end - -function lpeg.endstripper(str) -- or pattern (yet undocumented) - return Cs((1 - P(str) * P(-1))^0) -end - --- Just for fun I looked at the used bytecode and --- p = (p and p + pp) or pp gets one more (testset). - -function lpeg.replacer(one,two) - if type(one) == "table" then - local no = #one - if no > 0 then - local p - for i=1,no do - local o = one[i] - local pp = P(o[1]) / o[2] - if p then - p = p + pp - else - p = pp - end - end - return Cs((p + 1)^0) - end - else - two = two or "" - return Cs((P(one)/two + 1)^0) - end -end - -local splitters_f, splitters_s = { }, { } - -function lpeg.firstofsplit(separator) -- always return value - local splitter = splitters_f[separator] - if not splitter then - separator = P(separator) - splitter = C((1 - separator)^0) - splitters_f[separator] = splitter - end - return splitter -end - -function lpeg.secondofsplit(separator) -- nil if not split - local splitter = splitters_s[separator] - if not splitter then - separator = P(separator) - splitter = (1 - separator)^0 * separator * C(anything^0) - splitters_s[separator] = splitter - end - return splitter -end - -function lpeg.balancer(left,right) - left, right = P(left), P(right) - return P { left * ((1 - left - right) + V(1))^0 * right } -end - ---~ print(1,match(lpeg.firstofsplit(":"),"bc:de")) ---~ print(2,match(lpeg.firstofsplit(":"),":de")) -- empty ---~ print(3,match(lpeg.firstofsplit(":"),"bc")) ---~ print(4,match(lpeg.secondofsplit(":"),"bc:de")) ---~ print(5,match(lpeg.secondofsplit(":"),"bc:")) -- empty ---~ print(6,match(lpeg.secondofsplit(":",""),"bc")) ---~ print(7,match(lpeg.secondofsplit(":"),"bc")) ---~ print(9,match(lpeg.secondofsplit(":","123"),"bc")) - ---~ -- slower: ---~ ---~ function lpeg.counter(pattern) ---~ local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.anything)^0 ---~ return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end ---~ end - -local nany = utf8char/"" - -function lpeg.counter(pattern) - pattern = Cs((P(pattern)/" " + nany)^0) - return function(str) - return #match(pattern,str) - end -end - -if utfgmatch then - - function lpeg.count(str,what) -- replaces string.count - if type(what) == "string" then - local n = 0 - for _ in utfgmatch(str,what) do - n = n + 1 - end - return n - else -- 4 times slower but still faster than / function - return #match(Cs((P(what)/" " + nany)^0),str) - end - end - -else - - local cache = { } - - function lpeg.count(str,what) -- replaces string.count - if type(what) == "string" then - local p = cache[what] - if not p then - p = Cs((P(what)/" " + nany)^0) - cache[p] = p - end - return #match(p,str) - else -- 4 times slower but still faster than / function - return #match(Cs((P(what)/" " + nany)^0),str) - end - end - -end - -local patterns_escapes = { -- also defines in l-string - ["%"] = "%%", - ["."] = "%.", - ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", - ["["] = "%[", ["]"] = "%]", - ["("] = "%)", [")"] = "%)", - -- ["{"] = "%{", ["}"] = "%}" - -- ["^"] = "%^", ["$"] = "%$", -} - -local simple_escapes = { -- also defines in l-string - ["-"] = "%-", - ["."] = "%.", - ["?"] = ".", - ["*"] = ".*", -} - -local p = Cs((S("-.+*%()[]") / patterns_escapes + anything)^0) -local s = Cs((S("-.+*%()[]") / simple_escapes + anything)^0) - -function string.escapedpattern(str,simple) - return match(simple and s or p,str) -end - --- utf extensies - -lpeg.UP = lpeg.P - -if utfcharacters then - - function lpeg.US(str) - local p - for uc in utfcharacters(str) do - if p then - p = p + P(uc) - else - p = P(uc) - end - end - return p - end - - -elseif utfgmatch then - - function lpeg.US(str) - local p - for uc in utfgmatch(str,".") do - if p then - p = p + P(uc) - else - p = P(uc) - end - end - return p - end - -else - - function lpeg.US(str) - local p - local f = function(uc) - if p then - p = p + P(uc) - else - p = P(uc) - end - end - match((utf8char/f)^0,str) - return p - end - -end - -local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) - -local utfchar = unicode and unicode.utf8 and unicode.utf8.char - -function lpeg.UR(str,more) - local first, last - if type(str) == "number" then - first = str - last = more or first - else - first, last = match(range,str) - if not last then - return P(str) - end - end - if first == last then - return P(str) - elseif utfchar and last - first < 8 then -- a somewhat arbitrary criterium - local p - for i=first,last do - if p then - p = p + P(utfchar(i)) - else - p = P(utfchar(i)) - end - end - return p -- nil when invalid range - else - local f = function(b) - return b >= first and b <= last - end - return utf8byte / f -- nil when invalid range - end -end - ---~ lpeg.print(lpeg.R("ab","cd","gh")) ---~ lpeg.print(lpeg.P("a","b","c")) ---~ lpeg.print(lpeg.S("a","b","c")) - ---~ print(lpeg.count("äáà a",lpeg.P("á") + lpeg.P("à "))) ---~ print(lpeg.count("äáà a",lpeg.UP("áà "))) ---~ print(lpeg.count("äáà a",lpeg.US("à á"))) ---~ print(lpeg.count("äáà a",lpeg.UR("aá"))) ---~ print(lpeg.count("äáà a",lpeg.UR("à á"))) ---~ print(lpeg.count("äáà a",lpeg.UR(0x0000,0xFFFF))) - -function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then") - if type(list) ~= "table" then - list = { list, ... } - end - -- sort(list) -- longest match first - local p = P(list[1]) - for l=2,#list do - p = p + P(list[l]) - end - return p -end - -function lpeg.is_lpeg(p) - return p and lpegtype(p) == "pattern" -end - ---~ Cf(Ct("") * (Cg(C(...) * "=" * Cs(...)))^0, rawset) - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules = { } end modules ['l-boolean'] = { - version = 1.001, - 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 type, tonumber = type, tonumber - -boolean = boolean or { } -local boolean = boolean - --- function boolean.tonumber(b) --- return b and 1 or 0 -- test and test and return or return --- end - -function boolean.tonumber(b) - if b then return 1 else return 0 end -- test and return or return -end - -function toboolean(str,tolerant) - if tolerant then - local tstr = type(str) - if tstr == "string" then - return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t" - elseif tstr == "number" then - return tonumber(str) ~= 0 - elseif tstr == "nil" then - return false - else - return str - end - elseif str == "true" then - return true - elseif str == "false" then - return false - else - return str - end -end - -string.toboolean = toboolean - -function string.is_boolean(str,default) - if type(str) == "string" then - if str == "true" or str == "yes" or str == "on" or str == "t" then - return true - elseif str == "false" or str == "no" or str == "off" or str == "f" then - return false - end - end - return default -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules = { } end modules ['l-math'] = { - version = 1.001, - 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 floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan - -if not math.round then - function math.round(x) return floor(x + 0.5) end -end - -if not math.div then - function math.div(n,m) return floor(n/m) end -end - -if not math.mod then - function math.mod(n,m) return n % m end -end - -local pipi = 2*math.pi/360 - -if not math.sind then - function math.sind(d) return sin(d*pipi) end - function math.cosd(d) return cos(d*pipi) end - function math.tand(d) return tan(d*pipi) end -end - -if not math.odd then - function math.odd (n) return n % 2 == 0 end - function math.even(n) return n % 2 ~= 0 end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - if not modules then modules = { } end modules ['l-table'] = { version = 1.001, comment = "companion to luat-lib.mkiv", @@ -783,11 +139,12 @@ local type, next, tostring, tonumber, ipairs, table, string = type, next, tostri local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove local format, find, gsub, lower, dump, match = string.format, string.find, string.gsub, string.lower, string.dump, string.match local getmetatable, setmetatable = getmetatable, setmetatable +local getinfo = debug.getinfo -- Starting with version 5.2 Lua no longer provide ipairs, which makes -- sense. As we already used the for loop and # in most places the -- impact on ConTeXt was not that large; the remaining ipairs already --- have been replaced. In a similar fashio we also hardly used pairs. +-- have been replaced. In a similar fashion we also hardly used pairs. -- -- Just in case, we provide the fallbacks as discussed in Programming -- in Lua (http://www.lua.org/pil/7.3.html): @@ -1130,6 +487,8 @@ end -- problem: there no good number_to_string converter with the best resolution +local function dummy() end + local function do_serialize(root,name,depth,level,indexed) if level > 0 then depth = depth .. " " @@ -1324,18 +683,20 @@ local function do_serialize(root,name,depth,level,indexed) end elseif t == "function" then if functions then + local f = getinfo(v).what == "C" and dump(dummy) or dump(v) + -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v) if tk == "number" then -- or find(k,"^%d+$") then if hexify then - handle(format("%s [0x%04X]=loadstring(%q),",depth,k,dump(v))) + handle(format("%s [0x%04X]=loadstring(%q),",depth,k,f)) else - handle(format("%s [%s]=loadstring(%q),",depth,k,dump(v))) + handle(format("%s [%s]=loadstring(%q),",depth,k,f)) end elseif tk == "boolean" then - handle(format("%s [%s]=loadstring(%q),",depth,tostring(k),dump(v))) + handle(format("%s [%s]=loadstring(%q),",depth,tostring(k),f)) elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then - handle(format("%s %s=loadstring(%q),",depth,k,dump(v))) + handle(format("%s %s=loadstring(%q),",depth,k,f)) else - handle(format("%s [%q]=loadstring(%q),",depth,k,dump(v))) + handle(format("%s [%q]=loadstring(%q),",depth,k,f)) end end else @@ -1698,6 +1059,893 @@ function table.has_one_entry(t) return t and not next(t,next(t)) end +-- new + +function table.loweredkeys(t) -- maybe utf + local l = { } + for k, v in next, t do + l[lower(k)] = v + end + return l +end + +-- new, might move (maybe duplicate) + +function table.unique(old) + local hash = { } + local new = { } + local n = 0 + for i=1,#old do + local oi = old[i] + if not hash[oi] then + n = n + 1 + new[n] = oi + hash[oi] = true + end + end + return new +end + +-- function table.sorted(t,...) +-- table.sort(t,...) +-- return t -- still sorts in-place +-- end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules = { } end modules ['l-lpeg'] = { + version = 1.001, + 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" +} + + +-- a new lpeg fails on a #(1-P(":")) test and really needs a + P(-1) + +local lpeg = require("lpeg") + +-- tracing (only used when we encounter a problem in integration of lpeg in luatex) + +local report = texio and texio.write_nl or print + +--~ local lpmatch = lpeg.match +--~ local lpprint = lpeg.print +--~ local lpp = lpeg.P +--~ local lpr = lpeg.R +--~ local lps = lpeg.S +--~ local lpc = lpeg.C +--~ local lpb = lpeg.B +--~ local lpv = lpeg.V +--~ local lpcf = lpeg.Cf +--~ local lpcb = lpeg.Cb +--~ local lpcg = lpeg.Cg +--~ local lpct = lpeg.Ct +--~ local lpcs = lpeg.Cs +--~ local lpcc = lpeg.Cc +--~ local lpcmt = lpeg.Cmt +--~ local lpcarg = lpeg.Carg + +--~ function lpeg.match(l,...) report("LPEG MATCH") lpprint(l) return lpmatch(l,...) end + +--~ function lpeg.P (l) local p = lpp (l) report("LPEG P =") lpprint(l) return p end +--~ function lpeg.R (l) local p = lpr (l) report("LPEG R =") lpprint(l) return p end +--~ function lpeg.S (l) local p = lps (l) report("LPEG S =") lpprint(l) return p end +--~ function lpeg.C (l) local p = lpc (l) report("LPEG C =") lpprint(l) return p end +--~ function lpeg.B (l) local p = lpb (l) report("LPEG B =") lpprint(l) return p end +--~ function lpeg.V (l) local p = lpv (l) report("LPEG V =") lpprint(l) return p end +--~ function lpeg.Cf (l) local p = lpcf (l) report("LPEG Cf =") lpprint(l) return p end +--~ function lpeg.Cb (l) local p = lpcb (l) report("LPEG Cb =") lpprint(l) return p end +--~ function lpeg.Cg (l) local p = lpcg (l) report("LPEG Cg =") lpprint(l) return p end +--~ function lpeg.Ct (l) local p = lpct (l) report("LPEG Ct =") lpprint(l) return p end +--~ function lpeg.Cs (l) local p = lpcs (l) report("LPEG Cs =") lpprint(l) return p end +--~ function lpeg.Cc (l) local p = lpcc (l) report("LPEG Cc =") lpprint(l) return p end +--~ function lpeg.Cmt (l) local p = lpcmt (l) report("LPEG Cmt =") lpprint(l) return p end +--~ function lpeg.Carg (l) local p = lpcarg(l) report("LPEG Carg =") lpprint(l) return p end + +local type = type +local byte, char, gmatch = string.byte, string.char, string.gmatch + +-- Beware, we predefine a bunch of patterns here and one reason for doing so +-- is that we get consistent behaviour in some of the visualizers. + +lpeg.patterns = lpeg.patterns or { } -- so that we can share +local patterns = lpeg.patterns + +local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match +local Ct, C, Cs, Cc = lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc +local lpegtype = lpeg.type + +local utfcharacters = string.utfcharacters +local utfgmatch = unicode and unicode.utf8.gmatch + +local anything = P(1) +local endofstring = P(-1) +local alwaysmatched = P(true) + +patterns.anything = anything +patterns.endofstring = endofstring +patterns.beginofstring = alwaysmatched +patterns.alwaysmatched = alwaysmatched + +local digit, sign = R('09'), S('+-') +local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") +local newline = crlf + S("\r\n") -- cr + lf +local escaped = P("\\") * anything +local squote = P("'") +local dquote = P('"') +local space = P(" ") + +local utfbom_32_be = P('\000\000\254\255') +local utfbom_32_le = P('\255\254\000\000') +local utfbom_16_be = P('\255\254') +local utfbom_16_le = P('\254\255') +local utfbom_8 = P('\239\187\191') +local utfbom = utfbom_32_be + utfbom_32_le + + utfbom_16_be + utfbom_16_le + + utfbom_8 +local utftype = utfbom_32_be / "utf-32-be" + utfbom_32_le / "utf-32-le" + + utfbom_16_be / "utf-16-be" + utfbom_16_le / "utf-16-le" + + utfbom_8 / "utf-8" + alwaysmatched / "unknown" + +local utf8next = R("\128\191") + +patterns.utf8one = R("\000\127") +patterns.utf8two = R("\194\223") * utf8next +patterns.utf8three = R("\224\239") * utf8next * utf8next +patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next +patterns.utfbom = utfbom +patterns.utftype = utftype + +local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +local validutf8char = utf8char^0 * endofstring * Cc(true) + Cc(false) + +patterns.utf8 = utf8char +patterns.utf8char = utf8char +patterns.validutf8 = validutf8char +patterns.validutf8char = validutf8char + +patterns.digit = digit +patterns.sign = sign +patterns.cardinal = sign^0 * digit^1 +patterns.integer = sign^0 * digit^1 +patterns.float = sign^0 * digit^0 * P('.') * digit^1 +patterns.cfloat = sign^0 * digit^0 * P(',') * digit^1 +patterns.number = patterns.float + patterns.integer +patterns.cnumber = patterns.cfloat + patterns.integer +patterns.oct = P("0") * R("07")^1 +patterns.octal = patterns.oct +patterns.HEX = P("0x") * R("09","AF")^1 +patterns.hex = P("0x") * R("09","af")^1 +patterns.hexadecimal = P("0x") * R("09","AF","af")^1 +patterns.lowercase = R("az") +patterns.uppercase = R("AZ") +patterns.letter = patterns.lowercase + patterns.uppercase +patterns.space = space +patterns.tab = P("\t") +patterns.spaceortab = patterns.space + patterns.tab +patterns.eol = S("\n\r") +patterns.spacer = S(" \t\f\v") -- + char(0xc2, 0xa0) if we want utf (cf mail roberto) +patterns.newline = newline +patterns.emptyline = newline^1 +patterns.nonspacer = 1 - patterns.spacer +patterns.whitespace = patterns.eol + patterns.spacer +patterns.nonwhitespace = 1 - patterns.whitespace +patterns.equal = P("=") +patterns.comma = P(",") +patterns.commaspacer = P(",") * patterns.spacer^0 +patterns.period = P(".") +patterns.colon = P(":") +patterns.semicolon = P(";") +patterns.underscore = P("_") +patterns.escaped = escaped +patterns.squote = squote +patterns.dquote = dquote +patterns.nosquote = (escaped + (1-squote))^0 +patterns.nodquote = (escaped + (1-dquote))^0 +patterns.unsingle = (squote/"") * patterns.nosquote * (squote/"") +patterns.undouble = (dquote/"") * patterns.nodquote * (dquote/"") +patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble +patterns.unspacer = ((patterns.spacer^1)/"")^0 + +patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1 +patterns.beginline = #(1-newline) + +-- print(string.unquoted("test")) +-- print(string.unquoted([["t\"est"]])) +-- print(string.unquoted([["t\"est"x]])) +-- print(string.unquoted("\'test\'")) +-- print(string.unquoted('"test"')) +-- print(string.unquoted('"test"')) + +function lpeg.anywhere(pattern) --slightly adapted from website + return P { P(pattern) + 1 * V(1) } -- why so complex? +end + +function lpeg.splitter(pattern, action) + return (((1-P(pattern))^1)/action+1)^0 +end + +function lpeg.tsplitter(pattern, action) + return Ct((((1-P(pattern))^1)/action+1)^0) +end + +-- probleem: separator can be lpeg and that does not hash too well, but +-- it's quite okay as the key is then not garbage collected + +local splitters_s, splitters_m, splitters_t = { }, { }, { } + +local function splitat(separator,single) + local splitter = (single and splitters_s[separator]) or splitters_m[separator] + if not splitter then + separator = P(separator) + local other = C((1 - separator)^0) + if single then + local any = anything + splitter = other * (separator * C(any^0) + "") -- ? + splitters_s[separator] = splitter + else + splitter = other * (separator * other)^0 + splitters_m[separator] = splitter + end + end + return splitter +end + +local function tsplitat(separator) + local splitter = splitters_t[separator] + if not splitter then + splitter = Ct(splitat(separator)) + splitters_t[separator] = splitter + end + return splitter +end + +lpeg.splitat = splitat +lpeg.tsplitat = tsplitat + +function string.splitup(str,separator) + if not separator then + separator = "," + end + return match(splitters_m[separator] or splitat(separator),str) +end + +--~ local p = splitat("->",false) print(match(p,"oeps->what->more")) -- oeps what more +--~ local p = splitat("->",true) print(match(p,"oeps->what->more")) -- oeps what->more +--~ local p = splitat("->",false) print(match(p,"oeps")) -- oeps +--~ local p = splitat("->",true) print(match(p,"oeps")) -- oeps + +local cache = { } + +function lpeg.split(separator,str) + local c = cache[separator] + if not c then + c = tsplitat(separator) + cache[separator] = c + end + return match(c,str) +end + +function string.split(str,separator) + local c = cache[separator] + if not c then + c = tsplitat(separator) + cache[separator] = c + end + return match(c,str) +end + +local spacing = patterns.spacer^0 * newline -- sort of strip +local empty = spacing * Cc("") +local nonempty = Cs((1-spacing)^1) * spacing^-1 +local content = (empty + nonempty)^1 + +patterns.textline = content + +--~ local linesplitter = Ct(content^0) +--~ +--~ function string.splitlines(str) +--~ return match(linesplitter,str) +--~ end + +local linesplitter = tsplitat(newline) + +patterns.linesplitter = linesplitter + +function string.splitlines(str) + return match(linesplitter,str) +end + +local utflinesplitter = utfbom^-1 * tsplitat(newline) + +patterns.utflinesplitter = utflinesplitter + +function string.utfsplitlines(str) + return match(utflinesplitter,str or "") +end + +--~ lpeg.splitters = cache -- no longer public + +local cache = { } + +function lpeg.checkedsplit(separator,str) + local c = cache[separator] + if not c then + separator = P(separator) + local other = C((1 - separator)^1) + c = Ct(separator^0 * other * (separator^1 * other)^0) + cache[separator] = c + end + return match(c,str) +end + +function string.checkedsplit(str,separator) + local c = cache[separator] + if not c then + separator = P(separator) + local other = C((1 - separator)^1) + c = Ct(separator^0 * other * (separator^1 * other)^0) + cache[separator] = c + end + return match(c,str) +end + +--~ from roberto's site: + +local function f2(s) local c1, c2 = byte(s,1,2) return c1 * 64 + c2 - 12416 end +local function f3(s) local c1, c2, c3 = byte(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end +local function f4(s) local c1, c2, c3, c4 = byte(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end + +local utf8byte = patterns.utf8one/byte + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 + +patterns.utf8byte = utf8byte + +--~ local str = " a b c d " + +--~ local s = lpeg.stripper(lpeg.R("az")) print("["..lpeg.match(s,str).."]") +--~ local s = lpeg.keeper(lpeg.R("az")) print("["..lpeg.match(s,str).."]") +--~ local s = lpeg.stripper("ab") print("["..lpeg.match(s,str).."]") +--~ local s = lpeg.keeper("ab") print("["..lpeg.match(s,str).."]") + +local cache = { } + +function lpeg.stripper(str) + if type(str) == "string" then + local s = cache[str] + if not s then + s = Cs(((S(str)^1)/"" + 1)^0) + cache[str] = s + end + return s + else + return Cs(((str^1)/"" + 1)^0) + end +end + +local cache = { } + +function lpeg.keeper(str) + if type(str) == "string" then + local s = cache[str] + if not s then + s = Cs((((1-S(str))^1)/"" + 1)^0) + cache[str] = s + end + return s + else + return Cs((((1-str)^1)/"" + 1)^0) + end +end + +function lpeg.frontstripper(str) -- or pattern (yet undocumented) + return (P(str) + P(true)) * Cs(P(1)^0) +end + +function lpeg.endstripper(str) -- or pattern (yet undocumented) + return Cs((1 - P(str) * P(-1))^0) +end + +-- Just for fun I looked at the used bytecode and +-- p = (p and p + pp) or pp gets one more (testset). + +function lpeg.replacer(one,two) + if type(one) == "table" then + local no = #one + if no > 0 then + local p + for i=1,no do + local o = one[i] + local pp = P(o[1]) / o[2] + if p then + p = p + pp + else + p = pp + end + end + return Cs((p + 1)^0) + end + else + two = two or "" + return Cs((P(one)/two + 1)^0) + end +end + +local splitters_f, splitters_s = { }, { } + +function lpeg.firstofsplit(separator) -- always return value + local splitter = splitters_f[separator] + if not splitter then + separator = P(separator) + splitter = C((1 - separator)^0) + splitters_f[separator] = splitter + end + return splitter +end + +function lpeg.secondofsplit(separator) -- nil if not split + local splitter = splitters_s[separator] + if not splitter then + separator = P(separator) + splitter = (1 - separator)^0 * separator * C(anything^0) + splitters_s[separator] = splitter + end + return splitter +end + +function lpeg.balancer(left,right) + left, right = P(left), P(right) + return P { left * ((1 - left - right) + V(1))^0 * right } +end + +--~ print(1,match(lpeg.firstofsplit(":"),"bc:de")) +--~ print(2,match(lpeg.firstofsplit(":"),":de")) -- empty +--~ print(3,match(lpeg.firstofsplit(":"),"bc")) +--~ print(4,match(lpeg.secondofsplit(":"),"bc:de")) +--~ print(5,match(lpeg.secondofsplit(":"),"bc:")) -- empty +--~ print(6,match(lpeg.secondofsplit(":",""),"bc")) +--~ print(7,match(lpeg.secondofsplit(":"),"bc")) +--~ print(9,match(lpeg.secondofsplit(":","123"),"bc")) + +--~ -- slower: +--~ +--~ function lpeg.counter(pattern) +--~ local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.anything)^0 +--~ return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end +--~ end + +local nany = utf8char/"" + +function lpeg.counter(pattern) + pattern = Cs((P(pattern)/" " + nany)^0) + return function(str) + return #match(pattern,str) + end +end + +if utfgmatch then + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +else + + local cache = { } + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + nany)^0) + cache[p] = p + end + return #match(p,str) + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +end + +local patterns_escapes = { -- also defines in l-string + ["%"] = "%%", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", +} + +local simple_escapes = { -- also defines in l-string + ["-"] = "%-", + ["."] = "%.", + ["?"] = ".", + ["*"] = ".*", +} + +local p = Cs((S("-.+*%()[]") / patterns_escapes + anything)^0) +local s = Cs((S("-.+*%()[]") / simple_escapes + anything)^0) + +function string.escapedpattern(str,simple) + return match(simple and s or p,str) +end + +-- utf extensies + +lpeg.UP = lpeg.P + +if utfcharacters then + + function lpeg.US(str) + local p + for uc in utfcharacters(str) do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + + +elseif utfgmatch then + + function lpeg.US(str) + local p + for uc in utfgmatch(str,".") do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + +else + + function lpeg.US(str) + local p + local f = function(uc) + if p then + p = p + P(uc) + else + p = P(uc) + end + end + match((utf8char/f)^0,str) + return p + end + +end + +local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) + +local utfchar = unicode and unicode.utf8 and unicode.utf8.char + +function lpeg.UR(str,more) + local first, last + if type(str) == "number" then + first = str + last = more or first + else + first, last = match(range,str) + if not last then + return P(str) + end + end + if first == last then + return P(str) + elseif utfchar and last - first < 8 then -- a somewhat arbitrary criterium + local p + for i=first,last do + if p then + p = p + P(utfchar(i)) + else + p = P(utfchar(i)) + end + end + return p -- nil when invalid range + else + local f = function(b) + return b >= first and b <= last + end + return utf8byte / f -- nil when invalid range + end +end + +--~ lpeg.print(lpeg.R("ab","cd","gh")) +--~ lpeg.print(lpeg.P("a","b","c")) +--~ lpeg.print(lpeg.S("a","b","c")) + +--~ print(lpeg.count("äáà a",lpeg.P("á") + lpeg.P("à "))) +--~ print(lpeg.count("äáà a",lpeg.UP("áà "))) +--~ print(lpeg.count("äáà a",lpeg.US("à á"))) +--~ print(lpeg.count("äáà a",lpeg.UR("aá"))) +--~ print(lpeg.count("äáà a",lpeg.UR("à á"))) +--~ print(lpeg.count("äáà a",lpeg.UR(0x0000,0xFFFF))) + +function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then") + if type(list) ~= "table" then + list = { list, ... } + end + -- sort(list) -- longest match first + local p = P(list[1]) + for l=2,#list do + p = p + P(list[l]) + end + return p +end + +function lpeg.is_lpeg(p) + return p and lpegtype(p) == "pattern" +end + +-- For the moment here, but it might move to utilities. Beware, we need to +-- have the longest keyword first, so 'aaa' comes beforte 'aa' which is why we +-- loop back from the end cq. prepend. + +local sort, fastcopy, sortedkeys = table.sort, table.fastcopy, table.sortedkeys -- dependency! + +function lpeg.append(list,pp,delayed,checked) + local p = pp + if #list > 0 then + local keys = fastcopy(list) + sort(keys) + for i=#keys,1,-1 do + local k = keys[i] + if p then + p = P(k) + p + else + p = P(k) + end + end + elseif delayed then -- hm, it looks like the lpeg parser resolves anyway + local keys = sortedkeys(list) + if p then + for i=1,#keys,1 do + local k = keys[i] + local v = list[k] + p = P(k)/list + p + end + else + for i=1,#keys do + local k = keys[i] + local v = list[k] + if p then + p = P(k) + p + else + p = P(k) + end + end + if p then + p = p / list + end + end + elseif checked then + -- problem: substitution gives a capture + local keys = sortedkeys(list) + for i=1,#keys do + local k = keys[i] + local v = list[k] + if p then + if k == v then + p = P(k) + p + else + p = P(k)/v + p + end + else + if k == v then + p = P(k) + else + p = P(k)/v + end + end + end + else + local keys = sortedkeys(list) + for i=1,#keys do + local k = keys[i] + local v = list[k] + if p then + p = P(k)/v + p + else + p = P(k)/v + end + end + end + return p +end + +-- inspect(lpeg.append({ a = "1", aa = "1", aaa = "1" } ,nil,true)) +-- inspect(lpeg.append({ ["degree celsius"] = "1", celsius = "1", degree = "1" } ,nil,true)) + +-- function lpeg.exact_match(words,case_insensitive) +-- local pattern = concat(words) +-- if case_insensitive then +-- local pattern = S(upper(characters)) + S(lower(characters)) +-- local list = { } +-- for i=1,#words do +-- list[lower(words[i])] = true +-- end +-- return Cmt(pattern^1, function(_,i,s) +-- return list[lower(s)] and i +-- end) +-- else +-- local pattern = S(concat(words)) +-- local list = { } +-- for i=1,#words do +-- list[words[i]] = true +-- end +-- return Cmt(pattern^1, function(_,i,s) +-- return list[s] and i +-- end) +-- end +-- end + +-- experiment: + +local function make(t) + local p +-- for k, v in next, t do + for k, v in table.sortedhash(t) do + if not p then + if next(v) then + p = P(k) * make(v) + else + p = P(k) + end + else + if next(v) then + p = p + P(k) * make(v) + else + p = p + P(k) + end + end + end + return p +end + +function lpeg.utfchartabletopattern(list) + local tree = { } + for i=1,#list do + local t = tree + for c in gmatch(list[i],".") do + if not t[c] then + t[c] = { } + end + t = t[c] + end + end + return make(tree) +end + +-- inspect ( lpeg.utfchartabletopattern { +-- utfchar(0x00A0), -- nbsp +-- utfchar(0x2000), -- enquad +-- utfchar(0x2001), -- emquad +-- utfchar(0x2002), -- enspace +-- utfchar(0x2003), -- emspace +-- utfchar(0x2004), -- threeperemspace +-- utfchar(0x2005), -- fourperemspace +-- utfchar(0x2006), -- sixperemspace +-- utfchar(0x2007), -- figurespace +-- utfchar(0x2008), -- punctuationspace +-- utfchar(0x2009), -- breakablethinspace +-- utfchar(0x200A), -- hairspace +-- utfchar(0x200B), -- zerowidthspace +-- utfchar(0x202F), -- narrownobreakspace +-- utfchar(0x205F), -- math thinspace +-- } ) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules = { } end modules ['l-boolean'] = { + version = 1.001, + 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 type, tonumber = type, tonumber + +boolean = boolean or { } +local boolean = boolean + +function boolean.tonumber(b) + if b then return 1 else return 0 end -- test and return or return +end + +function toboolean(str,tolerant) + if tolerant then + local tstr = type(str) + if tstr == "string" then + return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t" + elseif tstr == "number" then + return tonumber(str) ~= 0 + elseif tstr == "nil" then + return false + else + return str + end + elseif str == "true" then + return true + elseif str == "false" then + return false + else + return str + end +end + +string.toboolean = toboolean + +function string.is_boolean(str,default) + if type(str) == "string" then + if str == "true" or str == "yes" or str == "on" or str == "t" then + return true + elseif str == "false" or str == "no" or str == "off" or str == "f" then + return false + end + end + return default +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules = { } end modules ['l-math'] = { + version = 1.001, + 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 floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan + +if not math.round then + function math.round(x) return floor(x + 0.5) end +end + +if not math.div then + function math.div(n,m) return floor(n/m) end +end + +if not math.mod then + function math.mod(n,m) return n % m end +end + +local pipi = 2*math.pi/360 + +if not math.sind then + function math.sind(d) return sin(d*pipi) end + function math.cosd(d) return cos(d*pipi) end + function math.tand(d) return tan(d*pipi) end +end + +if not math.odd then + function math.odd (n) return n % 2 ~= 0 end + function math.even(n) return n % 2 == 0 end +end + end -- closure do -- begin closure to overcome local limits and interference @@ -1817,10 +2065,10 @@ end local trick_1 = char(1) local trick_2 = "^" .. trick_1 .. "/+" -function file.join(...) +function file.join(...) -- rather dirty local lst = { ... } local a, b = lst[1], lst[2] - if a == "" then + if not a or a == "" then -- not a added lst[1] = trick_1 elseif b and find(a,"^/+$") and find(b,"^/") then lst[1] = "" @@ -1850,9 +2098,39 @@ end --~ print(file.join("http:///a","/y")) --~ print(file.join("//nas-1","/y")) +-- We should be able to use: +-- +-- function file.is_writable(name) +-- local a = attributes(name) or attributes(dirname(name,".")) +-- return a and sub(a.permissions,2,2) == "w" +-- end +-- +-- But after some testing Taco and I came up with: + function file.is_writable(name) - local a = attributes(name) or attributes(dirname(name,".")) - return a and sub(a.permissions,2,2) == "w" + if lfs.isdir(name) then + name = name .. "/m_t_x_t_e_s_t.tmp" + local f = io.open(name,"wb") + if f then + f:close() + os.remove(name) + return true + end + elseif lfs.isfile(name) then + local f = io.open(name,"ab") + if f then + f:close() + return true + end + else + local f = io.open(name,"ab") + if f then + f:close() + os.remove(name) + return true + end + end + return false end function file.is_readable(name) @@ -1900,7 +2178,7 @@ end --~ variant. After some skyping we got it sort of compatible with the old --~ one. After that the anchoring to currentdir was added in a better way. --~ Of course there are some optimizations too. Finally we had to deal with ---~ windows drive prefixes and thinsg like sys://. +--~ windows drive prefixes and things like sys://. function file.collapsepath(str,anchor) if anchor and not find(str,"^/") and not find(str,"^%a:") then @@ -1928,7 +2206,7 @@ function file.collapsepath(str,anchor) if element == '.' then -- do nothing elseif element == '..' then - local n = i -1 + local n = i - 1 while n > 0 do local element = oldelements[n] if element ~= '..' and element ~= '.' then @@ -2102,10 +2380,42 @@ local path = C(((1-slash)^0 * slash)^0) local suffix = period * C(P(1-period)^0 * P(-1)) local base = C((1-suffix)^0) -local pattern = (drive + Cc("")) * (path + Cc("")) * (base + Cc("")) * (suffix + Cc("")) +drive = drive + Cc("") +path = path + Cc("") +base = base + Cc("") +suffix = suffix + Cc("") + +local pattern_a = drive * path * base * suffix +local pattern_b = path * base * suffix +local pattern_c = C(drive * path) * C(base * suffix) + +function file.splitname(str,splitdrive) + if splitdrive then + return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix + else + return lpegmatch(pattern_b,str) -- returns path, base, suffix + end +end -function file.splitname(str) -- returns drive, path, base, suffix - return lpegmatch(pattern,str) +function file.nametotable(str,splitdrive) -- returns table + local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str) + if splitdrive then + return { + path = path, + drive = drive, + subpath = subpath, + name = name, + base = base, + suffix = suffix, + } + else + return { + path = path, + name = name, + base = base, + suffix = suffix, + } + end end -- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end @@ -2390,6 +2700,11 @@ function io.readstring(f,n,m) return str end +-- + +if not io.i_limiter then function io.i_limiter() end end -- dummy so we can test safely +if not io.o_limiter then function io.o_limiter() end end -- dummy so we can test safely + end -- closure do -- begin closure to overcome local limits and interference @@ -2476,6 +2791,8 @@ local remapper = { dfont = "truetype fonts", -- "truetype dictionary", cid = "cid maps", fea = "font feature files", + pfa = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! + pfb = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! } function resolvers.findfile(name,fileformat) @@ -2587,7 +2904,7 @@ end local function makefullname(path,name) if path and path ~= "" then name = "temp-" .. name -- clash prevention - return file.addsuffix(file.join(path,name),"lua") + return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),"luc") end end @@ -2598,26 +2915,67 @@ 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() + local data = false + local luaname, lucname = makefullname(paths[i],name) + if lucname and lfs.isfile(lucname) then + texio.write(string.format("(load: %s)",lucname)) + data = loadfile(lucname) end + if not data and luaname and lfs.isfile(luaname) then + texio.write(string.format("(load: %s)",luaname)) + data = loadfile(luaname) + end + return data and data() 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,true,{ reduce = true }) + local luaname, lucname = makefullname(path,name) + if luaname then + texio.write(string.format("(save: %s)",luaname)) + table.tofile(luaname,data,true,{ reduce = true }) + if lucname and type(caches.compile) == "function" then + os.remove(lucname) -- better be safe + texio.write(string.format("(save: %s)",lucname)) + caches.compile(data,luaname,lucname) + end end end +-- According to KH os.execute is not permitted in plain/latex so there is +-- no reason to use the normal context way. So the method here is slightly +-- different from the one we have in context. We also use different suffixes +-- as we don't want any clashes (sharing cache files is not that handy as +-- context moves on faster.) -- +-- Beware: serialization might fail on large files (so maybe we should pcall +-- this) in which case one should limit the method to luac and enable support +-- for execution. + +caches.compilemethod = "luac" -- luac dump both -local table.setmetatableindex(t,f) +function caches.compile(data,luaname,lucname) + local done = false + if caches.compilemethod == "luac" or caches.compilemethod == "both" then + local command = "-o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname) + done = os.spawn("texluac " .. command) == 0 + end + if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then + local d = table.serialize(data,true) + if d and d ~= "" then + local f = io.open(lucname,'w') + if f then + local s = loadstring(d) + f:write(string.dump(s)) + f:close() + end + end + end +end + +-- + +function table.setmetatableindex(t,f) setmetatable(t,{ __index = f }) end @@ -2917,12 +3275,15 @@ if not modules then modules = { } end modules ['font-con'] = { } +-- some names of table entries will be changed (no _) + local utf = unicode.utf8 local next, tostring, rawget = next, tostring, rawget local format, match, lower, gsub = string.format, string.match, string.lower, string.gsub local utfbyte = utf.byte local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy +local derivetable = table.derive 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) @@ -2935,32 +3296,128 @@ local report_defining = logs.reporter("fonts","defining") <p>Here we only implement a few helper functions.</p> --ldx]]-- -local fonts = fonts -local constructors = { } -fonts.constructors = constructors -local handlers = { } -fonts.handlers = handlers +local fonts = fonts +local constructors = { } +fonts.constructors = constructors +local handlers = { } +fonts.handlers = handlers -local specifiers = fonts.specifiers -local contextsetups = specifiers.contextsetups -local contextnumbers = specifiers.contextnumbers +local specifiers = fonts.specifiers +local contextsetups = specifiers.contextsetups +local contextnumbers = specifiers.contextnumbers -local allocate = utilities.storage.allocate -local setmetatableindex = table.setmetatableindex +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex -- will be directives -constructors.dontembed = allocate() -constructors.mathactions = { } -constructors.autocleanup = true -constructors.namemode = "fullpath" -- will be a function +constructors.dontembed = allocate() +constructors.autocleanup = true +constructors.namemode = "fullpath" -- will be a function -constructors.version = 1.01 -constructors.cache = containers.define("fonts", "constructors", constructors.version, false) +constructors.version = 1.01 +constructors.cache = containers.define("fonts", "constructors", constructors.version, false) -constructors.privateoffset = 0xF0000 -- 0x10FFFF +constructors.privateoffset = 0xF0000 -- 0x10FFFF + +-- Some experimental helpers (handy for tracing): +-- +-- todo: extra: +-- +-- extra_space => space.extra +-- space => space.width +-- space_stretch => space.stretch +-- space_shrink => space.shrink + +-- We do keep the x-height, extra_space, space_shrink and space_stretch +-- around as these are low level official names. + +constructors.keys = { + properties = { + encodingbytes = "number", + embedding = "number", + cidinfo = { + }, + format = "string", + fontname = "string", + fullname = "string", + filename = "filename", + psname = "string", + name = "string", + virtualized = "boolean", + hasitalics = "boolean", + autoitalicamount = "basepoints", + nostackmath = "boolean", + noglyphnames = "boolean", + mode = "string", + hasmath = "boolean", + mathitalics = "boolean", + textitalics = "boolean", + finalized = "boolean", + }, + parameters = { + mathsize = "number", + scriptpercentage = "float", + scriptscriptpercentage = "float", + units = "cardinal", + designsize = "scaledpoints", + expansion = { + stretch = "integerscale", -- might become float + shrink = "integerscale", -- might become float + step = "integerscale", -- might become float + auto = "boolean", + }, + protrusion = { + auto = "boolean", + }, + slantfactor = "float", + extendfactor = "float", + factor = "float", + hfactor = "float", + vfactor = "float", + size = "scaledpoints", + units = "scaledpoints", + scaledpoints = "scaledpoints", + slantperpoint = "scaledpoints", + spacing = { + width = "scaledpoints", + stretch = "scaledpoints", + shrink = "scaledpoints", + extra = "scaledpoints", + }, + xheight = "scaledpoints", + quad = "scaledpoints", + ascender = "scaledpoints", + descender = "scaledpoints", + synonyms = { + space = "spacing.width", + spacestretch = "spacing.stretch", + spaceshrink = "spacing.shrink", + extraspace = "spacing.extra", + x_height = "xheight", + space_stretch = "spacing.stretch", + space_shrink = "spacing.shrink", + extra_space = "spacing.extra", + em = "quad", + ex = "xheight", + slant = "slantperpoint", + }, + }, + description = { + width = "basepoints", + height = "basepoints", + depth = "basepoints", + boundingbox = { }, + }, + character = { + width = "scaledpoints", + height = "scaledpoints", + depth = "scaledpoints", + italic = "scaledpoints", + }, +} --- This might become an interface; +-- This might become an interface: local designsizes = allocate() constructors.designsizes = designsizes @@ -3009,7 +3466,7 @@ excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p> -- The scaler is only used for otf and afm and virtual fonts. If -- a virtual font has italic correction make sure to set the --- italic_correction flag. Some more flags will be added in +-- hasitalics flag. Some more flags will be added in -- the future. --[[ldx-- @@ -3055,39 +3512,75 @@ function constructors.calculatescale(tfmdata,scaledpoints) return scaledpoints, scaledpoints / (parameters.units or 1000) -- delta end -function constructors.assignmathparameters(target,tfmdata) +local unscaled = { + ScriptPercentScaleDown = true, + ScriptScriptPercentScaleDown = true, + RadicalDegreeBottomRaisePercent = true +} + +function constructors.assignmathparameters(target,original) -- simple variant, not used in context -- when a tfm file is loaded, it has already been scaled - -- and it never enters the scaled so this is otf only + -- and it never enters the scaled so this is otf only and + -- even then we do some extra in the context math plugins local mathparameters = original.mathparameters if mathparameters and next(mathparameters) then local targetparameters = target.parameters + local targetproperties = target.properties local targetmathparameters = { } - local factor = targetparameters.factor + local factor = targetproperties.math_is_scaled and 1 or targetparameters.factor for name, value in next, mathparameters do - if name == "RadicalDegreeBottomRaisePercent" then + if unscaled[name] then targetmathparameters[name] = value else targetmathparameters[name] = value * factor end end - if not targetmathparameters.AccentBaseHeight then - targetmathparameters.AccentBaseHeight = nil -- safeguard, still needed? - end if not targetmathparameters.FractionDelimiterSize then - targetmathparameters.FractionDelimiterSize = 0 + targetmathparameters.FractionDelimiterSize = 1.01 * targetparameters.size end if not mathparameters.FractionDelimiterDisplayStyleSize then - targetmathparameters.FractionDelimiterDisplayStyleSize = 0 + targetmathparameters.FractionDelimiterDisplayStyleSize = 2.40 * targetparameters.size end target.mathparameters = targetmathparameters end end +function constructors.beforecopyingcharacters(target,original) + -- can be used for additional tweaking +end + +function constructors.aftercopyingcharacters(target,original) + -- can be used for additional tweaking +end + +function constructors.enhanceparameters(parameters) + local xheight = parameters.x_height + local quad = parameters.quad + local space = parameters.space + local stretch = parameters.space_stretch + local shrink = parameters.space_shrink + local extra = parameters.extra_space + local slant = parameters.slant + parameters.xheight = xheight + parameters.spacestretch = stretch + parameters.spaceshrink = shrink + parameters.extraspace = extra + parameters.em = quad + parameters.ex = xheight + parameters.slantperpoint = slant + parameters.spacing = { + width = space, + stretch = stretch, + shrink = shrink, + extra = extra, + } +end + function constructors.scale(tfmdata,specification) local target = { } -- the new table -- if tonumber(specification) then - specification = { size = specification } + specification = { size = specification } end -- local scaledpoints = specification.size @@ -3104,10 +3597,9 @@ function constructors.scale(tfmdata,specification) local mathparameters = tfmdata.mathparameters or { } -- local targetcharacters = { } - local targetdescriptions = table.derive(descriptions) - local targetparameters = table.derive(parameters) - -- local targetmathparameters = table.fastcopy(mathparameters) -- happens elsewhere - local targetproperties = table.derive(properties) + local targetdescriptions = derivetable(descriptions) + local targetparameters = derivetable(parameters) + local targetproperties = derivetable(properties) local targetgoodies = goodies -- we need to loop so no metatable target.characters = targetcharacters target.descriptions = targetdescriptions @@ -3123,9 +3615,10 @@ function constructors.scale(tfmdata,specification) -- specification.textsize : natural (text)size -- parameters.mathsize : 1=text 2=script 3=scriptscript >1000 enforced size (feature value other than yes) -- - local mathsize = tonumber(specification.mathsize) or 0 - local textsize = tonumber(specification.textsize) or scaledpoints - local forcedsize = tonumber(parameters.mathsize ) or 0 + local mathsize = tonumber(specification.mathsize) or 0 + local textsize = tonumber(specification.textsize) or scaledpoints + local forcedsize = tonumber(parameters.mathsize ) or 0 + local extrafactor = tonumber(specification.factor ) or 1 if (mathsize == 2 or forcedsize == 2) and parameters.scriptpercentage then scaledpoints = parameters.scriptpercentage * textsize / 100 elseif (mathsize == 3 or forcedsize == 3) and parameters.scriptscriptpercentage then @@ -3212,17 +3705,17 @@ function constructors.scale(tfmdata,specification) target.auto_protrude = protrusion.auto end -- widening - local extend_factor = parameters.extend_factor or 0 - if extend_factor ~= 0 and extend_factor ~= 1 then - hdelta = hdelta * extend_factor - target.extend = extend_factor * 1000 -- extent ? + local extendfactor = parameters.extendfactor or 0 + if extendfactor ~= 0 and extendfactor ~= 1 then + hdelta = hdelta * extendfactor + target.extend = extendfactor * 1000 -- extent ? else target.extend = 1000 -- extent ? end -- slanting - local slant_factor = parameters.slant_factor or 0 - if slant_factor ~= 0 then - target.slant = slant_factor * 1000 + local slantfactor = parameters.slantfactor or 0 + if slantfactor ~= 0 then + target.slant = slantfactor * 1000 else target.slant = 0 end @@ -3234,19 +3727,20 @@ function constructors.scale(tfmdata,specification) targetparameters.units = units targetparameters.scaledpoints = askedscaledpoints -- - local isvirtual = properties.virtualized or tfmdata.type == "virtual" - local hasquality = target.auto_expand or target.auto_protrude - local hasitalic = properties.italic_correction - local stackmath = not properties.no_stackmath - local nonames = properties.noglyphnames - local nodemode = properties.mode == "node" + local isvirtual = properties.virtualized or tfmdata.type == "virtual" + local hasquality = target.auto_expand or target.auto_protrude + local hasitalics = properties.hasitalics + local autoitalicamount = properties.autoitalicamount + local stackmath = not properties.nostackmath + local nonames = properties.noglyphnames + local nodemode = properties.mode == "node" -- if changed and not next(changed) then changed = false end -- target.type = isvirtual and "virtual" or "real" - -- this will move to some subtable so that it is copied at once + -- target.postprocessors = tfmdata.postprocessors -- local targetslant = (parameters.slant or parameters[1] or 0) @@ -3257,7 +3751,7 @@ function constructors.scale(tfmdata,specification) local targetquad = (parameters.quad or parameters[6] or 0)*hdelta local targetextra_space = (parameters.extra_space or parameters[7] or 0)*hdelta -- - targetparameters.slant = targetslant + targetparameters.slant = targetslant -- slantperpoint targetparameters.space = targetspace targetparameters.space_stretch = targetspace_stretch targetparameters.space_shrink = targetspace_shrink @@ -3273,25 +3767,27 @@ function constructors.scale(tfmdata,specification) if descender then targetparameters.descender = delta * descender end - -- copies, might disappear - targetparameters.xheight = targetparameters.xheight or parameters.x_height - targetparameters.extraspace = targetparameters.extraspace or parameters.extra_space - targetparameters.spacestretch = targetparameters.spacestretch or parameters.space_stretch - targetparameters.spaceshrink = targetparameters.spaceshrink or parameters.space_shrink + -- + constructors.enhanceparameters(targetparameters) -- official copies for us -- local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0 local scaledwidth = defaultwidth * hdelta local scaledheight = defaultheight * vdelta local scaleddepth = defaultdepth * vdelta -- - local hasmath = (properties.has_math or next(mathparameters)) and true + if trace_defining then + report_defining("scaling by (%s,%s): name '%s', fullname: '%s', filename: '%s'", + hdelta,vdelta,name or "noname",fullname or "nofullname",filename or "nofilename") + end + -- + local hasmath = (properties.hasmath or next(mathparameters)) and true if hasmath then if trace_defining then report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'", name or "noname",fullname or "nofullname",filename or "nofilename") end constructors.assignmathparameters(target,tfmdata) -- does scaling and whatever is needed - properties.has_math = true + properties.hasmath = true target.nomath = false target.MathConstants = target.mathparameters else @@ -3299,29 +3795,75 @@ function constructors.scale(tfmdata,specification) report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'", name or "noname",fullname or "nofullname",filename or "nofilename") end - properties.has_math = false + properties.hasmath = false target.nomath = true target.mathparameters = nil -- nop end -- - local sharedkerns = { } + local italickey = "italic" + -- + -- some context specific trickery (this will move to a plugin) + -- + if hasmath then + if properties.mathitalics then + italickey = "italic_correction" + if trace_defining then + report_defining("math italics disabled for: name '%s', fullname: '%s', filename: '%s'", + name or "noname",fullname or "nofullname",filename or "nofilename") + end + end + autoitalicamount = false -- new + else + if properties.textitalics then + italickey = "italic_correction" + if trace_defining then + report_defining("text italics disabled for: name '%s', fullname: '%s', filename: '%s'", + name or "noname",fullname or "nofullname",filename or "nofilename") + end + if properties.delaytextitalics then + autoitalicamount = false + end + end + end + -- + -- end of context specific trickery + -- + constructors.beforecopyingcharacters(target,tfmdata) + -- + local sharedkerns = { } + -- + -- we can have a dumb mode (basemode without math etc) that skips most -- for unicode, character in next, characters do - local chr, description, index + local chr, description, index, touni if changed then - -- basemode hack + -- basemode hack (we try to catch missing tounicodes, e.g. needed for ssty in math cambria) local c = changed[unicode] if c then - description = descriptions[c] or character + description = descriptions[c] or descriptions[unicode] or character character = characters[c] or character index = description.index or c + if tounicode then + touni = tounicode[index] -- nb: index! + if not touni then -- goodie + local d = descriptions[unicode] or characters[unicode] + local i = d.index or unicode + touni = tounicode[i] -- nb: index! + end + end else description = descriptions[unicode] or character index = description.index or unicode + if tounicode then + touni = tounicode[index] -- nb: index! + end end else description = descriptions[unicode] or character index = description.index or unicode + if tounicode then + touni = tounicode[index] -- nb: index! + end end local width = description.width local height = description.height @@ -3364,15 +3906,12 @@ function constructors.scale(tfmdata,specification) } end end + if touni then + chr.tounicode = touni + end -- if trace_scaling then -- report_defining("t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or "",index or 0,description.name or '-',description.class or '-') -- end - if tounicode then - local tu = tounicode[index] -- nb: index! - if tu then - chr.tounicode = tu - end - end if hasquality then -- we could move these calculations elsewhere (saves calculations) local ve = character.expansion_factor @@ -3388,11 +3927,21 @@ function constructors.scale(tfmdata,specification) chr.right_protruding = protrusionfactor*width*vr end end - -- todo: hasitalic - if hasitalic then - local vi = description.italic or character.italic + -- + if autoitalicamount then + local vi = description.italic + if not vi then + local vi = description.boundingbox[3] - description.width + autoitalicamount + if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic + chr[italickey] = vi*hdelta + end + elseif vi ~= 0 then + chr[italickey] = vi*hdelta + end + elseif hasitalics then + local vi = description.italic if vi and vi ~= 0 then - chr.italic = vi*hdelta + chr[italickey] = vi*hdelta end end -- to be tested @@ -3523,6 +4072,9 @@ function constructors.scale(tfmdata,specification) end targetcharacters[unicode] = chr end + -- + constructors.aftercopyingcharacters(target,tfmdata) + -- return target end @@ -3563,12 +4115,12 @@ function constructors.finalize(tfmdata) parameters.size = tfmdata.size end -- - if not parameters.extend_factor then - parameters.extend_factor = tfmdata.extend or 0 + if not parameters.extendfactor then + parameters.extendfactor = tfmdata.extend or 0 end -- - if not parameters.slant_factor then - parameters.slant_factor = tfmdata.slant or 0 + if not parameters.slantfactor then + parameters.slantfactor = tfmdata.slant or 0 end -- if not parameters.designsize then @@ -3621,8 +4173,8 @@ function constructors.finalize(tfmdata) -- tfmdata.fonts -- tfmdata.unscaled -- - if not properties.has_math then - properties.has_math = not tfmdata.nomath + if not properties.hasmath then + properties.hasmath = not tfmdata.nomath end -- tfmdata.MathConstants = nil @@ -3907,7 +4459,7 @@ a helper function.</p> function constructors.checkedfeatures(what,features) local defaults = handlers[what].features.defaults if features and next(features) then - features = fastcopy(features) -- can be inherited + features = fastcopy(features) -- can be inherited (mt) but then no loops possible for key, value in next, defaults do if features[key] == nil then features[key] = value @@ -3941,7 +4493,7 @@ function constructors.initializefeatures(what,tfmdata,features,trace,report) for i=1,#initializers do local step = initializers[i] local feature = step.name --- we could intercept mode here .. needs a rewrite of this whole loop then btu it's cleaner that way +-- we could intercept mode here .. needs a rewrite of this whole loop then but it's cleaner that way local value = features[feature] if not value then -- disabled @@ -4010,6 +4562,9 @@ function constructors.collectprocessors(what,tfmdata,features,trace,report) end end end + elseif trace then + report("no feature processors for mode %s for font %s", + mode or 'unknown', tfmdata.properties.fullname or 'unknown') end end return processes @@ -4068,7 +4623,7 @@ fonts.encodings.agl = { } setmetatable(fonts.encodings.agl, { __index = function(t,k) if k == "unicodes" then texio.write(" <loading (extended) adobe glyph list>") - local unicodes = dofile(resolvers.findfile("font-agl.lua")) + local unicodes = dofile(resolvers.findfile("font-age.lua")) fonts.encodings.agl = { unicodes = unicodes } return unicodes else @@ -4351,6 +4906,15 @@ local function tounicode16sequence(unicodes) return concat(t) end +local function fromunicode16(str) + if #str == 4 then + return tonumber(str,16) + else + local l, r = match(str,"(....)(....)") + return (tonumber(l,16)- 0xD800)*0x400 + tonumber(r,16) - 0xDC00 + end +end + --~ This is quite a bit faster but at the cost of some memory but if we --~ do this we will also use it elsewhere so let's not follow this route --~ now. I might use this method in the plain variant (no caching there) @@ -4375,6 +4939,7 @@ mappings.loadlumtable = loadlumtable mappings.makenameparser = makenameparser mappings.tounicode16 = tounicode16 mappings.tounicode16sequence = tounicode16sequence +mappings.fromunicode16 = fromunicode16 local separator = S("_.") local other = C((1 - separator)^1) @@ -4430,7 +4995,9 @@ function mappings.addtounicode(data,filename) 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 unicodevector[name] if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + originals[index] = unicode + tounicode[index] = tounicode16(unicode) + ns = ns + 1 end -- cidmap heuristics, beware, there is no guarantee for a match unless -- the chain resolves @@ -4439,7 +5006,9 @@ function mappings.addtounicode(data,filename) if foundindex then unicode = cidcodes[foundindex] -- name to number if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + originals[index] = unicode + tounicode[index] = tounicode16(unicode) + ns = ns + 1 else local reference = cidnames[foundindex] -- number to name if reference then @@ -4447,16 +5016,23 @@ function mappings.addtounicode(data,filename) if foundindex then unicode = cidcodes[foundindex] if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + originals[index] = unicode + tounicode[index] = tounicode16(unicode) + ns = ns + 1 end end if not unicode then local foundcodes, multiple = lpegmatch(uparser,reference) if foundcodes then + originals[index] = foundcodes if multiple then - originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + tounicode[index] = tounicode16sequence(foundcodes) + nl = nl + 1 + unicode = true else - originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + tounicode[index] = tounicode16(foundcodes) + ns = ns + 1 + unicode = foundcodes end end end @@ -4467,19 +5043,8 @@ function mappings.addtounicode(data,filename) -- a.whatever or a_b_c.whatever or a_b_c (no numbers) if not unicode then local split = lpegmatch(ligsplitter,name) - local nplit = (split and #split) or 0 - if nplit == 0 then - -- skip - elseif nplit == 1 then - local base = split[1] - unicode = unicodes[base] or unicodevector[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 nplit = split and #split or 0 + if nplit >= 2 then local t, n = { }, 0 for l=1,nplit do local base = split[l] @@ -4497,26 +5062,39 @@ function mappings.addtounicode(data,filename) if n == 0 then -- done then -- nothing elseif n == 1 then - originals[index], tounicode[index], nl, unicode = t[1], tounicode16(t[1]), nl + 1, true + originals[index] = t[1] + tounicode[index] = tounicode16(t[1]) else - originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true + originals[index] = t + tounicode[index] = tounicode16sequence(t) end + nl = nl + 1 + unicode = true + else + -- skip: already checked and we don't want privates here end end - -- last resort + -- last resort (we might need to catch private here as well) 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 + originals[index] = foundcodes + tounicode[index] = tounicode16sequence(foundcodes) + nl = nl + 1 + unicode = true else - originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + originals[index] = foundcodes + tounicode[index] = tounicode16(foundcodes) + ns = ns + 1 + unicode = foundcodes end end end - if not unicode then - originals[index], tounicode[index] = 0xFFFD, "FFFD" - end + -- if not unicode then + -- originals[index] = 0xFFFD + -- tounicode[index] = "FFFD" + -- end end end if trace_mapping then @@ -4782,6 +5360,8 @@ if not modules then modules = { } end modules ['font-otf'] = { -- to_table -> totable -- ascent descent +-- more checking against low level calls of functions + local utf = unicode.utf8 local utfbyte = utf.byte @@ -4792,7 +5372,7 @@ local getn = table.getn local lpegmatch = lpeg.match local reversed, concat, remove = table.reversed, table.concat, table.remove local ioflush = io.flush -local fastcopy, tohash = table.fastcopy, table.tohash +local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive local allocate = utilities.storage.allocate local registertracker = trackers.register @@ -4817,7 +5397,7 @@ local otf = fonts.handlers.otf otf.glists = { "gsub", "gpos" } -otf.version = 2.730 -- beware: also sync font-mis.lua +otf.version = 2.736 -- beware: also sync font-mis.lua otf.cache = containers.define("fonts", "otf", otf.version, true) local fontdata = fonts.hashes.identifiers @@ -4884,15 +5464,21 @@ local function showfeatureorder(rawdata,filename) 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 + if type(scripts) == "table" then + 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 + report_otf(" %s: %s",feature,concat(tt," ")) + end + else + if trace_loading then + report_otf(" %s: %s",feature,tostring(scripts)) end - tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) - end - if trace_loading then - report_otf(" %s: %s",feature,concat(tt," ")) end end end @@ -5061,11 +5647,14 @@ end -- patches.register("before","migrate metadata","cambria",function() end) function patches.register(what,where,pattern,action) - local ww = what[where] - if ww then - ww[pattern] = action - else - ww = { [pattern] = action} + local pw = patches[what] + if pw then + local ww = pw[where] + if ww then + ww[pattern] = action + else + pw[where] = { [pattern] = action} + end end end @@ -5106,8 +5695,8 @@ function otf.load(filename,format,sub,featurefile) local attr = lfs.attributes(name) featurefiles[#featurefiles+1] = { name = name, - size = size, - time = time, + size = attr and attr.size or 0, + time = attr and attr.modification or 0, } end end @@ -5190,6 +5779,9 @@ function otf.load(filename,format,sub,featurefile) duplicates = { -- alternative unicodes }, + variants = { + -- alternative unicodes (variants) + }, lookuptypes = { }, }, @@ -5214,6 +5806,7 @@ function otf.load(filename,format,sub,featurefile) if packdata then if cleanup > 0 then collectgarbage("collect") +--~ lua.collectgarbage() end enhance("pack",data,filename,nil) end @@ -5221,6 +5814,7 @@ function otf.load(filename,format,sub,featurefile) data = containers.write(otf.cache, hash, data) if cleanup > 1 then collectgarbage("collect") +--~ lua.collectgarbage() end stoptiming(data) if elapsedtime then -- not in generic @@ -5229,10 +5823,12 @@ function otf.load(filename,format,sub,featurefile) fontloader.close(fontdata) -- free memory if cleanup > 3 then collectgarbage("collect") +--~ lua.collectgarbage() end data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one if cleanup > 2 then collectgarbage("collect") +--~ lua.collectgarbage() end else data = nil @@ -5269,7 +5865,7 @@ local mt = { } actions["prepare tables"] = function(data,filename,raw) - data.properties.italic_correction = false + data.properties.hasitalics = false end actions["add dimensions"] = function(data,filename) @@ -5354,7 +5950,7 @@ local function somecopy(old) -- fast one end end --- not setting italic_correction and class (when nil) during +-- not setting hasitalics and class (when nil) during -- table cronstruction can save some mem actions["prepare glyphs"] = function(data,filename,raw) @@ -5370,6 +5966,7 @@ actions["prepare glyphs"] = function(data,filename,raw) local unicodes = resources.unicodes -- name to unicode local indices = resources.indices -- index to unicode local duplicates = resources.duplicates + local variants = resources.variants if rawsubfonts then @@ -5469,11 +6066,28 @@ actions["prepare glyphs"] = function(data,filename,raw) } local altuni = glyph.altuni if altuni then - local d = { } + local d for i=1,#altuni do - d[#d+1] = altuni[i].unicode + local a = altuni[i] + local u = a.unicode + local v = a.variant + if v then + local vv = variants[v] + if vv then + vv[u] = unicode + else -- xits-math has some: + vv = { [u] = unicode } + variants[v] = vv + end + elseif d then + d[#d+1] = u + else + d = { u } + end + end + if d then + duplicates[unicode] = d end - duplicates[unicode] = d end else report_otf("potential problem: glyph 0x%04X is used but empty",index) @@ -5495,9 +6109,8 @@ actions["check encoding"] = function(data,filename,raw) local properties = data.properties local unicodes = resources.unicodes -- name to unicode local indices = resources.indices -- index to unicodes - local duplicates = resources.duplicates - -- begin of messy (not needed whwn cidmap) + -- begin of messy (not needed when cidmap) local mapdata = raw.map or { } local unicodetoindex = mapdata and mapdata.map or { } @@ -5571,7 +6184,6 @@ actions["add duplicates"] = function(data,filename,raw) end end end - end -- class : nil base mark ligature component (maybe we don't need it in description) @@ -5582,9 +6194,9 @@ actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this local resources = data.resources local metadata = data.metadata local properties = data.properties - local italic_correction = false + local hasitalics = false local widths = { } - local marks = { } + local marks = { } -- always present (saves checking) for unicode, description in next, descriptions do local glyph = description.glyph local italic = glyph.italic_correction @@ -5594,7 +6206,7 @@ actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this -- skip else description.italic = italic - italic_correction = true + hasitalics = true end local width = glyph.width widths[width] = (widths[width] or 0) + 1 @@ -5607,7 +6219,7 @@ actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this end end -- flag italic - properties.italic_correction = italic_correction + properties.hasitalics = hasitalics -- flag marks resources.marks = marks -- share most common width for cjk fonts @@ -5729,6 +6341,29 @@ actions["prepare tounicode"] = function(data,filename,raw) fonts.mappings.addtounicode(data,filename) end +local g_directions = { + gsub_contextchain = 1, + gpos_contextchain = 1, + -- gsub_context = 1, + -- gpos_context = 1, + gsub_reversecontextchain = -1, + gpos_reversecontextchain = -1, +} + +-- Research by Khaled Hosny has demonstrated that the font loader merges +-- regular and AAT features and that these can interfere (especially because +-- we dropped checking for valid features elsewhere. So, we just check for +-- the special flag and drop the feature if such a tag is found. + +local function supported(features) + for i=1,#features do + if features[i].ismac then + return false + end + end + return true +end + actions["reorganize subtables"] = function(data,filename,raw) local resources = data.resources local sequences = { } @@ -5741,72 +6376,72 @@ actions["reorganize subtables"] = function(data,filename,raw) if dw then for k=1,#dw do local gk = dw[k] - local typ = gk.type - local chain = - (typ == "gsub_contextchain" or typ == "gpos_contextchain") and 1 or - (typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain") and -1 or 0 - -- - local subtables = gk.subtables - if subtables then - local t = { } - for s=1,#subtables do - t[s] = subtables[s].name - end - subtables = t - end - local flags, markclass = gk.flags, nil - if flags then - local t = { -- forcing false packs nicer - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false, - } - markclass = flags.mark_class - if markclass then - markclass = resources.markclasses[markclass] - end - flags = t - end - -- - local name = gk.name - -- local features = gk.features - if features then - -- scripts, tag, ismac - local f = { } - for i=1,#features do - local df = features[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 i=1,#dscripts do - local d = dscripts[i] - local languages = d.langs - local script = strip(lower(d.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 +-- if features and supported(features) then + if not features or supported(features) then -- not always features ! + local typ = gk.type + local chain = g_directions[typ] or 0 + local subtables = gk.subtables + if subtables then + local t = { } + for s=1,#subtables do + t[s] = subtables[s].name + end + subtables = t + end + local flags, markclass = gk.flags, nil + if flags then + local t = { -- forcing false packs nicer + (flags.ignorecombiningmarks and "mark") or false, + (flags.ignoreligatures and "ligature") or false, + (flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, + } + markclass = flags.mark_class + if markclass then + markclass = resources.markclasses[markclass] + end + flags = t + end + -- + local name = gk.name + -- + if features then + -- scripts, tag, ismac + local f = { } + for i=1,#features do + local df = features[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 i=1,#dscripts do + local d = dscripts[i] + local languages = d.langs + local script = strip(lower(d.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 + sequences[#sequences+1] = { + type = typ, + chain = chain, + flags = flags, + name = name, + subtables = subtables, + markclass = markclass, + features = f, + } + else + lookups[name] = { + type = typ, + chain = chain, + flags = flags, + subtables = subtables, + markclass = markclass, + } end - sequences[#sequences+1] = { - type = typ, - chain = chain, - flags = flags, - name = name, - subtables = subtables, - markclass = markclass, - features = f, - } - else - lookups[name] = { - type = typ, - chain = chain, - flags = flags, - subtables = subtables, - markclass = markclass, - } end end end @@ -5847,19 +6482,20 @@ end local function t_hashed(t,cache) if t then - local h = { } + local ht = { } for i=1,#t do local ti = t[i] - local h = cache[ti] - if not h then - h = { } + local tih = cache[ti] + if not tih then + tih = { } for i=1,#ti do - h[ti] = true + tih[ti[i]] = true end + cache[ti] = tih end - cache[ti] = h + ht[i] = tih end - return h + return ht else return nil end @@ -5943,7 +6579,7 @@ actions["reorganize lookups"] = function(data,filename,raw) for i=1,#current do current[i] = current_class[current[i]] or { } if lookups and not lookups[i] then - lookups[i] = false + lookups[i] = false -- e.g. we can have two lookups and one replacement end end rule.current = t_hashed(current,h_cache) @@ -6040,8 +6676,6 @@ actions["reorganize lookups"] = function(data,filename,raw) end end --- to be checked italic_correction - local function check_variants(unicode,the_variants,splitter,unicodes) local variants = the_variants.variants if variants then -- use splitter @@ -6441,9 +7075,9 @@ local function copytotfm(data,cache_id) if data then local metadata = data.metadata local resources = data.resources - local properties = table.derive(data.properties) - local descriptions = table.derive(data.descriptions) - local goodies = table.derive(data.goodies) + local properties = derivetable(data.properties) + local descriptions = derivetable(data.descriptions) + local goodies = derivetable(data.goodies) local characters = { } local parameters = { } local mathparameters = { } @@ -6569,6 +7203,11 @@ local function copytotfm(data,cache_id) local fullname = metadata.fullname or fontname local units = metadata.units_per_em or 1000 -- + if units == 0 then -- catch bugs in fonts + units = 1000 + metadata.units_per_em = 1000 + end + -- parameters.slant = 0 parameters.space = spaceunits -- 3.333 (cmr10) parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10) @@ -6654,9 +7293,9 @@ local function otftotfm(specification) tfmdata.shared = shared end shared.rawdata = rawdata - shared.features = features -- default + -- shared.features = features -- default shared.dynamics = { } - shared.processes = { } + -- shared.processes = { } tfmdata.changed = { } shared.features = features shared.processes = otf.setfeatures(tfmdata,features) @@ -6675,7 +7314,8 @@ local function read_from_otf(specification) tfmdata.properties.sub = specification.sub -- tfmdata = constructors.scale(tfmdata,specification) - constructors.applymanipulators("otf",tfmdata,specification.features.normal,trace_features,report_otf) + local allfeatures = tfmdata.shared.features or specification.features.normal + constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf) constructors.setname(tfmdata,specification) -- only otf? fonts.loggers.register(tfmdata,file.extname(specification.filename),specification) end @@ -6741,15 +7381,15 @@ local function check_otf(forced,specification,suffix,what) if forced then name = file.addsuffix(name,suffix,true) end - local fullname, tfmdata = findbinfile(name,suffix) or "", nil -- one shot + local fullname = findbinfile(name,suffix) or "" if fullname == "" then - fullname = fonts.names.getfilename(name,suffix) + fullname = fonts.names.getfilename(name,suffix) or "" end if fullname ~= "" then - specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then - tfmdata = read_from_otf(specification) -- we need to do it for all matches / todo + specification.filename = fullname + specification.format = what + return read_from_otf(specification) end - return tfmdata end local function opentypereader(specification,suffix,what) @@ -6817,6 +7457,8 @@ local otf = fonts.handlers.otf local otffeatures = fonts.constructors.newfeatures("otf") local registerotffeature = otffeatures.register +otf.defaultbasealternate = "none" -- first last + local wildcard = "*" local default = "dflt" @@ -6849,6 +7491,22 @@ local function cref(feature,lookupname) end end +local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment) + report_prepare("%s: base alternate %s => %s (%s => %s)",cref(feature,lookupname), + gref(descriptions,unicode),replacement and gref(descriptions,replacement) or "-", + tostring(value),comment) +end + +local function report_substitution(feature,lookupname,descriptions,unicode,substitution) + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,substitution)) +end + +local function report_ligature(feature,lookupname,descriptions,unicode,ligature) + report_prepare("%s: base ligature %s => %s",cref(feature,lookupname), + gref(descriptions,ligature),gref(descriptions,unicode)) +end + local basemethods = { } local basemethod = "<unset>" @@ -7013,27 +7671,47 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis local lookuptypes = resources.lookuptypes local ligatures = { } + local alternate = tonumber(value) + local defaultalt = otf.defaultbasealternate + + local trace_singles = trace_baseinit and trace_singles + local trace_alternatives = trace_baseinit and trace_alternatives + local trace_ligatures = trace_baseinit and trace_ligatures local actions = { substitution = function(lookupdata,lookupname,description,unicode) - if trace_baseinit and trace_singles then - report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), - gref(descriptions,unicode),gref(descriptions,replacement)) + if trace_singles then + report_substitution(feature,lookupname,descriptions,unicode,lookupdata) end changed[unicode] = lookupdata end, alternate = function(lookupdata,lookupname,description,unicode) - local replacement = lookupdata[value] or lookupdata[#lookupdata] - if trace_baseinit and trace_alternatives then - report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), - tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + local replacement = lookupdata[alternate] + if replacement then + changed[unicode] = replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") + end + elseif defaultalt == "first" then + replacement = lookupdata[1] + changed[unicode] = replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + elseif defaultalt == "last" then + replacement = lookupdata[#data] + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + else + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") + end end - changed[unicode] = replacement end, ligature = function(lookupdata,lookupname,description,unicode) - if trace_baseinit and trace_alternatives then - report_prepare("%s: base ligature %s %s => %s",cref(feature,lookupname), - tostring(value),gref(descriptions,lookupdata),gref(descriptions,unicode)) + if trace_ligatures then + report_ligature(feature,lookupname,descriptions,unicode,lookupdata) end ligatures[#ligatures+1] = { unicode, lookupdata } end, @@ -7199,6 +7877,12 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis local lookuptypes = resources.lookuptypes local ligatures = { } + local alternate = tonumber(value) + local defaultalt = otf.defaultbasealternate + + local trace_singles = trace_baseinit and trace_singles + local trace_alternatives = trace_baseinit and trace_alternatives + local trace_ligatures = trace_baseinit and trace_ligatures for l=1,#lookuplist do local lookupname = lookuplist[l] @@ -7206,20 +7890,38 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis local lookuptype = lookuptypes[lookupname] for unicode, data in next, lookupdata do if lookuptype == "substitution" then - if trace_baseinit and trace_singles then - report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), - gref(descriptions,unicode),gref(descriptions,data)) + if trace_singles then + report_substitution(feature,lookupname,descriptions,unicode,data) end changed[unicode] = data elseif lookuptype == "alternate" then - local replacement = data[value] or data[#data] - if trace_baseinit and trace_alternatives then - report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), - tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + local replacement = data[alternate] + if replacement then + changed[unicode] = replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") + end + elseif defaultalt == "first" then + replacement = data[1] + changed[unicode] = replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + elseif defaultalt == "last" then + replacement = data[#data] + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + else + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") + end end - changed[unicode] = replacement elseif lookuptype == "ligature" then ligatures[#ligatures+1] = { unicode, data, lookupname } + if trace_ligatures then + report_ligature(feature,lookupname,descriptions,unicode,data) + end end end end @@ -7244,13 +7946,6 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname) end - if done then - for lookupname, list in next, done do - report_prepare("%s: base ligatures %s => %s",cref(feature,lookupname), - tostring(value),gref(descriptions,done)) - end - end - end end @@ -7356,13 +8051,13 @@ registerotffeature { description = "features", default = true, initializers = { ---~ position = 1, -- after setscript (temp hack ... we need to force script / language to 1 + -- position = 1, -- after setscript (temp hack ... we need to force script / language to 1 base = featuresinitializer, } } -- independent : collect lookups independently (takes more runtime ... neglectable) --- shared : shares lookups with node mode (takes more memory ... noticeable) +-- shared : shares lookups with node mode (takes more memory unless also a node mode variant is used ... noticeable) directives.register("fonts.otf.loader.basemethod", function(v) if basemethods[v] then @@ -7410,6 +8105,7 @@ 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 copy_node = node.copy local insert_node_before = node.insert_before local insert_node_after = node.insert_after @@ -7420,17 +8116,38 @@ local cursbase = attributes.private('cursbase') local curscurs = attributes.private('curscurs') local cursdone = attributes.private('cursdone') local kernpair = attributes.private('kernpair') +local ligacomp = attributes.private('ligacomp') +local fontkern = attributes.private('fontkern') + +if context then + + local kern = nodes.pool.register(newkern()) + + set_attribute(kern,fontkern,1) -- we can have several, attributes are shared + + newkern = function(k) + local c = copy_node(kern) + c.kern = k + return c + end + +end + +-- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as +-- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner +-- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure +-- that this code is not 100% okay but examples are needed to figure things out. local cursives = { } local marks = { } local kerns = { } --- currently we do gpos/kern in a bit inofficial way but when we --- have the extra fields in glyphnodes to manipulate ht/dp/wd --- explicitly i will provide an alternative; also, we can share --- tables +-- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in +-- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we +-- can share tables. --- for the moment we pass the r2l key ... volt/arabtype tests +-- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs +-- checking with husayni (volt and fontforge). function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) @@ -7473,14 +8190,16 @@ function injections.setkern(current,factor,rlmode,x,tfmchr) end end -function injections.setmark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, ma=markanchor - local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) - local bound = has_attribute(base,markbase) +function injections.setmark(start,base,factor,rlmode,ba,ma,index) -- ba=baseanchor, ma=markanchor + local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this + local bound = has_attribute(base,markbase) -- fails again we should pass it +local index = 1 if bound then local mb = marks[bound] if mb then - if not index then index = #mb + 1 end - mb[index] = { dx, dy } + -- if not index then index = #mb + 1 end +index = #mb + 1 + mb[index] = { dx, dy, rlmode } set_attribute(start,markmark,bound) set_attribute(start,markdone,index) return dx, dy, bound @@ -7488,6 +8207,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,index) --ba=baseancho report_injections("possible problem, U+%05X is base mark without data (id: %s)",base.char,bound) end end +-- index = index or 1 index = index or 1 bound = #marks + 1 set_attribute(base,markbase,bound) @@ -7534,7 +8254,7 @@ local function trace(head) end else m = m[1] - report_injections(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") + report_injections(" markmark: bound=%s, dx=%s, dy=%s",mm,m and m[1] or "?",m and m[2] or "?") end end if cb then @@ -7552,7 +8272,8 @@ end -- todo: reuse tables (i.e. no collection), but will be extra fields anyway -- todo: check for attribute --- we can have a fast test on a font being processed, so we can check faster for marks etc +-- We can have a fast test on a font being processed, so we can check faster for marks etc +-- but I'll make a context variant anyway. function injections.handler(head,where,keep) local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) @@ -7690,6 +8411,7 @@ function injections.handler(head,where,keep) local p_markbase = has_attribute(p,markbase) if p_markbase then local mrks = marks[p_markbase] + local nofmarks = #mrks for n in traverse_id(glyph_code,p.next) do local n_markmark = has_attribute(n,markmark) if p_markbase == n_markmark then @@ -7703,7 +8425,7 @@ function injections.handler(head,where,keep) -- that makes sense but we no longer do that so as a -- consequence the sign of p.width was changed (we need -- to keep an eye on it as we don't have that many fonts - -- that enter this branch .. i'm still not sure if this + -- that enter this branch .. I'm still not sure if this -- one is right local k = wx[p] if k then @@ -7726,9 +8448,14 @@ function injections.handler(head,where,keep) else n.yoffset = n.yoffset + p.yoffset + d[2] end + if nofmarks == 1 then + break + else + nofmarks = nofmarks - 1 + end end else - break + -- KE: there can be <mark> <mkmk> <mark> sequences in ligatures end end end @@ -7740,23 +8467,32 @@ function injections.handler(head,where,keep) -- todo : combine if next(wx) then for n, k in next, wx do - -- only w can be nil, can be sped up when w == nil - 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)) + -- only w can be nil (kernclasses), can be sped up when w == nil + local x, w = k[2] or 0, k[4] + if w then + local rl = k[1] -- r2l = k[6] + local wx = w - x + if rl < 0 then -- KE: don't use r2l here + 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 + elseif x ~= 0 then + -- this needs checking for rl < 0 but it is unlikely that a r2l script + -- uses kernclasses between glyphs so we're probably safe (KE has a + -- problematic font where marks interfere with rl < 0 in the previous + -- case) + insert_node_before(head,n,newkern(x)) end end end @@ -7795,9 +8531,9 @@ function injections.handler(head,where,keep) end if w then -- copied from above - local r2l = kk[6] + -- local r2l = kk[6] local wx = w - x - if r2l then + if rl < 0 then -- KE: don't use r2l here if wx ~= 0 then insert_node_before(head,n,newkern(wx)) end @@ -7865,6 +8601,7 @@ if not modules then modules = { } end modules ['font-otn'] = { -- default features (per language, script) -- handle positions (we need example fonts) -- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) +-- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) --[[ldx-- <p>This module is a bit more split up that I'd like but since we also want to test @@ -7961,25 +8698,27 @@ local random = math.random local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes +local registertracker = trackers.register + local fonts = fonts local otf = fonts.handlers.otf -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) +local trace_lookups = false registertracker("otf.lookups", function(v) trace_lookups = v end) +local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end) +local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end) +local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures = false registertracker("otf.ligatures", function(v) trace_ligatures = v end) +local trace_contexts = false registertracker("otf.contexts", function(v) trace_contexts = v end) +local trace_marks = false registertracker("otf.marks", function(v) trace_marks = v end) +local trace_kerns = false registertracker("otf.kerns", function(v) trace_kerns = v end) +local trace_cursive = false registertracker("otf.cursive", function(v) trace_cursive = v end) +local trace_preparing = false registertracker("otf.preparing", function(v) trace_preparing = v end) +local trace_bugs = false registertracker("otf.bugs", function(v) trace_bugs = v end) +local trace_details = false registertracker("otf.details", function(v) trace_details = v end) +local trace_applied = false registertracker("otf.applied", function(v) trace_applied = v end) +local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end) +local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end) +local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end) local report_direct = logs.reporter("fonts","otf direct") local report_subchain = logs.reporter("fonts","otf subchain") @@ -7987,15 +8726,15 @@ local report_chain = logs.reporter("fonts","otf chain") local report_process = logs.reporter("fonts","otf process") local report_prepare = logs.reporter("fonts","otf prepare") -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) +registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) +registertracker("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") +registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") +registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") +registertracker("otf.actions","otf.replacements,otf.positions") +registertracker("otf.injections","nodes.injections") -trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing") +registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") local insert_node_after = node.insert_after local delete_node = nodes.delete @@ -8003,6 +8742,7 @@ 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 flush_node_list = node.flush_list local setmetatableindex = table.setmetatableindex @@ -8027,14 +8767,21 @@ local ligature_code = glyphcodes.ligature local privateattribute = attributes.private +-- Something is messed up: we have two mark / ligature indices, one at the injection +-- end and one here ... this is bases in KE's patches but there is something fishy +-- there as I'm pretty sure that for husayni we need some connection (as it's much +-- more complex than an average font) but I need proper examples of all cases, not +-- of only some. + local state = privateattribute('state') local markbase = privateattribute('markbase') local markmark = privateattribute('markmark') -local markdone = privateattribute('markdone') +local markdone = privateattribute('markdone') -- assigned at the injection end local cursbase = privateattribute('cursbase') local curscurs = privateattribute('curscurs') local cursdone = privateattribute('cursdone') local kernpair = privateattribute('kernpair') +local ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined) local injections = nodes.injections local setmark = injections.setmark @@ -8054,6 +8801,8 @@ local registerotffeature = otffeatures.register local onetimemessage = fonts.loggers.onetimemessage +otf.defaultnodealternate = "none" -- first last + -- we share some vars here, after all, we have no nested lookups and -- less code @@ -8156,21 +8905,35 @@ end local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- brr head if start == stop then start.char = char + return start elseif discfound then -- print("start->stop",nodes.tosequence(start,stop)) + local components = start.components + if components then + flush_node_list(components) + start.components = nil + end local lignode = copy_node(start) - lignode.font, lignode.char, lignode.subtype = start.font, char, ligature_code - local next, prev = stop.next, start.prev + lignode.font = start.font + lignode.char = char + lignode.subtype = ligature_code + local next = stop.next + local prev = start.prev stop.next = nil - lignode = node.do_ligature_n(start, stop, lignode) + start.prev = nil + lignode.components = start + -- print("lignode",nodes.tosequence(lignode)) + -- print("components",nodes.tosequence(lignode.components)) prev.next = lignode if next then next.prev = lignode end - lignode.next, lignode.prev = next, prev - start = lignode + lignode.next = next + lignode.prev = prev -- print("start->end",nodes.tosequence(start)) - else -- start is the ligature + return lignode + else + -- start is the ligature local deletemarks = markflag ~= "mark" local n = copy_node(start) local current @@ -8180,48 +8943,46 @@ local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- if snext then snext.prev = current end - start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, ligature_code, start + start.prev = nil + stop.next = nil + current.char = char + current.subtype = ligature_code + current.components = 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 + -- this is messy ... we should get rid of the components eventually + local i = 0 -- is index of base + while start do + if not marks[start.char] then + i = i + 1 + elseif not deletemarks then -- quite fishy + set_attribute(start,ligacomp,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)) + end + start = start.next + end + start = current.next + while start and start.id == glyph_code do + if marks[start.char] then + set_attribute(start,ligacomp,i) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) end - start = start.next - end - start = current.next - while start and start.id == glyph_code 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 + else + break end + start = start.next end + -- + -- we do need components in funny kerning mode but maybe I can better reconstruct then + -- as we do have the font components info available; removing components makes the + -- previous code much simpler + -- + -- flush_node_list(head.components) return head end - return start end function handlers.gsub_single(start,kind,lookupname,replacement) @@ -8232,8 +8993,12 @@ function handlers.gsub_single(start,kind,lookupname,replacement) return start, true end -local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname) -- chainname and chainlookupname optional - local value, choice, n = featurevalue or tfmdata.shared.features[kind], nil, #alternatives -- global value, brrr +local function get_alternative_glyph(start,alternatives,value) + -- needs checking: (global value, brrr) + local choice = nil + local n = #alternatives + local char = start.char + -- if value == "random" then local r = random(1,n) value, choice = format("random, choice %s",r), alternatives[r] @@ -8246,23 +9011,26 @@ local function alternative_glyph(start,alternatives,kind,chainname,chainlookupna 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] + local defaultalt = otf.defaultnodealternate + if defaultalt == "first" then + value, choice = format("no %s variants, taking %s",value,n), alternatives[n] + elseif defaultalt == "last" then + value, choice = format("no %s variants, taking %s",value,1), alternatives[1] + else + value, choice = format("no %s variants, ignoring",value), false + end elseif value == 0 then - value, choice = format("choice %s (no change)",value), start.char + value, choice = format("choice %s (no change)",value), 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 + return choice end -local function multiple_glyphs(start,multiple) +local function multiple_glyphs(start,multiple) -- marks ? local nofmultiples = #multiple if nofmultiples > 0 then start.char = multiple[1] @@ -8290,11 +9058,18 @@ local function multiple_glyphs(start,multiple) 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) + local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue + local choice = get_alternative_glyph(start,alternative,value) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(char),gref(choice),choice) + end + start.char = choice + else + if trace_alternatives then + logwarning("%s: no variant %s for %s",pref(kind,lookupname),tostring(value),gref(char)) + end end - start.char = choice return start, true end @@ -8457,18 +9232,14 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) local markchar = start.char if marks[markchar] then local base = start.prev -- [glyph] [optional marks] [start=mark] - local index = 1 if base and base.id == glyph_code 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_code and base.subtype<256 and base.font == currentfont then basechar = base.char - if marks[basechar] then - index = index + 1 - else + if not marks[basechar] then break end else @@ -8479,8 +9250,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) end end end - local i = has_attribute(start,markdone) - if i then index = i end + local index = has_attribute(start,ligacomp) local baseanchors = descriptions[basechar] if baseanchors then baseanchors = baseanchors.anchors @@ -8494,7 +9264,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if ma then ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.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) @@ -8525,46 +9295,55 @@ end function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) local markchar = start.char if marks[markchar] then ---~ local alreadydone = markonce and has_attribute(start,markmark) ---~ if not alreadydone then - local base = start.prev -- [glyph] [basemark] [start=mark] - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go - local basechar = base.char - local baseanchors = descriptions[basechar] + local base = start.prev -- [glyph] [basemark] [start=mark] + -- while base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp) do + -- base = base.prev -- KE: prevents mkmk for marks on different components of a ligature + -- end + local slc = has_attribute(start,ligacomp) + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = has_attribute(base,ligacomp) + if blc and blc ~= slc then + base = base.prev + else + break + end + end + end + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors if baseanchors then - baseanchors = baseanchors.anchors + baseanchors = baseanchors['basemark'] 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 = setmark(start,base,tfmdata.parameters.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 + 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 = setmark(start,base,tfmdata.parameters.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 - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - 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 - else -- if trace_bugs then - -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end - elseif trace_bugs then - logwarning("%s: prev node is no mark",pref(kind,lookupname)) + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end ---~ elseif trace_marks and trace_details then ---~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) ---~ 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 @@ -8718,33 +9497,6 @@ end local logwarning = report_subchain -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) - logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return start, false -end - --- handled later: --- --- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- end - -function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,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,lookuphash,currentlookup,chainlookupname,n) - logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) - return start, false -end - --- handled later: --- --- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- end - local function logprocess(...) if trace_steps then registermessage(...) @@ -8762,6 +9514,11 @@ function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,lookuphash return start, false end +function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) + logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return start, false +end + -- The reversesub is a special case, which is why we need to store the replacements -- in a bit weird way. There is no lookup and the replacement comes from the lookup -- itself. It is meant mostly for dealing with Urdu. @@ -8793,17 +9550,29 @@ example, the following is valid:</p> <p>Therefore we we don't really do the replacement here already unless we have the single lookup case. The efficiency of the replacements can be improved by deleting -as less as needed but that would also mke the code even more messy.</p> +as less as needed but that would also make the code even more messy.</p> --ldx]]-- -local function delete_till_stop(start,stop,ignoremarks) - if start ~= stop then - -- todo keep marks +local function delete_till_stop(start,stop,ignoremarks) -- keeps start + local n = 1 + if start == stop then + -- done + elseif ignoremarks then + repeat -- start x x m x x stop => start m + local next = start.next + if not marks[next.char] then + delete_node(start,next) + end + n = n + 1 + until next == stop + else -- start x x x stop => start repeat local next = start.next delete_node(start,next) + n = n + 1 until next == stop end + return n end --[[ldx-- @@ -8813,10 +9582,6 @@ match.</p> function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -- todo: marks ? ---~ if not chainindex then ---~ delete_till_stop(start,stop) -- ,currentlookup.flags[1] ---~ stop = start ---~ end local current = start local subtables = currentlookup.subtables if #subtables > 1 then @@ -8862,7 +9627,7 @@ the match.</p> --ldx]]-- function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - delete_till_stop(start,stop) + delete_till_stop(start,stop) -- we could pass ignoremarks as #3 .. local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] @@ -8887,36 +9652,54 @@ function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,looku return start, false end +-- function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) +-- logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) +-- return start, false +-- end + +chainmores.gsub_multiple = chainprocs.gsub_multiple + --[[ldx-- <p>Here we replace start by new glyph. First we delete the rest of the match.</p> --ldx]]-- +-- char_1 mark_1 -> char_x mark_1 (ignore marks) +-- char_1 mark_1 -> char_x + +-- to be checked: do we always have just one glyph? +-- we can also have alternates for marks +-- marks come last anyway +-- are there cases where we need to delete the mark + function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - -- todo: marks ? - delete_till_stop(start,stop) local current = start local subtables = currentlookup.subtables + local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue while current do - if current.id == glyph_code then + if current.id == glyph_code then -- is this check needed? local currentchar = current.char local lookupname = subtables[1] local alternatives = lookuphash[lookupname] if not alternatives then if trace_bugs then - logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) + logwarning("%s: no alternative hit",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) + if alternatives then + local choice = get_alternative_glyph(current,alternatives,value) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),gref(char),gref(choice),choice) + end + start.char = choice + else + if trace_alternatives then + logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),tostring(value),gref(char)) + end end + elseif trace_bugs then + logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) end end return start, true @@ -8929,6 +9712,13 @@ function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,look return start, false end +-- function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) +-- logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) +-- return start, false +-- end + +chainmores.gsub_alternate = chainprocs.gsub_alternate + --[[ldx-- <p>When we replace ligatures we use a helper that handles the marks. I might change this function (move code inline and handle the marks by a separate function). We @@ -9079,18 +9869,14 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, end if markanchors then local base = start.prev -- [glyph] [optional marks] [start=mark] - local index = 1 if base and base.id == glyph_code 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_code and base.subtype<256 and base.font == currentfont then basechar = base.char - if marks[basechar] then - index = index + 1 - else + if not marks[basechar] then break end else @@ -9102,8 +9888,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, end end -- todo: like marks a ligatures hash - local i = has_attribute(start,markdone) - if i then index = i end + local index = has_attribute(start,ligacomp) local baseanchors = descriptions[basechar].anchors if baseanchors then local baseanchors = baseanchors['baselig'] @@ -9115,7 +9900,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if ma then ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.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) @@ -9156,6 +9941,20 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,look end if markanchors then local base = start.prev -- [glyph] [basemark] [start=mark] + -- while (base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp)) do + -- base = base.prev -- KE: prevents mkmk for marks on different components of a ligature + -- end + local slc = has_attribute(start,ligacomp) + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = has_attribute(base,ligacomp) + if blc and blc ~= slc then + base = base.prev + else + break + end + end + end if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go local basechar = base.char local baseanchors = descriptions[basechar].anchors @@ -9372,36 +10171,40 @@ end local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,lookuphash) -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] - local flags, done = sequence.flags, false - local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] - local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) - local markclass = sequence.markclass -- todo, first we need a proper test - local skipped = false + local flags = sequence.flags + local done = false + local skipmark = flags[1] + local skipligature = flags[2] + local skipbase = flags[3] + local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + local markclass = sequence.markclass -- todo, first we need a proper test + 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 + local match = true + local current = start + local last = start + local ck = contexts[k] + local seq = ck[3] + local s = #seq -- f..l = mid string if s == 1 then -- never happens match = current.id == glyph_code and current.subtype<256 and current.font == currentfont and seq[1][current.char] else - -- todo: better space check (maybe check for glue) + -- maybe we need a better space check (maybe check for glue or category or combination) + -- we cannot optimize for n=2 because there can be disc nodes local f, l = ck[4], ck[5] -- current match - if f == 1 and f == l then + if f == 1 and f == l then -- current only -- already a hit - match = true - else + -- match = true + else -- before/current/after | before/current | current/after -- no need to test first hit (to be optimized) - local n = f + 1 - last = last.next - -- we cannot optimize for n=2 because there can be disc nodes - -- if not someskip and n == l then - -- -- n=2 and no skips then faster loop - -- match = last and last.id == glyph_code and last.subtype<256 and last.font == currentfont and seq[n][last.char] - -- else + if f == l then -- new, else last out of sync (f is > 1) + -- match = true + else + local n = f + 1 + last = last.next while n <= l do if last then local id = last.id @@ -9423,24 +10226,29 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence end n = n + 1 else - match = false break + match = false + break end else - match = false break + match = false + break end else - match = false break + match = false + break end - elseif id == disc_code then -- what to do with kerns? + elseif id == disc_code then last = last.next else - match = false break + match = false + break end else - match = false break + match = false + break end end - -- end + end end -- before if match and f > 1 then @@ -9464,26 +10272,31 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence elseif seq[n][char] then n = n -1 else - match = false break + match = false + break end else - match = false break + match = false + break end else - match = false break + match = false + break end elseif id == disc_code then -- skip 'm elseif seq[n][32] then n = n -1 else - match = false break + match = false + break end prev = prev.prev elseif seq[n][32] then -- somehat special, as zapfino can have many preceding spaces n = n -1 else - match = false break + match = false + break end end elseif f == 2 then @@ -9491,7 +10304,8 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else for n=f-1,1 do if not seq[n][32] then - match = false break + match = false + break end end end @@ -9519,26 +10333,31 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence elseif seq[n][char] then n = n + 1 else - match = false break + match = false + break end else - match = false break + match = false + break end else - match = false break + match = false + break end elseif id == disc_code then -- skip 'm elseif seq[n][32] then -- brrr n = n + 1 else - match = false break + match = false + break end current = current.next elseif seq[n][32] then n = n + 1 else - match = false break + match = false + break end end elseif s-l == 1 then @@ -9546,7 +10365,8 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else for n=l+1,s do if not seq[n][32] then - match = false break + match = false + break end end end @@ -9572,11 +10392,15 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence 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,lookuphash,chainlookup,chainlookupname,nil,sequence) - else - logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + if chainlookup then + local cp = chainprocs[chainlookup.type] + if cp then + start, done = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + else + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + else -- shouldn't happen + logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) end else local i = 1 @@ -9598,8 +10422,8 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence end end local chainlookupname = chainlookups[i] - local chainlookup = lookuptable[chainlookupname] - local cp = chainmores[chainlookup.type] + local chainlookup = lookuptable[chainlookupname] -- can be false (n matches, <n replacement) + local cp = chainlookup and chainmores[chainlookup.type] if cp then local ok, n start, ok, n = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) @@ -9612,7 +10436,8 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence i = i + 1 end else - logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + -- is valid + -- logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup and chainlookup.type or "?") i = i + 1 end start = start.next @@ -9722,7 +10547,7 @@ local function initialize(sequence,script,language,enabled) if valid then local languages = scripts[script] or scripts[wildcard] if languages and (languages[language] or languages[wildcard]) then - return { valid, special_attributes[kind] or false, sequence.chain or 0, kind } + return { valid, special_attributes[kind] or false, sequence.chain or 0, kind, sequence } end end end @@ -9730,7 +10555,7 @@ local function initialize(sequence,script,language,enabled) return false end -function otf.dataset(ftfmdata,sequences,font) -- generic variant, overloaded in context +function otf.dataset(tfmdata,sequences,font) -- generic variant, overloaded in context local shared = tfmdata.shared local properties = tfmdata.properties local language = properties.language or "dflt" @@ -9759,6 +10584,22 @@ function otf.dataset(ftfmdata,sequences,font) -- generic variant, overloaded in return rl end +-- elseif id == glue_code then +-- if p[5] then -- chain +-- local pc = pp[32] +-- if pc then +-- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) +-- if ok then +-- done = true +-- end +-- if start then start = start.next end +-- else +-- start = start.next +-- end +-- else +-- start = start.next +-- end + local function featuresprocessor(head,font,attr) local lookuphash = lookuphashes[font] -- we can also check sequences here @@ -9788,67 +10629,147 @@ local function featuresprocessor(head,font,attr) local done = false local datasets = otf.dataset(tfmdata,sequences,font,attr) + local dirstack = { } -- could move outside function + + -- We could work on sub start-stop ranges instead but I wonder if there is that + -- much speed gain (experiments showed that it made not much sense) and we need + -- to keep track of directions anyway. Also at some point I want to play with + -- font interactions and then we do need the full sweeps. + for s=1,#sequences do - local pardir, txtdir, success = 0, { }, false -- we could reuse txtdir and use a top pointer - local sequence = sequences[s] - local dataset = datasets[s] -- cache - featurevalue = dataset and dataset[1] -- todo: pass to function instead of using a global - if featurevalue then - local attribute, chain, typ, subtables = dataset[2], dataset[3], sequence.type, sequence.subtables ---~ print(typ) ---~ table.print(table.keys(sequence)) - if chain < 0 then - -- this is a limited case, no special treatments like 'init' etc - local handler = handlers[typ] - -- we need to get rid of this slide ! - local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo - while start do - local id = start.id - if id == glyph_code 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 = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - start, success = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if success then - break + local dataset = datasets[s] + if dataset then + featurevalue = dataset[1] -- todo: pass to function instead of using a global + if featurevalue then + local sequence = sequences[s] -- also dataset[5] + local rlparmode = 0 + local topstack = 0 + local success = false + local attribute = dataset[2] + local chain = dataset[3] -- sequence.chain or 0 + local typ = sequence.type + local subtables = sequence.subtables + if chain < 0 then + -- this is a limited case, no special treatments like 'init' etc + local handler = handlers[typ] + -- we need to get rid of this slide! probably no longer needed in latest luatex + local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo + while start do + local id = start.id + if id == glyph_code 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 = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + start, success = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if success then + break + end end + else + report_missing_cache(typ,lookupname) end - else - report_missing_cache(typ,lookupname) end + if start then start = start.prev end + else + start = start.prev 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 start = head -- local ? - rlmode = 0 -- to be checked ? - if ns == 1 then - local lookupname = subtables[1] - local lookupcache = lookuphash[lookupname] - if not lookupcache then - report_missing_cache(typ,lookupname) + else + local handler = handlers[typ] + local ns = #subtables + local start = head -- local ? + rlmode = 0 -- to be checked ? + if ns == 1 then -- happens often + local lookupname = subtables[1] + local lookupcache = lookuphash[lookupname] + if not lookupcache then -- also check for empty cache + report_missing_cache(typ,lookupname) + else + while start do + local id = start.id + if id == glyph_code 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 + -- sequence kan weg + local ok + start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,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_code then -- will be function + local subtype = start.subtype + if subtype == dir_code then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + topstack = topstack + 1 + dirstack[topstack] = dir + elseif dir == "-TRT" or dir == "-TLT" then + topstack = topstack - 1 + end + local newdir = dirstack[topstack] + if newdir == "+TRT" then + rlmode = -1 + elseif newdir == "+TLT" then + rlmode = 1 + else + rlmode = rlparmode + end + if trace_directions then + report_process("directions after txtdir %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) + end + elseif subtype == localpar_code then + local dir = start.dir + if dir == "TRT" then + rlparmode = -1 + elseif dir == "TLT" then + rlparmode = 1 + else + rlparmode = 0 + end + rlmode = rlparmode + if trace_directions then + report_process("directions after pardir %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) + end + end + start = start.next + else + start = start.next + end + end + end else while start do local id = start.id @@ -9861,13 +10782,22 @@ local function featuresprocessor(head,font,attr) a = not attribute or has_attribute(start,state,attribute) end if a then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- sequence kan weg - local ok - start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - success = true + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + success = true + break + end + end + else + report_missing_cache(typ,lookupname) end end if start then start = start.next end @@ -9877,54 +10807,39 @@ local function featuresprocessor(head,font,attr) else start = start.next end - -- elseif id == glue_code then - -- if p[5] then -- chain - -- local pc = pp[32] - -- if pc then - -- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) - -- if ok then - -- done = true - -- end - -- if start then start = start.next end - -- else - -- start = start.next - -- end - -- else - -- start = start.next - -- end elseif id == whatsit_code then local subtype = start.subtype if subtype == dir_code then local dir = start.dir if dir == "+TRT" or dir == "+TLT" then - insert(txtdir,dir) + topstack = topstack + 1 + dirstack[topstack] = dir elseif dir == "-TRT" or dir == "-TLT" then - remove(txtdir) + topstack = topstack - 1 end - local d = txtdir[#txtdir] - if d == "+TRT" then + local newdir = dirstack[topstack] + if newdir == "+TRT" then rlmode = -1 - elseif d == "+TLT" then + elseif newdir == "+TLT" then rlmode = 1 else - rlmode = pardir + rlmode = rlparmode end if trace_directions then - report_process("directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + report_process("directions after txtdir %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) end elseif subtype == localpar_code then local dir = start.dir if dir == "TRT" then - pardir = -1 + rlparmode = -1 elseif dir == "TLT" then - pardir = 1 + rlparmode = 1 else - pardir = 0 + rlparmode = 0 end - rlmode = pardir - --~ txtdir = { } + rlmode = rlparmode if trace_directions then - report_process("directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + report_process("directions after pardir %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) end end start = start.next @@ -9933,105 +10848,13 @@ local function featuresprocessor(head,font,attr) end end end - else - while start do - local id = start.id - if id == glyph_code 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 = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- we could move all code inline but that makes things even more unreadable - local ok - start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,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 == glue_code then - -- if p[5] then -- chain - -- local pc = pp[32] - -- if pc then - -- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) - -- if ok then - -- done = true - -- end - -- if start then start = start.next end - -- else - -- start = start.next - -- end - -- else - -- start = start.next - -- end - elseif id == whatsit_code then - local subtype = start.subtype - if subtype == dir_code 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 - report_process("directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) - end - elseif subtype == localpar_code then - local dir = start.dir - if dir == "TRT" then - pardir = -1 - elseif dir == "TLT" then - pardir = 1 - else - pardir = 0 - end - rlmode = pardir - --~ txtdir = { } - if trace_directions then - report_process("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) + if success then + done = true + end + if trace_steps then -- ? + registerstep(head) + end end end end @@ -10110,55 +10933,59 @@ local function prepare_lookups(tfmdata) local description = descriptions[unicode] - local lookups = description.slookups - if lookups then - for lookupname, lookupdata in next, lookups do - action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + if description then + + local lookups = description.slookups + if lookups then + for lookupname, lookupdata in next, lookups do + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + end end - end - local lookups = description.mlookups - if lookups then - for lookupname, lookuplist in next, lookups do - local lookuptype = lookuptypes[lookupname] - for l=1,#lookuplist do - local lookupdata = lookuplist[l] - action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + local lookups = description.mlookups + if lookups then + for lookupname, lookuplist in next, lookups do + local lookuptype = lookuptypes[lookupname] + for l=1,#lookuplist do + local lookupdata = lookuplist[l] + action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + end end end - end - local list = description.kerns - if list then - for lookup, krn in next, list do -- ref to glyph, saves lookup - local target = lookuphash[lookup] - if target then - target[unicode] = krn - else - lookuphash[lookup] = { [unicode] = krn } + local list = description.kerns + if list then + for lookup, krn in next, list do -- ref to glyph, saves lookup + local target = lookuphash[lookup] + if target then + target[unicode] = krn + else + lookuphash[lookup] = { [unicode] = krn } + end end end - end - local list = description.anchors - if list then - for typ, anchors in next, list do -- types - if typ == "mark" or typ == "cexit" then -- or entry? - for name, anchor in next, anchors do - local lookups = anchor_to_lookup[name] - if lookups then - for lookup, _ in next, lookups do - local target = lookuphash[lookup] - if target then - target[unicode] = anchors - else - lookuphash[lookup] = { [unicode] = anchors } + local list = description.anchors + if list then + for typ, anchors in next, list do -- types + if typ == "mark" or typ == "cexit" then -- or entry? + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local target = lookuphash[lookup] + if target then + target[unicode] = anchors + else + lookuphash[lookup] = { [unicode] = anchors } + end end end end end end end + end end @@ -10340,7 +11167,7 @@ end --~ end local valid = { - coverage = { chainsub = true, chainpos = true }, + coverage = { chainsub = true, chainpos = true, contextsub = true }, reversecoverage = { reversesub = true }, glyphs = { chainsub = true, chainpos = true }, } @@ -11250,7 +12077,7 @@ registerotffeature { methods.latn = analyzers.setstate --- this info eventually will go into char-def adn we will have a state +-- this info eventually will go into char-def and we will have a state -- table for generic then local zwnj = 0x200C @@ -11280,6 +12107,12 @@ local isol_fina = { [0x076C] = true, [0x0771] = true, [0x0773] = true, [0x0774] = true, [0x0778] = true, [0x0779] = true, [0xFEF5] = true, [0xFEF7] = true, [0xFEF9] = true, [0xFEFB] = true, + + -- syriac + + [0x0710] = true, [0x0715] = true, [0x0716] = true, [0x0717] = true, + [0x0718] = true, [0x0719] = true, [0x0728] = true, [0x072A] = true, + [0x072C] = true, [0x071E] = true, } local isol_fina_medi_init = { @@ -11317,10 +12150,19 @@ local isol_fina_medi_init = { [0x0772] = true, [0x0775] = true, [0x0776] = true, [0x0777] = true, [0x077A] = true, [0x077B] = true, [0x077C] = true, [0x077D] = true, [0x077E] = true, [0x077F] = true, [zwj] = true, + + -- syriac + + [0x0712] = true, [0x0713] = true, [0x0714] = true, [0x071A] = true, + [0x071B] = true, [0x071C] = true, [0x071D] = true, [0x071F] = true, + [0x0720] = true, [0x0721] = true, [0x0722] = true, [0x0723] = true, + [0x0725] = true, [0x0726] = true, [0x0727] = true, [0x0729] = true, + [0x072B] = true, [0x0724] = true, [0x0706] = true, [0x0707] = true, } local arab_warned = { } + -- todo: gref local function warning(current,what) @@ -11438,6 +12280,8 @@ function methods.arab(head,font,attr) -- maybe make a special version with no tr return head, done end +methods.syrc = methods.arab + directives.register("otf.analyze.useunicodemarks",function(v) analyzers.useunicodemarks = v end) @@ -11605,7 +12449,7 @@ function definers.registersplit(symbol,action,verbosename) end end -function definers.makespecification(specification, lookup, name, sub, method, detail, size) +function definers.makespecification(specification,lookup,name,sub,method,detail,size) size = size or 655360 if trace_defining then report_defining("%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", @@ -11656,13 +12500,14 @@ end function resolvers.name(specification) local resolve = fonts.names.resolve if resolve then - local resolved, sub = fonts.names.resolve(specification.name,specification.sub) - specification.resolved, specification.sub = resolved, sub + local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions if resolved then + specification.resolved = resolved + specification.sub = sub local suffix = file.suffix(resolved) if fonts.formats[suffix] then specification.forced = suffix - specification.name = file.removesuffix(resolved) + specification.name = file.removesuffix(resolved) else specification.name = resolved end @@ -11675,10 +12520,12 @@ end function resolvers.spec(specification) local resolvespec = fonts.names.resolvespec if resolvespec then - specification.resolved, specification.sub = fonts.names.resolvespec(specification.name,specification.sub) - if specification.resolved then - specification.forced = file.extname(specification.resolved) - specification.name = file.removesuffix(specification.resolved) + local resolved, sub = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions + if resolved then + specification.resolved = resolved + specification.sub = sub + specification.forced = file.extname(resolved) + specification.name = file.removesuffix(resolved) end else resolvers.name(specification) @@ -11697,17 +12544,6 @@ function definers.resolve(specification) else specification.forced = specification.forced end - -- for the moment here (goodies set outside features) - local goodies = specification.goodies - if goodies and goodies ~= "" then - local normal = specification.features.normal - if not normal then - specification.features.normal = { goodies = goodies } - elseif not normal.goodies then - normal.goodies = goodies - end - end - -- specification.hash = lower(specification.name .. ' @ ' .. constructors.hashfeatures(specification)) if specification.sub and specification.sub ~= "" then specification.hash = specification.sub .. ' @ ' .. specification.hash @@ -11731,25 +12567,43 @@ features (esp in virtual fonts) so let's not do that now.</p> specification yet.</p> --ldx]]-- --- not in context, at least not now: --- +-- very experimental: + +function definers.applypostprocessors(tfmdata) + local postprocessors = tfmdata.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash = postprocessors[i](tfmdata) -- after scaling etc + if type(extrahash) == "string" and extrahash ~= "" then + -- e.g. a reencoding needs this + extrahash = gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end + -- function definers.applypostprocessors(tfmdata) --- local postprocessors = tfmdata.postprocessors --- if postprocessors then --- for i=1,#postprocessors do --- local extrahash = postprocessors[i](tfmdata) -- after scaling etc --- if type(extrahash) == "string" and extrahash ~= "" then --- -- e.g. a reencoding needs this --- extrahash = gsub(lower(extrahash),"[^a-z]","-") --- tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) --- end --- end --- end -- return tfmdata -- end -function definers.applypostprocessors(tfmdata) - return tfmdata +local function checkembedding(tfmdata) + local properties = tfmdata.properties + local embedding + if directive_embedall then + embedding = "full" + elseif properties and properties.filename and constructors.dontembed[properties.filename] then + embedding = "no" + else + embedding = "subset" + end + if properties then + properties.embedding = embedding + else + tfmdata.properties = { embedding = embedding } + end + tfmdata.embedding = embedding end function definers.loadfont(specification) @@ -11781,21 +12635,8 @@ function definers.loadfont(specification) end end if tfmdata then - local properties = tfmdata.properties - local embedding - if directive_embedall then - embedding = "full" - elseif properties.filename and constructors.dontembed[properties.filename] then - embedding = "no" - else - embedding = "subset" - end - if properties then - properties.embedding = embedding - else - tfmdata.properties = { embedding = embedding } - end tfmdata = definers.applypostprocessors(tfmdata) + checkembedding(tfmdata) -- todo: general postprocessor loadedfonts[hash] = tfmdata designsizes[specification.hash] = tfmdata.parameters.designsize end @@ -11881,14 +12722,25 @@ function definers.read(specification,size,id) -- id can be optional, name can al specification = definers.resolve(specification) local hash = constructors.hashinstance(specification) local tfmdata = definers.registered(hash) -- id - if not tfmdata then + if tfmdata then + if trace_defining then + report_defining("already hashed: %s",hash) + end + else tfmdata = definers.loadfont(specification) -- can be overloaded if tfmdata then ---~ constructors.checkvirtualid(tfmdata) -- interferes + if trace_defining then + report_defining("loaded and hashed: %s",hash) + end + --~ constructors.checkvirtualid(tfmdata) -- interferes tfmdata.properties.hash = hash if id then definers.register(tfmdata,id) end + else + if trace_defining then + report_defining("not loaded and hashed: %s",hash) + end end end lastdefined = tfmdata or id -- todo ! ! ! ! ! @@ -11898,7 +12750,7 @@ function definers.read(specification,size,id) -- id can be optional, name can al local properties = tfmdata.properties or { } local parameters = tfmdata.parameters or { } report_defining("using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - properties.type or "unknown", + properties.format or "unknown", id or "?", properties.name or "?", parameters.size or "default", @@ -12054,7 +12906,7 @@ local function initializeitlc(tfmdata,value) d.italic = it end end - tfmdata.properties.italic_correction = true + tfmdata.properties.hasitalics = true end end end @@ -12079,7 +12931,7 @@ local function initializeslant(tfmdata,value) elseif value < -1 then value = -1 end - tfmdata.parameters.slant_factor = value + tfmdata.parameters.slantfactor = value end otffeatures.register { @@ -12100,7 +12952,7 @@ local function initializeextend(tfmdata,value) elseif value < -10 then value = -10 end - tfmdata.parameters.extend_factor = value + tfmdata.parameters.extendfactor = value end otffeatures.register { diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-syn.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-syn.lua index 36a74d0f497..36a74d0f497 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-syn.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-syn.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts-tfm.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-tfm.lua index b9bb1bd0f28..b9bb1bd0f28 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts-tfm.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-tfm.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts.lua index 23d33f26b8f..f5045a4e32a 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts.lua @@ -7,9 +7,20 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { } -- The following code isolates the generic ConTeXt code from already --- defined or to be defined namespaces. - --- todo: all global namespaces in called modules will get local shortcuts +-- defined or to be defined namespaces. This is the reference loader +-- for plain, but the generic code is also used in luaotfload (which +-- is is a file meant for latex) and that is maintained by Khaled +-- Hosny. We do our best to keep the interface as clean as possible. +-- +-- The code base is rather stable now, especially if you stay away from +-- the non generic code. All relevant data is organized in tables within +-- the main table of a font instance. There are a few places where in +-- context other code is plugged in, but this does not affect the core +-- code. Users can (given that their macro package provides this option) +-- access the font data (characters, descriptions, properties, parameters, +-- etc) of this main table. +-- +-- Todo: all global namespaces in called modules will get local shortcuts. utf = unicode.utf8 @@ -122,10 +133,10 @@ else -- version 1.0 there will be an official api defined. loadmodule('l-string.lua') + loadmodule('l-table.lua') loadmodule('l-lpeg.lua') loadmodule('l-boolean.lua') loadmodule('l-math.lua') - loadmodule('l-table.lua') loadmodule('l-file.lua') loadmodule('l-io.lua') diff --git a/Master/texmf-dist/tex/generic/context/luatex-fonts.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts.tex index 8ccc9f3d566..a7c8bc2b8fd 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-fonts.tex +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts.tex @@ -4,7 +4,7 @@ %D title=\LUATEX\ Support Macros, %D subtitle=Generic \OPENTYPE\ Font Handler, %D author=Hans Hagen, -%D copyright=see context related readme files] +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %D \subject{Welcome} %D diff --git a/Master/texmf-dist/tex/generic/context/luatex-mplib.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua index c6628acb3b8..c6628acb3b8 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-mplib.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-mplib.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.tex index 69195864670..ef6dfff958b 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-mplib.tex +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.tex @@ -4,7 +4,7 @@ %D title=\LUATEX\ Support Macros, %D subtitle=\METAPOST\ to \PDF\ conversion, %D author=Taco Hoekwater \& Hans Hagen, -%D copyright=see context related readme files] +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %D This is the companion to the \LUA\ module \type {supp-mpl.lua}. Further %D embedding is up to others. A simple example of usage in plain \TEX\ is: diff --git a/Master/texmf-dist/tex/generic/context/luatex/luatex-plain.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-plain.tex new file mode 100644 index 00000000000..e47ad58ad9a --- /dev/null +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-plain.tex @@ -0,0 +1,25 @@ +%D \module +%D [ file=luatex-plain, +%D version=2009.12.01, +%D title=\LUATEX\ Macros, +%D subtitle=Plain Format, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] + +\input plain + +\directlua {tex.enableprimitives('', tex.extraprimitives())} + +\pdfoutput=1 + +\everyjob \expandafter {% + \the\everyjob + \input luatex-basics\relax + \input luatex-fonts\relax + \input luatex-mplib\relax +} + +\edef\fmtversion{\fmtversion+luatex} + +\dump diff --git a/Master/texmf-dist/tex/generic/context/luatex-preprocessor-test.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor-test.tex index 857b28f831b..857b28f831b 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-preprocessor-test.tex +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor-test.tex diff --git a/Master/texmf-dist/tex/generic/context/luatex-preprocessor.lua b/Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor.lua index 8faa0b47e69..8faa0b47e69 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-preprocessor.lua +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor.lua diff --git a/Master/texmf-dist/tex/generic/context/luatex-preprocessor.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor.tex index fe4872d6101..03b483f4185 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-preprocessor.tex +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-preprocessor.tex @@ -4,7 +4,7 @@ %D title=\LUATEX\ Support Macros, %D subtitle=Generic Preprocessor, %D author=Hans Hagen, -%D copyright=public domain] +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] \directlua{dofile(kpse.find_file('luatex-preprocessor.lua'))} diff --git a/Master/texmf-dist/tex/generic/context/luatex-test.tex b/Master/texmf-dist/tex/generic/context/luatex/luatex-test.tex index 1c3aeb1b689..3020b5594cc 100644 --- a/Master/texmf-dist/tex/generic/context/luatex-test.tex +++ b/Master/texmf-dist/tex/generic/context/luatex/luatex-test.tex @@ -5,13 +5,15 @@ %D subtitle=Simple Test File, %D author=Hans Hagen, %D date=\currentdate, -%D copyright=public domain] +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %D See \type {luatex-plain.tex} (or on my machine \type {luatex.tex} %D for how to make a format. \pdfoutput=1 +% \directlua{generic_context.caches.compilemethod = "both" } % none luac dump both + \font\testa=file:lmroman10-regular at 12pt \testa \input tufte \par \font\testb=file:lmroman12-regular:+liga; at 24pt \testb effe flink fietsen \par \font\testc=file:lmroman12-regular:mode=node;+liga; at 24pt \testc effe flink fietsen \par diff --git a/Master/texmf-dist/tex/generic/context/m-metapo.tex b/Master/texmf-dist/tex/generic/context/m-metapo.tex deleted file mode 100644 index 8680f69c6b2..00000000000 --- a/Master/texmf-dist/tex/generic/context/m-metapo.tex +++ /dev/null @@ -1,89 +0,0 @@ -%D \module -%D [ file=m-metapo, -%D version=1999.03.26, -%D title=\LATEX\ Modules, -%D subtitle=\METAPOST\ Inclusion, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ distribution and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -%D We quit when \CONTEXT\ is found and use some deep down -%D macro to trigger this: - -\ifx\undefined\dodoplaceexternalfigure \else \expandafter \endinput \fi - -%D \macros -%D {includeMPgraphics} -%D -%D This rather small \LATEX\ module is dedicated to David -%D Arnold. It takes care of including the fonts used in -%D \METAPOST\ graphics in the file. This hack is needed when -%D one uses another \DVI\ driver than \DVIPS. This module -%D falls back on the generic \CONTEXT\ support module: - -\ifx\undefined\includeMPfonts \input supp-mps.tex \relax \fi - -%D Instead of using \type {\includegraphics}, one should use its -%D little brother \type {\includeMPgraphics}. This macro takes -%D the same arguments. - -\def\includeMPgraphics#1#% - {\leavevmode\vbox\bgroup\hbox\bgroup - \def\includeMPgraphics##1% - {\includeMPfonts{##1}% - \includegraphics#1{##1}% - \egroup\egroup}% - \includeMPgraphics} - -%D An example of using this module is given below: -%D -%D \starttyping -%D \documentclass[10pt]{article} -%D -%D \usepackage{graphicx} -%D \usepackage{m-metapo} -%D -%D \begin{document} -%D \includeMPgraphics{somefile.1} -%D \includeMPgraphics[angle=90]{somefile.2} -%D \end{document} -%D \stoptyping -%D -%D This module needs \type {supp-mps} and \type {supp-mis}, -%D that both are present in the \CONTEXT\ path. -%D -%D Please do not forget to say \type {prologues:=1} at the -%D top of the metapost file! -%D -%D For non \LATEX\ (and \CONTEXT) users we provide an -%D alternative inclusion macro. This one has no optional -%D arguments. - -\ifx\includegraphics\undefined - - \ifx\undefined\dogetEPSboundingbox \input supp-eps.tex \relax \fi - - \def\includeMPgraphics#1% - {\hbox\bgroup - \includeMPfonts{#1}% - \dogetEPSboundingbox{#1}{\dimen2}{\dimen4}{\dimen6}{\dimen8}% - \vbox to \dimen8 - {\forgetall - \hsize\dimen6 - \vfill - \hbox to \hsize - {\special - {PSfile="#1"\space - llx=\EPSllx\space - lly=\EPSlly\space - urx=\EPSurx\space - ury=\EPSury\space}}}% - \egroup} - -\fi - -\endinput diff --git a/Master/texmf-dist/tex/generic/context/mptopdf.tex b/Master/texmf-dist/tex/generic/context/mptopdf/mptopdf.tex index a0ac34a42b3..3efe573924a 100644 --- a/Master/texmf-dist/tex/generic/context/mptopdf.tex +++ b/Master/texmf-dist/tex/generic/context/mptopdf/mptopdf.tex @@ -5,7 +5,7 @@ %D subtitle=conversion to \PDF, %D author=Hans Hagen, %D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for diff --git a/Master/texmf-dist/tex/generic/context/m-ch-de.tex b/Master/texmf-dist/tex/generic/context/ppchtex/m-ch-de.tex index 467cdc67044..467cdc67044 100644 --- a/Master/texmf-dist/tex/generic/context/m-ch-de.tex +++ b/Master/texmf-dist/tex/generic/context/ppchtex/m-ch-de.tex diff --git a/Master/texmf-dist/tex/generic/context/m-ch-en.tex b/Master/texmf-dist/tex/generic/context/ppchtex/m-ch-en.tex index 6bd4353199b..6bd4353199b 100644 --- a/Master/texmf-dist/tex/generic/context/m-ch-en.tex +++ b/Master/texmf-dist/tex/generic/context/ppchtex/m-ch-en.tex diff --git a/Master/texmf-dist/tex/generic/context/m-ch-nl.tex b/Master/texmf-dist/tex/generic/context/ppchtex/m-ch-nl.tex index c9d77733ac5..c9d77733ac5 100644 --- a/Master/texmf-dist/tex/generic/context/m-ch-nl.tex +++ b/Master/texmf-dist/tex/generic/context/ppchtex/m-ch-nl.tex diff --git a/Master/texmf-dist/tex/generic/context/ppchtex.noc b/Master/texmf-dist/tex/generic/context/ppchtex/ppchtex.noc index 99d073255cb..99d073255cb 100644 --- a/Master/texmf-dist/tex/generic/context/ppchtex.noc +++ b/Master/texmf-dist/tex/generic/context/ppchtex/ppchtex.noc |