Merge pull request #261 from seeburger-ag/temp_branch_for_upstream_PRs_02_2022

Multiple changes to do with XRechnung and OrderX
This commit is contained in:
Jochen Staerk
2022-01-31 21:06:10 +01:00
committed by GitHub
4 changed files with 176 additions and 37 deletions

View File

@@ -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;
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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
*/

View File

@@ -116,6 +116,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
* metadata level, this will not e.g. convert graphics to JPG-2000)
@@ -221,6 +226,20 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
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 ((this.profile != null) && (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;
}
}