protected void processRowLevelNode(final RenderNode node) {
    assert (node instanceof FinishedRenderNode);

    node.setCachedX(computeRowPosition(node));
    node.setCachedWidth(node.getMaximumBoxWidth());
    nodeContext.updateParentX2(node.getCachedX2());
  }
  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;
  }
  protected void processTableRowLevelNode(final RenderNode node) {
    assert (node instanceof FinishedRenderNode);

    node.setCachedX(nodeContext.getX1());
    node.setCachedWidth(nodeContext.getContentAreaWidth());
  }