DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/parport/printer.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #include "printer.h"
00020 
00021 #if C_PRINTER
00022 
00023 #include <math.h>
00024 #include "setup.h"
00025 #include "mapper.h"
00026 #include "printer_if.h"
00027 #include "support.h"
00028 #include "cross.h"
00029 #include "printer_charmaps.h"
00030 #include "control.h"
00031 
00032 #include "pic.h" // for timeout
00033 
00034 extern void GFX_CaptureMouse(void);
00035 extern bool mouselocked;
00036 
00037 static CPrinter* defaultPrinter = NULL;
00038 
00039 #define PARAM16(I) (params[I+1]*256+params[I])
00040 #define PIXX ((Bitu)floor(curX*dpi+0.5))
00041 #define PIXY ((Bitu)floor(curY*dpi+0.5))
00042 
00043 static Bit16u confdpi, confwidth, confheight;
00044 static Bitu printer_timout;
00045 static bool timeout_dirty;
00046 static const char* document_path;
00047 //static const char* font_path;
00048 static char confoutputDevice[50];
00049 static bool confmultipageOutput;
00050 
00051 void CPrinter::FillPalette(Bit8u redmax, Bit8u greenmax, Bit8u bluemax, Bit8u colorID, SDL_Palette* pal)
00052 {
00053         float red = (float)redmax / (float)30.9;
00054     float green = (float)greenmax / (float)30.9;
00055     float blue = (float)bluemax / (float)30.9;
00056 
00057         Bit8u colormask = colorID<<=5;
00058 
00059         for(int i = 0; i < 32; i++) {
00060                 pal->colors[i+colormask].r = 255 - (Bit8u)floor(red * (float)i);
00061                 pal->colors[i+colormask].g = 255 - (Bit8u)floor(green * (float)i);
00062                 pal->colors[i+colormask].b = 255 - (Bit8u)floor(blue * (float)i);
00063         }
00064 }
00065 
00066 CPrinter::CPrinter(Bit16u dpi, Bit16u width, Bit16u height, char* output, bool multipageOutput) 
00067 {
00068         if (FT_Init_FreeType(&FTlib))
00069         {
00070                 LOG(LOG_MISC,LOG_ERROR)("PRINTER: Unable to init Freetype2. Printing disabled");
00071                 page = NULL;
00072         }
00073         else
00074         {
00075                 this->dpi = dpi;
00076                 this->output = output;
00077                 this->multipageOutput = multipageOutput;
00078 
00079                 defaultPageWidth = (Real64)width / (Real64)10;
00080                 defaultPageHeight = (Real64)height / (Real64)10;
00081 
00082                 // Create page
00083                 page = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)(defaultPageWidth*dpi), (int)(defaultPageHeight*dpi), 8, 0, 0, 0, 0);
00084 
00085                 // Set a grey palette
00086                 SDL_Palette* palette = page->format->palette;
00087                 
00088                 for (Bitu i = 0; i < 32; i++)
00089                 {
00090                         palette->colors[i].r = 255;
00091                         palette->colors[i].g = 255;
00092                         palette->colors[i].b = 255;
00093                 }
00094                 // 0 = all white needed for logic 000
00095                 FillPalette(  0,   0,   0, 1, palette);
00096                 // 1 = magenta* 001
00097                 FillPalette(  0, 255,   0, 1, palette);
00098                 // 2 = cyan*    010
00099                 FillPalette(255,   0,   0, 2, palette);
00100                 // 3 = "violet" 011
00101                 FillPalette(255, 255,   0, 3, palette);
00102                 // 4 = yellow*  100
00103                 FillPalette(  0,   0, 255, 4, palette);
00104                 // 5 = red      101
00105                 FillPalette(  0, 255, 255, 5, palette);
00106                 // 6 = green    110
00107                 FillPalette(255,   0, 255, 6, palette);
00108                 // 7 = black    111
00109                 FillPalette(255, 255, 255, 7, palette);
00110 
00111                 // yyyxxxxx bit pattern: yyy=color xxxxx = intensity: 31=max
00112                 // Printing colors on top of each other ORs them and gets the
00113                 // correct resulting color.
00114                 // i.e. magenta on blank page yyy=001
00115                 // then yellow on magenta 001 | 100 = 101 = red
00116                 
00117                 color = COLOR_BLACK;
00118                 
00119                 curFont = NULL;
00120                 charRead = false;
00121                 autoFeed = false;
00122                 outputHandle = NULL;
00123 
00124                 resetPrinter();
00125 
00126                 if (strcasecmp(output, "printer") == 0)
00127                 {
00128 #if defined (WIN32)
00129                         // Show Print dialog to obtain a printer device context
00130                         PRINTDLG pd;
00131                         pd.lStructSize = sizeof(PRINTDLG); 
00132                         pd.hDevMode = (HANDLE) NULL; 
00133                         pd.hDevNames = (HANDLE) NULL; 
00134                         pd.Flags = PD_RETURNDC; 
00135                         pd.hwndOwner = NULL; 
00136                         pd.hDC = (HDC) NULL; 
00137                         pd.nFromPage = 1; 
00138                         pd.nToPage = 1; 
00139                         pd.nMinPage = 0; 
00140                         pd.nMaxPage = 0; 
00141                         pd.nCopies = 1; 
00142                         pd.hInstance = NULL; 
00143                         pd.lCustData = 0L; 
00144                         pd.lpfnPrintHook = (LPPRINTHOOKPROC) NULL; 
00145                         pd.lpfnSetupHook = (LPSETUPHOOKPROC) NULL; 
00146                         pd.lpPrintTemplateName = (LPSTR) NULL; 
00147                         pd.lpSetupTemplateName = (LPSTR)  NULL; 
00148                         pd.hPrintTemplate = (HANDLE) NULL; 
00149                         pd.hSetupTemplate = (HANDLE) NULL; 
00150                         PrintDlg(&pd);
00151                         // TODO: what if user presses cancel?
00152                         printerDC = pd.hDC;
00153 #endif
00154                 }
00155                 LOG(LOG_MISC,LOG_NORMAL)("PRINTER: Enabled");
00156         }
00157 }
00158 
00159 void CPrinter::resetPrinterHard()
00160 {
00161         charRead = false;
00162         resetPrinter();
00163 }
00164 
00165 void CPrinter::resetPrinter()
00166 {
00167                 color = COLOR_BLACK;
00168                 curX = curY = 0.0;
00169                 ESCSeen = false;
00170                 FSSeen = false;
00171                 ESCCmd = 0;
00172                 numParam = neededParam = 0;
00173                 topMargin = leftMargin = 0.0;
00174                 rightMargin = pageWidth = defaultPageWidth;
00175                 bottomMargin = pageHeight = defaultPageHeight;
00176                 lineSpacing = (Real64)1 / 6;
00177                 cpi = 10.0;
00178                 curCharTable = 1;
00179                 style = 0;
00180                 extraIntraSpace = 0.0;
00181                 printUpperContr = true;
00182                 bitGraph.remBytes = 0;
00183                 densk = 0;
00184                 densl = 1;
00185                 densy = 2;
00186                 densz = 3;
00187                 charTables[0] = 0; // Italics
00188                 charTables[1] = charTables[2] = charTables[3] = 437;
00189                 definedUnit = -1;
00190                 multipoint = false;
00191                 multiPointSize = 0.0;
00192                 multicpi = 0.0;
00193                 hmi = -1.0;
00194                 msb = 255;
00195                 numPrintAsChar = 0;
00196                 LQtypeFace = courier;
00197 
00198                 selectCodepage(charTables[curCharTable]);
00199 
00200                 updateFont();
00201 
00202                 newPage(false,true);
00203 
00204                 // Default tabs => Each eight characters
00205                 for (Bitu i = 0; i < 32; i++)
00206                         horiztabs[i] = i * 8 * (1 / (Real64)cpi);
00207                 numHorizTabs = 32;
00208                 numVertTabs = 255;
00209 }
00210 
00211 
00212 CPrinter::~CPrinter(void)
00213 {
00214         finishMultipage();
00215         if (page != NULL)
00216         {
00217                 SDL_FreeSurface(page);
00218                 page = NULL;
00219                 FT_Done_FreeType(FTlib);
00220         }
00221 #if defined (WIN32)
00222         DeleteDC(printerDC);
00223 #endif
00224 }
00225 
00226 void CPrinter::selectCodepage(Bit16u cp)
00227 {
00228         const Bit16u* mapToUse = NULL;
00229 
00230         Bitu i = 0;
00231         while(charmap[i].codepage!=0)
00232     {
00233                 if(charmap[i].codepage==cp)
00234         {
00235                         mapToUse = charmap[i].map;
00236                         break;
00237                 }
00238                 i++;
00239         }
00240         if (mapToUse == NULL)
00241     {
00242                 LOG(LOG_MISC,LOG_WARN)("Unsupported codepage %i. Using CP437 instead.", cp);
00243                 selectCodepage(437);
00244                 return;
00245         }
00246         for (int i = 0; i < 256; i++)
00247                 curMap[i] = mapToUse[i];
00248 }
00249 
00250 void CPrinter::updateFont()
00251 {
00252         if (curFont != NULL)
00253                 FT_Done_Face(curFont);
00254 
00255         const char* fontName;
00256 
00257         switch (LQtypeFace)
00258         {
00259             case roman:
00260                     fontName = "./FONTS/roman.ttf";
00261                     break;
00262             case sansserif:
00263                     fontName = "./FONTS/sansserif.ttf";
00264                     break;
00265             case courier:
00266                     fontName = "./FONTS/courier.ttf";
00267                     break;
00268             case script:
00269                     fontName = "./FONTS/script.ttf";
00270                     break;
00271             case ocra:
00272             case ocrb:
00273                     fontName = "./FONTS/ocra.ttf";
00274                     break;
00275             default:
00276                     fontName = "./FONTS/roman.ttf";
00277         }
00278         
00279 #ifndef WIN32
00280         std::string configfont;
00281         Cross::GetPlatformConfigDir(configfont);
00282         configfont += fontName;
00283         fontName = configfont.c_str();
00284 #endif
00285         
00286         if (FT_New_Face(FTlib, fontName, 0, &curFont))
00287         {
00288                 //LOG(LOG_MISC,LOG_ERROR)("Unable to load font %s", fontName);
00289                 LOG_MSG("Unable to load font %s", fontName);
00290                 curFont = NULL;
00291         }
00292 
00293         Real64 horizPoints = 10.5;
00294         Real64 vertPoints = 10.5;
00295 
00296         if (!multipoint)
00297     {
00298                 actcpi = cpi;
00299 /*
00300                 switch(style & (STYLE_CONDENSED|STYLE_PROP)) {
00301                         case STYLE_CONDENSED: // only condensed
00302                                 if (cpi == 10.0) {
00303                                         actcpi = 17.14;
00304                                         horizPoints *= 10.0/17.14;
00305                                 } else if(cpi == 12.0) {
00306                                         actcpi = 20.0;
00307                                         horizPoints *= 10.0/20.0;
00308                                         vertPoints *= 10.0/12.0;
00309                                 } else {
00310                                         // ignored
00311                                 }
00312                                 break;
00313                         case STYLE_PROP|STYLE_CONDENSED:
00314                                 horizPoints /= 2.0;
00315                                 break;
00316                         case 0: // neither
00317                         case STYLE_PROP: // only proportional
00318                                 horizPoints *= 10.0/cpi;
00319                                 vertPoints *= 10.0/cpi;
00320                                 break;
00321                 }
00322 */
00323                 if (!(style & STYLE_CONDENSED))
00324         {
00325                         horizPoints *= 10.0 / cpi;
00326                         vertPoints *= 10.0 / cpi;
00327                 }
00328 
00329                 if (!(style & STYLE_PROP))
00330         {
00331                         if ((cpi == 10.0) && (style & STYLE_CONDENSED))
00332             {
00333                                 actcpi = 17.14;
00334                                 horizPoints *= 10.0 / 17.14;
00335                         }
00336                         if ((cpi == 12.0) && (style & STYLE_CONDENSED))
00337             {
00338                                 actcpi = 20.0;
00339                                 horizPoints *= 10.0 / 20.0;
00340                                 vertPoints *= 10.0 / 12.0;
00341                         }       
00342                 } else if (style & STYLE_CONDENSED) horizPoints /= 2.0;
00343 
00344 
00345                 if ((style & STYLE_DOUBLEWIDTH) || (style & STYLE_DOUBLEWIDTHONELINE))
00346         {
00347                         actcpi /= 2.0;
00348                         horizPoints *= 2.0;
00349                 }
00350 
00351                 if (style & STYLE_DOUBLEHEIGHT) vertPoints *= 2.0;
00352         }
00353     else
00354     {
00355         // multipoint true
00356                 actcpi = multicpi;
00357                 horizPoints = vertPoints = multiPointSize;
00358         }
00359 
00360         if ((style & STYLE_SUPERSCRIPT) || (style & STYLE_SUBSCRIPT)) {
00361                 horizPoints *= 2.0/3.0;
00362                 vertPoints *= 2.0/3.0;
00363                 actcpi /= 2.0/3.0;
00364         }
00365 
00366         FT_Set_Char_Size(curFont, (Bit16u)horizPoints*64, (Bit16u)vertPoints*64, dpi, dpi);
00367         
00368         if (style & STYLE_ITALICS || charTables[curCharTable] == 0)
00369         {
00370                 FT_Matrix  matrix;
00371                 matrix.xx = 0x10000L;
00372                 matrix.xy = (FT_Fixed)(0.20 * 0x10000L);
00373                 matrix.yx = 0;
00374                 matrix.yy = 0x10000L;
00375                 FT_Set_Transform(curFont, &matrix, 0);
00376         }
00377 }
00378 
00379 bool CPrinter::processCommandChar(Bit8u ch)
00380 {
00381         if (ESCSeen || FSSeen)
00382         {
00383                 ESCCmd = ch;
00384                 if(FSSeen) ESCCmd |= 0x800;
00385                 ESCSeen = FSSeen = false;
00386                 numParam = 0;
00387 
00388                 switch (ESCCmd) {
00389                     case 0x02: // Undocumented
00390                     case 0x0a: // Reverse line feed                                                                                     (ESC LF)
00391                     case 0x0c: // Return to top of current page                                                         (ESC FF)
00392                     case 0x0e: // Select double-width printing (one line)                                       (ESC SO)                
00393                     case 0x0f: // Select condensed printing                                                                     (ESC SI)
00394                     case 0x23: // Cancel MSB control                                                                            (ESC #)
00395                     case 0x30: // Select 1/8-inch line spacing                                                          (ESC 0)
00396                     case 0x31: // Select 7/60-inch line spacing                                                         (ESC 1)
00397                     case 0x32: // Select 1/6-inch line spacing                                                          (ESC 2)
00398                     case 0x34: // Select italic font                                                                            (ESC 4)
00399                     case 0x35: // Cancel italic font                                                                            (ESC 5)
00400                     case 0x36: // Enable printing of upper control codes                                        (ESC 6)
00401                     case 0x37: // Enable upper control codes                                                            (ESC 7)
00402                     case 0x38: // Disable paper-out detector                                                            (ESC 8)
00403                     case 0x39: // Enable paper-out detector                                                                     (ESC 9)
00404                     case 0x3c: // Unidirectional mode (one line)                                                        (ESC <)
00405                     case 0x3d: // Set MSB to 0                                                                                          (ESC =)
00406                     case 0x3e: // Set MSB to 1                                                                                          (ESC >)
00407                     case 0x40: // Initialize printer                                                                            (ESC @)
00408                     case 0x45: // Select bold font                                                                                      (ESC E)
00409                     case 0x46: // Cancel bold font                                                                                      (ESC F)
00410                     case 0x47: // Select double-strike printing                                                         (ESC G)
00411                     case 0x48: // Cancel double-strike printing                                                         (ESC H)
00412                     case 0x4d: // Select 10.5-point, 12-cpi                                                                     (ESC M)
00413                     case 0x4f: // Cancel bottom margin [conflict]                                                       (ESC O)
00414                     case 0x50: // Select 10.5-point, 10-cpi                                                                     (ESC P)
00415                     case 0x54: // Cancel superscript/subscript printing                                         (ESC T)
00416                     case 0x5e: // Enable printing of all character codes on next character      (ESC ^)
00417                     case 0x67: // Select 10.5-point, 15-cpi                                                                     (ESC g)
00418 
00419                     case 0x834: // Select italic font                                                           (FS 4)  (= ESC 4)
00420                     case 0x835: // Cancel italic font                                                           (FS 5)  (= ESC 5)
00421                     case 0x846: // Select forward feed mode                                                     (FS F)
00422                     case 0x852: // Select reverse feed mode                                                     (FS R)
00423                             neededParam = 0;
00424                             break;
00425                     case 0x19: // Control paper loading/ejecting                                                        (ESC EM)
00426                     case 0x20: // Set intercharacter space                                                                      (ESC SP)
00427                     case 0x21: // Master select                                                                                         (ESC !)
00428                     case 0x2b: // Set n/360-inch line spacing                                                           (ESC +)
00429                     case 0x2d: // Turn underline on/off                                                                         (ESC -)
00430                     case 0x2f: // Select vertical tab channel                                                           (ESC /)
00431                     case 0x33: // Set n/180-inch line spacing                                                           (ESC 3)
00432                     case 0x41: // Set n/60-inch line spacing                                                            (ESC A)
00433                     case 0x43: // Set page length in lines                                                                      (ESC C)
00434                     case 0x49: // Select character type and print pitch                                         (ESC I)
00435                     case 0x4a: // Advance print position vertically                                                     (ESC J)
00436                     case 0x4e: // Set bottom margin                                                                                     (ESC N)
00437                     case 0x51: // Set right margin                                                                                      (ESC Q)
00438                     case 0x52: // Select an international character set                                         (ESC R)
00439                     case 0x53: // Select superscript/subscript printing                                         (ESC S)
00440                     case 0x55: // Turn unidirectional mode on/off                                                       (ESC U)
00441                     //case 0x56: // Repeat data                                                                                         (ESC V)
00442                     case 0x57: // Turn double-width printing on/off                                                     (ESC W)
00443                     case 0x61: // Select justification                                                                          (ESC a)
00444                     case 0x66: // Absolute horizontal tab in columns [conflict]                         (ESC f)
00445                     case 0x68: // Select double or quadruple size                                                       (ESC h)
00446                     case 0x69: // Immediate print                                                                                       (ESC i)
00447                     case 0x6a: // Reverse paper feed                                                                            (ESC j)
00448                     case 0x6b: // Select typeface                                                                                       (ESC k)
00449                     case 0x6c: // Set left margin                                                                                       (ESC 1)
00450                     case 0x70: // Turn proportional mode on/off                                                         (ESC p)
00451                     case 0x72: // Select printing color                                                                         (ESC r)
00452                     case 0x73: // Low-speed mode on/off                                                                         (ESC s)
00453                     case 0x74: // Select character table                                                                        (ESC t)
00454                     case 0x77: // Turn double-height printing on/off                                            (ESC w)
00455                     case 0x78: // Select LQ or draft                                                                            (ESC x)
00456                     case 0x7e: // Select/Deselect slash zero                                                            (ESC ~)
00457 
00458                     case 0x832: // Select 1/6-inch line spacing                                         (FS 2)  (= ESC 2)
00459                     case 0x833: // Set n/360-inch line spacing                                          (FS 3)  (= ESC +)
00460                     case 0x841: // Set n/60-inch line spacing                                           (FS A)  (= ESC A)
00461                     case 0x843: // Select LQ type style                                                         (FS C)  (= ESC k)
00462                     case 0x845: // Select character width                                                       (FS E)
00463                     case 0x849: // Select character table                                                       (FS I)  (= ESC t)
00464                     case 0x853: // Select High Speed/High Density elite pitch           (FS S)
00465                     case 0x856: // Turn double-height printing on/off                           (FS V)  (= ESC w)
00466                             neededParam = 1;
00467                             break;
00468                     case 0x24: // Set absolute horizontal print position                                        (ESC $)
00469                     case 0x3f: // Reassign bit-image mode                                                                       (ESC ?)
00470                     case 0x4b: // Select 60-dpi graphics                                                                        (ESC K)
00471                     case 0x4c: // Select 120-dpi graphics                                                                       (ESC L)
00472                     case 0x59: // Select 120-dpi, double-speed graphics                                         (ESC Y)
00473                     case 0x5a: // Select 240-dpi graphics                                                                       (ESC Z)
00474                     case 0x5c: // Set relative horizontal print position                                        (ESC \)
00475                     case 0x63: // Set horizontal motion index (HMI)     [conflict]                              (ESC c)
00476                     case 0x65: // Set vertical tab stops every n lines                                          (ESC e)
00477                     case 0x85a: // Print 24-bit hex-density graphics                                            (FS Z)
00478                             neededParam = 2;
00479                             break;
00480                     case 0x2a: // Select bit image                                                                                      (ESC *)
00481                     case 0x58: // Select font by pitch and point [conflict]                                     (ESC X)
00482                             neededParam = 3;
00483                             break;
00484                     case 0x5b: // Select character height, width, line spacing
00485                             neededParam = 7;
00486                             break;
00487                     case 0x62: // Set vertical tabs in VFU channels                                                     (ESC b) 
00488                     case 0x42: // Set vertical tabs                                                                                     (ESC B)
00489                             numVertTabs = 0;
00490                             return true;
00491                     case 0x44: // Set horizontal tabs                                                                           (ESC D)
00492                             numHorizTabs = 0;
00493                             return true;
00494                     case 0x25: // Select user-defined set                                                                       (ESC %)
00495                     case 0x26: // Define user-defined characters                                                        (ESC &)
00496                     case 0x3a: // Copy ROM to RAM                                                                                       (ESC :)
00497                             LOG(LOG_MISC,LOG_ERROR)("User-defined characters not supported!");
00498                             return true;
00499                     case 0x28: // Two bytes sequence
00500                             return true;
00501                     default:
00502                             LOG_MSG("PRINTER: Unknown command %s (%02Xh) %c , unable to skip parameters.",
00503                                     (ESCCmd & 0x800)?"FS":"ESC",ESCCmd, ESCCmd);
00504                         
00505                             neededParam = 0;
00506                             ESCCmd = 0;
00507                             return true;
00508                 }
00509 
00510                 if (neededParam > 0)
00511                         return true;
00512         }
00513 
00514         // Two bytes sequence
00515         if (ESCCmd == '(')
00516         {
00517                 ESCCmd = 0x200 + ch;
00518 
00519                 switch (ESCCmd)
00520                 {
00521                     case 0x242: // Bar code setup and print (ESC (B)
00522                     case 0x25e: // Print data as characters (ESC (^)
00523                             neededParam = 2;
00524                             break;
00525                     case 0x255: // Set unit (ESC (U)
00526                             neededParam = 3;
00527                             break;
00528                     case 0x243: // Set page length in defined unit (ESC (C)
00529                     case 0x256: // Set absolute vertical print position (ESC (V)
00530                     case 0x276: // Set relative vertical print position (ESC (v)
00531                             neededParam = 4;
00532                             break;
00533                     case 0x274: // Assign character table (ESC (t)
00534                     case 0x22d: // Select line/score (ESC (-)
00535                             neededParam = 5;
00536                             break;
00537                     case 0x263: // Set page format (ESC (c)
00538                             neededParam = 6;
00539                             break;
00540 
00541             default:
00542                             // ESC ( commands are always followed by a "number of parameters" word parameter
00543                             //LOG(LOG_MISC,LOG_ERROR)
00544                                     LOG_MSG("PRINTER: Skipping unsupported command ESC ( %c (%02X).", ESCCmd, ESCCmd);
00545                             neededParam = 2;
00546                             ESCCmd = 0x101;
00547                             return true;
00548                 }
00549 
00550                 if (neededParam > 0)
00551                         return true;
00552         }
00553 
00554         // Ignore VFU channel setting
00555         if (ESCCmd == 0x62)
00556     {
00557                 ESCCmd = 0x42;
00558                 return true;
00559         }
00560 
00561         // Collect vertical tabs
00562         if (ESCCmd == 0x42)
00563     {
00564                 if (ch == 0 || (numVertTabs > 0 && verttabs[numVertTabs-1] > (Real64)ch * lineSpacing)) // Done
00565                         ESCCmd = 0;
00566                 else
00567                         if (numVertTabs < 16)
00568                                 verttabs[numVertTabs++] = (Real64)ch * lineSpacing;
00569         }
00570 
00571         // Collect horizontal tabs
00572         if (ESCCmd == 0x44) 
00573         {
00574                 if (ch == 0 || (numHorizTabs > 0 && horiztabs[numHorizTabs-1] > (Real64)ch * (1 / (Real64)cpi))) // Done
00575                         ESCCmd = 0;
00576                 else
00577                         if (numHorizTabs < 32)
00578                                 horiztabs[numHorizTabs++] = (Real64)ch*(1 / (Real64)cpi);
00579         }
00580 
00581         if (numParam < neededParam)
00582         {
00583                 params[numParam++] = ch;
00584 
00585                 if (numParam < neededParam)
00586                         return true;
00587         }
00588 
00589         if (ESCCmd != 0)
00590         {
00591                 switch (ESCCmd)
00592                 {
00593                     case 0x02: // Undocumented
00594                             // Ignore
00595                             break;
00596                     case 0x0e: // Select double-width printing (one line) (ESC SO)              
00597                             if (!multipoint)
00598                             {
00599                                     hmi = -1;
00600                                     style |= STYLE_DOUBLEWIDTHONELINE;
00601                                     updateFont();
00602                             }
00603                             break;
00604                     case 0x0f: // Select condensed printing (ESC SI)
00605                             if (!multipoint && (cpi != 15.0))
00606                 {
00607                                     hmi = -1;
00608                                     style |= STYLE_CONDENSED;
00609                                     updateFont();
00610                             }
00611                             break;
00612                     case 0x19: // Control paper loading/ejecting (ESC EM)
00613                             // We are not really loading paper, so most commands can be ignored
00614                             if (params[0] == 'R')
00615                                     newPage(true, false); // TODO resetx?
00616                             break;
00617                     case 0x20: // Set intercharacter space (ESC SP)
00618                             if (!multipoint)
00619                             {
00620                                     extraIntraSpace = (Real64)params[0] / (printQuality == QUALITY_DRAFT ? 120 : 180);
00621                                     hmi = -1;
00622                                     updateFont();
00623                             }
00624                             break;
00625                     case 0x21: // Master select (ESC !)
00626                             cpi = params[0] & 0x01 ? 12:10;
00627 
00628                             // Reset first seven bits
00629                             style &= 0xFF80;
00630                             if (params[0] & 0x02)
00631                                     style |= STYLE_PROP;
00632                             if (params[0] & 0x04)
00633                                     style |= STYLE_CONDENSED;
00634                             if (params[0] & 0x08)
00635                                     style |= STYLE_BOLD;
00636                             if (params[0] & 0x10)
00637                                     style |= STYLE_DOUBLESTRIKE;
00638                             if (params[0] & 0x20)
00639                                     style |= STYLE_DOUBLEWIDTH;
00640                             if (params[0] & 0x40)
00641                                     style |= STYLE_ITALICS;
00642                             if (params[0] & 0x80)
00643                             {
00644                                     score = SCORE_SINGLE;
00645                                     style |= STYLE_UNDERLINE;
00646                             }
00647 
00648                             hmi = -1;
00649                             multipoint = false;
00650                             updateFont();
00651                             break;
00652                     case 0x23: // Cancel MSB control (ESC #)
00653                             msb = 255;
00654                             break;
00655                     case 0x24: // Set absolute horizontal print position (ESC $)
00656                             {
00657                                     Real64 unitSize = definedUnit;
00658                                     if (unitSize < 0)
00659                                             unitSize = (Real64)60.0;
00660 
00661                                     Real64 newX = leftMargin + ((Real64)PARAM16(0) / unitSize);
00662                                     if (newX <= rightMargin)
00663                                             curX = newX;
00664                             }
00665                             break;
00666                     case 0x85a: // Print 24-bit hex-density graphics (FS Z)
00667                             setupBitImage(40, PARAM16(0));
00668                             break;
00669                     case 0x2a: // Select bit image (ESC *)
00670                             setupBitImage(params[0], PARAM16(1));
00671                             break;
00672                     case 0x2b: // Set n/360-inch line spacing (ESC +)
00673                     case 0x833: // Set n/360-inch line spacing (FS 3)
00674                             lineSpacing = (Real64)params[0] / 360;
00675                             break;
00676                     case 0x2d: // Turn underline on/off (ESC -)
00677                             if (params[0] == 0 || params[0] == 48)
00678                                     style &= ~STYLE_UNDERLINE;
00679                             if (params[0] == 1 || params[0] == 49)
00680                             {
00681                                     style |= STYLE_UNDERLINE;
00682                                     score = SCORE_SINGLE;
00683                             }
00684                             updateFont();
00685                             break;
00686                     case 0x2f: // Select vertical tab channel (ESC /)
00687                             // Ignore
00688                             break;
00689                     case 0x30: // Select 1/8-inch line spacing (ESC 0)
00690                             lineSpacing = (Real64)1 / 8;
00691                             break;
00692                     case 0x32: // Select 1/6-inch line spacing (ESC 2)
00693                             lineSpacing = (Real64)1 / 6;
00694                             break;
00695                     case 0x33: // Set n/180-inch line spacing (ESC 3)
00696                             lineSpacing = (Real64)params[0] / 180;
00697                             break;
00698                     case 0x34: // Select italic font (ESC 4)
00699                             style |= STYLE_ITALICS;
00700                             updateFont();
00701                             break;
00702                     case 0x35: // Cancel italic font (ESC 5)
00703                             style &= ~STYLE_ITALICS;
00704                             updateFont();
00705                             break;
00706                     case 0x36: // Enable printing of upper control codes (ESC 6)
00707                             printUpperContr = true;
00708                             break;
00709                     case 0x37: // Enable upper control codes (ESC 7)
00710                             printUpperContr = false;
00711                             break;
00712                     case 0x3c: // Unidirectional mode (one line) (ESC <)
00713                             // We don't have a print head, so just ignore this
00714                             break;
00715                     case 0x3d: // Set MSB to 0 (ESC =)
00716                             msb = 0;
00717                             break;
00718                     case 0x3e: // Set MSB to 1 (ESC >)
00719                             msb = 1;
00720                             break;
00721                     case 0x3f: // Reassign bit-image mode (ESC ?)
00722                             if (params[0] == 75)
00723                                     densk = params[1];
00724                             if (params[0] == 76)
00725                                     densl = params[1];
00726                             if (params[0] == 89)
00727                                     densy = params[1];
00728                             if (params[0] == 90)
00729                                     densz = params[1];
00730                             break;
00731                     case 0x40: // Initialize printer (ESC @)
00732                             resetPrinter();
00733                             break;
00734                     case 0x41: // Set n/60-inch line spacing
00735                     case 0x841:
00736                             lineSpacing = (Real64)params[0] / 60;
00737                             break;
00738                     case 0x43: // Set page length in lines (ESC C)
00739                             if (params[0] != 0)
00740                                     pageHeight = bottomMargin = (Real64)params[0] * lineSpacing;
00741                             else // == 0 => Set page length in inches
00742                             {
00743                                     neededParam = 1;
00744                                     numParam = 0;
00745                                     ESCCmd = 0x100;
00746                                     return true;
00747                             }
00748                             break;
00749                     case 0x45: // Select bold font (ESC E)
00750                             style |= STYLE_BOLD;
00751                             updateFont();
00752                             break;
00753                     case 0x46: // Cancel bold font (ESC F)
00754                             style &= ~STYLE_BOLD;
00755                             updateFont();
00756                             break;
00757                     case 0x47: // Select dobule-strike printing (ESC G)
00758                             style |= STYLE_DOUBLESTRIKE;
00759                             break;
00760                     case 0x48: // Cancel double-strike printing (ESC H)
00761                             style &= ~STYLE_DOUBLESTRIKE;
00762                             break;
00763                     case 0x4a: // Advance print position vertically (ESC J n)
00764                             curY += (Real64)((Real64)params[0] / 180);
00765                             if (curY > bottomMargin)
00766                                     newPage(true, false);
00767                             break;
00768                     case 0x4b: // Select 60-dpi graphics (ESC K)
00769                             setupBitImage(densk, PARAM16(0));
00770                             break;
00771                     case 0x4c: // Select 120-dpi graphics (ESC L)
00772                             setupBitImage(densl, PARAM16(0));
00773                             break;
00774                     case 0x4d: // Select 10.5-point, 12-cpi (ESC M)
00775                             cpi = 12;
00776                             hmi = -1;
00777                             multipoint = false;
00778                             updateFont();
00779                             break;
00780                     case 0x4e: // Set bottom margin (ESC N)
00781                             topMargin = 0.0;
00782                             bottomMargin = (Real64)params[0] * lineSpacing; 
00783                             break;
00784                     case 0x4f: // Cancel bottom (and top) margin
00785                             topMargin = 0.0;
00786                             bottomMargin = pageHeight;
00787                             break;
00788                     case 0x50: // Select 10.5-point, 10-cpi (ESC P)
00789                             cpi = 10;
00790                             hmi = -1;
00791                             multipoint = false;
00792                             updateFont();
00793                             break;
00794                     case 0x51: // Set right margin
00795                             rightMargin = (Real64)(params[0] - 1.0) / (Real64)cpi;
00796                             break;
00797                     case 0x52: // Select an international character set (ESC R)
00798                             if (params[0] <= 13 || params[0] == 64)
00799                             {
00800                                     if (params[0] == 64)
00801                                             params[0] = 14;
00802 
00803                                     curMap[0x23] = intCharSets[params[0]][0];
00804                                     curMap[0x24] = intCharSets[params[0]][1];
00805                                     curMap[0x40] = intCharSets[params[0]][2];
00806                                     curMap[0x5b] = intCharSets[params[0]][3];
00807                                     curMap[0x5c] = intCharSets[params[0]][4];
00808                                     curMap[0x5d] = intCharSets[params[0]][5];
00809                                     curMap[0x5e] = intCharSets[params[0]][6];
00810                                     curMap[0x60] = intCharSets[params[0]][7];
00811                                     curMap[0x7b] = intCharSets[params[0]][8];
00812                                     curMap[0x7c] = intCharSets[params[0]][9];
00813                                     curMap[0x7d] = intCharSets[params[0]][10];
00814                                     curMap[0x7e] = intCharSets[params[0]][11];
00815                             }
00816                             break;
00817                     case 0x53: // Select superscript/subscript printing (ESC S)
00818                             if (params[0] == 0 || params[0] == 48)
00819                                     style |= STYLE_SUBSCRIPT;
00820                             if (params[0] == 1 || params[1] == 49)
00821                                     style |= STYLE_SUPERSCRIPT;
00822                             updateFont();
00823                             break;
00824                     case 0x54: // Cancel superscript/subscript printing (ESC T)
00825                             style &= 0xFFFF - STYLE_SUPERSCRIPT - STYLE_SUBSCRIPT;
00826                             updateFont();
00827                             break;
00828                     case 0x55: // Turn unidirectional mode on/off (ESC U)
00829                             // We don't have a print head, so just ignore this
00830                             break;
00831                     case 0x57: // Turn double-width printing on/off (ESC W)
00832                             if (!multipoint)
00833                             {
00834                                     hmi = -1;
00835                                     if (params[0] == 0 || params[0] == 48)
00836                                             style &= ~STYLE_DOUBLEWIDTH;
00837                                     if (params[0] == 1 || params[0] == 49)
00838                                             style |= STYLE_DOUBLEWIDTH;
00839                                     updateFont();
00840                             }
00841                             break;
00842                     case 0x58: // Select font by pitch and point (ESC X)
00843                             multipoint = true;
00844                             // Copy currently non-multipoint CPI if no value was set so far
00845                             if (multicpi == 0)
00846                                     multicpi = cpi;
00847                             if (params[0] > 0)  // Set CPI
00848                             {
00849                                     if (params[0] == 1) // Proportional spacing
00850                                             style |= STYLE_PROP;
00851                                     else if (params[0] >= 5)
00852                                             multicpi = (Real64)360 / (Real64)params[0];
00853                             }
00854                             if (multiPointSize == 0)
00855                                     multiPointSize = (Real64)10.5;
00856                             if (PARAM16(1) > 0) // Set points
00857                                     multiPointSize = ((Real64)PARAM16(1)) / 2;                  
00858                             updateFont();
00859                             break;
00860                     case 0x59: // Select 120-dpi, double-speed graphics (ESC Y)
00861                             setupBitImage(densy, PARAM16(0));
00862                             break;
00863                     case 0x5a: // Select 240-dpi graphics (ESC Z)
00864                             setupBitImage(densz, PARAM16(0));
00865                             break;
00866                     case 0x5c: // Set relative horizontal print position (ESC \)
00867                             {
00868                                     Bit16s toMove = PARAM16(0);
00869                                     Real64 unitSize = definedUnit;
00870                                     if (unitSize < 0)
00871                                             unitSize = (Real64)(printQuality == QUALITY_DRAFT ? 120.0 : 180.0);
00872                                     curX += (Real64)((Real64)toMove / unitSize);
00873                             }
00874                             break;
00875                     case 0x61: // Select justification (ESC a)
00876                             // Ignore
00877                             break;
00878                     case 0x63: // Set horizontal motion index (HMI) (ESC c)
00879                             hmi = (Real64)PARAM16(0) / (Real64)360.0;
00880                             extraIntraSpace = 0.0;
00881                             break;
00882                     case 0x67: // Select 10.5-point, 15-cpi (ESC g)
00883                             cpi = 15;
00884                             hmi = -1;
00885                             multipoint = false;
00886                             updateFont();
00887                             break;
00888                     case 0x846: // Select forward feed mode (FS F) - set reverse not implemented yet
00889                             if (lineSpacing < 0) lineSpacing *= -1;
00890                             break;
00891                     case 0x6a: // Reverse paper feed (ESC j)
00892                             {
00893                                     Real64 reverse = (Real64)PARAM16(0) / (Real64)216.0;
00894                                     reverse = curY - reverse;
00895                                     if (reverse < leftMargin)
00896                         curY = leftMargin;
00897                                     else
00898                         curY = reverse;
00899                                     break;
00900                             }
00901                     case 0x6b: // Select typeface (ESC k)
00902                             if (params[0] <= 11 || params[0] == 30 || params[0] == 31) 
00903                                     LQtypeFace = (Typeface)params[0];
00904                             updateFont();
00905                             break;
00906                     case 0x6c: // Set left margin (ESC l)
00907                             leftMargin =  (Real64)(params[0] - 1.0) / (Real64)cpi;
00908                             if (curX < leftMargin)
00909                                     curX = leftMargin;
00910                             break;
00911                     case 0x70: // Turn proportional mode on/off (ESC p)
00912                             if (params[0] == 0 || params[0] == 48)
00913                                     style &= (0xffff - STYLE_PROP);
00914                             if (params[0] == 1 || params[0] == 49)
00915                             {
00916                                     style |= STYLE_PROP;
00917                                     printQuality = QUALITY_LQ;
00918                             }
00919                             multipoint = false;
00920                             hmi = -1;
00921                             updateFont();
00922                             break;
00923                     case 0x72: // Select printing color (ESC r)
00924                             if (params[0] == 0 || params[0] > 6)
00925                     color = COLOR_BLACK;
00926                             else
00927                     color = params[0] << 5;       
00928                             break;
00929                     case 0x73: // Select low-speed mode (ESC s)
00930                             // Ignore
00931                             break;
00932                     case 0x74: // Select character table (ESC t)
00933                     case 0x849: // Select character table (FS I)
00934                             if (params[0] < 4)
00935                                     curCharTable = params[0];
00936                             if (params[0] >= 48 && params[0] <= 51)
00937                                     curCharTable = params[0] - 48;
00938                             selectCodepage(charTables[curCharTable]);
00939                             updateFont();
00940                             break;
00941                     case 0x77: // Turn double-height printing on/off (ESC w)
00942                             if (!multipoint)
00943                             {
00944                                     if (params[0] == 0 || params[0] == 48)
00945                                             style &= ~STYLE_DOUBLEHEIGHT;
00946                                     if (params[0] == 1 || params[0] == 49)
00947                                             style |= STYLE_DOUBLEHEIGHT;
00948                                     updateFont();
00949                             }
00950                             break;
00951                     case 0x78: // Select LQ or draft (ESC x)
00952                             if (params[0] == 0 || params[0] == 48) {
00953                                     printQuality = QUALITY_DRAFT;
00954                                     style |= STYLE_CONDENSED;
00955                             }
00956                             if (params[0] == 1 || params[0] == 49) {
00957                                     printQuality = QUALITY_LQ;
00958                                     style &= ~STYLE_CONDENSED;
00959                             }
00960                             hmi = -1;
00961                             updateFont();
00962                             break;
00963                     case 0x100: // Set page length in inches (ESC C NUL)
00964                             pageHeight = (Real64)params[0];
00965                             bottomMargin = pageHeight;
00966                             topMargin = 0.0;
00967                             break;
00968                     case 0x101: // Skip unsupported ESC ( command
00969                             neededParam = PARAM16(0);
00970                             numParam = 0;
00971                             break;
00972                     case 0x274: // Assign character table (ESC (t)
00973                             if (params[2] < 4 && params[3] < 16)
00974                             {
00975                                     charTables[params[2]] = codepages[params[3]];
00976                                     //LOG_MSG("curr table: %d, p2: %d, p3: %d",curCharTable,params[2],params[3]);
00977                                     if (params[2] == curCharTable)
00978                                             selectCodepage(charTables[curCharTable]);
00979                             }
00980                             break;
00981                     case 0x22d: // Select line/score (ESC (-) 
00982                             style &= ~(STYLE_UNDERLINE | STYLE_STRIKETHROUGH | STYLE_OVERSCORE);
00983                             score = params[4];
00984                             if (score)
00985                             {
00986                                     if (params[3] == 1)
00987                                             style |= STYLE_UNDERLINE;
00988                                     if (params[3] == 2)
00989                                             style |= STYLE_STRIKETHROUGH;
00990                                     if (params[3] == 3)
00991                                             style |= STYLE_OVERSCORE;
00992                             }
00993                             updateFont();
00994                             break;
00995                     case 0x242: // Bar code setup and print (ESC (B)
00996                             LOG(LOG_MISC,LOG_ERROR)("PRINTER: Bardcode printing not supported");
00997                             // Find out how many bytes to skip
00998                             neededParam = PARAM16(0);
00999                             numParam = 0;
01000                             break;
01001                     case 0x243: // Set page length in defined unit (ESC (C)
01002                             if (params[0] != 0 && definedUnit > 0)
01003                             {
01004                                     pageHeight = bottomMargin = ((Real64)PARAM16(2)) * definedUnit;
01005                                     topMargin = 0.0;
01006                             }
01007                             break;
01008                     case 0x255: // Set unit (ESC (U)
01009                             definedUnit = (Real64)params[2] / (Real64)3600;
01010                             break;
01011                     case 0x256: // Set absolute vertical print position (ESC (V)
01012                             {
01013                                     Real64 unitSize = definedUnit;
01014                                     if (unitSize < 0)
01015                                             unitSize = (Real64)360.0;
01016                                     Real64 newPos = topMargin + (((Real64)PARAM16(2)) * unitSize);
01017                                     if (newPos > bottomMargin)
01018                                             newPage(true, false);
01019                                     else
01020                                             curY = newPos;
01021                             }
01022                             break;
01023                     case 0x25e: // Print data as characters (ESC (^)
01024                             numPrintAsChar = PARAM16(0);
01025                             break;
01026                     case 0x263: // Set page format (ESC (c)
01027                             if (definedUnit > 0)
01028                             {
01029                                     Real64 newTop, newBottom;
01030                                     newTop = ((Real64)PARAM16(2)) * definedUnit;
01031                                     newBottom = ((Real64)PARAM16(4)) * definedUnit;
01032                                     if(newTop >= newBottom) break;
01033                                     if(newTop < pageHeight) topMargin = newTop;
01034                                     if(newBottom < pageHeight) bottomMargin = newBottom;
01035                                     if(topMargin > curY) curY = topMargin;
01036                                     //LOG_MSG("du %d, p1 %d, p2 %d, newtop %f, newbott %f, nt %f, nb %f, ph %f",
01037                                     //  (Bitu)definedUnit,PARAM16(2),PARAM16(4),topMargin,bottomMargin,
01038                                     //  newTop,newBottom,pageHeight);
01039                             }
01040                             break;
01041                     case 0x276: // Set relative vertical print position (ESC (v)
01042                             {
01043                                     Real64 unitSize = definedUnit;
01044                                     if (unitSize < 0)
01045                                             unitSize = (Real64)360.0;
01046                                     Real64 newPos = curY + ((Real64)((Bit16s)PARAM16(2)) * unitSize);
01047                                     if (newPos > topMargin)
01048                                     {
01049                                             if (newPos > bottomMargin)
01050                                                     newPage(true, false);
01051                                             else
01052                                                     curY = newPos;      
01053                                     }
01054                             }
01055                             break;
01056                     default:
01057                             if (ESCCmd < 0x100)
01058                                     //LOG(LOG_MISC,LOG_WARN)
01059                                     LOG_MSG("PRINTER: Skipped unsupported command ESC %c (%02X)", ESCCmd, ESCCmd);
01060                             else
01061                                     //LOG(LOG_MISC,LOG_WARN)
01062                                     LOG_MSG("PRINTER: Skipped unsupported command ESC ( %c (%02X)", ESCCmd-0x200, ESCCmd-0x200);
01063                 }
01064 
01065                 ESCCmd = 0;
01066                 return true;
01067         }
01068 
01069         switch (ch)
01070         {
01071             case 0x00:  // NUL is ignored by the printer
01072                     return true;
01073             case 0x07:  // Beeper (BEL)
01074                     // BEEEP!
01075                     return true;
01076             case 0x08:  // Backspace (BS)
01077                     {
01078                             Real64 newX = curX - (1/(Real64)actcpi);
01079                             if (hmi > 0)
01080                                     newX = curX - hmi;
01081                             if (newX >= leftMargin)
01082                                     curX = newX;
01083                     }
01084                     return true;
01085             case 0x09:  // Tab horizontally (HT)
01086                     {
01087                             // Find tab right to current pos
01088                             Real64 moveTo = -1;
01089                             for (Bit8u i = 0; i < numHorizTabs; i++)
01090                                     if (horiztabs[i] > curX)
01091                                             moveTo = horiztabs[i];
01092                             // Nothing found => Ignore
01093                             if (moveTo > 0 && moveTo < rightMargin)
01094                                     curX = moveTo;
01095                     }
01096                     return true;
01097             case 0x0b:  // Tab vertically (VT)
01098                     if (numVertTabs == 0) // All tabs cancelled => Act like CR
01099                             curX = leftMargin;
01100                     else if (numVertTabs == 255) // No tabs set since reset => Act like LF
01101                     {
01102                             curX = leftMargin;
01103                             curY += lineSpacing;
01104                             if (curY > bottomMargin)
01105                                     newPage(true, false);
01106                     }
01107                     else
01108                     {
01109                             // Find tab below current pos
01110                             Real64 moveTo = -1;
01111                             for (Bit8u i = 0; i < numVertTabs; i++)
01112                                     if (verttabs[i] > curY)
01113                                             moveTo = verttabs[i];
01114 
01115                             // Nothing found => Act like FF
01116                             if (moveTo > bottomMargin || moveTo < 0)
01117                                     newPage(true, false);
01118                             else
01119                                     curY = moveTo;
01120                     }
01121                     if (style & STYLE_DOUBLEWIDTHONELINE)
01122                     {
01123                             style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
01124                             updateFont();
01125                     }
01126                     return true;
01127             case 0x0c:          // Form feed (FF)
01128                     if (style & STYLE_DOUBLEWIDTHONELINE)
01129                     {
01130                             style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
01131                             updateFont();
01132                     }
01133                     newPage(true, true);
01134                     return true;
01135             case 0x0d:          // Carriage Return (CR)
01136                     curX = leftMargin;
01137                     if (!autoFeed)
01138                             return true;
01139             case 0x0a:          // Line feed
01140                     if (style & STYLE_DOUBLEWIDTHONELINE)
01141                     {
01142                             style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE;
01143                             updateFont();
01144                     }
01145                     curX = leftMargin;
01146                     curY += lineSpacing;
01147                     if (curY > bottomMargin)
01148                             newPage(true, false);
01149                     return true;
01150             case 0x0e:          //Select Real64-width printing (one line) (SO)
01151                     if (!multipoint)
01152                     {
01153                             hmi = -1;
01154                             style |= STYLE_DOUBLEWIDTHONELINE;
01155                             updateFont();
01156                     }
01157                     return true;
01158             case 0x0f:          // Select condensed printing (SI)
01159                     if (!multipoint && (cpi != 15.0))
01160             {
01161                             hmi = -1;
01162                             style |= STYLE_CONDENSED;
01163                             updateFont();
01164                     }
01165                     return true;
01166             case 0x11:          // Select printer (DC1)
01167                     // Ignore
01168                     return true;
01169             case 0x12:          // Cancel condensed printing (DC2)
01170                     hmi = -1;
01171                     style &= ~STYLE_CONDENSED;
01172                     updateFont();
01173                     return true;
01174             case 0x13:          // Deselect printer (DC3)
01175                     // Ignore
01176                     return true;
01177             case 0x14:          // Cancel double-width printing (one line) (DC4)
01178                     hmi = -1;
01179                     style &= ~STYLE_DOUBLEWIDTHONELINE;
01180                     updateFont();
01181                     return true;
01182             case 0x18:          // Cancel line (CAN)
01183                     return true;
01184             case 0x1b:          // ESC
01185                     ESCSeen = true;
01186                     return true;
01187             case 0x1c:          // FS (IBM commands)
01188                     FSSeen = true;
01189                     return true;
01190             default:
01191                     return false;
01192         }
01193 
01194         return false;
01195 }
01196 
01197 static void PRINTER_EventHandler(Bitu param);
01198 
01199 void CPrinter::newPage(bool save, bool resetx)
01200 {
01201         PIC_RemoveEvents(PRINTER_EventHandler);
01202         if (printer_timout)
01203         timeout_dirty=false;
01204         if (save)
01205                 outputPage();
01206         if (resetx)
01207         curX = leftMargin;
01208         curY = topMargin;
01209 
01210         SDL_Rect rect;
01211         rect.x = 0;
01212         rect.y = 0;
01213         rect.w = page->w;
01214         rect.h = page->h;
01215         SDL_FillRect(page, &rect, SDL_MapRGB(page->format, 255, 255, 255));
01216 
01217         /*for(int i = 0; i < 256; i++)
01218         {
01219         *((Bit8u*)page->pixels+i)=i;
01220         }*/
01221 }
01222 
01223 void CPrinter::printChar(Bit8u ch)
01224 {
01225         charRead = true;
01226         if (page == NULL) return;
01227 
01228         // Don't think that DOS programs uses this but well: Apply MSB if desired
01229         if (msb != 255) {
01230                 if (msb == 0) ch &= 0x7F;
01231                 if (msb == 1) ch |= 0x80;
01232         }
01233 
01234         // Are we currently printing a bit graphic?
01235         if (bitGraph.remBytes > 0) {
01236                 printBitGraph(ch);
01237                 return;
01238         }
01239 
01240         // Print everything?
01241         if (numPrintAsChar > 0) numPrintAsChar--;
01242         else if (processCommandChar(ch)) return;
01243 
01244         // Do not print if no font is available
01245         if (!curFont) return;
01246 
01247         if(ch == 0x1) ch = 0x20;
01248         
01249         // Find the glyph for the char to render
01250         FT_UInt index = FT_Get_Char_Index(curFont, curMap[ch]);
01251         
01252         // Load the glyph 
01253         FT_Load_Glyph(curFont, index, FT_LOAD_DEFAULT);
01254 
01255         // Render a high-quality bitmap
01256         FT_Render_Glyph(curFont->glyph, FT_RENDER_MODE_NORMAL);
01257 
01258         Bit16u penX = (Bit16u)(PIXX + curFont->glyph->bitmap_left);
01259         Bit16u penY = (Bit16u)(PIXY - curFont->glyph->bitmap_top + curFont->size->metrics.ascender / 64);
01260 
01261         if (style & STYLE_SUBSCRIPT) penY += curFont->glyph->bitmap.rows / 2;
01262 
01263         // Copy bitmap into page
01264         SDL_LockSurface(page);
01265 
01266         blitGlyph(curFont->glyph->bitmap, penX, penY, false);
01267         blitGlyph(curFont->glyph->bitmap, penX+1, penY, true);
01268 
01269         // Doublestrike => Print the glyph a second time one pixel below
01270         if (style & STYLE_DOUBLESTRIKE)
01271     {
01272                 blitGlyph(curFont->glyph->bitmap, penX, penY+1, true);
01273                 blitGlyph(curFont->glyph->bitmap, penX+1, penY+1, true);
01274         }
01275 
01276         // Bold => Print the glyph a second time one pixel to the right
01277         // or be a bit more bold...
01278         if (style & STYLE_BOLD)
01279     {
01280                 blitGlyph(curFont->glyph->bitmap, penX+1, penY, true);
01281                 blitGlyph(curFont->glyph->bitmap, penX+2, penY, true);
01282                 blitGlyph(curFont->glyph->bitmap, penX+3, penY, true);
01283         }
01284         SDL_UnlockSurface(page);
01285 
01286         // For line printing
01287         Bit16u lineStart = (Bit16u)PIXX;
01288 
01289         // advance the cursor to the right
01290         Real64 x_advance;
01291         if (style &     STYLE_PROP)
01292                 x_advance = (Real64)((Real64)(curFont->glyph->advance.x) / (Real64)(dpi * 64));
01293         else
01294     {
01295                 if (hmi < 0)
01296             x_advance = 1 / (Real64)actcpi;
01297                 else
01298             x_advance = hmi;
01299         }
01300         x_advance += extraIntraSpace;
01301     curX += x_advance;
01302 
01303         // Draw lines if desired
01304         if ((score != SCORE_NONE) && (style & (STYLE_UNDERLINE|STYLE_STRIKETHROUGH|STYLE_OVERSCORE)))
01305         {
01306                 // Find out where to put the line
01307                 Bit16u lineY = (Bit16u)PIXY;
01308                 double height = (curFont->size->metrics.height >> 6); // TODO height is fixed point madness...
01309 
01310                 if (style & STYLE_UNDERLINE)
01311             lineY = (Bit16u)PIXY + (Bit16u)(height * 0.9);
01312                 else if (style & STYLE_STRIKETHROUGH)
01313             lineY = (Bit16u)PIXY + (Bit16u)(height * 0.45);
01314                 else if (style & STYLE_OVERSCORE)
01315                         lineY = (Bit16u)PIXY - (Bit16u)(((score == SCORE_DOUBLE) || (score == SCORE_DOUBLEBROKEN)) ? 5 : 0);
01316 
01317                 drawLine(lineStart, PIXX, lineY, score == SCORE_SINGLEBROKEN || score == SCORE_DOUBLEBROKEN);
01318 
01319                 // draw second line if needed
01320                 if ((score == SCORE_DOUBLE)||(score == SCORE_DOUBLEBROKEN))
01321                         drawLine(lineStart, PIXX, lineY + 5, score == SCORE_SINGLEBROKEN || score == SCORE_DOUBLEBROKEN);
01322         }
01323         // If the next character would go beyond the right margin, line-wrap.
01324         if((curX + x_advance) > rightMargin)
01325     {
01326                 curX = leftMargin;
01327                 curY += lineSpacing;
01328                 if (curY > bottomMargin) newPage(true, false);
01329         }
01330 }
01331 
01332 void CPrinter::blitGlyph(FT_Bitmap bitmap, Bit16u destx, Bit16u desty, bool add) {
01333         for (Bitu y = 0; y < bitmap.rows; y++)
01334     {
01335                 for (Bitu x = 0; x < bitmap.width; x++)
01336         {
01337                         // Read pixel from glyph bitmap
01338                         Bit8u source = *(bitmap.buffer + x + y * bitmap.pitch);
01339 
01340                         // Ignore background and don't go over the border
01341                         if (source > 0 && (destx + x < (Bitu)page->w) && (desty + y < (Bitu)page->h))
01342             {
01343                                 Bit8u* target = (Bit8u*)page->pixels + (x + destx) + (y + desty) * page->pitch;
01344                                 source>>=3;
01345                                 
01346                                 if (add)
01347                 {
01348                                         if (((*target) & 0x1f) + source > 31)
01349                         *target |= (color | 0x1f);
01350                                         else
01351                     {
01352                                                 *target += source;
01353                                                 *target |= color;
01354                                         }
01355                                 }
01356                                 else
01357                     *target = source|color;
01358                         }
01359                 }
01360         }
01361 }
01362 
01363 void CPrinter::drawLine(Bitu fromx, Bitu tox, Bitu y, bool broken)
01364 {
01365         SDL_LockSurface(page);
01366 
01367         Bitu breakmod = dpi / 15;
01368         Bitu gapstart = (breakmod * 4) / 5;
01369 
01370         // Draw anti-aliased line
01371         for (Bitu x = fromx; x <= tox; x++)
01372         {
01373                 // Skip parts if broken line or going over the border
01374                 if ((!broken || (x % breakmod <= gapstart)) && (x < (Bitu)page->w))
01375                 {
01376                         if (y > 0 && (y - 1) < (Bitu)page->h)
01377                                 *((Bit8u*)page->pixels + x + (y - 1) * (Bitu)page->pitch) = 240;
01378                         if (y < (Bitu)page->h)
01379                                 *((Bit8u*)page->pixels + x + y * (Bitu)page->pitch) = !broken ? 255 : 240;
01380                         if (y + 1 < (Bitu)page->h)
01381                                 *((Bit8u*)page->pixels + x + (y + 1) * (Bitu)page->pitch) = 240;
01382                 }
01383         }
01384         SDL_UnlockSurface(page);
01385 }
01386 
01387 void CPrinter::setAutofeed(bool feed)
01388 {
01389         autoFeed = feed;
01390 }
01391 
01392 bool CPrinter::getAutofeed()
01393 {
01394         return autoFeed;
01395 }
01396 
01397 bool CPrinter::isBusy()
01398 {
01399         // We're never busy
01400         return false;
01401 }
01402 
01403 bool CPrinter::ack()
01404 {
01405         // Acknowledge last char read
01406         if(charRead)
01407     {
01408                 charRead=false;
01409                 return true;
01410         }
01411         return false;
01412 }
01413 
01414 void CPrinter::setupBitImage(Bit8u dens, Bit16u numCols)
01415 {
01416         switch (dens)
01417         {
01418             case 0:
01419                     bitGraph.horizDens = 60;
01420                     bitGraph.vertDens = 60;
01421                     bitGraph.adjacent = true;
01422                     bitGraph.bytesColumn = 1;
01423                     break;
01424             case 1:
01425                     bitGraph.horizDens = 120;
01426                     bitGraph.vertDens = 60;
01427                     bitGraph.adjacent = true;
01428                     bitGraph.bytesColumn = 1;
01429                     break;
01430             case 2:
01431                     bitGraph.horizDens = 120;
01432                     bitGraph.vertDens = 60;
01433                     bitGraph.adjacent = false;
01434                     bitGraph.bytesColumn = 1;
01435                     break;
01436             case 3:
01437                     bitGraph.horizDens = 60;
01438                     bitGraph.vertDens = 240;
01439                     bitGraph.adjacent = false;
01440                     bitGraph.bytesColumn = 1;
01441                     break;
01442             case 4:
01443                     bitGraph.horizDens = 80;
01444                     bitGraph.vertDens = 60;
01445                     bitGraph.adjacent = true;
01446                     bitGraph.bytesColumn = 1;
01447                     break;
01448             case 6:
01449                     bitGraph.horizDens = 90;
01450                     bitGraph.vertDens = 60;
01451                     bitGraph.adjacent = true;
01452                     bitGraph.bytesColumn = 1;
01453                     break;
01454             case 32:
01455                     bitGraph.horizDens = 60;
01456                     bitGraph.vertDens = 180;
01457                     bitGraph.adjacent = true;
01458                     bitGraph.bytesColumn = 3;
01459                     break;
01460             case 33:
01461                     bitGraph.horizDens = 120;
01462                     bitGraph.vertDens = 180;
01463                     bitGraph.adjacent = true;
01464                     bitGraph.bytesColumn = 3;
01465                     break;
01466             case 38:
01467                     bitGraph.horizDens = 90;
01468                     bitGraph.vertDens = 180;
01469                     bitGraph.adjacent = true;
01470                     bitGraph.bytesColumn = 3;
01471                     break;
01472             case 39:
01473                     bitGraph.horizDens = 180;
01474                     bitGraph.vertDens = 180;
01475                     bitGraph.adjacent = true;
01476                     bitGraph.bytesColumn = 3;
01477                     break;
01478             case 40:
01479                     bitGraph.horizDens = 360;
01480                     bitGraph.vertDens = 180;
01481                     bitGraph.adjacent = false;
01482                     bitGraph.bytesColumn = 3;
01483                     break;
01484             case 71:
01485                     bitGraph.horizDens = 180;
01486                     bitGraph.vertDens = 360;
01487                     bitGraph.adjacent = true;
01488                     bitGraph.bytesColumn = 6;
01489                     break;
01490             case 72:
01491                     bitGraph.horizDens = 360;
01492                     bitGraph.vertDens = 360;
01493                     bitGraph.adjacent = false;
01494                     bitGraph.bytesColumn = 6;
01495                     break;
01496             case 73:
01497                     bitGraph.horizDens = 360;
01498                     bitGraph.vertDens = 360;
01499                     bitGraph.adjacent = true;
01500                     bitGraph.bytesColumn = 6;
01501                     break;
01502             default:
01503                     LOG(LOG_MISC,LOG_ERROR)("PRINTER: Unsupported bit image density %i", dens);
01504         }
01505 
01506         bitGraph.remBytes = numCols * bitGraph.bytesColumn;
01507         bitGraph.readBytesColumn = 0;
01508 }
01509 
01510 void CPrinter::printBitGraph(Bit8u ch)
01511 {
01512         bitGraph.column[bitGraph.readBytesColumn++] = ch;
01513         bitGraph.remBytes--;
01514 
01515         // Only print after reading a full column
01516         if (bitGraph.readBytesColumn < bitGraph.bytesColumn)
01517                 return;
01518 
01519         Real64 oldY = curY;
01520 
01521         SDL_LockSurface(page);
01522 
01523         // When page dpi is greater than graphics dpi, the drawn pixels get "bigger"
01524         Bitu pixsizeX = 1; 
01525         Bitu pixsizeY = 1;
01526         if(bitGraph.adjacent)
01527     {
01528                 pixsizeX = dpi / bitGraph.horizDens > 0 ? dpi / bitGraph.horizDens : 1;
01529                 pixsizeY = dpi / bitGraph.vertDens > 0 ? dpi / bitGraph.vertDens : 1;
01530         }
01531         // TODO figure this out for 360dpi mode in windows
01532 
01533 //      Bitu pixsizeX = dpi/bitGraph.horizDens > 0? dpi/bitGraph.horizDens : 1;
01534 //      Bitu pixsizeY = dpi/bitGraph.vertDens > 0? dpi/bitGraph.vertDens : 1;
01535 
01536         for (Bitu i = 0; i < bitGraph.bytesColumn; i++)
01537         {
01538         // for each byte
01539                 for (Bitu j = 128; j != 0; j >>= 1)
01540         {
01541             // for each bit
01542                         if (bitGraph.column[i] & j)
01543             {
01544                                 for (Bitu xx=0; xx<pixsizeX; xx++)
01545                                         for (Bitu yy=0; yy<pixsizeY; yy++)
01546                     {
01547                                                 if (((PIXX + xx) < (Bitu)page->w) && ((PIXY + yy) < (Bitu)page->h))
01548                                                         *((Bit8u*)page->pixels + (PIXX + xx) + (PIXY + yy)*page->pitch) |= (color | 0x1F);
01549                                         }
01550                         } // else white pixel
01551                         curY += (Real64)1 / (Real64)bitGraph.vertDens; // TODO line wrap?
01552                 }
01553         }
01554 
01555         SDL_UnlockSurface(page);
01556 
01557         curY = oldY;
01558 
01559         bitGraph.readBytesColumn = 0;
01560 
01561         // Advance to the left
01562         curX += (Real64)1 / (Real64)bitGraph.horizDens;
01563 }
01564 
01565 void CPrinter::formFeed()
01566 {
01567         // Don't output blank pages
01568         newPage(!isBlank(), true);
01569         finishMultipage();
01570 }
01571 
01572 static void findNextName(const char* front, const char* ext, char* fname)
01573 {
01574         int i = 1;
01575         Bitu slen = (Bitu)strlen(document_path);
01576         if(slen > (200 - 15))
01577     {
01578                 fname[0] = 0;
01579                 return;
01580         }
01581 
01582     FILE *test = NULL;
01583         do
01584         {
01585                 strcpy(fname, document_path);
01586 #ifdef WIN32
01587                 const char* const pathstring = "\\%s%d%s";
01588 #else 
01589                 const char* const pathstring = "/%s%d%s";
01590 #endif
01591                 sprintf(fname + strlen(fname), pathstring, front, i++, ext);
01592                 test = fopen(fname, "rb");
01593                 if (test != NULL) fclose(test);
01594         }
01595         while (test != NULL);
01596 }
01597 
01598 void CPrinter::outputPage() 
01599 {
01600         char fname[200]; 
01601 
01602         if (strcasecmp(output, "printer") == 0)
01603         {
01604 #if defined (WIN32)
01605                 // You'll need the mouse for the print dialog
01606                 if(mouselocked)
01607                          GFX_CaptureMouse();
01608 
01609                 Bit16u physW = GetDeviceCaps(printerDC, PHYSICALWIDTH);
01610                 Bit16u physH = GetDeviceCaps(printerDC, PHYSICALHEIGHT);
01611 
01612                 Real64 scaleW, scaleH;
01613 
01614                 if (page->w > physW) 
01615                 scaleW = (Real64)page->w / (Real64)physW;
01616             else 
01617                         scaleW = (Real64)physW / (Real64)page->w; 
01618  
01619                 if (page->h > physH) 
01620                 scaleH = (Real64)page->h / (Real64)physH;
01621             else 
01622                         scaleH = (Real64)physH / (Real64)page->h; 
01623 
01624                 HDC memHDC = CreateCompatibleDC(printerDC);
01625                 HBITMAP bitmap = CreateCompatibleBitmap(memHDC, page->w, page->h);
01626                 SelectObject(memHDC, bitmap);
01627 
01628                 // Start new printer job?
01629                 if (outputHandle == NULL)
01630                 {
01631                         DOCINFO docinfo;
01632                         memset(&docinfo, 0, sizeof(docinfo));
01633                         docinfo.cbSize = sizeof(docinfo);
01634                         docinfo.lpszDocName = "DOSBOX Printer";
01635                         docinfo.lpszOutput = NULL;
01636                         docinfo.lpszDatatype = NULL;
01637                         docinfo.fwType = 0;
01638 
01639                         StartDoc(printerDC, &docinfo);
01640                         multiPageCounter = 1;
01641                 }
01642 
01643                 if (StartPage(printerDC) < 0)
01644         {
01645                         LOG_MSG("PRINTER: Cannot start page.");
01646                         DeleteObject(bitmap);
01647                         DeleteDC(memHDC);
01648                         return;
01649                 }
01650                 SDL_LockSurface(page);
01651 
01652                 SDL_Palette* sdlpal = page->format->palette;
01653 
01654                 for (Bit16u y = 0; y < page->h; y++)
01655                 {
01656                         for (Bit16u x = 0; x < page->w; x++)
01657                         {
01658                                 Bit8u pixel = *((Bit8u*)page->pixels + x + (y*page->pitch));
01659                                 Bit32u color = 0;
01660                                 color |= sdlpal->colors[pixel].r;
01661                                 color |= ((Bit32u)sdlpal->colors[pixel].g) << 8;
01662                                 color |= ((Bit32u)sdlpal->colors[pixel].b) << 16;
01663                                 SetPixel(memHDC, x, y, color);
01664                         }
01665                 }
01666 
01667                 SDL_UnlockSurface(page);
01668         
01669                 StretchBlt(printerDC, 0, 0, physW, physH, memHDC, 0, 0, page->w, page->h, SRCCOPY);
01670 
01671                 EndPage(printerDC);
01672 
01673                 if (multipageOutput)
01674                 {
01675                         multiPageCounter++;
01676                         outputHandle = printerDC;
01677                 }
01678                 else
01679                 {
01680                         EndDoc(printerDC);
01681                         outputHandle = NULL;
01682                 }
01683                 DeleteObject(bitmap);
01684                 DeleteDC(memHDC);
01685 #else
01686                 LOG_MSG("PRINTER: Direct printing not supported under this OS");
01687 #endif
01688         }
01689 #ifdef C_LIBPNG
01690         else if (strcasecmp(output, "png") == 0)
01691         {
01692                 // Find a page that does not exists
01693                 findNextName("page", ".png", &fname[0]);
01694         
01695                 png_structp png_ptr;
01696                 png_infop info_ptr;
01697                 png_bytep* row_pointers;
01698                 png_color palette[256];
01699                 Bitu i;
01700 
01701                 /* Open the actual file */
01702                 FILE* fp = fopen(fname,"wb");
01703                 if (!fp) 
01704                 {
01705                         LOG(LOG_MISC,LOG_ERROR)("PRINTER: Can't open file %s for printer output", fname);
01706                         return;
01707                 }
01708 
01709                 /* First try to alloacte the png structures */
01710                 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
01711                 if (!png_ptr) return;
01712                 info_ptr = png_create_info_struct(png_ptr);
01713                 if (!info_ptr)
01714         {
01715                         png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
01716                         return;
01717                 }
01718 
01719                 /* Finalize the initing of png library */
01720                 png_init_io(png_ptr, fp);
01721                 png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
01722                 
01723                 /* set other zlib parameters */
01724                 png_set_compression_mem_level(png_ptr, 8);
01725                 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
01726                 png_set_compression_window_bits(png_ptr, 15);
01727                 png_set_compression_method(png_ptr, 8);
01728                 png_set_compression_buffer_size(png_ptr, 8192);
01729                 
01730                 png_set_IHDR(png_ptr, info_ptr, page->w, page->h,
01731                         8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
01732                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
01733                 for (i = 0; i < 256; i++) 
01734                 {
01735                         palette[i].red = page->format->palette->colors[i].r;
01736                         palette[i].green = page->format->palette->colors[i].g;
01737                         palette[i].blue = page->format->palette->colors[i].b;
01738                 }
01739                 png_set_PLTE(png_ptr, info_ptr, palette,256);
01740                 
01741                 SDL_LockSurface(page);
01742 
01743                 // Allocate an array of scanline pointers
01744                 row_pointers = (png_bytep*)malloc(page->h * sizeof(png_bytep));
01745                 for (i = 0; i < (Bitu)page->h; i++) 
01746                         row_pointers[i] = ((Bit8u*)page->pixels + (i * page->pitch));
01747 
01748                 // tell the png library what to encode.
01749                 png_set_rows(png_ptr, info_ptr, row_pointers);
01750                 
01751                 // Write image to file
01752                 png_write_png(png_ptr, info_ptr, 0, NULL);
01753 
01754                 SDL_UnlockSurface(page);
01755                 
01756                 /*close file*/
01757                 fclose(fp);
01758         
01759                 /*Destroy PNG structs*/
01760                 png_destroy_write_struct(&png_ptr, &info_ptr);
01761                 
01762                 /*clean up dynamically allocated RAM.*/
01763                 free(row_pointers);
01764         }
01765 #endif
01766         else if (strcasecmp(output, "ps") == 0)
01767         {
01768                 FILE* psfile = NULL;
01769                 
01770                 // Continue postscript file?
01771                 if (outputHandle != NULL)
01772                         psfile = (FILE*)outputHandle;
01773 
01774                 // Create new file?
01775                 if (psfile == NULL)
01776                 {
01777                         if (!multipageOutput)
01778                                 findNextName("page", ".ps", &fname[0]);
01779                         else
01780                                 findNextName("doc", ".ps", &fname[0]);
01781 
01782                         psfile = fopen(fname, "wb");
01783                         if (!psfile) 
01784                         {
01785                                 LOG(LOG_MISC,LOG_ERROR)("PRINTER: Can't open file %s for printer output", fname);
01786                                 return;
01787                         }
01788 
01789                         // Print header
01790                         fprintf(psfile, "%%!PS-Adobe-3.0\n");
01791                         fprintf(psfile, "%%%%Pages: (atend)\n");
01792                         fprintf(psfile, "%%%%BoundingBox: 0 0 %i %i\n", (Bit16u)(defaultPageWidth * 72), (Bit16u)(defaultPageHeight * 72));
01793                         fprintf(psfile, "%%%%Creator: DOSBOX Virtual Printer\n");
01794                         fprintf(psfile, "%%%%DocumentData: Clean7Bit\n");
01795                         fprintf(psfile, "%%%%LanguageLevel: 2\n");
01796                         fprintf(psfile, "%%%%EndComments\n");
01797                         multiPageCounter = 1;
01798                 }
01799 
01800                 fprintf(psfile, "%%%%Page: %i %i\n", multiPageCounter, multiPageCounter);
01801                 fprintf(psfile, "%i %i scale\n", (Bit16u)(defaultPageWidth * 72), (Bit16u)(defaultPageHeight * 72));
01802                 fprintf(psfile, "%i %i 8 [%i 0 0 -%i 0 %i]\n", page->w, page->h, page->w, page->h, page->h);
01803                 fprintf(psfile, "currentfile\n");
01804                 fprintf(psfile, "/ASCII85Decode filter\n");
01805                 fprintf(psfile, "/RunLengthDecode filter\n");
01806                 fprintf(psfile, "image\n");
01807 
01808                 SDL_LockSurface(page);
01809 
01810                 Bit32u pix = 0;
01811                 Bit32u numpix = page->h * page->w;
01812                 ASCII85BufferPos = ASCII85CurCol = 0;
01813 
01814                 while (pix < numpix)
01815                 {
01816                         // Compress data using RLE
01817                         if ((pix < numpix - 2) && (getPixel(pix) == getPixel(pix + 1)) && (getPixel(pix) == getPixel(pix + 2)))
01818                         {
01819                                 // Found three or more pixels with the same color
01820                                 Bit8u sameCount = 3;
01821                                 Bit8u col = getPixel(pix);
01822                                 while (sameCount < 128 && sameCount + pix < numpix && col == getPixel(pix + sameCount))
01823                                         sameCount++;
01824 
01825                                 fprintASCII85(psfile, 257 - sameCount);
01826                                 fprintASCII85(psfile, 255 - col);
01827 
01828                                 // Skip ahead
01829                                 pix += sameCount;
01830                         }
01831                         else
01832                         {
01833                                 // Find end of heterogenous area
01834                                 Bit8u diffCount = 1;
01835                                 while (
01836                     diffCount < 128 && diffCount + pix < numpix && 
01837                                         (
01838                                         (diffCount + pix < numpix - 2) || (getPixel(pix + diffCount) != getPixel(pix + diffCount+1)) || (getPixel(pix + diffCount) != getPixel(pix + diffCount+2))
01839                                         )
01840                 ) diffCount++;
01841 
01842                                 fprintASCII85(psfile, diffCount-1);
01843                                 for (Bit8u i = 0; i < diffCount; i++)
01844                                         fprintASCII85(psfile, 255 - getPixel(pix++));
01845                         }
01846                 }
01847 
01848                 // Write EOD for RLE and ASCII85
01849                 fprintASCII85(psfile, 128);
01850                 fprintASCII85(psfile, 256);
01851 
01852                 SDL_UnlockSurface(page);
01853 
01854                 fprintf(psfile, "showpage\n");
01855 
01856                 if (multipageOutput)
01857                 {
01858                         multiPageCounter++;
01859                         outputHandle = psfile;
01860                 }
01861                 else
01862                 {
01863                         fprintf(psfile, "%%%%Pages: 1\n");
01864                         fprintf(psfile, "%%%%EOF\n");
01865                         fclose(psfile);
01866                         outputHandle = NULL;
01867                 }
01868         }
01869         else
01870         {       
01871                 // Find a page that does not exists
01872                 findNextName("page", ".bmp", &fname[0]);
01873                 SDL_SaveBMP(page, fname);
01874         }
01875 }
01876 
01877 void CPrinter::fprintASCII85(FILE* f, Bit16u b)
01878 {
01879         if (b != 256)
01880         {
01881                 if (b < 256)
01882                         ASCII85Buffer[ASCII85BufferPos++] = (Bit8u)b;
01883 
01884                 if (ASCII85BufferPos == 4 || b == 257)
01885                 {
01886                         Bit32u num = (Bit32u)ASCII85Buffer[0] << 24 | (Bit32u)ASCII85Buffer[1] << 16 | (Bit32u)ASCII85Buffer[2] << 8 | (Bit32u)ASCII85Buffer[3];
01887 
01888                         // Deal with special case
01889                         if (num == 0 && b != 257)
01890                         {
01891                                 fprintf(f, "z");
01892                                 if (++ASCII85CurCol >= 79)
01893                                 {
01894                                         ASCII85CurCol = 0;
01895                                         fprintf(f, "\n");
01896                                 }
01897                         }
01898                         else
01899                         {
01900                                 char buffer[5];
01901                                 for (Bit8s i = 4; i >= 0; i--)
01902                                 {
01903                                         buffer[i] = (Bit8u)((Bit32u)num % (Bit32u)85);
01904                                         buffer[i] += 33;
01905                                         num /= (Bit32u)85;
01906                                 }
01907 
01908                                 // Make sure a line never starts with a % (which may be mistaken as start of a comment)
01909                                 if (ASCII85CurCol == 0 && buffer[0] == '%')
01910                                         fprintf(f, " ");
01911                                 
01912                                 for (int i = 0; i < ((b != 257) ? 5 : ASCII85BufferPos + 1); i++)
01913                                 {
01914                                         fprintf(f, "%c", buffer[i]);
01915                                         if (++ASCII85CurCol >= 79)
01916                                         {
01917                                                 ASCII85CurCol = 0;
01918                                                 fprintf(f, "\n");
01919                                         }
01920                                 }
01921                         }
01922 
01923                         ASCII85BufferPos = 0;
01924                 }
01925 
01926         }
01927         else // Close string
01928         {
01929                 // Partial tupel if there are still bytes in the buffer
01930                 if (ASCII85BufferPos > 0)
01931                 {
01932                         for (Bit8u i = ASCII85BufferPos; i < 4; i++)
01933                                 ASCII85Buffer[i] = 0;
01934 
01935                         fprintASCII85(f, 257);
01936                 }
01937 
01938                 fprintf(f, "~");
01939                 fprintf(f, ">\n");
01940         }
01941 }
01942 
01943 void CPrinter::finishMultipage()
01944 {
01945         if (outputHandle != NULL)
01946         {
01947                 if (strcasecmp(output, "ps") == 0)
01948                 {
01949                         FILE* psfile = (FILE*)outputHandle;
01950                         fprintf(psfile, "%%%%Pages: %i\n", multiPageCounter);
01951                         fprintf(psfile, "%%%%EOF\n");
01952                         fclose(psfile);
01953                 }
01954                 else if (strcasecmp(output, "printer") == 0)
01955                 {
01956 #if defined (WIN32)
01957                         EndDoc(printerDC);
01958 #endif
01959                 }
01960                 outputHandle = NULL;
01961         }
01962 }
01963 
01964 bool CPrinter::isBlank()
01965 {
01966         bool blank = true;
01967 
01968         SDL_LockSurface(page);
01969 
01970         for (Bit16u y = 0; y < page->h; y++)
01971                 for (Bit16u x = 0; x < page->w; x++)
01972                         if (*((Bit8u*)page->pixels + x + (y * page->pitch)) != 0)
01973                                 blank = false;
01974 
01975         SDL_UnlockSurface(page);
01976         return blank;
01977 }
01978 
01979 Bit8u CPrinter::getPixel(Bit32u num)
01980 {
01981         // Respect the pitch
01982         return *((Bit8u*)page->pixels + (num % page->w) + ((num / page->w) * page->pitch));
01983 }
01984 
01985 static Bit8u dataregister; // contents of the parallel port data register
01986 
01987 Bitu PRINTER_readdata(Bitu port,Bitu iolen)
01988 {
01989     (void)port;
01990     (void)iolen;
01991         return dataregister;
01992 }
01993 
01994 void PRINTER_writedata(Bitu port,Bitu val,Bitu iolen)
01995 {
01996     (void)port;
01997     (void)iolen;
01998         dataregister = (Bit8u)val;
01999 }
02000 Bit8u controlreg = 0x04;
02001 
02002 Bitu PRINTER_readstatus(Bitu port,Bitu iolen)
02003 {
02004     (void)port;
02005     (void)iolen;
02006         //LOG_MSG("PRINTER_readstatus CS:IP %8x:%8x",SegValue(cs),reg_eip);
02007         // Don't create a CPrinter unless the program really wants to print
02008         // Return standard: No error, printer online, no ack and not busy
02009         if (!defaultPrinter)
02010                 return 0xdf;
02011 
02012         // Printer is always online and never reports an error
02013         Bit8u status = 0x1f;// 0x18;
02014 
02015 //      if (controlreg&0x08==0)
02016 //              status |= 0x10;
02017 
02018         if (!defaultPrinter->isBusy())
02019                 status |= 0x80;
02020 
02021         if (!defaultPrinter->ack())
02022                 status |= 0x40;
02023 
02024         return status;
02025 }
02026 
02027 static void FormFeed(bool pressed)
02028 {
02029         if (pressed)
02030                 if (defaultPrinter)
02031         {
02032                         PIC_RemoveEvents(PRINTER_EventHandler);
02033                         if (printer_timout) timeout_dirty = false;
02034                         
02035                         defaultPrinter->formFeed();
02036                 }
02037 }
02038 
02039 
02040 static void PRINTER_EventHandler(Bitu param)
02041 {
02042     (void)param;
02043         //LOG_MSG("printerevent");
02044         if (timeout_dirty)
02045     {
02046         // add another
02047                 PIC_AddEvent(PRINTER_EventHandler, (float)printer_timout, 0);
02048                 //LOG_MSG("timeout renew");
02049                 timeout_dirty = false;
02050         }
02051     else
02052     {
02053                 timeout_dirty = false;
02054                 FormFeed(true);
02055         }
02056 }
02057 
02058 void PRINTER_writecontrol(Bitu port,Bitu val,Bitu iolen)
02059 {
02060     (void)port;
02061     (void)iolen;
02062         //LOG_MSG("PRINTER_writecontrol CS:IP %8x:%8x",SegValue(cs),reg_eip);
02063         // init printer if bit 4 is switched on
02064         if ((val & 0x04) && defaultPrinter && (!(controlreg & 0x04)))
02065                 defaultPrinter->resetPrinterHard();
02066 
02067         // data is strobed to the parallel printer on the falling edge of strobe bit
02068         if(!(val & 0x1) && (controlreg & 0x1)) {
02069                 if (!defaultPrinter)
02070                     defaultPrinter = new CPrinter(confdpi, confwidth, confheight, confoutputDevice, confmultipageOutput);
02071                 defaultPrinter->printChar(dataregister);
02072                 if (!timeout_dirty)
02073         {
02074                         PIC_AddEvent(PRINTER_EventHandler, (float)printer_timout, 0);
02075                         timeout_dirty = true;
02076                 }
02077         }
02078 
02079         controlreg = (Bit8u)val;
02080         if (defaultPrinter)
02081                 defaultPrinter->setAutofeed((val & 0x02) > 0);
02082 }
02083 
02084 Bitu PRINTER_readcontrol(Bitu port,Bitu iolen)
02085 {
02086     (void)port;
02087     (void)iolen;
02088         //LOG_MSG("PRINTER_readcontrol CS:IP %8x:%8x",SegValue(cs),reg_eip);
02089         // Don't create a CPrinter unless the program really wants to print
02090         if (!defaultPrinter)
02091                 return 0xe0 | controlreg; //0xe8;
02092 
02093         return 0xe0 | (defaultPrinter->getAutofeed() ? 0x02 : 0x00) | (controlreg & 0xfd);
02094 }
02095 
02096 void PRINTER_Shutdown(Section* sec)
02097 {
02098     (void)sec;//UNUSED
02099 
02100         if (defaultPrinter)
02101         {
02102                 delete defaultPrinter;
02103                 defaultPrinter = NULL;
02104         }
02105 }
02106 
02107 bool inited = false;
02108 bool PRINTER_isInited()
02109 {
02110         return inited;
02111 }
02112 
02113 void PRINTER_Init() 
02114 {
02115         Section_prop * section = static_cast<Section_prop *>(control->GetSection("printer"));
02116     AddExitFunction(AddExitFunctionFuncPair(PRINTER_Shutdown), true);
02117 
02118         // Set base address of LPT1 in the BIOS variable segment
02119         //real_writew(0x0040, 0x0008, LPTPORT);
02120 
02121         if (!section->Get_bool("printer")) return;
02122         inited = true;
02123         document_path = section->Get_string("docpath");
02124         //font_path = section->Get_string("fontpath");
02125         confdpi = section->Get_int("dpi");
02126         confwidth = section->Get_int("width");
02127         confheight = section->Get_int("height");
02128         printer_timout = section->Get_int("timeout");
02129         if(!printer_timout) timeout_dirty = true; // this will lock up the timeout
02130         else timeout_dirty = false;
02131         strcpy(&confoutputDevice[0], section->Get_string("printoutput"));
02132         confmultipageOutput = section->Get_bool("multipage");
02133 
02134         //IO_RegisterWriteHandler(LPTPORT,PRINTER_writedata,IO_MB);
02135         //IO_RegisterReadHandler(LPTPORT,PRINTER_readdata,IO_MB);
02136         
02137         //IO_RegisterReadHandler(LPTPORT+1,PRINTER_readstatus,IO_MB);
02138         //IO_RegisterWriteHandler(LPTPORT+2,PRINTER_writecontrol,IO_MB);
02139         //IO_RegisterReadHandler(LPTPORT+2,PRINTER_readcontrol,IO_MB);
02140 
02141         MAPPER_AddHandler(FormFeed, MK_f2 , MMOD1, "ejectpage", "formfeed");
02142 }
02143 
02144 #endif