如何将 SVG 图像添加到使用 HTML 和 Flying Saucer 库(和 Batik)构建的 PDF 中?
How to add SVG image to PDF built with HTML and Flying Saucer library (and Batik)?
我正在使用飞碟库(旧但开源)使用 XHTML 生成 PDF。我可以正常工作,但我还想添加 SVG 图像。我已经开始着手整合 batik 以尝试让它发挥作用,但我 运行 遇到了问题。不绘制 SVG 图像。 XHTML 仍然呈现,但它似乎没有显示 SVG。我已经让 SVG 在单独的 PDF 上呈现,但从未与飞碟结果一起呈现。
我添加了常用的 ReplacedElementFactory(它也适用于常规图像,但没有包含该代码)。唯一相关的方法(确实会被调用和一切)如下:
@Override
public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox, UserAgentCallback userAgentCallback, int cssWidth, int cssHeight) {
Element element = blockBox.getElement();
if (element == null) {
return null;
}
String nodeName = element.getNodeName();
if ("img".equals(nodeName)) {
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
SVGDocument svgImage = null;
try {
svgImage = factory.createSVGDocument(new File("logo.svg").toURL()
.toString());
} catch (IOException e) {
e.printStackTrace();
}
Element svgElement = svgImage.getDocumentElement();
Document htmlDoc = element.getOwnerDocument();
Node importedNode = htmlDoc.importNode(svgElement, true);
element.appendChild(importedNode);
return new SVGReplacedElement(svgImage, cssWidth, cssHeight);
}
return this.superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
}
之后我尝试用它来绘画:
import java.awt.Graphics2D;
import java.awt.Point;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.GraphicsNode;
import org.w3c.dom.svg.SVGDocument;
import org.xhtmlrenderer.css.style.CalculatedStyle;
import org.xhtmlrenderer.layout.LayoutContext;
import org.xhtmlrenderer.pdf.ITextOutputDevice;
import org.xhtmlrenderer.pdf.ITextReplacedElement;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.render.PageBox;
import org.xhtmlrenderer.render.RenderingContext;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;
public class SVGReplacedElement implements ITextReplacedElement {
private Point location = new Point(0, 0);
private SVGDocument svg;
private int cssWidth;
private int cssHeight;
public SVGReplacedElement(SVGDocument importedNode, int cssWidth, int cssHeight) {
this.cssWidth = cssWidth;
this.cssHeight = cssHeight;
this.svg = importedNode;
}
@Override
Methods....
@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice,
BlockBox blockBox) {
UserAgent userAgent = new UserAgentAdapter();
DocumentLoader loader = new DocumentLoader(userAgent);
BridgeContext ctx = new BridgeContext(userAgent, loader);
ctx.setDynamicState(BridgeContext.DYNAMIC);
GVTBuilder builder = new GVTBuilder();
blockBox.paintDebugOutline(renderingContext);
PdfContentByte cb = outputDevice.getWriter().getDirectContent();
float width = cssWidth / outputDevice.getDotsPerPoint();
float height = cssHeight / outputDevice.getDotsPerPoint();
PdfTemplate map = cb.createTemplate(width, height);
Graphics2D g2d = map.createGraphics(width, height);
GraphicsNode mapGraphics = builder.build(ctx, svg);
mapGraphics.paint(g2d);
g2d.dispose();
PageBox page = renderingContext.getPage();
float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
renderingContext, CalculatedStyle.BOTTOM);
cb.addTemplate(map, x, y);
}
}
有趣的是,blockBox.paintDebugOutline(renderingContext);
确实勾勒出了图像应在的位置。 Eclipse 调试还显示正确的文件已连接到 IMG 元素。
CSS 如下所示:
.header {
position: absolute;
display: inline-block;
right: 0;
top: 0;
width: 150px;
height: 54px;
}
我也试过 display:block;
。我试过的 xhtml 示例:
<img class='header' src='icon.svg' alt='Logo'/>
<svg class='header' type='image/svg+xml' data='icon.svg' />
<object class='header' type='image/svg+xml' data='icon.svg' />
非常感谢您的关注和反馈(以及可能的回答)
编辑:最初问题略有不同,但我已经解决了。无法将 SVGImage 附加到实际文档。现在它只是不画。如指南中所述,我已将 CSS 添加到 display:block 等。
编辑:更简洁的代码
编辑:添加了更多我尝试过的内容
我不知道为什么,但是替换 SVGReplacedElement.paint(...) 中的代码修复了它。
新代码:
@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice,
BlockBox blockBox) {
PdfContentByte cb = outputDevice.getWriter().getDirectContent();
float width = (float) (cssWidth / outputDevice.getDotsPerPoint());
float height = (float) (cssHeight / outputDevice.getDotsPerPoint());
PdfTemplate template = cb.createTemplate(width, height);
Graphics2D g2d = template.createGraphics(width, height);
PrintTranscoder prm = new PrintTranscoder();
TranscoderInput ti = new TranscoderInput(svg);
prm.transcode(ti, null);
PageFormat pg = new PageFormat();
Paper pp = new Paper();
pp.setSize(width, height);
pp.setImageableArea(0, 0, width, height);
pg.setPaper(pp);
prm.print(g2d, pg, 0);
g2d.dispose();
PageBox page = renderingContext.getPage();
float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
renderingContext, CalculatedStyle.BOTTOM);
x /= outputDevice.getDotsPerPoint();
y /= outputDevice.getDotsPerPoint();
cb.addTemplate(template, x, y);
}
从 tutorial.
获得
可能与我创建的未链接到原始文档的新 UserAgent 和 DocumentLoader 等有关。无论如何,它现在可以工作了。希望它将来对某人有所帮助。如果人们想评论或添加另一个答案,说明为什么现在可以使用,这可能有助于其他人稍后阅读。
我正在使用飞碟库(旧但开源)使用 XHTML 生成 PDF。我可以正常工作,但我还想添加 SVG 图像。我已经开始着手整合 batik 以尝试让它发挥作用,但我 运行 遇到了问题。不绘制 SVG 图像。 XHTML 仍然呈现,但它似乎没有显示 SVG。我已经让 SVG 在单独的 PDF 上呈现,但从未与飞碟结果一起呈现。 我添加了常用的 ReplacedElementFactory(它也适用于常规图像,但没有包含该代码)。唯一相关的方法(确实会被调用和一切)如下:
@Override
public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox, UserAgentCallback userAgentCallback, int cssWidth, int cssHeight) {
Element element = blockBox.getElement();
if (element == null) {
return null;
}
String nodeName = element.getNodeName();
if ("img".equals(nodeName)) {
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
SVGDocument svgImage = null;
try {
svgImage = factory.createSVGDocument(new File("logo.svg").toURL()
.toString());
} catch (IOException e) {
e.printStackTrace();
}
Element svgElement = svgImage.getDocumentElement();
Document htmlDoc = element.getOwnerDocument();
Node importedNode = htmlDoc.importNode(svgElement, true);
element.appendChild(importedNode);
return new SVGReplacedElement(svgImage, cssWidth, cssHeight);
}
return this.superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
}
之后我尝试用它来绘画:
import java.awt.Graphics2D;
import java.awt.Point;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.GraphicsNode;
import org.w3c.dom.svg.SVGDocument;
import org.xhtmlrenderer.css.style.CalculatedStyle;
import org.xhtmlrenderer.layout.LayoutContext;
import org.xhtmlrenderer.pdf.ITextOutputDevice;
import org.xhtmlrenderer.pdf.ITextReplacedElement;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.render.PageBox;
import org.xhtmlrenderer.render.RenderingContext;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;
public class SVGReplacedElement implements ITextReplacedElement {
private Point location = new Point(0, 0);
private SVGDocument svg;
private int cssWidth;
private int cssHeight;
public SVGReplacedElement(SVGDocument importedNode, int cssWidth, int cssHeight) {
this.cssWidth = cssWidth;
this.cssHeight = cssHeight;
this.svg = importedNode;
}
@Override
Methods....
@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice,
BlockBox blockBox) {
UserAgent userAgent = new UserAgentAdapter();
DocumentLoader loader = new DocumentLoader(userAgent);
BridgeContext ctx = new BridgeContext(userAgent, loader);
ctx.setDynamicState(BridgeContext.DYNAMIC);
GVTBuilder builder = new GVTBuilder();
blockBox.paintDebugOutline(renderingContext);
PdfContentByte cb = outputDevice.getWriter().getDirectContent();
float width = cssWidth / outputDevice.getDotsPerPoint();
float height = cssHeight / outputDevice.getDotsPerPoint();
PdfTemplate map = cb.createTemplate(width, height);
Graphics2D g2d = map.createGraphics(width, height);
GraphicsNode mapGraphics = builder.build(ctx, svg);
mapGraphics.paint(g2d);
g2d.dispose();
PageBox page = renderingContext.getPage();
float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
renderingContext, CalculatedStyle.BOTTOM);
cb.addTemplate(map, x, y);
}
}
有趣的是,blockBox.paintDebugOutline(renderingContext);
确实勾勒出了图像应在的位置。 Eclipse 调试还显示正确的文件已连接到 IMG 元素。
CSS 如下所示:
.header {
position: absolute;
display: inline-block;
right: 0;
top: 0;
width: 150px;
height: 54px;
}
我也试过 display:block;
。我试过的 xhtml 示例:
<img class='header' src='icon.svg' alt='Logo'/>
<svg class='header' type='image/svg+xml' data='icon.svg' />
<object class='header' type='image/svg+xml' data='icon.svg' />
非常感谢您的关注和反馈(以及可能的回答)
编辑:最初问题略有不同,但我已经解决了。无法将 SVGImage 附加到实际文档。现在它只是不画。如指南中所述,我已将 CSS 添加到 display:block 等。
编辑:更简洁的代码
编辑:添加了更多我尝试过的内容
我不知道为什么,但是替换 SVGReplacedElement.paint(...) 中的代码修复了它。
新代码:
@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice,
BlockBox blockBox) {
PdfContentByte cb = outputDevice.getWriter().getDirectContent();
float width = (float) (cssWidth / outputDevice.getDotsPerPoint());
float height = (float) (cssHeight / outputDevice.getDotsPerPoint());
PdfTemplate template = cb.createTemplate(width, height);
Graphics2D g2d = template.createGraphics(width, height);
PrintTranscoder prm = new PrintTranscoder();
TranscoderInput ti = new TranscoderInput(svg);
prm.transcode(ti, null);
PageFormat pg = new PageFormat();
Paper pp = new Paper();
pp.setSize(width, height);
pp.setImageableArea(0, 0, width, height);
pg.setPaper(pp);
prm.print(g2d, pg, 0);
g2d.dispose();
PageBox page = renderingContext.getPage();
float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
renderingContext, CalculatedStyle.BOTTOM);
x /= outputDevice.getDotsPerPoint();
y /= outputDevice.getDotsPerPoint();
cb.addTemplate(template, x, y);
}
从 tutorial.
获得可能与我创建的未链接到原始文档的新 UserAgent 和 DocumentLoader 等有关。无论如何,它现在可以工作了。希望它将来对某人有所帮助。如果人们想评论或添加另一个答案,说明为什么现在可以使用,这可能有助于其他人稍后阅读。