diff --git a/.eslintignore b/.eslintignore index d307ae3ee..d1163609c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,3 +9,4 @@ android/ apple/ windows/ src/lib/extract/transform.js +src/lib/extract/verticalAlign.js diff --git a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java index dccc8d900..b381409e6 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java @@ -687,11 +687,6 @@ public void setBaselineShift(K node, Dynamic baselineShift) { node.setBaselineShift(baselineShift); } - @ReactProp(name = "verticalAlign") - public void setVerticalAlign(K node, @Nullable Dynamic verticalAlign) { - node.setVerticalAlign(verticalAlign); - } - @ReactProp(name = "rotate") public void setRotate(K node, Dynamic rotate) { node.setRotate(rotate); diff --git a/android/src/main/java/com/horcrux/svg/TextView.java b/android/src/main/java/com/horcrux/svg/TextView.java index fdf5cb2a0..62a0c0c7f 100644 --- a/android/src/main/java/com/horcrux/svg/TextView.java +++ b/android/src/main/java/com/horcrux/svg/TextView.java @@ -80,28 +80,6 @@ public void setBaselineShift(Dynamic baselineShift) { invalidate(); } - public void setVerticalAlign(Dynamic dynamicVerticalAlign) { - String verticalAlign = SVGLength.toString(dynamicVerticalAlign); - if (verticalAlign != null) { - verticalAlign = verticalAlign.trim(); - int i = verticalAlign.lastIndexOf(' '); - try { - mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i)); - } catch (IllegalArgumentException e) { - mAlignmentBaseline = AlignmentBaseline.baseline; - } - try { - mBaselineShift = verticalAlign.substring(0, i); - } catch (IndexOutOfBoundsException e) { - mBaselineShift = null; - } - } else { - mAlignmentBaseline = AlignmentBaseline.baseline; - mBaselineShift = null; - } - invalidate(); - } - public void setRotate(Dynamic rotate) { mRotate = SVGLength.arrayFrom(rotate); invalidate(); diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerDelegate.java index 1c7914577..7c56d462e 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerDelegate.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerDelegate.java @@ -145,9 +145,6 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "alignmentBaseline": mViewManager.setAlignmentBaseline(view, value == null ? null : (String) value); break; - case "verticalAlign": - mViewManager.setVerticalAlign(view, new DynamicFromObject(value)); - break; case "content": mViewManager.setContent(view, value == null ? null : (String) value); break; diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerInterface.java index 7f23f5a6e..ede4ad8a1 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerInterface.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTSpanManagerInterface.java @@ -55,6 +55,5 @@ public interface RNSVGTSpanManagerInterface { void setBaselineShift(T view, Dynamic value); void setLengthAdjust(T view, @Nullable String value); void setAlignmentBaseline(T view, @Nullable String value); - void setVerticalAlign(T view, Dynamic value); void setContent(T view, @Nullable String value); } diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerDelegate.java index 14c8fea7c..3f02bd8ad 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerDelegate.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerDelegate.java @@ -145,9 +145,6 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "alignmentBaseline": mViewManager.setAlignmentBaseline(view, value == null ? null : (String) value); break; - case "verticalAlign": - mViewManager.setVerticalAlign(view, new DynamicFromObject(value)); - break; default: super.setProperty(view, propName, value); } diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerInterface.java index 2b0d3f6f4..0b039b359 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerInterface.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextManagerInterface.java @@ -55,5 +55,4 @@ public interface RNSVGTextManagerInterface { void setBaselineShift(T view, Dynamic value); void setLengthAdjust(T view, @Nullable String value); void setAlignmentBaseline(T view, @Nullable String value); - void setVerticalAlign(T view, Dynamic value); } diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerDelegate.java index 61fab1a7b..3da39ffae 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerDelegate.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerDelegate.java @@ -145,9 +145,6 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "alignmentBaseline": mViewManager.setAlignmentBaseline(view, value == null ? null : (String) value); break; - case "verticalAlign": - mViewManager.setVerticalAlign(view, new DynamicFromObject(value)); - break; case "href": mViewManager.setHref(view, value == null ? null : (String) value); break; diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerInterface.java index 9479a9fc5..f2a69ddee 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerInterface.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGTextPathManagerInterface.java @@ -55,7 +55,6 @@ public interface RNSVGTextPathManagerInterface { void setBaselineShift(T view, Dynamic value); void setLengthAdjust(T view, @Nullable String value); void setAlignmentBaseline(T view, @Nullable String value); - void setVerticalAlign(T view, Dynamic value); void setHref(T view, @Nullable String value); void setSide(T view, @Nullable String value); void setMethod(T view, @Nullable String value); diff --git a/apple/ViewManagers/RNSVGTextManager.mm b/apple/ViewManagers/RNSVGTextManager.mm index cf6aeab5f..8aed98b49 100644 --- a/apple/ViewManagers/RNSVGTextManager.mm +++ b/apple/ViewManagers/RNSVGTextManager.mm @@ -60,7 +60,6 @@ - (RNSVGRenderable *)node } RCT_EXPORT_VIEW_PROPERTY(lengthAdjust, NSString) RCT_EXPORT_VIEW_PROPERTY(alignmentBaseline, NSString) -RCT_EXPORT_VIEW_PROPERTY(verticalAlign, NSString) // unused on iOS RCT_CUSTOM_VIEW_PROPERTY(fontSize, id, RNSVGText) { diff --git a/apps/common/test/Test2475.tsx b/apps/common/test/Test2475.tsx new file mode 100644 index 000000000..9d2cb93f9 --- /dev/null +++ b/apps/common/test/Test2475.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import Svg, {Line, Text} from 'react-native-svg'; + +const testCases = [ + { + label: 'baseline', + alignmentBaseline: 'baseline', + baselineShift: '0', + }, + {label: 'middle', alignmentBaseline: 'middle', baselineShift: '0'}, + {label: 'central', alignmentBaseline: 'central', baselineShift: '0'}, + { + label: 'text-top', + alignmentBaseline: 'text-top', + baselineShift: '0', + }, + { + label: 'text-bottom', + alignmentBaseline: 'text-bottom', + baselineShift: '0', + }, + {label: 'sub', alignmentBaseline: 'baseline', baselineShift: 'sub'}, + {label: 'super', alignmentBaseline: 'baseline', baselineShift: 'super'}, + {label: '15px shift', alignmentBaseline: 'baseline', baselineShift: '15px'}, + { + label: '-15px shift', + alignmentBaseline: 'baseline', + baselineShift: '-15px', + }, +]; + +export default () => { + return ( + + {testCases.map((t, i) => { + const y = 50 + i * 50; + const verticalAlign = `${t.alignmentBaseline} ${t.baselineShift}`; + + return ( + + + + {t.label} + + + ); + })} + + ); +}; diff --git a/package.json b/package.json index ee5c9d394..44929435f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "format-js": "prettier --write README.md CONTRIBUTING.md CODE_OF_CONDUCT.md USAGE.md ./src/**/*.{ts,tsx} ./apps/**/*.{ts,tsx}", "jest": "jest", "lint": "eslint --ext .ts,.tsx src", - "peg": "pegjs -o src/lib/extract/transform.js ./src/lib/extract/transform.peg && peggy -o src/filter-image/extract/extractFiltersString.js src/filter-image/extract/extractFiltersString.pegjs && peggy -o src/lib/extract/transformToRn.js src/lib/extract/transformToRn.pegjs", + "peg": "pegjs -o src/lib/extract/transform.js ./src/lib/extract/transform.peg && peggy -o src/filter-image/extract/extractFiltersString.js src/filter-image/extract/extractFiltersString.pegjs && peggy -o src/lib/extract/transformToRn.js src/lib/extract/transformToRn.pegjs && peggy -o src/lib/extract/verticalAlign.js src/lib/extract/verticalAlign.pegjs", "prepare": "npm run bob && husky install", "release": "npm login && release-it", "test": "npm run lint && npm run tsc", diff --git a/src/fabric/TSpanNativeComponent.ts b/src/fabric/TSpanNativeComponent.ts index 1e85f40bf..db2032992 100644 --- a/src/fabric/TSpanNativeComponent.ts +++ b/src/fabric/TSpanNativeComponent.ts @@ -66,7 +66,6 @@ interface SvgTextCommonProps { baselineShift?: UnsafeMixed; lengthAdjust?: string; alignmentBaseline?: string; - verticalAlign?: UnsafeMixed; } interface NativeProps diff --git a/src/fabric/TextNativeComponent.ts b/src/fabric/TextNativeComponent.ts index 539b228b9..bec3a4056 100644 --- a/src/fabric/TextNativeComponent.ts +++ b/src/fabric/TextNativeComponent.ts @@ -66,7 +66,6 @@ interface SvgTextCommonProps { baselineShift?: UnsafeMixed; lengthAdjust?: string; alignmentBaseline?: string; - verticalAlign?: UnsafeMixed; } interface NativeProps diff --git a/src/fabric/TextPathNativeComponent.ts b/src/fabric/TextPathNativeComponent.ts index e6d630766..f034d9473 100644 --- a/src/fabric/TextPathNativeComponent.ts +++ b/src/fabric/TextPathNativeComponent.ts @@ -66,7 +66,6 @@ interface SvgTextCommonProps { baselineShift?: UnsafeMixed; lengthAdjust?: string; alignmentBaseline?: string; - verticalAlign?: UnsafeMixed; } interface NativeProps extends ViewProps, diff --git a/src/lib/extract/extractText.tsx b/src/lib/extract/extractText.tsx index 0cd72cc4b..0fca43031 100644 --- a/src/lib/extract/extractText.tsx +++ b/src/lib/extract/extractText.tsx @@ -4,6 +4,7 @@ import { Children } from 'react'; import extractLengthList from './extractLengthList'; import { pickNotNil } from '../util'; import type { NumberArray, NumberProp } from './types'; +import { parse as parseVerticalAlign } from './verticalAlign'; const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?(?:%|px|em|pt|pc|mm|cm|in]))*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i; @@ -146,18 +147,18 @@ export type TextProps = { } & fontProps; export default function extractText(props: TextProps, container: boolean) { - const { - x, - y, - dx, - dy, - rotate, - children, - inlineSize, - baselineShift, - verticalAlign, - alignmentBaseline, - } = props; + const { x, y, dx, dy, rotate, children, inlineSize, verticalAlign } = props; + let baselineShift, alignmentBaseline; + + if (verticalAlign) { + const { baselineShift: parsedShift, alignmentBaseline: parsedAlign } = + parseVerticalAlign(String(verticalAlign)); + baselineShift = parsedShift; + alignmentBaseline = parsedAlign; + } + + baselineShift = props.baselineShift ?? baselineShift; + alignmentBaseline = props.alignmentBaseline ?? alignmentBaseline; const textChildren = typeof children === 'string' || typeof children === 'number' ? ( @@ -175,7 +176,6 @@ export default function extractText(props: TextProps, container: boolean) { children: textChildren, inlineSize, baselineShift, - verticalAlign, alignmentBaseline, font: extractFont(props), x: extractLengthList(x), diff --git a/src/lib/extract/verticalAlign.d.ts b/src/lib/extract/verticalAlign.d.ts new file mode 100644 index 000000000..511728a00 --- /dev/null +++ b/src/lib/extract/verticalAlign.d.ts @@ -0,0 +1,9 @@ +import { AlignmentBaseline } from './types'; + +export function parse( + verticalAlign: string, + options?: object +): { + alignmentBaseline: AlignmentBaseline | null; + baselineShift: string | null; +}; diff --git a/src/lib/extract/verticalAlign.js b/src/lib/extract/verticalAlign.js new file mode 100644 index 000000000..dbb48f390 --- /dev/null +++ b/src/lib/extract/verticalAlign.js @@ -0,0 +1,878 @@ +// @generated by Peggy 4.0.3. +// +// https://peggyjs.org/ + +'use strict'; + +function peg$subclass(child, parent) { + function C() { + this.constructor = child; + } + C.prototype = parent.prototype; + child.prototype = new C(); +} + +function peg$SyntaxError(message, expected, found, location) { + var self = Error.call(this, message); + // istanbul ignore next Check is a necessary evil to support older environments + if (Object.setPrototypeOf) { + Object.setPrototypeOf(self, peg$SyntaxError.prototype); + } + self.expected = expected; + self.found = found; + self.location = location; + self.name = 'SyntaxError'; + return self; +} + +peg$subclass(peg$SyntaxError, Error); + +function peg$padEnd(str, targetLength, padString) { + padString = padString || ' '; + if (str.length > targetLength) { + return str; + } + targetLength -= str.length; + padString += padString.repeat(targetLength); + return str + padString.slice(0, targetLength); +} + +peg$SyntaxError.prototype.format = function (sources) { + var str = 'Error: ' + this.message; + if (this.location) { + var src = null; + var k; + for (k = 0; k < sources.length; k++) { + if (sources[k].source === this.location.source) { + src = sources[k].text.split(/\r\n|\n|\r/g); + break; + } + } + var s = this.location.start; + var offset_s = + this.location.source && typeof this.location.source.offset === 'function' + ? this.location.source.offset(s) + : s; + var loc = + this.location.source + ':' + offset_s.line + ':' + offset_s.column; + if (src) { + var e = this.location.end; + var filler = peg$padEnd('', offset_s.line.toString().length, ' '); + var line = src[s.line - 1]; + var last = s.line === e.line ? e.column : line.length + 1; + var hatLen = last - s.column || 1; + str += + '\n --> ' + + loc + + '\n' + + filler + + ' |\n' + + offset_s.line + + ' | ' + + line + + '\n' + + filler + + ' | ' + + peg$padEnd('', s.column - 1, ' ') + + peg$padEnd('', hatLen, '^'); + } else { + str += '\n at ' + loc; + } + } + return str; +}; + +peg$SyntaxError.buildMessage = function (expected, found) { + var DESCRIBE_EXPECTATION_FNS = { + literal: function (expectation) { + return '"' + literalEscape(expectation.text) + '"'; + }, + + class: function (expectation) { + var escapedParts = expectation.parts.map(function (part) { + return Array.isArray(part) + ? classEscape(part[0]) + '-' + classEscape(part[1]) + : classEscape(part); + }); + + return ( + '[' + (expectation.inverted ? '^' : '') + escapedParts.join('') + ']' + ); + }, + + any: function () { + return 'any character'; + }, + + end: function () { + return 'end of input'; + }, + + other: function (expectation) { + return expectation.description; + }, + }; + + function hex(ch) { + return ch.charCodeAt(0).toString(16).toUpperCase(); + } + + function literalEscape(s) { + return s + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\0/g, '\\0') + .replace(/\t/g, '\\t') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/[\x00-\x0F]/g, function (ch) { + return '\\x0' + hex(ch); + }) + .replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) { + return '\\x' + hex(ch); + }); + } + + function classEscape(s) { + return s + .replace(/\\/g, '\\\\') + .replace(/\]/g, '\\]') + .replace(/\^/g, '\\^') + .replace(/-/g, '\\-') + .replace(/\0/g, '\\0') + .replace(/\t/g, '\\t') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/[\x00-\x0F]/g, function (ch) { + return '\\x0' + hex(ch); + }) + .replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) { + return '\\x' + hex(ch); + }); + } + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + var descriptions = expected.map(describeExpectation); + var i, j; + + descriptions.sort(); + + if (descriptions.length > 0) { + for (i = 1, j = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + ' or ' + descriptions[1]; + + default: + return ( + descriptions.slice(0, -1).join(', ') + + ', or ' + + descriptions[descriptions.length - 1] + ); + } + } + + function describeFound(found) { + return found ? '"' + literalEscape(found) + '"' : 'end of input'; + } + + return ( + 'Expected ' + + describeExpected(expected) + + ' but ' + + describeFound(found) + + ' found.' + ); +}; + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + var peg$FAILED = {}; + var peg$source = options.grammarSource; + + var peg$startRuleFunctions = { VerticalAlign: peg$parseVerticalAlign }; + var peg$startRuleFunction = peg$parseVerticalAlign; + + var peg$c0 = 'baseline'; + var peg$c1 = 'middle'; + var peg$c2 = 'central'; + var peg$c3 = 'text-top'; + var peg$c4 = 'text-bottom'; + var peg$c5 = 'top'; + var peg$c6 = 'bottom'; + var peg$c7 = 'alphabetic'; + var peg$c8 = 'ideographic'; + var peg$c9 = 'mathematical'; + var peg$c10 = 'center'; + var peg$c11 = 'sub'; + var peg$c12 = 'super'; + var peg$c13 = '-'; + var peg$c14 = '.'; + var peg$c15 = 'px'; + var peg$c16 = 'em'; + var peg$c17 = 'rem'; + var peg$c18 = '%'; + + var peg$r0 = /^[0-9]/; + var peg$r1 = /^[ \t\r\n]/; + + var peg$e0 = peg$literalExpectation('baseline', false); + var peg$e1 = peg$literalExpectation('middle', false); + var peg$e2 = peg$literalExpectation('central', false); + var peg$e3 = peg$literalExpectation('text-top', false); + var peg$e4 = peg$literalExpectation('text-bottom', false); + var peg$e5 = peg$literalExpectation('top', false); + var peg$e6 = peg$literalExpectation('bottom', false); + var peg$e7 = peg$literalExpectation('alphabetic', false); + var peg$e8 = peg$literalExpectation('ideographic', false); + var peg$e9 = peg$literalExpectation('mathematical', false); + var peg$e10 = peg$literalExpectation('center', false); + var peg$e11 = peg$literalExpectation('sub', false); + var peg$e12 = peg$literalExpectation('super', false); + var peg$e13 = peg$literalExpectation('-', false); + var peg$e14 = peg$classExpectation([['0', '9']], false, false); + var peg$e15 = peg$literalExpectation('.', false); + var peg$e16 = peg$literalExpectation('px', false); + var peg$e17 = peg$literalExpectation('em', false); + var peg$e18 = peg$literalExpectation('rem', false); + var peg$e19 = peg$literalExpectation('%', false); + var peg$e20 = peg$classExpectation([' ', '\t', '\r', '\n'], false, false); + + var peg$f0 = function (parts) { + let alignment = null; + let shift = null; + for (const p of parts) { + if (p.type === 'alignment') alignment = p.value; + if (p.type === 'shift') shift = p.value; + } + return makeResult(alignment, shift); + }; + var peg$f1 = function (align) { + return { type: 'alignment', value: align }; + }; + var peg$f2 = function (shift) { + return { type: 'shift', value: shift }; + }; + var peg$f3 = function (sign, int, frac, unit) { + return ( + (sign || '') + + int.join('') + + (frac ? '.' + frac[1].join('') : '') + + (unit || '') + ); + }; + var peg$currPos = options.peg$currPos | 0; + var peg$savedPos = peg$currPos; + var peg$posDetailsCache = [{ line: 1, column: 1 }]; + var peg$maxFailPos = peg$currPos; + var peg$maxFailExpected = options.peg$maxFailExpected || []; + var peg$silentFails = options.peg$silentFails | 0; + + var peg$result; + + if (options.startRule) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error( + 'Can\'t start parsing from rule "' + options.startRule + '".' + ); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos, + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = + location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = + location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: 'literal', text: text, ignoreCase: ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase) { + return { + type: 'class', + parts: parts, + inverted: inverted, + ignoreCase: ignoreCase, + }; + } + + function peg$anyExpectation() { + return { type: 'any' }; + } + + function peg$endExpectation() { + return { type: 'end' }; + } + + function peg$otherExpectation(description) { + return { type: 'other', description: description }; + } + + function peg$computePosDetails(pos) { + var details = peg$posDetailsCache[pos]; + var p; + + if (details) { + return details; + } else { + if (pos >= peg$posDetailsCache.length) { + p = peg$posDetailsCache.length - 1; + } else { + p = pos; + while (!peg$posDetailsCache[--p]) {} + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column, + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos, offset) { + var startPosDetails = peg$computePosDetails(startPos); + var endPosDetails = peg$computePosDetails(endPos); + + var res = { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column, + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column, + }, + }; + if (offset && peg$source && typeof peg$source.offset === 'function') { + res.start = peg$source.offset(res.start); + res.end = peg$source.offset(res.end); + } + return res; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { + return; + } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseVerticalAlign() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parseVerticalAlignPart(); + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parseVerticalAlignPart(); + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f0(s1); + } + s0 = s1; + + return s0; + } + + function peg$parseVerticalAlignPart() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parse_(); + s2 = peg$parseAlignmentBaseline(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f1(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parse_(); + s2 = peg$parseBaselineShift(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f2(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseAlignmentBaseline() { + var s0; + + if (input.substr(peg$currPos, 8) === peg$c0) { + s0 = peg$c0; + peg$currPos += 8; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e0); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c1) { + s0 = peg$c1; + peg$currPos += 6; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e1); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c2) { + s0 = peg$c2; + peg$currPos += 7; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e2); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c3) { + s0 = peg$c3; + peg$currPos += 8; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e3); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c4) { + s0 = peg$c4; + peg$currPos += 11; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e4); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c5) { + s0 = peg$c5; + peg$currPos += 3; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e5); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c6) { + s0 = peg$c6; + peg$currPos += 6; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e6); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c7) { + s0 = peg$c7; + peg$currPos += 10; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e7); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c8) { + s0 = peg$c8; + peg$currPos += 11; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e8); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c9) { + s0 = peg$c9; + peg$currPos += 12; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e9); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c10) { + s0 = peg$c10; + peg$currPos += 6; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e10); + } + } + } + } + } + } + } + } + } + } + } + } + + return s0; + } + + function peg$parseBaselineShift() { + var s0; + + if (input.substr(peg$currPos, 3) === peg$c11) { + s0 = peg$c11; + peg$currPos += 3; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e11); + } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c12) { + s0 = peg$c12; + peg$currPos += 5; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e12); + } + } + if (s0 === peg$FAILED) { + s0 = peg$parseLength(); + } + } + + return s0; + } + + function peg$parseLength() { + var s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 45) { + s1 = peg$c13; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e13); + } + } + if (s1 === peg$FAILED) { + s1 = null; + } + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e14); + } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e14); + } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 46) { + s4 = peg$c14; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e15); + } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = input.charAt(peg$currPos); + if (peg$r0.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e14); + } + } + if (s6 !== peg$FAILED) { + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = input.charAt(peg$currPos); + if (peg$r0.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e14); + } + } + } + } else { + s5 = peg$FAILED; + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = null; + } + if (input.substr(peg$currPos, 2) === peg$c15) { + s4 = peg$c15; + peg$currPos += 2; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e16); + } + } + if (s4 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c16) { + s4 = peg$c16; + peg$currPos += 2; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e17); + } + } + if (s4 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c17) { + s4 = peg$c17; + peg$currPos += 3; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e18); + } + } + if (s4 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 37) { + s4 = peg$c18; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e19); + } + } + } + } + } + if (s4 === peg$FAILED) { + s4 = null; + } + peg$savedPos = s0; + s0 = peg$f3(s1, s2, s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parse_() { + var s0, s1; + + s0 = []; + s1 = input.charAt(peg$currPos); + if (peg$r1.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e20); + } + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = input.charAt(peg$currPos); + if (peg$r1.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$e20); + } + } + } + + return s0; + } + + function makeResult(alignment, shift) { + return { + alignmentBaseline: alignment || null, + baselineShift: shift || null, + }; + } + + peg$result = peg$startRuleFunction(); + + if (options.peg$library) { + return /** @type {any} */ ({ + peg$result, + peg$currPos, + peg$FAILED, + peg$maxFailExpected, + peg$maxFailPos, + }); + } + if (peg$result !== peg$FAILED && peg$currPos === input.length) { + return peg$result; + } else { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } +} + +module.exports = { + StartRules: ['VerticalAlign'], + SyntaxError: peg$SyntaxError, + parse: peg$parse, +}; diff --git a/src/lib/extract/verticalAlign.pegjs b/src/lib/extract/verticalAlign.pegjs new file mode 100644 index 000000000..6738131e2 --- /dev/null +++ b/src/lib/extract/verticalAlign.pegjs @@ -0,0 +1,36 @@ +{ + function makeResult(alignment, shift) { + return { + alignmentBaseline: alignment || null, + baselineShift: shift || null + }; + } +} + +VerticalAlign + = parts:VerticalAlignPart+ { + let alignment = null; + let shift = null; + for (const p of parts) { + if (p.type === "alignment") alignment = p.value; + if (p.type === "shift") shift = p.value; + } + return makeResult(alignment, shift); + } + +VerticalAlignPart + = _ align:AlignmentBaseline { return { type: "alignment", value: align }; } + / _ shift:BaselineShift { return { type: "shift", value: shift }; } + +AlignmentBaseline + = "baseline" / "middle" / "central" / "text-top" / "text-bottom" / "top" / "bottom" / "alphabetic" / "ideographic" / "mathematical" / "center" + +BaselineShift + = "sub" / "super" / Length + +Length + = sign:"-"? int:[0-9]+ frac:("." [0-9]+)? unit:("px" / "em" / "rem" / "%" )? { + return (sign || "") + int.join("") + (frac ? "." + frac[1].join("") : "") + (unit || ""); + } + +_ = [ \t\r\n]* diff --git a/src/web/types.ts b/src/web/types.ts index 81add91b1..2daf54ab8 100644 --- a/src/web/types.ts +++ b/src/web/types.ts @@ -63,4 +63,5 @@ export interface BaseProps { // different tranform props gradientTransform?: TransformProps['transform']; patternTransform?: TransformProps['transform']; + verticalAlign?: NumberProp; } diff --git a/src/web/utils/parseVerticalAlign.ts b/src/web/utils/parseVerticalAlign.ts new file mode 100644 index 000000000..c9adfe4f2 --- /dev/null +++ b/src/web/utils/parseVerticalAlign.ts @@ -0,0 +1,21 @@ +import { NumberProp } from '../../lib/extract/types'; +import { parse } from '../../lib/extract/verticalAlign'; + +export const parseVerticalAlign = (verticalAlign?: NumberProp) => { + if (!verticalAlign) { + return { alignmentBaseline: null, baselineShift: null }; + } + let { alignmentBaseline, baselineShift } = parse(String(verticalAlign)); + + // on the web 'text-bottom' have to be mapped into 'text-after-edge' + if (alignmentBaseline === 'text-bottom') { + alignmentBaseline = 'text-after-edge'; + } + + // on the web 'text-top' have to be mapped into 'text-before-edge' + if (alignmentBaseline === 'text-top') { + alignmentBaseline = 'text-before-edge'; + } + + return { alignmentBaseline, baselineShift }; +}; diff --git a/src/web/utils/prepare.ts b/src/web/utils/prepare.ts index 410678441..d6b5a9db1 100644 --- a/src/web/utils/prepare.ts +++ b/src/web/utils/prepare.ts @@ -7,8 +7,9 @@ import { WebShape } from '../WebShape'; import { hasTouchableProperty } from './hasProperty'; import { parseTransformProp } from './parseTransform'; import { resolve } from '../../lib/resolve'; -import { NumberProp } from '../../lib/extract/types'; +import { AlignmentBaseline, NumberProp } from '../../lib/extract/types'; import { resolveAssetUri } from '../../lib/resolveAssetUri'; +import { parseVerticalAlign } from './parseVerticalAlign'; /** * `react-native-svg` supports additional props that aren't defined in the spec. * This function replaces them in a spec conforming manner. @@ -36,6 +37,7 @@ export const prepare = ( gradientTransform, patternTransform, onPress, + verticalAlign, ...rest } = props; @@ -104,8 +106,21 @@ export const prepare = ( fontFamily?: string; fontSize?: NumberProp; fontWeight?: NumberProp; + alignmentBaseline?: AlignmentBaseline; + baselineShift?: NumberProp; } = {}; + const { baselineShift, alignmentBaseline } = + parseVerticalAlign(verticalAlign); + + if (baselineShift != null) { + styles.baselineShift = baselineShift; + } + + if (alignmentBaseline != null) { + styles.alignmentBaseline = alignmentBaseline; + } + if (fontFamily != null) { styles.fontFamily = fontFamily; }