Convert attributed string, to, “simple” tagged html

落花浮王杯 提交于 2019-11-27 02:22:12

According to the documentation of enumerateAttribute:inRange:options:usingBlock:, especially the Discussion part which states:

If this method is sent to an instance of NSMutableAttributedString, mutation (deletion, addition, or change) is allowed, as long as it is within the range provided to the block; after a mutation, the enumeration continues with the range immediately following the processed range, after the length of the processed range is adjusted for the mutation. (The enumerator basically assumes any change in length occurs in the specified range.) For example, if block is called with a range starting at location N, and the block deletes all the characters in the supplied range, the next call will also pass N as the index of the range.

In other words, in the closure/block, with the range, you can delete/replace characters there. The OS will put a marker on that end of the range. Once you did your modifications, it will compute the marker new range in order that the next iteration of the enumeration will start from that new marker. So you don't have to keep all the ranges in an array and apply the changes afterwards by doing a backward replacement to not modify the range. Don't bother you with that, the methods does it already.

I'm not a Swift developper, I'm more an Objective-C one. So my Swift code may not respect all "Swift rules", and may be a little ugly (optionals, wrapping, etc badly done, if let not done, etc.)

Here is my solution:

func attrStrSimpleTag() -> Void {

    let htmlStr = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"> <html> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"> <meta http-equiv=\"Content-Style-Type\" content=\"text/css\"> <title></title> <meta name=\"Generator\" content=\"Cocoa HTML Writer\"> <style type=\"text/css\"> p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'Some Font'} span.s1 {font-family: 'SomeFont-ItalicOrWhatever'; font-weight: normal; font-style: normal; font-size: 14.00pt} span.s2 {font-family: 'SomeFont-SemiboldItalic'; font-weight: bold; font-style: italic; font-size: 14.00pt} </style> </head> <body> <p class=\"p1\"><span class=\"s1\">So, </span><span class=\"s2\">here is</span><span class=\"s1\"> some</span> stuff</p> </body></html>"
    let attr = try! NSMutableAttributedString.init(data: htmlStr.data(using: .utf8)!,
                                                   options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
                                                   documentAttributes: nil)
    print("Attr: \(attr)")
    attr.enumerateAttribute(NSFontAttributeName, in: NSRange.init(location: 0, length: attr.length), options: []) { (value, range, stop) in
        if let font = value as? UIFont {
            print("font found:\(font)")
            let isBold = font.fontDescriptor.symbolicTraits.contains(.traitBold)
            let isItalic = font.fontDescriptor.symbolicTraits.contains(.traitItalic)
            let occurence = attr.attributedSubstring(from: range).string
            let replacement = self.formattedString(initialString: occurence, bold: isBold, italic: isItalic)
            attr.replaceCharacters(in: range, with: replacement)
        }
    };

    let taggedString = attr.string
    print("taggedString: \(taggedString)")

}

func formattedString(initialString:String, bold: Bool, italic: Bool) -> String {
    var retString = initialString
    if bold {
        retString = "<b>".appending(retString)
        retString.append("</b>")
    }
    if italic
    {
        retString = "<i>".appending(retString)
        retString.append("</i>")
    }

    return retString
}

Output (for the last one, the other two prints are just for debug):

$> taggedString: So, <i><b>here is</b></i> some stuff

Edit: Objective-C Version (quickly written, maybe some issue).

-(void)attrStrSimpleTag
{
    NSString *htmlStr = @"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"> <html> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"> <meta http-equiv=\"Content-Style-Type\" content=\"text/css\"> <title></title> <meta name=\"Generator\" content=\"Cocoa HTML Writer\"> <style type=\"text/css\"> p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'Some Font'} span.s1 {font-family: 'SomeFont-ItalicOrWhatever'; font-weight: normal; font-style: normal; font-size: 14.00pt} span.s2 {font-family: 'SomeFont-SemiboldItalic'; font-weight: bold; font-style: italic; font-size: 14.00pt} </style> </head> <body> <p class=\"p1\"><span class=\"s1\">So, </span><span class=\"s2\">here is</span><span class=\"s1\"> some</span> stuff</p> </body></html>";
    NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithData:[htmlStr dataUsingEncoding:NSUTF8StringEncoding]
                                                                              options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}
                                                                   documentAttributes:nil
                                                                                error:nil];
    NSLog(@"Attr: %@", attr);

    [attr enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, [attr length]) options:0 usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
        UIFont *font = (UIFont *)value;
        NSLog(@"Font found: %@", font);
        BOOL isBold =  UIFontDescriptorTraitBold & [[font fontDescriptor] symbolicTraits];
        BOOL isItalic =  UIFontDescriptorTraitItalic & [[font fontDescriptor] symbolicTraits];
        NSString *occurence = [[attr attributedSubstringFromRange:range] string];
        NSString *replacement = [self formattedStringWithString:occurence isBold:isBold andItalic:isItalic];
        [attr replaceCharactersInRange:range withString:replacement];
    }];

    NSString *taggedString = [attr string];
    NSLog(@"taggedString: %@", taggedString);
}


-(NSString *)formattedStringWithString:(NSString *)string isBold:(BOOL)isBold andItalic:(BOOL)isItalic
{
    NSString *retString = string;
    if (isBold)
    {
        retString = [NSString stringWithFormat:@"<b>%@</b>", retString];
    }
    if (isItalic)
    {
        retString = [NSString stringWithFormat:@"<i>%@</i>", retString];
    }
    return retString;
}

I have good way to convert NSAttributedString into simple HTML string .

1) Take UIWebView and UITextView.

2) Set your Attributed string in WebView.

[webView loadHTMLString:[yourAttributedString stringByReplacingOccurrencesOfString:@"\n" withString:@"<br/>"] baseURL:nil];

3) Get your HTML string from UIWebView.

NSString *simpleHtmlString = [webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"];
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!