slatex / RusTeX

A (somewhat experimental) implementation of a TeX engine in rust, used to convert LaTeX documents to xhtml.
22 stars 0 forks source link

xrightarrow not nice #8

Open kohlhase opened 1 year ago

kohlhase commented 1 year ago

In MiKoMH/AI/source/game-play/slides/game-formalization.en.tex we see

Screenshot 2022-12-10 at 06 55 24

It comes from \symdef{withaction}[args=3]{#2\xrightarrow{#1}#3}

Jazzpirate commented 1 year ago

huh, I didn't know that command before, but it seems like \xrightarrow is a variant of \rightarrow whose length is adapted to the argument, and it seems that is implemented as just concatenating dashes. It looks ugly in the HTML then, because the spacing between the dashes is off. I see two ways of solving that:

  1. Trying to get the spacing between the dashes to work: Probably involves lots of fiddling and the length of the arrow would probably still be off, because the length of a dash is likely different in tex dimensions and in HTML
  2. Probably the smarter option: I need to figure out if there's a way to get a scaled arrow in mathml. I should probably check what latexml does with \xrightarrow...
rajeeshknambiar commented 1 year ago
2. Probably the smarter option: I need to figure out if there's a way to get a scaled arrow in mathml. I should probably check what latexml does with `\xrightarrow`...

I would recommend this option, as MathML3 spec §3.2.5.8 Stretching of operators, fences and accents specifies the attribute stretchy='true' which LaTeXML also seem to do.

That said, the example mentioned in MathML spec (§3.2.5.8.3 Horizontal Stretching Rules) does not explicitly mention the stretchy attribute. I also wonder #x27F6 LONG RIGHTWARDS ARROW shouldn't the right choice instead of #x2192. image

Jazzpirate commented 1 year ago

yeah, munder seems to occasionally (but not always) stretch automatically - depending on the rendering engine and the precise unicode characters involved. I can't claim to fully understand all the details. Either way, we likely need special support for \xrightarrow, which is what I usually try to avoid in rustex -.-

rajeeshknambiar commented 1 year ago

Ok; could you explain what is meant by needing special support? mml:munder is anyway required and adding <mml:mo stretchy='true'> should solve it neatly adhering to the standard, right? The rendering is upto individual engines (which may/may not have full support, but that is a different story).

Jazzpirate commented 1 year ago

\xrightarrow is not a TeX primitive - it is a defined macro in some package somewhere. RusTeX attempts to operate entirely on TeX primitives, expanding defined macros as pdflatex would do. In his case however, that results in a concatentation of dashes, ending with the arrow-character, because that's how \xrightarrow is actually implemented in whatever-package-it-comes-from :)

The "morally best" way to deal with this would be to fiddle with the spacing with the dashes, because I assume that the same problem will occur with other symbols as well - i.e. it would not just solve \xrightarrow, but a whole bunch of similar macros at once.

The alternative is to implement a special treatment for \xrightarrow. That solve this particular problem, but ultimately just kicks the can down the road until the next macro that does something similar shows up ;)

rajeeshknambiar commented 1 year ago

Understood. Yes, \xrightarrow is defined by amsmath as

\ams@def\relbar{\mathrel{\mathpalette\mathsm@sh\std@minus}}
\ams@def\Relbar{\mathrel\std@equal}
\def\arrowfill@#1#2#3#4{%
  $\m@th\thickmuskip0mu\medmuskip\thickmuskip\thinmuskip\thickmuskip
   \relax#4#1\mkern-7mu%
   \cleaders\hbox{$#4\mkern-2mu#2\mkern-2mu$}\hfill
   \mkern-7mu#3$%
}
\def\rightarrowfill@{\arrowfill@\relbar\relbar\rightarrow}
\newcommand{\xrightarrow}[2][]{\ext@arrow 0359\rightarrowfill@{#1}{#2}}

My (potentially incorrect/incomplete) reading is that as you mention the arrowfill command places two \relbar together which actually is the minus symbol glued with negative kerning values.

So this could very well be rendering engine issue; this is how Firefox 114 renders the page — notice there are no breaks in the long minus (so mkern is probably working correctly) but there's space between long minuses and arrow.

image

The generated MathML looks far too nested which may (or may not) cause the spacing between long minus and arrow...

image
Jazzpirate commented 1 year ago

It's worse than that: \arrowfill uses \cleaders\hbox{... #2 ...}, which means:

This is worse, because it means a) the result will be an ungodly nesting of <math><mrow>... and <mtext><span>... to switch between MathML and HTML and b) there's no analogue for leaders in HTML. There should be, but there isn't >.<

Jazzpirate commented 1 year ago

...I have no idea though why thefirst part of the arrow is, like, significantly higher up than the remainder - Chrome doesn't do that. That seems like a problem with Firefox's MathML rendering engine

rajeeshknambiar commented 1 year ago

This is worse, because it means a) the result will be an ungodly nesting of <math><mrow>... and <mtext><span>... to switch between MathML and HTML and b) there's no analogue for leaders in HTML. There should be, but there isn't >.<

Thanks for the insight.

Taking a step back; one could perceive this as an implementation detail (that amsmath defines \xrightarrow using \cleaders for postscript/pdf o/p) and thus arrive at a conclusion that there can/may not be one-to-one equivalence between amsmath (or other packages) implementation and xml/mathml implementation that RusTeX wants to achieve...

Jazzpirate commented 1 year ago

More generally: There is not a one-to-one correspondance between TeX primitives and HTML+MathML. That is not surprising, and I was aware of that, but I will still try to solve problems ideally in generality in terms of primitives, rather than having to solve lots of special cases for individual packages/macros ;) Probably no good way to do that here though, so better to just implement a dedicated MathML translation for \xrightarrow and call it a day ^^

rajeeshknambiar commented 1 year ago

Ok; if you plan to implement a separate MathML translation in RusTeX; how would the approach be? Would you, when implemented, add the details in commit log so that it could act as an education point for someone who want to contribute enhancements?

Jazzpirate commented 1 year ago

Oh, there already are a couple of those :) They are implemented here: https://github.com/slatex/RusTeX/blob/master/rustex/src/commands/latex_bindings.rs

They idea is to provide a CommandListener, that gets called by the interpreter whenever a control sequence is being (re)defined and may "interject" to replace it by a different implementation. After that, things depend on what it's supposed to do. There are implementations for \mapsto and \overbrace/\underbrace, if you want/need inspiration.

Keep in mind, that the CommandListener has to be "registered" with the state, e.g. by adding it to the all_listeners() method at the end of the file

Jazzpirate commented 1 year ago

It should be noted that the whole API for adding macro implementations is not very programmer-friendly or nice - it's more like "what is the minimum I need to implement to get the functionality I need right now". If I had the time to do so, I should definitely overhaul that at some point, but... yeah.