首页 > 解决方案 > 使用 Itext 和 html2Pdf 实现自定义 Css 属性的正确方法

问题描述

我正在使用 Itext 7 和他们的 html2Pdf 库。有没有办法实现例如 cmyk 颜色。

.wootWorkingCMYK-color{
  color: cmyk( 1 , 0.69 , 0.08 , 0.54);
}

我知道 itext 核心部分非常好,正在寻找使用 html2Pdf 方面。我知道 CssApplierFactory 但这似乎离链条很远。

标签: itextitext7

解决方案


好吧,当然有一种处理自定义 CSS 属性(如cmyk颜色)的方法,但不幸的是,代码会非常庞大​​,您需要为不同的情况编写相当多的代码。我将展示如何为字体应用自定义颜色,但例如对于背景、边框或其他情况,您需要以类似的方式编写单独的代码。其背后的原因是 iText 布局结构,虽然设计时考虑了 HTML/CSS,但并不是 100% 相似,并且有一些差异,您必须编写代码。

话虽如此,如果您可以从源代码中分叉、构建和使用您的自定义版本,这就是我建议的方式。虽然它有一些缺点,比如必须重新设置基础才能获得更新,但解决方案会更简单、更通用。为此,请搜索CssUtils.parseRgbaColorpdfHTML 模块中的用法,您会发现它用于BackgroundApplierUtil, BorderStyleApplierUtil, FontStyleApplierUtil, OutlineApplierUtil. 在那里你会找到类似的代码

if (!CssConstants.TRANSPARENT.equals(cssColorPropValue)) {
    float[] rgbaColor = CssUtils.parseRgbaColor(cssColorPropValue);
    Color color = new DeviceRgb(rgbaColor[0], rgbaColor[1], rgbaColor[2]);
    float opacity = rgbaColor[3];
    transparentColor = new TransparentColor(color, opacity);
} else {
    transparentColor = new TransparentColor(ColorConstants.BLACK, 0f);
}

我相信您也可以对其进行调整cmyk,因为您知道自己非常了解核心部分。

现在,没有自定义 pdfHTML 版本的解决方案实际上是从实现ICssApplierFactory或子类化默认实现开始DefaultCssApplierFactory。我们最感兴趣的是自定义 and 的实现SpanTagCssApplierBlockCssApplier但您可以咨询DefaultTagCssApplierMapping以获取应用程序和使用它们的案例的完整列表,以便您可以决定要在代码中处理哪些。

我将向您展示如何在我提到的两个主要应用程序类中添加对字体颜色的自定义颜色空间的支持,您可以从那里开始工作。

private static class CustomCssApplierFactory implements ICssApplierFactory {
    private static final ICssApplierFactory DEFAULT_FACTORY = new DefaultCssApplierFactory();

    @Override
    public ICssApplier getCssApplier(IElementNode tag) {
        ICssApplier defaultApplier = DEFAULT_FACTORY.getCssApplier(tag);
        if (defaultApplier instanceof SpanTagCssApplier) {
            return new CustomSpanTagCssApplier();
        } else if (defaultApplier instanceof BlockCssApplier) {
            return new CustomBlockCssApplier();
        } else {
            return defaultApplier;
        }
    }
}

private static class CustomSpanTagCssApplier extends SpanTagCssApplier {
    @Override
    protected void applyChildElementStyles(IPropertyContainer element, Map<String, String> css, ProcessorContext context, IStylesContainer stylesContainer) {
        super.applyChildElementStyles(element, css, context, stylesContainer);
        String color = css.get("color2");
        if (color != null) {
            color = color.trim();
            if (color.startsWith("cmyk")) {
                element.setProperty(Property.FONT_COLOR, new TransparentColor(parseCmykColor(color)));
            }
        }
    }
}

private static class CustomBlockCssApplier extends BlockCssApplier {
    @Override
    public void apply(ProcessorContext context, IStylesContainer stylesContainer, ITagWorker tagWorker) {
        super.apply(context, stylesContainer, tagWorker);
        IPropertyContainer container = tagWorker.getElementResult();
        if (container != null) {
            String color = stylesContainer.getStyles().get("color2");
            if (color != null) {
                color = color.trim();
                if (color.startsWith("cmyk")) {
                    container.setProperty(Property.FONT_COLOR, new TransparentColor(parseCmykColor(color)));
                }
            }
        }
    }
}

// You might want a safer implementation with better handling of corner cases
private static DeviceCmyk parseCmykColor(String color) {
    final String delim = "cmyk(), \t\r\n\f";
    StringTokenizer tok = new StringTokenizer(color, delim);
    float[] res = new float[]{0, 0, 0, 0};
    for (int k = 0; k < 3; ++k) {
        if (tok.hasMoreTokens()) {
            res[k] = Float.parseFloat(tok.nextToken());
        }
    }
    return new DeviceCmyk(res[0], res[1], res[2], res[3]);
}

拥有该自定义代码,您应该ConverterProperties相应地配置并将其传递给 HtmlConverter:

ConverterProperties properties = new ConverterProperties();
properties.setCssApplierFactory(new CustomCssApplierFactory());
HtmlConverter.convertToPdf(..., properties);

您可能已经注意到我使用color2了代替color,这是有原因的。pdfHTML 有一个 CSS 属性验证机制(就像浏览器一样),在计算元素的有效属性时丢弃无效的 CSS 属性。不幸的是,目前没有自定义此验证逻辑的机制,当然它目前将cmyk颜色视为无效声明。因此,如果您真的想拥有自定义color属性,您将不得不预处理您的 HTML 并替换声明,如color: cmyk...tocolor2: cmyk..或您可能想要使用的任何属性名称。

正如我在答案开头提到的那样,我的建议是构建您自己的自定义版本:)


推荐阅读