package org.simantics.export.core.pdf; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProduct; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.InstanceScope; import org.osgi.service.prefs.Preferences; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.accessor.RecordAccessor; import org.simantics.databoard.accessor.UnionAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.error.ReferenceException; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.accessor.reference.LabelReference; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.forms.DataboardForm; import org.simantics.databoard.type.DoubleType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.StringType; import org.simantics.databoard.type.UnionType; import org.simantics.export.core.ExportContext; import org.simantics.export.core.error.ExportException; import org.simantics.export.core.intf.FormatClass; import org.simantics.export.core.manager.Content; import org.simantics.export.core.util.ExporterUtils; import org.simantics.utils.page.MarginUtils; import org.simantics.utils.page.MarginUtils.Margin; 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.FontFactory; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.PdfBoolean; import com.lowagie.text.pdf.PdfCopy; import com.lowagie.text.pdf.PdfName; import com.lowagie.text.pdf.PdfWriter; /** * Represents the actions to Pdf File format in Export Core. * * @author toni.kalajainen@semantum.fi */ public class ExportPdfFormat implements FormatClass { public static final Pattern PATTERN_PDF = Pattern.compile(".*\\.pdf$", Pattern.CASE_INSENSITIVE); // Options references public static String S_PDF = "Portable Document Format (PDF)"; public static ChildReference P_PDF_TITLE = ChildReference.parsePath(S_PDF+"/Title"); public static ChildReference P_PDF_AUTHOR = ChildReference.parsePath(S_PDF+"/Author"); public static ChildReference P_PDF_SUBJECT = ChildReference.parsePath(S_PDF+"/Subject"); public static ChildReference P_PDF_KEYWORDS = ChildReference.parsePath(S_PDF+"/Keywords"); public static ChildReference P_PDF_COMPRESSION = ChildReference.parsePath(S_PDF+"/Compression"); public static String S_SIGN = "Digital Signature"; public static LabelReference P_SIGN = new LabelReference( S_SIGN ); public static ChildReference P_SIGN_KEYSTORE = ChildReference.concatenate(P_SIGN, new LabelReference("Keystore") ); public static ChildReference P_SIGN_KEYSTOREPASSWORD = ChildReference.concatenate(P_SIGN, new LabelReference("Keystore Password")); public static ChildReference P_SIGN_PRIVATEKEYPASSWORD = ChildReference.concatenate(P_SIGN, new LabelReference("Private Key Password")); public static ChildReference P_SIGN_LOCATION = ChildReference.concatenate(P_SIGN, new LabelReference("Sign Location")); public static ChildReference P_SIGN_REASON = ChildReference.concatenate(P_SIGN, new LabelReference("Sign Reason")); public static String S_PAGE = "Page Settings"; public static ChildReference P_PAGE = new LabelReference( S_PAGE ); public static ChildReference P_PAGE_PAPERSIZE = ChildReference.concatenate(P_PAGE, new LabelReference("Size")); public static ChildReference P_PAGE_ORIENTATION = ChildReference.concatenate(P_PAGE, new LabelReference("Orientation")); public static ChildReference P_PAGE_MARGIN_LEFT = ChildReference.concatenate(P_PAGE, new LabelReference("Margin Left")); public static ChildReference P_PAGE_MARGIN_RIGHT = ChildReference.concatenate(P_PAGE, new LabelReference("Margin Right")); public static ChildReference P_PAGE_MARGIN_TOP = ChildReference.concatenate(P_PAGE, new LabelReference("Margin Top")); public static ChildReference P_PAGE_MARGIN_BOTTOM = ChildReference.concatenate(P_PAGE, new LabelReference("Margin Bottom")); // Preference page IDs public static String PAGE_PLUGIN_ID = "org.simantics.modeling.ui"; public static String P_DEFAULT_PAGE_SIZE = "page.default.size"; private static final AtomicBoolean fontFactoryInitialized = new AtomicBoolean(); public static final RecordType pdfOptions; static { pdfOptions = new RecordType(); DoubleType mm = new DoubleType(); mm.setUnit("mm"); // PDF metadata RecordType pdfRecord = new RecordType(); pdfRecord.addComponent("Title", Datatypes.STRING ); pdfRecord.addComponent("Author", Datatypes.STRING ); pdfRecord.addComponent("Subject", Datatypes.STRING ); pdfRecord.addComponent("Keywords", DataboardForm.TEXTBOX ); // Formatter options pdfRecord.addComponent("Compression", UnionType.newEnum("0 (No compression)", "1", "2", "3", "4", "5", "6", "7", "8", "9 (Best)")); // PDF Encryption RecordType pdfSign = new RecordType(); StringType certificate = DataboardForm.fileOpenDialog("PKCS#12 keystore (.p12)", "*.p12", "PFX (.pfx)", "*.pfx"); //, "Privacy Enhanced Mail (.pem)", ".pem" pdfSign.addComponent("Keystore", certificate); pdfSign.addComponent("Keystore Password", DataboardForm.PASSWORD); pdfSign.addComponent("Private Key Password", DataboardForm.PASSWORD); pdfSign.addComponent("Sign Location", Datatypes.STRING); pdfSign.addComponent("Sign Reason", Datatypes.STRING); PageDesc[] descs = PageDesc.PDF_ITEMS; String[] pageSizesStr = new String[ descs.length ]; for ( int i=0; i validate(ExportContext context, Variant options) throws ExportException { List problems = new ArrayList(); try { RecordAccessor ra = Accessors.getAccessor( options ); // Assert the keystore file exists, if set. String keystoreFileName = (String) ra.getValue(P_SIGN_KEYSTORE, Bindings.STRING); if ( keystoreFileName != null && !keystoreFileName.isEmpty() ) { File keystoreFile = new File( keystoreFileName ); if ( !keystoreFile.exists() ) { problems.add( "Keystore file was not found: "+keystoreFile ); } } } catch (AccessorConstructionException e) { throw new ExportException( e ); } catch (AccessorException e) { throw new ExportException( e ); } return problems; } @Override public void closeFile(ExportContext context, Object handle) throws ExportException { if ( handle instanceof ImportPdfReader ) { ImportPdfReader reader = (ImportPdfReader) handle; reader.close(); } if ( handle instanceof ExportPdfWriter ) { ExportPdfWriter writer = (ExportPdfWriter) handle; writer.close(); try { RecordAccessor ra = Accessors.getAccessor( writer.options ); String keystoreFileName = (String) ra.getValue(P_SIGN_KEYSTORE, Bindings.STRING); String keystorePassword = (String) ra.getValue(P_SIGN_KEYSTOREPASSWORD, Bindings.STRING); String privateKeyPassword = (String) ra.getValue(P_SIGN_PRIVATEKEYPASSWORD, Bindings.STRING); String signLocation = (String) ra.getValue(P_SIGN_LOCATION, Bindings.STRING); String signReason = (String) ra.getValue(P_SIGN_REASON, Bindings.STRING); if ( keystoreFileName != null && !keystoreFileName.isEmpty() ) { File keystoreFile = new File( keystoreFileName ); if ( !keystoreFile.exists() ) { throw new ExportException( "Keystore file was not found: "+keystoreFile ); } writer.sign(keystoreFile, keystorePassword, privateKeyPassword, signLocation, signReason); } } catch (AccessorConstructionException e) { throw new ExportException( e.getClass().getName()+": "+e.getMessage() ); } catch (AccessorException e) { throw new ExportException( e.getClass().getName()+": "+e.getMessage() ); } // Encrypt /* 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); 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(); */ } } @Override public void addAttachment(ExportContext ctx, Object handle, List attachments) throws ExportException { ExportPdfWriter writer = (ExportPdfWriter) handle; writer.pdfCopy.addViewerPreference(PdfName.USEATTACHMENTS, PdfBoolean.PDFTRUE); for ( Content content : attachments ) { writer.addAttachment( content ); } } @Override public void savePref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException { try { RecordAccessor ra = Accessors.getAccessor(options); String s; s = (String) ra.getValue(P_PDF_TITLE, Bindings.STRING); if ( s!=null ) contentScopeNode.put(P_PDF_TITLE.tail().toString(), s); s = (String) ra.getValue(P_PDF_AUTHOR, Bindings.STRING); if ( s!=null ) workbenchScopeNode.put(P_PDF_AUTHOR.tail().toString(), s); s = (String) ra.getValue(P_PDF_SUBJECT, Bindings.STRING); if ( s!=null ) contentScopeNode.put(P_PDF_SUBJECT.tail().toString(), s); s = (String) ra.getValue(P_PDF_KEYWORDS, Bindings.STRING); if ( s!=null ) contentScopeNode.put(P_PDF_KEYWORDS.tail().toString(), s); s = (String) ra.getValue(P_SIGN_KEYSTORE, Bindings.STRING); if ( s!=null ) workbenchScopeNode.put(P_SIGN_KEYSTORE.tail().toString(), s); s = (String) ra.getValue(P_SIGN_KEYSTOREPASSWORD, Bindings.STRING); if ( s!=null ) workbenchScopeNode.put(P_SIGN_KEYSTOREPASSWORD.tail().toString(), s); s = (String) ra.getValue(P_SIGN_PRIVATEKEYPASSWORD, Bindings.STRING); if ( s!=null ) workbenchScopeNode.put(P_SIGN_PRIVATEKEYPASSWORD.tail().toString(), s); s = (String) ra.getValue(P_SIGN_LOCATION, Bindings.STRING); if ( s!=null ) workbenchScopeNode.put(P_SIGN_LOCATION.tail().toString(), s); s = (String) ra.getValue(P_SIGN_REASON, Bindings.STRING); if ( s!=null ) workbenchScopeNode.put(P_SIGN_REASON.tail().toString(), s); try { UnionAccessor ua = ra.getComponent(P_PDF_COMPRESSION); int tag = ua.getTag(); workbenchScopeNode.putInt(P_PDF_COMPRESSION.tail().toString(), tag); } catch (ReferenceException re) { } Double d = (Double) ra.getValue(P_PAGE_MARGIN_LEFT, Bindings.DOUBLE); if ( d!=null ) contentScopeNode.putDouble(P_PAGE_MARGIN_LEFT.tail().toString(), d); d = (Double) ra.getValue(P_PAGE_MARGIN_RIGHT, Bindings.DOUBLE); if ( d!=null ) contentScopeNode.putDouble(P_PAGE_MARGIN_RIGHT.tail().toString(), d); d = (Double) ra.getValue(P_PAGE_MARGIN_BOTTOM, Bindings.DOUBLE); if ( d!=null ) contentScopeNode.putDouble(P_PAGE_MARGIN_BOTTOM.tail().toString(), d); d = (Double) ra.getValue(P_PAGE_MARGIN_TOP, Bindings.DOUBLE); if ( d!=null ) contentScopeNode.putDouble(P_PAGE_MARGIN_TOP.tail().toString(), d); try { UnionAccessor ua = ra.getComponent(P_PAGE_PAPERSIZE); int tag = ua.getTag(); contentScopeNode.putInt(P_PAGE_PAPERSIZE.tail().toString(), tag); } catch (ReferenceException re) { } try { UnionAccessor ua = ra.getComponent(P_PAGE_ORIENTATION); int tag = ua.getTag(); contentScopeNode.putInt(P_PAGE_ORIENTATION.tail().toString(), tag); } catch (ReferenceException re) { } } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } } @Override public void loadPref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException { try { RecordAccessor ra = Accessors.getAccessor(options); String s; s = contentScopeNode.get(P_PDF_TITLE.tail().toString(), null); if ( s!=null ) ra.setValue(P_PDF_TITLE, Bindings.STRING, s); s = workbenchScopeNode.get(P_PDF_AUTHOR.tail().toString(), null); if ( s!=null ) ra.setValue(P_PDF_AUTHOR, Bindings.STRING, s); s = contentScopeNode.get(P_PDF_SUBJECT.tail().toString(), null); if ( s!=null ) ra.setValue(P_PDF_SUBJECT, Bindings.STRING, s); s = contentScopeNode.get(P_PDF_KEYWORDS.tail().toString(), null); if ( s!=null ) ra.setValue(P_PDF_KEYWORDS, Bindings.STRING, s); s = workbenchScopeNode.get(P_SIGN_KEYSTORE.tail().toString(), null); if ( s!=null ) ra.setValue(P_SIGN_KEYSTORE, Bindings.STRING, s); s = workbenchScopeNode.get(P_SIGN_KEYSTOREPASSWORD.tail().toString(), null); if ( s!=null ) ra.setValue(P_SIGN_KEYSTOREPASSWORD, Bindings.STRING, s); s = workbenchScopeNode.get(P_SIGN_PRIVATEKEYPASSWORD.tail().toString(), null); if ( s!=null ) ra.setValue(P_SIGN_PRIVATEKEYPASSWORD, Bindings.STRING, s); s = workbenchScopeNode.get(P_SIGN_LOCATION.tail().toString(), null); if ( s!=null ) ra.setValue(P_SIGN_LOCATION, Bindings.STRING, s); s = workbenchScopeNode.get(P_SIGN_REASON.tail().toString(), null); if ( s!=null ) ra.setValue(P_SIGN_REASON, Bindings.STRING, s); int tag = workbenchScopeNode.getInt(P_PDF_COMPRESSION.tail().toString(), -1); if ( tag>=0 ) try { UnionAccessor ua = ra.getComponent(P_PDF_COMPRESSION); ua.setComponentValue(tag, Bindings.VOID, null); } catch (ReferenceException re) { } s = contentScopeNode.get(P_PAGE_MARGIN_LEFT.tail().toString(), null); if ( s!=null ) ra.setValue(P_PAGE_MARGIN_LEFT, Bindings.DOUBLE, Double.valueOf(s)); s = contentScopeNode.get(P_PAGE_MARGIN_RIGHT.tail().toString(), null); if ( s!=null ) ra.setValue(P_PAGE_MARGIN_RIGHT, Bindings.DOUBLE, Double.valueOf(s)); s = contentScopeNode.get(P_PAGE_MARGIN_TOP.tail().toString(), null); if ( s!=null ) ra.setValue(P_PAGE_MARGIN_TOP, Bindings.DOUBLE, Double.valueOf(s)); s = contentScopeNode.get(P_PAGE_MARGIN_BOTTOM.tail().toString(), null); if ( s!=null ) ra.setValue(P_PAGE_MARGIN_BOTTOM, Bindings.DOUBLE, Double.valueOf(s)); tag = contentScopeNode.getInt(P_PAGE_PAPERSIZE.tail().toString(), -1); if ( tag>=0 ) try { UnionAccessor ua = ra.getComponent(P_PAGE_PAPERSIZE); ua.setComponentValue(tag, Bindings.VOID, null); } catch (ReferenceException re) { } tag = contentScopeNode.getInt(P_PAGE_ORIENTATION.tail().toString(), -1); if ( tag>=0 ) try { UnionAccessor ua = ra.getComponent(P_PAGE_ORIENTATION); ua.setComponentValue(tag, Bindings.VOID, null); } catch (ReferenceException re) { } } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } } @Override public void fillDefaultPrefs( ExportContext ctx, Variant options) throws ExportException { try { RecordAccessor ra = Accessors.getAccessor(options); Preferences instPrefs = InstanceScope.INSTANCE.getNode( PAGE_PLUGIN_ID ); Preferences defaultPrefs = DefaultScope.INSTANCE.getNode( PAGE_PLUGIN_ID ); String value = ExporterUtils.getPrefString(instPrefs, defaultPrefs, P_DEFAULT_PAGE_SIZE); if ( value != null ) { try { StringTokenizer tok = new StringTokenizer( value ); String w = tok.nextToken().trim(); String h = tok.nextToken().trim(); String orientation = tok.nextToken().trim(); if ( orientation!=null ) { try { UnionAccessor ua = ra.getComponent(P_PAGE_ORIENTATION); int index = ua.type().getComponentIndex2(orientation); if ( index<0 ) index = 1; ua.setComponentValue(index, Bindings.VOID, new Object()); } catch (ReferenceException re) { } } String centering = tok.nextToken().trim(); String pagesize = tok.nextToken("\u0001").trim(); ExporterUtils.setUnionValue(ra, P_PAGE_PAPERSIZE, pagesize); String margins = tok.nextToken().trim(); if ( margins != null ) { String[] split = margins.split(";"); if (split.length == 4) { for (int i=0; i<4; i++) { String[] tok2 = split[i].split(" "); String controlRelative = tok2[0]; String controlAbsolute = tok2[1]; String diagramAbsolute = tok2[2]; ChildReference ref = null; switch (i) { case 0: ref = P_PAGE_MARGIN_TOP; break; case 1: ref = P_PAGE_MARGIN_BOTTOM; break; case 2: ref = P_PAGE_MARGIN_LEFT; break; case 3: ref = P_PAGE_MARGIN_RIGHT; break; } double margin_mm = Double.valueOf(diagramAbsolute); ra.setValue(ref, Bindings.DOUBLE, margin_mm); } } } } catch( Exception e ) { // This is not so important that pdf export should fail. e.printStackTrace(); } } String username = System.getProperty("user.name"); if ( username != null ) ra.setValue(P_PDF_AUTHOR, Bindings.STRING, username); ra.setValue(P_SIGN_REASON, Bindings.STRING, "Document Created"); ExporterUtils.setUnionValue(ra, P_PDF_COMPRESSION, 9); } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } } 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; } public static boolean isPdf(String filename) { return PATTERN_PDF.matcher(filename).matches(); } public static boolean isPdf(File file) { return PATTERN_PDF.matcher(file.getName()).matches(); } }