diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/x-mathml.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/x-mathml.lua | 547 |
1 files changed, 516 insertions, 31 deletions
diff --git a/Master/texmf-dist/tex/context/base/x-mathml.lua b/Master/texmf-dist/tex/context/base/x-mathml.lua index fa5e617e607..aa2e4e716f3 100644 --- a/Master/texmf-dist/tex/context/base/x-mathml.lua +++ b/Master/texmf-dist/tex/context/base/x-mathml.lua @@ -6,29 +6,60 @@ if not modules then modules = { } end modules ['x-mathml'] = { license = "see context related readme files" } +local utf = unicode.utf8 + +lxml = lxml or { } lxml.mml = lxml.mml or { } local texsprint = tex.sprint local format = string.format +local lower = string.lower +local utfchar = utf.char +local utffind = utf.find local xmlsprint = xml.sprint local xmlcprint = xml.cprint +local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues + -- an alternative is to remap to private codes, where we can have -- different properties .. to be done; this will move and become -- generic +-- todo: handle opening/closing mo's here ... presentation mml is such a mess ... + +local doublebar = utfchar(0x2016) + local n_replacements = { --- [" "] = utf.char(0x2002), -- "&textspace;" -> tricky, no &; in mkiv +-- [" "] = utfchar(0x2002), -- "&textspace;" -> tricky, no &; in mkiv ["."] = "{.}", [","] = "{,}", [" "] = "", } -local o_replacements = { +local l_replacements = { -- in main table + ["|"] = "\\mmlleftdelimiter\\vert", + ["{"] = "\\mmlleftdelimiter\\lbrace", + ["("] = "\\mmlleftdelimiter(", + ["["] = "\\mmlleftdelimiter[", + ["<"] = "\\mmlleftdelimiter<", + [doublebar] = "\\mmlleftdelimiter\\Vert", +} +local r_replacements = { -- in main table + ["|"] = "\\mmlrightdelimiter\\vert", + ["}"] = "\\mmlrightdelimiter\\rbrace", + [")"] = "\\mmlrightdelimiter)", + ["]"] = "\\mmlrightdelimiter]", + [">"] = "\\mmlrightdelimiter>", + [doublebar] = "\\mmlrightdelimiter\\Vert", +} + +local o_replacements = { -- in main table ["@l"] = "\\mmlleftdelimiter.", ["@r"] = "\\mmlrightdelimiter.", ["{"] = "\\mmlleftdelimiter\\lbrace", ["}"] = "\\mmlrightdelimiter\\rbrace", + ["|"] = "\\mmlleftorrightdelimiter\\vert", + [doublebar] = "\\mmlleftorrightdelimiter\\Vert", ["("] = "\\mmlleftdelimiter(", [")"] = "\\mmlrightdelimiter)", ["["] = "\\mmlleftdelimiter[", @@ -43,6 +74,12 @@ local o_replacements = { ["_"] = "\\mmlchar{95}{}", -- so we need the {} ["~"] = "\\mmlchar{126}", [" "] = "", + ["°"] = "^\\circ", -- hack + + [utfchar(0xF103C)] = "\\mmlleftdelimiter<", + [utfchar(0xF1026)] = "\\mmlchar{38}", + [utfchar(0xF103E)] = "\\mmlleftdelimiter>", + } local i_replacements = { @@ -103,36 +140,369 @@ local i_replacements = { ["as"] = "{\\mr as}", } +local csymbols = { + arith1 = { + lcm = "lcm", + big_lcm = "lcm", + gcd = "gcd", + big_gcd = "big_gcd", + plus = "plus", + unary_minus = "minus", + minus = "minus", + times = "times", + divide = "divide", + power = "power", + abs = "abs", + root = "root", + sum = "sum", + product ="product", + }, + fns = { + domain = "domain", + range = "codomain", + image = "image", + identity = "ident", +--~ left_inverse = "", +--~ right_inverse = "", + inverse = "inverse", + left_compose = "compose", + lambda = "labmda", + }, + linalg1 = { + vectorproduct = "vectorproduct", + scalarproduct = "scalarproduct", + outerproduct = "outerproduct", + transpose = "transpose", + determinant = "determinant", + vector_selector = "selector", +--~ matrix_selector = "matrix_selector", + }, + logic1 = { + equivalent = "equivalent", + ["not"] = "not", + ["and"] = "and", +--~ big_and = "", + ["xor"] = "xor", +--~ big_xor = "", + ["or"] = "or", +--~ big-or= "", + implies = "implies", + ["true"] = "true", + ["false"] = "false", + }, + nums1 = { +--~ based_integer = "based_integer" + rational = "rational", + inifinity = "infinity", + e = "expenonentiale", + i ="imaginaryi", + pi = "pi", + gamma = "gamma", + NaN, "NaN", + }, + relation1 = { + eq = "eq", + lt = "lt", + gt = "gt", + neq = "neq", + leq = "leq", + geq = "geq", + approx = "approx", + }, + set1 = { + cartesian_product = "cartesianproduct", + empty_set = "emptyset", + map = "map", + size = "card", +--~ suchthat = "suchthat", + set = "set", + intersect = "intersect", +--~ big_intersect = "", + union = "union", +--~ big_union = "", + setdiff = "setdiff", + subset = "subset", + ["in"] = "in", + notin = "notin", + prsubset = "prsubset", + notsubset = "notsubset", + notprsubset = "notprsubset", + }, + veccalc1 = { + divergence = "divergence", + grad = "grad", + curl = "curl", + Laplacian = "laplacian", + }, + calculus1 = { + diff = "diff", +--~ nthdiff = "", + partialdiff = "partialdiff", + int = "int", +--~ defint = "defint", + }, + integer1 = { + factorof = "factorof", + factorial = "factorial", + quotient = "quotient", + remainder = "rem", + }, + linalg2 = { + vector = "vector", + matrix = "matrix", + matrixrow = "matrixrow", + }, + mathmkeys = { +--~ equiv = "", +--~ contentequiv = "", +--~ contentequiv_strict = "", + }, + rounding1 = { + ceiling = "ceiling", + floor = "floor", +--~ trunc = "trunc", +--~ round = "round", + }, + setname1 = { + P = "primes", + N = "naturalnumbers", + Z = "integers", + rationals = "rationals", + R = "reals", + complexes = "complexes", + }, + complex1 = { +--~ complex_cartesian = "complex_cartesian", -- ci ? + real = "real", + imaginary = "imaginary", +--~ complex_polar = "complex_polar", -- ci ? + argument = "arg", + conjugate = "conjugate", + }, + interval1 = { -- not an apply +--~ "integer_interval" = "integer_interval", + interval = "interval", + interval_oo = { tag = "interval", closure = "open" }, + interval_cc = { tag = "interval", closure = "closed" }, + interval_oc = { tag = "interval", closure = "open-closed" }, + interval_co = { tag = "interval", closure = "closed-open" }, + }, + linalg3 = { +--~ vector = "vector.column", +--~ matrixcolumn = "matrixcolumn", +--~ matrix = "matrix.column", + }, + minmax1 = { + min = "min", +--~ big_min = "", + max = "max", +--~ big_max = "", + }, + piece1 = { + piecewise = "piecewise", + piece = "piece", + otherwise = "otherwise", + }, + error1 = { +--~ unhandled_symbol = "", +--~ unexpected_symbol = "", +--~ unsupported_CD = "", + }, + limit1 = { +--~ limit = "limit", +--~ both_sides = "both_sides", +--~ above = "above", +--~ below = "below", +--~ null = "null", + tendsto = "tendsto", + }, + list1 = { +--~ map = "", +--~ suchthat = "", +--~ list = "list", + }, + multiset1 = { + size = { tag = "card", type="multiset" }, + cartesian_product = { tag = "cartesianproduct", type="multiset" }, + empty_set = { tag = "emptyset", type="multiset" }, +--~ multi_set = { tag = "multiset", type="multiset" }, + intersect = { tag = "intersect", type="multiset" }, +--~ big_intersect = "", + union = { tag = "union", type="multiset" }, +--~ big_union = "", + setdiff = { tag = "setdiff", type="multiset" }, + subset = { tag = "subset", type="multiset" }, + ["in"] = { tag = "in", type="multiset" }, + notin = { tag = "notin", type="multiset" }, + prsubset = { tag = "prsubset", type="multiset" }, + notsubset = { tag = "notsubset", type="multiset" }, + notprsubset = { tag = "notprsubset", type="multiset" }, + }, + quant1 = { + forall = "forall", + exists = "exists", + }, + s_dist = { +--~ mean = "mean.dist", +--~ sdev = "sdev.dist", +--~ variance = "variance.dist", +--~ moment = "moment.dist", + }, + s_data = { + mean = "mean", + sdev = "sdev", + variance = "vriance", + mode = "mode", + median = "median", + moment = "moment", + }, + transc1 = { + log = "log", + ln = "ln", + exp = "exp", + sin = "sin", + cos = "cos", + tan = "tan", + sec = "sec", + csc = "csc", + cot = "cot", + sinh = "sinh", + cosh = "cosh", + tanh = "tanh", + sech = "sech", + csch = "cscs", + coth = "coth", + arcsin = "arcsin", + arccos = "arccos", + arctan = "arctan", + arcsec = "arcsec", + arcscs = "arccsc", + arccot = "arccot", + arcsinh = "arcsinh", + arccosh = "arccosh", + arctanh = "arstanh", + arcsech = "arcsech", + arccsch = "arccsch", + arccoth = "arccoth", + }, +} + +function xml.functions.remapmmlcsymbol(r,d,k) + local dk = d[k] + local at = dk.at + local cd = at.cd + if cd then + cd = csymbols[cd] + if cd then + local tx = dk.dt[1] + if tx and tx ~= "" then + local tg = cd[tx] + if tg then + at.cd = nil + at.cdbase = nil + dk.dt = { } + if type(tg) == "table" then + for k, v in pairs(tg) do + if k == "tag" then + dk.tg = v + else + at[k] = v + end + end + else + dk.tg = tg + end + end + end + end + end +end + +function xml.functions.remapmmlbind(r,d,k) + d[k].tg = "apply" +end + +function xml.functions.remapopenmath(r,d,k) + local dk = d[k] + local tg = dk.tg + if tg == "OMOBJ" then + dk.tg = "math" + elseif tg == "OMA" then + dk.tg = "apply" + elseif tg == "OMB" then + dk.tg = "apply" + elseif tg == "OMS" then + -- xml.functions.remapmmlcsymbol(r,d,k) + local at = dk.at + dk.tg = "csymbol" + dk.dt = { at.name or "unknown" } + at.name = nil + elseif tg == "OMV" then + local at = dk.at + dk.tg = "ci" + dk.dt = { at.name or "unknown" } + at.name = nil + elseif tg == "OMI" then + dk.tg = "ci" + end + dk.rn = "mml" +end + function lxml.mml.checked_operator(str) - texsprint(tex.ctxcatcodes,(str:gsub(".",o_replacements))) + texsprint(tex.ctxcatcodes,(utf.gsub(str,".",o_replacements))) +end + +function lxml.mml.stripped(str) + tex.sprint(tex.ctxcatcodes,str:strip()) end function lxml.mml.mn(id,pattern) + -- maybe at some point we need to interpret the number, but + -- currently we assume an upright font local str = xml.content(lxml.id(id),pattern) or "" + -- str = str:gsub("^%s*(.-)%s*$","%1") + str = str:gsub("(%s+)",utfchar(0x205F)) -- medspace e.g.: twenty one (nbsp is not seen) texsprint(tex.ctxcatcodes,(str:gsub(".",n_replacements))) end + +function characters.remapentity(chr,slot) + texsprint(format("{\\catcode%s=13\\xdef%s{\\string%s}}",slot,utfchar(slot),chr)) +end + function lxml.mml.mo(id,pattern) local str = xml.content(lxml.id(id),pattern) or "" - tex.sprint(tex.ctxcatcodes,(str:gsub(".",o_replacements))) + texsprint(tex.ctxcatcodes,(utf.gsub(str,".",o_replacements))) end + function lxml.mml.mi(id,pattern) local str = xml.content(lxml.id(id),pattern) or "" - str = str:gsub("^%s*(.*)%s*$","%1") + -- str = str:gsub("^%s*(.-)%s*$","%1") local rep = i_replacements[str] if rep then - tex.sprint(tex.ctxcatcodes,rep) + texsprint(tex.ctxcatcodes,rep) else - tex.sprint(tex.ctxcatcodes,(str:gsub(".",i_replacements))) + texsprint(tex.ctxcatcodes,(str:gsub(".",i_replacements))) end end +function table.keys_as_string(t) + local k = { } + for k,_ in pairs(t) do + k[#k+1] = k + end + return concat(k,"") +end + +--~ local leftdelimiters = "[" .. table.keys_as_string(l_replacements) .. "]" +--~ local rightdelimiters = "[" .. table.keys_as_string(r_replacements) .. "]" + function lxml.mml.mfenced(id,pattern) -- multiple separators id = lxml.id(id) local left, right, separators = id.at.open or "(", id.at.close or ")", id.at.separators or "," - local l, r = left:find("[%(%{%<%[]"), right:find("[%)%}%>%]]") + local l, r = l_replacements[left], r_replacements[right] texsprint(tex.ctxcatcodes,"\\enabledelimiter") if l then - texsprint(tex.ctxcatcodes,o_replacements[left]) + texsprint(tex.ctxcatcodes,l_replacements[left] or o_replacements[left] or "") else texsprint(tex.ctxcatcodes,o_replacements["@l"]) texsprint(tex.ctxcatcodes,left) @@ -145,7 +515,7 @@ function lxml.mml.mfenced(id,pattern) -- multiple separators lxml.all(id,pattern) else local t = { } - for s in utf.gmatch(separators,"([^%s])") do + for s in utf.gmatch(separators,"[^%s]") do t[#t+1] = s end for i=1,n do @@ -154,6 +524,8 @@ function lxml.mml.mfenced(id,pattern) -- multiple separators local m = t[i] or t[#t] or "" if m == "|" then m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter" + elseif m == doublebar then + m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter" elseif m == "{" then m = "\\{" elseif m == "}" then @@ -165,7 +537,7 @@ function lxml.mml.mfenced(id,pattern) -- multiple separators end texsprint(tex.ctxcatcodes,"\\enabledelimiter") if r then - texsprint(tex.ctxcatcodes,o_replacements[right]) + texsprint(tex.ctxcatcodes,r_replacements[right] or o_replacements[right] or "") else texsprint(tex.ctxcatcodes,right) texsprint(tex.ctxcatcodes,o_replacements["@r"]) @@ -242,8 +614,106 @@ local frametypes = { dashed = "on", } -function lxml.mml.mtable(root) +-- crazy element ... should be a proper structure instead of such a mess + +function lxml.mml.mcolumn(root) + root = lxml.id(root) + local matrix, numbers = { }, 0 + local function collect(m,dk) + local tag = dk.tg + if tag == "mi" or tag == "mn" or tag == "mo" or tag == "mtext" then + local str = xml.content(dk) + for s in utfcharacters(str) do -- utf.gmatch(str,".") btw, the gmatch was bugged + m[#m+1] = { tag, s } + end + if tag == "mn" then + local n = utf.len(str) + if n > numbers then + numbers = n + end + end + elseif tag == "mspace" or tag == "mline" then + local str = dk.at.spacing or "" + for s in utfcharacters(str) do -- utf.gmatch(str,".") btw, the gmatch was bugged + m[#m+1] = { tag, s } + end + elseif tag == "mline" then + m[#m+1] = { tag, dk } + end + end + for r, d, k in xml.elements(root,"/*") do + local m = { } + matrix[#matrix+1] = m + local dk = d[k] + if dk.tg == "mrow" then + -- only one level + for r, d, k in xml.elements(dk,"/*") do + collect(m,d[k]) + end + else + collect(m,dk) + end + end + tex.sprint(tex.ctxcatcodes,"\\halign\\bgroup\\hss$#$&$#$\\cr") + for i=1,#matrix do + local m = matrix[i] + local mline = true + for j=1,#m do + if m[j][1] ~= "mline" then + mline = false + break + end + end + if mline then + tex.sprint(tex.ctxcatcodes,"\\noalign{\\obeydepth\\nointerlineskip}") + end + for j=1,#m do + local mm = m[j] + local tag, chr = mm[1], mm[2] + if tag == "mline" then +--~ local n, p = true, true +--~ for c=1,#matrix do +--~ local mc = matrix[c][j] +--~ if mc then +--~ mc = mc[2] +--~ if type(mc) ~= "string" then +--~ n, p = false, false +--~ break +--~ elseif mc:find("^[%d ]$") then -- rangecheck is faster +--~ -- digit +--~ elseif not mc:find("^[%.%,]$") then -- rangecheck is faster +--~ -- punctuation +--~ else +--~ n = false +--~ break +--~ end +--~ end +--~ end +--~ if n then +--~ chr = "\\mmlmcolumndigitrule" +--~ elseif p then +--~ chr = "\\mmlmcolumnpunctuationrule" +--~ else +--~ chr = "\\mmlmcolumnsymbolrule" -- should be widest char +--~ end + chr = "\\hrulefill" + elseif tag == "mspace" then + chr = "\\mmlmcolumndigitspace" -- utfchar(0x2007) + end + if j == numbers + 1 then + tex.sprint(tex.ctxcatcodes,"&") + end + local nchr = n_replacements[chr] + tex.sprint(tex.ctxcatcodes,nchr or chr) + end + tex.sprint(tex.ctxcatcodes,"\\crcr") + end + tex.sprint(tex.ctxcatcodes,"\\egroup") +end + +local spacesplitter = lpeg.Ct(lpeg.splitat(" ")) +function lxml.mml.mtable(root) root = lxml.id(root) -- todo: align, rowspacing, columnspacing, rowlines, columnlines @@ -252,10 +722,13 @@ function lxml.mml.mtable(root) local rowalign = at.rowalign local columnalign = at.columnalign local frame = at.frame - local rowaligns = rowalign and rowalign :split(" ") -- we have a faster one - local columnaligns = columnalign and columnalign:split(" ") -- we have a faster one - local frames = frame and frame :split(" ") -- we have a faster one - local framespacing = at.framespacing or ".5ex" + local rowaligns = rowalign and spacesplitter:match(rowalign) + local columnaligns = columnalign and spacesplitter:match(columnalign) + local frames = frame and spacesplitter:match(frame) + local framespacing = at.framespacing or "0pt" + local framespacing = at.framespacing or "-\\ruledlinewidth" -- make this an option + +--~ function texsprint(a,b) print(b) end texsprint(tex.ctxcatcodes, format("\\bTABLE[frame=%s,offset=%s]",frametypes[frame or "none"] or "off",framespacing)) for r, d, k in xml.elements(root,"/(mml:mtr|mml:mlabeledtr)") do @@ -266,23 +739,28 @@ function lxml.mml.mtable(root) local rfr = at.frame or (frames and frames [#frames]) local rra = at.rowalign or (rowaligns and rowaligns [#rowaligns]) local rca = at.columnalign or (columnaligns and columnaligns[#columnaligns]) + local ignorelabel = dk.tg == "mlabeledtr" for rr, dd, kk in xml.elements(dk,"/mml:mtd") do col = col + 1 - local dk = dd[kk] - local at = dk.at - local rowspan, columnspan = at.rowspan or 1, at.columnspan or 1 - local cra = rowalignments [at.rowalign or (rowaligns and rowaligns [col]) or rra or "center"] or "lohi" - local cca = columnalignments[at.columnalign or (columnaligns and columnaligns[col]) or rca or "center"] or "middle" - local cfr = frametypes [at.frame or (frames and frames [col]) or rfr or "none" ] or "off" - texsprint(tex.ctxcatcodes,format("\\bTD[align={%s,%s},frame=%s,nx=%s,ny=%s]$\\ignorespaces",cra,cca,cfr,columnspan,rowspan)) - xmlcprint(dk) - texsprint(tex.ctxcatcodes,"\\removeunwantedspaces$\\eTD") -- $ - end - if dk.tg == "mlabeledtr" then - texsprint(tex.ctxcatcodes,"\\bTD") - xmlcprint(xml.first(dk,"/!mml:mtd")) - texsprint(tex.ctxcatcodes,"\\eTD") + if ignorelabel and col == 1 then + -- get rid of label, should happen at the document level + else + local dk = dd[kk] + local at = dk.at + local rowspan, columnspan = at.rowspan or 1, at.columnspan or 1 + local cra = rowalignments [at.rowalign or (rowaligns and rowaligns [col]) or rra or "center"] or "lohi" + local cca = columnalignments[at.columnalign or (columnaligns and columnaligns[col]) or rca or "center"] or "middle" + local cfr = frametypes [at.frame or (frames and frames [col]) or rfr or "none" ] or "off" + texsprint(tex.ctxcatcodes,format("\\bTD[align={%s,%s},frame=%s,nx=%s,ny=%s]$\\ignorespaces",cra,cca,cfr,columnspan,rowspan)) + xmlcprint(dk) + texsprint(tex.ctxcatcodes,"\\removeunwantedspaces$\\eTD") -- $ + end end +--~ if dk.tg == "mlabeledtr" then +--~ texsprint(tex.ctxcatcodes,"\\bTD") +--~ xmlcprint(xml.first(dk,"/!mml:mtd")) +--~ texsprint(tex.ctxcatcodes,"\\eTD") +--~ end texsprint(tex.ctxcatcodes,"\\eTR") end texsprint(tex.ctxcatcodes, "\\eTABLE") @@ -291,10 +769,17 @@ end function lxml.mml.csymbol(root) root = lxml.id(root) local encoding = root.at.encoding or "" - local hash = url.hashed((root.at.definitionUrl or ""):lower()) + local hash = url.hashed(lower(root.at.definitionUrl or "")) local full = hash.original or "" local base = hash.path or "" local text = string.strip(xml.content(root) or "") texsprint(tex.ctxcatcodes,format("\\mmlapplycsymbol{%s}{%s}{%s}{%s}",full,base,encoding,text)) end +function lxml.mml.menclosepattern(root) + root = lxml.id(root) + local a = root.at.notation + if a and a ~= "" then + texsprint("mml:enclose:"..a:gsub(" +",",mml:enclose:")) + end +end |