protected void processOtherNode(final RenderNode node) {
   if (isValidDrawTarget(node)) {
     if (bounds == null) {
       bounds = new StrictBounds(node.getX(), node.getY(), node.getWidth(), node.getHeight());
     } else {
       bounds.add(node.getX(), node.getY(), node.getWidth(), node.getHeight());
     }
   }
 }
  protected void drawHyperlink(
      final RenderNode box, final String target, final String window, final String title) {
    if (box.isNodeVisible(getDrawArea()) == false) {
      return;
    }

    final PdfAction action = createActionForLink(target);

    final AffineTransform affineTransform = getGraphics().getTransform();
    final float translateX = (float) affineTransform.getTranslateX();

    final float leftX = translateX + (float) (StrictGeomUtility.toExternalValue(box.getX()));
    final float rightX =
        translateX + (float) (StrictGeomUtility.toExternalValue(box.getX() + box.getWidth()));
    final float lowerY =
        (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY() + box.getHeight()));
    final float upperY = (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY()));

    if (action != null) {
      final PdfAnnotation annotation =
          new PdfAnnotation(writer, leftX, lowerY, rightX, upperY, action);
      writer.addAnnotation(annotation);
    } else if (StringUtils.isEmpty(title) == false) {
      final Rectangle rect = new Rectangle(leftX, lowerY, rightX, upperY);
      final PdfAnnotation commentAnnotation =
          PdfAnnotation.createText(writer, rect, "Tooltip", title, false, null);
      commentAnnotation.setAppearance(
          PdfAnnotation.APPEARANCE_NORMAL,
          writer.getDirectContent().createAppearance(rect.getWidth(), rect.getHeight()));
      writer.addAnnotation(commentAnnotation);
    }
  }
  protected boolean drawPdfScript(final RenderNode box) {
    final Object attribute =
        box.getAttributes()
            .getAttribute(AttributeNames.Pdf.NAMESPACE, AttributeNames.Pdf.SCRIPT_ACTION);
    if (attribute == null) {
      return false;
    }

    final String attributeText = String.valueOf(attribute);
    final PdfAction action = PdfAction.javaScript(attributeText, writer, false);

    final AffineTransform affineTransform = getGraphics().getTransform();
    final float translateX = (float) affineTransform.getTranslateX();

    final float leftX = translateX + (float) (StrictGeomUtility.toExternalValue(box.getX()));
    final float rightX =
        translateX + (float) (StrictGeomUtility.toExternalValue(box.getX() + box.getWidth()));
    final float lowerY =
        (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY() + box.getHeight()));
    final float upperY = (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY()));
    final PdfAnnotation annotation =
        new PdfAnnotation(writer, leftX, lowerY, rightX, upperY, action);
    writer.addAnnotation(annotation);
    return true;
  }
  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 testWeirdTocLayout() throws ReportProcessingException, ContentProcessingException {
    Element textField = new Element();
    textField.setName("textField");
    textField.getStyle().setStyleProperty(TextStyleKeys.FONT, "Arial");
    textField.getStyle().setStyleProperty(TextStyleKeys.FONTSIZE, 14);
    textField.getStyle().setStyleProperty(TextStyleKeys.TEXT_WRAP, TextWrap.NONE);
    textField.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 97f);
    textField.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, 20f);
    textField.getStyle().setStyleProperty(ElementStyleKeys.POS_X, 0f);
    textField.getStyle().setStyleProperty(ElementStyleKeys.POS_Y, 0f);
    textField.setElementType(LabelType.INSTANCE);
    textField.setAttribute(
        AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, "Classic Cars");

    Element dotField = new Element();
    dotField.setName("dotField");
    dotField.getStyle().setStyleProperty(TextStyleKeys.FONT, "Arial");
    dotField.getStyle().setStyleProperty(TextStyleKeys.FONTSIZE, 14);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.ALIGNMENT, ElementAlignment.RIGHT);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.VALIGNMENT, ElementAlignment.TOP);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.POS_X, 97f);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.POS_Y, 0f);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 628.463f);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 20f);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.WIDTH, 100f);
    dotField.getStyle().setStyleProperty(ElementStyleKeys.MAX_WIDTH, 100f);
    dotField.setElementType(LabelType.INSTANCE);
    dotField.setAttribute(
        AttributeNames.Core.NAMESPACE,
        AttributeNames.Core.VALUE,
        " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
            + " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
            + ". . . . . . . . . . . . . . . . . .");

    Band band = new Band();
    band.setName("outer-box");
    band.setLayout("inline");
    band.getStyle().setStyleProperty(ElementStyleKeys.BOX_SIZING, BoxSizing.CONTENT_BOX);
    band.getStyle().setStyleProperty(ElementStyleKeys.OVERFLOW_X, false);
    band.getStyle().setStyleProperty(ElementStyleKeys.OVERFLOW_Y, false);
    band.getStyle().setStyleProperty(TextStyleKeys.LINEHEIGHT, 1f);
    band.getStyle()
        .setStyleProperty(TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.PRESERVE_BREAKS);
    band.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 708f);
    band.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, 12f);
    band.getStyle().setStyleProperty(ElementStyleKeys.MAX_HEIGHT, 20f);
    band.addElement(textField);
    band.addElement(dotField);

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

    LogicalPageBox logicalPageBox =
        DebugReportRunner.layoutSingleBand(report, report.getReportHeader(), false, false);
    // ModelPrinter.INSTANCE.print(logicalPageBox);

    RenderBox outerBox = (RenderBox) MatchFactory.findElementByName(logicalPageBox, "outer-box");
    RenderNode dotFieldBox = MatchFactory.findElementByName(logicalPageBox, "dotField");
    RenderNode textFieldBox = MatchFactory.findElementByName(logicalPageBox, "textField");
    assertNotNull(outerBox);
    assertNotNull(dotFieldBox);
    assertNotNull(textFieldBox);

    assertEquals(0, outerBox.getY());
    assertEquals(0, dotFieldBox.getY());
    assertEquals(0, textFieldBox.getY());

    // box only contains one line, and min-size is set to 8, max size = 20, so the line-height of
    // 14.024 is used.
    assertEquals(StrictGeomUtility.toInternalValue(14.024), outerBox.getHeight());
    assertEquals(StrictGeomUtility.toInternalValue(14.024), outerBox.getFirstChild().getHeight());
    assertEquals(StrictGeomUtility.toInternalValue(14), dotFieldBox.getHeight());
    assertEquals(StrictGeomUtility.toInternalValue(14), textFieldBox.getHeight());
  }