Skip to content

Commit

Permalink
FIXED: SVG <image> tags now render correctly with correct transforms,…
Browse files Browse the repository at this point in the history
… if the image data is embedded in the SVG (external images MIGHT OR MIGHT NOT work - we have no test data)
  • Loading branch information
adamgit committed Feb 16, 2013
1 parent ad35caa commit 0a00f44
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 35 deletions.
2 changes: 2 additions & 0 deletions SVGKit-iOS.xcodeproj/project.pbxproj
Expand Up @@ -261,6 +261,7 @@
66988DCE168A129500EC93C7 /* CSSValueList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSValueList.m; sourceTree = "<group>"; };
66988DD1168A13BC00EC93C7 /* CSSValue_ForSubclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSValue_ForSubclasses.h; sourceTree = "<group>"; };
66988DD9168A2F4200EC93C7 /* CSSPrimitiveValue_ConfigurablePixelsPerInch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSPrimitiveValue_ConfigurablePixelsPerInch.h; sourceTree = "<group>"; };
66A09CB016CFF494003CD5CD /* SVGFitToViewBox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGFitToViewBox.h; sourceTree = "<group>"; };
66E8630E1688C2770059C9C4 /* AppleSucksDOMImplementation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleSucksDOMImplementation.h; sourceTree = "<group>"; };
66E8630F1688C2770059C9C4 /* AppleSucksDOMImplementation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppleSucksDOMImplementation.m; sourceTree = "<group>"; };
66E863101688C2770059C9C4 /* Attr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Attr.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -609,6 +610,7 @@
663FD01216CAEF0B00CCBFB3 /* SVGHelperUtilities.h */,
663FD01316CAEF0C00CCBFB3 /* SVGHelperUtilities.m */,
663FD01716CAF05900CCBFB3 /* SVGTransformable.h */,
66A09CB016CFF494003CD5CD /* SVGFitToViewBox.h */,
661ADBF016CC2FBE006F4BC3 /* SVGTextPositioningElement.h */,
661ADBF116CC2FBE006F4BC3 /* SVGTextPositioningElement.m */,
661ADBF516CC2FCA006F4BC3 /* SVGTextContentElement.h */,
Expand Down
17 changes: 17 additions & 0 deletions Source/DOM classes/SVG-DOM/SVGFitToViewBox.h
@@ -0,0 +1,17 @@
/**
* http://www.w3.org/TR/SVG/types.html#InterfaceSVGFitToViewBox
interface SVGFitToViewBox {
readonly attribute SVGAnimatedRect viewBox;
readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
*/

#import <Foundation/Foundation.h>

#import <QuartzCore/QuartzCore.h>

@protocol SVGFitToViewBox <NSObject>

@property (nonatomic) /* SVGAnimatedRect */ CGRect viewBox;

@end
8 changes: 4 additions & 4 deletions Source/DOM classes/SVG-DOM/SVGHelperUtilities.m
Expand Up @@ -48,11 +48,11 @@ +(CGAffineTransform) transformRelativeIncludingViewportForTransformableOrViewpor
|| transformableOrSVGSVGElement.viewportElement == transformableOrSVGSVGElement // if it's some-other-object, then: we simply don't need to worry about it
)
{
SVGSVGElement* svgSVGElement = (SVGSVGElement*) transformableOrSVGSVGElement;
SVGElement<SVGFitToViewBox>* svgSVGElement = (SVGElement<SVGFitToViewBox>*) transformableOrSVGSVGElement;

/** Calculate the "implicit" viewport transform (caused by the <SVG> tag's possible "viewBox" attribute) */
CGRect frameViewBox = svgSVGElement.viewBoxFrame;
CGRect frameViewport = CGRectFromSVGRect( svgSVGElement.viewport );
CGRect frameViewBox = svgSVGElement.viewBox;
CGRect frameViewport = CGRectFromSVGRect( ((SVGSVGElement*)svgSVGElement).viewport );

if( ! CGRectIsEmpty( frameViewBox ) )
{
Expand Down Expand Up @@ -245,7 +245,7 @@ +(CALayer *) newCALayerForPathBasedSVGElement:(SVGElement<SVGTransformable>*) sv

//if( _shapeLayer != nil && svgGradient != nil ) //this nil check here is distrubing but blocking
{
CAGradientLayer *gradientLayer = [((CAGradientLayer *)svgGradient) newGradientLayerForObjectRect:_shapeLayer.frame viewportRect:svgElement.rootOfCurrentDocumentFragment.viewBoxFrame];
CAGradientLayer *gradientLayer = [((CAGradientLayer *)svgGradient) newGradientLayerForObjectRect:_shapeLayer.frame viewportRect:svgElement.rootOfCurrentDocumentFragment.viewBox];

NSLog(@"DOESNT WORK, APPLE's API APPEARS BROKEN???? - About to mask layer frame (%@) with a mask of frame (%@)", NSStringFromCGRect(gradientLayer.frame), NSStringFromCGRect(_shapeLayer.frame));
gradientLayer.mask =_shapeLayer;
Expand Down
2 changes: 1 addition & 1 deletion Source/DOM classes/Unported or Partial DOM/SVGElement.m
Expand Up @@ -66,7 +66,7 @@ + (BOOL)shouldStoreContent {
-(void) reCalculateAndSetViewportElementReferenceUsingFirstSVGAncestor:(SVGElement*) firstAncestor
{
// NB the root svg element IS a viewport, but SVG Spec defines it as NOT a viewport, and so we will overwrite this later
BOOL isTagAllowedToBeAViewport = [self.tagName isEqualToString:@"svg"] || [self.tagName isEqualToString:@"image"] || [self.tagName isEqualToString:@"foreignObject"];
BOOL isTagAllowedToBeAViewport = [self.tagName isEqualToString:@"svg"] || [self.tagName isEqualToString:@"foreignObject"]; // NB: Spec lists "image" tag too but only as an IMPLICIT CREATOR - we don't actually handle it (it creates an <SVG> tag ... that will be handled later)

BOOL isTagDefiningAViewport = [self.attributes getNamedItem:@"width"] != nil || [self.attributes getNamedItem:@"height"] != nil;

Expand Down
11 changes: 2 additions & 9 deletions Source/DOM classes/Unported or Partial DOM/SVGImageElement.h
@@ -1,19 +1,12 @@
//
// SVGImageElement.h
// SvgLoader
//
// Created by Joshua May on 24/06/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "SVGElement.h"
#import "SVGTransformable.h"
#import "SVGFitToViewBox.h"

#import "SVGElement_ForParser.h" // to resolve Xcode circular dependencies; in long term, parsing SHOULD NOT HAPPEN inside any class whose name starts "SVG" (because those are reserved classes for the SVG Spec)

@interface SVGImageElement : SVGElement <SVGTransformable, SVGLayeredElement>
@interface SVGImageElement : SVGElement <SVGTransformable, SVGLayeredElement, SVGFitToViewBox>

@property (nonatomic, readonly) CGFloat x;
@property (nonatomic, readonly) CGFloat y;
Expand Down
28 changes: 19 additions & 9 deletions Source/DOM classes/Unported or Partial DOM/SVGImageElement.m
@@ -1,11 +1,3 @@
//
// SVGKImageElement.m
// SvgLoader
//
// Created by Joshua May on 24/06/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "SVGImageElement.h"

#import "SVGHelperUtilities.h"
Expand Down Expand Up @@ -42,6 +34,7 @@ @interface SVGImageElement()
@implementation SVGImageElement

@synthesize transform; // each SVGElement subclass that conforms to protocol "SVGTransformable" has to re-synthesize this to work around bugs in Apple's Objective-C 2.0 design that don't allow @properties to be extended by categories / protocols
@synthesize viewBox; // each SVGElement subclass that conforms to protocol "SVGFitToViewBox" has to re-synthesize this to work around bugs in Apple's Objective-C 2.0 design that don't allow @properties to be extended by categories / protocols

@synthesize x = _x;
@synthesize y = _y;
Expand Down Expand Up @@ -75,10 +68,24 @@ - (void)postProcessAttributesAddingErrorsTo:(SVGKParseResult *)parseResult {
self.href = [self getAttribute:@"href"];
}


- (CALayer *) newLayer
{
NSAssert( FALSE, @"NOT SUPPORTED: SVG Image Element . newLayer -- must be upgraded using the algorithm in SVGShapeElement.newLayer");
CAShapeLayer* newLayer = [[CALayer alloc] init];
newLayer.name = self.identifier;
[newLayer setValue:self.identifier forKey:kSVGElementIdentifier];

/** transform our LOCAL path into ABSOLUTE space */
CGRect frame = CGRectMake(_x, _y, _width, _height);
frame = CGRectApplyAffineTransform(frame, [SVGHelperUtilities transformAbsoluteIncludingViewportForTransformableOrViewportEstablishingElement:self]);
newLayer.frame = frame;

NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_href]];
SVGImageRef image = [SVGImage imageWithData:imageData];

newLayer.contents = (id)SVGImageCGImage(image);

#if OLD_CODE
__block CALayer *layer = [[CALayer layer] retain];

layer.name = self.identifier;
Expand All @@ -102,6 +109,9 @@ - (CALayer *) newLayer
});

return layer;
#endif

return newLayer;
}

- (void)layoutLayer:(CALayer *)layer {
Expand Down
5 changes: 2 additions & 3 deletions Source/DOM classes/Unported or Partial DOM/SVGSVGElement.h
Expand Up @@ -47,6 +47,7 @@
*/

#import "DocumentCSS.h"
#import "SVGFitToViewBox.h"

#import "SVGElement.h"
#import "SVGViewSpec.h"
Expand All @@ -65,7 +66,7 @@

#import "SVGLayeredElement.h"

@interface SVGSVGElement : SVGElement < DocumentCSS, /* FIXME: refactor and delete this, it's in violation of the spec: */ SVGLayeredElement >
@interface SVGSVGElement : SVGElement < DocumentCSS, SVGFitToViewBox, /* FIXME: refactor and delete this, it's in violation of the spec: */ SVGLayeredElement >



Expand Down Expand Up @@ -111,8 +112,6 @@

#pragma mark - below here VIOLATES THE STANDARD, but needs to be CAREFULLY merged with spec

@property (nonatomic, readonly) CGRect viewBoxFrame; // FIXME: this has NON TRIVIAL relationship to the viewport property above

- (SVGElement *)findFirstElementOfClass:(Class)class; /*< temporary convenience method until SVGDocument support is complete */

@end
15 changes: 6 additions & 9 deletions Source/DOM classes/Unported or Partial DOM/SVGSVGElement.m
Expand Up @@ -10,10 +10,6 @@
#import <UIKit/UIKit.h>
#endif

@interface SVGSVGElement()
@property (nonatomic, readwrite) CGRect viewBoxFrame;
@end

@implementation SVGSVGElement

@synthesize x;
Expand All @@ -32,12 +28,13 @@ @implementation SVGSVGElement
@synthesize currentScale;
@synthesize currentTranslate;

@synthesize viewBox = _viewBox;

#pragma mark - NON SPEC, violating, properties
@synthesize viewBoxFrame = _viewBoxFrame;

-(void)dealloc
{
self.viewBoxFrame = CGRectNull;
self.viewBox = CGRectNull;
[super dealloc];
}

Expand Down Expand Up @@ -115,15 +112,15 @@ - (void)postProcessAttributesAddingErrorsTo:(SVGKParseResult *)parseResult {
{
NSArray* boxElements = [[self getAttribute:@"viewBox"] componentsSeparatedByString:@" "];

_viewBoxFrame = CGRectMake([[boxElements objectAtIndex:0] floatValue], [[boxElements objectAtIndex:1] floatValue], [[boxElements objectAtIndex:2] floatValue], [[boxElements objectAtIndex:3] floatValue]);
_viewBox = CGRectMake([[boxElements objectAtIndex:0] floatValue], [[boxElements objectAtIndex:1] floatValue], [[boxElements objectAtIndex:2] floatValue], [[boxElements objectAtIndex:3] floatValue]);

NSLog(@"[%@] WARNING: SVG spec says we should calculate the 'intrinsic aspect ratio'. Some badly-made SVG files work better if you do this and then post-multiply onto the specified viewBox attribute ... BUT they ALSO require that you 're-center' them inside the newly-created viewBox; and the SVG Spec DOES NOT SAY you should do that. All examples so far were authored in Inkscape, I think, so ... I think it's a serious bug in Inkscape that has tricked people into making incorrect SVG files. For example, c.f. http://en.wikipedia.org/wiki/File:BlankMap-World6-Equirectangular.svg", [self class]);
//osx logging
#if TARGET_OS_IPHONE
NSLog(@"[%@] DEBUG INFO: set document viewBox = %@", [self class], NSStringFromCGRect(self.viewBoxFrame));
NSLog(@"[%@] DEBUG INFO: set document viewBox = %@", [self class], NSStringFromCGRect(self.viewBox));
#else
//mac logging
NSLog(@"[%@] DEBUG INFO: set document viewBox = %@", [self class], NSStringFromRect(self.viewBoxFrame));
NSLog(@"[%@] DEBUG INFO: set document viewBox = %@", [self class], NSStringFromRect(self.viewBox));
#endif

}
Expand Down
1 change: 1 addition & 0 deletions Source/SVGKImage.m
Expand Up @@ -452,6 +452,7 @@ - (CALayer *)newLayerWithElement:(SVGElement <SVGLayeredElement> *)element
for (SVGElement *child in childNodes )
{
if ([child conformsToProtocol:@protocol(SVGLayeredElement)]) {

CALayer *sublayer = [self newLayerWithElement:(SVGElement<SVGLayeredElement> *)child];

if (!sublayer) {
Expand Down

0 comments on commit 0a00f44

Please sign in to comment.