/*
* 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.plaintext.driver;
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;
import java.awt.print.Paper;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
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;
}
}