gsplus/src/printer.cpp

2107 lines
54 KiB
C++

/*
GSPLUS - Advanced Apple IIGS Emulator Environment
Based on the KEGS emulator written by Kent Dickey
See COPYRIGHT.txt for Copyright information
See LICENSE.txt for license (GPL v2)
*/
/*
* Copyright (C) 2002-2004 The DOSBox Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//Modified for the KEGS emulator by Christopher G. Mason 02/2010
//Added support for configuring the built in printer fonts
#include "printer.h"
#include <math.h>
#include "glog.h"
#include "support.h"
//#include "png.h"
//#pragma comment( lib, "libpng.lib" )
//#pragma comment (lib, "zdll.lib" )
static CPrinter* defaultPrinter = NULL;
static FILE *textPrinterFile = NULL;
#ifdef WIN32
const char* const textPrinterFileName = ".\\printer.txt";
#else
const char* const textPrinterFileName = "./printer.txt";
#endif
#define PARAM16(I) (params[I+1]*256+params[I])
#define PIXX ((Bitu)floor(curX*dpi+0.5))
#define PIXY ((Bitu)floor(curY*dpi+0.5))
static Bitu printer_timout;
static bool timeout_dirty;
static const char* document_path;
extern "C" char* g_printer_font_roman;
extern "C" char* g_printer_font_sans;
extern "C" char* g_printer_font_courier;
extern "C" char* g_printer_font_prestige;
extern "C" char* g_printer_font_script;
extern "C" char* g_printer_font_ocra;
Bit8u paramc = '0';
#include "printer_charmaps.h"
#ifdef HAVE_SDL
void CPrinter::FillPalette(Bit8u redmax, Bit8u greenmax, Bit8u bluemax, Bit8u colorID, SDL_Palette* pal)
{
float red=redmax/30.9;
float green=greenmax/30.9;
float blue=bluemax/30.9;
Bit8u colormask=colorID<<=5;
for(int i = 0; i < 32;i++) {
pal->colors[i+colormask].r=255-(red*(float)i);
pal->colors[i+colormask].g=255-(green*(float)i);
pal->colors[i+colormask].b=255-(blue*(float)i);
}
}
#endif // HAVE_SDL
CPrinter::CPrinter(Bit16u dpi, Bit16u width, Bit16u height, char* output, bool multipageOutput)
{
#ifdef HAVE_SDL
if (FT_Init_FreeType(&FTlib))
{
page = NULL;
}
else
{
this->dpi = dpi;
this->output = output;
this->multipageOutput = multipageOutput;
defaultPageWidth = (Real64)width/(Real64)10;
defaultPageHeight = (Real64)height/(Real64)10;
// Create page
page = SDL_CreateRGBSurface(
SDL_SWSURFACE,
(Bitu)(defaultPageWidth*dpi),
(Bitu)(defaultPageHeight*dpi),
8,
0,
0,
0,
0);
// Set a grey palette
SDL_Palette* palette = page->format->palette;
for (Bitu i=0; i<32; i++)
{
palette->colors[i].r =255;
palette->colors[i].g =255;
palette->colors[i].b =255;
}
// 0 = all white needed for logic 000
FillPalette( 0, 0, 0, 1, palette);
// 1 = magenta* 001
FillPalette( 0, 255, 0, 1, palette);
// 2 = cyan* 010
FillPalette(255, 0, 0, 2, palette);
// 3 = "violet" 011
FillPalette(255, 255, 0, 3, palette);
// 4 = yellow* 100
FillPalette( 0, 0, 255, 4, palette);
// 5 = red 101
FillPalette( 0, 255, 255, 5, palette);
// 6 = green 110
FillPalette(255, 0, 255, 6, palette);
// 7 = black 111
FillPalette(255, 255, 255, 7, palette);
// yyyxxxxx bit pattern: yyy=color xxxxx = intensity: 31=max
// Printing colors on top of each other ORs them and gets the
// correct resulting color.
// i.e. magenta on blank page yyy=001
// then yellow on magenta 001 | 100 = 101 = red
color=COLOR_BLACK;
curFont = NULL;
charRead = false;
autoFeed = false;
outputHandle = NULL;
resetPrinter();
if (strcasecmp(output, "printer") == 0)
{
#if defined (WIN32)
// Show Print dialog to obtain a printer device context
PRINTDLG pd;
pd.lStructSize = sizeof(PRINTDLG);
pd.hDevMode = (HANDLE) NULL;
pd.hDevNames = (HANDLE) NULL;
pd.Flags = PD_RETURNDC;
pd.hwndOwner = NULL;
pd.hDC = (HDC) NULL;
pd.nFromPage = 1;
pd.nToPage = 1;
pd.nMinPage = 0;
pd.nMaxPage = 0;
pd.nCopies = 1;
pd.hInstance = NULL;
pd.lCustData = 0L;
pd.lpfnPrintHook = (LPPRINTHOOKPROC) NULL;
pd.lpfnSetupHook = (LPSETUPHOOKPROC) NULL;
pd.lpPrintTemplateName = (LPCSTR) NULL;
pd.lpSetupTemplateName = (LPCSTR) NULL;
pd.hPrintTemplate = (HANDLE) NULL;
pd.hSetupTemplate = (HANDLE) NULL;
PrintDlg(&pd);
// TODO: what if user presses cancel?
printerDC = pd.hDC;
#endif // WIN32
}
}
#endif // HAVE_SDL
#ifndef HAVE_SDL
this->output = output;
this->multipageOutput = multipageOutput;
#endif // !HAVE_SDL
};
void CPrinter::resetPrinterHard()
{
#ifdef HAVE_SDL
charRead = false;
resetPrinter();
#endif // HAVE_SDL
}
void CPrinter::resetPrinter()
{
#ifdef HAVE_SDL
color=COLOR_BLACK;
curX = curY = 0.0;
ESCSeen = false;
FSSeen = false;
ESCCmd = 0;
numParam = neededParam = 0;
topMargin = 0.0;
leftMargin = 0.0;
rightMargin = pageWidth = defaultPageWidth;
bottomMargin = pageHeight = defaultPageHeight;
lineSpacing = (Real64)1/6;
cpi = 10.0;
curCharTable = 1;
style = 0;
extraIntraSpace = 0.0;
printUpperContr = true;
bitGraph.remBytes = 0;
densk = 0;
densl = 1;
densy = 2;
densz = 3;
charTables[0] = 0; // Italics
charTables[1] = charTables[2] = charTables[3] = 437;
definedUnit = -1;
multipoint = false;
multiPointSize = 0.0;
multicpi = 0.0;
hmi = -1.0;
msb = 255;
numPrintAsChar = 0;
LQtypeFace = roman;
selectCodepage(charTables[curCharTable]);
updateFont();
#endif // HAVE_SDL
newPage(false,true);
#ifdef HAVE_SDL
// Default tabs => Each eight characters
for (Bitu i=0;i<32;i++)
horiztabs[i] = i*8*(1/(Real64)cpi);
numHorizTabs = 32;
numVertTabs = 255;
#endif // HAVE_SDL
}
CPrinter::~CPrinter(void)
{
#ifdef HAVE_SDL
finishMultipage();
if (page != NULL)
{
SDL_FreeSurface(page);
page = NULL;
FT_Done_FreeType(FTlib);
}
#if defined (WIN32)
DeleteDC(printerDC);
#endif
#endif // HAVE_SDL
};
#ifdef HAVE_SDL
void CPrinter::selectCodepage(Bit16u cp)
{
Bit16u *mapToUse = NULL;
switch(cp)
{
case 0: // Italics, use cp437
case 437:
mapToUse = (Bit16u*)&cp437Map;
break;
case 737:
mapToUse = (Bit16u*)&cp737Map;
break;
case 775:
mapToUse = (Bit16u*)&cp775Map;
break;
case 850:
mapToUse = (Bit16u*)&cp850Map;
break;
case 852:
mapToUse = (Bit16u*)&cp852Map;
break;
case 855:
mapToUse = (Bit16u*)&cp855Map;
break;
case 857:
mapToUse = (Bit16u*)&cp857Map;
break;
case 860:
mapToUse = (Bit16u*)&cp860Map;
break;
case 861:
mapToUse = (Bit16u*)&cp861Map;
break;
case 863:
mapToUse = (Bit16u*)&cp863Map;
break;
case 864:
mapToUse = (Bit16u*)&cp864Map;
break;
case 865:
mapToUse = (Bit16u*)&cp865Map;
break;
case 866:
mapToUse = (Bit16u*)&cp866Map;
break;
default:
//LOG(LOG_MISC,LOG_WARN)("Unsupported codepage %i. Using CP437 instead.", cp);
mapToUse = (Bit16u*)&cp437Map;
}
for (int i=0; i<256; i++)
curMap[i] = mapToUse[i];
}
#endif // HAVE_SDL
#ifdef HAVE_SDL
void CPrinter::updateFont()
{
// char buffer[1000];
if (curFont != NULL)
FT_Done_Face(curFont);
char* fontName;
switch (LQtypeFace)
{
case roman:
fontName = g_printer_font_roman;
break;
case sansserif:
fontName = g_printer_font_sans;
break;
case courier:
fontName = g_printer_font_courier;
break;
case prestige:
fontName = g_printer_font_prestige;
break;
case script:
fontName = g_printer_font_script;
break;
case ocra:
case ocrb:
fontName = g_printer_font_ocra;
break;
default:
fontName = g_printer_font_roman;
}
if (FT_New_Face(FTlib, fontName, 0, &curFont))
{
glogf("Unable to load font %s", fontName);
curFont = NULL;
}
Real64 horizPoints = 10.5;
Real64 vertPoints = 10.5;
if (!multipoint) {
actcpi = cpi;
/*
switch(style & (STYLE_CONDENSED|STYLE_PROP)) {
case STYLE_CONDENSED: // only condensed
if (cpi == 10.0) {
actcpi = 17.14;
horizPoints *= 10.0/17.14;
} else if(cpi == 12.0) {
actcpi = 20.0;
horizPoints *= 10.0/20.0;
vertPoints *= 10.0/12.0;
} else {
// ignored
}
break;
case STYLE_PROP|STYLE_CONDENSED:
horizPoints /= 2.0;
break;
case 0: // neither
case STYLE_PROP: // only proportional
horizPoints *= 10.0/cpi;
vertPoints *= 10.0/cpi;
break;
}
*/
if (!(style & STYLE_CONDENSED)) {
horizPoints *= 10.0/cpi;
vertPoints *= 10.0/cpi;
}
if (!(style & STYLE_PROP)) {
if ((cpi == 10.0) && (style & STYLE_CONDENSED)) {
actcpi = 17.14;
horizPoints *= 10.0/17.14;
}
if ((cpi == 12.0) && (style & STYLE_CONDENSED)) {
actcpi = 20.0;
horizPoints *= 10.0/20.0;
vertPoints *= 10.0/12.0;
}
} else if (style & STYLE_CONDENSED) horizPoints /= 2.0;
if ((style & STYLE_DOUBLEWIDTH) || (style & STYLE_DOUBLEWIDTHONELINE)) {
actcpi /= 2.0;
horizPoints *= 2.0;
}
if (style & STYLE_DOUBLEHEIGHT) vertPoints *= 2.0;
} else { // multipoint true
actcpi = multicpi;
horizPoints = vertPoints = multiPointSize;
}
if ((style & STYLE_SUPERSCRIPT) || (style & STYLE_SUBSCRIPT)) {
horizPoints *= 2.0/3.0;
vertPoints *= 2.0/3.0;
actcpi /= 2.0/3.0;
}
FT_Set_Char_Size(curFont, (Bit16u)horizPoints*64, (Bit16u)vertPoints*64, dpi, dpi);
if (style & STYLE_ITALICS || charTables[curCharTable] == 0)
{
FT_Matrix matrix;
matrix.xx = 0x10000L;
matrix.xy = (FT_Fixed)(0.20 * 0x10000L);
matrix.yx = 0;
matrix.yy = 0x10000L;
FT_Set_Transform(curFont, &matrix, 0);
}
}
#endif // HAVE_SDL
#ifdef HAVE_SDL
bool CPrinter::processCommandChar(Bit8u ch)
{
if (ESCSeen || FSSeen)
{
ESCCmd = ch;
if(FSSeen) ESCCmd |= 0x800;
ESCSeen = FSSeen = false;
numParam = 0;
switch (ESCCmd) {
case 0x02: // Undocumented
case 0x0a: // Reverse line feed (ESC LF)
case 0x0c: // Return to top of current page (ESC FF)
case 0x0e: // Select double-width printing (one line) (ESC SO)
case 0x0f: // Select condensed printing (ESC SI)
case 0x23: // Cancel MSB control (ESC #)
case 0x30: // Select 1/8-inch line spacing (ESC 0)
case 0x31: // Select 7/60-inch line spacing (ESC 1)
case 0x32: // Select 1/6-inch line spacing (ESC 2)
case 0x34: // Select italic font (ESC 4)
case 0x35: // Cancel italic font (ESC 5)
case 0x36: // Enable printing of upper control codes (ESC 6)
case 0x37: // Enable upper control codes (ESC 7)
case 0x38: // Disable paper-out detector (ESC 8)
case 0x39: // Enable paper-out detector (ESC 9)
case 0x3c: // Unidirectional mode (one line) (ESC <)
case 0x3d: // Set MSB to 0 (ESC =)
case 0x3e: // Set MSB to 1 (ESC >)
case 0x40: // Initialize printer (ESC @)
case 0x45: // Select bold font (ESC E)
case 0x46: // Cancel bold font (ESC F)
case 0x47: // Select double-strike printing (ESC G)
case 0x48: // Cancel double-strike printing (ESC H)
case 0x4d: // Select 10.5-point, 12-cpi (ESC M)
case 0x4f: // Cancel bottom margin [conflict] (ESC O)
case 0x50: // Select 10.5-point, 10-cpi (ESC P)
case 0x54: // Cancel superscript/subscript printing (ESC T)
case 0x5e: // Enable printing of all character codes on next character (ESC ^)
case 0x67: // Select 10.5-point, 15-cpi (ESC g)
case 0x834: // Select italic font (FS 4) (= ESC 4)
case 0x835: // Cancel italic font (FS 5) (= ESC 5)
case 0x846: // Select forward feed mode (FS F)
case 0x852: // Select reverse feed mode (FS R)
neededParam = 0;
break;
case 0x19: // Control paper loading/ejecting (ESC EM)
case 0x20: // Set intercharacter space (ESC SP)
case 0x21: // Master select (ESC !)
case 0x2b: // Set n/360-inch line spacing (ESC +)
case 0x2d: // Turn underline on/off (ESC -)
case 0x2f: // Select vertical tab channel (ESC /)
case 0x33: // Set n/180-inch line spacing (ESC 3)
case 0x41: // Set n/60-inch line spacing (ESC A)
case 0x43: // Set page length in lines (ESC C)
case 0x49: // Select character type and print pitch (ESC I)
case 0x4a: // Advance print position vertically (ESC J)
case 0x4e: // Set bottom margin (ESC N)
case 0x51: // Set right margin (ESC Q)
case 0x52: // Select an international character set (ESC R)
case 0x53: // Select superscript/subscript printing (ESC S)
case 0x55: // Turn unidirectional mode on/off (ESC U)
//case 0x56: // Repeat data (ESC V)
case 0x57: // Turn double-width printing on/off (ESC W)
case 0x61: // Select justification (ESC a)
case 0x66: // Absolute horizontal tab in columns [conflict] (ESC f)
case 0x68: // Select double or quadruple size (ESC h)
case 0x69: // Immediate print (ESC i)
case 0x6a: // Reverse paper feed (ESC j)
case 0x6b: // Select typeface (ESC k)
case 0x6c: // Set left margin (ESC 1)
case 0x70: // Turn proportional mode on/off (ESC p)
case 0x72: // Select printing color (ESC r)
case 0x73: // Low-speed mode on/off (ESC s)
case 0x74: // Select character table (ESC t)
case 0x77: // Turn double-height printing on/off (ESC w)
case 0x78: // Select LQ or draft (ESC x)
case 0x7e: // Select/Deselect slash zero (ESC ~)
case 0x832: // Select 1/6-inch line spacing (FS 2) (= ESC 2)
case 0x833: // Set n/360-inch line spacing (FS 3) (= ESC +)
case 0x841: // Set n/60-inch line spacing (FS A) (= ESC A)
case 0x843: // Select LQ type style (FS C) (= ESC k)
case 0x845: // Select character width (FS E)
case 0x849: // Select character table (FS I) (= ESC t)
case 0x853: // Select High Speed/High Density elite pitch (FS S)
case 0x856: // Turn double-height printing on/off (FS V) (= ESC w)
neededParam = 1;
break;
case 0x24: // Set absolute horizontal print position (ESC $)
case 0x3f: // Reassign bit-image mode (ESC ?)
case 0x4b: // Select 60-dpi graphics (ESC K)
case 0x4c: // Select 120-dpi graphics (ESC L)
case 0x59: // Select 120-dpi, double-speed graphics (ESC Y)
case 0x5a: // Select 240-dpi graphics (ESC Z)
case 0x5c: // Set relative horizontal print position (ESC \)
case 0x63: // Set horizontal motion index (HMI) [conflict] (ESC c)
case 0x65: // Set vertical tab stops every n lines (ESC e)
case 0x85a: // Print 24-bit hex-density graphics (FS Z)
neededParam = 2;
break;
case 0x2a: // Select bit image (ESC *)
case 0x58: // Select font by pitch and point [conflict] (ESC X)
neededParam = 3;
break;
case 0x5b: // Select character height, width, line spacing
neededParam = 7;
break;
case 0x62: // Set vertical tabs in VFU channels (ESC b)
case 0x42: // Set vertical tabs (ESC B)
numVertTabs = 0;
return true;
case 0x44: // Set horizontal tabs (ESC D)
numHorizTabs = 0;
return true;
case 0x25: // Select user-defined set (ESC %)
case 0x26: // Define user-defined characters (ESC &)
case 0x3a: // Copy ROM to RAM (ESC :)
//LOG(LOG_MISC,LOG_ERROR)("User-defined characters not supported!");
return true;
case 0x28: // Two bytes sequence
return true;
default:
/*LOG_MSG("PRINTER: Unknown command %c (%02Xh) %c , unable to skip parameters.",
(ESCCmd & 0x800)?"FS":"ESC",ESCCmd, ESCCmd);*/
neededParam = 0;
ESCCmd = 0;
return true;
}
if (neededParam > 0)
return true;
}
// Two bytes sequence
if (ESCCmd == '(')
{
ESCCmd = 0x200 + ch;
switch (ESCCmd)
{
case 0x242: // Bar code setup and print (ESC (B)
case 0x25e: // Print data as characters (ESC (^)
neededParam = 2;
break;
case 0x255: // Set unit (ESC (U)
neededParam = 3;
break;
case 0x243: // Set page length in defined unit (ESC (C)
case 0x256: // Set absolute vertical print position (ESC (V)
case 0x276: // Set relative vertical print position (ESC (v)
neededParam = 4;
break;
case 0x274: // Assign character table (ESC (t)
case 0x22d: // Select line/score (ESC (-)
neededParam = 5;
break;
case 0x263: // Set page format (ESC (c)
neededParam = 6;
break;
default:
// ESC ( commands are always followed by a "number of parameters" word parameter
//LOG(LOG_MISC,LOG_ERROR)
printf("PRINTER: Skipping unsupported command ESC ( %c (%02X).", ESCCmd, ESCCmd);
neededParam = 2;
ESCCmd = 0x101;
return true;
}
if (neededParam > 0)
return true;
}
// Ignore VFU channel setting
if (ESCCmd == 0x62) {
ESCCmd = 0x42;
return true;
}
// Collect vertical tabs
if (ESCCmd == 0x42) {
if (ch == 0 || (numVertTabs>0 && verttabs[numVertTabs-1] > (Real64)ch*lineSpacing)) // Done
ESCCmd = 0;
else
if (numVertTabs < 16)
verttabs[numVertTabs++] = (Real64)ch*lineSpacing;
}
// Collect horizontal tabs
if (ESCCmd == 0x44)
{
if (ch == 0 || (numHorizTabs>0 && horiztabs[numHorizTabs-1] > (Real64)ch*(1/(Real64)cpi))) // Done
ESCCmd = 0;
else
if (numHorizTabs < 32)
horiztabs[numHorizTabs++] = (Real64)ch*(1/(Real64)cpi);
}
if (numParam < neededParam)
{
params[numParam++] = ch;
if (numParam < neededParam)
return true;
}
if (ESCCmd != 0)
{
switch (ESCCmd)
{
case 0x02: // Undocumented
// Ignore
break;
case 0x0e: // Select double-width printing (one line) (ESC SO)
if (!multipoint)
{
hmi = -1;
style |= STYLE_DOUBLEWIDTHONELINE;
updateFont();
}
break;
case 0x0f: // Select condensed printing (ESC SI)
if (!multipoint && (cpi!=15.0)) {
hmi = -1;
style |= STYLE_CONDENSED;
updateFont();
}
break;
case 0x19: // Control paper loading/ejecting (ESC EM)
// We are not really loading paper, so most commands can be ignored
if (params[0] == 'R')
newPage(true,false); // TODO resetx?
break;
case 0x20: // Set intercharacter space (ESC SP)
if (!multipoint)
{
extraIntraSpace = (Real64)params[0] / (printQuality==QUALITY_DRAFT?120:180);
hmi = -1;
updateFont();
}
break;
case 0x21: // Master select (ESC !)
cpi = params[0] & 0x01 ? 12:10;
// Reset first seven bits
style &= 0xFF80;
if (params[0] & 0x02)
style |= STYLE_PROP;
if (params[0] & 0x04)
style |= STYLE_CONDENSED;
if (params[0] & 0x08)
style |= STYLE_BOLD;
if (params[0] & 0x10)
style |= STYLE_DOUBLESTRIKE;
if (params[0] & 0x20)
style |= STYLE_DOUBLEWIDTH;
if (params[0] & 0x40)
style |= STYLE_ITALICS;
if (params[0] & 0x80)
{
score = SCORE_SINGLE;
style |= STYLE_UNDERLINE;
}
hmi = -1;
multipoint = false;
updateFont();
break;
case 0x23: // Cancel MSB control (ESC #)
msb = 255;
break;
case 0x24: // Set absolute horizontal print position (ESC $)
{
Real64 unitSize = definedUnit;
if (unitSize < 0)
unitSize = (Real64)60.0;
Real64 newX = leftMargin + ((Real64)PARAM16(0)/unitSize);
if (newX <= rightMargin)
curX = newX;
}
break;
case 0x85a: // Print 24-bit hex-density graphics (FS Z)
setupBitImage(40, PARAM16(0));
break;
case 0x2a: // Select bit image (ESC *)
setupBitImage(params[0], PARAM16(1));
break;
case 0x2b: // Set n/360-inch line spacing (ESC +)
case 0x833: // Set n/360-inch line spacing (FS 3)
lineSpacing = (Real64)params[0]/360;
break;
case 0x2d: // Turn underline on/off (ESC -)
if (params[0] == 0 || params[0] == 48)
style &= ~STYLE_UNDERLINE;
if (params[0] == 1 || params[0] == 49)
{
style |= STYLE_UNDERLINE;
score = SCORE_SINGLE;
}
updateFont();
break;
case 0x2f: // Select vertical tab channel (ESC /)
// Ignore
break;
case 0x30: // Select 1/8-inch line spacing (ESC 0)
lineSpacing = (Real64)1/8;
break;
case 0x32: // Select 1/6-inch line spacing (ESC 2)
lineSpacing = (Real64)1/6;
break;
case 0x33: // Set n/180-inch line spacing (ESC 3)
lineSpacing = (Real64)params[0]/180;
break;
case 0x34: // Select italic font (ESC 4)
style |= STYLE_ITALICS;
updateFont();
break;
case 0x35: // Cancel italic font (ESC 5)
style &= ~STYLE_ITALICS;
updateFont();
break;
case 0x36: // Enable printing of upper control codes (ESC 6)
printUpperContr = true;
break;
case 0x37: // Enable upper control codes (ESC 7)
printUpperContr = false;
break;
case 0x3c: // Unidirectional mode (one line) (ESC <)
// We don't have a print head, so just ignore this
break;
case 0x3d: // Set MSB to 0 (ESC =)
msb = 0;
break;
case 0x3e: // Set MSB to 1 (ESC >)
msb = 1;
break;
case 0x3f: // Reassign bit-image mode (ESC ?)
if (params[0] == 75)
densk = params[1];
if (params[0] == 76)
densl = params[1];
if (params[0] == 89)
densy = params[1];
if (params[0] == 90)
densz = params[1];
break;
case 0x40: // Initialize printer (ESC @)
resetPrinter();
break;
case 0x41: // Set n/60-inch line spacing
case 0x841:
lineSpacing = (Real64)params[0]/60;
break;
case 0x43: // Set page length in lines (ESC C)
if (params[0] != 0)
pageHeight = bottomMargin = (Real64)params[0] * lineSpacing;
else // == 0 => Set page length in inches
{
neededParam = 1;
numParam = 0;
ESCCmd = 0x100;
return true;
}
break;
case 0x45: // Select bold font (ESC E)
style |= STYLE_BOLD;
updateFont();
break;
case 0x46: // Cancel bold font (ESC F)
style &= ~STYLE_BOLD;
updateFont();
break;
case 0x47: // Select dobule-strike printing (ESC G)
style |= STYLE_DOUBLESTRIKE;
break;
case 0x48: // Cancel double-strike printing (ESC H)
style &= ~STYLE_DOUBLESTRIKE;
break;
case 0x4a: // Advance print position vertically (ESC J n)
curY += (Real64)((Real64)params[0] / 180);
if (curY > bottomMargin)
newPage(true,false);
break;
case 0x4b: // Select 60-dpi graphics (ESC K)
setupBitImage(densk, PARAM16(0));
break;
case 0x4c: // Select 120-dpi graphics (ESC L)
setupBitImage(densl, PARAM16(0));
break;
case 0x4d: // Select 10.5-point, 12-cpi (ESC M)
cpi = 12;
hmi = -1;
multipoint = false;
updateFont();
break;
case 0x4e: // Set bottom margin (ESC N)
topMargin = 0.0;
bottomMargin = (Real64)params[0] * lineSpacing;
break;
case 0x4f: // Cancel bottom (and top) margin
topMargin = 0.0;
bottomMargin = pageHeight;
break;
case 0x50: // Select 10.5-point, 10-cpi (ESC P)
cpi = 10;
hmi = -1;
multipoint = false;
updateFont();
break;
case 0x51: // Set right margin
rightMargin = (Real64)(params[0]-1.0) / (Real64)cpi;
break;
case 0x52: // Select an international character set (ESC R)
if (params[0] <= 13 || params[0] == 64)
{
if (params[0] == 64)
params[0] = 14;
curMap[0x23] = intCharSets[params[0]][0];
curMap[0x24] = intCharSets[params[0]][1];
curMap[0x40] = intCharSets[params[0]][2];
curMap[0x5b] = intCharSets[params[0]][3];
curMap[0x5c] = intCharSets[params[0]][4];
curMap[0x5d] = intCharSets[params[0]][5];
curMap[0x5e] = intCharSets[params[0]][6];
curMap[0x60] = intCharSets[params[0]][7];
curMap[0x7b] = intCharSets[params[0]][8];
curMap[0x7c] = intCharSets[params[0]][9];
curMap[0x7d] = intCharSets[params[0]][10];
curMap[0x7e] = intCharSets[params[0]][11];
}
break;
case 0x53: // Select superscript/subscript printing (ESC S)
if (params[0] == 0 || params[0] == 48)
style |= STYLE_SUBSCRIPT;
if (params[0] == 1 || params[1] == 49)
style |= STYLE_SUPERSCRIPT;
updateFont();
break;
case 0x54: // Cancel superscript/subscript printing (ESC T)
style &= 0xFFFF - STYLE_SUPERSCRIPT - STYLE_SUBSCRIPT;
updateFont();
break;
case 0x55: // Turn unidirectional mode on/off (ESC U)
// We don't have a print head, so just ignore this
break;
case 0x57: // Turn double-width printing on/off (ESC W)
if (!multipoint)
{
hmi = -1;
if (params[0] == 0 || params[0] == 48)
style &= ~STYLE_DOUBLEWIDTH;
if (params[0] == 1 || params[0] == 49)
style |= STYLE_DOUBLEWIDTH;
updateFont();
}
break;
case 0x58: // Select font by pitch and point (ESC X)
multipoint = true;
// Copy currently non-multipoint CPI if no value was set so far
if (multicpi == 0)
multicpi = cpi;
if (params[0] > 0) // Set CPI
{
if (params[0] == 1) // Proportional spacing
style |= STYLE_PROP;
else if (params[0] >= 5)
multicpi = (Real64)360 / (Real64)params[0];
}
if (multiPointSize == 0)
multiPointSize = (Real64)10.5;
if (PARAM16(1) > 0) // Set points
multiPointSize = ((Real64)PARAM16(1)) / 2;
updateFont();
break;
case 0x59: // Select 120-dpi, double-speed graphics (ESC Y)
setupBitImage(densy, PARAM16(0));
break;
case 0x5a: // Select 240-dpi graphics (ESC Z)
setupBitImage(densz, PARAM16(0));
break;
case 0x5c: // Set relative horizontal print position (ESC \)
{
Bit16s toMove = PARAM16(0);
Real64 unitSize = definedUnit;
if (unitSize < 0)
unitSize = (Real64)(printQuality==QUALITY_DRAFT?120.0:180.0);
curX += (Real64)((Real64)toMove / unitSize);
}
break;
case 0x61: // Select justification (ESC a)
// Ignore
break;
case 0x63: // Set horizontal motion index (HMI) (ESC c)
hmi = (Real64)PARAM16(0) / (Real64)360.0;
extraIntraSpace = 0.0;
break;
case 0x67: // Select 10.5-point, 15-cpi (ESC g)
cpi = 15;
hmi = -1;
multipoint = false;
updateFont();
break;
case 0x846: // Select forward feed mode (FS F) - set reverse not implemented yet
if(lineSpacing < 0) lineSpacing *= -1;
break;
case 0x6a: // Reverse paper feed (ESC j)
{
Real64 reverse = (Real64)PARAM16(0) / (Real64)216.0;
reverse = curY - reverse;
if(reverse < leftMargin) curY = leftMargin;
else curY = reverse;
break;
}
case 0x6b: // Select typeface (ESC k)
paramc = params[0]-'0';
//if (params[0] <= 11 || params[0] == 30 || params[0] == 31)
if (paramc <= 11 || paramc == 30 || paramc == 31)
LQtypeFace = (Typeface)paramc;
updateFont();
break;
case 0x6c: // Set left margin (ESC l)
leftMargin = (Real64)(params[0]-1.0) / (Real64)cpi;
if (curX < leftMargin)
curX = leftMargin;
break;
case 0x70: // Turn proportional mode on/off (ESC p)
if (params[0] == 0 || params[0] == 48)
style &= (0xffff - STYLE_PROP);
if (params[0] == 1 || params[0] == 49)
{
style |= STYLE_PROP;
printQuality = QUALITY_LQ;
}
multipoint = false;
hmi = -1;
updateFont();
break;
case 0x72: // Select printing color (ESC r)
if(params[0]==0 || params[0] > 6) color = COLOR_BLACK;
else color = params[0]<<5;
break;
case 0x73: // Select low-speed mode (ESC s)
// Ignore
break;
case 0x74: // Select character table (ESC t)
case 0x849: // Select character table (FS I)
if (params[0] < 4)
curCharTable = params[0];
if (params[0] >= 48 && params[0] <= 51)
curCharTable = params[0] - 48;
selectCodepage(charTables[curCharTable]);
updateFont();
break;
case 0x77: // Turn double-height printing on/off (ESC w)
if (!multipoint)
{
if (params[0] == 0 || params[0] == 48)
style &= ~STYLE_DOUBLEHEIGHT;
if (params[0] == 1 || params[0] == 49)
style |= STYLE_DOUBLEHEIGHT;
updateFont();
}
break;
case 0x78: // Select LQ or draft (ESC x)
if (params[0] == 0 || params[0] == 48) {
printQuality = QUALITY_DRAFT;
style |= STYLE_CONDENSED;
}
if (params[0] == 1 || params[0] == 49) {
printQuality = QUALITY_LQ;
style &= ~STYLE_CONDENSED;
}
hmi = -1;
updateFont();
break;
case 0x100: // Set page length in inches (ESC C NUL)
pageHeight = (Real64)params[0];
bottomMargin = pageHeight;
topMargin = 0.0;
break;
case 0x101: // Skip unsupported ESC ( command
neededParam = PARAM16(0);
numParam = 0;
break;
case 0x274: // Assign character table (ESC (t)
if (params[2] < 4 && params[3] < 16)
{
charTables[params[2]] = codepages[params[3]];
//LOG_MSG("curr table: %d, p2: %d, p3: %d",curCharTable,params[2],params[3]);
if (params[2] == curCharTable)
selectCodepage(charTables[curCharTable]);
}
break;
case 0x22d: // Select line/score (ESC (-)
style &= ~(STYLE_UNDERLINE | STYLE_STRIKETHROUGH | STYLE_OVERSCORE);
score = params[4];
if (score)
{
if (params[3] == 1)
style |= STYLE_UNDERLINE;
if (params[3] == 2)
style |= STYLE_STRIKETHROUGH;
if (params[3] == 3)
style |= STYLE_OVERSCORE;
}
updateFont();
break;
case 0x242: // Bar code setup and print (ESC (B)
//LOG(LOG_MISC,LOG_ERROR)("PRINTER: Bardcode printing not supported");
// Find out how many bytes to skip
neededParam = PARAM16(0);
numParam = 0;
break;
case 0x243: // Set page length in defined unit (ESC (C)
if (params[0] != 0 && definedUnit > 0)
{
pageHeight = bottomMargin = ((Real64)PARAM16(2)) * definedUnit;
topMargin = 0.0;
}
break;
case 0x255: // Set unit (ESC (U)
definedUnit = (Real64)params[2] / (Real64)3600;
break;
case 0x256: // Set absolute vertical print position (ESC (V)
{
Real64 unitSize = definedUnit;
if (unitSize < 0)
unitSize = (Real64)360.0;
Real64 newPos = topMargin + (((Real64)PARAM16(2)) * unitSize);
if (newPos > bottomMargin)
newPage(true,false);
else
curY = newPos;
}
break;
case 0x25e: // Print data as characters (ESC (^)
numPrintAsChar = PARAM16(0);
break;
case 0x263: // Set page format (ESC (c)
if (definedUnit > 0)
{
Real64 newTop, newBottom;
newTop = ((Real64)PARAM16(2)) * definedUnit;
newBottom = ((Real64)PARAM16(4)) * definedUnit;
if(newTop >= newBottom) break;
if(newTop < pageHeight) topMargin = newTop;
if(newBottom < pageHeight) bottomMargin = newBottom;
if(topMargin > curY) curY = topMargin;
//LOG_MSG("du %d, p1 %d, p2 %d, newtop %f, newbott %f, nt %f, nb %f, ph %f",
// (Bitu)definedUnit,PARAM16(2),PARAM16(4),topMargin,bottomMargin,
// newTop,newBottom,pageHeight);
}
break;
case 0x276: // Set relative vertical print position (ESC (v)
{
Real64 unitSize = definedUnit;
if (unitSize < 0)
unitSize = (Real64)360.0;
Real64 newPos = curY + ((Real64)((Bit16s)PARAM16(2)) * unitSize);
if (newPos > topMargin)
{
if (newPos > bottomMargin)
newPage(true,false);
else
curY = newPos;
}
}
break;
default:
if (ESCCmd < 0x100);
//LOG(LOG_MISC,LOG_WARN)
//LOG_MSG("PRINTER: Skipped unsupported command ESC %c (%02X)", ESCCmd, ESCCmd);
else;
//LOG(LOG_MISC,LOG_WARN)
//LOG_MSG("PRINTER: Skipped unsupported command ESC ( %c (%02X)", ESCCmd-0x200, ESCCmd-0x200);
}
ESCCmd = 0;
return true;
}
switch (ch)
{
case 0x00: // NUL is ignored by the printer
return true;
case 0x07: // Beeper (BEL)
// BEEEP!
return true;
case 0x08: // Backspace (BS)
{
Real64 newX = curX - (1/(Real64)actcpi);
if (hmi > 0)
newX = curX - hmi;
if (newX >= leftMargin)
curX = newX;
}
return true;
case 0x09: // Tab horizontally (HT)
{
// Find tab right to current pos
Real64 moveTo = -1;
for (Bit8u i=0; i<numHorizTabs; i++)
if (horiztabs[i] > curX)
moveTo = horiztabs[i];
// Nothing found => Ignore
if (moveTo > 0 && moveTo < rightMargin)
curX = moveTo;
}
return true;
case 0x0b: // Tab vertically (VT)
if (numVertTabs == 0) // All tabs cancelled => Act like CR
curX = leftMargin;
else if (numVertTabs == 255) // No tabs set since reset => Act like LF
{
curX = leftMargin;
curY += lineSpacing;
if (curY > bottomMargin)
newPage(true,false);
}
else
{
// Find tab below current pos
Real64 moveTo = -1;
for (Bit8u i=0; i<numVertTabs; i++)
if (verttabs[i] > curY)
moveTo = verttabs[i];
// Nothing found => Act like FF
if (moveTo > bottomMargin || moveTo < 0)
newPage(true,false);
else
curY = moveTo;
}
if (style & STYLE_DOUBLEWIDTHONELINE)
{
style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
updateFont();
}
return true;
case 0x0c: // Form feed (FF)
if (style & STYLE_DOUBLEWIDTHONELINE)
{
style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
updateFont();
}
newPage(true,true);
return true;
case 0x0d: // Carriage Return (CR)
curX = leftMargin;
if (!autoFeed)
return true;
case 0x0a: // Line feed
if (style & STYLE_DOUBLEWIDTHONELINE)
{
style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
updateFont();
}
curX = leftMargin;
curY += lineSpacing;
if (curY > bottomMargin)
newPage(true,false);
return true;
case 0x0e: //Select Real64-width printing (one line) (SO)
if (!multipoint)
{
hmi = -1;
style |= STYLE_DOUBLEWIDTHONELINE;
updateFont();
}
return true;
case 0x0f: // Select condensed printing (SI)
if (!multipoint && (cpi!=15.0)) {
hmi = -1;
style |= STYLE_CONDENSED;
updateFont();
}
return true;
case 0x11: // Select printer (DC1)
// Ignore
return true;
case 0x12: // Cancel condensed printing (DC2)
hmi = -1;
style &= ~STYLE_CONDENSED;
updateFont();
return true;
case 0x13: // Deselect printer (DC3)
// Ignore
return true;
case 0x14: // Cancel double-width printing (one line) (DC4)
hmi = -1;
style &= ~STYLE_DOUBLEWIDTHONELINE;
updateFont();
return true;
case 0x18: // Cancel line (CAN)
return true;
case 0x1b: // ESC
ESCSeen = true;
return true;
case 0x1c: // FS (IBM commands)
FSSeen = true;
return true;
default:
return false;
}
return false;
}
#endif // HAVE_SDL
//static void PRINTER_EventHandler(Bitu param);
void CPrinter::newPage(bool save, bool resetx)
{
//PIC_RemoveEvents(PRINTER_EventHandler);
if(printer_timout) timeout_dirty=false;
#ifdef HAVE_SDL
if (save)
outputPage();
if(resetx) curX=leftMargin;
curY = topMargin;
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = page->w;
rect.h = page->h;
SDL_FillRect(page, &rect, SDL_MapRGB(page->format, 255, 255, 255));
/*for(int i = 0; i < 256; i++)
{
*((Bit8u*)page->pixels+i)=i;
}*/
#endif // HAVE_SDL
if (strcasecmp(output, "text") == 0) { /* Text file */
if (textPrinterFile) {
fclose(textPrinterFile);
textPrinterFile = NULL;
}
}
}
void CPrinter::printChar(Bit8u ch)
{
#ifdef HAVE_SDL
charRead = true;
if (page == NULL) return;
#endif // HAVE_SDL
// Don't think that DOS programs uses this but well: Apply MSB if desired
if (msb != 255) {
if (msb == 0) ch &= 0x7F;
if (msb == 1) ch |= 0x80;
}
if (strcasecmp(output, "text") == 0) {
if (!textPrinterFile) {
textPrinterFile = fopen(textPrinterFileName,"ab");
}
fprintf(textPrinterFile,"%c",ch);
fflush(textPrinterFile);
return;
}
#ifdef HAVE_SDL
// Are we currently printing a bit graphic?
if (bitGraph.remBytes > 0) {
printBitGraph(ch);
return;
}
// Print everything?
if (numPrintAsChar > 0) numPrintAsChar--;
else if (processCommandChar(ch)) return;
// Do not print if no font is available
if (!curFont) return;
if(ch==0x1) ch=0x20;
// Find the glyph for the char to render
FT_UInt index = FT_Get_Char_Index(curFont, curMap[ch]);
// Load the glyph
FT_Load_Glyph(curFont, index, FT_LOAD_DEFAULT);
// Render a high-quality bitmap
FT_Render_Glyph(curFont->glyph, FT_RENDER_MODE_NORMAL);
Bit16u penX = PIXX + curFont->glyph->bitmap_left;
Bit16u penY = PIXY - curFont->glyph->bitmap_top + curFont->size->metrics.ascender/64;
if (style & STYLE_SUBSCRIPT) penY += curFont->glyph->bitmap.rows / 2;
// Copy bitmap into page
SDL_LockSurface(page);
blitGlyph(curFont->glyph->bitmap, penX, penY, false);
blitGlyph(curFont->glyph->bitmap, penX+1, penY, true);
// Doublestrike => Print the glyph a second time one pixel below
if (style & STYLE_DOUBLESTRIKE) {
blitGlyph(curFont->glyph->bitmap, penX, penY+1, true);
blitGlyph(curFont->glyph->bitmap, penX+1, penY+1, true);
}
// Bold => Print the glyph a second time one pixel to the right
// or be a bit more bold...
if (style & STYLE_BOLD) {
blitGlyph(curFont->glyph->bitmap, penX+1, penY, true);
blitGlyph(curFont->glyph->bitmap, penX+2, penY, true);
blitGlyph(curFont->glyph->bitmap, penX+3, penY, true);
}
SDL_UnlockSurface(page);
// For line printing
Bit16u lineStart = PIXX;
// advance the cursor to the right
Real64 x_advance;
if (style & STYLE_PROP)
x_advance = (Real64)((Real64)(curFont->glyph->advance.x)/(Real64)(dpi*64));
else {
if (hmi < 0) x_advance = 1/(Real64)actcpi;
else x_advance = hmi;
}
x_advance += extraIntraSpace;
curX += x_advance;
// Draw lines if desired
if ((score != SCORE_NONE) && (style &
(STYLE_UNDERLINE|STYLE_STRIKETHROUGH|STYLE_OVERSCORE)))
{
// Find out where to put the line
Bit16u lineY = PIXY;
double height = (curFont->size->metrics.height>>6); // TODO height is fixed point madness...
if (style & STYLE_UNDERLINE) lineY = PIXY + (Bit16u)(height*0.9);
else if (style & STYLE_STRIKETHROUGH) lineY = PIXY + (Bit16u)(height*0.45);
else if (style & STYLE_OVERSCORE)
lineY = PIXY - (((score == SCORE_DOUBLE)||(score == SCORE_DOUBLEBROKEN))?5:0);
drawLine(lineStart, PIXX, lineY, score==SCORE_SINGLEBROKEN || score==SCORE_DOUBLEBROKEN);
// draw second line if needed
if ((score == SCORE_DOUBLE)||(score == SCORE_DOUBLEBROKEN))
drawLine(lineStart, PIXX, lineY + 5, score==SCORE_SINGLEBROKEN || score==SCORE_DOUBLEBROKEN);
}
// If the next character would go beyond the right margin, line-wrap.
if((curX + x_advance) > rightMargin) {
curX = leftMargin;
curY += lineSpacing;
if (curY > bottomMargin) newPage(true,false);
}
#endif // HAVE_SDL
}
#ifdef HAVE_SDL
void CPrinter::blitGlyph(FT_Bitmap bitmap, Bit16u destx, Bit16u desty, bool add) {
for (Bitu y=0; y<bitmap.rows; y++) {
for (Bitu x=0; x<bitmap.width; x++) {
// Read pixel from glyph bitmap
Bit8u source = *(bitmap.buffer + x + y*bitmap.pitch);
// Ignore background and don't go over the border
if (source > 0 && (destx+x < page->w) && (desty+y < page->h) ) {
Bit8u* target = (Bit8u*)page->pixels + (x+destx) + (y+desty)*page->pitch;
source>>=3;
if (add) {
if (((*target)&0x1f )+ source > 31) *target |= (color|0x1f);
else {
*target += source;
*target |= color;
}
}
else *target = source|color;
}
}
}
}
void CPrinter::drawLine(Bitu fromx, Bitu tox, Bitu y, bool broken)
{
SDL_LockSurface(page);
Bitu breakmod = dpi / 15;
Bitu gapstart = (breakmod * 4)/5;
// Draw anti-aliased line
for (Bitu x=fromx; x<=tox; x++)
{
// Skip parts if broken line or going over the border
if ((!broken || (x%breakmod <= gapstart)) && (x < page->w))
{
if (y > 0 && (y-1) < page->h)
*((Bit8u*)page->pixels + x + (y-1)*page->pitch) = 240;
if (y < page->h)
*((Bit8u*)page->pixels + x + y*page->pitch) = !broken?255:240;
if (y+1 < page->h)
*((Bit8u*)page->pixels + x + (y+1)*page->pitch) = 240;
}
}
SDL_UnlockSurface(page);
}
void CPrinter::setAutofeed(bool feed) {
autoFeed = feed;
}
bool CPrinter::getAutofeed() {
return autoFeed;
}
bool CPrinter::isBusy() {
// We're never busy
return false;
}
bool CPrinter::ack() {
// Acknowledge last char read
if(charRead) {
charRead=false;
return true;
}
return false;
}
void CPrinter::setupBitImage(Bit8u dens, Bit16u numCols) {
switch (dens)
{
case 0:
bitGraph.horizDens = 60;
bitGraph.vertDens = 60;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 1:
bitGraph.horizDens = 120;
bitGraph.vertDens = 60;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 2:
bitGraph.horizDens = 120;
bitGraph.vertDens = 60;
bitGraph.adjacent = false;
bitGraph.bytesColumn = 1;
break;
case 3:
bitGraph.horizDens = 60;
bitGraph.vertDens = 240;
bitGraph.adjacent = false;
bitGraph.bytesColumn = 1;
break;
case 4:
bitGraph.horizDens = 80;
bitGraph.vertDens = 60;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 6:
bitGraph.horizDens = 90;
bitGraph.vertDens = 60;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 32:
bitGraph.horizDens = 60;
bitGraph.vertDens = 180;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 33:
bitGraph.horizDens = 120;
bitGraph.vertDens = 180;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 38:
bitGraph.horizDens = 90;
bitGraph.vertDens = 180;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 39:
bitGraph.horizDens = 180;
bitGraph.vertDens = 180;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 40:
bitGraph.horizDens = 360;
bitGraph.vertDens = 180;
bitGraph.adjacent = false;
bitGraph.bytesColumn = 3;
break;
case 71:
bitGraph.horizDens = 180;
bitGraph.vertDens = 360;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 6;
break;
case 72:
bitGraph.horizDens = 360;
bitGraph.vertDens = 360;
bitGraph.adjacent = false;
bitGraph.bytesColumn = 6;
break;
case 73:
bitGraph.horizDens = 360;
bitGraph.vertDens = 360;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 6;
break;
default:
//break;
printf("PRINTER: Unsupported bit image density");
}
bitGraph.remBytes = numCols * bitGraph.bytesColumn;
bitGraph.readBytesColumn = 0;
}
void CPrinter::printBitGraph(Bit8u ch)
{
bitGraph.column[bitGraph.readBytesColumn++] = ch;
bitGraph.remBytes--;
// Only print after reading a full column
if (bitGraph.readBytesColumn < bitGraph.bytesColumn)
return;
Real64 oldY = curY;
SDL_LockSurface(page);
// When page dpi is greater than graphics dpi, the drawn pixels get "bigger"
Bitu pixsizeX=1;
Bitu pixsizeY=1;
if(bitGraph.adjacent) {
pixsizeX = dpi/bitGraph.horizDens > 0? dpi/bitGraph.horizDens : 1;
pixsizeY = dpi/bitGraph.vertDens > 0? dpi/bitGraph.vertDens : 1;
}
// TODO figure this out for 360dpi mode in windows
// Bitu pixsizeX = dpi/bitGraph.horizDens > 0? dpi/bitGraph.horizDens : 1;
// Bitu pixsizeY = dpi/bitGraph.vertDens > 0? dpi/bitGraph.vertDens : 1;
for (Bitu i=0; i<bitGraph.bytesColumn; i++) // for each byte
{
for (Bitu j=128; j!=0; j>>=1) { // for each bit
if (bitGraph.column[i] & j) {
for (Bitu xx=0; xx<pixsizeX; xx++)
for (Bitu yy=0; yy<pixsizeY; yy++) {
if (((PIXX + xx) < page->w) && ((PIXY + yy) < page->h))
*((Bit8u*)page->pixels + (PIXX+xx) + (PIXY+yy)*page->pitch) |= (color|0x1F);
}
} // else white pixel
curY += (Real64)1/(Real64)bitGraph.vertDens; // TODO line wrap?
}
}
SDL_UnlockSurface(page);
curY = oldY;
bitGraph.readBytesColumn = 0;
// Advance to the left
curX += (Real64)1/(Real64)bitGraph.horizDens;
}
#endif // HAVE_SDL
void CPrinter::formFeed()
{
#ifdef HAVE_SDL
// Don't output blank pages
newPage(!isBlank(),true);
finishMultipage();
#endif // HAVE_SDL
}
#ifdef HAVE_SDL
static void findNextName(char* front, char* ext, char* fname)
{
document_path = "";
Bitu i = 1;
Bitu slen = strlen(document_path);
if(slen>(200-15)) {
fname[0]=0;
return;
}
FILE *test = NULL;
do
{
strcpy(fname, document_path);
printf("%s",fname);
#ifdef WIN32
const char* const pathstring = ".\\%s%d%s";
#else
const char* const pathstring = "./%s%d%s";
#endif
sprintf(fname+strlen(fname), pathstring, front,i++,ext);
test = fopen(fname, "rb");
if (test != NULL)
fclose(test);
}
while (test != NULL );
}
void CPrinter::outputPage()
{
/* SDL_Surface *screen;
screen = SDL_SetVideoMode(1024, 768, 16, SDL_DOUBLEBUF | SDL_RESIZABLE);
if (screen == NULL) {
printf("Unable to set video mode: %s\n", SDL_GetError());
}
SDL_Surface *image;
SDL_LockSurface(page);
image = SDL_DisplayFormat(page);
SDL_UnlockSurface(page);
SDL_Rect src, dest;
src.x = 0;
src.y = 0;
src.w = image->w;
src.h = image->h;
dest.x = 100;
dest.y = 100;
dest.w = image->w;
dest.h = image->h;
SDL_BlitSurface(image, &src, screen, &dest);
SDL_Flip(screen);
SDL_Delay(2000);
SDL_FreeSurface(image);*/
char fname[200];
if (strcasecmp(output, "printer") == 0)
{
#if defined (WIN32)
Bit16u physW = GetDeviceCaps(printerDC, PHYSICALWIDTH);
Bit16u physH = GetDeviceCaps(printerDC, PHYSICALHEIGHT);
Real64 scaleW, scaleH;
if (page->w > physW)
scaleW = (Real64)page->w / (Real64)physW;
else
scaleW = (Real64)physW / (Real64)page->w;
if (page->h > physH)
scaleH = (Real64)page->h / (Real64)physH;
else
scaleH = (Real64)physH / (Real64)page->h;
HDC memHDC = CreateCompatibleDC(printerDC);
BITMAPINFO *BitmapInfo;
HBITMAP bitmap;
// Start new printer job?
if (outputHandle == NULL)
{
DOCINFO docinfo;
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = "GSport Virtual Printer";
docinfo.lpszOutput = NULL;
docinfo.lpszDatatype = NULL;
docinfo.fwType = 0;
StartDoc(printerDC, &docinfo);
multiPageCounter = 1;
}
SDL_LockSurface(page);
StartPage(printerDC);
DWORD TotalSize;
HGDIOBJ Prev;
void* Pixels;
BitmapInfo = (BITMAPINFO*)
malloc (sizeof (BITMAPINFO)+255*sizeof (RGBQUAD));
memset (BitmapInfo,0,sizeof (bitmap));
BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo->bmiHeader.biWidth = page->w;
BitmapInfo->bmiHeader.biHeight = -page->h;
BitmapInfo->bmiHeader.biPlanes = 1;
BitmapInfo->bmiHeader.biBitCount = page->format->BitsPerPixel;
BitmapInfo->bmiHeader.biCompression = BI_RGB;
BitmapInfo->bmiHeader.biSizeImage = page->h * page->pitch;
BitmapInfo->bmiHeader.biXPelsPerMeter = 0;
BitmapInfo->bmiHeader.biYPelsPerMeter = 0;
BitmapInfo->bmiHeader.biClrUsed = page->format->palette->ncolors;
BitmapInfo->bmiHeader.biClrImportant = 0;
if (page->format->palette) {
for (int I=0; I<page->format->palette->ncolors; I++) {
BitmapInfo->bmiColors[I].rgbRed =
(page->format->palette->colors+I)->r;
BitmapInfo->bmiColors[I].rgbGreen =
(page->format->palette->colors+I)->g;
BitmapInfo->bmiColors[I].rgbBlue =
(page->format->palette->colors+I)->b;
}
}
memHDC = CreateCompatibleDC(printerDC);
if (memHDC) {
bitmap = CreateDIBSection(memHDC, BitmapInfo, DIB_RGB_COLORS,
(&Pixels), NULL, 0);
if (bitmap) {
memcpy (Pixels, page->pixels,
BitmapInfo->bmiHeader.biSizeImage);
Prev = SelectObject (memHDC, bitmap);
StretchBlt(printerDC, 0, 0, physW, physH, memHDC, 0, 0, page->w, page->h, SRCCOPY);
SelectObject (memHDC,Prev);
DeleteObject (bitmap);
}
}
free (BitmapInfo);
SDL_UnlockSurface(page);
EndPage(printerDC);
if (multipageOutput)
{
multiPageCounter++;
outputHandle = printerDC;
}
else
{
EndDoc(printerDC);
outputHandle = NULL;
}
DeleteObject(bitmap);
DeleteDC(memHDC);
#else
//LOG_MSG("PRINTER: Direct printing not supported under this OS");
#endif
}
#ifdef C_LIBPNG
else if (strcasecmp(output, "png") == 0)
{
// Find a page that does not exists
findNextName("page", ".png", &fname[0]);
png_structp png_ptr;
png_infop info_ptr;
png_bytep * row_pointers;
png_color palette[256];
Bitu i;
/* Open the actual file */
FILE * fp=fopen(fname,"wb");
if (!fp)
{
//LOG(LOG_MISC,LOG_ERROR)("PRINTER: Can't open file %s for printer output", fname);
return;
}
/* First try to alloacte the png structures */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL);
if (!png_ptr) return;
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr,(png_infopp)NULL);
return;
}
/* Finalize the initing of png library */
png_init_io(png_ptr, fp);
png_set_compression_level(png_ptr,Z_BEST_COMPRESSION);
/* set other zlib parameters */
png_set_compression_mem_level(png_ptr, 8);
png_set_compression_strategy(png_ptr,Z_DEFAULT_STRATEGY);
png_set_compression_window_bits(png_ptr, 15);
png_set_compression_method(png_ptr, 8);
png_set_compression_buffer_size(png_ptr, 8192);
png_set_IHDR(png_ptr, info_ptr, page->w, page->h,
8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
for (i=0;i<256;i++)
{
palette[i].red = page->format->palette->colors[i].r;
palette[i].green = page->format->palette->colors[i].g;
palette[i].blue = page->format->palette->colors[i].b;
}
png_set_PLTE(png_ptr, info_ptr, palette,256);
png_set_packing(png_ptr);
SDL_LockSurface(page);
// Allocate an array of scanline pointers
row_pointers = (png_bytep*)malloc(page->h*sizeof(png_bytep));
for (i=0; i<page->h; i++)
row_pointers[i] = ((Bit8u*)page->pixels+(i*page->pitch));
// tell the png library what to encode.
png_set_rows(png_ptr, info_ptr, row_pointers);
// Write image to file
png_write_png(png_ptr, info_ptr, 0, NULL);
SDL_UnlockSurface(page);
/*close file*/
fclose(fp);
/*Destroy PNG structs*/
png_destroy_write_struct(&png_ptr, &info_ptr);
/*clean up dynamically allocated RAM.*/
free(row_pointers);
}
#endif
else if (strcasecmp(output, "ps") == 0)
{
FILE* psfile = NULL;
// Continue postscript file?
if (outputHandle != NULL)
psfile = (FILE*)outputHandle;
// Create new file?
if (psfile == NULL)
{
if (!multipageOutput)
findNextName((char *)"page", (char *)".ps", &fname[0]);
else
findNextName((char *)"doc", (char *)".ps", &fname[0]);
psfile = fopen(fname, "wb");
if (!psfile)
{
printf("PRINTER: Can't open file %s for printer output", fname);
return;
}
// Print header
fprintf(psfile, "%%!PS-Adobe-3.0\n");
fprintf(psfile, "%%%%Pages: (atend)\n");
fprintf(psfile, "%%%%BoundingBox: 0 0 %i %i\n", (Bit16u)(defaultPageWidth*72), (Bit16u)(defaultPageHeight*72));
fprintf(psfile, "%%%%Creator: GSport Virtual Printer\n");
fprintf(psfile, "%%%%DocumentData: Clean7Bit\n");
fprintf(psfile, "%%%%LanguageLevel: 2\n");
fprintf(psfile, "%%%%EndComments\n");
multiPageCounter = 1;
}
fprintf(psfile, "%%%%Page: %i %i\n", multiPageCounter, multiPageCounter);
fprintf(psfile, "%i %i scale\n", (Bit16u)(defaultPageWidth*72), (Bit16u)(defaultPageHeight*72));
fprintf(psfile, "%i %i 8 [%i 0 0 -%i 0 %i]\n", page->w, page->h, page->w, page->h, page->h);
fprintf(psfile, "currentfile\n");
fprintf(psfile, "/ASCII85Decode filter\n");
fprintf(psfile, "/RunLengthDecode filter\n");
fprintf(psfile, "image\n");
SDL_LockSurface(page);
Bit32u pix = 0;
Bit32u numpix = page->h*page->w;
ASCII85BufferPos = ASCII85CurCol = 0;
while (pix < numpix)
{
// Compress data using RLE
if ((pix < numpix-2) && (getPixel(pix) == getPixel(pix+1)) && (getPixel(pix) == getPixel(pix+2)))
{
// Found three or more pixels with the same color
Bit8u sameCount = 3;
Bit8u col = getPixel(pix);
while (sameCount < 128 && sameCount+pix < numpix && col == getPixel(pix+sameCount))
sameCount++;
fprintASCII85(psfile, 257-sameCount);
fprintASCII85(psfile, 255-col);
// Skip ahead
pix += sameCount;
}
else
{
// Find end of heterogenous area
Bit8u diffCount = 1;
while (diffCount < 128 && diffCount+pix < numpix &&
(
(diffCount+pix < numpix-2)
|| (getPixel(pix+diffCount) != getPixel(pix+diffCount+1))
|| (getPixel(pix+diffCount) != getPixel(pix+diffCount+2))
))
diffCount++;
fprintASCII85(psfile, diffCount-1);
for (Bit8u i=0; i<diffCount; i++)
fprintASCII85(psfile, 255-getPixel(pix++));
}
}
// Write EOD for RLE and ASCII85
fprintASCII85(psfile, 128);
fprintASCII85(psfile, 256);
SDL_UnlockSurface(page);
fprintf(psfile, "showpage\n");
if (multipageOutput)
{
multiPageCounter++;
outputHandle = psfile;
}
else
{
fprintf(psfile, "%%%%Pages: 1\n");
fprintf(psfile, "%%%%EOF\n");
fclose(psfile);
outputHandle = NULL;
}
}
else
{
// Find a page that does not exists
findNextName((char *)"page", (char *)".bmp", &fname[0]);
SDL_SaveBMP(page, fname);
}
}
void CPrinter::fprintASCII85(FILE* f, Bit16u b)
{
if (b != 256)
{
if (b < 256)
ASCII85Buffer[ASCII85BufferPos++] = (Bit8u)b;
if (ASCII85BufferPos == 4 || b == 257)
{
Bit32u num = (Bit32u)ASCII85Buffer[0] << 24 | (Bit32u)ASCII85Buffer[1] << 16 | (Bit32u)ASCII85Buffer[2] << 8 | (Bit32u)ASCII85Buffer[3];
// Deal with special case
if (num == 0 && b != 257)
{
fprintf(f, "z");
if (++ASCII85CurCol >= 79)
{
ASCII85CurCol = 0;
fprintf(f, "\n");
}
}
else
{
char buffer[5];
for (Bit8s i=4; i>=0; i--)
{
buffer[i] = (Bit8u)((Bit32u)num % (Bit32u)85);
buffer[i] += 33;
num /= (Bit32u)85;
}
// Make sure a line never starts with a % (which may be mistaken as start of a comment)
if (ASCII85CurCol == 0 && buffer[0] == '%')
fprintf(f, " ");
for (int i=0; i<((b != 257)?5:ASCII85BufferPos+1); i++)
{
fprintf(f, "%c", buffer[i]);
if (++ASCII85CurCol >= 79)
{
ASCII85CurCol = 0;
fprintf(f, "\n");
}
}
}
ASCII85BufferPos = 0;
}
}
else // Close string
{
// Partial tupel if there are still bytes in the buffer
if (ASCII85BufferPos > 0)
{
for (Bit8u i = ASCII85BufferPos; i < 4; i++)
ASCII85Buffer[i] = 0;
fprintASCII85(f, 257);
}
fprintf(f, "~");
fprintf(f, ">\n");
}
}
void CPrinter::finishMultipage()
{
if (outputHandle != NULL)
{
if (strcasecmp(output, "ps") == 0)
{
FILE* psfile = (FILE*)outputHandle;
fprintf(psfile, "%%%%Pages: %i\n", multiPageCounter);
fprintf(psfile, "%%%%EOF\n");
fclose(psfile);
}
else if (strcasecmp(output, "printer") == 0)
{
#if defined (WIN32)
EndDoc(printerDC);
#endif
}
outputHandle = NULL;
}
}
bool CPrinter::isBlank() {
bool blank = true;
SDL_LockSurface(page);
for (Bit16u y=0; y<page->h; y++)
for (Bit16u x=0; x<page->w; x++)
if (*((Bit8u*)page->pixels + x + (y*page->pitch)) != 0)
blank = false;
SDL_UnlockSurface(page);
return blank;
}
Bit8u CPrinter::getPixel(Bit32u num) {
Bit32u pixel = *((Bit8u*)page->pixels + (num % page->w) + ((num / page->w) * page->pitch));
return *((Bit8u*)page->pixels + (num % page->w) + ((num / page->w) * page->pitch));
}
#endif // HAVE_SDL
//Interfaces to C code
extern "C" void printer_init(int pdpi, int pwidth, int pheight, char* poutput, bool mpage)
{
if (defaultPrinter != NULL) return;
defaultPrinter = new CPrinter(pdpi, pwidth,pheight, poutput, mpage);
}
extern "C" void printer_loop(Bit8u pchar)
{
if (defaultPrinter == NULL) return;
defaultPrinter->printChar(pchar);
}
extern "C" void printer_close()
{
delete defaultPrinter;
defaultPrinter = NULL;
}
extern "C" void printer_feed()
{
if(defaultPrinter == NULL) return;
defaultPrinter->formFeed();
}