/** * Takes a DOM structure and renders a PDF * * @param doc DOM structure * @param xslFileName XSL file to use to translate the DOM document to FOP */ @SuppressWarnings("unchecked") protected void generatePDF(Document doc, OutputStream streamOut) { String xslFileName = "participants-all-attrs.xsl"; Locale currentLocale = rb.getLocale(); if (currentLocale != null) { String fullLocale = currentLocale.toString(); xslFileName = "participants-all-attrs_" + fullLocale + ".xsl"; if (getClass().getClassLoader().getResourceAsStream(xslFileName) == null) { xslFileName = "participants-all-attrs_" + currentLocale.getCountry() + ".xsl"; if (getClass().getClassLoader().getResourceAsStream(xslFileName) == null) { // We use the default file xslFileName = "participants-all-attrs.xsl"; } } } String configFileName = "userconfig.xml"; DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); try { Configuration cfg = cfgBuilder.build(getClass().getClassLoader().getResourceAsStream(configFileName)); FopFactory fopFactory = FopFactory.newInstance(); fopFactory.setUserConfig(cfg); fopFactory.setStrictValidation(false); FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); if (!StringUtils.isEmpty(ServerConfigurationService.getString("pdf.default.font"))) { // this allows font substitution to support i18n chars in PDFs - SAK-21909 FontQualifier fromQualifier = new FontQualifier(); fromQualifier.setFontFamily("DEFAULT_FONT"); FontQualifier toQualifier = new FontQualifier(); toQualifier.setFontFamily( ServerConfigurationService.getString("pdf.default.font", "Helvetica")); FontSubstitutions result = new FontSubstitutions(); result.add(new FontSubstitution(fromQualifier, toQualifier)); fopFactory.getFontManager().setFontSubstitutions(result); } Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, streamOut); InputStream in = getClass().getClassLoader().getResourceAsStream(xslFileName); Transformer transformer = transformerFactory.newTransformer(new StreamSource(in)); transformer.setParameter("titleName", rb.getString("sitegen.siteinfolist.title.name")); transformer.setParameter("titleSection", rb.getString("sitegen.siteinfolist.title.section")); transformer.setParameter("titleId", rb.getString("sitegen.siteinfolist.title.id")); transformer.setParameter("titleCredit", rb.getString("sitegen.siteinfolist.title.credit")); transformer.setParameter("titleRole", rb.getString("sitegen.siteinfolist.title.role")); transformer.setParameter("titleStatus", rb.getString("sitegen.siteinfolist.title.status")); Source src = new DOMSource(doc); transformer.transform(src, new SAXResult(fop.getDefaultHandler())); } catch (Exception e) { e.printStackTrace(); log.warn(this + ".generatePDF(): " + e); return; } }
/** * Create and configure FopFactory. * * @param userConfig * @param defaultBaseURI * @return * @throws FOPException */ protected FopFactory createFopFactory(String userConfig) throws FOPException { // This class won't compile unless you have some version of FOP on your path. /* * Unfortunately, <=1.1 requires Configuration object, * and >1.1 requires InputStream. * * So either we pass Configuration object into this method, * and use DefaultConfigurationSerializer to get an input stream from it, * or we pass a string, and make it into a Configuration object in the case * where it is required. * * I've submitted a patch to FOP allowing configuration * using a Configuration object. Let's see whether it is applied. * * In the absence of that, passing a String (or an InputStream) seems better going forward. * So that's what the code accepts for now. */ InputStream is = null; try { is = IOUtils.toInputStream(userConfig, "UTF-8"); } catch (IOException e2) { e2.printStackTrace(); } FopFactory fopFactory = null; // If FopConfParser is on path, it is post 1.1 try { Class fopConfParserClass = Class.forName("org.apache.fop.apps.FopConfParser"); URI defaultBaseURI = new URI("http://dummy.domain"); // Default base URI must not be null, but we don't need it. // at org.apache.fop.apps.EnvironmentalProfileFactory$Profile // (EnvironmentalProfileFactory.java:92) Object o = fopConfParserClass .getConstructor(InputStream.class, URI.class) .newInstance(is, defaultBaseURI); Method method = fopConfParserClass.getDeclaredMethod("getFopFactoryBuilder", new Class[0]); Object fopFactoryBuilder = method.invoke(o); Class fopFactoryBuilderClass = Class.forName("org.apache.fop.apps.FopFactoryBuilder"); method = fopFactoryBuilderClass.getDeclaredMethod("build", new Class[0]); fopFactory = (FopFactory) method.invoke(fopFactoryBuilder); log.debug("FOP 2.1 configured OK."); } catch (Exception e) { log.error("Can't set up FOP 2.1; " + e.getMessage()); log.error("Please verify you have fop 2.1, batik 1.8 and jaxb-xslfo jars on your classpath."); log.error(e.getMessage(), e); e.printStackTrace(); // eg java.lang.ClassNotFoundException: org.apache.fop.apps.FopConfParser // legacy FOP 1.0 or 1.1 config. try { log.error("Falling back to try FOP 1.1|1.0..."); Method method; Class[] params = new Class[1]; // FopFactory fopFactory = FopFactory.newInstance(); method = FopFactory.class.getDeclaredMethod("newInstance", new Class[0]); fopFactory = (FopFactory) method.invoke(null); // fopFactory.setUserConfig(userConfig); params[0] = Configuration.class; method = FopFactory.class.getDeclaredMethod("setUserConfig", params); // There isn't a method which takes it as a string :-( DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); method.invoke(fopFactory, cfgBuilder.build(is)); log.debug("Legacy FOP configured OK."); } catch (Exception e1) { log.error("FOP not found; neither 2.1 nor earlier. Can't convert FO to PDF."); log.error(e.getMessage(), e); e1.printStackTrace(); // java.lang.IllegalAccessException: Class org.docx4j.fonts.fop.util.FopFactoryUtil // can not access a member of class org.apache.fop.apps.FopFactory // with modifiers "protected" } } return fopFactory; }