Add alignment-baseline aliases to support legacy content.

This commit is contained in:
Mikael Sand
2017-07-25 10:00:32 +03:00
parent b140d673d7
commit 2d699b4e56
3 changed files with 165 additions and 132 deletions

View File

@@ -31,7 +31,20 @@ enum AlignmentBaseline {
textTop("text-top"), textTop("text-top"),
bottom("bottom"), bottom("bottom"),
center("center"), center("center"),
top("top"); top("top"),
/*
SVG implementations may support the following aliases in order to support legacy content:
text-before-edge = text-top
text-after-edge = text-bottom
*/
textBeforeEdge("text-before-edge"),
textAfterEdge("text-after-edge"),
// SVG 1.1
beforeEdge("before-edge"),
afterEdge("after-edge"),
hanging("hanging"),
;
private final String alignment; private final String alignment;

View File

@@ -25,6 +25,8 @@ import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG; import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG;
import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG; import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG;
@@ -184,12 +186,124 @@ class TSpanShadowNode extends TextShadowNode {
final double letterSpacing = font.letterSpacing; final double letterSpacing = font.letterSpacing;
final boolean autoKerning = !font.manualKerning; final boolean autoKerning = !font.manualKerning;
/*
https://developer.mozilla.org/en/docs/Web/CSS/vertical-align
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
https://www.microsoft.com/typography/otspec/base.htm
http://apike.ca/prog_svg_text_style.html
https://www.w3schools.com/tags/canvas_textbaseline.asp
http://vanseodesign.com/web-design/svg-text-baseline-alignment/
https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align
https://tympanus.net/codrops/css_reference/vertical-align/
https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty
11.10.2.6. The alignment-baseline property
This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3]
https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
The vertical-align property shorthand should be preferred in new content.
SVG 2 introduces some changes to the definition of this property.
In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed.
For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and
'text-after-edge' should be mapped to 'text-bottom'.
Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property.
*/
Paint.FontMetrics fm = paint.getFontMetrics(); Paint.FontMetrics fm = paint.getFontMetrics();
double top = fm.top; double top = -fm.top;
double bottom = fm.bottom; double bottom = fm.bottom;
double ascent = fm.ascent; double ascenderHeight = -fm.ascent;
double descent = fm.descent; double descenderDepth = fm.descent;
double totalHeight = top - bottom; double totalHeight = top + bottom;
double baselineShift = 0;
if (mAlignmentBaseline != null) {
// TODO alignment-baseline
switch (mAlignmentBaseline) {
// https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
default:
case baseline:
// Use the dominant baseline choice of the parent.
// Match the boxs corresponding baseline to that of its parent.
baselineShift = 0;
break;
case textBottom:
case afterEdge:
case textAfterEdge:
// Match the bottom of the box to the bottom of the parents content area.
// text-after-edge = text-bottom
// text-after-edge = descender depth
baselineShift = descenderDepth;
break;
case alphabetic:
// Match the boxs alphabetic baseline to that of its parent.
// alphabetic = 0
baselineShift = 0;
break;
case ideographic:
// Match the boxs ideographic character face under-side baseline to that of its parent.
// ideographic = descender depth
baselineShift = descenderDepth;
break;
case middle:
// Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.
// middle = x height / 2
Rect bounds = new Rect();
// this will just retrieve the bounding rect for 'x'
paint.getTextBounds("x", 0, 1, bounds);
int xHeight = bounds.height();
baselineShift = xHeight / 2;
break;
case central:
// Match the boxs central baseline to the central baseline of its parent.
// central = (ascender height - descender depth) / 2
baselineShift = (ascenderHeight - descenderDepth) / 2;
break;
case mathematical:
// Match the boxs mathematical baseline to that of its parent.
// Hanging and mathematical baselines
// There are no obvious formulas to calculate the position of these baselines.
// At the time of writing FOP puts the hanging baseline at 80% of the ascender
// height and the mathematical baseline at 50%.
baselineShift = ascenderHeight / 2;
break;
case hanging:
baselineShift = 0.8 * ascenderHeight;
break;
case textTop:
case beforeEdge:
case textBeforeEdge:
// Match the top of the box to the top of the parents content area.
// text-before-edge = text-top
// text-before-edge = ascender height
baselineShift = ascenderHeight;
break;
case bottom:
// Align the top of the aligned subtree with the top of the line box.
baselineShift = bottom;
break;
case center:
// Align the center of the aligned subtree with the center of the line box.
baselineShift = totalHeight / 2;
break;
case top:
// Align the bottom of the aligned subtree with the bottom of the line box.
baselineShift = top;
break;
}
}
final char[] chars = line.toCharArray(); final char[] chars = line.toCharArray();
for (int index = 0; index < length; index++) { for (int index = 0; index < length; index++) {
@@ -211,7 +325,6 @@ class TSpanShadowNode extends TextShadowNode {
adjustments are calculated as distance adjustments along the path, calculated adjustments are calculated as distance adjustments along the path, calculated
using the user agent's distance along the path algorithm. using the user agent's distance along the path algorithm.
*/ */
if (autoKerning) { if (autoKerning) {
double bothCharsWidth = paint.measureText(previous + current) * renderMethodScaling; double bothCharsWidth = paint.measureText(previous + current) * renderMethodScaling;
kerning = bothCharsWidth - previousCharWidth - charWidth; kerning = bothCharsWidth - previousCharWidth - charWidth;
@@ -272,18 +385,21 @@ class TSpanShadowNode extends TextShadowNode {
*/ */
assert pm != null; assert pm != null;
if (startpoint < 0 || endpoint > distance) { if (startpoint < 0 || endpoint > distance) {
/* /*
In the calculation above, if either the startpoint-on-the-path In the calculation above, if either the startpoint-on-the-path
or the endpoint-on-the-path is off the end of the path, or the endpoint-on-the-path is off the end of the path,
TODO then extend the path beyond its end points with a straight line TODO then extend the path beyond its end points with a straight line
that is parallel to the tangent at the path at its end point that is parallel to the tangent at the path at its end point
so that the midpoint-on-the-path can still be calculated. so that the midpoint-on-the-path can still be calculated.
TODO suggest change in wording of svg spec: TODO suggest change in wording of svg spec:
so that the midpoint-on-the-path can still be calculated so that the midpoint-on-the-path can still be calculated.
to to
so that the angle of the glyph-midline to the x-axis can still be calculated so that the angle of the glyph-midline to the x-axis can still be calculated.
*/ or
so that the line through the startpoint-on-the-path and the
endpoint-on-the-path can still be calculated.
*/
final int flags = POSITION_MATRIX_FLAG | TANGENT_MATRIX_FLAG; final int flags = POSITION_MATRIX_FLAG | TANGENT_MATRIX_FLAG;
pm.getMatrix((float) midpoint, mid, flags); pm.getMatrix((float) midpoint, mid, flags);
} else { } else {
@@ -294,127 +410,26 @@ class TSpanShadowNode extends TextShadowNode {
start.getValues(startPointMatrixData); start.getValues(startPointMatrixData);
end.getValues(endPointMatrixData); end.getValues(endPointMatrixData);
double startX = startPointMatrixData[2]; double startX = startPointMatrixData[MTRANS_X];
double startY = startPointMatrixData[5]; double startY = startPointMatrixData[MTRANS_Y];
double endX = endPointMatrixData[2]; double endX = endPointMatrixData[MTRANS_X];
double endY = endPointMatrixData[5]; double endY = endPointMatrixData[MTRANS_Y];
double glyphMidlineAngle = Math.atan2(endY - startY, endX - startX) * radToDeg; /*
line through the startpoint-on-the-path and the endpoint-on-the-path
*/
double lineX = endX - startX;
double lineY = endY - startY;
mid.preRotate((float) glyphMidlineAngle); double glyphMidlineAngle = Math.atan2(lineY, lineX);
mid.preRotate((float) (glyphMidlineAngle * radToDeg));
} }
/* /*
TODO alignment-baseline
Align the glyph vertically relative to the midpoint-on-the-path based on property Align the glyph vertically relative to the midpoint-on-the-path based on property
alignment-baseline and any specified values for attribute dy on a tspan element. alignment-baseline and any specified values for attribute dy on a tspan element.
https://developer.mozilla.org/en/docs/Web/CSS/vertical-align
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
https://www.microsoft.com/typography/otspec/base.htm
http://apike.ca/prog_svg_text_style.html
https://www.w3schools.com/tags/canvas_textbaseline.asp
http://vanseodesign.com/web-design/svg-text-baseline-alignment/
https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align
https://tympanus.net/codrops/css_reference/vertical-align/
11.10.2.6. The alignment-baseline property
This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3]
The vertical-align property shorthand should be preferred in new content.
SVG 2 introduces some changes to the definition of this property.
In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed.
For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and
'text-after-edge' should be mapped to 'text-bottom'.
Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property.
SVG implementations may support the following aliases in order to support legacy content:
TODO?
text-before-edge = text-top
text-after-edge = text-bottom
*/ */
double baselineShift = 0;
if (mAlignmentBaseline != null) {
switch (mAlignmentBaseline) {
// https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
default:
case baseline:
// Use the dominant baseline choice of the parent.
// Match the boxs corresponding baseline to that of its parent.
baselineShift = 0;
break;
case textBottom:
// Match the bottom of the box to the bottom of the parents content area.
// text-after-edge = text-bottom
// text-after-edge = descender depth
baselineShift = descent;
break;
case alphabetic:
// Match the boxs alphabetic baseline to that of its parent.
// alphabetic = 0
baselineShift = 0;
break;
case ideographic:
// Match the boxs ideographic character face under-side baseline to that of its parent.
// ideographic = descender depth
baselineShift = descent;
break;
case middle:
// Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.
// middle = x height / 2
Rect bounds = new Rect();
// this will just retrieve the bounding rect for 'x'
paint.getTextBounds("x", 0, 1, bounds);
int xHeight = bounds.height();
baselineShift = xHeight / 2;
break;
case central:
// Match the boxs central baseline to the central baseline of its parent.
// central = (ascender height - descender depth) / 2
baselineShift = (ascent - descent) / 2;
break;
case mathematical:
// Match the boxs mathematical baseline to that of its parent.
// Hanging and mathematical baselines
// There are no obvious formulas to calculate the position of these baselines.
// At the time of writing FOP puts the hanging baseline at 80% of the ascender
// height and the mathematical baseline at 50%.
baselineShift = ascent / 2;
break;
case textTop:
// Match the top of the box to the top of the parents content area.
// text-before-edge = text-top
// text-before-edge = ascender height
baselineShift = ascent;
break;
case bottom:
// Align the top of the aligned subtree with the top of the line box.
baselineShift = bottom;
break;
case center:
// Align the center of the aligned subtree with the center of the line box.
baselineShift = totalHeight / 2;
break;
case top:
// Align the bottom of the aligned subtree with the bottom of the line box.
baselineShift = top;
break;
}
}
mid.preTranslate((float) -halfway, (float) (dy - baselineShift)); mid.preTranslate((float) -halfway, (float) (dy - baselineShift));
mid.preScale((float) renderMethodScaling, (float) renderMethodScaling); mid.preScale((float) renderMethodScaling, (float) renderMethodScaling);
mid.postTranslate(0, (float) y); mid.postTranslate(0, (float) y);
@@ -483,6 +498,8 @@ class TSpanShadowNode extends TextShadowNode {
paint.setTypeface(typeface); paint.setTypeface(typeface);
paint.setTextSize((float) fontSize); paint.setTextSize((float) fontSize);
paint.setTextAlign(Paint.Align.LEFT); paint.setTextAlign(Paint.Align.LEFT);
// Do these have any effect for anyone? Not for me (@msand) at least.
paint.setUnderlineText(underlineText); paint.setUnderlineText(underlineText);
paint.setStrikeThruText(strikeThruText); paint.setStrikeThruText(strikeThruText);
} }

View File

@@ -175,7 +175,9 @@ const textLength = PropTypes.string;
/* /*
Name: alignment-baseline Name: alignment-baseline
Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
1.1 Value: auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | inherit
2.0 Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
Initial: baseline Initial: baseline
Applies to: inline-level boxes, flex items, grid items, table cells Applies to: inline-level boxes, flex items, grid items, table cells
Inherited: no Inherited: no
@@ -185,10 +187,11 @@ const textLength = PropTypes.string;
Canonical order: per grammar Canonical order: per grammar
Animation type: discrete Animation type: discrete
https://drafts.csswg.org/css-inline/#propdef-alignment-baseline https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
https://www.w3.org/TR/SVG11/text.html#AlignmentBaselineProperty
https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/alignment-baseline https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/alignment-baseline
*/ */
const alignmentBaseline = PropTypes.oneOf(['baseline', 'text-bottom', 'alphabetic', 'ideographic', 'middle', 'central', 'mathematical', 'text-top', 'bottom', 'center', 'top']); const alignmentBaseline = PropTypes.oneOf(['baseline', 'text-bottom', 'alphabetic', 'ideographic', 'middle', 'central', 'mathematical', 'text-top', 'bottom', 'center', 'top', 'text-before-edge', 'text-after-edge', 'before-edge', 'after-edge', 'hanging']);
const textSpecificProps = { const textSpecificProps = {
...pathProps, ...pathProps,