itext - 使用 Itext 和 html2Pdf 实现自定义 Css 属性的正确方法
问题描述
我正在使用 Itext 7 和他们的 html2Pdf 库。有没有办法实现例如 cmyk 颜色。
.wootWorkingCMYK-color{
color: cmyk( 1 , 0.69 , 0.08 , 0.54);
}
我知道 itext 核心部分非常好,正在寻找使用 html2Pdf 方面。我知道 CssApplierFactory 但这似乎离链条很远。
解决方案
好吧,当然有一种处理自定义 CSS 属性(如cmyk
颜色)的方法,但不幸的是,代码会非常庞大,您需要为不同的情况编写相当多的代码。我将展示如何为字体应用自定义颜色,但例如对于背景、边框或其他情况,您需要以类似的方式编写单独的代码。其背后的原因是 iText 布局结构,虽然设计时考虑了 HTML/CSS,但并不是 100% 相似,并且有一些差异,您必须编写代码。
话虽如此,如果您可以从源代码中分叉、构建和使用您的自定义版本,这就是我建议的方式。虽然它有一些缺点,比如必须重新设置基础才能获得更新,但解决方案会更简单、更通用。为此,请搜索CssUtils.parseRgbaColor
pdfHTML 模块中的用法,您会发现它用于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 的实现SpanTagCssApplier
,BlockCssApplier
但您可以咨询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..
或您可能想要使用的任何属性名称。
正如我在答案开头提到的那样,我的建议是构建您自己的自定义版本:)
推荐阅读
- python - NumPy:为什么 np.linalg.eig 和 np.linalg.svd 给出不同的 SVD V 值?
- javascript - 在 amMap 中使用 javascript 更新图像 - Amcharts 4
- r - 找到向量的值以将数据分成组,每组中的数据数量相同
- laravel - 使用 Maatwebsite/Laravel-Excel 生成 csv 文件如何使标题返回动态数组
- json - 通过“路径”从 json 对象中提取数据
- linux - 如何为用户清除 linux 机器上的缓存?
- reactjs - ReasonReact useState 与获取的数据
- r - 根据另一列中的条件过滤重复项
- javascript - 如何延迟使用 Javascript 制作的警报框?
- python - 如何将索引的名称更改为熊猫中的其他值