mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-08-16 02:44:58 +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 LETTER_SPACING = "letterSpacing";
|
||||
private static final String TEXT_DECORATION = "textDecoration";
|
||||
private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings";
|
||||
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
|
||||
|
||||
final double fontSize;
|
||||
@@ -27,6 +28,7 @@ class FontData {
|
||||
final FontStyle fontStyle;
|
||||
final ReadableMap fontData;
|
||||
final FontWeight fontWeight;
|
||||
final String fontFeatureSettings;
|
||||
final FontVariantLigatures fontVariantLigatures;
|
||||
|
||||
final TextAnchor textAnchor;
|
||||
@@ -45,6 +47,7 @@ class FontData {
|
||||
fontFamily = "";
|
||||
fontStyle = FontStyle.normal;
|
||||
fontWeight = FontWeight.Normal;
|
||||
fontFeatureSettings = "";
|
||||
fontVariantLigatures = FontVariantLigatures.normal;
|
||||
|
||||
textAnchor = TextAnchor.start;
|
||||
@@ -88,6 +91,7 @@ class FontData {
|
||||
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
|
||||
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;
|
||||
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;
|
||||
|
||||
textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor;
|
||||
|
||||
@@ -223,12 +223,41 @@ class TSpanShadowNode extends TextShadowNode {
|
||||
Media: visual
|
||||
Computed value: as specified
|
||||
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) {
|
||||
String required = "'rlig', 'liga', 'clig', 'calt', 'locl', 'ccmp', 'mark', 'mkmk',";
|
||||
String defaultFeatures = required + "'kern', ";
|
||||
if (allowOptionalLigatures) {
|
||||
paint.setFontFeatureSettings("'kern', 'liga', 'clig', 'dlig', 'hlig', 'cala', 'rlig'");
|
||||
String additionalLigatures = "'hlig', 'cala', ";
|
||||
paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings);
|
||||
} 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
|
||||
@@ -601,10 +630,32 @@ class TSpanShadowNode extends TextShadowNode {
|
||||
switch (baselineShiftString) {
|
||||
case "sub":
|
||||
// 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;
|
||||
|
||||
case "super":
|
||||
// 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;
|
||||
|
||||
case "baseline":
|
||||
@@ -625,9 +676,6 @@ class TSpanShadowNode extends TextShadowNode {
|
||||
final float[] startPointMatrixData = new float[9];
|
||||
final float[] endPointMatrixData = new float[9];
|
||||
|
||||
String previous = "";
|
||||
double previousCharWidth = 0;
|
||||
|
||||
for (int index = 0; index < length; index++) {
|
||||
char currentChar = chars[index];
|
||||
String current = String.valueOf(currentChar);
|
||||
@@ -638,23 +686,21 @@ class TSpanShadowNode extends TextShadowNode {
|
||||
advances horizontally when the glyph is drawn using horizontal text layout).
|
||||
*/
|
||||
boolean hasLigature = false;
|
||||
if (allowOptionalLigatures) {
|
||||
if (alreadyRenderedGraphemeCluster) {
|
||||
current = "";
|
||||
} else {
|
||||
int nextIndex = index;
|
||||
while (++nextIndex < length) {
|
||||
float nextWidth = advances[nextIndex];
|
||||
if (nextWidth > 0) {
|
||||
break;
|
||||
}
|
||||
String nextLigature = current + String.valueOf(chars[nextIndex]);
|
||||
boolean hasNextLigature = PaintCompat.hasGlyph(paint, nextLigature);
|
||||
if (hasNextLigature) {
|
||||
ligature[nextIndex] = true;
|
||||
current = nextLigature;
|
||||
hasLigature = true;
|
||||
}
|
||||
if (alreadyRenderedGraphemeCluster) {
|
||||
current = "";
|
||||
} else {
|
||||
int nextIndex = index;
|
||||
while (++nextIndex < length) {
|
||||
float nextWidth = advances[nextIndex];
|
||||
if (nextWidth > 0) {
|
||||
break;
|
||||
}
|
||||
String nextLigature = current + String.valueOf(chars[nextIndex]);
|
||||
boolean hasNextLigature = PaintCompat.hasGlyph(paint, nextLigature);
|
||||
if (hasNextLigature) {
|
||||
ligature[nextIndex] = true;
|
||||
current = nextLigature;
|
||||
hasLigature = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,16 +716,8 @@ class TSpanShadowNode extends TextShadowNode {
|
||||
using the user agent's distance along the path algorithm.
|
||||
*/
|
||||
if (autoKerning) {
|
||||
if (allowOptionalLigatures) {
|
||||
double kerned = advances[index] * scaleSpacingAndGlyphs;
|
||||
kerning = kerned - charWidth;
|
||||
} else {
|
||||
double bothCharsWidth = paint.measureText(previous + current) * scaleSpacingAndGlyphs;
|
||||
double kerned = bothCharsWidth - previousCharWidth;
|
||||
kerning = kerned - charWidth;
|
||||
previousCharWidth = charWidth;
|
||||
previous = current;
|
||||
}
|
||||
double kerned = advances[index] * scaleSpacingAndGlyphs;
|
||||
kerning = kerned - charWidth;
|
||||
}
|
||||
|
||||
boolean isWordSeparator = currentChar == ' ';
|
||||
@@ -699,8 +737,8 @@ class TSpanShadowNode extends TextShadowNode {
|
||||
continue;
|
||||
}
|
||||
|
||||
advance = advance * side;
|
||||
charWidth = charWidth * side;
|
||||
advance *= side;
|
||||
charWidth *= side;
|
||||
double cursor = offset + (x + dx) * side;
|
||||
double startPoint = cursor - advance;
|
||||
|
||||
|
||||
+2
-1
@@ -32,7 +32,8 @@ function fontDiffer(a, b) {
|
||||
a.wordSpacing !== b.wordSpacing ||
|
||||
a.kerning !== b.kerning ||
|
||||
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,
|
||||
kerning,
|
||||
fontVariantLigatures,
|
||||
fontFeatureSettings,
|
||||
} = props;
|
||||
let {
|
||||
fontSize,
|
||||
@@ -80,6 +81,7 @@ export function extractFont(props) {
|
||||
wordSpacing,
|
||||
kerning,
|
||||
fontVariantLigatures,
|
||||
fontFeatureSettings,
|
||||
}, prop => !_.isNil(prop));
|
||||
|
||||
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]);
|
||||
|
||||
/*
|
||||
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 = {
|
||||
...pathProps,
|
||||
...fontProps,
|
||||
@@ -269,6 +369,7 @@ const textSpecificProps = {
|
||||
lengthAdjust,
|
||||
textLength,
|
||||
fontData: PropTypes.object,
|
||||
fontFeatureSettings,
|
||||
};
|
||||
|
||||
// https://svgwg.org/svg2-draft/text.html#TSpanAttributes
|
||||
|
||||
Reference in New Issue
Block a user