Permalink
Browse files

Initial attempt to support Prince CSS formatter

  • Loading branch information...
ndw committed Jul 30, 2013
1 parent 1f1c05e commit 696a4718a5fa90e270c9c95a18b4d5ea4d88a806
Showing with 281 additions and 0 deletions.
  1. +14 −0 build.xml
  2. +267 −0 src/com/xmlcalabash/util/CssPrince.java
View
@@ -183,6 +183,7 @@
<exclude name="com/xmlcalabash/util/FoFOP.java"/>
<exclude name="com/xmlcalabash/util/FoAH.java"/>
<exclude name="com/xmlcalabash/util/CssAH.java"/>
+ <exclude name="com/xmlcalabash/util/CssPrince.java"/>
<!-- Only for debugging -->
<exclude name="com/xmlcalabash/drivers/SaxonProblem.java"/>
@@ -301,6 +302,19 @@
</javac>
</target>
+ <available classname="com.princexml.Prince" property="Prince.present">
+ <classpath refid="build.classpath"/>
+ </available>
+
+ <target name="compile-Prince" depends="init" if="Prince.present">
+ <javac destdir="${build.dir}"
+ classpathref="build.classpath"
+ source="1.6" target="1.6" includeantruntime="false">
+ <src path="src"/>
+ <include name="com/xmlcalabash/util/CssPrince.java"/>
+ </javac>
+ </target>
+
<available classname="javax.mail.Address" property="Mail.present">
<classpath refid="build.classpath"/>
</available>
@@ -0,0 +1,267 @@
+package com.xmlcalabash.util;
+
+import com.princexml.Prince;
+import com.princexml.PrinceEvents;
+import com.xmlcalabash.config.CssProcessor;
+import com.xmlcalabash.core.XProcConstants;
+import com.xmlcalabash.core.XProcException;
+import com.xmlcalabash.core.XProcRuntime;
+import com.xmlcalabash.runtime.XStep;
+import net.sf.saxon.s9api.QName;
+import net.sf.saxon.s9api.XdmNode;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Properties;
+import java.util.Vector;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: ndw
+ * Date: 9/1/11
+ * Time: 4:24 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class CssPrince implements CssProcessor {
+ private static final QName _content_type = new QName("content-type");
+ private static final QName _encoding = new QName("", "encoding");
+
+ XProcRuntime runtime = null;
+ Properties options = null;
+ String primarySS = null;
+ Vector<String> userSS = new Vector<String> ();
+
+ XStep step = null;
+ Prince prince = null;
+
+ public void initialize(XProcRuntime runtime, XStep step, Properties options) {
+ this.runtime = runtime;
+ this.step = step;
+ this.options = options;
+
+ String exePath = getStringProp("exePath");
+ if (exePath == null) {
+ exePath = System.getProperty("com.xmlcalabash.css.prince.exepath");
+ }
+ if (exePath == null || "".equals(exePath)) {
+ throw new XProcException("Attempt to use Prince as CSS formater but no Prince exePath specified");
+ }
+
+ prince = new Prince(exePath, new PrinceMessages());
+
+ String s = getStringProp("baseURL");
+ if (s != null) {
+ prince.setBaseURL(s);
+ }
+
+ Boolean b = getBooleanProp("compress");
+ if (b != null) {
+ prince.setCompress(b);
+ }
+
+ b = getBooleanProp("debug");
+ if (b != null) {
+ prince.setDebug(b);
+ }
+
+ b = getBooleanProp("embedFonts");
+ if (b != null) {
+ prince.setEmbedFonts(b);
+ }
+
+ b = getBooleanProp("encrypt");
+ if (b != null) {
+ prince.setEncrypt(b);
+ }
+
+ Integer keyBits = getIntProp("keyBits");
+ if (keyBits != null) {
+ String up = getStringProp("userPassword");
+ String op = getStringProp("ownerPassword");
+ b = getBooleanProp("disallowPrint");
+ boolean dp = b == null ? false : b;
+ b = getBooleanProp("disallowModify");
+ boolean dm = b == null ? false : b;
+ b = getBooleanProp("disallowCopy");
+ boolean dc = b == null ? false : b;
+ b = getBooleanProp("disallowAnnotate");
+ boolean da = b == null ? false : b;
+ prince.setEncryptInfo(keyBits, up, op, dp, dm, dc, da);
+ }
+
+ s = getStringProp("fileRoot");
+ if (s != null) {
+ prince.setFileRoot(s);
+ }
+
+
+ b = getBooleanProp("html");
+ if (b != null) {
+ prince.setHTML(b);
+ }
+
+ s = getStringProp("httpPassword");
+ if (s != null) {
+ prince.setHttpPassword(s);
+ }
+
+ s = getStringProp("httpUsername");
+ if (s != null) {
+ prince.setHttpUsername(s);
+ }
+
+ s = getStringProp("httpProxy");
+ if (s != null) {
+ prince.setHttpProxy(s);
+ }
+
+ s = getStringProp("inputType");
+ if (s != null) {
+ prince.setInputType(s);
+ }
+
+ b = getBooleanProp("javascript");
+ if (b != null) {
+ prince.setJavaScript(b);
+ }
+
+ s = getStringProp("log");
+ if (s != null) {
+ prince.setLog(s);
+ }
+
+ b = getBooleanProp("network");
+ if (b != null) {
+ prince.setNetwork(b);
+ }
+
+ b = getBooleanProp("subsetFonts");
+ if (b != null) {
+ prince.setSubsetFonts(b);
+ }
+
+ b = getBooleanProp("verbose");
+ if (b != null) {
+ prince.setVerbose(b);
+ }
+
+ b = getBooleanProp("XInclude");
+ if (b != null) {
+ prince.setXInclude(b);
+ }
+
+ s = getStringProp("scripts");
+ if (s != null) {
+ for (String js : s.split("\\s+")) {
+ prince.addScript(js);
+ }
+ }
+ }
+
+ public void addStylesheet(XdmNode doc) {
+ doc = S9apiUtils.getDocumentElement(doc);
+
+ String stylesheet = null;
+ if ((XProcConstants.c_data.equals(doc.getNodeName())
+ && "application/octet-stream".equals(doc.getAttributeValue(_content_type)))
+ || "base64".equals(doc.getAttributeValue(_encoding))) {
+ byte[] decoded = Base64.decode(doc.getStringValue());
+ stylesheet = new String(decoded);
+ } else {
+ stylesheet = doc.getStringValue();
+ }
+
+ String prefix = "temp";
+ String suffix = ".css";
+
+ File temp;
+ try {
+ temp = File.createTempFile(prefix, suffix);
+ } catch (IOException ioe) {
+ throw new XProcException(step.getNode(), "Failed to create temporary file for CSS");
+ }
+
+ temp.deleteOnExit();
+
+ try {
+ PrintStream cssout = new PrintStream(temp);
+ cssout.print(stylesheet);
+ cssout.close();
+ } catch (FileNotFoundException fnfe) {
+ throw new XProcException(step.getNode(), "Failed to write to temporary CSS file");
+ }
+
+ if (primarySS == null) {
+ primarySS = temp.toURI().toASCIIString();
+ } else {
+ userSS.add(temp.toURI().toASCIIString());
+ }
+ }
+
+ public void format(XdmNode doc, OutputStream out, String contentType) {
+ if (contentType != null && !"application/pdf".equals(contentType)) {
+ throw new XProcException(step.getNode(), "Unsupported content-type on p:css-formatter: " + contentType);
+ }
+
+ try {
+ if (primarySS != null) {
+ prince.addStyleSheet(primarySS);
+ }
+
+ for (String uri : userSS) {
+ prince.addStyleSheet(uri);
+ }
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(doc.toString().getBytes("UTF-8"));
+ prince.convert(bis, out);
+ } catch (IOException e) {
+ if (runtime.getDebug()) {
+ e.printStackTrace();
+ }
+ throw new XProcException(e);
+ }
+ }
+
+ private String getStringProp(String name) {
+ return options.getProperty(name);
+ }
+
+ private Integer getIntProp(String name) {
+ String s = getStringProp(name);
+ if (s != null) {
+ try {
+ int i = Integer.parseInt(s);
+ return new Integer(i);
+ } catch (NumberFormatException nfe) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private Boolean getBooleanProp(String name) {
+ String s = options.getProperty(name);
+ if (s != null) {
+ return "true".equals(s);
+ }
+ return null;
+ }
+
+ private class PrinceMessages implements PrinceEvents {
+ @Override
+ public void onMessage(String msgType, String msgLoc, String msgText) {
+ if ("inf".equals(msgType)) {
+ step.info(step.getNode(), msgText);
+ } else if ("wrn".equals(msgType)) {
+ step.warning(step.getNode(), msgText);
+ } else {
+ step.error(step.getNode(), msgText, new QName(XProcConstants.NS_XPROC_ERROR_EX, "prince"));
+ }
+ }
+ }
+}

0 comments on commit 696a471

Please sign in to comment.