/**
   * 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;
    }
  }
  /*
   * Actual implementation of the rendering process. When a function in this
   * module is called, this method is executed with the given inputs. @param
   * Sequence[] args (XSL-FO, mime-type, parameters) @param Sequence
   * contextSequence (default sequence)
   *
   * @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[],
   *      org.exist.xquery.value.Sequence)
   */
  public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
    // gather input XSL-FO document
    // if no input document (empty), return empty result as we need data to
    // process
    if (args[0].isEmpty()) return Sequence.EMPTY_SEQUENCE;
    Item inputNode = args[0].itemAt(0);

    // get mime-type
    String mimeType = args[1].getStringValue();

    // get parameters
    Properties parameters = new Properties();
    if (!args[2].isEmpty()) {
      parameters = ModuleUtils.parseParameters(((NodeValue) args[2].itemAt(0)).getNode());
    }

    try {
      // setup a transformer handler
      TransformerHandler handler =
          TransformerFactoryAllocator.getTransformerFactory(context.getBroker())
              .newTransformerHandler();
      Transformer transformer = handler.getTransformer();

      // set the parameters if any
      if (parameters.size() > 0) {
        Enumeration keys = parameters.keys();
        while (keys.hasMoreElements()) {
          String name = (String) keys.nextElement();
          String value = parameters.getProperty(name);
          transformer.setParameter(name, value);
        }
      }

      // setup the FopFactory
      FopFactory fopFactory = FopFactory.newInstance();
      if (args.length == 4 && args[3] != null && !args[3].isEmpty()) {
        FopConfigurationBuilder cfgBuilder = new FopConfigurationBuilder(context.getBroker());
        Configuration cfg = cfgBuilder.buildFromItem(args[3].itemAt(0));
        fopFactory.setUserConfig(cfg);
      }

      // setup the foUserAgent, using given parameters held in the
      // transformer handler
      FOUserAgent foUserAgent =
          setupFOUserAgent(fopFactory.newFOUserAgent(), parameters, transformer);

      // create new instance of FOP using the mimetype, the created user
      // agent, and the output stream
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      Fop fop = fopFactory.newFop(mimeType, foUserAgent, baos);

      // Obtain FOP's DefaultHandler
      DefaultHandler dh = fop.getDefaultHandler();

      // process the XSL-FO
      dh.startDocument();
      inputNode.toSAX(context.getBroker(), dh, new Properties());
      dh.endDocument();

      // return the result
      return new Base64Binary(baos.toByteArray());
    } catch (TransformerException te) {
      throw new XPathException(te);
    } catch (SAXException se) {
      throw new XPathException(se);
    }
  }