From 429f1969c3dd97e178ee6786df1169256bc27281 Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Mon, 30 Jul 2018 05:45:50 +0300 Subject: [PATCH] Implement basic support for unicode emoji --- .../java/com/horcrux/svg/TSpanShadowNode.java | 23 +++++++++--- ios/Text/RNSVGTSpan.m | 36 ++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java b/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java index 01f74352..027dfce0 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java @@ -672,6 +672,7 @@ class TSpanShadowNode extends TextShadowNode { final Matrix end = new Matrix(); final float[] startPointMatrixData = new float[9]; + final float[] midPointMatrixData = new float[9]; final float[] endPointMatrixData = new float[9]; for (int index = 0; index < length; index++) { @@ -729,7 +730,7 @@ class TSpanShadowNode extends TextShadowNode { double dy = gc.nextDeltaY(); double r = gc.nextRotation(); - if (alreadyRenderedGraphemeCluster) { + if (alreadyRenderedGraphemeCluster || isWordSeparator) { // Skip rendering other grapheme clusters of ligatures (already rendered), // But, make sure to increment index positions by making gc.next() calls. continue; @@ -852,8 +853,20 @@ class TSpanShadowNode extends TextShadowNode { } else { glyph = bag.getOrCreateAndCache(currentChar, current); } - glyph.transform(mid); - path.addPath(glyph); + RectF bounds = new RectF(); + glyph.computeBounds(bounds, true); + float width = bounds.width(); + if (width == 0) { // Render unicode emoji + mid.getValues(midPointMatrixData); + double midX = midPointMatrixData[MTRANS_X]; + double midY = midPointMatrixData[MTRANS_Y]; + canvas.rotate((float) r, (float)midX, (float)midY); + canvas.drawText(current, (float)midX, (float)midY, paint); + canvas.rotate((float) -r, (float)midX, (float)midY); + } else { + glyph.transform(mid); + path.addPath(glyph); + } } return path; @@ -929,8 +942,8 @@ class TSpanShadowNode extends TextShadowNode { paint.setTextAlign(Paint.Align.LEFT); // Do these have any effect for anyone? Not for me (@msand) at least. - paint.setUnderlineText(underlineText); - paint.setStrikeThruText(strikeThruText); + // paint.setUnderlineText(underlineText); + // paint.setStrikeThruText(strikeThruText); } private void setupTextPath() { diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index e55862ba..decd8d52 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -82,7 +82,7 @@ static double RNSVGTSpan_radToDeg = 180 / M_PI; [self pushGlyphContext]; - CGMutablePathRef path = [self getLinePath:text]; + CGMutablePathRef path = [self getLinePath:text context:context]; _cache = CGPathRetain(CFAutorelease(CGPathCreateCopy(path))); @@ -91,7 +91,7 @@ static double RNSVGTSpan_radToDeg = 180 / M_PI; return (CGPathRef)CFAutorelease(path); } -- (CGMutablePathRef)getLinePath:(NSString *)str +- (CGMutablePathRef)getLinePath:(NSString *)str context:(CGContextRef)context { // Create a dictionary for this font CTFontRef fontRef = [self getFontFromContext]; @@ -720,6 +720,10 @@ static double RNSVGTSpan_radToDeg = 180 / M_PI; double dy = [gc nextDeltaY]; double r = [[gc nextRotation] doubleValue] / RNSVGTSpan_radToDeg; + if (isWordSeparator) { + continue; + } + CFIndex endIndex = g + 1 == runGlyphCount ? currIndex : indices[g + 1]; while (++currIndex < endIndex) { // Skip rendering other grapheme clusters of ligatures (already rendered), @@ -805,8 +809,32 @@ static double RNSVGTSpan_radToDeg = 180 / M_PI; transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(r), transform); } - transform = CGAffineTransformScale(transform, 1.0, -1.0); - CGPathAddPath(path, &transform, glyphPath); + CGRect box = CGPathGetBoundingBox(glyphPath); + CGFloat width = box.size.width; + + if (width == 0) { // Render unicode emoji + UILabel *label = [[UILabel alloc] init]; + CFIndex startIndex = indices[g]; + NSString* currChars = [str substringWithRange:NSMakeRange(startIndex, MAX(1, endIndex - startIndex))]; + label.text = currChars; + label.opaque = NO; + label.backgroundColor = UIColor.clearColor; + CGSize measuredSize = [currChars sizeWithAttributes: + @{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}]; + label.frame = CGRectMake(0, 0, ceilf(measuredSize.width), ceilf(measuredSize.height)); + + CGFloat fontScale = 34; + CGContextConcatCTM(context, transform); + CGContextScaleCTM(context, fontSize / fontScale, fontSize / fontScale); + CGContextTranslateCTM(context, 0, -measuredSize.height); + [label.layer renderInContext:context]; + CGContextTranslateCTM(context, 0, measuredSize.height); + CGContextScaleCTM(context, fontScale / fontSize, fontScale / fontSize); + CGContextConcatCTM(context, CGAffineTransformInvert(transform)); + } else { + transform = CGAffineTransformScale(transform, 1.0, -1.0); + CGPathAddPath(path, &transform, glyphPath); + } CGPathRelease(glyphPath); } }