@SuppressWarnings("HardCodedStringLiteral")
public class PdfDocumentWriter {
  private static final Log logger = LogFactory.getLog(PdfDocumentWriter.class);

  /** A useful constant for specifying the PDF creator. */
  private static final String CREATOR =
      ClassicEngineInfo.getInstance().getName()
          + " version "
          + ClassicEngineInfo.getInstance().getVersion();

  /**
   * A bytearray containing an empty password. iText replaces the owner password with random values,
   * but Adobe allows to have encryption without an owner password set. Copied from iText
   */
  private static final byte[] PDF_PASSWORD_PAD = {
    (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, (byte) 0x4E, (byte) 0x75,
    (byte) 0x8A, (byte) 0x41, (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56,
    (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E,
    (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80,
    (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE, (byte) 0x64, (byte) 0x53,
    (byte) 0x69, (byte) 0x7A
  };

  private Document document;
  private PdfOutputProcessorMetaData metaData;
  private OutputStream out;
  private PdfWriter writer;
  private boolean awaitOpenDocument;
  private Configuration config;
  private ResourceManager resourceManager;
  private LFUMap<ResourceKey, com.lowagie.text.Image> imageCache;
  private char version;

  public PdfDocumentWriter(
      final PdfOutputProcessorMetaData metaData,
      final OutputStream out,
      final ResourceManager resourceManager) {
    if (metaData == null) {
      throw new NullPointerException();
    }
    if (out == null) {
      throw new NullPointerException();
    }
    if (resourceManager == null) {
      throw new NullPointerException();
    }

    this.imageCache = new LFUMap<ResourceKey, com.lowagie.text.Image>(50);
    this.resourceManager = resourceManager;
    this.metaData = metaData;
    this.out = out;
    this.config = metaData.getConfiguration();
  }

  private Document getDocument() {
    return document;
  }

  public void open() throws DocumentException {
    this.document = new Document();
    // pageSize, marginLeft, marginRight, marginTop, marginBottom));

    writer = PdfWriter.getInstance(document, out);
    writer.setLinearPageMode();

    version = getVersion();
    writer.setPdfVersion(version);
    writer.setViewerPreferences(getViewerPreferences());

    final String encrypt =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Encryption");

    if (encrypt != null) {
      if (encrypt.equals(PdfPageableModule.SECURITY_ENCRYPTION_128BIT) == true
          || encrypt.equals(PdfPageableModule.SECURITY_ENCRYPTION_40BIT) == true) {
        final String userpassword =
            config.getConfigProperty(
                "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.UserPassword");
        final String ownerpassword =
            config.getConfigProperty(
                "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.OwnerPassword");
        // Log.debug ("UserPassword: "******" - OwnerPassword: "******"org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Title",
            config.getConfigProperty("org.pentaho.reporting.engine.classic.core.metadata.Title"));
    final String subject =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Description",
            config.getConfigProperty(
                "org.pentaho.reporting.engine.classic.core.metadata.Description"));
    final String author =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Author",
            config.getConfigProperty("org.pentaho.reporting.engine.classic.core.metadata.Author"));
    final String keyWords =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Keywords",
            config.getConfigProperty(
                "org.pentaho.reporting.engine.classic.core.metadata.Keywords"));

    if (title != null) {
      document.addTitle(title);
    }
    if (author != null) {
      document.addAuthor(author);
    }
    if (keyWords != null) {
      document.addKeywords(keyWords);
    }
    if (keyWords != null) {
      document.addSubject(subject);
    }

    document.addCreator(PdfDocumentWriter.CREATOR);
    document.addCreationDate();

    // getDocument().open();
    awaitOpenDocument = true;
  }

  /**
   * Extracts the Page Layout and page mode settings for this PDF (ViewerPreferences). All
   * preferences are defined as properties which have to be set before the target is opened.
   *
   * @return the ViewerPreferences.
   */
  private int getViewerPreferences() {
    final String pageLayout =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PageLayout");
    final String pageMode =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PageMode");
    final String fullScreenMode =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.FullScreenMode");
    final boolean hideToolBar =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.HideToolBar"));
    final boolean hideMenuBar =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.HideMenuBar"));
    final boolean hideWindowUI =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.HideWindowUI"));
    final boolean fitWindow =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.FitWindow"));
    final boolean centerWindow =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.CenterWindow"));
    final boolean displayDocTitle =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.DisplayDocTitle"));
    final boolean printScalingNone =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PrintScalingNone"));
    final String direction =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Direction");

    int viewerPreferences = 0;
    if ("PageLayoutOneColumn".equals(pageLayout)) {
      viewerPreferences = PdfWriter.PageLayoutOneColumn;
    } else if ("PageLayoutSinglePage".equals(pageLayout)) {
      viewerPreferences = PdfWriter.PageLayoutSinglePage;
    } else if ("PageLayoutTwoColumnLeft".equals(pageLayout)) {
      viewerPreferences = PdfWriter.PageLayoutTwoColumnLeft;
    } else if ("PageLayoutTwoColumnRight".equals(pageLayout)) {
      viewerPreferences = PdfWriter.PageLayoutTwoColumnRight;
    } else if ("PageLayoutTwoPageLeft".equals(pageLayout)) {
      viewerPreferences = PdfWriter.PageLayoutTwoPageLeft;
    } else if ("PageLayoutTwoPageRight".equals(pageLayout)) {
      viewerPreferences = PdfWriter.PageLayoutTwoPageRight;
    }

    if ("PageModeUseNone".equals(pageMode)) {
      viewerPreferences |= PdfWriter.PageModeUseNone;
    } else if ("PageModeUseOutlines".equals(pageMode)) {
      viewerPreferences |= PdfWriter.PageModeUseOutlines;
    } else if ("PageModeUseThumbs".equals(pageMode)) {
      viewerPreferences |= PdfWriter.PageModeUseThumbs;
    } else if ("PageModeFullScreen".equals(pageMode)) {
      viewerPreferences |= PdfWriter.PageModeFullScreen;
      if ("NonFullScreenPageModeUseNone".equals(fullScreenMode)) {
        viewerPreferences = PdfWriter.NonFullScreenPageModeUseNone;
      } else if ("NonFullScreenPageModeUseOC".equals(fullScreenMode)) {
        viewerPreferences |= PdfWriter.NonFullScreenPageModeUseOC;
      } else if ("NonFullScreenPageModeUseOutlines".equals(fullScreenMode)) {
        viewerPreferences |= PdfWriter.NonFullScreenPageModeUseOutlines;
      } else if ("NonFullScreenPageModeUseThumbs".equals(fullScreenMode)) {
        viewerPreferences |= PdfWriter.NonFullScreenPageModeUseThumbs;
      }
    } else if ("PageModeUseOC".equals(pageMode)) {
      viewerPreferences |= PdfWriter.PageModeUseOC;
    } else if ("PageModeUseAttachments".equals(pageMode)) {
      viewerPreferences |= PdfWriter.PageModeUseAttachments;
    }

    if (hideToolBar) {
      viewerPreferences |= PdfWriter.HideToolbar;
    }

    if (hideMenuBar) {
      viewerPreferences |= PdfWriter.HideMenubar;
    }

    if (hideWindowUI) {
      viewerPreferences |= PdfWriter.HideWindowUI;
    }

    if (fitWindow) {
      viewerPreferences |= PdfWriter.FitWindow;
    }
    if (centerWindow) {
      viewerPreferences |= PdfWriter.CenterWindow;
    }
    if (displayDocTitle) {
      viewerPreferences |= PdfWriter.DisplayDocTitle;
    }
    if (printScalingNone) {
      viewerPreferences |= PdfWriter.PrintScalingNone;
    }

    if ("DirectionL2R".equals(direction)) {
      viewerPreferences |= PdfWriter.DirectionL2R;
    } else if ("DirectionR2L".equals(direction)) {
      viewerPreferences |= PdfWriter.DirectionR2L;
    }

    if (logger.isDebugEnabled()) {
      logger.debug("viewerPreferences = 0b" + Integer.toBinaryString(viewerPreferences));
    }
    return viewerPreferences;
  }

  public void processPhysicalPage(
      final PageGrid pageGrid,
      final LogicalPageBox logicalPage,
      final int row,
      final int col,
      final PhysicalPageKey pageKey)
      throws DocumentException {
    final PhysicalPageBox page = pageGrid.getPage(row, col);
    if (page == null) {
      return;
    }

    final float width = (float) StrictGeomUtility.toExternalValue(page.getWidth());
    final float height = (float) StrictGeomUtility.toExternalValue(page.getHeight());

    final Rectangle pageSize = new Rectangle(width, height);

    final float marginLeft = (float) StrictGeomUtility.toExternalValue(page.getImageableX());
    final float marginRight =
        (float)
            StrictGeomUtility.toExternalValue(
                page.getWidth() - page.getImageableWidth() - page.getImageableX());
    final float marginTop = (float) StrictGeomUtility.toExternalValue(page.getImageableY());
    final float marginBottom =
        (float)
            StrictGeomUtility.toExternalValue(
                page.getHeight() - page.getImageableHeight() - page.getImageableY());

    final Document document = getDocument();
    document.setPageSize(pageSize);
    document.setMargins(marginLeft, marginRight, marginTop, marginBottom);

    if (awaitOpenDocument) {
      document.open();
      awaitOpenDocument = false;
    }

    final PdfContentByte directContent = writer.getDirectContent();
    final Graphics2D graphics = new PdfGraphics2D(directContent, width, height, metaData);
    final PdfLogicalPageDrawable logicalPageDrawable =
        new PdfLogicalPageDrawable(
            logicalPage, metaData, writer, page, resourceManager, imageCache, version);
    final PhysicalPageDrawable drawable = new PhysicalPageDrawable(logicalPageDrawable, page);
    drawable.draw(graphics, new Rectangle2D.Double(0, 0, width, height));

    graphics.dispose();

    document.newPage();
  }

  public void processLogicalPage(final LogicalPageKey key, final LogicalPageBox logicalPage)
      throws DocumentException {

    final float width = (float) StrictGeomUtility.toExternalValue(logicalPage.getPageWidth());
    final float height = (float) StrictGeomUtility.toExternalValue(logicalPage.getPageHeight());

    final Rectangle pageSize = new Rectangle(width, height);

    final Document document = getDocument();
    document.setPageSize(pageSize);
    document.setMargins(0, 0, 0, 0);

    if (awaitOpenDocument) {
      document.open();
      awaitOpenDocument = false;
    }

    final Graphics2D graphics =
        new PdfGraphics2D(writer.getDirectContent(), width, height, metaData);
    // and now process the box ..
    final PdfLogicalPageDrawable logicalPageDrawable =
        new PdfLogicalPageDrawable(
            logicalPage, metaData, writer, null, resourceManager, imageCache, version);
    logicalPageDrawable.draw(graphics, new Rectangle2D.Double(0, 0, width, height));

    graphics.dispose();

    document.newPage();
  }

  /** Closes the document. */
  public void close() {
    this.getDocument().close();
    try {
      this.out.flush();
    } catch (IOException e) {
      PdfDocumentWriter.logger.info("Flushing the PDF-Export-Stream failed.");
    }
    this.document = null;
    this.writer = null;
  }

  /**
   * Extracts the to be generated PDF version as iText parameter from the given property value. The
   * value has the form "1.x" where x is the extracted version.
   *
   * @return the itext character defining the version.
   */
  private char getVersion() {
    final String version =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.Version");

    if (version == null) {
      return '5';
    }
    if (version.length() < 3) {
      PdfDocumentWriter.logger.warn(
          "PDF version specification is invalid, using default version '1.4'.");
      return '5';
    }
    final char retval = version.charAt(2);
    if (retval < '2' || retval > '9') {
      PdfDocumentWriter.logger.warn(
          "PDF version specification is invalid, using default version '1.4'.");
      return '5';
    }
    return retval;
  }

  /**
   * Extracts the permissions for this PDF. The permissions are returned as flags in the integer
   * value. All permissions are defined as properties which have to be set before the target is
   * opened.
   *
   * @return the permissions.
   */
  private int getPermissions() {
    final String printLevel =
        config.getConfigProperty(
            "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PrintLevel");

    final boolean allowPrinting = "none".equals(printLevel) == false;
    final boolean allowDegradedPrinting = "degraded".equals(printLevel);

    final boolean allowModifyContents =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.AllowModifyContents"));
    final boolean allowModifyAnn =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.AllowModifyAnnotations"));

    final boolean allowCopy =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.AllowCopy"));
    final boolean allowFillIn =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.AllowFillIn"));
    final boolean allowScreenReaders =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.AllowScreenReader"));
    final boolean allowAssembly =
        "true"
            .equals(
                config.getConfigProperty(
                    "org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.AllowAssembly"));

    int permissions = 0;
    if (allowPrinting) {
      permissions |= PdfWriter.ALLOW_PRINTING;
    }
    if (allowModifyContents) {
      permissions |= PdfWriter.ALLOW_MODIFY_CONTENTS;
    }
    if (allowModifyAnn) {
      permissions |= PdfWriter.ALLOW_MODIFY_ANNOTATIONS;
    }
    if (allowCopy) {
      permissions |= PdfWriter.ALLOW_COPY;
    }
    if (allowFillIn) {
      permissions |= PdfWriter.ALLOW_FILL_IN;
    }
    if (allowScreenReaders) {
      permissions |= PdfWriter.ALLOW_SCREENREADERS;
    }
    if (allowAssembly) {
      permissions |= PdfWriter.ALLOW_ASSEMBLY;
    }
    if (allowDegradedPrinting) {
      permissions |= PdfWriter.ALLOW_DEGRADED_PRINTING;
    }
    return permissions;
  }
}
/**
 * Creation-Date: 09.05.2007, 14:52:05
 *
 * @author Thomas Morgner
 */
public class RTFPrinter {
  private static final Log logger = LogFactory.getLog(RTFPrinter.class);

  private static final String CREATOR =
      ClassicEngineInfo.getInstance().getName()
          + " version "
          + ClassicEngineInfo.getInstance().getVersion();

  private OutputStream outputStream;
  private Configuration config;
  private ResourceManager resourceManager;
  private RTFImageCache imageCache;
  private Document document;
  private Table table;
  private CellBackgroundProducer cellBackgroundProducer;

  public RTFPrinter() {}

  public void init(
      final Configuration config,
      final OutputStream outputStream,
      final ResourceManager resourceManager) {
    this.outputStream = outputStream;
    this.config = config;
    this.resourceManager = resourceManager;
  }

  /** @noinspection IOResourceOpenedButNotSafelyClosed */
  public void print(
      final LogicalPageKey logicalPageKey,
      final LogicalPageBox logicalPage,
      final TableContentProducer contentProducer,
      final RTFOutputProcessorMetaData metaData,
      final boolean incremental)
      throws ContentProcessingException {
    final int startRow = contentProducer.getFinishedRows();
    final int finishRow = contentProducer.getFilledRows();
    if (incremental && startRow == finishRow) {
      return;
    }

    if (document == null) {
      this.cellBackgroundProducer =
          new CellBackgroundProducer(
              metaData.isFeatureSupported(AbstractTableOutputProcessor.TREAT_ELLIPSE_AS_RECTANGLE),
              metaData.isFeatureSupported(OutputProcessorFeature.UNALIGNED_PAGEBANDS));

      final PhysicalPageBox pageFormat = logicalPage.getPageGrid().getPage(0, 0);
      final float urx = (float) StrictGeomUtility.toExternalValue(pageFormat.getWidth());
      final float ury = (float) StrictGeomUtility.toExternalValue(pageFormat.getHeight());

      final float marginLeft =
          (float) StrictGeomUtility.toExternalValue(pageFormat.getImageableX());
      final float marginRight =
          (float)
              StrictGeomUtility.toExternalValue(
                  pageFormat.getWidth()
                      - pageFormat.getImageableWidth()
                      - pageFormat.getImageableX());
      final float marginTop = (float) StrictGeomUtility.toExternalValue(pageFormat.getImageableY());
      final float marginBottom =
          (float)
              StrictGeomUtility.toExternalValue(
                  pageFormat.getHeight()
                      - pageFormat.getImageableHeight()
                      - pageFormat.getImageableY());
      final Rectangle pageSize = new Rectangle(urx, ury);

      document = new Document(pageSize, marginLeft, marginRight, marginTop, marginBottom);
      imageCache = new RTFImageCache(resourceManager);

      // rtf does not support PageFormats or other meta data...
      final PatchRtfWriter2 instance =
          PatchRtfWriter2.getInstance(document, new NoCloseOutputStream(outputStream));
      instance.getDocumentSettings().setAlwaysUseUnicode(true);

      final String author =
          config.getConfigProperty(
              "org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.Author");
      if (author != null) {
        document.addAuthor(author);
      }

      final String title =
          config.getConfigProperty(
              "org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.Title");
      if (title != null) {
        document.addTitle(title);
      }

      document.addProducer();
      document.addCreator(RTFPrinter.CREATOR);

      try {
        document.addCreationDate();
      } catch (Exception e) {
        RTFPrinter.logger.debug("Unable to add creation date. It will have to work without it.", e);
      }

      document.open();
    }

    // Start a new page.
    try {
      final SheetLayout sheetLayout = contentProducer.getSheetLayout();
      final int columnCount = contentProducer.getColumnCount();
      if (table == null) {
        final int rowCount = contentProducer.getRowCount();
        table = new Table(columnCount, rowCount);
        table.setAutoFillEmptyCells(false);
        table.setWidth(100); // span the full page..
        // and finally the content ..

        final float[] cellWidths = new float[columnCount];
        for (int i = 0; i < columnCount; i++) {
          cellWidths[i] =
              (float) StrictGeomUtility.toExternalValue(sheetLayout.getCellWidth(i, i + 1));
        }
        table.setWidths(cellWidths);
      }

      // logger.debug ("Processing: " + startRow + " " + finishRow + " " + incremental);

      for (int row = startRow; row < finishRow; row++) {
        for (short col = 0; col < columnCount; col++) {
          final RenderBox content = contentProducer.getContent(row, col);
          final CellMarker.SectionType sectionType = contentProducer.getSectionType(row, col);

          if (content == null) {
            final RenderBox backgroundBox = contentProducer.getBackground(row, col);
            final CellBackground background;
            if (backgroundBox != null) {
              background =
                  cellBackgroundProducer.getBackgroundForBox(
                      logicalPage, sheetLayout, col, row, 1, 1, true, sectionType, backgroundBox);
            } else {
              background =
                  cellBackgroundProducer.getBackgroundAt(
                      logicalPage, sheetLayout, col, row, true, sectionType);
            }
            if (background == null) {
              // An empty cell .. ignore
              final PatchRtfCell cell = new PatchRtfCell();
              cell.setBorderWidth(0);
              cell.setMinimumHeight(
                  (float) StrictGeomUtility.toExternalValue(sheetLayout.getRowHeight(row)));
              table.addCell(cell, row, col);
              continue;
            }

            // A empty cell with a defined background ..
            final PatchRtfCell cell = new PatchRtfCell();
            cell.setBorderWidth(0);
            cell.setMinimumHeight(
                (float) StrictGeomUtility.toExternalValue(sheetLayout.getRowHeight(row)));
            updateCellStyle(cell, background);
            table.addCell(cell, row, col);
            continue;
          }

          if (content.isCommited() == false) {
            throw new InvalidReportStateException("Uncommited content encountered");
          }

          final long contentOffset = contentProducer.getContentOffset(row, col);
          final long colPos = sheetLayout.getXPosition(col);
          final long rowPos = sheetLayout.getYPosition(row);
          if (content.getX() != colPos || (content.getY() + contentOffset) != rowPos) {
            // A spanned cell ..
            continue;
          }

          final int colSpan = sheetLayout.getColSpan(col, content.getX() + content.getWidth());
          final int rowSpan =
              sheetLayout.getRowSpan(row, content.getY() + content.getHeight() + contentOffset);

          final CellBackground realBackground =
              cellBackgroundProducer.getBackgroundForBox(
                  logicalPage,
                  sheetLayout,
                  col,
                  row,
                  colSpan,
                  rowSpan,
                  false,
                  sectionType,
                  content);

          final PatchRtfCell cell = new PatchRtfCell();
          cell.setRowspan(rowSpan);
          cell.setColspan(colSpan);
          cell.setBorderWidth(0);
          cell.setMinimumHeight(
              (float) StrictGeomUtility.toExternalValue(sheetLayout.getRowHeight(row)));
          if (realBackground != null) {
            updateCellStyle(cell, realBackground);
          }

          computeCellStyle(content, cell);

          // export the cell and all content ..
          final RTFTextExtractor etx = new RTFTextExtractor(metaData);
          etx.compute(content, cell, imageCache);

          table.addCell(cell, row, col);
          content.setFinishedTable(true);
          // logger.debug("set Finished to cell (" + col + ", " + row + "," + content.getName() +
          // ")");
        }
      }

      if (incremental == false) {
        document.add(table);
        table = null;
      }
    } catch (DocumentException e) {
      throw new ContentProcessingException("Failed to generate RTF-Document", e);
    }
  }

  private void computeCellStyle(final RenderBox content, final Cell cell) {
    final ElementAlignment verticalAlign = content.getNodeLayoutProperties().getVerticalAlignment();
    if (ElementAlignment.BOTTOM.equals(verticalAlign)) {
      cell.setVerticalAlignment(Element.ALIGN_BOTTOM);
    } else if (ElementAlignment.MIDDLE.equals(verticalAlign)) {
      cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
    } else {
      cell.setVerticalAlignment(Element.ALIGN_TOP);
    }

    final ElementAlignment textAlign =
        (ElementAlignment) content.getStyleSheet().getStyleProperty(ElementStyleKeys.ALIGNMENT);
    if (ElementAlignment.RIGHT.equals(textAlign)) {
      cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
    } else if (ElementAlignment.JUSTIFY.equals(textAlign)) {
      cell.setHorizontalAlignment(Element.ALIGN_JUSTIFIED);
    } else if (ElementAlignment.CENTER.equals(textAlign)) {
      cell.setHorizontalAlignment(Element.ALIGN_CENTER);
    } else {
      cell.setHorizontalAlignment(Element.ALIGN_LEFT);
    }
  }

  public void close() {
    if (document != null) {
      // cleanup..
      document.close();
      try {
        outputStream.flush();
      } catch (IOException e) {
        RTFPrinter.logger.info("Failed to flush the RTF-Output stream.");
      } finally {
        document = null;
      }
    }
  }

  private int translateBorderStyle(final BorderStyle borderStyle) {
    if (BorderStyle.DASHED.equals(borderStyle)) {
      return RtfBorder.BORDER_DASHED;
    }
    if (BorderStyle.DOT_DASH.equals(borderStyle)) {
      return RtfBorder.BORDER_DOT_DASH;
    }
    if (BorderStyle.DOT_DOT_DASH.equals(borderStyle)) {
      return RtfBorder.BORDER_DOT_DOT_DASH;
    }
    if (BorderStyle.DOTTED.equals(borderStyle)) {
      return RtfBorder.BORDER_DOTTED;
    }
    if (BorderStyle.DOUBLE.equals(borderStyle)) {
      return RtfBorder.BORDER_DOUBLE;
    }
    if (BorderStyle.HIDDEN.equals(borderStyle)) {
      return RtfBorder.BORDER_NONE;
    }
    if (BorderStyle.NONE.equals(borderStyle)) {
      return RtfBorder.BORDER_NONE;
    }
    if (BorderStyle.GROOVE.equals(borderStyle)) {
      return RtfBorder.BORDER_ENGRAVE;
    }
    if (BorderStyle.RIDGE.equals(borderStyle)) {
      return RtfBorder.BORDER_EMBOSS;
    }
    if (BorderStyle.INSET.equals(borderStyle)) {
      return RtfBorder.BORDER_SINGLE;
    }
    if (BorderStyle.OUTSET.equals(borderStyle)) {
      return RtfBorder.BORDER_SINGLE;
    }
    if (BorderStyle.SOLID.equals(borderStyle)) {
      return RtfBorder.BORDER_SINGLE;
    }
    if (BorderStyle.WAVE.equals(borderStyle)) {
      return RtfBorder.BORDER_WAVY;
    }
    return RtfBorder.BORDER_NONE;
  }

  private void updateCellStyle(final Cell cell, final CellBackground background) {

    final Color backgroundColor = background.getBackgroundColor();
    if (backgroundColor != null) {
      cell.setBackgroundColor(backgroundColor);
    }
    final BorderEdge top = background.getTop();
    if (BorderEdge.EMPTY.equals(top) == false) {
      cell.setBorderColorTop(top.getColor());
      cell.setBorderWidthTop((float) StrictGeomUtility.toExternalValue(top.getWidth()));
    }

    final BorderEdge left = background.getLeft();
    if (BorderEdge.EMPTY.equals(left) == false) {
      cell.setBorderColorLeft(left.getColor());
      cell.setBorderWidthLeft((float) StrictGeomUtility.toExternalValue(left.getWidth()));
    }

    final BorderEdge bottom = background.getBottom();
    if (BorderEdge.EMPTY.equals(bottom) == false) {
      cell.setBorderColorBottom(bottom.getColor());
      cell.setBorderWidthBottom((float) StrictGeomUtility.toExternalValue(bottom.getWidth()));
    }

    final BorderEdge right = background.getRight();
    if (BorderEdge.EMPTY.equals(right) == false) {
      cell.setBorderColorRight(right.getColor());
      cell.setBorderWidthRight((float) StrictGeomUtility.toExternalValue(right.getWidth()));
    }
  }
}