/**
   * 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;
  }