diff --git a/android/src/main/java/com/horcrux/svg/TSpanView.java b/android/src/main/java/com/horcrux/svg/TSpanView.java index 32248517..2695425e 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanView.java +++ b/android/src/main/java/com/horcrux/svg/TSpanView.java @@ -21,6 +21,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.os.Build; +import android.view.View; import android.view.ViewParent; import com.facebook.react.bridge.ReactContext; @@ -106,6 +107,50 @@ class TSpanView extends TextView { return mPath; } + double getSubtreeTextChunksTotalAdvance(Paint paint) { + double advance = 0; + + if (mContent == null) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof TextView) { + TextView text = (TextView)child; + advance += text.getSubtreeTextChunksTotalAdvance(paint); + } + } + return advance; + } + + String line = mContent; + final int length = line.length(); + + if (length == 0) { + return advance; + } + + GlyphContext gc = getTextRootGlyphContext(); + FontData font = gc.getFont(); + applyTextPropertiesToPaint(paint, font); + + double letterSpacing = font.letterSpacing; + final boolean allowOptionalLigatures = letterSpacing == 0 && + font.fontVariantLigatures == FontVariantLigatures.normal; + + 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) { + String additionalLigatures = "'hlig', 'cala', "; + paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings); + } else { + String disableDiscretionaryLigatures = "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, "; + paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); + } + } + + return paint.measureText(line); + } + @SuppressWarnings("ConstantConditions") private Path getLinePath(String line, Paint paint, Canvas canvas) { final int length = line.length(); @@ -311,8 +356,10 @@ class TSpanView extends TextView { attributes, such as a ‘dx’ attribute value on a ‘tspan’ element. */ final TextAnchor textAnchor = font.textAnchor; - final double textMeasure = paint.measureText(line); + TextView anchorRoot = getTextAnchorRoot(); + final double textMeasure = anchorRoot.getSubtreeTextChunksTotalAdvance(paint); double offset = getTextAnchorOffset(textAnchor, textMeasure); + applyTextPropertiesToPaint(paint, font); int side = 1; double startOfRendering = 0; diff --git a/android/src/main/java/com/horcrux/svg/TextView.java b/android/src/main/java/com/horcrux/svg/TextView.java index 22be697d..d1600851 100644 --- a/android/src/main/java/com/horcrux/svg/TextView.java +++ b/android/src/main/java/com/horcrux/svg/TextView.java @@ -14,6 +14,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Region; +import android.view.View; import android.view.ViewParent; import com.facebook.react.bridge.Dynamic; @@ -38,6 +39,7 @@ class TextView extends GroupView { @Nullable ArrayList mRotate; @Nullable ArrayList mDeltaX; @Nullable ArrayList mDeltaY; + double cachedAdvance = Double.NaN; public TextView(ReactContext reactContext) { super(reactContext); @@ -48,6 +50,7 @@ class TextView extends GroupView { if (mPath == null) { return; } + cachedAdvance = Double.NaN; super.invalidate(); clearChildCache(); } @@ -207,4 +210,42 @@ class TextView extends GroupView { boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView); getTextRootGlyphContext().pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate); } + + TextView getTextAnchorRoot() { + GlyphContext gc = getTextRootGlyphContext(); + FontData font = gc.getFont(); + TextProperties.TextAnchor textAnchor = font.textAnchor; + if (textAnchor == TextProperties.TextAnchor.start) { + return this; + } + ViewParent parent = this.getParent(); + if (parent instanceof TextView) { + TextView parentText = (TextView)parent; + GlyphContext parentGc = parentText.getGlyphContext(); + FontData parentFont = parentGc.getFont(); + if (parentFont.textAnchor == TextProperties.TextAnchor.start) { + return this; + } else { + return parentText.getTextAnchorRoot(); + } + } + return this; + } + + double getSubtreeTextChunksTotalAdvance(Paint paint) { + if (!Double.isNaN(cachedAdvance)) { + return cachedAdvance; + } + double advance = 0; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof TextView) { + TextView text = (TextView)child; + advance += text.getSubtreeTextChunksTotalAdvance(paint); + } + } + cachedAdvance = advance; + return advance; + } + }