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

      box.setCachedX(nodeContext.getX());
      box.setContentAreaX1(nodeContext.getX1());
      box.setContentAreaX2(nodeContext.getX2());
      if (finishTableContext(box) == false) {
        box.setCachedWidth(
            MinorAxisLayoutStepUtil.resolveNodeWidthOnFinish(
                box, nodeContext, isStrictLegacyMode()));
      }
      nodeContext.updateParentX2(box.getCachedX2());

      finishParagraphBox(box);
    } finally {
      nodeContext = nodeContext.pop();
    }
  }
  protected void finishCanvasLevelBox(final RenderBox box) {
    try {
      if (checkCacheValid(box)) {
        nodeContext.updateParentX2(box.getCachedX2());
        return;
      }

      // make sure that the width takes all the borders and paddings into account.
      box.setCachedX(nodeContext.getX());
      box.setContentAreaX1(nodeContext.getX1());
      box.setContentAreaX2(nodeContext.getX2());
      if (finishTableContext(box) == false) {
        box.setCachedWidth(
            MinorAxisLayoutStepUtil.resolveNodeWidthOnFinish(
                box, nodeContext, isStrictLegacyMode()));
      }
      nodeContext.updateParentX2(box.getCachedX2());

      finishParagraphBox(box);
    } finally {
      nodeContext = nodeContext.pop();
    }
  }
  public void performLastLineAlignment() {
    if (pagebreakCount == 0) {
      throw new IllegalStateException("Alignment processor has not been initialized correctly.");
    }

    Arrays.fill(elementDimensions, 0);
    Arrays.fill(elementPositions, 0);

    int lastPosition = iterate(sequenceElements, sequenceFill);
    if (lastPosition == 0) {
      // This could evolve into an infinite loop. Thats evil.
      // We have two choices to prevent that:
      // (1) Try to break the element.
      //      if (getBreakableIndex() >= 0)
      //      {
      //        // Todo: Breaking is not yet implemented ..
      //      }
      if (getSkipIndex() >= 0) {
        // This causes an overflow ..
        performSkipAlignment(getSkipIndex());
        lastPosition = getSkipIndex();
      } else {
        // Skip the complete line. Oh, thats not good, really!
        lastPosition = sequenceFill;
      }
    }

    // the elements up to the 'lastPosition' are now aligned according to the alignment rules.
    // now, update the element's positions and dimensions ..

    if (lastPosition == sequenceFill || lastLineAlignment) {
      // First, the simple case: The line's content did fully fit into the linebox. No linebreaks
      // were necessary.
      RenderBox firstBox = null;
      for (int i = 0; i < lastPosition; i++) {
        final RenderNode node = nodes[i];
        final InlineSequenceElement element = sequenceElements[i];
        if (element instanceof EndSequenceElement) {
          final long boxX2 = (elementPositions[i] + elementDimensions[i]);
          final RenderBox box = (RenderBox) node;
          box.setCachedWidth(boxX2 - box.getCachedX());
          continue;
        }

        if (element instanceof StartSequenceElement) {
          final RenderBox box = (RenderBox) node;
          box.setCachedX(elementPositions[i]);
          if (firstBox == null) {
            firstBox = box;
          }
          continue;
        }

        // Content element: Perform a deep-deriveForAdvance, so that we preserve the
        // possibly existing sub-nodes.
        node.setCachedX(elementPositions[i]);
        node.setCachedWidth(elementDimensions[i]);
      }

      return;
    }

    // The second case is more complicated. The text did not fit fully into the text-element.

    // Left align all elements after the layouted content ..
    if (leftAlignProcessor == null) {
      leftAlignProcessor = new LeftAlignmentProcessor();
    }
    leftAlignProcessor.initializeForLastLineAlignment(this);
    leftAlignProcessor.performLastLineAlignment();
    leftAlignProcessor.deinitialize();
  }
  public RenderBox next() {
    cleanFirstSpacers();

    Arrays.fill(elementDimensions, 0);
    Arrays.fill(elementPositions, 0);

    int lastPosition = iterate(sequenceElements, sequenceFill);
    if (lastPosition == 0) {
      // This could evolve into an infinite loop. Thats evil.
      // We have two choices to prevent that:
      // (1) Try to break the element.
      //      if (getBreakableIndex() >= 0)
      //      {
      //        // Todo: Breaking is not yet implemented ..
      //      }
      if (getSkipIndex() >= 0) {
        // This causes an overflow ..
        performSkipAlignment(getSkipIndex());
        lastPosition = getSkipIndex();
      } else {
        // Skip the complete line. Oh, thats not good, really!
        lastPosition = sequenceFill;
      }
    }

    // now, build the line and update the array ..
    pendingElements.clear();
    contexts.clear();
    RenderBox firstBox = null;
    RenderBox box = null;
    for (int i = 0; i < lastPosition; i++) {
      final RenderNode node = nodes[i];
      final InlineSequenceElement element = sequenceElements[i];
      if (element instanceof EndSequenceElement) {
        contexts.pop();
        final long boxX2 = (elementPositions[i] + elementDimensions[i]);
        box.setCachedWidth(boxX2 - box.getCachedX());

        if (contexts.isEmpty()) {
          box = null;
        } else {
          final RenderNode tmpnode = box;
          box = contexts.peek();
          box.addGeneratedChild(tmpnode);
        }
        continue;
      }

      if (element instanceof StartSequenceElement) {
        box = (RenderBox) node.derive(false);
        box.setCachedX(elementPositions[i]);
        contexts.push(box);
        if (firstBox == null) {
          firstBox = box;
        }
        continue;
      }

      if (box == null) {
        throw new IllegalStateException(
            "Invalid sequence: " + "Cannot have elements before we open the box context.");
      }

      // Content element: Perform a deep-deriveForAdvance, so that we preserve the
      // possibly existing sub-nodes.
      final RenderNode child = node.derive(true);
      child.setCachedX(elementPositions[i]);
      child.setCachedWidth(elementDimensions[i]);
      if (box.getStaticBoxLayoutProperties().isPreserveSpace()
          && box.getStyleSheet().getBooleanStyleProperty(TextStyleKeys.TRIM_TEXT_CONTENT)
              == false) {
        // Take a shortcut as we know that we will never have any pending elements if preserve is
        // true and
        // trim-content is false.
        box.addGeneratedChild(child);
        continue;
      }

      if (child.isIgnorableForRendering()) {
        pendingElements.add(child);
      } else {
        for (int j = 0; j < pendingElements.size(); j++) {
          final RenderNode pendingNode = pendingElements.get(j);
          box.addGeneratedChild(pendingNode);
        }
        pendingElements.clear();
        box.addGeneratedChild(child);
      }
    }

    // Remove all spacers and other non printable content that might
    // look ugly at the beginning of a new line ..
    for (; lastPosition < sequenceFill; lastPosition++) {
      final RenderNode node = nodes[lastPosition];
      final StyleSheet styleSheet = node.getStyleSheet();
      if (WhitespaceCollapse.PRESERVE.equals(
              styleSheet.getStyleProperty(TextStyleKeys.WHITE_SPACE_COLLAPSE))
          && styleSheet.getBooleanStyleProperty(TextStyleKeys.TRIM_TEXT_CONTENT) == false) {
        break;
      }

      if (node.isIgnorableForRendering() == false) {
        break;
      }
    }

    // If there are open contexts, then add the split-result to the new line
    // and update the width of the current line
    RenderBox previousContext = null;
    final int openContexts = contexts.size();
    for (int i = 0; i < openContexts; i++) {
      final RenderBox renderBox = contexts.get(i);
      final long cachedWidth = getEndOfLine() - renderBox.getCachedX();
      renderBox.setCachedWidth(cachedWidth);

      final InlineRenderBox rightBox =
          (InlineRenderBox) renderBox.split(RenderNode.HORIZONTAL_AXIS);
      sequenceElements[i] = StartSequenceElement.INSTANCE;
      nodes[i] = rightBox;
      if (previousContext != null) {
        previousContext.addGeneratedChild(renderBox);
      }
      previousContext = renderBox;
    }

    final int length = sequenceFill - lastPosition;
    System.arraycopy(sequenceElements, lastPosition, sequenceElements, openContexts, length);
    System.arraycopy(nodes, lastPosition, nodes, openContexts, length);
    sequenceFill = openContexts + length;
    Arrays.fill(sequenceElements, sequenceFill, sequenceElements.length, null);
    Arrays.fill(nodes, sequenceFill, nodes.length, null);

    return firstBox;
  }