Skip to content

Commit d8e0552

Browse files
committed
Support for image resize inside markdown
1 parent a9c7615 commit d8e0552

File tree

4 files changed

+84
-9
lines changed

4 files changed

+84
-9
lines changed

Examples/Demo/Demo/ImagesView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ struct ImagesView: View {
77
Then wrap the link for the image in parentheses `()`.
88
99
```
10-
![This is an image](https://picsum.photos/id/91/400/300)
10+
![This is an image](https://picsum.photos/id/91/400/300){width=50px}
1111
```
1212
13-
![This is an image](https://picsum.photos/id/91/400/300)
13+
![This is an image](https://picsum.photos/id/91/400/300){width=50px}
1414
1515
― Photo by Jennifer Trovato
1616
"""
@@ -45,6 +45,7 @@ struct ImagesView: View {
4545
}
4646
.markdownBlockStyle(\.image) { configuration in
4747
configuration.label
48+
.scaledToFit()
4849
.clipShape(RoundedRectangle(cornerRadius: 8))
4950
.shadow(radius: 8, y: 8)
5051
.markdownMargin(top: .em(1.6), bottom: .em(1.6))

Package.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.6
1+
// swift-tools-version:5.8
22

33
import PackageDescription
44

@@ -29,6 +29,9 @@ let package = Package(
2929
.product(name: "cmark-gfm", package: "swift-cmark"),
3030
.product(name: "cmark-gfm-extensions", package: "swift-cmark"),
3131
.product(name: "NetworkImage", package: "NetworkImage"),
32+
],
33+
swiftSettings: [
34+
.enableUpcomingFeature("BareSlashRegexLiterals"),
3235
]
3336
),
3437
.testTarget(

Sources/MarkdownUI/Utility/InlineNode+RawImageData.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,41 @@ extension InlineNode {
2020
}
2121
}
2222
}
23+
24+
extension InlineNode {
25+
@available(iOS 16.0, macOS 13.0, tvOS 13.0, watchOS 6.0, *)
26+
var size: MarkdownImageSize? {
27+
switch self {
28+
case .text(let input):
29+
let pattern = /{(?:width\s*=\s*(\d+)px\s*)?(?:height\s*=\s*(\d+)px\s*)?(?:width\s*=\s*(\d+)px\s*)?(?:height\s*=\s*(\d+)px\s*)?\}/
30+
31+
if let match = input.wholeMatch(of: pattern) {
32+
let widthParts = [match.output.1, match.output.3].compactMap { $0 }
33+
let heightParts = [match.output.2, match.output.4].compactMap { $0 }
34+
35+
let width = widthParts.compactMap { Float(String($0)) }.last
36+
let height = heightParts.compactMap { Float(String($0)) }.last
37+
38+
return MarkdownImageSize(width: width.map(CGFloat.init), height: height.map(CGFloat.init))
39+
}
40+
41+
return nil
42+
default:
43+
return nil
44+
}
45+
}
46+
}
47+
48+
/// A value type representating an image size suffix.
49+
///
50+
/// Example: `![This is an image](https://foo/bar.png){width=50px}`
51+
///
52+
/// Suffix can be either
53+
/// - {width=50px}
54+
/// - {height=50px}
55+
/// - {width=50px height=100px}
56+
/// - {height=50px width=100px}
57+
struct MarkdownImageSize {
58+
let width: CGFloat?
59+
let height: CGFloat?
60+
}

Sources/MarkdownUI/Views/Inlines/ImageView.swift

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ struct ImageView: View {
66
@Environment(\.imageBaseURL) private var baseURL
77

88
private let data: RawImageData
9+
private let size: MarkdownImageSize?
910

10-
init(data: RawImageData) {
11+
init(data: RawImageData, size: MarkdownImageSize? = nil) {
1112
self.data = data
13+
self.size = size
1214
}
1315

1416
var body: some View {
@@ -18,6 +20,7 @@ struct ImageView: View {
1820
content: .init(block: self.content)
1921
)
2022
)
23+
.frame(size: size)
2124
}
2225

2326
private var label: some View {
@@ -49,12 +52,16 @@ struct ImageView: View {
4952
}
5053

5154
extension ImageView {
52-
init?(_ inlines: [InlineNode]) {
53-
guard inlines.count == 1, let data = inlines.first?.imageData else {
54-
return nil
55+
init?(_ inlines: [InlineNode]) {
56+
if inlines.count == 2, #available(iOS 16.0, macOS 13.0, tvOS 16.0, *), let data = inlines.first?.imageData, let size = inlines.last?.size {
57+
self.init(data: data, size: size)
58+
}
59+
else if inlines.count == 1, let data = inlines.first?.imageData {
60+
self.init(data: data)
61+
} else {
62+
return nil
63+
}
5564
}
56-
self.init(data: data)
57-
}
5865
}
5966

6067
extension View {
@@ -88,3 +95,29 @@ private struct LinkModifier: ViewModifier {
8895
}
8996
}
9097
}
98+
99+
extension View {
100+
fileprivate func frame(size: MarkdownImageSize?) -> some View {
101+
self.modifier(ImageViewFrameModifier(size: size))
102+
}
103+
}
104+
105+
private struct ImageViewFrameModifier: ViewModifier {
106+
let size: MarkdownImageSize?
107+
108+
func body(content: Content) -> some View {
109+
if let size {
110+
if let width = size.width, let height = size.height {
111+
content.frame(width: width, height: height)
112+
} else if let width = size.width, size.height == nil {
113+
content.frame(width: width)
114+
} else if let height = size.height, size.width == nil {
115+
content.frame(height: height)
116+
} else {
117+
content
118+
}
119+
} else {
120+
content
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)