Realign foreignObject to its rightful position
But also, help! This started as an easy fix, and ended up touching alignment code. I think it is correct, but I'd rather have some comments now, before spending more time on it. I really hope I am right, because it improves the TikZ output in a small but quite substantial way.
The aim is to align foreign objects properly inside SVGs.
-
Flip
svg:foreignObjectaround its center, instead of the center of an imaginary line of height 16.6px (the hardcoded global value of\baselineskip). Compare with equivalent code inPost::SVG::convertNode. I have concluded that\baselineskipwas just a mistake. -
Add a CSS flex wrapper to
svg:foreignObject, so that the foreignObject box and its content share the same center, instead of the content being anchored north east, which looks bad in most situations.
(1) and (2) make normal TikZ figures (with no halign) perfect, modulo font metric errors. For instance,
becomes
Small difference, but the equal sign is where it should be. I have played with other figures and they all look better this way.
- (commits 3 + 4) Now TikZ-cd is even worse than before. The root cause: Core::Alignment hardcodes the depth to 0. That should rather be the depth of the last line. With the depth available, the heuristic code can be removed altogether. I seemingly get perfect alignment in TikZ-cd.
Example: before
after
which is much closer to the PDF
Very exciting work! @brucemiller please help :>
This is how it looks in Chrome as of 318ba9f.
I should summarize what 318ba9f is doing:
- I used CSS text-box to make the bottom of (the content of) the foreignObject be its baseline. This seems to be impossible in old CSS, which is really a shame.
- I used flex to do two things: align the bottom of the content (so its baseline) to the bottom of foreignObject, and to center the content horizontally. The former can probably be achieved with a single wrapper and
bottom:0. The latter is more tricky, because the content will typically overflow sotext-align:centerdoesn't cut it; this may require flex for real.
I am inclined to say that this is the correct approach. The rules are sound and predictable, and the output looks good in the early tests.
PS: text-box can make titled frames much more similar to the LaTeX ones.
@xworld21 I assume you are aware of the limited availability of CSS text-box today? I wonder if we're risking breaking too much on Firefox if we use it for v0.8.9.
@xworld21 I assume you are aware of the limited availability of CSS text-box today? I wonder if we're risking breaking too much on Firefox if we use it for v0.8.9.
Yes, that is a major problem. I have a reasonable mitigation in mind though. I think my general idea here is that text-box is correct, so we might as well start with text-box as our baseline (no pun intended). I will add some extra CSS gated behind @supports with a less precise fallback (something like translate: 0 $depth;; it just takes a bit of finessing to make that happen on the right object, right depth, etc). I just haven't got round to it yet.
I wouldn't say it breaks Firefox, it's more that it breaks it differently compared to now.
I wouldn't say it breaks Firefox, it's more that it breaks it differently compared to now.
This is the kind of comment I like to make. I like your plan.
Most of your latest examples, above, look pretty good, but I'm not getting as good results from your branch on the collection of tikz tests; Maybe I've screwed something up in comparison? But as this branch is drifting from master, it's getting harder to compare code. If #2534 is appropriate, maybe we should merge it and rebase here?
The big difficulty I'm having is figuring out where xy, respectively pgf, "think they are" when they drop a foreignObject; that coupled with Firefox & Chrome occasionally having randomly different placement. I keep encountering trivially perfected cases, but if patched at the wrong level or context, inevitably break several other cases!
Of course, both systems rely completely on TeX's measurements to place things, put boxes around them, etc. But then the browsers use different fonts, and multiline text comes out particularly mismatched (eg the consort-flowchart test). I found that cranking down CSS line-height and letter-spacing helps a lot. But maybe more sophisticated CSS would be better? text-box sounds good on paper; I'm a bit sceptical that flex is the way to go, though.
I was surprised to see how LaTeXML's too-crude sizing of sub/superscripts threw xy way off. Of course, if you fix that without realizing the root cause, everything else goes off.
I have implemented a hasty fallback for browsers not implementing text-box. It's not enough! Comments on the fallback:
- The fallback is via
line-heightand it is essentially equivalent to my previous version of this PR. This definitely causes issues with multiline content. I have resetline-heightin inline-block children to mitigate some of it. - Using CSS variables is debatable, but it makes it easy to toggle different switches on and off, so please ignore that for now!
- Google Chrome < 133 works well with the fallback.
- Safari 18.2 understands text-box, but I think it has a bug: if the content spans multiple lines, text-box trims under the first line rather than the last line. That... doesn't seem to be what the spec says.
- Firefox: hopeless. The
line-heightfallback seems to have the desired effect on text content, but MathML only moves by like one pixel. To see what I mean, try settingline-height:0pxand see how it affects text vs math. So this needs some Firefox-specific workaround.
Talking of the foundations behind this PR, I should restate the principle, for clarity. TeX creates a box with a certain height, width, baseline; CSS creates a block with similar measures. What I am trying hard to do is to make the centre point of the baseline to be the same in TeX and in CSS. This is easy with text-box and flex; it kind of works with line-height for Blink/WebKit. Without those adjustments, the two boxes are attached at the northeast corner instead (and actually LaTeXML fudges that with an extra \baselineskip, which I have removed).
PS: also, my additional translate: fallback triggers an >20 years old bug in WebKit which completely breaks foreign objects, so that will need to be addressed as well (no point trying to perfect this until we understand Firefox, though).
In fairness to your effort, I should let you know that I've also been doing some experimentation. And also, in case my observations help you, since it's not clear at this point which approach will be most fruitful.
I've been frustrated by the game of making "obvious, intuitive" adjustments that end up fixing one thing and breaking another, I finally tried to be a bit more scientific about it. I added some extra svg:rules and other markings to show me exactly the position & size (w,h,d) of each box that LaTeXML thinks TeX wants to place that will create a foreignObject. And then I can compare where the browser placed the content relative to where LaTeXML thought it would be.
After some hopefully defensible tweaks, I end up with a couple of surprises:
- The pgf/tikz hboxes seem to be where they should be, but with xy, most (but not all!) boxes seem to have their baseline aligned to the math-axis of the surrounding material.
- What the browsers (Firefox, Chrome) draw is contained in a rectangle which is more block-like, and (usually?) has at least a height corresponding to the line-height (not the height of the actual glyphs). Moreover, with math there's often a lot of extra horizontal spacing (eg. left/right padding for "+").
That first point obviously affects the vertical positions; it seems like it must be a LaTeXML bug and handled at the TeX/LaTeXML level; it almost looks like we've lost a \raise or some sneaky TeX internal that moves math?
The second point probably also needs some LaTeXML recognition at least of the browser's apparent box size & position (eg. line-height). Maybe CSS level magic is needed, as well.
I'll try to prepare a draft PR on Monday, at least for comparison purposes.
In fairness to your effort, I should let you know that I've also been doing some experimentation.
I was hoping you would – I am running out of ideas! text-box has been the only reliable solution so far, because it does what I want it to do (give me access to the actual baseline). The weird WebKit behaviour is likely a bug and hopefully something we can work around. line-height instead is the wrong approach; I have investigated a bit further, and it just does not do what I think it does. So I still don't have a solution for Firefox and other old browsers.
- The pgf/tikz hboxes seem to be where they should be, but with xy, most (but not all!) boxes seem to have their baseline aligned to the math-axis of the surrounding material.
Indeed, something is definitely off with xy. The arrows themselves start and end at the wrong points!
Indeed, something is definitely off with xy. The arrows themselves start and end at the wrong points!
Actually, assuming (!!) my interpretation of my surprise is correct, the arrows are in the right place, but it's the things they point to that are off by the math-axis (or some similar length). And that would be easy to expediently hack, except that the arrow labels (marked up as-if sub/superscripts) are not offset by that amount.
Aha; curious bit is that xy does some positioning using \fontdimen22\textfont2, which currently gets 0, instead of the proper math-axis. Fix gets xy pretty close to good, and will be in the aforementioned draft PR...soon...