/* * 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.ReportAttributeMap; import org.pentaho.reporting.engine.classic.core.layout.TextCache; import org.pentaho.reporting.engine.classic.core.layout.model.InlineRenderBox; 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.RenderNode; import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition; 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.text.ComplexTextFactory; import org.pentaho.reporting.engine.classic.core.layout.text.DefaultRenderableTextFactory; import org.pentaho.reporting.engine.classic.core.layout.text.RenderableTextFactory; import org.pentaho.reporting.engine.classic.core.style.StyleSheet; import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys; import org.pentaho.reporting.libraries.fonts.encoding.CodePointBuffer; import org.pentaho.reporting.libraries.fonts.encoding.manual.Utf16LE; /** * Creation-Date: 31.07.2007, 19:09:52 * * @author Thomas Morgner */ public final class RevalidateTextEllipseProcessStep extends IterateStructuralProcessStep { private final boolean complexTextFeature; private long contentAreaX1; private long contentAreaX2; private TextCache textCache; private RenderableTextFactory textFactory; private CodePointBuffer buffer; private String ellipseOverride; private int[] bufferArray; public RevalidateTextEllipseProcessStep( final OutputProcessorMetaData metaData ) { this.bufferArray = new int[500]; this.textCache = new TextCache( 500 ); this.complexTextFeature = metaData.isFeatureSupported( OutputProcessorFeature.COMPLEX_TEXT ); if ( complexTextFeature ) { this.textFactory = new ComplexTextFactory(); } else { this.textFactory = new DefaultRenderableTextFactory( metaData ); } } public String getEllipseOverride() { return ellipseOverride; } public void setEllipseOverride( final String ellipseOverride ) { this.ellipseOverride = ellipseOverride; } public void compute( final RenderBox box, final long contentAreaX1, final long contentAreaX2 ) { this.contentAreaX1 = contentAreaX1; this.contentAreaX2 = contentAreaX2; startProcessing( box ); } public long getContentAreaX1() { return contentAreaX1; } public long getContentAreaX2() { return contentAreaX2; } protected boolean startInlineBox( final InlineRenderBox box ) { // compute the box's text-ellipse if necessary. if ( box.getTextEllipseBox() != null ) { return false; } final StaticBoxLayoutProperties sblp = box.getStaticBoxLayoutProperties(); final BoxDefinition bdef = box.getBoxDefinition(); final long boxContentX2 = ( box.getX() + box.getWidth() - bdef.getPaddingRight() - sblp.getBorderRight() ); if ( boxContentX2 > getContentAreaX2() ) { // This is an overflow. Compute the text-ellipse .. final RenderBox textEllipseBox = processTextEllipse( box, getContentAreaX2() ); box.setTextEllipseBox( textEllipseBox ); } return true; } private RenderBox processTextEllipse( final RenderBox box, final long x2 ) { final StyleSheet style = box.getStyleSheet(); final String reslit = (String) style.getStyleProperty( TextStyleKeys.RESERVED_LITERAL, ellipseOverride ); if ( reslit == null || "".equals( reslit ) ) { // oh, no ellipse. Thats nice. return null; } if ( complexTextFeature ) { return processTextEllipseComplex( box, x2, reslit ); } else { return processTextEllipseNormal( box, x2, reslit ); } } private RenderBox processTextEllipseComplex( final RenderBox box, final long x2, final String reslit ) { // todo Implement Arabic support return null; // return processTextEllipseNormal(box, x2, reslit); } private RenderBox processTextEllipseNormal( final RenderBox box, final long x2, String reslit ) { final StyleSheet style = box.getStyleSheet(); final RenderBox textEllipse = (RenderBox) box.derive( false ); final ReportAttributeMap map = box.getAttributes(); final TextCache.Result result = textCache.get( style.getId(), style.getChangeTracker(), map.getChangeTracker(), reslit ); if ( result != null ) { textEllipse.addGeneratedChilds( result.getText() ); textEllipse.addGeneratedChilds( result.getFinish() ); performTextEllipseLayout( textEllipse, x2 ); return textEllipse; } if ( buffer != null ) { buffer.setCursor( 0 ); } buffer = Utf16LE.getInstance().decodeString( reslit, buffer ); bufferArray = buffer.getBuffer( bufferArray ); textFactory.startText(); final RenderNode[] renderNodes = textFactory.createText( bufferArray, 0, buffer.getLength(), style, box.getElementType(), box.getInstanceId(), map ); final RenderNode[] finishNodes = textFactory.finishText(); textEllipse.addGeneratedChilds( renderNodes ); textEllipse.addGeneratedChilds( finishNodes ); textCache.store( style.getId(), style.getChangeTracker(), map.getChangeTracker(), reslit, style, map, renderNodes, finishNodes ); performTextEllipseLayout( textEllipse, x2 ); return textEllipse; } private void performTextEllipseLayout( final RenderBox box, final long x2 ) { // we do assume that the text-ellipse box is a simple box with no sub-boxes. if ( box == null ) { return; } long x = x2; RenderNode node = box.getLastChild(); while ( node != null ) { final long nodeWidth = node.getMaximumBoxWidth(); node.setX( x - nodeWidth ); node.setWidth( node.getMaximumBoxWidth() ); node.setY( box.getY() ); node.setHeight( box.getHeight() ); node = node.getNext(); x -= nodeWidth; } box.setX( x ); box.setWidth( x2 - x ); box.setContentAreaX1( x ); box.setContentAreaX2( x2 - x ); } protected void processParagraphChilds( final ParagraphRenderBox box ) { startProcessing( box.getEffectiveLineboxContainer() ); } }