-
Notifications
You must be signed in to change notification settings - Fork 388
/
Copy pathInlineNode+RawImageData.swift
103 lines (88 loc) · 3.47 KB
/
InlineNode+RawImageData.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import Foundation
struct RawImageData: Hashable {
var source: String
var alt: String
var destination: String?
}
extension InlineNode {
var imageData: RawImageData? {
switch self {
case .image(let source, let children):
return .init(source: source, alt: children.renderPlainText())
case .link(let destination, let children) where children.count == 1:
guard var imageData = children.first?.imageData else { return nil }
imageData.destination = destination
return imageData
default:
return nil
}
}
}
extension InlineNode {
var size: MarkdownImageSize? {
switch self {
case .text(let input):
// Trying first to found a fixed pattern match
let fixedPattern = "\\{(?:width\\s*=\\s*(\\d+)px\\s*)?(?:height\\s*=\\s*(\\d+)px\\s*)?(?:width\\s*=\\s*(\\d+)px\\s*)?(?:height\\s*=\\s*(\\d+)px\\s*)?\\}"
if let (width, height) = extract(regexPattern: fixedPattern, from: input) {
return MarkdownImageSize(value: .fixed(width, height))
}
// Trying then to found a relative pattern match
let relativePattern = "\\{(?:width\\s*=\\s*(\\d+)%\\s*)?(?:height\\s*=\\s*(\\d+)%\\s*)?(?:width\\s*=\\s*(\\d+)%\\s*)?(?:height\\s*=\\s*(\\d+)%\\s*)?\\}"
if let (wRatio, hRatio) = extract(regexPattern: relativePattern, from: input) {
return MarkdownImageSize(value: .relative((wRatio ?? 100)/100, (hRatio ?? 100)/100))
}
return nil
default:
return nil
}
}
private func extract(
regexPattern pattern: String,
from input: String
) -> (width: CGFloat?, height: CGFloat?)? {
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
return nil
}
let range = NSRange(input.startIndex..<input.endIndex, in: input)
guard let match = regex.firstMatch(in: input, options: [], range: range) else {
return nil
}
var width: CGFloat?
var height: CGFloat?
if let widthRange = Range(match.range(at: 1), in: input), let widthValue = Int(input[widthRange]) {
width = CGFloat(widthValue)
} else if let widthRange = Range(match.range(at: 3), in: input), let widthValue = Int(input[widthRange]) {
width = CGFloat(widthValue)
}
if let heightRange = Range(match.range(at: 2), in: input), let heightValue = Int(input[heightRange]) {
height = CGFloat(heightValue)
} else if let heightRange = Range(match.range(at: 4), in: input), let heightValue = Int(input[heightRange]) {
height = CGFloat(heightValue)
}
return (width, height)
}
}
/// A value type representating an image size suffix.
///
/// Example:
/// - `{width=50px}`
/// - `{width=50%}`
///
/// Suffix can either be:
/// - `{width=50px}`
/// - `{height=50px}`
/// - `{width=50px height=100px}`
/// - `{height=50px width=100px}`
/// - `{width=50%}`
///
/// - Note: Relative height is not supported
struct MarkdownImageSize {
let value: Value
enum Value {
/// Represents a fixed value size: `.fixed(width, height)`
case fixed(CGFloat?, CGFloat?)
/// Represents a relative value size: `.relative(relativeWidth, relativeHeight)`
case relative(CGFloat, CGFloat)
}
}