Package org.pentaho.reporting.engine.classic.core.layout.process

Source Code of org.pentaho.reporting.engine.classic.core.layout.process.ComputeStaticPropertiesProcessStep

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.core.layout.process;

import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.layout.model.Border;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderLength;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableComplexText;
import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition;
import org.pentaho.reporting.engine.classic.core.layout.model.context.NodeLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.process.util.StaticChunkWidthUpdate;
import org.pentaho.reporting.engine.classic.core.layout.process.util.StaticChunkWidthUpdatePool;
import org.pentaho.reporting.engine.classic.core.layout.process.util.StaticRootChunkWidthUpdate;
import org.pentaho.reporting.engine.classic.core.layout.text.ExtendedBaselineInfo;
import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.WhitespaceCollapse;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;

/**
* Computes the width for all elements. This uses the CSS algorithm, percentages are resolved against the parent's
* already known width.
*
* @author Thomas Morgner
*/
public final class ComputeStaticPropertiesProcessStep extends IterateSimpleStructureProcessStep
{

  // Set the maximum height to an incredibly high value. This is now 2^43 micropoints or more than
  // 3000 kilometers. Please call me directly at any time if you need more space for printing.
  public static final long MAX_AUTO = StrictGeomUtility.toInternalValue(0x80000000000L);
  private static final StaticRootChunkWidthUpdate ROOT = new StaticRootChunkWidthUpdate();

  private OutputProcessorMetaData metaData;
  private ResourceManager resourceManager;
  private boolean overflowXSupported;
  private boolean overflowYSupported;
  private boolean widowsEnabled;
  private StaticChunkWidthUpdate chunkWidthUpdate;
  private StaticChunkWidthUpdatePool chunkWidthUpdatePool;
  private boolean widowOrphanDefinitionsEncountered;
  private boolean designTime;

  public ComputeStaticPropertiesProcessStep()
  {
    chunkWidthUpdatePool = new StaticChunkWidthUpdatePool();
  }

  public void initialize(final OutputProcessorMetaData metaData,
                         final ProcessingContext processingContext)
  {
    this.metaData = metaData;
    this.overflowXSupported = metaData.isFeatureSupported(OutputProcessorFeature.ASSUME_OVERFLOW_X);
    this.overflowYSupported = metaData.isFeatureSupported(OutputProcessorFeature.ASSUME_OVERFLOW_Y);
    this.widowsEnabled = !ClassicEngineBoot.isEnforceCompatibilityFor(processingContext.getCompatibilityLevel(), 3, 8);
    this.widowOrphanDefinitionsEncountered = false;
    this.designTime = metaData.isFeatureSupported(OutputProcessorFeature.DESIGNTIME);
    this.resourceManager = processingContext.getResourceManager();
  }

  public boolean isWidowOrphanDefinitionsEncountered()
  {
    return widowOrphanDefinitionsEncountered;
  }

  public void compute(final LogicalPageBox root)
  {
    this.chunkWidthUpdate = ROOT;
    startProcessing(root);
    this.chunkWidthUpdate = null;
  }

  protected boolean startBox(final RenderBox box)
  {
    final long changeTracker = box.getChangeTracker();
    final long age = box.getStaticBoxPropertiesAge();
    if (changeTracker == age)
    {
      return false;
    }

    this.chunkWidthUpdate = createChunkWidthUpdate(box);

    updateStaticProperties(box);
    computeWidowOrphanIndicator(box);

    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
    {
      processParagraphChilds((ParagraphRenderBox) box);
      return false;
    }

    return true;
  }

  protected void finishBox(final RenderBox box)
  {
    final long changeTracker = box.getChangeTracker();
    final long age = box.getStaticBoxPropertiesAge();
    if (changeTracker == age)
    {
      return;
    }

    updateMinimumChunkWidth(box);
  }

  protected void processOtherNode(final RenderNode node)
  {
    if (node instanceof RenderableComplexText)
    {
      RenderableComplexText t = (RenderableComplexText) node;
      t.computeMinimumChunkWidth(metaData, resourceManager);
    }

    chunkWidthUpdate.update(node.getMinimumChunkWidth());
  }

  protected void processParagraphChilds(final ParagraphRenderBox box)
  {
    final ExtendedBaselineInfo extendedBaselineInfo = box.getStaticBoxLayoutProperties().getNominalBaselineInfo();
    if (extendedBaselineInfo == null)
    {
      throw new IllegalStateException("Baseline info must not be null at this point");
    }
    final StyleSheet styleSheet = box.getNodeLayoutProperties().getStyleSheet();
    final double value = styleSheet.getDoubleStyleProperty(TextStyleKeys.LINEHEIGHT, 0);
    final long afterEdge = extendedBaselineInfo.getBaseline(ExtendedBaselineInfo.AFTER_EDGE);
    if (value <= 0)
    {
      box.getPool().setLineHeight(afterEdge);
    }
    else
    {
      box.getPool().setLineHeight(RenderLength.resolveLength(afterEdge, value));
    }
    startProcessing(box.getPool());
  }

  private void computeBreakIndicator(final RenderBox box)
  {

    final StyleSheet styleSheet = box.getStyleSheet();
    final RenderBox parent = box.getParent();
    if (parent != null)
    {
      final boolean breakBefore = styleSheet.getBooleanStyleProperty(BandStyleKeys.PAGEBREAK_BEFORE);
      final boolean breakAfter = box.isBreakAfter();
      final int nodeType = parent.getLayoutNodeType();
      if ((breakBefore) && (nodeType != LayoutNodeTypes.TYPE_BOX_PARAGRAPH))
      {
        box.setManualBreakIndicator(RenderBox.BreakIndicator.DIRECT_MANUAL_BREAK);
        applyIndirectManualBreakIndicator(parent);
        return;
      }
      if (breakAfter && (nodeType != LayoutNodeTypes.TYPE_BOX_PARAGRAPH))
      {
        applyIndirectManualBreakIndicator(parent);
      }
    }

    final boolean fixedPosition = RenderLength.AUTO.equals
        (styleSheet.getStyleProperty(BandStyleKeys.FIXED_POSITION, RenderLength.AUTO)) == false;
    if (fixedPosition)
    {
      applyIndirectManualBreakIndicator(box);
    }
    else
    {
      box.setManualBreakIndicator(RenderBox.BreakIndicator.NO_MANUAL_BREAK);
    }
  }

  private void applyIndirectManualBreakIndicator(RenderBox node)
  {
    while (node != null)
    {
      if (node.getManualBreakIndicator() != RenderBox.BreakIndicator.NO_MANUAL_BREAK)
      {
        return;
      }
      node.setManualBreakIndicator(RenderBox.BreakIndicator.INDIRECT_MANUAL_BREAK);
      node = node.getParent();
    }
  }

  /**
   * Collects and possibly computes the static properties according to the CSS layouting model. The classic JFreeReport
   * layout model does not know anything about margins or borders, so in that case resolving against the CSS model is
   * ok.
   *
   * @param box the box that should be processed.
   * @return true if the box is new, false otherwise
   */
  private void updateStaticProperties(final RenderBox box)
  {
    final BoxDefinition boxDefinition = box.getBoxDefinition();
    final StaticBoxLayoutProperties sblp = box.getStaticBoxLayoutProperties();
    if (sblp.isBaselineCalculated())
    {
      // mark box as seen ..
      return;
    }

    computeMarginsAndBorders(box, boxDefinition, sblp);
    computeResolvedStyleProperties(box, sblp);
    computeBreakIndicator(box);
  }

  private void computeWidowOrphanIndicator(final RenderBox box)
  {
    final RenderBox parent = box.getParent();
    if (parent == null)
    {
      box.setParentWidowContexts(0);
      return;
    }

    if (widowsEnabled == false)
    {
      return;
    }

    final StaticBoxLayoutProperties sblp = parent.getStaticBoxLayoutProperties();
    if (sblp.getOrphans() > 0 || sblp.getWidows() > 0 || sblp.isAvoidPagebreakInside())
    {
      widowOrphanDefinitionsEncountered = true;
      box.setParentWidowContexts(parent.getParentWidowContexts() + 1);
    }
    else
    {
      box.setParentWidowContexts(parent.getParentWidowContexts());
    }

  }

  private void computeResolvedStyleProperties(final RenderBox box, final StaticBoxLayoutProperties sblp)
  {
    final StyleSheet style = box.getStyleSheet();

    NodeLayoutProperties nlp = box.getNodeLayoutProperties();
    if (designTime)
    {
      // at design-time elements can be generated that are not visible in the final output
      // the report designer needs them to create a smooth design experience.
      RenderBox parent = box.getParent();
      if (parent == null)
      {
        nlp.setVisible(style.getBooleanStyleProperty(ElementStyleKeys.VISIBLE));
      }
      else if (parent.isEmptyNodesHaveSignificance() == false)
      {
        nlp.setVisible(style.getBooleanStyleProperty(ElementStyleKeys.VISIBLE));
      }
    }

    final int nodeType = box.getLayoutNodeType();
    if ((nodeType & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
    {
      sblp.setAvoidPagebreakInside(true);
    }
    else if (nodeType == LayoutNodeTypes.TYPE_BOX_TABLE_ROW || nodeType == LayoutNodeTypes.TYPE_BOX_ROWBOX)
    {
      sblp.setAvoidPagebreakInside(style.getBooleanStyleProperty(ElementStyleKeys.AVOID_PAGEBREAK_INSIDE, true));
    }
    else
    {
      sblp.setAvoidPagebreakInside(style.getBooleanStyleProperty(ElementStyleKeys.AVOID_PAGEBREAK_INSIDE, false));
    }

    sblp.setDominantBaseline(-1);

    if (widowsEnabled)
    {
      sblp.setOrphans(style.getIntStyleProperty(ElementStyleKeys.ORPHANS, 0));
      sblp.setWidows(style.getIntStyleProperty(ElementStyleKeys.WIDOWS, 0));

      final boolean orphanOptOut = style.getBooleanStyleProperty(ElementStyleKeys.WIDOW_ORPHAN_OPT_OUT, true);
      sblp.setWidowOrphanOptOut(orphanOptOut);
    }
    final ExtendedBaselineInfo baselineInfo = metaData.getBaselineInfo('x', style);
    if (baselineInfo == null)
    {
      throw new IllegalStateException();
    }
    sblp.setNominalBaselineInfo(baselineInfo);
    sblp.setFontFamily(metaData.getNormalizedFontFamilyName((String) style.getStyleProperty(TextStyleKeys.FONT)));

    final Object collapse = style.getStyleProperty(TextStyleKeys.WHITE_SPACE_COLLAPSE);
    sblp.setPreserveSpace(WhitespaceCollapse.PRESERVE.equals(collapse));
    sblp.setOverflowX(style.getBooleanStyleProperty(ElementStyleKeys.OVERFLOW_X, overflowXSupported));
    sblp.setOverflowY(style.getBooleanStyleProperty(ElementStyleKeys.OVERFLOW_Y, overflowYSupported));
    sblp.setInvisibleConsumesSpace(style.getBooleanStyleProperty
        (ElementStyleKeys.INVISIBLE_CONSUMES_SPACE, nodeType == LayoutNodeTypes.TYPE_BOX_ROWBOX));
    sblp.setVisible(style.getBooleanStyleProperty(ElementStyleKeys.VISIBLE));

    final RenderBox parent = box.getParent();
    if (parent != null &&
        style.getDoubleStyleProperty(ElementStyleKeys.MIN_WIDTH, 0) == 0 &&
        style.getDoubleStyleProperty(ElementStyleKeys.WIDTH, 0) == 0)
    {
      // todo: Should that flag also take paddings and borders of the parent into account?
      // They alter the available space for the childs, and thus it would make sense to establish a new
      // context for resolving percentage-widths

      // only a box with a parent can try to inherit a context ..
      if ((parent.getLayoutNodeType() & LayoutNodeTypes.TYPE_BOX_BLOCK) == LayoutNodeTypes.TYPE_BOX_BLOCK)
      {
        // a block level box always creates a block-context.
        sblp.setDefinedWidth(true);
      }
      else
      {
        sblp.setDefinedWidth(false);
      }
    }
    else
    {
      sblp.setDefinedWidth(true);
    }
  }

  private void computeMarginsAndBorders(final RenderBox box,
                                        final BoxDefinition boxDefinition,
                                        final StaticBoxLayoutProperties sblp)
  {
    final int nodeType = box.getLayoutNodeType();
    if (nodeType == LayoutNodeTypes.TYPE_BOX_TABLE_ROW || nodeType == LayoutNodeTypes.TYPE_BOX_TABLE_SECTION)
    {
      sblp.setBorderBottom(0);
      sblp.setBorderTop(0);
      sblp.setBorderRight(0);
      sblp.setBorderLeft(0);
    }
    else
    {
      final Border border = boxDefinition.getBorder();
      sblp.setBorderTop(border.getTop().getWidth());
      sblp.setBorderLeft(border.getLeft().getWidth());
      sblp.setBorderBottom(border.getBottom().getWidth());
      sblp.setBorderRight(border.getRight().getWidth());
    }
  }

  private StaticChunkWidthUpdate createChunkWidthUpdate(final RenderBox box)
  {
    if (chunkWidthUpdate.isInline())
    {
      return chunkWidthUpdatePool.createInline(chunkWidthUpdate, box);
    }

    final int nodeType = box.getLayoutNodeType();
    if (nodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
    {
      return chunkWidthUpdatePool.createParagraph(chunkWidthUpdate, (ParagraphRenderBox) box);
    }
    if (nodeType == LayoutNodeTypes.TYPE_BOX_ROWBOX ||
        nodeType == LayoutNodeTypes.TYPE_BOX_TABLE_ROW)
    {
      return chunkWidthUpdatePool.createHorizontal(chunkWidthUpdate, box);
    }
    return chunkWidthUpdatePool.createVertical(chunkWidthUpdate, box);
  }

  protected void updateMinimumChunkWidth(final RenderBox box)
  {
    final long changeTracker = box.getChangeTracker();
    final long age = box.getStaticBoxPropertiesAge();
    if (changeTracker == age)
    {
      // update the parent
      if (box.isVisible())
      {
        chunkWidthUpdate.update(box.getMinimumChunkWidth());
      }
      return;
    }

    box.setStaticBoxPropertiesAge(box.getChangeTracker());

    final StaticChunkWidthUpdate boxUpdate = chunkWidthUpdate;
    boxUpdate.finish();

    chunkWidthUpdate = chunkWidthUpdate.pop();
    if (box.isVisible())
    {
      chunkWidthUpdate.update(box.getMinimumChunkWidth());
    }
  }


}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.layout.process.ComputeStaticPropertiesProcessStep

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.