Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Math — somehow (general discussion of approaches and status) #220

Closed
6 of 22 tasks
akavel opened this issue Nov 26, 2015 · 68 comments
Closed
6 of 22 tasks

Support Math — somehow (general discussion of approaches and status) #220

akavel opened this issue Nov 26, 2015 · 68 comments
Labels
Milestone

Comments

@akavel
Copy link
Contributor

akavel commented Nov 26, 2015

I'm aware MathJax is listed on the wiki as "up-for-grabs", but I just wanted to start by asking: have you maybe tried pushing some stuff from the MathJax's unpacked/ subdir through some js-to-lua translator (or other)?

I'm on Windows, so personally, before I maybe start trying to contribute, I have to first manage to compile the stuff (yes, I've seen the issue tracking this)...

(By the way, I got here by accidentally watching the 2015 FOSDEM video of yours; very entertaining and interesting :) although I've apparently already +1'ed your project at some point, only this video made me aware of many things about it (both feature demos, architecture, and how it relates and improves over *TeX) and thus more interested.)


edit: A few years later, after some prototyping and experiments, I'm expanding this initial post with more detailed current status of the project. The path I've eventually picked to try and implement this is based on ASCIIMath, not MathJax.

Current TODO tasks

Roughly in priority/painfulness order. I'd be especially grateful if someone could help with the ones marked "HELP NEEDED", I don't know how to proceed with those unfortunately :(. Jumping in to help with any task is enthusiastically encouraged!

  • Translate ASCIIMathML.js from JS to Lua (done and tested)
  • Translate SVGMath from Python to Lua (done with help of https://github.com/akavel/pylua based on https://github.com/kevinw/pylua)
  • Write SVG parser and renderer for SILE supporting SVGs generated by SVGMath (done)
  • Integrate the tools into a pipeline callable from SILE with an \asciimath directive (done)
  • The <text> nodes in SVGs generated by svgmath have text-anchor="middle", so their horizontal position should be tweaked, but I couldn't yet find out how to do that properly (moving them by -shape[1].width/2 didn't work for me for now, it broke things more than fixed). This would be important fix to the current buggy layout. This should be verified by rendering some correct SVG (e.g. https://github.com/akavel/svgmath/blob/master/testdata/test13.svg) through the final part of the pipeline, that is, \mathsvg. (done)
  • Make the SILE bounding box have correct size (how?), matching the content inside.
    • 🆘 [HELP NEEDED] 🆘 how to force SILE to draw a debugging rectangle around the current bounding box?
  • Match the sizes of fonts used by MathML->SVG translation to the font size used in the current paragraph in SILE
  • Use TTF font paths from SILE instead of hard-coded paths in svgmath/config.lua
  • Write a README in akavel/silemath repo, with:
  • Dynamically calculate font metrics for MathML->SVG translation, based on info from SILE, instead of current hard-coded parameters in svgmath/config.lua
  • Make it possible to choose the font(s) used by MathML->SVG translation
  • Fix bugs in rendering of some expressions; e.g. some ASCIIMath samples currently crash later stages of the pipeline (IIRC the MathML->SVG translation).
  • Fix any remaining bugs in SVG rendering step. This should be verified by rendering some correct SVG (e.g. https://github.com/akavel/svgmath/blob/master/testdata/test13.svg) through the final part of the pipeline, that is, \mathsvg.
  • Fix layouting bugs in svgmath
    • Note: should fix \mathsvg first, as mentioned in the task above
    • There are a lot of testcases in https://github.com/akavel/svgmath/tree/master/testdata. The *.out.svg files were generated by Lua, and the *.svg files by Python. (1) Fix the Lua code so that the *.out.svg files match the *.svg files. (2) Fix the few files which crashed the original Python code too (e.g. test20.mml?).
    • consult https://github.com/acivux/svgmath, maybe they fixed some of the bugs present in the original Python code
  • Integrate into SILE core — consult with @simoncozens
  • [LATER] Write some smarter parser on SILE side, so that:
    • users don't have to escape each { and } and \ in math expressions
    • maybe "common" LaTeX syntax like $ ...math... $ or \[ ...math... \] could be used?
  • 🆘 [HELP NEEDED] 🆘 simplify the renderGlyph function — currently it looks super ugly and probably very non-idiomatic SILE code, but I have no idea if/how it could be improved
  • [OPTIONAL] Migrate above tasks to issues in the akavel/silemath repo.
  • [LATER] Add support for UnicodeMath, which seems to be an interesting new development, inspired by ASCIIMath, apparently supported by both MS Office/Word and LaTeX (TODO: or is it just a name collision??), and thus apparently used by some people already. (See also some more or less related plugin for SublimeText.)

Current example result

@alerque alerque added the enhancement Software improvement or feature request label Aug 12, 2016
This was referenced Jan 20, 2017
@akavel
Copy link
Contributor Author

akavel commented Apr 15, 2017

Notes from the first failed attempt: I tried translating KaTeX with castl. The translation "succeeded" (using KaTeX v0.7.1 from git and castl 1.2.4), with commands roughly as below:

$ cd katex
$ git checkout v0.7.1
$ for f in katex.js src/*.js; do castl $f -o --debug --node; done

However, trying to run the following Lua script afterwards with Lua 5.2:

local katex = assert(loadfile 'katex.js.lua')()

failed with a Lua error: [...] too many C levels (limit is 200) [...], which seems to mean there's too deep nesting in the code.

(Reported on castl project as PaulBernier/castl#18. TODO: try some next translator, maybe colony.)

@akavel
Copy link
Contributor Author

akavel commented Apr 15, 2017

After some more looking around, I want to try my luck with ASCIIMath. It seems small enough, that even in case js2lua converters won't help, a manual port seems feasible. It does not implement Full Parity With TeX™; but as we all know too well I think, TeX is Turing-complete, and full parity is not necessarily even desirable. Anyway, even if I succeed, in Future™ someone can always just ditch the ASCIIMath and/or incrementally replace it with something More Powerful™ or whatever.

Now, with that said, there's one thing I now realized I'm not really clear on. Namely, what would be the best format to output from a math module for SILE to consume? ASCIIMath I think produces MathML; KaTeX claims to emit HTML+CSS; MathJaX is I believe super-duper-dynamic-adaptive-autodetect and can produce MathML, SVG, possibly images, and I don't even know what else. What would SILE like best? Imagine we're free of constraints; would it be PDF? SVG? Lua rendering code? glyphs + coords + scales? PostScript? glues penalties and some other weird stuff like this? (not even sure if it's there for math). On the other hand: is there/are there some simple format(s) (hopefully, vector) that SILE could reasonably accept for a reasonable first prototype/PoC?

As a side note for future: in case of failure, I think one more direction to research would be troff's eqn processor.

@suhr
Copy link

suhr commented Apr 16, 2017

I want to try my luck with ASCIIMath.

It doesn't have \cfact though, not even talking about commutative diagrams.

Anyway, good luck. It is hard to live without even basic math.

@alerque
Copy link
Member

alerque commented Apr 16, 2017

PDF would seem to make the most sense. That way the output can be directly embeded without much chance for scale or other interpretive errors.

An SVG route would be an entirely different pipeline and introduce some other pain points, but if there was a Math→SVG processor we might be able to bolt that on entirely outside of Lua.

@akavel
Copy link
Contributor Author

akavel commented Apr 16, 2017

@suhr Did you ever try groff's eqn? Do you think it's currently more powerful or less, compared to ASCIIMath?

If PDF sounds best, then I think eqn sounds like more bang for the buck for a first shot. With various caveats, first among them GPL I suppose (or alternatively GPL-incompatible Lucene license for Plan9's troff+eqn; not sure about PDF support there too). Or do you know by chance of someone already trying to integrate eqn with SILE and stumbling upon some problems?

EDIT: Hmm; it seems groff emits PDF by means of a sub-tool named "gropdf", which seems to be written in Perl. I'm not really convinced requiring Perl is a good match for SILE. On the other hand, for the time being it could be treated as a "plugin" to SILE, and as such not part of it, so Perl could be acceptable then.

Still, now it's not clear to me how would one approach "merging" PDF into SILE as input. It seems to me that PDF describes whole pages (or am I not right?). But equations are usually just small fragments on a page. Can SILE cut fragments of PDFs? Do you maybe know of some already preexisting mechanisms for something like that, of which I'm not aware yet? @alerque Do you know if there's maybe a PDF parser or something in SILE?

EDIT 2: @alerque Hm, but there seems to be already some machinery for SVG in SILE, no?

@akavel
Copy link
Contributor Author

akavel commented Apr 17, 2017

Prototype translation of ASCIIMathML.js to Lua is done and tested. I used the very cool castl translator. Also, given that it's not my first port, I must shout out, that comprehensive end-to-end tests do really boost porting speed awesomely!

TODO: test if svgmath will work reasonably well to translate the resulting MathML to SVG. It seems quite a small and readable MIT codebase, so should be feasible to hand-port to Lua if it's working well. I imagine it should be possible to migrate the SVG emitter to a PDF-or-whatever emitter more fitting for SILE later, should the PoC prove successful.

@alerque
Copy link
Member

alerque commented Apr 17, 2017

@akavel SILE already has a way to embed PDFs inside PDFs. Use the \img[src=file.pdf] function passing it a PDF as the input image. This is easy and reliable because the PDF doesn't have to be edited in any way, it can basically just be wrapped up and packaged inside the final output PDF with a location where it should be included.

Yes there is also rudimentary SVG support, but this isn't really up to snuff yet and involves being able to render whatever is in the SVG and convert it to a post script representation that can be embedded in the PDF. This is nowhere near as reliable as just nesting PDF content. It works alright for some things, but doesn't understand a lot of SVG instructions yet.

@akavel
Copy link
Contributor Author

akavel commented Apr 17, 2017

@alerque Do you know if it's possible to create a PDF with "tiny paper size", so that it would have size of exactly the bounding box of an equation (i.e. 2cm x 1cm, or something like that)? Or does \img[src=...] crop the contents to extract only the "black" contents, ignoring paper size?

@alerque
Copy link
Member

alerque commented Apr 17, 2017

Embeding a PDF as an image does nothing in the way of cropping or finding contents, it just uses the bounding box and inserts that at the requested overall width, hence scaling the source's paper size to the embeded size.

Obviously if you're trying to bold on some other render you might end up with an awkward mating like this, but ideally of course a native Lua parser would transform the formula to native drawables that could be passed to the output engine (which at this point is basically always the PDF output engine but keep in mind that isn't supported to be a hard coded limitation).

@akavel
Copy link
Contributor Author

akavel commented Apr 17, 2017

Ok, did a quick test with groff and indeed this direction seems to (kinda) work already? Main initial caveats:

  • groff's "document-class" counterpart is obviously unaccustomed to tiny paper sizes, pushing the equation to second page if I reduce "page size" to 1in x 1in, making it not show up in SILE. Learning more groff/troff (or finding someone with knowledge) would be needed to adjust/build an appropriate "document class".
  • how to get information about the baseline in the original PDF?
  • edit: haven't really tested anything truly equation-y in the .troff file... done, see next comment;
  • how to automatically adjust the bounding box of the equation, so that it's cropped properly and without spurious padding?

For anyone interested, here comes a quick & rough transcript of my session. Note I'm using Nix package manager:

$ nix-channel --list
nixos http://nixos.org/channels/nixos-17.03

$ nix-shell -p sile groff

[nix-shell:~]$ cat try3.troff    # NOTE: encoded in UTF-8, as expected for `-k` below IIUC
.PRINTSTYLE TYPESET
.PAGE 1i 2i .05i .05i .05i .05i
.FAMILY T \" Times Roman
.START
.EQ
∇²ϕ = 0
.EN

[nix-shell:~]$ cat eqn-pdf.sil
\begin[class=book,papersize=129mm x 198mm]{document}
\script[src=packages/image]
...
Foo bar \img[src=try3.pdf] baz.
...
\end{document}

[nix-shell:~]$ pdfmom -e -k try3.troff > try3.pdf
[mom]: Your chosen bottom margin for running text is too close to the footer margin.
       No footers or bottom-of-page page numbers will be printed.
       Please reset B_MARGIN or FOOTER_MARGIN to allow enough space.
       If no footers or bottom-of-page page numbers are required,
       invoke .FOOTER_MARGIN 0 before .START
troff: environment stack underflow

[nix-shell:~]$ sile eqn-pdf.sil
This is SILE 0.9.4
</vm-share/eqn-pdf.sil>

! Font 'Gentium' not available, falling back to 'DejaVu Sans'
[1] [2] 

screenshot

@akavel
Copy link
Contributor Author

akavel commented Apr 17, 2017

For the record, a "more advanced" groff equation worked as well:

sum from i=0 to {inf} x sup i { sqrt {alpha + beta} } over {sin (x)}

screenshot

@alerque
Copy link
Member

alerque commented Apr 17, 2017

how to get information about the baseline in the original PDF?

What do you mean by "original" here? The baseline in the SILE PDF should be straight forward enough. The trick is going to be leaning where the baseline of an equation PDF is in the event of an inline equation such as the ones in your examples. Once you do learn that you can offset the image in SILE using \lower[height=Y]{}. Combined with the right leading mechanism (fit-glyph?) this should allow lines to move around to accommodate formulas.

@akavel
Copy link
Contributor Author

akavel commented Apr 17, 2017

(@alerque Yes, I meant exactly what you mean by "learning where the baseline of an equation PDF is".)

Personally, at this point I still plan to pursue the path of: ASCIIMath → MathML → SVG → SILE. I believe it's more promising in the long run. That said, groff (specifically, eqn + pic) seems totally more useful in the short run, especially given that their language is much more powerful and expressive than ASCIIMath (I imagine commutative diagrams should be totally doable with eqn or eqn+pic). But as far as my current plans go, I'm leaving perfecting of the groff path as an... cue ominous thunder... "excercise for the reader".

@akavel
Copy link
Contributor Author

akavel commented Apr 17, 2017

Cool, SVGMath seems to work! Below is a quick screenshot of:

sum_(i=0)^oo x^i (sqrt(alpha + beta))/sin(x)

after passing through:

  asciimath.lua(MathML)math2svg.py(SVG)Firefox

(Note: I had to add <html><head></head><body> boilerplate + MathML namespace on the <math> element.)

The screenshot:

screenshot

It complained about some missing metrics and whatnot, so I suspect this could look better with some caring hand. (Still positively surprised super much that it actually worked, and so well so soon!)

@simoncozens
Copy link
Member

I don't have much to add to this but I'm really happy it's being worked on. The baseline/leading issues will be quite easy to fix when then time comes.

@akavel
Copy link
Contributor Author

akavel commented Apr 19, 2017

@simoncozens How about the bounding box? If groff emitted a big, white page with a (comparably) small equation in some quasi-random place on it, would you find it easy to extract just the "dirty" area? If so, it would make the task much easier on the groff side, I think — it should be just deleting all page decorations, and voilà, the thing's done?

@alerque alerque assigned alerque and akavel and unassigned alerque Apr 20, 2017
@alerque
Copy link
Member

alerque commented Apr 20, 2017

@akavel I don't think SILE should be responsible for parsing PDF data. It really isn't setup for that. It knows how to write it and it can take a chunk of PDF data and embed it somewhere, but parsing through the PDF data and doing a virtual rendering to find the bounds of actual content in it is more than a little outside the purview of the typesetting engine.

That being said it may not have to be groff that does the work. There are other tools for examining and modifying PDFs. Given that groff is already an external dependency it wouldn't be entirely out of the question to include another tool in the pipeline. However I'm concerned that even if we found such a tool the result would still be a hack. What happens when the formula is so large that it overflows the random spacious page size you assign? Is there really no way to have groff match the CropBox value to the resulting content it generates?

@akavel
Copy link
Contributor Author

akavel commented Apr 20, 2017

(@alerque Sorry, but can you please remove my "assignee" status from this issue? I don't want this to start feeling like work to me; I'm doing this just out of fun and because I want to help and like this project. A subjectively perceived connotation of "responsibility" and "obligation" coming from the assignment status unfortunately heavily impacts on my fun and is counter-productive for me working on this. Also, I don't want to make anyone feel like "this issue is occupied so I won't waste my time contributing to it"; I'm totally ok with someone else even finishing it before me and closing the issue (I don't lack things to do). Finally, I never promised I'll complete this, so I don't want anybody to think so by accident. I just fancy to have a stupid plan to try my luck at this, with 100% undefined schedule. As also shown by a ~1.5yr gap between my first and second comment in this thread. TIA! I'd do this myself, but can't find how.)

@alerque
Copy link
Member

alerque commented Apr 21, 2017

@akavel Of course. I didn't intend it in the sense of responsibility at all only that because you've got code kicking around in this department it would make sense to coordinate with you before launching into this one.

Speaking of which do you have a branch somewhere with your work in progress? If it was posted and linked from here that would give somebody else the chance to come along and extend or clean it up.

@akavel
Copy link
Contributor Author

akavel commented Apr 21, 2017

@alerque Thanks!

Re: branch link — sure, good idea. Summarizing the currently explored approach:

  • The asciimath port is already done, see: https://github.com/akavel/asciimath.
  • Now I'm working on porting svgmath (a Python MathML->SVG renderer/translator) to Lua here: https://github.com/akavel/svgmath.
    • To help with this I'm using a fork of kevinw's pylua — a basic Python->Lua translator — to automate as much work as reasonably possible, extending it as needed here: https://github.com/akavel/pylua.
  • Later, some form of integration with SILE will be needed, but I haven't forked SILE yet, too early still.

Recapping possible alternatives I thought of, with some pros & cons I'm aware of:

  • ASCIIMath -> MathML; then rendering the MathML e.g. with SVGMath (Python) to SVG; alternatively a different renderer straight to PDF could potentially be found?
    • ➕ ASCIIMath already translated from JS to Lua, as mentioned above;
    • ➕ SVGMath is a relatively small Python codebase, seems reasonable to translate/port to Lua, possibly with help of automated tools (work already started, as mentioned above). Given MIT licensing, poses a good chance for future integration with core SILE. Reasonable possibility that the SVG intermediate format could be replaced/supplemented with an immediate PDF emitter in future. Generally, having it in Lua arguably makes it a good fit for future development by people living in the SILE ecosystem.
    • ➕ keeping MathML as an explicit intermediate format/protocol (a de facto AST) gives a good possibility for interoperability with other tools and input/output processors.
    • ➖ / ➕ different equations syntax than LaTeX; arguably easier and more readable, but still different than the de facto current standard;
    • ➖ as reported in a comment earlier in this thread, less powerful than LaTeX's syntax (e.g. no commutative diagrams)
  • groff (specifically, eqn + pdfmom/gropdf) -> PDF -> \img[src=x.pdf] — as mentioned in comments above
    • standing on shoulders of giantseqn was co-created by Brian Kernighan of K&R fame in 1974; — battle-proven application and codebase.
    • ➕ potentially similar power to LaTeX's equations, especially if paired with pic; though I don't know details. But for sure much smaller (a few MB) than LaTeX (a few hundreds MB), and usually already installed on most Linux distributions (because used for manpages formatting); not sure about availability on Windows.
    • ➕ extending to add pic support for general diagramming and chem support for chemical molecule diagrams, and potentially grap for graphs should be easy/trivial afterwards;
    • ➕ looks easiest to do quite quickly; a quick PoC in a post above.
    • ➖ / ➕ different equations syntax than LaTeX; arguably easier and more readable, but still different than the de facto current standard;
    • ➖ ❔ all implementations I know of are GPL-licensed; this limits potential for inclusion in "core SILE", but it could well be maintained as a separate extension/plugin, even a "standard and official" one, keeping core SILE MIT for anybody who doesn't require equations;
      • edit:neateqn (doc) is a BSD-like-licensed clone of eqn (ISC license) (see also neatroff by the same author). Neateqn seems not to produce PDF, but most probably emits an intermediate format similar to groff_out (see below).
      • dpic (doc) is a BSD-licensed clone of pic. It seems to produce PDF and SVG (among others), but doesn't seem to produce groff_out-like intermediate format. Given the multitude of supported output formats, adding one more should not be too hard.
    • ☐ needs writing the "glue code" for integration with SILE,
    • ☐ and some more research on groff side how to lay out the resulting PDFs to make importing them into SILE easier (or, alternatively, writing some PDF processing on SILE side).
      • Additional note: groff/troff appears to use a fairly simple-looking intermediate format (man 5 groff_out (via)), so maybe this could be used to calculate the bounding box, or even translated to SILE intermediate format, at least for bounding box detection, if not for full integration and rendering.
  • MathJax
    • ➕ very powerful; potentially feature-parity with LaTeX equations (?)
    • ➕ the de facto standard on the Web currently; — rather battle-proven application and codebase;
    • ➕ implements LaTeX syntax, the current de facto standard for math writing; IIUC, also supports the simpler ASCIIMath syntax.
    • ➖ bigger in size than ASCIIMath+SVGMath, thus most probably harder to port from JS to Lua.
    • ➖ not clear how to convert the results to a format consumable by SILE. MathJax reportedly "auto-detects" features of a browser it's running in, adjusting the generated output. OTOH, it reportedly uses MathML internally, so there's some chance of hooking on to that.
  • KaTeX — an alternative to MathJax; somewhat simpler IIUC. Personally had less than ideal results running any of known JS->Lua translators on it (caveat: my knowledge of JS is very limited). Reportedly generates HTML+CSS → again not clear how to handle this output in SILE.
  • other ideas?...

All/some of the above alternatives could potentially peacefully coexist so that they can be played to their relative strengths.

@akavel
Copy link
Contributor Author

akavel commented May 2, 2017

The Lua port of SVGMath is now giving results, and very similar ones to what the original Python version gave. On the available test data/examples (*.mml), some have minor/medium spacing issues and there are some non-italic π characters (fixed). Also, 2 examples fail, but they fail in Python too (test18 and test20).

Example Lua results (testdata/*.out.svg):

Reference Python results (testdata/*.svg):

At this point I'd like to ask for help integrating this as an experimental plugin/feature into SILE. Can you possibly give me some guidance on how to start? I'd be very grateful for hints, would considerably speed this up for me. Please note, that I have no practical knowledge/experience of SILE, other than reading the manual.

@akavel
Copy link
Contributor Author

akavel commented May 4, 2017

Uh, oh; do I see correctly that SVG import seems not yet working as of current state of the SILE repo?

@alerque
Copy link
Member

alerque commented Sep 13, 2021

@akavel True. Actually I would be really interested to know how to build a HEAD package for Nix too, but we should probably ask that on their issue tracker or look into other venues for how such a thing is done. Arch Linux has a sile-git package in the AUR which you can get running pretty easily. I can post a Dockerfile to get that running if that would help you.

Math will land in a tagged release soon, really the only hold up is wanting to have examples fixed on the website before we attract attention with the math release announcement.

@OlivierNicole
Copy link
Member

OlivierNicole commented Sep 13, 2021 via email

@akavel
Copy link
Contributor Author

akavel commented Sep 14, 2021

@alerque as to building HEAD package first steps towards building HEAD package, what I did and seemed to work was:

  1. copy the default.nix from nixpkgs into the local clone of the SILE git repo (found the path in nixpkgs thanks to "The NixOS Hound")

  2. tweak the Nix expression's args list to work outside nixpkgs, by making it by default pull packages from <nixpkgs>:

    { nixpkgs ? import <nixpkgs> {}
    , lib ? nixpkgs.lib
    , stdenv ? nixpkgs.stdenv
    , darwin ? nixpkgs.darwin
    , fetchurl ? nixpkgs.fetchurl
    , makeWrapper ? nixpkgs.makeWrapper
    , pkg-config ? nixpkgs.pkg-config
    , poppler_utils ? nixpkgs.poppler_utils
    , gitMinimal ? nixpkgs.gitMinimal
    , harfbuzz ? nixpkgs.harfbuzz
    , icu ? nixpkgs.icu
    , fontconfig ? nixpkgs.fontconfig
    , lua ? nixpkgs.lua
    , libiconv ? nixpkgs.libiconv
    , makeFontsConf ? nixpkgs.makeFontsConf
    , gentium ? nixpkgs.gentium
    }:
    
  3. $ nix-build - worked for me!

  4. $ ./result/bin/sile

    SILE v0.11.1 (Lua 5.2)
    > 
    

Haven't tried to do anything more advanced with it yet.

(Please note I'm kinda early-intermediate with Nix, definitely not an expert.)

EDIT: Uhh, this will actually probably still build the 0.11.1 version fetched from github. Need to do some tweaks still to load from local code; sorry, as I said above, I'm still learning Nix :/

I think the next thing to do is to change the src attribute to something like src = ./.; but I'm not 100% sure, and this starts to give me some errors; can't work on it now, will see when I can try pushing this further.

@suhr
Copy link

suhr commented Sep 14, 2021

Don't forget to update the hash, otherwise nix might pull a wrong source.

@suhr
Copy link

suhr commented Sep 14, 2021

Anyway, you want this if you want to build from master:

  src = fetchFromGitHub {
    owner = "sile-typesetter";
    repo = "sile";
    rev = "190683a82eabff2aeccc29fc36d3c1d610b70a87";
    sha256 = "sha256-vStmwsdqwD7yIdNYCk3Vu853fXhU661xFuRmzsa3N2M=";
  };

@alerque
Copy link
Member

alerque commented Sep 14, 2021

@akavel When you get that working I'd be happy to accept a PR to put it in the project source repo. I'm coming up with uses for it already, starting with the SILE website. Building the examples in CI is great but it produces a chicken and egg problem because the CI runner has the last tagged version. Or if there is an official way to setup the sile package in nixpkgs to have a HEAD build option (like Homebrew has) that would be great too. @suhr happen to know anything about that?

@suhr
Copy link

suhr commented Sep 14, 2021

I would probably just add flake.nix to the project. Need fixing the build first though, and I'm too lazy to do that.

@akavel
Copy link
Contributor Author

akavel commented Sep 16, 2021

@alerque @suhr initial flake.nix attempt, up for discussion in the PR at: #1223; managed to get it to work on my machine.

@akavel
Copy link
Contributor Author

akavel commented Sep 17, 2021

@OlivierNicole hmh; so, I tried, and generally, I managed to fairly quickly get able to pass the asciimath->mathml translator's output to the \mathml command. Thing is, it appears that it quite extensively uses some elements not yet supported by \mathml, so much that it doesn't seem to make much sense for me to publish this for now (basically, most expressions don't render); notably, some unsupported elements I already observed in some simple attempts:

  • <mover>
  • <munder>
  • <munderover>
  • <mtext>

A good (most probably ultimate) reference on various MathML outputs that ASCIIMath can produce can be seen at: https://github.com/akavel/silemath/blob/72102bde232f8d3828ad5fee67a3b268dccd872a/asciimath/testall.lua

So, the difficult question now: is there a chance you might consider trying to add support for those elements to the \mathml command?...

@akavel
Copy link
Contributor Author

akavel commented Sep 18, 2021

@OlivierNicole Alternatively, if I wanted to try my luck at attempting to add support for those elements, would you possibly fancy giving me some hints as to where in the code could I even start looking into this? I mean especially the math layouting engine, I think...

@OlivierNicole
Copy link
Member

OlivierNicole commented Sep 18, 2021

@akavel Regarding <mover>, <munder> and <munderover>, you are right that they are not implemented. Or rather, they are half implemented, because those tags are used for two things: typesetting operator limits and typesetting so-called “accents” and “under-accents”.

Accents are not supported yet, mainly because it requires to support the construction arbitrarily stretchable glyphs, something conceptually not complicated and all the pieces are there, I just did not prioritize that in the window of time when I was working on this.

Limits are supported. However, I did a mistake in my interpretation of the MathML standard, so limits are managed by the <msup> and <msub> tags. For instance, you can typeset a sum as <msubsup><mo>&sum;</mo> <mrow>i = 0</mrow> <mn>n</mn></msubsup>, whereas the spirit of MathML commands that you use munderover instead of msubsup.

I have unfortunately little time these days to work on this side project, but I can try to fix this and support big operator limits through <munder> and <mover>. The accents I can't work on yet, however.

@OlivierNicole Alternatively, if I wanted to try my luck at attempting to add support for those elements, would you possibly fancy giving me some hints as to where in the code could I even start looking into this? I mean especially the math layouting engine, I think...

I believe the math package documentation in the manual (at the end of the section) gives a very brief overview of how to implement support for new MathML tags.

Edit.: <mtext> is also not implemented. I think it would not be very complicated to implement, especially as long as there is no line breaking inside math. I think it would essentially require to call some of SILE's dedicated functions to typeset a text on a line, then use the width, height and depth of that typeset text as the dimensions of the mtext element.

@akavel
Copy link
Contributor Author

akavel commented Sep 18, 2021

@OlivierNicole thanks! I now see the lines near the end in packages/math.lua describing mbox, shape, output, etc.

If you could possibly do the <mover>/<munder>/<munderover> fix, that would be awesome! Do you think things equivalent to \stackrel (a.k.a. \underset/\overset) would work already via the "big operator limits" layouting when specified using <mover>/<munder>?

As to the stretchable glyphs/accents, if you're not planning to work on that for now, would it be possible you'd try to outline the thought process and general idea how to try approaching this, as much as you could? Any hints here could be helpful! ❤️ I admit I currently would have no idea how to start neither conceptually, nor where to find the "pieces that are there"... if you wouldn't mind helping me with some hints, maybe I could try experimenting with that!

Thanks for the outline on <mtext>; sounds like something that could be doable for me, assuming I can find the SILE functions you mention that could help with that... actually, there might even be something like that in my old code that I tried to write for #220; though not 100% sure; if not, I can maybe try asking @alerque if he'd have some hints. Edit: Hm, I think at the time I tried to do something like this indeed, using SILE.shaper:shapeToken; though the way I did that seemed suspicious to myself already then, so it might not be right... :/

@OlivierNicole
Copy link
Member

Tags munder, mover and munderover implemented. See #1240.

Do you think things equivalent to \stackrel (a.k.a. \underset/\overset) would work already via the "big operator limits" layouting when specified using <mover>/<munder>?

Yes, as long as stretchable operators are part of the equation (pun intended), it should work.

@OlivierNicole
Copy link
Member

OlivierNicole commented Sep 21, 2021

As to the stretchable glyphs/accents, if you're not planning to work on that for now, would it be possible you'd try to outline the thought process and general idea how to try approaching this, as much as you could? Any hints here could be helpful! heart I admit I currently would have no idea how to start neither conceptually, nor where to find the "pieces that are there"... if you wouldn't mind helping me with some hints, maybe I could try experimenting with that!

When I said “the pieces are there”, I was thinking about the glyphs that were drawn by the OpenType font designers. In the end, the only thing that the math package has to do is positioning these glyphs at the right positions and give them the right sizes to typeset a formula.

Stretchable glyphs are a bit trickier. They can be done in two ways. First, some glyphs have several variants of increasing sizes: vertical variants (like vertical braces or parentheses) or horizontal variants (like arrows or horizontal braces). When the biggest variant is still too short, arbitrarily long versions can be constructed by overlapping base components of the glyph. The component glyphs, as well as some numeric parameters governing the scaffolding construction, are provided by the font in the MathGlyphConstruction table (see OpenType spec). The parsing of OpenType MATH tables into Lua tables has already been implemented by @harrysummer, so what's left is actually constructing stretchable glyphs, when needed.

I implemented the easy part: for delimiter glyphs, using the vertical variant closest to the target height. I didn't implement anything for horizontal variants, however.

OpenType Math Illuminated is probably also a useful reference for the parameters governing the positioning of glyphs.

@akavel
Copy link
Contributor Author

akavel commented Sep 23, 2021

@OlivierNicole thank you so much for the amazing description and references ❤️, this will most certainly be helpful to anyone interested in trying to implement this! (which might or might not be me - it makes no sense for me to give any promises here, given my poor track record with delivering stuff related to this issue...)

@simoncozens
Copy link
Member

This is what a glyph construction table (in Rust) looks like:

let integral = MathGlyphConstruction {
            glyphAssembly: Offset16::to(GlyphAssembly {
                italicsCorrection: MathValueRecord::new(0),
                partRecords: vec![
                    GlyphPartRecord {
                        glyphID: 2048,
                        startConnectorLength: 0,
                        endConnectorLength: 212,
                        fullAdvance: 893,
                        partFlags: 0,
                    },
                    GlyphPartRecord {
                        glyphID: 2075,
                        startConnectorLength: 893,
                        endConnectorLength: 893,
                        fullAdvance: 893,
                        partFlags: 1,
                    },
                    GlyphPartRecord {
                        glyphID: 2047,
                        startConnectorLength: 212,
                        endConnectorLength: 0,
                        fullAdvance: 893,
                        partFlags: 0,
                    },
                ],
            }),
            mathGlyphVariantRecord: vec![],
        };

For the integral sign, there are no math variants, but there is a glyph assembly table, which tells you how to build a stretchable integral sign: start by picking GID 2048 (integralbottom), and then, at anywhere from (top - 212 units) to the top of that glyph, you can place a connector (GID 2075, integral extension). This can be repeated if necessary (partflags: 1), and you can cut off up to 893 units from the bottom or top of that glyph to fit the desired height. Finally you take GID 2047 (integraltop) and you can cut off anything from 0 to 212 units from the bottom of that glyph and stick it on top of your extension.

@OlivierNicole
Copy link
Member

Thanks @simoncozens for this example, it's actually quite illuminating!

@khaledhosny
Copy link
Contributor

For the integral sign, there are no math variants, but there is a glyph assembly table

That is quite odd, I’d expect it to be the other way around for integral signs, what font is that?

@simoncozens
Copy link
Member

Libertinus.

@khaledhosny
Copy link
Contributor

Either your version of the font is broken or the code dumping the MATH table, this is what I see in TTX dump:

      <VertGlyphConstruction index="14">
        <!-- VariantCount=2 -->
        <MathGlyphVariantRecord index="0">
          <VariantGlyph value="integral"/>
          <AdvanceMeasurement value="1100"/>
        </MathGlyphVariantRecord>
        <MathGlyphVariantRecord index="1">
          <VariantGlyph value="integral.size1"/>
          <AdvanceMeasurement value="2185"/>
        </MathGlyphVariantRecord>
      </VertGlyphConstruction>

@simoncozens
Copy link
Member

Sorry, not Libertinus, LinLibertine_R.otf.

@khaledhosny
Copy link
Contributor

I see, makes sense as that fonts MATH table was not that usable.

@ctrlcctrlv
Copy link
Member

This should be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

No branches or pull requests

7 participants