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);
    }
  }
  public void initialize(
      final ProcessingContext processingContext,
      final RenderBox parentBox,
      final RenderNodeFactory renderNodeFactory) {
    if (parentBox == null) {
      throw new NullPointerException();
    }
    if (processingContext == null) {
      throw new NullPointerException();
    }

    if (this.processingContext != processingContext) {
      this.processingContext = processingContext;
      this.metaData = processingContext.getOutputProcessorMetaData();
      this.strictLegacyMode =
          metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY);
      this.designtime = metaData.isFeatureSupported(OutputProcessorFeature.DESIGNTIME);

      this.renderNodeFactory = renderNodeFactory;
      this.textProducer = createTextProducer();
    }

    this.context = new DefaultLayoutModelBuilderContext(null, parentBox);
  }
  public void startSection(final ReportElement element, final int sectionSize) {
    final StyleSheet resolverStyleSheet = element.getComputedStyle();
    final String layoutMode;
    final boolean legacyMode =
        metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY);
    if (legacyMode) {
      layoutMode = BandStyleKeys.LAYOUT_BLOCK;
    } else {
      String layout =
          (String)
              resolverStyleSheet.getStyleProperty(BandStyleKeys.LAYOUT, BandStyleKeys.LAYOUT_AUTO);
      if (BandStyleKeys.LAYOUT_INLINE.equals(layout)
          && !this.context.getRenderBox().isAcceptInlineBoxes()) {
        layoutMode = BandStyleKeys.LAYOUT_BLOCK;
      } else {
        layoutMode = layout;
      }
    }

    final GroupSection groupSection =
        new GroupSection(
            renderNodeFactory.produceRenderBox(element, resolverStyleSheet, layoutMode, null),
            renderNodeFactory.createAutoGeneratedSectionStyleSheet(resolverStyleSheet),
            sectionSize,
            legacyMode);
    this.context = new SectionLayoutModelBuilderContext(this.context, groupSection, legacyMode);
    this.context.setEmpty(true);

    if (element instanceof GroupBody || element instanceof Group) {
      // PRD-3154 - do we need to set placeholder to true?
      // todo: PRD-3154: This is black magic, placeholder box true is evil.
      // Need to evaluate side-effects of this beast. Is it safe for keep-together boxes?
      this.context
          .getRenderBox()
          .getStaticBoxLayoutProperties()
          .setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.SECTION);
    }
    this.textProducer.startText();
  }
  public void startSection() {
    final String layoutMode;
    if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY)) {
      layoutMode = BandStyleKeys.LAYOUT_BLOCK;
    } else {
      layoutMode = BandStyleKeys.LAYOUT_AUTO;
    }

    final RenderBox renderBox = renderNodeFactory.produceSectionBox(layoutMode, null);
    if (isAllowMergeSection()) {
      this.context =
          new BandSectionLayoutModelBuilderContext(this.metaData, this.context, renderBox);
    } else {
      this.context = new DefaultLayoutModelBuilderContext(this.context, renderBox);
    }
    this.context.setEmpty(true);

    if (legacySectionName != null) {
      this.context.getRenderBox().setName(legacySectionName);
    }

    this.textProducer.startText();
  }
  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);
  }
  protected void drawText(final RenderableText renderableText, final long contentX2) {
    if (renderableText.getLength() == 0) {
      return;
    }

    final long posX = renderableText.getX();
    final long posY = renderableText.getY();
    final float x1 = (float) (StrictGeomUtility.toExternalValue(posX));

    final PdfContentByte cb;
    PdfTextSpec textSpec = (PdfTextSpec) getTextSpec();
    if (textSpec == null) {
      final StyleSheet layoutContext = renderableText.getStyleSheet();

      // The code below may be weird, but at least it is predictable weird.
      final String fontName =
          getMetaData()
              .getNormalizedFontFamilyName(
                  (String) layoutContext.getStyleProperty(TextStyleKeys.FONT));
      final String encoding = (String) layoutContext.getStyleProperty(TextStyleKeys.FONTENCODING);
      final float fontSize =
          (float) layoutContext.getDoubleStyleProperty(TextStyleKeys.FONTSIZE, 10);

      final boolean embed =
          globalEmbed || layoutContext.getBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT);
      final boolean bold = layoutContext.getBooleanStyleProperty(TextStyleKeys.BOLD);
      final boolean italics = layoutContext.getBooleanStyleProperty(TextStyleKeys.ITALIC);

      final BaseFontFontMetrics fontMetrics =
          getMetaData()
              .getBaseFontFontMetrics(fontName, fontSize, bold, italics, encoding, embed, false);

      final PdfGraphics2D g2 = (PdfGraphics2D) getGraphics();
      final Color cssColor = (Color) layoutContext.getStyleProperty(ElementStyleKeys.PAINT);
      g2.setPaint(cssColor);
      g2.setFillPaint();
      g2.setStrokePaint();
      // final float translateY = (float) affineTransform.getTranslateY();

      cb = g2.getRawContentByte();

      textSpec = new PdfTextSpec(layoutContext, getMetaData(), g2, fontMetrics, cb);
      setTextSpec(textSpec);

      cb.beginText();
      cb.setFontAndSize(fontMetrics.getBaseFont(), fontSize);
    } else {
      cb = textSpec.getContentByte();
    }

    final BaseFontFontMetrics baseFontRecord = textSpec.getFontMetrics();
    final BaseFont baseFont = baseFontRecord.getBaseFont();
    final float ascent = baseFont.getFontDescriptor(BaseFont.BBOXURY, textSpec.getFontSize());
    final float y2 = (float) (StrictGeomUtility.toExternalValue(posY) + ascent);
    final float y = globalHeight - y2;

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

    final FontNativeContext nativeContext = baseFontRecord.getNativeContext();
    if (baseFontRecord.isTrueTypeFont()
        && textSpec.isBold()
        && nativeContext.isNativeBold() == false) {
      final float strokeWidth = textSpec.getFontSize() / 30.0f; // right from iText ...
      if (strokeWidth == 1) {
        cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
      } else {
        cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
        cb.setLineWidth(strokeWidth);
      }
    } else {
      cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
    }

    // if the font does not declare to be italics already, emulate it ..
    if (baseFontRecord.isTrueTypeFont()
        && textSpec.isItalics()
        && nativeContext.isNativeItalics() == false) {
      final float italicAngle =
          baseFont.getFontDescriptor(BaseFont.ITALICANGLE, textSpec.getFontSize());
      if (italicAngle == 0) {
        // italics requested, but the font itself does not supply italics gylphs.
        cb.setTextMatrix(1, 0, PdfLogicalPageDrawable.ITALIC_ANGLE, 1, x1 + translateX, y);
      } else {
        cb.setTextMatrix(x1 + translateX, y);
      }
    } else {
      cb.setTextMatrix(x1 + translateX, y);
    }

    final OutputProcessorMetaData metaData = getMetaData();
    final GlyphList gs = renderableText.getGlyphs();
    final int offset = renderableText.getOffset();

    final CodePointBuffer codePointBuffer = getCodePointBuffer();
    if (metaData.isFeatureSupported(OutputProcessorFeature.FAST_FONTRENDERING)
        && isNormalTextSpacing(renderableText)) {
      final int maxLength = renderableText.computeMaximumTextSize(contentX2);
      final String text = gs.getText(renderableText.getOffset(), maxLength, codePointBuffer);

      cb.showText(text);
    } else {
      final PdfTextArray textArray = new PdfTextArray();
      final StringBuilder buffer = new StringBuilder(gs.getSize());
      final int maxPos = offset + renderableText.computeMaximumTextSize(contentX2);

      for (int i = offset; i < maxPos; i++) {
        final Glyph g = gs.getGlyph(i);
        final Spacing spacing = g.getSpacing();
        if (i != offset) {
          final float optimum = (float) StrictGeomUtility.toFontMetricsValue(spacing.getMinimum());
          if (optimum != 0) {
            textArray.add(buffer.toString());
            textArray.add(-optimum / textSpec.getFontSize());
            buffer.setLength(0);
          }
        }

        final String text = gs.getGlyphAsString(i, codePointBuffer);
        buffer.append(text);
      }
      if (buffer.length() > 0) {
        textArray.add(buffer.toString());
      }
      cb.showText(textArray);
    }
  }
  private static boolean isEmptyElement(
      final ReportElement band, final StyleSheet style, final OutputProcessorMetaData metaData) {
    if (isControlBand(style)) {
      return false;
    }

    if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY)) {
      if (band instanceof Band) {
        final Band b = (Band) band;
        if (b.getElementCount() > 0) {
          return false;
        }
      }
    }

    if (BandStyleKeys.LAYOUT_AUTO.equals(style.getStyleProperty(BandStyleKeys.LAYOUT))) {
      // A auto-band is considered empty.
      return true;
    }

    // A band is not empty, if it has a defined minimum or preferred height
    if (isLengthDefined(ElementStyleKeys.HEIGHT, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.WIDTH, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.POS_Y, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.POS_X, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.MIN_HEIGHT, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.MIN_WIDTH, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.PADDING_TOP, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.PADDING_LEFT, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.PADDING_BOTTOM, style)) {
      return false;
    }
    if (isLengthDefined(ElementStyleKeys.PADDING_RIGHT, style)) {
      return false;
    }
    if (BorderStyle.NONE.equals(
            style.getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_STYLE, BorderStyle.NONE))
        == false) {
      return false;
    }
    if (BorderStyle.NONE.equals(
            style.getStyleProperty(ElementStyleKeys.BORDER_TOP_STYLE, BorderStyle.NONE))
        == false) {
      return false;
    }
    if (BorderStyle.NONE.equals(
            style.getStyleProperty(ElementStyleKeys.BORDER_LEFT_STYLE, BorderStyle.NONE))
        == false) {
      return false;
    }
    if (BorderStyle.NONE.equals(
            style.getStyleProperty(ElementStyleKeys.BORDER_RIGHT_STYLE, BorderStyle.NONE))
        == false) {
      return false;
    }
    if (style.getStyleProperty(ElementStyleKeys.BACKGROUND_COLOR) != null) {
      return false;
    }

    if (metaData.isExtraContentElement(band.getStyle(), band.getAttributes())) {
      return false;
    }
    return true;
  }
  private InstanceID startBox(
      final ReportElement element,
      final StyleSheet styleSheet,
      final String layout,
      final boolean auto) {
    closeAutoGeneratedPostfixBoxes();

    if (BandStyleKeys.LAYOUT_AUTO.equals(layout)) {
      this.context =
          new DefaultLayoutModelBuilderContext(
              this.context,
              renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
    } else if (BandStyleKeys.LAYOUT_INLINE.equals(layout)) {
      if (this.context.getRenderBox().isAcceptInlineBoxes() == false) {
        // parent context is not a inline-inside context.
        // So we need to create a auto-paragraph wrapper to open a inline-context
        this.context =
            new DefaultLayoutModelBuilderContext(
                this.context, renderNodeFactory.createAutoParagraph(element, styleSheet, stateKey));

        // PRD-3750 - A empty inline-band that creates a auto-paragraph reserves space on the
        // vertical axis.
        if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY)
            || metaData.isFeatureSupported(OutputProcessorFeature.PRD_3750)) {
          this.context.setAutoGeneratedWrapperBox(true);

          this.context =
              new DefaultLayoutModelBuilderContext(
                  this.context,
                  renderNodeFactory.produceRenderBox(
                      element,
                      styleSheet,
                      DefaultRenderNodeFactory.LAYOUT_PARAGRAPH_LINEBOX,
                      stateKey));
        }
      } else {
        this.context =
            new DefaultLayoutModelBuilderContext(
                this.context,
                renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
      }
    } else if (this.context.getRenderBox().isAcceptInlineBoxes()) {
      // inline elements only accept inline element childs
      this.context =
          new DefaultLayoutModelBuilderContext(
              this.context,
              renderNodeFactory.produceRenderBox(
                  element, styleSheet, BandStyleKeys.LAYOUT_INLINE, stateKey));
    } else if (BandStyleKeys.LAYOUT_TABLE_CELL.equals(layout)) {
      // a table body always needs a table parent ..
      if (LayoutNodeTypes.TYPE_BOX_TABLE_ROW != this.context.getRenderBox().getLayoutNodeType()) {
        startBox(
            element,
            renderNodeFactory.createAutoGeneratedSectionStyleSheet(styleSheet),
            BandStyleKeys.LAYOUT_TABLE_ROW,
            true);
      }
      this.context =
          new DefaultLayoutModelBuilderContext(
              this.context,
              renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
    } else if (BandStyleKeys.LAYOUT_TABLE_ROW.equals(layout)) {
      // a table body always needs a table parent ..
      if (LayoutNodeTypes.TYPE_BOX_TABLE_SECTION
          != this.context.getRenderBox().getLayoutNodeType()) {
        startBox(
            element,
            renderNodeFactory.createAutoGeneratedSectionStyleSheet(styleSheet),
            BandStyleKeys.LAYOUT_TABLE_BODY,
            true);
      }
      this.context =
          new DefaultLayoutModelBuilderContext(
              this.context,
              renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
    } else if (BandStyleKeys.LAYOUT_TABLE_BODY.equals(layout)
        || BandStyleKeys.LAYOUT_TABLE_FOOTER.equals(layout)
        || BandStyleKeys.LAYOUT_TABLE_HEADER.equals(layout)) {
      // a table body always needs a table parent ..
      if (LayoutNodeTypes.TYPE_BOX_TABLE != this.context.getRenderBox().getLayoutNodeType()) {
        startBox(
            element,
            renderNodeFactory.createAutoGeneratedSectionStyleSheet(styleSheet),
            BandStyleKeys.LAYOUT_TABLE,
            true);
      }
      this.context =
          new DefaultLayoutModelBuilderContext(
              this.context,
              renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
    } else {
      // handle ordinary elements, block, canvas, row ..
      this.context =
          new DefaultLayoutModelBuilderContext(
              this.context,
              renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
    }

    this.context.setAutoGeneratedWrapperBox(auto);
    this.context.setEmpty(isEmptyElement(element, styleSheet, metaData));
    if (!auto) {
      if (isControlBand(styleSheet)) {
        this.context
            .getRenderBox()
            .getStaticBoxLayoutProperties()
            .setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.SECTION);
      } else {
        this.context
            .getRenderBox()
            .getStaticBoxLayoutProperties()
            .setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.NONE);
      }
    }
    this.textProducer.startText();
    return this.context.getRenderBox().getInstanceId();
  }