gsplus/src/imagewriter.cpp

2243 lines
59 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 GSport emulator by Christopher G. Mason 03/2014
Extensively rewritten to provide full emulation of the Apple ImageWriter II
and LQ printers.
Information used to write this emulator was provided by
Apple's "ImageWriter II Technical Reference Manual"
ISBN# 0-201-17766-8
and
Apple's "ImageWriter LQ Reference Manual"
ISBN# 0-201-17751-X
*/
#include "imagewriter.h"
#include <math.h>
#include "support.h"
//#include "png.h"
//#pragma comment( lib, "libpng.lib" )
//#pragma comment (lib, "zdll.lib" )
static Imagewriter* defaultImagewriter = 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))
//These ugly defines are so we can convert multibyte parameters from strings into some nice ints.
#define paramc(I) (params[I]-'0')
#define PARAM2(I) (paramc(I)*10+paramc(I+1))
#define PARAM3(I) (paramc(I)*100+paramc(I+1)*10+paramc(I+2))
#define PARAM4(I) (paramc(I)*1000+paramc(I+1)*100+paramc(I+2)*10+paramc(I+3))
static Bitu printer_timout;
static bool timeout_dirty;
static const char* document_path;
extern "C" char* g_imagewriter_fixed_font;
extern "C" char* g_imagewriter_prop_font;
extern "C" int iw_scc_write;
#include "iw_charmaps.h"
#ifdef HAVE_SDL
void Imagewriter::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
Imagewriter::Imagewriter(Bit16u dpi, Bit16u paperSize, Bit16u bannerSize, char* output, bool multipageOutput)
{
#ifdef HAVE_SDL
if (FT_Init_FreeType(&FTlib))
{
page = NULL;
}
else
{
SDL_Init(SDL_INIT_EVERYTHING);
this->output = output;
this->multipageOutput = multipageOutput;
this->port = port;
if (bannerSize)
{
defaultPageWidth = ((Real64)paperSizes[0][0]/(Real64)72);
defaultPageHeight = (((Real64)paperSizes[0][1]*bannerSize)/(Real64)72);
dpi = 144;
}
else
{
defaultPageWidth = ((Real64)paperSizes[paperSize][0]/(Real64)72);
defaultPageHeight = ((Real64)paperSizes[paperSize][1]/(Real64)72);
}
this->dpi = dpi;
// 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);
// 0 = all white needed for logic 000
/*FillPalette( 0, 0, 0, 1, palette);
// 1 = yellow* 100 IW
FillPalette( 0, 0, 255, 1, palette);
// 2 = magenta* 001 IW
FillPalette( 0, 255, 0, 2, palette);
// 3 = cyan* 010 IW
FillPalette(255, 0, 0, 3, palette);
// 4 = red 101 IW
FillPalette( 0, 255, 255, 4, palette);
// 5 = green 110 IW
FillPalette(255, 0, 255, 5, palette);
// 6 = "violet" 011 IW
FillPalette(255, 255, 0, 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();
//Only initialize native printer here if multipage output is off. That way the user doesn't get prompted every page.
if (strcasecmp(output, "printer") == 0 && !multipageOutput)
{
#if defined (WIN32)
// Show Print dialog to obtain a printer device context
ShowCursor(1);
PRINTDLG pd;
pd.lStructSize = sizeof(PRINTDLG);
pd.hDevMode = (HANDLE) NULL;
pd.hDevNames = (HANDLE) NULL;
pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
pd.hwndOwner = NULL;
pd.hDC = (HDC) NULL;
pd.nFromPage = 0xFFFF;
pd.nToPage = 0xFFFF;
pd.nMinPage = 1;
pd.nMaxPage = 0xFFFF;
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;
if(!PrintDlg(&pd))
{
//If user presses cancel, warn them with a dialog and switch output to bitmap files
this->output = "bmp";
MessageBox(NULL,"You did not select a printer.\nAll printer output will be saved as bitmap files.\nTo select a printer, press F4 and select 'Reset Virtual ImageWriter'",NULL,MB_ICONEXCLAMATION);
}
printerDC = pd.hDC;
ShowCursor(0);
#endif // WIN32
}
}
#endif // HAVE_SDL
#ifndef HAVE_SDL
this->output = output;
this->multipageOutput = multipageOutput;
#endif // !HAVE_SDL
};
void Imagewriter::resetPrinterHard()
{
#ifdef HAVE_SDL
charRead = false;
resetPrinter();
#endif // HAVE_SDL
}
void Imagewriter::resetPrinter()
{
#ifdef HAVE_SDL
printRes = 0;
color=COLOR_BLACK;
curX = curY = 0.0;
ESCSeen = false;
FSSeen = false;
ESCCmd = 0;
numParam = neededParam = 0;
topMargin = 0.0;
leftMargin = 0.25; //Most all Apple II software including GS/OS assume a 1/4 inch margin on an Imagewriter
rightMargin = pageWidth = defaultPageWidth;
bottomMargin = pageHeight = defaultPageHeight;
lineSpacing = (Real64)1/6;
cpi = 12.0;
printRes = 2;
style &= (0xffff - STYLE_PROP);
definedUnit = 96;
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;
multipoint = false;
multiPointSize = 0.0;
multicpi = 0.0;
hmi = -1.0;
switcha = 0;
switchb = ' ';
numPrintAsChar = 0;
LQtypeFace = fixed;
verticalDot = 0;
selectCodepage(charTables[curCharTable]);
updateFont();
updateSwitch();
#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 = 0;
numVertTabs = 0;
#endif // HAVE_SDL
}
Imagewriter::~Imagewriter(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 Imagewriter::selectCodepage(Bit16u cp)
{
Bit16u *mapToUse = NULL;
switch(cp)
{
case 0: // Italics, use cp437
case 437:
mapToUse = (Bit16u*)&cp437Map;
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 Imagewriter::updateFont()
{
// char buffer[1000];
if (curFont != NULL)
FT_Done_Face(curFont);
char* fontName;
switch (LQtypeFace)
{
case fixed:
fontName = g_imagewriter_fixed_font;
break;
case prop:
fontName = g_imagewriter_prop_font;
break;
default:
fontName = g_imagewriter_fixed_font;
}
if (FT_New_Face(FTlib, fontName, 0, &curFont))
{
printf("Unable to load font %s\n", fontName);
//LOG_MSG("Unable to load font %s", fontName);
curFont = NULL;
}
Real64 horizPoints = 10;
Real64 vertPoints = 10;
if (!multipoint)
{
actcpi = cpi;
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)) {
actcpi /= 2.0;
horizPoints *= 2.0;
}
} else { // multipoint true
actcpi = multicpi;
horizPoints = vertPoints = multiPointSize;
}
if (style & STYLE_SUPERSCRIPT || style & STYLE_SUBSCRIPT || style & STYLE_HALFHEIGHT) {
//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);
}
}
void Imagewriter::updateSwitch()
{
//Set international character mappping (Switches A-1 to A3)
int charmap = switcha &= 7;
curMap[0x23] = intCharSets[charmap][0];
curMap[0x40] = intCharSets[charmap][1];
curMap[0x5b] = intCharSets[charmap][2];
curMap[0x5c] = intCharSets[charmap][3];
curMap[0x5d] = intCharSets[charmap][4];
curMap[0x60] = intCharSets[charmap][5];
curMap[0x7b] = intCharSets[charmap][6];
curMap[0x7c] = intCharSets[charmap][7];
curMap[0x7d] = intCharSets[charmap][8];
curMap[0x7e] = intCharSets[charmap][9];
//MSB control (Switch B-6)
if (!(switchb&32))
{
msb = 255;
}
else msb = 0;
}
void Imagewriter::slashzero(Bit16u penX, Bit16u penY)
{
FT_Face slashFont = curFont;
FT_UInt slashindex = FT_Get_Char_Index(slashFont, curMap[0x2f]);
FT_Load_Glyph(slashFont, slashindex, FT_LOAD_DEFAULT);
FT_Render_Glyph(slashFont->glyph, FT_RENDER_MODE_NORMAL);
blitGlyph(slashFont->glyph->bitmap, penX, penY, false);
blitGlyph(slashFont->glyph->bitmap, penX+1, penY, true);
if (style & STYLE_BOLD) {
blitGlyph(slashFont->glyph->bitmap, penX+1, penY, true);
blitGlyph(slashFont->glyph->bitmap, penX+2, penY, true);
blitGlyph(slashFont->glyph->bitmap, penX+3, penY, true);
}
}
#endif // HAVE_SDL
#ifdef HAVE_SDL
bool Imagewriter::processCommandChar(Bit8u ch)
{
if (ESCSeen || FSSeen)
{
ESCCmd = ch;
if(FSSeen) ESCCmd |= 0x800;
ESCSeen = FSSeen = false;
numParam = 0;
switch (ESCCmd) {
case 0x21: // Select bold font (ESC !) IW
case 0x22: // Cancel bold font (ESC ") IW
case 0x24: // Cancel MSB control and Mousetext (ESC $) IW
case 0x2b: // custom char width is 8 dots (ESC -) IW
case 0x2e: // custom char width is 8 dots (ESC +) IW
case 0x30: // Clear all tabs (ESC 0) IW
case 0x31: // Insert 1 intercharacter spaces (ESC 1) IW
case 0x32: // Insert 2 intercharacter spaces (ESC 2) IW
case 0x33: // Insert 3 intercharacter spaces (ESC 3) IW
case 0x34: // Insert 4 intercharacter spaces (ESC 4) IW
case 0x35: // Insert 5 intercharacter spaces (ESC 5) IW
case 0x36: // Insert 6 intercharacter spaces (ESC 6) IW
case 0x3c: // bidirectional mode (one line) (ESC <) IW
case 0x3e: // Unidirectional mode (one line) (ESC >) IW
case 0x3f: // Send ID string (ESC ?) IW
case 0x41: // Select 1/6-inch line spacing (ESC A) IW
case 0x42: // Select 1/8-inch line spacing (ESC B) IW
case 0x45: // 12 cpi, 96 dpi graphics (ESC E) IW
case 0x4d: // Same as ESC a2 (ESC M) IW
case 0x4e: // 10 cpi, 80 dpi graphics (ESC N) IW
case 0x4f: // Disable paper-out detector (ESC O) IW
case 0x50: // Proportional, 160 dpi graphics (ESC P) IW
case 0x51: // 17 cpi, 136 dpi graphics (ESC Q) IW
case 0x57: // Cancel halfheight printing (ESC W) IW
case 0x58: // Turn underline on (ESC X) IW
case 0x59: // Turn underline off (ESC Y) IW
case 0x63: // Initialize printer (ESC c) IW
case 0x65: // 13.4 cpi, 107 dpi graphics (ESC e) IW
case 0x66: // Select forward feed mode (ESC f) IW
case 0x6b: // Select optional font (ESC k) IW LQ
case 0x6d: // Same as ESC a0 (ESC m) IW
case 0x6e: // 9 cpi, 72 dpi graphics (ESC n) IW
case 0x6f: // Enable paper-out detector (ESC o) IW
case 0x70: // Proportional, 144 dpi graphics (ESC p) IW
case 0x72: // Select reverse feed mode (ESC r) IW
case 0x71: // 15 cpi, 120 dpi graphics (ESC q) IW
case 0x77: // Select halfheight printing (ESC w) IW
case 0x78: // Select superscript printing (ESC x) IW
case 0x79: // Select subscript printing (ESC y) IW
case 0x7a: // Cancel superscript/subscript printing (ESC z) IW
neededParam = 0;
break;
case 0x3d: // Internal font ID (ESC = n) IW LQ
case 0x40: // Select output bin (ESC @ n) IW LQ
case 0x4b: // Select printing color (ESC K n) IW
case 0x61: // Select font (ESC a n) IW
case 0x6c: // Insert CR before LF and FF (ESC l n) IW
case 0x73: // Set intercharacter space (ESC s n) IW
case 0x74: // Shift printing downward n/216 inch (ESC t n) IW LQ
case 0x833: // Feed n lines of blank space (US n) IW
neededParam = 1;
break;
case 0x44: // Set soft switches to closed (on)= 1 (ESC D nn) IW
case 0x54: // Distance between lines to be nn/144 inch (ESC T nn) IW
case 0x5a: // Set soft switches to open (off) = 0 (ESC Z nn) IW
neededParam = 2;
break;
case 0x4c: // Set left margin at column nnn (ESC L nnn) IW
case 0x67: // Print graphics for next nnn * 8 databytes (ESC g nnn) IW
case 0x75: // Add one tab stop at nnn (ESC u nnn) IW
neededParam = 3;
break;
case 0x28: // Set horizontal tabs (ESC ( nnn,) IW
numHorizTabs = 0;
case 0x29: // Delete horizontal tabs (ESC ) nnn,) IW
case 0x43: // Print hi-res graphics for next nnnn*3 databytes (ESC C nnnn) IW LQ
case 0x47: // Print graphics for next nnnn databytes (ESC G nnnn) IW
case 0x46: // Place printhead nnnn dots from left margin (ESC F nnnn) IW
case 0x48: // Set pagelength to nnnn/144 (ESC H nnnn) IW
case 0x53: // Print graphics for next nnnn databytes (ESC S nnnn) IW
case 0x52: // Repeat character c nnn times (ESC R nnn c) IW
case 0x68: // Place printhead nnnn hi-res dots from left margin (ESC h nnnn) IW LQ
neededParam = 4;
break;
case 0x56: //Repeat Print nnnn repetitions of dot column c (ESC V nnnn c) IW
neededParam = 5;
msb = 255;
break;
case 0x55: //Repeat Print nnnn repetitions of hi-res dot column abc (ESC U nnnn abc) IW LQ
neededParam = 7;
msb = 255;
break;
case 0x27: // Select user-defined set (ESC ')
case 0x49: // Define user-defined characters (ESC I)
//LOG(LOG_MISC,LOG_ERROR)("User-defined characters not supported!");
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;
}
if (numParam < neededParam)
{
params[numParam++] = ch;
if (numParam < neededParam)
return true;
}
if (ESCCmd != 0)
{
switch (ESCCmd)
{
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 0x73: // Set intercharacter space (ESC s) IW
if (style & STYLE_PROP)
{
extraIntraSpace = (Real64)paramc(0);
updateFont();
}
break;
case 0x46: // Set absolute horizontal print position (ESC F nnnn) IW
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
Real64 unitSize = definedUnit;
if (unitSize < 0)
unitSize = (Real64)72.0;
Real64 newX = leftMargin + ((Real64)PARAM4(0)/unitSize);
if (newX <= rightMargin)
curX = newX;
}
break;
case 0x68: // Set absolute horizontal hi-res print position (ESC h nnnn) IW LQ
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
Real64 unitSize = definedUnit*2;
if (unitSize < 0)
unitSize = (Real64)72.0;
Real64 newX = leftMargin + ((Real64)PARAM4(0)/unitSize);
if (newX <= rightMargin)
curX = newX;
}
break;
case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36:// Insert 1-6 intercharacter spaces (ESC 1 to 6) IW
{
if (style & STYLE_PROP) //This function only works in proportional mode
{
Real64 unitSize = definedUnit;
if (unitSize < 0)
unitSize = (Real64)72.0;
Real64 newX = ((Real64)(ESCCmd-'0')/unitSize);
if (newX <= rightMargin)
curX = newX;
}
break;
}
case 0x47: case 0x53: // Print graphics (ESC G nnnn) IW
{
int x = 0;
printRes &= ~8;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
setupBitImage(printRes, PARAM4(0));
break;
}
case 0x43: // Print hi-res graphics (ESC G nnnn) IW LQ
{
int x = 0;
printRes |= 8;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
setupBitImage(printRes, PARAM4(0));
break;
}
case 0x67: // Print graphics (ESC g nnn) IW
{
int x = 0;
printRes &= ~8;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
setupBitImage(printRes, (PARAM3(0)*8));
break;
}
case 0x56: //Repeat Print nnnn repetitions of dot column byte c (ESC V nnnn c)IW
{
int x = 0;
printRes &= ~8;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
x = 0;
while ( x < PARAM4(0))
{
setupBitImage(printRes, 1);
printBitGraph(params[4]);
x++;
}
msb = 0;
break;
}
case 0x55: //Repeat Print nnnn repetitions of hi-res dot column byte abc (ESC U nnnn abc) IW LQ
{
int x = 0;
printRes |= 8;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
x = 0;
while ( x < PARAM4(0))
{
setupBitImage(printRes, 1);
printBitGraph(params[4]);
printBitGraph(params[5]);
printBitGraph(params[6]);
x++;
}
msb = 0;
break;
}
case 0x74: // Shift printing downward n/216 inch (ESC t n) IW LQ
{
verticalDot = paramc(0);
break;
}
case 0x6e: // 9 cpi, 72/144 dpi graphics (ESC n) IW
cpi = 9.0;
style &= (0xffff - STYLE_PROP);
printRes = 0;
definedUnit = 72;
extraIntraSpace = 0.0;
LQtypeFace = fixed;
updateFont();
break;
case 0x4e: // 10 cpi, 80/160 dpi graphics (ESC N) IW
cpi = 10.0;
printRes = 1;
style &= (0xffff - STYLE_PROP);
definedUnit = 80;
extraIntraSpace = 0.0;
LQtypeFace = fixed;
updateFont();
break;
case 0x45: // 12 cpi, 96/192 dpi graphics (ESC E) IW
cpi = 12.0;
printRes = 2;
style &= (0xffff - STYLE_PROP);
definedUnit = 96;
extraIntraSpace = 0.0;
LQtypeFace = fixed;
updateFont();
break;
case 0x65: // 13.4 cpi, 107/216 dpi graphics (ESC e) IW
cpi = 13.4;
printRes = 3;
style &= (0xffff - STYLE_PROP);
definedUnit = 107;
extraIntraSpace = 0.0;
LQtypeFace = fixed;
updateFont();
break;
case 0x71: // 15 cpi, 120/240 dpi graphics (ESC q) IW
cpi = 15;
printRes = 4;
style &= (0xffff - STYLE_PROP);
definedUnit = 120;
extraIntraSpace = 0.0;
LQtypeFace = fixed;
updateFont();
break;
case 0x51: // 17 cpi, 136/272 dpi graphics (ESC Q) IW
cpi = 17;
printRes = 5;
style &= (0xffff - STYLE_PROP);
definedUnit = 136;
extraIntraSpace = 0.0;
LQtypeFace = fixed;
updateFont();
break;
case 0x70: // Proportional, 144/288 dpi graphics (ESC p) IW
style |= STYLE_PROP;
cpi = 10;
//printQuality = QUALITY_LQ;
printRes = 6;
definedUnit = 144;
LQtypeFace = prop;
updateFont();
break;
case 0x50: // Proportional, 160/320 dpi graphics (ESC P) IW
style |= STYLE_PROP;
cpi = 12;
//printQuality = QUALITY_LQ;
printRes = 7;
definedUnit = 160;
LQtypeFace = prop;
updateFont();
break;
case 0x54: // Set n/144-inch line spacing (ESC T nn) IW
lineSpacing = (Real64)PARAM2(0)/144;
break;
case 0x59: // Turn underline off (ESC Y) IW
style &= ~STYLE_UNDERLINE;
updateFont();
break;
case 0x58: // Turn underline on (ESC X) IW
style |= STYLE_UNDERLINE;
score = SCORE_SINGLE;
updateFont();
break;
case 0x42: // Select 1/8-inch line spacing (ESC B) IW
lineSpacing = (Real64)1/8;
break;
case 0x41: // Select 1/6-inch line spacing (ESC A) IW
lineSpacing = (Real64)1/6;
break;
case 0x3c: case 0x3e: // Unidirectional mode (one line) (ESC <)
// We don't have a print head, so just ignore this
break;
case 0x63: // Initialize printer (ESC c) IW
resetPrinter();
break;
case 0x48: // Set page length in lines (ESC H nnnn) IW
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
pageHeight = (Real64)PARAM4(0)/144;
bottomMargin = pageHeight;
topMargin = 0.0;
break;
}
case 0x21: // Select bold font (ESC !) IW
style |= STYLE_BOLD;
updateFont();
break;
case 0x22: // Cancel bold font (ESC ") IW
style &= ~STYLE_BOLD;
updateFont();
break;
case 0x78: // Select superscript printing (ESC x) IW
style |= STYLE_SUPERSCRIPT;
updateFont();
break;
case 0x79: // Select subscript printing (ESC y) IW
style |= STYLE_SUBSCRIPT;
updateFont();
break;
case 0x7a: // Cancel superscript/subscript printing (ESC z) IW
style &= 0xFFFF - STYLE_SUPERSCRIPT - STYLE_SUBSCRIPT;
updateFont();
break;
case 0x77: // Select halfheight printing (ESC w) IW
style |= STYLE_HALFHEIGHT;
updateFont();
break;
case 0x57: // Cancel halfheight printing (ESC W) IW
style &= ~STYLE_HALFHEIGHT;
updateFont();
break;
case 0x72: // Reverse paper feed (ESC r) IW
{
printf("Reverse Feed\n");
if(lineSpacing > 0) lineSpacing *= -1;
break;
}
case 0x66: // Forward paper feed (ESC f) IW
{
if(lineSpacing < 0) lineSpacing *= -1;
break;
}
case 0x61: // Select typeface (ESC a n) IW worry about this later
break;
case 0x6d: case 0x4d: //Same as ESC a n commands
break;
case 0x4c: // Set left margin (ESC L nnn) IW
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
leftMargin = (Real64)(PARAM3(0)-1.0) / (Real64)cpi;
if (curX < leftMargin)
curX = leftMargin;
break;
}
case 0x4b: // Select printing color (ESC K) IW
switch (paramc(0))
{
case 0: break;
case 1: params[0] = 4; break;
case 2: params[0] = 1; break;
case 3: params[0] = 2; break;
case 4: params[0] = 5; break;
case 5: params[0] = 6; break;
case 6: params[0] = 3; break;
}
if(paramc(0)==0) color = COLOR_BLACK;
else color = params[0]<<5;
break;
case 0x3d: // Internal font ID (ESC = n) IW LQ
//Ignore for now
break;
case 0x3f: //Send ID string to computer (ESC ?) IW
//insert SCC send code here
printf("Sending ID String\n");
iw_scc_write = true;
break;
case 0x52: // Repeat character c for nnn times (ESC R nnn c) IW
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
x = 0;
ESCCmd = 0;
while (x < PARAM3(0))
{
printChar(params[3]);
x++;
}
break;
}
case 0x30: //Clear all tabs
numHorizTabs = 0;
break;
case 0x28: // Set horizontal tabs (ESC ( nnn,) IW
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
if (params[3] == '.' || (numHorizTabs>0 && horiztabs[numHorizTabs-1] > (Real64)PARAM3(0)*(1/(Real64)cpi)))
{
horiztabs[numHorizTabs++] = (Real64)PARAM3(0)*(1/(Real64)cpi);
//printf("Adding tab %d, and end\n",PARAM3(0));
//printf("Number of Tabs:%d\n",numHorizTabs);
}
else if (params[3] == ',' && numHorizTabs < 32)
{
horiztabs[numHorizTabs++] = (Real64)PARAM3(0)*(1/(Real64)cpi);
numParam = 0;
neededParam = 4;
//printf("Adding tab %d, plus more\n", PARAM3(0));
//printf("Number of Tabs:%d\n",numHorizTabs);
return true;
}
x = 0;
break;
}
case 0x29: // Delete horizontal tabs (ESC ) nnn,) IW
{
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
x = 0;
while (x < numHorizTabs)
{
if (horiztabs[x] == (Real64)PARAM3(0)*(1/(Real64)cpi))
{ printf("Tab Found %d\n",PARAM3(0));
horiztabs[x] = 0;
}
x++;
}
if (params[3] == '.')
{
printf("Deleting tab %d, and end\n",PARAM3(0));
}
else if (params[3] == ',')
{
numParam = 0;
neededParam = 4;
//printf("Deleting tab %d, plus more\n", PARAM3(0));
return true;
}
x = 0;
break;
}
case 0x5a: // Set switches to open (off) (ESC Z nn) IW
//printf ("switcha is: %x switchb is: %x\n",switcha,switchb);
//printf ("(Setting to 0) param 0 is: %x param 1 is: %x\n",params[0],params[1]);
switcha &= ~params[0];
switchb &= ~params[1];
//printf ("switcha is now: %x switchb is now: %x\n",switcha,switchb);
updateSwitch();
break;
case 0x44: // Set switches to closed (on) (ESC D nn) IW
//printf ("switcha is: %x switchb is: %x\n",switcha,switchb);
//printf ("(Setting to 1) param 0 is: %x param 1 is: %x\n",params[0],params[1]);
switcha |= params[0];
switchb |= params[1];
//printf ("switcha is now: %x switchb is now: %x\n",switcha,switchb);
updateSwitch();
break;
case 0x75: // Add one tab stop at nnn (ESC u nnn) IW
{
bool haveStop = false;
int lastEmpty;
//If the list is full, we assume there are no empty spaces to fill until the list is scanned
if (numHorizTabs == 32) lastEmpty = 33;
else lastEmpty = numHorizTabs;
int x = 0;
//convert any leading spaces in parameters to zeros
while (x < 4)
{
if (params[x] == ' ') params[x] = '0';
x++;
}
x = 0;
//see if we have the tab stop already on the list and check for any deleted entries to reuse
while (x < numHorizTabs)
{
if (horiztabs[x] == (Real64)PARAM3(0)*(1/(Real64)cpi))
{
//printf("We have this tab already! at list entry: %d\n", x);
haveStop = true;
}
if (horiztabs[x] == 0) lastEmpty = x;
//printf("at list entry: %d\n", x);
x++;
}
if (!haveStop && lastEmpty < 33)
{
//printf("Adding tab %d\n", PARAM3(0));
horiztabs[lastEmpty] = (Real64)PARAM3(0)*(1/(Real64)cpi);
if (lastEmpty == numHorizTabs) numHorizTabs++; //only increase if we don't reuse an empty tab entry
//printf("Number of Tabs:%d\n",numHorizTabs);
}
}
break;
case 0x833: // Feed n lines of blank space (US n) IW
{
int x = 0;
while (x < paramc(0))
{
curY += lineSpacing;
if (curY > bottomMargin)
newPage(true,false);
x++;
}
}
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;
}
return true;
case 0x0c: // Form feed (FF)
newPage(true,true);
return true;
case 0x0d: // Carriage Return (CR)
curX = leftMargin;
if ((switcha&=128)) curY += lineSpacing; // If switch A-8 is set, send a LF after CR
if (!autoFeed)
return true;
case 0x0a: // Line feed
//curX = leftMargin;
curY += lineSpacing;
if (curY > bottomMargin)
newPage(true,false);
return true;
case 0x0e: //Select double width printing (SO) IW
style |= STYLE_DOUBLEWIDTH;
updateFont();
return true;
case 0x0f: // Dis-select double width printing (SI) IW
style &= ~STYLE_DOUBLEWIDTH;
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)
return true;
case 0x18: // Cancel line (CAN)
return true;
case 0x1b: // ESC
ESCSeen = true;
return true;
case 0x1f: // unit seperator (US) Feed 1 to 15 line commands
FSSeen = true;
return true;
default:
return false;
}
return false;
}
#endif // HAVE_SDL
//static void PRINTER_EventHandler(Bitu param);
void Imagewriter::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 Imagewriter::printChar(Bit8u ch)
{
#ifdef HAVE_SDL
charRead = true;
if (page == NULL) return;
// Apply MSB if desired, but only if we aren't printing graphics!
if (msb != 255) {
if (!bitGraph.remBytes) ch &= 0x7F;
}
#endif // HAVE_SDL
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;
//if (style & STYLE_HALFHEIGHT) penY += curFont->glyph->bitmap.rows / 4;
if (style & STYLE_SUBSCRIPT) penY += 20;
if (style & STYLE_SUPERSCRIPT) penY -= 10;
if (style & STYLE_HALFHEIGHT) penY += 15;
// Copy bitmap into page
SDL_LockSurface(page);
blitGlyph(curFont->glyph->bitmap, penX, penY, false);
blitGlyph(curFont->glyph->bitmap, penX+1, penY, 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;
// Print a slashed zero if the softswitch B-1 is set
if(switchb&1 && ch=='0') slashzero(penX,penY);
// 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 {
x_advance = 1/(Real64)actcpi;
}
x_advance += extraIntraSpace;
curX += x_advance;
// Draw lines if desired
if ((score != SCORE_NONE) && (style &
(STYLE_UNDERLINE)))
{
// 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);
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 Imagewriter::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 Imagewriter::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 Imagewriter::setAutofeed(bool feed) {
autoFeed = feed;
}
bool Imagewriter::getAutofeed() {
return autoFeed;
}
bool Imagewriter::isBusy() {
// We're never busy
return false;
}
bool Imagewriter::ack() {
// Acknowledge last char read
if(charRead) {
charRead=false;
return true;
}
return false;
}
void Imagewriter::setupBitImage(Bit8u dens, Bit16u numCols) {
switch (dens)
{
case 0:
bitGraph.horizDens = 72;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 1:
bitGraph.horizDens = 80;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 2:
bitGraph.horizDens = 96;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 3:
bitGraph.horizDens = 107;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 4:
bitGraph.horizDens = 120;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 5:
bitGraph.horizDens = 136;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 6:
bitGraph.horizDens = 144;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
case 7:
bitGraph.horizDens = 160;
bitGraph.vertDens = 72;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 1;
break;
//Imagewriter LQ resolutions
case 8:
bitGraph.horizDens = 144;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 9:
bitGraph.horizDens = 160;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 10:
bitGraph.horizDens = 192;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 11:
bitGraph.horizDens = 216;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 12:
bitGraph.horizDens = 240;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 13:
bitGraph.horizDens = 272;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 14:
bitGraph.horizDens = 288;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
case 15:
bitGraph.horizDens = 320;
bitGraph.vertDens = 216;
bitGraph.adjacent = true;
bitGraph.bytesColumn = 3;
break;
default:
//break;
printf("PRINTER: Unsupported bit image density");
}
bitGraph.remBytes = numCols * bitGraph.bytesColumn;
bitGraph.readBytesColumn = 0;
}
void Imagewriter::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;
if(dpi%bitGraph.horizDens && bitGraph.horizDens < dpi)
{
if(PIXX%(bitGraph.horizDens*8) || (PIXX == 0)) //Primative scaling function
{
pixsizeX++;
}
}
pixsizeY = dpi/bitGraph.vertDens > 0? dpi/bitGraph.vertDens : 1;
if(bitGraph.vertDens == 216)
{
if(PIXY%(bitGraph.vertDens*8) || (PIXY == 0)) //Primative scaling function
{
pixsizeY++;
}
}
}
if ((printRes > 7) && (verticalDot != 0)) //for ESC t
{
curY += (Real64)verticalDot/(Real64)bitGraph.vertDens;
}
// 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=1; j<256; 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 Imagewriter::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 Imagewriter::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)
if (multipageOutput && outputHandle == NULL)
{
ShowCursor(1);
PRINTDLG pd;
pd.lStructSize = sizeof(PRINTDLG);
pd.hDevMode = (HANDLE) NULL;
pd.hDevNames = (HANDLE) NULL;
pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
pd.hwndOwner = NULL;
pd.hDC = (HDC) NULL;
pd.nFromPage = 0xFFFF;
pd.nToPage = 0xFFFF;
pd.nMinPage = 1;
pd.nMaxPage = 0xFFFF;
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;
if(!PrintDlg(&pd))
{
//If user clicks cancel, show warning dialog and force all output to bitmaps as failsafe.
MessageBox(NULL,"You did not select a printer.\nAll output from this print job will be saved as bitmap files.",NULL,MB_ICONEXCLAMATION);
findNextName((char *)"page", (char *)".bmp", &fname[0]);
SDL_SaveBMP(page, fname); //Save first page as bitmap.
outputHandle = printerDC;
printerDC = NULL;
ShowCursor(0);
return;
}
else
{
//Create device context.
printerDC = pd.hDC;
ShowCursor(0);
}
}
if (!printerDC) //Fall thru for subsequent pages if printer dialog was cancelled.
{
findNextName((char *)"page", (char *)".bmp", &fname[0]);
SDL_SaveBMP(page, fname); //Save remaining pages.
return;
}
Bit32u physW = GetDeviceCaps(printerDC, PHYSICALWIDTH);
Bit32u physH = GetDeviceCaps(printerDC, PHYSICALHEIGHT);
Bit16u printeroffsetW = GetDeviceCaps(printerDC, PHYSICALOFFSETX); //printer x offset in actual pixels
Bit16u printeroffsetH = GetDeviceCaps(printerDC, PHYSICALOFFSETY); //printer y offset in actual pixels
Bit16u deviceDPIW = GetDeviceCaps(printerDC, LOGPIXELSX);
Bit16u deviceDPIH = GetDeviceCaps(printerDC, LOGPIXELSY);
Real64 physoffsetW = (Real64)printeroffsetW/deviceDPIW; //printer x offset in inches
Real64 physoffsetH = (Real64)printeroffsetH/deviceDPIH; //printer y offset in inches
Bit16u dpiW = page->w/defaultPageWidth; //Get currently set DPI of the emulated printer in an indirect way
Bit16u dpiH = page->h/defaultPageHeight;
Real64 soffsetW = physoffsetW*dpiW; //virtual page x offset in actual pixels
Real64 soffsetH = physoffsetH*dpiH; //virtual page y offset in actual pixels
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 ImageWriter";
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, soffsetW, soffsetH, 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((char *)"page", (char *)".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, "colorps") == 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)
{
//LOG(LOG_MISC,LOG_ERROR)("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, "false 3\n");
fprintf(psfile, "colorimage\n");
SDL_LockSurface(page);
Bit8u * templine;
templine = (Bit8u*) malloc(page->w*3);
Bit32u x = 0;
Bit32u numy = page->h;
Bit32u numx = page->w;
Bit32u numpix = page->w*3;
Bit32u pix = 0;
Bit32u currDot = 0;
Bit32u y = 0;
Bit32u plane = 0;
Bit8u r, g, b;
ASCII85BufferPos = ASCII85CurCol = 0;
for (y=0; y < numy;y++)
{
currDot = 0;
for (x = 0; x < numx; x++)
{
SDL_GetRGB(getxyPixel(x,y), page->format, &r, &g, &b);
templine[currDot] = ~r; currDot++;
templine[currDot] = ~g; currDot++;
templine[currDot] = ~b; currDot++;
}
// Compress data using RLE
pix = 0;
while (pix < numpix)
{
if ((pix < numpix-2) && (templine[pix] == templine[pix+1]) && (templine[pix] == templine[pix+2]))
{
// Found three or more pixels with the same color
Bit8u sameCount = 3;
Bit8u col = templine[pix];
while (sameCount < 128 && sameCount+pix < numpix && col == templine[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)
|| (templine[pix+diffCount] != templine[pix+diffCount+1])
|| (templine[pix+diffCount] != templine[pix+diffCount+2])
))
diffCount++;
fprintASCII85(psfile, diffCount-1);
for (Bit8u i=0; i<diffCount; i++)
fprintASCII85(psfile, 255-templine[pix++]);
}
}
}
// Write EOD for RLE and ASCII85
fprintASCII85(psfile, 128);
fprintASCII85(psfile, 256);
SDL_UnlockSurface(page);
free(templine);
fprintf(psfile, "showpage\n");
if (multipageOutput)
{
multiPageCounter++;
outputHandle = psfile;
}
else
{
fprintf(psfile, "%%%%Pages: 1\n");
fprintf(psfile, "%%%%EOF\n");
fclose(psfile);
outputHandle = NULL;
}
}
else if (strcasecmp(output, "ps") == 0)
{
FILE* psfile = NULL;
printf("%d\n",getPixel(2));
// 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)
{
//LOG(LOG_MISC,LOG_ERROR)("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 Imagewriter::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 Imagewriter::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);
}
if (strcasecmp(output, "colorps") == 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 Imagewriter::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 Imagewriter::getxyPixel(Bit32u x,Bit32u y) {
Bit8u *p;
/* get the X/Y values within the bounds of this surface */
if ((unsigned) x > (unsigned) page->w - 1u)
x = (x < 0) ? 0 : page->w - 1;
if ((unsigned) y > (unsigned) page->h - 1u)
y = (y < 0) ? 0 : page->h - 1;
/* Set a pointer to the exact location in memory of the pixel
in question: */
p = (Bit8u *) (((Bit8u *) page->pixels) + /* Start at top of RAM */
(y * page->pitch) + /* Go down Y lines */
x); /* Go in X pixels */
/* Return the correctly-sized piece of data containing the
* pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
* RGB value) */
return (*p);
}
Bit8u Imagewriter::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 imagewriter_init(int pdpi, int ppaper, int banner, char* poutput, bool mpage)
{
if (defaultImagewriter != NULL) return; //if Imagewriter on this port is initialized, reuse it
defaultImagewriter = new Imagewriter(pdpi, ppaper, banner, poutput, mpage);
}
extern "C" void imagewriter_loop(Bit8u pchar)
{
if (defaultImagewriter == NULL) return;
defaultImagewriter->printChar(pchar);
}
extern "C" void imagewriter_close()
{
delete defaultImagewriter;
defaultImagewriter = NULL;
}
extern "C" void imagewriter_feed()
{
if(defaultImagewriter == NULL) return;
defaultImagewriter->formFeed();
}