24

I have an NSAttributed string (coming from HTML) that I set for a UITextView.

- (void)setHtml:(NSString *)html {

    NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];

    // Create the HTML string
    NSDictionary *importParams = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
    NSError *error = nil;
    self.htmlString = [[NSAttributedString alloc] initWithData:htmlData options:importParams documentAttributes:NULL error:&error];

    self.editorView.attributedText = self.htmlString;

}

I then let the user edit what they want, and I would like to then convert it out to HTML again, so I use:

- (NSString *)getHTML {
    NSDictionary *exportParams = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
    NSData *htmlData = [self.editorView.attributedText dataFromRange:NSMakeRange(0, self.editorView.attributedText.length) documentAttributes:exportParams error:nil];
    return [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
}

It does return HTML, but it isn't how I want it. Everything is given a class attribute, and the CSS it put at the top of the document. Things like images and links are not even included in the returned HTML and probably tons more issues.

Is there a better way to get HTML from an NSAttributedString? Or, is there a way I could parse the NSAttributedString and write my own HTML?

Zword
  • 6,605
  • 3
  • 27
  • 52
Nic Hubbard
  • 41,587
  • 63
  • 251
  • 412

3 Answers3

13

May be you could look at that repository: https://github.com/IdeasOnCanvas/Ashton

there is 2 interesting class:

AshtonHTMLReader.h

 - (NSAttributedString *)attributedStringFromHTMLString:(NSString *)htmlString;

And the writer:

AshtonHTMLWriter.h

- (NSString *)HTMLStringFromAttributedString:(NSAttributedString *)input;

The html generated isn't very nice but if you try to display it in a uiwebview, it looks pretty good.

Simple idea for image: encode it with base64 and put it directly in a < img > tag with the right frame.

It's ugly but it works => I've used this process to create and edit some html file few month ago

Alban
  • 1,624
  • 11
  • 21
  • 1
    This is a cool idea, but checking the source shows it is quite limited in its parsing capabilities. Styling would be an issue. – Léo Natan Feb 11 '14 at 14:31
  • Actually this is not ideal. But this project are used to retrieve fonts, colors, Italic / Bold style etc ...Another problem is that the use of base64 encoding for images directly into the html makes pages very long to load! – Alban Feb 11 '14 at 15:15
  • As an extension to the question: I am interested in a way to include images in HTML without using base64 encoding. Something like the microsoft's .chm format, if someone has any idea! – Alban Feb 11 '14 at 15:25
  • This project is great! I had an issue where I wanted to paste a UITextView's attributed text into an E-Mail message that the user could send to themselves. Using the method in the original question resulted in loss of much of the formatting (font type and size). With this project, you can just call ```mn_HTMLRepresentation``` which is a category method on NSAttributedString and pass that to an MFMailComposeViewController as the body, and voila, a beautifully formatted E-Mail that looks exactly as it did in the app's textview. – rvijay007 May 02 '14 at 20:26
9

This is a complex issue, and I will start with a shorter answer. You may ask me questions in the comments, and I will expand on the answer as needed.

We also tried to go the attributed string route, but found it not suited for full HTML editing. Many elements are just not supported, either because the converter is not fully developed, or these elements were deemed outside of scope by Apple. Parsing the attributed string is not good enough, because the attributed string has already lost most of the richness of the HTML by the time you attempt to recreate it.

Instead, we use a webview, load the document normally, and enable contentEditable on the body element. What this does is allow editing of the document in its fullest, limited only by WebKit. At the end, to retrieve the HTML back, we disable contentEditable and take the document.outerHTML to get an entire HTML as it was before, with changes made by the user.

Don't take the decision to implement this method lightly. It is a somewhat complex solution, but certainly possible. A webview is not as nice as a textview, but it can be, given enough massage.

I will expand on this answer as needed.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • Thanks for the response, but we are specifically leaving the UIWebView implementation as it has some serious issues with speed/UIScrollViews. I've been able to get everything working except Lists. – Jesse Naugher Feb 04 '14 at 18:43
  • 1
    @JesseNaugher I have extensive knowledge in fixing webview bugs. If you have specific questions, you can ask questions here or contact me directly. Anyway you look at it, webview is the most robust method for complex editing, unless you wish to enter the world of CoreText (aka the Apple Pages implementation). – Léo Natan Feb 04 '14 at 18:44
  • This is the route I ended up going too. One issue I still have is being able to return valid XHTML. Did you fine a solution to this? – Nic Hubbard Feb 04 '14 at 19:00
  • @NicHubbard While I have not tried, wouldn't giving an XHTML input work? I think edited HTML would preserve the strictness of XHTML just fine. – Léo Natan Feb 04 '14 at 19:07
  • @LeoNatan Yes, it does. But, since you have to add a `xmlns` namespace, we found that when we grab the `innerHTML` that it adds that attribute to all block elements. So we were not sure how to get around that strange issue. – Nic Hubbard Feb 04 '14 at 19:11
  • @NicHubbard Are you adding this to the topmost `html` tag or to a specific element? – Léo Natan Feb 04 '14 at 19:15
  • @LeoNatan The `xmlns` attribute is on the `html` element. Without that it is not valid XHTML and the WebView show a big error. – Nic Hubbard Feb 04 '14 at 19:16
  • @NicHubbard Could be a bug in webkit (i.e. does the same issue reproduce on Safari or Chrome?), but it sound somewhat easy to fix in postprocessing. – Léo Natan Feb 04 '14 at 19:19
  • @LeoNatan Yeah, I could clean up the string, but I really want to get to the bottom of the issue. No, doesn't happen in Safari or even using console.log and checking Safari dev tools. – Nic Hubbard Feb 04 '14 at 19:21
  • @NicHubbard From a quick test in Chrome, this did not reproduce. – Léo Natan Feb 04 '14 at 19:26
  • How do you manage the cursor position with the webview? For example, if you wanted to focus the cursor at the start of the . – ebi Feb 14 '14 at 03:57
3

I also had to convert a NSAtttributedString to HTML in one of my projects. The code for doing this is as follows

//self.attributed String is the attributedString 
NSDictionary *documentAttributes = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
NSData *htmlData = [self.attributedString dataFromRange:NSMakeRange(0, self.attributedString.length) documentAttributes:documentAttributes error:NULL];
NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
NSLog(@"%@", htmlString);
Md. Ibrahim Hassan
  • 5,359
  • 1
  • 25
  • 45
  • The limitation with this approach as already stated in the question is that it skips the img tag. I tested it out and found the img tag is skipped in 9.0 but works fine with iOS 10 – SHN Dec 14 '16 at 11:05
  • Ohk I am glad it works for iOS 10. Sorry it doesn't work for iOS 9 – Md. Ibrahim Hassan Dec 14 '16 at 12:16