Converting HTML invoices into ZUGFeRD PDF/A-3 invoices

This is a code example of iText PDF, discover more.

1st September 2015
admin-marketing

Switch code for this example

PdfInvoicesComfort.java
  1. /*
  2.  * This example was written by Bruno Lowagie.
  3.  */
  4. package zugferd;
  5.  
  6. import com.itextpdf.text.BadElementException;
  7. import com.itextpdf.text.BaseColor;
  8. import com.itextpdf.text.Document;
  9. import com.itextpdf.text.DocumentException;
  10. import com.itextpdf.text.ExceptionConverter;
  11. import com.itextpdf.text.Font;
  12. import com.itextpdf.text.FontProvider;
  13. import com.itextpdf.text.Image;
  14. import com.itextpdf.text.pdf.AFRelationshipValue;
  15. import com.itextpdf.text.pdf.BaseFont;
  16. import com.itextpdf.text.pdf.ICC_Profile;
  17. import com.itextpdf.text.pdf.PdfAConformanceLevel;
  18. import com.itextpdf.text.pdf.PdfAWriter;
  19. import com.itextpdf.text.pdf.PdfArray;
  20. import com.itextpdf.text.pdf.PdfDate;
  21. import com.itextpdf.text.pdf.PdfDictionary;
  22. import com.itextpdf.text.pdf.PdfFileSpecification;
  23. import com.itextpdf.text.pdf.PdfName;
  24. import com.itextpdf.text.pdf.PdfWriter;
  25. import com.itextpdf.text.xml.xmp.PdfAXmpWriter;
  26. import com.itextpdf.text.zugferd.InvoiceDOM;
  27. import com.itextpdf.text.zugferd.exceptions.DataIncompleteException;
  28. import com.itextpdf.text.zugferd.exceptions.InvalidCodeException;
  29. import com.itextpdf.text.zugferd.profiles.ComfortProfile;
  30. import com.itextpdf.tool.xml.XMLWorker;
  31. import com.itextpdf.tool.xml.XMLWorkerHelper;
  32. import com.itextpdf.tool.xml.css.CssFile;
  33. import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
  34. import com.itextpdf.tool.xml.html.CssAppliers;
  35. import com.itextpdf.tool.xml.html.CssAppliersImpl;
  36. import com.itextpdf.tool.xml.html.Tags;
  37. import com.itextpdf.tool.xml.parser.XMLParser;
  38. import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
  39. import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
  40. import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
  41. import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
  42. import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
  43. import com.itextpdf.tool.xml.pipeline.html.ImageProvider;
  44. import com.itextpdf.xmp.XMPException;
  45.  
  46. import java.io.ByteArrayInputStream;
  47. import java.io.ByteArrayOutputStream;
  48. import java.io.File;
  49. import java.io.FileInputStream;
  50. import java.io.FileOutputStream;
  51. import java.io.IOException;
  52. import java.io.OutputStreamWriter;
  53. import java.io.Writer;
  54. import java.sql.SQLException;
  55. import java.util.HashMap;
  56. import java.util.List;
  57. import java.util.Map;
  58.  
  59. import javax.xml.parsers.ParserConfigurationException;
  60. import javax.xml.transform.Transformer;
  61. import javax.xml.transform.TransformerException;
  62. import javax.xml.transform.TransformerFactory;
  63. import javax.xml.transform.stream.StreamResult;
  64. import javax.xml.transform.stream.StreamSource;
  65.  
  66. import org.xml.sax.SAXException;
  67.  
  68. import zugferd.data.InvoiceData;
  69. import zugferd.pojo.Invoice;
  70. import zugferd.pojo.PojoFactory;
  71.  
  72. /**
  73.  * @author Bruno Lowagie
  74.  */
  75. public class PdfInvoicesComfort {
  76.    
  77.     class MyFontProvider implements FontProvider {
  78.  
  79.         protected BaseFont regular;
  80.         protected BaseFont bold;
  81.        
  82.         public MyFontProvider() throws DocumentException, IOException {
  83.             regular = BaseFont.createFont("resources/fonts/OpenSans-Regular.ttf", BaseFont.IDENTITY_H, true);
  84.             bold = BaseFont.createFont("resources/fonts/OpenSans-Bold.ttf", BaseFont.IDENTITY_H, true);
  85.         }
  86.        
  87.         public boolean isRegistered(String fontname) {
  88.             return true;
  89.         }
  90.  
  91.         public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color) {
  92.             Font font;
  93.             switch (style) {
  94.                 case Font.BOLD:
  95.                     font = new Font(bold, size);
  96.                     break;
  97.                 default:
  98.                     font = new Font(regular, size);
  99.             }
  100.             font.setColor(color);
  101.             return font;
  102.         }
  103.     }
  104.    
  105.     class MyImageProvider implements ImageProvider {
  106.  
  107.         protected Map cache = new HashMap();
  108.        
  109.         public Image retrieve(String src) {
  110.             Image img = cache.get(src);
  111.             try {
  112.                 if (img == null) {
  113.                     img = Image.getInstance(getImageRootPath() + src);
  114.                     store(src, img);
  115.                 }
  116.             } catch (BadElementException ex) {
  117.                 throw new ExceptionConverter(ex);
  118.             } catch (IOException ex) {
  119.                 throw new ExceptionConverter(ex);
  120.             }
  121.             return img;
  122.         }
  123.  
  124.         public String getImageRootPath() {
  125.             return "resources/zugferd/";
  126.         }
  127.  
  128.         public void store(String src, Image img) {
  129.             cache.put(src, img);
  130.         }
  131.  
  132.         public void reset() {
  133.             cache = new HashMap();
  134.         }
  135.     }
  136.    
  137.     public static final String DEST = "results/zugferd/pdf/comfort%05d.pdf";
  138.     public static final String XSL = "resources/zugferd/invoice.xsl";
  139.     public static final String CSS = "resources/zugferd/invoice.css";
  140.     public static final String ICC = "resources/data/sRGB_CS_profile.icm";
  141.    
  142.     public static void main(String[] args) throws SQLException, IOException, ParserConfigurationException, SAXException, DataIncompleteException, InvalidCodeException, TransformerException, DocumentException, XMPException {
  143.         File file = new File(DEST);
  144.         file.getParentFile().mkdirs();
  145.         PdfInvoicesComfort app = new PdfInvoicesComfort();
  146.         StreamSource xsl = new StreamSource(new File(XSL));
  147.         TransformerFactory factory = TransformerFactory.newInstance();
  148.         Transformer transformer = factory.newTransformer(xsl);
  149.         ComfortProfile comfort;
  150.         InvoiceData invoiceData = new InvoiceData();
  151.         PojoFactory pojofactory = PojoFactory.getInstance();
  152.         List invoices = pojofactory.getInvoices();
  153.         for (Invoice invoice : invoices) {
  154.             comfort = invoiceData.createComfortProfileData(invoice);
  155.             InvoiceDOM dom = new InvoiceDOM(comfort);
  156.             byte[] xml = dom.toXML();
  157.             app.createPdf(xml, app.createHtml(transformer, xml), String.format(DEST, invoice.getId()));
  158.         }
  159.         pojofactory.close();
  160.     }
  161.    
  162.     public void createPdf(byte[] xml, byte[] invoice, String dest) throws DocumentException, IOException, XMPException {
  163.         // step 1
  164.         Document document = new Document();
  165.         // step 2
  166.         PdfAWriter writer = PdfAWriter.getInstance(document, new FileOutputStream(dest), PdfAConformanceLevel.ZUGFeRDComfort);
  167.         writer.setTagged();
  168.         writer.setPdfVersion(PdfWriter.VERSION_1_7);
  169.         writer.createXmpMetadata();
  170.         writer.getXmpWriter().setProperty(PdfAXmpWriter.zugferdSchemaNS, PdfAXmpWriter.zugferdDocumentFileName, "ZUGFeRD-invoice.xml");
  171.         // step 3
  172.         document.open();
  173.         // step 4
  174.         ICC_Profile icc = ICC_Profile.getInstance(new FileInputStream(ICC));
  175.         writer.setOutputIntents("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", icc);
  176.        
  177.         // CSS
  178.         CSSResolver cssResolver = new StyleAttrCSSResolver();
  179.         CssFile cssFile = XMLWorkerHelper.getCSS(new FileInputStream(CSS));
  180.         cssResolver.addCss(cssFile);
  181.        
  182.         // HTML
  183.         CssAppliers cssAppliers = new CssAppliersImpl(new MyFontProvider());
  184.         HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
  185.         htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
  186.         htmlContext.setImageProvider(new MyImageProvider());
  187.        
  188.         // Pipelines
  189.         PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
  190.         HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
  191.         CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
  192.        
  193.         // XML Worker
  194.         XMLWorker worker = new XMLWorker(css, true);
  195.         XMLParser p = new XMLParser(worker);
  196.         p.parse(new ByteArrayInputStream(invoice));
  197.        
  198.         PdfDictionary parameters = new PdfDictionary();
  199.         parameters.put(PdfName.MODDATE, new PdfDate());
  200.         PdfFileSpecification fileSpec = writer.addFileAttachment(
  201.                 "ZUGFeRD invoice", xml, null,
  202.                 "ZUGFeRD-invoice.xml", "application/xml",
  203.                 AFRelationshipValue.Alternative, parameters);
  204.         PdfArray array = new PdfArray();
  205.         array.add(fileSpec.getReference());
  206.         writer.getExtraCatalog().put(PdfName.AF, array);
  207.         // step 5
  208.         document.close();
  209.     }
  210.    
  211.     public byte[] createHtml(Transformer transformer, byte[] comfort) throws IOException, ParserConfigurationException, SAXException, DataIncompleteException, InvalidCodeException, TransformerException {
  212.         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  213.         StreamSource xml = new StreamSource(new ByteArrayInputStream(comfort));
  214.         Writer writer = new OutputStreamWriter(baos);
  215.         transformer.transform(xml, new StreamResult(writer));
  216.         writer.flush();
  217.         writer.close();
  218.         return baos.toByteArray();
  219.     }
  220. }
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