protected boolean startRowLevelBox(final RenderBox box) {
    if (lineBreakState.isInsideParagraph()) {
      // The break-state exists only while we are inside of an paragraph
      // and suspend can only happen on inline elements.
      // A block-element inside a paragraph cannot be (and if it does, it is
      // a bug)
      throw new IllegalStateException("This cannot be.");
    }

    nodeContext = nodeContextPool.createContext(box, nodeContext, false);

    if (checkCacheValid(box)) {
      return false;
    }

    startTableContext(box);

    final long x = computeRowPosition(box);
    final long left = box.getInsetsLeft();
    final long right = box.getInsetsRight();
    final long width = MinorAxisLayoutStepUtil.resolveNodeWidthOnStart(box, nodeContext, x);
    assert width >= 0;

    nodeContext.setArea(x, left, right, width);

    if (startParagraphBox(box) == false) {
      return false;
    }

    return true;
  }
  protected void finishRowBox(final RenderBox box) {
    if (box.getStaticBoxLayoutProperties().isVisible() == false) {
      return;
    }

    textExtractorHelper.finishBox(box.getInstanceId(), box.getAttributes());
  }
  protected boolean startTableRowLevelBox(final RenderBox box) {
    if (lineBreakState.isInsideParagraph()) {
      // The break-state exists only while we are inside of an paragraph
      // and suspend can only happen on inline elements.
      // A block-element inside a paragraph cannot be (and if it does, it is
      // a bug)
      throw new IllegalStateException("This cannot be.");
    }

    nodeContext = nodeContextPool.createContext(box, nodeContext, false);

    if (box.getNodeType() != LayoutNodeTypes.TYPE_BOX_TABLE_CELL) {
      startTableSectionOrRow(box);
      return true;
    }

    final MinorAxisTableContext tableContext = getTableContext();
    final TableCellRenderBox tableCellRenderBox = (TableCellRenderBox) box;

    // This is slightly different for table cells ...
    final int columnIndex = tableCellRenderBox.getColumnIndex();
    final TableColumnModel columnModel = tableContext.getColumnModel();

    // cell-size does not include border spacing
    final long startOfRowX = nodeContext.getParentX1();

    final long x = startOfRowX + columnModel.getCellPosition(columnIndex);
    final long insetsLeft = Math.max(box.getInsetsLeft(), columnModel.getBorderSpacing() / 2);
    final long insetsRight = Math.max(box.getInsetsRight(), columnModel.getBorderSpacing() / 2);
    final long width = computeCellWidth(tableCellRenderBox);
    nodeContext.setArea(x, insetsLeft, insetsRight, width);
    return true;
  }
  public void processContent(
      final ReportElement element, final Object computedValue, final Object rawValue) {
    if (computedValue == null) {
      final StyleSheet resolvedStyle = element.getComputedStyle();
      final RenderBox parentRenderBox = this.context.getRenderBox();
      if (parentRenderBox.isEmptyNodesHaveSignificance()
          || metaData.isExtraContentElement(resolvedStyle, element.getAttributes())) {
        ensureEmptyChildIsAdded(parentRenderBox, element);
        this.context.setEmpty(false);
      }
      return;
    }

    if (String.class.equals(computedValue.getClass())) {
      processText(element, (String) computedValue, rawValue);
    } else if (computedValue instanceof Shape) {
      final StyleSheet resolvedStyle = element.getComputedStyle();
      final Shape shape = (Shape) computedValue;
      final ReportDrawable reportDrawable =
          new ShapeDrawable(
              shape, resolvedStyle.getBooleanStyleProperty(ElementStyleKeys.KEEP_ASPECT_RATIO));
      processReportDrawable(element, reportDrawable, rawValue);
    } else if (computedValue instanceof ReportDrawable) {
      processReportDrawable(element, (ReportDrawable) computedValue, rawValue);
    } else if (computedValue instanceof ImageContainer
        || computedValue instanceof DrawableWrapper) {
      processReplacedContent(element, computedValue, rawValue);
    } else if (DrawableWrapper.isDrawable(computedValue)) {
      processReplacedContent(element, new DrawableWrapper(computedValue), rawValue);
    } else {
      processText(element, String.valueOf(computedValue), rawValue);
    }
  }
  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());
  }
  protected boolean startBlockLevelBox(final RenderBox box) {
    if (lineBreakState.isInsideParagraph()) {
      throw new InvalidReportStateException(
          "A block-level element inside a paragraph is not allowed.");
    }

    nodeContext = nodeContextPool.createContext(box, nodeContext, true);

    if (checkCacheValid(box)) {
      return false;
    }

    startTableContext(box);

    final long x = nodeContext.getParentX1();
    final long left = box.getInsetsLeft();
    final long right = box.getInsetsRight();
    final long width = MinorAxisLayoutStepUtil.resolveNodeWidthOnStart(box, nodeContext, x);

    assert width >= 0;

    nodeContext.setArea(x, left, right, width);

    if (startParagraphBox(box) == false) {
      return false;
    }

    return true;
  }
  protected void finishTableRowLevelBox(final RenderBox box) {
    try {
      box.setCachedX(nodeContext.getX());
      box.setContentAreaX1(nodeContext.getX1());
      box.setContentAreaX2(nodeContext.getX2());

      if (box.getNodeType() != LayoutNodeTypes.TYPE_BOX_TABLE_CELL) {
        // break-marker boxes etc.
        box.setCachedWidth(resolveTableWidthOnFinish(box));
        nodeContext.updateParentX2(box.getCachedX2());
      } else {
        box.setCachedWidth(
            MinorAxisLayoutStepUtil.resolveNodeWidthOnFinish(
                box, nodeContext, isStrictLegacyMode()));

        final TableCellRenderBox cell = (TableCellRenderBox) box;
        final MinorAxisTableContext tableContext = getTableContext();
        final TableRenderBox table = tableContext.getTable();
        if (tableContext.isStructureValidated() == false) {
          table
              .getColumnModel()
              .updateCellSize(
                  cell.getColumnIndex(), cell.getColSpan(), box.getCachedWidth() - box.getInsets());
        }
        nodeContext.updateParentX2(box.getCachedX2());
      }
    } finally {
      nodeContext = nodeContext.pop();
    }
  }
  public boolean isEmptyElementsHaveSignificance() {
    if (designtime) {
      return true;
    }

    final RenderBox box = this.context.getRenderBox();
    return box.isEmptyNodesHaveSignificance();
  }
 public InstanceID createSubflowPlaceholder(final ReportElement element) {
   final StyleSheet resolverStyleSheet = element.getComputedStyle();
   final RenderBox subReportBox =
       renderNodeFactory.produceSubReportPlaceholder(element, resolverStyleSheet, stateKey);
   this.context.addChild(subReportBox);
   this.context.setEmpty(false);
   return subReportBox.getInstanceId();
 }
  public boolean isEmptyElementsHaveSignificanceInParent() {
    final LayoutModelBuilderContext parent = this.context.getParent();
    if (parent == null) {
      return false;
    }

    final RenderBox box = parent.getRenderBox();
    return box.isEmptyNodesHaveSignificance();
  }
  protected void computeInlineBlock(
      final RenderBox box, final long position, final long itemElementWidth) {
    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    box.setCachedX(position + blp.getMarginLeft());
    final long width = itemElementWidth - blp.getMarginLeft() - blp.getMarginRight();
    if (width == 0) {
      // ModelPrinter.printParents(box);

      throw new IllegalStateException(
          "A box without any width? "
              + Integer.toHexString(System.identityHashCode(box))
              + ' '
              + box.getClass());
    }
    box.setCachedWidth(width);

    final BoxDefinition bdef = box.getBoxDefinition();
    final long leftInsets = bdef.getPaddingLeft() + blp.getBorderLeft();
    final long rightInsets = bdef.getPaddingRight() + blp.getBorderRight();
    box.setContentAreaX1(box.getCachedX() + leftInsets);
    box.setContentAreaX2(box.getCachedX() + box.getCachedWidth() - rightInsets);

    //    final InfiniteMinorAxisLayoutStep layoutStep = new InfiniteMinorAxisLayoutStep(metaData);
    //    layoutStep.continueComputation(getPageGrid(), box);
  }
  /**
   * Like a canvas box, a row-box should be split into several cells already. Therefore we treat it
   * as a generic content container instead.
   */
  protected boolean startRowBox(final RenderBox box) {
    if (box.getStaticBoxLayoutProperties().isVisible() == false) {
      return false;
    }

    return textExtractorHelper.startBox(
        box.getInstanceId(),
        box.getAttributes(),
        box.getStyleSheet(),
        box.getBoxDefinition(),
        true);
  }
  protected void finishTableSectionLevelBox(final RenderBox box) {
    try {
      box.setCachedX(nodeContext.getX());
      box.setContentAreaX1(nodeContext.getX1());
      box.setContentAreaX2(nodeContext.getX2());
      box.setCachedWidth(resolveTableWidthOnFinish(box));

      nodeContext.updateParentX2(box.getCachedX2());
    } finally {
      nodeContext = nodeContext.pop();
    }
  }
  private boolean abortIfNoTable(final RenderBox box) {
    if (box.getTableRefCount() == 0) {
      return false;
    }

    if (box.getTableValidationAge() == box.getChangeTracker()) {
      return false;
    }

    box.setTableValidationAge(box.getChangeTracker());
    return true;
  }
  public void startSubFlow(final InstanceID insertationPoint) {
    final RenderBox box;
    if (insertationPoint == null) {
      throw new IllegalStateException();
    }

    final RenderBox rootBox = getLayoutRoot();
    box = (RenderBox) rootBox.findNodeById(insertationPoint);
    if (box == null) {
      dontPushBoxToContext();
    } else {
      pushBoxToContext(box, false);
    }
  }
 public void addProgressMarkerBox() {
   final RenderBox parent = this.context.getRenderBox();
   final RenderNode child = parent.getLastChild();
   if (isCollapseProgressMarker()
       && child != null
       && child.getNodeType() == LayoutNodeTypes.TYPE_BOX_PROGRESS_MARKER) {
     final ProgressMarkerRenderBox markerRenderBox = (ProgressMarkerRenderBox) child;
     markerRenderBox.setStateKey(stateKey);
   } else {
     final ProgressMarkerRenderBox markerBox = new ProgressMarkerRenderBox();
     markerBox.setStateKey(stateKey);
     this.context.addChild(markerBox);
     markerBox.close();
   }
   this.context.setEmpty(false);
 }
 private void ensureEmptyChildIsAdded(final RenderBox parent, final ReportElement element) {
   final StyleSheet resolverStyleSheet = element.getComputedStyle();
   final RenderBox box;
   if (parent.isAcceptInlineBoxes()) {
     box =
         renderNodeFactory.produceRenderBox(
             element, resolverStyleSheet, BandStyleKeys.LAYOUT_INLINE, getStateKey());
   } else {
     box =
         renderNodeFactory.produceRenderBox(
             element, resolverStyleSheet, BandStyleKeys.LAYOUT_BLOCK, getStateKey());
   }
   box.getStaticBoxLayoutProperties()
       .setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.SECTION);
   box.close();
   parent.addChild(box);
 }
 protected boolean startBox(final RenderBox box) {
   if (isValidDrawTarget(box)) {
     if (bounds == null) {
       bounds = new StrictBounds(box.getX(), box.getY(), box.getWidth(), box.getHeight());
     } else {
       bounds.add(box.getX(), box.getY(), box.getWidth(), box.getHeight());
     }
   }
   return true;
 }
 private void testIsLogicalPageParent(RenderBox b, final RenderBox logicalPageBox) {
   while (b != null) {
     if (b == logicalPageBox) {
       return;
     }
     b = b.getParent();
   }
   throw new IllegalStateException();
 }
  protected void processParagraphChilds(final ParagraphRenderBox box) {
    paragraphLayoutWatch.start();
    try {
      final MinorAxisNodeContext nodeContext = getNodeContext();
      final MinorAxisParagraphBreakState breakState = getLineBreakState();

      if (box.isComplexParagraph()) {
        final RenderBox lineboxContainer = box.getLineboxContainer();
        RenderNode node = lineboxContainer.getFirstChild();
        while (node != null) {
          // all childs of the linebox container must be inline boxes. They
          // represent the lines in the paragraph. Any other element here is
          // a error that must be reported
          if (node.getNodeType() != LayoutNodeTypes.TYPE_BOX_LINEBOX) {
            throw new IllegalStateException("Expected ParagraphPoolBox elements.");
          }

          final ParagraphPoolBox inlineRenderBox = (ParagraphPoolBox) node;
          if (startLine(inlineRenderBox)) {
            processBoxChilds(inlineRenderBox);
            finishLine(inlineRenderBox, nodeContext, breakState);
          }

          node = node.getNext();
        }
      } else {
        final ParagraphPoolBox node = box.getPool();

        if (node.getFirstChild() == null) {
          return;
        }

        // all childs of the linebox container must be inline boxes. They
        // represent the lines in the paragraph. Any other element here is
        // a error that must be reported
        if (startLine(node)) {
          processBoxChilds(node);
          finishLine(node, nodeContext, breakState);
        }
      }
    } finally {
      paragraphLayoutWatch.stop(true);
    }
  }
 protected void finishParagraphBox(final RenderBox box) {
   final MinorAxisParagraphBreakState lineBreakState = getLineBreakState();
   if (lineBreakState.isInsideParagraph()) {
     if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH) {
       final ParagraphRenderBox paragraph = (ParagraphRenderBox) box;
       paragraph.updateMinorLayoutAge();
       paragraph.setCachedMaxChildX2(nodeContext.getMaxChildX2());
       lineBreakState.deinit();
     }
   }
 }
  public InlineSubreportMarker processSubReport(final SubReport element) {
    if (isLimitedSubReports()) {
      logger.debug("Not adding subreport: Subreports in header or footer area are not allowed.");
      return null;
    }

    final RenderBox parentBox = this.context.getRenderBox();
    if (parentBox.isAcceptInlineBoxes()) {
      logger.warn("Not adding subreport: Subreports in inline-contexts are not supported.");
      return null;
    }

    final StyleSheet resolverStyleSheet = element.getComputedStyle();
    final RenderBox subReportBox =
        renderNodeFactory.produceSubReportPlaceholder(element, resolverStyleSheet, stateKey);
    this.context.addChild(subReportBox);
    this.context.setEmpty(false);
    final InstanceID subReportBoxId = subReportBox.getInstanceId();
    // the box will be closed
    return new InlineSubreportMarker(element, subReportBoxId, SubReportProcessType.INLINE);
  }
  protected boolean startTableColGroupLevelBox(final RenderBox box) {
    nodeContext = nodeContextPool.createContext(box, nodeContext, false);

    if (checkCacheValid(box)) {
      return false;
    }

    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_COL) {
      startTableCol((TableColumnNode) box);
    }
    return true;
  }
  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);
    }
  }
  protected void finishInlineLevelBox(final RenderBox box) {
    if (lineBreakState.isInsideParagraph() == false) {
      throw new InvalidReportStateException(
          "A inline-level box outside of a paragraph box is not allowed.");
    }

    final int nodeType = box.getLayoutNodeType();
    if (nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT) {
      return;
    }

    lineBreakState.add(EndSequenceElement.INSTANCE, box);
  }
  protected boolean startParagraphBox(final RenderBox box) {
    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH) {
      final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
      if (paragraphBox.isLineBoxUnchanged()) {
        nodeContext.updateX2(paragraphBox.getCachedMaxChildX2());
        return false;
      }

      paragraphBox.clearLayout();
      getLineBreakState().init(paragraphBox);
    }
    return true;
  }
  protected void finishTableColGroupLevelBox(final RenderBox box) {
    try {
      if (checkCacheValid(box)) {
        return;
      }

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_COL) {
        finishTableCol((TableColumnNode) box);
      }
    } finally {
      nodeContext = nodeContext.pop();
    }
  }
  protected void processReplacedContent(
      final ReportElement element, final Object value, final Object rawValue) {
    final RenderBox box = this.context.getRenderBox();
    final SimpleStyleSheet resolverStyleSheet = element.getComputedStyle();

    final StyleSheet elementStyle;
    if (box.isAcceptInlineBoxes() == false) {
      if (isTableContext(box) == false
          && resolverStyleSheet.getBooleanStyleProperty(ElementStyleKeys.DYNAMIC_HEIGHT) == false) {
        elementStyle = new NonDynamicReplacedContentStyleSheet(resolverStyleSheet);
      } else {
        elementStyle = new DynamicReplacedContentStyleSheet(resolverStyleSheet);
      }
    } else {
      elementStyle = resolverStyleSheet;
    }

    final RenderableReplacedContentBox child =
        renderNodeFactory.createReplacedContent(element, elementStyle, value, rawValue, stateKey);
    child.setName(element.getName());
    this.context.addChild(child);
    this.context.setEmpty(false);
  }
  public boolean performOutput(final RenderBox content, final StyleBuilder.StyleCarrier[] cellStyle)
      throws IOException {
    styleBuilder.clear();
    clearText();
    setRawResult(null);
    result = false;
    processStack = new HtmlTextExtractorState(null, false, cellStyle);
    textExtractorHelper.setFirstElement(content.getInstanceId(), processStack);

    try {
      final int nodeType = content.getNodeType();
      if (nodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH) {
        processInitialBox((ParagraphRenderBox) content);
      } else if (nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT) {
        processRenderableContent((RenderableReplacedContentBox) content);
      } else {
        processBoxChilds(content);
      }
    } finally {
      processStack = null;
    }
    return result;
  }
  public void startSubFlow(final ReportElement element) {
    final StyleSheet resolverStyleSheet = element.getComputedStyle();

    final RenderBox box;
    if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY)) {
      final StyleSheet styleSheet =
          new SubReportStyleSheet(
              resolverStyleSheet.getBooleanStyleProperty(BandStyleKeys.PAGEBREAK_BEFORE),
              (resolverStyleSheet.getBooleanStyleProperty(BandStyleKeys.PAGEBREAK_AFTER)));

      final SimpleStyleSheet reportStyle = new SimpleStyleSheet(styleSheet);
      final BoxDefinition boxDefinition = renderNodeFactory.getBoxDefinition(reportStyle);
      box =
          new BlockRenderBox(
              reportStyle,
              element.getObjectID(),
              boxDefinition,
              SubReportType.INSTANCE,
              element.getAttributes(),
              null);
    } else {
      box =
          renderNodeFactory.produceRenderBox(
              element, resolverStyleSheet, BandStyleKeys.LAYOUT_BLOCK, stateKey);
    }

    box.getStaticBoxLayoutProperties()
        .setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.SECTION);
    if (element.getName() != null) {
      box.setName("Banded-SubReport-Section" + ": name=" + element.getName());
    } else {
      box.setName("Banded-SubReport-Section");
    }

    pushBoxToContext(box, false);
  }