/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.modeling.ui.pdf; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.security.Security; import java.util.Collection; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.IProduct; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubMonitor; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.util.SessionGarbageCollection; import org.simantics.db.management.ISessionContext; import org.simantics.document.DocumentSettings; import org.simantics.document.DocumentUtils; import org.simantics.export.core.pdf.ServiceBasedPdfExportPageEvent; import org.simantics.modeling.requests.CollectionRequest; import org.simantics.modeling.requests.CollectionResult; import org.simantics.modeling.requests.Node; import org.simantics.modeling.ui.preferences.DiagramPreferenceUtil; import org.simantics.ui.jobs.SessionGarbageCollectorJob; import org.simantics.utils.page.PageDesc; import org.simantics.utils.page.PageOrientation; import org.simantics.utils.threads.WorkerThread; import com.kitfox.svg.SVGCache; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.FontFactory; import com.lowagie.text.PageSize; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.DefaultFontMapper; import com.lowagie.text.pdf.PdfBoolean; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfName; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; public class DiagramPrinter { public static CollectionResult browse(IProgressMonitor monitor, RequestProcessor processor, Resource[] input) throws DatabaseException { final CollectionResult result = processor.syncRequest(new CollectionRequest(monitor, DiagramPreferenceUtil.getDefaultPreferences().getCompletePageDesc(), input)); return result; } private static final AtomicBoolean fontFactoryInitialized = new AtomicBoolean(); /** * @param monitor the progress monitor to use for reporting progress to the * user. It is the caller's responsibility to call done() on the * given monitor. Accepts null, indicating that no * progress should be reported and that the operation cannot be * cancelled. * * @param exportPath * @param flattenedNodes * @param sessionContext * @throws DocumentException * @throws FileNotFoundException */ public static void printToPdf( IProgressMonitor monitor, PDFExportPlan exportPlan, String exportPath, Collection flattenedNodes, ISessionContext sessionContext) throws PdfException { Collection flattened = flattenedNodes; SubMonitor progress = SubMonitor.convert(monitor, "Export to PDF", flattened.size() * 2); WorkerThread workerThread = new WorkerThread("Diagram PDF Painter"); workerThread.start(); PdfWriter writer = null; Document document = null; try { progress.subTask("Loading system fonts"); DefaultFontMapper mapper = new DefaultFontMapper(); if (fontFactoryInitialized.compareAndSet(false, true)) { // Only register directories once. FontFactory.registerDirectories(); } SessionGarbageCollectorJob.getInstance().setEnabled(false); boolean first = true; int i = 0; for (Node d : flattened) { ++i; //System.out.println("PAGE DESC: " + d.getPageDesc()); //System.out.println("PAGE SIZE: " + pageSize); Rectangle pageSize = toPageSize(d.getPageDesc()); if (writer == null) { document = new Document(pageSize); writer = PdfWriter.getInstance(document, new FileOutputStream(exportPath)); writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7); writer.setPageEvent(new ServiceBasedPdfExportPageEvent()); if ( exportPlan.attachTG ) { writer.addViewerPreference(PdfName.USEATTACHMENTS, PdfBoolean.PDFTRUE); } String creator = getCreator(); document.addCreator(creator); /* File keystoreFile = new File("c:\\0009278.p12"); String password = "ka7GfzI9Oq"; try { KeyStore ks = KeyStore.getInstance("pkcs12"); ks.load(new FileInputStream(keystoreFile), password.toCharArray()); List aliases = Collections.list(ks.aliases()); String alias = aliases.get(0); PrivateKey key = (PrivateKey)ks.getKey(alias, password.toCharArray()); Certificate[] chain = ks.getCertificateChain(alias); int permission = PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY; PdfEncryption crypto = new PdfEncryption(); //for (Certificate c : chain) crypto.addRecipient(c, permission); //crypto.addRecipient(chain[2], permission); crypto.setCryptoMode(PdfWriter.ENCRYPTION_AES_128, 0); crypto.setupByEncryptionKey(key.getEncoded(), key.getEncoded().length*8); crypto.getEncryptionDictionary(); } catch (Exception e) { e.printStackTrace(); }*/ /* writer.setEncryption( new Certificate[] {}, new int[] {PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING}, PdfWriter.STANDARD_ENCRYPTION_128); */ //writer.setEncryption(PdfWriter.STANDARD_ENCRYPTION_128, "", "password", PdfWriter.ALLOW_FILL_IN|PdfWriter.ALLOW_PRINTING|PdfWriter.ALLOW_COPY|PdfWriter.ALLOW_ASSEMBLY); // PdfName companyName = new PdfName("SMTC"); // PdfDeveloperExtension ext = new PdfDeveloperExtension(companyName, PdfWriter.PDF_VERSION_1_7, 3); // writer.addDeveloperExtension( ext ); document.open(); } if (!first) { document.setPageSize(pageSize); document.newPage(); } /* /// ATTACHMENTS - TG /// byte[] attachment = null; if ( exportPlan.attachTG && !d.getDefiningResources().isEmpty() ) try { PdfDictionary fileParameter = new PdfDictionary(); { final Resource composite = d.getDefiningResources().iterator().next(); final Session session = exportPlan.sessionContext.getSession(); SimanticsClipboard clipboard = session.syncRequest(new Read() { @Override public SimanticsClipboard perform(ReadGraph graph) throws DatabaseException { CopyHandler ch = graph.adapt(composite, CopyHandler.class); SimanticsClipboardImpl clipboard = new SimanticsClipboardImpl(); ch.copyToClipboard(graph, clipboard); return clipboard; } }); for (Set object : clipboard.getContents()) { TransferableGraph1 tg = ClipboardUtils.accept(object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH); String filename = d.getName()+".diagram"; try { byte[] data = DataContainers.writeFile( new DataContainer("aprosDiagram", 1, new Variant(TransferableGraph1.BINDING, tg)) ); PdfFileSpecification fs = PdfFileSpecification.fileEmbedded( writer, "/Diagram", filename, data, true, "application/simantics/diagram", fileParameter); writer.addFileAttachment(d.getName()+".diagram", fs); } catch ( NullPointerException npe ) { throw new PdfException("Experiment must be activated to export attachments"+npe.getMessage(), npe); } } } } catch (DatabaseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } */ ////////////////////////// String diagramName = formDiagramName(d, true); String subTask = "Page (" + i + "/" + flattened.size() + "): " + diagramName; Resource diagram = d.getDiagramResource(); if (diagram == null) { // No diagram, skip page. subTask += " skipped, no diagram."; System.out.println(subTask); continue; } System.out.println(subTask); progress.subTask(subTask); try { PDFPainter.render(workerThread, sessionContext, exportPlan, d, writer, mapper, pageSize, d.getPageDesc(), exportPlan.fitContentToPageMargins, 10000); } catch (InterruptedException e) { e.printStackTrace(); } catch (DatabaseException e) { e.printStackTrace(); } // Paint diagram path/name on the page // TODO: remove this hard coded diagram name printing and // replace it with a page templates that is loaded along with // the rest of the diagram int w = (int) pageSize.getWidth(); int h = (int) pageSize.getHeight(); // Write Page Number PdfContentByte cb = writer.getDirectContent(); // PdfTemplate tp = cb.createTemplate(w, h); // Graphics2D g2d = tp.createGraphics(w, h, mapper); // g2d.setColor(Color.black); // java.awt.Font thisFont = new java.awt.Font("Arial", java.awt.Font.ITALIC, 10); // g2d.setFont(thisFont); // FontMetrics metrics = g2d.getFontMetrics(); // int width = metrics.stringWidth(diagramName); // g2d.drawString(diagramName, (w - width) / 2, document.getPageSize().getHeight() - PageDesc.toPoints(5)); // g2d.dispose(); // cb.addTemplate(tp, 0, 0); /// ATTACHMENTS - Write WIKI /// if ( exportPlan.attachWiki && !d.getDefiningResources().isEmpty() ) { final Session session = exportPlan.sessionContext.getSession(); Resource composite = d.getDefiningResources().iterator().next(); DocumentUtils du = new DocumentUtils(); StringBuilder wiki = new StringBuilder(); StringBuilder css = new StringBuilder(); du.getDocumentWikiTextRecursive(session, composite, wiki, css); DocumentSettings settings = du.getDocumentSettings(session, composite); PdfTemplate tp_ = cb.createTemplate(w, h); if ( wiki.length()>0 ) { String wikiText = wiki.toString(); String cssText = css.toString(); du.print(session, composite, wikiText, cssText, settings, tp_.getPdfWriter(), document); } cb.addTemplate(tp_, 0, 0); } ////////////////////////// progress.worked(1); first = false; if (progress.isCanceled()) throw new OperationCanceledException(); System.out.println("GC"); SVGCache.getSVGUniverse().clearUnreferenced(); SessionGarbageCollection.gc(null, sessionContext.getSession(), true, null); System.gc(); System.out.println("GC finished"); progress.worked(1); } } catch (DatabaseException e) { throw new PdfException(e); } catch (FileNotFoundException e) { throw new PdfException(e); } catch (DocumentException e) { throw new PdfException(e); } finally { workerThread.stopDispatchingEvents(true); System.out.println("closing document"); try { if ( document!=null ) document.close(); if ( writer!=null ) writer.close(); } catch(RuntimeException e) { e.printStackTrace(); } System.out.println("document closed"); SessionGarbageCollectorJob.getInstance().setEnabled(true).scheduleAfterQuietTime(); } } public static Rectangle toPageSize(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; } public static String formDiagramName(Node node, boolean parents) { Node d = node; String ret = d.getName(); if (parents) { while (d.getParent() != null) { d = d.getParent(); ret = d.getName() + " / " + ret; } } // String[] pg = node.getPartOfGroups(); // if (pg.length > 0) // ret += " [" + EString.implode(pg, " / ") + " / " + node.getName() + "]"; return ret; } public static String getCreator() { String creator = null; IProduct product = Platform.getProduct(); if (product != null) { creator = product.getDescription(); if (creator == null) { creator = product.getName(); } } if (creator == null) { creator = "Simantics"; } return creator; } static { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } }