SVGDocument.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /**
  2. SVGDocument
  3. SVG spec defines this as part of the DOM version of SVG:
  4. http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGDocument
  5. */
  6. #import "Document+Mutable.h"
  7. #import "SVGDocument.h"
  8. #import "SVGDocument_Mutable.h"
  9. #import "NamedNodeMap_Iterable.h" // needed for the allPrefixesByNamespace implementation
  10. @implementation SVGDocument
  11. @synthesize title;
  12. @synthesize referrer;
  13. @synthesize domain;
  14. @synthesize URL;
  15. @synthesize rootElement=_rootElement;
  16. - (id)init
  17. {
  18. self = [super initType:DOMNodeType_DOCUMENT_NODE name:@"#document"];
  19. if (self) {
  20. }
  21. return self;
  22. }
  23. -(void)setRootElement:(SVGSVGElement *)rootElement
  24. {
  25. _rootElement = rootElement;
  26. /*! SVG spec has two variables with same name, because DOM was written to support
  27. weak programming languages that don't provide full OOP polymorphism.
  28. So, we'd better keep the two variables in sync!
  29. */
  30. super.documentElement = rootElement;
  31. }
  32. -(void)setDocumentElement:(Element *)newDocumentElement
  33. {
  34. NSAssert( [newDocumentElement isKindOfClass:[SVGSVGElement class]], @"Cannot set the documentElement property on an SVG doc unless it's of type SVGSVGDocument" );
  35. super.documentElement = newDocumentElement;
  36. /*! SVG spec has two variables with same name, because DOM was written to support
  37. weak programming languages that don't provide full OOP polymorphism.
  38. So, we'd better keep the two variables in sync!
  39. */
  40. self.rootElement = (SVGSVGElement*) self.documentElement;
  41. }
  42. #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
  43. /**
  44. Recursively goes through the document finding all declared namespaces in-use by any tag or attribute.
  45. @return a dictionary mapping "namespace" to "ARRAY of prefix-strings"
  46. */
  47. -(NSMutableDictionary*) allPrefixesByNamespace
  48. {
  49. NSMutableDictionary* result = [NSMutableDictionary dictionary];
  50. [SVGDocument accumulateNamespacesForNode:self.rootElement intoDictionary:result];
  51. return result;
  52. }
  53. /** implementation of allPrefixesByNamespace - stores "namespace string" : "ARRAY of prefix strings"
  54. */
  55. +(void) accumulateNamespacesForNode:(Node*) node intoDictionary:(NSMutableDictionary*) output
  56. {
  57. /**
  58. First, find all the attributes that declare a new Namespace at this point */
  59. NSDictionary* nodeMapsByNamespace = [node.attributes allNodesUnsortedDOM2];
  60. NSString* xmlnsNamespace = @"http://www.w3.org/2000/xmlns/";
  61. NSDictionary* xmlnsNodemap = [nodeMapsByNamespace objectForKey:xmlnsNamespace];
  62. for( NSString* xmlnsNodeName in xmlnsNodemap )
  63. {
  64. Node* namespaceDeclaration = [xmlnsNodemap objectForKey:xmlnsNodeName];
  65. NSMutableArray* prefixesForNamespace = [output objectForKey:namespaceDeclaration.nodeValue];
  66. if( prefixesForNamespace == nil )
  67. {
  68. prefixesForNamespace = [NSMutableArray array];
  69. [output setObject:prefixesForNamespace forKey:namespaceDeclaration.nodeValue];
  70. }
  71. if( ! [prefixesForNamespace containsObject:namespaceDeclaration.nodeName])
  72. [prefixesForNamespace addObject:namespaceDeclaration.localName];
  73. }
  74. for( Node* childNode in node.childNodes )
  75. {
  76. [self accumulateNamespacesForNode:childNode intoDictionary:output];
  77. }
  78. }
  79. /**
  80. As per allPrefixesByNamespace, but takes the output and guarantees that:
  81. 1. There is AT MOST ONE namespace with no prefix
  82. 2. The "prefixless" namespace is the SVG namespace (if possible. This should always be possible for an SVG doc!)
  83. 3. All other namespaces have EXACTLY ONE prefix (if there are multiple, it discards excess ones)
  84. 4. All prefixes are UNIQUE (not used by more than one Namespace)
  85. This is critically important when writing-out an SVG file to disk - As far as I can tell, it's a major ommission from
  86. the XML Spec (which SVG sits on top of). Without this info, you can't construct the appropriate/correct "xmlns" directives
  87. at the start of a file.
  88. USAGE INSTRUCTIONS:
  89. 1. Call this method to get the complete list of namespaces, including any prefixes used
  90. 2. Invoke Node's "appendXMLToString:..." method, passing-in this output, so it can correctly output prefixes for all nodes and subnodes
  91. @return a dictionary mapping "namespace" to "prefix-string or empty-string for the default namespace"
  92. */
  93. -(NSMutableDictionary*) allPrefixesByNamespaceNormalized
  94. {
  95. NSMutableDictionary* prefixArraysByNamespace = [self allPrefixesByNamespace];
  96. NSMutableDictionary* normalizedPrefixesByNamespace = [NSMutableDictionary dictionary];
  97. for( NSString* namespace in prefixArraysByNamespace )
  98. {
  99. NSArray* prefixes = [prefixArraysByNamespace objectForKey:namespace];
  100. BOOL exportedAUniquePrefix = FALSE;
  101. for( NSString* nextPrefix in prefixes )
  102. {
  103. if( ! [normalizedPrefixesByNamespace.allValues containsObject:nextPrefix])
  104. {
  105. [normalizedPrefixesByNamespace setObject:nextPrefix forKey:namespace];
  106. exportedAUniquePrefix = TRUE;
  107. break;
  108. }
  109. }
  110. /** If that failed to find a unique prefix, we need to either generate one, or use the default prefix */
  111. if( ! exportedAUniquePrefix )
  112. {
  113. if( [namespace isEqualToString:@"http://w3.org/2000/svg"])
  114. {
  115. [normalizedPrefixesByNamespace setObject:@"" forKey:namespace];
  116. }
  117. else
  118. {
  119. /** Generate a new shortname that will OVERRIDE AND REPLACE whatever prefixes this attribute has */
  120. int suffix = 1;
  121. NSString* newPrefix = [namespace lastPathComponent];
  122. while( [normalizedPrefixesByNamespace.allValues containsObject:newPrefix])
  123. {
  124. suffix++;
  125. newPrefix = [NSString stringWithFormat:@"%@-%i", [namespace lastPathComponent], suffix];
  126. }
  127. [normalizedPrefixesByNamespace setObject:newPrefix forKey:namespace];
  128. }
  129. }
  130. }
  131. SVGKitLogVerbose(@"Normalized prefixes:\n%@", normalizedPrefixesByNamespace );
  132. return normalizedPrefixesByNamespace;
  133. }
  134. @end