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();
  }
  private void assertPageValid(final List<LogicalPageBox> pages, final int page, final long offset)
      throws Exception {
    final LogicalPageBox pageBox = pages.get(page);
    final long pageOffset = pageBox.getPageOffset();

    // ModelPrinter.INSTANCE.print(pageBox);

    final RenderNode[] elementsByNodeType =
        MatchFactory.findElementsByNodeType(pageBox, LayoutNodeTypes.TYPE_BOX_TABLE_SECTION);
    Assert.assertEquals(2, elementsByNodeType.length);
    final TableSectionRenderBox header = (TableSectionRenderBox) elementsByNodeType[0];
    Assert.assertEquals(TableSectionRenderBox.Role.HEADER, header.getDisplayRole());
    final TableSectionRenderBox body = (TableSectionRenderBox) elementsByNodeType[1];
    Assert.assertEquals(TableSectionRenderBox.Role.BODY, body.getDisplayRole());
    final RenderNode[] rows =
        MatchFactory.findElementsByNodeType(body, LayoutNodeTypes.TYPE_BOX_TABLE_ROW);
    Assert.assertTrue("Have rows on page " + page, rows.length > 0);

    Assert.assertEquals("Header starts at top of page " + page, pageOffset + offset, header.getY());
    Assert.assertEquals(
        "Row starts after the header on page " + page,
        header.getY() + header.getHeight(),
        rows[0].getY());

    final RenderNode[] table =
        MatchFactory.findElementsByNodeType(pageBox, LayoutNodeTypes.TYPE_BOX_TABLE);
    Assert.assertEquals(1, table.length);
    final RenderBox box = (RenderBox) table[0];
    final RenderNode lastChild = box.getLastChild();
    Assert.assertEquals(
        "Table height extends correctly on page " + page,
        box.getY() + box.getHeight(),
        lastChild.getY() + lastChild.getHeight());
  }
  public void testRichText() throws ReportProcessingException, ContentProcessingException {
    final Element e = new Element();
    e.setElementType(LabelType.INSTANCE);
    e.setAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.RICH_TEXT_TYPE, "text/html");
    e.setAttribute(
        AttributeNames.Core.NAMESPACE,
        AttributeNames.Core.VALUE,
        "Hi I am trying to use the <b>rich text type = text/html</b> in PRD version - 3.7.");
    e.getStyle().setStyleProperty(TextStyleKeys.FONTSIZE, 12);
    e.getStyle().setStyleProperty(TextStyleKeys.FONT, "Arial");
    e.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 285f);
    e.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, 20f);

    final MasterReport report = new MasterReport();
    report.getReportHeader().addElement(e);

    final LogicalPageBox logicalPageBox =
        DebugReportRunner.layoutSingleBand(report, report.getReportHeader(), false, false);
    logicalPageBox.getRepeatFooterArea().setY(logicalPageBox.getContentArea().getHeight());
    logicalPageBox.getFooterArea().setY(logicalPageBox.getContentArea().getHeight());

    // ModelPrinter.INSTANCE.print(logicalPageBox);

    final RenderNode[] elementsByNodeType =
        MatchFactory.findElementsByNodeType(logicalPageBox, LayoutNodeTypes.TYPE_NODE_TEXT);
    assertEquals(
        17, elementsByNodeType.length); // quick and easy way to see that all elements are there.

    // debugPrintText(elementsByNodeType);

    final LocalFontRegistry registry = new LocalFontRegistry();
    registry.initialize();

    final GraphicsOutputProcessorMetaData metaData =
        new GraphicsOutputProcessorMetaData(new DefaultFontStorage(registry));
    metaData.initialize(report.getConfiguration());

    final LogicalPageDrawable drawable = new LogicalPageDrawable();
    drawable.init(logicalPageBox, metaData, report.getResourceManager());

    final TracingGraphics g2 = new TracingGraphics();
    drawable.draw(g2, new Rectangle2D.Double(0, 0, 500, 500));
    /*
        for (int i = 0; i < g2.records.size(); i++)
        {
          final TextTraceRecord record = g2.records.get(i);
          System.out.println ("goldenSamples.add(new TextTraceRecord(" + record.x + ", " + record.y + ", \"" + record.text +"\"));");
        }
    */
    assertEquals(getSamples(), g2.records);
  }
  public void testPagebreakHonoredOnFirstPage() throws Exception {
    final MasterReport masterReport = DebugReportRunner.parseGoldenSampleReport("Pre-492.prpt");
    final LogicalPageBox page0 = DebugReportRunner.layoutPage(masterReport, 0);
    final RenderNode[] elementsByElementType =
        MatchFactory.findElementsByElementType(page0.getContentArea(), AutoLayoutBoxType.INSTANCE);
    assertEquals(31, elementsByElementType.length);
    assertEquals(
        StrictGeomUtility.toInternalValue(199),
        elementsByElementType[elementsByElementType.length - 1].getY());

    final LogicalPageBox page1 = DebugReportRunner.layoutPage(masterReport, 1);
    final RenderNode[] elementsPage1 =
        MatchFactory.findElementsByElementType(page1.getContentArea(), AutoLayoutBoxType.INSTANCE);
    assertEquals(34, elementsPage1.length);
    assertEquals(
        StrictGeomUtility.toInternalValue(211), elementsPage1[elementsPage1.length - 1].getY());
    //  ModelPrinter.INSTANCE.print(page1);
  }
  public void init(
      final LogicalPageBox rootBox,
      final PdfOutputProcessorMetaData metaData,
      final ResourceManager resourceManager,
      final PhysicalPageBox page) {
    super.init(rootBox, metaData, resourceManager);

    if (page != null) {
      this.globalHeight =
          (float)
              StrictGeomUtility.toExternalValue(
                  page.getHeight() - page.getImageableY() + page.getGlobalY());
    } else {
      this.globalHeight = rootBox.getPageHeight();
    }
    this.globalEmbed = getMetaData().isFeatureSupported(OutputProcessorFeature.EMBED_ALL_FONTS);
  }
  /** @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);
    }
  }