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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM