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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.modeling.ui.pdf;
\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
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
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
55 public class DiagramPrinter {
\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
62 private static final AtomicBoolean fontFactoryInitialized = new AtomicBoolean();
\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
72 * @param flattenedNodes
\r
73 * @param sessionContext
\r
74 * @throws DocumentException
\r
75 * @throws FileNotFoundException
\r
77 public static void printToPdf(
\r
78 IProgressMonitor monitor,
\r
79 PDFExportPlan exportPlan,
\r
81 Collection<Node> flattenedNodes,
\r
82 ISessionContext sessionContext)
\r
83 throws PdfException {
\r
84 Collection<Node> flattened = flattenedNodes;
\r
86 SubMonitor progress = SubMonitor.convert(monitor, "Export to PDF", flattened.size() * 2);
\r
88 WorkerThread workerThread = new WorkerThread("Diagram PDF Painter");
\r
89 workerThread.start();
\r
91 PdfWriter writer = null;
\r
92 Document document = null;
\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
102 SessionGarbageCollectorJob.getInstance().setEnabled(false);
\r
105 boolean first = true;
\r
107 for (Node d : flattened) {
\r
110 //System.out.println("PAGE DESC: " + d.getPageDesc());
\r
111 //System.out.println("PAGE SIZE: " + pageSize);
\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
122 String creator = getCreator();
\r
123 document.addCreator(creator);
\r
126 File keystoreFile = new File("c:\\0009278.p12");
\r
127 String password = "ka7GfzI9Oq";
\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
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
146 } catch (Exception e) {
\r
147 e.printStackTrace();
\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
156 //writer.setEncryption(PdfWriter.STANDARD_ENCRYPTION_128, "", "password", PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY);
\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
167 document.setPageSize(pageSize);
\r
168 document.newPage();
\r
172 /// ATTACHMENTS - TG ///
\r
173 byte[] attachment = null;
\r
174 if ( exportPlan.attachTG && !d.getDefiningResources().isEmpty() )
\r
176 PdfDictionary fileParameter = new PdfDictionary();
\r
179 final Resource composite = d.getDefiningResources().iterator().next();
\r
180 final Session session = exportPlan.sessionContext.getSession();
\r
182 SimanticsClipboard clipboard = session.syncRequest(new Read<SimanticsClipboard>() {
\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
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
195 byte[] data = DataContainers.writeFile(
\r
196 new DataContainer("aprosDiagram", 1, new Variant(TransferableGraph1.BINDING, tg))
\r
198 PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(
\r
200 "/Diagram", filename, data, true, "application/simantics/diagram",
\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
209 } catch (DatabaseException e) {
\r
210 e.printStackTrace();
\r
211 } catch (IOException e) {
\r
212 e.printStackTrace();
\r
215 //////////////////////////
\r
217 String diagramName = formDiagramName(d, true);
\r
218 String subTask = "Page (" + i + "/" + flattened.size() + "): " + diagramName;
\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
228 System.out.println(subTask);
\r
229 progress.subTask(subTask);
\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
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
245 int w = (int) pageSize.getWidth();
\r
246 int h = (int) pageSize.getHeight();
\r
248 // Write Page Number
\r
249 PdfContentByte cb = writer.getDirectContent();
\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
260 // cb.addTemplate(tp, 0, 0);
\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
277 cb.addTemplate(tp_, 0, 0);
\r
279 //////////////////////////
\r
281 progress.worked(1);
\r
284 if (progress.isCanceled())
\r
285 throw new OperationCanceledException();
\r
287 System.out.println("GC");
\r
288 SVGCache.getSVGUniverse().clearUnreferenced();
\r
289 SessionGarbageCollection.gc(null, sessionContext.getSession(), true, null);
\r
291 System.out.println("GC finished");
\r
292 progress.worked(1);
\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
301 workerThread.stopDispatchingEvents(true);
\r
302 System.out.println("closing document");
\r
304 if ( document!=null ) document.close();
\r
305 if ( writer!=null ) writer.close();
\r
306 } catch(RuntimeException e) {
\r
307 e.printStackTrace();
\r
309 System.out.println("document closed");
\r
310 SessionGarbageCollectorJob.getInstance().setEnabled(true).scheduleAfterQuietTime();
\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
318 if (PageOrientation.Landscape == pageDesc.getOrientation())
\r
321 // Disable inherent borders from the PDF writer.
\r
327 public static String formDiagramName(Node node, boolean parents) {
\r
329 String ret = d.getName();
\r
331 while (d.getParent() != null) {
\r
333 ret = d.getName() + " / " + ret;
\r
336 // String[] pg = node.getPartOfGroups();
\r
337 // if (pg.length > 0)
\r
338 // ret += " [" + EString.implode(pg, " / ") + " / " + node.getName() + "]";
\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
351 if (creator == null) {
\r
352 creator = "Simantics";
\r
358 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
\r