使用PDFBox渲染SVG时如何修改图标颜色?
如何在使用PDFBox将SVG渲染到PDF时修改颜色?
我尝试用PDFBox和SVG Salamander将SVG渲染到PDF,想把图标改成绿色,但不管怎么设置颜色,最终显示都是黑色。我的代码如下:
File svgFile = new File("roots.svg"); SVGUniverse svgUniverse = new SVGUniverse(); SVGDiagram diagram = svgUniverse.getDiagram(svgUniverse.loadSVG(svgFile.toURL())); PdfBoxGraphics2D graphics = new PdfBoxGraphics2D(document, 32, 32); graphics.setColor(Color.green); try { diagram.render(graphics); } catch (SVGException e) { e.printStackTrace(); } finally { graphics.dispose(); } PDFormXObject xform = graphics.getXFormObject(); try (PDPageContentStream contentWriter = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, false)) { AffineTransform transform = AffineTransform.getTranslateInstance(0, 0); transform.scale(2,2); xform.setMatrix(transform); contentWriter.setNonStrokingColor(Color.green); contentWriter.setStrokingColor(Color.green); contentWriter.drawForm(xform); }
依赖配置:
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.25</version> </dependency> <dependency> <groupId>com.weblookandfeel</groupId> <artifactId>svg-salamander</artifactId> <version>1.1.2.2</version> </dependency>
调整contentWriter.setNonStrokingColor和setStrokingColor也没有效果,请问该怎么解决?
问题原因
- SVG文件内部的元素可能已经定义了明确的
fill或stroke颜色,渲染时会优先使用这些属性,覆盖你通过Graphics2D.setColor设置的全局颜色。 - 绘制XForm对象时设置的颜色不会生效,因为XForm是独立的内容流,内部已经包含了完整的绘图指令(包括颜色),外部设置的颜色无法穿透到XForm内部。
解决方案
方案1:渲染前修改SVG元素的颜色(推荐)
直接通过SVG Salamander的API遍历SVG的DOM结构,修改所有元素的填充和描边颜色。
示例代码:
File svgFile = new File("roots.svg"); SVGUniverse svgUniverse = new SVGUniverse(); URI svgURI = svgFile.toURI(); SVGDiagram diagram = svgUniverse.getDiagram(svgUniverse.loadSVG(svgURI)); // 获取SVG根元素,遍历所有子元素修改颜色 Element root = diagram.getRoot(); changeElementColor(root, Color.green); // 后续渲染逻辑不变 PdfBoxGraphics2D graphics = new PdfBoxGraphics2D(document, diagram.getWidth(), diagram.getHeight()); try { diagram.render(graphics); } catch (SVGException e) { e.printStackTrace(); } finally { graphics.dispose(); } // ... 剩余绘制XForm的代码不变
添加颜色修改的工具方法:
private static void changeElementColor(Element element, Color targetColor) { // 修改当前元素的填充和描边颜色 element.setAttributeNS(null, "fill", String.format("#%02x%02x%02x", targetColor.getRed(), targetColor.getGreen(), targetColor.getBlue())); element.setAttributeNS(null, "stroke", String.format("#%02x%02x%02x", targetColor.getRed(), targetColor.getGreen(), targetColor.getBlue())); // 递归处理子元素 NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node instanceof Element) { changeElementColor((Element) node, targetColor); } } }
方案2:自定义Graphics2D拦截颜色设置
继承PdfBoxGraphics2D,重写颜色相关的方法,强制使用目标颜色。
示例代码:
class ColorOverrideGraphics2D extends PdfBoxGraphics2D { private final Color targetColor; public ColorOverrideGraphics2D(PDDocument document, float width, float height, Color targetColor) { super(document, width, height); this.targetColor = targetColor; } @Override public void setColor(Color c) { // 忽略传入的颜色,强制使用目标颜色 super.setColor(targetColor); } @Override public void setPaint(Paint paint) { // 如果是Color类型,强制替换;否则保留原Paint(渐变/图案等) if (paint instanceof Color) { super.setPaint(targetColor); } else { super.setPaint(paint); } } @Override public void setStroke(Stroke s) { super.setStroke(s); // 同时设置描边颜色 super.setColor(targetColor); } }
使用这个自定义Graphics2D:
PdfBoxGraphics2D graphics = new ColorOverrideGraphics2D(document, 32, 32, Color.green); try { diagram.render(graphics); } catch (SVGException e) { e.printStackTrace(); } finally { graphics.dispose(); }
方案3:修改SVG文件内容
如果允许修改原始SVG文件,可以直接在SVG中把所有fill和stroke属性改成目标颜色,或者删除这些属性让渲染时继承全局颜色。
注意事项
- 方案1需要确保SVG的元素都使用了
fill/stroke属性,如果有使用CSS类定义颜色,需要额外处理CSS样式。 - 方案2对复杂SVG(如包含渐变、图案填充)可能不适用,因为这类填充不是简单的Color类型。
内容的提问来源于stack exchange,提问作者Christoph Daniel
相关产品推荐
相关产品推荐

