]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.export.core/src/org/simantics/export/core/pdf/ExportPdfWriter.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.export.core / src / org / simantics / export / core / pdf / ExportPdfWriter.java
index 6318e5de2d5bdb5d90cb40e274504a8b2b1df71d..204763b687175b215e1a31531d236e54629e1bb3 100644 (file)
-package org.simantics.export.core.pdf;\r
-\r
-import java.awt.Graphics2D;\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.security.KeyStore;\r
-import java.security.KeyStoreException;\r
-import java.security.NoSuchAlgorithmException;\r
-import java.security.PrivateKey;\r
-import java.security.UnrecoverableKeyException;\r
-import java.security.cert.Certificate;\r
-import java.security.cert.CertificateException;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.simantics.Simantics;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.export.core.ExportContext;\r
-import org.simantics.export.core.error.ExportException;\r
-import org.simantics.export.core.intf.Format;\r
-import org.simantics.export.core.manager.Content;\r
-import org.simantics.utils.page.MarginUtils.Margins;\r
-import org.simantics.utils.page.PageDesc;\r
-import org.simantics.utils.page.PageOrientation;\r
-\r
-import com.lowagie.text.Document;\r
-import com.lowagie.text.DocumentException;\r
-import com.lowagie.text.Element;\r
-import com.lowagie.text.ExceptionConverter;\r
-import com.lowagie.text.Font;\r
-import com.lowagie.text.PageSize;\r
-import com.lowagie.text.Phrase;\r
-import com.lowagie.text.Rectangle;\r
-import com.lowagie.text.pdf.AcroFields;\r
-import com.lowagie.text.pdf.BadPdfFormatException;\r
-import com.lowagie.text.pdf.ColumnText;\r
-import com.lowagie.text.pdf.FontMapper;\r
-import com.lowagie.text.pdf.PdfContentByte;\r
-import com.lowagie.text.pdf.PdfCopy;\r
-import com.lowagie.text.pdf.PdfDictionary;\r
-import com.lowagie.text.pdf.PdfFileSpecification;\r
-import com.lowagie.text.pdf.PdfImportedPage;\r
-import com.lowagie.text.pdf.PdfReader;\r
-import com.lowagie.text.pdf.PdfSignatureAppearance;\r
-import com.lowagie.text.pdf.PdfStamper;\r
-import com.lowagie.text.pdf.PdfTemplate;\r
-import com.lowagie.text.pdf.PdfWriter;\r
-\r
-/**\r
- * A PDF writer object.  \r
- *\r
- * @author toni.kalajainen@semantum.fi\r
- */\r
-public class ExportPdfWriter {\r
-\r
-       /** PDF Document */\r
-       public Document document;\r
-\r
-       /** PDF Output stream */\r
-       public PdfCopy pdfCopy; \r
-       \r
-       /** Open output stream */\r
-       public FileOutputStream fos;\r
-\r
-       /** The direct content byte of the document */\r
-       public PdfContentByte cb;\r
-       \r
-       /** Contains Pdf Templates, e.g. symbols. Resource Uri -> Template mapping */\r
-       public Map<String, PdfTemplate> templates = new HashMap<String, PdfTemplate>();\r
-       \r
-       /** Pages */\r
-       public List<Page> pages = new ArrayList<Page>(); \r
-       \r
-       /** Suggested Page Desc */\r
-       public PageDesc defaultPageDesc;\r
-       \r
-       /** PageDesc as PDF rectangle */\r
-       public Rectangle defaultRectangle;\r
-       \r
-       /** Initialized FontMapper */\r
-       public FontMapper fontMapper;\r
-       \r
-       /** The output file */\r
-       public File outputFile;\r
-       \r
-       /** All options */\r
-       public Variant options;\r
-       \r
-       /** Export Context */\r
-       public ExportContext ctx;\r
-       \r
-       /** The margins the user selected. */\r
-       public Margins margins;\r
-       \r
-       /** Compression Level */\r
-       public int compressionLevel;\r
-\r
-       /**\r
-        * Create new page.\r
-        * \r
-        * @param (Optional) page description. If null is used, the default size is used.\r
-        * @return Page for writing\r
-        * @throws ExportException\r
-        */\r
-       public Page createPage( PageDesc pd ) throws ExportException {\r
-                               \r
-               Rectangle rect; \r
-               if ( pd == null || pd.isInfinite() ) {\r
-                       pd = defaultPageDesc;\r
-                       rect = defaultRectangle;\r
-               } else {\r
-                       rect = toRectangle( pd );\r
-               }\r
-               \r
-               Page page = new Page( pd, rect, pages.size() );                         \r
-               pages.add(page);                \r
-               return page;\r
-       }\r
-       \r
-       /**\r
-        * Create a new template.\r
-        * \r
-        * Note, template is not visible on the document until you add it with \r
-        *   cb.addTemplate( template.tp, 0, 0);\r
-        *  \r
-        * @param name (Optional) Template identifier \r
-        * @param pd (Optional) template size description. If null is used, the default size is used.\r
-        * @return Template handle\r
-        * @throws ExportException\r
-        */\r
-       public Template createTemplate( String name, PageDesc pd ) throws ExportException {\r
-               Rectangle rect; \r
-               if ( pd == null || pd.isInfinite() ) {\r
-                       pd = defaultPageDesc;\r
-                       rect = defaultRectangle;\r
-               } else {\r
-                       rect = toRectangle( pd );\r
-               }\r
-               \r
-        int w = (int) pd.getWidth();\r
-        int h = (int) pd.getHeight();\r
-        PdfTemplate tp = cb.createTemplate(w, h);        \r
-               Template canvas = new Template( name, pd, rect, tp );\r
-               canvas.name = name;\r
-        if ( name!=null ) templates.put(name, tp);             \r
-               return canvas;\r
-       }       \r
-       \r
-       /**\r
-        * Sign the file with a private+public key pair (PPK).\r
-        * The file must be closed already. \r
-        * \r
-        * @param keystoreFile the keystore file\r
-        * @param keystorePassword (optional) password \r
-        * @param privateKeyPassword (optional) password\r
-        * @param signLocation (optional) sign locaiton, e.g. "Helsinki"\r
-        * @param signReason (optional) e.g. "approved"\r
-        * @throws ExportException \r
-        */\r
-       public void sign( File keystoreFile, String keystorePassword, String privateKeyPassword, String signLocation, String signReason) throws ExportException {\r
-               // Add Bouncycastle, if found. If not, try anyway.\r
-               /*\r
-        if (providerAdded.compareAndSet(false, true)) {\r
-               try {\r
-                       String className = "org.bouncycastle.jce.provider.BouncyCastleProvider";\r
-                       Class<?> clazz = Class.forName(className);\r
-                       Provider provide = (Provider) clazz.newInstance();\r
-                       Security.addProvider( provide );\r
-               } catch (SecurityException se) {\r
-                       se.printStackTrace();\r
-               } catch (NullPointerException npe) {\r
-                       npe.printStackTrace();\r
-               } catch (ClassNotFoundException e) {\r
-                       e.printStackTrace();\r
-                       } catch (InstantiationException e) {\r
-                               e.printStackTrace();\r
-                       } catch (IllegalAccessException e) {\r
-                               e.printStackTrace();\r
-                       }\r
-        }*/\r
-\r
-        // Sign\r
-               FileInputStream  ksfis = null;\r
-               FileInputStream  fis = null;\r
-               FileOutputStream fos = null;\r
-               File signedFile = null;\r
-               try {\r
-                       KeyStore ks = KeyStore.getInstance("pkcs12");\r
-                       signedFile = new File( outputFile.getCanonicalPath()+".signed" );\r
-                       if (signedFile.exists()) signedFile.delete();\r
-                       ksfis = new FileInputStream(keystoreFile);\r
-                       fis = new FileInputStream(outputFile);\r
-                       fos = new FileOutputStream( signedFile );\r
-                       ks.load(ksfis, keystorePassword != null ? keystorePassword.toCharArray() : null);\r
-                                       \r
-                       List<String> aliases = Collections.list(ks.aliases());\r
-                       String alias = aliases.get(0);\r
-                       PrivateKey key = (PrivateKey)ks.getKey(alias, privateKeyPassword != null ? privateKeyPassword.toCharArray() : null);\r
-                       Certificate[] chain = ks.getCertificateChain(alias);\r
-\r
-                       PdfReader reader = new PdfReader( fis );\r
-                       PdfStamper stp = PdfStamper.createSignature(reader, fos, '\0');\r
-                       PdfSignatureAppearance sap = stp.getSignatureAppearance();\r
-\r
-                       /// Signature\r
-                       String fieldName = "sign"; //signReason==null?"sign":URIUtil.encodeFilename( signReason );\r
-               AcroFields af = stp.getAcroFields();\r
-               AcroFields.Item item = af.getFieldItem(fieldName);\r
-                       if (signReason!=null) sap.setReason( signReason );\r
-                       if (signLocation!=null) sap.setLocation( signLocation );\r
-                       sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);\r
-                       sap.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);\r
-                       sap.setRender(PdfSignatureAppearance.SignatureRenderNameAndDescription);\r
-                       \r
-                       // Make field the signature\r
-                       //sap.setVisibleSignature(fieldName);\r
-                       \r
-                       // Visible signature\r
-               //AcroFields af = stp.getAcroFields();\r
-               //AcroFields.Item item = af.getFieldItem(fieldName);\r
-                       //sap.setVisibleSignature(new Rectangle(0, 0, 100, 10), 0, fieldName);\r
-                                       \r
-                       // comment next line to have an invisible signature\r
-                       //sap.setVisibleSignature(new Rectangle(682, 130, 822, 145), 1, "approved_by");\r
-                                       \r
-                       //stp.getAcroFields().setField(fieldName, "someValue");\r
-                                       \r
-                       stp.close();    \r
-                       reader.close();\r
-                       fis.close();\r
-               } catch (DocumentException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );\r
-               } catch (UnrecoverableKeyException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );\r
-               } catch (NoSuchAlgorithmException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );\r
-               } catch (CertificateException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(),e  );\r
-               } catch (IOException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );\r
-               } catch (KeyStoreException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );\r
-               } finally {\r
-                       if ( ksfis != null ) try { ksfis.close(); } catch (IOException e) {}\r
-                       if ( fis != null ) try { fis.close(); } catch (IOException e) {}\r
-                       if ( fos != null ) try { fos.close(); } catch (IOException e) {}\r
-                                       \r
-                       if ( signedFile!=null && signedFile.exists() && outputFile.exists() ) {\r
-                               outputFile.delete();\r
-                               signedFile.renameTo( outputFile );\r
-                       }\r
-                       \r
-               }\r
-       }\r
-       \r
-       public void addAttachment(Content content) throws ExportException {\r
-               try {\r
-                       if ( content.tmpFile == null ) throw new ExportException("Could not export "+content.filename+", null file.");\r
-                       if ( !content.tmpFile.exists() ) throw new ExportException("Could not export "+content.filename+", file not found.");\r
-       \r
-                       Format format = ctx.eep.getFormat( content.formatId );\r
-                       //byte[] data = StreamUtil.readFully( content.tmpFile );\r
-                   PdfDictionary fileParameter = new PdfDictionary();      \r
-           PdfFileSpecification spec = PdfFileSpecification.fileEmbedded(\r
-                       pdfCopy,\r
-                       content.tmpFile.getAbsolutePath(),\r
-                       content.filename, \r
-                       null, \r
-                       true, \r
-                       "application/simantics/"+format.id(), \r
-                       fileParameter);\r
-           \r
-           pdfCopy.addFileAttachment( content.filename, spec );            \r
-           \r
-                       \r
-               } catch (IOException e) {\r
-                       throw new ExportException( e.getClass().getName()+": "+e.getMessage() );\r
-               }\r
-       }\r
-       \r
-       public void close() throws ExportException {\r
-               // Flush & close\r
-               try {\r
-                       if ( pages.isEmpty() ) {\r
-                               Page page = createPage(null);                           \r
-                               Graphics2D g2d = page.createGraphics(true);\r
-                               try {\r
-                                       g2d.drawString("This page is intentionally left blank.", 100, 100);\r
-                               } finally {\r
-                                       g2d.dispose();\r
-                               }\r
-                       }\r
-                       \r
-                       for ( Page page : pages ) page.close();\r
-                       \r
-                       Font f = new Font(Font.HELVETICA, 8);\r
-\r
-                       int totalPages = 0;\r
-                       int currentPage = 1;\r
-                       \r
-                       for ( Page page : pages ) {\r
-                               PdfReader reader = new PdfReader( page.tmpFile.getAbsolutePath() );\r
-                               try {\r
-                               totalPages += reader.getNumberOfPages();\r
-                               } finally {\r
-                                       reader.close();\r
-                               }\r
-                       }\r
-                       \r
-                       for ( Page page : pages ) {\r
-                               PdfReader reader = new PdfReader( page.tmpFile.getAbsolutePath() );\r
-                               try {\r
-                               int n = reader.getNumberOfPages();\r
-\r
-                               for (int i = 0; i < n; ) {\r
-                                   Rectangle pageSize = reader.getPageSizeWithRotation(n);\r
-\r
-                                   PdfImportedPage imp = pdfCopy.getImportedPage(reader, ++i);\r
-                                   PdfCopy.PageStamp ps = pdfCopy.createPageStamp(imp);\r
-\r
-                                   PdfContentByte over = ps.getOverContent();\r
-\r
-                                   ColumnText.showTextAligned(over, Element.ALIGN_RIGHT,\r
-                                               new Phrase(\r
-                                                               String.format("%d / %d", currentPage++, totalPages), f),\r
-                                                               pageSize.getWidth()-12, 12, 0);\r
-                                   ps.alterContents();\r
-                                   pdfCopy.addPage(imp);\r
-                                   \r
-                               }\r
-                               } finally {\r
-                                       reader.close();\r
-                               }\r
-               }                                               \r
-               } catch (IOException e) {\r
-                       throw new ExportException( e );\r
-               } catch (ExceptionConverter e) {\r
-                       throw new ExportException( e );\r
-               } catch (BadPdfFormatException e) {\r
-                       throw new ExportException( e );\r
-               } catch (Exception e) {\r
-                       throw new ExportException( e );\r
-               } finally {\r
-                       for ( Page page : pages ) {\r
-                               if ( page.tmpFile != null ) { page.tmpFile.delete(); page.tmpFile = null; }                             \r
-                       }\r
-                       pages.clear();\r
-                       \r
-                       if ( document != null ) { document.close(); document = null; }\r
-                       if ( pdfCopy != null ) { pdfCopy.close(); pdfCopy = null; }\r
-                       if ( fos != null ) { try {\r
-                               fos.close();\r
-                       } catch (IOException e) {\r
-                               throw new ExportException(e);\r
-                       } fos = null; }\r
-               }\r
-       }\r
-       \r
-       public class Page {\r
-\r
-               /** PDF Output stream */\r
-               public PdfWriter pdfWriter;\r
-               \r
-               /** PDF Document */\r
-               public Document document;\r
-               \r
-               /** Open output stream */\r
-               public FileOutputStream fos;\r
-\r
-               /** The direct content byte of the document */\r
-               public PdfContentByte cb;\r
-               \r
-               /** Tmp-file where the page is written to */\r
-               public File tmpFile;\r
-               \r
-               /** Suggested Page Desc */\r
-               public PageDesc pageDesc;\r
-               \r
-               /** PageDesc as PDF rectangle */\r
-               public Rectangle rectangle;\r
-               \r
-               /** Page number */\r
-               public int pageNumber;\r
-               \r
-               Page(PageDesc pageDesc, Rectangle rect, int pageNumber) throws ExportException {\r
-                       try {\r
-                               this.pageDesc = pageDesc;\r
-                               this.rectangle = rect;\r
-                               this.pageNumber = pageNumber;\r
-                               this.tmpFile = Simantics.getTempfile("export.core", "pdf");\r
-                               this.fos = new FileOutputStream( tmpFile, false );                      \r
-                               this.document = new Document(rectangle);                        \r
-                               this.document.setPageSize( rect ); // redundant?\r
-                               this.pdfWriter = PdfWriter.getInstance(document, fos);\r
-                               this.pdfWriter.setPdfVersion(PdfWriter.PDF_VERSION_1_7);\r
-                               this.pdfWriter.setCompressionLevel( compressionLevel );\r
-                               this.pdfWriter.setPageEvent(new ServiceBasedPdfExportPageEvent());\r
-                               this.document.open();\r
-                               this.cb = this.pdfWriter.getDirectContent();\r
-                               if (!this.document.newPage()) throw new ExportException("Failed to create new page.");\r
-                       } catch (IOException e) {\r
-                               throw new ExportException( e );\r
-                       } catch (DocumentException e) {\r
-                               throw new ExportException( e );\r
-                       }\r
-               }\r
-               \r
-               /**\r
-                * Create a graphics 2d Context that uses millimeters.\r
-                * \r
-                * @param applyMargins top left position of margins is applied\r
-                * @return graphics 2d context\r
-                */\r
-               public Graphics2D createGraphics(boolean applyMargins) {\r
-            float w = rectangle.getWidth();\r
-            float h = rectangle.getHeight();\r
-                       double pw = pageDesc.getOrientedWidth();\r
-                       double ph = pageDesc.getOrientedHeight();\r
-                       Graphics2D g2d = cb.createGraphics(w, h, fontMapper);\r
-                       \r
-                       if ( applyMargins ) {\r
-                               Margins m = pageDesc.getMargins();\r
-\r
-                   double mw = pw - m.left.diagramAbsolute - m.right.diagramAbsolute;\r
-                   double mh = ph - m.top.diagramAbsolute - m.bottom.diagramAbsolute;\r
-                   double sx = m.left.diagramAbsolute;\r
-                   double sy = m.top.diagramAbsolute;\r
-                   \r
-                   // Convert to points\r
-                   mw = PageDesc.toPoints( mw );\r
-                   mh = PageDesc.toPoints( mh );\r
-                   sx = PageDesc.toPoints( sx );\r
-                   sy = PageDesc.toPoints( sy );\r
-                   \r
-                               g2d.translate(sx, sy);\r
-                       }\r
-                       \r
-                       g2d.scale(w/pw, h/ph);          \r
-                       return g2d;\r
-               }\r
-               \r
-               /**\r
-                * Of area inside the margins, return the width of the page in millimeters. \r
-                * \r
-                * @return width (mm)\r
-                */\r
-               public double getWidth() {\r
-                       Margins m = pageDesc.getMargins();\r
-                       return pageDesc.getOrientedWidth() - m.left.diagramAbsolute - m.right.diagramAbsolute; \r
-               }\r
-               \r
-               public double getHeight() {\r
-                       Margins m = pageDesc.getMargins();\r
-                       return pageDesc.getOrientedHeight() - m.top.diagramAbsolute - m.bottom.diagramAbsolute; \r
-               }\r
-               \r
-               /**\r
-                * Add attachment to this page\r
-                * @param content\r
-                * @throws ExportException\r
-                */\r
-               public void addAttachment(Content content) throws ExportException {\r
-                       /*\r
-                       try {\r
-                               if ( content.tmpFile == null ) throw new ExportException("Could not export "+content.filename+", null file.");\r
-                               if ( !content.tmpFile.exists() ) throw new ExportException("Could not export "+content.filename+", file not found.");\r
-               \r
-                               Format format = ctx.eep.getFormat( content.formatId );\r
-                               //byte[] data = StreamUtil.readFully( content.tmpFile );\r
-                           PdfDictionary fileParameter = new PdfDictionary();      \r
-                   PdfFileSpecification spec = PdfFileSpecification.fileEmbedded(\r
-                               pdfWriter,\r
-                               content.tmpFile.getAbsolutePath(),\r
-                               content.filename, \r
-                               null, \r
-                               true, \r
-                               "application/simantics/"+format.id(), \r
-                               fileParameter);\r
-                   \r
-                   pdfWriter.addFileAttachment( content.filename, spec );                                              \r
-                       } catch (IOException e) {\r
-                               throw new ExportException( e.getClass().getName()+": "+e.getMessage() );\r
-                       }*/\r
-                       ExportPdfWriter.this.addAttachment(content);\r
-               }\r
-               \r
-               public void close() throws ExportException {\r
-                       try {\r
-                               if ( document != null ) { document.close(); document = null; }\r
-                               if ( pdfWriter != null ) { pdfWriter.close(); pdfWriter = null; }\r
-                               if ( fos != null ) { fos.close(); fos = null; }\r
-                               if ( cb != null ) { cb = null; }\r
-                       } catch (IOException e) {\r
-                               throw new ExportException(e);\r
-                       }\r
-               }\r
-               \r
-       }\r
-       \r
-       public class Template {\r
-\r
-               /** Suggested Page Desc */\r
-               public PageDesc pageDesc;\r
-               \r
-               /** PageDesc as PDF rectangle */\r
-               public Rectangle rectangle;\r
-               \r
-               /** Template name */\r
-               public String name;\r
-               \r
-               /** PdfTemplate */\r
-               public PdfTemplate tp;\r
-\r
-               Template(String name, PageDesc pd, Rectangle rect, PdfTemplate tp) {\r
-                       this.pageDesc = pd;\r
-                       this.rectangle = rect;\r
-                       this.name = name;\r
-                       this.tp = tp;\r
-               }\r
-               \r
-               public Graphics2D createGraphics() {                    \r
-            double w = pageDesc.getWidth();\r
-            double h = pageDesc.getHeight();\r
-                       return tp.createGraphics((float) w, (float) h, fontMapper);\r
-               }\r
-               \r
-       }\r
-       \r
-    public static Rectangle toRectangle(PageDesc pageDesc) {\r
-        String arg = PageDesc.toPoints(pageDesc.getWidth()) + " " + PageDesc.toPoints(pageDesc.getHeight());\r
-        Rectangle r = PageSize.getRectangle(arg);\r
-\r
-        if (PageOrientation.Landscape == pageDesc.getOrientation())\r
-            r = r.rotate();\r
-\r
-        // Disable inherent borders from the PDF writer.\r
-        r.setBorder(0);\r
-\r
-        return r;\r
-    }\r
-       \r
-}\r
+package org.simantics.export.core.pdf;
+
+import java.awt.Graphics2D;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.simantics.Simantics;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.export.core.ExportContext;
+import org.simantics.export.core.error.ExportException;
+import org.simantics.export.core.intf.Format;
+import org.simantics.export.core.manager.Content;
+import org.simantics.utils.page.MarginUtils.Margins;
+import org.simantics.utils.page.PageDesc;
+import org.simantics.utils.page.PageOrientation;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Font;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.AcroFields;
+import com.lowagie.text.pdf.BadPdfFormatException;
+import com.lowagie.text.pdf.ColumnText;
+import com.lowagie.text.pdf.FontMapper;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfCopy;
+import com.lowagie.text.pdf.PdfDictionary;
+import com.lowagie.text.pdf.PdfFileSpecification;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfSignatureAppearance;
+import com.lowagie.text.pdf.PdfStamper;
+import com.lowagie.text.pdf.PdfTemplate;
+import com.lowagie.text.pdf.PdfWriter;
+
+/**
+ * A PDF writer object.  
+ *
+ * @author toni.kalajainen@semantum.fi
+ */
+public class ExportPdfWriter {
+
+       /** PDF Document */
+       public Document document;
+
+       /** PDF Output stream */
+       public PdfCopy pdfCopy; 
+       
+       /** Open output stream */
+       public FileOutputStream fos;
+
+       /** The direct content byte of the document */
+       public PdfContentByte cb;
+       
+       /** Contains Pdf Templates, e.g. symbols. Resource Uri -> Template mapping */
+       public Map<String, PdfTemplate> templates = new HashMap<String, PdfTemplate>();
+       
+       /** Pages */
+       public List<Page> pages = new ArrayList<Page>(); 
+       
+       /** Suggested Page Desc */
+       public PageDesc defaultPageDesc;
+       
+       /** PageDesc as PDF rectangle */
+       public Rectangle defaultRectangle;
+       
+       /** Initialized FontMapper */
+       public FontMapper fontMapper;
+       
+       /** The output file */
+       public File outputFile;
+       
+       /** All options */
+       public Variant options;
+       
+       /** Export Context */
+       public ExportContext ctx;
+       
+       /** The margins the user selected. */
+       public Margins margins;
+       
+       /** Compression Level */
+       public int compressionLevel;
+
+       /**
+        * Create new page.
+        * 
+        * @param (Optional) page description. If null is used, the default size is used.
+        * @return Page for writing
+        * @throws ExportException
+        */
+       public Page createPage( PageDesc pd ) throws ExportException {
+                               
+               Rectangle rect; 
+               if ( pd == null || pd.isInfinite() ) {
+                       pd = defaultPageDesc;
+                       rect = defaultRectangle;
+               } else {
+                       rect = toRectangle( pd );
+               }
+               
+               Page page = new Page( pd, rect, pages.size() );                         
+               pages.add(page);                
+               return page;
+       }
+       
+       /**
+        * Create a new template.
+        * 
+        * Note, template is not visible on the document until you add it with 
+        *   cb.addTemplate( template.tp, 0, 0);
+        *  
+        * @param name (Optional) Template identifier 
+        * @param pd (Optional) template size description. If null is used, the default size is used.
+        * @return Template handle
+        * @throws ExportException
+        */
+       public Template createTemplate( String name, PageDesc pd ) throws ExportException {
+               Rectangle rect; 
+               if ( pd == null || pd.isInfinite() ) {
+                       pd = defaultPageDesc;
+                       rect = defaultRectangle;
+               } else {
+                       rect = toRectangle( pd );
+               }
+               
+        int w = (int) pd.getWidth();
+        int h = (int) pd.getHeight();
+        PdfTemplate tp = cb.createTemplate(w, h);        
+               Template canvas = new Template( name, pd, rect, tp );
+               canvas.name = name;
+        if ( name!=null ) templates.put(name, tp);             
+               return canvas;
+       }       
+       
+       /**
+        * Sign the file with a private+public key pair (PPK).
+        * The file must be closed already. 
+        * 
+        * @param keystoreFile the keystore file
+        * @param keystorePassword (optional) password 
+        * @param privateKeyPassword (optional) password
+        * @param signLocation (optional) sign locaiton, e.g. "Helsinki"
+        * @param signReason (optional) e.g. "approved"
+        * @throws ExportException 
+        */
+       public void sign( File keystoreFile, String keystorePassword, String privateKeyPassword, String signLocation, String signReason) throws ExportException {
+               // Add Bouncycastle, if found. If not, try anyway.
+               /*
+        if (providerAdded.compareAndSet(false, true)) {
+               try {
+                       String className = "org.bouncycastle.jce.provider.BouncyCastleProvider";
+                       Class<?> clazz = Class.forName(className);
+                       Provider provide = (Provider) clazz.newInstance();
+                       Security.addProvider( provide );
+               } catch (SecurityException se) {
+                       se.printStackTrace();
+               } catch (NullPointerException npe) {
+                       npe.printStackTrace();
+               } catch (ClassNotFoundException e) {
+                       e.printStackTrace();
+                       } catch (InstantiationException e) {
+                               e.printStackTrace();
+                       } catch (IllegalAccessException e) {
+                               e.printStackTrace();
+                       }
+        }*/
+
+        // Sign
+               FileInputStream  ksfis = null;
+               FileInputStream  fis = null;
+               FileOutputStream fos = null;
+               File signedFile = null;
+               try {
+                       KeyStore ks = KeyStore.getInstance("pkcs12");
+                       signedFile = new File( outputFile.getCanonicalPath()+".signed" );
+                       if (signedFile.exists()) signedFile.delete();
+                       ksfis = new FileInputStream(keystoreFile);
+                       fis = new FileInputStream(outputFile);
+                       fos = new FileOutputStream( signedFile );
+                       ks.load(ksfis, keystorePassword != null ? keystorePassword.toCharArray() : null);
+                                       
+                       List<String> aliases = Collections.list(ks.aliases());
+                       String alias = aliases.get(0);
+                       PrivateKey key = (PrivateKey)ks.getKey(alias, privateKeyPassword != null ? privateKeyPassword.toCharArray() : null);
+                       Certificate[] chain = ks.getCertificateChain(alias);
+
+                       PdfReader reader = new PdfReader( fis );
+                       PdfStamper stp = PdfStamper.createSignature(reader, fos, '\0');
+                       PdfSignatureAppearance sap = stp.getSignatureAppearance();
+
+                       /// Signature
+                       String fieldName = "sign"; //signReason==null?"sign":URIUtil.encodeFilename( signReason );
+               AcroFields af = stp.getAcroFields();
+               AcroFields.Item item = af.getFieldItem(fieldName);
+                       if (signReason!=null) sap.setReason( signReason );
+                       if (signLocation!=null) sap.setLocation( signLocation );
+                       sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);
+                       sap.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
+                       sap.setRender(PdfSignatureAppearance.SignatureRenderNameAndDescription);
+                       
+                       // Make field the signature
+                       //sap.setVisibleSignature(fieldName);
+                       
+                       // Visible signature
+               //AcroFields af = stp.getAcroFields();
+               //AcroFields.Item item = af.getFieldItem(fieldName);
+                       //sap.setVisibleSignature(new Rectangle(0, 0, 100, 10), 0, fieldName);
+                                       
+                       // comment next line to have an invisible signature
+                       //sap.setVisibleSignature(new Rectangle(682, 130, 822, 145), 1, "approved_by");
+                                       
+                       //stp.getAcroFields().setField(fieldName, "someValue");
+                                       
+                       stp.close();    
+                       reader.close();
+                       fis.close();
+               } catch (DocumentException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
+               } catch (UnrecoverableKeyException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
+               } catch (NoSuchAlgorithmException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
+               } catch (CertificateException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(),e  );
+               } catch (IOException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
+               } catch (KeyStoreException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
+               } finally {
+                       if ( ksfis != null ) try { ksfis.close(); } catch (IOException e) {}
+                       if ( fis != null ) try { fis.close(); } catch (IOException e) {}
+                       if ( fos != null ) try { fos.close(); } catch (IOException e) {}
+                                       
+                       if ( signedFile!=null && signedFile.exists() && outputFile.exists() ) {
+                               outputFile.delete();
+                               signedFile.renameTo( outputFile );
+                       }
+                       
+               }
+       }
+       
+       public void addAttachment(Content content) throws ExportException {
+               try {
+                       if ( content.tmpFile == null ) throw new ExportException("Could not export "+content.filename+", null file.");
+                       if ( !content.tmpFile.exists() ) throw new ExportException("Could not export "+content.filename+", file not found.");
+       
+                       Format format = ctx.eep.getFormat( content.formatId );
+                       //byte[] data = StreamUtil.readFully( content.tmpFile );
+                   PdfDictionary fileParameter = new PdfDictionary();      
+           PdfFileSpecification spec = PdfFileSpecification.fileEmbedded(
+                       pdfCopy,
+                       content.tmpFile.getAbsolutePath(),
+                       content.filename, 
+                       null, 
+                       true, 
+                       "application/simantics/"+format.id(), 
+                       fileParameter);
+           
+           pdfCopy.addFileAttachment( content.filename, spec );            
+           
+                       
+               } catch (IOException e) {
+                       throw new ExportException( e.getClass().getName()+": "+e.getMessage() );
+               }
+       }
+       
+       public void close() throws ExportException {
+               // Flush & close
+               try {
+                       if ( pages.isEmpty() ) {
+                               Page page = createPage(null);                           
+                               Graphics2D g2d = page.createGraphics(true);
+                               try {
+                                       g2d.drawString("This page is intentionally left blank.", 100, 100);
+                               } finally {
+                                       g2d.dispose();
+                               }
+                       }
+                       
+                       for ( Page page : pages ) page.close();
+                       
+                       Font f = new Font(Font.HELVETICA, 8);
+
+                       int totalPages = 0;
+                       int currentPage = 1;
+                       
+                       for ( Page page : pages ) {
+                               PdfReader reader = new PdfReader( page.tmpFile.getAbsolutePath() );
+                               try {
+                               totalPages += reader.getNumberOfPages();
+                               } finally {
+                                       reader.close();
+                               }
+                       }
+                       
+                       for ( Page page : pages ) {
+                               PdfReader reader = new PdfReader( page.tmpFile.getAbsolutePath() );
+                               try {
+                               int n = reader.getNumberOfPages();
+
+                               for (int i = 0; i < n; ) {
+                                   Rectangle pageSize = reader.getPageSizeWithRotation(n);
+
+                                   PdfImportedPage imp = pdfCopy.getImportedPage(reader, ++i);
+                                   PdfCopy.PageStamp ps = pdfCopy.createPageStamp(imp);
+
+                                   PdfContentByte over = ps.getOverContent();
+
+                                   ColumnText.showTextAligned(over, Element.ALIGN_RIGHT,
+                                               new Phrase(
+                                                               String.format("%d / %d", currentPage++, totalPages), f),
+                                                               pageSize.getWidth()-12, 12, 0);
+                                   ps.alterContents();
+                                   pdfCopy.addPage(imp);
+                                   
+                               }
+                               } finally {
+                                       reader.close();
+                               }
+               }                                               
+               } catch (IOException e) {
+                       throw new ExportException( e );
+               } catch (ExceptionConverter e) {
+                       throw new ExportException( e );
+               } catch (BadPdfFormatException e) {
+                       throw new ExportException( e );
+               } catch (Exception e) {
+                       throw new ExportException( e );
+               } finally {
+                       for ( Page page : pages ) {
+                               if ( page.tmpFile != null ) { page.tmpFile.delete(); page.tmpFile = null; }                             
+                       }
+                       pages.clear();
+                       
+                       if ( document != null ) { document.close(); document = null; }
+                       if ( pdfCopy != null ) { pdfCopy.close(); pdfCopy = null; }
+                       if ( fos != null ) { try {
+                               fos.close();
+                       } catch (IOException e) {
+                               throw new ExportException(e);
+                       } fos = null; }
+               }
+       }
+       
+       public class Page {
+
+               /** PDF Output stream */
+               public PdfWriter pdfWriter;
+               
+               /** PDF Document */
+               public Document document;
+               
+               /** Open output stream */
+               public FileOutputStream fos;
+
+               /** The direct content byte of the document */
+               public PdfContentByte cb;
+               
+               /** Tmp-file where the page is written to */
+               public File tmpFile;
+               
+               /** Suggested Page Desc */
+               public PageDesc pageDesc;
+               
+               /** PageDesc as PDF rectangle */
+               public Rectangle rectangle;
+               
+               /** Page number */
+               public int pageNumber;
+               
+               Page(PageDesc pageDesc, Rectangle rect, int pageNumber) throws ExportException {
+                       try {
+                               this.pageDesc = pageDesc;
+                               this.rectangle = rect;
+                               this.pageNumber = pageNumber;
+                               this.tmpFile = Simantics.getTempfile("export.core", "pdf");
+                               this.fos = new FileOutputStream( tmpFile, false );                      
+                               this.document = new Document(rectangle);                        
+                               this.document.setPageSize( rect ); // redundant?
+                               this.pdfWriter = PdfWriter.getInstance(document, fos);
+                               this.pdfWriter.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
+                               this.pdfWriter.setCompressionLevel( compressionLevel );
+                               this.pdfWriter.setPageEvent(new ServiceBasedPdfExportPageEvent());
+                               this.document.open();
+                               this.cb = this.pdfWriter.getDirectContent();
+                               if (!this.document.newPage()) throw new ExportException("Failed to create new page.");
+                       } catch (IOException e) {
+                               throw new ExportException( e );
+                       } catch (DocumentException e) {
+                               throw new ExportException( e );
+                       }
+               }
+               
+               /**
+                * Create a graphics 2d Context that uses millimeters.
+                * 
+                * @param applyMargins top left position of margins is applied
+                * @return graphics 2d context
+                */
+               public Graphics2D createGraphics(boolean applyMargins) {
+            float w = rectangle.getWidth();
+            float h = rectangle.getHeight();
+                       double pw = pageDesc.getOrientedWidth();
+                       double ph = pageDesc.getOrientedHeight();
+                       Graphics2D g2d = cb.createGraphics(w, h, fontMapper);
+                       
+                       if ( applyMargins ) {
+                               Margins m = pageDesc.getMargins();
+
+                   double mw = pw - m.left.diagramAbsolute - m.right.diagramAbsolute;
+                   double mh = ph - m.top.diagramAbsolute - m.bottom.diagramAbsolute;
+                   double sx = m.left.diagramAbsolute;
+                   double sy = m.top.diagramAbsolute;
+                   
+                   // Convert to points
+                   mw = PageDesc.toPoints( mw );
+                   mh = PageDesc.toPoints( mh );
+                   sx = PageDesc.toPoints( sx );
+                   sy = PageDesc.toPoints( sy );
+                   
+                               g2d.translate(sx, sy);
+                       }
+                       
+                       g2d.scale(w/pw, h/ph);          
+                       return g2d;
+               }
+               
+               /**
+                * Of area inside the margins, return the width of the page in millimeters. 
+                * 
+                * @return width (mm)
+                */
+               public double getWidth() {
+                       Margins m = pageDesc.getMargins();
+                       return pageDesc.getOrientedWidth() - m.left.diagramAbsolute - m.right.diagramAbsolute; 
+               }
+               
+               public double getHeight() {
+                       Margins m = pageDesc.getMargins();
+                       return pageDesc.getOrientedHeight() - m.top.diagramAbsolute - m.bottom.diagramAbsolute; 
+               }
+               
+               /**
+                * Add attachment to this page
+                * @param content
+                * @throws ExportException
+                */
+               public void addAttachment(Content content) throws ExportException {
+                       /*
+                       try {
+                               if ( content.tmpFile == null ) throw new ExportException("Could not export "+content.filename+", null file.");
+                               if ( !content.tmpFile.exists() ) throw new ExportException("Could not export "+content.filename+", file not found.");
+               
+                               Format format = ctx.eep.getFormat( content.formatId );
+                               //byte[] data = StreamUtil.readFully( content.tmpFile );
+                           PdfDictionary fileParameter = new PdfDictionary();      
+                   PdfFileSpecification spec = PdfFileSpecification.fileEmbedded(
+                               pdfWriter,
+                               content.tmpFile.getAbsolutePath(),
+                               content.filename, 
+                               null, 
+                               true, 
+                               "application/simantics/"+format.id(), 
+                               fileParameter);
+                   
+                   pdfWriter.addFileAttachment( content.filename, spec );                                              
+                       } catch (IOException e) {
+                               throw new ExportException( e.getClass().getName()+": "+e.getMessage() );
+                       }*/
+                       ExportPdfWriter.this.addAttachment(content);
+               }
+               
+               public void close() throws ExportException {
+                       try {
+                               if ( document != null ) { document.close(); document = null; }
+                               if ( pdfWriter != null ) { pdfWriter.close(); pdfWriter = null; }
+                               if ( fos != null ) { fos.close(); fos = null; }
+                               if ( cb != null ) { cb = null; }
+                       } catch (IOException e) {
+                               throw new ExportException(e);
+                       }
+               }
+               
+       }
+       
+       public class Template {
+
+               /** Suggested Page Desc */
+               public PageDesc pageDesc;
+               
+               /** PageDesc as PDF rectangle */
+               public Rectangle rectangle;
+               
+               /** Template name */
+               public String name;
+               
+               /** PdfTemplate */
+               public PdfTemplate tp;
+
+               Template(String name, PageDesc pd, Rectangle rect, PdfTemplate tp) {
+                       this.pageDesc = pd;
+                       this.rectangle = rect;
+                       this.name = name;
+                       this.tp = tp;
+               }
+               
+               public Graphics2D createGraphics() {                    
+            double w = pageDesc.getWidth();
+            double h = pageDesc.getHeight();
+                       return tp.createGraphics((float) w, (float) h, fontMapper);
+               }
+               
+       }
+       
+    public static Rectangle toRectangle(PageDesc pageDesc) {
+        String arg = PageDesc.toPoints(pageDesc.getWidth()) + " " + PageDesc.toPoints(pageDesc.getHeight());
+        Rectangle r = PageSize.getRectangle(arg);
+
+        if (PageOrientation.Landscape == pageDesc.getOrientation())
+            r = r.rotate();
+
+        // Disable inherent borders from the PDF writer.
+        r.setBorder(0);
+
+        return r;
+    }
+       
+}