mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-06 16:32:24 +00:00
Implement default and required css 3 fonts features, fontFeatureSettings, sub-/super- script baseline-shift.
Activate OpenType localized forms and features required for proper display of composed characters and marks.
This commit is contained in:
@@ -20,6 +20,7 @@ class FontData {
|
|||||||
private static final String WORD_SPACING = "wordSpacing";
|
private static final String WORD_SPACING = "wordSpacing";
|
||||||
private static final String LETTER_SPACING = "letterSpacing";
|
private static final String LETTER_SPACING = "letterSpacing";
|
||||||
private static final String TEXT_DECORATION = "textDecoration";
|
private static final String TEXT_DECORATION = "textDecoration";
|
||||||
|
private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings";
|
||||||
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
|
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
|
||||||
|
|
||||||
final double fontSize;
|
final double fontSize;
|
||||||
@@ -27,6 +28,7 @@ class FontData {
|
|||||||
final FontStyle fontStyle;
|
final FontStyle fontStyle;
|
||||||
final ReadableMap fontData;
|
final ReadableMap fontData;
|
||||||
final FontWeight fontWeight;
|
final FontWeight fontWeight;
|
||||||
|
final String fontFeatureSettings;
|
||||||
final FontVariantLigatures fontVariantLigatures;
|
final FontVariantLigatures fontVariantLigatures;
|
||||||
|
|
||||||
final TextAnchor textAnchor;
|
final TextAnchor textAnchor;
|
||||||
@@ -45,6 +47,7 @@ class FontData {
|
|||||||
fontFamily = "";
|
fontFamily = "";
|
||||||
fontStyle = FontStyle.normal;
|
fontStyle = FontStyle.normal;
|
||||||
fontWeight = FontWeight.Normal;
|
fontWeight = FontWeight.Normal;
|
||||||
|
fontFeatureSettings = "";
|
||||||
fontVariantLigatures = FontVariantLigatures.normal;
|
fontVariantLigatures = FontVariantLigatures.normal;
|
||||||
|
|
||||||
textAnchor = TextAnchor.start;
|
textAnchor = TextAnchor.start;
|
||||||
@@ -88,6 +91,7 @@ class FontData {
|
|||||||
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
|
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
|
||||||
fontStyle = font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
|
fontStyle = font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
|
||||||
fontWeight = font.hasKey(FONT_WEIGHT) ? FontWeight.getEnum(font.getString(FONT_WEIGHT)) : parent.fontWeight;
|
fontWeight = font.hasKey(FONT_WEIGHT) ? FontWeight.getEnum(font.getString(FONT_WEIGHT)) : parent.fontWeight;
|
||||||
|
fontFeatureSettings = font.hasKey(FONT_FEATURE_SETTINGS) ? font.getString(FONT_FEATURE_SETTINGS) : parent.fontFeatureSettings;
|
||||||
fontVariantLigatures = font.hasKey(FONT_VARIANT_LIGATURES) ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) : parent.fontVariantLigatures;
|
fontVariantLigatures = font.hasKey(FONT_VARIANT_LIGATURES) ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) : parent.fontVariantLigatures;
|
||||||
|
|
||||||
textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor;
|
textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor;
|
||||||
|
|||||||
@@ -223,12 +223,41 @@ class TSpanShadowNode extends TextShadowNode {
|
|||||||
Media: visual
|
Media: visual
|
||||||
Computed value: as specified
|
Computed value: as specified
|
||||||
Animatable: no
|
Animatable: no
|
||||||
|
|
||||||
|
https://drafts.csswg.org/css-fonts-3/#default-features
|
||||||
|
|
||||||
|
7.1. Default features
|
||||||
|
|
||||||
|
For OpenType fonts, user agents must enable the default features defined in the OpenType
|
||||||
|
documentation for a given script and writing mode.
|
||||||
|
|
||||||
|
Required ligatures, common ligatures and contextual forms must be enabled by default
|
||||||
|
(OpenType features: rlig, liga, clig, calt),
|
||||||
|
along with localized forms (OpenType feature: locl),
|
||||||
|
and features required for proper display of composed characters and marks
|
||||||
|
(OpenType features: ccmp, mark, mkmk).
|
||||||
|
|
||||||
|
These features must always be enabled, even when the value of the ‘font-variant’ and
|
||||||
|
‘font-feature-settings’ properties is ‘normal’.
|
||||||
|
|
||||||
|
Individual features are only disabled when explicitly overridden by the author,
|
||||||
|
as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’.
|
||||||
|
|
||||||
|
TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features
|
||||||
|
are required.
|
||||||
|
|
||||||
|
TODO For upright text within vertical text runs,
|
||||||
|
vertical alternates (OpenType feature: vert) must be enabled.
|
||||||
*/
|
*/
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
String required = "'rlig', 'liga', 'clig', 'calt', 'locl', 'ccmp', 'mark', 'mkmk',";
|
||||||
|
String defaultFeatures = required + "'kern', ";
|
||||||
if (allowOptionalLigatures) {
|
if (allowOptionalLigatures) {
|
||||||
paint.setFontFeatureSettings("'kern', 'liga', 'clig', 'dlig', 'hlig', 'cala', 'rlig'");
|
String additionalLigatures = "'hlig', 'cala', ";
|
||||||
|
paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings);
|
||||||
} else {
|
} else {
|
||||||
paint.setFontFeatureSettings("'kern', 'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, 'rlig'");
|
String disableDiscretionaryLigatures = "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, ";
|
||||||
|
paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// OpenType.js font data
|
// OpenType.js font data
|
||||||
@@ -601,10 +630,32 @@ class TSpanShadowNode extends TextShadowNode {
|
|||||||
switch (baselineShiftString) {
|
switch (baselineShiftString) {
|
||||||
case "sub":
|
case "sub":
|
||||||
// TODO
|
// TODO
|
||||||
|
if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) {
|
||||||
|
int unitsPerEm = fontData.getInt("unitsPerEm");
|
||||||
|
ReadableMap tables = fontData.getMap("tables");
|
||||||
|
if (tables.hasKey("os2")) {
|
||||||
|
ReadableMap os2 = tables.getMap("os2");
|
||||||
|
if (os2.hasKey("ySubscriptYOffset")) {
|
||||||
|
double subOffset = os2.getDouble("ySubscriptYOffset");
|
||||||
|
baselineShift += fontSize * subOffset / unitsPerEm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "super":
|
case "super":
|
||||||
// TODO
|
// TODO
|
||||||
|
if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) {
|
||||||
|
int unitsPerEm = fontData.getInt("unitsPerEm");
|
||||||
|
ReadableMap tables = fontData.getMap("tables");
|
||||||
|
if (tables.hasKey("os2")) {
|
||||||
|
ReadableMap os2 = tables.getMap("os2");
|
||||||
|
if (os2.hasKey("ySuperscriptYOffset")) {
|
||||||
|
double superOffset = os2.getDouble("ySuperscriptYOffset");
|
||||||
|
baselineShift -= fontSize * superOffset / unitsPerEm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "baseline":
|
case "baseline":
|
||||||
@@ -625,9 +676,6 @@ class TSpanShadowNode extends TextShadowNode {
|
|||||||
final float[] startPointMatrixData = new float[9];
|
final float[] startPointMatrixData = new float[9];
|
||||||
final float[] endPointMatrixData = new float[9];
|
final float[] endPointMatrixData = new float[9];
|
||||||
|
|
||||||
String previous = "";
|
|
||||||
double previousCharWidth = 0;
|
|
||||||
|
|
||||||
for (int index = 0; index < length; index++) {
|
for (int index = 0; index < length; index++) {
|
||||||
char currentChar = chars[index];
|
char currentChar = chars[index];
|
||||||
String current = String.valueOf(currentChar);
|
String current = String.valueOf(currentChar);
|
||||||
@@ -638,23 +686,21 @@ class TSpanShadowNode extends TextShadowNode {
|
|||||||
advances horizontally when the glyph is drawn using horizontal text layout).
|
advances horizontally when the glyph is drawn using horizontal text layout).
|
||||||
*/
|
*/
|
||||||
boolean hasLigature = false;
|
boolean hasLigature = false;
|
||||||
if (allowOptionalLigatures) {
|
if (alreadyRenderedGraphemeCluster) {
|
||||||
if (alreadyRenderedGraphemeCluster) {
|
current = "";
|
||||||
current = "";
|
} else {
|
||||||
} else {
|
int nextIndex = index;
|
||||||
int nextIndex = index;
|
while (++nextIndex < length) {
|
||||||
while (++nextIndex < length) {
|
float nextWidth = advances[nextIndex];
|
||||||
float nextWidth = advances[nextIndex];
|
if (nextWidth > 0) {
|
||||||
if (nextWidth > 0) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
String nextLigature = current + String.valueOf(chars[nextIndex]);
|
||||||
String nextLigature = current + String.valueOf(chars[nextIndex]);
|
boolean hasNextLigature = PaintCompat.hasGlyph(paint, nextLigature);
|
||||||
boolean hasNextLigature = PaintCompat.hasGlyph(paint, nextLigature);
|
if (hasNextLigature) {
|
||||||
if (hasNextLigature) {
|
ligature[nextIndex] = true;
|
||||||
ligature[nextIndex] = true;
|
current = nextLigature;
|
||||||
current = nextLigature;
|
hasLigature = true;
|
||||||
hasLigature = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,16 +716,8 @@ class TSpanShadowNode extends TextShadowNode {
|
|||||||
using the user agent's distance along the path algorithm.
|
using the user agent's distance along the path algorithm.
|
||||||
*/
|
*/
|
||||||
if (autoKerning) {
|
if (autoKerning) {
|
||||||
if (allowOptionalLigatures) {
|
double kerned = advances[index] * scaleSpacingAndGlyphs;
|
||||||
double kerned = advances[index] * scaleSpacingAndGlyphs;
|
kerning = kerned - charWidth;
|
||||||
kerning = kerned - charWidth;
|
|
||||||
} else {
|
|
||||||
double bothCharsWidth = paint.measureText(previous + current) * scaleSpacingAndGlyphs;
|
|
||||||
double kerned = bothCharsWidth - previousCharWidth;
|
|
||||||
kerning = kerned - charWidth;
|
|
||||||
previousCharWidth = charWidth;
|
|
||||||
previous = current;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isWordSeparator = currentChar == ' ';
|
boolean isWordSeparator = currentChar == ' ';
|
||||||
@@ -699,8 +737,8 @@ class TSpanShadowNode extends TextShadowNode {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
advance = advance * side;
|
advance *= side;
|
||||||
charWidth = charWidth * side;
|
charWidth *= side;
|
||||||
double cursor = offset + (x + dx) * side;
|
double cursor = offset + (x + dx) * side;
|
||||||
double startPoint = cursor - advance;
|
double startPoint = cursor - advance;
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -32,7 +32,8 @@ function fontDiffer(a, b) {
|
|||||||
a.wordSpacing !== b.wordSpacing ||
|
a.wordSpacing !== b.wordSpacing ||
|
||||||
a.kerning !== b.kerning ||
|
a.kerning !== b.kerning ||
|
||||||
a.fontVariantLigatures !== b.fontVariantLigatures ||
|
a.fontVariantLigatures !== b.fontVariantLigatures ||
|
||||||
a.fontData !== b.fontData
|
a.fontData !== b.fontData ||
|
||||||
|
a.fontFeatureSettings !== b.fontFeatureSettings
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export function extractFont(props) {
|
|||||||
wordSpacing,
|
wordSpacing,
|
||||||
kerning,
|
kerning,
|
||||||
fontVariantLigatures,
|
fontVariantLigatures,
|
||||||
|
fontFeatureSettings,
|
||||||
} = props;
|
} = props;
|
||||||
let {
|
let {
|
||||||
fontSize,
|
fontSize,
|
||||||
@@ -80,6 +81,7 @@ export function extractFont(props) {
|
|||||||
wordSpacing,
|
wordSpacing,
|
||||||
kerning,
|
kerning,
|
||||||
fontVariantLigatures,
|
fontVariantLigatures,
|
||||||
|
fontFeatureSettings,
|
||||||
}, prop => !_.isNil(prop));
|
}, prop => !_.isNil(prop));
|
||||||
|
|
||||||
if (typeof font === 'string') {
|
if (typeof font === 'string') {
|
||||||
|
|||||||
+101
@@ -260,6 +260,106 @@ const alignmentBaseline = PropTypes.oneOf(['baseline', 'text-bottom', 'alphabeti
|
|||||||
*/
|
*/
|
||||||
const baselineShift = PropTypes.oneOfType([PropTypes.oneOf(['sub', 'super', 'baseline']), PropTypes.arrayOf(numberProp), PropTypes.string]);
|
const baselineShift = PropTypes.oneOfType([PropTypes.oneOf(['sub', 'super', 'baseline']), PropTypes.arrayOf(numberProp), PropTypes.string]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
6.12. Low-level font feature settings control: the font-feature-settings property
|
||||||
|
|
||||||
|
Name: font-feature-settings
|
||||||
|
Value: normal | <feature-tag-value> #
|
||||||
|
Initial: normal
|
||||||
|
Applies to: all elements
|
||||||
|
Inherited: yes
|
||||||
|
Percentages: N/A
|
||||||
|
Media: visual
|
||||||
|
Computed value: as specified
|
||||||
|
Animatable: no
|
||||||
|
|
||||||
|
This property provides low-level control over OpenType font features.
|
||||||
|
|
||||||
|
It is intended as a way of providing access to font features
|
||||||
|
that are not widely used but are needed for a particular use case.
|
||||||
|
|
||||||
|
Authors should generally use ‘font-variant’ and its related subproperties
|
||||||
|
whenever possible and only use this property for special cases where its use
|
||||||
|
is the only way of accessing a particular infrequently used font feature.
|
||||||
|
|
||||||
|
enable small caps and use second swash alternate
|
||||||
|
font-feature-settings: "smcp", "swsh" 2;
|
||||||
|
A value of ‘normal’ means that no change in glyph selection or positioning occurs due to this property.
|
||||||
|
|
||||||
|
Feature tag values have the following syntax:
|
||||||
|
|
||||||
|
<feature-tag-value> = <string> [ <integer> | on | off ]?
|
||||||
|
The <string> is a case-sensitive OpenType feature tag. As specified in the OpenType specification,
|
||||||
|
feature tags contain four ASCII characters.
|
||||||
|
|
||||||
|
Tag strings longer or shorter than four characters,
|
||||||
|
or containing characters outside the U+20–7E codepoint range are invalid.
|
||||||
|
|
||||||
|
Feature tags need only match a feature tag defined in the font,
|
||||||
|
so they are not limited to explicitly registered OpenType features.
|
||||||
|
|
||||||
|
Fonts defining custom feature tags should follow the tag name rules
|
||||||
|
defined in the OpenType specification [OPENTYPE-FEATURES].
|
||||||
|
|
||||||
|
Feature tags not present in the font are ignored;
|
||||||
|
a user agent must not attempt to synthesize fallback behavior based on these feature tags.
|
||||||
|
|
||||||
|
The one exception is that user agents may synthetically support the kern feature with fonts
|
||||||
|
that contain kerning data in the form of a ‘kern’ table but lack kern feature support in the ‘GPOS’ table.
|
||||||
|
|
||||||
|
In general, authors should use the ‘font-kerning’ property to explicitly enable or disable kerning
|
||||||
|
since this property always affects fonts with either type of kerning data.
|
||||||
|
|
||||||
|
If present, a value indicates an index used for glyph selection.
|
||||||
|
|
||||||
|
An <integer> value must be 0 or greater.
|
||||||
|
|
||||||
|
A value of 0 indicates that the feature is disabled.
|
||||||
|
|
||||||
|
For boolean features, a value of 1 enables the feature.
|
||||||
|
|
||||||
|
For non-boolean features, a value of 1 or greater enables the feature and indicates the feature selection index.
|
||||||
|
|
||||||
|
A value of ‘on’ is synonymous with 1 and ‘off’ is synonymous with 0.
|
||||||
|
|
||||||
|
If the value is omitted, a value of 1 is assumed.
|
||||||
|
|
||||||
|
font-feature-settings: "dlig" 1; /* dlig=1 enable discretionary ligatures * /
|
||||||
|
font-feature-settings: "smcp" on; /* smcp=1 enable small caps * /
|
||||||
|
font-feature-settings: 'c2sc'; /* c2sc=1 enable caps to small caps * /
|
||||||
|
font-feature-settings: "liga" off; /* liga=0 no common ligatures * /
|
||||||
|
font-feature-settings: "tnum", 'hist'; /* tnum=1, hist=1 enable tabular numbers and historical forms * /
|
||||||
|
font-feature-settings: "tnum" "hist"; /* invalid, need a comma-delimited list * /
|
||||||
|
font-feature-settings: "silly" off; /* invalid, tag too long * /
|
||||||
|
font-feature-settings: "PKRN"; /* PKRN=1 enable custom feature * /
|
||||||
|
font-feature-settings: dlig; /* invalid, tag must be a string * /
|
||||||
|
|
||||||
|
When values greater than the range supported by the font are specified, the behavior is explicitly undefined.
|
||||||
|
|
||||||
|
For boolean features, in general these will enable the feature.
|
||||||
|
|
||||||
|
For non-boolean features, out of range values will in general be equivalent to a 0 value.
|
||||||
|
|
||||||
|
However, in both cases the exact behavior will depend upon the way the font is designed
|
||||||
|
(specifically, which type of lookup is used to define the feature).
|
||||||
|
|
||||||
|
Although specifically defined for OpenType feature tags,
|
||||||
|
feature tags for other modern font formats that support font features may be added in the future.
|
||||||
|
|
||||||
|
Where possible, features defined for other font formats
|
||||||
|
should attempt to follow the pattern of registered OpenType tags.
|
||||||
|
|
||||||
|
The Japanese text below will be rendered with half-width kana characters:
|
||||||
|
|
||||||
|
body { font-feature-settings: "hwid"; /* Half-width OpenType feature * / }
|
||||||
|
|
||||||
|
<p>毎日カレー食べてるのに、飽きない</p>
|
||||||
|
|
||||||
|
https://drafts.csswg.org/css-fonts-3/#propdef-font-feature-settings
|
||||||
|
https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings
|
||||||
|
*/
|
||||||
|
const fontFeatureSettings = PropTypes.string;
|
||||||
|
|
||||||
const textSpecificProps = {
|
const textSpecificProps = {
|
||||||
...pathProps,
|
...pathProps,
|
||||||
...fontProps,
|
...fontProps,
|
||||||
@@ -269,6 +369,7 @@ const textSpecificProps = {
|
|||||||
lengthAdjust,
|
lengthAdjust,
|
||||||
textLength,
|
textLength,
|
||||||
fontData: PropTypes.object,
|
fontData: PropTypes.object,
|
||||||
|
fontFeatureSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://svgwg.org/svg2-draft/text.html#TSpanAttributes
|
// https://svgwg.org/svg2-draft/text.html#TSpanAttributes
|
||||||
|
|||||||
Reference in New Issue
Block a user