用Itext和html2Pdf实现自定义CSS属性的正确方法

时间:2018-11-20 15:17:23

标签: itext itext7

我正在使用Itext 7及其html2Pdf库。 有没有一种方法可以实现例如cmyk颜色。

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

我知道itext核心部分相当不错,正在寻找使用html2Pdf的一面。我知道CssApplierFactory,但是这似乎已经远远超过了。

1 个答案:

答案 0 :(得分:4)

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

话虽如此,如果您可以从源代码中派生,构建和使用自定义版本,这就是我建议的方法。尽管它有必须重新获得更新的缺点,但该解决方案将更简单,更通用。为此,请在pdfHTML模块中搜索CssUtils.parseRgbaColor的用法,您会发现它已在BackgroundApplierUtilBorderStyleApplierUtilFontStyleApplierUtilOutlineApplierUtil中使用。在那里,您会找到类似

的代码
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子类化。我们主要对定制SpanTagCssApplierBlockCssApplier的实现感兴趣,但是您可以咨询DefaultTagCssApplierMapping以获得应用程序及其使用案例的完整列表,以便您确定您要在代码中处理的其中的一个。

我将向您展示如何在我提到的两个主要的applier类中增加对字体颜色的自定义颜色空间的支持,然后您就可以在那里工作了。

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...之类的声明替换为color2: cmyk..或任何您想使用的属性名。 / p>

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