diff --git a/.editorconfig b/.editorconfig index 801f12d78..2e2ad0d30 100644 --- a/.editorconfig +++ b/.editorconfig @@ -24,7 +24,7 @@ max_line_length = 120 # StyLua keys collapse_simple_statement = never -space_after_functions = definitions +space_after_function_names = definitions # EmmyLuaCodeStyle keys continuation_indent = 6 diff --git a/README.md b/README.md index 1ead3b47c..1abea4988 100644 --- a/README.md +++ b/README.md @@ -70,15 +70,18 @@ If you install LuaRocks for use with SILE via `pacman`, use the `lua51-*` varian #### Fedora -A [COPR][copr] repository is available for Fedora users with packages of SILE and all the necessary dependencies. -Fedora 38 and later are supported. -There is work in progress to get the packages added to the official Fedora repository. +Fedora Linux has SILE in their official repositories. To install run: ```console -$ dnf copr enable jonny/SILE $ dnf install sile ``` +Not all the fonts are not installed by default, to install them: + +```console +$ dnf install sil-gentium-plus-fonts alerque-libertinus-fonts hack-fonts +``` + #### OpenSUSE OpenSUSE has official packages ready to install the usual way: diff --git a/configure.ac b/configure.ac index 5b7b9362e..74e42b2da 100644 --- a/configure.ac +++ b/configure.ac @@ -146,7 +146,7 @@ AM_COND_IF([SYSTEM_LIBTEXPDF], [AC_MSG_FAILURE([--with-system-libtexpdf was given, but test for libtexpdf failed])])], [AC_CONFIG_SUBDIRS([libtexpdf])]) -PKG_CHECK_MODULES(ICU, icu-uc icu-io, [ +PKG_CHECK_MODULES(ICU, icu-uc icu-i18n icu-io, [ with_icu=yes ],[ AC_CHECK_TOOL(ICU_CONFIG, icu-config, no) diff --git a/flake.lock b/flake.lock index 3ce425e0c..a159ca01e 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "type": "github" }, "original": { @@ -57,11 +57,11 @@ "libtexpdf-src": { "flake": false, "locked": { - "lastModified": 1662109873, - "narHash": "sha256-yThIb+D/m1xeJZESojRd3u4OugbWl7f2s8oJohspwZs=", + "lastModified": 1725528813, + "narHash": "sha256-pWpDdb7oy3XhrDKTb7sR0WleHBnGgjM8KZi6Q8WpsN4=", "owner": "sile-typesetter", "repo": "libtexpdf", - "rev": "736a5e7530c13582ea704a061a358d0caa774916", + "rev": "1891bee5e0b73165e4a259f910d3ea3fe1df0b42", "type": "github" }, "original": { @@ -72,11 +72,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724114134, - "narHash": "sha256-V/w5MIQy4jTG/L7/V/AL2BF5gSEWCfxHVDQdzLBCV18=", + "lastModified": 1729850857, + "narHash": "sha256-WvLXzNNnnw+qpFOmgaM3JUlNEH+T4s22b5i2oyyCpXE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f02fa2f654c7bcc45f0e815c29d093da7f1245b4", + "rev": "41dea55321e5a999b17033296ac05fe8a8b5a257", "type": "github" }, "original": { diff --git a/packages/math/base-elements.lua b/packages/math/base-elements.lua index 90d907b08..290d47c0a 100644 --- a/packages/math/base-elements.lua +++ b/packages/math/base-elements.lua @@ -908,8 +908,8 @@ end function elements.text:_init (kind, attributes, script, text) elements.terminal._init(self) - if not (kind == "number" or kind == "identifier" or kind == "operator") then - SU.error("Unknown text node kind '" .. kind .. "'; should be one of: number, identifier, operator") + if not (kind == "number" or kind == "identifier" or kind == "operator" or kind == "string") then + SU.error("Unknown text node kind '" .. kind .. "'; should be one of: number, identifier, operator, string") end self.kind = kind self.script = script diff --git a/packages/math/texlike.lua b/packages/math/texlike.lua index 5d1073fef..393616ffa 100644 --- a/packages/math/texlike.lua +++ b/packages/math/texlike.lua @@ -42,8 +42,11 @@ local mathGrammar = function (_ENV) return ret end local group = P"{" * V"mathlist" * (P"}" + E("`}` expected")) + -- Simple amsmath-like \text command (no embedded math) + local textgroup = P"{" * C((1-P"}")^1) * (P"}" + E("`}` expected")) local element_no_infix = V"def" + + V"text" + -- Important: before command V"command" + group + V"argument" + @@ -115,6 +118,11 @@ local mathGrammar = function (_ENV) sub = element_no_infix * _ * P"_" * _ * element_no_infix atom = natural + C(utf8code - S"\\{}%^_&") + (P"\\{" + P"\\}") / function (s) return string.sub(s, -1) end + text = ( + P"\\text" * + Cg(parameters, "options") * + textgroup + ) command = ( P"\\" * Cg(ctrl_sequence_name, "command") * @@ -252,6 +260,23 @@ local compileToStr = function (argEnv, mathlist) end end +local function isBigOperator (tree) + if tree.command ~= "mo" then + return false + end + -- Case \mo[atom=big]{ops} + -- E.g. \mo[atom=big]{lim} + if tree.options and tree.options.atom == "big" then + return true + end + -- Case \mo{ops} where ops is registered as big operator (unicode-symbols) + -- E.g. \mo{∑) or \sum + if tree[1] and symbolDefaults[tree[1]] and symbolDefaults[tree[1]].atom == atomType.bigOperator then + return true + end + return false +end + local function compileToMathML_aux (_, arg_env, tree) if type(tree) == "string" then return tree @@ -328,21 +353,13 @@ local function compileToMathML_aux (_, arg_env, tree) tree.options = {} -- Translate TeX-like sub/superscripts to `munderover` or `msubsup`, -- depending on whether the base is a big operator - elseif tree.id == "sup" and tree[1].command == "mo" and tree[1].atom == atomType.bigOperator then + elseif tree.id == "sup" and isBigOperator(tree[1]) then tree.command = "mover" - elseif tree.id == "sub" and tree[1].command == "mo" and symbolDefaults[tree[1][1]].atom == atomType.bigOperator then + elseif tree.id == "sub" and isBigOperator(tree[1]) then tree.command = "munder" - elseif - tree.id == "subsup" - and tree[1].command == "mo" - and symbolDefaults[tree[1][1]].atom == atomType.bigOperator - then + elseif tree.id == "subsup" and isBigOperator(tree[1]) then tree.command = "munderover" - elseif - tree.id == "supsub" - and tree[1].command == "mo" - and symbolDefaults[tree[1][1]].atom == atomType.bigOperator - then + elseif tree.id == "supsub" and isBigOperator(tree[1]) then tree.command = "munderover" local tmp = tree[2] tree[2] = tree[3] @@ -365,6 +382,8 @@ local function compileToMathML_aux (_, arg_env, tree) return compileToMathML_aux(nil, compiledArgs, tree[1]) end) return nil + elseif tree.id == "text" then + tree.command = "mtext" elseif tree.id == "command" and commands[tree.command] then local argTypes = commands[tree.command][1] local cmdFun = commands[tree.command][2] @@ -480,6 +499,21 @@ compileToMathML( \def{bi}{\mi[mathvariant=bold-italic]{#1}} \def{dsi}{\mi[mathvariant=double-struck]{#1}} + \def{lim}{\mo[atom=big]{lim}} + + % From amsmath: + \def{to}{\mo[atom=bin]{→}} + \def{gcd}{\mo[atom=big]{gcd}} + \def{sup}{\mo[atom=big]{sup}} + \def{inf}{\mo[atom=big]{inf}} + \def{max}{\mo[atom=big]{max}} + \def{min}{\mo[atom=big]{min}} + % Those use U+202F NARROW NO-BREAK SPACE in their names + \def{limsup}{\mo[atom=big]{lim sup}} + \def{liminf}{\mo[atom=big]{lim inf}} + \def{projlim}{\mo[atom=big]{proj lim}} + \def{injlim}{\mo[atom=big]{inj lim}} + % Standard spaces gleaned from plain TeX \def{thinspace}{\mspace[width=thin]} \def{negthinspace}{\mspace[width=-thin]} diff --git a/packages/math/typesetter.lua b/packages/math/typesetter.lua index b54be4080..b2b7adcee 100644 --- a/packages/math/typesetter.lua +++ b/packages/math/typesetter.lua @@ -45,7 +45,7 @@ function ConvertMathML (_, content) local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant) local text = content[1] if type(text) ~= "string" then - SU.error("mi command contains " .. text .. ", which is not text") + SU.error("mi command contains content which is not text") end script = script or (luautf8.len(text) == 1 and b.scriptType.italic or b.scriptType.upright) return b.text("identifier", {}, script, text) @@ -67,7 +67,7 @@ function ConvertMathML (_, content) end end if type(text) ~= "string" then - SU.error("mo command contains " .. text .. ", which is not text") + SU.error("mo command contains content which is not text") end return b.text("operator", attributes, script, text) elseif content.command == "mn" then @@ -75,7 +75,7 @@ function ConvertMathML (_, content) or b.scriptType.upright local text = content[1] if type(text) ~= "string" then - SU.error("mn command contains " .. text .. ", which is not text") + SU.error("mn command contains content which is not text") end if string.sub(text, 1, 1) == "-" then text = "−" .. string.sub(text, 2) @@ -136,6 +136,19 @@ function ConvertMathML (_, content) return b.mtr(convertChildren(content)) elseif content.command == "mtd" then return b.stackbox("H", convertChildren(content)) + elseif content.command == "mtext" or content.command == "ms" then + if #content > 1 then + SU.error("Wrong number of children in " .. content.command .. ": " .. #content) + end + local text = content[1] or "" -- empty mtext is allowed, and found in examples... + if type(text) ~= "string" then + SU.error(content.command .. " command contains content which is not text") + end + -- MathML Core 3.2.1.1 Layout of has some wording about forced line breaks + -- and soft wrap opportunities: ignored here. + -- There's also some explanations about CSS, italic correction etc. which we ignore too. + text = text:gsub("[\n\r]", " ") + return b.text("string", {}, b.scriptType.upright, text:gsub("%s+", " ")) else SU.error("Unknown math command " .. content.command) end diff --git a/packages/math/unicode-symbols.lua b/packages/math/unicode-symbols.lua index 830a718c6..42b784bb3 100644 --- a/packages/math/unicode-symbols.lua +++ b/packages/math/unicode-symbols.lua @@ -2567,10 +2567,12 @@ symbols.alpha = "α" symbols.beta = "β" symbols.gamma = "γ" symbols.delta = "δ" -symbols.epsilon = "ε" +symbols.epsilon = "ϵ" +symbols.varepsilon = "ε" symbols.zeta = "ζ" symbols.eta = "η" symbols.theta = "θ" +symbols.vartheta = "ϑ" symbols.iota = "ι" symbols.kappa = "κ" symbols.lambda = "λ" @@ -2579,11 +2581,15 @@ symbols.nu = "ν" symbols.xi = "ξ" symbols.omicron = "ο" symbols.pi = "π" +symbols.varpi = "ϖ" symbols.rho = "ρ" +symbols.varrho = "ϱ" symbols.sigma = "σ" +symbols.varsigma = "ς" symbols.tau = "τ" symbols.upsilon = "υ" -symbols.phi = "φ" +symbols.phi = "ϕ" +symbols.varphi = "φ" symbols.chi = "χ" symbols.psi = "ψ" symbols.omega = "ω" diff --git a/src/cli.rs b/src/cli.rs index f4baa1590..fb766e0d3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -91,9 +91,9 @@ pub struct Cli { /// Set or override document class options. /// /// Can be used to change default options or override the ones specified in a document. - /// For example setting `--options papersize=letter` would override both the default `papersize` of A4 and any specific one set in the document’s options. + /// For example setting `--option papersize=letter` would override both the default `papersize` of A4 and any specific one set in the document’s options. /// May be specified more than once. - #[clap(short = 'O', long)] + #[clap(short = 'O', long, alias = "options")] pub option: Option>, /// Include the contents of a SIL, XML, or other resource file before the input document content. diff --git a/src/lib.rs b/src/lib.rs index dc7e48fb4..77af6a24e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,15 @@ pub fn run( has_input_filename = true; } if let Some(options) = options { - sile_input.set("options", options)?; + // TODO: when mlua v0.10 merges, adapt this like the uses parsing to avoid chunking + for option in options.iter() { + let option = lua.create_string(option)?; + lua.load(chunk! { + local parameter = SILE.parserBits.parameters:match($option); + SILE.input.options = pl.tablex.merge(SILE.input.options, parameter, true) + }) + .eval::<()>()?; + } } if let Some(modules) = uses { // let parser_bits: LuaTable = sile.get("parserBits")?; diff --git a/tests/math-bigops.xml b/tests/math-bigops.xml index 15c8cae65..ca08c6735 100644 --- a/tests/math-bigops.xml +++ b/tests/math-bigops.xml @@ -225,7 +225,7 @@ Integrals, display (large font), MathML: Integrals, text, TeX: \oiint_S \mi[mathvariant=bold]{E} \cdot - \mi[mathvariant=bold]{dS} = \frac{1}{\epsilon_0} + \mi[mathvariant=bold]{dS} = \frac{1}{\varepsilon_0} \iiint_V \rho \mi[mathvariant=normal]{dV} = \int_0^x {f(t)\mo{d}t} @@ -233,7 +233,7 @@ Integrals, text, TeX: Integrals, display, TeX: \oiint_S \mi[mathvariant=bold]{E} \cdot - \mi[mathvariant=bold]{dS} = \frac{1}{\epsilon_0} + \mi[mathvariant=bold]{dS} = \frac{1}{\varepsilon_0} \iiint_V \rho \mi[mathvariant=normal]{dV} = \int_0^x {f(t)\mo{d}t}