使用iText库将html转换为pdf时未应用hr的内联CSS

时间:2016-11-25 10:33:48

标签: android html itext xmlworker

我正在使用Itext库for android将html转换为pdf,这工作正常但在某些事情上它没有正确解析。我想创建一个红色的虚线分隔符,但它总是给我一个深灰色的实线分隔符。

我的html标签是

<hr noshade style="border: 0; width:100%;border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: red">

我的转换代码

        Document document = new Document(PageSize.A4);

        //this sets the margin to the created pdf
        document.setMargins(35, 35, 150, 100);

        PdfWriter writer = PdfWriter.getInstance(document,
                new FileOutputStream(fileWithinMyDir));
        if (isPrescription) {
            HeaderFooterPageEvent event = new HeaderFooterPageEvent();
            writer.setPageEvent(event);
        } else {
            CertificateFooterPageEvent event = new CertificateFooterPageEvent();
            writer.setPageEvent(event);
        }
        document.open();
        HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
        htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
        htmlContext.setImageProvider(new AbstractImageProvider() {
            public String getImageRootPath() {

                Uri uri = Uri.parse("file:///android_asset/");

                return uri.toString();
            }
        });


        CSSResolver cssResolver =
                XMLWorkerHelper.getInstance().getDefaultCssResolver(false);

        // Pipelines
        PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
        HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
        CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);

        XMLWorker worker = new XMLWorker(css, true);

        XMLParser p = new XMLParser(worker);
        InputStream is = new ByteArrayInputStream(htmlString.getBytes());
        XMLWorkerHelper.getInstance().parseXHtml(writer, document, is);
        p.parse(is);

        document.close();

2 个答案:

答案 0 :(得分:5)

我是.NET开发人员,因此代码在C#中。但您应该能够轻松翻译以下内容。

iText是一个PDF优先的库,[X]HTML解析非常复杂,因此在这方面并不完整。每当解析[X]HTML并且事情不符合您对特定标记的期望时,您应遵循的基本步骤是:

  1. 验证XML Worker支持代码:Tags class
  2. 如果 支持标记,在这种情况下为true,请查看默认实现。这是由the HorizontalRule class处理的。但是,我们发现不支持您的用例,因此一种方法是将该代码用作蓝图。 (如下所示)您还可以继承特定的标记类并覆盖End()方法as done here。无论哪种方式,您所做的只是实现自定义标签处理器。
  3. 如果支持标记,则需要通过继承AbstractTagProcessor来推广自己的自定义标记处理器。
  4. 无论如何,这是一个让你入门的简单例子。首先,自定义标签处理器:

    public class CustomHorizontalRule : AbstractTagProcessor
    {
        public override IList<IElement> Start(IWorkerContext ctx, Tag tag)
        {
            IList<IElement> result;
            LineSeparator lineSeparator;
            var cssUtil = CssUtils.GetInstance();
    
            try
            {
                IList<IElement> list = new List<IElement>();
                HtmlPipelineContext htmlPipelineContext = this.GetHtmlPipelineContext(ctx);
    
                Paragraph paragraph = new Paragraph();
                IDictionary<string, string> css = tag.CSS;
                float baseValue = 12f;
                if (css.ContainsKey("font-size"))
                {
                    baseValue = cssUtil.ParsePxInCmMmPcToPt(css["font-size"]);
                }
                string text;
                css.TryGetValue("margin-top", out text);
                if (text == null) text = "0.5em";
    
                string text2;
                css.TryGetValue("margin-bottom", out text2);
                if (text2 == null) text2 = "0.5em";
    
                string border;
                css.TryGetValue(CSS.Property.BORDER_BOTTOM_STYLE, out border);
                lineSeparator = border != null && border == "dotted"
                    ? new DottedLineSeparator()
                    : new LineSeparator();
    
                var element = (LineSeparator)this.GetCssAppliers().Apply(
                    lineSeparator, tag, htmlPipelineContext
                );
    
                string color;
                css.TryGetValue(CSS.Property.BORDER_BOTTOM_COLOR, out color);
                if (color != null)
                {
                    // WebColors deprecated, but docs don't state replacement
                    element.LineColor = WebColors.GetRGBColor(color);
                }
    
                paragraph.SpacingBefore += cssUtil.ParseValueToPt(text, baseValue);
                paragraph.SpacingAfter += cssUtil.ParseValueToPt(text2, baseValue);
                paragraph.Leading = 0f;
                paragraph.Add(element);
                list.Add(paragraph);
                result = list;
            }
            catch (NoCustomContextException cause)
            {
                throw new RuntimeWorkerException(
                    LocaleMessages.GetInstance().GetMessage("customcontext.404"),
                    cause
                );
            }
            return result;
        }
    }
    

    大多数代码都是直接从现有来源获取的,但CSS.Property.BORDER_BOTTOM_STYLECSS.Property.BORDER_BOTTOM_COLOR的检查除了设置{{1}内嵌的边框样式和颜色外} <hr>属性。

    然后将上面的自定义标记处理器添加到XML Worker style

    TagProcessorFactory

    有一点需要注意的是,尽管我们使用的是速记using (var stream = new FileStream(OUTPUT_FILE, FileMode.Create)) { using (var document = new Document()) { var writer = PdfWriter.GetInstance(document, stream); document.Open(); var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory(); // custom tag processor above tagProcessorFactory.AddProcessor( new CustomHorizontalRule(), new string[] { HTML.Tag.HR } ); var htmlPipelineContext = new HtmlPipelineContext(null); htmlPipelineContext.SetTagFactory(tagProcessorFactory); var pdfWriterPipeline = new PdfWriterPipeline(document, writer); var htmlPipeline = new HtmlPipeline(htmlPipelineContext, pdfWriterPipeline); var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true); var cssResolverPipeline = new CssResolverPipeline( cssResolver, htmlPipeline ); var worker = new XMLWorker(cssResolverPipeline, true); var parser = new XMLParser(worker); var xHtml = "<hr style='border:1px dotted red' />"; using (var stringReader = new StringReader(xHtml)) { parser.Parse(stringReader); } } } 内联样式,但iText的CSS解析器似乎在内部设置了 all 样式。即,您可以使用四种速记样式中的任何一种进行检查 - 我恰好使用borderCSS.Property.BORDER_BOTTOM_STYLE

    生成的PDF:

    enter image description here

答案 1 :(得分:0)

您可以使用div而不使用任何内容或任何内容而不是hr,并为该div提供边框样式,我相信它会适用于您的情况。