Mustang 1.6.0 with XMP access and Factur-X support

This commit is contained in:
Jochen Stärk
2019-02-03 14:39:33 +01:00
parent 6e1b1c09a9
commit 55ea730a02
4 changed files with 102 additions and 8 deletions

View File

@@ -1,8 +1,12 @@
1.5.6 1.6.0
===== =====
2019-02-03
closes #69. Closes #69.
Corrected EN 16931 XMP Schema Extension header Corrected EN 16931 XMP Schema Extension header
Can now read xmp metadata via ZUGFeRDImporter getXMP
Exporting valid EN16931 Factur-X when version/profile set to 2/EN16931 mode
1.5.5 1.5.5
===== =====

Binary file not shown.

View File

@@ -33,17 +33,11 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
@@ -79,6 +73,7 @@ public class ZUGFeRDImporter {
private boolean amountFound; private boolean amountFound;
private boolean extractAttempt = false; private boolean extractAttempt = false;
private boolean parsed = false; private boolean parsed = false;
private String xmpString = null; // XMP metadata
private static final Logger LOG = Logger.getLogger(ZUGFeRDImporter.class.getName()); private static final Logger LOG = Logger.getLogger(ZUGFeRDImporter.class.getName());
/** /**
@@ -99,6 +94,21 @@ public class ZUGFeRDImporter {
} }
} }
static String convertStreamToString(java.io.InputStream is) {
// source https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java referring to
// https://community.oracle.com/blogs/pat/2004/10/23/stupid-scanner-tricks
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
/***
* get xmp metadata of the PDF, null if not available
* @return string
*/
public String getXMP() {
return xmpString;
}
/** /**
* Extracts a ZUGFeRD invoice from a PDF document represented by an input * Extracts a ZUGFeRD invoice from a PDF document represented by an input
* stream. Errors are reported via exception handling. * stream. Errors are reported via exception handling.
@@ -112,6 +122,10 @@ public class ZUGFeRDImporter {
try (PDDocument doc = PDDocument.load(pdfStream)) { try (PDDocument doc = PDDocument.load(pdfStream)) {
// PDDocumentInformation info = doc.getDocumentInformation(); // PDDocumentInformation info = doc.getDocumentInformation();
PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog()); PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
//start
InputStream XMP=doc.getDocumentCatalog().getMetadata().exportXMPMetadata();
xmpString=convertStreamToString(XMP);
etn = names.getEmbeddedFiles(); etn = names.getEmbeddedFiles();
if (etn == null) { if (etn == null) {
return; return;
@@ -134,6 +148,7 @@ public class ZUGFeRDImporter {
} }
} }
private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException { private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException {
for (String filename : names.keySet()) { for (String filename : names.keySet()) {

View File

@@ -378,6 +378,81 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta
String amount = zi.getAmount(); String amount = zi.getAmount();
assertEquals("<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" +
" <x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" +
" <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" +
" <rdf:Description rdf:about=\"\" xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\">\n" +
" <xmpMM:InstanceID>uuid:6776DD95-DAF6-768E-DAE6-2697750D95C5</xmpMM:InstanceID>\n" +
" <xmpMM:DocumentID>uuid:47380404-5938-FE2C-8F0B-B505DCE43BF5</xmpMM:DocumentID>\n" +
" </rdf:Description>\n" +
" <rdf:Description rdf:about=\"\" xmlns:zf=\"urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#\">\n" +
" <zf:ConformanceLevel>BASIC</zf:ConformanceLevel>\n" +
" <zf:DocumentFileName>ZUGFeRD-invoice.xml</zf:DocumentFileName>\n" +
" <zf:DocumentType>INVOICE</zf:DocumentType>\n" +
" <zf:Version>1.0</zf:Version>\n" +
" </rdf:Description>\n" +
" <rdf:Description rdf:about=\"\" xmlns:pdfaExtension=\"http://www.aiim.org/pdfa/ns/extension/\" xmlns:pdfaSchema=\"http://www.aiim.org/pdfa/ns/schema#\" xmlns:pdfaProperty=\"http://www.aiim.org/pdfa/ns/property#\">\n" +
" <pdfaExtension:schemas>\n" +
" <rdf:Bag>\n" +
" <rdf:li rdf:parseType=\"Resource\">\n" +
" <pdfaSchema:schema>ZUGFeRD PDFA Extension Schema</pdfaSchema:schema>\n" +
" <pdfaSchema:namespaceURI>urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>\n" +
" <pdfaSchema:prefix>zf</pdfaSchema:prefix>\n" +
" <pdfaSchema:property>\n" +
" <rdf:Seq>\n" +
" <rdf:li rdf:parseType=\"Resource\">\n" +
" <pdfaProperty:name>DocumentFileName</pdfaProperty:name>\n" +
" <pdfaProperty:valueType>Text</pdfaProperty:valueType>\n" +
" <pdfaProperty:category>external</pdfaProperty:category>\n" +
" <pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>\n" +
" </rdf:li>\n" +
" <rdf:li rdf:parseType=\"Resource\">\n" +
" <pdfaProperty:name>DocumentType</pdfaProperty:name>\n" +
" <pdfaProperty:valueType>Text</pdfaProperty:valueType>\n" +
" <pdfaProperty:category>external</pdfaProperty:category>\n" +
" <pdfaProperty:description>INVOICE</pdfaProperty:description>\n" +
" </rdf:li>\n" +
" <rdf:li rdf:parseType=\"Resource\">\n" +
" <pdfaProperty:name>Version</pdfaProperty:name>\n" +
" <pdfaProperty:valueType>Text</pdfaProperty:valueType>\n" +
" <pdfaProperty:category>external</pdfaProperty:category>\n" +
" <pdfaProperty:description>The actual version of the ZUGFeRD XML schema</pdfaProperty:description>\n" +
" </rdf:li>\n" +
" <rdf:li rdf:parseType=\"Resource\">\n" +
" <pdfaProperty:name>ConformanceLevel</pdfaProperty:name>\n" +
" <pdfaProperty:valueType>Text</pdfaProperty:valueType>\n" +
" <pdfaProperty:category>external</pdfaProperty:category>\n" +
" <pdfaProperty:description>The conformance level of the embedded ZUGFeRD data</pdfaProperty:description>\n" +
" </rdf:li>\n" +
" </rdf:Seq>\n" +
" </pdfaSchema:property>\n" +
" </rdf:li>\n" +
" </rdf:Bag>\n" +
" </pdfaExtension:schemas>\n" +
" </rdf:Description>\n" +
" <rdf:Description rdf:about=\"\" xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" +
" <pdfaid:part>3</pdfaid:part>\n" +
" <pdfaid:conformance>B</pdfaid:conformance>\n" +
" </rdf:Description>\n" +
" <rdf:Description rdf:about=\"\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" +
" <xmp:CreateDate>2014-07-11T13:39:46+02:00</xmp:CreateDate>\n" +
" <xmp:ModifyDate>2014-07-11T13:39:46+02:00</xmp:ModifyDate>\n" +
" <xmp:CreatorTool>zugferd_invoice.java</xmp:CreatorTool>\n" +
" <xmp:MetadataDate>2014-07-11T13:39:46+02:00</xmp:MetadataDate>\n" +
" </rdf:Description>\n" +
" <rdf:Description rdf:about=\"\" xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" +
" <pdf:Producer>PDFlib Personalization Server 9.0.3 (JDK 1.6/Mac OS X-10.6 64)</pdf:Producer>\n" +
" </rdf:Description>\n" +
" <rdf:Description rdf:about=\"\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" +
" <dc:title>\n" +
" <rdf:Alt>\n" +
" <rdf:li xml:lang=\"x-default\">ZUGFeRD Rechnung $Revision: 1.13 $</rdf:li>\n" +
" </rdf:Alt>\n" +
" </dc:title>\n" +
" </rdf:Description>\n" +
" </rdf:RDF>\n" +
" </x:xmpmeta>\n" +
"<?xpacket end=\"r\"?>", zi.getXMP());
// this resembles the data written in MustangReaderWriterCustomXMLTest // this resembles the data written in MustangReaderWriterCustomXMLTest
assertEquals(amount, "1005.55"); assertEquals(amount, "1005.55");