NSAttributedString to HTML to NSAttributedString color degradation

332 Views Asked by At

I have a rich text editor in which the user can edit font, color etc. The model keeps this information in memory as an instance of NSAttributedString. When i want to store this info and write to disk, i convert the attributed string to html via the following function:

func attributedStringToHtml(attributedString: NSAttributedString) -> String {
    var ret = ""
    do {
        let htmlData = try attributedString.data(from: NSRange( location: 0, length: attributedString.length), documentAttributes: [.documentType: NSAttributedString.DocumentType.html])
        ret =  String.init(data: htmlData, encoding: String.Encoding.utf8)!
    } catch { print("error:", error) }
    return ret
}

and to pull the data from disk back into an instance of of NSAttributedString I use:

func htmlToAttributedString(htmlString: String) -> NSAttributedString {
    return  NSAttributedString.init(html: htmlString.data(using: String.Encoding.utf8)!, documentAttributes: nil)!
}

The problem occurs by cycling through these functions multiple times, that is:

saving -> loading -> saving -> loading -> saving -> loading.

After each load cycle the stored color becomes darker and darker. I believe this has to do with the conversions of colorspaces ?

This behavior does not happen on macOS 10.13. Copy and paste this into a playground as an example of what is happening:

import AppKit
import PlaygroundSupport

func htmlToAttributedString(htmlString: String) -> NSAttributedString {
    return  NSAttributedString.init(html: htmlString.data(using: String.Encoding.utf8)!, documentAttributes: nil)!
    }

func attributedStringToHtml(attributedString: NSAttributedString) -> String {
    var ret = ""
    do {
        let htmlData = try attributedString.data(from: NSRange( location: 0, length: attributedString.length), documentAttributes: [.documentType: NSAttributedString.DocumentType.html])
        ret =  String.init(data: htmlData, encoding: String.Encoding.utf8)!
    } catch { 
        print("error:", error) 
    }
    return ret
}

let initialHtmlString = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n<title></title>\n<meta name=\"Generator\" content=\"Cocoa HTML Writer\">\n<meta name=\"CocoaVersion\" content=\"1671.5\">\n<style type=\"text/css\">\np.p1 {margin: 0.0px 0.0px 0.0px 0.0px; text-align: center; line-height: 16.0px; font: 14.0px Helvetica; color: #FF0000; -webkit-text-stroke: #000000}\nspan.s1 {font-kerning: none}\n</style>\n</head>\n<body>\n<p class=\"p1\"><span class=\"s1\">Double-click to edit this text</span></p>\n</body>\n</html>\n"

var attributedString = htmlToAttributedString(htmlString: initialHtmlString)
var backToHtml = attributedStringToHtml(attributedString: attributedString)
var backToAttributedString = htmlToAttributedString(htmlString: backToHtml)
var backToHtmlAgain = attributedStringToHtml(attributedString: backToAttributedString)

print(backToHtmlAgain) // notice the html color value is now f6000b
1

There are 1 best solutions below

1
NSCaleb On

i was able to work around this by using NSAttributedString.DocumentType.rtf in place of html