HTML转PDF之HTML解析
开始想先写PDF的内容显示,但是发现大家对HTML的解析更为感兴趣,所以我先写HTML的解析。
HTML的解析,很多人都会想着用IHTMLDocument这个内置对象来完成,但是我始终觉得这个是在COM的基础上来.NET,感觉不是很爽,同时它并不能完全满足我的要求,所以我自己实现这一过程。这个过程也许很多人认为是个很大的工程量,但是事实上,经过仔细的分析,并没有想象的那么复杂。最后我只用了三四个小时,三四百行的C#代码就实现了。
从功能上,我把HTML的解析分成了结构解析,和显示的外观解析,外观解析这个把它和PDF的生成放到一起,因为他们的关系很密切这里先说结构解析。从宏观上来看,HTML也是一种很类似于XML的一种格式,只是相对来说它的容错性上要好很多。所以HTML的解析事实上很容易就转化成了写一个有容错性的XML解析器。
HTML解析后的结构可以有多种,我比较喜欢DOM模型,所以我把他们往DOM模型方向去解析。熟悉HTML的人都知道,一个HTML标签可以分解成为以:一个标签名,若干个属性,具体的文本内容 这样的一种形式。所以抛开显示的因素去想,这时候整个HTML用这个模型就可以很容易地解析了个大概了,剩下的没有处理的就是style标签和style属性,还有外挂的样式文件。估计写得大家会比较晕,下面就来看我具体的解析过程。
首先是解析生成HTML节点树。在OO出现之前,描述节点树的数据结构还是比较复杂的,感觉要用个二维的树形结构,因为每个节点里会含多个属性,同时也会含有多个子节点,但是有了OO,很简单了,放两个ArrayList一个里面放属性对象,另一个放子节点。为了处理方便,我先用正则把里面的script块和注释块给替换掉了。下面的解析中,就是根据起始标签的<,>号去分解。至于容错,开始想是用迭代法来实现,也就是在前面找到一个标签后,到后面去找反标签,但是后来发现要处理的异常情况太多了,所以就放弃了。而使用堆栈的形式。每找到一个正标签,就把把它压栈,当找到一个封闭的标签的时候,就把它出栈,当然不能和栈顶的标签来匹配的时候,就会往下找,直到找到为止。事实上这个压栈的顺序就含着包含的关系,所以生成这个树的过程很容易就完成了。
其次就是属性的解析,因为HTML的高容错性,所以属性有多种情况,有单独的一个单词做属性如:checked,selected,readonly等,还有A=BA=“B” A=‘B’ 这三种形式,为了容易解析,这里用了一个正则来实现,为了方便大家的使用,就把它写出来了:((\\w+)=((\"[^\"]*\")|('[^']*')|([^\"^' ]+)))|readonly|checked|selected。对匹配后的结果进行分析,这样就完成了大部分的属性的解析。当然这里面还有一个STYLE块的解析的问题还没有解决,不过相对来说也不难,就是根据属性值里面的;号进行分割,然后再按:号来取样式的名称和值。
这样一个HTML解析过程基本上就完成了。
下一次我将写PDF的内容显示,也就是PostScript。
HTML的解析,很多人都会想着用IHTMLDocument这个内置对象来完成,但是我始终觉得这个是在COM的基础上来.NET,感觉不是很爽,同时它并不能完全满足我的要求,所以我自己实现这一过程。这个过程也许很多人认为是个很大的工程量,但是事实上,经过仔细的分析,并没有想象的那么复杂。最后我只用了三四个小时,三四百行的C#代码就实现了。
从功能上,我把HTML的解析分成了结构解析,和显示的外观解析,外观解析这个把它和PDF的生成放到一起,因为他们的关系很密切这里先说结构解析。从宏观上来看,HTML也是一种很类似于XML的一种格式,只是相对来说它的容错性上要好很多。所以HTML的解析事实上很容易就转化成了写一个有容错性的XML解析器。
HTML解析后的结构可以有多种,我比较喜欢DOM模型,所以我把他们往DOM模型方向去解析。熟悉HTML的人都知道,一个HTML标签可以分解成为以:一个标签名,若干个属性,具体的文本内容 这样的一种形式。所以抛开显示的因素去想,这时候整个HTML用这个模型就可以很容易地解析了个大概了,剩下的没有处理的就是style标签和style属性,还有外挂的样式文件。估计写得大家会比较晕,下面就来看我具体的解析过程。
首先是解析生成HTML节点树。在OO出现之前,描述节点树的数据结构还是比较复杂的,感觉要用个二维的树形结构,因为每个节点里会含多个属性,同时也会含有多个子节点,但是有了OO,很简单了,放两个ArrayList一个里面放属性对象,另一个放子节点。为了处理方便,我先用正则把里面的script块和注释块给替换掉了。下面的解析中,就是根据起始标签的<,>号去分解。至于容错,开始想是用迭代法来实现,也就是在前面找到一个标签后,到后面去找反标签,但是后来发现要处理的异常情况太多了,所以就放弃了。而使用堆栈的形式。每找到一个正标签,就把把它压栈,当找到一个封闭的标签的时候,就把它出栈,当然不能和栈顶的标签来匹配的时候,就会往下找,直到找到为止。事实上这个压栈的顺序就含着包含的关系,所以生成这个树的过程很容易就完成了。
其次就是属性的解析,因为HTML的高容错性,所以属性有多种情况,有单独的一个单词做属性如:checked,selected,readonly等,还有A=BA=“B” A=‘B’ 这三种形式,为了容易解析,这里用了一个正则来实现,为了方便大家的使用,就把它写出来了:((\\w+)=((\"[^\"]*\")|('[^']*')|([^\"^' ]+)))|readonly|checked|selected。对匹配后的结果进行分析,这样就完成了大部分的属性的解析。当然这里面还有一个STYLE块的解析的问题还没有解决,不过相对来说也不难,就是根据属性值里面的;号进行分割,然后再按:号来取样式的名称和值。
这样一个HTML解析过程基本上就完成了。
下一次我将写PDF的内容显示,也就是PostScript。