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;
  }
  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();
    }
  }
 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();
     }
   }
 }
  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;
  }
  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 finishTableLevelBox(final RenderBox box) {
    try {
      if (checkCacheValid(box)) {
        nodeContext.updateParentX2(box.getCachedX2());
        return;
      }

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_COL_GROUP) {
        finishTableColGroup((TableColumnGroupNode) box);
      } else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_COL) {
        finishTableCol((TableColumnNode) box);
      } else {
        box.setCachedX(nodeContext.getX());
        box.setContentAreaX1(nodeContext.getX1());
        box.setContentAreaX2(nodeContext.getX2());
        box.setCachedWidth(resolveTableWidthOnFinish(box));
        nodeContext.updateParentX2(box.getCachedX2());
      }
    } finally {
      nodeContext = nodeContext.pop();
    }
  }
  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;
  }
  // Table-sections or auto-boxes masking as tables (treated as table-sections nonetheless).
  protected boolean startTableLevelBox(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, true);

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

    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_COL_GROUP) {
      startTableColGroup((TableColumnGroupNode) box);
    } else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_COL) {
      startTableCol((TableColumnNode) box);
    } else {
      startTableSectionOrRow(box);
    }
    return true;
  }
  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;
  }