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做相關的展示處理。