#212 Updated dot-matrix printer emulation

open
nobody
None
5
2012-09-07
2008-08-23
Mark Junker
No

This is the update of the following patch: [ 899195 ] Dot-matrix printer emulation.

This new version of the patch compiles fine with the dosbox CVS sources as of 2008-08-09.

Discussion

  • Mark Junker
    Mark Junker
    2008-08-23

    Logged In: YES
    user_id=995396
    Originator: YES

    Hmm ... I'll try to upload the patch again.

     
  • Mark Junker
    Mark Junker
    2008-08-23

    Logged In: YES
    user_id=995396
    Originator: YES

    Third try ... I hope that sf.net fixed the problem.

     
  • Mark Junker
    Mark Junker
    2008-08-23

    Logged In: YES
    user_id=995396
    Originator: YES

    I'll post the patch here because I cannot upload it (File Upload: ArtifactFile: Could not open file for writing). Sorry for the inconvenience. Here's the patch:

    diff --git a/INSTALL b/INSTALL
    index 6be2b89..10a6d81 100644
    --- a/INSTALL
    +++ b/INSTALL
    @@ -41,6 +41,9 @@ ALSA_Headers
    for Alsa support under linux. Part of the linux kernel sources
    Licensed under LGPL

    +FreeType2
    + For printer support (optional). Get it from http://www.freetype.org/.
    +
    If you want compile from the CVS under a unix system, you'll also need
    automake (>=1.6), autoconf(>=2.50). Should be available at http://www.gnu.org

    diff --git a/README b/README
    index 71cb23e..8baa66d 100644
    --- a/README
    +++ b/README
    @@ -47,9 +47,9 @@ well as the description of the MOUNT command (section 4).

    -=======
    -2. FAQ:
    -=======
    +====================================
    +2. FAQ (Frequently Asked Questions):
    +====================================

    Some Frequently Asked Questions:

    diff --git a/autogen.sh b/autogen.sh
    old mode 100755
    new mode 100644
    index 4d7d5ba..e3b71d9
    diff --git a/configure.in b/configure.in
    index a773aa0..09c2f73 100644
    --- a/configure.in
    +++ b/configure.in
    @@ -302,10 +302,12 @@ else
    fi

    AH_TEMPLATE(C_SSHOT,[Define to 1 to enable screenshots, requires libpng])
    +AH_TEMPLATE(C_LIBPNG,[Define to 1 if you have libpng])
    AC_CHECK_HEADER(png.h,have_png_h=yes,)
    AC_CHECK_LIB(png, png_check_sig, have_png_lib=yes, ,-lz)
    if test x$have_png_lib = xyes -a x$have_png_h = xyes ; then
    LIBS="$LIBS -lpng -lz"
    + AC_DEFINE(C_LIBPNG,1)
    AC_DEFINE(C_SSHOT,1)
    else
    AC_MSG_WARN([Can't find libpng, screenshot support disabled])
    @@ -378,6 +380,17 @@ else
    AC_MSG_WARN([Can't find libSDL_sound, libSDL_sound support disabled])
    fi

    +AH_TEMPLATE(C_PRINTER,[Define to 1 to use printer support])
    +AC_ARG_ENABLE(printer,AC_HELP_STRING([--disable-printer],[Disable printer support]),,enable_printer=yes)
    +AC_PATH_PROG(FREETYPE_CONFIG, freetype-config, no)
    +if test "$FREETYPE_CONFIG" = "no" ; then
    + AC_MSG_WARN([Can't find FreeType2, printer disabled])
    +else
    + AC_DEFINE(C_PRINTER,1)
    + CXXFLAGS="$CXXFLAGS $FREETYPE_CONFIG --cflags"
    + LIBS="$LIBS $FREETYPE_CONFIG --libs"
    +fi
    +
    dnl Check for mprotect. Needed for 64 bits linux
    AH_TEMPLATE(C_HAVE_MPROTECT,[Define to 1 if you have the mprotect function])
    AC_CHECK_HEADER([sys/mman.h], [
    diff --git a/include/Makefile.am b/include/Makefile.am
    index ce2c03c..209d791 100644
    --- a/include/Makefile.am
    +++ b/include/Makefile.am
    @@ -33,5 +33,6 @@ shell.h \ support.h \ timer.h \ vga.h \ -video.h
    +video.h \ +printer.h

    diff --git a/include/printer.h b/include/printer.h
    new file mode 100644
    index 0000000..4c41b82
    --- /dev/null
    +++ b/include/printer.h
    @@ -0,0 +1,233 @@
    +/
    + * 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.
    + /
    +
    +#include <dosbox.h>
    +
    +#if C_PRINTER
    +
    +#if !defined PRINTER_H
    +#define
    PRINTER_H
    +
    +#include "SDL.h"
    +
    +#include <ft2build.h>
    +#include FT_FREETYPE_H
    +
    +#if defined (WIN32)
    +#include <windows.h>
    +#include <winspool.h>
    +#endif
    +
    +#define STYLE_PROP 0x01
    +#define STYLE_CONDENSED 0x02
    +#define STYLE_BOLD 0x04
    +#define STYLE_DOUBLESTRIKE 0x08
    +#define STYLE_DOUBLEWIDTH 0x10
    +#define STYLE_ITALICS 0x20
    +#define STYLE_UNDERLINE 0x40
    +#define STYLE_SUPERSCRIPT 0x80
    +#define STYLE_SUBSCRIPT 0x100
    +#define STYLE_STRIKETHROUGH 0x200
    +#define STYLE_OVERSCORE 0x400
    +#define STYLE_DOUBLEWIDTHONELINE 0x800
    +#define STYLE_DOUBLEHEIGHT 0x1000
    +
    +#define SCORE_NONE 0x00
    +#define SCORE_SINGLE 0x01
    +#define SCORE_DOUBLE 0x02
    +#define SCORE_SINGLEBROKEN 0x05
    +#define SCORE_DOUBLEBROKEN 0x06
    +
    +#define QUALITY_DRAFT 0x01
    +#define QUALITY_LQ 0x02
    +
    +enum Typeface
    +{
    + roman = 0,
    + sansserif,
    + courier,
    + prestige,
    + script,
    + ocrb,
    + ocra,
    + orator,
    + orators,
    + scriptc,
    + romant,
    + sansserifh,
    + svbusaba = 30,
    + svjittra = 31
    +};
    +
    +
    +class CPrinter {
    +public:
    +
    + CPrinter (Bit16u dpi, Bit16u width, Bit16u height, char
    output, bool multipageOutput);
    + virtual ~CPrinter();
    +
    + // Process one character sent to virtual printer
    + void printChar(Bit8u ch);
    +
    + // Hard Reset (like switching printer off and on)
    + void resetPrinterHard();
    +
    + // Set Autofeed value
    + void setAutofeed(bool feed);
    +
    + // Get Autofeed value
    + bool getAutofeed();
    +
    + // True if printer is unable to process more data right now (do not use printChar)
    + bool isBusy();
    +
    + // True if the last sent character was received
    + bool ack();
    +
    + // Manual formfeed
    + void formFeed();
    +
    + // Returns true if the current page is blank
    + bool isBlank();
    +
    +private:
    +
    + // Checks if given char belongs to a command and process it. If false, the character
    + // should be printed
    + bool processCommandChar(Bit8u ch);
    +
    + // Resets the printer to the factory settings
    + void resetPrinter();
    +
    + // Reload font. Must be called after changing dpi, style or cpi
    + void updateFont();
    +
    + // Clears page. If save is true, saves the current page to a bitmap
    + void newPage(bool save);
    +
    + // Blits the given glyph on the page surface. If add is true, the values of bitmap are
    + // added to the values of the pixels in the page
    + void blitGlyph(FT_Bitmap bitmap, Bit16u destx, Bit16u desty, bool add);
    +
    + // Draws an anti-aliased line from (fromx, y) to (tox, y). If broken is true, gaps are included
    + void drawLine(Bit8u fromx, Bit8u tox, Bit8u y, bool broken);
    +
    + // Setup the bitGraph structure
    + void setupBitImage(Bit8u dens, Bit16u numCols);
    +
    + // Process a character that is part of bit image. Must be called iff bitGraph.remBytes > 0.
    + void printBitGraph(Bit8u ch);
    +
    + // Copies the codepage mapping from the constant array to CurMap
    + void selectCodepage(Bit16u cp);
    +
    + // Output current page
    + void outputPage();
    +
    + // Prints out a byte using ASCII85 encoding (only outputs something every four bytes). When b>255, closes the ASCII85 string
    + void fprintASCII85(FILE f, Bit16u b);
    +
    + // Closes a multipage document
    + void finishMultipage();
    +
    + // Returns value of the num-th pixel (couting left-right, top-down) in a safe way
    + Bit8u getPixel(Bit32u num);
    +
    + FT_Library FTlib; // FreeType2 library used to render the characters
    +
    + SDL_Surface
    page; // Surface representing the current page
    + FT_Face curFont; // The font currently used to render characters
    +
    + Real64 curX, curY; // Position of the print head (in inch)
    +
    + Bit16u dpi; // dpi of the page
    + Bit16u ESCCmd; // ESC-command that is currently processed
    + bool ESCSeen; // True if last read character was an ESC (0x1B)
    +
    + Bit8u numParam, neededParam; // Numbers of parameters already read/needed to process command
    +
    + Bit8u params[20]; // Buffer for the read params
    + Bit16u style; // Style of font (see STYLE_ constants)
    + Real64 cpi, actcpi; // CPI value set by program and the actual one (taking in account font types)
    + Bit8u score; // Score for lines (see SCORE_
    constants)
    +
    + Real64 topMargin, bottomMargin, rightMargin, leftMargin; // Margins of the page (in inch)
    + Real64 pageWidth, pageHeight; // Size of page (in inch)
    + Real64 defaultPageWidth, defaultPageHeight; // Default size of page (in inch)
    + Real64 lineSpacing; // Size of one line (in inch)
    +
    + Real64 horiztabs[32]; // Stores the set horizontal tabs (in inch)
    + Bit8u numHorizTabs; // Number of configured tabs
    +
    + Real64 verttabs[16]; // Stores the set vertical tabs (in inch)
    + Bit8u numVertTabs; // Number of configured tabs
    +
    + Bit8u curCharTable; // Currently used char table und charset
    + Bit8u printQuality; // Print quality (see QUALITY_ constants)
    +
    + Typeface LQtypeFace; // Typeface used in LQ printing mode
    +
    + Real64 extraIntraSpace; // Extra space between two characters (set by program, in inch)
    +
    + bool charRead; // True if a character was read since the printer was last initialized
    + bool autoFeed; // True if a LF should automatically added after a CR
    + bool printUpperContr; // True if the upper command characters should be printed
    +
    + struct bitGraphicParams // Holds information about printing bit images
    + {
    + Bit16u horizDens, vertDens; // Density of image to print (in dpi)
    + bool adjacent; // Print adjacent pixels? (ignored)
    + Bit8u bytesColumn; // Bytes per column
    + Bit16u remBytes; // Bytes left to read before image is done
    + Bit8u column[6]; // Bytes of the current and last column
    + Bit8u readBytesColumn; // Bytes read so far for the current column
    + } bitGraph;
    +
    + Bit8u densk, densl, densy, densz; // Image density modes used in ESC K/L/Y/Z commands
    +
    + Bit16u curMap[256]; // Currently used ASCII => Unicode mapping
    + Bit16u charTables[4]; // Charactertables
    +
    + Real64 definedUnit; // Unit used by some ESC/P2 commands (negative => use default)
    +
    + bool multipoint; // If multipoint mode is enabled
    + Real64 multiPointSize; // Point size of font in multipoint mode
    + Real64 multicpi; // CPI used in multipoint mode
    +
    + Real64 hmi; // Horizontal motion index (in inch; overrides CPI settings)
    +
    + Bit8u msb; // MSB mode
    + Bit16u numPrintAsChar; // Number of bytes to print as characters (even when normally control codes)
    +
    +#if defined (WIN32)
    + HDC printerDC; // Win32 printer device
    +#endif
    +
    + char
    output; // Output method selected by user
    + void outputHandle; // If not null, additional pages will be appended to the given handle
    + bool multipageOutput; // If true, all pages are combined to one file/print job etc. until the "eject page" button is pressed
    + Bit16u multiPageCounter; // Current page (when printing multipages)
    +
    + Bit8u ASCII85Buffer[4]; // Buffer used in ASCII85 encoding
    + Bit8u ASCII85BufferPos; // Position in ASCII85 encode buffer
    + Bit8u ASCII85CurCol; // Columns printed so far in the current lines
    +};
    +
    +#endif
    +
    +#endif
    diff --git a/src/dos/Makefile.am b/src/dos/Makefile.am
    index 3bdfbf5..f2cf307 100644
    --- a/src/dos/Makefile.am
    +++ b/src/dos/Makefile.am
    @@ -5,6 +5,6 @@ EXTRA_DIST = scsidefs.h wnaspi32.h dos_codepages.h dos_keyboard_layout_data.h
    libdos_a_SOURCES = dos.cpp dos_devices.cpp dos_execute.cpp dos_files.cpp dos_ioctl.cpp dos_memory.cpp \ dos_misc.cpp dos_classes.cpp dos_programs.cpp dos_tables.cpp \ drives.cpp drives.h drive_virtual.cpp drive_local.cpp drive_cache.cpp drive_fat.cpp \ - drive_iso.cpp dev_con.h dos_mscdex.cpp dos_keyboard_layout.cpp \ + drive_iso.cpp dev_con.h dev_lpt.h dos_mscdex.cpp dos_keyboard_layout.cpp \ cdrom.h cdrom.cpp cdrom_ioctl_win32.cpp cdrom_aspi_win32.cpp cdrom_ioctl_linux.cpp cdrom_image.cpp \ cdrom_ioctl_os2.cpp
    diff --git a/src/dos/dev_lpt.h b/src/dos/dev_lpt.h
    new file mode 100644
    index 0000000..a13e481
    --- /dev/null
    +++ b/src/dos/dev_lpt.h
    @@ -0,0 +1,71 @@
    +/

    + * 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.
    +
    /
    +
    +#include "dos_inc.h"
    +#include "inout.h"
    +
    +class device_LPT : public DOS_Device {
    +public:
    + // Creates a LPT device that communicates with the num-th parallel port, i.e. is LPTnum
    + device_LPT(char name, Bit8u num);
    + bool Read(Bit8u * data,Bit16u * size);
    + bool Write(Bit8u * data,Bit16u * size);
    + bool Seek(Bit32u * pos,Bit32u type);
    + bool Close();
    + Bit16u GetInformation(void);
    +private:
    + Bit8u num; // This device is LPTnum
    +};
    +
    +bool device_LPT::Read(Bit8u * data,Bit16u * size)
    +{
    +
    size=0;
    + LOG(LOG_DOSMISC,LOG_NORMAL)("LPTDEVICE:Read called");
    + return true;
    +}
    +
    +
    +bool device_LPT::Write(Bit8u * data,Bit16u * size)
    +{
    + for (Bit16u i=0; i<size; i++)
    + IO_WriteB(real_readw(0x0040, 0x0008 + ((num-1)
    2)), data[i]);
    +
    + return true;
    +}
    +
    +bool device_LPT::Seek(Bit32u * pos,Bit32u type)
    +{
    + pos = 0;
    + return true;
    +}
    +
    +bool device_LPT::Close()
    +{
    + return false;
    +}
    +
    +Bit16u device_LPT::GetInformation(void)
    +{
    + return 0x80A0;
    +};
    +
    +device_LPT::device_LPT(char
    name, Bit8u num)
    +{
    + this->name = name;
    + this->num = num;
    +}
    diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp
    index 5b0539d..b48db9e 100644
    --- a/src/dos/dos.cpp
    +++ b/src/dos/dos.cpp
    @@ -29,6 +29,8 @@
    #include "regs.h"
    #include "dos_inc.h"
    #include "setup.h"
    +/// Added ... maybe superfluous
    +#include "inout.h"
    #include "support.h"
    #include "serialport.h"

    @@ -103,8 +105,13 @@ static Bitu DOS_21Handler(void) {
    }
    break;
    case 0x05: / Write Character to PRINTER /
    - E_Exit("DOS:Unhandled call %02X",reg_ah);
    +#ifdef C_PRINTER
    + // DOS function only allows printing to LPT1 and returns nothing
    + IO_WriteB(real_readw(0x0040, 0x0008), reg_dl);
    break;
    +#else
    + E_Exit("DOS:Unhandled call %02X",reg_ah);
    +#endif
    case 0x06: / Direct Console Output / Input /
    switch (reg_dl) {
    case 0xFF: / Input /
    diff --git a/src/dos/dos_devices.cpp b/src/dos/dos_devices.cpp
    index e052a59..41ccc1b 100644
    --- a/src/dos/dos_devices.cpp
    +++ b/src/dos/dos_devices.cpp
    @@ -31,6 +31,9 @@

    #include "dev_con.h"

    +#ifdef C_PRINTER
    +#include "dev_lpt.h"
    +#endif

    DOS_Device * Devices[DOS_DEVICES];

    @@ -142,6 +145,12 @@ Bit8u DOS_FindDevice(char const * name) {
    char dot = strrchr(name_start,'.');
    if(dot)
    dot = 0; //no ext checking

    +#ifdef C_PRINTER
    + // PRN is a alias for the printer port
    + if (strcmp(name, "PRN") == 0)
    + name = "LPT1";
    +#endif
    +
    / loop through devices /
    for(Bit8u index = 0;index < DOS_DEVICES;index++) {
    if (Devices[index]) {
    @@ -185,6 +194,11 @@ void DOS_SetupDevices(void) {
    newdev2=new device_NUL();
    DOS_AddDevice(newdev2);
    DOS_Device * newdev3;
    +#ifdef C_PRINTER
    + newdev3=new device_LPT("LPT1", 1);
    + DOS_AddDevice(newdev3);
    +#else
    newdev3=new device_LPT1();
    DOS_AddDevice(newdev3);
    +#endif
    }
    diff --git a/src/dos/dos_ioctl.cpp b/src/dos/dos_ioctl.cpp
    index 4b1ae04..1c8e033 100644
    --- a/src/dos/dos_ioctl.cpp
    +++ b/src/dos/dos_ioctl.cpp
    @@ -60,6 +60,11 @@ bool DOS_IOCTL(void) {
    }
    reg_ax=reg_dx; //Destroyed officially
    return true;
    + case 0x01: / Set Device Information /
    + // Fake success
    + reg_ax=0x0000;
    + LOG(LOG_IOCTL,LOG_NORMAL)("01:Faked success: Setting device info of handle %d to %02X",handle, reg_dl);
    + return true;
    case 0x02: / Read from Device Control Channel /
    if (Files[handle]->GetInformation() & 0xc000) {
    / is character device with IOCTL support /
    @@ -125,6 +130,10 @@ bool DOS_IOCTL(void) {
    }
    reg_ax=0x300;
    return true;
    + case 0x0A: / Is Device of Handle Remote? /
    + reg_dx=0x8000;
    + LOG(LOG_IOCTL,LOG_NORMAL)("0A:Faked output: device of handle %d is remote",handle);
    + return true;
    case 0x0D: / Generic block device request /
    {
    if (Drives[drive]->isRemovable()) {
    diff --git a/src/dosbox.cpp b/src/dosbox.cpp
    index 388a9cd..a40a747 100644
    --- a/src/dosbox.cpp
    +++ b/src/dosbox.cpp
    @@ -112,6 +112,10 @@ void SHELL_Init(void);

    void INT10_Init(Section*);

    +#if C_PRINTER
    +void PRINTER_Init(Section* sec);
    +#endif
    +
    static LoopHandler * loop;

    bool SDLNetInited;
    @@ -662,6 +666,39 @@ void DOSBOX_Init(void) {
    #endif
    // secprop->AddInitFunction(&CREDITS_Init);

    +#if C_PRINTER
    + secprop=control->AddSection_prop("printer",&PRINTER_Init);
    + Pbool = secprop->Add_bool("printer", Property::Changeable::OnlyAtStart, true);
    + Pbool->Set_help("Enable printer emulation.");
    +
    + Pint = secprop->Add_int("dpi", Property::Changeable::OnlyAtStart, 360);
    + Pint->Set_help("Resolution of printer (default 360).");
    +
    + Pint = secprop->Add_int("width", Property::Changeable::OnlyAtStart, 85);
    + Pint->Set_help("Width of paper in 1/10 inch (default 85 = 8.5'').");
    +
    + Pint = secprop->Add_int("height", Property::Changeable::OnlyAtStart, 110);
    + Pint->Set_help("Height of paper in 1/10 inch (default 110 = 11.0'').");
    +
    + const char* printerOutputs[] = {
    +#ifdef C_LIBPNG
    + "png",
    +#endif
    + "ps",
    + "bmp",
    +#if defined (WIN32)
    + "printer",
    +#endif
    + 0 };
    +
    + Pstring = secprop->Add_string("output", Property::Changeable::OnlyAtStart, "ps");
    + Pstring->Set_help("Output method for finished pages.");
    + Pstring->Set_values(printerOutputs);
    +
    + Pbool = secprop->Add_bool("multipage", Property::Changeable::OnlyAtStart, false);
    + Pbool->Set_help("Adds all pages to one Postscript file or printer job until ALT-F8 is pressed.");
    +#endif
    +
    //TODO ?
    secline=control->AddSection_line("autoexec",&AUTOEXEC_Init);
    MSG_Add("AUTOEXEC_CONFIGFILE_HELP",
    diff --git a/src/hardware/Makefile.am b/src/hardware/Makefile.am
    index 7a9f7bf..30e31ac 100644
    --- a/src/hardware/Makefile.am
    +++ b/src/hardware/Makefile.am
    @@ -10,6 +10,5 @@ libhardware_a_SOURCES = adlib.cpp dma.cpp gameblaster.cpp hardware.cpp iohandler
    memory.cpp mixer.cpp pcspeaker.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \ vga.cpp vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_gfx.cpp vga_other.cpp \ vga_memory.cpp vga_misc.cpp vga_seq.cpp vga_xga.cpp vga_s3.cpp vga_tseng.cpp vga_paradise.cpp \ - cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp
    -
    + cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp printer.cpp

    diff --git a/src/hardware/printer.cpp b/src/hardware/printer.cpp
    new file mode 100644
    index 0000000..5e6628b
    --- /dev/null
    +++ b/src/hardware/printer.cpp
    @@ -0,0 +1,2206 @@
    +/
    + * 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.
    + /
    +
    +#include "printer.h"
    +
    +#if C_PRINTER
    +
    +#include <math.h>
    +#include "inout.h"
    +#include "keyboard.h"
    +#include "mapper.h"
    +#include "mem.h"
    +#include "support.h"
    +#include "setup.h"
    +
    +#ifdef C_LIBPNG
    +#include <png.h>
    +#endif
    +
    +#define LPTPORT 0x378
    +
    +static CPrinter
    defaultPrinter = NULL;
    +
    +#define PARAM16(I) (params[I+1]256+params[I])
    +#define PIXX ((Bitu)floor(curX
    dpi+0.5))
    +#define PIXY ((Bitu)floor(curYdpi+0.5))
    +
    +static Bit16u confdpi, confwidth, confheight;
    +static char confoutputDevice[50];
    +static bool confmultipageOutput;
    +
    +// Various ASCII codepage to unicode maps
    +
    +static const Bit16u cp437Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
    +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp737Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
    +0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
    +0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c2,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03c9,0x03ac,0x03ad,0x03ae,0x03ca,0x03af,0x03cc,0x03cd,0x03cb,0x03ce,0x0386,0x0388,0x0389,0x038a,0x038c,0x038e,
    +0x038f,0x00b1,0x2265,0x2264,0x03aa,0x03ab,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp775Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x0106,0x00fc,0x00e9,0x0101,0x00e4,0x0123,0x00e5,0x0107,0x0142,0x0113,0x0156,0x0157,0x012b,0x0179,0x00c4,0x00c5,
    +0x00c9,0x00e6,0x00c6,0x014d,0x00f6,0x0122,0x00a2,0x015a,0x015b,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x00a4,
    +0x0100,0x012a,0x00f3,0x017b,0x017c,0x017a,0x201d,0x00a6,0x00a9,0x00ae,0x00ac,0x00bd,0x00bc,0x0141,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x0104,0x010c,0x0118,0x0116,0x2563,0x2551,0x2557,0x255d,0x012e,0x0160,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0172,0x016a,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x017d,
    +0x0105,0x010d,0x0119,0x0117,0x012f,0x0161,0x0173,0x016b,0x017e,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x00d3,0x00df,0x014c,0x0143,0x00f5,0x00d5,0x00b5,0x0144,0x0136,0x0137,0x013b,0x013c,0x0146,0x0112,0x0145,0x2019,
    +0x00ad,0x00b1,0x201c,0x00be,0x00b6,0x00a7,0x00f7,0x201e,0x00b0,0x2219,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp850Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
    +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x0192,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
    +0x00f0,0x00d0,0x00ca,0x00cb,0x00c8,0x0131,0x00cd,0x00ce,0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580,
    +0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,0x00fe,0x00de,0x00da,0x00db,0x00d9,0x00fd,0x00dd,0x00af,0x00b4,
    +0x00ad,0x00b1,0x2017,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp852Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x016f,0x0107,0x00e7,0x0142,0x00eb,0x0150,0x0151,0x00ee,0x0179,0x00c4,0x0106,
    +0x00c9,0x0139,0x013a,0x00f4,0x00f6,0x013d,0x013e,0x015a,0x015b,0x00d6,0x00dc,0x0164,0x0165,0x0141,0x00d7,0x010d,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x0104,0x0105,0x017d,0x017e,0x0118,0x0119,0x00ac,0x017a,0x010c,0x015f,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x011a,0x015e,0x2563,0x2551,0x2557,0x255d,0x017b,0x017c,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0102,0x0103,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
    +0x0111,0x0110,0x010e,0x00cb,0x010f,0x0147,0x00cd,0x00ce,0x011b,0x2518,0x250c,0x2588,0x2584,0x0162,0x016e,0x2580,
    +0x00d3,0x00df,0x00d4,0x0143,0x0144,0x0148,0x0160,0x0161,0x0154,0x00da,0x0155,0x0170,0x00fd,0x00dd,0x0163,0x00b4,
    +0x00ad,0x02dd,0x02db,0x02c7,0x02d8,0x00a7,0x00f7,0x00b8,0x00b0,0x00a8,0x02d9,0x0171,0x0158,0x0159,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp855Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x0452,0x0402,0x0453,0x0403,0x0451,0x0401,0x0454,0x0404,0x0455,0x0405,0x0456,0x0406,0x0457,0x0407,0x0458,0x0408,
    +0x0459,0x0409,0x045a,0x040a,0x045b,0x040b,0x045c,0x040c,0x045e,0x040e,0x045f,0x040f,0x044e,0x042e,0x044a,0x042a,
    +0x0430,0x0410,0x0431,0x0411,0x0446,0x0426,0x0434,0x0414,0x0435,0x0415,0x0444,0x0424,0x0433,0x0413,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x0445,0x0425,0x0438,0x0418,0x2563,0x2551,0x2557,0x255d,0x0439,0x0419,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x043a,0x041a,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
    +0x043b,0x041b,0x043c,0x041c,0x043d,0x041d,0x043e,0x041e,0x043f,0x2518,0x250c,0x2588,0x2584,0x041f,0x044f,0x2580,
    +0x042f,0x0440,0x0420,0x0441,0x0421,0x0442,0x0422,0x0443,0x0423,0x0436,0x0416,0x0432,0x0412,0x044c,0x042c,0x2116,
    +0x00ad,0x044b,0x042b,0x0437,0x0417,0x0448,0x0428,0x044d,0x042d,0x0449,0x0429,0x0447,0x0427,0x00a7,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp857Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x0131,0x00c4,0x00c5,
    +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x0130,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x015e,0x015f,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x011e,0x011f,0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
    +0x00ba,0x00aa,0x00ca,0x00cb,0x00c8,0x0000,0x00cd,0x00ce,0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580,
    +0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,0x0000,0x00d7,0x00da,0x00db,0x00d9,0x00ec,0x00ff,0x00af,0x00b4,
    +0x00ad,0x00b1,0x0000,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp860Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e3,0x00e0,0x00c1,0x00e7,0x00ea,0x00ca,0x00e8,0x00cd,0x00d4,0x00ec,0x00c3,0x00c2,
    +0x00c9,0x00c0,0x00c8,0x00f4,0x00f5,0x00f2,0x00da,0x00f9,0x00cc,0x00d5,0x00dc,0x00a2,0x00a3,0x00d9,0x20a7,0x00d3,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x00d2,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp861Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00d0,0x00f0,0x00de,0x00c4,0x00c5,
    +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00fe,0x00fb,0x00dd,0x00fd,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00c1,0x00cd,0x00d3,0x00da,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp862Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
    +0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,0x05e8,0x05e9,0x05ea,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp863Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00c2,0x00e0,0x00b6,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x2017,0x00c0,0x00a7,
    +0x00c9,0x00c8,0x00ca,0x00f4,0x00cb,0x00cf,0x00fb,0x00f9,0x00a4,0x00d4,0x00dc,0x00a2,0x00a3,0x00d9,0x00db,0x0192,
    +0x00a6,0x00b4,0x00f3,0x00fa,0x00a8,0x00b8,0x00b3,0x00af,0x00ce,0x2310,0x00ac,0x00bd,0x00bc,0x00be,0x00ab,0x00bb,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp864Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x066a,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00b0,0x00b7,0x2219,0x221a,0x2592,0x2500,0x2502,0x253c,0x2524,0x252c,0x251c,0x2534,0x2510,0x250c,0x2514,0x2518,
    +0x03b2,0x221e,0x03c6,0x00b1,0x00bd,0x00bc,0x2248,0x00ab,0x00bb,0xfef7,0xfef8,0x0000,0x0000,0xfefb,0xfefc,0x0000,
    +0x00a0,0x00ad,0xfe82,0x00a3,0x00a4,0xfe84,0x0000,0x0000,0xfe8e,0xfe8f,0xfe95,0xfe99,0x060c,0xfe9d,0xfea1,0xfea5,
    +0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,0x0668,0x0669,0xfed1,0x061b,0xfeb1,0xfeb5,0xfeb9,0x061f,
    +0x00a2,0xfe80,0xfe81,0xfe83,0xfe85,0xfeca,0xfe8b,0xfe8d,0xfe91,0xfe93,0xfe97,0xfe9b,0xfe9f,0xfea3,0xfea7,0xfea9,
    +0xfeab,0xfead,0xfeaf,0xfeb3,0xfeb7,0xfebb,0xfebf,0xfec1,0xfec5,0xfecb,0xfecf,0x00a6,0x00ac,0x00f7,0x00d7,0xfec9,
    +0x0640,0xfed3,0xfed7,0xfedb,0xfedf,0xfee3,0xfee7,0xfeeb,0xfeed,0xfeef,0xfef3,0xfebd,0xfecc,0xfece,0xfecd,0xfee1,
    +0xfe7d,0x0651,0xfee5,0xfee9,0xfeec,0xfef0,0xfef2,0xfed0,0xfed5,0xfef5,0xfef6,0xfedd,0xfed9,0xfef1,0x25a0,
    +};
    +
    +static const Bit16u cp865Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
    +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192,
    +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00a4,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
    +};
    +
    +static const Bit16u cp866Map[256] = {
    +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
    +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
    +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    +0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
    +0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
    +0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
    +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    +0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,
    +0x0401,0x0451,0x0404,0x0454,0x0407,0x0457,0x040e,0x045e,0x00b0,0x2219,0x00b7,0x221a,0x2116,0x00a4,0x25a0,0x00a0
    +};
    +
    +static const Bit16u codepages[15] = {0, 437, 932, 850, 851, 853, 855, 860, 863, 865, 852, 857, 862, 864, 866};
    +
    +// TODO: Implement all international charsets
    +static const Bit16u intCharSets[15][12] =
    +{
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, // USA
    + {0x0023, 0x0024, 0x00e0, 0x00ba, 0x00e7, 0x00a7, 0x005e, 0x0060, 0x00e9, 0x00f9, 0x00e8, 0x00a8}, // France
    + {0x0023, 0x0024, 0x00a7, 0x00c4, 0x00d6, 0x00dc, 0x005e, 0x0060, 0x00e4, 0x00f6, 0x00fc, 0x00df}, // Germany
    + {0x00a3, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, // UK
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e},
    + {0x0023, 0x0024, 0x00a7, 0x00c4, 0x0027, 0x0022, 0x00b6, 0x0060, 0x00a9, 0x00ae, 0x2020, 0x2122} // Legal
    +};
    +
    +CPrinter::CPrinter(Bit16u dpi, Bit16u width, Bit16u height, char
    output, bool multipageOutput)
    +{
    + if (FT_Init_FreeType(&FTlib))
    + {
    + LOG(LOG_MISC,LOG_ERROR)("PRINTER: Unable to init Freetype2. Printing disabled");
    + 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)(defaultPageWidthdpi),
    + (Bitu)(defaultPageHeight
    dpi),
    + 8,
    + 0,
    + 0,
    + 0,
    + 0);
    +
    + // Set a grey palette
    + SDL_Palette palette = page->format->palette;
    +
    + for (Bitu i=0; i<255; i++)
    + palette->colors[i].r = palette->colors[i].g = palette->colors[i].b = 255-i;
    +
    + 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 = (LPSTR) NULL;
    + pd.lpSetupTemplateName = (LPSTR) NULL;
    + pd.hPrintTemplate = (HANDLE) NULL;
    + pd.hSetupTemplate = (HANDLE) NULL;
    + PrintDlg(&pd);
    + printerDC = pd.hDC;
    +#endif
    + }
    +
    + LOG(LOG_MISC,LOG_NORMAL)("PRINTER: Enabled");
    + }
    +};
    +
    +void CPrinter::resetPrinterHard()
    +{
    + charRead = false;
    + resetPrinter();
    +}
    +
    +void CPrinter::resetPrinter()
    +{
    + curX = curY = 0.0;
    + ESCSeen = 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();
    +
    + newPage(false);
    +
    + // Default tabs => Each eight characters
    + for (Bitu i=0;i<32;i++)
    + horiztabs[i] = i
    8(1/(Real64)cpi);
    + numHorizTabs = 32;
    +
    + numVertTabs = 255;
    +}
    +
    +
    +CPrinter::~CPrinter(void)
    +{
    + finishMultipage();
    + if (page != NULL)
    + {
    + SDL_FreeSurface(page);
    + page = NULL;
    + FT_Done_FreeType(FTlib);
    + }
    +#if defined (WIN32)
    + DeleteDC(printerDC);
    +#endif
    +};
    +
    +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];
    +}
    +
    +void CPrinter::updateFont()
    +{
    + if (curFont != NULL)
    + FT_Done_Face(curFont);
    +
    + char fontName;
    +
    + switch (LQtypeFace)
    + {
    + case roman:
    + fontName = "roman.ttf";
    + break;
    + case sansserif:
    + fontName = "sansserif.ttf";
    + break;
    + case courier:
    + fontName = "courier.ttf";
    + break;
    + case script:
    + fontName = "script.ttf";
    + break;
    + case ocra:
    + case ocrb:
    + fontName = "ocra.ttf";
    + break;
    + default:
    + fontName = "roman.ttf";
    + }
    +
    + if (FT_New_Face(FTlib, fontName, 0, &curFont))
    + {
    + LOG(LOG_MISC,LOG_ERROR)("Unable to load font %s", fontName);
    + curFont = NULL;
    + }
    +
    + Real64 horizPoints = 10.5;
    + Real64 vertPoints = 10.5;
    +
    + if (!multipoint)
    + {
    + actcpi = cpi;
    +
    + if (cpi != 10 && !(style & STYLE_CONDENSED))
    + {
    + horizPoints
    = (Real64)10/(Real64)cpi;
    + vertPoints = (Real64)10/(Real64)cpi;
    + }
    +
    + if (!style & STYLE_PROP)
    + {
    + if (cpi == 10 && (style & STYLE_CONDENSED))
    + {
    + actcpi = 17.14;
    + horizPoints
    = (Real64)10/(Real64)17.14;
    + vertPoints = (Real64)10/(Real64)17.14;
    + }
    +
    + if (cpi == 12 && (style & STYLE_CONDENSED))
    + {
    + actcpi = 20.0;
    + horizPoints
    = (Real64)10/(Real64)20.0;
    + vertPoints = (Real64)10/(Real64)20.0;
    + }
    + }
    +
    + if (style & (STYLE_PROP | STYLE_CONDENSED))
    + {
    + horizPoints /= (Real64)2.0;
    + vertPoints /= (Real64)2.0;
    + }
    +
    + if ((style & STYLE_DOUBLEWIDTH) || (style & STYLE_DOUBLEWIDTHONELINE))
    + {
    + actcpi /= 2;
    + horizPoints
    = (Real64)2.0;
    + }
    +
    + if (style & STYLE_DOUBLEHEIGHT)
    + vertPoints = (Real64)2.0;
    + }
    + else
    + {
    + actcpi = multicpi;
    + horizPoints = vertPoints = multiPointSize;
    + }
    +
    + if ((style & STYLE_SUPERSCRIPT) || (style & STYLE_SUBSCRIPT))
    + {
    + horizPoints
    = (Real64)2/(Real64)3;
    + vertPoints = (Real64)2/(Real64)3;
    + actcpi /= (Real64)2/(Real64)3;
    + }
    +
    +
    + FT_Set_Char_Size(curFont, (Bit16u)horizPoints
    64, (Bit16u)vertPoints64, 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);
    + }
    +}
    +
    +bool CPrinter::processCommandChar(Bit8u ch)
    +{
    + if (ESCSeen)
    + {
    + ESCCmd = ch;
    + ESCSeen = false;
    + numParam = 0;
    +
    + switch (ESCCmd)
    + {
    + case 0x02: // Undocumented
    + 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 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 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
    + case 0x50: // Select 10.5-point, 10-cpi (ESC P)
    + case 0x54: // Cancel superscript/subscript printing (ESC T)
    + case 0x67: // Select 10.5-point, 15-cpi (ESC g)
    + case 0x73: // Select low-speed mode (ESC s)
    + 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
    + case 0x43: // Set page length in lines (ESC C)
    + case 0x4a: // Advance print position vertically (ESC J n)
    + 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 0x57: // Turn double-width printing on/off (ESC W)
    + case 0x61: // Select justification (ESC a)
    + 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 0x74: // Select character table (ESC t)
    + case 0x77: // Turn double-height printing on/off (ESC w)
    + case 0x78: // Select LQ or draft (ESC x)
    + 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) (ESC c)
    + neededParam = 2;
    + break;
    + case 0x2a: // Select bit image (ESC
    )
    + case 0x58: // Select font by pitch and point (ESC X)
    + neededParam = 3;
    + 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(LOG_MISC,LOG_ERROR)("PRINTER: Unknown command ESC %c (%02X). Unable to skip parameters.", ESCCmd, ESCCmd);
    + neededParam = 0;
    + ESCCmd = 0;
    + return true;
    + }
    +
    + if (neededParam > 0)
    + return true;
    + }
    +
    + // Two bytes sequence
    + if (ESCCmd == 0x28)
    + {
    + 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 0x228: // 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)("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)chlineSpacing)) // 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)
    + {
    + 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);
    + 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 0x2a: // Select bit image (ESC )
    + setupBitImage(params[0], PARAM16(1));
    + break;
    + case 0x2b: // Set n/360-inch line spacing (ESC +)
    + lineSpacing = (Real64)params[0]/360;
    + break;
    + case 0x2d: // Turn underline on/off (ESC -)
    + if (params[0] == 0 || params[0] == 48)
    + style &= 0xFFFF - 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 &= 0xFFFF - 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
    + 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 &= 0xFFFF - 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 &= 0xFFFF - STYLE_DOUBLESTRIKE;
    + break;
    + case 0x4a: // Advance print position vertically (ESC J n)
    + curY += (Real64)((Real64)params[0] / 180);
    + if (curY > bottomMargin)
    + newPage(true);
    + 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 &= 0xFFFF - 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 0x6b: // Select typeface (ESC k)
    + if (params[0] <= 11 || params[0] == 30 || params[0] == 31)
    + LQtypeFace = (Typeface)params[0];
    + updateFont();
    + break;
    + case 0x6c: // Set left margin (ESC 1)
    + 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)
    + LOG(LOG_MISC,LOG_WARN)("PRINTER: Color printing not supported");
    + break;
    + case 0x73: // Select low-speed mode (ESC s)
    + // Ignore
    + break;
    + case 0x74: // Select character table (ESC t)
    + 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 &= 0xFFFF - 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;
    + if (params[0] == 1 || params[0] == 49)
    + printQuality = QUALITY_LQ;
    + 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 0x228: // Assign character table (ESC (t)
    + if (params[2] < 4 && params[3] < 16)
    + {
    + charTables[params[2]] = codepages[params[3]];
    + if (params[2] == curCharTable)
    + selectCodepage(charTables[curCharTable]);
    + }
    + break;
    + case 0x22d: // Select line/score (ESC (-)
    + style &= 0xFFFF - 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)3600 / (Real64)params[2];
    + 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);
    + 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)
    + {
    + topMargin = ((Real64)PARAM16(2)) * definedUnit;
    + bottomMargin = ((Real64)PARAM16(4)) * definedUnit;
    + }
    + 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);
    + else
    + curY = newPos;
    + }
    + }
    + break;
    + default:
    + if (ESCCmd < 0x100)
    + LOG(LOG_MISC,LOG_WARN)("PRINTER: Skipped unsupported command ESC %c (%02X)", ESCCmd, ESCCmd);
    + else
    + LOG(LOG_MISC,LOG_WARN)("PRINTER: Skipped unsupported command ESC ( %c (%02X)", ESCCmd-0x200, ESCCmd-0x200);
    + }
    +
    + ESCCmd = 0;
    + return true;
    + }
    +
    + switch (ch)
    + {
    + 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<span="">[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);
    + }
    + else
    + {
    + // Find tab below current pos
    + Real64 moveTo = -1;
    + for (Bit8u i=0; i<numVertTabs; i++)="" +="" if="" (verttabs<span="">[i] > curY)
    + moveTo = verttabs[i];
    +
    + // Nothing found => Act like FF
    + if (moveTo > bottomMargin || moveTo < 0)
    + newPage(true);
    + 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);
    + 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);
    + 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)
    + {
    + 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 &= 0xFFFF - 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 &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
    + updateFont();
    + return true;
    + case 0x18: // Cancel line (CAN)
    + return true;
    + case 0x1b: // ESC
    + ESCSeen = true;
    + return true;
    + default:
    + return false;
    + }
    +
    + return false;
    +}
    +
    +void CPrinter::newPage(bool save)
    +{
    + if (save)
    + outputPage();
    +
    + 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));
    +}
    +
    +void CPrinter::printChar(Bit8u ch)
    +{
    + charRead = true;
    +
    + if (page == NULL)
    + return;
    +
    + // 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;
    + }
    +
    + // 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;
    +
    + // 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);
    +
    + // Doublestrike => Print the glyph a second time one pixel below
    + if (style & STYLE_DOUBLESTRIKE)
    + blitGlyph(curFont->glyph->bitmap, penX, penY+1, true);
    +
    + // Bold => Print the glyph a second time one pixel to the right
    + if (style & STYLE_BOLD)
    + blitGlyph(curFont->glyph->bitmap, penX+1, penY, true);
    +
    + SDL_UnlockSurface(page);
    +
    + // For line printing
    + Bit16u lineStart = PIXX;
    +
    + if (style & STYLE_PROP)
    + curX += (Real64)((Real64)(curFont->glyph->advance.x)/(Real64)(dpi
    64));
    + else
    + {
    + if (hmi < 0)
    + curX += 1/(Real64)actcpi;
    + else
    + curX += hmi;
    + }
    +
    + curX += extraIntraSpace;
    +
    + // 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;
    +
    + if (style & STYLE_UNDERLINE)
    + lineY = penY + 5 + curFont->glyph->bitmap.rows;
    + if (style & STYLE_STRIKETHROUGH)
    + lineY = PIXY + curFont->size->metrics.ascender/128;
    + if (style & STYLE_OVERSCORE)
    + lineY = PIXY - ((score == SCORE_DOUBLE || score == SCORE_DOUBLEBROKEN)?5:0);
    +
    + drawLine(penX, PIXX, lineY, score==SCORE_SINGLEBROKEN || score==SCORE_DOUBLEBROKEN);
    +
    + if (score == SCORE_DOUBLE || score == SCORE_DOUBLEBROKEN)
    + drawLine(lineStart, PIXX, lineY + 5, score==SCORE_SINGLEBROKEN || score==SCORE_DOUBLEBROKEN);
    + }
    +}
    +
    +void CPrinter::blitGlyph(FT_Bitmap bitmap, Bit16u destx, Bit16u desty, bool add)
    +{
    + for (int y=0; y<bitmap.rows; y++)="" +="" {="" +="" for="" (int="" 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;
    + if (add)
    + {
    + if (
    target + source > 255)
    +
    target = 255;
    + else
    + target += source;
    + }
    + else
    + target = source;
    + }
    + }
    + }
    +}
    +
    +void CPrinter::drawLine(Bit8u fromx, Bit8u tox, Bit8u y, bool broken)
    +{
    + SDL_LockSurface(page);
    +
    + Bitu breakmod = dpi / 15;
    + Bitu gapstart = (breakmod * 4)/5;
    +
    + // Draw anti-aliased line
    + for (int 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) = 120;
    + if (y < page->h)
    +
    ((Bit8u)page->pixels + x + ypage->pitch) = !broken?255:120;
    + if (y+1 < page->h)
    + ((Bit8u)page->pixels + x + (y+1)page->pitch) = 120;
    + }
    + }
    +
    + 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
    + return charRead;
    +}
    +
    +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:
    + LOG(LOG_MISC,LOG_ERROR)("PRINTER: Unsupported bit image density %i", dens);
    + }
    +
    + 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 = dpi/bitGraph.horizDens > 0?dpi/bitGraph.horizDens:1;
    + Bitu pixsizeY = dpi/bitGraph.vertDens > 0?dpi/bitGraph.vertDens:1;
    +
    + for (Bit8u i=0; i<bitGraph.bytesColumn; i++)="" +="" {="" +="" for="" (Bits="" j="7;" j="">=0; j--)
    + {
    + Bit8u pixel = (bitGraph.column[i] >> j) & 0x01;
    +
    + if (pixel != 0)
    + {
    + 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) = 255;
    + }
    +
    + curY += (Real64)1/(Real64)bitGraph.vertDens;
    + }
    + }
    +
    + SDL_UnlockSurface(page);
    +
    + curY = oldY;
    +
    + bitGraph.readBytesColumn = 0;
    +
    + // Advance to the left
    + curX += (Real64)1/(Real64)bitGraph.horizDens;
    +}
    +
    +void CPrinter::formFeed()
    +{
    + // Don't output blank pages
    + newPage(!isBlank());
    +
    + finishMultipage();
    +}
    +
    +static void findNextName(char front, char ext, char fname)
    +{
    + char buffer[10];
    + Bit16u num = 1;
    + FILE
    test = NULL;
    + do
    + {
    + strcpy(fname, front);
    + sprintf(&buffer[0], "%d", num++);
    + strcat(fname, &buffer[0]);
    + strcat(fname, ext);
    + test = fopen(fname, "rb");
    + if (test != NULL)
    + fclose(test);
    + }
    + while (test != NULL);
    +}
    +
    +void CPrinter::outputPage()
    +{
    + char fname[200];
    +
    + if (strcasecmp(output, "printer") == 0)
    + {
    +#if defined (WIN32)
    + Bit16u physW = GetDeviceCaps(printerDC, PHYSICALWIDTH);
    + Bit16u physH = GetDeviceCaps(printerDC, PHYSICALHEIGHT);
    +
    + Real64 scaleW, scaleH;
    + //Bit32u topX, topY;
    +
    + 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);
    + HBITMAP bitmap = CreateCompatibleBitmap(memHDC, page->w, page->h);
    + SelectObject(memHDC, bitmap);
    +
    + // Start new printer job?
    + if (outputHandle == NULL)
    + {
    + DOCINFO docinfo;
    + docinfo.cbSize = sizeof(docinfo);
    + docinfo.lpszDocName = "DOSBOX Printer";
    + docinfo.lpszOutput = NULL;
    + docinfo.lpszDatatype = NULL;
    + docinfo.fwType = 0;
    +
    + StartDoc(printerDC, &docinfo);
    + multiPageCounter = 1;
    + }
    +
    + StartPage(printerDC);
    + SDL_LockSurface(page);
    +
    + SDL_Palette sdlpal = page->format->palette;
    +
    + for (Bit16u y=0; y<page->h; y++)
    + {
    + for (Bit16u x=0; x<page->w; x++)
    + {
    + Bit8u pixel =
    ((Bit8u)page->pixels + x + (ypage->pitch));
    + Bit32u color = 0;
    + color |= sdlpal->colors[pixel].r;
    + color |= ((Bit32u)sdlpal->colors[pixel].g) << 8;
    + color |= ((Bit32u)sdlpal->colors[pixel].b) << 16;
    + SetPixel(memHDC, x, y, color);
    + }
    + }
    +
    + SDL_UnlockSurface(page);
    +
    + StretchBlt(printerDC, 0, 0, physW, physH, memHDC, 0, 0, page->w, page->h, SRCCOPY);
    +
    + EndPage(printerDC);
    +
    + if (multipageOutput)
    + {
    + multiPageCounter++;
    + outputHandle = printerDC;
    + }
    + else
    + {
    + EndDoc(printerDC);
    + outputHandle = NULL;
    + }
    +
    + DeleteDC(memHDC);
    +#else
    + LOG(LOG_MISC,LOG_ERROR)("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];
    +
    + / 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 (Bitu 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);
    +
    + SDL_LockSurface(page);
    +
    + // Allocate an array of scanline pointers
    + row_pointers = (png_bytep)malloc(page->hsizeof(png_bytep));
    + for (int i=0; i!=page->h; ++i)
    + row_pointers[i] = ((Bit8u)page->pixels+(ipage->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("page", ".ps", &fname[0]);
    + else
    + findNextName("doc", ".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)(defaultPageWidth74), (Bit16u)(defaultPageHeight74));
    + fprintf(psfile, "%%%%Creator: DOSBOX 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)(defaultPageWidth74), (Bit16u)(defaultPageHeight74));
    + 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->hpage->w;
    + ASCII85BufferPos = ASCII85CurCol = 0;
    +
    + //Bit8u buffer[5];
    +
    + 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("page",="" ".bmp",="" &fname<span="">[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 + (ypage->pitch)) != 0)
    + blank = false;
    +
    + SDL_UnlockSurface(page);
    +
    + return blank;
    +}
    +
    +Bit8u CPrinter::getPixel(Bit32u num)
    +{
    + // Respect the pitch
    + return ((Bit8u)page->pixels + (num % page->w) + ((num / page->w) * page->pitch));
    +}
    +
    +void PRINTER_writedata(Bitu port,Bitu val,Bitu iolen)
    +{
    + if (!defaultPrinter)
    + defaultPrinter = new CPrinter(confdpi, confwidth, confheight, confoutputDevice, confmultipageOutput);
    +
    + defaultPrinter->printChar(val);
    +}
    +
    +Bitu PRINTER_readstatus(Bitu port,Bitu iolen)
    +{
    + // Don't create a CPrinter unless the program really wants to print
    + // Return standard: No error, printer online, no ack and not busy
    + if (!defaultPrinter)
    + return 0xD8;
    +
    + // Printer is always online and never reports an error
    + Bit8u status = 0x18;
    +
    + if (!defaultPrinter->isBusy())
    + status |= 0x80;
    +
    + if (!defaultPrinter->ack())
    + status |= 0x40;
    +
    + return status;
    +}
    +
    +void PRINTER_writecontrol(Bitu port,Bitu val,Bitu iolen)
    +{
    + if (defaultPrinter)
    + defaultPrinter->setAutofeed((val & 0x02) != 0);
    +
    + if ((val & 0x04) && defaultPrinter)
    + defaultPrinter->resetPrinterHard();
    +}
    +
    +Bitu PRINTER_readcontrol(Bitu port,Bitu iolen)
    +{
    + // Don't create a CPrinter unless the program really wants to print
    + if (!defaultPrinter)
    + return 0x08;
    +
    + return 0x08 | (defaultPrinter->getAutofeed()?0x02:0x00);
    +}
    +
    +static void FormFeed(void)
    +{
    + if (defaultPrinter)
    + defaultPrinter->formFeed();
    +}
    +
    +static void ForceFormFeed(bool pressed)
    +{
    + if (!pressed)
    + return;
    + FormFeed();
    +}
    +
    +void PRINTER_Shutdown(Section sec)
    +{
    + if (defaultPrinter)
    + {
    + delete defaultPrinter;
    + defaultPrinter = NULL;
    + }
    +}
    +
    +void PRINTER_Init(Section
    sec)
    +{
    + Section_prop * section=static_cast<Section_prop *="">(sec);
    + section->AddDestroyFunction(&PRINTER_Shutdown);
    +
    + // Set base address of LPT1 in the BIOS variable segment
    + real_writew(0x0040, 0x0008, LPTPORT);
    +
    + if(!section->Get_bool("printer"))
    + return;
    +
    + confdpi = section->Get_int("dpi");
    + confwidth = section->Get_int("width");
    + confheight = section->Get_int("height");
    + strcpy(&confoutputDevice[0], section->Get_string("output"));
    + confmultipageOutput = section->Get_bool("multipage");
    +
    + IO_RegisterWriteHandler(LPTPORT,PRINTER_writedata,0xFF); // LPT1 DATA
    + IO_RegisterReadHandler(LPTPORT+1,PRINTER_readstatus,0xFF); // LPT1 STATUS
    + IO_RegisterWriteHandler(LPTPORT+2,PRINTER_writecontrol,0xFF); // LPT1 CONTROL
    + IO_RegisterReadHandler(LPTPORT+2,PRINTER_readcontrol,0xFF); // LPT1 CONTROL
    +
    + MAPPER_AddHandler(ForceFormFeed,MK_f8,MMOD2,"formfeed","Send FF to printer");
    +}
    +
    +#endif
    diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp
    index 8c560dc..78abaab 100644
    --- a/src/ints/bios.cpp
    +++ b/src/ints/bios.cpp
    @@ -370,16 +370,58 @@ static Bitu INT12_Handler(void) {
    return CBRET_NONE;
    };

    +
    +#ifdef C_PRINTER
    +// Get status of the num-th (0-3) parallel port
    +static Bit8u getPrinterStatus(Bit8u num)
    +{
    + Bit8u stat = IO_ReadB(real_readw(0x0040, 0x0008+num2)+1);
    +
    + // Bit3 == 0 or Bit4 == 0 => Error, report an time-out
    + if ((stat & 0x18) != 0x18)
    + return 0x01;
    +
    + // No error, printer is online
    + Bit8u ret = 0x10;
    +
    + // Forward ACK message
    + if (!(stat & 0x40))
    + ret |= 0x40;
    +
    + // Printer busy?
    + if (stat & 0x80)
    + ret |= 0x80;
    +
    + return ret;
    +}
    +#endif
    +
    static Bitu INT17_Handler(void) {
    - LOG(LOG_BIOS,LOG_NORMAL)("INT17:Function %X",reg_ah);
    + //LOG(LOG_BIOS,LOG_NORMAL)("INT17:Function %X",reg_ah);
    switch(reg_ah) {
    case 0x00: /
    PRINTER: Write Character /
    +#ifdef C_PRINTER
    + IO_WriteB(real_readw(0x0040, 0x0008+reg_dx
    2), reg_al);
    + // FIXME: Possible data loss
    + reg_ah=getPrinterStatus(reg_dx);
    +#else
    reg_ah=1; / Report a timeout /
    +#endif
    break;
    case 0x01: / PRINTER: Initialize port /
    +#ifdef C_PRINTER
    + IO_WriteB(real_readw(0x0040, 0x0008+reg_dx2)+2, IO_ReadB(real_readw(0x0040, 0x0008+reg_dx2)+2) | 0x04);
    + // FIXME: Possible data loss
    + reg_ah=getPrinterStatus(reg_dx);
    +#endif
    break;
    case 0x02: / PRINTER: Get Status /
    +#ifdef C_PRINTER
    + // FIXME: Possible data loss
    + reg_ah=getPrinterStatus(reg_dx);
    +#else
    reg_ah=0;
    +#endif
    break;
    case 0x20: / Some sort of printerdriver install check/
    break;