w3c / mathml-core

MathML Core draft
https://w3c.github.io/mathml-core
38 stars 14 forks source link

largeop ascent/descent when using a vertical glyph assembly #55

Open fred-wang opened 4 years ago

fred-wang commented 4 years ago

From https://docs.microsoft.com/en-us/typography/opentype/spec/math:

"Note that for assemblies growing in the vertical direction, the distribution of height between ascent and descent is not defined. The math-layout engine is responsible for positioning the resulting assembly relative to the baseline."

Per https://mathml-refresh.github.io/mathml-core/#the-glyphassembly-table there are two cases:

(1) "Set B to the advanced width, ascent and descent of the glyph of id MathGlyphVariantRecord.variantGlyph. "

(2) "If there is valid GlyphAssembly subtable then set B to the glyph assembly width, glyph assembly ascent and glyph assembly descent for the target size T." which is itself "If GlyphAssembly is vertical, the width is the maximum advance width of the glyphs of id GlyphPartRecord.glyphID for all the GlyphPartRecord in GlyphAssembly.partRecords, the ascent is the glyph assembly stretch size for a given target size T and the descent is 0."

So in case of a largeop glyph assembly, the descent is always 0. I don't think people care about this case, but just opening to be sure...

(For stretchy glyph assembly the ascent/descent of the mo is later calculated from the non-stretchy siblings)

fred-wang commented 4 years ago

To summarize:

  1. The ascent/descent of a vertical stretchy operator is determined by the non-stretchy siblings.

  2. The ascent/descent of a (non-stretchy) displaystyle largeop using a single glyph is obtained from the font.

  3. The ascent and descent of a (non-stretchy) displaystyle largeop using a glyph assembly are assembly height and 0.

  4. is really a edge case and I don't think it's used in practice, so the proposal is to keep the spec as it.

brucemiller commented 4 years ago

Doesn't the quoted section only say that the descent is 0 for a horizontal GlyphAssembly?

rwlbuis commented 4 years ago

Fred can probably weigh in later, but our current internal branch code follows this algorithm: https://mathml-refresh.github.io/mathml-core/#dfn-box-metrics-of-a-stretchy-glyph

fred-wang commented 4 years ago

I would need to check again the code & spec, but I think we use ascent/descent for horizontal assembly and align glyphs on their baseline. Probably quoted section is about vertical assembly....

rwlbuis commented 4 years ago

Fred can probably weigh in later, but our current internal branch code follows this algorithm: https://mathml-refresh.github.io/mathml-core/#dfn-box-metrics-of-a-stretchy-glyph

rwlbuis commented 4 years ago

Nm my last comment....

NSoiffer commented 4 years ago

From the April 6 meeting, three alternatives:

  1. The bottom piece probably has a depth which may be have a depth ≠ 0. If so, that's potentially the value to use
  2. Use the height/depth of the non-stretchy version (if there is one), and scale that
  3. Center it on the math axis (which is what should happen if symmetric is true; not sure if the above algorithm does that in a subsequent step; if not that's a bug)
NSoiffer commented 4 years ago

@davidcarlisle checked the font table for some font and eventually the OpenType math spec and that rules out the first option above. Still options 2 and 3 seem preferable to using '0', and in any case, symmetric ="true" should be checked and obeyed (happens by default if we center on the math axis).

fred-wang commented 4 years ago

It seems there is a lot of confusion here. As a reminder: this issue is about largeop where stretchy="false" (in particular symmetric is not involved at all here) AND a glyph assembly is used (i.e. all available glyph variants have size less than DisplayOperatorMinHeight). If a single glyph is used for the largeop OR if stretchy="true" then we already have an ascent/descent, so there is no problem at all, it's already well-specified.

Has anyone been able to provide a concrete use case for that since I raised it? I'm not sure there is any real OpenType MATH font in the world that has both a largeop with a glyph assembly but no corresponding glyph variant of height at least DisplayOperatorMinHeight. So I still lean toward the simplest option of choosing 0.

NSoiffer commented 4 years ago

I think the case is more general than the title implies; the discussions on this issue in the meetings were about the more general case. From the OpenType description quoted when the issue was opened, this is about any glyph, not just large operators. The table almost certainly exists only for stretchy glyphs (not just operators), but non-stretchy ones that don't have a fixed size character potentially could be a problem (although I think we all agree that this is highly unlikely to occur inthe real world). I don't understand why the issue brings DisplayOperatorMinHeight this is problem is far more general than what the OpenType spec says it is meant for.

If the glyph is part of an mrow, then the non-stretchy elements will determine the height/depth. If there are no non-stretchy elements, then there is no forced height+depth other than minsize. If minsize exceeds any fixed size versions for the operator (or there are no fixed size versions), then the glyph needs to be assembled and the OpenType spec says the browser is responsible for positioning it relative to the baseline. In this case, there is no target ascent/descent and the assembled glyph does not say what they should be. Isn't that the issue being addressed?

The core spec current says the ascent/descent for the assembly is given by:

The glyph assembly width, glyph assembly ascent and glyph assembly descent are defined as follows:

  • If GlyphAssembly is horizontal, the width is the maximum advance width of the glyphs of id GlyphPartRecord.glyphID for all the GlyphPartRecord in GlyphAssembly.partRecords, the ascent is the glyph assembly stretch size for a given target size T and the descent is 0.
  • Otherwise, the GlyphAssembly is vertical, the width is glyph assembly stretch size for a given target size T while the ascent (respectively descent) is the the maximum ascent (respectively descent) of the glyphs of id GlyphPartRecord.glyphID for all the GlyphPartRecord in GlyphAssembly.partRecords.

Determining the ascent/descent by taking the max of the ascent/decent of the pieces doesn't make sense because the ascent+descent of the resulting glyph would not be its height. Perhaps this is a copy/paste error and it should be the sum of the ascents/depths? What @davidcarlisle found was there is not ascent/decent specified for the assembled pieces, so the piece's ascent/decent can't be used to compute the assembled ascent/decent.

I think the current options are:

  1. Always make the depth = 0.
  2. Use the ascent/descent of the non-stretchy version (if there is one), and scale that. Otherwise ???
  3. Center it on the math axis.

If symmetric ="true", then the spec says '3' should happen (i.e, ascent/descent are defined in that case).

fred-wang commented 4 years ago

In MathML Core, this MathVariant table for vertical stretching is only used in two cases:

(1) When operator is a vertical stretchy operator. (2) When operator is a non-stretchy largeop.

For (1), the spec says that the target ascent/descent is determined from the non-stretchy siblings, minsize, and symmetric properties (layout code). The glyph is stretched to at least the target ascent+descent size (by the low-level font API) and then positioned vertically so that the vertical centers of the target box and actual picked glyph/assembly aligns (by the layout code).

For (2), the spec says to that the layout code should tell the low-level code to return a glyph/assembly of at least DisplayOperatorMinHeight. If the result is a single glyph, the layout code also get the ascent/descent and can position it vertically. If it is a glyph assembly, it does not know how to do that. This is what the issue is about.

So in case of a largeop glyph assembly, the descent is always 0. I don't think people care about this case, but just opening to be sure...

This is where I was raising the issue for (2) in the initial comment.

(For stretchy glyph assembly the ascent/descent of the mo is later calculated from the non-stretchy siblings)

From this side comment, I meant to say we don't care about (1) as it's already completely specified.

fred-wang commented 4 years ago

I think the case is more general than the title implies; the discussions on this issue in the meetings were about the more general case. From the OpenType description quoted when the issue was opened, this is about any glyph, not just large operators.

This issue is only about non-stretchy displaystyle largeop. The other situation (stretchy) is already completely specified.

The table almost certainly exists only for stretchy glyphs (not just operators), but non-stretchy ones that don't have a fixed size character potentially could be a problem (although I think we all agree that this is highly unlikely to occur in the real world).

Text that is not stretchy or displaystyle largeop is not laid out by this table. Only the latter is incomplete.

I don't understand why the issue brings DisplayOperatorMinHeight this is problem is far more general than what the OpenType spec says it is meant for.

displaystyle largeop operators use DisplayOperatorMinHeight as the min target size.

If the glyph is part of an mrow, then the non-stretchy elements will determine the height/depth. If there are no non-stretchy elements, then there is no forced height+depth other than minsize.

The spec says: "If T = 0 then set Tascent and Tdescent to minsize/2.". This means the glyph will be center with respect to the baseline. We can change if we prefer to make it symmetric with respect to the math axis, for now it's already specified and again that's not what this issue is about.

If minsize exceeds any fixed size versions for the operator (or there are no fixed size versions), then the glyph needs to be assembled and the OpenType spec says the browser is responsible for positioning it relative to the baseline. In this case, there is no target ascent/descent and the assembled glyph does not say what they should be. Isn't that the issue being addressed?

No, the issue is about non-stretchy displaystyle largeop using a glyph assembly. For stretchy operator with siblings of zero size, target ascent/descent is minsize/2 and minsize/2 so they are aligned with respect to the baseline.

Determining the ascent/descent by taking the max of the ascent/decent of the pieces doesn't make sense because the ascent+descent of the resulting glyph would not be its height. Perhaps this is a copy/paste error and it should be the sum of the ascents/depths?

This is what Bruce was mentioning. vertical and horizontal are switched here. I'll fix that.

What @davidcarlisle found was there is not ascent/decent specified for the assembled pieces, so the piece's ascent/decent can't be used to compute the assembled ascent/decent.

I'm not sure what you mean here. But the low-level API calculate the stretch size for the assembly, which is used for the ascent while the descent is currently always 0 as I said in the first comment. This is only a problem for largeop since stretchy op are latter vertically positioned by the layout code using target stretch constraints.

fred-wang commented 4 years ago

Doesn't the quoted section only say that the descent is 0 for a horizontal GlyphAssembly?

Fixed by https://github.com/mathml-refresh/mathml-core/commit/c53555cb02af6bc414869c2f9593454f9877c352 and in the issue description.

fred-wang commented 2 years ago

I need to check the status of this...