sile-typesetter / sile

The SILE Typesetter — Simon’s Improved Layout Engine
https://sile-typesetter.org
MIT License
1.63k stars 96 forks source link

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

Closed akavel closed 2 years ago

akavel commented 8 years ago

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!

Current example result

alerque commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

@alerque @suhr initial flake.nix attempt, up for discussion in the PR at: https://github.com/sile-typesetter/sile/pull/1223; managed to get it to work on my machine.

akavel commented 2 years ago

@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:

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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

@OlivierNicole thank you so much for the amazing description and references :heart:, 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 commented 2 years ago

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 commented 2 years ago

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

khaledhosny commented 2 years ago

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 commented 2 years ago

Libertinus.

khaledhosny commented 2 years ago

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 commented 2 years ago

Sorry, not Libertinus, LinLibertine_R.otf.

khaledhosny commented 2 years ago

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

ctrlcctrlv commented 2 years ago

This should be closed.