Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 717198457b | |||
| e2991ab8b0 | |||
| dda3e5ed8c | |||
| c3a0fb5b91 | |||
| 7ce4520a56 | |||
| 368d19ea81 | |||
| d26c2f489f | |||
| 75b0d55b63 | |||
| 310308cd8a | |||
| 278f405a41 | |||
| 7addd97d0c | |||
| d01ca0f3bb | |||
| 6d7ad17e2a | |||
| deb09eaf04 |
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user