/*
* 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.libraries.pixie.wmf.records;
import org.pentaho.reporting.libraries.pixie.wmf.BrushConstants;
import org.pentaho.reporting.libraries.pixie.wmf.MfDcState;
import org.pentaho.reporting.libraries.pixie.wmf.MfLogFont;
import org.pentaho.reporting.libraries.pixie.wmf.MfRecord;
import org.pentaho.reporting.libraries.pixie.wmf.MfType;
import org.pentaho.reporting.libraries.pixie.wmf.TextConstants;
import org.pentaho.reporting.libraries.pixie.wmf.WmfFile;
import java.awt.*;
/**
* The ExtTextOut function draws text using the currently selected font, background color, and text color. You can
* optionally provide dimensions to be used for clipping, opaquing, or both.
* <p/>
* META_EXTTEXTOUT <h1>NEAREST API CALL</h1>
* <pre>#include <windows.h>
* BOOL32 ExtTextOutA
* (
* HDC32 hdc,
* INT32 x,
* INT32 y,
* UINT32 flags,
* const RECT32 *lprect,
* LPCSTR str,
* UINT32 count,
* const INT32 *lpDx
* );
* </pre>
*/
public class MfCmdExtTextOut extends MfCmd {
private static final int POS_Y = 0;
private static final int POS_X = 1;
private static final int POS_CHAR_COUNT = 2;
private static final int POS_FLAGS = 3;
private static final int POS_CLIP_X = 4;
private static final int POS_CLIP_Y = 5;
private static final int POS_CLIP_W = 6;
private static final int POS_CLIP_H = 7;
private static final int RECORD_BASE_SIZE_CLIPPED = 8;
private static final int RECORD_BASE_SIZE_STANDARD = 4;
public static final int ETO_OPAQUE = 0x0002;
public static final int ETO_CLIPPED = 0x0004;
public static final int ETO_GLYPH_INDEX = 0x0010;
public static final int ETO_RTLREADING = 0x0080;
public static final int ETO_IGNORELANGUAGE = 0x1000;
private int flags;
private int x;
private int y;
private int cx;
private int cy;
private int cw;
private int ch;
private int scaled_x;
private int scaled_y;
private int scaled_cx;
private int scaled_cy;
private int scaled_cw;
private int scaled_ch;
private String text;
public MfCmdExtTextOut() {
}
/**
* Replays the command on the given WmfFile.
*
* @param file the meta file.
*/
public void replay( final WmfFile file ) {
final Graphics2D graphics = file.getGraphics2D();
final MfDcState state = file.getCurrentState();
final MfLogFont lFont = state.getLogFont();
state.prepareDrawText();
final FontMetrics metrics = graphics.getFontMetrics();
final int textWidth = metrics.stringWidth( text );
final Point p = getScaledOrigin();
final int x = p.x + calcDeltaX( state.getVerticalTextAlignment(), textWidth );
int y = p.y + calcDeltaY( state.getHorizontalTextAlignment(), metrics );
if ( isOpaque() || state.getBkMode() != BrushConstants.TRANSPARENT ) {
final Rectangle background = new Rectangle( x, y - metrics.getAscent(), textWidth, metrics.getHeight() );
graphics.setColor( state.getBkColor() );
graphics.fill( background );
graphics.setColor( state.getTextColor() );
}
// System.out.println("X: " + x + " Y: " + p.y + " " + calcDeltaY(state.getHorizontalTextAlignment(), metrics));
final Graphics2D g2 = (Graphics2D) graphics.create();
g2.drawString( text, x, y );
if ( lFont.isUnderline() ) { // Underline.
y += metrics.getDescent() / 8 + 1;
//state.prepareDraw();
g2.setStroke( new BasicStroke( metrics.getHeight() / 14 ) );
g2.drawLine( x, y, x + textWidth, y );
//state.postDraw();
}
if ( lFont.isStrikeOut() ) { // Underline.
//state.prepareDraw();
y -= metrics.getAscent() / 2.5 + 1;
g2.setStroke( new BasicStroke( metrics.getHeight() / 14 ) );
g2.drawLine( x, y, x + textWidth, y );
//state.postDraw();
}
state.postDrawText();
}
protected int calcDeltaX( final int valign, final int textWidth ) {
if ( valign == TextConstants.TA_LEFT ) {
return 0;
} else if ( valign == TextConstants.TA_CENTER ) {
return textWidth / -2;
} else {
return textWidth * -1;
}
}
protected int calcDeltaY( final int halign, final FontMetrics fm ) {
if ( halign == TextConstants.TA_TOP ) {
return ( fm.getAscent() );
} else if ( halign == TextConstants.TA_BOTTOM ) {
return ( fm.getDescent() );
} else {
return 0;
}
}
/**
* Creates a empty unintialized copy of this command implementation.
*
* @return a new instance of the command.
*/
public MfCmd getInstance() {
return new MfCmdExtTextOut();
}
/**
* Reads the function identifier. Every record type is identified by a function number corresponding to one of the
* Windows GDI functions used.
*
* @return the function identifier.
*/
public int getFunction() {
return MfType.EXT_TEXT_OUT;
}
/**
* Reads the command data from the given record and adjusts the internal parameters according to the data parsed.
* <p/>
* After the raw record was read from the datasource, the record is parsed by the concrete implementation.
*
* @param record the raw data that makes up the record.
*/
public void setRecord( final MfRecord record ) {
final int y = record.getParam( POS_Y );
final int x = record.getParam( POS_X );
final int count = record.getParam( POS_CHAR_COUNT );
final int flag = record.getParam( POS_FLAGS );
final int stringOffset;
int cx = 0;
int cy = 0;
int cw = 0;
int ch = 0;
if ( ( flag & ETO_CLIPPED ) == ETO_CLIPPED ) {
cx = record.getParam( POS_CLIP_X );
cy = record.getParam( POS_CLIP_Y );
cw = record.getParam( POS_CLIP_W );
ch = record.getParam( POS_CLIP_H );
stringOffset = RECORD_BASE_SIZE_CLIPPED;
} else {
stringOffset = RECORD_BASE_SIZE_STANDARD;
}
final String text = record.getStringParam( stringOffset, count );
setOrigin( x, y );
setText( text );
setClippingRect( cx, cy, cw, ch );
setFlags( flag );
}
/**
* Creates a new record based on the data stored in the MfCommand. This writer does not write a char-spacing record.
*
* @return the created record.
*/
public MfRecord getRecord() {
final String text = getText();
final int flag = getFlags();
final int parcnt;
if ( ( flag & ETO_CLIPPED ) == ETO_CLIPPED ) {
parcnt = RECORD_BASE_SIZE_CLIPPED;
} else {
parcnt = RECORD_BASE_SIZE_STANDARD;
}
final int recordLength = (int) ( Math.ceil( text.length() / 2 ) * 2 ) + parcnt;
final MfRecord record = new MfRecord( recordLength );
final Point origin = getOrigin();
record.setParam( POS_Y, (int) origin.getY() );
record.setParam( POS_X, (int) origin.getX() );
record.setParam( POS_CHAR_COUNT, text.length() );
record.setParam( POS_FLAGS, flag );
if ( ( flag & ETO_CLIPPED ) == ETO_CLIPPED ) {
final Rectangle rect = getClippingRect();
record.setParam( POS_CLIP_X, rect.x );
record.setParam( POS_CLIP_Y, rect.y );
record.setParam( POS_CLIP_W, rect.width );
record.setParam( POS_CLIP_H, rect.height );
}
record.setStringParam( parcnt, text );
return record;
}
public String toString() {
final StringBuffer b = new StringBuffer();
b.append( "[EXT_TEXT_OUT] text=" );
b.append( getText() );
b.append( " origin=" );
b.append( getOrigin() );
b.append( " clippingRect=" );
b.append( getClippingRect() );
b.append( " flags=" );
b.append( getFlags() );
return b.toString();
}
public void setOrigin( final int x, final int y ) {
this.x = x;
this.y = y;
scaleXChanged();
scaleYChanged();
}
public Point getOrigin() {
return new Point( x, y );
}
public Point getScaledOrigin() {
return new Point( scaled_x, scaled_y );
}
public boolean isClipped() {
return ( flags & ETO_CLIPPED ) == ETO_CLIPPED;
}
public boolean isOpaque() {
return ( flags & ETO_OPAQUE ) == ETO_OPAQUE;
}
public int getFlags() {
return flags;
}
public void setFlags( final int flags ) {
this.flags = flags;
}
public void setClippingRect( final int cx, final int cy, final int cw, final int ch ) {
this.cx = cx;
this.cy = cy;
this.cw = cw;
this.ch = ch;
scaleXChanged();
scaleYChanged();
}
public Rectangle getClippingRect() {
return new Rectangle( cx, cy, cw, ch );
}
public Rectangle getScaledClippingRect() {
return new Rectangle( scaled_cx, scaled_cy, scaled_cw, scaled_ch );
}
public void setText( final String text ) {
this.text = text;
}
public String getText() {
return text;
}
protected void scaleXChanged() {
scaled_x = getScaledX( x );
scaled_cx = getScaledX( cx );
scaled_cw = getScaledX( cw );
}
protected void scaleYChanged() {
scaled_y = getScaledY( y );
scaled_cy = getScaledY( cy );
scaled_ch = getScaledY( ch );
}
}