XRechnung and OrderX improvements for upstream.
This commit is contained in:
@@ -44,6 +44,7 @@ import org.apache.xmpbox.type.BadFieldValueException;
|
||||
import org.apache.xmpbox.xml.DomXmpParser;
|
||||
import org.apache.xmpbox.xml.XmpParsingException;
|
||||
import org.apache.xmpbox.xml.XmpSerializer;
|
||||
import org.mustangproject.EStandard;
|
||||
import org.mustangproject.FileAttachment;
|
||||
|
||||
import javax.activation.DataSource;
|
||||
@@ -104,6 +105,12 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
@Deprecated
|
||||
protected String subject;
|
||||
|
||||
/**
|
||||
* OrderX document type. As of version 1.0 it may be
|
||||
* ORDER, ORDER_RESPONSE, or ORDER_CHANGE
|
||||
*/
|
||||
protected String orderXDocumentType = "ORDER";
|
||||
|
||||
|
||||
private HashMap<String, byte[]> additionalXMLs = new HashMap<String, byte[]>();
|
||||
|
||||
@@ -223,6 +230,24 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Embeds an external file (generic - any type allowed) in the PDF.
|
||||
* The embedding is done in the default PDF document.
|
||||
*
|
||||
* @param filename name of the file that will become attachment name in the PDF
|
||||
* @param relationship how the file relates to the content, e.g. "Alternative"
|
||||
* @param description Human-readable description of the file content
|
||||
* @param subType type of the data e.g. could be "text/xml" - mime like
|
||||
* @param data the binary data of the file/attachment
|
||||
* @throws java.io.IOException if anything is wrong with filename
|
||||
*/
|
||||
public void PDFAttachGenericFile(String filename, String relationship, String description,
|
||||
String subType, byte[] data) throws IOException {
|
||||
PDFAttachGenericFile(this.doc, filename, relationship, description, subType, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Embeds an external file (generic - any type allowed) in the PDF.
|
||||
*
|
||||
@@ -374,6 +399,20 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property orderXDocumentType.
|
||||
*
|
||||
* @param orderXDocumentType new value. Expected: ORDER, ORDER_RESPONSE, or ORDER_CHANGE
|
||||
*
|
||||
* @return this exporter
|
||||
*/
|
||||
public OXExporterFromA3 setOrderXDocumentType(String orderXDocumentType)
|
||||
{
|
||||
this.orderXDocumentType = orderXDocumentType;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected OXExporterFromA3 setAttachZUGFeRDHeaders(boolean attachHeaders) {
|
||||
this.attachZUGFeRDHeaders = attachHeaders;
|
||||
return this;
|
||||
@@ -393,11 +432,11 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, 1, true, xmlProvider.getProfile(),
|
||||
"urn:factur-x:pdfa:CrossIndustryDocument:1p0#", "fx",
|
||||
"order-x.xml");
|
||||
zf.setType("ORDER");
|
||||
zf.setType(this.orderXDocumentType);
|
||||
|
||||
metadata.addSchema(zf);
|
||||
// also add the schema extensions...
|
||||
XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(this, metadata, 1, attachZUGFeRDHeaders);
|
||||
XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(this, metadata, 1, attachZUGFeRDHeaders, EStandard.orderx);
|
||||
pdfaex.setZUGFeRDVersion(1);
|
||||
metadata.addSchema(pdfaex);
|
||||
}
|
||||
@@ -424,7 +463,7 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
xmlProvider.generateXML(trans);
|
||||
String filename = "order-x.xml";
|
||||
PDFAttachGenericFile(doc, filename, "Alternative",
|
||||
"Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
|
||||
"Order metadata conforming to the Order-X standard (https://www.ferd-net.de/standards/order-x/index.html)",
|
||||
"text/xml", xmlProvider.getXML());
|
||||
|
||||
for (FileAttachment attachment : fileAttachments) {
|
||||
@@ -438,14 +477,14 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
* Reads the XMPMetadata from the PDDocument, if it exists.
|
||||
* Otherwise creates XMPMetadata.
|
||||
*/
|
||||
protected XMPMetadata getXmpMetadata() {
|
||||
protected XMPMetadata getXmpMetadata() throws IOException {
|
||||
PDMetadata meta = doc.getDocumentCatalog().getMetadata();
|
||||
if (meta != null) {
|
||||
if ((meta != null) && (meta.getLength() > 0)) {
|
||||
try {
|
||||
DomXmpParser xmpParser = new DomXmpParser();
|
||||
return xmpParser.parse(meta.toByteArray());
|
||||
} catch (XmpParsingException | IOException e) {
|
||||
// TODO use logging or handle the error somehow
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
return XMPMetadata.createXMPMetadata();
|
||||
@@ -490,7 +529,7 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
// This should be impossible, because it would occur only if an illegal
|
||||
// conformance level is supplied,
|
||||
// however the enum enforces that the conformance level is valid.
|
||||
throw new Error(ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
pdfaid.setPart(3);
|
||||
@@ -579,7 +618,7 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
/**
|
||||
* Adds an OutputIntent and the sRGB color profile if no OutputIntent exist
|
||||
*/
|
||||
protected void addSRGBOutputIntend() {
|
||||
protected void addSRGBOutputIntend() throws IOException {
|
||||
if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -595,7 +634,7 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
|
||||
doc.getDocumentCatalog().addOutputIntent(intent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO use logging or handle the error somehow
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.apache.xmpbox.XMPMetadata;
|
||||
import org.apache.xmpbox.XmpConstants;
|
||||
import org.apache.xmpbox.schema.PDFAExtensionSchema;
|
||||
import org.apache.xmpbox.type.*;
|
||||
import org.mustangproject.EStandard;
|
||||
|
||||
/**
|
||||
* Additionally to adding a RDF namespace with a indication which file
|
||||
@@ -52,7 +53,7 @@ public class XMPSchemaPDFAExtensions extends PDFAExtensionSchema {
|
||||
public final String prefix_pdfaProperty = "pdfaProperty";
|
||||
public String namespace = null;
|
||||
public String prefix = null;
|
||||
|
||||
|
||||
protected IZUGFeRDExporter exporter;
|
||||
|
||||
|
||||
@@ -90,20 +91,21 @@ public class XMPSchemaPDFAExtensions extends PDFAExtensionSchema {
|
||||
}
|
||||
|
||||
public XMPSchemaPDFAExtensions(IZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion) {
|
||||
super(metadata);
|
||||
exporter=ze;
|
||||
setZUGFeRDVersion(ZFVersion);
|
||||
attachExtensions(metadata, true);
|
||||
this(ze, metadata, ZFVersion, true);
|
||||
}
|
||||
|
||||
public XMPSchemaPDFAExtensions(IZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion, boolean withZF) {
|
||||
this(ze, metadata, ZFVersion, withZF, null);
|
||||
}
|
||||
|
||||
public XMPSchemaPDFAExtensions(IZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion, boolean withZF, EStandard eStandard) {
|
||||
super(metadata);
|
||||
exporter=ze;
|
||||
setZUGFeRDVersion(ZFVersion);
|
||||
attachExtensions(metadata, withZF);
|
||||
attachExtensions(metadata, withZF, eStandard);
|
||||
}
|
||||
|
||||
public void attachExtensions(XMPMetadata metadata, boolean withZF) {
|
||||
public void attachExtensions(XMPMetadata metadata, boolean withZF, EStandard eStandard) {
|
||||
|
||||
addNamespace(xmlns_pdfaSchema, prefix_pdfaSchema);
|
||||
addNamespace(xmlns_pdfaProperty, prefix_pdfaProperty);
|
||||
@@ -130,11 +132,21 @@ public class XMPSchemaPDFAExtensions extends PDFAExtensionSchema {
|
||||
PDFASchemaType.PROPERTY, Cardinality.Seq);
|
||||
li.addProperty(newSeq);
|
||||
|
||||
addProperty(newSeq, "DocumentFileName", "Text", "external", "name of the embedded XML invoice file");
|
||||
addProperty(newSeq, "DocumentType", "Text", "external", "INVOICE");
|
||||
addProperty(newSeq, "Version", "Text", "external", "The actual version of the ZUGFeRD XML schema");
|
||||
addProperty(newSeq, "ConformanceLevel", "Text", "external",
|
||||
"The selected ZUGFeRD profile completeness");
|
||||
if ((eStandard != null) && (eStandard == EStandard.orderx))
|
||||
{
|
||||
addProperty(newSeq, "DocumentFileName", "Text", "external", "Name of the embedded XML Order (or related) file");
|
||||
addProperty(newSeq, "DocumentType", "Text", "external", "ORDER, ORDER_RESPONSE, or ORDER_CHANGE");
|
||||
addProperty(newSeq, "Version", "Text", "external", "The actual version of the Order-X XML schema");
|
||||
addProperty(newSeq, "ConformanceLevel", "Text", "external",
|
||||
"The selected Order-X profile completeness");
|
||||
} else
|
||||
{
|
||||
addProperty(newSeq, "DocumentFileName", "Text", "external", "name of the embedded XML invoice file");
|
||||
addProperty(newSeq, "DocumentType", "Text", "external", "INVOICE");
|
||||
addProperty(newSeq, "Version", "Text", "external", "The actual version of the ZUGFeRD XML schema");
|
||||
addProperty(newSeq, "ConformanceLevel", "Text", "external",
|
||||
"The selected ZUGFeRD profile completeness");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ public class XMPSchemaZugferd extends XMPSchema {
|
||||
|
||||
/***
|
||||
* This is what needs to be added to the RDF metadata - basically the name of the embedded Zugferd file
|
||||
*
|
||||
* @param metadata the xmp to be added to
|
||||
* @param zfVersion which ZF version to use (2 for FX)
|
||||
* @param isFacturX whether to export as Factur-X
|
||||
@@ -41,7 +42,26 @@ public class XMPSchemaZugferd extends XMPSchema {
|
||||
* @param prefix the xml namespace prefix for the XMP, zf for ZUGFeRD, fx for Factur-X
|
||||
* @param filename the filename of the invoice
|
||||
*/
|
||||
public XMPSchemaZugferd(XMPMetadata metadata, int zfVersion, boolean isFacturX, Profile conformanceLevel, String URN, String prefix, String filename) {
|
||||
public XMPSchemaZugferd(XMPMetadata metadata, int zfVersion, boolean isFacturX, Profile conformanceLevel,
|
||||
String URN, String prefix, String filename) {
|
||||
this(metadata, zfVersion, isFacturX, conformanceLevel, URN, prefix, filename, null);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* This is what needs to be added to the RDF metadata - basically the name of the embedded Zugferd file
|
||||
*
|
||||
* @param metadata the xmp to be added to
|
||||
* @param zfVersion which ZF version to use (2 for FX)
|
||||
* @param isFacturX whether to export as Factur-X
|
||||
* @param conformanceLevel e.g. conformanceLevel.EN16931
|
||||
* @param URN the xml URI for the XMP
|
||||
* @param prefix the xml namespace prefix for the XMP, zf for ZUGFeRD, fx for Factur-X
|
||||
* @param filename the filename of the invoice
|
||||
* @param version meta-data version to set. May be null, and then it is set automatically
|
||||
*/
|
||||
public XMPSchemaZugferd(XMPMetadata metadata, int zfVersion, boolean isFacturX, Profile conformanceLevel,
|
||||
String URN, String prefix, String filename, String version) {
|
||||
super(metadata, URN, prefix, "ZUGFeRD Schema");
|
||||
|
||||
setAboutAsSimple("");
|
||||
@@ -50,14 +70,17 @@ public class XMPSchemaZugferd extends XMPSchema {
|
||||
setTextPropertyValue("ConformanceLevel", conformanceLevelValue);
|
||||
setTextPropertyValue("DocumentType", "INVOICE");
|
||||
setTextPropertyValue("DocumentFileName", filename);
|
||||
String version="1.0";
|
||||
if ((zfVersion==2)&&(!isFacturX)) {
|
||||
version="2p0";
|
||||
|
||||
if (version == null) {
|
||||
version = "1.0";
|
||||
if ((zfVersion==2)&&(!isFacturX)) {
|
||||
version="2p0";
|
||||
}
|
||||
}
|
||||
setTextPropertyValue("Version", version);
|
||||
}
|
||||
|
||||
/***
|
||||
/***
|
||||
* Specify a custom XMP metadata document type
|
||||
* @param type INVOICE or ORDER
|
||||
*/
|
||||
|
||||
@@ -115,6 +115,11 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
|
||||
protected int ZFVersion = DefaultZUGFeRDVersion;
|
||||
private boolean attachZUGFeRDHeaders = true;
|
||||
|
||||
|
||||
// Specific metaData version in case of XRechnung. We need it to be settable
|
||||
// by the caller if necessary.
|
||||
protected String XRechnungVersion = null; // Default XRechnung as of late 2021 is 2p0
|
||||
|
||||
/**
|
||||
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
|
||||
@@ -220,6 +225,20 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
isFacturX = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a specific XRechnung version from outside. This version needs to be present in the
|
||||
* meta-data as well. The caller may wish to generate a specific version of XRechnung
|
||||
* depending on the time period etc.
|
||||
*
|
||||
* @param XRechnungVersion the XRechnung version
|
||||
*/
|
||||
public void setXRechnungSpecificVersion(String XRechnungVersion)
|
||||
{
|
||||
this.XRechnungVersion = XRechnungVersion;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Generate ZF2.0/2.1 files with filename zugferd-invoice.xml instead of factur-x.xml
|
||||
@@ -301,6 +320,24 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Embeds an external file (generic - any type allowed) in the PDF.
|
||||
* The embedding is done in the default PDF document.
|
||||
*
|
||||
* @param filename name of the file that will become attachment name in the PDF
|
||||
* @param relationship how the file relates to the content, e.g. "Alternative"
|
||||
* @param description Human-readable description of the file content
|
||||
* @param subType type of the data e.g. could be "text/xml" - mime like
|
||||
* @param data the binary data of the file/attachment
|
||||
* @throws java.io.IOException if anything is wrong with filename
|
||||
*/
|
||||
public void PDFAttachGenericFile(String filename, String relationship, String description,
|
||||
String subType, byte[] data) throws IOException {
|
||||
PDFAttachGenericFile(this.doc, filename, relationship, description, subType, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Embeds an external file (generic - any type allowed) in the PDF.
|
||||
*
|
||||
@@ -467,10 +504,19 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
*/
|
||||
protected void addXMP(XMPMetadata metadata) {
|
||||
|
||||
String metaDataVersion = null; // default will be used
|
||||
|
||||
// The XRechnung version may be settable from outside.
|
||||
if ((this.XRechnungVersion != null) && (this.profile != null) &&
|
||||
this.profile.getName().equalsIgnoreCase(Profiles.getByName("XRECHNUNG").getName()))
|
||||
{
|
||||
metaDataVersion = this.XRechnungVersion;
|
||||
}
|
||||
|
||||
if (attachZUGFeRDHeaders) {
|
||||
XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, ZFVersion, isFacturX, xmlProvider.getProfile(),
|
||||
getNamespaceForVersion(ZFVersion), getPrefixForVersion(ZFVersion),
|
||||
getFilenameForVersion(ZFVersion, xmlProvider.getProfile()));
|
||||
getFilenameForVersion(ZFVersion, xmlProvider.getProfile()), metaDataVersion);
|
||||
|
||||
metadata.addSchema(zf);
|
||||
}
|
||||
@@ -480,8 +526,9 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
metadata.addSchema(pdfaex);
|
||||
}
|
||||
|
||||
protected void removeCidSet(PDDocumentCatalog catalog, PDDocument doc) {
|
||||
|
||||
private void removeCidSet(PDDocumentCatalog catalog, PDDocument doc)
|
||||
throws IOException
|
||||
{
|
||||
// https://github.com/ZUGFeRD/mustangproject/issues/249
|
||||
|
||||
COSName cidSet = COSName.getPDFName("CIDSet");
|
||||
@@ -507,7 +554,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
// do stuff with the font
|
||||
}
|
||||
@@ -567,7 +614,21 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
prepareDocument();
|
||||
xmlProvider.generateXML(trans);
|
||||
String filename = getFilenameForVersion(ZFVersion, xmlProvider.getProfile());
|
||||
PDFAttachGenericFile(doc, filename, "Alternative",
|
||||
|
||||
String relationship = "Alternative";
|
||||
// ZUGFeRD 2.1.1 Technical Supplement | Part A | 2.2.2. Data Relationship
|
||||
// See documentation ZUGFeRD211_EN/Documentation/ZUGFeRD-2.1.1 - Specification_TA_Part-A.pdf
|
||||
// https://www.ferd-net.de/standards/zugferd-2.1.1/index.html
|
||||
if (ZFVersion >= 2)
|
||||
{
|
||||
if (this.profile.getName().equalsIgnoreCase(Profiles.getByName("MINIMUM").getName()) ||
|
||||
this.profile.getName().equalsIgnoreCase(Profiles.getByName("BASICWL").getName()))
|
||||
{
|
||||
relationship = "Data";
|
||||
}
|
||||
}
|
||||
|
||||
PDFAttachGenericFile(doc, filename, relationship,
|
||||
"Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
|
||||
"text/xml", xmlProvider.getXML());
|
||||
|
||||
@@ -582,14 +643,16 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
* Reads the XMPMetadata from the PDDocument, if it exists.
|
||||
* Otherwise creates XMPMetadata.
|
||||
*/
|
||||
protected XMPMetadata getXmpMetadata() {
|
||||
protected XMPMetadata getXmpMetadata()
|
||||
throws IOException
|
||||
{
|
||||
PDMetadata meta = doc.getDocumentCatalog().getMetadata();
|
||||
if (meta != null) {
|
||||
if ((meta != null) && (meta.getLength() > 0)) {
|
||||
try {
|
||||
DomXmpParser xmpParser = new DomXmpParser();
|
||||
return xmpParser.parse(meta.toByteArray());
|
||||
} catch (XmpParsingException | IOException e) {
|
||||
// TODO use logging or handle the error somehow
|
||||
} catch (XmpParsingException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
return XMPMetadata.createXMPMetadata();
|
||||
@@ -723,7 +786,9 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
/**
|
||||
* Adds an OutputIntent and the sRGB color profile if no OutputIntent exist
|
||||
*/
|
||||
protected void addSRGBOutputIntend() {
|
||||
protected void addSRGBOutputIntend()
|
||||
throws IOException
|
||||
{
|
||||
if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -739,7 +804,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
||||
doc.getDocumentCatalog().addOutputIntent(intent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO use logging or handle the error somehow
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user