mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 22:05:14 +00:00
Implement rotate and textDecoration
This commit is contained in:
@@ -28,10 +28,13 @@ public class GlyphContext {
|
|||||||
private ArrayList<PointF> mDeltaContext;
|
private ArrayList<PointF> mDeltaContext;
|
||||||
private ArrayList<ArrayList<Float>> mDeltaXContext;
|
private ArrayList<ArrayList<Float>> mDeltaXContext;
|
||||||
private ArrayList<ArrayList<Float>> mDeltaYContext;
|
private ArrayList<ArrayList<Float>> mDeltaYContext;
|
||||||
|
private ArrayList<ArrayList<Float>> mRotationContext;
|
||||||
|
private ArrayList<Float> mGlyphRotationContext;
|
||||||
private ArrayList<Float> mXContext;
|
private ArrayList<Float> mXContext;
|
||||||
private ArrayList<Float> mYContext;
|
private ArrayList<Float> mYContext;
|
||||||
private @Nonnull PointF mCurrentLocation;
|
private @Nonnull PointF mCurrentLocation;
|
||||||
private @Nonnull PointF mCurrentDelta;
|
private @Nonnull PointF mCurrentDelta;
|
||||||
|
private float mRotation = 0;
|
||||||
private float mScale;
|
private float mScale;
|
||||||
private float mWidth;
|
private float mWidth;
|
||||||
private float mHeight;
|
private float mHeight;
|
||||||
@@ -51,6 +54,8 @@ public class GlyphContext {
|
|||||||
mDeltaContext = new ArrayList<>();
|
mDeltaContext = new ArrayList<>();
|
||||||
mDeltaXContext = new ArrayList<>();
|
mDeltaXContext = new ArrayList<>();
|
||||||
mDeltaYContext = new ArrayList<>();
|
mDeltaYContext = new ArrayList<>();
|
||||||
|
mRotationContext = new ArrayList<>();
|
||||||
|
mGlyphRotationContext = new ArrayList<>();
|
||||||
mXContext = new ArrayList<>();
|
mXContext = new ArrayList<>();
|
||||||
mYContext = new ArrayList<>();
|
mYContext = new ArrayList<>();
|
||||||
}
|
}
|
||||||
@@ -66,6 +71,8 @@ public class GlyphContext {
|
|||||||
mFontContext.add(font);
|
mFontContext.add(font);
|
||||||
mDeltaXContext.add(new ArrayList<Float>());
|
mDeltaXContext.add(new ArrayList<Float>());
|
||||||
mDeltaYContext.add(new ArrayList<Float>());
|
mDeltaYContext.add(new ArrayList<Float>());
|
||||||
|
mRotationContext.add(new ArrayList<Float>());
|
||||||
|
mGlyphRotationContext.add(mRotation);
|
||||||
mXContext.add(location.x);
|
mXContext.add(location.x);
|
||||||
mYContext.add(location.y);
|
mYContext.add(location.y);
|
||||||
|
|
||||||
@@ -73,7 +80,7 @@ public class GlyphContext {
|
|||||||
mContextLength++;
|
mContextLength++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushContext(@Nullable ReadableMap font, @Nullable ReadableArray deltaX, @Nullable ReadableArray deltaY, @Nullable String positionX, @Nullable String positionY) {
|
public void pushContext(@Nullable ReadableMap font, @Nullable ReadableArray rotate, @Nullable ReadableArray deltaX, @Nullable ReadableArray deltaY, @Nullable String positionX, @Nullable String positionY) {
|
||||||
PointF location = mCurrentLocation;
|
PointF location = mCurrentLocation;
|
||||||
|
|
||||||
mDeltaContext.add(mCurrentDelta);
|
mDeltaContext.add(mCurrentDelta);
|
||||||
@@ -94,6 +101,8 @@ public class GlyphContext {
|
|||||||
mFontContext.add(font);
|
mFontContext.add(font);
|
||||||
mDeltaXContext.add(getFloatArrayListFromReadableArray(deltaX));
|
mDeltaXContext.add(getFloatArrayListFromReadableArray(deltaX));
|
||||||
mDeltaYContext.add(getFloatArrayListFromReadableArray(deltaY));
|
mDeltaYContext.add(getFloatArrayListFromReadableArray(deltaY));
|
||||||
|
mRotationContext.add(getFloatArrayListFromReadableArray(rotate));
|
||||||
|
mGlyphRotationContext.add(mRotation);
|
||||||
mXContext.add(location.x);
|
mXContext.add(location.x);
|
||||||
mYContext.add(location.y);
|
mYContext.add(location.y);
|
||||||
|
|
||||||
@@ -104,11 +113,14 @@ public class GlyphContext {
|
|||||||
public void popContext() {
|
public void popContext() {
|
||||||
float x = mXContext.get(mContextLength - 1);
|
float x = mXContext.get(mContextLength - 1);
|
||||||
float y = mYContext.get(mContextLength - 1);
|
float y = mYContext.get(mContextLength - 1);
|
||||||
|
float r = mGlyphRotationContext.get(mContextLength - 1);
|
||||||
mFontContext.remove(mContextLength - 1);
|
mFontContext.remove(mContextLength - 1);
|
||||||
mLocationContext.remove(mContextLength - 1);
|
mLocationContext.remove(mContextLength - 1);
|
||||||
mDeltaContext.remove(mContextLength - 1);
|
mDeltaContext.remove(mContextLength - 1);
|
||||||
mDeltaXContext.remove(mContextLength - 1);
|
mDeltaXContext.remove(mContextLength - 1);
|
||||||
mDeltaYContext.remove(mContextLength - 1);
|
mDeltaYContext.remove(mContextLength - 1);
|
||||||
|
mRotationContext.remove(mContextLength - 1);
|
||||||
|
mGlyphRotationContext.remove(mContextLength - 1);
|
||||||
mXContext.remove(mContextLength - 1);
|
mXContext.remove(mContextLength - 1);
|
||||||
mYContext.remove(mContextLength - 1);
|
mYContext.remove(mContextLength - 1);
|
||||||
|
|
||||||
@@ -117,12 +129,14 @@ public class GlyphContext {
|
|||||||
if (mContextLength != 0) {
|
if (mContextLength != 0) {
|
||||||
mXContext.set(mContextLength - 1, x);
|
mXContext.set(mContextLength - 1, x);
|
||||||
mYContext.set(mContextLength - 1, y);
|
mYContext.set(mContextLength - 1, y);
|
||||||
|
mGlyphRotationContext.set(mContextLength - 1, r);
|
||||||
PointF lastLocation = mLocationContext.get(mContextLength - 1);
|
PointF lastLocation = mLocationContext.get(mContextLength - 1);
|
||||||
PointF lastDelta = mDeltaContext.get(mContextLength - 1);
|
PointF lastDelta = mDeltaContext.get(mContextLength - 1);
|
||||||
mCurrentLocation = clonePointF(lastLocation);
|
mCurrentLocation = clonePointF(lastLocation);
|
||||||
mCurrentDelta = clonePointF(lastDelta);
|
mCurrentDelta = clonePointF(lastDelta);
|
||||||
mCurrentLocation.x = lastLocation.x = x;
|
mCurrentLocation.x = lastLocation.x = x;
|
||||||
mCurrentLocation.y = lastLocation.y = y;
|
mCurrentLocation.y = lastLocation.y = y;
|
||||||
|
mRotation = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,8 +148,8 @@ public class GlyphContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PointF getNextGlyphDelta() {
|
public PointF getNextGlyphDelta() {
|
||||||
float dx = mScale * getNextDelta(mDeltaXContext);
|
float dx = mScale * getNextFloat(mDeltaXContext);
|
||||||
float dy = mScale * getNextDelta(mDeltaYContext);
|
float dy = mScale * getNextFloat(mDeltaYContext);
|
||||||
|
|
||||||
if (mContextLength > 0) {
|
if (mContextLength > 0) {
|
||||||
for (PointF point: mDeltaContext) {
|
for (PointF point: mDeltaContext) {
|
||||||
@@ -149,13 +163,50 @@ public class GlyphContext {
|
|||||||
return new PointF(dx, dy);
|
return new PointF(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getNextDelta(ArrayList<ArrayList<Float>> deltaContext) {
|
public float getNextGlyphRotation() {
|
||||||
float value = 0;
|
if (hasNextFloat(mRotationContext)) {
|
||||||
|
float r = getNextFloat(mRotationContext);
|
||||||
|
|
||||||
|
if (mContextLength > 0) {
|
||||||
|
for (int i = 0; i < mContextLength; i++) {
|
||||||
|
mGlyphRotationContext.set(i, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mGlyphRotationContext.get(mContextLength - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
} else if (mContextLength > 0) {
|
||||||
|
return mGlyphRotationContext.get(mContextLength - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getNextFloat(ArrayList<ArrayList<Float>> context) {
|
||||||
|
return getNextFloat(context, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasNextFloat(ArrayList<ArrayList<Float>> context) {
|
||||||
|
int index = mContextLength - 1;
|
||||||
|
|
||||||
|
for (; index >= 0; index--) {
|
||||||
|
ArrayList<Float> delta = context.get(index);
|
||||||
|
|
||||||
|
if (delta.size() != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getNextFloat(ArrayList<ArrayList<Float>> context, float value) {
|
||||||
boolean valueSet = false;
|
boolean valueSet = false;
|
||||||
int index = mContextLength - 1;
|
int index = mContextLength - 1;
|
||||||
|
|
||||||
for (; index >= 0; index--) {
|
for (; index >= 0; index--) {
|
||||||
ArrayList<Float> delta = deltaContext.get(index);
|
ArrayList<Float> delta = context.get(index);
|
||||||
|
|
||||||
if (delta.size() != 0) {
|
if (delta.size() != 0) {
|
||||||
if (!valueSet) {
|
if (!valueSet) {
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ public class GroupShadowNode extends RenderableShadowNode {
|
|||||||
return getTextRoot().getGlyphContext().getNextGlyphDelta();
|
return getTextRoot().getGlyphContext().getNextGlyphDelta();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected float getNextGlyphRotationFromContext() {
|
||||||
|
return getTextRoot().getGlyphContext().getNextGlyphRotation();
|
||||||
|
}
|
||||||
|
|
||||||
public void draw(final Canvas canvas, final Paint paint, final float opacity) {
|
public void draw(final Canvas canvas, final Paint paint, final float opacity) {
|
||||||
setupGlyphContext();
|
setupGlyphContext();
|
||||||
if (opacity > MIN_OPACITY_FOR_DRAW) {
|
if (opacity > MIN_OPACITY_FOR_DRAW) {
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ public class TSpanShadowNode extends TextShadowNode {
|
|||||||
String current;
|
String current;
|
||||||
PointF glyphPoint;
|
PointF glyphPoint;
|
||||||
PointF glyphDelta;
|
PointF glyphDelta;
|
||||||
|
float glyphRotation;
|
||||||
String previous = "";
|
String previous = "";
|
||||||
char[] chars = line.toCharArray();
|
char[] chars = line.toCharArray();
|
||||||
float[] widths = new float[length];
|
float[] widths = new float[length];
|
||||||
@@ -166,6 +167,7 @@ public class TSpanShadowNode extends TextShadowNode {
|
|||||||
|
|
||||||
glyphPoint = getGlyphPointFromContext(glyphPosition, width);
|
glyphPoint = getGlyphPointFromContext(glyphPosition, width);
|
||||||
glyphDelta = getGlyphDeltaFromContext();
|
glyphDelta = getGlyphDeltaFromContext();
|
||||||
|
glyphRotation = getNextGlyphRotationFromContext();
|
||||||
glyphPosition += width;
|
glyphPosition += width;
|
||||||
matrix = new Matrix();
|
matrix = new Matrix();
|
||||||
|
|
||||||
@@ -199,6 +201,8 @@ public class TSpanShadowNode extends TextShadowNode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matrix.preRotate(glyphRotation);
|
||||||
|
|
||||||
paint.getTextPath(current, 0, 1, 0, 0, glyph);
|
paint.getTextPath(current, 0, 1, 0, 0, glyph);
|
||||||
glyph.transform(matrix);
|
glyph.transform(matrix);
|
||||||
path.addPath(glyph);
|
path.addPath(glyph);
|
||||||
@@ -220,6 +224,11 @@ public class TSpanShadowNode extends TextShadowNode {
|
|||||||
paint.setLetterSpacing(letterSpacing / fontSize); // setLetterSpacing is only available from LOLLIPOP and on
|
paint.setLetterSpacing(letterSpacing / fontSize); // setLetterSpacing is only available from LOLLIPOP and on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int decoration = getTextDecoration();
|
||||||
|
|
||||||
|
paint.setUnderlineText(decoration == TEXT_DECORATION_UNDERLINE);
|
||||||
|
paint.setStrikeThruText(decoration == TEXT_DECORATION_LINE_THROUGH);
|
||||||
|
|
||||||
boolean isBold = font.hasKey(PROP_FONT_WEIGHT) && "bold".equals(font.getString(PROP_FONT_WEIGHT));
|
boolean isBold = font.hasKey(PROP_FONT_WEIGHT) && "bold".equals(font.getString(PROP_FONT_WEIGHT));
|
||||||
boolean isItalic = font.hasKey(PROP_FONT_STYLE) && "italic".equals(font.getString(PROP_FONT_STYLE));
|
boolean isItalic = font.hasKey(PROP_FONT_STYLE) && "italic".equals(font.getString(PROP_FONT_STYLE));
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,15 @@ public class TextShadowNode extends GroupShadowNode {
|
|||||||
static final int TEXT_ANCHOR_MIDDLE = 2;
|
static final int TEXT_ANCHOR_MIDDLE = 2;
|
||||||
static final int TEXT_ANCHOR_END = 3;
|
static final int TEXT_ANCHOR_END = 3;
|
||||||
|
|
||||||
|
static final int TEXT_DECORATION_NONE = 0;
|
||||||
|
static final int TEXT_DECORATION_UNDERLINE = 1;
|
||||||
|
static final int TEXT_DECORATION_OVERLINE = 2;
|
||||||
|
static final int TEXT_DECORATION_LINE_THROUGH = 3;
|
||||||
|
static final int TEXT_DECORATION_BLINK = 4;
|
||||||
|
|
||||||
private int mTextAnchor = TEXT_ANCHOR_AUTO;
|
private int mTextAnchor = TEXT_ANCHOR_AUTO;
|
||||||
|
private int mTextDecoration = TEXT_DECORATION_NONE;
|
||||||
|
private @Nullable ReadableArray mRotate;
|
||||||
private @Nullable ReadableArray mDeltaX;
|
private @Nullable ReadableArray mDeltaX;
|
||||||
private @Nullable ReadableArray mDeltaY;
|
private @Nullable ReadableArray mDeltaY;
|
||||||
private @Nullable String mPositionX;
|
private @Nullable String mPositionX;
|
||||||
@@ -50,6 +58,18 @@ public class TextShadowNode extends GroupShadowNode {
|
|||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = "textDecoration", defaultInt = TEXT_DECORATION_NONE)
|
||||||
|
public void setTextDecoration(int textDecoration) {
|
||||||
|
mTextDecoration = textDecoration;
|
||||||
|
markUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = "rotate")
|
||||||
|
public void setRotate(@Nullable ReadableArray rotate) {
|
||||||
|
mRotate = rotate;
|
||||||
|
markUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = "deltaX")
|
@ReactProp(name = "deltaX")
|
||||||
public void setDeltaX(@Nullable ReadableArray deltaX) {
|
public void setDeltaX(@Nullable ReadableArray deltaX) {
|
||||||
mDeltaX = deltaX;
|
mDeltaX = deltaX;
|
||||||
@@ -103,9 +123,33 @@ public class TextShadowNode extends GroupShadowNode {
|
|||||||
return mTextAnchor;
|
return mTextAnchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getTextDecoration() {
|
||||||
|
int decoration = mTextDecoration;
|
||||||
|
if (decoration != TEXT_DECORATION_NONE) {
|
||||||
|
return decoration;
|
||||||
|
}
|
||||||
|
ReactShadowNode shadowNode = this.getParent();
|
||||||
|
|
||||||
|
while (shadowNode instanceof GroupShadowNode) {
|
||||||
|
if (shadowNode instanceof TextShadowNode) {
|
||||||
|
decoration = ((TextShadowNode) shadowNode).getTextDecoration();
|
||||||
|
if (decoration != TEXT_DECORATION_NONE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowNode = shadowNode.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoration;
|
||||||
|
}
|
||||||
|
|
||||||
int getComputedTextAnchor() {
|
int getComputedTextAnchor() {
|
||||||
int anchor = mTextAnchor;
|
int anchor = mTextAnchor;
|
||||||
ReactShadowNode shadowNode = this;
|
if (anchor != TEXT_ANCHOR_AUTO) {
|
||||||
|
return anchor;
|
||||||
|
}
|
||||||
|
ReactShadowNode shadowNode = this.getParent();
|
||||||
|
|
||||||
while (shadowNode instanceof GroupShadowNode) {
|
while (shadowNode instanceof GroupShadowNode) {
|
||||||
if (shadowNode instanceof TextShadowNode) {
|
if (shadowNode instanceof TextShadowNode) {
|
||||||
@@ -141,6 +185,6 @@ public class TextShadowNode extends GroupShadowNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void pushGlyphContext() {
|
protected void pushGlyphContext() {
|
||||||
getTextRoot().getGlyphContext().pushContext(mFont, mDeltaX, mDeltaY, mPositionX, mPositionY);
|
getTextRoot().getGlyphContext().pushContext(mFont, mRotate, mDeltaX, mDeltaY, mPositionX, mPositionY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,8 +107,10 @@ const TextAttributes = {
|
|||||||
diff: fontDiffer
|
diff: fontDiffer
|
||||||
},
|
},
|
||||||
textAnchor: true,
|
textAnchor: true,
|
||||||
|
textDecoration: true,
|
||||||
deltaX: arrayDiffer,
|
deltaX: arrayDiffer,
|
||||||
deltaY: arrayDiffer,
|
deltaY: arrayDiffer,
|
||||||
|
rotate: arrayDiffer,
|
||||||
positionX: true,
|
positionX: true,
|
||||||
positionY: true,
|
positionY: true,
|
||||||
...RenderableAttributes
|
...RenderableAttributes
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ const anchors = {
|
|||||||
end: 3
|
end: 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const decorations = {
|
||||||
|
none: 0,
|
||||||
|
underline: 1,
|
||||||
|
overline: 2,
|
||||||
|
'line-through': 3,
|
||||||
|
blink: 4
|
||||||
|
};
|
||||||
let cachedFontObjectsFromString = {};
|
let cachedFontObjectsFromString = {};
|
||||||
|
|
||||||
function extractSingleFontFamily(fontFamilyString) {
|
function extractSingleFontFamily(fontFamilyString) {
|
||||||
@@ -91,15 +98,18 @@ export default function(props, container) {
|
|||||||
y,
|
y,
|
||||||
dx,
|
dx,
|
||||||
dy,
|
dy,
|
||||||
|
rotate,
|
||||||
method,
|
method,
|
||||||
spacing,
|
spacing,
|
||||||
textAnchor,
|
textAnchor,
|
||||||
startOffset
|
startOffset,
|
||||||
|
textDecoration
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
|
||||||
const deltaX = parseDelta(dx);
|
const deltaX = parseDelta(dx);
|
||||||
const deltaY = parseDelta(dy);
|
const deltaY = parseDelta(dy);
|
||||||
|
const rotates = parseDelta(rotate);
|
||||||
let { children } = props;
|
let { children } = props;
|
||||||
let content = null;
|
let content = null;
|
||||||
|
|
||||||
@@ -123,12 +133,14 @@ export default function(props, container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
textDecoration: decorations[textDecoration] || 0,
|
||||||
textAnchor: anchors[textAnchor] || 0,
|
textAnchor: anchors[textAnchor] || 0,
|
||||||
font: extractFont(props),
|
font: extractFont(props),
|
||||||
children,
|
children,
|
||||||
content,
|
content,
|
||||||
deltaX,
|
deltaX,
|
||||||
deltaY,
|
deltaY,
|
||||||
|
rotate: rotates,
|
||||||
method,
|
method,
|
||||||
spacing,
|
spacing,
|
||||||
startOffset: (startOffset || 0).toString(),
|
startOffset: (startOffset || 0).toString(),
|
||||||
|
|||||||
Reference in New Issue
Block a user