Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML to NSAttributedString and NSAttributedString to HTML

I want to convert a html string to NSAttributedString and then work on the string like (change colors, fontsizes, fontfamily, background- , foreground-color...) and then convert the string back to plain html from the NSAttributedString.

Converting isn't a problem, but on each time I convert html to NSAS and back the fontsize getting bigger and bigger...

Sample playground:

// 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()

This is the playground result. You can see that the second text is bigger as the first text but I didn't change the font size anywhere.

Is this a bug or had I have to set a fix font size?

Here is the playground result, you can see that the second text is bigger then the first text but I didn't change the font size anywhere.

UPDATE:

I accept @Lou Franco answer. I don´t know why NSAS convert px to pt and back but here is my workaround:

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
    }

The only disadvantage of this is that you lose different text heights in your NSAS....

If anybody has the solution or better work around feel free to post your answer.

like image 839
Konstantin Heinrich Avatar asked Feb 10 '15 20:02

Konstantin Heinrich


1 Answers

I solved this by applying 0.75 ratio on each fond size in your string. Say if you have multiple fonts in your attributed string, when you looping though all of them, simple apply the ratio and then you are all set. Here is my code in 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

This is the piece of code that is running in my application

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
    }
}

To use it:

let htmlStr: String = "<font size=\"6\">font a</font><font size=\"16\">Font b</font>"
let attriStr: NSAttributedString? = htmlStr.htmlAttributedString()
like image 102
Fangming Avatar answered Sep 28 '22 04:09

Fangming