将归因字符串转换为“简单”标签的html [英] Convert attributed string, to, "simple" tagged html

查看:131
本文介绍了将归因字符串转换为“简单”标签的html的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将NSAttributedString转换为html,如下所示:

 这是一个< i>字符串< / i> ;有一些< b>简单< / b> < I>< b取代; HTML< / B>< / I>标签在里面。 

不幸的是,如果您使用苹果的内置系统,它会生成详细的基于CSS的html。 (以下示例供参考。)



那么如何从NSAttributedString生成简单的标记html?

这是一个很糟糕的解决方案。

  func simpleTagStyle(fromNSAttributedString att:NSAttributedString) - > ; String {

//详细,脆弱的解决方案

//本质上,迭代attString
中的所有属性范围//记下它们的风格是粗体斜体等
//(完全忽略对我们不感兴趣的任何内容)
//然后基本上得到简单的字符串,然后在这些范围内找到它。
//注意令人讨厌的多重属性情况
//(另一种方法是重复排除属性范围
//一个接一个,直到没有遗漏为止。)

让rangeAll = NSRange(位置:0,长度:att.length)

//记下所有粗体/斜体的范围
// (使用元组来记住哪个是哪个)
var allBlocks:[(NSRange,String)] = []

att.enumerateAttribute(
NSFontAttributeName,
in :rangeAll,
options:.longestEffectiveRangeNotRequired

{value,range,stop in

处理程序:if let font = value as? UIFont {

让b = font.fontDescriptor.symbolicTraits.contains(.traitBold)
让i = font.fontDescriptor.symbolicTraits.contains(.traitItalic)

如果b&&我{
allBlocks.append((range,bolditalic))
中断处理程序//注意不要重复
}

if b {
allBlocks.append((范围,bold))
中断处理程序
}

如果我{
allBlocks.append((range,italic) )
中断处理程序
}
}

}

//向后遍历它们并将它们移走

var plainString = att.string

for allBlocks.reversed(){

let r = oneBlock.0.range(for:plainString)!

let w = plainString.substring(with:r)

if oneBlock.1 ==bolditalic{
plainString.replaceSubrange(r,with: < b>< i>+ w +< / i>< / b>)
}

if oneBlock.1 ==bold{
plainString.replaceSubrange(r,with:< b>+ w +< / b>)
}

if oneBlock.1 ==italic{
plainString.replaceSubrange(r,with:< i>+ w +< / i>)
}

}

返回plainString
}






如何使用苹果的内置系统,不幸的是生成全功能CSS等。

  x = ...您的NSAttributedText 
var resultHtmlText =
do {

let r = NSRange(location:0,length:x.length)
let att = [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType]

let d = try x.data(fro m:r,documentAttributes:att)

if h = String(data:d,encoding:.utf8){
resultHtmlText = h
}
}
catch {
print(完全无法转换为html! \\\
> \(x)< \\\)
}
print(resultHtmlText)

$ b $

 <!DOCTYPE html PUBLIC -  // W3C / / DTD HTML 4.01 // ENhttp://www.w3.org/TR/html4/strict.dtd\"&b 
< html>
< head>
< ; meta http-equiv =Content-Typecontent =text / html; charset = UTF-8>
< meta http-equiv =Content-Style-Typecontent =text / css>
< title>< / title>
< meta name =Generatorcontent =Cocoa HTML Writer>
< style type =text / css>
p.p1 {margin:0.0px 0.0px 0.0 px 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>因此,< / span>< span class = s2>这里是< / span>< span class =s1>一些< / span>的东西< / p>
< / body>
< / html>


解决方案

根据 enumerateAttribute:inRange:options:usingBlock: ,尤其是讨论 部分:


如果将此方法发送到NSMutableAttributedString的实例,则允许
突变(删除,添加或更改),只要它在提供给该区块的范围内为
;突变后,
枚举继续处理紧接在
处理范围之后的范围,之后处理范围的长度将被调整为
。 (枚举器基本上假设在指定范围内发生
长度的任何变化)。例如,如果块被称为
且范围从位置N开始,并且该块将删除所有
字符在提供的范围内,下一次调用也将通过N作为
范围的索引。


换句话说,在闭包/块中,使用范围,可以删除/替换那里的字符。操作系统会在该范围的末端放置一个标记。一旦你做了修改,它将计算标记新的范围,以便枚举的下一次迭代将从新标记开始。
因此,您不必保留数组中的所有范围,然后通过执行后向替换来应用更改,以不修改范围。不要打扰你,这些方法已经做到了。


我不是Swift开发者,我更像是一个Objective-C版本。所以我的Swift代码可能并不尊重所有的Swift规则,并且可能会有点丑陋(可选项,包装等没有完成,如果没有完成>等) / p>

以下是我的解决方案:

  func attrStrSimpleTag() - >无效{

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.0 px 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 \>因此,< / span>< span class = \s2 \>>这里是< / span> < span class = \s1\>一些< / span>材料< / p>< / body>< / html>
让attr =试试! 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))
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>)

如果是斜体
{
retString =< i>。appending(retString)
retString.append(< / i>)
}

返回retString
}

输出对于最后一个,其他两个打印仅用于调试):

  $> taggedString:因此,< i>< b>这里是< / b>< / i>一些东西

编辑:
Objective-C Version写的,也许是一些问题)。

$ $ $ $ $ $ $ $ $ $ {
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.0 px 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 \>因此,< / span>< span class = \s2 \>>这里是< / span> < span class = \s1\>一些< / span>材料< / 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 *)值;
NSLog(@找到的字体:%@,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);


$ b - (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];
}
返回retString;
}


I want to convert an NSAttributedString, to html like this:

This is a <i>string</i> with some <b>simple</b> <i><b>html</b></i> tags in it.

Unfortunately if you use apple's built-in system it generates verbose css-based html. (Example below for reference.)

So how to generate simple tagged html from an NSAttributedString?

I wrote a very verbose, fragile call to do it, which is a poor solution.

func simpleTagStyle(fromNSAttributedString att: NSAttributedString)->String {

    // verbose, fragile solution

    // essentially, iterate all the attribute ranges in the attString
    // make a note of what style they are, bold italic etc
    // (totally ignore any not of interest to us)
    // then basically get the plain string, and munge it for those ranges.
    // be careful with the annoying "multiple attribute" case
    // (an alternative would be to repeatedly munge out attributed ranges
    // one by one until there are none left.)

    let rangeAll = NSRange(location: 0, length: att.length)

    // make a note of all of the ranges of bold/italic
    // (use a tuple to remember which is which)
    var allBlocks: [(NSRange, String)] = []

    att.enumerateAttribute(
        NSFontAttributeName,
        in: rangeAll,
        options: .longestEffectiveRangeNotRequired
        )
            { value, range, stop in

            handler: if let font = value as? UIFont {

                let b = font.fontDescriptor.symbolicTraits.contains(.traitBold)
                let i = font.fontDescriptor.symbolicTraits.contains(.traitItalic)

                if b && i {
                    allBlocks.append( (range, "bolditalic") )
                    break handler   // take care not to duplicate
                }

                if b {
                    allBlocks.append( (range, "bold") )
                    break handler
                }

                if i {
                    allBlocks.append( (range, "italic") )
                    break handler
                }
            }

        }

    // traverse those backwards and munge away

    var plainString = att.string

    for oneBlock in allBlocks.reversed() {

        let r = oneBlock.0.range(for: plainString)!

        let w = plainString.substring(with: r)

        if oneBlock.1 == "bolditalic" {
            plainString.replaceSubrange(r, with: "<b><i>" + w + "</i></b>")
        }

        if oneBlock.1 == "bold" {
            plainString.replaceSubrange(r, with: "<b>" + w + "</b>")
        }

        if oneBlock.1 == "italic" {
            plainString.replaceSubrange(r, with: "<i>" + w + "</i>")
        }

    }

    return plainString
}


So here's how to use Apple's built in system, which unfortunately generates full-on CSS etc.

x = ... your NSAttributedText
var resultHtmlText = ""
do {

    let r = NSRange(location: 0, length: x.length)
    let att = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]

    let d = try x.data(from: r, documentAttributes: att)

    if let h = String(data: d, encoding: .utf8) {
        resultHtmlText = h
    }
}
catch {
    print("utterly failed to convert to html!!! \n>\(x)<\n")
}
print(resultHtmlText)

Example output....

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

解决方案

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

这篇关于将归因字符串转换为“简单”标签的html的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆