From c3bd01a442c7daf2fb4cc0ef981eda1d20a1927e Mon Sep 17 00:00:00 2001 From: IntlUser Date: Sat, 9 May 2020 00:44:55 +0300 Subject: [PATCH 1/2] SVG processing added --- pom.xml | 2 +- src/main/java/com/metanorma/fop/Util.java | 20 ++++++ src/main/java/com/metanorma/fop/mn2pdf.java | 71 +++++++++++++++++++-- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 37692a70..7df23694 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.metanorma.fop mn2pdf - 1.13 + 1.14 Metanorma XML to PDF converter jar https://www.metanorma.com diff --git a/src/main/java/com/metanorma/fop/Util.java b/src/main/java/com/metanorma/fop/Util.java index ae0271a7..23d95eb0 100644 --- a/src/main/java/com/metanorma/fop/Util.java +++ b/src/main/java/com/metanorma/fop/Util.java @@ -1,9 +1,11 @@ package com.metanorma.fop; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -11,11 +13,18 @@ import java.nio.channels.ReadableByteChannel; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Base64; import java.util.Enumeration; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; /** * @@ -124,4 +133,15 @@ public static String getAppVersion() { return version; } + + public static String getDecodedBase64SVGnode(String encodedString) { //throws SAXException, IOException, ParserConfigurationException { + byte[] decodedBytes = Base64.getDecoder().decode(encodedString); + String decodedString = new String(decodedBytes); + return decodedString; + /*if (decodedString.startsWith("") + 2); + } else { + return decodedString; + }*/ + } } diff --git a/src/main/java/com/metanorma/fop/mn2pdf.java b/src/main/java/com/metanorma/fop/mn2pdf.java index 5e7e3ff1..643fe475 100644 --- a/src/main/java/com/metanorma/fop/mn2pdf.java +++ b/src/main/java/com/metanorma/fop/mn2pdf.java @@ -3,16 +3,22 @@ import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; -import java.io.StringReader; import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.CodeSource; import java.text.MessageFormat; +import java.util.UUID; +import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.ErrorListener; @@ -22,6 +28,7 @@ import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; @@ -42,6 +49,9 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -130,15 +140,17 @@ public class mn2pdf { * @throws IOException In case of an I/O problem * @throws FOPException, SAXException In case of a FOP problem */ - public void convertmn2pdf(String fontPath, File xml, File xsl, File pdf) throws IOException, FOPException, SAXException, TransformerException, TransformerConfigurationException, TransformerConfigurationException { + public void convertmn2pdf(String fontPath, File xml, File xsl, File pdf) throws IOException, FOPException, SAXException, TransformerException, TransformerConfigurationException, TransformerConfigurationException, ParserConfigurationException { + File srcxml = ImageUpdate(xml); + try { //Setup XSLT TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource(xsl)); //Setup input for XSLT transformation - Source src = new StreamSource(xml); + Source src = new StreamSource(srcxml); //DEBUG: write intermediate FO to file if (DEBUG) { @@ -347,4 +359,55 @@ private static String getJaxpImplementationInfo(String componentName, Class comp componentClass.getName(), source == null ? "Java Runtime" : source.getLocation()); } + + private File ImageUpdate(File xml) throws IOException, SAXException, ParserConfigurationException, TransformerException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + InputStream xmlstream = new FileInputStream(xml); + Document sourceXML = dBuilder.parse(xmlstream); + NodeList images = sourceXML.getElementsByTagName("image"); + String strTmpPath = System.getProperty("java.io.tmpdir"); + String uuid = UUID.randomUUID().toString(); + Path tmpfilepath = Paths.get(strTmpPath, xml.getName(), uuid.toString()); + boolean changed = false; + for (int i = 0; i < images.getLength(); i++) { + Node image = images.item(i); + Node mimetype = image.getAttributes().getNamedItem("mimetype"); + if (mimetype != null && mimetype.getTextContent().equals("image/svg+xml")) { + // decode base64 svg into external tmp file + Node src = image.getAttributes().getNamedItem("src"); + if (src != null && src.getTextContent().startsWith("data:image")) { + String base64svg = src.getTextContent().substring(src.getTextContent().indexOf("base64,")+7); + String xmlsvg = Util.getDecodedBase64SVGnode(base64svg); + Files.createDirectories(tmpfilepath); + Path svgpath = Paths.get(tmpfilepath.toString(), "" + i + ".svg"); + try (BufferedWriter bw = Files.newBufferedWriter(svgpath)) + { + bw.write(xmlsvg); + } + src.setNodeValue(svgpath.toFile().toURI().toURL().toString()); + changed = true; + } + } + } + if (changed) { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer; + transformer = tf.newTransformer(); + StringWriter writer = new StringWriter(); + + //transform document to string + transformer.transform(new DOMSource(sourceXML), new StreamResult(writer)); + String xmlString = writer.getBuffer().toString(); + Path updatedxmlpath = Paths.get(tmpfilepath.toString(), "doc.xml"); + try (BufferedWriter bw = Files.newBufferedWriter(updatedxmlpath)) + { + bw.write(xmlString); + } + return updatedxmlpath.toFile(); + } else { + return xml; + } + + } } From 2fbc9a7ef6dd5da0689329b8f8257ee10ad149e1 Mon Sep 17 00:00:00 2001 From: IntlUser Date: Sat, 9 May 2020 14:41:53 +0300 Subject: [PATCH 2/2] SVG processing updated --- src/main/java/com/metanorma/fop/Util.java | 8 -- src/main/java/com/metanorma/fop/mn2pdf.java | 139 +++++++++++++------- 2 files changed, 90 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/metanorma/fop/Util.java b/src/main/java/com/metanorma/fop/Util.java index 23d95eb0..f808cae5 100644 --- a/src/main/java/com/metanorma/fop/Util.java +++ b/src/main/java/com/metanorma/fop/Util.java @@ -1,11 +1,9 @@ package com.metanorma.fop; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -19,12 +17,6 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.SAXException; /** * diff --git a/src/main/java/com/metanorma/fop/mn2pdf.java b/src/main/java/com/metanorma/fop/mn2pdf.java index 643fe475..3b7e0967 100644 --- a/src/main/java/com/metanorma/fop/mn2pdf.java +++ b/src/main/java/com/metanorma/fop/mn2pdf.java @@ -15,6 +15,9 @@ import java.nio.file.Paths; import java.security.CodeSource; import java.text.MessageFormat; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -49,7 +52,9 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.w3c.dom.Attr; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -130,6 +135,10 @@ public class mn2pdf { static final int ERROR_EXIT_CODE = -1; + static final String TMPDIR = System.getProperty("java.io.tmpdir"); + + final Path tmpfilepath = Paths.get(TMPDIR, UUID.randomUUID().toString()); + /** * Converts an XML file to a PDF file using FOP * @@ -142,15 +151,19 @@ public class mn2pdf { */ public void convertmn2pdf(String fontPath, File xml, File xsl, File pdf) throws IOException, FOPException, SAXException, TransformerException, TransformerConfigurationException, TransformerConfigurationException, ParserConfigurationException { - File srcxml = ImageUpdate(xml); + + String imagesxml = getImageFilePath(xml); + try { //Setup XSLT TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource(xsl)); + transformer.setParameter("svg_images", imagesxml); + //Setup input for XSLT transformation - Source src = new StreamSource(srcxml); + Source src = new StreamSource(xml); //DEBUG: write intermediate FO to file if (DEBUG) { @@ -186,11 +199,22 @@ public void convertmn2pdf(String fontPath, File xml, File xsl, File pdf) throws fontcfg.setPDFUAmode("DISABLED"); runFOP(fontcfg, src, pdf, transformer); } - } catch (Exception e) { e.printStackTrace(System.err); System.exit(ERROR_EXIT_CODE); } + // flush temporary folder + if (!DEBUG) { + //Files.deleteIfExists(tmpfilepath); + try { + Files.walk(tmpfilepath) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } catch (Exception ex) { + ex.printStackTrace(); + } + } } private void runFOP (fontConfig fontcfg, Source src, File pdf, Transformer transformer) throws IOException, FOPException, SAXException, TransformerException, TransformerConfigurationException, TransformerConfigurationException { @@ -249,7 +273,7 @@ public void error(TransformerException exc) public void fatalError(TransformerException exc) throws TransformerException { String excstr=exc.toString(); - if (excstr.contains("PDFConformanceException") && excstr.contains("all fonts, even the base 14 fonts, have to be embedded") && !PDFUA_error) { + if (excstr.contains("PDFConformanceException") && excstr.contains("PDF/UA-1") && !PDFUA_error) { // excstr.contains("all fonts, even the base 14 fonts, have to be embedded") System.err.println(exc.toString()); PDFUA_error = true; } else { @@ -360,54 +384,71 @@ private static String getJaxpImplementationInfo(String componentName, Class comp source == null ? "Java Runtime" : source.getLocation()); } - private File ImageUpdate(File xml) throws IOException, SAXException, ParserConfigurationException, TransformerException { - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - InputStream xmlstream = new FileInputStream(xml); - Document sourceXML = dBuilder.parse(xmlstream); - NodeList images = sourceXML.getElementsByTagName("image"); - String strTmpPath = System.getProperty("java.io.tmpdir"); - String uuid = UUID.randomUUID().toString(); - Path tmpfilepath = Paths.get(strTmpPath, xml.getName(), uuid.toString()); - boolean changed = false; - for (int i = 0; i < images.getLength(); i++) { - Node image = images.item(i); - Node mimetype = image.getAttributes().getNamedItem("mimetype"); - if (mimetype != null && mimetype.getTextContent().equals("image/svg+xml")) { - // decode base64 svg into external tmp file - Node src = image.getAttributes().getNamedItem("src"); - if (src != null && src.getTextContent().startsWith("data:image")) { - String base64svg = src.getTextContent().substring(src.getTextContent().indexOf("base64,")+7); - String xmlsvg = Util.getDecodedBase64SVGnode(base64svg); - Files.createDirectories(tmpfilepath); - Path svgpath = Paths.get(tmpfilepath.toString(), "" + i + ".svg"); - try (BufferedWriter bw = Files.newBufferedWriter(svgpath)) - { - bw.write(xmlsvg); + private String getImageFilePath(File xml) { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + InputStream xmlstream = new FileInputStream(xml); + Document sourceXML = dBuilder.parse(xmlstream); + NodeList images = sourceXML.getElementsByTagName("image"); + + HashMap svgmap = new HashMap<>(); + for (int i = 0; i < images.getLength(); i++) { + Node image = images.item(i); + Node mimetype = image.getAttributes().getNamedItem("mimetype"); + if (mimetype != null && mimetype.getTextContent().equals("image/svg+xml")) { + // decode base64 svg into external tmp file + Node svg_src = image.getAttributes().getNamedItem("src"); + Node svg_id = image.getAttributes().getNamedItem("id"); + if (svg_src != null && svg_id != null && svg_src.getTextContent().startsWith("data:image")) { + String base64svg = svg_src.getTextContent().substring(svg_src.getTextContent().indexOf("base64,")+7); + String xmlsvg = Util.getDecodedBase64SVGnode(base64svg); + try { + Files.createDirectories(tmpfilepath); + String id = svg_id.getTextContent(); + Path svgpath = Paths.get(tmpfilepath.toString(), id + ".svg"); + try (BufferedWriter bw = Files.newBufferedWriter(svgpath)) { + bw.write(xmlsvg); + } + svgmap.put(id, svgpath.toFile().toURI().toURL().toString()); + } catch (IOException ex) { + System.err.println("Can't save svg file into a temporary directory " + tmpfilepath.toString()); + ex.printStackTrace();; + } } - src.setNodeValue(svgpath.toFile().toURI().toURL().toString()); - changed = true; } } - } - if (changed) { - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer; - transformer = tf.newTransformer(); - StringWriter writer = new StringWriter(); - - //transform document to string - transformer.transform(new DOMSource(sourceXML), new StreamResult(writer)); - String xmlString = writer.getBuffer().toString(); - Path updatedxmlpath = Paths.get(tmpfilepath.toString(), "doc.xml"); - try (BufferedWriter bw = Files.newBufferedWriter(updatedxmlpath)) - { - bw.write(xmlString); + if (!svgmap.isEmpty()) { + // crate map file for svg images + DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); + Document document = documentBuilder.newDocument(); + Element root = document.createElement("images"); + document.appendChild(root); + for (Map.Entry item : svgmap.entrySet()) { + Element image = document.createElement("image"); + root.appendChild(image); + Attr attr_id = document.createAttribute("id"); + attr_id.setValue(item.getKey()); + image.setAttributeNode(attr_id); + Attr attr_path = document.createAttribute("src"); + attr_path.setValue(item.getValue()); + image.setAttributeNode(attr_path); + } + // save xml 'images.xml' with svg links to temporary folder + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource domSource = new DOMSource(document); + Path outputPath = Paths.get(tmpfilepath.toString(), "images.xml"); + StreamResult streamResult = new StreamResult(outputPath.toFile()); + transformer.transform(domSource, streamResult); + + return outputPath.toString(); } - return updatedxmlpath.toFile(); - } else { - return xml; + } catch (Exception ex) { + System.err.println("Can't save images.xml into temporary folder"); + ex.printStackTrace(); } - - } + return ""; + } }