mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 14:05:09 +00:00
480 lines
16 KiB
Objective-C
480 lines
16 KiB
Objective-C
#import "RNSVGGlyphContext.h"
|
||
#import <React/RCTFont.h>
|
||
#import "RNSVGNode.h"
|
||
#import "RNSVGPropHelper.h"
|
||
#import "RNSVGFontData.h"
|
||
#import "RNSVGText.h"
|
||
|
||
// https://www.w3.org/TR/SVG/text.html#TSpanElement
|
||
@interface RNSVGGlyphContext () {
|
||
@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_;
|
||
RNSVGFontData *topFont_;
|
||
|
||
// Current accumulated values
|
||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
|
||
// <coordinate> syntax is the same as that for <length>
|
||
double mX_;
|
||
double mY_;
|
||
|
||
// https://www.w3.org/TR/SVG/types.html#Length
|
||
double mDX_;
|
||
double mDY_;
|
||
|
||
// Current <list-of-coordinates> 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 <list-of-lengths> 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 <list-of-numbers> 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)pushContext:(RNSVGText *)node
|
||
font:(NSDictionary *)font
|
||
x:(NSArray*)x
|
||
y:(NSArray*)y
|
||
deltaX:(NSArray*)deltaX
|
||
deltaY:(NSArray*)deltaY
|
||
rotate:(NSArray*)rotate;
|
||
|
||
- (void)pushContext:(RNSVGGroup*)node
|
||
font:(NSDictionary *)font;
|
||
@end
|
||
|
||
@implementation RNSVGGlyphContext
|
||
|
||
|
||
- (CTFontRef)getGlyphFont
|
||
{
|
||
NSString *fontFamily = topFont_->fontFamily;
|
||
NSNumber * fontSize = [NSNumber numberWithDouble:topFont_->fontSize];
|
||
|
||
NSString * fontWeight = [RNSVGFontWeightToString(topFont_->fontWeight) lowercaseString];
|
||
NSString * fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle];
|
||
|
||
BOOL fontFamilyFound = NO;
|
||
NSArray *supportedFontFamilyNames = [UIFont familyNames];
|
||
|
||
if ([supportedFontFamilyNames containsObject:fontFamily]) {
|
||
fontFamilyFound = YES;
|
||
} else {
|
||
for (NSString *fontFamilyName in supportedFontFamilyNames) {
|
||
if ([[UIFont fontNamesForFamilyName: fontFamilyName] containsObject:fontFamily]) {
|
||
fontFamilyFound = YES;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
fontFamily = fontFamilyFound ? fontFamily : nil;
|
||
|
||
return (__bridge CTFontRef)[RCTFont updateFont:nil
|
||
withFamily:fontFamily
|
||
size:fontSize
|
||
weight:fontWeight
|
||
style:fontStyle
|
||
variant:nil
|
||
scaleMultiplier:1.0];
|
||
}
|
||
|
||
- (void)pushIndices
|
||
{
|
||
[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 {
|
||
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_ = RNSVGFontData_DEFAULT_FONT_SIZE;
|
||
self->topFont_ = [RNSVGFontData 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_];
|
||
[self pushIndices];
|
||
return self;
|
||
}
|
||
|
||
- (RNSVGFontData *)getFont {
|
||
return topFont_;
|
||
}
|
||
|
||
- (RNSVGFontData *)getTopOrParentFont:(RNSVGGroup *)child
|
||
{
|
||
if (self->mTop_ > 0) {
|
||
return self->topFont_;
|
||
} else {
|
||
RNSVGGroup* parentRoot = [child getParentTextRoot];
|
||
RNSVGFontData* Defaults = [RNSVGFontData Defaults];
|
||
while (parentRoot != nil) {
|
||
RNSVGFontData *map = [[parentRoot getGlyphContext] getFont];
|
||
if (map != Defaults) {
|
||
return map;
|
||
}
|
||
parentRoot = [parentRoot getParentTextRoot];
|
||
}
|
||
return Defaults;
|
||
}
|
||
}
|
||
|
||
- (void)pushNode:(RNSVGGroup *)node andFont:(NSDictionary *)font
|
||
{
|
||
RNSVGFontData *parent = [self getTopOrParentFont:node];
|
||
self->mTop_++;
|
||
if (font == nil) {
|
||
[self->mFontContext_ addObject:parent];
|
||
return;
|
||
}
|
||
RNSVGFontData *data = [RNSVGFontData 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 {
|
||
[self pushNode:node andFont:font];
|
||
[self pushIndices];
|
||
}
|
||
|
||
- (void)pushContext:(RNSVGText*)node
|
||
font:(NSDictionary*)font
|
||
x:(NSArray*)x
|
||
y:(NSArray*)y
|
||
deltaX:(NSArray*)deltaX
|
||
deltaY:(NSArray*)deltaY
|
||
rotate:(NSArray*)rotate {
|
||
[self pushNode:(RNSVGGroup*)node andFont:font];
|
||
if (x != nil && [x count] != 0) {
|
||
mXsIndex_++;
|
||
mXIndex_ = -1;
|
||
[mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]];
|
||
mXs_ = x;
|
||
[mXsContext_ addObject:mXs_];
|
||
}
|
||
if (y != nil && [y count] != 0) {
|
||
mYsIndex_++;
|
||
mYIndex_ = -1;
|
||
[mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]];
|
||
mYs_ = y;
|
||
[mYsContext_ addObject:mYs_];
|
||
}
|
||
if (deltaX != nil && [deltaX count] != 0) {
|
||
mDXsIndex_++;
|
||
mDXIndex_ = -1;
|
||
[mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]];
|
||
mDXs_ = deltaX;
|
||
[mDXsContext_ addObject:mDXs_];
|
||
}
|
||
if (deltaY != nil && [deltaY count] != 0) {
|
||
mDYsIndex_++;
|
||
mDYIndex_ = -1;
|
||
[mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]];
|
||
mDYs_ = deltaY;
|
||
[mDYsContext_ addObject:mDYs_];
|
||
}
|
||
if (rotate != nil && [rotate count] != 0) {
|
||
mRsIndex_++;
|
||
mRIndex_ = -1;
|
||
[mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]];
|
||
mRs_ = [rotate valueForKeyPath:@"self.doubleValue"];
|
||
[mRsContext_ addObject:mRs_];
|
||
}
|
||
[self pushIndices];
|
||
}
|
||
|
||
- (void)popContext {
|
||
[mFontContext_ 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_];
|
||
mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue];
|
||
}
|
||
if (y != mYsIndex_) {
|
||
[mYsContext_ removeObjectAtIndex:y];
|
||
mYs_ = [mYsContext_ objectAtIndex:mYsIndex_];
|
||
mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue];
|
||
}
|
||
if (dx != mDXsIndex_) {
|
||
[mDXsContext_ removeObjectAtIndex:dx];
|
||
mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_];
|
||
mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue];
|
||
}
|
||
if (dy != mDYsIndex_) {
|
||
[mDYsContext_ removeObjectAtIndex:dy];
|
||
mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_];
|
||
mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue];
|
||
}
|
||
if (r != mRsIndex_) {
|
||
[mRsContext_ removeObjectAtIndex:r];
|
||
mRs_ = [mRsContext_ objectAtIndex:mRsIndex_];
|
||
mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue];
|
||
}
|
||
}
|
||
|
||
+ (void)incrementIndices:(NSMutableArray *)indices topIndex:(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.
|
||
* <p>
|
||
* ‘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
|
||
* <p>
|
||
* 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.
|
||
* <p>
|
||
* 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.
|
||
* <p>
|
||
* 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.
|
||
* <p>
|
||
* 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 {
|
||
[RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_];
|
||
long nextIndex = mXIndex_ + 1;
|
||
if (nextIndex < [mXs_ count]) {
|
||
mDX_ = 0;
|
||
mXIndex_ = nextIndex;
|
||
NSString *string = [mXs_ objectAtIndex:nextIndex];
|
||
mX_ = [RNSVGPropHelper fromRelativeWithNSString:string
|
||
relative:mWidth_
|
||
offset:0
|
||
scale:mScale_
|
||
fontSize:mFontSize_];
|
||
}
|
||
mX_ += advance;
|
||
return mX_;
|
||
}
|
||
|
||
- (double)nextY {
|
||
[RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_];
|
||
long nextIndex = mYIndex_ + 1;
|
||
if (nextIndex < [mYs_ count]) {
|
||
mDY_ = 0;
|
||
mYIndex_ = nextIndex;
|
||
NSString *string = [mYs_ objectAtIndex:nextIndex];
|
||
mY_ = [RNSVGPropHelper fromRelativeWithNSString:string
|
||
relative:mHeight_
|
||
offset:0
|
||
scale:mScale_
|
||
fontSize:mFontSize_];
|
||
}
|
||
return mY_;
|
||
}
|
||
|
||
- (double)nextDeltaX {
|
||
[RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_];
|
||
long nextIndex = mDXIndex_ + 1;
|
||
if (nextIndex < [mDXs_ count]) {
|
||
mDXIndex_ = nextIndex;
|
||
NSString *string = [mDXs_ objectAtIndex:nextIndex];
|
||
double val = [RNSVGPropHelper fromRelativeWithNSString:string
|
||
relative:mWidth_
|
||
offset:0
|
||
scale:mScale_
|
||
fontSize:mFontSize_];
|
||
mDX_ += val;
|
||
}
|
||
return mDX_;
|
||
}
|
||
|
||
- (double)nextDeltaY {
|
||
[RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_];
|
||
long nextIndex = mDYIndex_ + 1;
|
||
if (nextIndex < [mDYs_ count]) {
|
||
mDYIndex_ = nextIndex;
|
||
NSString *string = [mDYs_ objectAtIndex:nextIndex];
|
||
double val = [RNSVGPropHelper fromRelativeWithNSString:string
|
||
relative:mHeight_
|
||
offset:0
|
||
scale:mScale_
|
||
fontSize:mFontSize_];
|
||
mDY_ += val;
|
||
}
|
||
return mDY_;
|
||
}
|
||
|
||
- (NSNumber*)nextRotation {
|
||
[RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_];
|
||
long nextIndex = mRIndex_ + 1;
|
||
long count = [mRs_ count];
|
||
if (nextIndex < count) {
|
||
mRIndex_ = nextIndex;
|
||
} else {
|
||
mRIndex_ = count - 1;
|
||
}
|
||
return mRs_[mRIndex_];
|
||
}
|
||
|
||
- (float)getWidth {
|
||
return mWidth_;
|
||
}
|
||
|
||
- (float)getHeight {
|
||
return mHeight_;
|
||
}
|
||
@end
|