From e02f723aeaa555296708d7d5caec3fee237f037c Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Fri, 24 May 2024 02:45:19 +1000 Subject: [PATCH 1/3] Added String extension for HTML to NSAttributedString conversion, and changed rendering of ItemOverviewView --- Shared/Extensions/String.swift | 17 +++++++++++++++++ Swiftfin/Views/ItemOverviewView.swift | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Shared/Extensions/String.swift b/Shared/Extensions/String.swift index cdd9abbcd..d01c66fa5 100644 --- a/Shared/Extensions/String.swift +++ b/Shared/Extensions/String.swift @@ -114,6 +114,23 @@ extension String { return s } + + var htmlToAttributedString: NSAttributedString? { + guard let data = data(using: .utf8) else { return nil } + do { + return try NSAttributedString( + data: data, + options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], + documentAttributes: nil + ) + } catch { + return nil + } + } + + var htmlToString: String { + htmlToAttributedString?.string ?? "" + } } extension CharacterSet { diff --git a/Swiftfin/Views/ItemOverviewView.swift b/Swiftfin/Views/ItemOverviewView.swift index d6a60f5ce..0a5a95a6f 100644 --- a/Swiftfin/Views/ItemOverviewView.swift +++ b/Swiftfin/Views/ItemOverviewView.swift @@ -28,7 +28,7 @@ struct ItemOverviewView: View { } if let itemOverview = item.overview { - Text(itemOverview) + Text(itemOverview.htmlToString) .font(.body) .multilineTextAlignment(.leading) } From 9b4bc3284ba3a603646a0ebbb80ebed80c9107cc Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Fri, 24 May 2024 17:37:08 +1000 Subject: [PATCH 2/3] Added a new Component to translate HTML text --- Swiftfin/Components/HTMLFormattedText.swift | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Swiftfin/Components/HTMLFormattedText.swift diff --git a/Swiftfin/Components/HTMLFormattedText.swift b/Swiftfin/Components/HTMLFormattedText.swift new file mode 100644 index 000000000..55e86b247 --- /dev/null +++ b/Swiftfin/Components/HTMLFormattedText.swift @@ -0,0 +1,60 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +struct HTMLFormattedText: UIViewRepresentable { + let text: String + private let textView = UITextView() + + init(_ content: String) { + self.text = content + } + + func makeUIView(context: UIViewRepresentableContext) -> UITextView { + textView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true + textView.isSelectable = false + textView.isUserInteractionEnabled = false + textView.translatesAutoresizingMaskIntoConstraints = false + textView.isScrollEnabled = false + return textView + } + + func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext) { + DispatchQueue.main.async { + if let attributeText = self.converHTML(text: text) { + textView.attributedText = attributeText + } else { + textView.text = "" + } + } + } + + private func converHTML(text: String) -> NSAttributedString? { + guard let data = text.data(using: .utf8) else { + return nil + } + + if let attributedString = try? NSMutableAttributedString( + data: data, + options: [.documentType: NSAttributedString.DocumentType.html], + documentAttributes: nil + ) { + let range = NSRange(location: 0, length: attributedString.length) + attributedString.enumerateAttribute(.font, in: range, options: []) { value, range, _ in + if let oldFont = value as? UIFont { + let fontSize = UIScreen.main.bounds.width * 0.05 // Adjust the multiplier as needed + let dynamicFont = UIFont.systemFont(ofSize: fontSize) + let newFont = oldFont.withSize(dynamicFont.pointSize) + attributedString.addAttributes([.font: newFont], range: range) + } + } + return attributedString + } else { + return nil + } + } +} From 3ff25de980893f042c4787407a8d07f034f4b2c9 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Fri, 24 May 2024 17:45:49 +1000 Subject: [PATCH 3/3] Fixed bug with not parsing HTML files correctly --- Shared/Extensions/String.swift | 17 ----------------- Swiftfin.xcodeproj/project.pbxproj | 4 ++++ Swiftfin/Components/HTMLFormattedText.swift | 2 ++ Swiftfin/Views/ItemOverviewView.swift | 4 +--- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Shared/Extensions/String.swift b/Shared/Extensions/String.swift index d01c66fa5..cdd9abbcd 100644 --- a/Shared/Extensions/String.swift +++ b/Shared/Extensions/String.swift @@ -114,23 +114,6 @@ extension String { return s } - - var htmlToAttributedString: NSAttributedString? { - guard let data = data(using: .utf8) else { return nil } - do { - return try NSAttributedString( - data: data, - options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], - documentAttributes: nil - ) - } catch { - return nil - } - } - - var htmlToString: String { - htmlToAttributedString?.string ?? "" - } } extension CharacterSet { diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 4a8cda5f5..ef97b6a37 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A872683142E00D78B61 /* ServerDiscovery.swift */; }; 091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A872683142E00D78B61 /* ServerDiscovery.swift */; }; + 49C012192C007C5000F7B130 /* HTMLFormattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C012182C007C5000F7B130 /* HTMLFormattedText.swift */; }; 4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; }; 4E8B34EA2AB91B6E0018F305 /* ItemFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */; }; 4E8B34EB2AB91B6E0018F305 /* ItemFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */; }; @@ -917,6 +918,7 @@ /* Begin PBXFileReference section */ 091B5A872683142E00D78B61 /* ServerDiscovery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerDiscovery.swift; sourceTree = ""; }; + 49C012182C007C5000F7B130 /* HTMLFormattedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLFormattedText.swift; sourceTree = ""; }; 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = ""; }; 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemFilter.swift; sourceTree = ""; }; 531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; @@ -2058,6 +2060,7 @@ E1581E26291EF59800D6C640 /* SplitContentView.swift */, E1D27EE62BBC955F00152D16 /* UnmaskSecureField.swift */, E157562F29355B7900976E1F /* UpdateView.swift */, + 49C012182C007C5000F7B130 /* HTMLFormattedText.swift */, ); path = Components; sourceTree = ""; @@ -4211,6 +4214,7 @@ E18E0208288749200022598C /* BlurView.swift in Sources */, E18E01E7288747230022598C /* CollectionItemContentView.swift in Sources */, E1E1643F28BB075C00323B0A /* SelectorView.swift in Sources */, + 49C012192C007C5000F7B130 /* HTMLFormattedText.swift in Sources */, C46DD8D22A8DC1F60046A504 /* LiveVideoPlayerCoordinator.swift in Sources */, E18ACA8B2A14301800BB4F35 /* ScalingButtonStyle.swift in Sources */, E18E01DF288747230022598C /* iPadOSMovieItemView.swift in Sources */, diff --git a/Swiftfin/Components/HTMLFormattedText.swift b/Swiftfin/Components/HTMLFormattedText.swift index 55e86b247..62cd0017b 100644 --- a/Swiftfin/Components/HTMLFormattedText.swift +++ b/Swiftfin/Components/HTMLFormattedText.swift @@ -6,6 +6,8 @@ // Copyright (c) 2024 Jellyfin & Jellyfin Contributors // +import SwiftUI + struct HTMLFormattedText: UIViewRepresentable { let text: String private let textView = UITextView() diff --git a/Swiftfin/Views/ItemOverviewView.swift b/Swiftfin/Views/ItemOverviewView.swift index 0a5a95a6f..bcb900a41 100644 --- a/Swiftfin/Views/ItemOverviewView.swift +++ b/Swiftfin/Views/ItemOverviewView.swift @@ -28,9 +28,7 @@ struct ItemOverviewView: View { } if let itemOverview = item.overview { - Text(itemOverview.htmlToString) - .font(.body) - .multilineTextAlignment(.leading) + HTMLFormattedText(itemOverview) } } .frame(maxWidth: .infinity, alignment: .leading)