当前位置: 首页 > 知识库问答 >
问题:

在iText7中,当html转换为pdf时,如何为某些页面提供一个横向方向?

祁飞飙
2023-03-14

我使用iText7使用pdfhtml的converttopdf()方法将HTML转换为PDF。我想为我的PDF文档中的几个特定页面更改页面方向。这些页面的内容是动态的,我们无法猜测有多少页面应该处于横向(即动态表的内容可能需要一个以上的页面)

当前情况:我创建了一个自定义工作者(实现了ITagWorker),该工作者在标签 后面对页面进行景观化

public byte[] generatePDF(String html) {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    PdfWriter pdfWriter = new PdfWriter(byteArrayOutputStream);
    PdfDocument pdfDocument = new PdfDocument(pdfWriter);
    try {

        ConverterProperties properties = new ConverterProperties();

        properties.setTagWorkerFactory(
                new DefaultTagWorkerFactory() {
                    @Override
                    public ITagWorker getCustomTagWorker(
                            IElementNode tag, ProcessorContext context) {
                        if ("landscape".equalsIgnoreCase(tag.name())) {
                            return new LandscapeDivTagWorker();
                        }
                        return null;
                    }
                } );

        MediaDeviceDescription mediaDeviceDescription = new MediaDeviceDescription(MediaType.PRINT);
        properties.setMediaDeviceDescription(mediaDeviceDescription);

        HtmlConverter.convertToPdf(html, pdfDocument, properties);
    } catch (IOException e) {
        e.printStackTrace();
    }
    pdfDocument.close();
    return byteArrayOutputStream.toByteArray();
}

自定义工作者:

public class LandscapeDivTagWorker implements ITagWorker {

    @Override
    public void processEnd(IElementNode element, ProcessorContext context) {
    }

    @Override
    public boolean processContent(String content, ProcessorContext context) {
        return false;
    }

    @Override
    public boolean processTagChild(ITagWorker childTagWorker, ProcessorContext context) {
        return false;
    }

    @Override
    public IPropertyContainer getElementResult() {
        return new AreaBreak(new PageSize(PageSize.A4).rotate());
    }
}

是否有一种方法来定义所有应该在景观中显示的内容?

<p>Display in portrait</p>
<landscape>
<div>
<p>display in landscape</p>
…
<table>
..
</table>
</div>
</landscape>
<p>Display in portrait</p>
<div class="landscape">
<p>display in landscape</p>
…
<table>
..
</table>
</div>

ps:我遵循这个提示,通过使用自定义的CssApplierFactory来更改PDF(由html创建)中的某些页面的页面方向,但结果是相同的=>只是使用landscape类的第一个页面在landscape中,而表的其他内容在landscape中

共有1个答案

梁渊
2023-03-14

做起来其实相当棘手,但整个机制还是足够灵活的,能够容纳这种需求。

我们将致力于支持以下语法:

<p>Display in portrait</p>
<landscape>
<div>
<p>display in landscape</p>
<p>content</p>
.....
<p>content</p>
</div>
</landscape>
<p> After portrait </p>

首先,我们需要将HTML内容转换为元素,然后将这些元素添加到文档中,而不是直接的HTML->PDF转换。这是必要的,因为在HTML的情况下,CSS规范规定了一个单独的页面大小处理机制,它不够灵活,无法满足您的需求,因此我们将使用本机iText布局机制。

其思想是,除了通过向areabreak传递参数来自定义新页面大小之外,我们还将更改pdfdocument的默认页面大小,以便使用该自定义新页面大小创建所有后续页面。为此,我们需要一直通过pdfdocument。高级代码如下所示:

PdfDocument pdfDocument = new PdfDocument(new PdfWriter(outFilePath));
ConverterProperties properties = new ConverterProperties();
properties.setTagWorkerFactory(new CustomTagWorkerFactory(pdfDocument));

Document document = new Document(pdfDocument);
List<IElement> elements = HtmlConverter.convertToElements(new FileInputStream(inputHtmlPath), properties);
for (IElement element : elements) {
    if (element instanceof IBlockElement) {
        document.add((IBlockElement) element);
    }
}

pdfDocument.close();

自定义标记工作者工厂也几乎没有变化--它只是将pdfdocument传递给标记工作者:

private static class CustomTagWorkerFactory extends DefaultTagWorkerFactory {
    PdfDocument pdfDocument;

    public CustomTagWorkerFactory(PdfDocument pdfDocument) {
        this.pdfDocument = pdfDocument;
    }

    @Override
    public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
        if ("landscape".equalsIgnoreCase(tag.name())) {
            return new LandscapeDivTagWorker(tag, context, pdfDocument);
        }
        return null;
    }
}

grandapedivtagworker的思想是创建一个div包装器,将 标记的内部内容放在那里,并且用areabreak元素包围它-前一个元素将强制以横向方向断开新的页面,并更改整个文档的默认页面大小,而后一个元素将所有内容还原-强制拆分为纵向页面大小,并将默认页面大小设置为纵向页面大小。注意,我们还将使用setnextrendererareabreak设置一个自定义呈现器,以便在出现中断时实际设置默认页面大小:

private static class LandscapeDivTagWorker extends DivTagWorker {
    private PdfDocument pdfDocument;

    public LandscapeDivTagWorker(IElementNode element, ProcessorContext context, PdfDocument pdfDocument) {
        super(element, context);
        this.pdfDocument = pdfDocument;
    }

    @Override
    public IPropertyContainer getElementResult() {
        IPropertyContainer baseElementResult = super.getElementResult();
        if (baseElementResult instanceof Div) {
            Div div = new Div();
            AreaBreak landscapeAreaBreak = new AreaBreak(new PageSize(PageSize.A4).rotate());
            landscapeAreaBreak.setNextRenderer(new DefaultPageSizeChangingAreaBreakRenderer(landscapeAreaBreak, pdfDocument));
            div.add(landscapeAreaBreak);
            div.add((IBlockElement) baseElementResult);
            AreaBreak portraitAreaBreak = new AreaBreak(new PageSize(PageSize.A4));
            portraitAreaBreak.setNextRenderer(new DefaultPageSizeChangingAreaBreakRenderer(portraitAreaBreak, pdfDocument));
            div.add(portraitAreaBreak);
            baseElementResult = div;
        }
        return baseElementResult;
    }
}

自定义区域分隔呈现器的实现非常简单--我们只将默认页面大小设置为pdfdocument--其余的都是通过默认实现来完成的,我们从:

private static class DefaultPageSizeChangingAreaBreakRenderer extends AreaBreakRenderer {
    private PdfDocument pdfDocument;
    private AreaBreak areaBreak;

    public DefaultPageSizeChangingAreaBreakRenderer(AreaBreak areaBreak, PdfDocument pdfDocument) {
        super(areaBreak);
        this.pdfDocument = pdfDocument;
        this.areaBreak = areaBreak;
    }

    @Override
    public LayoutResult layout(LayoutContext layoutContext) {
        pdfDocument.setDefaultPageSize(areaBreak.getPageSize());
        return super.layout(layoutContext);
    }
}

因此,您将得到与屏幕截图中类似的页面设置:

 类似资料:
  • 为了将html转换为pdf,我使用了IText7的API convertToDocument,传递参数模板的ByteArrayInputStream、PDFDocument和ConvertProperties。 相关代码段: “PDF间接对象属于其他PDF文档。请将对象复制到当前PDF文档。” 转换属性的创建 在每次调用convertToDocument API之前创建对象 我是不是漏掉了什么?

  • 本文向大家介绍如何使用wkhtml2pdf将html页面转换为pdf,包括了如何使用wkhtml2pdf将html页面转换为pdf的使用技巧和注意事项,需要的朋友参考一下 本文将指导您安装可以将HTML页面或HTML输出转换为PDF格式的工具。如果我们需要将Pdf发送给一组客户或客户,以及发送报告而不是HTML(您可以通过PDF格式通过电子邮件发送)的报告,则此功能还将有助于生成Pdf的代码。为您

  • 给定一个包含纵向页面的现有 PDF 文件,我该如何以编程方式(使用 .NET)处理该文件,以便在横向页面上生成具有相同内容的新文件。 新页面应该充分利用可用的横向宽度。页面数量可能会增加,因为现有的纵向页面可能不适合一个横向页面。 背景故事:我们使用Google Sheets REST API来生成pdf文档。如果有很多列,文档可能会很宽。不幸的是,Google Drive REST API总是以

  • 我有一个从LibreOffice Writer导出的PDF文件。这里有一个例子。一些页面具有纵向,其他页面具有横向。我正在使用Linux,当我在evice或Foxit Reader中查看文件时,它会正确显示,即所有文本行都是水平的。它也可以在我的打印机上用A4纸正确打印:横向页面逆时针旋转90度,使文本行垂直。 问题是:文档将在另一台设备(出版商中)上打印,我被告知所有页面都必须具有纵向方向。我不

  • 我想更改PDF文档中特定几页的页面方向。PDF文档是使用html2pdf在html模板外创建的。它是这样的:如果页面的内容(通常是一个表格)太宽而不能以纵向显示,则以横向显示页面。 按照[如何将页面旋转为横向,页面内容应该为纵向iTextpdf][1]中的提示 [1]:如何将页面旋转成横向,页面内容应该在纵向iTextpdf中我已经创建了我的自定义标签和TagWorker。 } 问题是:首先,这没