1 /*******************************************************************************
2 * Copyright (c) 2007, 2017 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - (#7084) refactoring, page numbering support
12 *******************************************************************************/
13 package org.simantics.modeling.ui.pdf;
15 import java.io.FileNotFoundException;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.nio.file.Path;
19 import java.nio.file.Paths;
20 import java.security.Security;
21 import java.util.Collection;
23 import org.eclipse.core.runtime.IProduct;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.OperationCanceledException;
26 import org.eclipse.core.runtime.Platform;
27 import org.eclipse.core.runtime.SubMonitor;
28 import org.simantics.db.RequestProcessor;
29 import org.simantics.db.Resource;
30 import org.simantics.db.Session;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.db.layer0.util.SessionGarbageCollection;
33 import org.simantics.document.DocumentSettings;
34 import org.simantics.document.DocumentUtils;
35 import org.simantics.export.core.pdf.FontMapping;
36 import org.simantics.export.core.pdf.PageNumbering;
37 import org.simantics.export.core.pdf.ServiceBasedPdfExportPageEvent;
38 import org.simantics.modeling.requests.CollectionRequest;
39 import org.simantics.modeling.requests.CollectionResult;
40 import org.simantics.modeling.requests.Node;
41 import org.simantics.modeling.ui.preferences.DiagramPreferenceUtil;
42 import org.simantics.ui.jobs.SessionGarbageCollectorJob;
43 import org.simantics.utils.page.PageDesc;
44 import org.simantics.utils.page.PageOrientation;
45 import org.simantics.utils.threads.WorkerThread;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import com.kitfox.svg.SVGCache;
50 import com.lowagie.text.Document;
51 import com.lowagie.text.DocumentException;
52 import com.lowagie.text.ExceptionConverter;
53 import com.lowagie.text.PageSize;
54 import com.lowagie.text.Rectangle;
55 import com.lowagie.text.pdf.FontMapper;
56 import com.lowagie.text.pdf.PdfBoolean;
57 import com.lowagie.text.pdf.PdfContentByte;
58 import com.lowagie.text.pdf.PdfName;
59 import com.lowagie.text.pdf.PdfTemplate;
60 import com.lowagie.text.pdf.PdfWriter;
63 * @author Tuukka Lehtonen
65 public class DiagramPrinter {
67 private static final Logger LOGGER = LoggerFactory.getLogger(DiagramPrinter.class);
69 public static CollectionResult browse(IProgressMonitor monitor, RequestProcessor processor, Resource[] input) throws DatabaseException {
70 final CollectionResult result = processor.syncRequest(new CollectionRequest(monitor, DiagramPreferenceUtil.getDefaultPreferences().getCompletePageDesc(), input));
75 * @param monitor the progress monitor to use for reporting progress to the
76 * user. It is the caller's responsibility to call done() on the
77 * given monitor. Accepts <code>null</code>, indicating that no
78 * progress should be reported and that the operation cannot be
82 * @param flattenedNodes
83 * @param sessionContext
84 * @throws DocumentException
85 * @throws FileNotFoundException
87 public static void printToPdf(
88 IProgressMonitor monitor,
89 PDFExportPlan exportPlan,
91 Collection<Node> flattenedNodes)
94 if (!exportPlan.addPageNumbers) {
95 printToPdfWithoutPageNumbers(monitor, exportPlan, exportPath, flattenedNodes);
97 SubMonitor mon = SubMonitor.convert(monitor, "Export to PDF", flattenedNodes.size() * 3);
99 Path tempOutput = Paths.get(exportPath + ".tmp");
100 Path finalOutput = Paths.get(exportPath);
102 int exportedPages = printToPdfWithoutPageNumbers(
103 mon.newChild(flattenedNodes.size() * 2, SubMonitor.SUPPRESS_NONE),
105 tempOutput.toString(),
108 if (mon.isCanceled()) {
109 tempOutput.toFile().delete();
110 throw new OperationCanceledException();
114 mon.setWorkRemaining(exportedPages);
115 mon.setTaskName("Numbering output pages");
117 PageNumbering.addPageNumbers(
118 mon.newChild(flattenedNodes.size()),
119 tempOutput, finalOutput,
120 exportPlan.pageNumberPosition,
121 exportPlan.pageNumberFormat);
122 } catch (IOException | DocumentException | ExceptionConverter e) {
123 throw new PdfException(e);
125 tempOutput.toFile().delete();
131 * @param monitor the progress monitor to use for reporting progress to the
132 * user. It is the caller's responsibility to call done() on the
133 * given monitor. Accepts <code>null</code>, indicating that no
134 * progress should be reported and that the operation cannot be
138 * @param flattenedNodes
139 * @return number of pages printed
140 * @throws PdfException
143 public static int printToPdfWithoutPageNumbers(
144 IProgressMonitor monitor,
145 PDFExportPlan exportPlan,
147 Collection<Node> flattenedNodes)
150 SubMonitor progress = SubMonitor.convert(monitor, "Export to PDF", flattenedNodes.size() * 2);
152 WorkerThread workerThread = new WorkerThread("Diagram PDF Painter");
153 workerThread.start();
155 PdfWriter writer = null;
156 Document document = null;
157 int exportedPages = 0;
160 progress.subTask("Loading system fonts");
161 FontMapper mapper = FontMapping.defaultFontMapper();
163 SessionGarbageCollectorJob.getInstance().setEnabled(false);
165 boolean first = true;
167 for (Node d : flattenedNodes) {
170 //System.out.println("PAGE DESC: " + d.getPageDesc());
171 //System.out.println("PAGE SIZE: " + pageSize);
173 Rectangle pageSize = toPageSize(d.getPageDesc());
174 if (writer == null) {
175 document = new Document(pageSize);
176 writer = PdfWriter.getInstance(document, new FileOutputStream(exportPath));
177 writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
178 writer.setPageEvent(new ServiceBasedPdfExportPageEvent());
179 if ( exportPlan.attachTG ) {
180 writer.addViewerPreference(PdfName.USEATTACHMENTS, PdfBoolean.PDFTRUE);
183 String creator = getCreator();
184 document.addCreator(creator);
187 File keystoreFile = new File("c:\\0009278.p12");
188 String password = "ka7GfzI9Oq";
191 KeyStore ks = KeyStore.getInstance("pkcs12");
192 ks.load(new FileInputStream(keystoreFile), password.toCharArray());
193 List<String> aliases = Collections.list(ks.aliases());
194 String alias = aliases.get(0);
195 PrivateKey key = (PrivateKey)ks.getKey(alias, password.toCharArray());
196 Certificate[] chain = ks.getCertificateChain(alias);
197 int permission = PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY;
199 PdfEncryption crypto = new PdfEncryption();
200 //for (Certificate c : chain) crypto.addRecipient(c, permission);
201 //crypto.addRecipient(chain[2], permission);
202 crypto.setCryptoMode(PdfWriter.ENCRYPTION_AES_128, 0);
203 crypto.setupByEncryptionKey(key.getEncoded(), key.getEncoded().length*8);
204 crypto.getEncryptionDictionary();
206 } catch (Exception e) {
211 writer.setEncryption(
212 new Certificate[] {},
213 new int[] {PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING},
214 PdfWriter.STANDARD_ENCRYPTION_128);
216 //writer.setEncryption(PdfWriter.STANDARD_ENCRYPTION_128, "", "password", PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY);
218 // PdfName companyName = new PdfName("SMTC");
219 // PdfDeveloperExtension ext = new PdfDeveloperExtension(companyName, PdfWriter.PDF_VERSION_1_7, 3);
220 // writer.addDeveloperExtension( ext );
226 document.setPageSize(pageSize);
231 /// ATTACHMENTS - TG ///
232 byte[] attachment = null;
233 if ( exportPlan.attachTG && !d.getDefiningResources().isEmpty() )
235 PdfDictionary fileParameter = new PdfDictionary();
238 final Resource composite = d.getDefiningResources().iterator().next();
239 final Session session = exportPlan.sessionContext.getSession();
241 SimanticsClipboard clipboard = session.syncRequest(new Read<SimanticsClipboard>() {
243 public SimanticsClipboard perform(ReadGraph graph) throws DatabaseException {
244 CopyHandler ch = graph.adapt(composite, CopyHandler.class);
245 SimanticsClipboardImpl clipboard = new SimanticsClipboardImpl();
246 ch.copyToClipboard(graph, clipboard);
250 for (Set<Representation> object : clipboard.getContents()) {
251 TransferableGraph1 tg = ClipboardUtils.accept(object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);
252 String filename = d.getName()+".diagram";
254 byte[] data = DataContainers.writeFile(
255 new DataContainer("aprosDiagram", 1, new Variant(TransferableGraph1.BINDING, tg))
257 PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(
259 "/Diagram", filename, data, true, "application/simantics/diagram",
261 writer.addFileAttachment(d.getName()+".diagram", fs);
262 } catch ( NullPointerException npe ) {
263 throw new PdfException("Experiment must be activated to export attachments"+npe.getMessage(), npe);
268 } catch (DatabaseException e) {
270 } catch (IOException e) {
274 //////////////////////////
276 String diagramName = formDiagramName(d, true);
277 String subTask = "Page (" + i + "/" + flattenedNodes.size() + "): " + diagramName;
279 Resource diagram = d.getDiagramResource();
280 if (diagram == null) {
281 // No diagram, skip page.
282 subTask += " skipped, no diagram.";
283 LOGGER.info(subTask);
287 LOGGER.info(subTask);
288 progress.subTask(subTask);
291 PDFPainter.render(workerThread, exportPlan, d, writer, mapper,
292 pageSize, d.getPageDesc(), exportPlan.fitContentToPageMargins, 10000);
294 } catch (DatabaseException | InterruptedException e) {
295 LOGGER.error("PDF rendering failed.", e);
298 /// ATTACHMENTS - Write WIKI ///
299 if ( exportPlan.attachWiki && !d.getDefiningResources().isEmpty() ) {
300 int w = (int) pageSize.getWidth();
301 int h = (int) pageSize.getHeight();
302 PdfContentByte cb = writer.getDirectContent();
303 Session session = exportPlan.sessionContext.getSession();
304 Resource composite = d.getDefiningResources().iterator().next();
305 DocumentUtils du = new DocumentUtils();
306 StringBuilder wiki = new StringBuilder();
307 StringBuilder css = new StringBuilder();
308 du.getDocumentWikiTextRecursive(session, composite, wiki, css);
309 DocumentSettings settings = du.getDocumentSettings(session, composite);
310 PdfTemplate tp_ = cb.createTemplate(w, h);
311 if ( wiki.length()>0 ) {
312 String wikiText = wiki.toString();
313 String cssText = css.toString();
315 exportedPages += du.print(session, composite, wikiText, cssText, settings, tp_.getPdfWriter(), document);
316 } catch (DatabaseException | DocumentException e) {
317 LOGGER.error("Wiki documentation to PDF rendering failed.", e);
320 cb.addTemplate(tp_, 0, 0);
322 //////////////////////////
327 if (progress.isCanceled())
328 throw new OperationCanceledException();
331 SVGCache.getSVGUniverse().clearUnreferenced();
332 SessionGarbageCollection.gc(null, exportPlan.sessionContext.getSession(), true, null);
334 LOGGER.trace("GC finished");
338 return exportedPages;
339 } catch (DatabaseException | FileNotFoundException | DocumentException | ExceptionConverter e) {
340 throw new PdfException(e);
342 workerThread.stopDispatchingEvents(true);
343 LOGGER.trace("closing document");
345 if ( document != null ) document.close();
346 if ( writer != null ) writer.close();
347 LOGGER.trace("document closed");
348 } catch (RuntimeException e) {
349 LOGGER.error("Error closing PDF document writer", e);
351 SessionGarbageCollectorJob.getInstance().setEnabled(true).scheduleAfterQuietTime();
355 public static Rectangle toPageSize(PageDesc pageDesc) {
356 String arg = PageDesc.toPoints(pageDesc.getWidth()) + " " + PageDesc.toPoints(pageDesc.getHeight());
357 Rectangle r = PageSize.getRectangle(arg);
359 if (PageOrientation.Landscape == pageDesc.getOrientation())
362 // Disable inherent borders from the PDF writer.
368 public static String formDiagramName(Node node, boolean parents) {
370 String ret = d.getName();
372 while (d.getParent() != null) {
374 ret = d.getName() + " / " + ret;
377 // String[] pg = node.getPartOfGroups();
378 // if (pg.length > 0)
379 // ret += " [" + EString.implode(pg, " / ") + " / " + node.getName() + "]";
383 public static String getCreator() {
384 String creator = null;
385 IProduct product = Platform.getProduct();
386 if (product != null) {
387 creator = product.getDescription();
388 if (creator == null) {
389 creator = product.getName();
392 if (creator == null) {
393 creator = "Simantics";
399 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());