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

    node.setCachedX(computeRowPosition(node));
    node.setCachedWidth(node.getMaximumBoxWidth());
    nodeContext.updateParentX2(node.getCachedX2());
  }
  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 void processInlineLevelNode(final RenderNode node) {
    if (lineBreakState.isInsideParagraph() == false) {
      throw new InvalidReportStateException(
          "A inline-level box outside of a paragraph box is not allowed.");
    }

    final int nodeType = node.getNodeType();
    if (nodeType == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE) {
      final FinishedRenderNode finNode = (FinishedRenderNode) node;
      node.setCachedWidth(finNode.getLayoutedWidth());
      return;
    }

    if (nodeType == LayoutNodeTypes.TYPE_NODE_TEXT) {
      lineBreakState.add(TextSequenceElement.INSTANCE, node);
    } else if (nodeType == LayoutNodeTypes.TYPE_NODE_SPACER) {
      final StyleSheet styleSheet = node.getStyleSheet();
      if (WhitespaceCollapse.PRESERVE.equals(
              styleSheet.getStyleProperty(TextStyleKeys.WHITE_SPACE_COLLAPSE))
          && styleSheet.getBooleanStyleProperty(TextStyleKeys.TRIM_TEXT_CONTENT) == false) {
        // bug-alert: This condition could indicate a workaround for a logic-flaw in the
        // text-processor
        lineBreakState.add(SpacerSequenceElement.INSTANCE, node);
      } else if (lineBreakState.isContainsContent()) {
        lineBreakState.add(SpacerSequenceElement.INSTANCE, node);
      }
    } else {
      lineBreakState.add(InlineNodeSequenceElement.INSTANCE, node);
    }
  }
 private boolean isValidDrawTarget(RenderNode node) {
   while (node != null) {
     if (ids.contains(node.getInstanceId())) {
       return true;
     }
     node = node.getParent();
   }
   return false;
 }
 private boolean isTableContext(RenderNode node) {
   while (node != null) {
     if ((node.getLayoutNodeType() & LayoutNodeTypes.MASK_BOX_TABLE)
         == LayoutNodeTypes.MASK_BOX_TABLE) {
       return true;
     }
     node = node.getParent();
   }
   return false;
 }
  public void testOrphan4() throws Exception {
    final MasterReport masterReport =
        DebugReportRunner.parseGoldenSampleReport("Prd-2087-Orphan-4.prpt");

    final LogicalPageBox box = DebugReportRunner.layoutPage(masterReport, 0);
    final RenderNode srs[] = MatchFactory.findElementsByElementType(box, SubReportType.INSTANCE);
    assertEquals(1, srs.length);
    assertEquals(StrictGeomUtility.toInternalValue(20), srs[0].getY());
    final RenderNode elementByName = MatchFactory.findElementByName(box, "outer-group");
    assertEquals(StrictGeomUtility.toInternalValue(20), elementByName.getY());
  }
  /**
   * Canvas elements do not shift content. Therefore the widow definition is not effective.
   *
   * @throws Exception
   */
  public void testOrphan5() throws Exception {
    final MasterReport masterReport =
        DebugReportRunner.parseGoldenSampleReport("Prd-2087-Orphan-5.prpt");
    // masterReport.setCompatibilityLevel(ClassicEngineBoot.computeVersionId(3, 8, 0));
    // DebugReportRunner.createXmlPageable(masterReport);

    final LogicalPageBox box = DebugReportRunner.layoutPage(masterReport, 0);
    final RenderNode elementByName = MatchFactory.findElementByName(box, "outer-group");
    assertEquals(StrictGeomUtility.toInternalValue(20), elementByName.getY());
    //    ModelPrinter.INSTANCE.print(box);
    //    DebugReportRunner.showDialog(masterReport);

  }
 private long computeCanvasPosition(final RenderNode node) {
   final long contentAreaX1 = nodeContext.getParentX1();
   final long bcw = nodeContext.getBlockContextWidth();
   final double posX = node.getNodeLayoutProperties().getPosX();
   final long position = RenderLength.resolveLength(bcw, posX);
   return (contentAreaX1 + position);
 }
 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);
 }
 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 drawBookmark(final RenderNode box, final String bookmark) {
    if (box.isNodeVisible(getDrawArea()) == false) {
      return;
    }
    final PdfOutline root = writer.getDirectContent().getRootOutline();

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

    final float upperY =
        translateX + (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY()));
    final float leftX = (float) (StrictGeomUtility.toExternalValue(box.getX()));
    final PdfDestination dest = new PdfDestination(PdfDestination.FIT, leftX, upperY, 0);
    new PdfOutline(root, dest, bookmark);
    // destination will always point to the 'current' page
    // todo: Make this a hierarchy ..
  }
  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;
  }
  protected void drawAnchor(final RenderNode content) {
    if (content.isNodeVisible(getDrawArea()) == false) {
      return;
    }
    final String anchorName =
        (String) content.getStyleSheet().getStyleProperty(ElementStyleKeys.ANCHOR_NAME);
    if (anchorName == null) {
      return;
    }
    final AffineTransform affineTransform = getGraphics().getTransform();
    final float translateX = (float) affineTransform.getTranslateX();

    final float upperY =
        translateX + (float) (globalHeight - StrictGeomUtility.toExternalValue(content.getY()));
    final float leftX = (float) (StrictGeomUtility.toExternalValue(content.getX()));
    final PdfDestination dest = new PdfDestination(PdfDestination.FIT, leftX, upperY, 0);
    writer.getDirectContent().localDestination(anchorName, dest);
  }
  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);
    }
  }
  public static String computePrintedText(RenderBox renderBox) {
    StringBuilder b = new StringBuilder();
    RenderNode lineChild = renderBox.getFirstChild();

    while (lineChild != null) {
      if (lineChild instanceof RenderableText) {
        RenderableText text = (RenderableText) lineChild;
        b.append(text.getRawText());
      } else if (lineChild instanceof SpacerRenderNode) {
        SpacerRenderNode spacer = (SpacerRenderNode) lineChild;
        for (int i = 0; i < spacer.getSpaceCount(); i += 1) b.append(' ');
      } else if (lineChild instanceof RenderBox) {
        b.append(computePrintedText((RenderBox) lineChild));
      }

      lineChild = lineChild.getNext();
    }
    return b.toString();
  }
  private long computeRowPosition(final RenderNode node) {
    // The y-position of a box depends on the parent.
    final RenderBox parent = node.getParent();

    // A table row is something special. Although it is a block box,
    // it layouts its children from left to right
    if (parent == null) {
      // there's no parent ..
      return 0;
    }

    final RenderNode prev = node.getPrev();
    if (prev != null) {
      // we have a sibling. Position yourself directly to the right of your sibling ..
      return prev.getCachedX() + prev.getCachedWidth();
    } else {
      return nodeContext.getParentX1();
    }
  }
  protected void drawComplexText(final RenderableComplexText renderableComplexText) {
    try {

      if (renderableComplexText.getRichText().isEmpty()) {
        // This text is empty.
        return;
      }
      if (renderableComplexText.isNodeVisible(getParagraphBounds(), isOverflowX(), isOverflowY())
          == false) {
        return;
      }

      // iterate through all inline elements
      for (final RichTextSpec.StyledChunk styledChunk :
          renderableComplexText.getRichText().getStyleChunks()) {
        RenderNode node = styledChunk.getOriginatingTextNode();
        InstanceID dummy = node.getInstanceId();
        textExtractorHelper.startInlineBox(
            dummy,
            styledChunk.getOriginalAttributes(),
            styledChunk.getStyleSheet(),
            BoxDefinition.EMPTY);
        if (node instanceof RenderableReplacedContentBox) {
          processRenderableContent((RenderableReplacedContentBox) node);
          result = true;
        } else {
          String text = styledChunk.getText();
          xmlWriter.writeText(characterEntityParser.encodeEntities(text));
          if (text.trim().length() > 0) {
            result = true;
          }
        }
        textExtractorHelper.finishBox(dummy, styledChunk.getOriginalAttributes());
        clearText();
      }
    } catch (final IOException ioe) {
      throw new InvalidReportStateException("Failed to write text", ioe);
    }
  }
  protected void processOtherNode(final RenderNode node) {
    try {
      final int nodeType = node.getNodeType();
      if (nodeType == LayoutNodeTypes.TYPE_NODE_TEXT
          || nodeType == LayoutNodeTypes.TYPE_NODE_COMPLEX_TEXT) {
        super.processOtherNode(node);
        return;
      }

      if (node.isVirtualNode()) {
        return;
      }

      if (nodeType == LayoutNodeTypes.TYPE_NODE_SPACER) {
        final SpacerRenderNode spacer = (SpacerRenderNode) node;
        final int count = Math.max(1, spacer.getSpaceCount());
        for (int i = 0; i < count; i++) {
          xmlWriter.writeText(" ");
        }
      }
    } catch (final IOException e) {
      throw new RuntimeException("Failed", e);
    }
  }
  protected void processLinksAndAnchors(final RenderNode box) {
    final StyleSheet styleSheet = box.getStyleSheet();
    if (drawPdfScript(box) == false) {
      final String target = (String) styleSheet.getStyleProperty(ElementStyleKeys.HREF_TARGET);
      final String title = (String) styleSheet.getStyleProperty(ElementStyleKeys.HREF_TITLE);
      if (target != null || title != null) {
        final String window = (String) styleSheet.getStyleProperty(ElementStyleKeys.HREF_WINDOW);
        drawHyperlink(box, target, window, title);
      }
    }

    final String anchor = (String) styleSheet.getStyleProperty(ElementStyleKeys.ANCHOR_NAME);
    if (anchor != null) {
      drawAnchor(box);
    }

    final String bookmark = (String) styleSheet.getStyleProperty(BandStyleKeys.BOOKMARK);
    if (bookmark != null) {
      drawBookmark(box, bookmark);
    }
  }
  private void finishLine(
      final RenderBox inlineRenderBox,
      final MinorAxisNodeContext nodeContext,
      final MinorAxisParagraphBreakState breakState) {
    if (breakState.isInsideParagraph() == false || breakState.isSuspended()) {
      throw new IllegalStateException("No active breakstate, finish-line cannot continue.");
    }

    final PageGrid pageGrid = getPageGrid();
    final OutputProcessorMetaData metaData = getMetaData();
    breakState.add(EndSequenceElement.INSTANCE, inlineRenderBox);

    final ParagraphRenderBox paragraph = breakState.getParagraph();

    final ElementAlignment textAlignment = paragraph.getTextAlignment();
    final long textIndent = paragraph.getTextIndent();
    final long firstLineIndent = paragraph.getFirstLineIndent();
    // This aligns all direct childs. Once that is finished, we have to
    // check, whether possibly existing inner-paragraphs are still valid
    // or whether moving them violated any of the inner-pagebreak constraints.
    final TextAlignmentProcessor processor = create(textAlignment);

    final SequenceList sequence = breakState.getSequence();

    final long lineEnd;
    final boolean overflowX = paragraph.getStaticBoxLayoutProperties().isOverflowX();
    if (overflowX) {
      lineEnd = nodeContext.getX1() + OVERFLOW_DUMMY_WIDTH;
    } else {
      lineEnd = nodeContext.getX2();
    }

    long lineStart = Math.min(lineEnd, nodeContext.getX1() + firstLineIndent);
    if (lineEnd - lineStart <= 0) {
      final long minimumChunkWidth = paragraph.getPool().getMinimumChunkWidth();
      processor.initialize(
          metaData, sequence, lineStart, lineStart + minimumChunkWidth, pageGrid, overflowX);
      nodeContext.updateX2(lineStart + minimumChunkWidth);
      logger.warn("Auto-Corrected zero-width first-line on paragraph - " + paragraph.getName());
    } else {
      processor.initialize(metaData, sequence, lineStart, lineEnd, pageGrid, overflowX);
      if (overflowX == false) {
        nodeContext.updateX2(lineEnd);
      }
    }

    while (processor.hasNext()) {
      final RenderNode linebox = processor.next();
      if (linebox.getLayoutNodeType() != LayoutNodeTypes.TYPE_BOX_LINEBOX) {
        throw new IllegalStateException("Line must not be null");
      }

      paragraph.addGeneratedChild(linebox);

      if (processor.hasNext()) {
        lineStart = Math.min(lineEnd, nodeContext.getX1() + textIndent);

        if (lineEnd - lineStart <= 0) {
          final long minimumChunkWidth = paragraph.getPool().getMinimumChunkWidth();
          processor.updateLineSize(lineStart, lineStart + minimumChunkWidth);
          nodeContext.updateX2(lineStart + minimumChunkWidth);
          logger.warn(
              "Auto-Corrected zero-width text-line on paragraph continuation - "
                  + paragraph.getName());
        } else {
          processor.updateLineSize(lineStart, lineEnd);
          if (overflowX == false) {
            nodeContext.updateX2(lineEnd);
          }
        }
      }
    }

    processor.deinitialize();
  }
 /**
  * The extra-space width for an element. Some elements can expand to fill some more space
  * (justified text is a good example, adding some space between the letters of each word to reduce
  * the inner-word spacing).
  *
  * @return
  */
 public long getMaximumWidth(final RenderNode node) {
   return node.getMaximumBoxWidth();
 }
  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;
  }
  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());
  }
  protected void processTableRowLevelNode(final RenderNode node) {
    assert (node instanceof FinishedRenderNode);

    node.setCachedX(nodeContext.getX1());
    node.setCachedWidth(nodeContext.getContentAreaWidth());
  }
  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();
  }
 /**
  * The width of the element. This is the minimum width of the element.
  *
  * @return
  */
 public long getMinimumWidth(final RenderNode node) {
   return node.getMinimumChunkWidth();
 }