iText 5 HTML+CSS 到 PDF/A-2:Helvetica 字体未嵌入错误

iText 5 HTML+CSS to PDF/A-2 : Helvetica font not embedded error

以下代码用于使用 iText5 将带有 CSS 的 HTML 文件转换为 PDF/A-2(此代码来自在线提供的示例):

public static final String HTML = "D:\PDFA2\html\sample.html";
public static final String CSS = "D:\PDFA2\html\sample.css";
public static final String DEST = "D:\PDFA2\html\sample.pdf";

public void createPdf(String file) throws IOException, DocumentException {

    Document document = new Document();


    PdfAWriter writer = PdfAWriter.getInstance(document, new FileOutputStream(file),PdfAConformanceLevel.PDF_A_2A);
    writer.setInitialLeading(12.5f);


    document.open();


    CSSResolver cssResolver = new StyleAttrCSSResolver();
    CssFile cssFile = XMLWorkerHelper.getCSS(new FileInputStream(CSS));
    cssResolver.addCss(cssFile);


    HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
    htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());


    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);
    p.parse(new FileInputStream(HTML));


    document.close();
}


public static void main(String[] args) throws IOException, DocumentException {
    File file = new File(DEST);
    file.getParentFile().mkdirs();
    new HTMLtoPDFA2_V2().createPdf(DEST);
}

以下是HTML文件内容:

<h1>Test</h1><p>Hello World</p>

而CSS文件内容为:

{
 font-family: "Arial";
 font-style: normal;
}

然而,这给出了以下异常:

Exception in thread "main" com.itextpdf.text.pdf.PdfAConformanceException: All the fonts must be embedded. This one isn't: Helvetica
at com.itextpdf.text.pdf.internal.PdfAConformanceImp.checkPdfAConformance(PdfAConformanceImp.java:90)
at com.itextpdf.text.pdf.PdfAWriter.checkPdfIsoConformance(PdfAWriter.java:204)
at com.itextpdf.text.pdf.PdfWriter.checkPdfIsoConformance(PdfWriter.java:3281)
at com.itextpdf.text.pdf.PdfWriter.addSimple(PdfWriter.java:2208)
at com.itextpdf.text.pdf.PdfContentByte.setFontAndSize(PdfContentByte.java:1624)
at com.itextpdf.text.pdf.PdfDocument.writeLineToContent(PdfDocument.java:1584)
at com.itextpdf.text.pdf.PdfDocument.flushLines(PdfDocument.java:1275)
at com.itextpdf.text.pdf.PdfDocument.newPage(PdfDocument.java:869)
at com.itextpdf.text.pdf.PdfDocument.close(PdfDocument.java:793)
at com.itextpdf.text.Document.close(Document.java:416)
at in.test.util.pdf.HTMLtoPDFA2_V2.createPdf(HTMLtoPDFA2_V2.java:67)
at in.test.util.pdf.HTMLtoPDFA2_V2.main(HTMLtoPDFA2_V2.java:76)

如何避免这种异常?我不需要使用 Helvetica 字体。 SO 上有很多帖子,但其中 none 似乎提供了解决方案。

将字体提供程序添加到 HtmlPipelineContext 而不是 null

XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("arial.ttf", "Arial");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);

HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());

对于 HTML 文件中的每个标签,在 CSS 文件中定义字体

p, h2 {
 font-family: "Arial";
 font-style: normal;
}

或者,为完整的 html 文件定义通用字体

html {
 font-family: "Arial";
 font-style: normal;
}

我们正在使用我们自己的 PrintFontProvider。这应该让你继续:

private static final CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
private static final HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
static {
    htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
    htmlContext.autoBookmark(false);

    // Our own PrintFontProvider provides Fonts as needed...
    htmlContext.setCssAppliers(new CssAppliersImpl(new PrintFontProvider()));

    // We apply some default styles to the pdf generation...
    try {
        String styling  = "* { font-size: 8pt; line-height: 1.3em; }\n";
        styling += "* * { font-size: inherit; }\n";
        styling += "p      { margin: 5px 2.5pt; }\n";
        styling += "ul, ol { margin: 2px 0; }\n";
        cssResolver.clear();
        cssResolver.addCss(styling, true);
    }
    catch (CssResolverException ex) {
        // Handle errors as needed...
    }
}

这是我们的实现:

public class PrintFontProvider extends FontFactoryImp {

    public static final String  DEFAULT_FONT    = "LiberationSans";

    @Override
    public Font getFont(String fontName, String encoding, boolean embedded, float size, int style, BaseColor color, boolean cached) {
        LogUtils.debug(PrintFontProvider.class, "in getFont with fontName = '" + fontName + "'");

        // ZapfDingbats ist ein Sonderfall... die wollen wir auch liefern!
        if ("ZapfDingbats".equals(fontName)) {
            return new Font(this.load("fonts/ZapfDingbats/zapf-dingbats-bt.ttf"), size, style, color);
        }

        // LiberationSans – http://de.wikipedia.org/wiki/Liberation_(Schriftart) – http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web
        if (style == Font.NORMAL)     return new Font(this.load("fonts/Liberation/LiberationSans-Regular.ttf"),    size, Font.NORMAL, color);
        if (style == Font.BOLD)       return new Font(this.load("fonts/Liberation/LiberationSans-Bold.ttf"),       size, Font.NORMAL, color);
        if (style == Font.BOLDITALIC) return new Font(this.load("fonts/Liberation/LiberationSans-BoldItalic.ttf"), size, Font.NORMAL, color);
        if (style == Font.ITALIC)     return new Font(this.load("fonts/Liberation/LiberationSans-Italic.ttf"),     size, Font.NORMAL, color);
        return new Font(this.load("fonts/Liberation/LiberationSans-Regular.ttf"), size, style, color);
    }

    private BaseFont load(String path) {
        LogUtils.debug(PrintFontProvider.class, "path = {0}", path);
        try {
            URL fontResource = PrintSettings.class.getResource(path);
            if (fontResource == null) {
                LogUtils.warn(PrintFontProvider.class, "Kann Font {0} nicht finden... liefere null!", path);
                return null;
            }

            String fontPath = fontResource.toExternalForm();
            if (fontPath.startsWith("vfs:")) fontPath = fontPath.substring(4);
            if (fontPath.startsWith("/content/")) fontPath = fontPath.substring(fontPath.indexOf("/our/package"));

            BaseFont baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            baseFont.setSubset(true); // Nur die benutzen Chars einbetten!
            return baseFont;
        }
        catch (DocumentException ex) {
            LogUtils.warn(PrintFontProvider.class, "Kann Font {0} nicht erzeugen... liefere DEFAULT_FONT!", path);
        }
        catch (IOException ex) {
            LogUtils.warn(PrintFontProvider.class, "Kann Font {0} nicht erzeugen... liefere DEFAULT_FONT!", path);
        }
        return FontFactory.getFont(DEFAULT_FONT, "UTF-8", true, 8f, Font.NORMAL, PrintSettings.COLOR_TEXT).getBaseFont();
    }

    private static boolean notInitialized = true;
    public static void initFactory() {
        if (notInitialized) {
            FontFactory.defaultEmbedding = true;
            FontFactory.setFontImp(new PrintFontProvider());
            notInitialized = false;
        }
    }
}

您应该调用 PrintFontProvider.initFactory() 将其设置为默认值。