【问题标题】:Skip adding empty tables to PDF when parsing XHTML using ITextSharp使用 ITextSharp 解析 XHTML 时跳过向 PDF 添加空表
【发布时间】:2014-06-30 19:14:43
【问题描述】:

当您尝试创建具有 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>();
    }
}

并使用标签处理器...

        //CSS
        var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true);

        //HTML
        var fontProvider = new XMLWorkerFontProvider();
        var cssAppliers = new CssAppliersImpl(fontProvider);

        var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory();
        tagProcessorFactory.AddProcessor(new EmptyTableTagProcessor(), new string[] { "table" });

        var htmlContext = new HtmlPipelineContext(cssAppliers);
        htmlContext.SetTagFactory(tagProcessorFactory);

        //PIPELINE
        var pipeline =
            new CssResolverPipeline(cssResolver,
            new HtmlPipeline(htmlContext,
            new PdfWriterPipeline(document, pdfWriter)));

        //XML WORKER
        var xmlWorker = new XMLWorker(pipeline, true);

        using (var stringReader = new StringReader(html))
        {
            xmlParser.Parse(stringReader);
        }

此解决方案删除了​​空表标签,仍然将 PDF 作为管道的一部分写入。

【问题讨论】:

    标签: pdf pdf-generation itextsharp itext html-table


    【解决方案1】:

    您应该能够编写自己的标签处理器,通过子类化iTextSharp.tool.xml.html.AbstractTagProcessor 来解决该场景。事实上,为了让您的生活更轻松,您可以将已经存在的更具体的iTextSharp.tool.xml.html.table.Table 子类化:

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

    不幸的是,如果您想使用自定义标签处理器,您不能使用快捷方式XMLWorkerHelper 类,而是需要将 HTML 解析为元素并将它们添加到您的文档中。为此,您需要一个 iTextSharp.tool.xml.IElementHandler 实例,您可以像这样创建:

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

    您可以将上述代码与以下代码一起使用,其中包含一些示例无效 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();
    
            }
        }
    }
    

    【讨论】:

    • 当我在不使用 TableTagProcessor 的情况下运行您发布的示例时,我收到一条错误消息,指出“此文档没有页面”。即使我只使用简单的有效 HTML。但是当我在没有 ElementHandler 的情况下使用 TableTagProcessor 时,我没有收到错误消息吗?它似乎在没有元素处理程序的情况下工作。
    • 如果你传入一个无效的表格,你将得到第一个异常。你可以通过总是在末尾添加一个带有空格的段落来欺骗系统。我想你也可以检查writer.PageEmpty。如果我注释掉 AddProcessor 行并使用 其他所有内容,包括 HTML 字符串,我仍然会得到您原来的异常。在没有看到您如何将上述内容转换为不使用 IElementHandler 的情况下,我无法与其他评论交谈。
    猜你喜欢
    • 1970-01-01
    • 2015-03-03
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 2014-12-12
    相关资源
    最近更新 更多