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

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

/*!
* 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) 2002-2013 Pentaho Corporation..  All rights reserved.
*/

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ElementAlignment;
import org.pentaho.reporting.engine.classic.core.InvalidReportStateException;
import org.pentaho.reporting.engine.classic.core.layout.ModelPrinter;
import org.pentaho.reporting.engine.classic.core.layout.model.BlockRenderBox;
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.PageBreakPositionList;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.process.util.BasePaginationTableState;
import org.pentaho.reporting.engine.classic.core.layout.process.util.PaginationShiftState;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;

/**
* A helper class that contains generic methods that would distract me from the actual pagination logic.
*/
public final class PaginationStepLib
{
  private static final Log logger = LogFactory.getLog(PaginationStepLib.class);

  private PaginationStepLib()
  {
  }

  public static void configureBreakUtility(final PageBreakPositionList breakUtility,
                                           final LogicalPageBox pageBox,
                                           final long[] allCurrentBreaks,
                                           final long reservedHeight,
                                           final long lastBreakLocal)
  {
    final PageBreakPositionList allPreviousBreak = pageBox.getAllVerticalBreaks();
    breakUtility.copyFrom(allPreviousBreak);

    final long pageOffset = pageBox.getPageOffset();
    final long headerHeight = pageBox.getHeaderArea().getHeight();
    // Then add all new breaks (but take the header and footer-size into account) ..
    if (allCurrentBreaks.length == 1)
    {
      breakUtility.addMajorBreak(pageOffset, headerHeight);
      breakUtility.addMajorBreak((lastBreakLocal - reservedHeight) + pageOffset, headerHeight);
    }
    else // more than one physical page; therefore header and footer are each on a separate canvas ..
    {
      breakUtility.addMajorBreak(pageOffset, headerHeight);
      final int breakCount = allCurrentBreaks.length - 1;
      for (int i = 1; i < breakCount; i++)
      {
        final long aBreak = allCurrentBreaks[i];
        breakUtility.addMinorBreak(pageOffset + (aBreak - headerHeight));
      }
      breakUtility.addMajorBreak(pageOffset + (lastBreakLocal - reservedHeight), headerHeight);
    }
  }

  public static void assertProgress(final LogicalPageBox pageBox)
  {
    final RenderNode lastChild = pageBox.getLastChild();
    if (lastChild != null)
    {
      final long lastChildY2 = lastChild.getY() + lastChild.getHeight();
      if (lastChildY2 < pageBox.getHeight())
      {
        //ModelPrinter.print(pageBox);
        throw new IllegalStateException
            ("Assertation failed: Pagination did not proceed: " + lastChildY2 + " < " + pageBox.getHeight());
      }
    }
  }

  public static long restrictPageAreaHeights(final LogicalPageBox pageBox,
                                             final long[] allCurrentBreaks)
  {
    final BlockRenderBox headerArea = pageBox.getHeaderArea();
    final long headerHeight = Math.min(headerArea.getHeight(), allCurrentBreaks[0]);
    headerArea.setHeight(headerHeight);

    final BlockRenderBox footerArea = pageBox.getFooterArea();
    final BlockRenderBox repeatFooterArea = pageBox.getRepeatFooterArea();
    if (allCurrentBreaks.length > 1)
    {
      final long lastBreakLocal = allCurrentBreaks[allCurrentBreaks.length - 1];
      final long lastPageHeight = lastBreakLocal - allCurrentBreaks[allCurrentBreaks.length - 2];
      final long footerHeight = Math.min(footerArea.getHeight(), lastPageHeight);
      footerArea.setHeight(footerHeight);

      final long repeatFooterHeight = Math.min(repeatFooterArea.getHeight(), lastPageHeight);
      repeatFooterArea.setHeight(repeatFooterHeight);
    }

    final long footerHeight = footerArea.getHeight();
    final long repeatFooterHeight = repeatFooterArea.getHeight();
    // Assertion: Make sure that we do not run into a infinite loop..
    return headerHeight + repeatFooterHeight + footerHeight;
  }

  public static void assertBlockPosition(final RenderBox box, final long shift)
  {
    if (box.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_SECTION)
    {
      // no point in testing table-sections, as the header will be an out-of-order band.
      return;
    }

    final boolean error;
    final long expectedYPos;
    if (box.getPrev() != null)
    {
      error = true;
      expectedYPos = box.getPrev().getY() + box.getPrev().getHeight();
    }
    else
    {
      if (box.getParent() != null)
      {
        error = false;
        expectedYPos = box.getParent().getY();
        final Object parentVAlignment = box.getParent().getStyleSheet().getStyleProperty(ElementStyleKeys.VALIGNMENT);
        if (parentVAlignment != null &&
            ElementAlignment.TOP.equals(parentVAlignment) == false)
        {
          return;
        }
      }
      else
      {
        error = true;
        expectedYPos = 0;
      }
    }

    final long realY = box.getY() + shift;
    if (realY != expectedYPos)
    {
      final long additionalShift = expectedYPos - realY;
      final long realShift = shift + additionalShift;
      if (error)
      {
        ModelPrinter.INSTANCE.print(box);
        ModelPrinter.INSTANCE.print(ModelPrinter.getRoot(box));
        throw new InvalidReportStateException(String.format("Assert: Shift is not as expected: " +
            "realY=%d != expectation=%d; Shift=%d; AdditionalShift=%d; RealShift=%d",
            realY, expectedYPos, shift, additionalShift, realShift));
      }
      else
      {
        if (logger.isDebugEnabled())
        {
          logger.debug(String.format("Assert: Shift is not as expected: realY=%d != expectation=%d; Shift=%d; " +
              "AdditionalShift=%d; RealShift=%d (False positive if block box has valign != TOP",
              realY, expectedYPos, shift, additionalShift, realShift));
        }
      }
    }
  }

  /**
   * Computes the height that will be required on this page to display at least some parts of the box.
   *
   * @param box the box for which the height is computed
   * @return the height in micro-points.
   */
  public static long computeNonBreakableBoxHeight(final RenderBox box,
                                                  final PaginationShiftState shiftState,
                                                  final BasePaginationTableState tableState)
  {
    // must return the reserved space starting from box's y position.
    final long widowSize = getWidowConstraint(box, shiftState, tableState);

    final StaticBoxLayoutProperties sblp = box.getStaticBoxLayoutProperties();
    if (sblp.isAvoidPagebreakInside() &&
        box.getRestrictFinishedClearOut() != RenderBox.RestrictFinishClearOut.RESTRICTED)
    {
      return Math.max(widowSize, box.getHeight());
    }

    final int nodeType = box.getLayoutNodeType();
    if ((nodeType & LayoutNodeTypes.MASK_BOX_BLOCK) != LayoutNodeTypes.MASK_BOX_BLOCK)
    {
      // Canvas, row or inline boxes have no notion of lines, and therefore they cannot have orphans and widows.
      return widowSize;
    }

    if (isOrphanConstraintNeeded(box, shiftState, tableState) == false)
    {
      return widowSize;
    }

    final long orphanHeight = box.getOrphanConstraintSize();
    if (widowSize + orphanHeight > box.getHeight())
    {
      // if the widows and orphan areas overlap, then the box becomes non-breakable.
      return Math.max(widowSize, box.getHeight());
    }

    return Math.max(orphanHeight, widowSize);
  }

  /**
   * Widow constraint evaluation is skipped if
   * <p/>
   * (a) the box has a widow/orphan-parent defining that sits on the current page's page-offset and
   * (b) this parent's orphan-restricted pagebreak-exclusion zone includes this box.
   *
   * @param box
   * @param shiftState
   * @param tableState
   * @return
   */
  private static boolean isOrphanConstraintNeeded(final RenderBox box,
                                                  final PaginationShiftState shiftState,
                                                  final BasePaginationTableState tableState)
  {
    final long boxY = box.getY() + shiftState.getShiftForNextChild();
    final long pageOffset = tableState.getPageOffset(boxY);
    if (pageOffset == boxY)
    {
      // no need to set a widow constraint, we are not shifting anyway ..
      return false;
    }
    if (box.getRestrictFinishedClearOut() != RenderBox.RestrictFinishClearOut.RESTRICTED)
    {
      return false;
    }

    if (isBoxInsideParentOrphanZoneOnThisPage(box, pageOffset, boxY))
    {
      return false;
    }
    return true;
  }

  private static boolean isBoxInsideParentOrphanZoneOnThisPage(final RenderBox box,
                                                               final long pageOffset,
                                                               final long boxYShifted)
  {
    RenderBox parent = box.getParent();
    while (parent != null)
    {
      if (parent.getRestrictFinishedClearOut() == RenderBox.RestrictFinishClearOut.UNRESTRICTED)
      {
        break;
      }

      if (parent.getY() < pageOffset)
      {
        // once the parent is sitting on the previous page, we no longer need to ask it ..
        break;
      }

      if (parent.getOrphanConstraintSize() == 0)
      {
        parent = parent.getParent();
        continue;
      }

      final long constraintBoundary = parent.getY() + parent.getOrphanConstraintSize();
      if (constraintBoundary > boxYShifted)
      {
        // this parent is not relevant.
        return true;
      }

      parent = parent.getParent();
    }
    return false;
  }


  public static long getWidowConstraint(final RenderBox box,
                                         final PaginationShiftState shiftState,
                                         final BasePaginationTableState tableState)
  {
    if (box.isWidowBox() == false)
    {
      return 0;
    }

    final long boxY = box.getY() + shiftState.getShiftForNextChild();
    long retval = 0;
    RenderBox parent = box.getParent();

    final long pageOffset = tableState.getPageOffset();
    if (pageOffset == boxY)
    {
      // no need to set a widow constraint, we are not shifting anyway ..
      return 0;
    }

    while (parent != null)
    {
      if (parent.getRestrictFinishedClearOut() == RenderBox.RestrictFinishClearOut.UNRESTRICTED)
      {
        break;
      }

      if (parent.getWidowConstraintSize() == 0)
      {
        parent = parent.getParent();
        continue;
      }

      final long y2 = parent.getY2();
      final long constraintBoundary;
      if (parent.getY() < pageOffset)
      {
        constraintBoundary = y2 - Math.max(0, parent.getWidowConstraintSize());
      }
      else
      {
        constraintBoundary = y2 - Math.max(0, parent.getWidowConstraintSizeWithKeepTogether());
      }
      if (constraintBoundary == boxY)
      {
        retval = Math.max(retval, y2 - boxY);
      }

      parent = parent.getParent();
    }
    return retval;
  }
}
TOP

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

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.