-
Notifications
You must be signed in to change notification settings - Fork 140
/
Copy pathSVGIDExtractor.swift
68 lines (56 loc) · 2.24 KB
/
SVGIDExtractor.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
This source file is part of the Swift.org open source project
Copyright (c) 2022 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import Foundation
// On non-Darwin platforms, Foundation's XML support is vended as a separate module:
// https://github.com/apple/swift-corelibs-foundation/blob/main/Docs/ReleaseNotes_Swift5.md#dependency-management
#if canImport(FoundationXML)
import FoundationXML
#endif
/// A basic XML parser that extracts the first `id` attribute found in the given SVG.
///
/// This is a single-purpose tool and should not be used for general-purpose SVG parsing.
enum SVGIDExtractor {
/// Extracts an SVG ID from the given data.
///
/// Exposed for testing. The sibling `extractID(from: URL)` method is intended to be
/// used within SwiftDocC.
static func _extractID(from data: Data) -> String? {
let delegate = SVGIDParserDelegate()
let svgParser = XMLParser(data: data)
svgParser.delegate = delegate
// The delegate aborts the parsing when it finds the ID so the larger parsing operation is not "successful"
_ = svgParser.parse()
return delegate.id
}
/// Returns the first `id` attribute found in the given SVG, if any.
///
/// Returns nil if any errors are encountered or if an `id` attribute is
/// not found in the given SVG.
static func extractID(from svg: URL) -> String? {
guard let data = try? Data(contentsOf: svg) else {
return nil
}
return _extractID(from: data)
}
}
private class SVGIDParserDelegate: NSObject, XMLParserDelegate {
var id: String?
func parser(
_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String] = [:]
) {
guard let id = attributeDict["id"] ?? attributeDict["ID"] ?? attributeDict["iD"] ?? attributeDict["Id"] else {
return
}
self.id = id
parser.abortParsing()
}
}