/*
* 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.PrinterSpecification;
import org.pentaho.reporting.engine.classic.core.modules.output.pageable.plaintext.helper.PrinterSpecificationManager;
import org.pentaho.reporting.engine.classic.core.util.PageFormatFactory;
import java.awt.print.Paper;
import java.io.IOException;
import java.io.OutputStream;
public abstract class AbstractEpsonPrinterDriver implements PrinterDriver {
public static final String OP_NO_ASSIGN_CHAR_TABLE = "no.assign.character.table";
protected static class DriverState {
private boolean bold;
private boolean underline;
private boolean italic;
private boolean strikethrough;
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 boolean isStrikethrough() {
return strikethrough;
}
public void setStrikethrough( final boolean strikethrough ) {
this.strikethrough = strikethrough;
}
public void reset() {
this.bold = false;
this.italic = false;
this.strikethrough = false;
this.underline = false;
this.font = PrinterDriverCommands.SELECT_FONT_ROMAN;
this.manualLeftBorder = 0;
}
}
public static final String FONT_15_CPI = "Epson.Font-15CPI-available";
private PrinterSpecification printerSpecification;
private FontMapper fontMapper;
private OutputStream out;
private float charsPerInch;
private float linesPerInch;
private EncodingUtilities encodingUtilities;
private boolean firstPage;
private byte fallBackCharset;
// private int borderTop;
// private int borderBottom;
private DriverState driverState;
private String encoding;
protected AbstractEpsonPrinterDriver( final OutputStream out, final float charsPerInch, final float linesPerInch,
final String printerModel ) {
if ( out == null ) {
throw new NullPointerException();
}
if ( printerModel == null ) {
throw new NullPointerException();
}
this.out = out;
this.charsPerInch = charsPerInch;
this.linesPerInch = linesPerInch;
this.printerSpecification = lookupPrinterSpecification( printerModel );
this.fontMapper = new DefaultFontMapper();
this.firstPage = true;
this.driverState = new DriverState();
// validate the CPI values
if ( isValidCPI( charsPerInch ) == false ) {
throw new IllegalArgumentException( "The given CPI of '" + charsPerInch
+ "' is invalid for the selected printer model ('" + printerModel + "'." );
}
// we cannot influence the LPI, so we have to accept what the user gives in
}
private boolean isValidCPI( final float charsPerInch ) {
if ( charsPerInch == PrinterDriverCommands.CPI_10 ) {
return true;
}
if ( charsPerInch == PrinterDriverCommands.CPI_12 ) {
return true;
}
if ( charsPerInch == PrinterDriverCommands.CPI_17 ) {
return true;
}
if ( charsPerInch == PrinterDriverCommands.CPI_20 ) {
return true;
}
if ( charsPerInch == PrinterDriverCommands.CPI_15
&& getPrinterSpecification().isFeatureAvailable( AbstractEpsonPrinterDriver.FONT_15_CPI ) ) {
return true;
}
return false;
}
public AbstractEpsonPrinterDriver.DriverState getDriverState() {
return driverState;
}
public FontMapper getFontMapper() {
return fontMapper;
}
public void setFontMapper( final FontMapper fontMapper ) {
if ( fontMapper == null ) {
throw new NullPointerException();
}
this.fontMapper = fontMapper;
}
protected OutputStream getOut() {
return out;
}
protected boolean isFirstPage() {
return firstPage;
}
public PrinterSpecification getPrinterSpecification() {
return printerSpecification;
}
/**
* 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 );
out.write( PrinterDriverCommands.LINE_FEED );
} else {
out.write( 0x7F );
}
}
/**
* 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 ) {
out.write( PrinterDriverCommands.FORM_FEED );
}
}
/**
* 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;
}
/**
* Flushes the output stream.
*
* @throws java.io.IOException
* if an IOError occured.
*/
public void flush() throws IOException {
out.flush();
}
/**
* 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(), chunk.isStrikethrough() );
getEncodingUtilities( encoding ).writeEncodedText( text, out );
}
protected abstract void sendFontStyle( boolean bold, boolean italic, boolean underline, boolean strikeTrough )
throws IOException;
protected void sendDefineFont( final byte printerFont ) throws IOException {
if ( getDriverState().getFont() != printerFont ) {
getOut().write( 0x1b );
getOut().write( 0x6b );
getOut().write( printerFont );
getDriverState().setFont( printerFont );
}
}
/**
* 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( getDriverState().isBold(), getDriverState().isItalic(), false, 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 {
sendFontStyle( false, false, false, false );
final int manualLeftBorder = getDriverState().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 lineHeightPoints = 72.0f / getLinesPerInch();
final float charWidthPoints = 72.0f / getCharactersPerInch();
// Quoted from the Epson Reference Manual page R-4:
// 1. Send an ESC @ to initialize the printer
sendResetPrinter();
driverState.reset();
// 2. Set the unit of line spacing to the minimum vertical increment necessary
sendDefineLineSpacing( lineHeightPoints );
// 3. Set the printing area
final int lines = (int) ( ( paper.getHeight() / 72.0f ) * getLinesPerInch() );
sendDefinePageLengthInLines( lines );
sendDefineCharacterWidth( getCharactersPerInch() );
final PageFormatFactory fact = PageFormatFactory.getInstance();
final int borderLeft = (int) ( fact.getLeftBorder( paper ) / charWidthPoints );
final int borderRight = (int) ( fact.getRightBorder( paper ) / charWidthPoints );
sendDefineHorizontalBorders( borderLeft, borderRight );
final int borderTop = (int) ( fact.getTopBorder( paper ) / lineHeightPoints );
// borderBottom = (int) (fact.getBottomBorder(paper) / lineHeightPoints);
// print the top margin ..
for ( int i = 0; i < borderTop; i++ ) {
startLine();
endLine( false );
}
// 4. Assign character tables to each of the four active tables as
// necessary. (ESC/P2 printers only)
//
// this is done before we start printing the text (and may change mid-page).
// sendDefineCodepage(encoding);
// 5. Define any user-defined characters.
//
// this is done before we start printing the text (and may change mid-page).
// sendDefineUserCharacters();
}
protected void sendDefineCharacterWidth( final float charactersPerInch ) throws IOException {
if ( charactersPerInch == PrinterDriverCommands.CPI_10 ) {
getOut().write( 0x12 ); // disable condensed printing
getOut().write( 0x1b );
getOut().write( 0x50 ); // select 10 CPI
} else if ( charactersPerInch == PrinterDriverCommands.CPI_12 ) {
getOut().write( 0x12 ); // disable condensed printing
getOut().write( 0x1b );
getOut().write( 0x4d ); // select 12 CPI
} else if ( charactersPerInch == PrinterDriverCommands.CPI_15 ) {
// All ESC/P2 and 24Pin ESC/P printers support that mode
// Additionally, the 9Pin printer models FX-2170 and DFX-5000+
// support that character width.
getOut().write( 0x12 ); // disable condensed printing
getOut().write( 0x1b );
getOut().write( 0x67 );
} else if ( charactersPerInch == PrinterDriverCommands.CPI_17 ) {
getOut().write( 0x0f ); // enable condensed printing
getOut().write( 0x1b );
getOut().write( 0x50 ); // select 10 CPI (-> 17.14 cpi because of condensed printing)
} else if ( charactersPerInch == PrinterDriverCommands.CPI_20 ) {
getOut().write( 0x0f ); // enable condensed printing
getOut().write( 0x1b );
getOut().write( 0x4d ); // select 12 CPI (-> 20 cpi because of condensed printing)
} else {
throw new IllegalArgumentException( "The given character width is invalid" );
}
}
protected void sendResetPrinter() throws IOException {
out.write( 0x1b );
out.write( 0x40 );
}
protected abstract void sendDefineLineSpacing( float lineHeightInPoints ) throws IOException;
protected void sendDefinePageLengthInLines( final int paperSizeInLines ) throws IOException {
// SideEffects: Setting the page size will mark the current position
// as TopOfForm position
//
// All printers support that command.
getOut().write( 0x1b ); // ESC
getOut().write( 0x43 ); // C
getOut().write( paperSizeInLines );
}
protected void sendDefineHorizontalBorders( final int left, final int right ) throws IOException {
if ( left < 256 ) {
// depends on the pitch to be defined correctly.
// In that implementation we can assume that this is ok.
getOut().write( 0x1b ); // ESC
getOut().write( 0x6c ); // l
getOut().write( left );
getDriverState().setManualLeftBorder( 0 );
} else {
getOut().write( 0x1b ); // ESC
getOut().write( 0x6c ); // l
getOut().write( 255 );
getDriverState().setManualLeftBorder( left - 255 );
}
// Compatibility: All printers support that command.
// don't care about the right border, the PlainTextPage makes sure that
// we do not violate that constraint.
}
protected void sendDefineCodepage( final String encoding, final int characterTable ) throws IOException {
if ( getPrinterSpecification().isFeatureAvailable( AbstractEpsonPrinterDriver.OP_NO_ASSIGN_CHAR_TABLE ) ) {
out.write( 0x1b ); // ESC
out.write( 0x52 ); // R
out.write( getFallBackCharset() );
} else {
final byte[] cp = getPrinterSpecification().getEncoding( encoding ).getCode();
out.write( 0x1b ); // ESC
out.write( 0x28 ); // (
out.write( 0x74 ); // t
out.write( 0x03 ); // const: 3
out.write( 0x00 ); // const: 0
out.write( characterTable ); // Define charset; (0 works on all printers)
out.write( cp ); // the codepage
}
out.write( 0x1b ); // ESC
out.write( 0x74 ); // t
out.write( 0x00 ); // Select charset 0 (works on all printers)
}
protected void sendDefineUserCharacters() {
}
protected EncodingUtilities getEncodingUtilities( final String encoding ) throws IOException {
if ( encodingUtilities != null && encodingUtilities.getEncoding().equals( encoding ) ) {
return encodingUtilities;
}
encodingUtilities = new EncodingUtilities( encoding );
sendDefineCodepage( encoding, 0 );
return encodingUtilities;
}
protected abstract PrinterSpecificationManager getPrinterSpecificationManager();
private PrinterSpecification lookupPrinterSpecification( final String model ) {
final PrinterSpecificationManager printerSpecificationManager = getPrinterSpecificationManager();
if ( model == null ) {
return PrinterSpecificationManager.getGenericPrinter();
}
final PrinterSpecification printerModel = printerSpecificationManager.getPrinter( model );
if ( printerModel == null ) {
throw new IllegalArgumentException( "The printer model is not supported." );
}
return printerModel;
}
public byte getFallBackCharset() {
return fallBackCharset;
}
public void setFallBackCharset( final byte fallBackCharset ) {
this.fallBackCharset = fallBackCharset;
}
}