/*
* 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.modules.output.pageable.base;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.ReportDefinition;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.layout.AbstractRenderer;
import org.pentaho.reporting.engine.classic.core.layout.ModelPrinter;
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.output.ContentProcessingException;
import org.pentaho.reporting.engine.classic.core.layout.output.LayoutPagebreakHandler;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.CleanPaginatedBoxesStep;
import org.pentaho.reporting.engine.classic.core.layout.process.CountBoxesStep;
import org.pentaho.reporting.engine.classic.core.layout.process.FillPhysicalPagesStep;
import org.pentaho.reporting.engine.classic.core.layout.process.OrphanStep;
import org.pentaho.reporting.engine.classic.core.layout.process.PaginationStep;
import org.pentaho.reporting.engine.classic.core.layout.process.WidowStep;
import org.pentaho.reporting.engine.classic.core.layout.process.util.PaginationResult;
import org.pentaho.reporting.engine.classic.core.states.PerformanceMonitorContext;
@SuppressWarnings( "HardCodedStringLiteral" )
public class PageableRenderer extends AbstractRenderer {
private static final Log logger = LogFactory.getLog( PageableRenderer.class );
private PaginationStep paginationStep;
private OrphanStep orphanStep;
private WidowStep widowStep;
private FillPhysicalPagesStep fillPhysicalPagesStep;
private CleanPaginatedBoxesStep cleanPaginatedBoxesStep;
private int pageCount;
private boolean pageStartPending;
private CountBoxesStep countBoxesStep;
private boolean widowsEnabled;
public PageableRenderer( final OutputProcessor outputProcessor ) {
super( outputProcessor );
this.paginationStep = new PaginationStep();
this.fillPhysicalPagesStep = new FillPhysicalPagesStep();
this.cleanPaginatedBoxesStep = new CleanPaginatedBoxesStep();
this.countBoxesStep = new CountBoxesStep();
this.orphanStep = new OrphanStep();
this.widowStep = new WidowStep();
initialize();
}
public void startReport( final ReportDefinition report, final ProcessingContext processingContext,
final PerformanceMonitorContext performanceMonitorContext ) {
super.startReport( report, processingContext, performanceMonitorContext );
pageCount = 0;
widowsEnabled = !ClassicEngineBoot.isEnforceCompatibilityFor( processingContext.getCompatibilityLevel(), 3, 8 );
}
protected void debugPrint( final LogicalPageBox pageBox ) {
// printConditional(5, pageBox);
// printConditional(18, pageBox);
}
protected void printConditional( final int page, final LogicalPageBox pageBox ) {
if ( logger.isDebugEnabled() == false ) {
return;
}
logger.debug( "Printing a page: " + pageCount );
if ( pageCount == page ) {
// leave the debug-code in until all of these cases are solved.
logger.debug( "1: **** Start Printing Page: " + pageCount );
// ModelPrinter.INSTANCE.print(clone);
ModelPrinter.INSTANCE.print( pageBox );
logger.debug( "1: **** Stop Printing Page: " + pageCount );
}
}
protected boolean preparePagination( final LogicalPageBox pageBox ) {
if ( widowsEnabled == false ) {
return true;
}
if ( isWidowOrphanDefinitionsEncountered() == false ) {
return true;
}
if ( orphanStep.processOrphanAnnotation( pageBox ) ) {
// logger.info("Orphans unlayoutable.");
return false;
}
if ( widowStep.processWidowAnnotation( pageBox ) ) {
// logger.info("Widows unlayoutable.");
return false;
}
return true;
}
protected boolean isPageFinished() {
final LogicalPageBox pageBox = getPageBox();
// final long sizeBeforePagination = pageBox.getHeight();
// final LogicalPageBox clone = (LogicalPageBox) pageBox.derive(true);
final PaginationResult pageBreak = paginationStep.performPagebreak( pageBox );
if ( pageBreak.isOverflow() || pageBox.isOpen() == false ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Detected pagebreak : " + pageBreak.getLastVisibleState() );
}
setLastStateKey( pageBreak.getLastVisibleState() );
return true;
}
return false;
}
protected boolean
performPagination( final LayoutPagebreakHandler layoutPagebreakHandler, final boolean performOutput )
throws ContentProcessingException {
// next: perform pagination.
final LogicalPageBox pageBox = getPageBox();
// final long sizeBeforePagination = pageBox.getHeight();
// final LogicalPageBox clone = (LogicalPageBox) pageBox.derive(true);
final PaginationResult pageBreak = paginationStep.performPagebreak( pageBox );
if ( pageBox.isOpen() && pageBreak.isOverflow() == false ) {
return false;
}
setLastStateKey( pageBreak.getLastVisibleState() );
setPagebreaks( getPagebreaks() + 1 );
pageBox.setAllVerticalBreaks( pageBreak.getAllBreaks() );
pageCount += 1;
// DebugLog.log("1: **** Start Printing Page: " + pageCount);
debugPrint( pageBox );
// DebugLog.log("PaginationResult: " + pageBreak);
// A new page has been started. Recover the page-grid, then restart
// everything from scratch. (We have to recompute, as the pages may
// be different now, due to changed margins or page definitions)
final OutputProcessor outputProcessor = getOutputProcessor();
final long nextOffset = pageBreak.getLastPosition();
final long pageOffset = pageBox.getPageOffset();
if ( logger.isDebugEnabled() ) {
logger.debug( "PageableRenderer: pageOffset=" + pageOffset + "; nextOffset=" + nextOffset );
}
if ( performOutput ) {
if ( outputProcessor.isNeedAlignedPage() ) {
final LogicalPageBox box = fillPhysicalPagesStep.compute( pageBox, pageOffset, nextOffset );
outputProcessor.processContent( box );
// DebugLog.log("Processing contents for Page " + pageCount + " Page-Offset: " + pageOffset + " -> " +
// nextOffset);
} else {
// DebugLog.log("Processing fast contents for Page " + pageCount + " Page-Offset: " + pageOffset + " -> " +
// nextOffset);
outputProcessor.processContent( pageBox );
}
} else {
// todo: When recomputing the contents, we have to update the page cursor or the whole excercise is next to
// useless ..
// DebugLog.log("Recomputing contents for Page " + pageCount + " Page-Offset: " + pageOffset + " -> " +
// nextOffset);
outputProcessor.processRecomputedContent( pageBox );
}
// Now fire the pagebreak. This goes through all layers and informs all
// components, that a pagebreak has been encountered and possibly a
// new page has been set. It does not save the state or perform other
// expensive operations. However, it updates the 'isPagebreakEncountered'
// flag, which will be active until the input-feed received a new event.
// Log.debug ("PageTime " + (currentPageAge - lastPageAge));
final boolean repeat = pageBox.isOpen() || ( pageBox.getHeight() > nextOffset );
if ( repeat ) {
pageBox.setPageOffset( nextOffset );
countBoxesStep.process( pageBox );
cleanPaginatedBoxesStep.compute( pageBox );
// todo PRD-4606
pageBox.resetCacheState( true );
if ( pageBreak.isNextPageContainsContent() ) {
if ( layoutPagebreakHandler != null ) {
layoutPagebreakHandler.pageStarted();
}
return true;
}
// No need to try again, we know that the result will not change, as the next page is
// empty. (We already tested it.)
pageStartPending = true;
return false;
} else {
pageBox.setPageOffset( nextOffset );
outputProcessor.processingFinished();
return false;
}
}
public boolean clearPendingPageStart( final LayoutPagebreakHandler layoutPagebreakHandler ) {
if ( pageStartPending == false ) {
return false;
}
if ( layoutPagebreakHandler != null ) {
layoutPagebreakHandler.pageStarted();
}
pageStartPending = false;
return true;
}
public int getPageCount() {
return pageCount;
}
public boolean isCurrentPageEmpty() {
// todo: Invent a test that checks whether the page is currently empty.
final LogicalPageBox logicalPageBox = getPageBox();
if ( logicalPageBox == null ) {
throw new IllegalStateException( "LogicalPageBox being null? You messed it up again!" );
}
final PageBreakPositionList breakPositionList = logicalPageBox.getAllVerticalBreaks();
final long masterBreak = breakPositionList.getLastMasterBreak();
final boolean nextPageContainsContent = ( logicalPageBox.getHeight() > masterBreak );
return nextPageContainsContent == false;
}
public boolean isPageStartPending() {
return pageStartPending;
}
public boolean isPendingPageHack() {
return true;
}
protected void initializeRendererOnStartReport( final ProcessingContext processingContext ) {
super.initializeRendererOnStartReport( processingContext );
paginationStep.initializePerformanceMonitoring( getPerformanceMonitorContext() );
fillPhysicalPagesStep.initializePerformanceMonitoring( getPerformanceMonitorContext() );
}
protected void close() {
super.close();
paginationStep.close();
fillPhysicalPagesStep.close();
}
}