]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/DiagramPrinter.java
ccdf57fbe89f8ec655bef4fb1dd3b5b5f7ed8986
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / pdf / DiagramPrinter.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.modeling.ui.pdf;\r
13 \r
14 import java.io.FileNotFoundException;\r
15 import java.io.FileOutputStream;\r
16 import java.security.Security;\r
17 import java.util.Collection;\r
18 import java.util.concurrent.atomic.AtomicBoolean;\r
19 \r
20 import org.eclipse.core.runtime.IProduct;\r
21 import org.eclipse.core.runtime.IProgressMonitor;\r
22 import org.eclipse.core.runtime.OperationCanceledException;\r
23 import org.eclipse.core.runtime.Platform;\r
24 import org.eclipse.core.runtime.SubMonitor;\r
25 import org.simantics.db.RequestProcessor;\r
26 import org.simantics.db.Resource;\r
27 import org.simantics.db.Session;\r
28 import org.simantics.db.exception.DatabaseException;\r
29 import org.simantics.db.layer0.util.SessionGarbageCollection;\r
30 import org.simantics.db.management.ISessionContext;\r
31 import org.simantics.document.DocumentSettings;\r
32 import org.simantics.document.DocumentUtils;\r
33 import org.simantics.modeling.requests.CollectionRequest;\r
34 import org.simantics.modeling.requests.CollectionResult;\r
35 import org.simantics.modeling.requests.Node;\r
36 import org.simantics.modeling.ui.preferences.DiagramPreferenceUtil;\r
37 import org.simantics.ui.jobs.SessionGarbageCollectorJob;\r
38 import org.simantics.utils.page.PageDesc;\r
39 import org.simantics.utils.page.PageOrientation;\r
40 import org.simantics.utils.threads.WorkerThread;\r
41 \r
42 import com.kitfox.svg.SVGCache;\r
43 import com.lowagie.text.Document;\r
44 import com.lowagie.text.DocumentException;\r
45 import com.lowagie.text.FontFactory;\r
46 import com.lowagie.text.PageSize;\r
47 import com.lowagie.text.Rectangle;\r
48 import com.lowagie.text.pdf.DefaultFontMapper;\r
49 import com.lowagie.text.pdf.PdfBoolean;\r
50 import com.lowagie.text.pdf.PdfContentByte;\r
51 import com.lowagie.text.pdf.PdfName;\r
52 import com.lowagie.text.pdf.PdfTemplate;\r
53 import com.lowagie.text.pdf.PdfWriter;\r
54 \r
55 public class DiagramPrinter {\r
56 \r
57     public static CollectionResult browse(IProgressMonitor monitor, RequestProcessor processor, Resource[] input) throws DatabaseException {\r
58         final CollectionResult result = processor.syncRequest(new CollectionRequest(monitor, DiagramPreferenceUtil.getDefaultPreferences().getCompletePageDesc(), input));\r
59         return result;\r
60     }\r
61 \r
62     private static final AtomicBoolean fontFactoryInitialized = new AtomicBoolean();\r
63 \r
64     /**\r
65      * @param monitor the progress monitor to use for reporting progress to the\r
66      *        user. It is the caller's responsibility to call done() on the\r
67      *        given monitor. Accepts <code>null</code>, indicating that no\r
68      *        progress should be reported and that the operation cannot be\r
69      *        cancelled.\r
70      * \r
71      * @param exportPath\r
72      * @param flattenedNodes\r
73      * @param sessionContext\r
74      * @throws DocumentException\r
75      * @throws FileNotFoundException\r
76      */\r
77     public static void printToPdf(\r
78                 IProgressMonitor monitor, \r
79                 PDFExportPlan exportPlan, \r
80                 String exportPath, \r
81                 Collection<Node> flattenedNodes,\r
82             ISessionContext sessionContext) \r
83     throws PdfException {\r
84         Collection<Node> flattened = flattenedNodes;\r
85 \r
86         SubMonitor progress = SubMonitor.convert(monitor, "Export to PDF", flattened.size() * 2);\r
87 \r
88         WorkerThread workerThread = new WorkerThread("Diagram PDF Painter");\r
89         workerThread.start();\r
90 \r
91         PdfWriter writer = null;\r
92         Document document = null;\r
93 \r
94         try {\r
95             progress.subTask("Loading system fonts");\r
96             DefaultFontMapper mapper = new DefaultFontMapper();\r
97             if (fontFactoryInitialized.compareAndSet(false, true)) {\r
98                 // Only register directories once.\r
99                 FontFactory.registerDirectories();\r
100             }\r
101 \r
102             SessionGarbageCollectorJob.getInstance().setEnabled(false);\r
103 \r
104 \r
105             boolean first = true;           \r
106             int i = 0;\r
107             for (Node d : flattened) {\r
108                 ++i;\r
109                 \r
110                 //System.out.println("PAGE DESC: " + d.getPageDesc());\r
111                 //System.out.println("PAGE SIZE: " + pageSize);\r
112 \r
113                 Rectangle pageSize = toPageSize(d.getPageDesc());\r
114                 if (writer == null) {\r
115                     document = new Document(pageSize);\r
116                     writer = PdfWriter.getInstance(document, new FileOutputStream(exportPath));\r
117                     writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);\r
118                     if ( exportPlan.attachTG ) {\r
119                         writer.addViewerPreference(PdfName.USEATTACHMENTS, PdfBoolean.PDFTRUE);\r
120                     }\r
121    \r
122                     String creator = getCreator();\r
123                     document.addCreator(creator);\r
124 \r
125                     /*\r
126                         File keystoreFile = new File("c:\\0009278.p12");\r
127                         String password = "ka7GfzI9Oq";\r
128 \r
129                         try {\r
130                                 KeyStore ks = KeyStore.getInstance("pkcs12");\r
131                                 ks.load(new FileInputStream(keystoreFile), password.toCharArray());\r
132                                 List<String> aliases = Collections.list(ks.aliases());\r
133                                 String alias = aliases.get(0);\r
134                                 PrivateKey key = (PrivateKey)ks.getKey(alias, password.toCharArray());\r
135                                 Certificate[] chain = ks.getCertificateChain(alias);\r
136                                 int permission = PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY;\r
137                                 \r
138                                 PdfEncryption crypto = new PdfEncryption();\r
139                                 //for (Certificate c : chain) crypto.addRecipient(c, permission);\r
140                                 //crypto.addRecipient(chain[2], permission);\r
141                         crypto.setCryptoMode(PdfWriter.ENCRYPTION_AES_128, 0);\r
142                                 crypto.setupByEncryptionKey(key.getEncoded(), key.getEncoded().length*8);\r
143                         crypto.getEncryptionDictionary();\r
144                         \r
145                                 \r
146                         } catch (Exception e) {\r
147                                 e.printStackTrace();\r
148                         }*/\r
149                     \r
150                     /*\r
151                     writer.setEncryption(\r
152                                 new Certificate[] {}, \r
153                                 new int[] {PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING}, \r
154                                 PdfWriter.STANDARD_ENCRYPTION_128);\r
155                                 */\r
156                     //writer.setEncryption(PdfWriter.STANDARD_ENCRYPTION_128, "", "password", PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY);\r
157                         \r
158                     \r
159 //                      PdfName companyName = new PdfName("SMTC");          \r
160 //                  PdfDeveloperExtension ext = new PdfDeveloperExtension(companyName, PdfWriter.PDF_VERSION_1_7, 3);\r
161 //                  writer.addDeveloperExtension( ext );\r
162                     \r
163                     document.open();\r
164                 }\r
165 \r
166                 if (!first) {\r
167                 document.setPageSize(pageSize);\r
168                 document.newPage();\r
169                 }\r
170 \r
171                 /*\r
172                 /// ATTACHMENTS - TG ///\r
173                 byte[] attachment = null;\r
174                 if ( exportPlan.attachTG && !d.getDefiningResources().isEmpty() ) \r
175                 try {\r
176                     PdfDictionary fileParameter = new PdfDictionary();\r
177 \r
178                         {\r
179                                 final Resource composite = d.getDefiningResources().iterator().next();                                          \r
180                                 final Session session = exportPlan.sessionContext.getSession();\r
181                                 \r
182                             SimanticsClipboard clipboard = session.syncRequest(new Read<SimanticsClipboard>() {\r
183                                 @Override\r
184                                 public SimanticsClipboard perform(ReadGraph graph) throws DatabaseException {\r
185                                     CopyHandler ch = graph.adapt(composite, CopyHandler.class);\r
186                                     SimanticsClipboardImpl clipboard = new SimanticsClipboardImpl();\r
187                                     ch.copyToClipboard(graph, clipboard);\r
188                                     return clipboard;\r
189                                 }\r
190                             });\r
191                             for (Set<Representation> object : clipboard.getContents()) {\r
192                                 TransferableGraph1 tg = ClipboardUtils.accept(object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);\r
193                                 String filename = d.getName()+".diagram";\r
194                                 try {\r
195                                         byte[] data = DataContainers.writeFile(                                 \r
196                                                 new DataContainer("aprosDiagram", 1, new Variant(TransferableGraph1.BINDING, tg))\r
197                                         );\r
198                                             PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(\r
199                                                         writer, \r
200                                                         "/Diagram", filename, data, true, "application/simantics/diagram", \r
201                                                         fileParameter);\r
202                                             writer.addFileAttachment(d.getName()+".diagram", fs);\r
203                                 } catch ( NullPointerException npe ) {\r
204                                         throw new PdfException("Experiment must be activated to export attachments"+npe.getMessage(), npe);\r
205                                 }\r
206                             }\r
207                         }\r
208 \r
209                 } catch (DatabaseException e) {\r
210                     e.printStackTrace();\r
211                 } catch (IOException e) {\r
212                     e.printStackTrace();\r
213                 }\r
214                 */\r
215                 //////////////////////////\r
216                 \r
217                 String diagramName = formDiagramName(d, true);\r
218                 String subTask = "Page (" + i + "/" + flattened.size() + "): " + diagramName;\r
219 \r
220                 Resource diagram = d.getDiagramResource();\r
221                 if (diagram == null) {\r
222                     // No diagram, skip page.\r
223                     subTask += " skipped, no diagram.";\r
224                     System.out.println(subTask);\r
225                     continue;\r
226                 }\r
227 \r
228                 System.out.println(subTask);\r
229                 progress.subTask(subTask);\r
230 \r
231                 try {\r
232                     PDFPainter.render(workerThread, sessionContext, exportPlan, d, writer, mapper,\r
233                             pageSize, d.getPageDesc(), exportPlan.fitContentToPageMargins, 10000);\r
234                 } catch (InterruptedException e) {\r
235                     e.printStackTrace();\r
236                 } catch (DatabaseException e) {\r
237                     e.printStackTrace();\r
238                 }\r
239 \r
240                 // Paint diagram path/name on the page\r
241                 // TODO: remove this hard coded diagram name printing and\r
242                 // replace it with a page templates that is loaded along with\r
243                 // the rest of the diagram\r
244 \r
245                 int w = (int) pageSize.getWidth();\r
246                 int h = (int) pageSize.getHeight();\r
247 \r
248                 // Write Page Number\r
249                 PdfContentByte cb = writer.getDirectContent();\r
250 \r
251 //                PdfTemplate tp = cb.createTemplate(w, h);\r
252 //                Graphics2D g2d = tp.createGraphics(w, h, mapper);\r
253 //                g2d.setColor(Color.black);\r
254 //                java.awt.Font thisFont = new java.awt.Font("Arial", java.awt.Font.ITALIC, 10);\r
255 //                g2d.setFont(thisFont);\r
256 //                FontMetrics metrics = g2d.getFontMetrics();\r
257 //                int width = metrics.stringWidth(diagramName);\r
258 //                g2d.drawString(diagramName, (w - width) / 2, document.getPageSize().getHeight() - PageDesc.toPoints(5));\r
259 //                g2d.dispose();\r
260 //                cb.addTemplate(tp, 0, 0);\r
261 \r
262                 /// ATTACHMENTS - Write WIKI ///\r
263                 if ( exportPlan.attachWiki && !d.getDefiningResources().isEmpty() ) {\r
264                     final Session session = exportPlan.sessionContext.getSession();\r
265                     Resource composite = d.getDefiningResources().iterator().next();\r
266                     DocumentUtils du = new DocumentUtils();\r
267                     StringBuilder wiki = new StringBuilder();\r
268                     StringBuilder css = new StringBuilder();\r
269                     du.getDocumentWikiTextRecursive(session, composite, wiki, css);\r
270                         DocumentSettings settings = du.getDocumentSettings(session, composite);\r
271                     PdfTemplate tp_ = cb.createTemplate(w, h);\r
272                     if ( wiki.length()>0 ) {\r
273                         String wikiText = wiki.toString();\r
274                         String cssText = css.toString();\r
275                         du.print(session, composite, wikiText, cssText, settings, tp_.getPdfWriter(), document);\r
276                     }\r
277                     cb.addTemplate(tp_, 0, 0);\r
278                 }\r
279                 //////////////////////////\r
280 \r
281                 progress.worked(1);\r
282                 first = false;\r
283 \r
284                 if (progress.isCanceled())\r
285                     throw new OperationCanceledException();\r
286 \r
287                 System.out.println("GC");\r
288                 SVGCache.getSVGUniverse().clearUnreferenced();\r
289                 SessionGarbageCollection.gc(null, sessionContext.getSession(), true, null);\r
290                 System.gc();\r
291                 System.out.println("GC finished");\r
292                 progress.worked(1);\r
293             }\r
294                 } catch (DatabaseException e) {\r
295                         throw new PdfException(e);\r
296                 } catch (FileNotFoundException e) {\r
297                         throw new PdfException(e);\r
298                 } catch (DocumentException e) {\r
299                         throw new PdfException(e);\r
300                 } finally {\r
301             workerThread.stopDispatchingEvents(true);\r
302             System.out.println("closing document");\r
303             try {\r
304                 if ( document!=null ) document.close();\r
305                 if ( writer!=null ) writer.close();\r
306             } catch(RuntimeException e) {\r
307                 e.printStackTrace();\r
308             }\r
309             System.out.println("document closed");\r
310             SessionGarbageCollectorJob.getInstance().setEnabled(true).scheduleAfterQuietTime();\r
311         }\r
312     }\r
313 \r
314     public static Rectangle toPageSize(PageDesc pageDesc) {\r
315         String arg = PageDesc.toPoints(pageDesc.getWidth()) + " " + PageDesc.toPoints(pageDesc.getHeight());\r
316         Rectangle r = PageSize.getRectangle(arg);\r
317 \r
318         if (PageOrientation.Landscape == pageDesc.getOrientation())\r
319             r = r.rotate();\r
320 \r
321         // Disable inherent borders from the PDF writer.\r
322         r.setBorder(0);\r
323 \r
324         return r;\r
325     }\r
326 \r
327     public static String formDiagramName(Node node, boolean parents) {\r
328         Node d = node;\r
329         String ret = d.getName();\r
330         if (parents) {\r
331             while (d.getParent() != null) {\r
332                 d = d.getParent();\r
333                 ret = d.getName() + " / " + ret;\r
334             }\r
335         }\r
336 //        String[] pg = node.getPartOfGroups();\r
337 //        if (pg.length > 0)\r
338 //            ret += " [" + EString.implode(pg, " / ") + " / " + node.getName() + "]";\r
339         return ret;\r
340     }\r
341 \r
342         public static String getCreator() {\r
343                 String creator = null;\r
344                 IProduct product = Platform.getProduct();\r
345                 if (product != null) {\r
346                         creator = product.getDescription();\r
347                         if (creator == null) {\r
348                                 creator = product.getName();\r
349                         }\r
350                 }\r
351                 if (creator == null) {\r
352                         creator = "Simantics";\r
353                 }\r
354                 return creator;\r
355         }\r
356 \r
357     static {\r
358                 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());\r
359     }\r
360 \r
361 }