view software/eXist/mpdl-modules/src/de/mpg/mpiwg/berlin/mpdl/util/MpdlITextRenderer.java @ 10:59ff47d1e237

TEI Unterst?tzung, Fehlerbehebungen, externe Objekte
author Josef Willenborg <jwillenborg@mpiwg-berlin.mpg.de>
date Fri, 11 Mar 2011 13:33:26 +0100
parents 408254cf2f1d
children 257f67be5c00
line wrap: on
line source

package de.mpg.mpiwg.berlin.mpdl.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.xhtmlrenderer.layout.SharedContext;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import org.xhtmlrenderer.util.XRRuntimeException;

import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.BaseFont;

import de.mpg.mpiwg.berlin.mpdl.escidoc.MetadataRecord;
import de.mpg.mpiwg.berlin.mpdl.exception.ApplicationException;
import de.mpg.mpiwg.berlin.mpdl.general.MpdlConstants;

public class MpdlITextRenderer {
  private static MpdlITextRenderer instance;
  private ITextRenderer renderer = new ITextRenderer();
  private Hashtable<String, String> fontFileNames;

  public static MpdlITextRenderer getInstance() throws ApplicationException {
    if (instance == null) {
      instance = new MpdlITextRenderer();
      instance.init();
    }
    return instance;
  }

  public void init() throws ApplicationException {
    renderer = new ITextRenderer();
    SharedContext rendererSharedContext = renderer.getSharedContext();
    MpdlITextUserAgent mpdlUserAgent = new MpdlITextUserAgent();  // user agent to get a callback handle to the web access of images (getImageResource(url))
    mpdlUserAgent.setSharedContext(rendererSharedContext);
    rendererSharedContext.setUserAgentCallback(mpdlUserAgent);
    fontFileNames = new Hashtable<String, String>();
    String fontJunicodeFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/Junicode-Regular.ttf";
    String fontJunicodeBoldFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/Junicode-Bold.ttf";
    String fontJunicodeItalicFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/Junicode-Italic.ttf";
    String fontJunicodeBoldItalicFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/Junicode-BoldItalic.ttf";
    String fontSunExtAFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/Sun-ExtA.ttf";  // chinese symbols
    String fontSunExtBFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/Sun-ExtB.ttf";  // chinese symbols
    String fontDejaVuFileName = MpdlConstants.MPDL_EXIST_DATA_DIR + "/fonts/DejaVuSans.ttf";  // arabic symbols
    setFont(fontJunicodeFileName);
    setFont(fontJunicodeBoldFileName);
    setFont(fontJunicodeItalicFileName);
    setFont(fontJunicodeBoldItalicFileName);  // if set then some not bold italic characters are shown bold (e.g. in Benedetti_1585.xml)
    setFont(fontSunExtAFileName);
    setFont(fontSunExtBFileName);
    setFont(fontDejaVuFileName);
  }
  
  public byte[] createPdf(String htmlPageFragment, String language, String topLeftStr, String topRightStr, String bottomLeftStr, String bottomRightStr) throws ApplicationException {
    byte[] pdfBytes = null;
    try {
      String htmlPageDoc = getPageHtmlDoc(htmlPageFragment, language, topLeftStr, topRightStr, bottomLeftStr, bottomRightStr);  
      renderer.setDocumentFromString(htmlPageDoc);
      renderer.layout();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      renderer.createPDF(baos);
      pdfBytes = baos.toByteArray();
      baos.close();
    } catch (Exception e) {
      init();
      String message = e.getMessage();
      if (message.indexOf("nausikaa") > 0 && message.indexOf("500") > 0) {
        throw new ApplicationException("Could not fetch image from nausikaa2.rz-berlin.mpg.de: please try again later");
      }
      throw new ApplicationException(e);
    }
    return pdfBytes;
  }
  
  public void createFile(boolean pdf, boolean html, String mode, MetadataRecord mdRecord) throws ApplicationException {
    OutputStream osPdf = null;
    OutputStream osHtml = null;
    OutputStream osHtmlPdf = null;
    String eXistIdentifier = mdRecord.getEXistIdentifier();
    String language = mdRecord.getLanguage();
    if (eXistIdentifier == null)
      throw new ApplicationException("Pdf/Html-Generation failed: no eXist-Identifier given in mdRecord");
    String eXistIdentifierWithoutExtension = eXistIdentifier.substring(0, eXistIdentifier.length() - 4);  // without ".xml"
    String destFileNamePdf = MpdlConstants.MPDL_EXIST_DATA_DIR + "/documents" + eXistIdentifierWithoutExtension + ".pdf";
    String destFileNameHtml = MpdlConstants.MPDL_EXIST_DATA_DIR + "/documents" + eXistIdentifierWithoutExtension + ".html";
    String destFileNameHtmlPdfTmp = MpdlConstants.MPDL_EXIST_DATA_DIR + "/documents" + eXistIdentifierWithoutExtension + "-4Pdf.html";
    try {
      // start document
      if (pdf) {
        osPdf = new FileOutputStream(new File(destFileNamePdf));
        osHtmlPdf = new FileOutputStream(new File(destFileNameHtmlPdfTmp));
      }
      if (html)
        osHtml = new FileOutputStream(new File(destFileNameHtml));
      int countPages = httpClientGetCountPages(mdRecord);
      // style page
      String pageStyleHtml = "float:left; clear:both; border: thin solid #808080; width: 21.0cm; margin-top: 0.2cm; margin-bottom: 1cm; margin-left: 0.7cm; margin-right: 0.7cm; padding: 0.2cm;";
      // firstPage
      String firstPageHtmlShort = getFirstPageHtml(mdRecord, true);
      String firstPageHtmlLong = getFirstPageHtml(mdRecord, false);
      String mdRecordStr = getMdRecordString(mdRecord);
      String htmlHeadStr = getHtmlHead(null, mdRecordStr);
      String fontStyle = getFontStyle(language);
      if(pdf) {
        write("<html>" + htmlHeadStr + "<body style=\"" + fontStyle +  "\">", osHtmlPdf);
        // first page
        write(firstPageHtmlLong, osHtmlPdf);
      }
      if (html) {
        write("<html>" + htmlHeadStr + "<body style=\"" + fontStyle +  "\">", osHtml);
        // first page
        write("<div style=\"" + pageStyleHtml + "\">", osHtml);
        write(firstPageHtmlShort, osHtml);
        write("</div>", osHtml);
      }
      // table of content of document
      String htmlToc = getTocHtml(mdRecord);
      if (html && htmlToc != null) {
        write("<div style=\"" + pageStyleHtml + "\">", osHtml);
        write(htmlToc, osHtml);
        write("</div>", osHtml);
      }
      if(pdf && htmlToc != null) {
        write(htmlToc, osHtmlPdf);
      }
      // all pages of the document
      for(int i=1; i<=countPages; i++) {
        String htmlPageFragment = httpClientGetPageFragmentHtml(eXistIdentifier, mode, i);
        htmlPageFragment = removeXmlStartString(htmlPageFragment);
        String pnHrefName = "<a name=\"pn" + i + "\"></a>";
        if (html) {
          write("<div style=\"" + "clear:both; text-align:right; width:21.0cm; font-weight:bold;" + "\">", osHtml);
          write(pnHrefName, osHtml);
          write("</div>", osHtml);
          write("<div style=\"" + pageStyleHtml + "\">", osHtml);
          String htmlPageFragmentWithImageUrl = htmlPageFragment.replaceAll("src=\"images/", "src=\"http://" + MpdlConstants.MPDL_FULL_EXIST_HOST_NAME + "/mpdl/images/");  // to find the camera.png file on webserver mpdl-proto
          write(htmlPageFragmentWithImageUrl, osHtml);
          write("</div>", osHtml);
        }
        htmlPageFragment = pnHrefName + htmlPageFragment;
        if(pdf) {
          String htmlPageFragmentWithImageDir = htmlPageFragment.replaceAll("src=\"images/", "src=\"../../../../../mpdl/images/");  // to find the camera.png file in webbapp/mpdl/image/ directory
          write(htmlPageFragmentWithImageDir, osHtmlPdf);
        }
      }
      if (html) {
        write("</body></html>", osHtml);
      }
      // create PDF document
      if(pdf) {
        write("</body></html>", osHtmlPdf);
        osHtmlPdf.close();
        renderer.setDocument(new File(destFileNameHtmlPdfTmp));
        renderer.layout();  // takes the most time
        renderer.createPDF(osPdf);
      }
    } catch (Exception e) {
      init();
      String message = e.getMessage();
      if (message != null && message.indexOf("nausikaa") > 0 && message.indexOf("500") > 0) {
        throw new ApplicationException("fetch image is not possible: " + message);
      }
      throw new ApplicationException(e);
    } finally {
      try {
        osHtmlPdf.close();
        osPdf.close();
        osHtml.close();
        FileUtil.getInstance().deleteFile(destFileNameHtmlPdfTmp);
      } catch (IOException e) {
        // nothing
      }
    }
  }

  private String getFirstPageHtml(MetadataRecord mdRecord, boolean shortPage) {
    String author = mdRecord.getCreator();
    String title = mdRecord.getTitle();
    String year = mdRecord.getYear();
    String existId = mdRecord.getEXistIdentifier();
    String firstPageHtml = "<div class=\"firstPage\">";
    firstPageHtml = firstPageHtml + "<h2 style=\"text-align:center\">" + "Max Planck Institute for the History of Science" + "</h2>";
    firstPageHtml = firstPageHtml + "<p style=\"text-align:center\">" + "Max-Planck-Institut für Wissenschaftsgeschichte" + "</p>";
    firstPageHtml = firstPageHtml + "<p style=\"text-align:center\">" + "MPDL project" + "</p>";
    firstPageHtml = firstPageHtml + "<br></br>";
    firstPageHtml = firstPageHtml + "<br></br>";
    if (! shortPage) {
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
    }
    if (author != null) {
      firstPageHtml = firstPageHtml + "<h2 style=\"text-align:center\">" + author + "</h2>";
    }
    if (title != null) {
      firstPageHtml = firstPageHtml + "<h2 style=\"text-align:center\">" + title + "</h2>";
    }
    if (year != null) {
      firstPageHtml = firstPageHtml + "<h2 style=\"text-align:center\">" + year + "</h2>";
    }
    if (! shortPage) {
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
      firstPageHtml = firstPageHtml + "<br></br>";
    }
    firstPageHtml = firstPageHtml + "<br></br>";
    firstPageHtml = firstPageHtml + "<br></br>";
    firstPageHtml = firstPageHtml + "<br></br>";
    firstPageHtml = firstPageHtml + "<br></br>";
    String urlDocuView = "http://" + MpdlConstants.MPDL_FULL_EXIST_HOST_NAME + "/mpdl/interface/echo/echoDocuView.xql";
    String document = "?document=" + existId;
    String urlDoc = urlDocuView + document;
    firstPageHtml = firstPageHtml + "<p style=\"font:11pt sans-serif;\">Document link: <br></br><a href=\"" + urlDoc + "\">" +  urlDocuView + " <br></br>" + document + "</a></p>";
    firstPageHtml = firstPageHtml + "</div>";
    return firstPageHtml;
  }
  
  private String getTocHtml(MetadataRecord mdRecord) throws ApplicationException {
    String htmlStr = null;
    String eXistIdentifier = mdRecord.getEXistIdentifier();
    String htmlToc = httpClientGetContentListHtml(eXistIdentifier);
    String resultSizeStr = XmlUtil.getInstance().evaluateToString(htmlToc, "//div[@class = 'queryResultHits']", null);
    int resultSize = 0;
    if (resultSizeStr != null)
      resultSize = Integer.parseInt(resultSizeStr);
    if (resultSize <= 0)
      return null;
    if (htmlToc != null) {
      htmlToc = removeXmlStartString(htmlToc);
      htmlToc = htmlToc.replaceAll("page-fragment\\.xql.*pn=", "#pn");
      htmlToc = htmlToc.replaceAll(">Page: ", ">");
      htmlToc = "<text style=\"font-weight:bold; font-size:20pt; margin-left:2%; \">Content</text>" + htmlToc;
      htmlStr = "<div class=\"tocPage\">";
      htmlStr = htmlStr + htmlToc;
      htmlStr = htmlStr + "</div>";
    }
    return htmlStr;
  }
  
  private String getPageHtmlDoc(String htmlFragment, String language, String topLeftStr, String topRightStr, String bottomLeftStr, String bottomRightStr) {
    String fontStyle = getFontStyle(language);
    String stylePage = getStylePage(topLeftStr, topRightStr, bottomLeftStr, bottomRightStr);
    String htmlStr = "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
    String htmlHeadStr = getHtmlHead(stylePage, topLeftStr);
    htmlStr = htmlStr + htmlHeadStr;
    htmlStr = htmlStr + "<body style=\"" + fontStyle +  "\">";
    htmlStr = htmlStr + htmlFragment;
    htmlStr = htmlStr + "</body>";
    htmlStr = htmlStr + "</html>";
    return htmlStr;
  }
  
  private String getMdRecordString(MetadataRecord mdRecord) {
    String author = mdRecord.getCreator();
    String title = mdRecord.getTitle();
    String year = mdRecord.getYear();
    String mdRecordStr = "";
    if (mdRecord != null) {
      if (author != null && ! author.equals(""))
        mdRecordStr = mdRecordStr + author;
      if (title != null && ! title.equals(""))
        mdRecordStr = mdRecordStr + ". " + title;
      if (year != null && ! year.equals(""))
        mdRecordStr = mdRecordStr + ". " + year + ".";
      else 
        mdRecordStr = mdRecordStr + ".";
    }
    return mdRecordStr;
  }
  
  private String getHtmlHead(String stylePageStr, String titleStr) {
    String htmlStr = "<head>";
    if (stylePageStr != null)
      htmlStr = htmlStr + "<style type=\"text/css\">" + stylePageStr + "</style>";
    htmlStr = htmlStr + "<title>" + titleStr + "</title>";
    String httpExistHostName = "http" + "://" + MpdlConstants.MPDL_FULL_EXIST_HOST_NAME;
    htmlStr = htmlStr + "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + httpExistHostName + "/mpdl/presentation/pageHtml.css\" />";
    htmlStr = htmlStr + "</head>";
    return htmlStr;
  }

  private String removeXmlStartString(String inputStr) {
    String xmlStartStr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
    boolean startsWithXmlStartStr = inputStr.startsWith(xmlStartStr);
    if (startsWithXmlStartStr) {
      int xmlStartStrLength = xmlStartStr.length();
      int xmlStartStrIndex = -1;
      xmlStartStrIndex = inputStr.indexOf(xmlStartStr);
      if (xmlStartStrIndex != -1)
        inputStr = inputStr.substring(xmlStartStrLength);
    }
    return inputStr;
  }
  
  private String getFontStyle(String language) {
    String fontFamily = "Junicode";
    if (language.equals("ar"))
      fontFamily = "DejaVu Sans";
    else if (language.equals("zh") || language.equals("zho-Hant"))
      fontFamily = "Sun-ExtA, Sun-ExtB";
    return "font-size:11pt; font-family:" + fontFamily + ";";
  }
  
  private String getStylePage(String topLeftStr, String topRightStr, String bottomLeftStr, String bottomRightStr) {
    String fontStylePage = "8pt, sans-serif; ";
    String stylePage = "@page {" + "size: A4;" + "margin-top: 1.5cm;" + "margin-bottom: 1cm;" + "margin-left: 0.7cm;" + "margin-right: 0.7cm;" + "border: thin solid #808080;" + "padding: 0.2cm;" + " font-size: 10px;" + 
      " @top-left { font: " + fontStylePage + " padding-left: 0.2cm; padding-right: 1cm; font-weight:bold; content: " + topLeftStr + ";}" + 
      " @top-right { font: " + fontStylePage + " white-space: nowrap; font-weight:bold; content: " + topRightStr + ";}" + 
      " @bottom-left { font: " + fontStylePage + " white-space: nowrap; font-weight:bold; content: " + bottomLeftStr + ";}" + 
      " @bottom-right { font: " + fontStylePage + " white-space: nowrap; font-weight:bold; content: " + bottomRightStr + ";}" + "}";
    return stylePage;
  }
  
  private String httpClientGetPageFragmentHtml(String docName, String mode, int pageNumber) throws ApplicationException {
    String retPageFragment = null;
    try {
      HttpClient httpClient = new HttpClient();
      String requestName = "/mpdl/interface/page-fragment.xql?document=" + docName + "&mode=" + mode + "&pn=" + pageNumber + "&characterNormalization=orig";
      String urlStr = "http" + "://" + MpdlConstants.MPDL_EXIST_HOST_NAME + ":" + MpdlConstants.MPDL_EXIST_PORT + requestName;
      GetMethod method = new GetMethod(urlStr);
      httpClient.executeMethod(method); 
      byte[] responseBody = method.getResponseBody();
      retPageFragment = new String(responseBody, "utf-8");
      method.releaseConnection();
    } catch (HttpException e) {
      throw new ApplicationException(e);
    } catch (IOException e) {
      throw new ApplicationException(e);
    }
    return retPageFragment;
  }
  
  private String httpClientGetContentListHtml(String docName) throws ApplicationException {
    String retHtmlFragment = null;
    try {
      HttpClient httpClient = new HttpClient();
      String requestName = "/mpdl/interface/doc-query.xql?document=" + docName + "&queryType=toc&queryResultPageSize=10000";
      String urlStr = "http" + "://" + MpdlConstants.MPDL_EXIST_HOST_NAME + ":" + MpdlConstants.MPDL_EXIST_PORT + requestName;
      GetMethod method = new GetMethod(urlStr);
      httpClient.executeMethod(method); 
      byte[] responseBody = method.getResponseBody();
      retHtmlFragment = new String(responseBody, "utf-8");
      method.releaseConnection();
    } catch (HttpException e) {
      throw new ApplicationException(e);
    } catch (IOException e) {
      throw new ApplicationException(e);
    }
    return retHtmlFragment;
  }
  
  private int httpClientGetCountPages(MetadataRecord mdRecord) throws ApplicationException {
    int count = -1;
    String docName = mdRecord.getEXistIdentifier();
    String docBase = mdRecord.getDocBase();
    String pbTag = "echo:pb";
    if (docBase != null && docBase.equals("archimedes"))
      pbTag = "pb";
    else if (docBase != null && docBase.equals("tei"))
      pbTag = "TEI:pb";
    try {
      HttpClient httpClient = new HttpClient();
      String requestName = "/mpdl/interface/xquery.xql?document=" + docName + "&xquery=count(//" + pbTag + ")";
      String urlStr = "http" + "://" + MpdlConstants.MPDL_EXIST_HOST_NAME + ":" + MpdlConstants.MPDL_EXIST_PORT + requestName;
      GetMethod method = new GetMethod(urlStr);
      httpClient.executeMethod(method); 
      byte[] responseBody = method.getResponseBody();
      String xmlResult = new String(responseBody, "utf-8");
      method.releaseConnection();
      if (xmlResult != null && ! xmlResult.equals("")) {
        XmlUtil xmlUtil = XmlUtil.getInstance();
        String countPagesStr = xmlUtil.evaluateToString(xmlResult, "/result/queryResult/records/record/content", null);
        count = Integer.parseInt(countPagesStr);
      }
      if (count == 0)
        count = 1;  // if no pb tag found then document consists of one page
    } catch (HttpException e) {
      throw new ApplicationException(e);
    } catch (IOException e) {
      throw new ApplicationException(e);
    }
    return count;
  }
  
  private void write(String str, OutputStream out) throws ApplicationException {
    try {
      byte[] bytes = str.getBytes("utf-8");
      out.write(bytes, 0, bytes.length);
      out.flush();
    } catch (UnsupportedEncodingException e) {
      throw new ApplicationException(e);
    } catch (FileNotFoundException e) {
      throw new ApplicationException(e);
    } catch (IOException e) {
      throw new ApplicationException(e);
    } 
  }

  private void setFont(String fontFileName) throws ApplicationException {
    try {
      String existingFontFileName = fontFileNames.get(fontFileName);
      if (existingFontFileName == null) {
        fontFileNames.put(fontFileName, fontFileName);
        ITextFontResolver fontResolver = renderer.getFontResolver();
        fontResolver.addFont(fontFileName, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);  // Identy_H is Unicode Horizontal; not_embedded means not embedded in the PDF doc
      }
    } catch (XRRuntimeException e) {
      init();
      String message = e.getMessage();
      if (message.indexOf("nausikaa") > 0 && message.indexOf("500") > 0) {
        throw new ApplicationException("Could not fetch image from nausikaa2.rz-berlin.mpg.de: please try again later");
      }
      throw new ApplicationException(e);
    } catch (IOException e) {
      init();
      String message = e.getMessage();
      if (message.indexOf("nausikaa") > 0 && message.indexOf("500") > 0) {
        throw new ApplicationException("fetch image is not possible: " + message);
      }
      throw new ApplicationException(e);
    } catch (DocumentException e) {
      init();
      String message = e.getMessage();
      if (message.indexOf("nausikaa") > 0 && message.indexOf("500") > 0) {
        throw new ApplicationException("fetch image is not possible: " + message);
      }
      throw new ApplicationException(e);
    }
  }

  // old method: each page is set as an own html page
  public void createFileOld(boolean pdf, boolean html, String mode, MetadataRecord mdRecord) throws ApplicationException {
    OutputStream osPdf = null;
    OutputStream osHtml = null;
    OutputStream osHtmlPdf = null;
    String eXistIdentifier = mdRecord.getEXistIdentifier();
    String language = mdRecord.getLanguage();
    if (eXistIdentifier == null)
      throw new ApplicationException("Pdf/Html-Generation failed: no eXist-Identifier given in mdRecord");
    String eXistIdentifierWithoutExtension = eXistIdentifier.substring(0, eXistIdentifier.length() - 4);  // without ".xml"
    String destFileNamePdf = MpdlConstants.MPDL_EXIST_DATA_DIR + "/documents" + eXistIdentifierWithoutExtension + ".pdf";
    String destFileNameHtml = MpdlConstants.MPDL_EXIST_DATA_DIR + "/documents" + eXistIdentifierWithoutExtension + ".html";
    String destFileNameHtmlPdfTmp = MpdlConstants.MPDL_EXIST_DATA_DIR + "/documents" + eXistIdentifierWithoutExtension + "-4Pdf.html";
    try {
      // start document
      if (pdf) {
        osPdf = new FileOutputStream(new File(destFileNamePdf));
        osHtmlPdf = new FileOutputStream(new File(destFileNameHtmlPdfTmp));
      }
      if (html)
        osHtml = new FileOutputStream(new File(destFileNameHtml));
      int countPages = httpClientGetCountPages(mdRecord);
      // style page
      String pageStyleHtml = "float:left; clear:both; border: thin solid #808080; width: 21.0cm; margin-top: 0.2cm; margin-bottom: 1cm; margin-left: 0.7cm; margin-right: 0.7cm; padding: 0.2cm;";
      // firstPage
      String firstPageHtmlShort = getFirstPageHtml(mdRecord, true);
      String firstPageHtmlLong = getFirstPageHtml(mdRecord, false);
      String mdRecordStr = getMdRecordString(mdRecord);
      String htmlHeadStr = getHtmlHead(null, mdRecordStr);
      String fontStyle = getFontStyle(language);
      if(pdf) {
        write("<html>" + htmlHeadStr + "<body style=\"" + fontStyle +  "\">", osHtmlPdf);
        // first page
        write(firstPageHtmlLong, osHtmlPdf);
        renderer.setDocumentFromString("<html>" + htmlHeadStr + "<body style=\"" + fontStyle +  "\">" + firstPageHtmlLong + "</body></html>");
        renderer.layout();
        renderer.createPDF(osPdf, false);
      }
      if (html) {
        write("<html>" + htmlHeadStr + "<body style=\"" + fontStyle +  "\">", osHtml);
        // first page
        write("<div style=\"" + pageStyleHtml + "\">", osHtml);
        write(firstPageHtmlShort, osHtml);
        write("</div>", osHtml);
      }
      // table of content of document
      String htmlToc = getTocHtml(mdRecord);
      if (html && htmlToc != null) {
        write("<div style=\"" + pageStyleHtml + "\">", osHtml);
        write(htmlToc, osHtml);
        write("</div>", osHtml);
      }
      if(pdf && htmlToc != null) {
        write(htmlToc, osHtmlPdf);
        renderer.setDocumentFromString("<html>" + htmlHeadStr + "<body style=\"" + fontStyle +  "\">" + htmlToc  + "</body></html>");
        renderer.layout();
        renderer.writeNextDocument();
      }
      // all pages of the document
      for(int i=1; i<=countPages; i++) {
        String htmlPageFragment = httpClientGetPageFragmentHtml(eXistIdentifier, mode, i);
        htmlPageFragment = removeXmlStartString(htmlPageFragment);
        String pnHrefName = "<a name=\"pn" + i + "\"></a>";
        if (html) {
          write("<div style=\"" + "clear:both; text-align:right; width:21.0cm; font-weight:bold;" + "\">", osHtml);
          write(pnHrefName, osHtml);
          write("</div>", osHtml);
          write("<div style=\"" + pageStyleHtml + "\">", osHtml);
          String htmlPageFragmentWithImageUrl = htmlPageFragment.replaceAll("src=\"images/", "src=\"http://" + MpdlConstants.MPDL_FULL_EXIST_HOST_NAME + "/mpdl/images/");  // to find the camera.png file on webserver mpdl-proto
          write(htmlPageFragmentWithImageUrl, osHtml);
          write("</div>", osHtml);
        }
        htmlPageFragment = pnHrefName + htmlPageFragment;
        if(pdf) {
          String htmlPageFragmentWithImageUrl = htmlPageFragment.replaceAll("src=\"images/", "src=\"http://" + MpdlConstants.MPDL_FULL_EXIST_HOST_NAME + "/mpdl/images/");  // to find the camera.png file on webserver mpdl-proto
          String htmlPageFragmentSinglePage = htmlPageFragmentWithImageUrl.replaceAll("class=\"page\">", "class=\"singlePage\">");
          String pnPdf = "&quot;Page " + i + " (&quot; counter(page) &quot;)&quot;";
          String htmlPage = getPageHtmlDoc(htmlPageFragmentSinglePage, language, "&quot;&quot;", pnPdf, "&quot;&quot;", "&quot;&quot;");  
          write(htmlPage, osHtmlPdf);
          renderer.setDocumentFromString(htmlPage);
          try {
            renderer.layout();
            renderer.writeNextDocument();
          } catch (XRRuntimeException e) {
            System.out.println("XXXX: " + e.getMessage());
          }
        }
      }
      if (html) {
        write("</body></html>", osHtml);
      }
      // create PDF document
      if(pdf) {
        write("</body></html>", osHtmlPdf);
        osHtmlPdf.close();
        renderer.finishPDF();
      }
    } catch (Exception e) {
      init();
      String message = e.getMessage();
      if (message.indexOf("nausikaa") > 0 && message.indexOf("500") > 0) {
        throw new ApplicationException("Could not fetch image from nausikaa2.rz-berlin.mpg.de: please try again later");
      }
      throw new ApplicationException(e);
    } finally {
      try {
        osHtmlPdf.close();
        osPdf.close();
        osHtml.close();
        FileUtil.getInstance().deleteFile(destFileNameHtmlPdfTmp);
      } catch (IOException e) {
        // nothing
      }
    }
  }

}