ZUGFeRD Invoice Example

1st September 2017
iText PDF
Customer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;
 
/**
 * Plain Old Java Object containing info about a Customer.
 * @author Bruno Lowagie (iText Software)
 */
public class Customer {
    
    /** The id. */
    protected int id;
    
    /** The first name. */
    protected String firstName;
    
    /** The last name. */
    protected String lastName;
    
    /** The street. */
    protected String street;
    
    /** The postal code. */
    protected String postalcode;
    
    /** The city. */
    protected String city;
    
    /** The country id. */
    protected String countryId;
 
    /**
     * Gets the id.
     *
     * @return the id
     */
    public int getId() {
        return id;
    }
 
    /**
     * Sets the id.
     *
     * @param id the new id
     */
    public void setId(int id) {
        this.id = id;
    }
 
    /**
     * Gets the first name.
     *
     * @return the first name
     */
    public String getFirstName() {
        return firstName;
    }
 
    /**
     * Sets the first name.
     *
     * @param firstName the new first name
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
 
    /**
     * Gets the last name.
     *
     * @return the last name
     */
    public String getLastName() {
        return lastName;
    }
 
    /**
     * Sets the last name.
     *
     * @param lastName the new last name
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
 
    /**
     * Gets the street.
     *
     * @return the street
     */
    public String getStreet() {
        return street;
    }
 
    /**
     * Sets the street.
     *
     * @param street the new street
     */
    public void setStreet(String street) {
        this.street = street;
    }
 
    /**
     * Gets the city.
     *
     * @return the city
     */
    public String getCity() {
        return city;
    }
 
    /**
     * Sets the city.
     *
     * @param city the new city
     */
    public void setCity(String city) {
        this.city = city;
    }
 
    /**
     * Gets the postal code.
     *
     * @return the postal code
     */
    public String getPostalcode() {
        return postalcode;
    }
 
    /**
     * Sets the postal code.
     *
     * @param postalcode the new postalcode
     */
    public void setPostalcode(String postalcode) {
        this.postalcode = postalcode;
    }
 
    /**
     * Gets the country id.
     *
     * @return the country id
     */
    public String getCountryId() {
        return countryId;
    }
 
    /**
     * Sets the country id.
     *
     * @param countryId the new country id
     */
    public void setCountryId(String countryId) {
        this.countryId = countryId;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(id).append("\n");
        sb.append("    First Name: ").append(firstName).append("\n");
        sb.append("    Last Name: ").append(lastName).append("\n");
        sb.append("    Street: ").append(street).append("\n");
        sb.append("    City: ").append(countryId).append(" ").append(postalcode).append(" ").append(city);
        return sb.toString();
    }
}
Invoice.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;
 
import java.util.Date;
import java.util.List;
 
/**
 * Plain Old Java Object containing info about an Invoice.
 * @author Bruno Lowagie (iText Software)
 */
public class Invoice {
    
    /** The id. */
    protected int id;
    
    /** The customer. */
    protected Customer customer;
    
    /** The total. */
    protected double total;
    
    /** The items. */
    protected List<Item> items;
    
    /** The invoice date. */
    protected Date invoiceDate;
 
    /**
     * Gets the id.
     *
     * @return the id
     */
    public int getId() {
        return id;
    }
 
    /**
     * Sets the id.
     *
     * @param id the new id
     */
    public void setId(int id) {
        this.id = id;
    }
 
    /**
     * Gets the customer.
     *
     * @return the customer
     */
    public Customer getCustomer() {
        return customer;
    }
 
    /**
     * Sets the customer.
     *
     * @param customer the new customer
     */
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
 
    /**
     * Gets the total.
     *
     * @return the total
     */
    public double getTotal() {
        return total;
    }
 
    /**
     * Sets the total.
     *
     * @param total the new total
     */
    public void setTotal(double total) {
        this.total = total;
    }
 
    /**
     * Gets the items.
     *
     * @return the items
     */
    public List<Item> getItems() {
        return items;
    }
 
    /**
     * Sets the items.
     *
     * @param items the new items
     */
    public void setItems(List<Item> items) {
        this.items = items;
    }
 
    /**
     * Gets the invoice date.
     *
     * @return the invoice date
     */
    public Date getInvoiceDate() {
        return invoiceDate;
    }
 
    /**
     * Sets the invoice date.
     *
     * @param invoiceDate the new invoice date
     */
    public void setInvoiceDate(Date invoiceDate) {
        this.invoiceDate = invoiceDate;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Invoice id: ").append(id).append(" Date: ").append(invoiceDate).append(" Total cost: ").append(total).append("\u20ac\n");
        sb.append("Customer: ").append(customer.toString()).append("\n");
        for (Item item : items) {
            sb.append(item.toString()).append("\n");
        }
        return sb.toString();
    }
}
Item.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;
 
/**
 * Plain Old Java Object containing info about an Item.
 * @author Bruno Lowagie (iText Software)
 */
public class Item {
    
    /** The item. */
    protected int item;
    
    /** The product. */
    protected Product product;
    
    /** The quantity. */
    protected int quantity;
    
    /** The cost. */
    protected double cost;
 
    /**
     * Gets the item.
     *
     * @return the item
     */
    public int getItem() {
        return item;
    }
 
    /**
     * Sets the item.
     *
     * @param item the new item
     */
    public void setItem(int item) {
        this.item = item;
    }
 
    /**
     * Gets the product.
     *
     * @return the product
     */
    public Product getProduct() {
        return product;
    }
 
    /**
     * Sets the product.
     *
     * @param product the new product
     */
    public void setProduct(Product product) {
        this.product = product;
    }
 
    /**
     * Gets the quantity.
     *
     * @return the quantity
     */
    public int getQuantity() {
        return quantity;
    }
 
    /**
     * Sets the quantity.
     *
     * @param quantity the new quantity
     */
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
 
    /**
     * Gets the cost.
     *
     * @return the cost
     */
    public double getCost() {
        return cost;
    }
 
    /**
     * Sets the cost.
     *
     * @param cost the new cost
     */
    public void setCost(double cost) {
        this.cost = cost;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("  #").append(item);
        sb.append(product.toString());
        sb.append("\tQuantity: ").append(quantity);
        sb.append("\tCost: ").append(cost).append("\u20ac");
        return sb.toString();
    }
}
PojoFactory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
 
/**
 * Factory that creates Invoice, Customer, Product, and Item classes.
 * @author Bruno Lowagie (iText Software)
 */
public class PojoFactory {
    
    /** Instance of this PojoFactory that will be reused. */
    protected static PojoFactory factory = null;
    
    /** The connection to the HSQLDB database. */
    protected Connection connection;
    
    /** The customer cache. */
    protected HashMap<Integer, Customer> customerCache = new HashMap<Integer, Customer>();
    
    /** The product cache. */
    protected HashMap<Integer, Product> productCache = new HashMap<Integer, Product>();
    
    /** Prepared statement to get customer data. */
    protected PreparedStatement getCustomer;
 
    /** Prepared statement to get product data. */
    protected PreparedStatement getProduct;
 
    /** Prepared statement to get items. */
    protected PreparedStatement getItems;
    
    /**
     * Instantiates a new POJO factory.
     *
     * @throws ClassNotFoundException the class not found exception
     * @throws SQLException the SQL exception
     */
    private PojoFactory() throws ClassNotFoundException, SQLException {
        Class.forName("org.hsqldb.jdbcDriver");
        connection = DriverManager.getConnection(
            "jdbc:hsqldb:resources/db/invoices", "SA", "");
        getCustomer = connection.prepareStatement("SELECT * FROM Customer WHERE id = ?");
        getProduct = connection.prepareStatement("SELECT * FROM Product WHERE id = ?");
        getItems = connection.prepareStatement("SELECT * FROM Item WHERE invoiceid = ?");
    }
    
    /**
     * Gets the single instance of PojoFactory.
     *
     * @return single instance of PojoFactory
     * @throws SQLException the SQL exception
     */
    public static PojoFactory getInstance() throws SQLException {
        if (factory == null || factory.connection.isClosed()) {
            try {
                factory = new PojoFactory();
            } catch (ClassNotFoundException cnfe) {
                throw new SQLException(cnfe.getMessage());
            }
        }
        return factory;
    }
    
    /**
     * Close the database connection.
     *
     * @throws SQLException the SQL exception
     */
    public void close() throws SQLException {
        connection.close();
    }
    
    /**
     * Gets all the {@link Invoice} objects stored in the database.
     *
     * @return the invoices
     * @throws SQLException the SQL exception
     */
    public List<Invoice> getInvoices() throws SQLException {
        List<Invoice> invoices = new ArrayList<Invoice>();
        Statement stm = connection.createStatement();
        ResultSet rs = stm.executeQuery("SELECT * FROM Invoice");
        while (rs.next()) {
            invoices.add(getInvoice(rs));
        }
        stm.close();
        return invoices;
    }
    
    /**
     * Creates an {@link Invoice} object from a database result set.
     *
     * @param rs the result set
     * @return the invoice object
     * @throws SQLException the SQL exception
     */
    public Invoice getInvoice(ResultSet rs) throws SQLException {
        Invoice invoice = new Invoice();
        invoice.setId(rs.getInt("id"));
        invoice.setCustomer(getCustomer(rs.getInt("customerid")));
        List<Item> items = getItems(rs.getInt("id"));
        invoice.setItems(items);
        double total = 0;
        for (Item item : items)
            total += item.getCost();
        invoice.setTotal(total);
        invoice.setInvoiceDate(rs.getDate("invoicedate"));
        return invoice;
    }
    
    /**
     * Creates an {@link Item} object from a database result set.
     *
     * @param rs the result set
     * @return the item object
     * @throws SQLException the SQL exception
     */
    public Item getItem(ResultSet rs) throws SQLException {
        Item item = new Item();
        item.setItem(rs.getInt("Item"));
        Product product = getProduct(rs.getInt("ProductId"));
        item.setProduct(product);
        item.setQuantity(rs.getInt("Quantity"));
        item.setCost(item.getQuantity() * product.getPrice());
        return item;
    }
    
    /**
     * Gets a {@link Customer} object, given a customer id.
     *
     * @param id the customer id
     * @return the customer object
     * @throws SQLException the SQL exception
     */
    public Customer getCustomer(int id) throws SQLException {
        if (customerCache.containsKey(id))
            return customerCache.get(id);
        getCustomer.setInt(1, id);
        ResultSet rs = getCustomer.executeQuery();
        if (rs.next()) {
            Customer customer = new Customer();
            customer.setId(id);
            customer.setFirstName(rs.getString("FirstName"));
            customer.setLastName(rs.getString("LastName"));
            customer.setStreet(rs.getString("Street"));
            customer.setPostalcode(rs.getString("Postalcode"));
            customer.setCity(rs.getString("City"));
            customer.setCountryId(rs.getString("CountryID"));
            customerCache.put(id, customer);
            return customer;
        }
        return null;
    }
    
    /**
     * Gets a {@link Product} object, given a product id.
     *
     * @param id the product id
     * @return the product object
     * @throws SQLException the SQL exception
     */
    public Product getProduct(int id) throws SQLException {
        if (productCache.containsKey(id))
            return productCache.get(id);
        getProduct.setInt(1, id);
        ResultSet rs = getProduct.executeQuery();
        if (rs.next()) {
            Product product = new Product();
            product.setId(id);
            product.setName(rs.getString("Name"));
            product.setPrice(rs.getDouble("Price"));
            product.setVat(rs.getDouble("Vat"));
            productCache.put(id, product);
            return product;
        }
        return null;
    }
    
    /**
     * Gets a list of {@link Item} objects for a specific invoice.
     *
     * @param invoiceid the invoice id
     * @return the items
     * @throws SQLException the SQL exception
     */
    public List<Item> getItems(int invoiceid) throws SQLException {
        List<Item> items = new ArrayList<Item>();
        getItems.setInt(1, invoiceid);
        ResultSet rs = getItems.executeQuery();
        while (rs.next()) {
            items.add(getItem(rs));
        }
        return items;
    }
}
Product.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;
 
/**
 * Plain Old Java Object containing info about a Product.
 * @author Bruno Lowagie (iText Software)
 */
public class Product {
    
    /** The id. */
    protected int id;
    
    /** The name. */
    protected String name;
    
    /** The price. */
    protected double price;
    
    /** The vat. */
    protected double vat;
 
    /**
     * Gets the id.
     *
     * @return the id
     */
    public int getId() {
        return id;
    }
 
    /**
     * Sets the id.
     *
     * @param id the new id
     */
    public void setId(int id) {
        this.id = id;
    }
 
    /**
     * Gets the name.
     *
     * @return the name
     */
    public String getName() {
        return name;
    }
 
    /**
     * Sets the name.
     *
     * @param name the new name
     */
    public void setName(String name) {
        this.name = name;
    }
 
    /**
     * Gets the price.
     *
     * @return the price
     */
    public double getPrice() {
        return price;
    }
 
    /**
     * Sets the price.
     *
     * @param price the new price
     */
    public void setPrice(double price) {
        this.price = price;
    }
 
    /**
     * Gets the vat.
     *
     * @return the vat
     */
    public double getVat() {
        return vat;
    }
 
    /**
     * Sets the vat.
     *
     * @param vat the new vat
     */
    public void setVat(double vat) {
        this.vat = vat;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("\t(").append(id).append(")\t").append(name).append("\t").append(price).append("\u20ac\tvat ").append(vat).append("%");
        return sb.toString();
    }
}
PdfInvoicesBasic.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/5-creating-pdf-invoices-basic-profile
 */
package com.itextpdf.zugferd;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
 
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
 
import com.itextpdf.kernel.pdf.filespec.PdfFileSpec;
import org.xml.sax.SAXException;
 
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfOutputIntent;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.licensekey.LicenseKey;
import com.itextpdf.zugferd.data.InvoiceData;
import com.itextpdf.zugferd.exceptions.DataIncompleteException;
import com.itextpdf.zugferd.exceptions.InvalidCodeException;
import com.itextpdf.zugferd.pojo.Invoice;
import com.itextpdf.zugferd.pojo.Item;
import com.itextpdf.zugferd.pojo.PojoFactory;
import com.itextpdf.zugferd.pojo.Product;
import com.itextpdf.zugferd.profiles.IBasicProfile;
 
/**
 * Reads invoice data from a test database and creates ZUGFeRD invoices
 * (Basic profile).
 * @author Bruno Lowagie
 */
public class PdfInvoicesBasic {
    
    /** The pattern of the destination paths. */
    public static final String DEST = "results/zugferd/pdf/basic%05d.pdf";
    
    /** The path to the color profile. */
    public static final String ICC = "resources/color/sRGB_CS_profile.icm";
    
    /** The path to a regular font. */
    public static final String REGULAR = "resources/fonts/OpenSans-Regular.ttf";
    
    /** The path to a bold font. */
    public static final String BOLD = "resources/fonts/OpenSans-Bold.ttf";
    
    /** A <code>String</code> with a newline character. */
    public static final String NEWLINE = "\n";
    
    /**
     * The main method.
     *
     * @param args the arguments
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParserConfigurationException the parser configuration exception
     * @throws SQLException the SQL exception
     * @throws SAXException the SAX exception
     * @throws TransformerException the transformer exception
     * @throws ParseException the parse exception
     * @throws DataIncompleteException the data incomplete exception
     * @throws InvalidCodeException the invalid code exception
     */
    public static void main(String[] args) throws IOException, ParserConfigurationException, SQLException, SAXException, TransformerException, ParseException, DataIncompleteException, InvalidCodeException {
        LicenseKey.loadLicenseFile(System.getenv("ITEXT7_LICENSEKEY") + "/itextkey-html2pdf_typography.xml");
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        PdfInvoicesBasic app = new PdfInvoicesBasic();
        PojoFactory factory = PojoFactory.getInstance();
        List<Invoice> invoices = factory.getInvoices();
        for (Invoice invoice : invoices) {
            app.createPdf(invoice);
        }
        factory.close();
    }
    
    /**
     * Creates a PDF file, given a certain invoice.
     *
     * @param invoice the invoice
     * @throws ParserConfigurationException the parser configuration exception
     * @throws SAXException the SAX exception
     * @throws TransformerException the transformer exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     * @throws DataIncompleteException the data incomplete exception
     * @throws InvalidCodeException the invalid code exception
     */
    public void createPdf(Invoice invoice) throws ParserConfigurationException, SAXException, TransformerException, IOException, ParseException, DataIncompleteException, InvalidCodeException {
        
        String dest = String.format(DEST, invoice.getId());
        
        // Create the XML
        InvoiceData invoiceData = new InvoiceData();
        IBasicProfile basic = invoiceData.createBasicProfileData(invoice);
        InvoiceDOM dom = new InvoiceDOM(basic);
        
        // Create the ZUGFeRD document
        ZugferdDocument pdfDocument = new ZugferdDocument(
                new PdfWriter(dest), ZugferdConformanceLevel.ZUGFeRDBasic,
                new PdfOutputIntent("Custom", "", "http://www.color.org",
                        "sRGB IEC61966-2.1", new FileInputStream(ICC)));
        pdfDocument.addFileAttachment("ZUGFeRD invoice", PdfFileSpec.createEmbeddedFileSpec(
                pdfDocument, dom.toXML(), "ZUGFeRD invoice", "ZUGFeRD-invoice.xml",
                PdfName.ApplicationXml, new PdfDictionary(), PdfName.Alternative));
        
        // Create the document
        Document document = new Document(pdfDocument);
        document.setFont(PdfFontFactory.createFont(REGULAR, true))
                .setFontSize(12);
        PdfFont bold = PdfFontFactory.createFont(BOLD, true);
        
        // Add the header
        document.add(
            new Paragraph()
            .   setTextAlignment(TextAlignment.RIGHT)
                .setMultipliedLeading(1)
                .add(new Text(String.format("%s %s\n", basic.getName(), basic.getId()))
                        .setFont(bold).setFontSize(14))
                .add(convertDate(basic.getDateTime(), "MMM dd, yyyy")));
        // Add the seller and buyer address
        document.add(getAddressTable(basic, bold));
        // Add the line items
        document.add(getLineItemTable(invoice, bold));
        // Add the grand totals
        document.add(getTotalsTable(
                basic.getTaxBasisTotalAmount(), basic.getTaxTotalAmount(), basic.getGrandTotalAmount(), basic.getGrandTotalAmountCurrencyID(),
                basic.getTaxTypeCode(), basic.getTaxApplicablePercent(),
                basic.getTaxBasisAmount(), basic.getTaxCalculatedAmount(), basic.getTaxCalculatedAmountCurrencyID(), bold));
        // Add the payment info
        document.add(getPaymentInfo(basic.getPaymentReference(), basic.getPaymentMeansPayeeFinancialInstitutionBIC(), basic.getPaymentMeansPayeeAccountIBAN()));
        
        document.close();
    }
 
    /**
     * Convert a date to a String in a certain format.
     *
     * @param d the date
     * @param newFormat the new format
     * @return the date as a string
     * @throws ParseException the parse exception
     */
    public String convertDate(Date d, String newFormat) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(newFormat);
        return sdf.format(d);
    }
    
    /**
     * Gets the address table.
     *
     * @param basic the {@link IBasicProfile} instance
     * @param bold a bold font
     * @return the address table
     */
    public Table getAddressTable(IBasicProfile basic, PdfFont bold) {
        Table table = new Table(new UnitValue[]{
                new UnitValue(UnitValue.PERCENT, 50),
                new UnitValue(UnitValue.PERCENT, 50)})
                .setWidth(UnitValue.createPercentValue(100));
        table.addCell(getPartyAddress("From:",
                basic.getSellerName(),
                basic.getSellerLineOne(),
                basic.getSellerLineTwo(),
                basic.getSellerCountryID(),
                basic.getSellerPostcode(),
                basic.getSellerCityName(),
                bold));
        table.addCell(getPartyAddress("To:",
                basic.getBuyerName(),
                basic.getBuyerLineOne(),
                basic.getBuyerLineTwo(),
                basic.getBuyerCountryID(),
                basic.getBuyerPostcode(),
                basic.getBuyerCityName(),
                bold));
        table.addCell(getPartyTax(basic.getSellerTaxRegistrationID(),
                basic.getSellerTaxRegistrationSchemeID(), bold));
        table.addCell(getPartyTax(basic.getBuyerTaxRegistrationID(),
                basic.getBuyerTaxRegistrationSchemeID(), bold));
        return table;
    }
    
    /**
     * Gets the party address.
     *
     * @param who either "To:" or "From:"
     * @param name the addressee
     * @param line1 line 1 of he address
     * @param line2 line 2 of the address
     * @param countryID the country ID
     * @param postcode the post code
     * @param city the city
     * @param bold a bold font
     * @return a formatted address cell
     */
    public Cell getPartyAddress(String who, String name, String line1, String line2, String countryID, String postcode, String city, PdfFont bold) {
        Paragraph p = new Paragraph()
                .setMultipliedLeading(1.0f)
                .add(new Text(who).setFont(bold)).add(NEWLINE)
                .add(name).add(NEWLINE)
                .add(line1).add(NEWLINE)
                .add(line2).add(NEWLINE)
                .add(String.format("%s-%s %s", countryID, postcode, city));
        Cell cell = new Cell()
                .setBorder(Border.NO_BORDER)
                .add(p);
        return cell;
    }
    
    /**
     * Gets the party tax.
     *
     * @param taxId the tax id
     * @param taxSchema the tax schema
     * @param bold a bold font
     * @return a formatted cell
     */
    public Cell getPartyTax(String[] taxId, String[] taxSchema, PdfFont bold) {
        Paragraph p = new Paragraph()
            .setFontSize(10).setMultipliedLeading(1.0f)
            .add(new Text("Tax ID(s):").setFont(bold));
        if (taxId.length == 0) {
            p.add("\nNot applicable");
        }
        else {
            int n = taxId.length;
            for (int i = 0; i < n; i++) {
                p.add(NEWLINE)
                .add(String.format("%s: %s", taxSchema[i], taxId[i]));
            }
        }
        return new Cell().setBorder(Border.NO_BORDER).add(p);
    }
    
    /**
     * Gets the line item table.
     *
     * @param invoice the invoice
     * @param bold a bold font
     * @return the line item table
     */
    public Table getLineItemTable(Invoice invoice, PdfFont bold) {
        Table table = new Table(new UnitValue[]{
                new UnitValue(UnitValue.PERCENT, 43.75f),
                new UnitValue(UnitValue.PERCENT, 12.5f),
                new UnitValue(UnitValue.PERCENT, 6.25f),
                new UnitValue(UnitValue.PERCENT, 12.5f),
                new UnitValue(UnitValue.PERCENT, 12.5f),
                new UnitValue(UnitValue.PERCENT, 12.5f)})
                .setWidth(UnitValue.createPercentValue(100))
                .setMarginTop(10).setMarginBottom(10);
        table.addHeaderCell(createCell("Item:", bold));
        table.addHeaderCell(createCell("Price:", bold));
        table.addHeaderCell(createCell("Qty:", bold));
        table.addHeaderCell(createCell("Subtotal:", bold));
        table.addHeaderCell(createCell("VAT:", bold));
        table.addHeaderCell(createCell("Total:", bold));
        Product product;
        for (Item item : invoice.getItems()) {
            product = item.getProduct();
            table.addCell(createCell(product.getName()));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(product.getPrice())))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(String.valueOf(item.getQuantity()))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(item.getCost())))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(product.getVat())))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(
                    item.getCost() + ((item.getCost() * product.getVat()) / 100))))
                .setTextAlignment(TextAlignment.RIGHT));
        }
        return table;
    }
    
    /**
     * Creates a cell with specific properties set.
     *
     * @param text the text that will be in the cell
     * @return the cell
     */
    
    public Cell createCell(String text) {
        return new Cell().setPadding(0.8f)
            .add(new Paragraph(text)
                .setMultipliedLeading(1));
    }
    
    /**
     * Creates a cell with specific properties set.
     *
     * @param text the text that will be in the cell
     * @param font the font
     * @return the cell
     */
    public Cell createCell(String text, PdfFont font) {
        return new Cell().setPadding(0.8f)
            .add(new Paragraph(text)
                .setFont(font).setMultipliedLeading(1));
    }
    
    /**
     * Gets the totals table.
     *
     * @param tBase the total tax base
     * @param tTax the total tax amount
     * @param tTotal the total tax
     * @param tCurrency the tax currency
     * @param type the tax types
     * @param percentage the tax percentages
     * @param base the base amounts
     * @param tax the tax amounts
     * @param currency the currencies
     * @param bold a bold font
     * @return the totals table
     */
    public Table getTotalsTable(String tBase, String tTax, String tTotal, String tCurrency,
            String[] type, String[] percentage, String base[], String tax[], String currency[],
            PdfFont bold) {
        Table table = new Table(new UnitValue[]{
                new UnitValue(UnitValue.PERCENT, 8.33f),
                new UnitValue(UnitValue.PERCENT, 8.33f),
                new UnitValue(UnitValue.PERCENT, 25f),
                new UnitValue(UnitValue.PERCENT, 25f),
                new UnitValue(UnitValue.PERCENT, 25f),
                new UnitValue(UnitValue.PERCENT, 8.34f)})
            .setWidth(UnitValue.createPercentValue(100));
        table.addCell(createCell("TAX:", bold));
        table.addCell(createCell("%", bold)
            .setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell("Base amount:", bold));
        table.addCell(createCell("Tax amount:", bold));
        table.addCell(createCell("Total:", bold));
        table.addCell(createCell("Curr.:", bold));
        int n = type.length;
        for (int i = 0; i < n; i++) {
            table.addCell(createCell(type[i])
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(percentage[i])
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(base[i])
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(tax[i])
                .setTextAlignment(TextAlignment.RIGHT));
            double total = Double.parseDouble(base[i]) + Double.parseDouble(tax[i]);
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(total)))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(currency[i]));
        }
        table.addCell(new Cell(1, 2).setBorder(Border.NO_BORDER));
        table.addCell(createCell(tBase, bold)
            .setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell(tTax, bold)
            .setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell(tTotal, bold)
            .setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell(tCurrency, bold));
        return table;
    }
    
    /**
     * Gets the payment info.
     *
     * @param ref the reference
     * @param bic the BIC code
     * @param iban the IBAN code
     * @return the payment info
     */
    public Paragraph getPaymentInfo(String ref, String[] bic, String[] iban) {
        Paragraph p = new Paragraph(String.format(
                "Please wire the amount due to our bank account using the following reference: %s",
                ref));
        int n = bic.length;
        for (int i = 0; i < n; i++) {
            p.add(NEWLINE).add(String.format("BIC: %s - IBAN: %s", bic[i], iban[i]));
        }
        return p;
    }
}
InvoiceData.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
 * Code written by Bruno Lowagie in the context of an example for the ZUGFeRD book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/
 */
package com.itextpdf.zugferd.data;
 
import java.util.Map;
import java.util.TreeMap;
 
import com.itextpdf.zugferd.pojo.Customer;
import com.itextpdf.zugferd.pojo.Invoice;
import com.itextpdf.zugferd.pojo.Item;
import com.itextpdf.zugferd.profiles.BasicProfileImp;
import com.itextpdf.zugferd.profiles.ComfortProfileImp;
import com.itextpdf.zugferd.profiles.IBasicProfile;
import com.itextpdf.zugferd.profiles.IComfortProfile;
import com.itextpdf.zugferd.validation.basic.DateFormatCode;
import com.itextpdf.zugferd.validation.basic.DocumentTypeCode;
import com.itextpdf.zugferd.validation.basic.TaxIDTypeCode;
import com.itextpdf.zugferd.validation.basic.TaxTypeCode;
import com.itextpdf.zugferd.validation.comfort.FreeTextSubjectCode;
import com.itextpdf.zugferd.validation.comfort.PaymentMeansCode;
import com.itextpdf.zugferd.validation.comfort.TaxCategoryCode;
 
/**
 * Class in which we can store all the data of an {@link Invoice} object.
 *
 * @author Bruno Lowagie (iText Software)
 */
public class InvoiceData {
    
    /**
     * Creates a new {@link InvoiceData} instance.
     */
    public InvoiceData() {
    }
    
    /**
     * Creates a object that implements the {@link IBasicProfile} interface,
     * given a specific {@link Invoice} object.
     *
     * @param invoice the invoice
     * @return the {@link IBasicProfile} implementation
     */
    public IBasicProfile createBasicProfileData(Invoice invoice) {
        BasicProfileImp profileImp = new BasicProfileImp(true);
        importData(profileImp, invoice);
        importBasicData(profileImp, invoice);
        return profileImp;
    }
    
    /**
     * Creates a object that implements the {@link IComfortProfile} interface,
     * given a specific {@link Invoice} object.
     *
     * @param invoice the invoice
     * @return the {@link IComfortProfile} implementation
     */
    public IComfortProfile createComfortProfileData(Invoice invoice) {
        ComfortProfileImp profileImp = new ComfortProfileImp(true);
        importData(profileImp, invoice);
        importComfortData(profileImp, invoice);
        return profileImp;
    }
   
    /**
     * Imports the data from an invoice into a {@link BasicProfileImp} instance.
     *
     * @param profileImp the {IBasicProfile} implementation
     * @param invoice the invoice
     */
    public void importData(BasicProfileImp profileImp, Invoice invoice) {
        profileImp.setTest(true);
        profileImp.setId(String.format("I/%05d", invoice.getId()));
        profileImp.setName("INVOICE");
        profileImp.setTypeCode(DocumentTypeCode.COMMERCIAL_INVOICE);
        profileImp.setDate(invoice.getInvoiceDate(), DateFormatCode.YYYYMMDD);
        profileImp.setSellerName("Das Company");
        profileImp.setSellerLineOne("ZUG Business Center");
        profileImp.setSellerLineTwo("Highway 1");
        profileImp.setSellerPostcode("9000");
        profileImp.setSellerCityName("Ghent");
        profileImp.setSellerCountryID("BE");
        profileImp.addSellerTaxRegistration(TaxIDTypeCode.FISCAL_NUMBER, "201/113/40209");
        profileImp.addSellerTaxRegistration(TaxIDTypeCode.VAT, "BE123456789");
        Customer customer = invoice.getCustomer();
        profileImp.setBuyerName(String.format("%s, %s", customer.getLastName(), customer.getFirstName()));
        profileImp.setBuyerPostcode(customer.getPostalcode());
        profileImp.setBuyerLineOne(customer.getStreet());
        profileImp.setBuyerLineTwo("");
        profileImp.setBuyerCityName(customer.getCity());
        profileImp.setBuyerCountryID(customer.getCountryId());
        profileImp.setPaymentReference(String.format("%09d", invoice.getId()));
        profileImp.setInvoiceCurrencyCode("EUR");
    }
    
    /**
     * Import the basic data into a {@link BasicProfileImp} instance.
     *
     * @param profileImp the {IBasicProfile} implementation
     * @param invoice the invoice
     */
    public void importBasicData(BasicProfileImp profileImp, Invoice invoice) {
        profileImp.addNote(
            new String[]{"This is a test invoice.\nNothing on this invoice is real.\nThis invoice is part of a tutorial."});
        profileImp.addPaymentMeans("", "", "BE 41 7360 0661 9710", "", "", "KREDBEBB", "", "KBC");
        profileImp.addPaymentMeans("", "", "BE 56 0015 4298 7888", "", "", "GEBABEBB", "", "BNP Paribas");
        Map<Double,Double> taxes = new TreeMap<Double, Double>();
        double tax;
        for (Item item : invoice.getItems()) {
            tax = item.getProduct().getVat();
            if (taxes.containsKey(tax)) {
                taxes.put(tax, taxes.get(tax) + item.getCost());
            }
            else {
                taxes.put(tax, item.getCost());
            }
            profileImp.addIncludedSupplyChainTradeLineItem(format4dec(item.getQuantity()), "C62", item.getProduct().getName());
        }
        double total, tA;
        double ltN = 0;
        double ttA = 0;
        double gtA = 0;
        for (Map.Entry<Double, Double> t : taxes.entrySet()) {
            tax = t.getKey();
            total = round(t.getValue());
            gtA += total;
            tA = round((100 * total) / (100 + tax));
            ttA += (total - tA);
            ltN += tA;
            profileImp.addApplicableTradeTax(format2dec(total - tA), "EUR", TaxTypeCode.VALUE_ADDED_TAX, format2dec(tA), "EUR", format2dec(tax));
        }
        profileImp.setMonetarySummation(format2dec(ltN), "EUR",
            format2dec(0), "EUR",
            format2dec(0), "EUR",
            format2dec(ltN), "EUR",
            format2dec(ttA), "EUR",
            format2dec(gtA), "EUR");
    }
   
    /**
     * Import comfort data into a {@link ComfortProfileImp} instance.
     *
     * @param profileImp the {IComfortProfile} implementation
     * @param invoice the invoice
     */
    public void importComfortData(ComfortProfileImp profileImp, Invoice invoice) {
        profileImp.addNote(
            new String[]{"This is a test invoice.\nNothing on this invoice is real.\nThis invoice is part of a tutorial."},
            FreeTextSubjectCode.REGULATORY_INFORMATION);
        profileImp.addPaymentMeans(
                PaymentMeansCode.PAYMENT_TO_BANK_ACCOUNT,
                new String[]{"This is the preferred bank account."},
                "", "",
                "", "",
                "BE 41 7360 0661 9710", "", "",
                "", "", "",
                "KREDBEBB", "", "KBC");
        profileImp.addPaymentMeans(
                PaymentMeansCode.PAYMENT_TO_BANK_ACCOUNT,
                new String[]{"Use this as an alternative account."},
                "", "",
                "", "",
                "BE 56 0015 4298 7888", "", "",
                "", "", "",
                "GEBABEBB", "", "BNP Paribas");
        Map<Double,Double> taxes = new TreeMap<Double, Double>();
        double tax;
        int counter = 0;
        for (Item item : invoice.getItems()) {
            counter++;
            tax = item.getProduct().getVat();
            if (taxes.containsKey(tax)) {
                taxes.put(tax, taxes.get(tax) + item.getCost());
            }
            else {
                taxes.put(tax, item.getCost());
            }
            profileImp.addIncludedSupplyChainTradeLineItem(
                    String.valueOf(counter),
                    null,
                    format4dec(item.getProduct().getPrice()), "EUR", null, null,
                    null, null, null, null,
                    null, null, null, null,
                    format4dec(item.getQuantity()), "C62",
                    new String[]{TaxTypeCode.VALUE_ADDED_TAX},
                    new String[1],
                    new String[]{TaxCategoryCode.STANDARD_RATE},
                    new String[]{format2dec(item.getProduct().getVat())},
                    format2dec(item.getCost()), "EUR",
                    null, null,
                    String.valueOf(item.getProduct().getId()), null,
                    item.getProduct().getName(), null
            );
        }
        double total, tA;
        double ltN = 0;
        double ttA = 0;
        double gtA = 0;
        for (Map.Entry<Double, Double> t : taxes.entrySet()) {
            tax = t.getKey();
            total = round(t.getValue());
            gtA += total;
            tA = round((100 * total) / (100 + tax));
            ttA += (total - tA);
            ltN += tA;
            profileImp.addApplicableTradeTax(
                    format2dec(total - tA), "EUR", TaxTypeCode.VALUE_ADDED_TAX,
                    null, format2dec(tA), "EUR",
                    TaxCategoryCode.STANDARD_RATE, format2dec(tax));
        }
        profileImp.setMonetarySummation(format2dec(ltN), "EUR",
            format2dec(0), "EUR",
            format2dec(0), "EUR",
            format2dec(ltN), "EUR",
            format2dec(ttA), "EUR",
            format2dec(gtA), "EUR");
    }
    
    /**
     * Round a double value.
     *
     * @param d the double value
     * @return the rounded double
     */
    public static double round(double d) {
        d = d * 100;
        long tmp = Math.round(d);
        return (double) tmp / 100;
    }
    
    /**
     * Format a double so that it has 2 decimals.
     *
     * @param d the double value
     * @return a string representation of the double value
     */
    public static String format2dec(double d) {
        return String.format("%.2f", d);
    }
    
    /**
     * Format a double so that it has 4 decimals.
     *
     * @param d the double value
     * @return a string representation of the double value
     */
    public static String format4dec(double d) {
        return String.format("%.4f", d);
    }
    
}


Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now