/*
* 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 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.output.pageable.plaintext.driver;
import java.awt.print.Paper;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import org.pentaho.reporting.engine.classic.core.modules.output.pageable.plaintext.helper.EncodingUtilities;
import org.pentaho.reporting.engine.classic.core.modules.output.pageable.plaintext.helper.PrinterEncoding;
import org.pentaho.reporting.engine.classic.core.modules.output.pageable.plaintext.helper.PrinterSpecification;
import org.pentaho.reporting.engine.classic.core.util.PageFormatFactory;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.fonts.encoding.EncodingRegistry;
public class IBMCompatiblePrinterDriver implements PrinterDriver
{
public static class GenericIBMPrinterSpecification implements PrinterSpecification
{
public GenericIBMPrinterSpecification()
{
}
public String getDisplayName()
{
return getName();
}
/**
* Returns the encoding definition for the given java encoding.
*
* @param encoding the java encoding that should be mapped into a printer specific encoding.
* @return the printer specific encoding.
* @throws IllegalArgumentException if the given encoding is not supported.
*/
public PrinterEncoding getEncoding(final String encoding)
{
try
{
return new PrinterEncoding
(encoding, encoding, encoding, IBMCompatiblePrinterDriver.GenericIBMPrinterSpecification.translateCodePage(
encoding));
}
catch (UnsupportedEncodingException e)
{
throw new IllegalArgumentException("The given encoding is not supported.");
}
}
/**
* Translates the given Codepage String into a IBM Byte Code. The encoding string must be in the format CpXXXX where
* XXXX is the number of the codepage.
* <p/>
*
* @param cp the code page
* @return the epson byte code.
* @throws java.io.UnsupportedEncodingException
* if the encoding is not supported.
*/
private static byte[] translateCodePage(final String cp)
throws UnsupportedEncodingException
{
// Mapping Rule:
// n = NumberofCodePage + (10000 if codepage contains a character (Cp437G))
if (StringUtils.startsWithIgnoreCase(cp, "cp"))
{
// check the supplied encoding ...
// only Cp- encodings are supported ...
if (EncodingRegistry.getInstance().isSupportedEncoding(cp) == false)
{
throw new UnsupportedEncodingException("The encoding " + cp + "is not valid");
}
final String encodingName = cp.substring(2);
try
{
int i;
if (Character.isDigit(encodingName.charAt(encodingName.length() - 1)) == false)
{
i = Integer.parseInt(encodingName.substring(0, encodingName.length() - 1));
i += 10000;
}
else
{
i = Integer.parseInt(encodingName);
}
final byte[] retval = new byte[2];
retval[0] = (byte) (i >> 8);
retval[1] = (byte) (i & 0xff);
return retval;
}
catch (Exception e)
{
throw new UnsupportedEncodingException("The encoding " + cp + "is not valid");
}
}
throw new UnsupportedEncodingException("The encoding " + cp + " is no codepage encoding");
}
/**
* Returns the name of the encoding mapping. This is usually the same as the printer model name.
*
* @return the printer model.
*/
public String getName()
{
return "Generic IBM Printer Specification";
}
/**
* Checks whether the given Java-encoding is supported.
*
* @param encoding the java encoding that should be mapped into a printer specific encoding.
* @return true, if there is a mapping, false otherwise.
*/
public boolean isEncodingSupported(final String encoding)
{
try
{
IBMCompatiblePrinterDriver.GenericIBMPrinterSpecification.translateCodePage(encoding);
return true;
}
catch (UnsupportedEncodingException use)
{
return false;
}
}
/**
* Returns true, if a given operation is supported, false otherwise.
*
* @param operationName the operation, that should be performed
* @return true, if the printer will be able to perform that operation, false otherwise.
*/
public boolean isFeatureAvailable(final String operationName)
{
// is not used yet.
return true;
}
}
private static class DriverState
{
private boolean bold;
private boolean underline;
private boolean italic;
private byte font;
private int manualLeftBorder;
protected DriverState()
{
}
public boolean isBold()
{
return bold;
}
public void setBold(final boolean bold)
{
this.bold = bold;
}
public boolean isItalic()
{
return italic;
}
public void setItalic(final boolean italic)
{
this.italic = italic;
}
public boolean isUnderline()
{
return underline;
}
public void setUnderline(final boolean underline)
{
this.underline = underline;
}
public byte getFont()
{
return font;
}
public void setFont(final byte font)
{
this.font = font;
}
public int getManualLeftBorder()
{
return manualLeftBorder;
}
public void setManualLeftBorder(final int manualLeftBorder)
{
this.manualLeftBorder = manualLeftBorder;
}
}
public static final int QUALITY_UNDEFINED = -1;
public static final int QUALITY_FAST_DRAFT = 0;
public static final int QUALITY_DRAFT = 0x40;
public static final int QUALITY_LETTER = 0x80;
public static final int QUALITY_ENHANCED_LETTER = 0xC0;
public static final int QUALITY_DEFAULT = 0xFF;
private OutputStream out;
private float charsPerInch;
private float linesPerInch;
private byte[] endOfPage;
private boolean autoLF;
private int printQuality;
private PrinterSpecification printerSpecification;
private EncodingUtilities encodingUtilities;
private DefaultFontMapper fontMapper;
private IBMCompatiblePrinterDriver.DriverState driverState;
private boolean firstPage;
private String encoding;
public IBMCompatiblePrinterDriver(final OutputStream out,
final float charsPerInch,
final float linesPerInch)
{
this.out = out;
this.charsPerInch = charsPerInch;
this.linesPerInch = linesPerInch;
this.endOfPage = new byte[]{(byte) PrinterDriverCommands.FORM_FEED};
this.printerSpecification = new IBMCompatiblePrinterDriver.GenericIBMPrinterSpecification();
this.fontMapper = new DefaultFontMapper();
this.fontMapper.setDefaultFont(PrinterDriverCommands.SELECT_FONT_FROM_MENU);
this.driverState = new IBMCompatiblePrinterDriver.DriverState();
this.firstPage = true;
}
public void setAutoLF(final boolean autoLF)
{
this.autoLF = autoLF;
}
public boolean isAutoLF()
{
return autoLF;
}
public int getPrintQuality()
{
return printQuality;
}
public void setPrintQuality(final int printQuality)
{
this.printQuality = printQuality;
}
/**
* Ends a new line.
*
* @param overflow
* @throws java.io.IOException if an IOError occures.
*/
public void endLine(final boolean overflow)
throws IOException
{
if (overflow == false)
{
out.write(PrinterDriverCommands.CARRIAGE_RETURN);
if (autoLF == false)
{
out.write(PrinterDriverCommands.LINE_FEED);
}
}
}
/**
* Ends the current page. Should print empty lines or an FORM_FEED command.
*
* @param overflow
* @throws java.io.IOException if there was an IOError while writing the command
*/
public void endPage(final boolean overflow)
throws IOException
{
if (overflow == false)
{
printRaw(endOfPage);
}
}
/**
* Flushes the output stream.
*
* @throws java.io.IOException if an IOError occured.
*/
public void flush()
throws IOException
{
out.flush();
}
/**
* Gets the default character width in CPI.
*
* @return the default character width in CPI.
*/
public float getCharactersPerInch()
{
return charsPerInch;
}
/**
* Gets the default line height.
*
* @return the default line height.
*/
public float getLinesPerInch()
{
return linesPerInch;
}
/**
* Prints a single text chunk at the given position on the current line. The chunk should not be printed, if an
* previous chunk overlays this chunk.
*
* @param chunk the chunk that should be written
* @throws java.io.IOException if an IO error occured.
*/
public void printChunk(final PlaintextDataChunk chunk)
throws IOException
{
final String text = chunk.getText().substring(0, chunk.getWidth());
final String fd = chunk.getFont();
sendDefineFont(fontMapper.getPrinterFont(fd));
sendFontStyle(chunk.isBold(), chunk.isItalic(), chunk.isUnderline());
getEncodingUtilities(encoding).writeEncodedText(text, out);
}
/**
* Prints an empty chunk. This is called for all undefined chunk-cells. The last defined font is used to print that
* empty text.
*
* @throws java.io.IOException if an IOError occured.
*/
public void printEmptyChunk(final int count)
throws IOException
{
sendFontStyle(driverState.isBold(), driverState.isItalic(), false);
for (int i = 0; i < count; i++)
{
out.write(PrinterDriverCommands.SPACE);
}
}
/**
* Prints some raw content. This content is not processed in any way, so be very carefull.
*
* @param raw the content that should be printed.
*/
public void printRaw(final byte[] raw)
throws IOException
{
out.write(raw);
}
/**
* Starts a new line.
*
* @throws java.io.IOException if an IOError occures.
*/
public void startLine()
throws IOException
{
final int manualLeftBorder = driverState.getManualLeftBorder();
for (int i = 0; i < manualLeftBorder; i++)
{
out.write(PrinterDriverCommands.SPACE);
}
}
/**
* Resets the printer and starts a new page. Prints the top border lines (if necessary).
*
* @throws java.io.IOException if there was an IOError while writing the command
*/
public void startPage(final Paper paper, final String encoding)
throws IOException
{
this.encoding = encoding;
final float charWidthPoints = 72.0f / getCharactersPerInch();
final float lineHeightPoints = 72.0f / getLinesPerInch();
if (firstPage)
{
// update the autoLF setting
sendAutoLF(isAutoLF());
sendDefinePrintQuality(getPrintQuality());
sendDefineCharacterWidth(getCharactersPerInch());
firstPage = false;
}
// set the line spacing ..
sendLineSpacing((int) lineHeightPoints);
// define the page size ..
// we redefine it for every page, as we do not assume that the page sizes
// will be the same for the whole report.
final int lines = (int) ((paper.getHeight() / 72.0f) * getLinesPerInch());
sendDefinePageLengthInLines(lines);
final PageFormatFactory fact = PageFormatFactory.getInstance();
final int borderLeft = (int) (fact.getLeftBorder(paper) / charWidthPoints);
final int borderRight = (int) (fact.getRightBorder(paper) / charWidthPoints);
final int borderTop = (int) (fact.getTopBorder(paper) / lineHeightPoints);
sendDefineHorizontalBorders(borderLeft, borderRight);
// print the top margin ..
for (int i = 0; i < borderTop; i++)
{
startLine();
endLine(false);
}
}
/**
* Defines the line spacing for the printer.
*
* @param lineHeight the height of a single line in points (1/72 inch).
* @throws java.io.IOException if an IOException occured while updating the printer state.
*/
public void sendLineSpacing(final int lineHeight)
throws IOException
{
out.write(0x1b);
out.write(0x41);
out.write(lineHeight);
out.write(0x1b);
out.write(0x32);
}
private void sendDefineHorizontalBorders(final int left, final int right)
throws IOException
{
out.write(0x1b);
out.write(0x58);
out.write(left);
out.write(right);
}
private void sendDefinePageLengthInLines(final int lines)
throws IOException
{
out.write(0x1b);
out.write(0x43);
out.write(lines);
}
private void sendDefinePrintQuality(final int quality)
throws IOException
{
out.write(0x1b);
out.write(0x5b);
out.write(0x64);
out.write(0x01);
out.write(0x00);
out.write(quality);
}
/**
* Defines the font style for the printed text. The IBM-CommandSet does not support strike-through.
*
* @param bold true, if the text should be printed in bold mode.
* @param italic true, if the text should be italic, false otherwise
* @param underline true, if the text should be underlined, false otherwise
* @throws java.io.IOException if there was an IOError while writing the command
*/
private void sendFontStyle(final boolean bold, final boolean italic,
final boolean underline)
throws IOException
{
if (driverState.isBold())
{
if (bold == false)
{
// disable bold
out.write(0x1b); // ESC
out.write(0x46); // F
}
}
else
{
if (bold == true)
{
// enable bold
out.write(0x1b); // ESC
out.write(0x45); // E
}
}
if (driverState.isItalic())
{
if (italic == false)
{
// disable italic
out.write(0x1b);
out.write(0x25);
out.write(0x48);
}
}
else
{
if (italic == true)
{
// enable italic
out.write(0x1b);
out.write(0x25);
out.write(0x47);
}
}
if (driverState.isUnderline())
{
if (underline == false)
{
// disable underline
out.write(0x1b); // ESC
out.write(0x2d); // -
out.write(0x00); // 0
}
}
else
{
if (underline == true)
{
// enable underline
out.write(0x1b); // ESC
out.write(0x2d); // -
out.write(0x01); // 1
}
}
driverState.setBold(bold);
driverState.setItalic(italic);
driverState.setUnderline(underline);
}
private float sendDefineCharacterWidth(final float charsPerInch)
throws IOException
{
if (charsPerInch <= 10)
{
out.write(0x12);
return 10;
}
else if (charsPerInch <= 12)
{
out.write(0x1b);
out.write(0x3a);
return 12;
}
else if (charsPerInch <= 15)
{
out.write(0x1b);
out.write(0x67);
return 15;
}
else if (charsPerInch <= 17.4)
{
out.write(0x0f);
return 17.4f;
}
else
{
out.write(0x1b);
out.write(0x0f);
return 20;
}
}
private void sendDefineCodepage(final String codePage)
throws IOException
{
final PrinterEncoding spec = getPrinterSpecification().getEncoding(codePage);
final byte[] cp = spec.getCode();
out.write(0x1b); // ESC
out.write(0x5b); // [
out.write(0x54); // T
out.write(0x04); // 0x04 (according to LexMark Manual P.30)
out.write(0x00); // const.
out.write(0x00); // const.
out.write(0x00); // const.
out.write(cp); // codepage as 2 byte sequence
}
private PrinterSpecification getPrinterSpecification()
{
return printerSpecification;
}
private void sendAutoLF(final boolean autoLF)
throws IOException
{
if (autoLF == false)
{
out.write(0x1b);
out.write(0x35);
out.write(0x30);
}
else
{
out.write(0x1b);
out.write(0x35);
out.write(0x31);
}
}
private void sendDefineFont(final byte b)
throws IOException
{
if (driverState.getFont() != b)
{
out.write(0x1b);
out.write(0x6b);
out.write(b);
driverState.setFont(b);
}
}
protected EncodingUtilities getEncodingUtilities(final String encoding)
throws IOException
{
if (encodingUtilities != null &&
encodingUtilities.getEncoding().equals(encoding))
{
return encodingUtilities;
}
encodingUtilities = new EncodingUtilities(encoding);
sendDefineCodepage(encoding);
return encodingUtilities;
}
}