From f737948e7e132f42a718746399290e3ae26b4ab9 Mon Sep 17 00:00:00 2001 From: Alex Ehlke Date: Tue, 30 Dec 2025 23:48:22 -0500 Subject: [PATCH 01/12] add macos --- Example/ContentView.swift | 2 +- .../Custom/HTMLStyleContainer+sample.swift | 8 ++- .../Custom/TagView/HeadinglevelTagView.swift | 13 +++- .../BasicUsage/BlockElementSample.swift | 6 +- .../Samples/BasicUsage/ImageLinkSample.swift | 19 ++++-- .../Samples/BasicUsage/LineBreakSample.swift | 19 ++++-- .../Samples/BasicUsage/TextStyleSample.swift | 8 +-- .../CustomTags/CustomHeadingSample.swift | 8 +-- .../Samples/CustomTags/CustomListSample.swift | 10 +-- .../CustomTags/HighlightTagSample.swift | 10 +-- .../Samples/CustomTags/VideoTagSample.swift | 10 +-- .../ParserIntegration/ComplexHTMLSample.swift | 27 ++++++-- .../CurrentParserSample.swift | 6 +- .../Samples/QuickStart/HelloWorldSample.swift | 6 +- .../QuickStart/READMEBasicUsageSample.swift | 10 +-- .../Samples/QuickStart/READMECSSStyling.swift | 10 +-- .../QuickStart/READMECustomTagsSample.swift | 10 +-- .../QuickStart/RichContentSample.swift | 25 ++++++-- Example/Samples/Styling/BoxModelSample.swift | 10 +-- Example/Samples/Styling/CSSColorSample.swift | 6 +- .../Samples/Styling/FontFamilySample.swift | 8 +-- .../Samples/Styling/LineHeightSample.swift | 49 ++++++++++----- .../Samples/Testing/SnapshotTestSample.swift | 22 ++++--- Package.swift | 2 +- .../Attachment/AttachmentLayoutManager.swift | 7 ++- .../Core/Attachment/AttachmentManager.swift | 21 ++----- .../CoreText/TextRangeFrameCalculator.swift | 1 - .../Core/Platform/Color+Platform.swift | 34 +++++++++++ .../Core/Platform/PlatformTypes.swift | 61 +++++++++++++++++++ .../View+NavigationTitleDisplayMode.swift | 13 ++++ .../HTMLStyleContainer+TextLine.swift | 13 +++- .../HTMLStyleContainer+Values.swift | 48 +++++++++++++-- .../StyleContainer/HTMLStyleContainer.swift | 2 +- .../Core/Util/CSSFontUtility.swift | 59 +++++++++--------- .../SwiftUIHTML/Tag/Inline/UnderlineTag.swift | 3 +- Sources/SwiftUIHTML/View/HTMLInline.swift | 21 +++++-- .../View/HTMLTextLayout/TextAttachment.swift | 22 +++---- SwiftUIHTMLExample.xcodeproj/project.pbxproj | 4 +- SwiftUIHTMLExampleTests/HTMLBasicTests.swift | 2 + .../ViewSnapshotTester.swift | 2 + 40 files changed, 434 insertions(+), 183 deletions(-) create mode 100644 Sources/SwiftUIHTML/Core/Platform/Color+Platform.swift create mode 100644 Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift create mode 100644 Sources/SwiftUIHTML/Core/Platform/View+NavigationTitleDisplayMode.swift diff --git a/Example/ContentView.swift b/Example/ContentView.swift index 1d99c3d..04a66bd 100644 --- a/Example/ContentView.swift +++ b/Example/ContentView.swift @@ -134,7 +134,7 @@ struct ContentView: View { } } .navigationTitle("Examples") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } } diff --git a/Example/Custom/HTMLStyleContainer+sample.swift b/Example/Custom/HTMLStyleContainer+sample.swift index cbf8668..c00b232 100644 --- a/Example/Custom/HTMLStyleContainer+sample.swift +++ b/Example/Custom/HTMLStyleContainer+sample.swift @@ -1,12 +1,18 @@ // Copyright © 2025 PRND. All rights reserved. +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit +private typealias ExampleFont = UIFont +#endif import SwiftUIHTML extension HTMLStyleContainer { static func sample(by lineBreakMode: LineBreakMode) -> HTMLStyleContainer { var style = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 12) + let font = ExampleFont.systemFont(ofSize: 12) style.uiFont = font style.textLine = .lineHeight(font: font, lineHeight: 24) style.lineBreakMode = lineBreakMode diff --git a/Example/Custom/TagView/HeadinglevelTagView.swift b/Example/Custom/TagView/HeadinglevelTagView.swift index a3065d9..65ffef9 100644 --- a/Example/Custom/TagView/HeadinglevelTagView.swift +++ b/Example/Custom/TagView/HeadinglevelTagView.swift @@ -1,5 +1,12 @@ // Copyright © 2025 PRND. All rights reserved. import SwiftUI +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif import SwiftUIHTML @@ -11,7 +18,7 @@ struct Headinglevel1TagView: BlockTag { } static func applyStyles(with attributes: [String : AttributeValue], to styleContainer: inout HTMLStyleContainer) { - styleContainer.uiFont = UIFont.boldSystemFont(ofSize: 24) + styleContainer.uiFont = ExampleFont.boldSystemFont(ofSize: 24) } } @@ -23,7 +30,7 @@ struct Headinglevel2TagView: BlockTag { self.element = element } static func applyStyles(with attributes: [String : AttributeValue], to styleContainer: inout HTMLStyleContainer) { - styleContainer.uiFont = UIFont.boldSystemFont(ofSize: 20) + styleContainer.uiFont = ExampleFont.boldSystemFont(ofSize: 20) } } @@ -36,6 +43,6 @@ struct Headinglevel3TagView: BlockTag { } static func applyStyles(with attributes: [String : AttributeValue], to styleContainer: inout HTMLStyleContainer) { - styleContainer.uiFont = UIFont.boldSystemFont(ofSize: 18) + styleContainer.uiFont = ExampleFont.boldSystemFont(ofSize: 18) } } diff --git a/Example/Samples/BasicUsage/BlockElementSample.swift b/Example/Samples/BasicUsage/BlockElementSample.swift index 7dafee5..44d799f 100644 --- a/Example/Samples/BasicUsage/BlockElementSample.swift +++ b/Example/Samples/BasicUsage/BlockElementSample.swift @@ -56,7 +56,7 @@ struct BlockElementSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Supported Block Tags @@ -102,7 +102,7 @@ struct BlockElementSample: View { """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -123,7 +123,7 @@ struct BlockElementSample: View { .padding() } .navigationTitle("Block Elements") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/BasicUsage/ImageLinkSample.swift b/Example/Samples/BasicUsage/ImageLinkSample.swift index dd2243e..3466e82 100644 --- a/Example/Samples/BasicUsage/ImageLinkSample.swift +++ b/Example/Samples/BasicUsage/ImageLinkSample.swift @@ -2,6 +2,13 @@ import SwiftUI import SwiftUIHTML +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif struct ImageLinkSample: View { @State private var htmlCode = """ @@ -83,14 +90,14 @@ struct ImageLinkSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 15) + let font = ExampleFont.systemFont(ofSize: 15) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 22) container.lineBreakMode = .byWordWrapping return container }()) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -111,12 +118,12 @@ struct ImageLinkSample: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 200) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -174,7 +181,7 @@ struct ImageLinkSample: View { .padding() } .navigationTitle("Images & Links") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } @@ -182,4 +189,4 @@ struct ImageLinkSample: View { NavigationView { ImageLinkSample() } -} \ No newline at end of file +} diff --git a/Example/Samples/BasicUsage/LineBreakSample.swift b/Example/Samples/BasicUsage/LineBreakSample.swift index 23cc22a..fb05629 100644 --- a/Example/Samples/BasicUsage/LineBreakSample.swift +++ b/Example/Samples/BasicUsage/LineBreakSample.swift @@ -2,6 +2,13 @@ import SwiftUI import SwiftUIHTML +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif struct LineBreakSample: View { @State private var htmlCode = """ @@ -59,7 +66,7 @@ struct LineBreakSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 20) container.lineBreakMode = .byWordWrapping @@ -83,7 +90,7 @@ struct LineBreakSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 20) container.lineBreakMode = .byCharWrapping @@ -115,12 +122,12 @@ struct LineBreakSample: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 150) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -201,7 +208,7 @@ struct LineBreakSample: View { .padding() } .navigationTitle("Line Break Modes") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } @@ -209,4 +216,4 @@ struct LineBreakSample: View { NavigationView { LineBreakSample() } -} \ No newline at end of file +} diff --git a/Example/Samples/BasicUsage/TextStyleSample.swift b/Example/Samples/BasicUsage/TextStyleSample.swift index 2204a05..7a9b5db 100644 --- a/Example/Samples/BasicUsage/TextStyleSample.swift +++ b/Example/Samples/BasicUsage/TextStyleSample.swift @@ -77,7 +77,7 @@ struct TextStyleSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -98,12 +98,12 @@ struct TextStyleSample: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 150) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -163,7 +163,7 @@ struct TextStyleSample: View { .padding() } .navigationTitle("Text Styling") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/CustomTags/CustomHeadingSample.swift b/Example/Samples/CustomTags/CustomHeadingSample.swift index 6ae21a2..82e7dee 100644 --- a/Example/Samples/CustomTags/CustomHeadingSample.swift +++ b/Example/Samples/CustomTags/CustomHeadingSample.swift @@ -42,7 +42,7 @@ struct CustomHeadingSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Custom Tag Description @@ -81,7 +81,7 @@ struct Headinglevel3TagView: BlockTag { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -98,7 +98,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -119,7 +119,7 @@ let configuration = HTMLConfiguration.default .padding() } .navigationTitle("Custom Headings") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/CustomTags/CustomListSample.swift b/Example/Samples/CustomTags/CustomListSample.swift index 76f01dc..ecbe4d9 100644 --- a/Example/Samples/CustomTags/CustomListSample.swift +++ b/Example/Samples/CustomTags/CustomListSample.swift @@ -52,7 +52,7 @@ struct CustomListSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Implementation Description @@ -96,7 +96,7 @@ struct ULTagView: BlockTag { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -111,7 +111,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -130,7 +130,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -152,7 +152,7 @@ let configuration = HTMLConfiguration.default .padding() } .navigationTitle("Custom List Tags") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/CustomTags/HighlightTagSample.swift b/Example/Samples/CustomTags/HighlightTagSample.swift index 6c1f331..619b4a8 100644 --- a/Example/Samples/CustomTags/HighlightTagSample.swift +++ b/Example/Samples/CustomTags/HighlightTagSample.swift @@ -86,7 +86,7 @@ struct HighlightTagSample: View { .htmlEnvironment(\.configuration, highlightConfiguration) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // InlineTag Description @@ -121,7 +121,7 @@ struct HighlightTag: InlineTag { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -137,7 +137,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -160,7 +160,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -200,7 +200,7 @@ let configuration = HTMLConfiguration.default .padding() } .navigationTitle("Highlight Tag") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/CustomTags/VideoTagSample.swift b/Example/Samples/CustomTags/VideoTagSample.swift index 4f16482..ef72b75 100644 --- a/Example/Samples/CustomTags/VideoTagSample.swift +++ b/Example/Samples/CustomTags/VideoTagSample.swift @@ -37,7 +37,7 @@ struct VideoTagSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Video tag description @@ -75,7 +75,7 @@ struct VideoTagView: BlockTag, Equatable { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -90,7 +90,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -104,7 +104,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -142,7 +142,7 @@ let configuration = HTMLConfiguration.default .padding() } .navigationTitle("Video Tag") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/ParserIntegration/ComplexHTMLSample.swift b/Example/Samples/ParserIntegration/ComplexHTMLSample.swift index e3985a9..13388c8 100644 --- a/Example/Samples/ParserIntegration/ComplexHTMLSample.swift +++ b/Example/Samples/ParserIntegration/ComplexHTMLSample.swift @@ -2,6 +2,13 @@ import SwiftUI import SwiftUIHTML +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif struct ComplexHTMLSample: View { let html = """ @@ -95,17 +102,17 @@ struct ComplexHTMLSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 20) container.lineBreakMode = .byWordWrapping return container }()) .padding() - .background(Color(.systemBackground)) + .background(platformBackgroundColor) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray5), lineWidth: 1) + .stroke(Color.platformSystemGray5, lineWidth: 1) ) // 복잡성 설명 @@ -178,19 +185,27 @@ struct ComplexHTMLSample: View { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } } .padding() } .navigationTitle("Complex HTML") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } +private var platformBackgroundColor: Color { +#if os(macOS) + return Color(nsColor: .windowBackgroundColor) +#else + return Color(.systemBackground) +#endif +} + #Preview { NavigationView { ComplexHTMLSample() } -} \ No newline at end of file +} diff --git a/Example/Samples/ParserIntegration/CurrentParserSample.swift b/Example/Samples/ParserIntegration/CurrentParserSample.swift index f88f185..557dd78 100644 --- a/Example/Samples/ParserIntegration/CurrentParserSample.swift +++ b/Example/Samples/ParserIntegration/CurrentParserSample.swift @@ -48,7 +48,7 @@ struct CurrentParserSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Parser Information @@ -82,7 +82,7 @@ struct HTMLFuziParser: HTMLParserable { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -114,7 +114,7 @@ struct HTMLFuziParser: HTMLParserable { .padding() } .navigationTitle("Current Parser") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/QuickStart/HelloWorldSample.swift b/Example/Samples/QuickStart/HelloWorldSample.swift index 8082e0d..431241e 100644 --- a/Example/Samples/QuickStart/HelloWorldSample.swift +++ b/Example/Samples/QuickStart/HelloWorldSample.swift @@ -31,7 +31,7 @@ struct HelloWorldSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Code @@ -52,7 +52,7 @@ HTMLView(html: html, parser: HTMLFuziParser()) """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -73,7 +73,7 @@ HTMLView(html: html, parser: HTMLFuziParser()) .padding() } .navigationTitle("Hello SwiftUIHTML") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/QuickStart/READMEBasicUsageSample.swift b/Example/Samples/QuickStart/READMEBasicUsageSample.swift index cf90e0d..9e9c7b1 100644 --- a/Example/Samples/QuickStart/READMEBasicUsageSample.swift +++ b/Example/Samples/QuickStart/READMEBasicUsageSample.swift @@ -49,7 +49,7 @@ struct READMEBasicUsageSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, createStyleContainer()) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -70,12 +70,12 @@ struct READMEBasicUsageSample: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 120) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -111,7 +111,7 @@ struct ContentView: View { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -164,7 +164,7 @@ struct ContentView: View { .padding() } .navigationTitle("README Basic Usage") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/QuickStart/READMECSSStyling.swift b/Example/Samples/QuickStart/READMECSSStyling.swift index 8ed789b..6406aca 100644 --- a/Example/Samples/QuickStart/READMECSSStyling.swift +++ b/Example/Samples/QuickStart/READMECSSStyling.swift @@ -80,7 +80,7 @@ struct READMECSSStyling: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: lineBreakMode.html)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -101,12 +101,12 @@ struct READMECSSStyling: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 140) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -125,7 +125,7 @@ HTMLView(html: html, parser: parser) """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -222,7 +222,7 @@ HTMLView(html: html, parser: parser) .padding() } .navigationTitle("README CSS Styling") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/QuickStart/READMECustomTagsSample.swift b/Example/Samples/QuickStart/READMECustomTagsSample.swift index 99508ff..2448969 100644 --- a/Example/Samples/QuickStart/READMECustomTagsSample.swift +++ b/Example/Samples/QuickStart/READMECustomTagsSample.swift @@ -52,7 +52,7 @@ struct READMECustomTagsSample: View { .htmlEnvironment(\.configuration, .sample) // configuration with custom tags registered .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -73,12 +73,12 @@ struct READMECustomTagsSample: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 140) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -96,7 +96,7 @@ let configuration = HTMLConfiguration.default """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -181,7 +181,7 @@ let configuration = HTMLConfiguration.default .padding() } .navigationTitle("README Custom Tags") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/QuickStart/RichContentSample.swift b/Example/Samples/QuickStart/RichContentSample.swift index bb2ea08..f44cd2f 100644 --- a/Example/Samples/QuickStart/RichContentSample.swift +++ b/Example/Samples/QuickStart/RichContentSample.swift @@ -2,6 +2,13 @@ import SwiftUI import SwiftUIHTML +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif struct RichContentSample: View { let html = """ @@ -60,17 +67,17 @@ struct RichContentSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 15) + let font = ExampleFont.systemFont(ofSize: 15) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 22) container.lineBreakMode = .byWordWrapping return container }()) .padding() - .background(Color(.systemBackground)) + .background(platformBackgroundColor) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray5), lineWidth: 1) + .stroke(Color.platformSystemGray5, lineWidth: 1) ) // Features @@ -111,17 +118,25 @@ struct RichContentSample: View { """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } } .padding() } .navigationTitle("Rich Content") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } +private var platformBackgroundColor: Color { +#if os(macOS) + return Color(nsColor: .windowBackgroundColor) +#else + return Color(.systemBackground) +#endif +} + #Preview { NavigationView { RichContentSample() diff --git a/Example/Samples/Styling/BoxModelSample.swift b/Example/Samples/Styling/BoxModelSample.swift index e704f30..3e35df1 100644 --- a/Example/Samples/Styling/BoxModelSample.swift +++ b/Example/Samples/Styling/BoxModelSample.swift @@ -85,7 +85,7 @@ struct BoxModelSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Box model structure @@ -122,7 +122,7 @@ padding: 5px 10px 15px 20px; """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -142,7 +142,7 @@ margin: 5px 10px 15px 20px; /* Top, right, bottom, left */ """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -164,7 +164,7 @@ border-radius: 4px 8px 12px 16px; """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -186,7 +186,7 @@ border-radius: 4px 8px 12px 16px; .padding() } .navigationTitle("Box Model") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/Styling/CSSColorSample.swift b/Example/Samples/Styling/CSSColorSample.swift index 9d8beae..895162b 100644 --- a/Example/Samples/Styling/CSSColorSample.swift +++ b/Example/Samples/Styling/CSSColorSample.swift @@ -64,7 +64,7 @@ struct CSSColorSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Supported Color Formats @@ -120,7 +120,7 @@ struct CSSColorSample: View { """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -142,7 +142,7 @@ struct CSSColorSample: View { .padding() } .navigationTitle("CSS Colors") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/Styling/FontFamilySample.swift b/Example/Samples/Styling/FontFamilySample.swift index e1249d2..4ca8ef0 100644 --- a/Example/Samples/Styling/FontFamilySample.swift +++ b/Example/Samples/Styling/FontFamilySample.swift @@ -87,7 +87,7 @@ struct FontFamilySample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) // Supported fonts @@ -124,7 +124,7 @@ font-family: 'Times New Roman'; """) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -157,7 +157,7 @@ font-family: 'Times New Roman'; """) .font(.system(.caption, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -180,7 +180,7 @@ font-family: 'Times New Roman'; .padding() } .navigationTitle("Font Families") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Example/Samples/Styling/LineHeightSample.swift b/Example/Samples/Styling/LineHeightSample.swift index 59e457c..5597555 100644 --- a/Example/Samples/Styling/LineHeightSample.swift +++ b/Example/Samples/Styling/LineHeightSample.swift @@ -2,6 +2,13 @@ import SwiftUI import SwiftUIHTML +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif struct LineHeightSample: View { let longText = """ @@ -43,7 +50,7 @@ struct LineHeightSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 16) container.lineBreakMode = .byWordWrapping @@ -72,7 +79,7 @@ struct LineHeightSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 20) container.lineBreakMode = .byWordWrapping @@ -101,7 +108,7 @@ struct LineHeightSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 28) container.lineBreakMode = .byWordWrapping @@ -130,7 +137,7 @@ struct LineHeightSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineSpacing(spacing: 8) container.lineBreakMode = .byWordWrapping @@ -149,17 +156,10 @@ struct LineHeightSample: View { Text("TextLine Configuration Method") .font(.headline) - Text(""" -// Line height setting (absolute value) -let font = UIFont.systemFont(ofSize: 14) -container.textLine = .lineHeight(font: font, lineHeight: 20) - -// Line spacing setting (relative value) -container.textLine = .lineSpacing(spacing: 8) -""") + Text(lineHeightCodeSample) .font(.system(.body, design: .monospaced)) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) } @@ -214,12 +214,31 @@ container.textLine = .lineSpacing(spacing: 8) .padding() } .navigationTitle("Line Height") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } +private var fontTypeName: String { +#if os(macOS) + return "NSFont" +#else + return "UIFont" +#endif +} + +private var lineHeightCodeSample: String { + """ +// Line height setting (absolute value) +let font = \(fontTypeName).systemFont(ofSize: 14) +container.textLine = .lineHeight(font: font, lineHeight: 20) + +// Line spacing setting (relative value) +container.textLine = .lineSpacing(spacing: 8) +""" +} + #Preview { NavigationView { LineHeightSample() } -} \ No newline at end of file +} diff --git a/Example/Samples/Testing/SnapshotTestSample.swift b/Example/Samples/Testing/SnapshotTestSample.swift index b9522af..aca9d2a 100644 --- a/Example/Samples/Testing/SnapshotTestSample.swift +++ b/Example/Samples/Testing/SnapshotTestSample.swift @@ -1,9 +1,15 @@ // Copyright © 2025 PRND. All rights reserved. import SwiftUI +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else +import UIKit +private typealias ExampleFont = UIFont +#endif -@testable import SwiftUIHTML -@testable import SwiftUIHTMLExample +import SwiftUIHTML struct SnapshotTestSample: View { enum LineBreakModeOption: String, CaseIterable, Hashable { @@ -107,7 +113,7 @@ struct SnapshotTestSample: View { .htmlEnvironment(\.configuration, .sample) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - let font = UIFont.systemFont(ofSize: 14) + let font = ExampleFont.systemFont(ofSize: 14) container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 20) container.lineBreakMode = selectedLineBreakModeOption.toLineBreakMode() @@ -115,10 +121,10 @@ struct SnapshotTestSample: View { }()) .frame(width: frameWidth) .padding() - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -139,12 +145,12 @@ struct SnapshotTestSample: View { TextEditor(text: $htmlCode) .font(.system(.body, design: .monospaced)) .padding(8) - .background(Color(.systemGray6)) + .background(Color.platformSystemGray6) .cornerRadius(8) .frame(minHeight: 200) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color(.systemGray4), lineWidth: 1) + .stroke(Color.platformSystemGray4, lineWidth: 1) ) } @@ -226,7 +232,7 @@ struct SnapshotTestSample: View { .padding() } .navigationTitle("Snapshot Test") - .navigationBarTitleDisplayMode(.inline) + .applyInlineNavigationTitleDisplayMode() } } diff --git a/Package.swift b/Package.swift index 4b9681f..ce78549 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "SwiftUIHTML", - platforms: [.iOS(.v15)], + platforms: [.iOS(.v15), .macOS(.v12)], products: [ .library( name: "SwiftUIHTML", diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift index 6ea833a..08f11f8 100644 --- a/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift @@ -1,6 +1,11 @@ // Copyright © 2024 PRND. All rights reserved. -import UIKit import Combine +import Foundation +#if os(macOS) +import AppKit +#else +import UIKit +#endif final class AttachmentLayoutEngine { diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift index 534c58a..4d411cb 100644 --- a/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift @@ -1,13 +1,14 @@ // Copyright © 2025 PRND. All rights reserved. -import UIKit import Combine +import CoreGraphics +import Foundation final class AttachmentManager: ObservableObject { lazy var layoutEngine = AttachmentLayoutEngine() private var cancellables = Set() - private class ImageCache: NSCache {} + private class ImageCache: NSCache {} private let textImages = ImageCache() init() { @@ -41,7 +42,7 @@ final class AttachmentManager: ObservableObject { } // SwiftUI Text(image:) 주입 이미지 - func sizeImage(key: AnyHashable, styleContainer: HTMLStyleContainer) -> UIImage { + func sizeImage(key: AnyHashable, styleContainer: HTMLStyleContainer) -> PlatformImage { var size = layoutEngine.getSize(key: key) var fontName: String? = nil var fontSize: CGFloat? = nil @@ -72,20 +73,10 @@ private extension AttachmentManager { } private struct EmptyImage { - let scale: CGFloat let size: CGSize - init(scale: CGFloat = 1, size: CGSize) { - self.scale = scale - self.size = size - } - - var image: UIImage { - if size == .zero { return UIImage() } - let format = UIGraphicsImageRendererFormat() - format.scale = 1 - let renderer = UIGraphicsImageRenderer(size: size, format: format) - return renderer.image { _ in } + var image: PlatformImage { + PlatformImage.manabiEmpty(size: size) } } diff --git a/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift b/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift index a146f0e..0865073 100644 --- a/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift +++ b/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift @@ -1,7 +1,6 @@ // Copyright © 2025 PRND. All rights reserved. import Foundation import CoreText -import UIKit /// Calculates frame rectangles for text ranges using Core Text final class TextRangeFrameCalculator { diff --git a/Sources/SwiftUIHTML/Core/Platform/Color+Platform.swift b/Sources/SwiftUIHTML/Core/Platform/Color+Platform.swift new file mode 100644 index 0000000..cac6c00 --- /dev/null +++ b/Sources/SwiftUIHTML/Core/Platform/Color+Platform.swift @@ -0,0 +1,34 @@ +// Copyright © 2025 PRND. All rights reserved. +import SwiftUI + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +public extension Color { + static var platformSystemGray4: Color { +#if os(macOS) + return Color(nsColor: NSColor.systemGray.withAlphaComponent(0.35)) +#else + return Color(.systemGray4) +#endif + } + + static var platformSystemGray5: Color { +#if os(macOS) + return Color(nsColor: NSColor.systemGray.withAlphaComponent(0.25)) +#else + return Color(.systemGray5) +#endif + } + + static var platformSystemGray6: Color { +#if os(macOS) + return Color(nsColor: NSColor.systemGray.withAlphaComponent(0.18)) +#else + return Color(.systemGray6) +#endif + } +} diff --git a/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift b/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift new file mode 100644 index 0000000..cdca03d --- /dev/null +++ b/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift @@ -0,0 +1,61 @@ +// Copyright © 2025 PRND. All rights reserved. +import CoreText +import Foundation + +#if os(macOS) +import AppKit +internal typealias PlatformFont = NSFont +internal typealias PlatformColor = NSColor +internal typealias PlatformImage = NSImage +internal typealias PlatformFontDescriptor = NSFontDescriptor +#else +import UIKit +internal typealias PlatformFont = UIFont +internal typealias PlatformColor = UIColor +internal typealias PlatformImage = UIImage +internal typealias PlatformFontDescriptor = UIFontDescriptor +#endif + +internal extension PlatformFont { + var manabiCTFont: CTFont { + CTFontCreateWithName(fontName as CFString, pointSize, nil) + } + + var manabiLineHeight: CGFloat { + let ctFont = manabiCTFont + return CTFontGetAscent(ctFont) + CTFontGetDescent(ctFont) + CTFontGetLeading(ctFont) + } + + var manabiFontHeight: CGFloat { + let ctFont = manabiCTFont + return CTFontGetAscent(ctFont) + CTFontGetDescent(ctFont) + } + + var manabiBoundingBoxMaxY: CGFloat { + CTFontGetBoundingBox(manabiCTFont).maxY + } +} + +internal extension PlatformImage { + static func manabiEmpty(size: CGSize) -> PlatformImage { +#if os(macOS) + if size == .zero { + return PlatformImage(size: .zero) + } + let image = PlatformImage(size: size) + image.lockFocus() + PlatformColor.clear.setFill() + NSRect(origin: .zero, size: size).fill() + image.unlockFocus() + return image +#else + if size == .zero { + return PlatformImage() + } + let format = UIGraphicsImageRendererFormat() + format.scale = 1 + let renderer = UIGraphicsImageRenderer(size: size, format: format) + return renderer.image { _ in } +#endif + } +} diff --git a/Sources/SwiftUIHTML/Core/Platform/View+NavigationTitleDisplayMode.swift b/Sources/SwiftUIHTML/Core/Platform/View+NavigationTitleDisplayMode.swift new file mode 100644 index 0000000..e1d9d2e --- /dev/null +++ b/Sources/SwiftUIHTML/Core/Platform/View+NavigationTitleDisplayMode.swift @@ -0,0 +1,13 @@ +// Copyright © 2025 PRND. All rights reserved. +import SwiftUI + +public extension View { + @ViewBuilder + func applyInlineNavigationTitleDisplayMode() -> some View { +#if os(iOS) + navigationBarTitleDisplayMode(.inline) +#else + self +#endif + } +} diff --git a/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+TextLine.swift b/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+TextLine.swift index 6b27798..ea43c11 100644 --- a/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+TextLine.swift +++ b/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+TextLine.swift @@ -1,5 +1,10 @@ // Copyright © 2024 PRND. All rights reserved. import SwiftUI +#if os(macOS) +import AppKit +#elseif os(iOS) +import UIKit +#endif extension HTMLStyleContainer { @@ -10,9 +15,15 @@ extension HTMLStyleContainer { } public extension HTMLStyleContainer.TextLineAttribute { +#if os(macOS) + static func lineHeight(font: NSFont, lineHeight: CGFloat) -> Self { + .lineHeight(fontLineHeight: font.manabiLineHeight, lineHeight: lineHeight) + } +#else static func lineHeight(font: UIFont, lineHeight: CGFloat) -> Self { - .lineHeight(fontLineHeight: font.lineHeight, lineHeight: lineHeight) + .lineHeight(fontLineHeight: font.manabiLineHeight, lineHeight: lineHeight) } +#endif } extension HTMLStyleContainer.TextLineAttribute { diff --git a/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+Values.swift b/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+Values.swift index 059adea..2abf897 100644 --- a/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+Values.swift +++ b/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer+Values.swift @@ -1,5 +1,10 @@ // Copyright © 2024 PRND. All rights reserved. import SwiftUI +#if os(macOS) +import AppKit +#elseif os(iOS) +import UIKit +#endif public extension HTMLStyleContainer { @@ -23,6 +28,15 @@ public extension HTMLStyleContainer { set { container.inlinePresentationIntent = newValue } } +#if os(macOS) + var uiFont: AttributeScopes.AppKitAttributes.FontAttribute.Value? { + get { container.appKit.font } + set { + container.swiftUI.font = nil + container.appKit.font = newValue + } + } +#else var uiFont: AttributeScopes.UIKitAttributes.FontAttribute.Value? { get { container.uiKit.font } set { @@ -30,34 +44,61 @@ public extension HTMLStyleContainer { container.uiKit.font = newValue } } +#endif var font: AttributeScopes.SwiftUIAttributes.FontAttribute.Value? { get { container.swiftUI.font } set { +#if os(macOS) + container.appKit.font = nil +#else container.uiKit.font = nil +#endif container.swiftUI.font = newValue } } +#if os(macOS) + var underlineStyle: AttributeScopes.AppKitAttributes.UnderlineStyleAttribute.Value? { + get { container.underlineStyle } + set { container.underlineStyle = newValue } + } + + var underlineColor: AttributeScopes.AppKitAttributes.UnderlineColorAttribute.Value? { + get { container.underlineColor } + set { container.underlineColor = newValue} + } + + var kern: AttributeScopes.AppKitAttributes.KernAttribute.Value? { + get { container.kern } + set { container.kern = newValue } + } + + var tracking: AttributeScopes.AppKitAttributes.TrackingAttribute.Value? { + get { container.tracking } + set { container.tracking = newValue } + } +#else var underlineStyle: AttributeScopes.UIKitAttributes.UnderlineStyleAttribute.Value? { get { container.underlineStyle } set { container.underlineStyle = newValue } } - + var underlineColor: AttributeScopes.UIKitAttributes.UnderlineColorAttribute.Value? { get { container.underlineColor } set { container.underlineColor = newValue} } - + var kern: AttributeScopes.UIKitAttributes.KernAttribute.Value? { get { container.kern } set { container.kern = newValue } } - + var tracking: AttributeScopes.UIKitAttributes.TrackingAttribute.Value? { get { container.tracking } set { container.tracking = newValue } } +#endif func merging(_ other: HTMLStyleContainer, mergePolicy: AttributedString.AttributeMergePolicy = .keepNew) -> HTMLStyleContainer { var copy = self @@ -66,4 +107,3 @@ public extension HTMLStyleContainer { return copy } } - diff --git a/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer.swift b/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer.swift index 3c01192..523170b 100644 --- a/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer.swift +++ b/Sources/SwiftUIHTML/Core/StyleContainer/HTMLStyleContainer.swift @@ -4,7 +4,7 @@ import SwiftUI public struct HTMLStyleContainer: Equatable, Sendable, Hashable { public func hash(into hasher: inout Hasher) { - if #available(iOS 16, *) { + if #available(iOS 16, macOS 13, *) { hasher.combine(container) } else { hasher.combine(HashableAttributeContainer(container)) diff --git a/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift b/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift index f64387b..205efba 100644 --- a/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift +++ b/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift @@ -1,5 +1,5 @@ // Copyright © 2025 PRND. All rights reserved. -import UIKit +import Foundation /// CSS 폰트 스타일 파싱 및 처리를 담당하는 유틸리티 클래스 @@ -11,9 +11,9 @@ public enum CSSFontUtility { /// - fontFamilyString: CSS font-family 문자열 (예: "Arial, Helvetica, sans-serif") /// - size: 적용할 폰트 크기 /// - Returns: 생성된 폰트 - static func createFont(fromFontFamily fontFamilyString: String?, size: CGFloat) -> UIFont { + static func createFont(fromFontFamily fontFamilyString: String?, size: CGFloat) -> PlatformFont { guard let fontFamilyString = fontFamilyString, !fontFamilyString.isEmpty else { - return UIFont.systemFont(ofSize: size) + return PlatformFont.systemFont(ofSize: size) } let fontNames = parseFontFamilyString(fontFamilyString) @@ -25,7 +25,7 @@ public enum CSSFontUtility { /// - fontSizeString: CSS font-size 문자열 (예: "12px", "1.5em", "large") /// - baseSize: 상대 크기 계산을 위한 기준 크기 /// - Returns: 파싱된 폰트 크기 - static func parseSize(fromFontSize fontSizeString: String?, baseSize: CGFloat = UIFont.systemFontSize) -> CGFloat { + static func parseSize(fromFontSize fontSizeString: String?, baseSize: CGFloat = PlatformFont.systemFontSize) -> CGFloat { guard let fontSizeString = fontSizeString, !fontSizeString.isEmpty else { return baseSize } @@ -78,8 +78,8 @@ public enum CSSFontUtility { /// - cssStyle: CSS 스타일 딕셔너리 /// - currentFont: 현재 설정된 폰트 (기준 크기 및 업데이트용) /// - Returns: 생성된 폰트 - static func createFont(fromCSSStyle cssStyle: CSSStyle, currentFont: UIFont? = nil) -> UIFont? { - let currentSize = currentFont?.pointSize ?? UIFont.systemFontSize + static func createFont(fromCSSStyle cssStyle: CSSStyle, currentFont: PlatformFont? = nil) -> PlatformFont? { + let currentSize = currentFont?.pointSize ?? PlatformFont.systemFontSize // font-size와 font-family 속성 추출 let fontSizeValue = cssStyle["font-size"]?.string @@ -98,7 +98,7 @@ public enum CSSFontUtility { } else if let family = fontFamilyValue { return createFont(fromFontFamily: family, size: parsedSize) } else { - return currentFont?.withSize(parsedSize) ?? UIFont.systemFont(ofSize: parsedSize) + return currentFont?.withSize(parsedSize) ?? PlatformFont.systemFont(ofSize: parsedSize) } } } @@ -110,7 +110,7 @@ public enum CSSFontUtility { if let fontFamily = fontFamilyValue { return createFont(fromFontFamily: fontFamily, size: size) } else if size != currentSize { - return currentFont?.withSize(size) ?? UIFont.systemFont(ofSize: size) + return currentFont?.withSize(size) ?? PlatformFont.systemFont(ofSize: size) } return currentFont @@ -129,7 +129,7 @@ public enum CSSFontUtility { .filter { !$0.isEmpty } } - private static func createFontWithFallbacks(fontNames: [String], size: CGFloat) -> UIFont { + private static func createFontWithFallbacks(fontNames: [String], size: CGFloat) -> PlatformFont { // 특정 폰트 이름과 제네릭 패밀리 분리 var specificFontNames: [String] = [] var genericFamily: String? = nil @@ -143,9 +143,9 @@ public enum CSSFontUtility { } // 첫 번째 유효한 폰트 찾기 - var primaryFont: UIFont! = nil + var primaryFont: PlatformFont! = nil for name in specificFontNames { - if let font = UIFont(name: name, size: size) { + if let font = PlatformFont(name: name, size: size) { primaryFont = font break } @@ -156,19 +156,19 @@ public enum CSSFontUtility { if let genericFamily = genericFamily { return createSystemFontForGenericFamily(genericFamily, size: size) } - return UIFont.systemFont(ofSize: size) + return PlatformFont.systemFont(ofSize: size) } // 기본 폰트의 descriptor 가져오기 let primaryDescriptor = primaryFont!.fontDescriptor // Cascade 목록 생성 - var cascadeList: [UIFontDescriptor] = [] + var cascadeList: [PlatformFontDescriptor] = [] // Fallback 폰트 추가 (첫 번째 이후의 특정 폰트들) if specificFontNames.count > 1 { for name in specificFontNames.dropFirst() { - if let font = UIFont(name: name, size: size) { + if let font = PlatformFont(name: name, size: size) { cascadeList.append(font.fontDescriptor) } } @@ -181,14 +181,18 @@ public enum CSSFontUtility { } // 최종 fallback으로 시스템 폰트 추가 - cascadeList.append(UIFont.systemFont(ofSize: size).fontDescriptor) + cascadeList.append(PlatformFont.systemFont(ofSize: size).fontDescriptor) // cascade 목록이 비어있지 않으면 descriptor 업데이트 if !cascadeList.isEmpty { let descriptorWithCascade = primaryDescriptor.addingAttributes([ - UIFontDescriptor.AttributeName.cascadeList: cascadeList + PlatformFontDescriptor.AttributeName.cascadeList: cascadeList ]) - return UIFont(descriptor: descriptorWithCascade, size: 0) // 0은 원래 크기 유지 +#if os(macOS) + return PlatformFont(descriptor: descriptorWithCascade, size: 0) ?? primaryFont // 0은 원래 크기 유지 +#else + return PlatformFont(descriptor: descriptorWithCascade, size: 0) // 0은 원래 크기 유지 +#endif } return primaryFont @@ -199,26 +203,25 @@ public enum CSSFontUtility { return genericFamilies.contains(name.lowercased()) } - private static func createSystemFontForGenericFamily(_ family: String, size: CGFloat) -> UIFont { + private static func createSystemFontForGenericFamily(_ family: String, size: CGFloat) -> PlatformFont { switch family.lowercased() { case "serif": - return UIFont(name: "TimesNewRomanPSMT", size: size) ?? UIFont.systemFont(ofSize: size) + return PlatformFont(name: "TimesNewRomanPSMT", size: size) ?? PlatformFont.systemFont(ofSize: size) case "sans-serif": - return UIFont.systemFont(ofSize: size) + return PlatformFont.systemFont(ofSize: size) case "monospace": - if #available(iOS 13.0, *) { - return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) - } else { - return UIFont(name: "Menlo-Regular", size: size) ?? UIFont.systemFont(ofSize: size) + if #available(iOS 13.0, macOS 10.15, *) { + return PlatformFont.monospacedSystemFont(ofSize: size, weight: .regular) } + return PlatformFont(name: "Menlo-Regular", size: size) ?? PlatformFont.systemFont(ofSize: size) case "cursive": - return UIFont(name: "SnellRoundhand", size: size) ?? UIFont.systemFont(ofSize: size) + return PlatformFont(name: "SnellRoundhand", size: size) ?? PlatformFont.systemFont(ofSize: size) case "fantasy": - return UIFont(name: "Papyrus", size: size) ?? UIFont.systemFont(ofSize: size) + return PlatformFont(name: "Papyrus", size: size) ?? PlatformFont.systemFont(ofSize: size) case "system-ui": - return UIFont.systemFont(ofSize: size) + return PlatformFont.systemFont(ofSize: size) default: - return UIFont.systemFont(ofSize: size) + return PlatformFont.systemFont(ofSize: size) } } diff --git a/Sources/SwiftUIHTML/Tag/Inline/UnderlineTag.swift b/Sources/SwiftUIHTML/Tag/Inline/UnderlineTag.swift index 2281f0c..97218b7 100644 --- a/Sources/SwiftUIHTML/Tag/Inline/UnderlineTag.swift +++ b/Sources/SwiftUIHTML/Tag/Inline/UnderlineTag.swift @@ -7,8 +7,7 @@ struct UnderlineTag: InlineTag { styleContainer.underlineStyle = .single if let underlineColor = attributes["underline-color"]?.toColor() { - styleContainer.underlineColor = UIColor(underlineColor) + styleContainer.underlineColor = PlatformColor(underlineColor) } } } - diff --git a/Sources/SwiftUIHTML/View/HTMLInline.swift b/Sources/SwiftUIHTML/View/HTMLInline.swift index 5ef2d6c..b352f0c 100644 --- a/Sources/SwiftUIHTML/View/HTMLInline.swift +++ b/Sources/SwiftUIHTML/View/HTMLInline.swift @@ -58,10 +58,7 @@ struct HTMLInline: View { case let .attachment(_, _, _, styleContainer): return result + ( Text( - Image(uiImage: attachmentManager.sizeImage( - key: type, - styleContainer: styleContainer - )) + attachmentImage(for: type, styleContainer: styleContainer) ).font(.system(size: 1)) ) } @@ -70,6 +67,22 @@ struct HTMLInline: View { } +private extension HTMLInline { + func attachmentImage(for type: TextType, styleContainer: HTMLStyleContainer) -> Image { +#if os(macOS) + return Image(nsImage: attachmentManager.sizeImage( + key: type, + styleContainer: styleContainer + )) +#else + return Image(uiImage: attachmentManager.sizeImage( + key: type, + styleContainer: styleContainer + )) +#endif + } +} + extension HTMLInline { var textLine: TextLine { let result: [HTMLStyleContainer.TextLineAttribute] = texts.compactMap { diff --git a/Sources/SwiftUIHTML/View/HTMLTextLayout/TextAttachment.swift b/Sources/SwiftUIHTML/View/HTMLTextLayout/TextAttachment.swift index eca99fb..7e44e6d 100644 --- a/Sources/SwiftUIHTML/View/HTMLTextLayout/TextAttachment.swift +++ b/Sources/SwiftUIHTML/View/HTMLTextLayout/TextAttachment.swift @@ -1,10 +1,14 @@ // Copyright © 2025 PRND. All rights reserved. +#if os(macOS) +import AppKit +#else import UIKit +#endif final class TextAttachment: NSTextAttachment { let key: AnyHashable - let font: UIFont? + let font: PlatformFont? let textLine: HTMLStyleContainer.TextLineAttribute? init( @@ -15,7 +19,7 @@ final class TextAttachment: NSTextAttachment { self.font = styleContainer.uiFont self.textLine = styleContainer.textLine super.init(data: nil, ofType: nil) - self.image = UIImage() + self.image = PlatformImage.manabiEmpty(size: .zero) } required init?(coder: NSCoder) { @@ -32,14 +36,14 @@ final class TextAttachment: NSTextAttachment { func getAdjustedSize() -> CGSize { guard let font else { return bounds.size } - let targetHeight = round(CTFontGetBoundingBox(font).maxY) + let targetHeight = round(font.manabiBoundingBoxMaxY) var adjustedHeight = bounds.size.height switch textLine { case let .lineHeight(_, lineHeight): adjustedHeight += targetHeight - lineHeight case .lineSpacing, .none: - adjustedHeight += targetHeight - font.lineHeight + adjustedHeight += targetHeight - font.manabiLineHeight } return CGSize( @@ -50,7 +54,7 @@ final class TextAttachment: NSTextAttachment { func getAdjustedOffset(point: CGPoint) -> CGPoint { guard let font else { return point } - let fontHeight = font.ascender + font.descender + let fontHeight = font.manabiFontHeight let boundHeight = bounds.size.height let lineHeight = textLine?.lineHeight ?? fontHeight @@ -65,11 +69,3 @@ final class TextAttachment: NSTextAttachment { } } - -private extension UIFont { - var fontHeight: CGFloat { - let ascent = CTFontGetAscent(self) - let descent = CTFontGetDescent(self) - return ascent + descent - } -} diff --git a/SwiftUIHTMLExample.xcodeproj/project.pbxproj b/SwiftUIHTMLExample.xcodeproj/project.pbxproj index 47dbd12..23ba08d 100644 --- a/SwiftUIHTMLExample.xcodeproj/project.pbxproj +++ b/SwiftUIHTMLExample.xcodeproj/project.pbxproj @@ -381,7 +381,7 @@ PRODUCT_BUNDLE_IDENTIFIER = prnd.co.kr.SwiftUIHTML; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -422,7 +422,7 @@ PRODUCT_BUNDLE_IDENTIFIER = prnd.co.kr.SwiftUIHTML; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/SwiftUIHTMLExampleTests/HTMLBasicTests.swift b/SwiftUIHTMLExampleTests/HTMLBasicTests.swift index fc89a27..98335a4 100644 --- a/SwiftUIHTMLExampleTests/HTMLBasicTests.swift +++ b/SwiftUIHTMLExampleTests/HTMLBasicTests.swift @@ -1,4 +1,5 @@ // Copyright © 2025 PRND. All rights reserved. +#if os(iOS) import SwiftUI import Testing @@ -455,6 +456,7 @@ class HTMLBasicTests { assertSnapshot(of: view, as: .image) } } +#endif extension UIView { diff --git a/SwiftUIHTMLExampleTests/ViewSnapshotTester.swift b/SwiftUIHTMLExampleTests/ViewSnapshotTester.swift index 9fad0c4..0bd8802 100644 --- a/SwiftUIHTMLExampleTests/ViewSnapshotTester.swift +++ b/SwiftUIHTMLExampleTests/ViewSnapshotTester.swift @@ -1,4 +1,5 @@ // Copyright © 2025 PRND. All rights reserved. +#if os(iOS) import SwiftUI import UIKit @@ -82,3 +83,4 @@ class ViewSnapshotTester { return hostingView } } +#endif From fd1ed203b73e3c21add8662ff54c2d69dc460908 Mon Sep 17 00:00:00 2001 From: Alex Ehlke Date: Wed, 31 Dec 2025 01:29:19 -0500 Subject: [PATCH 02/12] more test coverage --- Package.swift | 10 +- .../Core/Util/CSSFontUtility.swift | 8 +- Sources/SwiftUIHTML/HTMLMinifier.swift | 1 + .../SwiftUIHTMLTests/HTMLMinifierTests.swift | 176 ++++++++++++++++++ 4 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 Tests/SwiftUIHTMLTests/HTMLMinifierTests.swift diff --git a/Package.swift b/Package.swift index ce78549..5d24700 100644 --- a/Package.swift +++ b/Package.swift @@ -11,9 +11,17 @@ let package = Package( name: "SwiftUIHTML", targets: ["SwiftUIHTML"]), ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-testing.git", from: "6.2.3"), + ], targets: [ .target( name: "SwiftUIHTML", dependencies: []), + .testTarget( + name: "SwiftUIHTMLTests", + dependencies: [ + "SwiftUIHTML", + .product(name: "Testing", package: "swift-testing"), + ]), ] ) diff --git a/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift b/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift index 205efba..8c76e8f 100644 --- a/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift +++ b/Sources/SwiftUIHTML/Core/Util/CSSFontUtility.swift @@ -41,14 +41,14 @@ public enum CSSFontUtility { if let value = Double(trimmed.dropLast(2)) { return CGFloat(value) } - } else if trimmed.hasSuffix("em") { - if let value = Double(trimmed.dropLast(2)) { - return baseSize * CGFloat(value) - } } else if trimmed.hasSuffix("rem") { if let value = Double(trimmed.dropLast(3)) { return baseSize * CGFloat(value) } + } else if trimmed.hasSuffix("em") { + if let value = Double(trimmed.dropLast(2)) { + return baseSize * CGFloat(value) + } } else if trimmed.hasSuffix("%") { if let value = Double(trimmed.dropLast(1)) { return baseSize * CGFloat(value / 100) diff --git a/Sources/SwiftUIHTML/HTMLMinifier.swift b/Sources/SwiftUIHTML/HTMLMinifier.swift index d1e12ef..f031e52 100644 --- a/Sources/SwiftUIHTML/HTMLMinifier.swift +++ b/Sources/SwiftUIHTML/HTMLMinifier.swift @@ -28,6 +28,7 @@ public enum HTMLMinifier { // 4. Remove all remaining line breaks and tabs (excluding inside tags) result = result.replacingOccurrences(of: ">([^<]*?)[\\n\\r\\t]+([^<]*?)<", with: ">$1 $2<", options: .regularExpression) + result = result.replacingOccurrences(of: "\t", with: " ") return result } diff --git a/Tests/SwiftUIHTMLTests/HTMLMinifierTests.swift b/Tests/SwiftUIHTMLTests/HTMLMinifierTests.swift new file mode 100644 index 0000000..d83582f --- /dev/null +++ b/Tests/SwiftUIHTMLTests/HTMLMinifierTests.swift @@ -0,0 +1,176 @@ +// Copyright © 2025 PRND. All rights reserved. +import SwiftUI +import Testing +@testable import SwiftUIHTML + +struct HTMLMinifierTests { + @Test("Minify removes line breaks after
") + func minify_removesLineBreaksAfterBR() { + let html = "

Hello
\nWorld

" + let result = HTMLMinifier.minify(html) + #expect(result == "

Hello
World

") + } + + @Test("Minify removes tag-only newlines") + func minify_removesNewlinesBetweenTags() { + let html = "

One

\n

Two

" + let result = HTMLMinifier.minify(html) + #expect(result == "

One

Two

") + } + + @Test("Minify replaces pure whitespace between tags with  ") + func minify_replacesWhitespaceBetweenTagsWithNbsp() { + let html = "
" + let result = HTMLMinifier.minify(html) + #expect(result == "
   
") + } + + @Test("Minify replaces inline line breaks with a space") + func minify_replacesInlineLineBreaks() { + let html = "

Hello\nWorld\tTab

" + let result = HTMLMinifier.minify(html) + #expect(result == "

Hello World Tab

") + } + + @Test("Minify preserves short input", arguments: ["", "a", "ab", "abc"]) + func minify_preservesShortInput(_ html: String) { + #expect(HTMLMinifier.minify(html) == html) + } +} + +struct CSSFontUtilityTests { + @Test( + "parseSize handles keywords and units", + arguments: [ + ("large", 10.0, 12.0), + ("16px", 10.0, 16.0), + ("12pt", 10.0, 12.0), + ("1.5em", 10.0, 15.0), + ("2rem", 10.0, 20.0), + ("150%", 10.0, 15.0), + ("0.5", 10.0, 0.5) + ] + ) + func parseSize_handlesKeywordAndUnits(_ fontSize: String, baseSize: Double, expected: Double) { + let result = Double(CSSFontUtility.parseSize(fromFontSize: fontSize, baseSize: baseSize)) + #expect(abs(result - expected) < 0.0001) + } +} + +struct AttributeValueTests { + @Test("AttributeValue parses numeric and boolean helpers") + func attributeValue_parsingHelpers() { + #expect(AttributeValue(rawValue: "12px").int == 12) + #expect(AttributeValue(rawValue: "12px").float == 12) + #expect(AttributeValue(rawValue: "infinity").cgFloat == .infinity) + #expect(AttributeValue(rawValue: "true").toBool() == true) + #expect(AttributeValue(rawValue: "0").toBool() == false) + } + + @Test("AttributeValue parses URL and CSS style") + func attributeValue_urlAndCssStyle() { + #expect(AttributeValue(rawValue: "https://example.com").url?.host == "example.com") + let style = AttributeValue(rawValue: "color: red; line-height: 20").cssStyle + #expect(style?["color"]?.string == "red") + #expect(style?["line-height"]?.cgFloat == 20) + } +} + +struct CSSStyleTests { + @Test("CSSStyle handles empty and whitespace-only inputs") + func cssStyle_emptyString() { + #expect(CSSStyle(style: "") == nil) + let whitespaceStyle = CSSStyle(style: " ") + #expect(whitespaceStyle != nil) + #expect(whitespaceStyle?[""]?.string == "") + } +} + +struct EdgeInsetsParsingTests { + @Test("EdgeInsets parses 1-4 value CSS shorthands") + func edgeInsets_cssShorthand() { + let one = EdgeInsets(cssString: "10px") + #expect(one?.top == 10) + #expect(one?.leading == 10) + #expect(one?.bottom == 10) + #expect(one?.trailing == 10) + + let two = EdgeInsets(cssString: "10px 20px") + #expect(two?.top == 10) + #expect(two?.leading == 20) + #expect(two?.bottom == 10) + #expect(two?.trailing == 20) + + let three = EdgeInsets(cssString: "10px 20px 30px") + #expect(three?.top == 10) + #expect(three?.leading == 20) + #expect(three?.bottom == 30) + #expect(three?.trailing == 20) + + let four = EdgeInsets(cssString: "10px 20px 30px 40px") + #expect(four?.top == 10) + #expect(four?.leading == 40) + #expect(four?.bottom == 30) + #expect(four?.trailing == 20) + } +} + +struct ColorParsingTests { + @Test("Color parses hex, rgb, and named values") + func color_cssString() { + #expect(Color(cssString: "#ff0000") != nil) + #expect(Color(cssString: "rgb(255, 0, 0)") != nil) + #expect(Color(cssString: "red") != nil) + #expect(Color(cssString: "not-a-color") == nil) + } +} + +struct LineBreakModeTests { + @Test("Custom line-break modes compare by id") + func lineBreakMode_customEquality() { + let left = LineBreakMode.custom(id: "alpha", transform: { $0 }) + let right = LineBreakMode.custom(id: "alpha", transform: { $0 + "x" }) + let other = LineBreakMode.custom(id: "beta", transform: { $0 }) + #expect(left == right) + #expect(left != other) + } + + @Test("LineBreakMode description is stable") + func lineBreakMode_description() { + #expect(LineBreakMode.byWordWrapping.description == "byWordWrapping") + #expect(LineBreakMode.byCharWrapping.description == "byCharWrapping") + #expect(LineBreakMode.custom(id: "custom-id", transform: { $0 }).description == "custom-id") + } + + @Test("LineBreakMode callAsFunction applies byCharWrapping") + func lineBreakMode_callAsFunction() { + let result = LineBreakMode.byCharWrapping("ab") + #expect(result == "a\u{200B}b") + } +} + +struct HTMLConfigurationTests { + private struct TestInlineTag: InlineTag { + init() {} + static func applyStyles(with attributes: [String : AttributeValue], to styleContainer: inout HTMLStyleContainer) { + styleContainer.lineBreakMode = .byCharWrapping + } + } + + @MainActor + @Test("HTMLConfiguration register/remove updates tag type and styles") + func configuration_registerRemove() { + let config = HTMLConfiguration() + .removeAll() + .register(tag: "test-inline", renderer: TestInlineTag.self) + + #expect(config.tagType(of: "test-inline") == .inline) + + var container = HTMLStyleContainer() + config.applyStyles(tag: "test-inline", attributes: [:], to: &container) + #expect(container.lineBreakMode == .byCharWrapping) + + let removed = config.remove(tag: "test-inline") + #expect(removed.tagType(of: "test-inline") == nil) + } +} From f02de02660a27d4c13e2c0b60b1e8d912d35f1b3 Mon Sep 17 00:00:00 2001 From: Alex Ehlke Date: Thu, 1 Jan 2026 02:51:19 -0500 Subject: [PATCH 03/12] ruby tags and various optimizations --- Documentation/BasicUsage.md | 97 ++++- Documentation/CustomTags.md | 16 +- Documentation/ParserIntegration.md | 41 +- Documentation/Styling.md | 44 ++- Example/ContentView.swift | 10 +- .../Custom/HTMLStyleContainer+sample.swift | 5 + .../Custom/TagView/HeadinglevelTagView.swift | 5 + .../Samples/BasicUsage/ImageLinkSample.swift | 5 + .../Samples/BasicUsage/LineBreakSample.swift | 5 + .../Samples/BasicUsage/RubyTagSample.swift | 73 ++++ .../ParserIntegration/ComplexHTMLSample.swift | 5 + .../SwiftSoupParserSample.swift | 71 ++++ .../QuickStart/RichContentSample.swift | 5 + .../Samples/Styling/LineHeightSample.swift | 5 + .../Samples/Testing/SnapshotTestSample.swift | 5 + .../Testing/SyntheticStressSample.swift | 167 +++++++++ Example/SwiftUIHTMLExampleApp.swift | 14 + Package.resolved | 51 +++ Package.swift | 2 + README.md | 45 ++- README_ko.md | 45 ++- .../Attachment/AttachmentLayoutManager.swift | 94 +++-- .../Core/Attachment/AttachmentManager.swift | 10 +- .../DefaultAttributeStyle.swift | 2 +- .../AttributeStyle/LayoutStyleModifier.swift | 20 +- .../EffectsModifier.swift | 6 +- .../CoreText/TextRangeFrameCalculator.swift | 135 ++++++- .../Core/Element/InlineElement.swift | 9 +- .../HTMLConfiguration/HTMLConfiguration.swift | 3 +- Sources/SwiftUIHTML/Core/Node/HTMLNode.swift | 115 +++++- .../Core/Platform/PlatformTypes.swift | 2 + Sources/SwiftUIHTML/HTMLMinifier.swift | 27 +- Sources/SwiftUIHTML/Tag/Inline/RubyTag.swift | 256 +++++++++++++ .../ValueType/AttributeValue.swift | 2 +- Sources/SwiftUIHTML/ValueType/CSSStyle.swift | 57 ++- .../SwiftUIHTML/ValueType/LineBreakMode.swift | 2 +- Sources/SwiftUIHTML/View/HTMLBlock.swift | 9 +- Sources/SwiftUIHTML/View/HTMLInline.swift | 61 ++- .../View/HTMLTextLayout/HTMLTextLayout.swift | 8 +- Sources/SwiftUIHTML/View/HTMLView.swift | 36 +- Tests/SwiftUIHTMLTests/PerformanceTests.swift | 352 ++++++++++++++++++ Tests/SwiftUIHTMLTests/RubyTagTests.swift | 288 ++++++++++++++ .../SwiftSoupParserTests.swift | 192 ++++++++++ 43 files changed, 2256 insertions(+), 146 deletions(-) create mode 100644 Example/Samples/BasicUsage/RubyTagSample.swift create mode 100644 Example/Samples/ParserIntegration/SwiftSoupParserSample.swift create mode 100644 Example/Samples/Testing/SyntheticStressSample.swift create mode 100644 Package.resolved create mode 100644 Sources/SwiftUIHTML/Tag/Inline/RubyTag.swift create mode 100644 Tests/SwiftUIHTMLTests/PerformanceTests.swift create mode 100644 Tests/SwiftUIHTMLTests/RubyTagTests.swift create mode 100644 Tests/SwiftUIHTMLTests/SwiftSoupParserTests.swift diff --git a/Documentation/BasicUsage.md b/Documentation/BasicUsage.md index d49e91c..a03119b 100644 --- a/Documentation/BasicUsage.md +++ b/Documentation/BasicUsage.md @@ -61,7 +61,56 @@ struct TextStyleExample: View { } ``` -## 3. 이미지 삽입 / Image Embedding +## 3. 루비 주석 / Ruby Annotations + +### 한글 설명 +`ruby`와 `rt` 태그를 사용해 일본어 루비 주석을 렌더링할 수 있습니다. `rt` 텍스트는 루비 문자열로 사용되고 `rp`/`rtc`는 무시됩니다. 필요하면 `ruby-position`, `ruby-scale`, `ruby-font-name`, `ruby-font-size`, `ruby-annotation-font-*` 속성으로 조정할 수 있습니다. + +### English +You can render ruby annotations using `ruby` and `rt`. The `rt` text becomes the ruby string, and `rp`/`rtc` are ignored. Optional attributes include `ruby-position`, `ruby-scale`, `ruby-font-name`, `ruby-font-size`, and `ruby-annotation-font-*`. + +```swift +struct RubyExample: View { + let html = """ +

루비 주석 예제:

+

+ + 漢字かんじ + + 를 읽습니다. +

+

+ + 今日きょう + + 는 맑습니다. +

+

+ + 明日あした + + 도 맑습니다. +

+ """ + + var body: some View { + HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .default) + .htmlEnvironment(\.styleContainer, { + var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 18) +#else + let font = UIFont.systemFont(ofSize: 18) +#endif + container.uiFont = font + return container + }()) + } +} +``` + +## 4. 이미지 삽입 / Image Embedding ### 한글 설명 HTML 내에 이미지를 삽입하는 방법입니다. 웹 URL이나 로컬 이미지를 모두 지원합니다. @@ -92,7 +141,7 @@ struct ImageExample: View { } ``` -## 4. 링크 처리 / Link Handling +## 5. 링크 처리 / Link Handling ### 한글 설명 클릭 가능한 링크를 생성하는 방법입니다. 링크 태그는 자동으로 처리됩니다. @@ -119,7 +168,7 @@ struct LinkExample: View { } ``` -## 5. 블록 요소 구조 / Block Element Structure +## 6. 블록 요소 구조 / Block Element Structure ### 한글 설명 다양한 블록 요소들을 사용하는 방법입니다. div, p, section 등의 기본 블록 태그를 활용합니다. @@ -153,7 +202,7 @@ struct BlockElementExample: View { } ``` -## 6. 헤더와 섹션 구조 / Headers and Section Structure +## 7. 헤더와 섹션 구조 / Headers and Section Structure ### 한글 설명 HTML 문서 구조를 만드는 방법입니다. header, main, section, footer 태그를 활용합니다. @@ -197,7 +246,7 @@ struct DocumentStructureExample: View { } ``` -## 7. 줄바꿈 처리 / Line Break Handling +## 8. 줄바꿈 처리 / Line Break Handling ### 한글 설명 텍스트 줄바꿈을 처리하는 방법입니다. br 태그와 줄바꿈 모드를 설정할 수 있습니다. @@ -249,13 +298,13 @@ struct LineBreakExample: View { } ``` -## 8. 폰트 설정 / Font Configuration +## 9. 폰트 설정 / Font Configuration ### 한글 설명 -폰트 크기와 스타일을 설정하는 방법입니다. UIFont를 사용하여 시스템 폰트나 커스텀 폰트를 적용할 수 있습니다. +폰트 크기와 스타일을 설정하는 방법입니다. iOS는 UIFont, macOS는 NSFont를 사용하여 시스템 폰트나 커스텀 폰트를 적용할 수 있습니다. ### English -How to configure font size and style. You can apply system fonts or custom fonts using UIFont. +How to configure font size and style. Use UIFont on iOS and NSFont on macOS to apply system or custom fonts. ```swift struct FontExample: View { @@ -274,7 +323,12 @@ struct FontExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - container.uiFont = .systemFont(ofSize: 14) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else + let font = UIFont.systemFont(ofSize: 14) +#endif + container.uiFont = font return container }()) @@ -285,7 +339,12 @@ struct FontExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - container.uiFont = .systemFont(ofSize: 18) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 18) +#else + let font = UIFont.systemFont(ofSize: 18) +#endif + container.uiFont = font return container }()) } @@ -294,7 +353,7 @@ struct FontExample: View { } ``` -## 9. 줄 높이 설정 / Line Height Configuration +## 10. 줄 높이 설정 / Line Height Configuration ### 한글 설명 텍스트 줄 높이를 조정하는 방법입니다. 가독성을 높이기 위해 줄 간격을 설정할 수 있습니다. @@ -319,7 +378,11 @@ struct LineHeightExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else let font = UIFont.systemFont(ofSize: 14) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 18) return container @@ -331,7 +394,11 @@ struct LineHeightExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else let font = UIFont.systemFont(ofSize: 14) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 28) return container @@ -343,7 +410,7 @@ struct LineHeightExample: View { } ``` -## 10. 복합 예제 / Complex Example +## 11. 복합 예제 / Complex Example ### 한글 설명 여러 기능을 함께 사용하는 복합 예제입니다. @@ -398,7 +465,11 @@ struct ComplexExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 16) +#else let font = UIFont.systemFont(ofSize: 16) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 24) container.lineBreakMode = .byWordWrapping @@ -408,4 +479,4 @@ struct ComplexExample: View { } } } -``` \ No newline at end of file +``` diff --git a/Documentation/CustomTags.md b/Documentation/CustomTags.md index 35ab350..cda668f 100644 --- a/Documentation/CustomTags.md +++ b/Documentation/CustomTags.md @@ -3,6 +3,9 @@ SwiftUIHTML에서 커스텀 태그를 만들고 등록하는 방법을 소개합니다. This guide introduces how to create and register custom tags in SwiftUIHTML. +> 참고: `ruby`는 기본 InlineAttachmentTag로 제공되며 별도 등록이 필요 없습니다. +> Note: `ruby` is built-in as an InlineAttachmentTag and does not need custom registration. + ## 태그 타입 / Tag Types ### 한글 설명 @@ -17,6 +20,17 @@ SwiftUIHTML supports three types of tags: - **InlineTag**: Inline text elements (span, strong, em, etc.) - **InlineAttachmentTag**: Inline attachment elements (img, video, etc.) +### Built-in ruby example / 기본 ruby 예제 + +```swift +let html = """ +

+ 漢字かんじ + 를 읽습니다. +

+ """ +``` + ## 1. 커스텀 블록 태그 / Custom Block Tag ### 한글 설명 @@ -403,4 +417,4 @@ struct ContentView: View { - Custom tags can override default tags - BlockTag must implement the View protocol - InlineTag only applies styles; rendering is handled by the system -- InlineAttachmentTag creates views that are inserted inline \ No newline at end of file +- InlineAttachmentTag creates views that are inserted inline diff --git a/Documentation/ParserIntegration.md b/Documentation/ParserIntegration.md index 5881108..6a69116 100644 --- a/Documentation/ParserIntegration.md +++ b/Documentation/ParserIntegration.md @@ -3,6 +3,25 @@ SwiftUIHTML은 HTMLParserable 프로토콜을 통해 다양한 HTML 파서와 통합할 수 있습니다. SwiftUIHTML can integrate with various HTML parsers through the HTMLParserable protocol. +> Note: For ruby annotations, parsers should keep `` as a node with child `` nodes; the renderer extracts base/ruby text from those children. + +### Ruby parsing example / Ruby 파싱 예제 + +```swift +// Input: +// 漢字かんじ +// +// Expected node shape: +let rubyNode = HTMLNode( + tag: "ruby", + attributes: [:], + children: [ + .text("漢字"), + .node(HTMLNode(tag: "rt", attributes: [:], children: [.text("かんじ")])) + ] +) +``` + ## HTMLParserable 프로토콜 / HTMLParserable Protocol ### 한글 설명 @@ -28,8 +47,6 @@ struct HTMLNode { enum HTMLChild { case text(String) - case trimmingText(String) - case newLine case node(HTMLNode) } ``` @@ -89,11 +106,11 @@ struct HTMLFuziParser: HTMLParserable { .compactMap { node -> HTMLChild? in if node.type == .Text { let text = node.stringValue - return text.isEmpty ? nil : .trimmingText(text) + return text.isEmpty ? nil : .text(text) } else if let childElement = node.toElement() { // br 태그는 특별 처리 if childElement.tag == "br" { - return .newLine + return .text("\n") } return .node(try elementToHTMLNode(element: childElement)) } @@ -139,10 +156,16 @@ SwiftSoup is a Swift port of Java's JSoup HTML parser. It supports CSS selectors ```swift dependencies: [ - .package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.0.0") + // Latest mainline (SwiftSoup default branch is `master`). + .package(url: "https://github.com/scinfu/SwiftSoup.git", branch: "master") + // Or pin to the latest stable tag: + // .package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.11.3") ] ``` +> 참고: SwiftSoup의 기본 브랜치는 현재 `master`입니다. 최신 메인라인을 추적하려면 `branch: "master"`를 사용하세요. 안정 버전은 최신 릴리스를 사용하세요(현재 2.11.3). +> Note: SwiftSoup's default branch is currently `master`. Use `branch: "master"` to track the latest mainline. For stable builds, pin the latest release (currently 2.11.3). + ### 구현 / Implementation ```swift @@ -179,11 +202,11 @@ struct HTMLSwiftSoupParser: HTMLParserable { let children: [HTMLChild] = try element.getChildNodes().compactMap { node in if let textNode = node as? TextNode { let text = textNode.text() - return text.isEmpty ? nil : .trimmingText(text) + return text.isEmpty ? nil : .text(text) } else if let elementNode = node as? Element { // br 태그는 특별 처리 if elementNode.tagName() == "br" { - return .newLine + return .text("\n") } return .node(try elementToHTMLNode(element: elementNode)) } @@ -217,6 +240,8 @@ struct ContentView: View { } ``` +> Example app note: The SwiftUIHTML Example app includes a SwiftSoup parser sample that appears when SwiftSoup is linked into the target (guarded by `#if canImport(SwiftSoup)`). + --- ## 3. 커스텀 파서 구현 / Custom Parser Implementation @@ -426,4 +451,4 @@ extension String { return result } } -``` \ No newline at end of file +``` diff --git a/Documentation/Styling.md b/Documentation/Styling.md index 9666236..77a412c 100644 --- a/Documentation/Styling.md +++ b/Documentation/Styling.md @@ -143,7 +143,11 @@ struct StyleContainerExample: View { func smallStyleContainer() -> HTMLStyleContainer { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 12) +#else let font = UIFont.systemFont(ofSize: 12) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 16) container.lineBreakMode = .byWordWrapping @@ -152,7 +156,11 @@ struct StyleContainerExample: View { func largeStyleContainer() -> HTMLStyleContainer { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 18) +#else let font = UIFont.systemFont(ofSize: 18) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 28) container.lineBreakMode = .byWordWrapping @@ -188,7 +196,11 @@ struct LineSpacingExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else let font = UIFont.systemFont(ofSize: 14) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 18) return container @@ -201,7 +213,11 @@ struct LineSpacingExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else let font = UIFont.systemFont(ofSize: 14) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 24) return container @@ -214,7 +230,12 @@ struct LineSpacingExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() - container.uiFont = .systemFont(ofSize: 14) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else + let font = UIFont.systemFont(ofSize: 14) +#endif + container.uiFont = font container.textLine = .lineSpacing(spacing: 8) return container }()) @@ -255,7 +276,12 @@ struct LineBreakModeExample: View { .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() container.lineBreakMode = .byWordWrapping - container.uiFont = .systemFont(ofSize: 14) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else + let font = UIFont.systemFont(ofSize: 14) +#endif + container.uiFont = font return container }()) .frame(width: 250) @@ -268,7 +294,12 @@ struct LineBreakModeExample: View { .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() container.lineBreakMode = .byCharWrapping - container.uiFont = .systemFont(ofSize: 14) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else + let font = UIFont.systemFont(ofSize: 14) +#endif + container.uiFont = font return container }()) .frame(width: 250) @@ -471,7 +502,11 @@ struct ComplexStyleExample: View { .htmlEnvironment(\.configuration, .default) .htmlEnvironment(\.styleContainer, { var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 14) +#else let font = UIFont.systemFont(ofSize: 14) +#endif container.uiFont = font container.textLine = .lineHeight(font: font, lineHeight: 20) return container @@ -534,4 +569,5 @@ struct StyleInheritanceExample: View { - Not all CSS properties are supported. Use only the properties listed above. - HTMLStyleContainer is a global setting that affects all elements. - Inline styles have higher priority than HTMLStyleContainer settings. -- Colors can use hex codes (#RRGGBB) or color names (red, blue, etc.). \ No newline at end of file +- Colors can use hex codes (#RRGGBB) or color names (red, blue, etc.). +- For performance testing, use the Example app’s Testing section and the "Synthetic Stress" sample with higher section counts. diff --git a/Example/ContentView.swift b/Example/ContentView.swift index 04a66bd..465cbfd 100644 --- a/Example/ContentView.swift +++ b/Example/ContentView.swift @@ -40,6 +40,7 @@ enum SampleCategory: String, CaseIterable, Identifiable { SampleItem(title: "Text Styling", description: "Bold, italic, underline", view: TextStyleSample()), SampleItem(title: "Image & Links", description: "Images and links example", view: ImageLinkSample()), SampleItem(title: "Block Elements", description: "Block element structure", view: BlockElementSample()), + SampleItem(title: "Ruby Tags", description: "Ruby annotations with rt", view: RubyTagSample()), SampleItem(title: "Line Break Modes", description: "Line break mode comparison", view: LineBreakSample()) ] case .styling: @@ -57,13 +58,18 @@ enum SampleCategory: String, CaseIterable, Identifiable { SampleItem(title: "Highlight Tag", description: "Highlight inline tag", view: HighlightTagSample()) ] case .parserIntegration: - return [ + var items: [SampleItem] = [ SampleItem(title: "Current Parser", description: "Currently used Fuzi parser", view: AnyView(CurrentParserSample())), SampleItem(title: "Complex HTML", description: "Complex HTML structure parsing", view: AnyView(ComplexHTMLSample())) ] +#if canImport(SwiftSoup) + items.append(SampleItem(title: "SwiftSoup Parser", description: "Parser integration sample", view: AnyView(SwiftSoupParserSample()))) +#endif + return items case .testing: return [ - SampleItem(title: "Snapshot Test Preview", description: "Snapshot test preview - Image positioning and line height", view: SnapshotTestSample()) + SampleItem(title: "Snapshot Test Preview", description: "Snapshot test preview - Image positioning and line height", view: SnapshotTestSample()), + SampleItem(title: "Synthetic Stress", description: "Large synthetic HTML payload", view: SyntheticStressSample()) ] } } diff --git a/Example/Custom/HTMLStyleContainer+sample.swift b/Example/Custom/HTMLStyleContainer+sample.swift index c00b232..bc048a5 100644 --- a/Example/Custom/HTMLStyleContainer+sample.swift +++ b/Example/Custom/HTMLStyleContainer+sample.swift @@ -3,9 +3,14 @@ import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif import SwiftUIHTML diff --git a/Example/Custom/TagView/HeadinglevelTagView.swift b/Example/Custom/TagView/HeadinglevelTagView.swift index 65ffef9..31a7cee 100644 --- a/Example/Custom/TagView/HeadinglevelTagView.swift +++ b/Example/Custom/TagView/HeadinglevelTagView.swift @@ -4,9 +4,14 @@ import SwiftUI import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif import SwiftUIHTML diff --git a/Example/Samples/BasicUsage/ImageLinkSample.swift b/Example/Samples/BasicUsage/ImageLinkSample.swift index 3466e82..82092bf 100644 --- a/Example/Samples/BasicUsage/ImageLinkSample.swift +++ b/Example/Samples/BasicUsage/ImageLinkSample.swift @@ -6,9 +6,14 @@ import SwiftUIHTML import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif struct ImageLinkSample: View { @State private var htmlCode = """ diff --git a/Example/Samples/BasicUsage/LineBreakSample.swift b/Example/Samples/BasicUsage/LineBreakSample.swift index fb05629..dedb62e 100644 --- a/Example/Samples/BasicUsage/LineBreakSample.swift +++ b/Example/Samples/BasicUsage/LineBreakSample.swift @@ -6,9 +6,14 @@ import SwiftUIHTML import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif struct LineBreakSample: View { @State private var htmlCode = """ diff --git a/Example/Samples/BasicUsage/RubyTagSample.swift b/Example/Samples/BasicUsage/RubyTagSample.swift new file mode 100644 index 0000000..2e6f272 --- /dev/null +++ b/Example/Samples/BasicUsage/RubyTagSample.swift @@ -0,0 +1,73 @@ +// Copyright © 2025 PRND. All rights reserved. + +import SwiftUI +import SwiftUIHTML + +struct RubyTagSample: View { + private let html = """ +

Ruby annotation rendering:

+

+ + 漢字かんじ + + を読む +

+

+ + 学校がっこう + + へ行く +

+

+ + 今日きょう + + は晴れです +

+ """ + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading, spacing: 8) { + Text("Ruby Annotations") + .font(.largeTitle) + .fontWeight(.bold) + + Text("Basic / support using CoreText") + .font(.subheadline) + .foregroundColor(.secondary) + } + + Divider() + + HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) + .padding() + .background(Color.platformSystemGray6) + .cornerRadius(8) + + VStack(alignment: .leading, spacing: 8) { + Text("HTML") + .font(.headline) + + Text(html) + .font(.system(.body, design: .monospaced)) + .padding() + .background(Color.platformSystemGray6) + .cornerRadius(8) + } + } + .padding() + } + .navigationTitle("Ruby Tags") + .applyInlineNavigationTitleDisplayMode() + } +} + +#Preview { + NavigationView { + RubyTagSample() + } +} diff --git a/Example/Samples/ParserIntegration/ComplexHTMLSample.swift b/Example/Samples/ParserIntegration/ComplexHTMLSample.swift index 13388c8..09fb07e 100644 --- a/Example/Samples/ParserIntegration/ComplexHTMLSample.swift +++ b/Example/Samples/ParserIntegration/ComplexHTMLSample.swift @@ -6,9 +6,14 @@ import SwiftUIHTML import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif struct ComplexHTMLSample: View { let html = """ diff --git a/Example/Samples/ParserIntegration/SwiftSoupParserSample.swift b/Example/Samples/ParserIntegration/SwiftSoupParserSample.swift new file mode 100644 index 0000000..8d0a9c4 --- /dev/null +++ b/Example/Samples/ParserIntegration/SwiftSoupParserSample.swift @@ -0,0 +1,71 @@ +// Copyright © 2026 PRND. All rights reserved. + +#if canImport(SwiftSoup) +import SwiftUI +import SwiftSoup +import SwiftUIHTML + +struct SwiftSoupParserSample: View { + private let html = """ +
+

SwiftSoup Parser

+

Parsed with SwiftSoup and converted to HTMLNode.

+

Ruby: 漢字かんじ

+

Line break
after br tag.

+
+ """ + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + Text("SwiftSoup Parser") + .font(.title2) + .fontWeight(.semibold) + + HTMLView(html: html, parser: HTMLSwiftSoupParser()) + .htmlEnvironment(\.configuration, .sample) + } + .padding() + .navigationTitle("SwiftSoup Parser") + .applyInlineNavigationTitleDisplayMode() + } +} + +private struct HTMLSwiftSoupParser: HTMLParserable { + func parse(html: String) -> HTMLNode { + do { + let document = try SwiftSoup.parse(html) + if let body = document.body() { + return try elementToHTMLNode(element: body) + } else if let wrapper = try document.select("*").first() { + return try elementToHTMLNode(element: wrapper) + } + } catch { + return HTMLNode(tag: "div", attributes: [:], children: [.text("Parse error: \(error.localizedDescription)")]) + } + return HTMLNode(tag: "div", attributes: [:], children: []) + } + + private func elementToHTMLNode(element: SwiftSoup.Element) throws -> HTMLNode { + let tag = element.tagName() + let attributes = element.getAttributes()?.reduce(into: [String: String]()) { result, attribute in + result[attribute.getKey()] = attribute.getValue() + } ?? [:] + + let children: [HTMLChild] = try element.getChildNodes().compactMap { node in + if let textNode = node as? TextNode { + let text = textNode.text() + return text.isEmpty ? nil : .text(text) + } + if let elementNode = node as? Element { + if elementNode.tagName() == "br" { + return .text("\n") + } + return .node(try elementToHTMLNode(element: elementNode)) + } + return nil + } + + return HTMLNode(tag: tag, attributes: attributes, children: children) + } +} +#endif diff --git a/Example/Samples/QuickStart/RichContentSample.swift b/Example/Samples/QuickStart/RichContentSample.swift index f44cd2f..ad7405b 100644 --- a/Example/Samples/QuickStart/RichContentSample.swift +++ b/Example/Samples/QuickStart/RichContentSample.swift @@ -6,9 +6,14 @@ import SwiftUIHTML import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif struct RichContentSample: View { let html = """ diff --git a/Example/Samples/Styling/LineHeightSample.swift b/Example/Samples/Styling/LineHeightSample.swift index 5597555..9e2d5a5 100644 --- a/Example/Samples/Styling/LineHeightSample.swift +++ b/Example/Samples/Styling/LineHeightSample.swift @@ -6,9 +6,14 @@ import SwiftUIHTML import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif struct LineHeightSample: View { let longText = """ diff --git a/Example/Samples/Testing/SnapshotTestSample.swift b/Example/Samples/Testing/SnapshotTestSample.swift index aca9d2a..1019b49 100644 --- a/Example/Samples/Testing/SnapshotTestSample.swift +++ b/Example/Samples/Testing/SnapshotTestSample.swift @@ -5,9 +5,14 @@ import SwiftUI import AppKit private typealias ExampleFont = NSFont #else +#if os(macOS) +import AppKit +private typealias ExampleFont = NSFont +#else import UIKit private typealias ExampleFont = UIFont #endif +#endif import SwiftUIHTML diff --git a/Example/Samples/Testing/SyntheticStressSample.swift b/Example/Samples/Testing/SyntheticStressSample.swift new file mode 100644 index 0000000..14bf18e --- /dev/null +++ b/Example/Samples/Testing/SyntheticStressSample.swift @@ -0,0 +1,167 @@ +// Copyright © 2025 PRND. All rights reserved. + +import SwiftUI +import SwiftUIHTML + +struct SyntheticStressSample: View { + @State private var sectionCount: Int = 12 + @State private var includeMedia: Bool = true + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading, spacing: 8) { + Text("Synthetic Stress Sample") + .font(.largeTitle) + .fontWeight(.bold) + + Text("Generates large HTML that exercises most tags and styles.") + .font(.subheadline) + .foregroundColor(.secondary) + } + + Divider() + + VStack(alignment: .leading, spacing: 12) { + HStack { + Text("Sections: \(sectionCount)") + Spacer() + } + .font(.headline) + + Slider(value: Binding( + get: { Double(sectionCount) }, + set: { sectionCount = Int($0) } + ), in: 4...30, step: 1) + + Toggle("Include media (img/video)", isOn: $includeMedia) + } + .padding() + .background(Color.platformSystemGray6) + .cornerRadius(8) + + HTMLView(html: syntheticHTML(sectionCount: sectionCount, includeMedia: includeMedia), parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) + .padding() + .background(Color.platformSystemGray6) + .cornerRadius(8) + } + .padding() + } + .navigationTitle("Synthetic Stress") + .applyInlineNavigationTitleDisplayMode() + } +} + +private extension SyntheticStressSample { + func syntheticHTML(sectionCount: Int, includeMedia: Bool) -> String { + var chunks: [String] = [] + chunks.reserveCapacity(sectionCount * 6) + + chunks.append(""" + +
+

SwiftUIHTML Stress Test

+

Synthetic HTML payload exercising tags, CSS, and attachments.

+

+ Inline styling: font-size, + background + radius, + font-family. +

+
+
+ """) + + chunks.append(""" +
+

Feature Overview

+

+ Text styles with nested emphasis and + inline color. + Inline background + with underline and bold. +

+

+ Ruby variants: + 表現ひょうげん, + 漢字かんじ(ignored), + 東京とうきょう, + 可愛いかわいい. +

+
+ """) + + for index in 1...sectionCount { + let mediaHTML: String + if includeMedia { + mediaHTML = """ +

+ +

+ """ + } else { + mediaHTML = "" + } + + chunks.append(""" +
+

Section \(index)

+

Subtitle \(index)

+

+ Inline styles: red, + yellow, + bold, italic, underline. +

+

+ Font family mix: + Georgia 14px, + Courier 12px, + Size 16px. +

+

+ LongWord\(index)WithNoBreaksToExerciseLineWrappingAcrossTheLayoutEngine. +

+

+ Ruby: 漢字かんじ, + 現代語げんだいご, + 新年しんねん. +

+

+ Link: example.com/\(index) +

+
    +
  • Item \(index).1
  • +
  • Item \(index).2 with color
  • +
  • Item \(index).3 with bold and italic
  • +
+
+

+ Nested block with padding, margin, border, and background. + Inline badge + + green text. +

+
+ \(mediaHTML) +
+ """) + } + + chunks.append(""" +
+
+

Footer content for synthetic payload.

+
+ + """) + + return chunks.joined(separator: "\n") + } +} + +#Preview { + NavigationView { + SyntheticStressSample() + } +} diff --git a/Example/SwiftUIHTMLExampleApp.swift b/Example/SwiftUIHTMLExampleApp.swift index b48cb51..9b88e37 100644 --- a/Example/SwiftUIHTMLExampleApp.swift +++ b/Example/SwiftUIHTMLExampleApp.swift @@ -8,6 +8,20 @@ import SwiftUI struct SwiftUIHTMLExampleApp: App { var body: some Scene { WindowGroup { + RootView() + } + } +} + +private struct RootView: View { + private let stressOnly = ProcessInfo.processInfo.environment["SWIFTUIHTML_STRESS_ONLY"] == "1" + + var body: some View { + if stressOnly { + NavigationView { + SyntheticStressSample() + } + } else { ContentView() } } diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..20d9e26 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,51 @@ +{ + "originHash" : "49da0d5ac5ce63a88290fb541cb70aef6bb2c4b47141025062eb15740b8dbb2f", + "pins" : [ + { + "identity" : "lrucache", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nicklockwood/LRUCache.git", + "state" : { + "revision" : "0d91406ecd4d6c1c56275866f00508d9aeacc92a", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax.git", + "state" : { + "revision" : "4799286537280063c85a32f09884cfbca301b1a1", + "version" : "602.0.0" + } + }, + { + "identity" : "swift-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-testing.git", + "state" : { + "revision" : "48a471ab313e858258ab0b9b0bf2cea55a50cefb", + "version" : "6.2.3" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup.git", + "state" : { + "branch" : "master", + "revision" : "cd950c96d1d2ae53068828f1f394178afbc4d83a" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift index 5d24700..14190ef 100644 --- a/Package.swift +++ b/Package.swift @@ -13,6 +13,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/swiftlang/swift-testing.git", from: "6.2.3"), + .package(url: "https://github.com/scinfu/SwiftSoup.git", branch: "master"), ], targets: [ .target( @@ -22,6 +23,7 @@ let package = Package( dependencies: [ "SwiftUIHTML", .product(name: "Testing", package: "swift-testing"), + .product(name: "SwiftSoup", package: "SwiftSoup"), ]), ] ) diff --git a/README.md b/README.md index ed1a178..ad0b166 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ - **CSS Style Support**: Full inline style support (padding, margin, background, border, etc.) - **Flexible Parser Integration**: Works with external parsers like Fuzi and SwiftSoup - **Environment Value System**: Global configuration and style customization +- **Profiling Hooks**: Optional signposts for HTML parsing via `SWIFTUIHTML_SIGNPOSTS=1` --- @@ -32,16 +33,19 @@ |----------|------| | **Block** | `div`, `body`, `p`, `header`, `main`, `section`, `footer`, `h1`, `h2` | | **Inline** | `span`, `a`, `b`, `strong`, `i`, `em`, `u` | -| **Attachment** | `img` | +| **Attachment** | `img`, `ruby` | > Note: Tags like h3, ul, video can be registered as custom tags. +> Note: `ruby` renders `` annotations via CoreText. `` text is used for the ruby string; ``/`` are ignored. + ### CSS Style Properties - **Text Styles**: `color`, `background-color`, `font-family`, `font-size`, `line-height`, `word-break` - **Block Layout**: `padding`, `margin`, `border`, `border-radius` (block elements only: div, p, section, etc.) - **Inline Styles**: `color`, `background-color`, `border-radius` (inline elements: strong, em, span, etc.) > **Note**: `padding` and `margin` are not supported for inline elements (span, strong, em, etc.). +> **Ruby options**: `ruby-position` (`before`, `after`, `interCharacter`, `inline`), `ruby-scale` (e.g. `0.58`), `ruby-font-name`, `ruby-font-size`, `ruby-annotation-font-name`, `ruby-annotation-font-size`. --- @@ -83,13 +87,46 @@ struct ContentView: View { func createStyleContainer() -> HTMLStyleContainer { var container = HTMLStyleContainer() - container.uiFont = .systemFont(ofSize: 16) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 16) +#else + let font = UIFont.systemFont(ofSize: 16) +#endif + container.uiFont = font container.lineBreakMode = .byWordWrapping return container } } ``` +### Profiling + +Set `SWIFTUIHTML_SIGNPOSTS=1` in your environment to emit signposts around HTML parsing, then use Instruments to view the “HTML parse” intervals. +For perf comparisons, you can also toggle `SWIFTUIHTML_CACHE_FRAMESETTER=1` and use `SWIFTUIHTML_DISABLE_RANGE_SCAN_OPT=1` to force the legacy range scan path. + +### Performance Tests + +The SwiftUIHTML package includes lightweight performance smoke tests that log the median parse time for synthetic HTML (and SwiftSoup when available). Run `swift test` in the SwiftUIHTML package to see the output. + +### Ruby Example + +```swift +let html = """ +

+ + 今日きょう + + is sunny. +

+

+ + 明日あした + + is clear too. +

+ """ +``` + ### Parser Implementation You can use any HTML parser by implementing the HTMLParserable protocol: @@ -165,6 +202,8 @@ Protocol for external HTML parser integration ## 📱 Example Project For more examples, please refer to the project in the `Example` folder. +The Testing section includes a "Synthetic Stress" sample for profiling large HTML payloads. +When SwiftSoup is linked, the Parser Integration section includes a SwiftSoup parser sample. --- @@ -176,4 +215,4 @@ Contributions are welcome! Feel free to submit issues and pull requests. ## 📄 License -SwiftUIHTML is released under the MIT License. See [LICENSE](LICENSE) for details. \ No newline at end of file +SwiftUIHTML is released under the MIT License. See [LICENSE](LICENSE) for details. diff --git a/README_ko.md b/README_ko.md index 1645669..93fa87f 100644 --- a/README_ko.md +++ b/README_ko.md @@ -21,6 +21,7 @@ - **CSS 스타일 지원**: 인라인 스타일(padding, margin, background, border 등) 완벽 지원 - **유연한 파서 통합**: Fuzi, SwiftSoup 등 외부 파서 라이브러리와 연동 - **환경 값 시스템**: 전역 설정 관리 및 스타일 커스터마이징 +- **프로파일링 훅**: `SWIFTUIHTML_SIGNPOSTS=1` 설정 시 HTML 파싱 구간 signpost 출력 --- @@ -32,16 +33,19 @@ |---------|-----| | **블록** | `div`, `body`, `p`, `header`, `main`, `section`, `footer`, `h1`, `h2` | | **인라인** | `span`, `a`, `b`, `strong`, `i`, `em`, `u` | -| **첨부** | `img` | +| **첨부** | `img`, `ruby` | > 참고: h3, ul, video 등의 태그는 커스텀 태그로 등록하여 사용할 수 있습니다. +> 참고: `ruby`는 `` 형태의 주석을 CoreText로 렌더링합니다. `rt` 텍스트가 루비 문자열로 사용되며 `rp`/`rtc`는 무시됩니다. + ### CSS 스타일 속성 - **텍스트 스타일**: `color`, `background-color`, `font-family`, `font-size`, `line-height`, `word-break` - **블록 레이아웃**: `padding`, `margin`, `border`, `border-radius` (div, p, section 등 block 요소만) - **인라인 스타일**: `color`, `background-color`, `border-radius` (strong, em, span 등 inline 요소) > **참고**: inline 요소(span, strong, em 등)에서는 `padding`, `margin`이 지원되지 않습니다. +> **루비 옵션**: `ruby-position` (`before`, `after`, `interCharacter`, `inline`), `ruby-scale` (예: `0.58`), `ruby-font-name`, `ruby-font-size`, `ruby-annotation-font-name`, `ruby-annotation-font-size`. --- @@ -83,13 +87,46 @@ struct ContentView: View { func createStyleContainer() -> HTMLStyleContainer { var container = HTMLStyleContainer() - container.uiFont = .systemFont(ofSize: 16) +#if os(macOS) + let font = NSFont.systemFont(ofSize: 16) +#else + let font = UIFont.systemFont(ofSize: 16) +#endif + container.uiFont = font container.lineBreakMode = .byWordWrapping return container } } ``` +### Profiling + +환경 변수 `SWIFTUIHTML_SIGNPOSTS=1`을 설정하면 HTML 파싱 구간에 signpost가 출력됩니다. Instruments에서 “HTML parse” 구간을 확인하세요. +성능 비교용으로 `SWIFTUIHTML_CACHE_FRAMESETTER=1`을 켜거나 `SWIFTUIHTML_DISABLE_RANGE_SCAN_OPT=1`로 레거시 범위 스캔 경로를 강제로 사용할 수 있습니다. + +### Performance Tests + +SwiftUIHTML 패키지에는 synthetic HTML 파싱의 median 시간을 출력하는 가벼운 성능 스모크 테스트가 포함되어 있습니다(SwiftSoup 사용 시 추가 측정). SwiftUIHTML 패키지에서 `swift test`를 실행하면 결과가 출력됩니다. + +### 루비 예제 + +```swift +let html = """ +

+ + 今日きょう + + 는 맑습니다. +

+

+ + 明日あした + + 도 맑습니다. +

+ """ +``` + ### 파서 구현 HTMLParserable 프로토콜을 구현하여 원하는 HTML 파서를 사용할 수 있습니다: @@ -165,6 +202,8 @@ HTML 콘텐츠를 렌더링하는 메인 뷰 ## 📱 예제 프로젝트 더 많은 예제는 `Example` 폴더의 프로젝트를 참고하세요. +Testing 섹션에 대용량 HTML을 위한 "Synthetic Stress" 샘플이 있습니다. +SwiftSoup를 연결한 경우 Parser Integration 섹션에 SwiftSoup 파서 샘플이 표시됩니다. --- @@ -176,4 +215,4 @@ HTML 콘텐츠를 렌더링하는 메인 뷰 ## 📄 라이선스 -Copyright © 2025 PRND. All rights reserved. \ No newline at end of file +Copyright © 2025 PRND. All rights reserved. diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift index 08f11f8..3db8a29 100644 --- a/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift @@ -6,6 +6,9 @@ import AppKit #else import UIKit #endif +#if canImport(os) +import os +#endif final class AttachmentLayoutEngine { @@ -15,8 +18,18 @@ final class AttachmentLayoutEngine { private var keyAttachment: [AnyHashable: Weak] = [:] private var frameStore: [AnyHashable: CGRect] = [:] private var texts: [TextType] = [] + private var preparedString: NSMutableAttributedString? + private var targetCache: [(value: TextAttachment, range: NSRange)] = [] + private var attachmentKeys: [AnyHashable] = [] + private var attachmentRanges: [NSRange] = [] + private var attachmentSizePublishers: [AnyPublisher] = [] private var textRangeFrameCalculator = TextRangeFrameCalculator() private var cancelBag = Set() +#if canImport(os) + private static let signposter = OSSignposter( + logHandle: OSLog(subsystem: "SwiftUIHTML", category: "AttachmentLayout") + ) +#endif var lineSpacing: CGFloat { get { textRangeFrameCalculator.lineSpacing } @@ -37,6 +50,7 @@ final class AttachmentLayoutEngine { let containerSizePublisher = _containerSize.projectedValue .compactMap { $0 } .removeDuplicates() + .debounce(for: .milliseconds(16), scheduler: DispatchQueue.main) Publishers .CombineLatest(attributedStringPublisher, containerSizePublisher) @@ -72,21 +86,45 @@ final class AttachmentLayoutEngine { } func setContainerSize(_ size: CGSize) { + if containerSize == size { + return + } containerSize = size } @MainActor func setTexts(_ texts: [TextType]) { - let hasAttachment = !texts.lazy.filter(\.hasAttachment).isEmpty + let hasAttachment = texts.contains(where: \.hasAttachment) guard hasAttachment else { self.texts = [] self.attributedString = nil + self.preparedString = nil + self.targetCache = [] + self.attachmentKeys = [] + self.attachmentRanges = [] + self.attachmentSizePublishers = [] return } guard self.texts != texts else { return } self.texts = texts - attributedString = makeAttributedString(texts: texts) + let newAttributedString = makeAttributedString(texts: texts) + attributedString = newAttributedString + preparedString = textRangeFrameCalculator.prepareCoreTextString(newAttributedString) + targetCache = textRangeFrameCalculator.findAttribute( + in: newAttributedString, + for: .attachment, + type: TextAttachment.self + ) + attachmentKeys = targetCache.map(\.value.key) + attachmentRanges = targetCache.map(\.range) + attachmentSizePublishers = targetCache.map { target in + target.value + .publisher(for: \.bounds) + .removeDuplicates() + .filter { $0.size > .invisible } + .eraseToAnyPublisher() + } } @@ -146,27 +184,32 @@ private extension AttachmentLayoutEngine { func measureLayoutPublisher(attributedString: NSMutableAttributedString, containerSize: CGSize) -> AnyPublisher<[(AnyHashable, CGRect)], Never> { - let coreTextString = textRangeFrameCalculator - .prepareCoreTextString(attributedString) - - let targets = textRangeFrameCalculator - .findAttribute(in: attributedString, for: .attachment, type: TextAttachment.self) - - let attchmentSizeChange = targets - .map { target in - target.value - .publisher(for: \.bounds) - .removeDuplicates() - .filter { $0.size > .invisible } - } + let coreTextString = preparedString ?? textRangeFrameCalculator.prepareCoreTextString(attributedString) + + let targets = targetCache + if targets.isEmpty { + return Just([]).eraseToAnyPublisher() + } - let keys = targets.map(\.value.key) - let ranges = targets.map(\.range) + let keys = attachmentKeys + let ranges = attachmentRanges - return Publishers.MergeMany(attchmentSizeChange) + let sizePublishers = attachmentSizePublishers + return Publishers.MergeMany(sizePublishers) .receive(on: DispatchQueue.global(qos: .background)) .compactMap { [weak textRangeFrameCalculator] _ in - textRangeFrameCalculator? +#if canImport(os) + let shouldSignpost = ProcessInfo.processInfo.environment["SWIFTUIHTML_SIGNPOSTS"] == "1" + let intervalState = shouldSignpost + ? AttachmentLayoutEngine.signposter.beginInterval("Attachment layout", "\(ranges.count) ranges") + : nil + defer { + if let intervalState { + AttachmentLayoutEngine.signposter.endInterval("Attachment layout", intervalState) + } + } +#endif + return textRangeFrameCalculator? .measureLayout(for: coreTextString, in: containerSize, by: ranges) } .map { zip(keys, $0).map { ($0, $1) } } @@ -175,15 +218,24 @@ private extension AttachmentLayoutEngine { func storeRangeBounds(key: AnyHashable, bounds: CGRect) { + if let existing = frameStore[key], existing == bounds { + return + } frameStore[key] = bounds layoutUpdatePublisher.send(()) } func storeRangeBounds(_ values: [(key: AnyHashable, bounds: CGRect)]) { + var didChange = false values.forEach { - frameStore[$0.key] = $0.bounds + if frameStore[$0.key] != $0.bounds { + frameStore[$0.key] = $0.bounds + didChange = true + } + } + if didChange { + layoutUpdatePublisher.send(()) } - layoutUpdatePublisher.send(()) } } diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift index 4d411cb..e4707f4 100644 --- a/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift @@ -12,6 +12,8 @@ final class AttachmentManager: ObservableObject { private let textImages = ImageCache() init() { + textImages.countLimit = 512 + textImages.totalCostLimit = 8 * 1024 * 1024 layoutEngine.layoutUpdatePublisher .receive(on: DispatchQueue.main) .sink { [weak self] in @@ -43,7 +45,10 @@ final class AttachmentManager: ObservableObject { // SwiftUI Text(image:) 주입 이미지 func sizeImage(key: AnyHashable, styleContainer: HTMLStyleContainer) -> PlatformImage { - var size = layoutEngine.getSize(key: key) + let size = layoutEngine.getSize(key: key) + if size == .zero { + return PlatformImage.manabiEmpty(size: .zero) + } var fontName: String? = nil var fontSize: CGFloat? = nil if let uiFont = styleContainer.uiFont { @@ -60,7 +65,8 @@ final class AttachmentManager: ObservableObject { return image } let image = EmptyImage(size: size).image - textImages.setObject(image, forKey: cacheKey) + let cost = max(1, Int(size.width * size.height * 4)) + textImages.setObject(image, forKey: cacheKey, cost: cost) return image } diff --git a/Sources/SwiftUIHTML/Core/AttributeStyle/DefaultAttributeStyle.swift b/Sources/SwiftUIHTML/Core/AttributeStyle/DefaultAttributeStyle.swift index b004b9a..0f97e87 100644 --- a/Sources/SwiftUIHTML/Core/AttributeStyle/DefaultAttributeStyle.swift +++ b/Sources/SwiftUIHTML/Core/AttributeStyle/DefaultAttributeStyle.swift @@ -16,7 +16,7 @@ public struct DefaultAttributeStyler: AttributeStyleable { attributes: [String : AttributeValue], to styleContainer: inout HTMLStyleContainer ) { - let cssStyle = attributes["style"]?.cssStyle ?? .empty + guard let cssStyle = attributes["style"]?.cssStyle else { return } if let color = cssStyle["color"]?.toColor() { styleContainer.foregroundColor = color diff --git a/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleModifier.swift b/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleModifier.swift index e995184..c777ccf 100644 --- a/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleModifier.swift +++ b/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleModifier.swift @@ -6,13 +6,17 @@ struct LayoutStyleModifier: ViewModifier { let cssStyle: CSSStyle func body(content: Content) -> some View { - content - .modifier(EdgeModifier(property: "padding", cssStyle: cssStyle)) - .modifier(InnerLayoutModifier(cssStyle: cssStyle)) - .modifier(BackgroundModifier(cssStyle: cssStyle)) - .modifier(BorderModifier(cssStyle: cssStyle)) - .compositingGroup() - .modifier(EffectsModifier(cssStyle: cssStyle)) - .modifier(EdgeModifier(property: "margin", cssStyle: cssStyle)) + if cssStyle.isEmpty { + content + } else { + content + .modifier(EdgeModifier(property: "padding", cssStyle: cssStyle)) + .modifier(InnerLayoutModifier(cssStyle: cssStyle)) + .modifier(BackgroundModifier(cssStyle: cssStyle)) + .modifier(BorderModifier(cssStyle: cssStyle)) + .compositingGroup() + .modifier(EffectsModifier(cssStyle: cssStyle)) + .modifier(EdgeModifier(property: "margin", cssStyle: cssStyle)) + } } } diff --git a/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleSubModifier/EffectsModifier.swift b/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleSubModifier/EffectsModifier.swift index 7bdbb52..9778de2 100644 --- a/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleSubModifier/EffectsModifier.swift +++ b/Sources/SwiftUIHTML/Core/AttributeStyle/LayoutStyleSubModifier/EffectsModifier.swift @@ -31,9 +31,9 @@ private struct ShadowModifier: ViewModifier { private func parseShadow(_ boxShadow: String) -> (color: Color, radius: CGFloat, x: CGFloat, y: CGFloat) { var color: Color = .black.opacity(0.33) - var radius: CGFloat = 3 - var x: CGFloat = 0 - var y: CGFloat = 2 + let radius: CGFloat = 3 + let x: CGFloat = 0 + let y: CGFloat = 2 if boxShadow.lowercased() == "none" || boxShadow.lowercased() == "initial" { return (Color.clear, 0, 0, 0) diff --git a/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift b/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift index 0865073..cf49663 100644 --- a/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift +++ b/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift @@ -9,6 +9,9 @@ final class TextRangeFrameCalculator { /// Additional spacing between lines var lineSpacing: CGFloat = 0 + private var cachedFramesetter: CTFramesetter? + private var cachedStringID: ObjectIdentifier? + private let framesetterLock = NSLock() // MARK: - Public Methods @@ -115,7 +118,7 @@ private extension TextRangeFrameCalculator { /// - containerSize: Size of the container /// - Returns: A Core Text frame func createTextFrame(for attrString: NSAttributedString, containerSize: CGSize) -> CTFrame { - let framesetter = CTFramesetterCreateWithAttributedString(attrString) + let framesetter = framesetter(for: attrString) // Calculate optimal text size with constraints let suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints( @@ -128,8 +131,6 @@ private extension TextRangeFrameCalculator { // Ensure minimum dimensions let width = max(suggestedSize.width, containerSize.width) - let height = max(suggestedSize.height, containerSize.height) - let rect = CGRect(origin: .zero, size: CGSize(width: width, height: suggestedSize.height)) let path = CGPath(rect: rect, transform: nil) @@ -200,10 +201,27 @@ private extension TextRangeFrameCalculator { /// - targetRanges: Text ranges to measure /// - Returns: Array of rectangles for each range func calculateRangeRects(in frame: CTFrame, containerSize: CGSize, targetRanges: [NSRange]) -> [CGRect] { + if ProcessInfo.processInfo.environment["SWIFTUIHTML_DISABLE_RANGE_SCAN_OPT"] == "1" { + return calculateRangeRectsLegacy( + in: frame, + containerSize: containerSize, + targetRanges: targetRanges + ) + } + + return calculateRangeRectsOptimized( + in: frame, + containerSize: containerSize, + targetRanges: targetRanges + ) + } + + func calculateRangeRectsLegacy(in frame: CTFrame, containerSize: CGSize, targetRanges: [NSRange]) -> [CGRect] { let lines = CTFrameGetLines(frame) as! [CTLine] let adjustedOrigins = adjustLineOrigins(frame: frame, lines: lines, containerSize: containerSize) var rangeRects: [CGRect] = [] + rangeRects.reserveCapacity(targetRanges.count) // Process each line for (lineIndex, line) in lines.enumerated() { @@ -218,12 +236,11 @@ private extension TextRangeFrameCalculator { guard !intersectingRanges.isEmpty else { continue } let lineOrigin = adjustedOrigins[lineIndex] - let characterRects = calculateCharacterBounds(in: line, ranges: intersectingRanges) - - // Get line metrics - var ascent: CGFloat = 0 - var descent: CGFloat = 0 - _ = CTLineGetTypographicBounds(line, &ascent, &descent, nil) + let characterRects = calculateCharacterBounds( + in: line, + lineRange: lineNSRange, + ranges: intersectingRanges + ) // Create rect for each character range for charFrame in characterRects { @@ -240,16 +257,85 @@ private extension TextRangeFrameCalculator { return rangeRects } + func calculateRangeRectsOptimized(in frame: CTFrame, containerSize: CGSize, targetRanges: [NSRange]) -> [CGRect] { + let lines = CTFrameGetLines(frame) as! [CTLine] + guard !lines.isEmpty else { return [] } + + let adjustedOrigins = adjustLineOrigins(frame: frame, lines: lines, containerSize: containerSize) + + var rangesAreSorted = true + if targetRanges.count > 1 { + for index in 1..= ranges.count { + break + } + + var intersectingRanges: [NSRange] = [] + var scanIndex = rangeIndex + while scanIndex < ranges.count { + let range = ranges[scanIndex] + if range.location >= lineEnd { + break + } + if NSIntersectionRange(lineNSRange, range).length > 0 { + intersectingRanges.append(range) + } + scanIndex += 1 + } + + guard !intersectingRanges.isEmpty else { continue } + + let lineOrigin = adjustedOrigins[lineIndex] + let characterRects = calculateCharacterBounds( + in: line, + lineRange: lineNSRange, + ranges: intersectingRanges + ) + + for charFrame in characterRects { + let lineRect = CGRect( + x: charFrame.origin.x, + y: lineOrigin.y, + width: charFrame.size.width, + height: charFrame.size.height + ) + rangeRects.append(lineRect) + } + } + + return rangeRects + } + /// Calculates bounds for text ranges within a line /// - Parameters: /// - line: The Core Text line /// - ranges: Text ranges to measure /// - Returns: Array of rectangles for the ranges in this line - func calculateCharacterBounds(in line: CTLine, ranges: [NSRange]) -> [CGRect] { + func calculateCharacterBounds(in line: CTLine, lineRange: NSRange, ranges: [NSRange]) -> [CGRect] { var characterBounds: [CGRect] = [] - - let lineRange = CTLineGetStringRange(line) - let lineNSRange = NSRange(location: lineRange.location, length: lineRange.length) + characterBounds.reserveCapacity(ranges.count) // Calculate metrics for the line var ascent: CGFloat = 0 @@ -259,7 +345,7 @@ private extension TextRangeFrameCalculator { // Process each range for range in ranges { // Check if the range intersects with this line - let intersection = NSIntersectionRange(lineNSRange, range) + let intersection = NSIntersectionRange(lineRange, range) guard intersection.length > 0 else { continue } // Calculate offsets for the intersection range @@ -282,6 +368,27 @@ private extension TextRangeFrameCalculator { } } +private extension TextRangeFrameCalculator { + func framesetter(for attributedString: NSAttributedString) -> CTFramesetter { + guard ProcessInfo.processInfo.environment["SWIFTUIHTML_CACHE_FRAMESETTER"] == "1" else { + return CTFramesetterCreateWithAttributedString(attributedString) + } + + let objectID = ObjectIdentifier(attributedString as AnyObject) + framesetterLock.lock() + defer { framesetterLock.unlock() } + + if cachedStringID == objectID, let cachedFramesetter { + return cachedFramesetter + } + + let framesetter = CTFramesetterCreateWithAttributedString(attributedString) + cachedStringID = objectID + cachedFramesetter = framesetter + return framesetter + } +} + // MARK: - Extensions extension NSAttributedString.Key { diff --git a/Sources/SwiftUIHTML/Core/Element/InlineElement.swift b/Sources/SwiftUIHTML/Core/Element/InlineElement.swift index 232138a..c6b4169 100644 --- a/Sources/SwiftUIHTML/Core/Element/InlineElement.swift +++ b/Sources/SwiftUIHTML/Core/Element/InlineElement.swift @@ -34,19 +34,22 @@ extension Collection where Element == InlineElement { @MainActor func toHTMLTextType() -> [TextType] { + var results: [TextType] = [] + results.reserveCapacity(count) var textCount = 0 var attachmentCount = 0 - return map { element in + for element in self { switch element.type { case .text: defer { textCount += 1 } - return element.toHTMLTextType(id: textCount) + results.append(element.toHTMLTextType(id: textCount)) case .attachment: defer { attachmentCount += 1 } - return element.toHTMLTextType(id: attachmentCount) + results.append(element.toHTMLTextType(id: attachmentCount)) } } + return results } } diff --git a/Sources/SwiftUIHTML/Core/HTMLConfiguration/HTMLConfiguration.swift b/Sources/SwiftUIHTML/Core/HTMLConfiguration/HTMLConfiguration.swift index 0cf6d08..b2679df 100644 --- a/Sources/SwiftUIHTML/Core/HTMLConfiguration/HTMLConfiguration.swift +++ b/Sources/SwiftUIHTML/Core/HTMLConfiguration/HTMLConfiguration.swift @@ -2,7 +2,7 @@ import SwiftUI -public struct HTMLConfiguration: Sendable { +public struct HTMLConfiguration: @unchecked Sendable { enum TagType { case block case inline @@ -39,6 +39,7 @@ public struct HTMLConfiguration: Sendable { tag: "u", renderer: UnderlineTag.self ) + .register(tag: "ruby", renderer: RubyTag.self) .register(tag: "img", renderer: ImageTag.self) } } diff --git a/Sources/SwiftUIHTML/Core/Node/HTMLNode.swift b/Sources/SwiftUIHTML/Core/Node/HTMLNode.swift index e2afbbd..1bea169 100644 --- a/Sources/SwiftUIHTML/Core/Node/HTMLNode.swift +++ b/Sources/SwiftUIHTML/Core/Node/HTMLNode.swift @@ -21,11 +21,13 @@ extension HTMLNode { configuration: HTMLConfiguration, with styleContainer: HTMLStyleContainer ) -> BlockElement { - let contents = children.flatMap { child -> [TagElement] in - child.toElement( + var contents: [TagElement] = [] + contents.reserveCapacity(children.count) + for child in children { + contents.append(contentsOf: child.toElement( configuration: configuration, with: styleContainer - ) + )) } return BlockElement( tag: tag, @@ -73,15 +75,29 @@ fileprivate extension HTMLNode { var _styleContainer = styleContainer configuration.applyStyles(tag: tag, attributes: attributes, to: &_styleContainer) + if tag == "ruby", + let rubyData = rubyAttachmentData(from: children, styleContainer: _styleContainer) { + return [ + .inline(InlineElement( + tag: tag, + attributes: rubyData, + type: .attachment, + styleContainer: _styleContainer + )) + ] + } + switch configuration.tagType(of: tag) { case .inline: - return children - .flatMap { child -> [TagElement] in - child.toElement( - configuration: configuration, - with: _styleContainer - ) - } + var contents: [TagElement] = [] + contents.reserveCapacity(children.count) + for child in children { + contents.append(contentsOf: child.toElement( + configuration: configuration, + with: _styleContainer + )) + } + return contents case .attachment: return [ @@ -103,3 +119,82 @@ fileprivate extension HTMLNode { } } } + +private extension HTMLNode { + func rubyAttachmentData(from children: [HTMLChild], styleContainer: HTMLStyleContainer) -> [String: AttributeValue]? { + let rubyPieces = rubyComponents(from: children) + guard let baseText = rubyPieces.base, !baseText.isEmpty else { return nil } + + var result = attributes + result["ruby-base"] = AttributeValue(rawValue: baseText) + if let rubyText = rubyPieces.ruby, !rubyText.isEmpty { + result["ruby-text"] = AttributeValue(rawValue: rubyText) + } + + if let font = styleContainer.uiFont { + result["ruby-font-name"] = AttributeValue(rawValue: font.fontName) + result["ruby-font-size"] = AttributeValue(rawValue: "\(font.pointSize)") + } + + return result + } + + func rubyComponents(from children: [HTMLChild]) -> (base: String?, ruby: String?) { + var baseText = "" + var rubyText = "" + var hasBase = false + var hasRuby = false + + for child in children { + switch child { + case let .text(text): + if !text.isEmpty { + baseText.append(text) + hasBase = true + } + + case let .node(node): + switch node.tag { + case "rt": + let trimmedText = node.plainText().trimmingCharacters(in: .whitespacesAndNewlines) + if !trimmedText.isEmpty { + if hasRuby { + rubyText.append(" ") + } + rubyText.append(trimmedText) + hasRuby = true + } + case "rp", "rtc": + continue + case "rb": + let text = node.plainText() + if !text.isEmpty { + baseText.append(text) + hasBase = true + } + default: + let text = node.plainText() + if !text.isEmpty { + baseText.append(text) + hasBase = true + } + } + } + } + + return (base: hasBase ? baseText : nil, ruby: hasRuby ? rubyText : nil) + } + + func plainText() -> String { + var result = "" + for child in children { + switch child { + case let .text(text): + result.append(text) + case let .node(node): + result.append(node.plainText()) + } + } + return result + } +} diff --git a/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift b/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift index cdca03d..18e02cc 100644 --- a/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift +++ b/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift @@ -7,12 +7,14 @@ import AppKit internal typealias PlatformFont = NSFont internal typealias PlatformColor = NSColor internal typealias PlatformImage = NSImage +internal typealias PlatformView = NSView internal typealias PlatformFontDescriptor = NSFontDescriptor #else import UIKit internal typealias PlatformFont = UIFont internal typealias PlatformColor = UIColor internal typealias PlatformImage = UIImage +internal typealias PlatformView = UIView internal typealias PlatformFontDescriptor = UIFontDescriptor #endif diff --git a/Sources/SwiftUIHTML/HTMLMinifier.swift b/Sources/SwiftUIHTML/HTMLMinifier.swift index f031e52..9f71c27 100644 --- a/Sources/SwiftUIHTML/HTMLMinifier.swift +++ b/Sources/SwiftUIHTML/HTMLMinifier.swift @@ -3,6 +3,11 @@ import Foundation /// Class that provides HTML document compression functionality public enum HTMLMinifier { + private static let whitespaceBetweenTagsRegex: NSRegularExpression? = try? NSRegularExpression( + pattern: ">([\\s]+)<", + options: [] + ) + /// Removes unnecessary whitespace from HTML documents and optimizes them. /// - Parameter html: Original HTML string /// - Returns: Optimized HTML string @@ -15,15 +20,19 @@ public enum HTMLMinifier { result = result.replacingOccurrences(of: ">\\s*[\\n\\r]\\s*<", with: "><", options: .regularExpression) // 3. Convert only pure whitespace between tags to   (when no line breaks) - let pattern = ">([\\s]+)<" - - while let range = result.range(of: pattern, options: .regularExpression) { - let spacesRange = result.index(after: range.lowerBound).." + nbspString + "<") + if let regex = whitespaceBetweenTagsRegex { + let fullRange = NSRange(result.startIndex.. 1, + let matchRange = Range(match.range(at: 0), in: result), + let spacesRange = Range(match.range(at: 1), in: result) else { + continue + } + let spacesCount = result[spacesRange].count + let nbspString = String(repeating: " ", count: spacesCount) + result.replaceSubrange(matchRange, with: ">" + nbspString + "<") + } } // 4. Remove all remaining line breaks and tabs (excluding inside tags) diff --git a/Sources/SwiftUIHTML/Tag/Inline/RubyTag.swift b/Sources/SwiftUIHTML/Tag/Inline/RubyTag.swift new file mode 100644 index 0000000..c6dec03 --- /dev/null +++ b/Sources/SwiftUIHTML/Tag/Inline/RubyTag.swift @@ -0,0 +1,256 @@ +// Copyright © 2025 PRND. All rights reserved. +import SwiftUI +import CoreText +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +public struct RubyTag: InlineAttachmentTag { + public let attributes: [String: AttributeValue] + + public init(attributes: [String : AttributeValue]) { + self.attributes = attributes + } + + public var body: some View { + RubyInlineLabel( + baseText: baseText, + rubyText: rubyText, + font: baseFont, + rubyFont: rubyFont, + rubyPosition: rubyPosition, + rubyScale: rubyScale, + foregroundColor: foregroundColor + ) + } +} + +private extension RubyTag { + var baseText: String { + attributes["ruby-base"]?.string ?? "" + } + + var rubyText: String { + attributes["ruby-text"]?.string ?? "" + } + + var rubyPosition: CTRubyPosition { + switch attributes["ruby-position"]?.string { + case "after": return .after + case "before": return .before + case "interCharacter": return .interCharacter + case "inline": return .inline + default: return .before + } + } + + var rubyScale: CGFloat { + attributes["ruby-scale"]?.cgFloat ?? 0.58 + } + + var foregroundColor: Color? { + if let cssStyle = attributes["style"]?.cssStyle, + let color = cssStyle["color"]?.toColor() { + return color + } + if let color = attributes["ruby-color"]?.toColor() { + return color + } + return nil + } + + var baseFont: PlatformFont? { + if let fontName = attributes["ruby-font-name"]?.string, + let size = attributes["ruby-font-size"]?.cgFloat, + let font = PlatformFont(name: fontName, size: size) { + return font + } + + let cssStyle = attributes["style"]?.cssStyle ?? .empty + let font = CSSFontUtility.createFont(fromCSSStyle: cssStyle, currentFont: nil) + if let font { + return font + } + + if let size = cssStyle["font-size"]?.string { + let resolvedSize = CSSFontUtility.parseSize(fromFontSize: size, baseSize: PlatformFont.systemFontSize) + return PlatformFont.systemFont(ofSize: resolvedSize) + } + + return nil + } + + var rubyFont: PlatformFont? { + if let fontName = attributes["ruby-annotation-font-name"]?.string, + let size = attributes["ruby-annotation-font-size"]?.cgFloat, + let font = PlatformFont(name: fontName, size: size) { + return font + } + return baseFont + } +} + +private struct RubyInlineLabel: View { + let baseText: String + let rubyText: String + let font: PlatformFont? + let rubyFont: PlatformFont? + let rubyPosition: CTRubyPosition + let rubyScale: CGFloat + let foregroundColor: Color? + + var body: some View { +#if os(macOS) + RubyTextRepresentable(attributedText: attributedText) +#else + RubyTextRepresentable(attributedText: attributedText) +#endif + } + + private var attributedText: NSAttributedString { + let base = baseText.isEmpty ? rubyText : baseText + guard !base.isEmpty else { return NSAttributedString(string: "") } + guard !rubyText.isEmpty else { + return NSAttributedString(string: base, attributes: baseAttributes) + } + + var rubyAttributes: [CFString: Any] = [ + kCTRubyAnnotationSizeFactorAttributeName: rubyScale, + kCTRubyAnnotationScaleToFitAttributeName: false, + ] + if let rubyFont { + rubyAttributes[kCTFontAttributeName] = rubyFont + } + + let annotation = CTRubyAnnotationCreateWithAttributes( + .auto, + .auto, + rubyPosition, + rubyText as CFString, + rubyAttributes as CFDictionary + ) + + var attributes = baseAttributes + attributes[kCTRubyAnnotationAttributeName as NSAttributedString.Key] = annotation + return NSAttributedString(string: base, attributes: attributes) + } + + private var baseAttributes: [NSAttributedString.Key: Any] { + var attributes: [NSAttributedString.Key: Any] = [:] + if let font { + attributes[.font] = font + } + if let foregroundColor { +#if os(macOS) + attributes[.foregroundColor] = NSColor(foregroundColor) +#else + attributes[.foregroundColor] = UIColor(foregroundColor) +#endif + } + return attributes + } +} + +#if os(macOS) +private struct RubyTextRepresentable: NSViewRepresentable { + let attributedText: NSAttributedString + + func makeNSView(context: Context) -> RubyTextView { + let view = RubyTextView() + view.attributedText = attributedText + return view + } + + func updateNSView(_ nsView: RubyTextView, context: Context) { + nsView.attributedText = attributedText + } +} +#else +private struct RubyTextRepresentable: UIViewRepresentable { + let attributedText: NSAttributedString + + func makeUIView(context: Context) -> RubyTextView { + let view = RubyTextView() + view.setContentHuggingPriority(.required, for: .horizontal) + view.setContentHuggingPriority(.required, for: .vertical) + view.setContentCompressionResistancePriority(.required, for: .horizontal) + view.setContentCompressionResistancePriority(.required, for: .vertical) + view.attributedText = attributedText + return view + } + + func updateUIView(_ uiView: RubyTextView, context: Context) { + uiView.attributedText = attributedText + } +} +#endif + +private final class RubyTextView: PlatformView { + var attributedText: NSAttributedString? { + didSet { + guard oldValue != attributedText else { return } +#if os(macOS) + needsDisplay = true + invalidateIntrinsicContentSize() +#else + setNeedsDisplay() + invalidateIntrinsicContentSize() +#endif + } + } + +#if os(macOS) + override func draw(_ dirtyRect: CGRect) { + super.draw(dirtyRect) + guard let context = NSGraphicsContext.current?.cgContext else { return } + drawText(in: context, rect: bounds) + } +#else + override func draw(_ rect: CGRect) { + super.draw(rect) + guard let context = UIGraphicsGetCurrentContext() else { return } + drawText(in: context, rect: rect) + } +#endif + + override var intrinsicContentSize: CGSize { + guard let attributedText, attributedText.length > 0 else { + return .zero + } + let line = CTLineCreateWithAttributedString(attributedText as CFAttributedString) + var ascent: CGFloat = 0 + var descent: CGFloat = 0 + var leading: CGFloat = 0 + let idealWidth = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading)).rounded(.up) + let glyphBounds = CTLineGetBoundsWithOptions(line, [.useGlyphPathBounds]) + let glyphHeight = glyphBounds.height + let height = (glyphHeight.isNormal && glyphHeight > 0 ? glyphHeight : (ascent + descent + leading)).rounded(.up) + return CGSize(width: idealWidth, height: height) + } + + private func drawText(in context: CGContext, rect: CGRect) { + guard let attributedText, attributedText.length > 0 else { return } + + context.saveGState() + context.translateBy(x: 0, y: rect.height) + context.scaleBy(x: 1.0, y: -1.0) + + let line = CTLineCreateWithAttributedString(attributedText as CFAttributedString) + + let endAttributes = attributedText.attributes(at: max(0, attributedText.length - 1), effectiveRange: nil) + let token = NSAttributedString(string: "\u{2026}", attributes: endAttributes) + let tokenLine = CTLineCreateWithAttributedString(token as CFAttributedString) + + let truncated = CTLineCreateTruncatedLine(line, Double(rect.width), .end, tokenLine) ?? line + + let glyphBounds = CTLineGetBoundsWithOptions(truncated, [.useGlyphPathBounds]) + let baselineX = rect.minX - glyphBounds.origin.x + let baselineY = rect.minY - glyphBounds.origin.y + context.textPosition = CGPoint(x: baselineX, y: baselineY) + + CTLineDraw(truncated, context) + context.restoreGState() + } +} diff --git a/Sources/SwiftUIHTML/ValueType/AttributeValue.swift b/Sources/SwiftUIHTML/ValueType/AttributeValue.swift index 70624e5..14eae98 100644 --- a/Sources/SwiftUIHTML/ValueType/AttributeValue.swift +++ b/Sources/SwiftUIHTML/ValueType/AttributeValue.swift @@ -32,7 +32,7 @@ public struct AttributeValue: Sendable, Hashable { } public var cssStyle: CSSStyle? { - CSSStyle(style: rawValue) + CSSStyle.cached(style: rawValue) } public func toBool() -> Bool? { diff --git a/Sources/SwiftUIHTML/ValueType/CSSStyle.swift b/Sources/SwiftUIHTML/ValueType/CSSStyle.swift index aeaac18..ecf2e4d 100644 --- a/Sources/SwiftUIHTML/ValueType/CSSStyle.swift +++ b/Sources/SwiftUIHTML/ValueType/CSSStyle.swift @@ -4,22 +4,38 @@ import Foundation public struct CSSStyle: Sendable { private let styles: [String: AttributeValue] + private nonisolated(unsafe) static let cache: NSCache = { + let cache = NSCache() + cache.countLimit = 256 + return cache + }() init(styles: [String: AttributeValue]) { self.styles = styles } public init?(style: String) { - styles = Dictionary( - uniqueKeysWithValues: style - .split(separator: ";") - .compactMap { - let keyValue = $0.split(separator: ":") - guard let key = keyValue.first?.trimmingCharacters(in: .whitespaces), - let value = keyValue.last?.trimmingCharacters(in: .whitespaces) else { return nil } - return (String(key), AttributeValue(rawValue: value)) - } - ) + if style.isEmpty { + return nil + } + if style.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + styles = ["": AttributeValue(rawValue: "")] + return + } + + var parsed: [String: AttributeValue] = [:] + parsed.reserveCapacity(8) + + for component in style.split(separator: ";", omittingEmptySubsequences: true) { + let keyValue = component.split(separator: ":", maxSplits: 1) + guard keyValue.count == 2 else { continue } + let key = keyValue[0].trimmingCharacters(in: .whitespaces) + let value = keyValue[1].trimmingCharacters(in: .whitespaces) + guard !key.isEmpty, !value.isEmpty else { continue } + parsed[String(key)] = AttributeValue(rawValue: String(value)) + } + + styles = parsed if styles.isEmpty { return nil @@ -29,6 +45,10 @@ public struct CSSStyle: Sendable { public subscript(_ key: String) -> AttributeValue? { styles[key] } + + var isEmpty: Bool { + styles.isEmpty + } public func forEach(_ body: ((key: String, value: AttributeValue)) -> Void) { styles.forEach(body) @@ -37,5 +57,22 @@ public struct CSSStyle: Sendable { extension CSSStyle { static let empty = CSSStyle(styles: [:]) + + static func cached(style: String) -> CSSStyle? { + let key = style as NSString + if let cached = cache.object(forKey: key) { + return cached.value + } + guard let parsed = CSSStyle(style: style) else { return nil } + cache.setObject(CSSStyleBox(parsed), forKey: key) + return parsed + } } +private final class CSSStyleBox { + let value: CSSStyle + + init(_ value: CSSStyle) { + self.value = value + } +} diff --git a/Sources/SwiftUIHTML/ValueType/LineBreakMode.swift b/Sources/SwiftUIHTML/ValueType/LineBreakMode.swift index d6de507..ba9669f 100644 --- a/Sources/SwiftUIHTML/ValueType/LineBreakMode.swift +++ b/Sources/SwiftUIHTML/ValueType/LineBreakMode.swift @@ -16,7 +16,7 @@ extension LineBreakMode { return text.byCharWrapping() case .byWordWrapping: return text - case .custom(let id, let transform): + case .custom(_, let transform): return transform(text) } } diff --git a/Sources/SwiftUIHTML/View/HTMLBlock.swift b/Sources/SwiftUIHTML/View/HTMLBlock.swift index 64adf6b..5778cea 100644 --- a/Sources/SwiftUIHTML/View/HTMLBlock.swift +++ b/Sources/SwiftUIHTML/View/HTMLBlock.swift @@ -55,19 +55,20 @@ private extension HTMLBlock { func groupContents(contents: [TagElement]) -> [GroupContent] { var result: [GroupContent] = [] + result.reserveCapacity(contents.count) var currentInlineGroup: [InlineElement] = [] - contents.forEach { content in + for content in contents { switch content { case let .block(blockElement): if !currentInlineGroup.isEmpty { result.append(.inline(currentInlineGroup)) - currentInlineGroup.removeAll() + currentInlineGroup.removeAll(keepingCapacity: true) } - result += [.block(blockElement)] + result.append(.block(blockElement)) case let .inline(inlineElement): - currentInlineGroup += [inlineElement] + currentInlineGroup.append(inlineElement) } } diff --git a/Sources/SwiftUIHTML/View/HTMLInline.swift b/Sources/SwiftUIHTML/View/HTMLInline.swift index b352f0c..b120845 100644 --- a/Sources/SwiftUIHTML/View/HTMLInline.swift +++ b/Sources/SwiftUIHTML/View/HTMLInline.swift @@ -9,6 +9,8 @@ struct HTMLInline: View { } let texts: [TextType] + let hasAttachment: Bool + let textLine: TextLine @StateObject var attachmentManager = AttachmentManager() init(elements: [InlineElement]) { @@ -16,7 +18,10 @@ struct HTMLInline: View { } init(texts: [TextType]) { - self.texts = texts.trimmingNewLines() + let trimmed = texts.trimmingNewLines().coalescingTextRuns() + self.texts = trimmed + self.hasAttachment = trimmed.contains(where: \.hasAttachment) + self.textLine = Self.resolveTextLine(texts: trimmed) } var body: some View { @@ -36,10 +41,6 @@ struct HTMLInline: View { } } - var hasAttachment: Bool { - !texts.lazy.filter(\.hasAttachment).isEmpty - } - var content: some View { texts .reduce(Text("")) { result, type in @@ -68,6 +69,25 @@ struct HTMLInline: View { } private extension HTMLInline { + static func resolveTextLine(texts: [TextType]) -> TextLine { + var maxLineSpacing: CGFloat = 0 + var maxVerticalPadding: CGFloat = 0 + + for text in texts { + guard case .text(_, let attributes) = text, + let textLine = attributes.textLine else { continue } + maxLineSpacing = max(maxLineSpacing, textLine.lineSpacing) + if let verticalPadding = textLine.verticalPadding { + maxVerticalPadding = max(maxVerticalPadding, verticalPadding) + } + } + + return TextLine( + lineSpacing: maxLineSpacing, + verticalPadding: maxVerticalPadding + ) + } + func attachmentImage(for type: TextType, styleContainer: HTMLStyleContainer) -> Image { #if os(macOS) return Image(nsImage: attachmentManager.sizeImage( @@ -84,17 +104,6 @@ private extension HTMLInline { } extension HTMLInline { - var textLine: TextLine { - let result: [HTMLStyleContainer.TextLineAttribute] = texts.compactMap { - guard case .text(_, let attributes) = $0, let textLine = attributes.textLine else { return nil } - return textLine - } - - return TextLine( - lineSpacing: result.compactMap(\.lineSpacing).max() ?? 0, - verticalPadding: result.compactMap(\.verticalPadding).max() ?? 0 - ) - } } extension HTMLInline: @preconcurrency Equatable { @@ -114,4 +123,24 @@ private extension Array where Element == TextType { } return self } + + func coalescingTextRuns() -> [Element] { + var result: [Element] = [] + result.reserveCapacity(count) + + for element in self { + switch element { + case let .text(string, styleContainer): + guard !string.isEmpty else { continue } + if let last = result.last, case let .text(existing, existingStyle) = last, existingStyle == styleContainer { + result[result.count - 1] = .text(existing + string, styleContainer: existingStyle) + } else { + result.append(.text(string, styleContainer: styleContainer)) + } + default: + result.append(element) + } + } + return result + } } diff --git a/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift b/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift index c448205..c776e1f 100644 --- a/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift +++ b/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift @@ -2,13 +2,15 @@ import SwiftUI struct HTMLTextLayout: View { - var texts: [TextType] + let texts: [TextType] + let attachmentTexts: [TextType] @ObservedObject var attachmentManager: AttachmentManager @HTMLEnvironment(\._configuration) var configuration init(attachmentManager: AttachmentManager, texts: [TextType]) { self.texts = texts + self.attachmentTexts = texts.filter(\.hasAttachment) self.attachmentManager = attachmentManager attachmentManager.setTexts(texts) } @@ -26,10 +28,6 @@ struct HTMLTextLayout: View { } } - private var attachmentTexts: [TextType] { - texts.filter(\.hasAttachment) - } - @ViewBuilder private func attachmentView(for text: TextType) -> some View { if let info = extractAttachmentInfo(from: text) { diff --git a/Sources/SwiftUIHTML/View/HTMLView.swift b/Sources/SwiftUIHTML/View/HTMLView.swift index a6eb9ec..47d0f00 100644 --- a/Sources/SwiftUIHTML/View/HTMLView.swift +++ b/Sources/SwiftUIHTML/View/HTMLView.swift @@ -1,5 +1,8 @@ // Copyright © 2024 PRND. All rights reserved. import SwiftUI +#if canImport(os) +import os +#endif public struct HTMLView: View, Equatable { @@ -8,6 +11,12 @@ public struct HTMLView: View, Equatable { @HTMLEnvironment(\._configuration) var configuration let parser: () -> HTMLParserable + @State private var parsedNode: HTMLNode? +#if canImport(os) + private static let signposter = OSSignposter( + logHandle: OSLog(subsystem: "SwiftUIHTML", category: "HTMLView") + ) +#endif public init(html: String, parser: @autoclosure @escaping () -> HTMLParserable) { self.html = html @@ -15,8 +24,30 @@ public struct HTMLView: View, Equatable { } public var body: some View { - if !html.isEmpty { - HTMLNodeView(node: parser().parse(html: html)) + Group { + if let parsedNode { + HTMLNodeView(node: parsedNode) + } + } + .task(id: html) { + guard !html.isEmpty else { + parsedNode = nil + return + } +#if canImport(os) + let shouldSignpost = ProcessInfo.processInfo.environment["SWIFTUIHTML_SIGNPOSTS"] == "1" + let signpostID = shouldSignpost ? Self.signposter.makeSignpostID() : OSSignpostID.invalid + var intervalState: OSSignpostIntervalState? + if shouldSignpost { + intervalState = Self.signposter.beginInterval("HTML parse", id: signpostID, "\(html.count) chars") + } +#endif + parsedNode = parser().parse(html: html) +#if canImport(os) + if let intervalState { + Self.signposter.endInterval("HTML parse", intervalState) + } +#endif } } } @@ -26,4 +57,3 @@ extension HTMLView { lhs.html == rhs.html } } - diff --git a/Tests/SwiftUIHTMLTests/PerformanceTests.swift b/Tests/SwiftUIHTMLTests/PerformanceTests.swift new file mode 100644 index 0000000..f16912b --- /dev/null +++ b/Tests/SwiftUIHTMLTests/PerformanceTests.swift @@ -0,0 +1,352 @@ +// Copyright © 2026 PRND. All rights reserved. + +import Foundation +import Testing +@testable import SwiftUIHTML +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +struct PerformanceTests { + @Test + func parseSyntheticHTMLPerformanceSmoke() throws { + let html = syntheticHTML(sectionCount: 12, includeMedia: false) + let parser = SimpleHTMLParser() + + let iterations = 5 + let durations = (0.. 0) + } + + @Test + func parseSyntheticHTMLWithSwiftSoupPerformanceSmoke() throws { + let html = syntheticHTML(sectionCount: 8, includeMedia: false) + let parser = HTMLSwiftSoupParser() + + let iterations = 3 + let durations = (0.. 0) + } + + @Test + func textLayoutPerformanceSmoke() throws { + let font = PlatformFont.systemFont(ofSize: 14) + let text = String(repeating: "SwiftUIHTML layout performance ", count: 80) + let attributes: [NSAttributedString.Key: Any] = [.font: font] + let attributed = NSMutableAttributedString(string: text, attributes: attributes) + let calculator = TextRangeFrameCalculator() + let prepared = calculator.prepareCoreTextString(attributed) + let ranges = [ + NSRange(location: 0, length: min(40, attributed.length)), + NSRange(location: max(0, attributed.length / 3), length: min(60, attributed.length / 2)), + NSRange(location: max(0, attributed.length - 80), length: min(80, attributed.length)) + ] + let containerSize = CGSize(width: 320, height: 800) + + let iterations = 5 + let durations = (0.. 0) + } + + @Test + @MainActor + func framesetterCacheBenchmark() throws { + let font = PlatformFont.systemFont(ofSize: 14) + let text = String(repeating: "SwiftUIHTML framesetter cache ", count: 120) + let attributes: [NSAttributedString.Key: Any] = [.font: font] + let attributed = NSMutableAttributedString(string: text, attributes: attributes) + let ranges = [ + NSRange(location: 0, length: min(40, attributed.length)), + NSRange(location: max(0, attributed.length / 3), length: min(60, attributed.length / 2)), + NSRange(location: max(0, attributed.length - 80), length: min(80, attributed.length)) + ] + let containerSize = CGSize(width: 320, height: 800) + + let baseline = measureFramesetterCache(attributed: attributed, ranges: ranges, containerSize: containerSize, enabled: false) + let optimized = measureFramesetterCache(attributed: attributed, ranges: ranges, containerSize: containerSize, enabled: true) + + print("Framesetter cache baseline: \(String(format: "%.4f", baseline))s") + print("Framesetter cache optimized: \(String(format: "%.4f", optimized))s") + + #expect(baseline > 0) + #expect(optimized > 0) + } + + @Test + @MainActor + func rangeScanOptimizationBenchmark() throws { + let font = PlatformFont.systemFont(ofSize: 13) + let line = "SwiftUIHTML range scan text for layout.\n" + let text = String(repeating: line, count: 400) + let attributed = NSMutableAttributedString(string: text, attributes: [.font: font]) + + let strideSize = 12 + let ranges: [NSRange] = stride(from: 0, to: attributed.length, by: strideSize).map { location in + NSRange(location: location, length: min(6, attributed.length - location)) + } + let containerSize = CGSize(width: 320, height: 1800) + + let baseline = measureRangeScanOptimization(attributed: attributed, ranges: ranges, containerSize: containerSize, enabled: false) + let optimized = measureRangeScanOptimization(attributed: attributed, ranges: ranges, containerSize: containerSize, enabled: true) + + print("Range scan baseline: \(String(format: "%.4f", baseline))s") + print("Range scan optimized: \(String(format: "%.4f", optimized))s") + + #expect(baseline > 0) + #expect(optimized > 0) + } + + @Test + @MainActor + func inlineTextCoalescingPerformanceSmoke() throws { + let style = HTMLStyleContainer() + let elements = (0..<2000).map { _ in + InlineElement(tag: "_text", attributes: [:], text: "SwiftUIHTML ", styleContainer: style) + } + + let start = CFAbsoluteTimeGetCurrent() + let inline = HTMLInline(elements: elements) + let duration = CFAbsoluteTimeGetCurrent() - start + print("Inline merge baseline: \(String(format: "%.4f", duration))s, count \(inline.texts.count)") + + #expect(duration > 0) + } + + @Test + func cssStyleParsingPerformanceSmoke() throws { + let styles = (0..<2000).map { index in + "font-size: \(12 + index % 6)px; color: #333; margin: 4px 8px; padding: 2px; line-height: 1.4; border: 1px solid #eee;" + } + + let iterations = 4 + let durations = (0.. 0) + } +} + +private struct SimpleHTMLParser: HTMLParserable { + func parse(html: String) -> HTMLNode { + let stripped = html + .replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression) + .trimmingCharacters(in: .whitespacesAndNewlines) + if stripped.isEmpty { + return HTMLNode(tag: "div", attributes: [:], children: []) + } + return HTMLNode(tag: "div", attributes: [:], children: [.text(stripped)]) + } +} + +// Inline coalescing removed after stress benchmarking showed it was slower at scale. + +@MainActor +private func measureFramesetterCache( + attributed: NSMutableAttributedString, + ranges: [NSRange], + containerSize: CGSize, + enabled: Bool, + iterations: Int = 8 +) -> TimeInterval { + let key = "SWIFTUIHTML_CACHE_FRAMESETTER" + let previous = getenv(key) + setenv(key, enabled ? "1" : "0", 1) + defer { + if let previous { + setenv(key, previous, 1) + } else { + unsetenv(key) + } + } + + let calculator = TextRangeFrameCalculator() + let prepared = calculator.prepareCoreTextString(attributed) + var durations: [TimeInterval] = [] + durations.reserveCapacity(iterations) + + for _ in 0.. TimeInterval { + let key = "SWIFTUIHTML_DISABLE_RANGE_SCAN_OPT" + let previous = getenv(key) + if enabled { + unsetenv(key) + } else { + setenv(key, "1", 1) + } + defer { + if let previous { + setenv(key, previous, 1) + } else { + unsetenv(key) + } + } + + let calculator = TextRangeFrameCalculator() + let prepared = calculator.prepareCoreTextString(attributed) + var durations: [TimeInterval] = [] + durations.reserveCapacity(iterations) + + for _ in 0.. HTMLNode { + do { + let document = try SwiftSoup.parse(html) + if let body = document.body() { + return try elementToHTMLNode(element: body) + } else if let wrapper = try document.select("*").first() { + return try elementToHTMLNode(element: wrapper) + } + } catch { + return HTMLNode(tag: "div", attributes: [:], children: [.text("Parse error: \(error.localizedDescription)")]) + } + return HTMLNode(tag: "div", attributes: [:], children: []) + } + + private func elementToHTMLNode(element: SwiftSoup.Element) throws -> HTMLNode { + let tag = element.tagName() + let attributes = element.getAttributes()?.reduce(into: [String: String]()) { result, attribute in + result[attribute.getKey()] = attribute.getValue() + } ?? [:] + + let children: [HTMLChild] = try element.getChildNodes().compactMap { node in + if let textNode = node as? TextNode { + let text = textNode.text() + return text.isEmpty ? nil : .text(text) + } + if let elementNode = node as? Element { + if elementNode.tagName() == "br" { + return .text("\n") + } + return .node(try elementToHTMLNode(element: elementNode)) + } + return nil + } + + return HTMLNode(tag: tag, attributes: attributes, children: children) + } +} +#endif +private func syntheticHTML(sectionCount: Int, includeMedia: Bool) -> String { + var chunks: [String] = [] + chunks.reserveCapacity(sectionCount * 6) + + chunks.append(""" + +
+

SwiftUIHTML Stress Test

+

Synthetic HTML payload exercising tags, CSS, and attachments.

+
+
+ """) + + for index in 1...sectionCount { + let mediaHTML: String + if includeMedia { + mediaHTML = """ +

+ +

+ """ + } else { + mediaHTML = "" + } + + chunks.append(""" +
+

Section \(index)

+

+ Inline styles: red, + yellow, + bold, italic, underline. +

+

+ Ruby: 漢字かんじ. +

+
    +
  • Item \(index).1
  • +
  • Item \(index).2 with color
  • +
  • Item \(index).3
  • +
+ \(mediaHTML) +
+ """) + } + + chunks.append(""" +
+
+

Footer content for synthetic payload.

+
+ + """) + + return chunks.joined(separator: "\n") +} diff --git a/Tests/SwiftUIHTMLTests/RubyTagTests.swift b/Tests/SwiftUIHTMLTests/RubyTagTests.swift new file mode 100644 index 0000000..81fffea --- /dev/null +++ b/Tests/SwiftUIHTMLTests/RubyTagTests.swift @@ -0,0 +1,288 @@ +import SwiftUI +import Testing +@testable import SwiftUIHTML + +@MainActor +struct RubyTagTests { + @Test + func rubyNodeCreatesInlineAttachment() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .text("base"), + .node(HTMLNode(tag: "rt", children: [.text("ruby")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.tag == "ruby") + #expect(inline.attributes["ruby-base"]?.string == "base") + #expect(inline.attributes["ruby-text"]?.string == "ruby") + } + + @Test + func rubyNodeIgnoresRpAndUsesRbAndRt() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .node(HTMLNode(tag: "rb", children: [.text("漢字")])), + .node(HTMLNode(tag: "rp", children: [.text("(")])), + .node(HTMLNode(tag: "rt", children: [.text("かんじ")])), + .node(HTMLNode(tag: "rp", children: [.text(")")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-base"]?.string == "漢字") + #expect(inline.attributes["ruby-text"]?.string == "かんじ") + } + + @Test + func rubyNodePropagatesBaseFontIntoAttributes() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .text("猫"), + .node(HTMLNode(tag: "rt", children: [.text("ねこ")])) + ] + ) + + var container = HTMLStyleContainer() +#if os(macOS) + let font = NSFont.systemFont(ofSize: 17) +#else + let font = UIFont.systemFont(ofSize: 17) +#endif + container.uiFont = font + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: container) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-font-name"]?.string == font.fontName) + #expect(inline.attributes["ruby-font-size"]?.string == "\(font.pointSize)") + } + + @Test + func rubyNodeFlattensNestedInlineText() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .node(HTMLNode(tag: "span", children: [.text("漢")])), + .text("字"), + .node(HTMLNode(tag: "rt", children: [.text("かん")])), + .node(HTMLNode(tag: "rt", children: [.text("じ")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-base"]?.string == "漢字") + #expect(inline.attributes["ruby-text"]?.string == "かん じ") + } + + @Test + func rubyNodeWithoutRtOmitsRubyText() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .text("漢字") + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-base"]?.string == "漢字") + #expect(inline.attributes["ruby-text"] == nil) + } + + @Test + func rubyAttributesPreservePositionAndScale() { + let rubyNode = HTMLNode( + tag: "ruby", + attributes: [ + "ruby-position": "after", + "ruby-scale": "0.5" + ], + children: [ + .text("今日"), + .node(HTMLNode(tag: "rt", children: [.text("きょう")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-position"]?.string == "after") + #expect(inline.attributes["ruby-scale"]?.string == "0.5") + } + + @Test + func rubyNodeKeepsOriginalAttributes() { + let rubyNode = HTMLNode( + tag: "ruby", + attributes: [ + "style": "color: #f00;", + "data-test": "ruby" + ], + children: [ + .text("漢字"), + .node(HTMLNode(tag: "rt", children: [.text("かんじ")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["style"]?.string == "color: #f00;") + #expect(inline.attributes["data-test"]?.string == "ruby") + #expect(inline.attributes["ruby-base"]?.string == "漢字") + #expect(inline.attributes["ruby-text"]?.string == "かんじ") + } + + @Test + func rubyNodeWithOnlyRtDoesNotCreateRubyBase() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .node(HTMLNode(tag: "rt", children: [.text("かんじ")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-base"] == nil) + #expect(inline.attributes["ruby-text"] == nil) + } + + @Test + func rubyNodePreservesBaseWhitespace() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .text(" 漢字 "), + .node(HTMLNode(tag: "rt", children: [.text("かんじ")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-base"]?.string == " 漢字 ") + #expect(inline.attributes["ruby-text"]?.string == "かんじ") + } + + @Test + func rubyNodeTrimsRtWhitespace() { + let rubyNode = HTMLNode( + tag: "ruby", + children: [ + .text("漢字"), + .node(HTMLNode(tag: "rt", children: [.text(" かんじ \n")])), + .node(HTMLNode(tag: "rt", children: [.text("\tけんじ ")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-text"]?.string == "かんじ けんじ") + } + + @Test + func rubyNodePreservesRubyFontAttributes() { + let rubyNode = HTMLNode( + tag: "ruby", + attributes: [ + "ruby-font-name": "HelveticaNeue", + "ruby-font-size": "22", + "ruby-annotation-font-name": "HiraginoSans-W3", + "ruby-annotation-font-size": "12" + ], + children: [ + .text("明日"), + .node(HTMLNode(tag: "rt", children: [.text("あした")])) + ] + ) + + let root = HTMLNode(tag: "div", children: [.node(rubyNode)]) + let element = root.toElement(configuration: .default, with: HTMLStyleContainer()) + + #expect(element.contents.count == 1) + guard case let .inline(inline) = element.contents[0] else { + Issue.record("Expected inline element for ruby attachment") + return + } + + #expect(inline.attributes["ruby-font-name"]?.string == "HelveticaNeue") + #expect(inline.attributes["ruby-font-size"]?.string == "22") + #expect(inline.attributes["ruby-annotation-font-name"]?.string == "HiraginoSans-W3") + #expect(inline.attributes["ruby-annotation-font-size"]?.string == "12") + } +} diff --git a/Tests/SwiftUIHTMLTests/SwiftSoupParserTests.swift b/Tests/SwiftUIHTMLTests/SwiftSoupParserTests.swift new file mode 100644 index 0000000..3e151a4 --- /dev/null +++ b/Tests/SwiftUIHTMLTests/SwiftSoupParserTests.swift @@ -0,0 +1,192 @@ +// Copyright © 2026 PRND. All rights reserved. + +import Testing +import SwiftSoup +@testable import SwiftUIHTML + +struct SwiftSoupParserTests { + @Test + func swiftSoupParserMapsNodesAttributesAndLineBreaks() { + let html = """ + +

Hello
World

+ 漢字かんじ + + """ + let parser = HTMLSwiftSoupParser() + let root = parser.parse(html: html) + + #expect(root.tag == "body") + + let paragraph = firstNode(tag: "p", in: root) + #expect(paragraph?.attributes["data-x"]?.string == "1") + let paragraphText = paragraph?.children.compactMap(textValue(from:)) ?? [] + #expect(paragraphText.contains("Hello")) + #expect(paragraphText.contains("\n")) + #expect(paragraphText.contains("World")) + + let rubyNode = firstNode(tag: "ruby", in: root) + #expect(rubyNode?.attributes["ruby-position"]?.string == "after") + #expect(rubyNode?.children.contains(where: { child in + if case let .text(text) = child { return text.contains("漢字") } + return false + }) == true) + let rtNode = rubyNode.flatMap { firstNode(tag: "rt", in: $0) } + #expect(rtNode?.children.compactMap(textValue(from:)).contains("かんじ") == true) + } + + @Test + func swiftSoupParserDropsEmptyTextNodes() { + let html = """ + +
+
Value
+ + """ + let parser = HTMLSwiftSoupParser() + let root = parser.parse(html: html) + + let emptyDiv = firstNode(tag: "div", in: root) + let emptyText = emptyDiv?.children.compactMap(textValue(from:)) ?? [] + let trimmedEmptyText = emptyText.joined().trimmingCharacters(in: .whitespacesAndNewlines) + #expect(trimmedEmptyText.isEmpty) + + let secondDiv = secondNode(tag: "div", in: root) + let text = secondDiv?.children.compactMap(textValue(from:)) ?? [] + #expect(text == ["Value"]) + } + + @Test + func swiftSoupParserNormalizesWhitespaceAndNestedEmphasis() { + let html = """ + +

Alpha beta gamma delta

+

spaced text

+ + """ + let parser = HTMLSwiftSoupParser() + let root = parser.parse(html: html) + + let firstParagraph = firstNode(tag: "p", in: root) + let firstParagraphText = allText(in: firstParagraph) + #expect(firstParagraphText.contains("Alpha")) + #expect(firstParagraphText.contains("beta")) + #expect(firstParagraphText.contains("gamma")) + #expect(firstParagraphText.contains("delta")) + + let secondParagraph = secondNode(tag: "p", in: root) + let secondText = secondParagraph?.children.compactMap(textValue(from:)).joined() ?? "" + let normalized = secondText.replacingOccurrences(of: "\\s+", with: " ", options: .regularExpression).trimmingCharacters(in: .whitespacesAndNewlines) + #expect(normalized == "spaced text") + } + + @Test + func swiftSoupParserHandlesAttributesAndInlineTags() { + let html = """ + +

Red Link

+ + """ + let parser = HTMLSwiftSoupParser() + let root = parser.parse(html: html) + + let paragraph = firstNode(tag: "p", in: root) + #expect(paragraph?.attributes["data-id"]?.string == "42") + + let spanNode = firstNode(tag: "span", in: root) + #expect(spanNode?.attributes["style"]?.string.contains("color") == true) + + let linkNode = firstNode(tag: "a", in: root) + #expect(linkNode?.attributes["href"]?.string == "https://example.com") + } +} + +private struct HTMLSwiftSoupParser: HTMLParserable { + func parse(html: String) -> HTMLNode { + do { + let document = try SwiftSoup.parse(html) + if let body = document.body() { + return try elementToHTMLNode(element: body) + } else if let wrapper = try document.select("*").first() { + return try elementToHTMLNode(element: wrapper) + } else { + return HTMLNode(tag: "div", attributes: [:], children: []) + } + } catch { + return HTMLNode(tag: "div", attributes: [:], children: [.text("Parse error: \(error.localizedDescription)")]) + } + } + + private func elementToHTMLNode(element: SwiftSoup.Element) throws -> HTMLNode { + let tag = element.tagName() + let attributes = element.getAttributes()?.reduce(into: [String: String]()) { result, attribute in + result[attribute.getKey()] = attribute.getValue() + } ?? [:] + let children: [HTMLChild] = try element.getChildNodes().compactMap { node in + if let textNode = node as? TextNode { + let text = textNode.text() + return text.isEmpty ? nil : .text(text) + } + if let elementNode = node as? Element { + if elementNode.tagName() == "br" { + return .text("\n") + } + return .node(try elementToHTMLNode(element: elementNode)) + } + return nil + } + return HTMLNode(tag: tag, attributes: attributes, children: children) + } +} + +private func firstNode(tag: String, in node: HTMLNode) -> HTMLNode? { + for child in node.children { + if case let .node(childNode) = child { + if childNode.tag == tag { + return childNode + } + if let found = firstNode(tag: tag, in: childNode) { + return found + } + } + } + return nil +} + +private func secondNode(tag: String, in node: HTMLNode) -> HTMLNode? { + var matches: [HTMLNode] = [] + collectNodes(tag: tag, in: node, results: &matches) + return matches.count > 1 ? matches[1] : nil +} + +private func collectNodes(tag: String, in node: HTMLNode, results: inout [HTMLNode]) { + for child in node.children { + if case let .node(childNode) = child { + if childNode.tag == tag { + results.append(childNode) + } + collectNodes(tag: tag, in: childNode, results: &results) + } + } +} + +private func textValue(from child: HTMLChild) -> String? { + if case let .text(text) = child { + return text + } + return nil +} + +private func allText(in node: HTMLNode?) -> String { + guard let node else { return "" } + var result = "" + for child in node.children { + switch child { + case let .text(text): + result.append(text) + case let .node(inner): + result.append(allText(in: inner)) + } + } + return result +} From 0e96984e5006bdb0de39145c4d2439a088825164 Mon Sep 17 00:00:00 2001 From: Alex Ehlke Date: Sat, 24 Jan 2026 18:57:33 -0500 Subject: [PATCH 04/12] wip fixing tests --- .../Attachment/AttachmentDebugLogger.swift | 59 ++ .../Attachment/AttachmentLayoutManager.swift | 48 +- .../Core/Attachment/AttachmentManager.swift | 42 +- .../CoreText/TextRangeFrameCalculator.swift | 4 +- .../Core/Platform/PlatformTypes.swift | 10 +- .../SwiftUIHTML/Tag/Attachment/ImageTag.swift | 20 +- Sources/SwiftUIHTML/View/HTMLInline.swift | 19 + .../View/HTMLTextLayout/HTMLTextLayout.swift | 72 ++- SwiftUIHTMLExample.xcodeproj/project.pbxproj | 122 ++++ .../SwiftUIHTMLExampleMacOS.xcscheme | 96 ++++ SwiftUIHTMLExampleMacOS.xctestplan | 29 + .../MacViewSnapshotTester.swift | 189 +++++++ .../YomitanSnapshotTests.swift | 535 ++++++++++++++++++ ...tComplexImageLayout.complexImageLayout.png | Bin 0 -> 10205 bytes ...width-lineBreakMode.375-byCharWrapping.png | Bin 0 -> 85176 bytes ...width-lineBreakMode.375-byWordWrapping.png | Bin 0 -> 83972 bytes .../testHTMLImageWithLineHeight.1.png | Bin 0 -> 9014 bytes ...dLineHeight.imagePositionAndLineHeight.png | Bin 0 -> 9454 bytes ...ode.longWordsWithImages_byCharWrapping.png | Bin 0 -> 9577 bytes ...ode.longWordsWithImages_byWordWrapping.png | Bin 0 -> 10174 bytes ...extWithImages.nonEnglishTextWithImages.png | Bin 0 -> 10938 bytes ...YomitanEntrySnapshotMacOS.yomitanEntry.png | Bin 0 -> 9310 bytes ...LElements-lineBreakMode.byCharWrapping.png | Bin 0 -> 49740 bytes ...LElements-lineBreakMode.byWordWrapping.png | Bin 0 -> 51437 bytes ...LineBreak-lineBreakMode.byCharWrapping.png | Bin 0 -> 12423 bytes ...LineBreak-lineBreakMode.byWordWrapping.png | Bin 0 -> 12423 bytes ...LElements-lineBreakMode.byCharWrapping.png | Bin 0 -> 114990 bytes ...LElements-lineBreakMode.byWordWrapping.png | Bin 0 -> 117592 bytes .../testingWordBrek.1.png | Bin 0 -> 358774 bytes SwiftUIHTMLExampleTests/HTMLBasicTests.swift | 67 ++- .../ViewSnapshotTester.swift | 197 ++++++- .../testYomitanEntrySnapshot.yomitanEntry.png | Bin 0 -> 41138 bytes ...YomitanEntrySnapshotMacOS.yomitanEntry.png | Bin 0 -> 31345 bytes .../SwiftSoupParserTests.swift | 1 + mise.toml | 23 + 35 files changed, 1490 insertions(+), 43 deletions(-) create mode 100644 Sources/SwiftUIHTML/Core/Attachment/AttachmentDebugLogger.swift create mode 100644 SwiftUIHTMLExample.xcodeproj/xcshareddata/xcschemes/SwiftUIHTMLExampleMacOS.xcscheme create mode 100644 SwiftUIHTMLExampleMacOS.xctestplan create mode 100644 SwiftUIHTMLExampleMacOSTests/MacViewSnapshotTester.swift create mode 100644 SwiftUIHTMLExampleMacOSTests/YomitanSnapshotTests.swift create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testComplexImageLayout.complexImageLayout.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testHTMLImageAndNesting-width-lineBreakMode.375-byCharWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testHTMLImageAndNesting-width-lineBreakMode.375-byWordWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testHTMLImageWithLineHeight.1.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testImagePositionAndLineHeight.imagePositionAndLineHeight.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testLongWordsWithImages-lineBreakMode.longWordsWithImages_byCharWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testLongWordsWithImages-lineBreakMode.longWordsWithImages_byWordWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testNonEnglishTextWithImages.nonEnglishTextWithImages.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testYomitanEntrySnapshotMacOS.yomitanEntry.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingBasicHTMLElements-lineBreakMode.byCharWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingBasicHTMLElements-lineBreakMode.byWordWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingLineBreak-lineBreakMode.byCharWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingLineBreak-lineBreakMode.byWordWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingStyleHTMLElements-lineBreakMode.byCharWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingStyleHTMLElements-lineBreakMode.byWordWrapping.png create mode 100644 SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingWordBrek.1.png create mode 100644 SwiftUIHTMLExampleTests/__Snapshots__/HTMLBasicTests/testYomitanEntrySnapshot.yomitanEntry.png create mode 100644 SwiftUIHTMLExampleTests/__Snapshots__/HTMLBasicTests/testYomitanEntrySnapshotMacOS.yomitanEntry.png create mode 100644 mise.toml diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentDebugLogger.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentDebugLogger.swift new file mode 100644 index 0000000..b088950 --- /dev/null +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentDebugLogger.swift @@ -0,0 +1,59 @@ +// Copyright © 2025 PRND. All rights reserved. +import Foundation + +enum AttachmentDebugLogger { + private static nonisolated(unsafe) let lock = NSLock() + private static nonisolated(unsafe) var buffer: [String] = [] + private static let logFileURL = URL(fileURLWithPath: NSTemporaryDirectory()) + .appendingPathComponent("swiftuihtml-attachment.log") + + static func record(_ message: String) { + lock.lock() + buffer.append(message) + if let data = (message + "\n").data(using: .utf8) { + if let handle = try? FileHandle(forWritingTo: logFileURL) { + handle.seekToEndOfFile() + try? handle.write(contentsOf: data) + try? handle.close() + } else { + try? data.write(to: logFileURL) + } + } + lock.unlock() + } + + static func recordOnce(_ key: String, message: @autoclosure () -> String) { + lock.lock() + if buffer.contains(key) { + lock.unlock() + return + } + buffer.append(key) + let rendered = message() + buffer.append(rendered) + if let data = (rendered + "\n").data(using: .utf8) { + if let handle = try? FileHandle(forWritingTo: logFileURL) { + handle.seekToEndOfFile() + try? handle.write(contentsOf: data) + try? handle.close() + } else { + try? data.write(to: logFileURL) + } + } + lock.unlock() + } + + static func dump() -> String { + lock.lock() + let output = buffer.joined(separator: "\n") + lock.unlock() + return output + } + + static func clear() { + lock.lock() + buffer.removeAll(keepingCapacity: true) + try? FileManager.default.removeItem(at: logFileURL) + lock.unlock() + } +} diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift index 3db8a29..61d52cd 100644 --- a/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentLayoutManager.swift @@ -29,6 +29,7 @@ final class AttachmentLayoutEngine { private static let signposter = OSSignposter( logHandle: OSLog(subsystem: "SwiftUIHTML", category: "AttachmentLayout") ) + private static let logger = Logger(subsystem: "SwiftUIHTML", category: "AttachmentLayout") #endif var lineSpacing: CGFloat { @@ -42,6 +43,25 @@ final class AttachmentLayoutEngine { setup() } + private var shouldLog: Bool { + ProcessInfo.processInfo.environment["SWIFTUIHTML_ATTACHMENT_LOGS"] == "1" + || UserDefaults.standard.bool(forKey: "SWIFTUIHTML_ATTACHMENT_LOGS") + || NSClassFromString("XCTestCase") != nil + } + + private func log(_ message: @autoclosure () -> String) { + guard shouldLog else { return } + let rendered = message() + AttachmentDebugLogger.record("[AttachmentLayout] \(rendered)") +#if canImport(os) + if #available(iOS 14.0, macOS 11.0, *) { + AttachmentLayoutEngine.logger.debug("\(rendered, privacy: .public)") + } +#endif + NSLog("[SwiftUIHTML][AttachmentLayout] %@", rendered) + print("[SwiftUIHTML][AttachmentLayout] \(rendered)") + } + func setup() { let attributedStringPublisher = _attributedString.projectedValue .compactMap { $0 } @@ -70,6 +90,7 @@ final class AttachmentLayoutEngine { let oldSize = textAttachment.value?.bounds.size ?? .zero guard oldSize != size else { return } textAttachment.value?.updateSize(size) + log("setSize key=\(key) size=\(size)") layoutUpdatePublisher.send(()) } @@ -81,7 +102,9 @@ final class AttachmentLayoutEngine { func getOffset(key: AnyHashable) -> CGPoint { let point = frameStore[key]?.origin ?? .zero guard let textAttachment = keyAttachment[key] else { return point } - return textAttachment.value?.getAdjustedOffset(point: point) ?? point + let adjusted = textAttachment.value?.getAdjustedOffset(point: point) ?? point + log("getOffset key=\(key) frame=\(String(describing: frameStore[key])) adjusted=\(adjusted)") + return adjusted } @@ -90,6 +113,7 @@ final class AttachmentLayoutEngine { return } containerSize = size + log("setContainerSize \(size)") } @MainActor @@ -146,6 +170,12 @@ private extension AttachmentLayoutEngine { for element in texts { switch element { case let .text(string, styleContainer): + if let uiFont = styleContainer.uiFont { + AttachmentDebugLogger.recordOnce( + "uiFont:\(uiFont.fontName):\(uiFont.pointSize)", + message: "[Font] uiFont name=\(uiFont.fontName) size=\(uiFont.pointSize)" + ) + } let text = makeAttributedString( string: string, styleContainer: styleContainer @@ -153,6 +183,12 @@ private extension AttachmentLayoutEngine { attributedString.append(text) case let .newLine(styleContainer): + if let uiFont = styleContainer.uiFont { + AttachmentDebugLogger.recordOnce( + "uiFont:\(uiFont.fontName):\(uiFont.pointSize)", + message: "[Font] uiFont name=\(uiFont.fontName) size=\(uiFont.pointSize)" + ) + } let text = makeAttributedString( string: "\n", styleContainer: styleContainer @@ -160,6 +196,12 @@ private extension AttachmentLayoutEngine { attributedString.append(text) case let .attachment(_, _, _, styleContainer): + if let uiFont = styleContainer.uiFont { + AttachmentDebugLogger.recordOnce( + "uiFont:\(uiFont.fontName):\(uiFont.pointSize)", + message: "[Font] uiFont name=\(uiFont.fontName) size=\(uiFont.pointSize)" + ) + } let attachment = TextAttachment( key: element, styleContainer: styleContainer @@ -222,6 +264,8 @@ private extension AttachmentLayoutEngine { return } frameStore[key] = bounds + let container = containerSize ?? .zero + log("storeRangeBounds key=\(key) bounds=\(bounds) container=\(container)") layoutUpdatePublisher.send(()) } @@ -234,6 +278,8 @@ private extension AttachmentLayoutEngine { } } if didChange { + let container = containerSize ?? .zero + log("storeRangeBounds batch count=\(values.count) container=\(container)") layoutUpdatePublisher.send(()) } } diff --git a/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift b/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift index e4707f4..26fa14d 100644 --- a/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift +++ b/Sources/SwiftUIHTML/Core/Attachment/AttachmentManager.swift @@ -2,11 +2,22 @@ import Combine import CoreGraphics import Foundation +#if canImport(os) +import os +#endif final class AttachmentManager: ObservableObject { lazy var layoutEngine = AttachmentLayoutEngine() private var cancellables = Set() + private var shouldLog: Bool { + ProcessInfo.processInfo.environment["SWIFTUIHTML_ATTACHMENT_LOGS"] == "1" + || UserDefaults.standard.bool(forKey: "SWIFTUIHTML_ATTACHMENT_LOGS") + || NSClassFromString("XCTestCase") != nil + } +#if canImport(os) + private static let logger = Logger(subsystem: "SwiftUIHTML", category: "AttachmentManager") +#endif private class ImageCache: NSCache {} private let textImages = ImageCache() @@ -36,16 +47,22 @@ final class AttachmentManager: ObservableObject { layoutEngine.setSize(key: key, size: size) //NOTE: lineSpacing 각각 다를 수 있음 layoutEngine.lineSpacing = styleContainer.textLine?.lineSpacing ?? 0 + log("setAttachmentSize key=\(key) size=\(size) lineSpacing=\(layoutEngine.lineSpacing)") } func offset(key: AnyHashable) -> CGSize { let point = layoutEngine.getOffset(key: key) + log("offset key=\(key) point=\(point)") return CGSize(width: point.x, height: point.y) } // SwiftUI Text(image:) 주입 이미지 func sizeImage(key: AnyHashable, styleContainer: HTMLStyleContainer) -> PlatformImage { - let size = layoutEngine.getSize(key: key) + var size = layoutEngine.getSize(key: key) + if size == .zero, let fallbackSize = fallbackAttachmentSize(for: key) { + size = fallbackSize + log("fallback size key=\(key) size=\(size)") + } if size == .zero { return PlatformImage.manabiEmpty(size: .zero) } @@ -70,12 +87,35 @@ final class AttachmentManager: ObservableObject { return image } + private func fallbackAttachmentSize(for key: AnyHashable) -> CGSize? { + guard let textType = key.base as? TextType else { return nil } + guard case let .attachment(_, _, attributes, _) = textType else { return nil } + let elementSize = ElementSize(attributes: attributes) + guard let width = elementSize.width ?? elementSize.height, + let height = elementSize.height ?? elementSize.width else { + return nil + } + return CGSize(width: max(1, width), height: max(1, height)) + } + func clearImageCache() { textImages.removeAllObjects() } } private extension AttachmentManager { + func log(_ message: @autoclosure () -> String) { + guard shouldLog else { return } + let rendered = message() + AttachmentDebugLogger.record("[AttachmentManager] \(rendered)") +#if canImport(os) + if #available(iOS 14.0, macOS 11.0, *) { + AttachmentManager.logger.debug("\(rendered, privacy: .public)") + } +#endif + NSLog("[SwiftUIHTML][AttachmentManager] %@", rendered) + print("[SwiftUIHTML][AttachmentManager] \(rendered)") + } } private struct EmptyImage { diff --git a/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift b/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift index cf49663..0d0e652 100644 --- a/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift +++ b/Sources/SwiftUIHTML/Core/CoreText/TextRangeFrameCalculator.swift @@ -245,7 +245,7 @@ private extension TextRangeFrameCalculator { // Create rect for each character range for charFrame in characterRects { let lineRect = CGRect( - x: charFrame.origin.x, + x: charFrame.origin.x + lineOrigin.x, y: lineOrigin.y, width: charFrame.size.width, height: charFrame.size.height @@ -316,7 +316,7 @@ private extension TextRangeFrameCalculator { for charFrame in characterRects { let lineRect = CGRect( - x: charFrame.origin.x, + x: charFrame.origin.x + lineOrigin.x, y: lineOrigin.y, width: charFrame.size.width, height: charFrame.size.height diff --git a/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift b/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift index 18e02cc..6f52051 100644 --- a/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift +++ b/Sources/SwiftUIHTML/Core/Platform/PlatformTypes.swift @@ -20,7 +20,15 @@ internal typealias PlatformFontDescriptor = UIFontDescriptor internal extension PlatformFont { var manabiCTFont: CTFont { - CTFontCreateWithName(fontName as CFString, pointSize, nil) + if fontName.hasPrefix(".SF") || fontName.contains("SFUI") || fontName.contains("SFNS") { + AttachmentDebugLogger.recordOnce( + "font:\(fontName):\(pointSize)", + message: "[Font] system font name=\(fontName) size=\(pointSize)" + ) + return CTFontCreateUIFontForLanguage(.system, pointSize, nil) + ?? CTFontCreateWithName(fontName as CFString, pointSize, nil) + } + return CTFontCreateWithName(fontName as CFString, pointSize, nil) } var manabiLineHeight: CGFloat { diff --git a/Sources/SwiftUIHTML/Tag/Attachment/ImageTag.swift b/Sources/SwiftUIHTML/Tag/Attachment/ImageTag.swift index 2107a93..a1e1ff4 100644 --- a/Sources/SwiftUIHTML/Tag/Attachment/ImageTag.swift +++ b/Sources/SwiftUIHTML/Tag/Attachment/ImageTag.swift @@ -9,21 +9,29 @@ struct ImageTag: InlineAttachmentTag { self.attributes = attributes } var body: some View { + let size = ElementSize(attributes: attributes) AsyncImage(url: attributes["src"]?.url) { phase in switch phase { case .success(let image): - ImageContainer( - image: image, - size: ElementSize(attributes: attributes) - ) + ImageContainer(image: image, size: size) default: - Color.clear - .frame(width: 1, height: 1) + ImagePlaceholder(size: size) } } } } +private struct ImagePlaceholder: View { + let size: ElementSize + var body: some View { + if size.width == nil && size.height == nil { + Color.clear.frame(width: 1, height: 1) + } else { + Color.clear.modifier(SizeModifier(size: size)) + } + } +} + private struct ImageContainer: View { let image: Image let size: ElementSize diff --git a/Sources/SwiftUIHTML/View/HTMLInline.swift b/Sources/SwiftUIHTML/View/HTMLInline.swift index b120845..63869d1 100644 --- a/Sources/SwiftUIHTML/View/HTMLInline.swift +++ b/Sources/SwiftUIHTML/View/HTMLInline.swift @@ -12,6 +12,7 @@ struct HTMLInline: View { let hasAttachment: Bool let textLine: TextLine @StateObject var attachmentManager = AttachmentManager() + @HTMLEnvironment(\.styleContainer) private var styleContainer init(elements: [InlineElement]) { self.init(texts: elements.toHTMLTextType()) @@ -29,6 +30,24 @@ struct HTMLInline: View { .fixedSize(horizontal: false, vertical: true) .overlay(alignment: .topLeading) { htmlTextLayout } .modifier(TextLineModifier(textLine: textLine)) + .onAppear { + let styleLineSpacing = styleContainer.textLine?.lineSpacing ?? 0 + let styleVerticalPadding = styleContainer.textLine?.verticalPadding ?? 0 + let effectiveLineSpacing = max(textLine.lineSpacing, styleLineSpacing) + let effectiveVerticalPadding = max(textLine.verticalPadding, styleVerticalPadding) + AttachmentDebugLogger.record( + "[Inline] textLine spacing=\(textLine.lineSpacing) verticalPadding=\(textLine.verticalPadding) styleLineSpacing=\(styleLineSpacing) styleVerticalPadding=\(styleVerticalPadding) effectiveLineSpacing=\(effectiveLineSpacing) effectiveVerticalPadding=\(effectiveVerticalPadding)" + ) + } + .background( + GeometryReader { proxy in + Color.clear + .hidden() + .modifier(OnChangeViewModifier(of: proxy.size, initial: true) { _, newValue in + AttachmentDebugLogger.record("[Inline] contentSize=\(newValue)") + }) + } + ) } @ViewBuilder diff --git a/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift b/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift index c776e1f..5aa6407 100644 --- a/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift +++ b/Sources/SwiftUIHTML/View/HTMLTextLayout/HTMLTextLayout.swift @@ -1,5 +1,8 @@ // Copyright © 2024 PRND. All rights reserved. import SwiftUI +#if canImport(os) +import os +#endif struct HTMLTextLayout: View { let texts: [TextType] @@ -7,6 +10,14 @@ struct HTMLTextLayout: View { @ObservedObject var attachmentManager: AttachmentManager @HTMLEnvironment(\._configuration) var configuration + private var shouldLog: Bool { + ProcessInfo.processInfo.environment["SWIFTUIHTML_ATTACHMENT_LOGS"] == "1" + || UserDefaults.standard.bool(forKey: "SWIFTUIHTML_ATTACHMENT_LOGS") + || NSClassFromString("XCTestCase") != nil + } +#if canImport(os) + private static let logger = Logger(subsystem: "SwiftUIHTML", category: "HTMLTextLayout") +#endif init(attachmentManager: AttachmentManager, texts: [TextType]) { self.texts = texts @@ -21,19 +32,23 @@ struct HTMLTextLayout: View { .hidden() .modifier(OnChangeViewModifier(of: proxy.size, initial: true) { _, newValue in attachmentManager.setContainer(size: proxy.size) + log("containerSize=\(newValue)") }) - ForEach(attachmentTexts, id: \.hashValue) { text in + ForEach(attachmentTexts, id: \.self) { text in attachmentView(for: text) } } + .coordinateSpace(name: "HTMLTextLayoutSpace") } @ViewBuilder private func attachmentView(for text: TextType) -> some View { if let info = extractAttachmentInfo(from: text) { + let offset = attachmentManager.offset(key: text) info.view .modifier(LinkModifier(link: info.link)) - .offset(attachmentManager.offset(key: text)) + .offset(offset) + .background(attachmentFrameReader(for: text, offset: offset)) .background(attachmentSizeReader(for: text, styleContainer: info.styleContainer)) } } @@ -56,7 +71,60 @@ struct HTMLTextLayout: View { size: newValue, styleContainer: styleContainer ) + logAttachmentSize(text: text, size: newValue) + }) + } + } + + private func attachmentFrameReader(for text: TextType, offset: CGSize) -> some View { + GeometryReader { proxy in + Color.clear + .hidden() + .modifier(OnChangeViewModifier(of: proxy.frame(in: .named("HTMLTextLayoutSpace")), initial: true) { _, newValue in + logAttachmentFrame(text: text, frame: newValue, offset: offset) }) } } + + private func attachmentDebugInfo(for text: TextType) -> (id: AnyHashable, tag: String, attributes: [String: AttributeValue])? { + switch text { + case let .attachment(id, tag, attributes, _): + return (id, tag, attributes) + default: + return nil + } + } + + private func log(_ message: @autoclosure () -> String) { + guard shouldLog else { return } + let rendered = message() + AttachmentDebugLogger.record("[HTMLTextLayout] \(rendered)") +#if canImport(os) + if #available(iOS 14.0, macOS 11.0, *) { + HTMLTextLayout.logger.debug("\(rendered, privacy: .public)") + } +#endif + NSLog("[SwiftUIHTML][HTMLTextLayout] %@", rendered) + print("[SwiftUIHTML][HTMLTextLayout] \(rendered)") + } + + private func logAttachmentSize(text: TextType, size: CGSize) { + guard shouldLog else { return } + if let info = attachmentDebugInfo(for: text) { + let keys = info.attributes.keys.sorted() + log("attachmentSize id=\(info.id) tag=\(info.tag) size=\(size) attrs=\(keys)") + } else { + log("attachmentSize size=\(size)") + } + } + + private func logAttachmentFrame(text: TextType, frame: CGRect, offset: CGSize) { + guard shouldLog else { return } + if let info = attachmentDebugInfo(for: text) { + let keys = info.attributes.keys.sorted() + log("attachmentFrame id=\(info.id) tag=\(info.tag) frame=\(frame) offset=\(offset) attrs=\(keys)") + } else { + log("attachmentFrame frame=\(frame) offset=\(offset)") + } + } } diff --git a/SwiftUIHTMLExample.xcodeproj/project.pbxproj b/SwiftUIHTMLExample.xcodeproj/project.pbxproj index 23ba08d..888f4f6 100644 --- a/SwiftUIHTMLExample.xcodeproj/project.pbxproj +++ b/SwiftUIHTMLExample.xcodeproj/project.pbxproj @@ -10,8 +10,11 @@ 261846002DBF67F7001739B1 /* Fuzi in Frameworks */ = {isa = PBXBuildFile; productRef = 261845FF2DBF67F7001739B1 /* Fuzi */; }; 261CAB2B2DBB7BBF0015BD7E /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 261CAB2A2DBB7BBF0015BD7E /* SnapshotTesting */; }; 261CAB2E2DBB7BCF0015BD7E /* Fuzi in Frameworks */ = {isa = PBXBuildFile; productRef = 261CAB2D2DBB7BCF0015BD7E /* Fuzi */; }; + 27585C06A5564924A5E83908 /* SwiftUIHTML in Frameworks */ = {isa = PBXBuildFile; productRef = 26F6EF132DC1E8F600024B85 /* SwiftUIHTML */; }; + 2FFF49397FD849E7A06FC80A /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 261CAB2A2DBB7BBF0015BD7E /* SnapshotTesting */; }; 26F6EF122DC1E8E400024B85 /* SwiftUIHTML in Frameworks */ = {isa = PBXBuildFile; productRef = 26F6EF112DC1E8E400024B85 /* SwiftUIHTML */; }; 26F6EF142DC1E8F600024B85 /* SwiftUIHTML in Frameworks */ = {isa = PBXBuildFile; productRef = 26F6EF132DC1E8F600024B85 /* SwiftUIHTML */; }; + 48D828A9F4CE40D88A69C059 /* Fuzi in Frameworks */ = {isa = PBXBuildFile; productRef = 261CAB2D2DBB7BCF0015BD7E /* Fuzi */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -27,8 +30,10 @@ /* Begin PBXFileReference section */ 261CA9C22DBB7ADD0015BD7E /* SwiftUIHTMLExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIHTMLExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 261CAB1C2DBB7B840015BD7E /* SwiftUIHTMLExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUIHTMLExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 02EE68CE6E9340E5B60E57CF /* SwiftUIHTMLExampleMacOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SwiftUIHTMLExampleMacOS.xctestplan; sourceTree = ""; }; 269A4B682DC1F79200E54076 /* SwiftUIHTMLExample.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SwiftUIHTMLExample.xctestplan; sourceTree = ""; }; 26FC51D22DCB43E3005B4BD3 /* SwiftUIHTMLExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "SwiftUIHTMLExample-Info.plist"; sourceTree = ""; }; + E5E45B1DB81A46819201D9EB /* SwiftUIHTMLExampleMacOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUIHTMLExampleMacOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -42,6 +47,11 @@ path = SwiftUIHTMLExampleTests; sourceTree = ""; }; + 37FE6D108A4A4185A8E85107 /* SwiftUIHTMLExampleMacOSTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = SwiftUIHTMLExampleMacOSTests; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -64,6 +74,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E2F57790BBD346D689B33974 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 48D828A9F4CE40D88A69C059 /* Fuzi in Frameworks */, + 27585C06A5564924A5E83908 /* SwiftUIHTML in Frameworks */, + 2FFF49397FD849E7A06FC80A /* SnapshotTesting in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -79,8 +99,10 @@ children = ( 26FC51D22DCB43E3005B4BD3 /* SwiftUIHTMLExample-Info.plist */, 269A4B682DC1F79200E54076 /* SwiftUIHTMLExample.xctestplan */, + 02EE68CE6E9340E5B60E57CF /* SwiftUIHTMLExampleMacOS.xctestplan */, 261CA9C42DBB7ADD0015BD7E /* Example */, 261CAB1D2DBB7B840015BD7E /* SwiftUIHTMLExampleTests */, + 37FE6D108A4A4185A8E85107 /* SwiftUIHTMLExampleMacOSTests */, 261CA9C32DBB7ADD0015BD7E /* Products */, 261845FE2DBF67F7001739B1 /* Frameworks */, ); @@ -91,6 +113,7 @@ children = ( 261CA9C22DBB7ADD0015BD7E /* SwiftUIHTMLExample.app */, 261CAB1C2DBB7B840015BD7E /* SwiftUIHTMLExampleTests.xctest */, + E5E45B1DB81A46819201D9EB /* SwiftUIHTMLExampleMacOSTests.xctest */, ); name = Products; sourceTree = ""; @@ -148,6 +171,32 @@ productReference = 261CAB1C2DBB7B840015BD7E /* SwiftUIHTMLExampleTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 8654A28F6ECF41C8B9CCED96 /* SwiftUIHTMLExampleMacOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 992017C20FAE49F19FEB0F43 /* Build configuration list for PBXNativeTarget "SwiftUIHTMLExampleMacOSTests" */; + buildPhases = ( + B7531A2BADFF4DF2AB734195 /* Sources */, + E2F57790BBD346D689B33974 /* Frameworks */, + B44562C4FB84419EB6FFBA5E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + AE5B6A76B6A846F3899E6CB5 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 37FE6D108A4A4185A8E85107 /* SwiftUIHTMLExampleMacOSTests */, + ); + name = SwiftUIHTMLExampleMacOSTests; + packageProductDependencies = ( + 261CAB2A2DBB7BBF0015BD7E /* SnapshotTesting */, + 261CAB2D2DBB7BCF0015BD7E /* Fuzi */, + 26F6EF132DC1E8F600024B85 /* SwiftUIHTML */, + ); + productName = SwiftUIHTMLExampleMacOSTests; + productReference = E5E45B1DB81A46819201D9EB /* SwiftUIHTMLExampleMacOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -165,6 +214,10 @@ CreatedOnToolsVersion = 16.2; TestTargetID = 261CA9C12DBB7ADD0015BD7E; }; + 8654A28F6ECF41C8B9CCED96 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = 261CA9C12DBB7ADD0015BD7E; + }; }; }; buildConfigurationList = 261CA9BD2DBB7ADD0015BD7E /* Build configuration list for PBXProject "SwiftUIHTMLExample" */; @@ -188,6 +241,7 @@ targets = ( 261CA9C12DBB7ADD0015BD7E /* SwiftUIHTMLExample */, 261CAB1B2DBB7B840015BD7E /* SwiftUIHTMLExampleTests */, + 8654A28F6ECF41C8B9CCED96 /* SwiftUIHTMLExampleMacOSTests */, ); }; /* End PBXProject section */ @@ -207,6 +261,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + B44562C4FB84419EB6FFBA5E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -224,6 +285,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + B7531A2BADFF4DF2AB734195 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -232,6 +300,11 @@ target = 261CA9C12DBB7ADD0015BD7E /* SwiftUIHTMLExample */; targetProxy = 261CAB202DBB7B840015BD7E /* PBXContainerItemProxy */; }; + AE5B6A76B6A846F3899E6CB5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 261CA9C12DBB7ADD0015BD7E /* SwiftUIHTMLExample */; + targetProxy = 261CAB202DBB7B840015BD7E /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -472,6 +545,46 @@ }; name = Release; }; + 5BA626ACB55042B69251B76F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ZLQVH78B5Q; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = prnd.co.kr.SwiftUIHTMLMacOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUIHTMLExample.app/Contents/MacOS/SwiftUIHTMLExample"; + }; + name = Debug; + }; + C08F0646B2E948B1A46BAB72 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ZLQVH78B5Q; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = prnd.co.kr.SwiftUIHTMLMacOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUIHTMLExample.app/Contents/MacOS/SwiftUIHTMLExample"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -502,6 +615,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 992017C20FAE49F19FEB0F43 /* Build configuration list for PBXNativeTarget "SwiftUIHTMLExampleMacOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5BA626ACB55042B69251B76F /* Debug */, + C08F0646B2E948B1A46BAB72 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ diff --git a/SwiftUIHTMLExample.xcodeproj/xcshareddata/xcschemes/SwiftUIHTMLExampleMacOS.xcscheme b/SwiftUIHTMLExample.xcodeproj/xcshareddata/xcschemes/SwiftUIHTMLExampleMacOS.xcscheme new file mode 100644 index 0000000..402c9c8 --- /dev/null +++ b/SwiftUIHTMLExample.xcodeproj/xcshareddata/xcschemes/SwiftUIHTMLExampleMacOS.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftUIHTMLExampleMacOS.xctestplan b/SwiftUIHTMLExampleMacOS.xctestplan new file mode 100644 index 0000000..50e37ed --- /dev/null +++ b/SwiftUIHTMLExampleMacOS.xctestplan @@ -0,0 +1,29 @@ +{ + "configurations" : [ + { + "id" : "BBD6E8F9-5B05-4CDE-8A67-24C5D8D8F836", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "targetForVariableExpansion" : { + "containerPath" : "container:SwiftUIHTMLExample.xcodeproj", + "identifier" : "261CA9C12DBB7ADD0015BD7E", + "name" : "SwiftUIHTMLExample" + } + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:SwiftUIHTMLExample.xcodeproj", + "identifier" : "8654A28F6ECF41C8B9CCED96", + "name" : "SwiftUIHTMLExampleMacOSTests" + } + } + ], + "version" : 1 +} diff --git a/SwiftUIHTMLExampleMacOSTests/MacViewSnapshotTester.swift b/SwiftUIHTMLExampleMacOSTests/MacViewSnapshotTester.swift new file mode 100644 index 0000000..076a142 --- /dev/null +++ b/SwiftUIHTMLExampleMacOSTests/MacViewSnapshotTester.swift @@ -0,0 +1,189 @@ +// Copyright © 2026 PRND. All rights reserved. +#if os(macOS) +import AppKit +import SwiftUI +@_spi(Internals) import SnapshotTesting + +/// Utility class for rendering SwiftUI views in AppKit and generating snapshots. +class MacViewSnapshotTester { + static func iosSnapshotDirectory(filePath: StaticString = #filePath) -> String { + let url = URL(fileURLWithPath: "\(filePath)", isDirectory: false) + let root = url.deletingLastPathComponent().deletingLastPathComponent() + return root + .appendingPathComponent("SwiftUIHTMLExampleTests/__Snapshots__/HTMLBasicTests") + .path + } + + private static func sanitizePathComponent(_ string: String) -> String { + let sanitized = string.replacingOccurrences( + of: "\\W+", + with: "-", + options: .regularExpression + ) + return sanitized.replacingOccurrences( + of: "^-|-$", + with: "", + options: .regularExpression + ) + } + + private static func referencePixelSize( + snapshotDirectory: String?, + testName: String, + name: String? + ) -> CGSize? { + guard let snapshotDirectory else { return nil } + let safeTestName = sanitizePathComponent(testName) + let identifier = sanitizePathComponent(name ?? "1") + let url = URL(fileURLWithPath: snapshotDirectory, isDirectory: true) + .appendingPathComponent("\(safeTestName).\(identifier)") + .appendingPathExtension("png") + guard + let image = NSImage(contentsOf: url), + let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) + else { + return nil + } + return CGSize(width: cgImage.width, height: cgImage.height) + } + + private static func makeSnapshotImage( + of view: NSView, + size: CGSize, + scale: CGFloat + ) -> NSImage { + let pixelsWide = max(1, Int(size.width * scale)) + let pixelsHigh = max(1, Int(size.height * scale)) + let rep = NSBitmapImageRep( + bitmapDataPlanes: nil, + pixelsWide: pixelsWide, + pixelsHigh: pixelsHigh, + bitsPerSample: 8, + samplesPerPixel: 4, + hasAlpha: true, + isPlanar: false, + colorSpaceName: .deviceRGB, + bytesPerRow: 0, + bitsPerPixel: 0 + ) + rep?.size = size + + let image = NSImage(size: size) + if let rep { + NSGraphicsContext.saveGraphicsState() + if let context = NSGraphicsContext(bitmapImageRep: rep) { + NSGraphicsContext.current = context + context.cgContext.scaleBy(x: scale, y: scale) + view.draw(view.bounds) + } + NSGraphicsContext.restoreGraphicsState() + image.addRepresentation(rep) + } + return image + } + + @MainActor + static func snapshot( + of view: V, + width: CGFloat, + height: CGFloat? = nil, + named name: String? = nil, + record recording: Bool? = nil, + snapshotDirectory: String? = nil, + precision: Float = 0.9, + perceptualPrecision: Float = 0.85, + scale: CGFloat = 3, + sleep sleepDuration: Duration = .seconds(2), + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: String = #function, + line: UInt = #line, + column: UInt = #column + ) async throws { + let shouldRecord = recording ?? (ProcessInfo.processInfo.environment["SNAPSHOT_RECORD"] == "1") + let recordOverride: SnapshotTestingConfiguration.Record? = snapshotDirectory == nil ? nil : .never + let recordFlag: Bool? = snapshotDirectory == nil ? shouldRecord : nil + let referenceSize = referencePixelSize( + snapshotDirectory: snapshotDirectory, + testName: testName, + name: name + ) + + let rootView = view + .environment(\.colorScheme, .light) + .background(Color.white) + .ignoresSafeArea() + let hostingView = NSHostingView(rootView: rootView) + + let initialHeight: CGFloat = height ?? 1000 + var containerSize = NSSize(width: width, height: initialHeight) + let container = NSView(frame: NSRect(origin: .zero, size: containerSize)) + container.addSubview(hostingView) + hostingView.frame = container.bounds + hostingView.autoresizingMask = [.width, .height] + + let window = NSWindow( + contentRect: container.frame, + styleMask: [.titled, .closable], + backing: .buffered, + defer: false + ) + window.contentView = container + window.appearance = NSAppearance(named: .aqua) + window.makeKeyAndOrderFront(nil) + + try await Task.sleep(for: sleepDuration) + + container.layoutSubtreeIfNeeded() + + if let referenceSize { + let finalSize = NSSize( + width: referenceSize.width / scale, + height: referenceSize.height / scale + ) + container.setFrameSize(finalSize) + hostingView.frame = container.bounds + container.layoutSubtreeIfNeeded() + } else if height == nil { + let fittingSize = hostingView.fittingSize + let finalHeight = max(1, fittingSize.height) + let finalSize = NSSize(width: width, height: finalHeight) + container.setFrameSize(finalSize) + hostingView.frame = container.bounds + container.layoutSubtreeIfNeeded() + } + + let image = makeSnapshotImage( + of: container, + size: container.frame.size, + scale: scale + ) + let failure = withSnapshotTesting(record: recordOverride) { + verifySnapshot( + of: image, + as: .image( + precision: precision, + perceptualPrecision: perceptualPrecision + ), + named: name, + record: recordFlag, + snapshotDirectory: snapshotDirectory, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + } + if let message = failure { + recordIssue( + message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } + } +} +#endif diff --git a/SwiftUIHTMLExampleMacOSTests/YomitanSnapshotTests.swift b/SwiftUIHTMLExampleMacOSTests/YomitanSnapshotTests.swift new file mode 100644 index 0000000..1620c02 --- /dev/null +++ b/SwiftUIHTMLExampleMacOSTests/YomitanSnapshotTests.swift @@ -0,0 +1,535 @@ +// Copyright © 2026 PRND. All rights reserved. +#if os(macOS) +import SwiftUI +import Testing +import SnapshotTesting + +@testable import SwiftUIHTML +@testable import SwiftUIHTMLExample + +private let iosSnapshotDirectory = MacViewSnapshotTester.iosSnapshotDirectory() + +struct HTMLBasicMacOSTests { + @MainActor + @Test(arguments: [LineBreakMode.byWordWrapping, LineBreakMode.byCharWrapping]) + func testingBasicHTMLElements(lineBreakMode: LineBreakMode) async throws { + let html = """ +
+

Testing Basic HTML Elements

+

This is an example showcasing various fundamental HTML elements.

+
+ +
+
+

Text-related Elements

+

Testing strong and emphasized text.
Also, checking line breaks (`<br>`).

+

Testing multiple line breaks:

Text remains visible after two line breaks.

+
+ +
+

Links and Styles

+

Here's text with applied styles and a link that opens in a new tab.

+
+ +
+

List-related Elements

+

An example of an unordered list:

+
    +
  • First item
  • +
  • Second item
  • +
  • Third item
  • +
+
+ +
+

Inline Elements Test

+

Testing the rendering of multiple inline elements (span, strong, em) used consecutively.

+
+
+ +
+

© 2025 Basic HTML Test Page

+
+ """ + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: lineBreakMode)) + .frame(width: 375) + .fixedSize(horizontal: true, vertical: false) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 375, + named: "\(lineBreakMode)", + snapshotDirectory: iosSnapshotDirectory + ) + } + + @MainActor + @Test(arguments: [LineBreakMode.byCharWrapping, LineBreakMode.byWordWrapping]) + func testingLineBreak(lineBreakMode: LineBreakMode) async throws { + let html = """ +

1인 소유, 파노라믹 글라스 루프, 열선핸들 + 롱 레인지 트림으로 더욱 긴 거리를 운행할 수 있습니다. + 외관과 실내 컨디션이 깔끔한 차량입니다. + 파노라믹 글라스 루프가 적용되어 개방감 있는 가능합니다.

+

1인 소유, 파노라믹 글라스 루프, 열선핸들 + 롱 레인지 트림으로 더욱 긴 거리를 운행할 수 있습니다? + 외관과 실내 컨디션이 깔끔한 차량입니다. + 파노라믹 글라스 루프가 적용되어 개방감 있는 가능합니다.

+

1인 소유, 파노라믹 글라스 루프, 열선핸들 + 롱 레인지 트림으로 더욱 긴 거리를 운행할 수 있습니다. + 외관과 실내 컨디션이 깔끔한 차량입니다. + 파노라믹 글라스 루프가 적용되어 개방감 있는 가능합니다.

+ """ + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: lineBreakMode)) + .frame(width: 233, alignment: .leading) + .fixedSize(horizontal: true, vertical: false) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 233, + named: "\(lineBreakMode)", + snapshotDirectory: iosSnapshotDirectory + ) + } + + @MainActor + @Test(arguments: [(375, LineBreakMode.byCharWrapping), (375, LineBreakMode.byWordWrapping)]) + func testHTMLImageAndNesting(width: CGFloat, lineBreakMode: LineBreakMode) async throws { + let html = """ +
+

Testing HTML Structure with Image and Nesting

+

This example includes an image and demonstrates block and inline element nesting.

+
+ +
+
+

Image Element

+

Here's an image:

+ +

The image should appear above this text.

+
+ +
+

Inline Image Element

+

This text contains an inline image: Small inline image This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: Thinline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: Thinline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: This text contains an inline image: Small inline image and continues after it.

+

Another paragraph with an inline image Another small inline image within the text flow. This text contains an inline image: Small inline image and continues after it. Another paragraph with an inline image Another small inline image within the text flow.

+
+ +
+

Block inside Inline (with markers)

+

Inline start contains block

This is a block inside an inline.
inline end.

+

This tests how inline elements handle nested block elements with visual markers.

+
+ +
+

Inline inside Block inside Inline (with markers)

+

Inline start contains block

which contains inline This is inline inside block inside inline.
inline end.

+

This tests deeper nesting with visual markers.

+
+ +
+

List with Inline Elements

+

A list containing inline elements:

+
    +
  • First item with strong text.
  • +
  • Second item with emphasized text and a styled part.
  • +
  • Third item with a link.
  • +
+
+
+ +
+

© 2025 HTML Nesting Test Page

+
+ """ + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: lineBreakMode)) + .frame(width: width) + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: width, + named: "\(Int(width)),\(lineBreakMode)", + snapshotDirectory: iosSnapshotDirectory, + sleep: .seconds(2) + ) + } + + @MainActor + @Test() + func testHTMLImageWithLineHeight() async throws { + let html = """ +

+ 가나다라ja + 가나다라ja + 가나다라ja12 +

+ """ + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byCharWrapping)) + .frame(width: 375) + .background(alignment: .top) { + ZStack(alignment: .topLeading) { + Color.yellow.frame(height: 24).opacity(0.4) + Color.red.frame(height: 32).opacity(0.4) + Color.green.frame(height: 24).opacity(0.4) + .padding(.top, 8) + } + } + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 375, + snapshotDirectory: iosSnapshotDirectory, + sleep: .seconds(3) + ) + } + + @MainActor + @Test(arguments: [LineBreakMode.byWordWrapping, LineBreakMode.byCharWrapping]) + func testingStyleHTMLElements(lineBreakMode: LineBreakMode) async throws { + let html = """ +
+

Testing Basic HTML Elements

+

This is an example showcasing various fundamental HTML elements.

+
+ +
+
+

Text-related Elements

+

Testing strong and emphasized text.
Also, checking line breaks (`<br>`).

+

Testing multiple line breaks:

Text remains visible after two line breaks.

+
+ +
+

Links and Styles

+

Here's text with applied styles and a link that opens in a new tab.

+
+ +
+

List-related Elements

+

An example of an unordered list:

+
    +
  • First item
  • +
  • Second item
  • +
  • Third item
  • +
+
+ +
+

Inline Elements Test

+

Testing the rendering of multiple inline elements (span, strong, em) used consecutively.

+
+
+ +
+

© 2025 Basic HTML Test Page

+
+ """ + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: lineBreakMode)) + .frame(width: 375) + .fixedSize(horizontal: true, vertical: false) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 375, + named: "\(lineBreakMode)", + snapshotDirectory: iosSnapshotDirectory + ) + } + + @MainActor + @Test("Image Position and Line Height Test") + func testImagePositionAndLineHeight() async throws { + let html = """ +

Image Position and Line Height Test

+ +

Various sized inline image test: + Text with 8px image, + 16px image, + 24px image, + 32px image embedded naturally.

+ +

Line height 24px test: + 16px image and + 32px image + to verify how they affect line height alignment.

+ +

Consecutive images: positioned adjacently like this.

+ """ + + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) + .frame(width: 375) + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 375, + named: "imagePositionAndLineHeight", + snapshotDirectory: iosSnapshotDirectory, + sleep: .seconds(2) + ) + } + + @MainActor + @Test("Non-English Text with Images") + func testNonEnglishTextWithImages() async throws { + let html = """ +

Non-English Text with Images

+ +

Korean sentences with small icons and larger icons naturally integrated, plus bigger images aligned to line height.

+ +

Mixed language characters: abcdefghijk lmnopqrstuv + wxyzabcdefg hijklmnopqr + stuvwxyzabc defghijklmn.

+ +

Consecutive images with long text: + + Thisishowaverylongtextwithconsecutiveimageswillbehaveinlinebreakingscenarios.

+ """ + + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byCharWrapping)) + .frame(width: 350) + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 350, + named: "nonEnglishTextWithImages", + snapshotDirectory: iosSnapshotDirectory, + sleep: .seconds(3) + ) + } + + @MainActor + @Test(arguments: [LineBreakMode.byWordWrapping, LineBreakMode.byCharWrapping]) + func testLongWordsWithImages(lineBreakMode: LineBreakMode) async throws { + let html = """ +

Long Words and Images Test

+ +

Long English words: Supercalifragilisticexpialidocious + and pneumonoultramicroscopicsilicovolcanoconiosis + extremelylongwordswithoutspacesincludedintextcontent

+ +

This is a verylongwordthatwillnotfitinasingleline and + verylongEnglishwordsthatwillneedtobreakacrosslinesexample with images included in the middle.

+ """ + + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: lineBreakMode)) + .frame(width: 280) + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 280, + named: "longWordsWithImages_\(lineBreakMode)", + snapshotDirectory: iosSnapshotDirectory + ) + } + + @MainActor + @Test("Complex Image Layout") + func testComplexImageLayout() async throws { + let html = """ +
+

Complex Image Layout Test

+ +

First line: small image

+

Second line: medium image

+

Third line: large image

+ +

Consecutive images:

+ +

Text between image placement

+ +

Mixed sizes: + + + + + + + + various sized consecutive images

+
+ """ + + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) + .frame(width: 375) + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 375, + named: "complexImageLayout", + snapshotDirectory: iosSnapshotDirectory, + sleep: .seconds(3) + ) + } + + @MainActor + @Test + func testingWordBrek() async throws { + let html = """ + +

Word Break Test Examples

+ + +
+

Default/Normal:

+

This paragraph contains some normal text with spaces and then a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow to demonstrate line breaking behavior. We also include pneumonoultramicroscopicsilicovolcanoconiosis which is a very long word.

+
+ + +
+

word-break: break-all

+

This paragraph contains some normal text with spaces and then a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow to demonstrate line breaking behavior. We also include pneumonoultramicroscopicsilicovolcanoconiosis which is a very long word.

+
+ + +
+

word-break: keep-all

+

This paragraph contains some normal text with spaces and then a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow to demonstrate line breaking behavior. We also include pneumonoultramicroscopicsilicovolcanoconiosis which is a very long word.

+
+ + +
+

word-break: break-word

+

This paragraph contains some normal text with spaces and then a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow to demonstrate line breaking behavior. We also include pneumonoultramicroscopicsilicovolcanoconiosis which is a very long word.

+
+ + +
+

Parent: break-all

+

This text inherits parent break-all setting with a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow. The inheritance should apply automatically.

+

This text explicitly uses word-break: inherit with a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow which should result in the same behavior as the parent.

+
+ + +
+

Parent: break-all, Child: initial

+

This text uses word-break: initial with a verylongwordthatwillnotfitonasinglelineandwillneedtobreaksomehow which should ignore the parent's break-all setting and use the default behavior instead.

+
+ + +
+

Korean Text - Default:

+

이것은한국어로된매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우긴단어입니다 그리고 다음은 약간의 영어 텍스트입니다 with some long words interspersed.

+
+ +
+

Korean Text - break-all:

+

이것은한국어로된매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우긴단어입니다 그리고 다음은 약간의 영어 텍스트입니다 with some long words interspersed.

+
+ +
+

Korean Text - keep-all:

+

이것은한국어로된매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우매우긴단어입니다 그리고 다음은 약간의 영어 텍스트입니다 with some long words interspersed.

+
+ + +
+

Chinese Text - Default:

+

这是一个非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常长的中文单词示例 mixed with some English text to see how it breaks.

+
+ +
+

Chinese Text - break-all:

+

这是一个非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常长的中文单词示例 mixed with some English text to see how it breaks.

+
+ + +
+

URL - Default:

+

Please visit https://www.thisisaverylongdomainnamethatwillnotfitonasingleline.com/path/to/resource?param=value&anotherparam=test&evenmoreparam=value for more information.

+
+ +
+

URL - break-all:

+

Please visit https://www.thisisaverylongdomainnamethatwillnotfitonasingleline.com/path/to/resource?param=value&anotherparam=test&evenmoreparam=value for more information.

+
+ + +
+

Email - Default:

+

Please contact verylongemailaddressthatwillnotfitonasingleline@extremelylongdomainname.com for assistance.

+
+ +
+

Email - break-all:

+

Please contact verylongemailaddressthatwillnotfitonasingleline@extremelylongdomainname.com for assistance.

+
+ + """ + + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) + .frame(width: 375) + .fixedSize(horizontal: true, vertical: false) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 375, + snapshotDirectory: iosSnapshotDirectory + ) + } + + @MainActor + @Test("Yomitan entry snapshot (macOS)") + func testYomitanEntrySnapshotMacOS() async throws { + let html = """ +
+
+ 場面 + [ばめん] +
+
JMdict [2026-01-17]
+
+
+
    +
  • scene
  • +
  • setting
  • +
  • place (where something happens)
  • +
  • scenario
  • +
  • case
  • +
+
+
+
+ """ + + let view = HTMLView(html: html, parser: HTMLFuziParser()) + .htmlEnvironment(\.configuration, .sample) + .htmlEnvironment(\.styleContainer, .sample(by: .byWordWrapping)) + .frame(width: 320) + .fixedSize(horizontal: false, vertical: true) + + try await MacViewSnapshotTester.snapshot( + of: view, + width: 320, + named: "yomitanEntry", + snapshotDirectory: iosSnapshotDirectory, + sleep: .seconds(2) + ) + } +} +#endif diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testComplexImageLayout.complexImageLayout.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testComplexImageLayout.complexImageLayout.png new file mode 100644 index 0000000000000000000000000000000000000000..1bd9258338dc24d09418fef0bffb9a7577afa3c3 GIT binary patch literal 10205 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 N$v;+-E|!G*{|}EK+y?*v literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testHTMLImageAndNesting-width-lineBreakMode.375-byCharWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testHTMLImageAndNesting-width-lineBreakMode.375-byCharWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..75d8107bcddaac4d47002b261f89f56743b5f243 GIT binary patch literal 85176 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

>dWK;*rvMHdKD+&R&&3zbTqlmB6wC5@n2- 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�^P)Z0sS%-~4`M5_+T$skigYudV%dnc`c# ze;n^$dF2U1!Y1;^E+U@Vx~eCQxhS1^yk8SI^*+}ZIRD1jvvCj%d(p642Z!fqB1Aa- z=MKtoko7aC4E>WCAfMuu!*TJJkx1BlG^td>PC7(6H_pds^Kv~kqS?p z5%V0JXjW-iX<_q^o~GzIIF0D#OWzjhz7()EZ#tXgg|-NRKJA>bbzhurco5I7Jer5V z3IzCfy|nYCTJ*|!KBBHPLE+#0kcq=2qaw->hk%d$WQ z3f+zkz11VG*UCjDNq)3VwmPTFua_xugQJ|cW0b`eD18oOA?I^*?rR-~&O+T()Ud1V zLe?nR^&XrEww_$E3h!fUn&txk#TuFYzxU}ejrH>?!&~H2y`OfgVpZM7$UQz^Z&JWy zMk@{80uDk>Y>-9^&9B*BAW7pKQ(45Wrh6S34x-kySl5yqZ8 zSxvK3@X{H2eKm=B?Sd?wWBjwy-kEC$qGnWr$bv}w#mkKnLXYWtGj&_zW8QXT$^yQI zu181+O+}=-M_BFjMk{6(4(_5SV!}>Vg5Xh4FEVTPMhP7O$mP=4_G)Q)LI?v zPne1Sjws-Fg;s|cVrIO5|Nj5*?=5D=cmDALxZwu>o!3HrKdOCES z7ixa}cX0*O`hr?tQ0oh7eL<}+|2=y#)cT5AUs3BTYJEkmuc-C)za!R%!WR_2pzsBS zFDQIL;R_01{wCr6Cv$Bm{6*m}3V%`fi^5+N{-W>~g};CJ!2d%n4~k!)_yvkzp!fxf zU!eE}ieI4k1&Ut)d;rC-{>QZBDE^7!pD6x`;-4t~iQ=Cq{)ytBDE^7!pD6zQKUd3% z;@>F#jpE-Z{*B__DE|F_!@uvE_lKjr0RCru7f@J@!fF&&qp%u<)hMj~zrku0exUFJ zg&!#VK;g&#Z}@THzdg=%|JJQrL7dkm`;yFoyGhJ{%}D{{)}#QK7{%LvJHfT8wL1Fm z7JT~0I?j-B0XS0a+rJ!&3265oI?QA=7CioIL5D?1_0439u7j=VySO05t(>0@O5GKY-l^c6)j$0KW_TF7Ug+??U+( zD1I|l3kXsLAXNZT1q=WQ%<^EC2eZ5Z9)ejO%<^EC2eUk+3P7sBmrfOkA`)aPvSUu9 zX@-tiZd~SIfjkpFe}!|@=~S<|hy^_k_3l|C`}gf*J0fmBe}X9bW08G)gvR2`_gIIG z>z6m2%5SXqjtyM31~~_Lf)Z18|JvQFtyiDUh{IG(2x@0gY$p*T1c2Z!R||Fz=a5Z@raP2rsgh;I0%xMy2D-*-6slKBRP?Rsymxu*_TcwbPKi(SnsmevI!WB&(0lI{vwHw z3BJ_ItR1C|ZTX?c!e$~lxJ33q)uXg}cTe#cnttBK$8PoMS5qg(%w+HAFI`Ljiit5N zbTEUY6?@52VU2@{;A&ilt3VMjE}hE}zJA2TO`5(9C7ubTLlgSFa`gRXl+$4$naa&p z*oUHd7ZjJ$RP~N zIh2tjJ0fIUQq@~r7ki3CI;(3aQup8E{es9FRZV5DJznrpatc}EeOSCDC7x!h8j>_C zB_fyCO9;8jS6Sm_Y43^B9MOdjvhZz6d8NwVH~_D(lwy@olN%JQ+}1r}b>jy4A%F0J zdRvJHp@yO;!T1L0CWhmp`N;v~eofaNsSMzpvvz{?3!r#`?iH}3onv78zC!_8l z=8klcaX^?I<^%e;+)7t&XlQqoH9u6nE}Bswq{q2N^W)v*!q<2XSscpaVe}gsNJ^UX zFtz5M;=uq-eN~?4(z#?0dtf~4?eq1>YozvGe6rAyQIpel_^92byG&ybb>pnZm#T4! zzUMASDH6s{OI@q86WBc=p2hJ!M>{W-i0jbFdKFQ(Uu$8nA7*{1t}F=hZZB`JxruHV z5M?|$D6;Jxwl>;?i)^wQNKb8KBUXb8E8ew~;Mw`3wx~_ofnk*|M%ec@VK)okk3FYq z+>hj>`m~F(_s9A<>8A(kuX$b__L0#pt0`$MiN&E4c03zY;HKIE24V7A2_{EIEoGr{ zHNV>4)+U9~R~R*saAa!#>|L^1gKy^f^3COiM>-^lx_cV~Xkn~2Ru0CrDn>dcr=@`U zi0H;-n746~)O(a7S1LE@fLiRXRG3w!o92D@V#8@?8qZrhM^Lcr4DCx^5U0Ohh$>Ni z81~`SaC2nE{!95`F5bpweUF*{tiDwe+g-OR^<9V%6Q}qgMdWyy-*v;E+o64d5Vw*2 zd1rgdQ+lGO)>Xdc9Hsl3x-HAS9@XAq39@MV;8qoOH&y>jF{6{9dFjy91m7VOrSlXBY0J4#(8i<-I8YCj_rs7DL5dkqZX_VA70Wp~BizH&I;@Ffigg8M_ zB=0K?*QkSVDmic~)kULF8a_mAuMY%9eU5HuPJImlXg|b5UUMOfZpG{!0B*RzHW5ik|{dH8T{%Fd^KoJK} z>*u_jmsHUH2G@)X*ZtEtR>i2<)G&;33s)Kt^2$8)+Ssl;a85d3Q))gUZk)>B*D5@k zfmPCfh}o6;O-5R*0u2xq9UA;1KR*6Bg!cQ>Cz8Ny6l;WZOr<2q;6>P-CKZqp)mS2e zleQ&sOF~47Xq&Paqk=-4Y63DKQGw_fD@#tuyX&mLL*wSLZcW7T!92o$G!1PqfA+Ov zSZ?$CHTwxOmc1;6GxKh;BBcZ@XkB z1zB|zPWAYHxODk;&uySX_ZygiH@G#$!9UR$YU^hoD{sHU)34ur;GGPG!a zWH;H-af3Ffuwm|p`CfbUu^0lLz@I9TaN-@xir|I`4^_%`1BNgmfousV)p)`3Nr_0< zJv74+uUJ?VJTT)_1UzQ1U6Al4R#ZfeEr}Ro4{3bg#biQRRLU^S;E2X%nA2eIt*!Il zh*_KygTi4~ciT!H3cqQED0S&vupXAR4I%rGC*U=p6{VLBj8tn1iU zKY%`%4Cg9cox$p^t|ix^(xDPRBco=}Y2ns+X|y|$Fp(I^S%0ZTdN+Vs>wz7(!}qm5 zxt;_tdKQ0p^M~=vd0PvHpi&~ z#ntn-9rgkN8>B9G*9UMWhUhkiA`{(c9xy;mBylm^2*eRX;usN>DrchfH~WDD`S2-$ zsfJ|V;dQ#*l;QpaJm(|-2-?{sa3I%(*XXfr_G;~Mo6z$ES!y~e`n$F7yh z#^TuyhNs?=WloCuMwJl(|B3P{flijFj<{5^k*Z-hewb>P&k6rP(vJEzk$hKaS2(KR z@H@Iyh<3rwC!SHUz9|nE#>~hGNt9`07k=$P^`f?E>1#eO@{TCCg2`z=R6{F59L@n! zHlD$O!Wvv=xMU-gn$H!a<%7gE_#SzzCVHBzf6(c-QL{q@yYWba=r>_~-gN>D!ZfzI zJ!t%}yJ9wm9?vJ?iF*56(e+pg-SqlE>8+qP6IJIai#ha2rmHN`? zQoLD&!}o_|hfLhu+??DP4TRja%hU~T8+@(IXHUyJR2I~mR3K&GGN#$I^0Uvt59S)Y9|Mo0#BnH>|MiM zqg?ANMwO(#(@3sv5v`ZMO4xV(>BkZ_4`0bpDO72u?W%3wkZYCvqw)t_L+J9r@=Ak} z1<=$G)fpj;%8=RU*f87Jqk0V;#@rAy5$XuIy)Np6eJ{u{s@T%)I|(}pdzK!Q?vegngeFZN z;}x4R=r_1DXdN3*c$?0q;G@w0&RQ`pQ^@3?j;}&j{ml_=`Q+>rSHG%J>(3v3*TESL zT0vTgOZxR~`>M{3o?-XeS3FlDC^*qb(VqA>_)X03zuJs3kKKF)vutQ;>CkEUTF;r@ z%xX_&n(JBU+4~NKec7?*oGDq}k7AkrYMW)!Hu9&C%nD+aW~O20-b3EtUQe)MUw1|- zLkM99aa4J&x+1zF#z_Q9boBYkl$bP|B$_1eL|@?Vs0>yU`+Gf^(`$fr!DOH&dJ83_ z9A&Nov);T3w+UDXBZLXZ5-0fGhwMe6i{j{x=wY+^F*J=Z>yV?SD%+~O)zvjqJ2X4t zQ~XoQQ)El4qlx_~7yH$77`>+aoqN=w(z3%eJ|5G9d3n*?T% z9f+!!w&YmhAP<|*Y%S$fbzwGX)>@?!Jdx|=mf7=T)iNAB1HJ-N$kKeHE1oO%-Icj0 zzkfbxzm7fdg{?B|*ld|nYF1yQxx}x;7alpE(9F5unh|tX{G#;LUfol@I-RKsw`&}I zWIgHm9X+GD#)PQd=hJl|Cw=X8s>nuK87HT(ie7w(`bM=kw`+0cF9rP#D=vi`w zKiLM%sRNdG4qZO8ot&Pa8BTWIvgRtVfkDoU?Ixis zQ9qd?ndq3RD=+ob>b0$1w+_Rtspq+>%qm&*9v4~#fd!j=c@a`n;yCnqFs4bBE#M z4q2oi=(YQ$zzy6pQJ0hFCgm(Ay1s+SyERsN@_h(bw29 z-eLCGEi%w)4lH^0u=QVjX@on5Lj!8P{J5apF|?%l#RsmfeqUZ%&fn5@Qv33+Ey%>*V(?aUCrUB??lQS$zA@;YMrIa+@tz| zcV**`TZ=1MCxt`Tv)F3z3dS3ZJYW*=VNvfX{ZI2#@K(qmQdNwmIH9-OaS;{3LtdYkj*Jv zra&hyT%JLvKF_PM!r-?!pDc!XnHjq7?aDd=9IwCx;LU=;u$YQ)lDl0A{AoXB-Wd0!JTsOfe8zUl%f;s;~kRw*At(+hYLISFM{Gysep z+|C*FvyGXi+!sYf03(z}0>Hx%1KvO>7-$!PA^Bfg8ioM?_uuod06@4U0RI240YS%q zUJ20t&olpTgw2QjpB1pD`EdV314N-K08ig~YM}$NgRGV_0Dw>TPlu8FOnVL`kmMxA zR6Sr$a_*f;oIJ~KqSokE%HElV=TFU zNs$c*6I}VXDgb23007!8k}7tz?7tKjg$h|h__uztl(6s?%`*8(nv(z44?BNrg6H2R zi^5`2iuMV^ruI`O{Ur#^z41S0{lg*x07HC=^_>LjQojFoO-&*$~tRyU4OL6cxcB}U~(--(cm&>V7sj0CTN05Uv1}O?uAFCRib&g zs#?y|G8eCVnWS}|{D-(B^axg7vv;}3d~<1OG0%2j28oX-ok~CD$voaqk635t3X)e( z=j;YQ3*Q_sd0YAW-7rLPK|vCboLNYlo}S<7jFr2)PRH#g__B4+BUUA7 zVj|5{)oJ*BMmA8Hh6d=82r50zB`K>ulZdm-V_DTOF|ijYDNl?WM`3*_@5T6HKZ!W| zGiw7g*kb5H;GKia=5dR4S!Kfp`^D&}2{-R;oJlL?@_T7oLIr4Q2>PDodW$Yi@O>?eJhm@bv9=!l-=?)X`My z`y52SrFjNj{yDKPJvmVWn=&#$zgWu`Xw(1IN=z2Ew6wxw7Fo(^9_BLCT3oC%H_u%j zd-JBDRiH?)J>9V+L9`Tl*I4oHu{Q`*4%s4BhYOpDXbE!k(AZjMW?ew$$U8{#RhpI- zLPg8Vh@|!Wd=5aE z2GKRbyfII3i!Su^Slc{F8(Q~Iy|>&2rM3=_u+@o<4&&E#5bp;$11Vh?4^aee6o$+; zP~E#+8BzauIa0aMvulh5O_f&uy6h{n(Vg-dK zeMQCRk;dN|RX);NLq97ks(+T=-+T8SK|!n^t#sQ}MAAxo2B?%?$!y+m=IpMX&Ty!Y2OKQ{b`DZ_ob)^Sf$JYePeY-Nr)+ zOTT2HQuR)aV!}>Ho&cVm$n$e`>FFtne9Ld%?q(p9brmJzqK zb7}Go$o{*a{VU=cweu_)#v3K5>pzbfyF`DG`Lew3z+03=I1*<3q2c)CWS5le;&YX% z70a2K{*#{!#S4dhuFyda;Lv)xq|$i*Thm!E#}1at-c(P&N^da?S&K1Ugj;JJN9F1I zGpJT-=Y}6iwG1T8w}=Bb@8C!Hg=&bXQjIZdD_G|ph)+PiRuI|bQ9Poo3f<^5nC z(0XOx&BN{<{uVvpjbcJvl!IoJL<-t>YyJL1qYE zR7)h{m=@-9_wVe6eyqSPTjexce*1j%3j{%5`Qh#UqxYlRLv=ogr6TTmsx%f`YkZuv zh$UX+DfF=IJoBy!?7va>F-2Cl=6dA%`c+F>@D~+j-{M`?;lqvpOj<+ZjdQFlkEkDd zC^WVyv&kI2)9jvM_S;P0zMmF}4LVQ}WPdqygE*6QF%}7&UxgUF3c9~C(a0&VA{R;He3_xaw!(2i@S$AuoEhgXlOM31S?=wGxHaDU#7F`l) zFIuc&TtnkWfBr-fCr%If(3HPpF;K$hI}w;Wb9)=)cQ{+T8{)-KU)a``wd?@Yk~pAo zb!BNyCdSDl9tW9??GH|G1~~GMf%+zCF}a{lRtzW3@Wt1+`mj_K*Vk=deS*F_h&CpI zaA|$w%+L7Op0}-yKsf>;uq65T)V6$xWBXHuVUMB7j#ds@OngSh>q2xiX7Wlob%Zu~%2XFf^T+AV^dcCQs^g|_YIit}Gy3;mp&*rIO){VG| zG!G8_mPU=uJV(9GZqQ!bIzyq>hegv92txe_eR_G{#Ug9MoM0gUy zClr>bUd$ndTXsfH&j?h)J%m9SD1trPYV`qloYXiwT@4=(A3WRy*A_kYAGF&-GCYkF zoi$Hz_YRJIra6|kdP75NH8}&6J%o*)dVa!5`|7L&xGFE$Zp)WEL^$e)%Ambto$WhuVlEliQ^F>_nXFmC9DUK%l1NSz zn3(gPt?}|?){0jk+Nnw&i~tKX66{$H&?4dwRT7jo=OAD<{)7`lX>+Km6767R%OTZf znuZr|jwK48=L|8Jl9{G*UKy$9M5?Qsd}NPsiyBzCay~nJJRKniSTt!qAt1NrY?E8} zo{#LfKoF@e$9px$-}N-P-`!8rWc*mrCqB&#S$U0`DYZK@PDAJXw6b!r39FO!@;l8( z;HKa%fptvyq~Fego^$m)cf4^~?!D{bK=kYP%*@H7LNN&QT@7IEl;1NO{t_#HD>oh# znnTil$3CVR-~@^?$j|9s;1+~W*Mc{7kDa+lXI?+|A$P#E5gO84k+}KmVa}gX++?vc90~6#pGK083lPQHKapm1-=84t{aQmN5 zm_tlADf5`}oYCo3Opj)@FHm@c+;qQ#1_q@gqEDsei&eb)_b%GU<;A8($jP2lnUB9p z$wM#nKX{2QKtHbO`0TZ&p@>@gd+pVG0)+<^6(|HVyIqLhwwq#UL*W_TS8|;vhV?&H z=5H~Cp_BIS!Qv2kAz3IoNtGB`Nj6@4%VX9zs1#dwpcLCYm|xi%>3eb#2YzHcoZUR+ zRmkx=eY(O%eCc~InsW4Tc%>I$)O1i9%9f)9LKV2b&wi-aObLsiUqm3OOJ4p5>0tW1 zJ|2vU7Z}c>(~LW^!ylU7^2bbZ@lbMY6KMq;PJTT)2Znso&{?5Q)ca+}9QyUj^da5N zL~I?g&Pc5w=!mVuO4{meNA5v9PWME(M4E`*iO=2cg~5&b`H+g|E3UMl)B&c^w}OJo z%|y9JdZwxzYJ+BSy_N?eEf-%dFDrjw?uZN}YEBg2wwfCELmMCmaV=cx&Y?atQL*tU z0SX%Y5NqHp8TV|HTWeeySLbS?%kYT|Wo$evfCW$}0F6YfbkEouZACl$7nZNz&5(@V zI_$n+_(l>3BH?NhhM+m+SDcudc`l#M*6y$|e31Jt;Mh%&=Xjs&a{-4;fD?n;OUNPe zhxhg2#$;jHxBI)T{aJaEneISw477|&?AGAskmu=Y&7CFC-dkAd8*;v1?VsM`FA+r%LKnXipHG|YmhRU;js|z@ zMW;rlhQ1QI%M8nSh32#KTyiUl{uDLJ#Dbgxa#JU`w-hPpCG5*hP3o#nmg4RKTay^K zh?3a_d^kw*;KoLMWnTUJC_5Ro!rPdtu@6{>W0hOvP~_PBX?$K3k_8-JHLO91cKNA5 zBp&UR$mVJ#o^AjIyZA@Y+r7VrwKR5hPKKapWANG7|^-;{%D4ezFRDa;7gn&>KlA|-xJGa<}; zUq$oibLp-CC97)>`S5F?3tx7l)AQ#R-$xxrXQM{s;tVanPM{p6MNX7k!p9m9!lU3C zfS+D31wOUkkt(V*UzCsSG|?@!(Sa(nc!$?bz1JqfUUv3fU%|x1XDfXP*cWI%ddfOE zy!)kh!HCvFo(GVb)1h{4Ow#Ypv!dF^wv<*`gF)-xyk;2UDI*>1M)#obr!e;_d|J$@ zr;l1|7iQXX^wBp1UAo0c*U-wt-Tb5U?Sd;U>MGL)67W*2x$SU(o&9k)FLknN!rtHu z*)>MNE@D#wOXP3>#Xx3eFTvhGxwWGp%P^-(jcA?X)B5{+WXe|L#hzwK2ALn##zkjP z-!qN>QRz!SxS@>@-y(2w6%zO%^`pa1z_k@Ge!*SgQ1G|Nm1g6A!5iX!m- zcg0Sd5>+7EKQJ`eEn|%db*EvRoPTzknU4O(3+9BC72#9o^qevZer=Y~t;Ex(Aew-h zz~}HbWcTcj!gF)@u*ezauW~vE)w88NjRELT|BfCmF~DYNWzI2idVe$A#UvV|^B6fHN{Wsoku-ht4O?+DH(V;4VBt+i97+-(!z6!Y-i7E3i; z>dD#phLQVb$&$q1JVYDs6$;~op6cV+V1b_B6m6@cdzEBxJX`NJ!td{fXBf5iIwQ?y zH7fSUYXvS81|V)xPW@##2X1O~iQ*i~u?rkuM4HM()AMcL|Ke2P*g1-R9jq7Mp`sQ8yfA0&-l5ern} zb05F0V|)gSQ!s^gCEI!$d1|q&atUbBJKj~Rbz)SaTY6-|KhcS55czxNKCJTe%XqW% zvlI<;b_yR2N`HoA=z1N0%q2t?Bcgh*g}E2rJtdt>n5dFcywR4>RL#h6h_5Bld`q$@<;PHdi5V@!nP9o(`>EkX_>c2$lXm)3TaNVP{lQy zs77>&XiWgxE~44LHAqgZ$w ztQ2rd+HChpHlGw(8T{3xvPNM~GJxiEPnCrE<1SJ~#lsGetOe0LkVeW7rFI#ErcbhL zli6kfD<4$Z%m{%aklnO+`O%G%k^NUqO_ov!>Jj=(ehMc3yM7m74xJS)+6n!uYK2J- zg%?}3`#FD=3cER>v$@k3&_9#>tK^3i4+XjBnpdE|IBaGB(A+0~dr;n=M`IBW= zh(91qCMdXCqu^Bm(oX6hwX!mglho%YZ+un0fj{h}DeAWR+zB$Ty^$h=I}z+Ssj4?> zTW1?8eQ(_*Z=NLH@=>ZgjV7;k3D%U9qpT!XLy^# zL&W3SH$wOIxn$rJ<_h5=JhgFH1?fBIr{THOMVy9QeYiG-B4XzkDjdwBvo7~77k`CQFAEs0|-Mkmzgdqa?6VaFE2AzkqhTrx~_rxX@ z@R6(sP*{R#oS4l+m3b(4Ri>^{(Faa)(Sq=uVkgZeH2nUDD9xgHx)`T|caTozCGCP) z{|*=~;A$*6njz?Jd>jdF^M%gv6B-)MTcBPprdw7gO}Mb{!W6{Uu?o@V9!iWpBMHnZ z6!1`3PN1 z7Go~rnG>2Bx*Q%^He@=i%wTt*(olOr>KzW}tK-8Z8TMkgwXuZ?iTW@zVo=0j2Nqo6 zABwKD2ut#?Y?ER!a&9SaqiRopxGAC2grDgesMcyXXvch2)-i3gac|r-o9&D3i;EB` z-!x#`Z-agCpbeN2OD<0+X9c={#w_jZUVgM*7kG87$?2Xi1oLYFgTeHh)_i^k94=6q zl7=-p-Ll`e)r3s-as|liW=}iK1pt$5uKU}I5DO%gJyN<4L+JsMfsx3|(T5XvAz7lO z$xB?$rxMKvv}HpR1Co&J`Ot?LcMZs{|4}w+Mc~X7lM{y#RIm9-jpxT^m(Ro|LDVFH zu`rfTvfDItt+XGMGiYv22SK^9DuVIRIb3PsP)+z&EU(J>GM4v>Ra}*wF`ZnqVow_7mh!0{2OGYb(Y)0y*YerJ_42ETK1K2Yb60W00NrNCDP{D?61Fhb7v81fo2JMJxs739 z3*?&udtYn>OhfgiZ+cwtrEQ9y?D)0b4`peNMIzRD)=3gb(}E3xY30Su_^@y+y?63Q zQgnnOcY=+YYgoVe^=s*3DK*hG5j80u(IcwfDl2?(DhE7X@uHU^TlwY^?y9h=)Tt_~ zrl|bLskXdXM*2OCO;MfeUgw?Jr%&1OBzRgpDIRhs!CZACM^Q64Q9O|$kv);Io9M3S z97HGTvD!{vyLS2esP_zm=aw!lu`dbSym?dPCUqtA%@3p8m1LDZkF5vS3LCVCb*r^; z1*ign!IZ+ES~7XQ+D8m8l*-@Y2efSP6>}Aaqoku!qYo)VL*Jc4DCB?8cz5@m=1hD$ zxkQMAD(*9CfP8Uo8?sHlZK4*Lmvm7t0bMIwes?Q&-t8Y>q0k{{F@LdSv6Z2lp>^f! z#|ah16_Co1(YDdCN`#G!C6dDBTnZOb&};>nVeYyNb1QQ5koYcPCE?{N;d1OOo z=VraEyT~!LYHogC;9^W#k5Th@e|@{ROuG$*b+k?Imur+3S6)1SC#u_zK;-BZRO|gL znDFvR_RaQvd3^ck;!)kXnt)8X=w`v7f6k#OJUf=6cUT0NA4<5Nj64j@f0Joegf(he z4P6aAQfgOfR?>P$sl*rU`68{|xBYv&-HRCJz0|Asz3;bPv{QTeO44GnOsvRAmuQ)% zuz&FLjn)TdAOERn*$zspGzc_^`)*QRH~+w;>Uk*MaO>8VG&x-qNz`-39mZ< zf;~IQD4|JxgNM8ZK6c+McLojnUs)U57(4oOgg%#}b|ZeN5TMY|;-j2rQDYHjkuVdr!8oJc zj%M-mJhbN1Lm8pi)vNhz5G0uzH{Mu%%Ib6P1LH2^1n6w(f-YXk7?Iq_jcSPMv?}kR z)C;u>Ual^AQj#@3{=?FN(m`sCam{v(^#{7#ZDe4@YDI(tO6?aD!coleQ}T!8meiit zF?Mb$X+{UP;~KxiGmyicz~Pzxe5;}Q`7?Z}_ZRP~16Ta3gH4}8AI|->U!+Iw2U6IP zr)A=G;=+fShgQeLN0UxcwOh3v;8Oo7|1bV8Wmhr5m~M=h+#|U(xg9y5=8|TYpqLPt z{3m(c5F_0A4QRcgiD|yq!r|uxvn4ZUvm`U>PphA-utGF8iZ-pU!f(eV$HjN%@x$7p z4C&lNMRh=zc`Nr9vuMSvek86v7#@rtw9_s|^}Y6Vf7M(u zZX1UBirPYPs$9t7&Ha|^GpoE&h(B7a-+r;^11>=}Ox1oy)aZ^}*kmkbl%mt=G@v^*v>iJgpSTGp3vUJT96x)+j*>(1!z z?vcJiCwP1BDEOy9Bd7lB5A_xyW0C&|LMZ1L#l<2; z4>ee`J3WQR|;J9J~K7+Kq z@I?&u0ez|PeW7@5?Uu&En<_?ha*)jJdp2L*b7bsB| z$Hsacr{$K_JI_d--TnD^La52w1AQPmHo5Cw>&D*5>C|*2Hy$)bO+=j~6EAZxVtkmo zTXPuEl`x9xLp<~f^t>XSzFG9Eb{iiy5kFb!JGuX9z765Ge>5;5T8sXbyE2E#>M>bt z8r@0hF@170aI|NFYdBud+b%vHd-ZJP8ETTR!Ee)SNcl@gB%&(IP&&+~Q6yIMDtxYI zKd1@Jtl{wFI4G&nkUPEpD^kp9?zv+OhqqDSa7O z83Xw<45CnAm>L8=0m-K0ds%^P7@um?wpFYV9bwtIBqhv#$f~`n+7#)veV$*(7_+d| z1gJ3vQPY=bteln?PzzNB4GKyC=ilZt^0 z05I~NHIS+n&pH4wVN@069(aIOhngt?I0fVy@+1>?YKrK@1JT#<#5X@^5|6)&7a5%Dt_%#prUwn%(@qaTx zNuW@QJxqck&mRD?X8HHBzh?{Z_s4R0#E1V`8cOku{lAUD;4s+LaR`Do?hn8pTLSrO z3kHXJO{sFz{sCMacl)1DXOqjyVw|WYRTcj_5?V*~#|Ib*3G-S2UxdmZ0LE$fuMc2? z(`PS(@TCdA_wVQ;#5Ca`J3Dv@r#Ios`h8;(?rB1Da+XXHGBPM34LwV-3As0+;P_qS z5vnXgX?a%v6Y6OKiSV1aA&>|J5`jP>{0;>Ii9jF`&O+h;B@x6O L>WU@u$iV*tZ_Pr1 literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testLongWordsWithImages-lineBreakMode.longWordsWithImages_byCharWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testLongWordsWithImages-lineBreakMode.longWordsWithImages_byCharWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..4e17587a8432215db0d7d9e4058c9b6664890ee7 GIT binary patch literal 9577 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 Mx_P=jat{3dFa2mSaR2}S literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingBasicHTMLElements-lineBreakMode.byCharWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingBasicHTMLElements-lineBreakMode.byCharWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..3976e578188375e2ebc672df76cc134282d52def GIT binary patch literal 49740 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 Zr)}_uI&$1=mGbZM`D=evoxAnm{{vG5;G_Tm literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingBasicHTMLElements-lineBreakMode.byWordWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingBasicHTMLElements-lineBreakMode.byWordWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..29d298cc1245adb4defd32b801e1de7eb3a2a1d7 GIT binary patch literal 51437 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 SyELBx2s~Z=T-G@yGywn%#yZ^q literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingLineBreak-lineBreakMode.byCharWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingLineBreak-lineBreakMode.byCharWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..96b8c81dbdf15f0dfbbbcefce0a4caa6b9420ce9 GIT binary patch literal 12423 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 VStO%N-t`R}#b+vVB{IhT{{#MS8hZc$ literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingLineBreak-lineBreakMode.byWordWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingLineBreak-lineBreakMode.byWordWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..96b8c81dbdf15f0dfbbbcefce0a4caa6b9420ce9 GIT binary patch literal 12423 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 VStO%N-t`R}#b+vVB{IhT{{#MS8hZc$ literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingStyleHTMLElements-lineBreakMode.byCharWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingStyleHTMLElements-lineBreakMode.byCharWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef63208fa9861b944a388fdf9ebcbaa8c2caa9c GIT binary patch literal 114990 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 literal 0 HcmV?d00001 diff --git a/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingStyleHTMLElements-lineBreakMode.byWordWrapping.png b/SwiftUIHTMLExampleMacOSTests/__Snapshots__/YomitanSnapshotTests/testingStyleHTMLElements-lineBreakMode.byWordWrapping.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca700e99711bc9f4681f87466c1c206aa45fc9e GIT binary patch literal 117592 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