"files": [ { "sha": "34b734e6cd7bca0b9afe63be111c64cf4e0bf752", "filename": "Source/DOM classes/Core DOM/CSSPrimitiveValue.m", "status": "modified", "additions": 2, "deletions": 1, "changes": 3, "blob_url": "https://github.com/SVGKit/SVGKit/blob/97dfe7826516695d2a5d88c8d170e365903de352/Source%2FDOM%20classes%2FCore%20DOM%2FCSSPrimitiveValue.m", "raw_url": "https://github.com/SVGKit/SVGKit/raw/97dfe7826516695d2a5d88c8d170e365903de352/Source%2FDOM%20classes%2FCore%20DOM%2FCSSPrimitiveValue.m", "contents_url": "https://api.github.com/repos/SVGKit/SVGKit/contents/Source%2FDOM%20classes%2FCore%20DOM%2FCSSPrimitiveValue.m?ref=97dfe7826516695d2a5d88c8d170e365903de352", "patch": "@@ -271,13 +271,14 @@ -(void)setCssText:(NSString *)newCssText\n \t\t 1. It's a pure number, no units (in CSS, that's rare - but in SVG it's common, and defined by Spec to be \"the same as PX\")\n \t\t 2. It's a string, one of many different CSS string types\n \t\t 3. It's a corrupt file\n+ 4. It's a string, the x or y value of text is multi-valued. For example, x= '30 60 90 120'.\n \t\t */\n \t\t\n \t\t/**\n \t\t NSScaner is an Apple class that SPECIFICALLY will refuse to return a number if there are any non-numberic characters in the string */\n \t\tNSScanner *scanner = [NSScanner scannerWithString: _cssText];\n \t\tfloat floatToHoldTheOutput;\n-\t\tif( [scanner scanFloat:&floatToHoldTheOutput])\n+\t\tif( [scanner scanFloat:&floatToHoldTheOutput] && ![_cssText containsString:@\" \"])\n \t\t{\n \t\t\t/* Option 1: it's a pure number */\n \t\t\t[self setFloatValue:CSS_NUMBER floatValue:floatToHoldTheOutput];" }, { "sha": "690e6129aab1906ee33372f1bf8b6a7b1f418a1d", "filename": "Source/DOM classes/SVG-DOM/SVGLength.m", "status": "modified", "additions": 5, "deletions": 0, "changes": 5, "blob_url": "https://github.com/SVGKit/SVGKit/blob/97dfe7826516695d2a5d88c8d170e365903de352/Source%2FDOM%20classes%2FSVG-DOM%2FSVGLength.m", "raw_url": "https://github.com/SVGKit/SVGKit/raw/97dfe7826516695d2a5d88c8d170e365903de352/Source%2FDOM%20classes%2FSVG-DOM%2FSVGLength.m", "contents_url": "https://api.github.com/repos/SVGKit/SVGKit/contents/Source%2FDOM%20classes%2FSVG-DOM%2FSVGLength.m?ref=97dfe7826516695d2a5d88c8d170e365903de352", "patch": "@@ -42,6 +42,11 @@ -(float)value\n \treturn [self.internalCSSPrimitiveValue getFloatValue:self.internalCSSPrimitiveValue.primitiveType];\n }\n \n+-(NSString *)valueAsString\n+{\n+ return [self.internalCSSPrimitiveValue getStringValue];\n+}\n+\n -(SVG_LENGTH_TYPE)unitType\n {\n \tswitch( self.internalCSSPrimitiveValue.primitiveType )" }, { "sha": "6e02d634cb041a92d5a7ef012626db767b663324", "filename": "Source/DOM classes/Unported or Partial DOM/SVGTextElement.m", "status": "modified", "additions": 163, "deletions": 88, "changes": 251, "blob_url": "https://github.com/SVGKit/SVGKit/blob/97dfe7826516695d2a5d88c8d170e365903de352/Source%2FDOM%20classes%2FUnported%20or%20Partial%20DOM%2FSVGTextElement.m", "raw_url": "https://github.com/SVGKit/SVGKit/raw/97dfe7826516695d2a5d88c8d170e365903de352/Source%2FDOM%20classes%2FUnported%20or%20Partial%20DOM%2FSVGTextElement.m", "contents_url": "https://api.github.com/repos/SVGKit/SVGKit/contents/Source%2FDOM%20classes%2FUnported%20or%20Partial%20DOM%2FSVGTextElement.m?ref=97dfe7826516695d2a5d88c8d170e365903de352", "patch": "@@ -22,44 +22,120 @@ - (CALayer *) newLayer\n \t Because: Apple's classes REQUIRE us to provide a lot of this info up-front. Sigh\n \t And: SVGKit works by pre-baking everything into position (its faster, and avoids Apple's broken CALayer.transform property)\n \t */\n-\tCGAffineTransform textTransformAbsolute = [SVGHelperUtilities transformAbsoluteIncludingViewportForTransformableOrViewportEstablishingElement:self];\n-\t/** add on the local x,y that will NOT BE iNCLUDED IN THE TRANSFORM\n-\t AUTOMATICALLY BECAUSE THEY ARE NOT TRANSFORM COMMANDS IN SVG SPEC!!\n-\t -- but they ARE part of the \"implicit transform\" of text elements!! (bad SVG Spec design :( )\n-\t \n-\t NB: the local bits (x/y offset) have to be pre-transformed by\n-\t */\n+ if (self.x.valueAsString.length != 0 || self.y.valueAsString.length != 0)\n+ {\n+ // Simplify checks for x and y values being empty or not.\n+ NSString *xValue = [self stringValueOrFormatted:self.x.valueAsString withValue:self.x.value];\n+ NSString *yValue = [self stringValueOrFormatted:self.y.valueAsString withValue:self.y.value];\n+ \n+ // Split the text based on the provided X and Y values.\n+ NSArray *splitItem = [self splitText:self.textContent basedOnX:xValue andY:yValue];\n+ // If no split is needed, create a layer using the entire text content.\n+ if (splitItem == nil) {\n+ return [self newSubLayerWithX:self.x y:self.y textContent:self.textContent];\n+ }\n+ // If splitting is needed, create a parent layer and add sublayers for each text segment.\n+ CALayer *layer = [CALayer new];\n+ for (NSDictionary *item in splitItem)\n+ {\n+ CALayer *subLayer = [self newSubLayerWithX:[SVGLength svgLengthFromNSString:item[@\"x\"]]\n+ y:[SVGLength svgLengthFromNSString:item[@\"y\"]]\n+ textContent:item[@\"text\"]];\n+ [layer addSublayer:subLayer];\n+ }\n+ return layer;\n+ }\n+ \n+ // If no position is specified, create a layer using the current X and Y values and the entire text content.\n+ return [self newSubLayerWithX:self.x y:self.y textContent:self.textContent];\n+}\n+\n+/// Helper method to return the string value or its formatted version if empty.\n+- (NSString *)stringValueOrFormatted:(NSString *)valueString withValue:(CGFloat)value\n+{\n+ return valueString.length > 0 ? valueString : [NSString stringWithFormat:@\"%f\", value];\n+}\n+\n+/// Method to split the text into segments based on X and Y position strings.\n+/// This allows for text to be distributed across different positions if specified.\n+- (NSArray *)splitText:(NSString *)text basedOnX:(NSString *)xString andY:(NSString *)yString\n+{\n+ // Separate the X and Y position strings into arrays of individual positions.\n+ NSArray *xValues = [xString componentsSeparatedByString:@\" \"];\n+ NSArray *yValues = [yString componentsSeparatedByString:@\" \"];\n+ // Determine the number of splits based on the greater count of X or Y positions.\n+ NSInteger splitCount = MAX(xValues.count, yValues.count);\n+ if (splitCount == 0) {\n+ return nil; // If no splitting is needed, return nil.\n+ }\n+\n+ // Prepare an array to hold the results of the split.\n+ NSMutableArray *result = [NSMutableArray array];\n+\n+ NSString *previousNonNullableX = @\"\";\n+ NSString *previousNonNullableY = @\"\";\n+ for (NSInteger i = 0; i < splitCount; i++) {\n+ // Use the current or previous non-empty X and Y positions.\n+ NSString *xPart = i < xValues.count ? xValues[i] : previousNonNullableX;\n+ previousNonNullableX = xPart;\n+ NSString *yPart = i < yValues.count ? yValues[i] : previousNonNullableY;\n+ previousNonNullableY = yPart;\n+ \n+ // Split the text into parts according to the current index and remaining length.\n+ NSString *textPart = @\"\";\n+ if (i < text.length) {\n+ NSRange range = NSMakeRange(i, i == (splitCount - 1) ? (text.length - i) : 1);\n+ textPart = [text substringWithRange:range];\n+ }\n+\n+ // Create a dictionary for each text segment with its corresponding X and Y positions.\n+ NSDictionary *dict = @{@\"x\": xPart, @\"y\": yPart, @\"text\": textPart};\n+ [result addObject:dict];\n+ }\n+\n+ return result; // Return the array of dictionaries containing split text and positions.\n+}\n+\n+- (CALayer *) newSubLayerWithX:(SVGLength *)x y:(SVGLength *)y textContent:(NSString *)textContent\n+{\n+ CGAffineTransform textTransformAbsolute = [SVGHelperUtilities transformAbsoluteIncludingViewportForTransformableOrViewportEstablishingElement:self];\n+ /** add on the local x,y that will NOT BE iNCLUDED IN THE TRANSFORM\n+ AUTOMATICALLY BECAUSE THEY ARE NOT TRANSFORM COMMANDS IN SVG SPEC!!\n+ -- but they ARE part of the \"implicit transform\" of text elements!! (bad SVG Spec design :( )\n+ \n+ NB: the local bits (x/y offset) have to be pre-transformed by\n+ */\n CGRect viewport = CGRectFromSVGRect(self.rootOfCurrentDocumentFragment.viewBox);\n-\tCGAffineTransform textTransformAbsoluteWithLocalPositionOffset = CGAffineTransformConcat( CGAffineTransformMakeTranslation( [self.x pixelsValueWithDimension:viewport.size.width], [self.y pixelsValueWithDimension:viewport.size.height]), textTransformAbsolute);\n-\t\n-\t/**\n-\t Apple's CATextLayer is poor - one of those classes Apple hasn't finished writing?\n-\t \n-\t It's incompatible with UIFont (Apple states it is so), and it DOES NOT WORK by default:\n-\t \n-\t If you assign a font, and a font size, and text ... you get a blank empty layer of\n-\t size 0,0\n-\t \n-\t Because Apple requires you to ALSO do all the work of calculating the font size, shape,\n-\t position etc.\n-\t \n-\t But its the easiest way to get FULL control over size/position/rotation/etc in a CALayer\n-\t */\n+ CGAffineTransform textTransformAbsoluteWithLocalPositionOffset = CGAffineTransformConcat( CGAffineTransformMakeTranslation( [x pixelsValueWithDimension:viewport.size.width], [y pixelsValueWithDimension:viewport.size.height]), textTransformAbsolute);\n+ \n+ /**\n+ Apple's CATextLayer is poor - one of those classes Apple hasn't finished writing?\n+ \n+ It's incompatible with UIFont (Apple states it is so), and it DOES NOT WORK by default:\n+ \n+ If you assign a font, and a font size, and text ... you get a blank empty layer of\n+ size 0,0\n+ \n+ Because Apple requires you to ALSO do all the work of calculating the font size, shape,\n+ position etc.\n+ \n+ But its the easiest way to get FULL control over size/position/rotation/etc in a CALayer\n+ */\n \n /**\n Create font based on many information (font-family, font-weight, etc), fallback to system font when there are no available font matching the information.\n */\n UIFont *font = [SVGTextElement matchedFontWithElement:self];\n-\t\n-\t/** Convert the size down using the SVG transform at this point, before we calc the frame size etc */\n-//\teffectiveFontSize = CGSizeApplyAffineTransform( CGSizeMake(0,effectiveFontSize), textTransformAbsolute ).height; // NB important that we apply a transform to a \"CGSize\" here, so that Apple's library handles worrying about whether to ignore skew transforms etc\n+ \n+ /** Convert the size down using the SVG transform at this point, before we calc the frame size etc */\n+// effectiveFontSize = CGSizeApplyAffineTransform( CGSizeMake(0,effectiveFontSize), textTransformAbsolute ).height; // NB important that we apply a transform to a \"CGSize\" here, so that Apple's library handles worrying about whether to ignore skew transforms etc\n \n-\t/** Convert all whitespace to spaces, and trim leading/trailing (SVG doesn't support leading/trailing whitespace, and doesnt support CR LF etc) */\n-\t\n-\tNSString* effectiveText = self.textContent; // FIXME: this is a TEMPORARY HACK, UNTIL PROPER PARSING OF ELEMENTS IS ADDED\n-\t\n-\teffectiveText = [effectiveText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];\n-\teffectiveText = [effectiveText stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\" \"];\n+ /** Convert all whitespace to spaces, and trim leading/trailing (SVG doesn't support leading/trailing whitespace, and doesnt support CR LF etc) */\n+ \n+ NSString* effectiveText = textContent;\n+ \n+ effectiveText = [effectiveText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];\n+ effectiveText = [effectiveText stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\" \"];\n \n /**\n Stroke color && stroke width\n@@ -80,17 +156,17 @@ Create font based on many information (font-family, font-weight, etc), fallback\n Apple's CATextLayer can be filled using mask.\n */\n CGColorRef fillColor = [SVGHelperUtilities parseFillForElement:self];\n-\t\n-\t/** Calculate \n-\t \n-\t 1. Create an attributed string (Apple's APIs are hard-coded to require this)\n-\t 2. Set the font to be the correct one + correct size for whole string, inside the string\n-\t 3. Ask apple how big the final thing should be\n-\t 4. Use that to provide a layer.frame\n-\t */\n-\tNSMutableAttributedString* attributedString = [[NSMutableAttributedString alloc] initWithString:effectiveText];\n+ \n+ /** Calculate\n+ \n+ 1. Create an attributed string (Apple's APIs are hard-coded to require this)\n+ 2. Set the font to be the correct one + correct size for whole string, inside the string\n+ 3. Ask apple how big the final thing should be\n+ 4. Use that to provide a layer.frame\n+ */\n+ NSMutableAttributedString* attributedString = [[NSMutableAttributedString alloc] initWithString:effectiveText];\n NSRange stringRange = NSMakeRange(0, attributedString.string.length);\n-\t[attributedString addAttribute:NSFontAttributeName\n+ [attributedString addAttribute:NSFontAttributeName\n value:font\n range:stringRange];\n if (fillColor) {\n@@ -112,55 +188,55 @@ Create font based on many information (font-family, font-weight, etc), fallback\n value:@(strokeValue)\n range:stringRange];\n }\n-\tCTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFMutableAttributedStringRef) attributedString );\n+ CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFMutableAttributedStringRef) attributedString );\n CGSize suggestedUntransformedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), NULL);\n CFRelease(framesetter);\n-\t\n-\tCGRect unTransformedFinalBounds = CGRectMake( 0,\n-\t\t\t\t\t\t\t\t\t\t\t 0,\n-\t\t\t\t\t\t\t\t\t\t\t suggestedUntransformedSize.width,\n-\t\t\t\t\t\t\t\t\t\t\t suggestedUntransformedSize.height); // everything's been pre-scaled by [self transformAbsolute]\n-\t\n+ \n+ CGRect unTransformedFinalBounds = CGRectMake( 0,\n+ 0,\n+ suggestedUntransformedSize.width,\n+ suggestedUntransformedSize.height); // everything's been pre-scaled by [self transformAbsolute]\n+ \n CATextLayer *label = [SVGTextLayer layer];\n [SVGHelperUtilities configureCALayer:label usingElement:self];\n-\t\n-\t/** This is complicated for three reasons.\n-\t Partly: Apple and SVG use different defitions for the \"origin\" of a piece of text\n-\t Partly: Bugs in Apple's CoreText\n-\t Partly: flaws in Apple's CALayer's handling of frame,bounds,position,anchorPoint,affineTransform\n-\t \n-\t 1. CALayer.frame DOES NOT EXIST AS A REAL PROPERTY - if you read Apple's docs you eventually realise it is fake. Apple explicitly says it is \"not defined\". They should DELETE IT from their API!\n-\t 2. CALayer.bounds and .position ARE NOT AFFECTED BY .affineTransform - only the contents of the layer is affected\n-\t 3. SVG defines two SEMI-INCOMPATIBLE ways of positioning TEXT objects, that we have to correctly combine here.\n-\t 4. So ... to apply a transform to the layer text:\n-\t i. find the TRANSFORM\n-\t ii. merge it with the local offset (.x and .y from SVG) - which defaults to (0,0)\n-\t iii. apply that to the layer\n-\t iv. set the position to 0\n-\t v. BECAUSE SVG AND APPLE DEFINE ORIGIN DIFFERENTLY: subtract the \"untransformed\" height of the font ... BUT: pre-transformed ONLY BY the 'multiplying (non-translating)' part of the TRANSFORM.\n-\t vi. set the bounds to be (whatever Apple's CoreText says is necessary to render TEXT at FONT SIZE, with NO TRANSFORMS)\n-\t */\n+ \n+ /** This is complicated for three reasons.\n+ Partly: Apple and SVG use different defitions for the \"origin\" of a piece of text\n+ Partly: Bugs in Apple's CoreText\n+ Partly: flaws in Apple's CALayer's handling of frame,bounds,position,anchorPoint,affineTransform\n+ \n+ 1. CALayer.frame DOES NOT EXIST AS A REAL PROPERTY - if you read Apple's docs you eventually realise it is fake. Apple explicitly says it is \"not defined\". They should DELETE IT from their API!\n+ 2. CALayer.bounds and .position ARE NOT AFFECTED BY .affineTransform - only the contents of the layer is affected\n+ 3. SVG defines two SEMI-INCOMPATIBLE ways of positioning TEXT objects, that we have to correctly combine here.\n+ 4. So ... to apply a transform to the layer text:\n+ i. find the TRANSFORM\n+ ii. merge it with the local offset (.x and .y from SVG) - which defaults to (0,0)\n+ iii. apply that to the layer\n+ iv. set the position to 0\n+ v. BECAUSE SVG AND APPLE DEFINE ORIGIN DIFFERENTLY: subtract the \"untransformed\" height of the font ... BUT: pre-transformed ONLY BY the 'multiplying (non-translating)' part of the TRANSFORM.\n+ vi. set the bounds to be (whatever Apple's CoreText says is necessary to render TEXT at FONT SIZE, with NO TRANSFORMS)\n+ */\n label.bounds = unTransformedFinalBounds;\n-\t\n-\t/** NB: specific to Apple: the \"origin\" is the TOP LEFT corner of first line of text, whereas SVG uses the font's internal origin\n-\t (which is BOTTOM LEFT CORNER OF A LETTER SUCH AS 'a' OR 'x' THAT SITS ON THE BASELINE ... so we have to make the FRAME start \"font leading\" higher up\n-\t \n-\t WARNING: Apple's font-rendering system has some nasty bugs (c.f. StackOverflow)\n-\t \n-\t We TRIED to use the font's built-in numbers to correct the position, but Apple's own methods often report incorrect values,\n-\t and/or Apple has deprecated REQUIRED methods in their API (with no explanation - e.g. \"font leading\")\n-\t \n-\t If/when Apple fixes their bugs - or if you know enough about their API's to workaround the bugs, feel free to fix this code.\n-\t */\n+ \n+ /** NB: specific to Apple: the \"origin\" is the TOP LEFT corner of first line of text, whereas SVG uses the font's internal origin\n+ (which is BOTTOM LEFT CORNER OF A LETTER SUCH AS 'a' OR 'x' THAT SITS ON THE BASELINE ... so we have to make the FRAME start \"font leading\" higher up\n+ \n+ WARNING: Apple's font-rendering system has some nasty bugs (c.f. StackOverflow)\n+ \n+ We TRIED to use the font's built-in numbers to correct the position, but Apple's own methods often report incorrect values,\n+ and/or Apple has deprecated REQUIRED methods in their API (with no explanation - e.g. \"font leading\")\n+ \n+ If/when Apple fixes their bugs - or if you know enough about their API's to workaround the bugs, feel free to fix this code.\n+ */\n CTLineRef line = CTLineCreateWithAttributedString( (CFMutableAttributedStringRef) attributedString );\n CGFloat ascent = 0;\n CTLineGetTypographicBounds(line, &ascent, NULL, NULL);\n CFRelease(line);\n-\tCGFloat offsetToConvertSVGOriginToAppleOrigin = -ascent;\n-\tCGSize fakeSizeToApplyNonTranslatingPartsOfTransform = CGSizeMake( 0, offsetToConvertSVGOriginToAppleOrigin);\n-\t\n-\tlabel.position = CGPointMake( 0,\n-\t\t\t\t\t\t\t\t 0 + CGSizeApplyAffineTransform( fakeSizeToApplyNonTranslatingPartsOfTransform, textTransformAbsoluteWithLocalPositionOffset).height);\n+ CGFloat offsetToConvertSVGOriginToAppleOrigin = -ascent;\n+ CGSize fakeSizeToApplyNonTranslatingPartsOfTransform = CGSizeMake( 0, offsetToConvertSVGOriginToAppleOrigin);\n+ \n+ label.position = CGPointMake( 0,\n+ 0 + CGSizeApplyAffineTransform( fakeSizeToApplyNonTranslatingPartsOfTransform, textTransformAbsoluteWithLocalPositionOffset).height);\n \n NSString *textAnchor = [self cascadedValueForStylableProperty:@\"text-anchor\"];\n if( [@\"middle\" isEqualToString:textAnchor] )\n@@ -170,7 +246,7 @@ Create font based on many information (font-family, font-weight, etc), fallback\n else\n label.anchorPoint = CGPointZero; // WARNING: SVG applies transforms around the top-left as origin, whereas Apple defaults to center as origin, so we tell Apple to work \"like SVG\" here.\n \n-\tlabel.affineTransform = textTransformAbsoluteWithLocalPositionOffset;\n+ label.affineTransform = textTransformAbsoluteWithLocalPositionOffset;\n label.string = [attributedString copy];\n label.alignmentMode = kCAAlignmentLeft;\n \n@@ -181,12 +257,11 @@ Create font based on many information (font-family, font-weight, etc), fallback\n #endif\n \n return [self newCALayerForTextLayer:label transformAbsolute:textTransformAbsolute];\n-\n-\t/** VERY USEFUL when trying to debug text issues:\n-\tlabel.backgroundColor = [UIColor colorWithRed:0.5 green:0 blue:0 alpha:0.5].CGColor;\n-\tlabel.borderColor = [UIColor redColor].CGColor;\n-\t//DEBUG: SVGKitLogVerbose(@\"font size %2.1f at %@ ... final frame of layer = %@\", effectiveFontSize, NSStringFromCGPoint(transformedOrigin), NSStringFromCGRect(label.frame));\n-\t*/\n+ /** VERY USEFUL when trying to debug text issues:\n+ label.backgroundColor = [UIColor colorWithRed:0.5 green:0 blue:0 alpha:0.5].CGColor;\n+ label.borderColor = [UIColor redColor].CGColor;\n+ //DEBUG: SVGKitLogVerbose(@\"font size %2.1f at %@ ... final frame of layer = %@\", effectiveFontSize, NSStringFromCGPoint(transformedOrigin), NSStringFromCGRect(label.frame));\n+ */\n }\n \n -(CALayer *) newCALayerForTextLayer:(CATextLayer *)label transformAbsolute:(CGAffineTransform)transformAbsolute" } ]