diff --git a/mustang/pom.xml b/mustang/pom.xml
new file mode 100644
index 00000000..404aee03
--- /dev/null
+++ b/mustang/pom.xml
@@ -0,0 +1,33 @@
+
+ 4.0.0
+
+ org.mustangproject.ZUGFeRD
+ mustang
+ 1.0
+ jar
+
+ mustang
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+
+ org.apache.pdfbox
+ pdfbox
+ 2.0.0-SNAPSHOT
+ compile
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/App.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/App.java
new file mode 100644
index 00000000..2deff0ca
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/App.java
@@ -0,0 +1,13 @@
+package org.mustangproject.ZUGFeRD;
+
+/**
+ * Hello world!
+ *
+ */
+public class App
+{
+ public static void main( String[] args )
+ {
+ System.out.println( "Hello World!" );
+ }
+}
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java
new file mode 100644
index 00000000..8c49a4d6
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java
@@ -0,0 +1,49 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * Neccessary interface for ZUGFeRD exporter
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+
+
+public interface IZUGFeRDExportableContact {
+
+ /**
+ * First and last name of the recipient
+ * @return
+ */
+ String getName();
+
+ /**
+ * Postal code of the recipient
+ * @return
+ */
+ String getZIP();
+
+ /**
+ * VAT ID (Umsatzsteueridentifikationsnummer) of the contact
+ * @return
+ */
+ String getVATID();
+
+ /**
+ * two-letter country code of the contact
+ * @return
+ */
+ String getCountry();
+
+ /***
+ * Returns the city of the contact
+ * @return
+ */
+ String getLocation();
+
+ /***
+ * Returns the street address (street+number) of the contact
+ */
+ String getStreet();
+
+}
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java
new file mode 100644
index 00000000..49622a54
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java
@@ -0,0 +1,41 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * Neccessary interface for ZUGFeRD exporter
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+
+import java.math.BigDecimal;
+
+public interface IZUGFeRDExportableItem {
+
+ IZUGFeRDExportableProduct getProduct();
+
+ /**
+ * The price of one item incl. taxes
+ * @return
+ */
+ BigDecimal getPriceGross();
+
+ /**
+ * The price of one item excl. taxes
+ * @return
+ */
+ BigDecimal getPrice();
+
+ /***
+ * how many
+ * @return
+ */
+ BigDecimal getQuantity();
+
+ /**
+ * The price of quantity items incl. taxes
+ * @return
+ */
+ BigDecimal getTotalGross();
+
+}
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java
new file mode 100644
index 00000000..8f552eb2
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java
@@ -0,0 +1,61 @@
+package org.mustangproject.ZUGFeRD;
+import java.math.BigDecimal;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * Necessary interface for ZUGFeRD exporter
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+
+public interface IZUGFeRDExportableProduct {
+
+ /**
+ * Unit code of the product
+ * Most common ones are
+ C62 one (piece)
+ DAY day
+ HAR hectare
+ HUR hour
+ KGM kilogram
+ KTM kilometre
+ KWH kilowatt hour
+ LS lump sum
+ LTR litre
+ MIN minute
+ MMK square millimetre
+ MMT millimetre
+ MTK square metre
+ MTQ cubic metre
+ MTR metre
+ NAR number of articles
+ NPR number of pairs
+ P1 percent
+ SET set
+ TNE tonne (metric ton)
+ WEE week
+
+ * @return
+ */
+ String getUnit();
+
+ /**
+ * Short name of the product
+ * @return
+ */
+ String getName();
+
+ /**
+ * long description of the product
+ * @return
+ */
+ String getDescription();
+
+ /**
+ * VAT percent of the product (e.g. 19, or 5.1 if you like)
+ * @return
+ */
+ BigDecimal getVATPercent();
+
+}
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java
new file mode 100644
index 00000000..257c1cc2
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java
@@ -0,0 +1,102 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * Neccessary interface for ZUGFeRD exporter
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+
+
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.HashMap;
+
+public interface IZUGFeRDExportableTransaction {
+
+ /**
+ * Number, typically invoice number of the invoice
+ * @return
+ */
+ String getNumber();
+
+ /**
+ * the date when the invoice was created
+ * @return
+ */
+ Date getIssueDate();
+
+
+ /**
+ * total amount incl. taxes
+ * @return
+ */
+ BigDecimal getTotalGross();
+
+ /**
+ * total amount excl. taxes
+ * @return
+ */
+ BigDecimal getTotal();
+
+ /**
+ * when the invoice is to be paid
+ * @return
+ */
+ Date getDueDate();
+
+ IZUGFeRDExportableItem[] getZFItems();
+
+ /***
+ * the recipient
+ * @return
+ */
+ IZUGFeRDExportableContact getRecipient();
+
+ /***
+ * BIC of the sender
+ * @return
+ */
+ String getOwnBIC();
+
+ /***
+ * Bank name of the sender
+ * @return
+ */
+ String getOwnBankName();
+
+ /**
+ * IBAN of the sender
+ * @return
+ */
+ String getOwnIBAN();
+
+ /**
+ * Tax ID (not VAT ID) of the sender
+ */
+ String getOwnTaxID();
+
+ /**
+ * VAT ID (Umsatzsteueridentifikationsnummer) of the sender
+ * @return
+ */
+ String getOwnVATID();
+
+ /**
+ * own name
+ * @return
+ */
+ String getOwnOrganisationName();
+
+ /**
+ * which taxes have been used with which amounts in this transaction,
+ * empty for no taxes, or e.g. 19=>190 and 7=>14 if 1000 Eur were applicable
+ * to 19% VAT (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT)
+ * 190 Eur
+ * @return
+ */
+ HashMap getVATPercentAmountMap();
+
+}
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/LICENSE b/mustang/src/main/java/org/mustangproject/ZUGFeRD/LICENSE
new file mode 100644
index 00000000..d4ecc703
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/LICENSE
@@ -0,0 +1,335 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+EXTERNAL COMPONENTS
+
+Apache PDFBox includes a number of components with separate copyright notices
+and license terms. Your use of these components is subject to the terms and
+conditions of the following licenses.
+
+Contributions made to the original PDFBox, JempBox and FontBox projects:
+
+ Copyright (c) 2002-2007, www.pdfbox.org
+ Copyright (c) 2006-2007, www.jempbox.org
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of pdfbox; nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+Adobe Font Metrics (AFM) for PDF Core 14 Fonts
+
+ This file and the 14 PostScript(R) AFM files it accompanies may be used,
+ copied, and distributed for any purpose and without charge, with or without
+ modification, provided that all copyright notices are retained; that the
+ AFM files are not distributed without this file; that all modifications
+ to this file or any of the AFM files are prominently noted in the modified
+ file(s); and that this paragraph is not modified. Adobe Systems has no
+ responsibility or obligation to support the use of the AFM files.
+
+CMaps for PDF Fonts (http://opensource.adobe.com/wiki/display/cmap/Downloads)
+
+ Copyright 1990-2009 Adobe Systems Incorporated.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ THE POSSIBILITY OF SUCH DAMAGE.
+
+Glyphlist (http://www.adobe.com/devnet/opentype/archives/glyph.html)
+
+ Copyright (c) 1997,1998,2002,2007 Adobe Systems Incorporated
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this documentation file to use, copy, publish, distribute,
+ sublicense, and/or sell copies of the documentation, and to permit
+ others to do the same, provided that:
+ - No modification, editing or other alteration of this document is
+ allowed; and
+ - The above copyright notice and this permission notice shall be
+ included in all copies of the documentation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this documentation file, to create their own derivative works
+ from the content of this document to use, copy, publish, distribute,
+ sublicense, and/or sell the derivative works, and to permit others to do
+ the same, provided that the derived work is not represented as being a
+ copy or version of this document.
+
+ Adobe shall not be liable to any party for any loss of revenue or profit
+ or for indirect, incidental, special, consequential, or other similar
+ damages, whether based on tort (including without limitation negligence
+ or strict liability), contract or other legal or equitable grounds even
+ if Adobe has been advised or had reason to know of the possibility of
+ such damages. The Adobe materials are provided on an "AS IS" basis.
+ Adobe specifically disclaims all express, statutory, or implied
+ warranties relating to the Adobe materials, including but not limited to
+ those concerning merchantability or fitness for a particular purpose or
+ non-infringement of any third party rights regarding the Adobe
+ materials.
+
+
+PaDaF PDF/A preflight (http://sourceforge.net/projects/padaf)
+
+ Copyright 2010 Atos Worldline SAS
+
+ Licensed by Atos Worldline SAS under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ Atos Worldline SAS licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/NOTICE b/mustang/src/main/java/org/mustangproject/ZUGFeRD/NOTICE
new file mode 100644
index 00000000..75b846a1
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/NOTICE
@@ -0,0 +1,25 @@
+Mustangproject
+Copyright 2014 Jochen Staerk
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Based on Apache PDFBox developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Apache PDFBox was based on source code originally developed in the PDFBox, JempBox and FontBox projects.
+Copyright (c) 2002-2007, www.pdfbox.org
+Copyright (c) 2006-2007, www.jempbox.org
+
+Apache PDFBox was based on source code originally developed in the PaDaF project.
+Copyright (c) 2010 Atos Worldline SAS
\ No newline at end of file
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java
new file mode 100644
index 00000000..76c2b318
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java
@@ -0,0 +1,160 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * ZUGFeRD exporter helper class
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+import org.apache.jempbox.impl.XMLUtil;
+import org.apache.jempbox.xmp.XMPSchemaBasic;
+import org.w3c.dom.Element;
+
+ /**
+ * Additionally to adding a RDF namespace with a indication which file
+ * attachment if Zugferd, this namespace has to be described in a PDFA
+ * Extension Schema.
+ *
+ * I know there is a PDFAExtensionSchema in the context of PDFBox' XMPBOX
+ * but I have been using PDFBox' JempBOX so far because I could not find out
+ * how to write XMPBOX XMPMetadata to a PDF file.
+ *
+ * So this is my version of PDFAExtensionSchema
+ * for PDFBox' jempbox XMPMetadata
+ *
+ * @author jstaerk
+ *
+ */
+ public class XMPSchemaPDFAExtensions extends XMPSchemaBasic {
+
+ private void addProperty(Element listNode, String name, String type,
+ String category, String description) {
+ Element nameNode = schema.getOwnerDocument().createElement(
+ "pdfaProperty:name"); //$NON-NLS-1$
+ XMLUtil.setStringValue(nameNode, name);
+ listNode.appendChild(nameNode);
+
+ Element typeNode = schema.getOwnerDocument().createElement(
+ "pdfaProperty:valueType"); //$NON-NLS-1$
+ XMLUtil.setStringValue(typeNode, type);
+ listNode.appendChild(typeNode);
+
+ Element categoryNode = schema.getOwnerDocument().createElement(
+ "pdfaProperty:category"); //$NON-NLS-1$
+ XMLUtil.setStringValue(categoryNode, category);
+ listNode.appendChild(categoryNode);
+
+ Element descriptionNode = schema.getOwnerDocument().createElement(
+ "pdfaProperty:description"); //$NON-NLS-1$
+ XMLUtil.setStringValue(descriptionNode, description);
+ listNode.appendChild(descriptionNode);
+
+ }
+
+
+
+ public XMPSchemaPDFAExtensions(org.apache.jempbox.xmp.XMPMetadata parent) {
+ super(parent);
+
+ // add some namespaces
+ schema.setAttributeNS(NS_NAMESPACE, "xmlns:pdfaExtension", //$NON-NLS-1$
+ "http://www.aiim.org/pdfa/ns/extension/"); //$NON-NLS-1$
+
+ schema.setAttributeNS(NS_NAMESPACE, "xmlns:pdfaSchema", //$NON-NLS-1$
+ "http://www.aiim.org/pdfa/ns/schema#"); //$NON-NLS-1$
+
+ schema.setAttributeNS(NS_NAMESPACE, "xmlns:pdfaProperty", //$NON-NLS-1$
+ "http://www.aiim.org/pdfa/ns/property#"); //$NON-NLS-1$
+
+ // the superclass includes this two namespaces we don't need
+ schema.removeAttributeNS(NS_NAMESPACE, "xapGImg"); //$NON-NLS-1$
+ schema.removeAttributeNS(NS_NAMESPACE, "xmp"); //$NON-NLS-1$
+
+ /*
+ *What we attach is basically this:
+ *pdfaExtension:schemas-node
+ *+--bag
+ * +--rdf:li
+ * +--some text node (multiple)
+ * +--property node
+ * +--rdf:Seq
+ * +--rdf:li (multiple) attribute node
+ * +--some attribute property description text node (multiple)
+ */
+ Element schemasNode = schema.getOwnerDocument().createElement(
+ "pdfaExtension:schemas"); //$NON-NLS-1$
+
+ Element bagNode = schema.getOwnerDocument()
+ .createElement("rdf:Bag"); //$NON-NLS-1$
+
+ Element bagListNode = schema.getOwnerDocument().createElement(
+ "rdf:li"); //$NON-NLS-1$
+
+ bagListNode.setAttribute("rdf:parseType", "Resource"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ Element schemaInfoNode = schema.getOwnerDocument().createElement(
+ "pdfaSchema:schema"); //$NON-NLS-1$
+ XMLUtil.setStringValue(schemaInfoNode,
+ "ZUGFeRD PDFA Extension Schema"); //$NON-NLS-1$
+ bagListNode.appendChild(schemaInfoNode);
+
+ Element nsInfoNode = schema.getOwnerDocument().createElement(
+ "pdfaSchema:namespaceURI"); //$NON-NLS-1$
+ XMLUtil.setStringValue(nsInfoNode, "urn:ferd:pdfa:invoice:rc#"); //$NON-NLS-1$
+ bagListNode.appendChild(nsInfoNode);
+
+ Element prefixInfoNode = schema.getOwnerDocument().createElement(
+ "pdfaSchema:prefix"); //$NON-NLS-1$
+ XMLUtil.setStringValue(prefixInfoNode, "zf"); //$NON-NLS-1$
+ bagListNode.appendChild(prefixInfoNode);
+
+ Element propertyNode = schema.getOwnerDocument().createElement(
+ "pdfaSchema:property"); //$NON-NLS-1$
+
+ Element sequenceNode = schema.getOwnerDocument().createElement(
+ "rdf:Seq"); //$NON-NLS-1$
+
+ Element seqList1Node = schema.getOwnerDocument().createElement(
+ "rdf:li"); //$NON-NLS-1$
+
+ seqList1Node.setAttribute("rdf:parseType", "Resource"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ addProperty(seqList1Node, "DocumentFileName", "Text", "external", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "The name of the embedded Zugferd XML invoice file"); //$NON-NLS-1$
+
+ sequenceNode.appendChild(seqList1Node);
+
+ Element seqList2Node = schema.getOwnerDocument().createElement(
+ "rdf:li"); //$NON-NLS-1$
+ seqList2Node.setAttribute("rdf:parseType", "Resource"); //$NON-NLS-1$ //$NON-NLS-2$
+ addProperty(seqList2Node, "DocumentType", "Text", "external", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "INVOICE"); //$NON-NLS-1$
+ sequenceNode.appendChild(seqList2Node);
+
+ Element seqList3Node = schema.getOwnerDocument().createElement(
+ "rdf:li"); //$NON-NLS-1$
+ seqList3Node.setAttribute("rdf:parseType", "Resource"); //$NON-NLS-1$ //$NON-NLS-2$
+ addProperty(seqList3Node, "Version", "Text", "external", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "The version of the ZUGFeRD data"); //$NON-NLS-1$
+ sequenceNode.appendChild(seqList3Node);
+
+ Element seqList4Node = schema.getOwnerDocument().createElement(
+ "rdf:li"); //$NON-NLS-1$
+ seqList4Node.setAttribute("rdf:parseType", "Resource"); //$NON-NLS-1$ //$NON-NLS-2$
+ addProperty(seqList4Node, "ConformanceLevel", "Text", "external", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "The conformance level of the ZUGFeRD data, i.e. BASIC or EXTENDED"); //$NON-NLS-1$
+ sequenceNode.appendChild(seqList4Node);
+
+ propertyNode.appendChild(sequenceNode);
+
+ bagListNode.appendChild(propertyNode);
+
+ bagNode.appendChild(bagListNode);
+ schemasNode.appendChild(bagNode);
+
+ schema.appendChild(schemasNode);
+
+ }
+
+ }
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java
new file mode 100644
index 00000000..c06fa286
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java
@@ -0,0 +1,50 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * ZUGFeRD exporter helper class
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+import org.apache.jempbox.impl.XMLUtil;
+import org.apache.jempbox.xmp.XMPSchemaBasic;
+import org.w3c.dom.Element;
+
+
+public class XMPSchemaZugferd extends XMPSchemaBasic {
+
+ /**
+ * This is what needs to be added to the RDF metadata - basically the name of the embedded
+ * Zugferd file
+ * */
+ public XMPSchemaZugferd(org.apache.jempbox.xmp.XMPMetadata parent) {
+ super(parent);
+
+ schema.setAttributeNS(NS_NAMESPACE, "xmlns:zf", //$NON-NLS-1$
+ "urn:ferd:pdfa:invoice:rc#"); //$NON-NLS-1$
+// the superclass includes this two namespaces we don't need
+ schema.removeAttributeNS(NS_NAMESPACE, "xapGImg"); //$NON-NLS-1$
+ schema.removeAttributeNS(NS_NAMESPACE, "xmp"); //$NON-NLS-1$
+ Element textNode = schema.getOwnerDocument().createElement(
+ "zf:DocumentType"); //$NON-NLS-1$
+ XMLUtil.setStringValue(textNode, "INVOICE"); //$NON-NLS-1$
+ schema.appendChild(textNode);
+
+ textNode = schema.getOwnerDocument().createElement(
+ "zf:DocumentFileName"); //$NON-NLS-1$
+ XMLUtil.setStringValue(textNode, "ZUGFeRD-invoice.xml"); //$NON-NLS-1$
+ schema.appendChild(textNode);
+
+ textNode = schema.getOwnerDocument().createElement("zf:Version"); //$NON-NLS-1$
+ XMLUtil.setStringValue(textNode, "RC"); //$NON-NLS-1$
+ schema.appendChild(textNode);
+
+ textNode = schema.getOwnerDocument().createElement(
+ "zf:ConformanceLevel"); //$NON-NLS-1$
+ XMLUtil.setStringValue(textNode, "BASIC"); //$NON-NLS-1$
+ schema.appendChild(textNode);
+
+ }
+
+ }
\ No newline at end of file
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java
new file mode 100644
index 00000000..0b6d2d63
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java
@@ -0,0 +1,497 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * ZUGFeRD exporter
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.jempbox.xmp.XMPMetadata;
+import org.apache.jempbox.xmp.XMPSchemaBasic;
+import org.apache.jempbox.xmp.XMPSchemaDublinCore;
+import org.apache.jempbox.xmp.XMPSchemaPDF;
+import org.apache.jempbox.xmp.pdfa.XMPSchemaPDFAId;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
+import org.apache.pdfbox.pdmodel.PDDocumentInformation;
+import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
+import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
+import org.apache.pdfbox.pdmodel.common.PDMetadata;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
+
+
+public class ZUGFeRDExporter {
+ /***
+ * You will need Apache PDFBox. To use the ZUGFeRD exporter,
+ implement IZUGFeRDExportableTransaction in yourTransaction
+ (which will require you to implement Product, Item and Contact)
+ then call
+ doc = PDDocument.load(PDFfilename);
+ // automatically add Zugferd to all outgoing invoices
+ ZUGFeRDExporter ze = new ZUGFeRDExporter();
+ ze.PDFmakeA3compliant(doc, "Your application name",
+ System.getProperty("user.name"), true);
+ ze.PDFattachZugferdFile(doc, yourTransaction);
+
+ doc.save(PDFfilename);
+
+ * @author jstaerk
+ *
+ */
+
+
+
+
+
+ //// MAIN CLASS
+
+ private String conformanceLevel="U";
+ private String versionStr="1.0";
+
+ private String currencyFormat(BigDecimal value, char decimalDelimiter) {
+ /*
+ * I needed 123,45, locale independent.I tried
+ * NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is
+ * locale specific.I also tried DecimalFormat df = new DecimalFormat(
+ * "0,00" ); df.setDecimalSeparatorAlwaysShown(true);
+ * df.setGroupingUsed(false); DecimalFormatSymbols symbols = new
+ * DecimalFormatSymbols(); symbols.setDecimalSeparator(',');
+ * symbols.setGroupingSeparator(' ');
+ * df.setDecimalFormatSymbols(symbols);
+ *
+ * but that would not switch off grouping. Although I liked very much
+ * the (incomplete) "BNF diagram" in
+ * http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
+ * in the end I decided to calculate myself and take eur+sparator+cents
+ *
+ * This function will cut off, i.e. floor() subcent values Tests:
+ * System.err.println(utils.currencyFormat(new BigDecimal(0),
+ * ".")+"\n"+utils.currencyFormat(new BigDecimal("-1.10"),
+ * ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.1"),
+ * ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.01"),
+ * ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3489"),
+ * ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3419"),
+ * ",")+"\n"+utils.currencyFormat(new BigDecimal("12"), ","));
+ *
+ * results 0.00 -1,10 -1,10 -1,01 20000123,34 20000123,34 12,00
+ */
+ value=value.setScale( 2, BigDecimal.ROUND_HALF_UP ); // first, round so that e.g. 1.189999999999999946709294817992486059665679931640625 becomes 1.19
+ long totalCent = value.multiply(new BigDecimal(100)).intValue(); //now get the cents
+ long eurOnly = value.longValue();
+ long centOnly = Math.abs(totalCent % 100);
+ StringBuffer res = new StringBuffer();
+ res.append(eurOnly);
+ res.append(decimalDelimiter);
+ if (centOnly < 10) {
+ res.append('0');
+ }
+ res.append(centOnly);
+ return res.toString();
+ }
+
+ /**
+ All files are PDF/A-3, setConformance refers to the level conformance.
+
+ PDF/A-3 has three coformance levels, called "A", "U" and "B".
+
+ PDF/A-3-B where B means only visually
+ preservable, U -standard for Mustang- means visually and unicode
+ preservable and A means full compliance, i.e. visually,
+ unicode and structurally preservable and tagged PDF, i.e. useful metainformation for blind people.
+
+ Feel free to pass "A" as new level if you know what you are doing :-)
+
+
+ */
+ public void setConformanceLevel(String newLevel) {
+ conformanceLevel=newLevel;
+ }
+
+
+ /**
+ * 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)
+ * */
+ public PDDocumentCatalog PDFmakeA3compliant(PDDocument doc, String producer, String creator,
+ boolean attachZugferdHeaders) throws IOException,
+ TransformerException {
+ String fullProducer=producer+"(via mustangproject.org "+versionStr+")";
+ PDDocumentCatalog cat = doc.getDocumentCatalog();
+ PDMetadata metadata = new PDMetadata(doc);
+ cat.setMetadata(metadata);
+ // we're using the jempbox org.apache.jempbox.xmp.XMPMetadata version,
+ // not the xmpbox one
+ XMPMetadata xmp = new XMPMetadata();
+
+ XMPSchemaPDFAId pdfaid = new XMPSchemaPDFAId(xmp);
+ pdfaid.setAbout(""); //$NON-NLS-1$
+ xmp.addSchema(pdfaid);
+
+ XMPSchemaDublinCore dc = xmp.addDublinCoreSchema();
+ dc.addCreator(creator);
+ dc.setAbout(""); //$NON-NLS-1$
+
+ XMPSchemaBasic xsb = xmp.addBasicSchema();
+ xsb.setAbout(""); //$NON-NLS-1$
+
+ xsb.setCreatorTool(creator);
+ xsb.setCreateDate(GregorianCalendar.getInstance());
+ // PDDocumentInformation pdi=doc.getDocumentInformation();
+ PDDocumentInformation pdi = new PDDocumentInformation();
+ pdi.setProducer(fullProducer);
+ pdi.setAuthor(creator);
+ doc.setDocumentInformation(pdi);
+
+ XMPSchemaPDF pdf = xmp.addPDFSchema();
+ pdf.setProducer(fullProducer);
+ pdf.setAbout(""); //$NON-NLS-1$
+
+ /*
+ // Mandatory: PDF/A3-a is tagged PDF which has to be expressed using a
+ // MarkInfo dictionary (PDF A/3 Standard sec. 6.7.2.2)
+ PDMarkInfo markinfo = new PDMarkInfo();
+ markinfo.setMarked(true);
+ doc.getDocumentCatalog().setMarkInfo(markinfo);
+*/
+/*
+ *
+ To be on the safe side, we use level B without Markinfo because we can not
+ guarantee that the user correctly tagged the templates for the PDF.
+
+ * */
+ pdfaid.setConformance(conformanceLevel);//$NON-NLS-1$ //$NON-NLS-1$
+
+ pdfaid.setPart(3);
+
+ if (attachZugferdHeaders) {
+ addZugferdXMP(xmp); /*
+ * this is the only line where we do something
+ * Zugferd-specific, i.e. add PDF metadata
+ * specifically for Zugferd, not generically for
+ * a embedded file
+ */
+ }
+
+ metadata.importXMPMetadata(xmp);
+ return cat;
+ }
+
+ private String getZugferdXMLForTransaction(IZUGFeRDExportableTransaction trans) {
+ SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$
+ String xml= "\n" //$NON-NLS-1$
+ + "\n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " false\n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " urn:ferd:invoice:rc:comfort\n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getNumber()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " RECHNUNG\n" //$NON-NLS-1$
+ + " 380\n" //$NON-NLS-1$
+ + " "+zugferdDateFormat.format(trans.getIssueDate())+"\n" //date format was 20130605 //$NON-NLS-1$ //$NON-NLS-2$
+// + " \n"
+// + " \n"
+// + "Rechnung gemäß Bestellung Nr. 2013-471331 vom 01.03.2013.\n"
+// + "\n"
+// + " \n"
+// + " \n"
+// + " \n"
+// + " \n"
+// + "Es bestehen Rabatt- und Bonusvereinbarungen.\n"
+// + " \n"
+// + " AAK\n"
+// + " \n"
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+// + " AB-312\n"
+ + " \n" //$NON-NLS-1$
+// + " 4000001123452\n"
+ + " "+trans.getOwnOrganisationName()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+/* + " \n"
+ + " 80333\n"
+ + " Lieferantenstraße 20\n"
+ + " München\n"
+ + " DE\n"
+ + " \n"*/
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getOwnTaxID()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getOwnVATID()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+// + " GE2020211\n"
+// + " 4000001987658\n"
+ + " "+trans.getRecipient().getName()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+// + " \n"
+// + " xxx\n"
+// + " \n"
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getRecipient().getZIP()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+trans.getRecipient().getStreet()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+trans.getRecipient().getLocation()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+trans.getRecipient().getCountry()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getRecipient().getVATID()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+// + " \n"
+// + " 20130301\n"
+// + " 2013-471331\n"
+// + " \n"
+ + " \n" //$NON-NLS-1$
+/* + " \n"
+ + " \n"
+ + " 20130603\n"
+ + " \n"
+ + " \n"
+ + " 20130603\n"
+ + " 2013-51112\n"
+ + " \n"
+ + " \n"*/
+ + " \n" //$NON-NLS-1$
+ // it's unclear if this is supposed to refer to the SEPA mandate or the transaction purpose
+// + " "+trans.getNumber()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " EUR\n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " 42\n" //$NON-NLS-1$
+ + " Überweisung\n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getOwnIBAN()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+trans.getOwnBIC()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+trans.getOwnBankName()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n"; //$NON-NLS-1$
+
+
+
+ HashMap VATPercentAmountMap=trans.getVATPercentAmountMap();
+ for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
+ BigDecimal amount = VATPercentAmountMap.get(currentTaxPercent);
+ if (amount != null) {
+ xml += " \n" //$NON-NLS-1$
+ + " "+currencyFormat(amount, '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " VAT\n" //$NON-NLS-1$
+// + " 129.37\n"
+ + " S\n" //$NON-NLS-1$
+ + " "+currentTaxPercent+"\n" //$NON-NLS-1$
+ + " \n"; //$NON-NLS-1$
+
+
+
+ }
+ }
+/* xml+= "
+ + " \n"
+ + " false\n"
+ + " 10\n"
+ + " 1.00\n"
+ + " Sondernachlass\n"
+ + " \n"
+ + " VAT\n"
+ + " S\n"
+ + " 19\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " false\n"
+ + " 137.30\n"
+ + " 13.73\n"
+ + " Sondernachlass\n"
+ + " \n"
+ + " VAT\n"
+ + " S\n"
+ + " 7\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " Versandkosten\n"
+ + " 5.80\n"
+ + " \n"
+ + " VAT\n"
+ + " S\n"
+ + " 7\n"
+ + " \n"
+ + " \n"*/
+
+ xml=xml+ " \n" //$NON-NLS-1$
+// + " Zahlbar innerhalb 30 Tagen netto bis 04.07.2013, 3% Skonto innerhalb 10 Tagen bis 15.06.2013\n"
+ + " "+zugferdDateFormat.format(trans.getDueDate())+"\n"//20130704 //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+currencyFormat(trans.getTotal(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+// + " 5.80\n"
+// + " 14.73\n"
+ + " "+currencyFormat(trans.getTotal(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+currencyFormat(trans.getTotalGross().subtract(trans.getTotal()), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+currencyFormat(trans.getTotalGross(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+// + " 0.00\n"
+ + " "+currencyFormat(trans.getTotalGross(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n"; //$NON-NLS-1$
+// + " \n"
+// + " \n"
+// + " \n"
+// + " Wir erlauben uns Ihnen folgende Positionen aus der Lieferung Nr. 2013-51112 in Rechnung zu stellen:\n"
+// + " \n"
+// + " \n"
+// + " \n";
+
+
+ int lineID=0;
+ for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
+ lineID++;
+ xml=xml+ " \n"+ //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ + " "+lineID+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+currencyFormat(currentItem.getPriceGross(),'.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " 1\n" //$NON-NLS-1$ //$NON-NLS-2$
+// + " \n"
+// + " false\n"
+// + " 0.6667\n"
+// + " Rabatt\n"
+// + " \n"
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+currencyFormat(currentItem.getPrice(),'.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " 1\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+
+ + " \n" //$NON-NLS-1$
+ + " "+currentItem.getQuantity()+"\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " VAT\n" //$NON-NLS-1$
+ + " S\n" //$NON-NLS-1$
+ + " "+currentItem.getProduct().getVATPercent()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " "+currencyFormat(currentItem.getTotalGross(),'.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+// + " 4012345001235\n"
+// + " KR3M\n"
+// + " 55T01\n"
+ + " "+currentItem.getProduct().getName()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " "+currentItem.getProduct().getDescription()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n"; //$NON-NLS-1$
+
+
+
+ }
+
+
+ xml=xml + " \n" //$NON-NLS-1$
+ + ""; //$NON-NLS-1$
+ return xml;
+ }
+
+ /**
+ * embed the Zugferd XML structure in a file named ZUGFeRD-invoice.xml
+ * */
+ public void PDFattachZugferdFile(PDDocument doc, IZUGFeRDExportableTransaction trans) throws IOException {
+
+ // embedded files are stored in a named tree
+ PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode();
+
+ String filename="ZUGFeRD-invoice.xml"; //$NON-NLS-1$
+ // first create the file specification, which holds the embedded file
+ PDComplexFileSpecification fs = new PDComplexFileSpecification();
+ fs.setFile(filename);
+
+ COSDictionary dict = fs.getCOSDictionary();
+ // Relation "Source" for linking with eg. catalog
+ dict.setName("AFRelationship", "Alternative"); // as defined in Zugferd standard //$NON-NLS-1$ //$NON-NLS-2$
+
+ dict.setString("UF", filename); //$NON-NLS-1$
+
+ // create a dummy file stream, this would probably normally be a
+ // FileInputStream
+
+ byte[] zugferdRaw = getZugferdXMLForTransaction(trans).getBytes("UTF-8"); //$NON-NLS-1$
+
+ byte[] zugferdData;
+
+
+
+ if ((zugferdRaw[0]==(byte)0xEF)&&(zugferdRaw[1]==(byte)0xBB)&&(zugferdRaw[2]==(byte)0xBF)) {
+ // I don't like BOMs, lets remove it
+ zugferdData=new byte[zugferdRaw.length-3];
+ System.arraycopy(zugferdRaw,3,zugferdData,0,zugferdRaw.length-3);
+ } else {
+ zugferdData=zugferdRaw;
+ }
+
+ ByteArrayInputStream fakeFile = new ByteArrayInputStream(zugferdData);
+ PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile);
+ // now lets some of the optional parameters
+ ef.setSubtype("text/xml");// as defined in Zugferd standard //$NON-NLS-1$
+ ef.setSize(zugferdData.length);
+ ef.setCreationDate(new GregorianCalendar());
+
+ ef.setModDate(GregorianCalendar.getInstance());
+
+ fs.setEmbeddedFile(ef);
+
+ // now add the entry to the embedded file tree and set in the document.
+ efTree.setNames(Collections.singletonMap(filename, fs));
+ PDDocumentNameDictionary names = new PDDocumentNameDictionary(
+ doc.getDocumentCatalog());
+ names.setEmbeddedFiles(efTree);
+ doc.getDocumentCatalog().setNames(names);
+ // AF entry (Array) in catalog with the FileSpec
+ COSArray cosArray = new COSArray();
+ cosArray.add(fs);
+ doc.getDocumentCatalog().getCOSDictionary().setItem("AF", cosArray); //$NON-NLS-1$
+
+ }
+
+/***
+ * This will add both the RDF-indication which embedded file is Zugferd and the
+ * neccessary PDF/A schema extension description to be able to add this information to RDF
+ * @param metadata
+ */
+ private void addZugferdXMP(XMPMetadata metadata) {
+
+ XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata);
+ zf.setAbout(""); //$NON-NLS-1$
+ metadata.addSchema(zf);
+
+ XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(metadata);
+ pdfaex.setAbout(""); //$NON-NLS-1$
+ metadata.addSchema(pdfaex);
+
+ }
+
+}
diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java
new file mode 100644
index 00000000..7f1ea430
--- /dev/null
+++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java
@@ -0,0 +1,330 @@
+package org.mustangproject.ZUGFeRD;
+/**
+ * Mustangproject's ZUGFeRD implementation
+ * ZUGFeRD importer
+ * Licensed under the APLv2
+ * @date 2014-05-10
+ * @version 1.0
+ * @author jstaerk
+ * */
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
+import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
+import org.apache.pdfbox.pdmodel.common.COSObjectable;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+public class ZUGFeRDImporter {
+ /*
+ call extract(importFilename).
+ containsMeta() will return if ZUGFeRD data has been found,
+ afterwards you can call getBIC(), getIBAN() etc.
+
+*/
+
+ /**@var if metadata has been found */
+ private boolean containsMeta=false;
+ /**@var the reference (i.e. invoice number) of the sender */
+ private String foreignReference;
+ private String BIC;
+ private String IBAN;
+ private String holder;
+ private String amount;
+ private String meta;
+ private String bankName;
+ private boolean amountFound;
+
+
+
+
+ public void extract(String pdfFilename) {
+ PDDocument doc;
+ try {
+ doc = PDDocument.load(pdfFilename);
+// PDDocumentInformation info = doc.getDocumentInformation();
+ PDEmbeddedFilesNameTreeNode etn;
+ PDDocumentNameDictionary names = new PDDocumentNameDictionary(
+ doc.getDocumentCatalog());
+ etn = names.getEmbeddedFiles();
+ Map efMap = etn.getNames();
+ // String filePath = "/tmp/";
+
+ for (String filename : efMap.keySet()) {
+ /**
+ * currently (in the release candidate of version 1) only one
+ * attached file with the name ZUGFeRD-invoice.xml is allowed
+ * */
+ if (filename.equals("ZUGFeRD-invoice.xml")) { //$NON-NLS-1$
+ containsMeta = true;
+
+ PDComplexFileSpecification fileSpec = (PDComplexFileSpecification) efMap
+ .get(filename);
+ PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
+ // String embeddedFilename = filePath + filename;
+ // File file = new File(filePath + filename);
+ // System.out.println("Writing " + embeddedFilename);
+ // ByteArrayOutputStream fileBytes=new
+ // ByteArrayOutputStream();
+ // FileOutputStream fos = new FileOutputStream(file);
+
+ setMeta(new String(embeddedFile.getByteArray()));
+ // fos.write(embeddedFile.getByteArray());
+ // fos.close();
+ }
+ }
+
+ } catch (IOException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ }
+
+ public void parse() {
+ DocumentBuilderFactory factory = null;
+ DocumentBuilder builder = null;
+ Document document = null;
+
+ factory = DocumentBuilderFactory.newInstance();
+ try {
+ builder = factory.newDocumentBuilder();
+ } catch (ParserConfigurationException ex3) {
+ // TODO Auto-generated catch block
+ ex3.printStackTrace();
+ }
+
+ try {
+ InputStream bais = new ByteArrayInputStream(meta.getBytes());
+ document = builder.parse(bais);
+ } catch (SAXException ex1) {
+ ex1.printStackTrace();
+ } catch (IOException ex2) {
+ ex2.printStackTrace();
+ }
+ // rootNode = document.getDocumentElement();
+ // ApplicableSupplyChainTradeSettlement
+ NodeList ndList = document
+ .getElementsByTagName("PaymentReference"); //$NON-NLS-1$
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // if there is a attribute in the tag number:value
+
+ setForeignReference(booking.getTextContent());
+
+ }
+/*
+ ndList = document
+ .getElementsByTagName("GermanBankleitzahlID"); //$NON-NLS-1$
+
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // if there is a attribute in the tag number:value
+ setBIC(booking.getTextContent());
+
+ }
+
+ ndList = document.getElementsByTagName("ProprietaryID"); //$NON-NLS-1$
+
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // if there is a attribute in the tag number:value
+ setIBAN(booking.getTextContent());
+
+ }
+*/
+ ndList = document.getElementsByTagName("PayeePartyCreditorFinancialAccount"); //$NON-NLS-1$
+
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // there are many "name" elements, so get the one below
+ // SellerTradeParty
+ NodeList bookingDetails = booking.getChildNodes();
+ for (int detailIndex = 0; detailIndex < bookingDetails
+ .getLength(); detailIndex++) {
+ Node detail = bookingDetails.item(detailIndex);
+ if (detail.getNodeName().equals("IBANID")) { //$NON-NLS-1$
+ setIBAN(detail.getTextContent());
+ }
+ }
+
+ }
+
+ ndList = document.getElementsByTagName("PayeeSpecifiedCreditorFinancialInstitution"); //$NON-NLS-1$
+
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // there are many "name" elements, so get the one below
+ // SellerTradeParty
+ NodeList bookingDetails = booking.getChildNodes();
+ for (int detailIndex = 0; detailIndex < bookingDetails
+ .getLength(); detailIndex++) {
+ Node detail = bookingDetails.item(detailIndex);
+ if (detail.getNodeName().equals("BICID")) { //$NON-NLS-1$
+ setBIC(detail.getTextContent());
+ }
+ if (detail.getNodeName().equals("Name")) { //$NON-NLS-1$
+ setBankName(detail.getTextContent());
+ }
+ }
+
+ }
+
+ //
+ ndList = document.getElementsByTagName("SellerTradeParty"); //$NON-NLS-1$
+
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // there are many "name" elements, so get the one below
+ // SellerTradeParty
+ NodeList bookingDetails = booking.getChildNodes();
+ for (int detailIndex = 0; detailIndex < bookingDetails
+ .getLength(); detailIndex++) {
+ Node detail = bookingDetails.item(detailIndex);
+ if (detail.getNodeName().equals("Name")) { //$NON-NLS-1$
+ setHolder(detail.getTextContent());
+ }
+ }
+
+ }
+
+ ndList = document.getElementsByTagName("DuePayableAmount"); //$NON-NLS-1$
+
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // if there is a attribute in the tag number:value
+ amountFound=true;
+ setAmount(booking.getTextContent());
+
+ }
+
+
+ if (!amountFound) {
+ /* there is apparently no requirement to mention DuePayableAmount,,
+ * if it's not there, check for GrandTotalAmount
+ */
+ ndList = document.getElementsByTagName("GrandTotalAmount"); //$NON-NLS-1$
+ for (int bookingIndex = 0; bookingIndex < ndList
+ .getLength(); bookingIndex++) {
+ Node booking = ndList.item(bookingIndex);
+ // if there is a attribute in the tag number:value
+ amountFound=true;
+ setAmount(booking.getTextContent());
+
+ }
+
+ }
+
+
+ }
+
+
+ public boolean containsMeta() {
+ return containsMeta;
+ }
+
+ public String getForeignReference() {
+ return foreignReference;
+ }
+
+
+
+ public void setForeignReference(String foreignReference) {
+ this.foreignReference = foreignReference;
+ }
+
+
+
+ public String getBIC() {
+ return BIC;
+ }
+
+
+
+ private void setBIC(String bic) {
+ this.BIC = bic;
+ }
+
+
+ private void setBankName(String bankname) {
+ this.bankName = bankname;
+ }
+
+
+
+ public String getIBAN() {
+ return IBAN;
+ }
+
+
+ public String getBankName() {
+ return bankName;
+ }
+
+
+
+ public void setIBAN(String IBAN) {
+ this.IBAN = IBAN;
+ }
+
+
+
+ public String getHolder() {
+ return holder;
+ }
+
+
+
+ private void setHolder(String holder) {
+ this.holder = holder;
+ }
+
+
+
+ public String getAmount() {
+ return amount;
+ }
+
+
+ private void setAmount(String amount) {
+ this.amount = amount;
+ }
+
+ public void setMeta(String meta) {
+ this.meta=meta;
+ }
+
+ public String getMeta() {
+ return meta;
+ }
+
+ /**
+ * will return true if the metadata (just extract-ed or set with setMeta) contains ZUGFeRD XML
+ * */
+ public boolean canParse() {
+
+
+ //SpecifiedExchangedDocumentContext is in the schema, so a relatively good indication if zugferd is present - better than just invoice
+ return (meta!=null)&&( meta.length()>0)&&( meta.contains("SpecifiedExchangedDocumentContext")); //$NON-NLS-1$
+ }
+}
diff --git a/mustang/src/test/java/org/mustangproject/ZUGFeRD/AppTest.java b/mustang/src/test/java/org/mustangproject/ZUGFeRD/AppTest.java
new file mode 100644
index 00000000..a8cece5d
--- /dev/null
+++ b/mustang/src/test/java/org/mustangproject/ZUGFeRD/AppTest.java
@@ -0,0 +1,38 @@
+package org.mustangproject.ZUGFeRD;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest
+ extends TestCase
+{
+ /**
+ * Create the test case
+ *
+ * @param testName name of the test case
+ */
+ public AppTest( String testName )
+ {
+ super( testName );
+ }
+
+ /**
+ * @return the suite of tests being tested
+ */
+ public static Test suite()
+ {
+ return new TestSuite( AppTest.class );
+ }
+
+ /**
+ * Rigourous Test :-)
+ */
+ public void testApp()
+ {
+ assertTrue( true );
+ }
+}