DTCoreText项目使用浅析 (1)

DTCoreText在项目中已经有了成熟的应用,在iphone和ipad上都有着相关应用;在ipad也做到了图文混排,这里打算对DTCoreText项目做个简单的分析和归纳。

从整体的结构上来讲,DTCoreText的处理步骤为: 解析Html结构(使用libxml2库)、根据分析结果生成NSAttributedString、再通过NSAttributedString利用CoreText绘图,完成整个过程。

当然中间会涉及一系列的处理步骤和问题,我打算列出部分。

首先从解析Html结构开始,这里采用的是libxml2的SAX处理方式:

DTHTMLParser 类负责和libxml2库接口衔接,

void _startDocument(void *context);
void _endDocument(void *context);
void _startElement(void *context, const xmlChar *name,const xmlChar **atts);
void _endElement(void *context, const xmlChar *name);
void _characters(void *context, const xmlChar *ch, int len);
void _comment(void *context, const xmlChar *value);
void _dterror(void *context, const char *msg, ...);

void _cdataBlock(void *context, const xmlChar *value, int len);

void _ignorableWhitespace (void *context, const xmlChar *ch, int len);
// 注册回调的函数入口
if ([_delegate respondsToSelector:@selector(parserDidStartDocument:)])
    {
        _handler.startDocument = _startDocument;
    }
    else
    {
        _handler.startDocument = NULL;
    }
    
    if ([_delegate respondsToSelector:@selector(parserDidEndDocument:)])
    {
        _handler.endDocument = _endDocument;
    }
    else
    {
        _handler.endDocument = NULL;
    }
    
    if ([delegate respondsToSelector:@selector(parser:didStartElement:attributes:)])
    {
        _handler.startElement = _startElement;
    }
    else
    {
        _handler.startElement = NULL;
    }
xmlSAX2InitHtmlDefaultSAXHandler(&_handler); // 注册回调接口
    
// create a parse context 开始调用解析
    _parserContext = htmlCreatePushParserCtxt(&_handler, (__bridge void *)self, dataBytes, (int)dataSize, NULL, charEnc);
    
    // set some options
    htmlCtxtUseOptions(_parserContext, HTML_PARSE_RECOVER | HTML_PARSE_NONET | HTML_PARSE_COMPACT | HTML_PARSE_NOBLANKS);

DTHTMLAttributedStringBuilder类中负责处理对应的事件回调,这里使用了两个GCD队列来处理解析,_stringParsingQueue负责处理调用DTHTMLParser解析,

_stringAssemblyGroup负责处理对应标签回调事件中NSAttributedString处理,最后dispatch_group_wait等待两个队列都完成后,再返回结果。

解析具体步骤:

1. 处理前,通过_registerTagStartHandlers和_registerTagEndHandlers注册对应的标签block,这里可以定制想要处理的html块,如果对于部分不想处理的html标签,这里就可以忽略

2. 对于样式,通过DTCSSStylesheet来解析处理,其中方法 parseStyleBlock 用来解析css文本,这里使用了纯文本的解析方式,具体解析方法这里就不写了;其实个人感觉,html核心还是在CSS布局上,DTCoreText只提供对部分css的支持;后面再把css布局和CoreText联系起来。

3. 由于html是dom结构,DTHTMLAttributedStringBuilder采用了当前结构的方式,也就是保存当前的上下文 DTHTMLElement *currentTag;

 DTHTMLElement结构:

@property (nonatomic, strong) DTHTMLElement *parent;
@property (nonatomic, copy) DTCoreTextFontDescriptor *fontDescriptor;
@property (nonatomic, copy) DTCoreTextParagraphStyle *paragraphStyle;
@property (nonatomic, strong) DTTextAttachment *textAttachment;
@property (nonatomic, copy) NSURL *link;
@property (nonatomic, copy) NSString *anchorName;
@property (nonatomic, strong) DTColor *textColor;
@property (nonatomic, strong) DTColor *backgroundColor;
@property (nonatomic, copy) NSString * backgroundImageName;
@property (nonatomic, copy) NSString * answerInfo;
@property (nonatomic, assign) BOOL isAnswEnd;
@property (nonatomic, assign) BOOL isCntEnd;
@property (nonatomic, copy) NSString *tagName;
@property (nonatomic, copy) NSString *beforeContent;
@property (nonatomic, copy) NSString *text;
@property (nonatomic, copy) NSArray *shadows;
@property (nonatomic, assign) CTUnderlineStyle underlineStyle;
@property (nonatomic, assign) BOOL tagContentInvisible;
@property (nonatomic, assign) BOOL strikeOut;
@property (nonatomic, assign) NSInteger superscriptStyle;
@property (nonatomic, assign) NSInteger headerLevel;
@property (nonatomic, assign) DTHTMLElementDisplayStyle displayStyle;
@property (nonatomic, readonly) DTHTMLElementFloatStyle floatStyle;
@property (nonatomic, assign) BOOL isColorInherited;
@property (nonatomic, assign) BOOL preserveNewlines;
@property (nonatomic, assign) DTHTMLElementFontVariant fontVariant;
@property (nonatomic, assign) CGFloat textScale;
@property (nonatomic, assign) CGSize size;
@property (nonatomic, strong) NSDictionary *attributes;

这里保存了父节点,和诸多样式;这里可以扩展设置相关属性,用于后面的Coretext使用;比如 isAnswEnd isCntEnd都是我们项目中自定义的属性

4. 接下来就是调用

- (void)parser:(DTHTMLParser *)parser didStartElement:(NSString *)elementName attributes:(NSDictionary *)attributeDict

这个方法处理节点和样式,并负责调用标签对应的块,这里主要是设置各种属性,然后通过 [tmpString appendAttributedString:[currentTag attributedString]];来构造attributedString,通过

[tmpString appendString:string]; 增加下纯文本

5. 接着就是调用

- (void)parser:(DTHTMLParser *)parser didEndElement:(NSString *)elementName


DTHTMLElement *popChild = currentTag;

currentTag = currentTag.parent;

[currentTag removeChild:popChild];

包括一些退栈处理等

到此,NSAttributedString已经生成了,接下来就是要对NSAttributedString做相关的展示处理。

 

 

posted @ 2013-12-15 23:22  wtndcs  阅读(3635)  评论(3编辑  收藏  举报