/*
* 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 String
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 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;
}
}