mirror of
https://github.com/sarah-walker-pcem/pcem.git
synced 2025-07-23 03:33:02 +02:00
2411 lines
117 KiB
C
2411 lines
117 KiB
C
/*
|
|
* Copyright (C) 2002-2004 The DOSBox Team
|
|
* Copyright (C) 2020 Eluan Costa Miranda
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Most of this code was taken from an old (as of 2020) unfinished DOSBox patch.
|
|
* I've fixed numerous issues with the interface, drawing and printer logic and
|
|
* this should print most things correctly and with the right positioning.
|
|
*
|
|
* Tested under DOS (Print Screen key, Edit.exe, Word Perfect 5.1), Win 3.1
|
|
* (Write, Paintbrush) and Win 95 (Word 6, Paint). I've adapted it (ESC/P
|
|
* commands resolution, mainly) to emulate the Epson LX-810, which is the
|
|
* printer that I have.
|
|
*
|
|
* Dependencies: Freetype (until I have a dump of the character ROM - or maybe
|
|
* I keep it for an inaccurate "high-res" output?)
|
|
* Output: Postscript, BMP, PNG, TIFF, JPG.
|
|
* (Haven't tested the Windows printer forwarding yet.)
|
|
*
|
|
* These are the print modes for the Epson LX-810:
|
|
* -Bitmap printing emulates the pins but can optionally keep the old behaviour
|
|
* from the DOSBox patch and output continuously. Pin emulation is naive for
|
|
* now.
|
|
* -Draft printing uses freely redistributable fonts that imitate the Epson FX
|
|
* Printers, they look almost the same as the LX-810. Not all extended
|
|
* characters are available and sometimes a combination of styles and effects
|
|
* isn't available, so it will fallback to a similar one (in this case the dots
|
|
* can potentially have slightly different dimensions.) High-speed draft
|
|
* characters are not available. Should be OK for english ASCII printing.
|
|
* -Provide your own monospaced Roman and Sans Serif truetype or opentype fonts.
|
|
* Most fonts are pretty complete unicode-wise, so these will look nice.
|
|
* There is also code to handle non-monospaced fonts automatically that should
|
|
* work OK.
|
|
*
|
|
* INSTRUCTIONS:
|
|
* Currently, a "printer" directory on the pcem main path is used. On Linux this
|
|
* would be "~/.pcem/printer" by default. There, the fonts roman.ttf,
|
|
* roman_italics.ttf, sansserif.ttf and sansserif_italics.ttf should be present.
|
|
* Also a subdirectory "fonts" with all the Epson FX-80 opentype fonts from
|
|
* http://const-iterator.de/fxmatrix/ should exist. This same directory will be
|
|
* used for all the output files.
|
|
*
|
|
* There is also a menu for pressing some of the printer's control panel
|
|
* buttons. On Linux, access with a right-click while the mouse is ungrabbed.
|
|
*
|
|
* TODO/Notes:
|
|
* -Dump/Recreate the character ROM. This should make it possible to use the
|
|
* bitgraph functions to print the characters too.
|
|
* -Better TTF font handling. Use STB TTF?
|
|
* -Bind the printer panel buttons to key combinations in addition to the menu?
|
|
* -Menu commands should only work when the printer is not accepting data. (Use
|
|
* DC1 / DC3 for this? Or the select bit in the parallel port?)
|
|
* -Make the Windows print redirection better (currently is a hack that shows
|
|
* the printing dialog too soon).
|
|
* -Dump to PDF with GhostScript.
|
|
* -Printer redirection on linux (GhostScript, CUPS)
|
|
* -Update the Mingw Makefiles with FreeType.
|
|
* -Search for TODO in this file. :-)
|
|
*/
|
|
|
|
#define MAX_PATH_STRING 1024
|
|
#define false 0
|
|
#define true (!false)
|
|
#define bool int
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
|
|
#if defined(WIN32)
|
|
#include <windows.h>
|
|
#include <winspool.h>
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include "lpt_epsonlx810.h"
|
|
|
|
#include <pcem/devices.h>
|
|
#include <pcem/unsafe/config.h>
|
|
#include <pcem/logging.h>
|
|
#include <SDL.h>
|
|
|
|
#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_DOUBLEWIDTHONELINE 0x800
|
|
|
|
#define QUALITY_DRAFT 0x01
|
|
#define QUALITY_NLQ 0x02
|
|
|
|
#define OUTPUT_TYPE_POSTSCRIPT 0
|
|
#define OUTPUT_TYPE_BMP 1
|
|
#define OUTPUT_TYPE_PNG 2
|
|
#define OUTPUT_TYPE_TIFF 3
|
|
#define OUTPUT_TYPE_JPG 4
|
|
#define OUTPUT_TYPE_FORWARD_TO_REAL_PRINTER 5
|
|
|
|
typedef enum Typeface {
|
|
roman = 0,
|
|
sansserif,
|
|
} Typeface;
|
|
|
|
typedef struct lpt_epsonprinter_t {
|
|
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
|
|
|
|
// TODO: where exactly in relation to the pins are curX and curY?
|
|
double curX, curY; // Position of the print head (in inch)
|
|
|
|
uint16_t dpi; // dpi of the page
|
|
uint16_t ESCCmd; // ESC-command that is currently processed
|
|
bool ESCSeen; // True if last read character was an ESC (0x1B)
|
|
|
|
uint8_t numParam, neededParam; // Numbers of parameters already read/needed to process command
|
|
|
|
uint8_t params[20]; // Buffer for the read params
|
|
uint16_t defaultStyle, style; // Style of font (see STYLE_* constants)
|
|
double defaultCPI, cpi, actcpi; // CPI value set by DIP switch, program and the actual one (taking in account font types)
|
|
|
|
double topMargin, bottomMargin, rightMargin, leftMargin; // Margins of the page (in inch)
|
|
double pageWidth, pageHeight; // Size of page (in inch)
|
|
double defaultPageWidth, defaultPageHeight; // Default size of page (in inch)
|
|
double lineSpacing; // Size of one line (in inch)
|
|
|
|
double horiztabs[32]; // Stores the set horizontal tabs (in inch)
|
|
uint8_t numHorizTabs; // Number of configured tabs
|
|
|
|
double verttabs[16]; // Stores the set vertical tabs (in inch)
|
|
uint8_t numVertTabs; // Number of configured tabs
|
|
|
|
uint8_t defaultCharTable, curCharTable; // Default and currently used char table und charset
|
|
uint8_t defaultI18NCharset, curI18NCharset;
|
|
uint16_t defaultCodePage;
|
|
uint8_t defaultQuality, printQuality; // Print quality (see QUALITY_* constants)
|
|
|
|
Typeface defaultNLQtypeFace, NLQtypeFace; // Typeface used in NLQ printing mode
|
|
|
|
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
|
|
{
|
|
uint16_t horizDens, vertDens; // Density of image to print (in dpi)
|
|
bool adjacent; // Print adjacent pixels? (ignored)
|
|
uint8_t bytesColumn; // Bytes per column
|
|
uint16_t remBytes; // Bytes left to read before image is done
|
|
uint8_t column[6]; // Bytes of the current and last column
|
|
uint8_t readBytesColumn; // Bytes read so far for the current column
|
|
} bitGraph;
|
|
|
|
uint8_t densk, densl, densy, densz; // Image density modes used in ESC K/L/Y/Z commands
|
|
|
|
uint16_t curMap[256]; // Currently used ASCII => Unicode mapping
|
|
uint16_t charTables[2]; // Charactertables
|
|
|
|
#if defined(WIN32)
|
|
HDC printerDC; // Win32 printer device
|
|
#endif
|
|
|
|
int output_type; // 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
|
|
uint16_t multiPageCounter; // Current page (when printing multipages)
|
|
|
|
uint8_t ASCII85Buffer[4]; // Buffer used in ASCII85 encoding
|
|
uint8_t ASCII85BufferPos; // Position in ASCII85 encode buffer
|
|
uint8_t ASCII85CurCol; // Columns printed so far in the current lines
|
|
|
|
uint8_t last_data;
|
|
uint8_t controlreg;
|
|
char fontpath[MAX_PATH_STRING];
|
|
char outputpath[MAX_PATH_STRING];
|
|
|
|
int emulatePinsForBitmaps;
|
|
} lpt_epsonprinter_t;
|
|
|
|
// Process one character sent to virtual printer
|
|
void printChar(lpt_epsonprinter_t *printer);
|
|
|
|
// Hard Reset (like switching printer off and on)
|
|
void resetPrinterHard(lpt_epsonprinter_t *printer);
|
|
|
|
// Set Autofeed value
|
|
void setAutofeed(bool feed, lpt_epsonprinter_t *printer);
|
|
|
|
// Get Autofeed value
|
|
bool getAutofeed(lpt_epsonprinter_t *printer);
|
|
|
|
// True if printer is unable to process more data right now (do not use printChar)
|
|
bool isBusy(lpt_epsonprinter_t *printer);
|
|
|
|
// True if the last sent character was received
|
|
bool ack(lpt_epsonprinter_t *printer);
|
|
|
|
// Manual formfeed
|
|
void formFeed(lpt_epsonprinter_t *printer);
|
|
|
|
// Returns true if the current page is blank
|
|
bool isBlank(lpt_epsonprinter_t *printer);
|
|
|
|
// Checks if given char belongs to a command and process it. If false, the character
|
|
// should be printed
|
|
bool processCommandChar(uint8_t ch, lpt_epsonprinter_t *printer);
|
|
|
|
// Resets the printer to the factory settings
|
|
void resetPrinter(lpt_epsonprinter_t *printer, int software_command);
|
|
|
|
// Reload font. Must be called after changing dpi, style or cpi
|
|
void updateFont(lpt_epsonprinter_t *printer);
|
|
|
|
// Clears page. If save is true, saves the current page to a bitmap
|
|
void newPage(bool save, lpt_epsonprinter_t *printer);
|
|
|
|
// 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, uint16_t destx, uint16_t desty, bool add, lpt_epsonprinter_t *printer);
|
|
|
|
// Draws an anti-aliased line from (fromx, y) to (tox, y).
|
|
void drawLine(uint16_t fromx, uint16_t tox, uint16_t y, lpt_epsonprinter_t *printer);
|
|
|
|
// Setup the bitGraph structure
|
|
void setupBitImage(uint8_t dens, uint16_t numCols, lpt_epsonprinter_t *printer);
|
|
|
|
// Process a character that is part of bit image. Must be called iff bitGraph.remBytes > 0.
|
|
void printBitGraph(uint8_t ch, lpt_epsonprinter_t *printer);
|
|
|
|
// Copies the codepage mapping from the constant array to CurMap, along with international character sets for character < 0x80
|
|
void updateCharset(lpt_epsonprinter_t *printer);
|
|
|
|
// Output current page
|
|
void outputPage(lpt_epsonprinter_t *printer);
|
|
|
|
// Prints out a byte using ASCII85 encoding (only outputs something every four bytes). When b>255, closes the ASCII85 string
|
|
void fprintASCII85(FILE *f, uint16_t b, lpt_epsonprinter_t *printer);
|
|
|
|
// Closes a multipage document
|
|
void finishMultipage(lpt_epsonprinter_t *printer);
|
|
|
|
// Returns value of the num-th pixel (couting left-right, top-down) in a safe way
|
|
uint8_t getPixel(uint32_t num, lpt_epsonprinter_t *printer);
|
|
|
|
#define FREETYPE_FONT_MULTIPLIER 64
|
|
|
|
#define PARAM16(I) (printer->params[(I) + 1] * 256 + printer->params[(I)])
|
|
#define CURX_INCH_TO_PIXEL ((unsigned int)floor(printer->curX * printer->dpi + 0.5))
|
|
#define CURY_INCH_TO_PIXEL ((unsigned int)floor(printer->curY * printer->dpi + 0.5))
|
|
// Be careful to do this as the last step before outputting anything
|
|
#define CURX_INCH_TO_PIXEL_ADD(v) ((unsigned int)floor(printer->curX * printer->dpi + 0.5 + (v)))
|
|
#define CURY_INCH_TO_PIXEL_ADD(v) ((unsigned int)floor(printer->curY * printer->dpi + 0.5 + (v)))
|
|
|
|
// these are the bsd safe string handling functions
|
|
#ifndef HAVE_STRLCAT
|
|
size_t strlcat(char *dst, const char *src, size_t size) {
|
|
size_t srclen;
|
|
size_t dstlen;
|
|
|
|
dstlen = strlen(dst);
|
|
size -= dstlen + 1;
|
|
|
|
if (!size)
|
|
return (dstlen);
|
|
|
|
srclen = strlen(src);
|
|
|
|
if (srclen > size)
|
|
srclen = size;
|
|
|
|
memcpy(dst + dstlen, src, srclen);
|
|
dst[dstlen + srclen] = '\0';
|
|
|
|
return (dstlen + srclen);
|
|
}
|
|
#endif /* !HAVE_STRLCAT */
|
|
|
|
#ifndef HAVE_STRLCPY
|
|
size_t strlcpy(char *dst, const char *src, size_t size) {
|
|
size_t srclen;
|
|
|
|
size--;
|
|
srclen = strlen(src);
|
|
|
|
if (srclen > size)
|
|
srclen = size;
|
|
|
|
memcpy(dst, src, srclen);
|
|
dst[srclen] = '\0';
|
|
|
|
return (srclen);
|
|
}
|
|
#endif /* !HAVE_STRLCPY */
|
|
|
|
// Various ASCII codepage to unicode maps
|
|
|
|
static const uint16_t italicsMap[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,
|
|
// the rest of these should be rendered in italics
|
|
0x00e0, 0x00e8, 0x00f9, 0x00f2, 0x00ec, 0x00b0, 0x00a3, 0x00a1, 0x00bf, 0x00d1, 0x00f1, 0x00a4, 0x20a7, 0x00c5, 0x00e5,
|
|
0x00e7, // TODO: 0x00b0 may be 0x00ba? Also 0x20a7 is annother abbreviation (Pts) instead of the Pt from epson.
|
|
0x00a7, 0x00df, 0x00c6, 0x00e6, 0x00d8, 0x00f8, 0x00a8, 0x00c4, 0x00d6, 0x00dc, 0x00e4, 0x00f6, 0x00fc, 0x00c9, 0x00e9,
|
|
0x00a5, 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,
|
|
0x0030, // 0xff should be the slashed zero in italics? some fonts have that in codepoint 0xe00f
|
|
};
|
|
|
|
static const uint16_t 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, // TODO: 0x0015 should be 0x00a7 in some revisions of the LX-810?
|
|
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 uint16_t 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 uint16_t 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 uint16_t 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 uint16_t 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 uint16_t intCharSets[13][12] = {
|
|
{0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, // USA
|
|
{0x0023, 0x0024, 0x00e0, 0x00b0, 0x00e7, 0x00a7, 0x005e, 0x0060, 0x00e9, 0x00f9, 0x00e8,
|
|
0x00a8}, // France TODO: 0x00b0 may be 0x00ba?
|
|
{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, 0x00c6, 0x00d8, 0x00c5, 0x005e, 0x0060, 0x00e6, 0x00f8, 0x00e5, 0x007e}, // Denmark I
|
|
{0x0023, 0x00a4, 0x00c9, 0x00c4, 0x00d6, 0x00c5, 0x00dc, 0x00e9, 0x00e4, 0x00f6, 0x00e5, 0x00fc}, // Sweden
|
|
{0x0023, 0x0024, 0x0040, 0x00b0, 0x005c, 0x00e9, 0x005e, 0x00f9, 0x00e0, 0x00f2, 0x00e8,
|
|
0x00ec}, // Italy TODO: 0x00b0 may be 0x00ba?
|
|
{0x20a7, 0x0024, 0x0040, 0x00a1, 0x00d1, 0x00bf, 0x005e, 0x0060, 0x00a8, 0x00f1, 0x007d,
|
|
0x007e}, // Spain I TODO: 0x20a7 is annother abbreviation (Pts) instead of the Pt from epson.
|
|
{0x0023, 0x0024, 0x0040, 0x005b, 0x00a5, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, // Japan
|
|
{0x0023, 0x00a4, 0x00c9, 0x00c6, 0x00d8, 0x00c5, 0x00dc, 0x00e9, 0x00e6, 0x00f8, 0x00e5, 0x00fc}, // Norway
|
|
{0x0023, 0x0024, 0x00c9, 0x00c6, 0x00d8, 0x00c5, 0x00dc, 0x00e9, 0x00e6, 0x00f8, 0x00e5, 0x00fc}, // Denmark II
|
|
{0x0023, 0x0024, 0x00e1, 0x00a1, 0x00d1, 0x00bf, 0x00e9, 0x0060, 0x00ed, 0x00f1, 0x00f3, 0x00fa}, // Spain II
|
|
{0x0023, 0x0024, 0x00e1, 0x00a1, 0x00d1, 0x00bf, 0x00e9, 0x00fc, 0x00ed, 0x00f1, 0x00f3, 0x00fa}, // Latin America
|
|
};
|
|
|
|
void resetPrinterHard(lpt_epsonprinter_t *printer) {
|
|
printer->charRead = false;
|
|
resetPrinter(printer, false);
|
|
}
|
|
|
|
// TODO: see exaclty what is reset with ESC @
|
|
void resetPrinter(lpt_epsonprinter_t *printer, int software_command) {
|
|
if (!software_command) {
|
|
printer->curX = printer->curY = 0.0;
|
|
printer->ESCSeen = false;
|
|
printer->ESCCmd = 0;
|
|
printer->numParam = printer->neededParam = 0;
|
|
// TODO: PRINTABLE AREAS ARE BEING IGNORED! Ok because we "may" print to real paper and then use the areas in the
|
|
// real one? Have two options: 1) Have printable areas 2) Reduce the paper width accordingly.
|
|
}
|
|
printer->topMargin = 0.0;
|
|
printer->leftMargin = 0.0;
|
|
printer->rightMargin = printer->pageWidth = printer->defaultPageWidth;
|
|
printer->bottomMargin = printer->pageHeight = printer->defaultPageHeight;
|
|
printer->lineSpacing = (double)1 / 6;
|
|
printer->cpi = printer->defaultCPI;
|
|
printer->curCharTable = printer->defaultCharTable;
|
|
printer->curI18NCharset = printer->defaultI18NCharset;
|
|
printer->style = printer->defaultStyle;
|
|
if (printer->defaultCharTable == 0) // Italics
|
|
printer->printUpperContr = false;
|
|
else
|
|
printer->printUpperContr = true;
|
|
printer->bitGraph.remBytes = 0;
|
|
printer->densk = 0;
|
|
printer->densl = 1;
|
|
printer->densy = 2;
|
|
printer->densz = 3;
|
|
printer->charTables[0] = 0; // Italics
|
|
printer->charTables[1] = printer->defaultCodePage;
|
|
printer->NLQtypeFace = printer->defaultNLQtypeFace;
|
|
printer->printQuality = printer->defaultQuality;
|
|
|
|
updateCharset(printer);
|
|
|
|
updateFont(printer);
|
|
|
|
if (!software_command)
|
|
newPage(false, printer);
|
|
|
|
// Default tabs => Each eight characters
|
|
for (unsigned int i = 0; i < 32; i++)
|
|
printer->horiztabs[i] = i * 8 * (1 / (double)printer->cpi);
|
|
printer->numHorizTabs = 32;
|
|
|
|
printer->numVertTabs = 255;
|
|
|
|
printer->last_data = 0;
|
|
}
|
|
|
|
void updateCharset(lpt_epsonprinter_t *printer) {
|
|
uint16_t *mapToUse = NULL;
|
|
uint16_t cp = printer->charTables[printer->curCharTable];
|
|
|
|
switch (cp) {
|
|
case 0:
|
|
mapToUse = (uint16_t *)&italicsMap;
|
|
break;
|
|
case 437:
|
|
mapToUse = (uint16_t *)&cp437Map;
|
|
break;
|
|
case 850:
|
|
mapToUse = (uint16_t *)&cp850Map;
|
|
break;
|
|
case 860:
|
|
mapToUse = (uint16_t *)&cp860Map;
|
|
break;
|
|
case 863:
|
|
mapToUse = (uint16_t *)&cp863Map;
|
|
break;
|
|
case 865:
|
|
mapToUse = (uint16_t *)&cp865Map;
|
|
break;
|
|
default:
|
|
pclog("Unsupported codepage %i. Using CP437 instead.\n", cp);
|
|
mapToUse = (uint16_t *)&cp437Map;
|
|
}
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
printer->curMap[i] = mapToUse[i];
|
|
|
|
// TODO: See if it works like this
|
|
printer->curMap[0x23] = intCharSets[printer->curI18NCharset][0];
|
|
printer->curMap[0x24] = intCharSets[printer->curI18NCharset][1];
|
|
printer->curMap[0x40] = intCharSets[printer->curI18NCharset][2];
|
|
printer->curMap[0x5b] = intCharSets[printer->curI18NCharset][3];
|
|
printer->curMap[0x5c] = intCharSets[printer->curI18NCharset][4];
|
|
printer->curMap[0x5d] = intCharSets[printer->curI18NCharset][5];
|
|
printer->curMap[0x5e] = intCharSets[printer->curI18NCharset][6];
|
|
printer->curMap[0x60] = intCharSets[printer->curI18NCharset][7];
|
|
printer->curMap[0x7b] = intCharSets[printer->curI18NCharset][8];
|
|
printer->curMap[0x7c] = intCharSets[printer->curI18NCharset][9];
|
|
printer->curMap[0x7d] = intCharSets[printer->curI18NCharset][10];
|
|
printer->curMap[0x7e] = intCharSets[printer->curI18NCharset][11];
|
|
}
|
|
|
|
// TODO: all these fonts are approximations! Even the FX one is not exactly equal to the LX-810 and it is 7-bit ascii only.
|
|
// TODO: maybe we are calling this unnecessarily (some parameters only affect drawing)
|
|
void updateFont(lpt_epsonprinter_t *printer) {
|
|
if (printer->curFont != NULL)
|
|
FT_Done_Face(printer->curFont);
|
|
|
|
if (printer->printQuality == QUALITY_NLQ) {
|
|
const char *fontName;
|
|
|
|
switch (printer->NLQtypeFace) {
|
|
case roman:
|
|
if (printer->style & STYLE_ITALICS)
|
|
fontName = "roman_italics.ttf";
|
|
else
|
|
fontName = "roman.ttf";
|
|
break;
|
|
case sansserif:
|
|
if (printer->style & STYLE_ITALICS)
|
|
fontName = "sansserif_italics.ttf";
|
|
else
|
|
fontName = "sansserif.ttf";
|
|
break;
|
|
default:
|
|
fatal("Unknown font: %d\n", printer->NLQtypeFace);
|
|
}
|
|
|
|
char fullpath[MAX_PATH_STRING];
|
|
strlcpy(fullpath, printer->fontpath, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName, MAX_PATH_STRING);
|
|
|
|
if (FT_New_Face(printer->FTlib, fullpath, 0, &printer->curFont)) {
|
|
fatal("Unable to load font %s\n", fullpath);
|
|
printer->curFont = NULL;
|
|
}
|
|
|
|
double horizPoints = 10.5;
|
|
double vertPoints = 10.5;
|
|
|
|
printer->actcpi = printer->cpi;
|
|
|
|
// TODO: condensed only for draft fonts?
|
|
if (printer->cpi != 10 && !(printer->style & STYLE_CONDENSED)) {
|
|
horizPoints *= (double)10 / (double)printer->cpi;
|
|
// vertPoints *= (double)10 / (double)printer->cpi;
|
|
}
|
|
|
|
if (printer->cpi == 10 && (printer->style & STYLE_CONDENSED)) {
|
|
// TODO: 17.125, 17.16 or 17.14?
|
|
printer->actcpi = 17.125;
|
|
horizPoints *= (double)10 / (double)17.125;
|
|
// vertPoints *= (double)10 / (double)17.125;
|
|
}
|
|
|
|
if (printer->cpi == 12 && (printer->style & STYLE_CONDENSED)) {
|
|
printer->actcpi = 20.0;
|
|
horizPoints *= (double)10 / (double)20.0;
|
|
// vertPoints *= (double)10 / (double)20.0;
|
|
}
|
|
|
|
if ((printer->style & STYLE_DOUBLEWIDTH) || (printer->style & STYLE_DOUBLEWIDTHONELINE)) {
|
|
printer->actcpi /= 2;
|
|
horizPoints *= (double)2.0;
|
|
}
|
|
|
|
if ((printer->style & STYLE_SUPERSCRIPT) || (printer->style & STYLE_SUBSCRIPT)) {
|
|
// TODO: aparently the horizontal size is the same?
|
|
// horizPoints *= (double)2 / (double)3;
|
|
vertPoints *= (double)2 / (double)3;
|
|
// actcpi /= (double)2 / (double)3;
|
|
}
|
|
|
|
/* TODO: emphasized and double-strike also change width? maybe not */
|
|
|
|
FT_Set_Char_Size(printer->curFont, (uint16_t)horizPoints * FREETYPE_FONT_MULTIPLIER,
|
|
(uint16_t)vertPoints * FREETYPE_FONT_MULTIPLIER, printer->dpi, printer->dpi);
|
|
} else if (printer->printQuality == QUALITY_DRAFT) // TODO: high-speed draft
|
|
{
|
|
const char *fontName_Base;
|
|
// TODO: these fonts are ALL incomplete! Need one with all codepages and all 13 international characters for each.
|
|
// Can I dump this from my LX-810 and do something?
|
|
if (printer->charTables[printer->curCharTable] == 0)
|
|
fontName_Base = "FXMatrix105Mono";
|
|
else
|
|
fontName_Base = "FXMatrix105Mono";
|
|
|
|
const char *fontName_cpi;
|
|
if (!(printer->style & STYLE_CONDENSED)) {
|
|
if (printer->cpi == 10)
|
|
fontName_cpi = "Pica";
|
|
else if (printer->cpi == 12)
|
|
fontName_cpi = "Elite";
|
|
} else {
|
|
if (printer->cpi == 10)
|
|
fontName_cpi = "Compr";
|
|
else if (printer->cpi == 12)
|
|
fontName_cpi = "Compr"; // TODO: missing
|
|
}
|
|
|
|
const char *fontName_doublewidth;
|
|
if ((printer->style & STYLE_DOUBLEWIDTH) || (printer->style & STYLE_DOUBLEWIDTHONELINE))
|
|
fontName_doublewidth = "Exp";
|
|
else
|
|
fontName_doublewidth = "";
|
|
|
|
const char *fontName_doublestrike;
|
|
if (printer->style & STYLE_DOUBLESTRIKE)
|
|
fontName_doublestrike = "Dbl";
|
|
else
|
|
fontName_doublestrike = "";
|
|
|
|
const char *fontName_underline;
|
|
if (printer->style & STYLE_UNDERLINE)
|
|
fontName_underline = "UL";
|
|
else
|
|
fontName_underline = "";
|
|
|
|
const char *fontName_subsup;
|
|
if (printer->style & STYLE_SUPERSCRIPT)
|
|
fontName_subsup = "Sup";
|
|
else if (printer->style & STYLE_SUBSCRIPT)
|
|
fontName_subsup = "Sub";
|
|
else
|
|
fontName_subsup = "";
|
|
|
|
const char *fontName_itemph;
|
|
if (printer->style & STYLE_ITALICS) {
|
|
if (printer->style & STYLE_BOLD)
|
|
fontName_itemph = "BoldItalic";
|
|
else
|
|
fontName_itemph = "Italic";
|
|
} else if (printer->style & STYLE_BOLD)
|
|
fontName_itemph = "Bold";
|
|
else
|
|
fontName_itemph = "Regular";
|
|
|
|
double horizPoints = 10.5;
|
|
double vertPoints = 10.5;
|
|
|
|
printer->actcpi = printer->cpi;
|
|
|
|
if (printer->cpi != 10 && !(printer->style & STYLE_CONDENSED)) {
|
|
horizPoints *= (double)10 / (double)printer->cpi;
|
|
// vertPoints *= (double)10/ ( double)printer->cpi;
|
|
}
|
|
|
|
if (printer->cpi == 10 && (printer->style & STYLE_CONDENSED)) {
|
|
// TODO: 17.125, 17.16 or 17.14?
|
|
printer->actcpi = 17.125;
|
|
horizPoints *= (double)10 / (double)17.125;
|
|
// vertPoints *= (double)10 / (double)17.125;
|
|
}
|
|
|
|
if (printer->cpi == 12 && (printer->style & STYLE_CONDENSED)) {
|
|
printer->actcpi = 20.0;
|
|
horizPoints *= (double)10 / (double)20.0;
|
|
// vertPoints *= (double)10 / (double)20.0;
|
|
}
|
|
|
|
if ((printer->style & STYLE_DOUBLEWIDTH) || (printer->style & STYLE_DOUBLEWIDTHONELINE)) {
|
|
printer->actcpi /= 2;
|
|
// horizPoints *= (double)2.0; // TODO: commenting this because of the font. See
|
|
// also where we adjusted cpi/points because the font already accounted for that in
|
|
// its sizes
|
|
}
|
|
|
|
if ((printer->style & STYLE_SUPERSCRIPT) || (printer->style & STYLE_SUBSCRIPT)) {
|
|
// TODO: aparently the horizontal size is the same?
|
|
// horizPoints *= (double)2/(double)3;
|
|
vertPoints *= (double)2 / (double)3;
|
|
// printer->actcpi /= (double)2/(double)3;
|
|
}
|
|
|
|
/* TODO: emphasized and double-strike also change width? maybe not */
|
|
|
|
char fullpath[MAX_PATH_STRING];
|
|
strlcpy(fullpath, printer->fontpath, MAX_PATH_STRING);
|
|
strlcat(fullpath, "fonts/", MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_Base, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_cpi, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_doublewidth, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_doublestrike, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_underline, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_subsup, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_itemph, MAX_PATH_STRING);
|
|
strlcat(fullpath, ".otf", MAX_PATH_STRING);
|
|
|
|
if (FT_New_Face(printer->FTlib, fullpath, 0, &printer->curFont)) {
|
|
pclog("Unable to load font %s, trying default\n", fullpath);
|
|
strlcpy(fullpath, printer->fontpath, MAX_PATH_STRING);
|
|
strlcat(fullpath, "fonts/", MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_Base, MAX_PATH_STRING);
|
|
strlcat(fullpath, fontName_cpi, MAX_PATH_STRING);
|
|
strlcat(fullpath, "Regular.otf", MAX_PATH_STRING);
|
|
|
|
if (FT_New_Face(printer->FTlib, fullpath, 0, &printer->curFont)) {
|
|
fatal("Unable to load font %s\n", fullpath);
|
|
printer->curFont = NULL;
|
|
}
|
|
}
|
|
|
|
FT_Set_Char_Size(printer->curFont, (uint16_t)horizPoints * FREETYPE_FONT_MULTIPLIER,
|
|
(uint16_t)vertPoints * FREETYPE_FONT_MULTIPLIER, printer->dpi, printer->dpi);
|
|
} else
|
|
fatal("Unknown print quality: %d\n", printer->printQuality);
|
|
}
|
|
|
|
bool processCommandChar(uint8_t ch, lpt_epsonprinter_t *printer) {
|
|
if (printer->ESCSeen) {
|
|
printer->ESCCmd = ch;
|
|
printer->ESCSeen = false;
|
|
printer->numParam = 0;
|
|
|
|
switch (printer->ESCCmd) {
|
|
case 0x02: // Undocumented
|
|
case 0x0e: // Select double-width printing (one line) (ESC SO)
|
|
case 0x0f: // Select condensed printing (ESC SI)
|
|
case 0x1b: // Load/Eject (effect: rewind page) THIS IS NOT AN EPSON COMMAND! (ESC ESC)
|
|
case 0x30: // Select 1/8-inch line spacing (ESC 0)
|
|
case 0x31: // Select 7/72-inch line spacing (ESC 1)
|
|
case 0x32: // Select 1/6-inch line spacing (ESC 2)
|
|
case 0x34: // Select italic font (ESC 4)
|
|
case 0x35: // Cancel italic font (ESC 5)
|
|
case 0x36: // Enable printing of upper control codes (ESC 6)
|
|
case 0x37: // Enable upper control codes (ESC 7)
|
|
case 0x38: // Disable paper out detection (ESC 8)
|
|
case 0x39: // Enable paper out detection (ESC 9)
|
|
case 0x3c: // Unidirectional mode (one line) (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 0x73: // Select low-speed mode (ESC s)
|
|
printer->neededParam = 0;
|
|
break;
|
|
case 0x19: // Control paper loading/ejecting (ESC EM)
|
|
case 0x21: // Master select (ESC !)
|
|
case 0x2d: // Turn underline on/off (ESC -)
|
|
case 0x2f: // Select vertical tab channel (ESC /)
|
|
case 0x33: // Set n/216-inch line spacing (ESC 3)
|
|
case 0x41: // Set n/72-inch line spacing
|
|
case 0x43: // Set page length in lines (ESC C)
|
|
case 0x49: // Enable printing of control codes (ESC I n)
|
|
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 0x74: // Select character table (ESC t)
|
|
case 0x78: // Select NLQ or draft (ESC x)
|
|
printer->neededParam = 1;
|
|
break;
|
|
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)
|
|
printer->neededParam = 2;
|
|
break;
|
|
case 0x2a: // Select bit image (ESC *)
|
|
printer->neededParam = 3;
|
|
break;
|
|
case 0x62: // Set vertical tabs in VFU channels (ESC b)
|
|
case 0x42: // Set vertical tabs (ESC B)
|
|
printer->numVertTabs = 0;
|
|
return true;
|
|
case 0x44: // Set horizontal tabs (ESC D)
|
|
printer->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 :)
|
|
fatal("User-defined characters not supported!\n");
|
|
return true;
|
|
case 0x28: // Two bytes sequence
|
|
return true;
|
|
default:
|
|
fatal("PRINTER: Unknown command ESC %c (%02X). Unable to skip parameters.\n", printer->ESCCmd,
|
|
printer->ESCCmd);
|
|
printer->neededParam = 0;
|
|
printer->ESCCmd = 0;
|
|
return true;
|
|
}
|
|
|
|
if (printer->neededParam > 0)
|
|
return true;
|
|
}
|
|
|
|
// Ignore VFU channel setting
|
|
if (printer->ESCCmd == 0x62) {
|
|
// TODO: do not ignore this and ESC /?
|
|
printer->ESCCmd = 0x42;
|
|
return true;
|
|
}
|
|
|
|
// Collect vertical tabs
|
|
if (printer->ESCCmd == 0x42) {
|
|
if (ch == 0 || (printer->numVertTabs > 0 &&
|
|
printer->verttabs[printer->numVertTabs - 1] > (double)ch * printer->lineSpacing)) // Done
|
|
printer->ESCCmd = 0;
|
|
else if (printer->numVertTabs < 16)
|
|
printer->verttabs[printer->numVertTabs++] = (double)ch * printer->lineSpacing;
|
|
}
|
|
|
|
// Collect horizontal tabs
|
|
if (printer->ESCCmd == 0x44) {
|
|
if (ch == 0 || (printer->numHorizTabs > 0 &&
|
|
printer->horiztabs[printer->numHorizTabs - 1] > (double)ch * (1 / (double)printer->cpi))) // Done
|
|
printer->ESCCmd = 0;
|
|
else if (printer->numHorizTabs < 32)
|
|
printer->horiztabs[printer->numHorizTabs++] = (double)ch * (1 / (double)printer->cpi);
|
|
}
|
|
|
|
if (printer->numParam < printer->neededParam) {
|
|
printer->params[printer->numParam++] = ch;
|
|
|
|
if (printer->numParam < printer->neededParam)
|
|
return true;
|
|
}
|
|
|
|
if (printer->ESCCmd != 0) {
|
|
switch (printer->ESCCmd) {
|
|
case 0x02: // Undocumented
|
|
// Ignore
|
|
break;
|
|
case 0x0e: // Select double-width printing (one line) (ESC SO)
|
|
printer->style |= STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x0f: // Select condensed printing (ESC SI)
|
|
printer->style |= STYLE_CONDENSED;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x19: // Control paper loading/ejecting (ESC EM)
|
|
// We are not really loading paper, so most commands can be ignored
|
|
if (printer->params[0] == 'R')
|
|
newPage(true, printer);
|
|
break;
|
|
case 0x1b: // Load/Eject (effect: rewind page) THIS IS NOT AN EPSON COMMAND! (ESC ESC)
|
|
// TODO: check if this is the true behaviour when having continuous paper.
|
|
if (printer->style & STYLE_DOUBLEWIDTHONELINE) {
|
|
printer->style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
}
|
|
printer->curX = printer->leftMargin;
|
|
printer->curY = printer->topMargin;
|
|
break;
|
|
case 0x21: // Master select (ESC !)
|
|
printer->cpi = printer->params[0] & 0x01 ? 12 : 10;
|
|
|
|
// Reset first seven bits
|
|
printer->style &= 0xFF80;
|
|
if (printer->params[0] & 0x04)
|
|
printer->style |= STYLE_CONDENSED;
|
|
if (printer->params[0] & 0x08)
|
|
printer->style |= STYLE_BOLD;
|
|
if (printer->params[0] & 0x10)
|
|
printer->style |= STYLE_DOUBLESTRIKE;
|
|
if (printer->params[0] & 0x20)
|
|
printer->style |= STYLE_DOUBLEWIDTH;
|
|
if (printer->params[0] & 0x40)
|
|
printer->style |= STYLE_ITALICS;
|
|
if (printer->params[0] & 0x80)
|
|
printer->style |= STYLE_UNDERLINE;
|
|
|
|
updateFont(printer);
|
|
break;
|
|
case 0x2a: // Select bit image (ESC *)
|
|
setupBitImage(printer->params[0], PARAM16(1), printer);
|
|
break;
|
|
case 0x2d: // Turn underline on/off (ESC -)
|
|
if (printer->params[0] == 0 || printer->params[0] == 48)
|
|
printer->style &= 0xFFFF - STYLE_UNDERLINE;
|
|
if (printer->params[0] == 1 || printer->params[0] == 49)
|
|
printer->style |= STYLE_UNDERLINE;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x2f: // Select vertical tab channel (ESC /)
|
|
// Ignore
|
|
break;
|
|
case 0x30: // Select 1/8-inch line spacing (ESC 0)
|
|
printer->lineSpacing = (double)1 / 8;
|
|
break;
|
|
case 0x31: // Select 7/72-inch line spacing (ESC 1)
|
|
printer->lineSpacing = (double)7 / 72;
|
|
break;
|
|
case 0x32: // Select 1/6-inch line spacing (ESC 2)
|
|
printer->lineSpacing = (double)1 / 6;
|
|
break;
|
|
case 0x33: // Set n/216-inch line spacing (ESC 3)
|
|
printer->lineSpacing = (double)printer->params[0] / 216;
|
|
break;
|
|
case 0x34: // Select italic font (ESC 4)
|
|
printer->style |= STYLE_ITALICS;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x35: // Cancel italic font (ESC 5)
|
|
printer->style &= 0xFFFF - STYLE_ITALICS;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x36: // Enable printing of upper control codes (ESC 6)
|
|
printer->printUpperContr = true;
|
|
break;
|
|
case 0x37: // Enable upper control codes (ESC 7)
|
|
printer->printUpperContr = false;
|
|
break;
|
|
case 0x38: // Disable paper out detection (ESC 8)
|
|
// We have infinite paper, so just ignore this
|
|
break;
|
|
case 0x39: // Enable paper out detection (ESC 9)
|
|
// We have infinite paper, so just ignore this
|
|
break;
|
|
case 0x3c: // Unidirectional mode (one line) (ESC <)
|
|
// We don't have a print head, so just ignore this
|
|
break;
|
|
case 0x3f: // Reassign bit-image mode (ESC ?)
|
|
if (printer->params[0] == 75)
|
|
printer->densk = printer->params[1];
|
|
if (printer->params[0] == 76)
|
|
printer->densl = printer->params[1];
|
|
if (printer->params[0] == 89) // TODO: this option here is not present on the LX-810? (Even though the
|
|
// default mode for Y exists)
|
|
printer->densy = printer->params[1];
|
|
if (printer->params[0] == 90)
|
|
printer->densz = printer->params[1];
|
|
break;
|
|
case 0x40: // Initialize printer (ESC @) TODO: one more command that should clear the current line buffer
|
|
resetPrinter(printer, true);
|
|
break;
|
|
case 0x41: // Set n/72-inch line spacing
|
|
printer->lineSpacing = (double)printer->params[0] / 72;
|
|
break;
|
|
case 0x43: // Set page length in lines (ESC C)
|
|
if (printer->params[0] != 0)
|
|
printer->pageHeight = printer->bottomMargin =
|
|
(double)printer->params[0] *
|
|
printer->lineSpacing; // TODO: is this right? Also, we have ALREADY allocated the SDL
|
|
// surface! This can result in memory corruption
|
|
else // == 0 => Set page length in inches
|
|
{
|
|
printer->neededParam = 1;
|
|
printer->numParam = 0;
|
|
printer->ESCCmd = 0x100;
|
|
return true;
|
|
}
|
|
break;
|
|
case 0x45: // Select bold font (ESC E)
|
|
printer->style |= STYLE_BOLD;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x46: // Cancel bold font (ESC F)
|
|
printer->style &= 0xFFFF - STYLE_BOLD;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x47: // Select dobule-strike printing (ESC G)
|
|
printer->style |= STYLE_DOUBLESTRIKE;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x48: // Cancel double-strike printing (ESC H)
|
|
printer->style &= 0xFFFF - STYLE_DOUBLESTRIKE;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x49: // Enable printing of control codes (ESC I n)
|
|
// TODO: which program/driver did this? Is it undocumented but SHOULD WORK on the LX-810?
|
|
pclog("EPSON ESC I: printing of control codes not implemented on the LX-810!\n");
|
|
break;
|
|
case 0x4a: // Advance print position vertically (ESC J n)
|
|
printer->curY +=
|
|
(double)((double)printer->params[0] /
|
|
216); // TODO: this command produces an immediate line feed without carriage return?
|
|
if (printer->curY > printer->bottomMargin)
|
|
newPage(true, printer);
|
|
break;
|
|
case 0x4b: // Select 60-dpi graphics (ESC K)
|
|
setupBitImage(printer->densk, PARAM16(0), printer);
|
|
break;
|
|
case 0x4c: // Select 120-dpi graphics (ESC L)
|
|
setupBitImage(printer->densl, PARAM16(0), printer);
|
|
break;
|
|
case 0x4d: // Select 10.5-point, 12-cpi (ESC M)
|
|
printer->cpi = 12;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x4e: // Set bottom margin (ESC N) // TODO: does this work like this in the LX-810?
|
|
printer->topMargin = 0.0;
|
|
printer->bottomMargin = (double)printer->params[0] * printer->lineSpacing;
|
|
break;
|
|
case 0x4f: // Cancel bottom (and top) margin // TODO: does this work like this in the LX-810?
|
|
printer->topMargin = 0.0;
|
|
printer->bottomMargin = printer->pageHeight;
|
|
break;
|
|
case 0x50: // Select 10.5-point, 10-cpi (ESC P)
|
|
printer->cpi = 10;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x51: // Set right margin
|
|
// TODO: clear previous tab settings and all characters in the current line? Minimum space between the
|
|
// margins?
|
|
printer->rightMargin = (double)(printer->params[0] - 1.0) / (double)printer->cpi;
|
|
break;
|
|
case 0x52: // Select an international character set (ESC R)
|
|
if (printer->params[0] <= 12) {
|
|
printer->curI18NCharset = printer->params[0];
|
|
updateCharset(printer);
|
|
}
|
|
break;
|
|
case 0x53: // Select superscript/subscript printing (ESC S)
|
|
printer->style &= 0xFFFF - STYLE_SUPERSCRIPT - STYLE_SUBSCRIPT;
|
|
if (printer->params[0] == 0 || printer->params[0] == 48)
|
|
printer->style |= STYLE_SUPERSCRIPT;
|
|
if (printer->params[0] == 1 || printer->params[1] == 49)
|
|
printer->style |= STYLE_SUBSCRIPT;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x54: // Cancel superscript/subscript printing (ESC T)
|
|
printer->style &= 0xFFFF - STYLE_SUPERSCRIPT - STYLE_SUBSCRIPT;
|
|
updateFont(printer);
|
|
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)
|
|
// TODO: see if this is right
|
|
if (printer->params[0] == 0 || printer->params[0] == 48)
|
|
printer->style &= 0xFFFF - STYLE_DOUBLEWIDTH;
|
|
if (printer->params[0] == 1 || printer->params[0] == 49)
|
|
printer->style |= STYLE_DOUBLEWIDTH;
|
|
updateFont(printer);
|
|
break;
|
|
case 0x59: // Select 120-dpi, double-speed graphics (ESC Y)
|
|
setupBitImage(printer->densy, PARAM16(0), printer);
|
|
break;
|
|
case 0x5a: // Select 240-dpi graphics (ESC Z)
|
|
setupBitImage(printer->densz, PARAM16(0), printer);
|
|
break;
|
|
case 0x61: // Select justification (ESC a)
|
|
// TODO: do this?
|
|
break;
|
|
case 0x6b: // Select typeface (ESC k)
|
|
if (printer->params[0] <= 11 || printer->params[0] == 30 || printer->params[0] == 31)
|
|
printer->NLQtypeFace = (Typeface)printer->params[0];
|
|
updateFont(printer);
|
|
break;
|
|
case 0x6c: // Set left margin (ESC l)
|
|
// TODO: clear previous tab settings and all characters in the current line? Minimum space between the
|
|
// margins?
|
|
printer->leftMargin = (double)(printer->params[0] - 1.0) / (double)printer->cpi;
|
|
if (printer->curX < printer->leftMargin)
|
|
printer->curX = printer->leftMargin;
|
|
break;
|
|
case 0x70: // Turn proportional mode on/off (ESC p)
|
|
// TODO: Win3.1 driver did this. Is it undocumented but SHOULD WORK on the LX-810?
|
|
pclog("EPSON ESC p: proportional mode not supported by the LX-810!\n");
|
|
break;
|
|
case 0x73: // Select low-speed mode (ESC s)
|
|
// Ignore
|
|
break;
|
|
case 0x74: // Select character table (ESC t)
|
|
if (printer->params[0] < 2)
|
|
printer->curCharTable = printer->params[0];
|
|
updateCharset(printer);
|
|
updateFont(printer);
|
|
break;
|
|
case 0x78: // Select NLQ or draft (ESC x)
|
|
if (printer->params[0] == 0 || printer->params[0] == 48)
|
|
printer->printQuality = QUALITY_DRAFT;
|
|
if (printer->params[0] == 1 || printer->params[0] == 49)
|
|
printer->printQuality = QUALITY_NLQ;
|
|
break;
|
|
case 0x100: // Set page length in inches (ESC C NUL)
|
|
printer->pageHeight = (double)printer->params[0];
|
|
printer->bottomMargin = printer->pageHeight; // TODO: is this right?
|
|
printer->topMargin = 0.0;
|
|
break;
|
|
default:
|
|
pclog("PRINTER: Skipped unsupported command ESC %c (%02X)\n", printer->ESCCmd, printer->ESCCmd);
|
|
}
|
|
|
|
printer->ESCCmd = 0;
|
|
return true;
|
|
}
|
|
|
|
// TODO: Set Tab Increments (ESC e)
|
|
// TODO: Skip (ESC f)
|
|
// TODO: Select 9-Pin Graphics Mode (ESC ^) (has two bytes per column)
|
|
|
|
int control_code_shifted = false;
|
|
if (!printer->printUpperContr) {
|
|
if ((ch >= 0x80 && ch <= 0x9f) || ch == 0xff)
|
|
ch -= 0x80;
|
|
control_code_shifted = true;
|
|
}
|
|
|
|
switch (ch) {
|
|
case 0x07: // Beeper (BEL)
|
|
// BEEEP!
|
|
pclog("PRINTER: BEEP!\n");
|
|
return true;
|
|
case 0x08: // Backspace (BS)
|
|
{
|
|
// TODO: seems more complex than this
|
|
double newX = printer->curX - (1 / (double)printer->actcpi);
|
|
if (newX >= printer->leftMargin)
|
|
printer->curX = newX;
|
|
}
|
|
return true;
|
|
case 0x09: // Tab horizontally (HT)
|
|
{
|
|
// Find tab right to current pos
|
|
double moveTo = -1;
|
|
for (uint8_t i = 0; i < printer->numHorizTabs; i++)
|
|
if (printer->horiztabs[i] > printer->curX)
|
|
moveTo = printer->horiztabs[i];
|
|
// Nothing found => Ignore
|
|
if (moveTo > 0 && moveTo < printer->rightMargin)
|
|
printer->curX = moveTo;
|
|
}
|
|
return true;
|
|
case 0x0b: // Tab vertically (VT)
|
|
if (printer->numVertTabs == 0) // All tabs cancelled => Act like CR
|
|
printer->curX = printer->leftMargin;
|
|
else if (printer->numVertTabs == 255) // No tabs set since reset => Act like LF
|
|
{
|
|
printer->curX = printer->leftMargin;
|
|
printer->curY += printer->lineSpacing;
|
|
if (printer->curY > printer->bottomMargin)
|
|
newPage(true, printer);
|
|
} else {
|
|
// Find tab below current pos
|
|
double moveTo = -1;
|
|
for (uint8_t i = 0; i < printer->numVertTabs; i++)
|
|
if (printer->verttabs[i] > printer->curY)
|
|
moveTo = printer->verttabs[i];
|
|
|
|
// Nothing found => Act like FF
|
|
if (moveTo > printer->bottomMargin || moveTo < 0)
|
|
newPage(true, printer);
|
|
else
|
|
printer->curY = moveTo;
|
|
}
|
|
if (printer->style & STYLE_DOUBLEWIDTHONELINE) {
|
|
printer->style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
}
|
|
return true;
|
|
case 0x0c: // Form feed (FF)
|
|
if (printer->style & STYLE_DOUBLEWIDTHONELINE) {
|
|
printer->style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
}
|
|
newPage(true, printer);
|
|
return true;
|
|
case 0x0d: // Carriage Return (CR)
|
|
printer->curX = printer->leftMargin;
|
|
if (!printer->autoFeed)
|
|
return true;
|
|
case 0x0a: // Line feed
|
|
if (printer->style & STYLE_DOUBLEWIDTHONELINE) {
|
|
printer->style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
}
|
|
printer->curX = printer->leftMargin; // TODO: \n does this too or just \r?
|
|
printer->curY += printer->lineSpacing;
|
|
|
|
if (printer->curY > printer->bottomMargin)
|
|
newPage(true, printer);
|
|
return true;
|
|
case 0x0e: // Select double-width printing (one line) (SO)
|
|
printer->style |= STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
return true;
|
|
case 0x0f: // Select condensed printing (SI)
|
|
printer->style |= STYLE_CONDENSED;
|
|
updateFont(printer);
|
|
return true;
|
|
case 0x11: // Select printer (DC1)
|
|
// Ignore
|
|
return true;
|
|
case 0x12: // Cancel condensed printing (DC2)
|
|
printer->style &= 0xFFFF - STYLE_CONDENSED;
|
|
updateFont(printer);
|
|
return true;
|
|
case 0x13: // Deselect printer (DC3)
|
|
// Ignore
|
|
return true;
|
|
case 0x14: // Cancel double-width printing (one line) (DC4)
|
|
printer->style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
|
|
updateFont(printer);
|
|
return true;
|
|
case 0x18: // Cancel line (CAN)
|
|
return true; // TODO: is this used? should we cancel the line being printed?
|
|
case 0x1b: // ESC
|
|
printer->ESCSeen = true;
|
|
return true;
|
|
case 0x7f: // Delete character (DEL)
|
|
return true; // TODO: is this used? should we cancel the character being printed?
|
|
default:
|
|
if (control_code_shifted) {
|
|
// TODO: see how this should really happen when the control code isn't processed (there are codepages with
|
|
// printable symbols < 0x20)
|
|
ch += 0x80;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void newPage(bool save, lpt_epsonprinter_t *printer) {
|
|
if (save)
|
|
outputPage(printer);
|
|
|
|
printer->curY = printer->topMargin;
|
|
|
|
SDL_Rect rect;
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.w = printer->page->w;
|
|
rect.h = printer->page->h;
|
|
SDL_FillRect(printer->page, &rect, SDL_MapRGB(printer->page->format, 255, 255, 255));
|
|
}
|
|
|
|
void printChar(lpt_epsonprinter_t *printer) {
|
|
uint8_t ch = printer->last_data;
|
|
printer->charRead = true;
|
|
if (printer->page == NULL)
|
|
return;
|
|
|
|
// Are we currently printing a bit graphic?
|
|
if (printer->bitGraph.remBytes > 0) {
|
|
printBitGraph(ch, printer);
|
|
return;
|
|
}
|
|
|
|
// See if it was a command
|
|
if (processCommandChar(ch, printer))
|
|
return;
|
|
|
|
// Do not print if no font is available
|
|
if (!printer->curFont)
|
|
return;
|
|
if (ch == 0)
|
|
ch = 0x20;
|
|
|
|
// TODO: real italics for the italics charset, where the upper half is (mostly) the lower half italicized.
|
|
int italicize = false;
|
|
if (!(printer->style & STYLE_ITALICS) && printer->charTables[printer->curCharTable] == 0 &&
|
|
ch > 0x7f) // prevent double italics (they are present on the font)
|
|
{
|
|
italicize = true;
|
|
}
|
|
|
|
if (italicize) {
|
|
FT_Matrix matrix;
|
|
matrix.xx = 0x10000L;
|
|
matrix.xy = (FT_Fixed)(0.20 * 0x10000L);
|
|
matrix.yx = 0;
|
|
matrix.yy = 0x10000L;
|
|
FT_Set_Transform(printer->curFont, &matrix, NULL);
|
|
} else
|
|
FT_Set_Transform(printer->curFont, NULL, NULL);
|
|
|
|
// Find the glyph for the char to render
|
|
FT_UInt index = FT_Get_Char_Index(printer->curFont, printer->curMap[ch]);
|
|
|
|
// Load the glyph
|
|
FT_Load_Glyph(printer->curFont, index, FT_LOAD_DEFAULT);
|
|
|
|
// Render a high-quality bitmap
|
|
FT_Render_Glyph(printer->curFont->glyph, FT_RENDER_MODE_NORMAL);
|
|
|
|
// TODO: some fonts have negative left and/or top and as of this writing we don't do printable areas! So do this hack on
|
|
// unsigned ints...
|
|
uint16_t penX = CURX_INCH_TO_PIXEL_ADD(fmax(0.0, printer->curFont->glyph->bitmap_left));
|
|
uint16_t penY = CURY_INCH_TO_PIXEL_ADD(fmax(
|
|
0.0, -printer->curFont->glyph->bitmap_top + printer->curFont->size->metrics.ascender / FREETYPE_FONT_MULTIPLIER));
|
|
// handle proportional fonts
|
|
penX += (((double)printer->dpi / printer->actcpi) - printer->curFont->glyph->bitmap.width) / 2;
|
|
|
|
if (printer->style & STYLE_SUBSCRIPT) // TODO: this and other placementes are guesswork
|
|
penY += printer->curFont->glyph->metrics.vertAdvance / FREETYPE_FONT_MULTIPLIER * (double)1 / (double)3;
|
|
|
|
// Copy bitmap into page
|
|
SDL_LockSurface(printer->page);
|
|
|
|
blitGlyph(printer->curFont->glyph->bitmap, penX, penY, false, printer);
|
|
|
|
// Doublestrike => Print the glyph a second time one pixel below
|
|
if (printer->printQuality != QUALITY_DRAFT) // TODO: have the font variations for the NLQ fonts too
|
|
if (printer->style & STYLE_DOUBLESTRIKE)
|
|
blitGlyph(printer->curFont->glyph->bitmap, penX, penY + 1, true, printer);
|
|
|
|
// Bold => Print the glyph a second time one pixel to the right
|
|
if (printer->printQuality != QUALITY_DRAFT) // TODO: have the font variations for the NLQ fonts too
|
|
if (printer->style & STYLE_BOLD)
|
|
blitGlyph(printer->curFont->glyph->bitmap, penX + 1, penY, true, printer);
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
|
|
// For line printing
|
|
uint16_t lineStart = CURX_INCH_TO_PIXEL;
|
|
|
|
printer->curX += 1 / (double)printer->actcpi;
|
|
|
|
// Draw lines if desired
|
|
if (printer->printQuality != QUALITY_DRAFT) // TODO: have the font variations for the NLQ fonts too
|
|
if (printer->style & STYLE_UNDERLINE) {
|
|
// Find out where to put the line
|
|
uint16_t lineY =
|
|
CURY_INCH_TO_PIXEL_ADD(printer->curFont->size->metrics.height / FREETYPE_FONT_MULTIPLIER);
|
|
|
|
// TODO: use previous CURX_INCH_TO_PIXEL or penX here?
|
|
drawLine(lineStart, CURX_INCH_TO_PIXEL, lineY, printer);
|
|
}
|
|
}
|
|
|
|
void blitGlyph(FT_Bitmap bitmap, uint16_t destx, uint16_t desty, bool add, lpt_epsonprinter_t *printer) {
|
|
for (unsigned int y = 0; y < bitmap.rows; y++) {
|
|
for (unsigned int x = 0; x < bitmap.width; x++) {
|
|
// Read pixel from glyph bitmap
|
|
uint8_t *source = bitmap.buffer + x + y * bitmap.pitch;
|
|
|
|
// Ignore background and don't go over the border
|
|
// TODO: This should not be linear!
|
|
if (*source != 0 && (destx + x < (unsigned int)printer->page->w) &&
|
|
(desty + y < (unsigned int)printer->page->h)) {
|
|
uint8_t *target =
|
|
(uint8_t *)printer->page->pixels + (x + destx) + (y + desty) * printer->page->pitch;
|
|
if (add) {
|
|
if (*target + *source > 255)
|
|
*target = 255;
|
|
else
|
|
*target += *source;
|
|
} else
|
|
*target = *source;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawLine(uint16_t fromx, uint16_t tox, uint16_t y, lpt_epsonprinter_t *printer) {
|
|
SDL_LockSurface(printer->page);
|
|
|
|
// Draw anti-aliased line
|
|
for (unsigned int x = fromx; x <= tox; x++) {
|
|
// Skip parts if going over the border
|
|
if (x < (unsigned int)printer->page->w) {
|
|
if (y > 0 && (y - 1) < printer->page->h)
|
|
*((uint8_t *)printer->page->pixels + x + (y - 1) * printer->page->pitch) = 120;
|
|
if (y < printer->page->h)
|
|
*((uint8_t *)printer->page->pixels + x + y * printer->page->pitch) = 255;
|
|
if (y + 1 < printer->page->h)
|
|
*((uint8_t *)printer->page->pixels + x + (y + 1) * printer->page->pitch) = 120;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
}
|
|
|
|
void setAutofeed(bool feed, lpt_epsonprinter_t *printer) { printer->autoFeed = feed; }
|
|
|
|
bool getAutofeed(lpt_epsonprinter_t *printer) { return printer->autoFeed; }
|
|
|
|
bool isBusy(lpt_epsonprinter_t *printer) {
|
|
// We're never busy
|
|
return false;
|
|
}
|
|
|
|
bool ack(lpt_epsonprinter_t *printer) {
|
|
// Acknowledge last char read
|
|
return printer->charRead;
|
|
}
|
|
|
|
// TODO: see if densities are OK
|
|
// TODO: 240x144 setting under windows 95 looks bad when printing bitmap fonts because of the "no adjancent dots" rule? Shouldn't
|
|
// it look a little better?
|
|
void setupBitImage(uint8_t dens, uint16_t numCols, lpt_epsonprinter_t *printer) {
|
|
switch (dens) {
|
|
case 0: // Single-density
|
|
printer->bitGraph.horizDens = 60;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = true;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
case 1: // Double-density
|
|
printer->bitGraph.horizDens = 120;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = true;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
case 2: // High-speed double-density
|
|
printer->bitGraph.horizDens = 120;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = false;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
case 3: // Quadruple-density
|
|
printer->bitGraph.horizDens = 240;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = false;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
case 4: // CRT I
|
|
printer->bitGraph.horizDens = 80;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = true;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
case 5: // Plotter (1:1
|
|
printer->bitGraph.horizDens = 72;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = true;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
case 6: // CRT II
|
|
printer->bitGraph.horizDens = 90;
|
|
printer->bitGraph.vertDens = 72;
|
|
printer->bitGraph.adjacent = true;
|
|
printer->bitGraph.bytesColumn = 1;
|
|
break;
|
|
default:
|
|
fatal("PRINTER: Unsupported bit image density %i\n", dens);
|
|
}
|
|
|
|
printer->bitGraph.remBytes = numCols * printer->bitGraph.bytesColumn;
|
|
printer->bitGraph.readBytesColumn = 0;
|
|
}
|
|
|
|
// TODO: anti-alias because of high dpi, also be mindful that additions when superimposing pin strikes are not linear
|
|
// TODO: use this for printing when we have the character roms
|
|
void printBitGraph(uint8_t ch, lpt_epsonprinter_t *printer) {
|
|
printer->bitGraph.column[printer->bitGraph.readBytesColumn++] = ch;
|
|
printer->bitGraph.remBytes--;
|
|
|
|
// Only print after reading a full column
|
|
if (printer->bitGraph.readBytesColumn < printer->bitGraph.bytesColumn)
|
|
return;
|
|
|
|
double oldY = printer->curY;
|
|
|
|
SDL_LockSurface(printer->page);
|
|
|
|
// TODO: other rasterizations where we should do start->end instead of start + steps
|
|
double pixsizeX;
|
|
double pixsizeY;
|
|
if (printer->emulatePinsForBitmaps) {
|
|
pixsizeX = 0.01141732283464566929 * printer->dpi;
|
|
pixsizeY = 0.01141732283464566929 * printer->dpi;
|
|
} else {
|
|
pixsizeX = printer->dpi / printer->bitGraph.horizDens > 0 ? printer->dpi / printer->bitGraph.horizDens : 1;
|
|
pixsizeY = printer->dpi / printer->bitGraph.vertDens > 0 ? printer->dpi / printer->bitGraph.vertDens : 1;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < printer->bitGraph.bytesColumn; i++) {
|
|
for (int j = 7; j >= 0; j--) {
|
|
uint8_t pixel = (printer->bitGraph.column[i] >> j) & 0x01;
|
|
|
|
if (pixel != 0) {
|
|
int startx;
|
|
int endx;
|
|
int starty;
|
|
int endy;
|
|
|
|
if (printer->emulatePinsForBitmaps) {
|
|
startx = CURX_INCH_TO_PIXEL_ADD(-pixsizeX);
|
|
endx = CURX_INCH_TO_PIXEL_ADD(pixsizeX * 2);
|
|
starty = CURY_INCH_TO_PIXEL_ADD(-pixsizeY);
|
|
endy = CURY_INCH_TO_PIXEL_ADD(pixsizeY * 2);
|
|
} else {
|
|
startx = CURX_INCH_TO_PIXEL;
|
|
endx = CURX_INCH_TO_PIXEL_ADD(pixsizeX);
|
|
starty = CURY_INCH_TO_PIXEL;
|
|
endy = CURY_INCH_TO_PIXEL_ADD(pixsizeY);
|
|
}
|
|
|
|
for (int xx = startx; xx < endx; xx++)
|
|
for (int yy = starty; yy < endy; yy++)
|
|
if ((xx < printer->page->w) && (yy < printer->page->h)) {
|
|
if (printer->emulatePinsForBitmaps) {
|
|
double middlex = CURX_INCH_TO_PIXEL_ADD(pixsizeX / 2);
|
|
double middley = CURY_INCH_TO_PIXEL_ADD(pixsizeY / 2);
|
|
double dist = sqrt((xx - middlex) * (xx - middlex) +
|
|
(yy - middley) * (yy - middley));
|
|
double radius = pixsizeY / 2;
|
|
double ratio = dist / radius;
|
|
// pin diameter == 0.29mm == 0.01141732283464566929 inches. 0.35mm
|
|
// or 1/72 inch between pins.
|
|
uint8_t source = (ratio <= 1.0) ? (255 * (1 - ratio * ratio)) : 0;
|
|
// uint8_t source = (ratio <= 1.0) ? (255 * (1 - ratio)) : 0;
|
|
// uint8_t source = (ratio <= 1.0) ? 255 : 0;
|
|
uint8_t *target = (uint8_t *)printer->page->pixels + xx +
|
|
yy * printer->page->pitch;
|
|
if ((int)*target + (int)source > 255)
|
|
*target = 255;
|
|
else
|
|
*target += source;
|
|
} else
|
|
*((uint8_t *)printer->page->pixels + xx +
|
|
yy * printer->page->pitch) = 255;
|
|
}
|
|
}
|
|
|
|
printer->curY += (double)1 / (double)printer->bitGraph.vertDens;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
|
|
printer->curY = oldY;
|
|
|
|
printer->bitGraph.readBytesColumn = 0;
|
|
|
|
// Advance the cursor
|
|
printer->curX += (double)1 / (double)printer->bitGraph.horizDens;
|
|
}
|
|
|
|
void formFeed(lpt_epsonprinter_t *printer) {
|
|
// Don't output blank pages
|
|
newPage(!isBlank(printer), printer);
|
|
|
|
finishMultipage(printer);
|
|
}
|
|
|
|
static void findNextName(const char *front, const char *ext, char *filename, size_t filename_size, const char *base_path) {
|
|
char buffer[10];
|
|
uint16_t num = 1;
|
|
FILE *test = NULL;
|
|
do {
|
|
snprintf(&buffer[0], 10, "%d", num++);
|
|
strlcpy(filename, base_path, filename_size);
|
|
strlcat(filename, front, filename_size);
|
|
strlcat(filename, buffer, filename_size);
|
|
strlcat(filename, ext, filename_size);
|
|
|
|
test = fopen(filename, "rb");
|
|
if (test != NULL)
|
|
fclose(test);
|
|
if (num == 999999999)
|
|
fatal("Too many printed pages! Please make space!\n");
|
|
} while (test != NULL);
|
|
}
|
|
|
|
void outputPage(lpt_epsonprinter_t *printer) {
|
|
char filename[MAX_PATH_STRING];
|
|
|
|
if (printer->output_type == OUTPUT_TYPE_FORWARD_TO_REAL_PRINTER) {
|
|
#if defined(WIN32)
|
|
// You'll need the mouse for the print dialog
|
|
if (mouselocked)
|
|
CaptureMouse();
|
|
|
|
uint16_t physW = GetDeviceCaps(printer->printerDC, PHYSICALWIDTH);
|
|
uint16_t physH = GetDeviceCaps(printer->printerDC, PHYSICALHEIGHT);
|
|
|
|
double scaleW, scaleH;
|
|
|
|
if (printer->page->w > physW)
|
|
scaleW = (double)printer->page->w / (double)physW;
|
|
else
|
|
scaleW = (double)physW / (double)printer->page->w;
|
|
|
|
if (printer->page->h > physH)
|
|
scaleH = (double)printer->page->h / (double)physH;
|
|
else
|
|
scaleH = (double)physH / (double)printer->page->h;
|
|
|
|
HDC memHDC = CreateCompatibleDC(printer->printerDC);
|
|
HBITMAP bitmap = CreateCompatibleBitmap(memHDC, printer->page->w, printer->page->h);
|
|
SelectObject(memHDC, bitmap);
|
|
|
|
// Start new printer job?
|
|
if (printer->outputHandle == NULL) {
|
|
DOCINFO docinfo;
|
|
docinfo.cbSize = sizeof(docinfo);
|
|
docinfo.lpszDocName = "PCem Printer";
|
|
docinfo.lpszOutput = NULL;
|
|
docinfo.lpszDatatype = NULL;
|
|
docinfo.fwType = 0;
|
|
|
|
StartDoc(printer->printerDC, &docinfo);
|
|
printer->multiPageCounter = 1;
|
|
}
|
|
|
|
StartPage(printer->printerDC);
|
|
SDL_LockSurface(printer->page);
|
|
|
|
SDL_Palette *sdlpal = printer->page->format->palette;
|
|
|
|
for (uint16_t y = 0; y < printer->page->h; y++) {
|
|
for (uint16_t x = 0; x < printer->page->w; x++) {
|
|
uint8_t pixel = *((uint8_t *)printer->page->pixels + x + (y * printer->page->pitch));
|
|
uint32_t color = 0;
|
|
color |= sdlpal->colors[pixel].r;
|
|
color |= ((uint32_t)sdlpal->colors[pixel].g) << 8;
|
|
color |= ((uint32_t)sdlpal->colors[pixel].b) << 16;
|
|
SetPixel(memHDC, x, y, color);
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
|
|
StretchBlt(printer->printerDC, 0, 0, physW, physH, memHDC, 0, 0, printer->page->w, printer->page->h, SRCCOPY);
|
|
|
|
EndPage(printer->printerDC);
|
|
|
|
if (printer->multipageOutput) {
|
|
printer->multiPageCounter++;
|
|
printer->outputHandle = printer->printerDC;
|
|
} else {
|
|
EndDoc(printer->printerDC);
|
|
printer->outputHandle = NULL;
|
|
}
|
|
|
|
DeleteDC(memHDC);
|
|
#else
|
|
fatal("PRINTER: Direct printing not supported under this OS\n");
|
|
#endif
|
|
} else if (printer->output_type == OUTPUT_TYPE_POSTSCRIPT) {
|
|
// TODO: this was working even when the last entry on the palette was undefined - see if there is any workaround
|
|
// in this code
|
|
FILE *psfile = NULL;
|
|
|
|
// Continue postscript file?
|
|
if (printer->outputHandle != NULL)
|
|
psfile = (FILE *)printer->outputHandle;
|
|
|
|
// Create new file?
|
|
if (psfile == NULL) {
|
|
if (!printer->multipageOutput)
|
|
findNextName("page", ".ps", filename, MAX_PATH_STRING, printer->outputpath);
|
|
else
|
|
findNextName("doc", ".ps", filename, MAX_PATH_STRING, printer->outputpath);
|
|
|
|
psfile = fopen(filename, "wb");
|
|
if (!psfile) {
|
|
fatal("PRINTER: Can't open file %s for printer output\n", filename);
|
|
return;
|
|
}
|
|
|
|
// Print header
|
|
fprintf(psfile, "%%!PS-Adobe-3.0\n");
|
|
fprintf(psfile, "%%%%Pages: (atend)\n");
|
|
fprintf(psfile, "%%%%BoundingBox: 0 0 %i %i\n", (uint16_t)(printer->defaultPageWidth * printer->dpi),
|
|
(uint16_t)(printer->defaultPageHeight * printer->dpi)); // TODO: use current height here?
|
|
fprintf(psfile, "%%%%Creator: PCem Virtual Printer\n");
|
|
fprintf(psfile, "%%%%DocumentData: Clean7Bit\n");
|
|
fprintf(psfile, "%%%%LanguageLevel: 2\n");
|
|
fprintf(psfile, "%%%%EndComments\n");
|
|
printer->multiPageCounter = 1;
|
|
}
|
|
|
|
fprintf(psfile, "%%%%Page: %i %i\n", printer->multiPageCounter, printer->multiPageCounter);
|
|
fprintf(psfile, "%i %i scale\n", (uint16_t)(printer->defaultPageWidth * printer->dpi),
|
|
(uint16_t)(printer->defaultPageHeight * printer->dpi)); // TODO: use current height here?
|
|
fprintf(psfile, "%i %i 8 [%i 0 0 -%i 0 %i]\n", printer->page->w, printer->page->h, printer->page->w,
|
|
printer->page->h, printer->page->h);
|
|
fprintf(psfile, "currentfile\n");
|
|
fprintf(psfile, "/ASCII85Decode filter\n");
|
|
fprintf(psfile, "/RunLengthDecode filter\n");
|
|
fprintf(psfile, "image\n");
|
|
|
|
SDL_LockSurface(printer->page);
|
|
|
|
uint32_t pix = 0;
|
|
uint32_t numpix = printer->page->h * printer->page->w;
|
|
printer->ASCII85BufferPos = printer->ASCII85CurCol = 0;
|
|
|
|
while (pix < numpix) {
|
|
// Compress data using RLE
|
|
if ((pix < numpix - 2) && (getPixel(pix, printer) == getPixel(pix + 1, printer)) &&
|
|
(getPixel(pix, printer) == getPixel(pix + 2, printer))) {
|
|
// Found three or more pixels with the same color
|
|
uint8_t sameCount = 3;
|
|
uint8_t col = getPixel(pix, printer);
|
|
while (sameCount < 128 && sameCount + pix < numpix && col == getPixel(pix + sameCount, printer))
|
|
sameCount++;
|
|
|
|
fprintASCII85(psfile, 257 - sameCount, printer);
|
|
fprintASCII85(psfile, 255 - col, printer);
|
|
|
|
// Skip ahead
|
|
pix += sameCount;
|
|
} else {
|
|
// Find end of heterogenous area
|
|
uint8_t diffCount = 1;
|
|
while (diffCount < 128 && diffCount + pix < numpix &&
|
|
((diffCount + pix < numpix - 2) ||
|
|
(getPixel(pix + diffCount, printer) != getPixel(pix + diffCount + 1, printer)) ||
|
|
(getPixel(pix + diffCount, printer) != getPixel(pix + diffCount + 2, printer))))
|
|
diffCount++;
|
|
|
|
fprintASCII85(psfile, diffCount - 1, printer);
|
|
for (uint8_t i = 0; i < diffCount; i++)
|
|
fprintASCII85(psfile, 255 - getPixel(pix++, printer), printer);
|
|
}
|
|
}
|
|
|
|
// Write EOD for RLE and ASCII85
|
|
fprintASCII85(psfile, 128, printer);
|
|
fprintASCII85(psfile, 256, printer);
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
|
|
fprintf(psfile, "showpage\n");
|
|
|
|
if (printer->multipageOutput) {
|
|
printer->multiPageCounter++;
|
|
printer->outputHandle = psfile;
|
|
} else {
|
|
fprintf(psfile, "%%%%Pages: 1\n");
|
|
fprintf(psfile, "%%%%EOF\n");
|
|
fclose(psfile);
|
|
printer->outputHandle = NULL;
|
|
}
|
|
} else {
|
|
const char *ext;
|
|
const char *output_format;
|
|
switch (printer->output_type) {
|
|
case OUTPUT_TYPE_BMP:
|
|
ext = ".bmp";
|
|
output_format = IMAGE_BMP;
|
|
break;
|
|
case OUTPUT_TYPE_PNG:
|
|
ext = ".png";
|
|
output_format = IMAGE_PNG;
|
|
break;
|
|
case OUTPUT_TYPE_TIFF:
|
|
ext = ".tiff";
|
|
output_format = IMAGE_TIFF;
|
|
break;
|
|
case OUTPUT_TYPE_JPG:
|
|
ext = ".jpg";
|
|
output_format = IMAGE_JPG;
|
|
break;
|
|
default:
|
|
fatal("PRINTER: Unknown output type: %d\n", printer->output_type);
|
|
}
|
|
|
|
// Find a page that does not exists
|
|
findNextName("page", ext, filename, MAX_PATH_STRING, printer->outputpath);
|
|
|
|
char *rgb = malloc(printer->page->w * printer->page->h * 3);
|
|
char *rgbptr = rgb;
|
|
|
|
SDL_LockSurface(printer->page);
|
|
|
|
SDL_Palette *sdlpal = printer->page->format->palette;
|
|
|
|
for (int y = 0; y < printer->page->h; y++) {
|
|
for (int x = 0; x < printer->page->w; x++) {
|
|
uint8_t pixel = *((uint8_t *)printer->page->pixels + x + (y * printer->page->pitch));
|
|
|
|
*rgbptr++ = sdlpal->colors[pixel].r;
|
|
*rgbptr++ = sdlpal->colors[pixel].g;
|
|
*rgbptr++ = sdlpal->colors[pixel].b;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
|
|
// TODO: save as 8bpp instead of converting to rgb
|
|
// TODO: image will be copied twice (here and in the function below)
|
|
wx_image_save_fullpath(filename, output_format, rgb, printer->page->w, printer->page->h, false);
|
|
free(rgb);
|
|
}
|
|
}
|
|
|
|
void fprintASCII85(FILE *f, uint16_t b, lpt_epsonprinter_t *printer) {
|
|
if (b != 256) {
|
|
if (b < 256)
|
|
printer->ASCII85Buffer[printer->ASCII85BufferPos++] = (uint8_t)b;
|
|
|
|
if (printer->ASCII85BufferPos == 4 || b == 257) {
|
|
uint32_t num = (uint32_t)printer->ASCII85Buffer[0] << 24 | (uint32_t)printer->ASCII85Buffer[1] << 16 |
|
|
(uint32_t)printer->ASCII85Buffer[2] << 8 | (uint32_t)printer->ASCII85Buffer[3];
|
|
|
|
// Deal with special case
|
|
if (num == 0 && b != 257) {
|
|
fprintf(f, "z");
|
|
if (++printer->ASCII85CurCol >= 79) {
|
|
printer->ASCII85CurCol = 0;
|
|
fprintf(f, "\n");
|
|
}
|
|
} else {
|
|
char buffer[5];
|
|
for (int8_t i = 4; i >= 0; i--) {
|
|
buffer[i] = (uint8_t)((uint32_t)num % (uint32_t)85);
|
|
buffer[i] += 33;
|
|
num /= (uint32_t)85;
|
|
}
|
|
|
|
// Make sure a line never starts with a % (which may be mistaken as start of a comment)
|
|
if (printer->ASCII85CurCol == 0 && buffer[0] == '%')
|
|
fprintf(f, " ");
|
|
|
|
for (int i = 0; i < ((b != 257) ? 5 : printer->ASCII85BufferPos + 1); i++) {
|
|
fprintf(f, "%c", buffer[i]);
|
|
if (++printer->ASCII85CurCol >= 79) {
|
|
printer->ASCII85CurCol = 0;
|
|
fprintf(f, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
printer->ASCII85BufferPos = 0;
|
|
}
|
|
|
|
} else // Close string
|
|
{
|
|
// Partial tupel if there are still bytes in the buffer
|
|
if (printer->ASCII85BufferPos > 0) {
|
|
for (uint8_t i = printer->ASCII85BufferPos; i < 4; i++)
|
|
printer->ASCII85Buffer[i] = 0;
|
|
|
|
fprintASCII85(f, 257, printer);
|
|
}
|
|
|
|
fprintf(f, "~");
|
|
fprintf(f, ">\n");
|
|
}
|
|
}
|
|
|
|
void finishMultipage(lpt_epsonprinter_t *printer) {
|
|
if (printer->outputHandle != NULL) {
|
|
if (printer->output_type == OUTPUT_TYPE_POSTSCRIPT) {
|
|
FILE *psfile = (FILE *)printer->outputHandle;
|
|
fprintf(psfile, "%%%%Pages: %i\n", printer->multiPageCounter);
|
|
fprintf(psfile, "%%%%EOF\n");
|
|
fclose(psfile);
|
|
} else if (printer->output_type == OUTPUT_TYPE_FORWARD_TO_REAL_PRINTER) {
|
|
#if defined(WIN32)
|
|
EndDoc(printer->printerDC);
|
|
#endif
|
|
}
|
|
printer->outputHandle = NULL;
|
|
}
|
|
}
|
|
|
|
bool isBlank(lpt_epsonprinter_t *printer) {
|
|
bool blank = true;
|
|
|
|
SDL_LockSurface(printer->page);
|
|
|
|
for (uint16_t y = 0; y < printer->page->h; y++)
|
|
for (uint16_t x = 0; x < printer->page->w; x++)
|
|
if (*((uint8_t *)printer->page->pixels + x + (y * printer->page->pitch)) != 0)
|
|
blank = false;
|
|
|
|
SDL_UnlockSurface(printer->page);
|
|
|
|
return blank;
|
|
}
|
|
|
|
uint8_t getPixel(uint32_t num, lpt_epsonprinter_t *printer) {
|
|
// Respect the pitch
|
|
return *((uint8_t *)printer->page->pixels + (num % printer->page->w) + ((num / printer->page->w) * printer->page->pitch));
|
|
}
|
|
|
|
static void epsonprinter_write_data(uint8_t val, void *p) {
|
|
lpt_epsonprinter_t *lpt_epsonprinter = (lpt_epsonprinter_t *)p;
|
|
#ifdef PRINTER_DEBUG
|
|
pclog("lx-810 write data %02X\n", val);
|
|
#endif
|
|
lpt_epsonprinter->last_data = val;
|
|
}
|
|
|
|
static void epsonprinter_write_ctrl(uint8_t val, void *p) {
|
|
lpt_epsonprinter_t *lpt_epsonprinter = (lpt_epsonprinter_t *)p;
|
|
#ifdef PRINTER_DEBUG
|
|
pclog("lx-810 write control %02X\n", val);
|
|
#endif
|
|
|
|
setAutofeed((val & 0x02) != 0, lpt_epsonprinter);
|
|
if ((val & 0x01) && (!(lpt_epsonprinter->controlreg & 0x01)))
|
|
printChar(lpt_epsonprinter);
|
|
if ((val & 0x04) && (!(lpt_epsonprinter->controlreg & 0x04)))
|
|
resetPrinterHard(lpt_epsonprinter);
|
|
|
|
lpt_epsonprinter->controlreg = val;
|
|
}
|
|
|
|
static uint8_t epsonprinter_read_status(void *p) {
|
|
lpt_epsonprinter_t *lpt_epsonprinter = (lpt_epsonprinter_t *)p;
|
|
uint8_t temp;
|
|
|
|
// Printer is always online and never reports an error
|
|
temp = 0x1f; // 0x18;
|
|
|
|
// if (lpt_epsonprinter->controlreg & 0x08 == 0)
|
|
// temp |= 0x10;
|
|
|
|
if (!isBusy(lpt_epsonprinter))
|
|
temp |= 0x80;
|
|
|
|
if (!ack(lpt_epsonprinter))
|
|
temp |= 0x40;
|
|
#ifdef PRINTER_DEBUG
|
|
pclog("lx-810 read status %02X\n", temp);
|
|
#endif
|
|
return temp;
|
|
}
|
|
|
|
static uint8_t epsonprinter_read_ctrl(void *p) {
|
|
lpt_epsonprinter_t *lpt_epsonprinter = (lpt_epsonprinter_t *)p;
|
|
uint8_t temp = 0xe0 | (getAutofeed(lpt_epsonprinter) ? 0x02 : 0x00) | (lpt_epsonprinter->controlreg & 0xfd);
|
|
#ifdef PRINTER_DEBUG
|
|
pclog("lx-810 read control %02X\n", temp);
|
|
#endif
|
|
|
|
return temp;
|
|
}
|
|
|
|
static void *epsonprinter_init() {
|
|
lpt_epsonprinter_t *lpt_epsonprinter = (lpt_epsonprinter_t *)malloc(sizeof(lpt_epsonprinter_t));
|
|
memset(lpt_epsonprinter, 0, sizeof(lpt_epsonprinter_t));
|
|
|
|
lpt_epsonprinter->controlreg = 0x04;
|
|
|
|
// TODO: printable area? (make it an option because the user may want to REALLY print the results and we don't want to add
|
|
// two forced margins?)
|
|
// TODO: also follow all mode overrides (relation of dip switches, front panel, software cmds) to the letter from the user
|
|
// manual
|
|
// TODO: see if defaults are right everywhere
|
|
// TODO: possible overflows in 8-bit and 16-bit ints when doing high dpis
|
|
// TODO: auto-form feed after inactivity option
|
|
// TODO: ink intensity (high speed draft, dry ribbon, etc)
|
|
// TODO: simulate errors and out of paper just for fun?
|
|
// TODO: I've seen a postscript non-multipage in win95/word6 come out blank once. Couldn't repeat
|
|
// width and height unit is 1/10 inch, multipageoutput == keep appending on postscript or print job until manual formfeed
|
|
int dpi = device_get_config_int("dpi");
|
|
int output_type = device_get_config_int("outputtype");
|
|
int multipage = device_get_config_int("multipage");
|
|
int selected_papersize = device_get_config_int("papersize");
|
|
int width;
|
|
int height;
|
|
switch (selected_papersize) {
|
|
case 0:
|
|
width = 85;
|
|
height = 110;
|
|
break;
|
|
case 1:
|
|
// TODO: should we care about this precision loss?
|
|
width = 210 / 254;
|
|
height = 297 / 254;
|
|
break;
|
|
default:
|
|
fatal("PRINTER: Invalid paper size\n");
|
|
}
|
|
int cpi = device_get_config_int("cpi");
|
|
int selected_fontstyle = device_get_config_int("font");
|
|
uint16_t style;
|
|
uint8_t quality;
|
|
Typeface font;
|
|
// TODO: we are just setting the default NLQ font to Roman when starting with draft, see if this is right.
|
|
switch (selected_fontstyle) {
|
|
case 0:
|
|
style = 0;
|
|
quality = QUALITY_DRAFT;
|
|
font = roman;
|
|
break;
|
|
case 1:
|
|
style = STYLE_CONDENSED;
|
|
quality = QUALITY_DRAFT;
|
|
font = roman;
|
|
break;
|
|
case 2:
|
|
style = 0;
|
|
quality = QUALITY_NLQ;
|
|
font = roman;
|
|
break;
|
|
case 3:
|
|
style = 0;
|
|
quality = QUALITY_NLQ;
|
|
font = sansserif;
|
|
break;
|
|
default:
|
|
fatal("PRINTER: Invalid initial font setting: %d\n", selected_fontstyle);
|
|
}
|
|
int selected_charset = device_get_config_int("characterset");
|
|
uint8_t chartable, i18ncharset;
|
|
uint16_t codepage;
|
|
switch (selected_charset) {
|
|
case 0:
|
|
chartable = 0;
|
|
i18ncharset = 0;
|
|
codepage = 437;
|
|
break;
|
|
case 1:
|
|
chartable = 1;
|
|
i18ncharset = 0;
|
|
codepage = 437;
|
|
break;
|
|
case 2:
|
|
chartable = 1;
|
|
i18ncharset = 1;
|
|
codepage = 437;
|
|
break;
|
|
case 3:
|
|
chartable = 1;
|
|
i18ncharset = 2;
|
|
codepage = 437;
|
|
break;
|
|
case 4:
|
|
chartable = 1;
|
|
i18ncharset = 3;
|
|
codepage = 437;
|
|
break;
|
|
case 5:
|
|
chartable = 1;
|
|
i18ncharset = 4;
|
|
codepage = 437;
|
|
break;
|
|
case 6:
|
|
chartable = 1;
|
|
i18ncharset = 5;
|
|
codepage = 437;
|
|
break;
|
|
case 7:
|
|
chartable = 1;
|
|
i18ncharset = 6;
|
|
codepage = 437;
|
|
break;
|
|
case 8:
|
|
chartable = 1;
|
|
i18ncharset = 7;
|
|
codepage = 437;
|
|
break;
|
|
case 9:
|
|
chartable = 1;
|
|
i18ncharset = 8;
|
|
codepage = 437;
|
|
break;
|
|
case 10:
|
|
chartable = 1;
|
|
i18ncharset = 9;
|
|
codepage = 437;
|
|
break;
|
|
case 11:
|
|
chartable = 1;
|
|
i18ncharset = 10;
|
|
codepage = 437;
|
|
break;
|
|
case 12:
|
|
chartable = 1;
|
|
i18ncharset = 11;
|
|
codepage = 437;
|
|
break;
|
|
case 13:
|
|
chartable = 1;
|
|
i18ncharset = 12;
|
|
codepage = 437;
|
|
break;
|
|
case 14:
|
|
chartable = 1;
|
|
i18ncharset = 0;
|
|
codepage = 850;
|
|
break;
|
|
case 15:
|
|
chartable = 1;
|
|
i18ncharset = 0;
|
|
codepage = 860;
|
|
break;
|
|
case 16:
|
|
chartable = 1;
|
|
i18ncharset = 0;
|
|
codepage = 863;
|
|
break;
|
|
case 17:
|
|
chartable = 1;
|
|
i18ncharset = 0;
|
|
codepage = 865;
|
|
break;
|
|
default:
|
|
fatal("PRINTER: Invalid initial charset setting: %d\n", selected_charset);
|
|
}
|
|
int emulatepins = device_get_config_int("bitmaps_emulate_pins");
|
|
|
|
char s[512];
|
|
strlcpy(lpt_epsonprinter->fontpath, printer_path, MAX_PATH_STRING - 2);
|
|
put_backslash(lpt_epsonprinter->fontpath);
|
|
|
|
strlcpy(lpt_epsonprinter->outputpath, printer_path, MAX_PATH_STRING - 2);
|
|
put_backslash(lpt_epsonprinter->outputpath);
|
|
|
|
if (FT_Init_FreeType(&lpt_epsonprinter->FTlib)) {
|
|
fatal("PRINTER: Unable to init Freetype2. Printing disabled\n");
|
|
lpt_epsonprinter->page = NULL;
|
|
} else {
|
|
lpt_epsonprinter->dpi = dpi;
|
|
lpt_epsonprinter->output_type = output_type;
|
|
lpt_epsonprinter->multipageOutput = multipage;
|
|
|
|
lpt_epsonprinter->defaultPageWidth = (double)width / (double)10;
|
|
lpt_epsonprinter->defaultPageHeight = (double)height / (double)10;
|
|
|
|
lpt_epsonprinter->defaultCPI = cpi;
|
|
lpt_epsonprinter->defaultStyle = style;
|
|
lpt_epsonprinter->defaultQuality = quality;
|
|
lpt_epsonprinter->defaultNLQtypeFace = font;
|
|
lpt_epsonprinter->defaultCharTable = chartable;
|
|
lpt_epsonprinter->defaultI18NCharset = i18ncharset;
|
|
lpt_epsonprinter->defaultCodePage = codepage;
|
|
lpt_epsonprinter->emulatePinsForBitmaps = emulatepins;
|
|
|
|
// Create page
|
|
lpt_epsonprinter->page = SDL_CreateRGBSurface(
|
|
0, (unsigned int)(lpt_epsonprinter->defaultPageWidth * lpt_epsonprinter->dpi),
|
|
(unsigned int)(lpt_epsonprinter->defaultPageHeight * lpt_epsonprinter->dpi), 8, 0, 0, 0, 0);
|
|
|
|
// Set a grey palette
|
|
SDL_Palette *palette = lpt_epsonprinter->page->format->palette;
|
|
|
|
for (unsigned int i = 0; i < 256; i++)
|
|
palette->colors[i].r = palette->colors[i].g = palette->colors[i].b = 255 - i;
|
|
|
|
lpt_epsonprinter->curFont = NULL;
|
|
lpt_epsonprinter->autoFeed = false;
|
|
lpt_epsonprinter->outputHandle = NULL;
|
|
|
|
resetPrinterHard(lpt_epsonprinter);
|
|
|
|
if (lpt_epsonprinter->output_type == OUTPUT_TYPE_FORWARD_TO_REAL_PRINTER) {
|
|
#if defined(WIN32)
|
|
// TODO: this looks like a hack
|
|
// 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);
|
|
// TODO: what if user presses cancel?
|
|
lpt_epsonprinter->printerDC = pd.hDC;
|
|
#endif
|
|
}
|
|
pclog("PRINTER: Enabled\n");
|
|
}
|
|
|
|
// TODO: MAPPER_AddHandler(FormFeed,MK_f2,MMOD1,"ejectpage","formfeed");
|
|
|
|
return lpt_epsonprinter;
|
|
}
|
|
|
|
static void epsonprinter_close(void *p) {
|
|
lpt_epsonprinter_t *lpt_epsonprinter = (lpt_epsonprinter_t *)p;
|
|
|
|
formFeed(lpt_epsonprinter); // eject any pending page
|
|
if (lpt_epsonprinter->page != NULL) {
|
|
SDL_FreeSurface(lpt_epsonprinter->page);
|
|
lpt_epsonprinter->page = NULL;
|
|
FT_Done_FreeType(lpt_epsonprinter->FTlib);
|
|
}
|
|
#if defined(WIN32)
|
|
DeleteDC(lpt_epsonprinter->printerDC);
|
|
#endif
|
|
|
|
free(lpt_epsonprinter);
|
|
}
|
|
|
|
void *epsonprinter_init_lpt1() {
|
|
current_device = (device_t *)&lpt_epsonprinter_device;
|
|
void *p = epsonprinter_init();
|
|
current_device = NULL;
|
|
// lpt1_device_attach(&lpt_epsonprinter_device, p);
|
|
|
|
return p;
|
|
}
|
|
void epsonprinter_close_lpt1(void *p) {
|
|
epsonprinter_close(p);
|
|
// lpt1_device_detach();
|
|
}
|
|
|
|
static device_config_t epsonprinter_config[] = {
|
|
{.name = "dpi",
|
|
.description = "Output DPI",
|
|
.type = CONFIG_SELECTION,
|
|
.selection = {{.description = "240 DPI", .value = 240},
|
|
{.description = "360 DPI", .value = 360},
|
|
{.description = "480 DPI", .value = 480},
|
|
{.description = "600 DPI", .value = 600},
|
|
{.description = "720 DPI", .value = 720},
|
|
{.description = "840 DPI", .value = 840},
|
|
{.description = "960 DPI", .value = 960},
|
|
{.description = "1080 DPI", .value = 1080},
|
|
{.description = ""}},
|
|
.default_int = 360},
|
|
{.name = "outputtype",
|
|
.description = "Output type",
|
|
.type = CONFIG_SELECTION,
|
|
.selection = {{.description = "Postscript (.ps)", .value = OUTPUT_TYPE_POSTSCRIPT},
|
|
{.description = "Bitmap (.bmp)", .value = OUTPUT_TYPE_BMP},
|
|
{.description = "Portable Network Graphics (.png)", .value = OUTPUT_TYPE_PNG},
|
|
{.description = "Tagged Image File Format (.tiff)", .value = OUTPUT_TYPE_TIFF},
|
|
{.description = "JPEG (.jpg)", .value = OUTPUT_TYPE_JPG},
|
|
#if defined(WIN32)
|
|
{.description = "Redirect to real printer", .value = OUTPUT_TYPE_FORWARD_TO_REAL_PRINTER},
|
|
#endif
|
|
{.description = ""}},
|
|
.default_int = OUTPUT_TYPE_PNG},
|
|
{.name = "multipage",
|
|
.description = "Multipage for Postscript and real printer",
|
|
.type = CONFIG_BINARY,
|
|
.default_int = 1},
|
|
{.name = "papersize",
|
|
.description = "Default paper size",
|
|
.type = CONFIG_SELECTION,
|
|
.selection =
|
|
{// TODO: more default lengths are supported, see DIP switch settings on the manual and see to with paper types
|
|
// they map
|
|
{.description = "Letter (8 1/2\" x 11\")", .value = 0},
|
|
{.description = "A4 (210mm x 297mm)", .value = 1},
|
|
{.description = ""}},
|
|
.default_int = 0},
|
|
{.name = "cpi",
|
|
.description = "Default CPI (Characters per inch)",
|
|
.type = CONFIG_SELECTION,
|
|
.selection = {{.description = "10 CPI", .value = 10}, {.description = "12 CPI", .value = 12}, {.description = ""}},
|
|
.default_int = 10},
|
|
/*{ TODO
|
|
.name = "shapeofzero",
|
|
.description = "Shape of zero",
|
|
.type = CONFIG_SELECTION,
|
|
.selection =
|
|
{
|
|
{
|
|
.description = "Not Slashed",
|
|
.value = 0
|
|
},
|
|
{
|
|
.description = "Slashed",
|
|
.value = 1
|
|
},
|
|
{
|
|
.description = ""
|
|
}
|
|
},
|
|
.default_int = 0
|
|
},*/
|
|
{.name = "font",
|
|
.description = "Default font",
|
|
.type = CONFIG_SELECTION,
|
|
.selection = {{.description = "Draft", .value = 0},
|
|
{.description = "Draft condensed", .value = 1},
|
|
{.description = "NLQ Roman", .value = 2},
|
|
{.description = "NLQ Sans Serif", .value = 3},
|
|
{.description = ""}},
|
|
.default_int = 0},
|
|
/*{ TODO
|
|
.name = "draftmode",
|
|
.description = "Draft mode",
|
|
.type = CONFIG_SELECTION,
|
|
.selection =
|
|
{
|
|
{
|
|
.description = "Normal",
|
|
.value = 0
|
|
},
|
|
{
|
|
.description = "High-speed", // TODO: different font, only works at 10 CPI! Reverts do "Draft"
|
|
otherwise (what about styles?) .value = 1
|
|
},
|
|
{
|
|
.description = ""
|
|
}
|
|
},
|
|
.default_int = 0
|
|
},*/
|
|
{.name = "characterset",
|
|
.description = "Default character set",
|
|
.type = CONFIG_SELECTION,
|
|
.selection = {{.description = "Italics", .value = 0},
|
|
{.description = "CP437 + U.S.A.", .value = 1},
|
|
{.description = "CP437 + France", .value = 2},
|
|
{.description = "CP437 + Germany", .value = 3},
|
|
{.description = "CP437 + U.K.", .value = 4},
|
|
{.description = "CP437 + Denmark I", .value = 5},
|
|
{.description = "CP437 + Sweden", .value = 6},
|
|
{.description = "CP437 + Italy", .value = 7},
|
|
{.description = "CP437 + Spain I", .value = 8},
|
|
{.description = "CP437 + Japan (Only available as a printer command on real hardware)", .value = 9},
|
|
{.description = "CP437 + Norway (Only available as a printer command on real hardware)", .value = 10},
|
|
{.description = "CP437 + Denmark II (Only available as a printer command on real hardware)", .value = 11},
|
|
{.description = "CP437 + Spain II (Only available as a printer command on real hardware)", .value = 12},
|
|
{.description = "CP437 + Latin America (Only available as a printer command on real hardware)",
|
|
.value = 13},
|
|
// The international character sets above can be selected (via software) with the codepages below, but I
|
|
// don't know if it makes sense.
|
|
{.description = "CP850 (Multilingual)", .value = 14},
|
|
{.description = "CP860 (Portugal)", .value = 15},
|
|
{.description = "CP863 (Canada-French)", .value = 16},
|
|
{.description = "CP865 (Norway)", .value = 17},
|
|
{.description = ""}},
|
|
.default_int = 1},
|
|
/*{ TODO: Didn't implement because I didn't test if this is FORCE or just START ON
|
|
.name = "autolinefeed",
|
|
.description = "Force auto linefeed",
|
|
.type = CONFIG_BINARY,
|
|
.default_int = 0
|
|
},*/
|
|
/*{ TODO: this forces an upper and lower margin by default - half and inch at each. Can be overridden by software - check
|
|
if it goes back to these values after a software/command reset. .name = "skipperforation", .description = "1-inch skip
|
|
over perforation", .type = CONFIG_BINARY, .default_int = 0
|
|
},*/
|
|
{.name = "bitmaps_emulate_pins",
|
|
.description = "Emulate pin spacing when printing graphics",
|
|
.type = CONFIG_BINARY,
|
|
.default_int = 1},
|
|
{.type = -1}};
|
|
|
|
lpt_device_t lpt_epsonprinter_device = {"Epson LX-810 Printer",
|
|
0,
|
|
epsonprinter_init_lpt1,
|
|
epsonprinter_close_lpt1,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
epsonprinter_config,
|
|
epsonprinter_write_data,
|
|
epsonprinter_write_ctrl,
|
|
epsonprinter_read_status};
|