123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907 |
- #import "SVGKPointsAndPathsParser.h"
- #import "NSCharacterSet+SVGKExtensions.h"
- static inline SVGCurve SVGCurveMakePoint(CGPoint p)
- {
- SVGCurve curve;
- curve.type = SVGCurveTypePoint;
-
- curve.c1 = p;
- curve.c2 = p;
- curve.p = p;
- return curve;
- }
- static inline CGPoint SVGCurveReflectedControlPoint(SVGCurve prevCurve)
- {
- return CGPointMake(prevCurve.p.x+(prevCurve.p.x-prevCurve.c2.x), prevCurve.p.y+(prevCurve.p.y-prevCurve.c2.y));
- }
- @implementation SVGKPointsAndPathsParser
- + (SVGCurve) startingCurve
- {
- return SVGCurveMakePoint(CGPointZero);
- }
- /* references
- http://www.w3.org/TR/2011/REC-SVG11-20110816/paths.html#PathDataBNF
- http://www.w3.org/TR/2011/REC-SVG11-20110816/shapes.html#PointsBNF
-
- */
- /*
- http://www.w3.org/TR/2011/REC-SVG11-20110816/paths.html#PathDataBNF
- svg-path:
- wsp* moveto-drawto-command-groups? wsp*
- moveto-drawto-command-groups:
- moveto-drawto-command-group
- | moveto-drawto-command-group wsp* moveto-drawto-command-groups
- moveto-drawto-command-group:
- moveto wsp* drawto-commands?
- drawto-commands:
- drawto-command
- | drawto-command wsp* drawto-commands
- drawto-command:
- closepath
- | lineto
- | horizontal-lineto
- | vertical-lineto
- | curveto
- | smooth-curveto
- | quadratic-bezier-curveto
- | smooth-quadratic-bezier-curveto
- | elliptical-arc
- moveto:
- ( "M" | "m" ) wsp* moveto-argument-sequence
- moveto-argument-sequence:
- coordinate-pair
- | coordinate-pair comma-wsp? lineto-argument-sequence
- closepath:
- ("Z" | "z")
- lineto:
- ( "L" | "l" ) wsp* lineto-argument-sequence
- lineto-argument-sequence:
- coordinate-pair
- | coordinate-pair comma-wsp? lineto-argument-sequence
- horizontal-lineto:
- ( "H" | "h" ) wsp* horizontal-lineto-argument-sequence
- horizontal-lineto-argument-sequence:
- coordinate
- | coordinate comma-wsp? horizontal-lineto-argument-sequence
- vertical-lineto:
- ( "V" | "v" ) wsp* vertical-lineto-argument-sequence
- vertical-lineto-argument-sequence:
- coordinate
- | coordinate comma-wsp? vertical-lineto-argument-sequence
- curveto:
- ( "C" | "c" ) wsp* curveto-argument-sequence
- curveto-argument-sequence:
- curveto-argument
- | curveto-argument comma-wsp? curveto-argument-sequence
- curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair comma-wsp? coordinate-pair
- smooth-curveto:
- ( "S" | "s" ) wsp* smooth-curveto-argument-sequence
- smooth-curveto-argument-sequence:
- smooth-curveto-argument
- | smooth-curveto-argument comma-wsp? smooth-curveto-argument-sequence
- smooth-curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair
- quadratic-bezier-curveto:
- ( "Q" | "q" ) wsp* quadratic-bezier-curveto-argument-sequence
- quadratic-bezier-curveto-argument-sequence:
- quadratic-bezier-curveto-argument
- | quadratic-bezier-curveto-argument comma-wsp?
- quadratic-bezier-curveto-argument-sequence
- quadratic-bezier-curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair
- smooth-quadratic-bezier-curveto:
- ( "T" | "t" ) wsp* smooth-quadratic-bezier-curveto-argument-sequence
- smooth-quadratic-bezier-curveto-argument-sequence:
- coordinate-pair
- | coordinate-pair comma-wsp? smooth-quadratic-bezier-curveto-argument-sequence
- elliptical-arc:
- ( "A" | "a" ) wsp* elliptical-arc-argument-sequence
- elliptical-arc-argument-sequence:
- elliptical-arc-argument
- | elliptical-arc-argument comma-wsp? elliptical-arc-argument-sequence
- elliptical-arc-argument:
- nonnegative-number comma-wsp? nonnegative-number comma-wsp?
- number comma-wsp flag comma-wsp? flag comma-wsp? coordinate-pair
- coordinate-pair:
- coordinate comma-wsp? coordinate
- coordinate:
- number
- nonnegative-number:
- integer-constant
- | floating-point-constant
- number:
- sign? integer-constant
- | sign? floating-point-constant
- flag:
- "0" | "1"
- comma-wsp:
- (wsp+ comma? wsp*) | (comma wsp*)
- comma:
- ","
- integer-constant:
- digit-sequence
- floating-point-constant:
- fractional-constant exponent?
- | digit-sequence exponent
- fractional-constant:
- digit-sequence? "." digit-sequence
- | digit-sequence "."
- exponent:
- ( "e" | "E" ) sign? digit-sequence
- sign:
- "+" | "-"
- digit-sequence:
- digit
- | digit digit-sequence
- digit:
- "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
- */
- /*
- http://www.w3.org/TR/2011/REC-SVG11-20110816/shapes.html#PointsBNF
-
- list-of-points:
- wsp* coordinate-pairs? wsp*
- coordinate-pairs:
- coordinate-pair
- | coordinate-pair comma-wsp coordinate-pairs
- coordinate-pair:
- coordinate comma-wsp coordinate
- | coordinate negative-coordinate
- coordinate:
- number
- number:
- sign? integer-constant
- | sign? floating-point-constant
- negative-coordinate:
- "-" integer-constant
- | "-" floating-point-constant
- comma-wsp:
- (wsp+ comma? wsp*) | (comma wsp*)
- comma:
- ","
- integer-constant:
- digit-sequence
- floating-point-constant:
- fractional-constant exponent?
- | digit-sequence exponent
- fractional-constant:
- digit-sequence? "." digit-sequence
- | digit-sequence "."
- exponent:
- ( "e" | "E" ) sign? digit-sequence
- sign:
- "+" | "-"
- digit-sequence:
- digit
- | digit digit-sequence
- digit:
- "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
- */
- /**
- wsp:
- (#x20 | #x9 | #xD | #xA)
- */
- + (void) readWhitespace:(NSScanner*)scanner
- {
- /** This log message can be called literally hundreds of thousands of times in a single parse, which defeats
- even Cocoa Lumberjack.
-
- Even in "verbose" debugging, that's too much!
-
- Hence: commented-out
- SVGKitLogVerbose(@"Apple's implementation of scanCharactersFromSet seems to generate large amounts of temporary objects and can cause a crash here by taking literally megabytes of RAM in temporary internal variables. This is surprising, but I can't see anythign we're doing wrong. Adding this autoreleasepool drops memory usage (inside Apple's methods!) massively, so it seems to be the right thing to do");
- */
- @autoreleasepool
- {
- [scanner scanCharactersFromSet:[NSCharacterSet SVGWhitespaceCharacterSet]
- intoString:NULL];
- }
- }
- + (void) readCommaAndWhitespace:(NSScanner*)scanner
- {
- [SVGKPointsAndPathsParser readWhitespace:scanner];
- static NSString* comma = @",";
- [scanner scanString:comma intoString:NULL];
- [SVGKPointsAndPathsParser readWhitespace:scanner];
- }
- /**
- moveto-drawto-command-groups:
- moveto-drawto-command-group
- | moveto-drawto-command-group wsp* moveto-drawto-command-groups
- */
- + (SVGCurve) readMovetoDrawtoCommandGroups:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: move-to, draw-to command");
- #endif
- SVGCurve lastCurve = [SVGKPointsAndPathsParser readMovetoDrawto:scanner path:path relativeTo:origin isRelative:isRelative];
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- while (![scanner isAtEnd])
- {
- [SVGKPointsAndPathsParser readWhitespace:scanner];
- /** FIXME: wasn't originally, but maybe should be:
-
- origin = isRelative ? lastCoord : origin;
- */
- lastCurve = [SVGKPointsAndPathsParser readMovetoDrawto:scanner path:path relativeTo:origin isRelative:isRelative];
- }
-
- return lastCurve;
- }
- /** moveto-drawto-command-group:
- moveto wsp* drawto-commands?
- */
- + (SVGCurve) readMovetoDrawto:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- SVGCurve lastCurve = [SVGKPointsAndPathsParser readMoveto:scanner path:path relativeTo:origin isRelative:isRelative];
- [SVGKPointsAndPathsParser readWhitespace:scanner];
- return lastCurve;
- }
- /**
- moveto:
- ( "M" | "m" ) wsp* moveto-argument-sequence
- */
- + (SVGCurve) readMoveto:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Mm"];
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd] )
- {
- NSAssert(FALSE, @"failed to scan move to command");
- return SVGCurveMakePoint(origin);
- }
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readMovetoArgumentSequence:scanner path:path relativeTo:origin isRelative:isRelative];
- }
- /** moveto-argument-sequence:
- coordinate-pair
- | coordinate-pair comma-wsp? lineto-argument-sequence
- */
- + (SVGCurve) readMovetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- CGPoint coord = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- coord.x += origin.x;
- coord.y += origin.y;
-
- CGPathMoveToPoint(path, NULL, coord.x, coord.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: MOVED to %2.2f, %2.2f", [SVGKPointsAndPathsParser class], coord.x, coord.y );
- #endif
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- if ([scanner isAtEnd]) {
- return SVGCurveMakePoint(coord);
- } else {
- return [SVGKPointsAndPathsParser readLinetoArgumentSequence:scanner path:path relativeTo:(isRelative)?coord:origin isRelative:isRelative];
- }
- }
- /**
- coordinate-pair:
- coordinate comma-wsp? coordinate
- */
- + (CGPoint) readCoordinatePair:(NSScanner*)scanner
- {
- CGPoint p;
- [SVGKPointsAndPathsParser readCoordinate:scanner intoFloat:&p.x];
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
- [SVGKPointsAndPathsParser readCoordinate:scanner intoFloat:&p.y];
-
- return p;
- }
- + (void) readCoordinate:(NSScanner*)scanner intoFloat:(CGFloat*) floatPointer
- {
- #if CGFLOAT_IS_DOUBLE
- if( ![scanner scanDouble:floatPointer])
- NSAssert(FALSE, @"invalid coord");
- #else
- if( ![scanner scanFloat:floatPointer])
- NSAssert(FALSE, @"invalid coord");
- #endif
- }
- /**
- lineto:
- ( "L" | "l" ) wsp* lineto-argument-sequence
- */
- + (SVGCurve) readLinetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: line-to command");
- #endif
-
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Ll"];
-
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd] )
- {
- NSAssert( FALSE, @"failed to scan line to command");
- return SVGCurveMakePoint(origin);
- }
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readLinetoArgumentSequence:scanner path:path relativeTo:origin isRelative:isRelative];
- }
- /**
- lineto-argument-sequence:
- coordinate-pair
- | coordinate-pair comma-wsp? lineto-argument-sequence
- */
- + (SVGCurve) readLinetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- CGPoint p = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- CGPoint coord = CGPointMake(p.x+origin.x, p.y+origin.y);
- CGPathAddLineToPoint(path, NULL, coord.x, coord.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: LINE to %2.2f, %2.2f", [SVGKPointsAndPathsParser class], coord.x, coord.y );
- #endif
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- while( ![scanner isAtEnd])
- {
- origin = (isRelative)?coord:origin;
- p = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- coord = CGPointMake(p.x+origin.x, p.y+origin.y);
- CGPathAddLineToPoint(path, NULL, coord.x, coord.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: LINE to %2.2f, %2.2f", [SVGKPointsAndPathsParser class], coord.x, coord.y );
- #endif
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
- }
-
- return SVGCurveMakePoint(coord);
- }
- /**
- quadratic-bezier-curveto:
- ( "Q" | "q" ) wsp* quadratic-bezier-curveto-argument-sequence
- */
- + (SVGCurve) readQuadraticCurvetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: quadratic-bezier-curve-to command");
- #endif
-
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Qq"];
-
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd] )
- {
- NSAssert( FALSE, @"failed to scan quadratic curve to command");
- return SVGCurveMakePoint(origin);
- }
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readQuadraticCurvetoArgumentSequence:scanner path:path relativeTo:origin isRelative:isRelative];
- }
- /**
- quadratic-bezier-curveto-argument-sequence:
- quadratic-bezier-curveto-argument
- | quadratic-bezier-curveto-argument comma-wsp? quadratic-bezier-curveto-argument-sequence
- */
- + (SVGCurve) readQuadraticCurvetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- SVGCurve curve = [SVGKPointsAndPathsParser readQuadraticCurvetoArgument:scanner path:path relativeTo:origin];
-
- while(![scanner isAtEnd])
- {
- curve = [SVGKPointsAndPathsParser readQuadraticCurvetoArgument:scanner path:path relativeTo:(isRelative ? curve.p : origin)];
- }
-
- return curve;
- }
- /**
- quadratic-bezier-curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair
- */
- + (SVGCurve) readQuadraticCurvetoArgument:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- SVGCurve curveResult;
- curveResult.type = SVGCurveTypeQuadratic;
-
- curveResult.c1 = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- curveResult.c1.x += origin.x;
- curveResult.c1.y += origin.y;
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- curveResult.c2 = curveResult.c1;
-
- curveResult.p = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- curveResult.p.x += origin.x;
- curveResult.p.y += origin.y;
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- CGPathAddQuadCurveToPoint(path, NULL, curveResult.c1.x, curveResult.c1.y, curveResult.p.x, curveResult.p.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: QUADRATIC CURVE to (%2.2f, %2.2f)..(%2.2f, %2.2f)", [SVGKPointsAndPathsParser class], curveResult.c1.x, curveResult.c1.y, curveResult.p.x, curveResult.p.y);
- #endif
-
- return curveResult;
- }
- /**
- smooth-quadratic-bezier-curveto:
- ( "T" | "t" ) wsp* smooth-quadratic-bezier-curveto-argument-sequence
- */
- + (SVGCurve) readSmoothQuadraticCurvetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin withPrevCurve:(SVGCurve)prevCurve
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: smooth-quadratic-bezier-curve-to command");
- #endif
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Tt"];
-
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd] )
- {
- NSAssert( FALSE, @"failed to scan smooth quadratic curve to command");
- return prevCurve;
- }
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readSmoothQuadraticCurvetoArgumentSequence:scanner path:path relativeTo:origin withPrevCurve:prevCurve];
- }
- /**
- smooth-quadratic-bezier-curveto-argument-sequence:
- smooth-quadratic-bezier-curveto-argument
- | smooth-quadratic-bezier-curveto-argument comma-wsp? smooth-quadratic-bezier-curveto-argument-sequence
- */
- + (SVGCurve) readSmoothQuadraticCurvetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin withPrevCurve:(SVGCurve)prevCurve
- {
- SVGCurve curve = [SVGKPointsAndPathsParser readSmoothQuadraticCurvetoArgument:scanner path:path relativeTo:origin withPrevCurve:prevCurve];
-
- if (![scanner isAtEnd]) {
- curve = [SVGKPointsAndPathsParser readSmoothQuadraticCurvetoArgumentSequence:scanner path:path relativeTo:curve.p withPrevCurve:curve];
- }
-
- return curve;
- }
- /**
- smooth-quadratic-bezier-curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair
- */
- + (SVGCurve) readSmoothQuadraticCurvetoArgument:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin withPrevCurve:(SVGCurve)prevCurve
- {
- SVGCurve thisCurve;
- thisCurve.type = SVGCurveTypeQuadratic;
-
- thisCurve.c2 = (prevCurve.type == thisCurve.type) ? SVGCurveReflectedControlPoint(prevCurve) : prevCurve.p;
-
- thisCurve.c1 = thisCurve.c2; // this coordinate is never used, but c2 is better/safer than CGPointZero
-
- thisCurve.p = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- thisCurve.p.x += origin.x;
- thisCurve.p.y += origin.y;
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- CGPathAddQuadCurveToPoint(path, NULL, thisCurve.c2.x, thisCurve.c2.y, thisCurve.p.x, thisCurve.p.y );
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: SMOOTH QUADRATIC CURVE to (%2.2f, %2.2f)..(%2.2f, %2.2f)", [SVGKPointsAndPathsParser class], thisCurve.c1.x, thisCurve.c1.y, thisCurve.p.x, thisCurve.p.y );
- #endif
-
- return thisCurve;
- }
- /**
- curveto:
- ( "C" | "c" ) wsp* curveto-argument-sequence
- */
- + (SVGCurve) readCurvetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: curve-to command");
- #endif
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Cc"];
-
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd])
- {
- NSAssert( FALSE, @"failed to scan curve to command");
- return SVGCurveMakePoint(origin);
- }
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readCurvetoArgumentSequence:scanner path:path relativeTo:origin isRelative:isRelative];
- }
- /**
- curveto-argument-sequence:
- curveto-argument
- | curveto-argument comma-wsp? curveto-argument-sequence
- */
- + (SVGCurve) readCurvetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- SVGCurve curve = [SVGKPointsAndPathsParser readCurvetoArgument:scanner path:path relativeTo:origin];
-
- while( ![scanner isAtEnd])
- {
- CGPoint newOrigin = isRelative ? curve.p : origin;
-
- curve = [SVGKPointsAndPathsParser readCurvetoArgument:scanner path:path relativeTo:newOrigin];
- }
-
- return curve;
- }
- /**
- curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair comma-wsp? coordinate-pair
- */
- + (SVGCurve) readCurvetoArgument:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- SVGCurve curveResult;
- curveResult.type = SVGCurveTypeCubic;
-
- curveResult.c1 = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- curveResult.c1.x += origin.x; // avoid allocating a new struct, an allocation here could happen MILLIONS of times in a large parse!
- curveResult.c1.y += origin.y;
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- curveResult.c2 = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- curveResult.c2.x += origin.x; // avoid allocating a new struct, an allocation here could happen MILLIONS of times in a large parse!
- curveResult.c2.y += origin.y;
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- curveResult.p = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- curveResult.p.x += origin.x; // avoid allocating a new struct, an allocation here could happen MILLIONS of times in a large parse!
- curveResult.p.y += origin.y;
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- CGPathAddCurveToPoint(path, NULL, curveResult.c1.x, curveResult.c1.y, curveResult.c2.x, curveResult.c2.y, curveResult.p.x, curveResult.p.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: CURVE to (%2.2f, %2.2f)..(%2.2f, %2.2f)..(%2.2f, %2.2f)", [SVGKPointsAndPathsParser class], curveResult.c1.x, curveResult.c1.y, curveResult.c2.x, curveResult.c2.y, curveResult.p.x, curveResult.p.y);
- #endif
-
- return curveResult;
- }
- /**
- smooth-curveto:
- ( "S" | "s" ) wsp* smooth-curveto-argument-sequence
- */
- + (SVGCurve) readSmoothCurvetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin withPrevCurve:(SVGCurve)prevCurve isRelative:(BOOL) isRelative
- {
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Ss"];
- BOOL ok = [scanner scanCharactersFromSet:cmdFormat intoString:&cmd];
-
- NSAssert(ok, @"failed to scan smooth curve to command");
- if (!ok) return prevCurve;
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readSmoothCurvetoArgumentSequence:scanner path:path relativeTo:origin withPrevCurve:prevCurve isRelative:isRelative];
- }
- /**
- smooth-curveto-argument-sequence:
- smooth-curveto-argument
- | smooth-curveto-argument comma-wsp? smooth-curveto-argument-sequence
- */
- + (SVGCurve) readSmoothCurvetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin withPrevCurve:(SVGCurve)prevCurve isRelative:(BOOL) isRelative
- {
- SVGCurve curve = [SVGKPointsAndPathsParser readSmoothCurvetoArgument:scanner path:path relativeTo:origin withPrevCurve:prevCurve];
-
- if (![scanner isAtEnd]) {
- CGPoint newOrigin = isRelative ? curve.p : origin;
- curve = [SVGKPointsAndPathsParser readSmoothCurvetoArgumentSequence:scanner path:path relativeTo:newOrigin withPrevCurve:curve isRelative: isRelative];
- }
-
- return curve;
- }
- /**
- smooth-curveto-argument:
- coordinate-pair comma-wsp? coordinate-pair
- */
- + (SVGCurve) readSmoothCurvetoArgument:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin withPrevCurve:(SVGCurve)prevCurve
- {
- SVGCurve thisCurve;
- thisCurve.type = SVGCurveTypeCubic;
-
- thisCurve.c1 = (prevCurve.type == thisCurve.type) ? SVGCurveReflectedControlPoint(prevCurve) : prevCurve.p;
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
- thisCurve.c2 = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- thisCurve.c2.x += origin.x;
- thisCurve.c2.y += origin.y;
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
- thisCurve.p = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- thisCurve.p.x += origin.x;
- thisCurve.p.y += origin.y;
-
- CGPathAddCurveToPoint(path, NULL, thisCurve.c1.x, thisCurve.c1.y, thisCurve.c2.x, thisCurve.c2.y, thisCurve.p.x, thisCurve.p.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: SMOOTH CURVE to (%2.2f, %2.2f)..(%2.2f, %2.2f)..(%2.2f, %2.2f)", [SVGKPointsAndPathsParser class], thisCurve.c1.x, thisCurve.c1.y, thisCurve.c2.x, thisCurve.c2.y, thisCurve.p.x, thisCurve.p.y );
- #endif
-
- return thisCurve;
- }
- /**
- vertical-lineto-argument-sequence:
- coordinate
- | coordinate comma-wsp? vertical-lineto-argument-sequence
- */
- + (SVGCurve) readVerticalLinetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- // FIXME: reduce the allocations here; make one CGPoint and update it, not multiple
- CGFloat yValue;
- [SVGKPointsAndPathsParser readCoordinate:scanner intoFloat:&yValue];
- CGPoint vertCoord = CGPointMake(origin.x, origin.y+yValue);
- CGPoint currentPoint = CGPathGetCurrentPoint(path);
- CGPoint coord = CGPointMake(currentPoint.x, currentPoint.y+(vertCoord.y-currentPoint.y));
- CGPathAddLineToPoint(path, NULL, coord.x, coord.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: VERTICAL LINE to (%2.2f, %2.2f)", [SVGKPointsAndPathsParser class], coord.x, coord.y );
- #endif
- return SVGCurveMakePoint(coord);
- }
- /**
- vertical-lineto:
- ( "V" | "v" ) wsp* vertical-lineto-argument-sequence
- */
- + (SVGCurve) readVerticalLinetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: vertical-line-to command");
- #endif
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Vv"];
- BOOL ok = [scanner scanCharactersFromSet:cmdFormat intoString:&cmd];
-
- NSAssert(ok, @"failed to scan vertical line to command");
- if (!ok) return SVGCurveMakePoint(origin);
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readVerticalLinetoArgumentSequence:scanner path:path relativeTo:origin];
- }
- /**
- horizontal-lineto-argument-sequence:
- coordinate
- | coordinate comma-wsp? horizontal-lineto-argument-sequence
- */
- + (SVGCurve) readHorizontalLinetoArgumentSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- // FIXME: reduce the allocations here; make one CGPoint and update it, not multiple
-
- CGFloat xValue;
- [SVGKPointsAndPathsParser readCoordinate:scanner intoFloat:&xValue];
- CGPoint horizCoord = CGPointMake(origin.x+xValue, origin.y);
- CGPoint currentPoint = CGPathGetCurrentPoint(path);
- CGPoint coord = CGPointMake(currentPoint.x+(horizCoord.x-currentPoint.x), currentPoint.y);
- CGPathAddLineToPoint(path, NULL, coord.x, coord.y);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: HORIZONTAL LINE to (%2.2f, %2.2f)", [SVGKPointsAndPathsParser class], coord.x, coord.y );
- #endif
- return SVGCurveMakePoint(coord);
- }
- /**
- horizontal-lineto:
- ( "H" | "h" ) wsp* horizontal-lineto-argument-sequence
- */
- + (SVGCurve) readHorizontalLinetoCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: horizontal-line-to command");
- #endif
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Hh"];
-
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd] )
- {
- NSAssert( FALSE, @"failed to scan horizontal line to command");
- return SVGCurveMakePoint(origin);
- }
-
- [SVGKPointsAndPathsParser readWhitespace:scanner];
-
- return [SVGKPointsAndPathsParser readHorizontalLinetoArgumentSequence:scanner path:path relativeTo:origin];
- }
- + (SVGCurve) readCloseCommand:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- #if VERBOSE_PARSE_SVG_COMMAND_STRINGS
- SVGKitLogVerbose(@"Parsing command string: close command");
- #endif
- NSString* cmd = nil;
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Zz"];
-
- if( ! [scanner scanCharactersFromSet:cmdFormat intoString:&cmd] )
- {
- NSAssert( FALSE, @"failed to scan close command");
- return SVGCurveMakePoint(origin);
- }
-
- CGPathCloseSubpath(path);
- #if DEBUG_PATH_CREATION
- SVGKitLogWarn(@"[%@] PATH: finished path", [SVGKPointsAndPathsParser class] );
- #endif
- return SVGCurveMakePoint(CGPathGetCurrentPoint(path));
- }
- + (SVGCurve) readEllipticalArcArguments:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin isRelative:(BOOL) isRelative
- {
- NSCharacterSet* cmdFormat = [NSCharacterSet characterSetWithCharactersInString:@"Aa"];
- BOOL ok = [scanner scanCharactersFromSet:cmdFormat intoString:nil];
-
- NSAssert(ok, @"failed to scan arc to command");
- if (!ok) return SVGCurveMakePoint(origin);
-
- CGPoint endPoint = [SVGKPointsAndPathsParser readEllipticalArcArgumentsSequence:scanner path:path relativeTo:origin];
-
- while (![scanner isAtEnd]) {
- CGPoint newOrigin = isRelative ? endPoint : origin;
- endPoint = [SVGKPointsAndPathsParser readEllipticalArcArgumentsSequence:scanner path:path relativeTo:newOrigin];
- }
-
- return SVGCurveMakePoint(endPoint);
- }
- + (CGPoint)readEllipticalArcArgumentsSequence:(NSScanner*)scanner path:(CGMutablePathRef)path relativeTo:(CGPoint)origin
- {
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
- // need to find the center point of the ellipse from the two points and an angle
- // see http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes for these calculations
-
- CGPoint currentPt = CGPathGetCurrentPoint(path);
-
- CGFloat x1 = currentPt.x;
- CGFloat y1 = currentPt.y;
-
- CGPoint radii = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- CGFloat rx = fabs(radii.x);
- CGFloat ry = fabs(radii.y);
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- CGFloat phi;
-
- [SVGKPointsAndPathsParser readCoordinate:scanner intoFloat:&phi];
-
- phi *= M_PI/180.;
-
- phi = fmod(phi, 2 * M_PI);
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- CGPoint flags = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
-
- BOOL largeArcFlag = flags.x != 0.;
- BOOL sweepFlag = flags.y != 0.;
-
- [SVGKPointsAndPathsParser readCommaAndWhitespace:scanner];
-
- CGPoint endPoint = [SVGKPointsAndPathsParser readCoordinatePair:scanner];
- // end parsing
- CGFloat x2 = origin.x + endPoint.x;
- CGFloat y2 = origin.y + endPoint.y;
- if (rx == 0 || ry == 0)
- {
- CGPathAddLineToPoint(path, NULL, x2, y2);
- return CGPointMake(x2, y2);
- }
- CGFloat cosPhi = cos(phi);
- CGFloat sinPhi = sin(phi);
-
- CGFloat x1p = cosPhi * (x1-x2)/2. + sinPhi * (y1-y2)/2.;
- CGFloat y1p = -sinPhi * (x1-x2)/2. + cosPhi * (y1-y2)/2.;
-
- CGFloat lhs;
- {
- CGFloat rx_2 = rx * rx;
- CGFloat ry_2 = ry * ry;
- CGFloat xp_2 = x1p * x1p;
- CGFloat yp_2 = y1p * y1p;
- CGFloat delta = xp_2/rx_2 + yp_2/ry_2;
-
- if (delta > 1.0)
- {
- rx *= sqrt(delta);
- ry *= sqrt(delta);
- rx_2 = rx * rx;
- ry_2 = ry * ry;
- }
- CGFloat sign = (largeArcFlag == sweepFlag) ? -1 : 1;
- CGFloat numerator = rx_2 * ry_2 - rx_2 * yp_2 - ry_2 * xp_2;
- CGFloat denom = rx_2 * yp_2 + ry_2 * xp_2;
-
- numerator = MAX(0, numerator);
-
- if (denom == 0) {
- lhs = 0;
- }else {
- lhs = sign * sqrt(numerator/denom);
- }
- }
-
- CGFloat cxp = lhs * (rx*y1p)/ry;
- CGFloat cyp = lhs * -((ry * x1p)/rx);
-
- CGFloat cx = cosPhi * cxp + -sinPhi * cyp + (x1+x2)/2.;
- CGFloat cy = cxp * sinPhi + cyp * cosPhi + (y1+y2)/2.;
-
- // transform our ellipse into the unit circle
- CGAffineTransform tr = CGAffineTransformMakeScale(1./rx, 1./ry);
- tr = CGAffineTransformRotate(tr, -phi);
- tr = CGAffineTransformTranslate(tr, -cx, -cy);
-
- CGPoint arcPt1 = CGPointApplyAffineTransform(CGPointMake(x1, y1), tr);
- CGPoint arcPt2 = CGPointApplyAffineTransform(CGPointMake(x2, y2), tr);
-
- CGFloat startAngle = atan2(arcPt1.y, arcPt1.x);
- CGFloat endAngle = atan2(arcPt2.y, arcPt2.x);
-
- CGFloat angleDelta = endAngle - startAngle;;
-
- if (sweepFlag)
- {
- if (angleDelta < 0)
- angleDelta += 2. * M_PI;
- }
- else
- {
- if (angleDelta > 0)
- angleDelta = angleDelta - 2 * M_PI;
- }
- // construct the inverse transform
- CGAffineTransform trInv = CGAffineTransformMakeTranslation( cx, cy);
-
- trInv = CGAffineTransformRotate(trInv, phi);
- trInv = CGAffineTransformScale(trInv, rx, ry);
- // add a inversely transformed circular arc to the current path
- CGPathAddRelativeArc( path, &trInv, 0, 0, 1., startAngle, angleDelta);
-
- return CGPointMake(x2, y2);
- }
- @end
|