/* * 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.alignment; import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes; 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.SplittableRenderNode; import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.InlineBoxSequenceElement; import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.InlineSequenceElement; import org.pentaho.reporting.engine.classic.core.util.LongList; /** * Performs the left-alignment computations. * <p/> * The inf-min-step creates the initial sequence of elements. The alignment processor now iterates over the sequence and * produces the layouted line. * <p/> * Elements can be split, splitting is a local operation and does not copy the children. Text splitting may produce a * totally different text (see: TeX hyphenation system). * <p/> * The process is iterative and continues unless all elements have been consumed. * * @author Thomas Morgner */ public class LeftAlignmentProcessor extends AbstractAlignmentProcessor { private long position; private int pageSegment; public LeftAlignmentProcessor() { } public int getPageSegment() { return pageSegment; } public void setPageSegment( final int pageSegment ) { this.pageSegment = pageSegment; } private long getPosition() { return position; } private void setPosition( final long position ) { this.position = position; } private void addPosition( final long width ) { this.position += width; } public RenderBox next() { position = getStartOfLine(); pageSegment = 0; final RenderBox retval = super.next(); position = 0; pageSegment = 0; return retval; } public void performLastLineAlignment() { position = getStartOfLine(); pageSegment = 0; super.performLastLineAlignment(); position = 0; pageSegment = 0; } /** * Handle the next input chunk. * * @param start * the start index * @param count * the number of elements in the sequence * @return the index of the last element that will fit on the current line. */ protected int handleElement( final int start, final int count ) { final InlineSequenceElement[] sequenceElements = getSequenceElements(); final RenderNode[] nodes = getNodes(); final long[] elementDimensions = getElementDimensions(); final long[] elementPositions = getElementPositions(); long width = 0; final int endIndex = start + count; // In the given range, there should be only one content element. InlineSequenceElement contentElement = null; int contentIndex = start; for ( int i = start; i < endIndex; i++ ) { final InlineSequenceElement element = sequenceElements[i]; final RenderNode node = nodes[i]; if ( isBorderMarker( element ) ) { width += element.getMaximumWidth( node ); continue; } width += element.getMaximumWidth( node ); contentElement = element; contentIndex = i; } final long nextPosition = getPosition() + width; final long lastPageBreak = getPageBreak( getPagebreakCount() - 1 ); // Do we cross a page boundary? if ( nextPosition > lastPageBreak ) { // On outer break: Stop processing // Dont write through to the stored position; but prepare if // we have to fallback .. long position = getPosition(); for ( int i = start; i < endIndex; i++ ) { final InlineSequenceElement element = sequenceElements[i]; final RenderNode node = nodes[i]; elementPositions[i] = position; final long elementWidth = element.getMaximumWidth( node ); elementDimensions[i] = elementWidth; position += elementWidth; } // we cross a pagebreak. Stop working on it - we bail out here. if ( nodes[contentIndex] instanceof SplittableRenderNode ) { // the element may be splittable. Test, and if so, give a hint to the // outside world .. setSkipIndex( endIndex ); setBreakableIndex( contentIndex ); setBreakableMaxAllowedWidth( nextPosition - lastPageBreak ); return ( start ); } // This is the first element and it still does not fit. How evil. if ( start == 0 ) { if ( contentElement instanceof InlineBoxSequenceElement ) { final RenderNode node = nodes[contentIndex]; if ( ( node.getNodeType() & LayoutNodeTypes.MASK_BOX ) == LayoutNodeTypes.MASK_BOX ) { // OK, limit the size of the box to the maximum line width and // revalidate it. final long contentPosition = elementPositions[contentIndex]; final RenderBox box = (RenderBox) node; final long maxWidth = ( getEndOfLine() - contentPosition ); computeInlineBlock( box, contentPosition, maxWidth ); elementDimensions[endIndex - 1] = node.getCachedWidth(); } } setSkipIndex( endIndex ); } return ( start ); } final long innerPagebreak = getPageBreak( getPageSegment() ); if ( nextPosition > innerPagebreak ) { // It is an inner pagebreak and the current element would not fit into the remaining space. // Move the element to the next page segment (but only if the start is not on setPosition( innerPagebreak ); setPageSegment( getPageSegment() + 1 ); } // No, it is an ordinary advance .. // Check, whether we hit an item-sequence element if ( contentElement instanceof InlineBoxSequenceElement == false ) { for ( int i = start; i < endIndex; i++ ) { final RenderNode node = nodes[i]; final InlineSequenceElement element = sequenceElements[i]; elementPositions[i] = getPosition(); final long elementWidth = element.getMaximumWidth( node ); elementDimensions[i] = elementWidth; addPosition( elementWidth ); } return endIndex; } // Handle the ItemSequence element. // This is a bit more complicated. So we encountered an inline-block // element here. That means, the element will try to occuppy its // maximum-content-width. // Log.debug("Advance block at index " + contentIndex); // final long ceWidth = contentElement.getMinimumWidth(); // final long extraSpace = contentElement.getMaximumWidth(); // Log.debug("Advance block: Min " + ceWidth); // Log.debug("Advance block: Max " + extraSpace); final RenderNode contentNode = nodes[contentIndex]; final long itemElementWidth = contentElement.getMaximumWidth( contentNode ); if ( ( contentNode.getNodeType() & LayoutNodeTypes.MASK_BOX ) == LayoutNodeTypes.MASK_BOX ) { final RenderBox box = (RenderBox) contentNode; computeInlineBlock( box, getPosition(), itemElementWidth ); } else { contentNode.setCachedX( getPosition() ); contentNode.setCachedWidth( itemElementWidth ); } final long preferredEndingPos = getPosition() + itemElementWidth; if ( preferredEndingPos > getEndOfLine() ) { // We would eat the whole space up to the end of the line and more // So lets move that element to the next line instead... // But: We could easily end in an endless loop here. So check whether // the element is the first in the line if ( start == 0 ) { // As it is guaranteed, that each chunk contains at least one item, // checking for start == 0 is safe enough .. return endIndex; } return start; } for ( int i = start; i < contentIndex; i++ ) { final InlineSequenceElement element = sequenceElements[i]; final RenderNode node = nodes[contentIndex]; final long elementWidth = element.getMaximumWidth( node ); elementPositions[i] = getPosition(); elementDimensions[i] = elementWidth; addPosition( elementWidth ); } elementPositions[contentIndex] = getPosition(); elementDimensions[contentIndex] = itemElementWidth; setPosition( preferredEndingPos ); for ( int i = contentIndex + 1; i < endIndex; i++ ) { final InlineSequenceElement element = sequenceElements[i]; final RenderNode node = nodes[contentIndex]; final long elementWidth = element.getMaximumWidth( node ); elementPositions[i] = getPosition(); elementDimensions[i] = elementWidth; addPosition( elementWidth ); } return endIndex; } public void performSkipAlignment( final int endIndex ) { // this is a NO-OP method, as the skip-alignment is simply a left-alignment ... } protected void updateBreaksForLastLineAlignment() { final long[] horizontalBreaks = getPageGrid().getHorizontalBreaks(); final int breakCount = horizontalBreaks.length; final LongList pageLongList = new LongList( breakCount ); final long endOfLine = getEndOfLine(); final long startOfLine = getStartOfLine(); for ( int i = 0; i < breakCount; i++ ) { final long pos = horizontalBreaks[i]; if ( pos <= startOfLine ) { // skip .. continue; } if ( pos >= endOfLine ) { break; } pageLongList.add( pos ); } // pageLongList.add(endOfLine); pageLongList.add( Long.MAX_VALUE ); final long[] pagebreaks = getPageBreaks(); updatePageBreaks( pageLongList.toArray( pagebreaks ), pageLongList.size() ); } }