mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-21 09:02:44 +02:00
b298dedc22
Merge commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1' into feature/foss_update_1_11_19 * commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1': finish version++ version++ comments update submodule remove obsolete tests removed unused code update submodule fix Libolm removal update license macro update license Prepare for new sprint # Conflicts: # Config/AppVersion.xcconfig # IDETemplateMacros.plist # LICENSE # README.md # Riot/Categories/MXSession+Riot.m # Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift # Riot/Managers/KeyValueStorage/Extensions/Keychain.swift # Riot/Managers/KeyValueStorage/KeyValueStore.swift # Riot/Managers/KeyValueStorage/KeychainStore.swift # Riot/Managers/KeyValueStorage/MemoryStore.swift # Riot/Managers/PushNotification/PushNotificationService.m # Riot/Managers/Settings/RiotSettings.swift # Riot/Managers/Settings/Shared/RiotSharedSettings.swift # Riot/Modules/Analytics/AnalyticsUIElement.swift # Riot/Modules/Application/AppCoordinator.swift # Riot/Modules/Application/LegacyAppDelegate.h # Riot/Modules/Application/LegacyAppDelegate.m # Riot/Modules/Authentication/Legacy/AuthenticationViewController.h # Riot/Modules/Authentication/Legacy/AuthenticationViewController.m # Riot/Modules/Authentication/Legacy/Views/AuthInputsView.h # Riot/Modules/Authentication/Legacy/Views/AuthInputsView.m # Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m # Riot/Modules/Common/Recents/RecentsViewController.m # Riot/Modules/Common/WebViewController/WebViewViewController.m # Riot/Modules/Contacts/Details/ContactDetailsViewController.m # Riot/Modules/Contacts/Views/ContactTableViewCell.m # Riot/Modules/Favorites/FavouritesViewController.h # Riot/Modules/Favorites/FavouritesViewController.m # Riot/Modules/GlobalSearch/UnifiedSearchViewController.m # Riot/Modules/People/PeopleViewController.h # Riot/Modules/People/PeopleViewController.m # Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift # Riot/Modules/Room/DataSources/RoomDataSource.m # Riot/Modules/Room/Files/RoomFilesViewController.m # Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m # Riot/Modules/Room/Members/RoomParticipantsViewController.m # Riot/Modules/Room/RoomViewController.m # Riot/Modules/Room/Settings/RoomSettingsViewController.m # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift # Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h # Riot/Modules/Rooms/RoomsViewController.h # Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift # Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift # Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift # Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift # Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift # Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift # Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift # Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift # Riot/Modules/SetPinCode/PinCodePreferences.swift # Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift # Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m # Riot/Modules/Settings/Security/SecurityViewController.m # Riot/Modules/Settings/SettingsViewController.m # Riot/Modules/SplitView/SplitViewCoordinator.swift # Riot/Modules/SplitView/SplitViewCoordinatorType.swift # Riot/Modules/StartChat/StartChatViewController.m # Riot/Modules/TabBar/MasterTabBarController.h # Riot/Modules/TabBar/MasterTabBarController.m # Riot/Utils/EventFormatter.m # Riot/Utils/HTMLFormatter.swift # Riot/Utils/Tools.m # RiotNSE/NotificationService.swift
108 lines
4.9 KiB
Swift
108 lines
4.9 KiB
Swift
//
|
||
// Copyright 2021-2024 New Vector Ltd.
|
||
//
|
||
// SPDX-License-Identifier: AGPL-3.0-only
|
||
// Please see LICENSE in the repository root for full details.
|
||
//
|
||
|
||
import Foundation
|
||
import DTCoreText
|
||
import UIKit
|
||
|
||
@objcMembers
|
||
class HTMLFormatter: NSObject {
|
||
/// Builds an attributed string from a string containing html.
|
||
///
|
||
/// - Parameters:
|
||
/// - htmlString: The html string to use.
|
||
/// - allowedTags: The html tags that should be allowed.
|
||
/// - font: The default font to use.
|
||
/// - imageHandler: The image handler for the formatted string
|
||
/// - extraOptions: Extra (or override) options to apply for the format. See DTCoreText's documentation for available options.
|
||
/// - postFormatOperations: Optional block to provide operations to apply
|
||
/// - Returns: The built `NSAttributedString`.
|
||
/// - Note: It is recommended to include "p" and "body" tags in `allowedTags` as these are often added when parsing.
|
||
static func formatHTML(_ htmlString: String,
|
||
withAllowedTags allowedTags: [String],
|
||
font: UIFont,
|
||
andImageHandler imageHandler: DTHTMLElement.ImageHandler? = nil,
|
||
extraOptions: [AnyHashable: Any] = [:],
|
||
postFormatOperations: ((NSMutableAttributedString) -> Void)? = nil) -> NSAttributedString {
|
||
guard let data = htmlString.data(using: .utf8) else {
|
||
return NSAttributedString(string: htmlString)
|
||
}
|
||
|
||
let sanitizeCallback: DTHTMLAttributedStringBuilderWillFlushCallback = { [allowedTags, font, imageHandler] (element: DTHTMLElement?) in
|
||
element?.sanitize(with: allowedTags, bodyFont: font, imageHandler: imageHandler)
|
||
}
|
||
|
||
var options: [AnyHashable: Any] = [
|
||
DTUseiOS6Attributes: true,
|
||
DTDefaultFontFamily: font.familyName,
|
||
DTDefaultFontName: font.fontName,
|
||
DTDefaultFontSize: font.pointSize,
|
||
DTDefaultLinkDecoration: false,
|
||
DTDefaultLinkColor: ThemeService.shared().theme.colors.links,
|
||
DTWillFlushBlockCallBack: sanitizeCallback
|
||
]
|
||
options.merge(extraOptions) { (_, new) in new }
|
||
|
||
guard let string = self.formatHTML(data, options: options) else {
|
||
return NSAttributedString(string: htmlString)
|
||
}
|
||
|
||
let mutableString = NSMutableAttributedString(attributedString: string)
|
||
MXKTools.removeDTCoreTextArtifacts(mutableString)
|
||
postFormatOperations?(mutableString)
|
||
|
||
// Remove CTForegroundColorFromContext attribute to fix the iOS 16 black link color issue
|
||
// REF: https://github.com/Cocoanetics/DTCoreText/issues/792
|
||
mutableString.removeAttribute(NSAttributedString.Key("CTForegroundColorFromContext"), range: NSRange(location: 0, length: mutableString.length))
|
||
|
||
return mutableString
|
||
}
|
||
|
||
/// Builds an attributed string by replacing a `%@` placeholder with the supplied link text and URL.
|
||
/// - Parameters:
|
||
/// - string: The string to be formatted.
|
||
/// - link: The link text to be inserted.
|
||
/// - url: The URL to be linked to.
|
||
/// - Returns: An attributed string.
|
||
static func format(_ string: String, with link: String, using url: URL) -> NSAttributedString {
|
||
let baseString = NSMutableAttributedString(string: string)
|
||
let attributedLink = NSAttributedString(string: link, attributes: [.link: url])
|
||
|
||
let linkRange = (baseString.string as NSString).range(of: "%@")
|
||
baseString.replaceCharacters(in: linkRange, with: attributedLink)
|
||
|
||
return baseString
|
||
}
|
||
}
|
||
|
||
extension HTMLFormatter {
|
||
/// This replicates DTCoreText's NSAttributedString `initWithHTMLData`.
|
||
/// It sets the sanitize callback on the builder from Swift to avoid EXC_BAD_ACCESS crashes.
|
||
///
|
||
/// - Parameters:
|
||
/// - data: The data in HTML format from which to create the attributed string.
|
||
/// - options: Specifies how the document should be loaded.
|
||
/// - Returns: Returns an initialized object, or `nil` if the data can’t be decoded.
|
||
@objc static func formatHTML(_ data: Data,
|
||
options: [AnyHashable: Any]) -> NSAttributedString? {
|
||
guard !data.isEmpty else {
|
||
return nil
|
||
}
|
||
|
||
let stringBuilder = DTHTMLAttributedStringBuilder(html: data,
|
||
options: options,
|
||
// DTCoreText doesn't use document attributes anyway
|
||
documentAttributes: nil)
|
||
|
||
if let willFlushCallback = options[DTWillFlushBlockCallBack] as? DTHTMLAttributedStringBuilderWillFlushCallback {
|
||
stringBuilder?.willFlushCallback = willFlushCallback
|
||
}
|
||
|
||
return stringBuilder?.generatedAttributedString()
|
||
}
|
||
}
|