Compare commits

..

14 Commits

Author SHA1 Message Date
Curtis Hard 717198457b Adds new method to create a trimmed string
- adds `const` in various places
- uses the new trimmed string method
- adds various memory releases in
2022-01-24 14:33:54 +00:00
Curtis Hard e2991ab8b0 Fixes smooth quad / curves having incorrect path data on export 2022-01-04 14:04:23 +00:00
Curtis Hard dda3e5ed8c Merge branch 'feature/ijsvgcolorsub' 2022-01-04 11:17:38 +00:00
Curtis Hard c3a0fb5b91 Fixes 10.9 issue with const 2022-01-04 11:17:15 +00:00
Curtis Hard 7ce4520a56 Adds currentColor as a predefined constant 2021-12-29 16:25:52 +00:00
Curtis Hard 368d19ea81 Nullable types 2021-12-28 22:33:12 +00:00
Curtis Hard d26c2f489f Adds delegate methods for color and ID 2021-12-28 19:28:32 +00:00
Curtis Hard 75b0d55b63 #oops, wrong list being used 2021-12-26 18:50:51 +00:00
Curtis Hard 310308cd8a Fixes stroke colorList stop colors not being identified correctly 2021-12-26 18:11:30 +00:00
Curtis Hard 278f405a41 Possible fix for older OS's 2021-12-15 15:16:59 +00:00
Curtis Hard 7addd97d0c Fixes issue with clipPath not using correct units
- adds support for overflow attribute
2021-11-24 15:15:54 +00:00
Curtis Hard d01ca0f3bb Fixes issue with wrong colors being used 2021-10-19 20:49:57 +01:00
Curtis Hard 6d7ad17e2a Fixes threading race condition 2021-05-11 22:06:56 +01:00
Curtis Hard deb09eaf04 Merge branch 'feature/color-stroke-checking' 2021-04-27 09:45:29 +01:00
20 changed files with 287 additions and 118 deletions
@@ -167,6 +167,8 @@ typedef NS_ENUM(NSInteger, IJSVGPredefinedColor) {
IJSVGColorYellowgreen
};
extern NSString* const IJSVGColorCurrentColorName;
@interface IJSVGColor : NSObject
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness);
@@ -11,10 +11,13 @@
#import "IJSVGStringAdditions.h"
#import "IJSVGParsing.h"
NSString* const IJSVGColorCurrentColorName = @"currentColor";
@implementation IJSVGColor
static NSDictionary* _colorTree = nil;
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness)
{
hue *= (1.f / 360.f);
@@ -248,14 +251,15 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
return nil;
}
char* str = (char*)string.UTF8String;
if(strlen(str) == 0) {
const char* oString = string.UTF8String;
if(strlen(oString) == 0) {
return nil;
}
IJSVGTrimCharBuffer(str);
char* str = IJSVGTimmedCharBufferCreate(oString);
if (IJSVGCharBufferIsHEX(str) == YES) {
return [self.class colorFromHEXString:string];
(void)free(str), str = NULL;
return [self.class colorFromHEXString:string];
}
// is it RGB?
@@ -265,6 +269,9 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
methods = IJSVGParsingMethodParseString(str, &count);
IJSVGParsingStringMethod* method = methods[0];
// memory clean for the string
(void)free(str), str = NULL;
// nothing to return, just mem clean and get out of here
if(count == 0 || methods == NULL) {
if(methods != NULL) {
@@ -310,6 +317,7 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
color = [self computeColorSpace:color];
// memory clean!
(void)free(str), str = NULL;
(void)free(hsb), hsb = NULL;
(void)free(params), params = NULL;
return color;
@@ -318,10 +326,12 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
// is simply a clear color, dont fill
if (strcmp(str, "none") == 0 ||
strcmp(str, "transparent") == 0) {
(void)free(str), str = NULL;
return [self computeColorSpace:NSColor.clearColor];
}
// could return nil
(void)free(str), str = NULL;
return [self.class colorFromPredefinedColorName:string];
}
@@ -20,7 +20,8 @@ static IJSVGPathDataSequence* _sequence;
+ (IJSVGPathDataSequence*)pathDataSequence
{
if(_sequence == NULL) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sequence = (IJSVGPathDataSequence*)malloc(sizeof(IJSVGPathDataSequence) * 7);
_sequence[0] = kIJSVGPathDataSequenceTypeFloat;
_sequence[1] = kIJSVGPathDataSequenceTypeFloat;
@@ -29,7 +30,7 @@ static IJSVGPathDataSequence* _sequence;
_sequence[4] = kIJSVGPathDataSequenceTypeFlag;
_sequence[5] = kIJSVGPathDataSequenceTypeFloat;
_sequence[6] = kIJSVGPathDataSequenceTypeFloat;
}
});
return _sequence;
}
+53 -52
View File
@@ -867,68 +867,69 @@
- (IJSVGColorList*)colorList
{
IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease];
IJSVGNodeWalkHandler handler = ^(IJSVGNode* node, BOOL* allowChildNodes,
BOOL* stop) {
// dont do anything if not told to render
if(node.shouldRender == NO) {
*allowChildNodes = NO;
void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
^void(CALayer* layer, BOOL isMask, BOOL* stop) {
// dont do anything
if(([layer isKindOfClass:IJSVGShapeLayer.class] && isMask == NO &&
layer.isHidden == NO) == false) {
return;
}
// compute
IJSVGShapeLayer* sLayer = (IJSVGShapeLayer*)layer;
NSColor* color = nil;
// fill color
NSColor* color;
if((color = node.fillColor) != nil &&
(color = [IJSVGColor computeColorSpace:color]) != nil &&
color.alphaComponent != 0.f) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagFill];
[sheet addColor:type];
if (sLayer.fillColor != nil) {
color = [NSColor colorWithCGColor:sLayer.fillColor];
color = [IJSVGColor computeColorSpace:color];
if (color.alphaComponent != 0.f) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagFill];
[sheet addColor:type];
}
}
// stroke color
if((color = node.strokeColor) != nil &&
(color = [IJSVGColor computeColorSpace:color]) != nil &&
color.alphaComponent != 0.f) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagStroke];
[sheet addColor:type];
if (sLayer.strokeColor != nil) {
color = [NSColor colorWithCGColor:sLayer.strokeColor];
color = [IJSVGColor computeColorSpace:color];
if (color.alphaComponent != 0.f) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagStroke];
[sheet addColor:type];
}
}
// fill gradient
IJSVGGradient* gradient;
if((gradient = node.fillGradient) != nil) {
IJSVGColorList* list = gradient.colorList;
[sheet addColorsFromList:list];
}
// stroke gradient
if((gradient = node.strokeGradient) != nil) {
IJSVGColorList* list = gradient.colorList;
[sheet addColorsFromList:list];
// check for any patterns or strokes
if (sLayer.patternFillLayer != nil || sLayer.gradientFillLayer != nil ||
sLayer.gradientStrokeLayer != nil || sLayer.patternStrokeLayer != nil) {
// add any colors from gradients
IJSVGGradientLayer* gradLayer = nil;
IJSVGGradientLayer* gradStrokeLayer = nil;
// gradient fill
if ((gradLayer = sLayer.gradientFillLayer) != nil) {
IJSVGColorList* gradSheet = gradLayer.gradient.colorList;
[sheet addColorsFromList:gradSheet];
}
// gradient stroke layers
if ((gradStrokeLayer = sLayer.gradientStrokeLayer) != nil) {
IJSVGColorList* gradSheet = gradStrokeLayer.gradient.colorList;
[sheet addColorsFromList:gradSheet];
}
}
};
[IJSVGNode walkNodeTree:_group
handler:handler];
// is it blank? - check for pathed nodes
if(sheet.count == 0) {
IJSVGNodeWalkHandler checkHandler = ^(IJSVGNode* node,
BOOL* allowChildNodes,
BOOL* stop) {
if([node isKindOfClass:IJSVGPath.class] == YES) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:[IJSVGColor colorFromHEXInteger:0x000000]
flags:IJSVGColorTypeFlagFill];
[sheet addColor:type];
*stop = YES;
}
};
[IJSVGNode walkNodeTree:_group
handler:checkHandler];
}
// gogogo!
[IJSVGLayer recursivelyWalkLayer:self.layer
withBlock:block];
return sheet;
}
@@ -8,8 +8,14 @@
#import <Foundation/Foundation.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGColorType.h>
@class IJSVG;
@class IJSVGExporter;
@class IJSVGLayer;
@class IJSVGNode;
NS_ASSUME_NONNULL_BEGIN
typedef void (^IJSVGCGPathHandler)(const CGPathElement* pathElement);
typedef void (^IJSVGPathElementEnumerationBlock)(const CGPathElement* pathElement, CGPoint currentPoint);
@@ -39,7 +45,16 @@ typedef NS_OPTIONS(NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionConvertShapesToPaths = 1 << 19,
IJSVGExporterOptionRoundTransforms = 1 << 20,
IJSVGExporterOptionRemoveDefaultValues = 1 << 21,
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef | IJSVGExporterOptionRemoveUselessGroups | IJSVGExporterOptionCreateUseForPaths | IJSVGExporterOptionMoveAttributesToGroup | IJSVGExporterOptionSortAttributes | IJSVGExporterOptionCollapseGroups | IJSVGExporterOptionCleanupPaths | IJSVGExporterOptionRemoveHiddenElements | IJSVGExporterOptionScaleToSizeIfNecessary | IJSVGExporterOptionCompressOutput | IJSVGExporterOptionCollapseGradients | IJSVGExporterOptionRemoveWidthHeightAttributes | IJSVGExporterOptionColorAllowRRGGBBAA | IJSVGExporterOptionRemoveComments | IJSVGExporterOptionCenterWithinViewBox | IJSVGExporterOptionRemoveXMLDeclaration | IJSVGExporterOptionConvertArcs | IJSVGExporterOptionConvertShapesToPaths | IJSVGExporterOptionRoundTransforms | IJSVGExporterOptionRemoveDefaultValues
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef | IJSVGExporterOptionRemoveUselessGroups |
IJSVGExporterOptionCreateUseForPaths | IJSVGExporterOptionMoveAttributesToGroup |
IJSVGExporterOptionSortAttributes | IJSVGExporterOptionCollapseGroups |
IJSVGExporterOptionCleanupPaths | IJSVGExporterOptionRemoveHiddenElements |
IJSVGExporterOptionScaleToSizeIfNecessary | IJSVGExporterOptionCompressOutput |
IJSVGExporterOptionCollapseGradients | IJSVGExporterOptionRemoveWidthHeightAttributes |
IJSVGExporterOptionColorAllowRRGGBBAA | IJSVGExporterOptionRemoveComments |
IJSVGExporterOptionCenterWithinViewBox | IJSVGExporterOptionRemoveXMLDeclaration |
IJSVGExporterOptionConvertArcs | IJSVGExporterOptionConvertShapesToPaths |
IJSVGExporterOptionRoundTransforms | IJSVGExporterOptionRemoveDefaultValues
};
BOOL IJSVGExporterHasOption(IJSVGExporterOptions options, NSInteger option);
@@ -47,6 +62,22 @@ void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlo
const NSArray<NSString*>* IJSVGShortCharacterArray(void);
const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void);
@protocol IJSVGExporterDelegate <NSObject>
@optional
- (NSString* _Nullable)svgExporter:(IJSVGExporter*)exporter
identifierForElement:(NSXMLElement* _Nullable)element
type:(IJSVGNodeType)type
defaultID:(NSString* (^)(void))defaultID;
- (NSString* _Nullable)svgExporter:(IJSVGExporter*)exporter
stringForColor:(NSColor*)color
flags:(IJSVGColorTypeFlags)flag
options:(IJSVGColorStringOptions)options;
@end
@interface IJSVGExporter : NSObject {
@private
@@ -58,22 +89,30 @@ const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void);
NSInteger _idCount;
NSInteger _shortIdCount;
BOOL _appliedXLink;
struct {
unsigned int identifierForElement: 1;
unsigned int stringForColor: 1;
} _respondsTo;
}
@property (nonatomic, assign) id<IJSVGExporterDelegate> delegate;
@property (nonatomic, assign) IJSVGFloatingPointOptions floatingPointOptions;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, copy, nullable) NSString* title;
@property (nonatomic, copy, nullable) NSString* desc;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGString;
- (NSData*)SVGData;
- (IJSVG*)SVG:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
@@ -201,15 +201,16 @@ NSString* IJSVGHash(NSString* key)
size:(CGSize)size
options:(IJSVGExporterOptions)options
{
IJSVGFloatingPointOptions fpo = IJSVGFloatingPointOptionsDefault();
return [self initWithSVG:svg
size:size
options:options
floatingPointOptions:IJSVGFloatingPointOptionsDefault()];
floatingPointOptions:fpo];
}
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
if ((self = [super init]) != nil) {
@@ -219,15 +220,17 @@ NSString* IJSVGHash(NSString* key)
// defaults for floating point rounding, if any
_floatingPointOptions = floatingPointOptions;
// clear memory as soon as possible
@autoreleasepool {
[self _prepare];
}
}
return self;
}
- (void)setDelegate:(id<IJSVGExporterDelegate>)delegate
{
_delegate = delegate;
_respondsTo.identifierForElement = [delegate respondsToSelector:@selector(svgExporter:identifierForElement:type:defaultID:)];
_respondsTo.stringForColor = [delegate respondsToSelector:@selector(svgExporter:stringForColor:flags:options:)];
}
- (NSXMLElement*)defElement
{
if (_defElement != nil) {
@@ -375,8 +378,15 @@ NSString* IJSVGHash(NSString* key)
return [NSString stringWithFormat:@"%@%ld", chars[(_idCount++ % chars.count)], _shortIdCount];
}
- (void)_prepare
- (void)_generateDOMDocument
{
_idCount = 0;
_shortIdCount = 0;
_appliedXLink = NO;
(void)[_dom release], _dom = nil;
(void)[_defElement release], _defElement = nil;
// create the stand alone DOM
NSXMLElement* nestedRoot = nil;
NSXMLElement* rootNode = [self rootNode:&nestedRoot];
@@ -589,7 +599,7 @@ NSString* IJSVGHash(NSString* key)
if ([self compareElementChildren:gradientA toElement:gradientB] == YES) {
NSString* idString = [gradientB attributeForName:@"id"].stringValue;
if (idString == nil || idString.length == 0) {
idString = [self generateID];
idString = [self identifierForElement:gradientA];
IJSVGApplyAttributesToElement(@{ @"id" : idString }, gradientB);
}
NSDictionary* atts = @{ @"xlink:href" : IJSVGHash(idString) };
@@ -920,7 +930,7 @@ NSString* IJSVGHash(NSString* key)
element.name = @"path";
NSDictionary* atts = @{ @"d" : data,
@"id" : [self generateID] };
@"id" : [self identifierForElement:element] };
IJSVGApplyAttributesToElement(atts, element);
// store it against the def
@@ -1124,7 +1134,7 @@ NSString* IJSVGHash(NSString* key)
patternElement.name = @"pattern";
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
dict[@"id"] = [self generateID];
dict[@"id"] = [self identifierForElement:patternElement];
dict[@"width"] = IJSVGShortFloatStringWithOptions(layer.patternNode.width.value, _floatingPointOptions);
dict[@"height"] = IJSVGShortFloatStringWithOptions(layer.patternNode.height.value, _floatingPointOptions);
@@ -1172,7 +1182,6 @@ NSString* IJSVGHash(NSString* key)
toElement:(NSXMLElement*)element
{
IJSVGGradient* gradient = layer.gradient;
NSString* gradKey = [self generateID];
NSXMLElement* gradientElement = [[[NSXMLElement alloc] init] autorelease];
// work out linear gradient
@@ -1180,7 +1189,7 @@ NSString* IJSVGHash(NSString* key)
IJSVGLinearGradient* lGradient = (IJSVGLinearGradient*)gradient;
gradientElement.name = @"linearGradient";
NSDictionary* dict = @{ @"id" : gradKey,
NSDictionary* dict = @{
@"x1" : [lGradient.x1 stringValueWithFloatingPointOptions:_floatingPointOptions],
@"y1" : [lGradient.y1 stringValueWithFloatingPointOptions:_floatingPointOptions],
@"x2" : [lGradient.x2 stringValueWithFloatingPointOptions:_floatingPointOptions],
@@ -1193,7 +1202,7 @@ NSString* IJSVGHash(NSString* key)
// assume radial
IJSVGRadialGradient* rGradient = (IJSVGRadialGradient*)gradient;
gradientElement.name = @"radialGradient";
NSDictionary* dict = @{ @"id" : gradKey,
NSDictionary* dict = @{
@"cx" : [rGradient.cx stringValueWithFloatingPointOptions:_floatingPointOptions],
@"cy" : [rGradient.cy stringValueWithFloatingPointOptions:_floatingPointOptions],
@"fx" : [rGradient.fx stringValueWithFloatingPointOptions:_floatingPointOptions],
@@ -1203,6 +1212,10 @@ NSString* IJSVGHash(NSString* key)
// give it the attributes
IJSVGApplyAttributesToElement(dict, gradientElement);
}
// apply the identifier
NSString* gradKey = [self identifierForElement:gradientElement];
IJSVGApplyAttributesToElement(@{@"id": gradKey}, gradientElement);
// apply the units
if (layer.gradient.units == IJSVGUnitUserSpaceOnUse) {
@@ -1236,9 +1249,9 @@ NSString* IJSVGHash(NSString* key)
// add the color
IJSVGColorStringOptions options = IJSVGColorStringOptionForceHEX | IJSVGColorStringOptionAllowShortHand;
NSString* stopColor = [IJSVGColor colorStringFromColor:aColor
options:options];
NSString* stopColor = [self colorStringForColor:aColor
flag:IJSVGColorTypeFlagStop
options:options];
// dont bother adding default
if ([stopColor isEqualToString:@"#000"] == NO) {
atts[@"stop-color"] = stopColor;
@@ -1297,7 +1310,7 @@ NSString* IJSVGHash(NSString* key)
imageElement.name = @"image";
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
dict[@"id"] = [self generateID];
dict[@"id"] = [self identifierForElement:imageElement];
dict[@"width"] = IJSVGShortFloatStringWithOptions(layer.frame.size.width, _floatingPointOptions);
dict[@"height"] = IJSVGShortFloatStringWithOptions(layer.frame.size.height, _floatingPointOptions);
dict[@"xlink:href"] = base64String;
@@ -1682,8 +1695,9 @@ NSString* IJSVGHash(NSString* key)
// fill color
if (layer.fillColor != nil) {
NSColor* fillColor = [NSColor colorWithCGColor:layer.fillColor];
NSString* colorString = [IJSVGColor colorStringFromColor:fillColor
options:[self colorOptions]];
NSString* colorString = [self colorStringForColor:fillColor
flag:IJSVGColorTypeFlagFill
options:[self colorOptions]];
// could be none
if (colorString != nil) {
@@ -1734,8 +1748,9 @@ NSString* IJSVGHash(NSString* key)
} else if (strokeLayer.strokeColor != nil) {
NSColor* strokeColor = [NSColor colorWithCGColor:strokeLayer.strokeColor];
NSString* strokeColorString = [IJSVGColor colorStringFromColor:strokeColor
options:[self colorOptions]];
NSString* strokeColorString = [self colorStringForColor:strokeColor
flag:IJSVGColorTypeFlagStroke
options:[self colorOptions]];
// could be none
if (strokeColorString != nil) {
@@ -1858,7 +1873,7 @@ NSString* IJSVGHash(NSString* key)
mask.name = @"mask";
// create the key
NSString* maskKey = [self generateID];
NSString* maskKey = [self identifierForElement:mask];
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
dict[@"id"] = maskKey;
@@ -1884,6 +1899,16 @@ NSString* IJSVGHash(NSString* key)
[[self defElement] addChild:mask];
}
- (NSXMLDocument*)_dom
{
if(_dom == nil) {
@autoreleasepool {
[self _generateDOMDocument];
}
}
return _dom;
}
- (NSString*)SVGString
{
NSXMLNodeOptions options = NSXMLNodePrettyPrint;
@@ -1891,7 +1916,7 @@ NSString* IJSVGHash(NSString* key)
options = NSXMLNodeOptionsNone;
}
options |= NSXMLNodeCompactEmptyElement;
NSString* output = [_dom XMLStringWithOptions:options];
NSString* output = [[self _dom] XMLStringWithOptions:options];
if (IJSVGExporterHasOption(_options, IJSVGExporterOptionRemoveXMLDeclaration) == YES) {
return [output substringFromIndex:38];
}
@@ -2039,4 +2064,46 @@ void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlo
}
}
#pragma mark Delegate calling methods
- (NSString*)identifierForElement:(NSXMLElement* _Nullable)element
{
NSString* identifier = nil;
if(_respondsTo.identifierForElement == 1) {
__weak id weakSelf = self;
NSString* (^block)(void) = ^NSString*(void) {
return [weakSelf generateID];
};
IJSVGNodeType type = [IJSVGNode typeForString:element.localName
kind:element.kind];
identifier = [_delegate svgExporter:self
identifierForElement:element
type:type
defaultID:block];
if(identifier != nil) {
return identifier;
}
}
return [self generateID];
}
- (NSString*)colorStringForColor:(NSColor*)color
flag:(IJSVGColorTypeFlags)flag
options:(IJSVGColorStringOptions)options
{
NSString* colorString = nil;
if(_respondsTo.stringForColor == 1) {
color = [IJSVGColor computeColorSpace:color];
colorString = [_delegate svgExporter:self
stringForColor:color
flags:flag
options:options];
if(colorString != nil) {
return colorString;
}
}
return [IJSVGColor colorStringFromColor:color
options:options];
}
@end
@@ -364,16 +364,16 @@ void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length,
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2] autorelease];
nInstruction.data[0] = instruction.data[3];
nInstruction.data[1] = instruction.data[4];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
[nInstructions addObject:nInstruction];
continue;
} else if (lastInstruction.instruction == 't' && instruction.data[2] == lastInstruction.data[0] && instruction.data[3] == lastInstruction.data[1]) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2] autorelease];
nInstruction.data[0] = instruction.data[3];
nInstruction.data[1] = instruction.data[4];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
[nInstructions addObject:nInstruction];
continue;
}
@@ -40,6 +40,7 @@
_privateColorList = list.retain;
if (_CGGradient != nil) {
CGGradientRelease(_CGGradient);
_CGGradient = nil;
}
}
@@ -99,6 +99,11 @@ typedef NS_ENUM(NSInteger, IJSVGBlendMode) {
IJSVGBlendModeLuminosity = kCGBlendModeLuminosity
};
typedef NS_ENUM(NSInteger, IJSVGOverflowVisibility) {
IJSVGOverflowVisibilityHidden,
IJSVGOverflowVisibilityVisible
};
static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@interface IJSVGNode : NSObject <NSCopying>
@@ -144,6 +149,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@property (nonatomic, assign) IJSVGUnitType contentUnits;
@property (nonatomic, assign) IJSVGUnitType units;
@property (nonatomic, assign) IJSVGBlendMode blendMode;
@property (nonatomic, assign) IJSVGOverflowVisibility overflowVisibility;
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler;
@@ -50,11 +50,15 @@
+ (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind
{
const char* name = string.UTF8String;
// possible fix for older os's that complain
if(string == nil || kind == NSXMLCommentKind) {
return IJSVGNodeTypeNotFound;
}
const char* name = string.lowercaseString.UTF8String;
if(name == NULL) {
return IJSVGNodeTypeNotFound;
}
IJSVGCharBufferToLower((char*)name);
if (strcmp(name, "style") == 0) {
return IJSVGNodeTypeStyle;
}
@@ -233,6 +237,7 @@
self.shouldRender = node.shouldRender;
self.blendMode = node.blendMode;
self.overflowVisibility = node.overflowVisibility;
// dash array needs physical memory copied
CGFloat* nStrokeDashArray = (CGFloat*)malloc(node.strokeDashArrayCount * sizeof(CGFloat));
@@ -271,6 +276,7 @@
self.units = IJSVGUnitInherit;
self.blendMode = IJSVGBlendModeNormal;
self.overflowVisibility = IJSVGOverflowVisibilityVisible;
if (flag == YES) {
_def = [[IJSVGDef alloc] init];
@@ -72,6 +72,7 @@ extern NSString* const IJSVGAttributeOffset;
extern NSString* const IJSVGAttributeStopColor;
extern NSString* const IJSVGAttributeStopOpacity;
extern NSString* const IJSVGAttributeHref;
extern NSString* const IJSVGAttributeOverflow;
@class IJSVGParser;
@@ -56,6 +56,7 @@ NSString* const IJSVGAttributeOffset = @"offset";
NSString* const IJSVGAttributeStopColor = @"stop-color";
NSString* const IJSVGAttributeStopOpacity = @"stop-opacity";
NSString* const IJSVGAttributeHref = @"href";
NSString* const IJSVGAttributeOverflow = @"overflow";
@implementation IJSVGParser
@@ -535,6 +536,15 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
}
});
// overflow
attr(IJSVGAttributeOverflow, ^(NSString* value) {
if([value.lowercaseString isEqualToString:@"hidden"]) {
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
} else {
node.overflowVisibility = IJSVGOverflowVisibilityVisible;
}
});
// is there a title or desc?
for(NSXMLElement* childElement in element.children) {
IJSVGNodeType type = [IJSVGNode typeForString:childElement.localName
@@ -632,9 +642,17 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
switch (node.type) {
// mask
case IJSVGNodeTypeMask: {
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
node.units = IJSVGUnitObjectBoundingBox;
break;
}
// clippath
case IJSVGNodeTypeClipPath: {
node.units = IJSVGUnitObjectBoundingBox;
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
break;
}
// gradient
case IJSVGNodeTypeRadialGradient:
@@ -604,13 +604,13 @@
IJSVGGroupLayer* maskLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
// add clip mask
if (node.clipPath != nil) {
if (node.clipPath != nil && node.clipPath.overflowVisibility == IJSVGOverflowVisibilityHidden) {
IJSVGLayer* clip = [self layerForNode:node.clipPath];
// adjust the frame
if (node.clipPath.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:clip
toParentLayerFrame:layer];
toParentLayerFrame:layer];
}
// add the layer
@@ -618,26 +618,27 @@
}
// add the actual mask
if (node.mask != nil) {
if (node.mask != nil && node.mask.overflowVisibility == IJSVGOverflowVisibilityHidden) {
IJSVGLayer* mask = [self layerForNode:node.mask];
// only move if bounding box
if (node.mask.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:mask
toParentLayerFrame:layer];
toParentLayerFrame:layer];
}
// add the layer
[maskLayer addSublayer:mask];
}
// recursive colourize for each item
NSColor* color = [IJSVGColor computeColorSpace:NSColor.whiteColor];
[self _recursiveColorLayersFromLayer:maskLayer
withColor:color.CGColor];
// add the mask
layer.mask = maskLayer;
if(maskLayer.sublayers.count != 0) {
// recursive colourize for each item
NSColor* color = [IJSVGColor computeColorSpace:NSColor.whiteColor];
[self _recursiveColorLayersFromLayer:maskLayer
withColor:color.CGColor];
layer.mask = maskLayer;
}
}
}
@@ -91,7 +91,7 @@
+ (NSArray*)allowedColourKeys
{
return @[ @"fill", @"stroke-colour", @"stop-color", @"stroke" ];
return @[ @"fill", @"stroke-color", @"stop-color", @"stroke" ];
}
- (void)setProperties:(NSDictionary*)properties
@@ -15,7 +15,7 @@ typedef struct {
IJSVGParsingStringMethod* IJSVGParsingStringMethodCreate(void);
void IJSVGParsingStringMethodRelease(IJSVGParsingStringMethod* stringMethod);
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(char* string,
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(const char* string,
NSUInteger* count);
void IJSVGParsingStringMethodsRelease(IJSVGParsingStringMethod** methods,
NSUInteger count);
@@ -39,10 +39,10 @@ void IJSVGParsingStringMethodsRelease(IJSVGParsingStringMethod** methods,
(void)free(methods), methods = NULL;
}
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(char* string,
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(const char* string,
NSUInteger* count)
{
char* charString = string;
const char* charString = string;
unsigned long length = strlen(string);
char* buffer = (char*)calloc(sizeof(char), length);
char* originBuffer = buffer;
@@ -171,9 +171,8 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
const char* charString = string.UTF8String;
IJSVGParsingStringMethod** methods = NULL;
NSUInteger count = 0;
methods = IJSVGParsingMethodParseString((char*)charString, &count);
methods = IJSVGParsingMethodParseString(charString, &count);
for(int i = 0; i < count; i++) {
IJSVGParsingStringMethod* method = methods[i];
IJSVGTransformCommand commandType;
commandType = [self.class commandForCommandCString:method->name];
@@ -143,12 +143,12 @@
return nil;
}
const char* chars = string.UTF8String;
IJSVGTrimCharBuffer((char*)chars);
char* chars = IJSVGTimmedCharBufferCreate(string.UTF8String);
// is inherit or just nothing
size_t strl = strlen(chars);
if (strcmp(chars, "inherit") == 0 || strl == 0) {
(void)free(chars), chars = NULL;
return nil;
}
@@ -158,7 +158,6 @@
floatCount:1
charCount:(NSUInteger)strl
size:&length];
// not sure how this ended up but nothing returned
// even though there should had been
if(length == 0) {
@@ -170,12 +169,14 @@
unit.value = floats[0];
unit.type = IJSVGUnitLengthTypeNumber;
// memory free
(void)(free(floats)), floats = NULL;
IJSVGUnitLengthType type = [self typeForCString:chars];
unit.originalType = type;
// memory free
(void)(free(floats)), floats = NULL;
(void)free(chars), chars = NULL;
switch(type) {
case IJSVGUnitLengthTypePercentage: {
unit.value = [self convertUnitValue:unit.value
@@ -24,6 +24,7 @@ CGFloat degrees_to_radians(CGFloat degrees);
BOOL IJSVGCharBufferIsHEX(char* buffer);
BOOL IJSVGCharBufferHasPrefix(char* pre, char* str);
BOOL IJSVGCharBufferHasSuffix(char* s1, char* s2);
char* IJSVGTimmedCharBufferCreate(const char* buffer);
void IJSVGTrimCharBuffer(char* buffer);
void IJSVGCharBufferToLower(char* buffer);
size_t IJSVGCharBufferHash(char* buffer);
@@ -43,8 +43,23 @@ BOOL IJSVGCharBufferHasSuffix(char* s1, char* s2)
return strcmp(s1 + slen - tlen, s2) == 0;
}
void IJSVGTrimCharBuffer(char* buffer)
char* IJSVGTimmedCharBufferCreate(const char* buffer)
{
unsigned long start = 0;
unsigned long length = strlen(buffer);
while(length-1 > 0 && isspace(buffer[length-1])) {
length--;
}
while(isspace(buffer[start])) {
start++;
}
char* chars = (char*)malloc(sizeof(char)*((length-start)+1) ?: sizeof(char));
memcpy(chars, &buffer[start], length-start);
chars[length] = '\0';
return chars;
}
void IJSVGTrimCharBuffer(char* buffer) {
char* ptr = buffer;
unsigned long length = strlen(ptr);
while(length-1 > 0 && isspace(ptr[length-1])) {
@@ -235,7 +250,7 @@ CGFloat degrees_to_radians(CGFloat degrees)
const char* str = string.UTF8String;
NSUInteger count = 0;
IJSVGParsingStringMethod** methods;
methods = IJSVGParsingMethodParseString((char*)str, &count);
methods = IJSVGParsingMethodParseString(str, &count);
if(count == 0) {
IJSVGParsingStringMethodsRelease(methods, count);
return nil;