123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- /**
- SVGDocument
-
- SVG spec defines this as part of the DOM version of SVG:
-
- http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGDocument
- */
- #import "Document+Mutable.h"
- #import "SVGDocument.h"
- #import "SVGDocument_Mutable.h"
- #import "NamedNodeMap_Iterable.h" // needed for the allPrefixesByNamespace implementation
- @implementation SVGDocument
- @synthesize title;
- @synthesize referrer;
- @synthesize domain;
- @synthesize URL;
- @synthesize rootElement=_rootElement;
- - (id)init
- {
- self = [super initType:DOMNodeType_DOCUMENT_NODE name:@"#document"];
- if (self) {
-
- }
- return self;
- }
- -(void)setRootElement:(SVGSVGElement *)rootElement
- {
- _rootElement = rootElement;
-
- /*! SVG spec has two variables with same name, because DOM was written to support
- weak programming languages that don't provide full OOP polymorphism.
-
- So, we'd better keep the two variables in sync!
- */
- super.documentElement = rootElement;
- }
- -(void)setDocumentElement:(Element *)newDocumentElement
- {
- NSAssert( [newDocumentElement isKindOfClass:[SVGSVGElement class]], @"Cannot set the documentElement property on an SVG doc unless it's of type SVGSVGDocument" );
-
- super.documentElement = newDocumentElement;
-
- /*! SVG spec has two variables with same name, because DOM was written to support
- weak programming languages that don't provide full OOP polymorphism.
-
- So, we'd better keep the two variables in sync!
- */
- self.rootElement = (SVGSVGElement*) self.documentElement;
- }
- #pragma mark - Serialization methods that we think ought to be part of the SVG spec, as they are needed for a good implementation, but we can't find in the main Spec
- /**
- Recursively goes through the document finding all declared namespaces in-use by any tag or attribute.
-
- @return a dictionary mapping "namespace" to "ARRAY of prefix-strings"
- */
- -(NSMutableDictionary*) allPrefixesByNamespace
- {
- NSMutableDictionary* result = [NSMutableDictionary dictionary];
-
- [SVGDocument accumulateNamespacesForNode:self.rootElement intoDictionary:result];
-
- return result;
- }
- /** implementation of allPrefixesByNamespace - stores "namespace string" : "ARRAY of prefix strings"
- */
- +(void) accumulateNamespacesForNode:(Node*) node intoDictionary:(NSMutableDictionary*) output
- {
- /**
- First, find all the attributes that declare a new Namespace at this point */
- NSDictionary* nodeMapsByNamespace = [node.attributes allNodesUnsortedDOM2];
-
- NSString* xmlnsNamespace = @"http://www.w3.org/2000/xmlns/";
- NSDictionary* xmlnsNodemap = [nodeMapsByNamespace objectForKey:xmlnsNamespace];
-
- for( NSString* xmlnsNodeName in xmlnsNodemap )
- {
- Node* namespaceDeclaration = [xmlnsNodemap objectForKey:xmlnsNodeName];
-
- NSMutableArray* prefixesForNamespace = [output objectForKey:namespaceDeclaration.nodeValue];
- if( prefixesForNamespace == nil )
- {
- prefixesForNamespace = [NSMutableArray array];
- [output setObject:prefixesForNamespace forKey:namespaceDeclaration.nodeValue];
- }
-
- if( ! [prefixesForNamespace containsObject:namespaceDeclaration.nodeName])
- [prefixesForNamespace addObject:namespaceDeclaration.localName];
- }
-
- for( Node* childNode in node.childNodes )
- {
- [self accumulateNamespacesForNode:childNode intoDictionary:output];
- }
- }
- /**
- As per allPrefixesByNamespace, but takes the output and guarantees that:
-
- 1. There is AT MOST ONE namespace with no prefix
- 2. The "prefixless" namespace is the SVG namespace (if possible. This should always be possible for an SVG doc!)
- 3. All other namespaces have EXACTLY ONE prefix (if there are multiple, it discards excess ones)
- 4. All prefixes are UNIQUE (not used by more than one Namespace)
-
- This is critically important when writing-out an SVG file to disk - As far as I can tell, it's a major ommission from
- the XML Spec (which SVG sits on top of). Without this info, you can't construct the appropriate/correct "xmlns" directives
- at the start of a file.
-
- USAGE INSTRUCTIONS:
-
- 1. Call this method to get the complete list of namespaces, including any prefixes used
- 2. Invoke Node's "appendXMLToString:..." method, passing-in this output, so it can correctly output prefixes for all nodes and subnodes
-
- @return a dictionary mapping "namespace" to "prefix-string or empty-string for the default namespace"
- */
- -(NSMutableDictionary*) allPrefixesByNamespaceNormalized
- {
- NSMutableDictionary* prefixArraysByNamespace = [self allPrefixesByNamespace];
- NSMutableDictionary* normalizedPrefixesByNamespace = [NSMutableDictionary dictionary];
-
- for( NSString* namespace in prefixArraysByNamespace )
- {
- NSArray* prefixes = [prefixArraysByNamespace objectForKey:namespace];
-
- BOOL exportedAUniquePrefix = FALSE;
- for( NSString* nextPrefix in prefixes )
- {
- if( ! [normalizedPrefixesByNamespace.allValues containsObject:nextPrefix])
- {
- [normalizedPrefixesByNamespace setObject:nextPrefix forKey:namespace];
- exportedAUniquePrefix = TRUE;
- break;
- }
- }
-
- /** If that failed to find a unique prefix, we need to either generate one, or use the default prefix */
- if( ! exportedAUniquePrefix )
- {
- if( [namespace isEqualToString:@"http://w3.org/2000/svg"])
- {
- [normalizedPrefixesByNamespace setObject:@"" forKey:namespace];
- }
- else
- {
- /** Generate a new shortname that will OVERRIDE AND REPLACE whatever prefixes this attribute has */
- int suffix = 1;
- NSString* newPrefix = [namespace lastPathComponent];
- while( [normalizedPrefixesByNamespace.allValues containsObject:newPrefix])
- {
- suffix++;
-
- newPrefix = [NSString stringWithFormat:@"%@-%i", [namespace lastPathComponent], suffix];
- }
-
- [normalizedPrefixesByNamespace setObject:newPrefix forKey:namespace];
- }
- }
- }
-
- SVGKitLogVerbose(@"Normalized prefixes:\n%@", normalizedPrefixesByNamespace );
- return normalizedPrefixesByNamespace;
- }
- @end
|