-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Shrinkwrap vlists in table-like CSS. #768
Conversation
Wow. Enormously impressive. Of course this needs lots of cross-browser testing, so I'll help with that. This solves a tough problem. Now I have a question. There are couple of lines that contain a constant of 0.91:
I expected that value to be 0.9, not 0.91, because the KaTeX fonts have an ascend value of 0.9. Is there any particular reason you used 0.91? I'm glad you mentioned the extra space in the VerticalSpacing screenshotter test. When I was testing PR #670, I repeatedly saw this kind of extra space, and I'm glad that someone else has noticed it. In an earlier thread, @gagern mentioned that any use of tables should be careful that the CSS resets table style. I don't think your CSS would conflict, but it is a good thing to keep in mind. |
The 0.91 was determined empirically, by comparing baseline-aligned rules in and out of vlists. I'd think it would be 0.9 (the line-height is 1.2em and the typographic ascender 0.8em, so you'd think 0.1em at the top + 0.8 ascender = 0.9em), but it's not. I also added some comments, and thanks, added the Compatibility could be an issue here. In particular, the vertical positioning in this checkin makes assumptions about (1) the ascender of a particular font, (2) the way line-boxes are calculated. These assumptions are new: the old code did produce different results on different browsers, but only in terms of the extra whitespace around KaTeX math, rather than within the math itself. According to CSS 2.1 the ascender “should” be calculated using particular font metrics: sTypoAscender, sTypoDescender. But there might be differences on different platforms. So curious what happens on modern, KaTeX-supported browsers. |
Sounds very cool! I've always been confused/annoyed by line boxes, so it's great to see a possible fix in this context. Regarding @gagern's table comment, he may have also been referring to CSS code like this (taken from my own project), which changes the default style of all tables in a certain context. Both Markdown tables and KaTeX output could appear in this context (of a Markdown + LaTeX formatted message). But these rules are relevant only to tags like |
I've tested this code on my box, on my phone. and on BrowserStack with various styles, sizes, and images. It works great on Edge, IE 9-11, iOS Safari, Mac Safari, Windows Chrome, Android Chrome, Opera, and Firefox. Terrific stuff. |
Thanks @ronkok!! I had, though, an idea to make the mechanism less sensitive to font ascender dimensions, namely a tall However, Chrome versions don't match—in the horizontal direction. This was a surprise to me. It appears that Chrome rounds table-cell dimensions up to integral numbers of pixels, but does not do this for divs. Example diff attached. Maybe this is a blocker, maybe it's not. |
Annoying. Here's the Chrome issue. |
Personally, I think this approach is still worth persuing. It's not a huge loss, and Chrome will hopefully eventually fix, while we gain... a lot, I think? Can you show some examples where this new approach to vlists is a significant improvement over the current one? |
@edemaine thanks for digging up that issue. It sounds like they want to fix it and are prioritizing it so that's promising. @kohler since it looks like this bug is going to be fixed in Chrome and it doesn't exist in other browsers the rounding issues probably shouldn't be a block, but it'd be good to do some testing at smaller font sizes to see how much of an impact this has on layouts at those sizes. |
@kevinbarabash I've done some tests with 16px fonts, and the rounding issues are worse, but not dramatically worse. Three examples from screenshotter tests: |
I further updated the CSS. By using two table-rows, and a table-cell with vertical-align: bottom, all reliance on line-height and font ascent can be avoided, and the code is simpler. Only a small number of firefox test cases change. Most chrome test cases change, because of the rounding issue. Assuming that the updated CSS works on other browsers—it appears to, but @ronkok, if you could help check that would be amazing—this PR is in my opinion ready to merge. |
Thanks @ronkok! That was a real problem: Safari needed an extra table-cell to position the table correctly. I've fixed that problem and things display well on local Safari, at least. |
@ronkok, did you know that accent placement was off on Safari? The \vec’s are rightwards-displaced even on master. The commit I just pushed is correct vertically though. |
Yes, the |
@kohler, That last commit did the trick. This PR now works in Chrome, Firefox, Edge, IE 9-11, Safari, and Opera. This problem has caused me many hours of frustration, so I'm very glad that you have solved it. |
Ping—can a girl get a review? @kevinbarabash |
@kohler sorry for the delay. I'll review this this weekend. |
I noticed this when reviewing the screenshots. It seems like there is generally less extra padding which is a good thing imo.
Awesome article.
I was concerned about vertical/horizontal rules getting too close to numbers/letters. The examples in the screenshot look alright. |
} | ||
} | ||
const fontSizer = makeFontSizer(options, maxFontSize); | ||
pstrutSize += 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so that it's a little taller than any of the children?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, taller than any child—or the height of any child.
} | ||
} | ||
const fontSizer = makeFontSizer(options, maxFontSize); | ||
pstrutSize += 2; | ||
const pstrut = makeSpan(["pstrut"], []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about the name pstrut
... what does the p
represent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“positioning strut”.
for (i = 0; i < children.length; i++) { | ||
if (children[i].type === "elem") { | ||
maxFontSize = Math.max(maxFontSize, children[i].elem.maxFontSize); | ||
const child = children[i].elem; | ||
pstrutSize = Math.max(pstrutSize, child.maxFontSize, child.height); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this will have to be updated to support \rasiebox
eventually.
childWrap.depth += shift; | ||
childWrap.style.top = shift + "em"; | ||
const childWrap = makeSpan([], [pstrut, child]); | ||
childWrap.style.top = (-pstrutSize - currPos - child.depth) + "em"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just so I understand this correctly, the pstrut
in the childWrap
is taller than the child
so that we avoid issues with the baseline alignment and can align the bottom of the child's glyph bounding box with the bottom of the pstrut
? And then childWrap
is position vertically so that child
appears where we want it to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly.
const topStrut = makeSpan(["vlist-s"], [new domTree.symbolNode("\u200b")]); | ||
|
||
rows = [makeSpan(["vlist-r"], [vlist, topStrut]), | ||
makeSpan(["vlist-r"], [depthStrut])]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what situations is the depthStrut
necessary? I tried removing it from some fraction layouts and didn't notice a difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The depthStrut
signals to the browser how deep the box is. This isn't that relevant in normal layouts; KaTeX understands heights & depths. But it matters for layout of surrounding lines. That also might not matter given the current coding (which has separate struts done at the very outermost level), but I think it's good and useful to have the vlist coding produce a box of the correct height and depth.
src/buildCommon.js
Outdated
|
||
const vtable = makeSpan(["vlist-t"], rows); | ||
vtable.height = maxPos; | ||
vtable.depth = Math.max(-minPos, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realize that depth couldn't be negative. Why is this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In TeX it can be negative but usually isn't. The current code works, and I thought that it was producing the same results as the old code—should I check without the max-0?
@@ -564,16 +581,14 @@ | |||
} | |||
|
|||
// Lengthen the extensible arrows via padding. | |||
.x-arrow > span > span { | |||
text-align: center; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, this got moved down to line 591.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
@@ -564,16 +581,14 @@ | |||
} | |||
|
|||
// Lengthen the extensible arrows via padding. | |||
.x-arrow > span > span { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we have to be as specific as we were being before with these rules?
.vlist-s { | ||
display: table-cell; | ||
vertical-align: bottom; | ||
font-size: 0.05em; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the 0.05em b/c we want the impact of the font size on the base line of elements with this class to be negligible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly.
@@ -232,8 +232,25 @@ | |||
& + .mop.mtight { margin-left: @thinspace; } | |||
} | |||
|
|||
.vlist-t { | |||
display: inline-table; | |||
table-layout: fixed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout it says that table-layout: fixed
bases the width of the table on the first row. It looks like the first row is always contains the content for the vlist. Is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently table-layout: fixed
renders faster. I'm curious if the decision to use this was motivated by perf concerns or something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Motivated by the fact that the other table displays in katex.less have table-layout: fixed.
TeX and CSS treat line heights in fundamentally different ways. In TeX, every character is treated as a box of its precise height and depth; the line height (\baselineskip) applies after characters have been assembled into lines. In CSS, in contrast, every character creates a "line box" corresponding to the accompanying font. When characters of different fonts and sizes are placed into the same span, the resulting line box contains the line boxes of all children. This is unfortunate because, for example, we want `\frac{1}{2}` to behave in vertical spacing contexts like it is exactly as tall and deep as the visible fraction (which is the TeX behavior). Given CSS constraints, though, in most contexts the fraction has extra vertical space: the line boxes for the numerator and denominator create padding. For small boxes, this isn't so bad. To really see the problem put a tall rule in the denominator of a fraction, or check out the VerticalSpacing screenshotter test, which has way more space than it should. Solving this problem in CSS is difficult. There is no easy way to get rid of the extra line boxes. But there is *a* way, namely tables. A table-cell with vertical-align top, bottom, or middle is ignored for the purposes of line height calculation. So in this commit, makeVList puts its contents into a vertical-align:bottom table-cell (to clear unwanted line boxes), with an extra row used to represent depth. Many Chrome screenshotter tests change. This is because Chrome rounds table dimensions to integral numbers of pixels, while it uses sub-pixel positioning for non-table displayed tabs. That makes many vlists a fraction of a pixel wider than they used to be.
Screenshotter tests don't change.
Inspired by your question, I changed the PR slightly to produce negative heights and depths when appropriate. Screenshotter tests OK. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
It certainly unblocks the |
Do you pay for BrowserStack? I looked briefly and it looked painful to get free access.
… On Jul 30, 2017, at 12:48 PM, Ron Kok ***@***.***> wrote:
I'm finding a problem on Safari. It occurs in iOS only, not Mac. The screenshot below is from BrowserStack's rendition of an iPad Pro, iOS 10.3
That is trying to render: \( \color{blue} \sqrt{\frac{\frac{\frac{c}{c}}{c}}{\frac{c}{c}} } + \sqrt{\frac{\frac{\frac{\frac{c}{c}}{\frac{c}{c}} }{c}}{\frac{c}{c}} } \, \sqrt{\frac{\frac{\frac{\frac{c}{c}}{\frac{c}{c}} }{c}}{\frac{c}{\frac{\frac{c}{c}}{\frac{c}{c}}}} } \)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I do pay for BrowserStack. For the short term, at least. |
Another way to test on iOS is use the simulator that comes with Xcode. |
I still see the problems. I see them only when the iPad is in portrait orientation, not landscape. That opens up a line of investigation. The page I'm looking at contains some CSS media queries for screen width. Among these are:
I'll investigate some more and let you know if I can pin this down further. Maybe not today, though. |
Interestingly that test page looks good at 72px and then falls apart as the font size is decreased. |
Just wanted to say thank you for this change. I'm working on a canvas renderer implementation, and this is a huge life saver. |
This PR
is not ready to merge, but I'd appreciate feedback on the approachis ready. @ronkok @kevinbarabashRelevant commit message:
TeX and CSS treat line heights in fundamentally different ways. In
TeX, every character is treated as a box of its precise height and
depth; the line height (\baselineskip) applies after characters have
been assembled into lines. In CSS, in contrast, every character
creates a "line box" corresponding to the accompanying font. When
characters of different fonts and sizes are placed into the same
span, the resulting line box contains the line boxes of all children.
This is unfortunate because, for example, we want
\frac{1}{2}
tobehave in vertical spacing contexts like it is exactly as tall and
deep as the visible fraction (which is the TeX behavior). Given CSS
constraints, though, in most contexts the fraction has extra vertical
space: the line boxes for the numerator and denominator create
padding. For small boxes, this isn't so bad. To really see the
problem put a tall rule in the denominator of a fraction, or check
out the VerticalSpacing screenshotter test, which has way more space
than it should.
Solving this problem in CSS is difficult. There is no easy way to get
rid of the extra line boxes.
But there is a way, namely tables. A table-cell with vertical-align
top, bottom, or middle is ignored for the purposes of line height
calculation.
So in this commit, makeVList puts its contents into a
vertical-align:bottom table-cell (to clear unwanted line boxes), with
an extra row used to represent depth.
Many Chrome screenshotter tests change. This is because Chrome rounds
table dimensions to integral numbers of pixels, while it uses
sub-pixel positioning for non-table displayed tabs. That makes many
vlists a fraction of a pixel wider than they used to be.