HTML 转换为 NSAttributedString 和 NSAttributedString 转换为 HTML

9
我希望把一个HTML字符串转换为NSAttributedString,然后对这个字符串进行修改(比如:更改颜色、字体大小、字体类型、背景色和前景色),最后再把字符串从NSAttributedString转回普通的HTML格式。
转换(Conversion)并不是问题,但是每次我将HTML转换为NSAttributedString,再转回去时,字体大小会变得越来越大……
样例代码:
// Playground - noun: a place where people can play
// NSAS: - NSAttributedString

import UIKit

class Wrapper {

    //MARK: fields
    let apiHtml = "<div style='font-size: 18px'><span style='font-family:&#039;andale mono&#039;, times;'>Dies</span> <span style='font-family:&#039;comic sans ms&#039;, sans-serif;'>ist</span> <strong><span style='font-family:&#039;andale mono&#039;, sans-serif;';>eine</span></strong> <em>formatierte</em> <span style='text-decoration:underline;'>Karte</span>&#160;<span style='font-size:16px;'>die</span> <span style='background-color:#ffff00;'>es</span> zu &#220;bernehmen gilt</div>"

    var newGeneratedHtml = ""
    var textView : UITextView!

    //MARK: constructor
    init() {
        //init textview
        textView = UITextView(frame: CGRectMake(0, 0, 500, 300))

        //convert html into NSAS and set it to textview
        if let attributedText = getAttributedTextFromApiHtmlString(apiHtml) {
            textView.attributedText = attributedText
        }

        //get html text from textfields NSAS
        if let htmlText = getHtmlTextFromTextView() {
            newGeneratedHtml = htmlText
            println(htmlText)
        }

        //set the converted html from textfields NSAS
        if let attributedText = getAttributedTextFromApiHtmlString(newGeneratedHtml) {
            textView.attributedText = attributedText
        }

        //get html text from textfields NSAS
        if let htmlText = getHtmlTextFromTextView() {
            newGeneratedHtml = htmlText
            println(htmlText)
        }
    }

    //MARK: methods
    func getAttributedTextFromApiHtmlString(text : String) -> NSAttributedString? {
        if let attributedText = NSAttributedString(data: text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!, options: [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType], documentAttributes: nil, error: nil) {
            return attributedText
        }
        return nil
    }

    func getHtmlTextFromTextView() -> String? {
        let attributedTextFromTextView = textView.attributedText
        if let htmlData = attributedTextFromTextView.dataFromRange(NSMakeRange(0, attributedTextFromTextView.length), documentAttributes: [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType], error: nil) {
            if let htmlString = NSString(data: htmlData, encoding: NSUTF8StringEncoding) {
                return htmlString
            }
        }
        return nil
    }
}

var w = Wrapper()

这是游乐场的结果。你可以看到第二个文本比第一个文本大,但我没有在任何地方改变字体大小。
这是一个错误还是我需要设置固定字体大小?
更新:
我接受@Lou Franco的答案。我不知道为什么NSAS要将px转换为pt然后再转回来,但这是我的解决方法:
func getAttributedTextFromApiHtmlString(text : String) -> NSAttributedString? {
        if let attributedText = NSAttributedString(data: text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, options: [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType], documentAttributes: nil, error: nil) {

            var res : NSMutableAttributedString = attributedText.mutableCopy() as NSMutableAttributedString
            res.beginEditing()
            var found : Bool = false;
            res.enumerateAttribute(NSFontAttributeName, inRange:NSMakeRange(0, res.length) ,options:NSAttributedStringEnumerationOptions.allZeros, usingBlock: {(value:AnyObject!, range:NSRange, stop:UnsafeMutablePointer<ObjCBool>) -> Void in
                if ((value) != nil) {
                    let oldFont = value as UIFont;
                    let newFont = oldFont.fontWithSize(15)
                    res.removeAttribute(NSFontAttributeName, range:range)
                    res.addAttribute(NSFontAttributeName, value: newFont, range: range)
                    found = true
                    }
                })
            if !found {
                // No font was found - do something else?
            }
            res.endEditing()
            return res
        }
        return nil
    }

这样做的唯一缺点是您会在NSAS中失去不同的文本高度... 如果有人有解决方案或更好的解决方法,请随时发布您的答案。

有趣的是,如果你将生成的HTML再次通过循环输出,它会增加1/3的大小。它会无限重复。 - David Berry
为什么这不清晰或者没有用? - Konstantin Heinrich
3个回答

4

我通过在字符串中的每个字体大小上应用0.75比例来解决了这个问题。如果你的属性字符串中有多个字体,那么当你循环遍历所有字体时,只需简单地应用该比例,然后就可以了。以下是我的Swift 3.0代码:

yourAttrStr.beginEditing()
yourAttrStr.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, yourAttrStr.length), options: .init(rawValue: 0)) { 
        (value, range, stop) in
        if let font = value as? UIFont {
            let resizedFont = font.withSize(font.pointSize * 0.75)
            yourAttrStr.addAttribute(NSFontAttributeName, value: resizedFont, range: range)
        }
}
yourAttrStr.endEditing()//yourAttrStr will be the same size as html string

这是我的应用程序中正在运行的代码块。

extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
        guard let html = try? NSMutableAttributedString(
            data: data,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
            documentAttributes: nil) else { return nil }
        html.beginEditing()
        html.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, html.length), options: .init(rawValue: 0)) {
            (value, range, stop) in
            if let font = value as? UIFont {
                let resizedFont = font.withSize(font.pointSize * 0.75)
                html.addAttribute(NSFontAttributeName,
                                         value: resizedFont,
                                         range: range)
            }
        }
        html.endEditing()
        return html
    }
}

使用方法:

let htmlStr: String = "<font size=\"6\">font a</font><font size=\"16\">Font b</font>"
let attriStr: NSAttributedString? = htmlStr.htmlAttributedString()

1
在Swift 3中采用@fangming的解决方案,这对我很有用:
func newAttrSize(blockQuote: NSAttributedString) -> NSAttributedString
{
    let yourAttrStr = NSMutableAttributedString(attributedString: blockQuote)
    yourAttrStr.enumerateAttribute(.font, in: NSMakeRange(0, yourAttrStr.length), options: .init(rawValue: 0)) {
        (value, range, stop) in
        if let font = value as? UIFont {
            let resizedFont = font.withSize(font.pointSize * 0.75)
            yourAttrStr.addAttribute(.font, value: resizedFont, range: range)
        }
    }

    return yourAttrStr
}

0

这可能是往返过程中的四舍五入误差。尝试使用整数点大小(使用pt而不是px

好的,看着你的控制台输出,它正在将你的px转换为pt,所以也许你可以通过获取转换后来自HTML并将pt更改回px来进行修改。


与pts做相同的事情。 - David Berry
将HTML的字体大小从px更改为pt不会影响任何东西,它会在下一次变大。 - Konstantin Heinrich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接