使用ITextSharp解析XHTML时,跳过向PDF添加空表

使用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元

当您尝试创建包含0列的PdfTable时,ITextSharp会引发错误

我需要使用XSLT转换生成的XHTML并从中生成PDF。目前我正在使用ITextSharp来实现这一点。我遇到的问题是,生成的XHTML有时包含0行的表,因此当ITextSharp试图将它们解析到一个表中时,它抛出并错误地说表中有0列

它之所以说是0列,是因为ITextSharp将表中的列数设置为每行中列数的最大值,并且由于没有行,任何给定行中的最大列数都是0

如何捕获这些包含0行的HTML表声明并阻止它们被解析为PDF元素?

我发现导致错误的代码片段在HtmlPipeline中,因此我可以将实现复制并粘贴到一个扩展HtmlPipeline并重写其方法的类中,然后执行逻辑检查其中是否有空表,但这似乎是草率和低效的

是否有办法在解析空表之前捕获它?

=解决方案=

标签处理器

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();

        }
    }
}