使用ITextSharp解析XHTML时,跳过向PDF添加空表
当您尝试创建包含0列的PdfTable时,ITextSharp会引发错误 我需要使用XSLT转换生成的XHTML并从中生成PDF。目前我正在使用ITextSharp来实现这一点。我遇到的问题是,生成的XHTML有时包含0行的表,因此当ITextSharp试图将它们解析到一个表中时,它抛出并错误地说表中有0列 它之所以说是0列,是因为ITextSharp将表中的列数设置为每行中列数的最大值,并且由于没有行,任何给定行中的最大列数都是0 如何捕获这些包含0行的HTML表声明并阻止它们被解析为PDF元素? 我发现导致错误的代码片段在HtmlPipeline中,因此我可以将实现复制并粘贴到一个扩展HtmlPipeline并重写其方法的类中,然后执行逻辑检查其中是否有空表,但这似乎是草率和低效的 是否有办法在解析空表之前捕获它? =解决方案= 标签处理器使用ITextSharp解析XHTML时,跳过向PDF添加空表,pdf,pdf-generation,itextsharp,itext,html-table,Pdf,Pdf Generation,Itextsharp,Itext,Html Table,当您尝试创建包含0列的PdfTable时,ITextSharp会引发错误 我需要使用XSLT转换生成的XHTML并从中生成PDF。目前我正在使用ITextSharp来实现这一点。我遇到的问题是,生成的XHTML有时包含0行的表,因此当ITextSharp试图将它们解析到一个表中时,它抛出并错误地说表中有0列 它之所以说是0列,是因为ITextSharp将表中的列数设置为每行中列数的最大值,并且由于没有行,任何给定行中的最大列数都是0 如何捕获这些包含0行的HTML表声明并阻止它们被解析为PDF元
public class EmptyTableTagProcessor : Table
{
public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent)
{
if (currentContent.Count > 0)
{
return base.End(ctx, tag, currentContent);
}
return new List<IElement>();
}
}
此解决方案删除了空表标记,但仍将PDF作为管道的一部分写入。您应该能够编写自己的标记处理器,通过子类化来解释这种情况。事实上,为了让您的生活更加轻松,您可以将已经存在的更具体的子类化: 您可以将上面的代码与包含一些示例无效HTML的以下代码一起使用
//Hold everything in memory
using (var ms = new MemoryStream()) {
//Create new PDF document
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
//Sample HTML
string html = "<table><tr><td>Hello</td></tr></table><table></table>";
//Create an instance of our element helper
var XhtmlHelper = new SampleHandler();
//Begin pipeline
var htmlContext = new HtmlPipelineContext(null);
//Get the default tag processor
var tagFactory = iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory();
//Add an instance of our new processor
tagFactory.AddProcessor(new TableTagProcessor(), new string[] { "table" });
//Bind the above to the HTML context part of the pipeline
htmlContext.SetTagFactory(tagFactory);
//Get the default CSS handler and create some boilerplate pipeline stuff
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
var pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new ElementHandlerPipeline(XhtmlHelper, null)));//Here's where we add our IElementHandler
//The worker dispatches commands to the pipeline stuff above
var worker = new XMLWorker(pipeline, true);
//Create a parser with the worker listed as the dispatcher
var parser = new XMLParser();
parser.AddListener(worker);
//Finally, parse our HTML directly.
using (TextReader sr = new StringReader(html)) {
parser.Parse(sr);
}
//The above did not touch our document. Instead, all "proper" elements are stored in our helper class XhtmlHelper
foreach (var element in XhtmlHelper.elements) {
//Add these to the main document
doc.Add(element);
}
doc.Close();
}
}
}
//将所有内容保存在内存中
使用(var ms=new MemoryStream()){
//创建新的PDF文档
使用(var doc=new Document()){
使用(var writer=PdfWriter.GetInstance(doc,ms)){
doc.Open();
//示例HTML
字符串html=“你好”;
//创建元素助手的实例
var XhtmlHelper=新的SampleHandler();
//启动管道
var htmlContext=新的HtmlPipelineContext(null);
//获取默认的标记处理器
var tagFactory=iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory();
//添加新处理器的实例
tagFactory.AddProcessor(新的TableTagProcessor(),新的字符串[]{“table”});
//将上述内容绑定到管道的HTML上下文部分
htmlContext.SetTagFactory(tagFactory);
//获取默认的CSS处理程序并创建一些样板管道
var cssResolver=XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
var pipeline=new-cssResolver-pipeline(cssResolver,new-HtmlPipeline(htmlContext,new-elementhandlerpippeline(XhtmlHelper,null));//这里是我们添加IElementHandler的地方
//工作人员向上面的管道发送命令
var-worker=new-XMLWorker(管道,true);
//创建一个解析器,将工作进程列为调度程序
var parser=new XMLParser();
AddListener(worker);
//最后,直接解析我们的HTML。
使用(TextReader sr=new StringReader(html)){
parser.Parse(sr);
}
//上面的内容没有涉及到我们的文档,而是将所有“适当”的元素存储在我们的助手类XhtmlHelper中
foreach(XhtmlHelper.elements中的var元素){
//将这些添加到主文档中
单据新增(要素);
}
doc.Close();
}
}
}
当我运行您在未使用TableTagProcessor的情况下发布的示例时,出现一个错误,指出“此文档没有页面”。即使我只使用简单有效的HTML。但是当我在没有ElementHandler的情况下使用TableTagProcessor时,我没有得到错误?但在没有元素处理程序的情况下,它似乎可以正常工作。如果只传入无效表,则会出现第一个异常。你可以通过总是在结尾添加一个空格的段落来欺骗系统。我想你也可以检查writer.PageEmpty
。如果我注释掉AddProcessor
行,并按原样使用所有其他内容,包括HTML字符串,我仍然会得到您最初的异常。如果没有看到您如何将上述内容转换为不使用IElementHandler
,我就无法与其他注释对话。
public class TableTagProcessor : iTextSharp.tool.xml.html.table.Table {
public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent) {
//See if we've got anything to work with
if (currentContent.Count > 0) {
//If so, let our parent class worry about it
return base.End(ctx, tag, currentContent);
}
//Otherwise return an empty list which should make everyone happy
return new List<IElement>();
}
}
public class SampleHandler : iTextSharp.tool.xml.IElementHandler {
//Generic list of elements
public List<IElement> elements = new List<IElement>();
//Add the supplied item to the list
public void Add(IWritable w) {
if (w is WritableElement) {
elements.AddRange(((WritableElement)w).Elements());
}
}
}
//Hold everything in memory
using (var ms = new MemoryStream()) {
//Create new PDF document
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
//Sample HTML
string html = "<table><tr><td>Hello</td></tr></table><table></table>";
//Create an instance of our element helper
var XhtmlHelper = new SampleHandler();
//Begin pipeline
var htmlContext = new HtmlPipelineContext(null);
//Get the default tag processor
var tagFactory = iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory();
//Add an instance of our new processor
tagFactory.AddProcessor(new TableTagProcessor(), new string[] { "table" });
//Bind the above to the HTML context part of the pipeline
htmlContext.SetTagFactory(tagFactory);
//Get the default CSS handler and create some boilerplate pipeline stuff
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
var pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new ElementHandlerPipeline(XhtmlHelper, null)));//Here's where we add our IElementHandler
//The worker dispatches commands to the pipeline stuff above
var worker = new XMLWorker(pipeline, true);
//Create a parser with the worker listed as the dispatcher
var parser = new XMLParser();
parser.AddListener(worker);
//Finally, parse our HTML directly.
using (TextReader sr = new StringReader(html)) {
parser.Parse(sr);
}
//The above did not touch our document. Instead, all "proper" elements are stored in our helper class XhtmlHelper
foreach (var element in XhtmlHelper.elements) {
//Add these to the main document
doc.Add(element);
}
doc.Close();
}
}
}