/*
* 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 java.awt.BasicStroke;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
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;
/**
* 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);
}
}