Convert SVG to PDF
Thanks to Adrian for showing how the Batik rasterizer API is supposed to be used. However, I needed a more lightweight solution--- I can't write to temporary files, and I want fewer dependencies. So, starting from the methods he pointed to, I found a way to access the lower-level code to do the conversion and nothing else.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import org.apache.batik.transcoder.Transcoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.fop.svg.PDFTranscoder;
public class Test {
public static void main(String[] argv) throws TranscoderException, FileNotFoundException {
Transcoder transcoder = new PDFTranscoder();
TranscoderInput transcoderInput = new TranscoderInput(new FileInputStream(new File("/tmp/test.svg")));
TranscoderOutput transcoderOutput = new TranscoderOutput(new FileOutputStream(new File("/tmp/test.pdf")));
transcoder.transcode(transcoderInput, transcoderOutput);
}
}
The compile-and-run commands are
javac -cp batik-rasterizer.jar -d build Test.java
java -cp build:batik-rasterizer.jar Test
The important point is that TranscoderInput
and TranscoderOutput
can work with any InputStream
and OutputStream
, not just file streams. Note that one of the constructors takes a org.w3c.dom.Document
, which means that you don't even need to serialize an SVG DOM into an SVG string, saving an additional step.
This version also doesn't write anything to stdout/stderr, unlike the high-level API.
For JPEG, PNG, or TIFF output, replace org.apache.fop.svg.PDFTranscoder
with org.apache.batik.transcoder.image.JPEGTranscoder
, PNGTranscoder
, or TIFFTranscoder
(note that these raster formats are in a different package).
(I'm not quite sure how Java finds the org.apache.batk.transcoder.*
and org.apache.fop.svg.PDFTranscoder
classes, since I don't see them in the batik-rasterizer.jar
.)
Edit:
Although the simple commandline-compilation works with the batik-rasterizer.jar
only, it's doing some sort of classloader magic to find all the necessary classes. In a more realistic case (building a project with Ant), you have to find the classes by hand. They can be found in batik-1.7.zip
from the Batik project and fop-1.1.zip
from the FOP project. From Batik, you need to compile with batik-transcoder.jar
and run with
batik-transcoder.jar
batik-anim.jar
batik-awt-util.jar
batik-bridge.jar
batik-css.jar
batik-dom.jar
batik-ext.jar
batik-gvt.jar
batik-parser.jar
batik-script.jar
batik-svg-dom.jar
batik-util.jar
batik-xml.jar
xml-apis-ext.jar
From FOP, you need to compile with fop.jar
and run with
fop.jar
avalon-framework-4.2.0.jar
xmlgraphics-commons-1.5.jar
How do I convert an SVG to PDF in C#?
I've already solved the problem.
I downloaded the svg with selenium and used iText7 to generate the PDF-file.
I used itext7, which I've installed as NuGet-packet.
using iText;
using iText.Svg.Converter;
and here the code:
FileStream svgPath = File.Open("input.svg", FileMode.Open);
FileStream pdfPath = File.Create("output.pdf");
SvgConverter.CreatePdf(svgPath, pdfPath);
To avoid errors, you should set the current location equal to the location of the svg file. (Directory.SetCurrentDirectory())
SVG to PDF. How to?
Here's the solution I ended up going with, which does rely on any libraries.
The WebKit engine can render an SVG, so you can load an SVG into a WebView:
webView.loadUrl(Uri.fromFile(svgFile).toString());
WebView also has the ability to print, so then you can continue with:
// Get a PrintManager instance
PrintManager printManager = (PrintManager) getActivity()
.getSystemService(Context.PRINT_SERVICE);
// Get a print adapter instance
PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter();
// Create a print job with name and adapter instance
String jobName = getString(R.string.app_name) + " Document";
PrintJob printJob = printManager.print(jobName, printAdapter,
new PrintAttributes.Builder().build());
Converting from svg into pdf preserving image metadata
In the new Inkscape version 1.01
, most part of the metadata is preserved when converting from .svg
to .pdf
, while in the previous version, 0.92.5
, it was lost.
Thanks to @Moini for giving a hint in the release notes of Inkscape.
For Linux:
Install the newest Inkscape version 1.01 using flatpak
Convert SVG to PDF with iText, SVG is not fully displayed in PDF
As @A.Alexander correctly mentioned, iText out of the box doesn't support relative parameters for the circle element.
However, you can register your own renderer for the circle tag (instead of CircleSvgNodeRenderer) that is able to deal with percents.
There is the parseAbsoluteLength method in AbstractSvgNodeRenderer which can be useful for that.
Custom renderer.
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.svg.SvgConstants;
import com.itextpdf.svg.renderers.ISvgNodeRenderer;
import com.itextpdf.svg.renderers.SvgDrawContext;
import com.itextpdf.svg.utils.DrawUtils;
/**
* {@link ISvgNodeRenderer} implementation for the <circle> tag.
*/
public class CustomCircleSvgNodeRenderer extends AbstractSvgNodeRenderer {
private float cx;
private float cy;
float r;
@Override
protected void doDraw(SvgDrawContext context) {
PdfCanvas cv = context.getCurrentCanvas();
cv.writeLiteral("% ellipse\n");
if (setParameters(context)) {
// Use double type locally to have better precision of the result after applying arithmetic operations
cv.moveTo((double) cx + (double) r, cy);
DrawUtils.arc((double) cx - (double) r, (double) cy - (double) r, (double) cx + (double) r,
(double) cy + (double) r, 0, 360, cv);
}
}
private boolean setParameters(SvgDrawContext context) {
cx = 0;
cy = 0;
if (getAttribute(SvgConstants.Attributes.CX) != null) {
cx = parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CX), context.getCurrentViewPort().getWidth(), 0.0f, context);
}
if (getAttribute(SvgConstants.Attributes.CY) != null) {
cy = parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY), context.getCurrentViewPort().getHeight(), 0.0f, context);
}
if (getAttribute(SvgConstants.Attributes.R) != null
&& parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY), context.getCurrentViewPort().getHeight(), 0.0f, context) > 0) {
r = parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY), context.getCurrentViewPort().getHeight(), 0.0f, context);
} else {
return false; //No drawing if rx is absent
}
return true;
}
@Override
public ISvgNodeRenderer createDeepCopy() {
CustomCircleSvgNodeRenderer copy = new CustomCircleSvgNodeRenderer();
deepCopyAttributesAndStyles(copy);
return copy;
}
}
Then you need to customize DefaultSvgNodeRendererFactory.
New Factory must create instance of CustomCircleSvgNodeRenderer every time SvgConverter renders a circle.
e.g
public class CustomRendererFactory extends DefaultSvgNodeRendererFactory {
@Override
public ISvgNodeRenderer createSvgNodeRendererForTag(IElementNode tag, ISvgNodeRenderer parent) {
if (SvgConstants.Tags.CIRCLE.equals(tag.name())) {
return new CustomCircleSvgNodeRenderer();
}
return super.createSvgNodeRendererForTag(tag, parent);
}
}
Enforce SVGConverter to use CustomRendererFactory
You should add this factory to ConverterProperties instance and then use methods from SvgConverter that have a parameter of type ISvgConverterProperties.
SvgConverterProperties properties = new SvgConverterProperties();
properties.setRendererFactory(new RendererFactory());
// xObject
SvgConverter.convertToXObject(svg, pdfDoc, properties);
// or pdf
SvgConverter.createPdf(svgStream, pdfStream, properties);
Strange output when converting SVG to PDF javascript mermaid.js example
I suspect there is some rendering bug in the print rendering module since the SVG call and Mermaid response appears perfect, plus native read and print SVG also looks good, and proper. So your initial code (with a small necessary @media line looks like this as a PDF.
Likewise the Evo sample is perfectly rendered using the same method but allowing in that case the media default of system paper e.g. 8.5x11(Letter)
"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --headless --run-all-compositor-stages-before-draw --print-to-pdf-no-header --print-to-pdf="C:\Users\WDAGUtilityAccount\desktop\svg-example.pdf" https://www.evopdf.com/DemoAppFiles/HTML_Files/SVG_Examples.html & timeout 5 & svg-example.pdf
To reduce the media size from Chrome's default I had to add in your <head>
<head>
<meta http-equiv="Content-Style-Type" content="text/css">
<style>@media print { @page { margin: 0; size: 125px 238px ; } body { margin: 0; } }</style>
</head>
and you will note due to a slight rounding error in the media maths the size needs to be slightly larger than the SVG viewBox="0 0 124.63749694824219 231.20001220703125" oddly on this occasion just round up width but 6 extra units in height !
Thus I suggest replace the final printout method to the more normal JS methods to use the browsers native print function, should be only a few lines more than my one line method.
How to programmatically convert SVG to PDF on Windows?
There are two options:
- Batik (Java, open source)
- Inkscape (native binary, open source)
Batik will give you more control. With Inkscape, you can use the command line parameters (--export-pdf IIRC) to do the conversion without firing up the UI.
Related Topics
Try with Resources Introduce Unreachable Bytecode
Uninitialized Object VS Object Initialized to Null
Best Language to Parse Extremely Large Excel 2007 Files
How to Achieve Transfer File Between Client and Server Using Java Socket
Filling Combobox from Database by Using Hibernate in Java
Gc Overhead of Optional<T> in Java
A Confusion About Java String Literal Pool and String's Concatenation
How to View Bytecode of Class File
How to Get Windows Username in Java
How to Use the Command Update-Alternatives --Config Java
Difference Between Paint() and Paintcomponent()
Parsing Nested JSON Data Using Gson
No Serializer Found for Class Org.Hibernate.Proxy.Pojo.Bytebuddy.Bytebuddyinterceptor
Absolute Minimum Code to Get a Valid Oauth_Signature Populated in Java or Groovy
Auto Adjust the Height of Rows in a Jtable
Prevent Swing Gui Locking Up During a Background Task