From ec2a9675926350a2163b28af3123469751264c4b Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Sat, 16 Dec 2017 18:57:53 +0200 Subject: [PATCH] Implement getAlignmentBaseline and getBaselineShift in ios. (#527) * Implement correct transform parser * Implement getAlignmentBaseline and getBaselineShift in ios. Add comments to glyphcontext and align with android. Implement correct isWordSeparator predicate. Cleanup GlyphContext j2objc remnants. * Fix getBaselineShift bugs and font data invalidation. --- .../java/com/horcrux/svg/TSpanShadowNode.java | 82 ++-- ios/Elements/RNSVGGroup.m | 12 +- ios/Text/GlyphContext.h | 5 +- ios/Text/GlyphContext.m | 379 ++++++++++-------- ios/Text/RNSVGTSpan.m | 136 ++++--- ios/Text/RNSVGText.h | 3 + ios/Text/RNSVGText.m | 51 ++- ios/ViewManagers/RNSVGGroupManager.m | 2 + ios/ViewManagers/RNSVGRenderableManager.m | 1 - 9 files changed, 392 insertions(+), 279 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java b/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java index 59448cbf..6af11d8e 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java @@ -499,10 +499,10 @@ class TSpanShadowNode extends TextShadowNode { Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property. */ final Paint.FontMetrics fm = paint.getFontMetrics(); - final double top = -fm.top; - final double bottom = fm.bottom; - final double ascenderHeight = -fm.ascent; final double descenderDepth = fm.descent; + final double bottom = descenderDepth + fm.leading; + final double ascenderHeight = -fm.ascent + fm.leading; + final double top = -fm.top; final double totalHeight = top + bottom; double baselineShift = 0; String baselineShiftString = getBaselineShift(); @@ -593,6 +593,7 @@ class TSpanShadowNode extends TextShadowNode { baselineShift = top; break; } + } /* 2.2.2. Alignment Shift: baseline-shift longhand @@ -621,52 +622,51 @@ class TSpanShadowNode extends TextShadowNode { https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift */ - if (baselineShiftString != null) { - switch (baseline) { - case top: - case bottom: - break; + if (baselineShiftString != null && !baselineShiftString.isEmpty()) { + switch (baseline) { + case top: + case bottom: + break; - default: - 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; - } + default: + 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 += mScale * fontSize * subOffset / unitsPerEm; } } - break; + } + 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; - } + 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 -= mScale * fontSize * superOffset / unitsPerEm; } } - break; + } + break; - case "baseline": - break; + case "baseline": + break; - default: - baselineShift -= PropHelper.fromRelative(baselineShiftString, fontSize, 0, mScale, fontSize); - } - break; - } + default: + baselineShift -= PropHelper.fromRelative(baselineShiftString, mScale * fontSize, 0, mScale, fontSize); + } + break; } } diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index ed1649c2..3f2fd159 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -13,6 +13,16 @@ GlyphContext *_glyphContext; } +- (void)setFont:(NSDictionary*)font +{ + if (font == _font) { + return; + } + + [self invalidate]; + _font = font; +} + - (void)renderLayerTo:(CGContextRef)context { [self clip:context]; @@ -62,7 +72,7 @@ - (void)pushGlyphContext { - [[[self getTextRoot] getGlyphContext] pushContextWithRNSVGGroup:self font:self.font]; + [[[self getTextRoot] getGlyphContext] pushContext:self font:self.font]; } - (void)popGlyphContext diff --git a/ios/Text/GlyphContext.h b/ios/Text/GlyphContext.h index 17515146..912c400c 100644 --- a/ios/Text/GlyphContext.h +++ b/ios/Text/GlyphContext.h @@ -34,8 +34,7 @@ - (void)popContext; -- (void)pushContextwithRNSVGText:(RNSVGText *)node - reset:(bool)reset +- (void)pushContext:(RNSVGText *)node font:(NSDictionary *)font x:(NSArray*)x y:(NSArray*)y @@ -43,7 +42,7 @@ deltaY:(NSArray*)deltaY rotate:(NSArray*)rotate; -- (void)pushContextWithRNSVGGroup:(RNSVGGroup*)node +- (void)pushContext:(RNSVGGroup*)node font:(NSDictionary *)font; diff --git a/ios/Text/GlyphContext.m b/ios/Text/GlyphContext.m index ebb3cbda..1fba6917 100644 --- a/ios/Text/GlyphContext.m +++ b/ios/Text/GlyphContext.m @@ -5,74 +5,105 @@ #import "FontData.h" #import "RNSVGText.h" +// https://www.w3.org/TR/SVG/text.html#TSpanElement @interface GlyphContext () { @public + // Current stack (one per node push/pop) NSMutableArray *mFontContext_; + + // Unique input attribute lists (only added if node sets a value) NSMutableArray *mXsContext_; NSMutableArray *mYsContext_; NSMutableArray *mDXsContext_; NSMutableArray *mDYsContext_; NSMutableArray *mRsContext_; + + // Unique index into attribute list (one per unique list) NSMutableArray *mXIndices_; NSMutableArray *mYIndices_; NSMutableArray *mDXIndices_; NSMutableArray *mDYIndices_; NSMutableArray *mRIndices_; + + // Index of unique context used (one per node push/pop) NSMutableArray *mXsIndices_; NSMutableArray *mYsIndices_; NSMutableArray *mDXsIndices_; NSMutableArray *mDYsIndices_; NSMutableArray *mRsIndices_; + + // Calculated on push context, percentage and em length depends on parent font size double mFontSize_; FontData *topFont_; + + // Current accumulated values + // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate + // syntax is the same as that for double mX_; double mY_; + + // https://www.w3.org/TR/SVG/types.html#Length double mDX_; double mDY_; + + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates + + // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute NSArray *mXs_; + + // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute NSArray *mYs_; + + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeLengths + + // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute NSArray *mDXs_; + + // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute NSArray *mDYs_; + + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers + + // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute NSArray *mRs_; + + // Current attribute list index long mXsIndex_; long mYsIndex_; long mDXsIndex_; long mDYsIndex_; long mRsIndex_; + + // Current value index in current attribute list long mXIndex_; long mYIndex_; long mDXIndex_; long mDYIndex_; long mRIndex_; + + // Top index of stack long mTop_; + + // Constructor parameters float mScale_; float mWidth_; float mHeight_; } -- (void)pushIndices; +- (void)pushContext:(RNSVGText *)node + font:(NSDictionary *)font + x:(NSArray*)x + y:(NSArray*)y + deltaX:(NSArray*)deltaX + deltaY:(NSArray*)deltaY + rotate:(NSArray*)rotate; -- (void)reset; - -- (FontData *)getTopOrParentFontWithRNSVGGroup:(RNSVGGroup *)child; - -- (void)pushNodeAndFontWithRNSVGGroup:(RNSVGGroup *)node - withNSDictionary:(NSDictionary *)font; - -+ (void)incrementIndicesWithNSArray:(NSArray *)indices - withLong:(long)topIndex; - -- (void)pushContextwithRNSVGText:(RNSVGText *)node - reset:(bool)reset - font:(NSDictionary *)font - x:(NSArray*)x - y:(NSArray*)y - deltaX:(NSArray*)deltaX - deltaY:(NSArray*)deltaY - rotate:(NSArray*)rotate; - -- (void)pushContextWithRNSVGGroup:(RNSVGGroup*)node - font:(NSDictionary *)font; +- (void)pushContext:(RNSVGGroup*)node + font:(NSDictionary *)font; @end @implementation GlyphContext @@ -82,7 +113,7 @@ { NSString *fontFamily = topFont_->fontFamily; NSNumber * fontSize = [NSNumber numberWithDouble:topFont_->fontSize]; - + NSString * fontWeight = [FontWeightToString(topFont_->fontWeight) lowercaseString]; NSString * fontStyle = FontStyleStrings[topFont_->fontStyle]; @@ -110,52 +141,122 @@ scaleMultiplier:1.0]; } -- (void)pushIndices { - GlyphContext_pushIndices(self); +void pushIndices(GlyphContext *self) { + [self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]]; + [self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]]; + [self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]]; + [self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]]; + [self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]]; } - (instancetype)initWithScale:(float)scale_ width:(float)width height:(float)height { - GlyphContext_initWithFloat_withFloat_withFloat_(self, scale_, width, height); - return self; -} + self->mFontContext_ = [[NSMutableArray alloc]init]; + self->mXsContext_ = [[NSMutableArray alloc]init]; + self->mYsContext_ = [[NSMutableArray alloc]init]; + self->mDXsContext_ = [[NSMutableArray alloc]init]; + self->mDYsContext_ = [[NSMutableArray alloc]init]; + self->mRsContext_ = [[NSMutableArray alloc]init]; -- (void)reset { - GlyphContext_reset(self); + self->mXIndices_ = [[NSMutableArray alloc]init]; + self->mYIndices_ = [[NSMutableArray alloc]init]; + self->mDXIndices_ = [[NSMutableArray alloc]init]; + self->mDYIndices_ = [[NSMutableArray alloc]init]; + self->mRIndices_ = [[NSMutableArray alloc]init]; + + self->mXsIndices_ = [[NSMutableArray alloc]init]; + self->mYsIndices_ = [[NSMutableArray alloc]init]; + self->mDXsIndices_ = [[NSMutableArray alloc]init]; + self->mDYsIndices_ = [[NSMutableArray alloc]init]; + self->mRsIndices_ = [[NSMutableArray alloc]init]; + + self->mFontSize_ = FontData_DEFAULT_FONT_SIZE; + self->topFont_ = [FontData Defaults]; + + self->mXs_ = [[NSArray alloc]init]; + self->mYs_ = [[NSArray alloc]init]; + self->mDXs_ = [[NSArray alloc]init]; + self->mDYs_ = [[NSArray alloc]init]; + self->mRs_ = [[NSArray alloc]initWithObjects:@0, nil]; + + self->mXIndex_ = -1; + self->mYIndex_ = -1; + self->mDXIndex_ = -1; + self->mDYIndex_ = -1; + self->mRIndex_ = -1; + + self->mScale_ = scale_; + self->mWidth_ = width; + self->mHeight_ = height; + + [self->mXsContext_ addObject:self->mXs_]; + [self->mYsContext_ addObject:self->mYs_]; + [self->mDXsContext_ addObject:self->mDXs_]; + [self->mDYsContext_ addObject:self->mDYs_]; + [self->mRsContext_ addObject:self->mRs_]; + + [self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]]; + [self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]]; + [self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]]; + [self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]]; + [self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]]; + + [self->mFontContext_ addObject:self->topFont_]; + pushIndices(self); + return self; } - (FontData *)getFont { return topFont_; } -- (FontData *)getTopOrParentFontWithRNSVGGroup:(RNSVGGroup*)child { - return GlyphContext_getTopOrParentFontWithRNSVGGroup_(self, child); -} - -- (void)pushNodeAndFontWithRNSVGGroup:(RNSVGGroup*)node - withNSDictionary:(NSDictionary*)font { - GlyphContext_pushNodeAndFontWithRNSVGGroup_withNSDictionary_(self, node, font); -} - -- (void)pushContextWithRNSVGGroup:(RNSVGGroup*)node - font:(NSDictionary*)font { - GlyphContext_pushNodeAndFontWithRNSVGGroup_withNSDictionary_(self, node, font); - GlyphContext_pushIndices(self); -} - -- (void)pushContextwithRNSVGText:(RNSVGText*)node - reset:(bool)reset - font:(NSDictionary*)font - x:(NSArray*)x - y:(NSArray*)y - deltaX:(NSArray*)deltaX - deltaY:(NSArray*)deltaY - rotate:(NSArray*)rotate { - if (reset) { - GlyphContext_reset(self); +FontData *getTopOrParentFont(GlyphContext *self, RNSVGGroup* child) { + if (self->mTop_ > 0) { + return self->topFont_; + } else { + RNSVGGroup* parentRoot = [child getParentTextRoot]; + FontData* Defaults = [FontData Defaults]; + while (parentRoot != nil) { + FontData *map = [[parentRoot getGlyphContext] getFont]; + if (map != Defaults) { + return map; + } + parentRoot = [parentRoot getParentTextRoot]; + } + return Defaults; } - GlyphContext_pushNodeAndFontWithRNSVGGroup_withNSDictionary_(self, (RNSVGGroup*)node, font); +} + +void pushNodeAndFont(GlyphContext *self, RNSVGGroup* node, NSDictionary* font) { + FontData *parent = getTopOrParentFont(self, node); + self->mTop_++; + if (font == nil) { + [self->mFontContext_ addObject:parent]; + return; + } + FontData *data = [FontData initWithNSDictionary:font + parent:parent + scale:self->mScale_]; + self->mFontSize_ = data->fontSize; + [self->mFontContext_ addObject:data]; + self->topFont_ = data; +} + +- (void)pushContext:(RNSVGGroup*)node + font:(NSDictionary*)font { + pushNodeAndFont(self, node, font); + pushIndices(self); +} + +- (void)pushContext:(RNSVGText*)node + font:(NSDictionary*)font + x:(NSArray*)x + y:(NSArray*)y + deltaX:(NSArray*)deltaX + deltaY:(NSArray*)deltaY + rotate:(NSArray*)rotate { + pushNodeAndFont(self, (RNSVGGroup*)node, font); if (x != nil && [x count] != 0) { mXsIndex_++; mXIndex_ = -1; @@ -191,28 +292,33 @@ mRs_ = [rotate valueForKeyPath:@"self.doubleValue"]; [mRsContext_ addObject:mRs_]; } - GlyphContext_pushIndices(self); + pushIndices(self); } - (void)popContext { [mFontContext_ removeLastObject]; - [mXsIndices_ removeLastObject]; - [mYsIndices_ removeLastObject]; - [mDXsIndices_ removeLastObject]; - [mDYsIndices_ removeLastObject]; - [mRsIndices_ removeLastObject]; + [mXsIndices_ removeLastObject]; + [mYsIndices_ removeLastObject]; + [mDXsIndices_ removeLastObject]; + [mDYsIndices_ removeLastObject]; + [mRsIndices_ removeLastObject]; + mTop_--; + long x = mXsIndex_; long y = mYsIndex_; long dx = mDXsIndex_; long dy = mDYsIndex_; long r = mRsIndex_; + topFont_ = [mFontContext_ lastObject]; + mXsIndex_ = [[mXsIndices_ lastObject] longValue]; mYsIndex_ = [[mYsIndices_ lastObject] longValue]; mDXsIndex_ = [[mDXsIndices_ lastObject] longValue]; mDYsIndex_ = [[mDYsIndices_ lastObject] longValue]; mRsIndex_ = [[mRsIndices_ lastObject] longValue]; + if (x != mXsIndex_) { [mXsContext_ removeObjectAtIndex:x]; mXs_ = [mXsContext_ objectAtIndex:mXsIndex_]; @@ -240,17 +346,50 @@ } } -+ (void)incrementIndicesWithNSArray:(NSMutableArray *)indices - withLong:(long)topIndex { - GlyphContext_incrementIndicesWithNSArray_withLong_(indices, topIndex); +void incrementIndices(NSMutableArray *indices, long topIndex) { + for (long index = topIndex; index >= 0; index--) { + long xIndex = [[indices objectAtIndex:index] longValue]; + [indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index]; + } } +// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty + +/** + * Get font size from context. + *

+ * ‘font-size’ + * Value: < absolute-size > | < relative-size > | < length > | < percentage > | inherit + * Initial: medium + * Applies to: text content elements + * Inherited: yes, the computed value is inherited + * Percentages: refer to parent element's font size + * Media: visual + * Animatable: yes + *

+ * This property refers to the size of the font from baseline to + * baseline when multiple lines of text are set solid in a multiline + * layout environment. + *

+ * For SVG, if a < length > is provided without a unit identifier + * (e.g., an unqualified number such as 128), the SVG user agent + * processes the < length > as a height value in the current user + * coordinate system. + *

+ * If a < length > is provided with one of the unit identifiers + * (e.g., 12pt or 10%), then the SVG user agent converts the + * < length > into a corresponding value in the current user + * coordinate system by applying the rules described in Units. + *

+ * Except for any additional information provided in this specification, + * the normative definition of the property is in CSS2 ([CSS2], section 15.2.4). + */ - (double)getFontSize { return mFontSize_; } - (double)nextXWithDouble:(double)advance { - GlyphContext_incrementIndicesWithNSArray_withLong_(mXIndices_, mXsIndex_); + incrementIndices(mXIndices_, mXsIndex_); long nextIndex = mXIndex_ + 1; if (nextIndex < [mXs_ count]) { mDX_ = 0; @@ -267,7 +406,7 @@ } - (double)nextY { - GlyphContext_incrementIndicesWithNSArray_withLong_(mYIndices_, mYsIndex_); + incrementIndices(mYIndices_, mYsIndex_); long nextIndex = mYIndex_ + 1; if (nextIndex < [mYs_ count]) { mDY_ = 0; @@ -283,7 +422,7 @@ } - (double)nextDeltaX { - GlyphContext_incrementIndicesWithNSArray_withLong_(mDXIndices_, mDXsIndex_); + incrementIndices(mDXIndices_, mDXsIndex_); long nextIndex = mDXIndex_ + 1; if (nextIndex < [mDXs_ count]) { mDXIndex_ = nextIndex; @@ -299,7 +438,7 @@ } - (double)nextDeltaY { - GlyphContext_incrementIndicesWithNSArray_withLong_(mDYIndices_, mDYsIndex_); + incrementIndices(mDYIndices_, mDYsIndex_); long nextIndex = mDYIndex_ + 1; if (nextIndex < [mDYs_ count]) { mDYIndex_ = nextIndex; @@ -315,7 +454,7 @@ } - (NSNumber*)nextRotation { - GlyphContext_incrementIndicesWithNSArray_withLong_(mRIndices_, mRsIndex_); + incrementIndices(mRIndices_, mRsIndex_); long nextIndex = mRIndex_ + 1; long count = [mRs_ count]; if (nextIndex < count) { @@ -333,104 +472,4 @@ - (float)getHeight { return mHeight_; } - -void GlyphContext_pushIndices(GlyphContext *self) { - [self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]]; - [self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]]; - [self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]]; - [self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]]; - [self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]]; -} - -void GlyphContext_initWithFloat_withFloat_withFloat_(GlyphContext *self, float scale_, float width, float height) { - self->mFontContext_ = [[NSMutableArray alloc]init]; - self->mXsContext_ = [[NSMutableArray alloc]init]; - self->mYsContext_ = [[NSMutableArray alloc]init]; - self->mDXsContext_ = [[NSMutableArray alloc]init]; - self->mDYsContext_ = [[NSMutableArray alloc]init]; - self->mRsContext_ = [[NSMutableArray alloc]init]; - self->mXIndices_ = [[NSMutableArray alloc]init]; - self->mYIndices_ = [[NSMutableArray alloc]init]; - self->mDXIndices_ = [[NSMutableArray alloc]init]; - self->mDYIndices_ = [[NSMutableArray alloc]init]; - self->mRIndices_ = [[NSMutableArray alloc]init]; - self->mXsIndices_ = [[NSMutableArray alloc]init]; - self->mYsIndices_ = [[NSMutableArray alloc]init]; - self->mDXsIndices_ = [[NSMutableArray alloc]init]; - self->mDYsIndices_ = [[NSMutableArray alloc]init]; - self->mRsIndices_ = [[NSMutableArray alloc]init]; - self->mFontSize_ = FontData_DEFAULT_FONT_SIZE; - self->topFont_ = [FontData Defaults]; - self->mXs_ = [[NSArray alloc]init]; - self->mYs_ = [[NSArray alloc]init]; - self->mDXs_ = [[NSArray alloc]init]; - self->mDYs_ = [[NSArray alloc]init]; - self->mRs_ = [[NSArray alloc]initWithObjects:@0, nil]; - self->mXIndex_ = -1; - self->mYIndex_ = -1; - self->mDXIndex_ = -1; - self->mDYIndex_ = -1; - self->mRIndex_ = -1; - self->mScale_ = scale_; - self->mWidth_ = width; - self->mHeight_ = height; - [self->mXsContext_ addObject:self->mXs_]; - [self->mYsContext_ addObject:self->mYs_]; - [self->mDXsContext_ addObject:self->mDXs_]; - [self->mDYsContext_ addObject:self->mDYs_]; - [self->mRsContext_ addObject:self->mRs_]; - [self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]]; - [self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]]; - [self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]]; - [self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]]; - [self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]]; - [self->mFontContext_ addObject:self->topFont_]; - GlyphContext_pushIndices(self); -} - -void GlyphContext_reset(GlyphContext *self) { - self->mXsIndex_ = self->mYsIndex_ = self->mDXsIndex_ = self->mDYsIndex_ = self->mRsIndex_ = 0; - self->mXIndex_ = self->mYIndex_ = self->mDXIndex_ = self->mDYIndex_ = self->mRIndex_ = -1; - self->mX_ = self->mY_ = self->mDX_ = self->mDY_ = 0; -} - -FontData *GlyphContext_getTopOrParentFontWithRNSVGGroup_(GlyphContext *self, RNSVGGroup* child) { - if (self->mTop_ > 0) { - return self->topFont_; - } - else { - RNSVGGroup* parentRoot = [child getParentTextRoot]; - FontData* Defaults = [FontData Defaults]; - while (parentRoot != nil) { - FontData *map = [[parentRoot getGlyphContext] getFont]; - if (map != Defaults) { - return map; - } - parentRoot = [parentRoot getParentTextRoot]; - } - return Defaults; - } -} - -void GlyphContext_pushNodeAndFontWithRNSVGGroup_withNSDictionary_(GlyphContext *self, RNSVGGroup* node, NSDictionary* font) { - FontData *parent = GlyphContext_getTopOrParentFontWithRNSVGGroup_(self, node); - self->mTop_++; - if (font == nil) { - [self->mFontContext_ addObject:parent]; - return; - } - FontData *data = [FontData initWithNSDictionary:font - parent:parent - scale:self->mScale_]; - self->mFontSize_ = data->fontSize; - [self->mFontContext_ addObject:data]; - self->topFont_ = data; -} - -void GlyphContext_incrementIndicesWithNSArray_withLong_(NSMutableArray *indices, long topIndex) { - for (long index = topIndex; index >= 0; index--) { - long xIndex = [[indices objectAtIndex:index] longValue]; - [indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index]; - } -} @end diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index 3d6300f2..d23da576 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -12,6 +12,8 @@ #import "RNSVGTextPath.h" #import "FontData.h" +NSCharacterSet *separators = nil; + @implementation RNSVGTSpan { CGFloat startOffset; @@ -23,6 +25,17 @@ bool isClosed; } +- (id)init +{ + self = [super init]; + + if (separators == nil) { + separators = [NSCharacterSet whitespaceCharacterSet]; + } + + return self; +} + - (void)setContent:(NSString *)content { if (content == _content) { @@ -517,8 +530,8 @@ double totalHeight = top + bottom; double baselineShift = 0; // TODO, alignmentBaseline and baselineShift are always nil for some reason? - NSString *baselineShiftString = [self baselineShift]; - enum AlignmentBaseline baseline = AlignmentBaselineFromString([self alignmentBaseline]); + NSString *baselineShiftString = [self getBaselineShift]; + enum AlignmentBaseline baseline = AlignmentBaselineFromString([self getAlignmentBaseline]); if (baseline != AlignmentBaselineBaseline) { // TODO alignment-baseline, test / verify behavior // TODO get per glyph baselines from font baseline table, for high-precision alignment @@ -602,72 +615,72 @@ baselineShift = top; break; } - /* - 2.2.2. Alignment Shift: baseline-shift longhand + } + /* + 2.2.2. Alignment Shift: baseline-shift longhand - This property specifies by how much the box is shifted up from its alignment point. - It does not apply when alignment-baseline is top or bottom. + This property specifies by how much the box is shifted up from its alignment point. + It does not apply when alignment-baseline is top or bottom. - Authors should use the vertical-align shorthand instead of this property. + Authors should use the vertical-align shorthand instead of this property. - Values have the following meanings: + Values have the following meanings: - - Raise (positive value) or lower (negative value) by the specified length. - - Raise (positive value) or lower (negative value) by the specified percentage of the line-height. - TODO sub - Lower by the offset appropriate for subscripts of the parent’s box. - (The UA should use the parent’s font data to find this offset whenever possible.) - TODO super - Raise by the offset appropriate for superscripts of the parent’s box. - (The UA should use the parent’s font data to find this offset whenever possible.) + + Raise (positive value) or lower (negative value) by the specified length. + + Raise (positive value) or lower (negative value) by the specified percentage of the line-height. + TODO sub + Lower by the offset appropriate for subscripts of the parent’s box. + (The UA should use the parent’s font data to find this offset whenever possible.) + TODO super + Raise by the offset appropriate for superscripts of the parent’s box. + (The UA should use the parent’s font data to find this offset whenever possible.) - User agents may additionally support the keyword baseline as computing to 0 - if is necessary for them to support legacy SVG content. - Issue: We would prefer to remove this, - and are looking for feedback from SVG user agents as to whether it’s necessary. + User agents may additionally support the keyword baseline as computing to 0 + if is necessary for them to support legacy SVG content. + Issue: We would prefer to remove this, + and are looking for feedback from SVG user agents as to whether it’s necessary. - https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift - */ - if (baselineShiftString != nil && fontData != nil) { - switch (baseline) { - case AlignmentBaselineTop: - case AlignmentBaselineBottom: - break; - - default: - if ([baselineShiftString isEqualToString:@"sub"]) { - // TODO - NSDictionary* tables = [fontData objectForKey:@"tables"]; - NSNumber* unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; - NSDictionary* os2 = [tables objectForKey:@"os2"]; - NSNumber* ySubscriptYOffset = [os2 objectForKey:@"ySubscriptYOffset"]; - if (ySubscriptYOffset) { - double subOffset = [ySubscriptYOffset doubleValue]; - baselineShift += fontSize * subOffset / [unitsPerEm doubleValue]; - } - } else if ([baselineShiftString isEqualToString:@"super"]) { - // TODO - NSDictionary* tables = [fontData objectForKey:@"tables"]; - NSNumber* unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; - NSDictionary* os2 = [tables objectForKey:@"os2"]; - NSNumber* ySuperscriptYOffset = [os2 objectForKey:@"ySuperscriptYOffset"]; - if (ySuperscriptYOffset) { - double superOffset = [ySuperscriptYOffset doubleValue]; - baselineShift -= fontSize * superOffset / [unitsPerEm doubleValue]; - } - } else if ([baselineShiftString isEqualToString:@"baseline"]) { - } else { - baselineShift -= [PropHelper fromRelativeWithNSString:baselineShiftString - relative:fontSize - offset:0 - scale:1 - fontSize:fontSize]; + https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift + */ + if (baselineShiftString != nil && ![baselineShiftString isEqualToString:@""]) { + switch (baseline) { + case AlignmentBaselineTop: + case AlignmentBaselineBottom: + break; + default: + if (fontData != nil && [baselineShiftString isEqualToString:@"sub"]) { + // TODO + NSDictionary* tables = [fontData objectForKey:@"tables"]; + NSNumber* unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; + NSDictionary* os2 = [tables objectForKey:@"os2"]; + NSNumber* ySubscriptYOffset = [os2 objectForKey:@"ySubscriptYOffset"]; + if (ySubscriptYOffset) { + double subOffset = [ySubscriptYOffset doubleValue]; + baselineShift += fontSize * subOffset / [unitsPerEm doubleValue]; } - break; - } + } else if (fontData != nil && [baselineShiftString isEqualToString:@"super"]) { + // TODO + NSDictionary* tables = [fontData objectForKey:@"tables"]; + NSNumber* unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; + NSDictionary* os2 = [tables objectForKey:@"os2"]; + NSNumber* ySuperscriptYOffset = [os2 objectForKey:@"ySuperscriptYOffset"]; + if (ySuperscriptYOffset) { + double superOffset = [ySuperscriptYOffset doubleValue]; + baselineShift -= fontSize * superOffset / [unitsPerEm doubleValue]; + } + } else if ([baselineShiftString isEqualToString:@"baseline"]) { + } else { + baselineShift -= [PropHelper fromRelativeWithNSString:baselineShiftString + relative:fontSize + offset:0 + scale:1 + fontSize:fontSize]; + + } + break; } } int i = -1; @@ -709,7 +722,8 @@ kerning = kerned - charWidth; } - bool isWordSeparator = false; + char currentChar = [str characterAtIndex:i]; + bool isWordSeparator = [separators characterIsMember:currentChar]; double wordSpace = isWordSeparator ? wordSpacing : 0; double spacing = wordSpace + letterSpacing; double advance = charWidth + spacing; diff --git a/ios/Text/RNSVGText.h b/ios/Text/RNSVGText.h index e0d3902c..42f66e40 100644 --- a/ios/Text/RNSVGText.h +++ b/ios/Text/RNSVGText.h @@ -9,6 +9,7 @@ #import #import "RNSVGGroup.h" #import "RNSVGTextAnchor.h" +#import "AlignmentBaseline.h" @interface RNSVGText : RNSVGGroup @@ -25,5 +26,7 @@ - (void)releaseCachedPath; - (CGPathRef)getGroupPath:(CGContextRef)context; - (CTFontRef)getFontFromContext; +- (NSString*) getAlignmentBaseline; +- (NSString*) getBaselineShift; @end diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index 78e7495a..c0a4ab64 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -91,6 +91,54 @@ return _textRoot; } +- (NSString*) getAlignmentBaseline +{ + if (self.alignmentBaseline != nil) { + return self.alignmentBaseline; + } + UIView* parent = [self superview]; + while (parent != nil) { + if ([parent isKindOfClass:[RNSVGText class]]) { + RNSVGText* node = (RNSVGText*)parent; + NSString* baseline = node.alignmentBaseline; + if (baseline != nil) { + self.alignmentBaseline = baseline; + return baseline; + } + } + parent = [parent superview]; + } + if (self.alignmentBaseline == nil) { + self.alignmentBaseline = AlignmentBaselineStrings[0]; + } + return self.alignmentBaseline; +} + +- (NSString*) getBaselineShift +{ + if (self.baselineShift != nil) { + return self.baselineShift; + } + if (self.baselineShift == nil) { + UIView* parent = [self superview]; + while (parent != nil) { + if ([parent isKindOfClass:[RNSVGText class]]) { + RNSVGText* node = (RNSVGText*)parent; + NSString* baselineShift = node.baselineShift; + if (baselineShift != nil) { + self.baselineShift = baselineShift; + return baselineShift; + } + } + parent = [parent superview]; + } + } + if (self.baselineShift == nil) { + self.baselineShift = @""; + } + return self.baselineShift; +} + - (GlyphContext *)getGlyphContext { return _glyphContext; @@ -98,8 +146,7 @@ - (void)pushGlyphContext { - [[[self getTextRoot] getGlyphContext] pushContextwithRNSVGText:self - reset:false + [[[self getTextRoot] getGlyphContext] pushContext:self font:self.font x:self.positionX y:self.positionY diff --git a/ios/ViewManagers/RNSVGGroupManager.m b/ios/ViewManagers/RNSVGGroupManager.m index bb2ef64b..ffac110f 100644 --- a/ios/ViewManagers/RNSVGGroupManager.m +++ b/ios/ViewManagers/RNSVGGroupManager.m @@ -19,4 +19,6 @@ RCT_EXPORT_MODULE() return [RNSVGGroup new]; } +RCT_EXPORT_VIEW_PROPERTY(font, NSDictionary) + @end diff --git a/ios/ViewManagers/RNSVGRenderableManager.m b/ios/ViewManagers/RNSVGRenderableManager.m index 361b95e5..3cfa7bf0 100644 --- a/ios/ViewManagers/RNSVGRenderableManager.m +++ b/ios/ViewManagers/RNSVGRenderableManager.m @@ -32,6 +32,5 @@ RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, NSArray) RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat) RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat) RCT_EXPORT_VIEW_PROPERTY(propList, NSArray) -RCT_EXPORT_VIEW_PROPERTY(font, NSDictionary) @end