/* Part of XPCE --- The SWI-Prolog GUI toolkit Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ Copyright (c) 1998-2011, University of Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "include.h" #include /* file access */ #define USE_PRINTDLG 1 #ifdef USE_PRINTDLG #undef PAGESETUPDLG #define PAGESETUPDLG PRINTDLG #endif #define WinError() strName(WinStrError(GetLastError())) Class ClassWinPrinter; /* the class handle */ typedef struct win_printer *WinPrinter; typedef struct { HDC hdc; /* Device hdc */ #ifdef USE_PRINTDLG PRINTDLG info; #else PAGESETUPDLG info; /* Window page setup data */ #endif } ws_printer, *WsPrinter; NewClass(win_printer) /* class structure */ Name map_mode; /* Mapping mode */ Name job_name; /* Job Name */ Int job; /* Job Id */ Tuple range; /* Range of pages to print */ Any resolution; /* Resolution of the device */ Point origin; /* Origin of the coordinate system */ Any device; /* Output device */ WsPrinter ws_ref; /* Windows data */ End; static Chain WinPrinters; /* @win_printers */ static status closeWinPrinter(WinPrinter prt); static int nameToMapMode(Name name); static Name mapModeToName(int mode); static status mapModeWinPrinter(WinPrinter prt, Name mode); static status initialiseWinPrinter(WinPrinter prt, Name jobname) { if ( isDefault(jobname) ) jobname = CtoName("XPCE"); assign(prt, map_mode, DEFAULT); assign(prt, device, DEFAULT); assign(prt, job_name, jobname); assign(prt, job, NIL); assign(prt, resolution, DEFAULT); assign(prt, origin, newObject(ClassPoint, EAV)); prt->ws_ref = alloc(sizeof(ws_printer)); memset(prt->ws_ref, 0, sizeof(ws_printer)); return prependChain(WinPrinters, prt); } static void resetDataWinPrinter(WinPrinter prt) { if ( prt->ws_ref ) { PAGESETUPDLG *psd = &prt->ws_ref->info; if ( prt->ws_ref->hdc ) { DeleteDC(prt->ws_ref->hdc); prt->ws_ref->hdc = 0; } if ( psd->hDevMode ) GlobalFree(psd->hDevMode); memset(prt->ws_ref, 0, sizeof(ws_printer)); } } static status unlinkWinPrinter(WinPrinter prt) { closeWinPrinter(prt); resetDataWinPrinter(prt); if ( prt->ws_ref ) { unalloc(sizeof(ws_printer), prt->ws_ref); prt->ws_ref = NULL; } deleteChain(WinPrinters, prt); succeed; } static void closeAllWinPrinters(int rval) { if ( WinPrinters ) { Cell cell; for_cell(cell, WinPrinters) { WinPrinter prt = cell->value; closeWinPrinter(prt); } } } /******************************* * OPEN * *******************************/ static status setupWinPrinter(WinPrinter prt, FrameObj fr) { HWND hwnd = (isDefault(fr) ? 0 : getHwndFrame(fr)); Name mm; PAGESETUPDLG *psd = &prt->ws_ref->info; resetDataWinPrinter(prt); psd->lStructSize = sizeof(PAGESETUPDLG); psd->hwndOwner = hwnd; /* psd->hDevMode; psd->hDevNames; psd->Flags; */ #ifdef USE_PRINTDLG psd->Flags = (PD_ALLPAGES| PD_RETURNDC| PD_NOSELECTION| PD_USEDEVMODECOPIESANDCOLLATE); if ( notNil(prt->range) ) { psd->nFromPage = valInt(prt->range->first); psd->nToPage = valInt(prt->range->second); } else psd->Flags |= PD_NOPAGENUMS; /* Keep them here are reminder psd->hDC; psd->Flags; psd->nFromPage; psd->nToPage; psd->nMinPage; psd->nMaxPage; psd->nCopies; psd->hInstance; psd->lCustData; psd->lpfnPrintHook; psd->lpfnSetupHook; psd->lpPrintTemplateName; psd->lpSetupTemplateName; psd->hPrintTemplate; psd->hSetupTemplate; */ psd->nCopies = 1; #else /* psd->ptPaperSize; psd->rtMinMargin; psd->rtMargin; psd->hInstance; psd->lCustData; psd->lpfnPageSetupHook; psd->lpfnPagePaintHook; psd->lpPageSetupTemplateName; psd->hPageSetupTemplate; */ #endif if ( isName(prt->device) ) /* default printer name */ { char *s = strName(prt->device); HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, sizeof(DEVNAMES)+strlen(s)+1); DEVNAMES *names = GlobalLock(h); names->wDriverOffset = 0; /* i.e. "WINSPOOL" */ names->wDeviceOffset = sizeof(DEVNAMES); names->wOutputOffset = 0; /* port (or "FILE:") */ names->wDefault = 0; strcpy((char *)&names[1], s); GlobalUnlock(h); psd->hDevNames = h; } #ifdef USE_PRINTDLG if ( !PrintDlg(psd) ) #else if ( !PageSetupDlg(psd) ) #endif { DWORD rc; if ( (rc=CommDlgExtendedError()) ) { /* TBD: define kernel error and pass Windows message */ send(CurrentDisplay(NIL), NAME_report, NAME_error, CtoString("Unable to set up printer"), EAV); } fail; } if ( psd->hDevNames ) /* debugging */ { DEVNAMES *names = GlobalLock(psd->hDevNames); if ( names->wDriverOffset ) DEBUG(NAME_print, Cprintf("driver=%s\n", (char *)names+names->wDriverOffset)); if ( names->wDeviceOffset ) { assign(prt, device, CtoName((char *)names+names->wDeviceOffset)); DEBUG(NAME_print, Cprintf("dev=%s\n", (char *)names+names->wDeviceOffset)); } if ( names->wOutputOffset ) DEBUG(NAME_print, Cprintf("output=%s\n", (char *)names+names->wOutputOffset)); GlobalUnlock(psd->hDevNames); } #ifndef USE_PRINTDLG if ( psd->hDevMode ) { DEVMODE *dmode = GlobalLock(psd->hDevMode); prt->ws_ref->hdc = CreateDC("WINSPOOL", /* NT, NULL for Windows-95 */ dmode->dmDeviceName, NULL, psd->hDevMode); GlobalUnlock(psd->hDevMode); } #else prt->ws_ref->hdc = psd->hDC; if ( notNil(prt->range) && (psd->Flags & PD_PAGENUMS) ) { assign(prt->range, first, toInt(psd->nFromPage)); assign(prt->range, second, toInt(psd->nToPage)); } #endif if ( !prt->ws_ref->hdc ) { Cprintf("Failed to open Printer DC: %s\n", WinError()); fail; } SetBkMode(prt->ws_ref->hdc, TRANSPARENT); if ( (mm = mapModeToName(GetMapMode(prt->ws_ref->hdc))) ) assign(prt, map_mode, mm); DEBUG(NAME_print, Cprintf("Got MM %s from HDC=%p\n", pp(mm), prt->ws_ref->hdc)); succeed; } static status openWinPrinter(WinPrinter prt) { if ( prt->ws_ref->hdc ) { DOCINFO di; int job; char fname[PATH_MAX]; di.cbSize = sizeof(di); di.lpszDocName = strName(prt->job_name); if ( instanceOfObject(prt->device, ClassFile) ) { Name name = getOsNameFile(prt->device); if ( !_xos_os_filename(strName(name), fname, sizeof(fname)) ) return errorPce(prt, NAME_representation, NAME_nameTooLong); di.lpszOutput = fname; } else if ( prt->ws_ref->info.Flags & PD_PRINTTOFILE ) /* User selected print-to-file */ #if 1 { Name to; static Chain filter; if ( !filter ) filter = globalObject(NAME_printFileFilter, ClassChain, newObject(ClassTuple, NAME_printFile, CtoName("*.prn"), EAV), newObject(ClassTuple, NAME_allFiles, CtoName("*.*"), EAV), EAV); if ( (to = getWinFileNameDisplay(CurrentDisplay(NIL), NAME_save, filter, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)) ) { if ( !_xos_os_filename(strName(to), fname, sizeof(fname)) ) return errorPce(prt, NAME_representation, NAME_nameTooLong); di.lpszOutput = fname; } else fail; /* Use cancel */ #else { PAGESETUPDLG *psd = &prt->ws_ref->info; DEVNAMES *names = GlobalLock(psd->hDevNames); strcpy(fname, &((char *)names)[names->wOutputOffset]); di.lpszOutput = fname; DEBUG(NAME_print, Cprintf("Printing to file: \"%s\"\n", fname)); GlobalUnlock(psd->hDevNames); #endif } else di.lpszOutput = NULL; di.lpszDatatype = NULL; di.fwType = 0; if ( (job=StartDoc(prt->ws_ref->hdc, &di) > 0) && StartPage(prt->ws_ref->hdc) > 0 ) { if ( notDefault(prt->map_mode) ) SetMapMode(prt->ws_ref->hdc, nameToMapMode(prt->map_mode)); assign(prt, job, toInt(job)); succeed; } } fail; } static status nextPageWinPrinter(WinPrinter prt) { if ( prt->ws_ref->hdc ) { EndPage(prt->ws_ref->hdc); StartPage(prt->ws_ref->hdc); if ( notDefault(prt->map_mode) ) /* Windows 95 resets this */ SetMapMode(prt->ws_ref->hdc, nameToMapMode(prt->map_mode)); succeed; } fail; } static status closeWinPrinter(WinPrinter prt) { if ( notNil(prt->job) && prt->ws_ref->hdc ) { EndPage(prt->ws_ref->hdc); EndDoc(prt->ws_ref->hdc); assign(prt, job, NIL); } succeed; } /******************************* * SET DIMENSIONS * *******************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A long search has resulted in these magical formulas to relate the Physical (Dx/Dy) and Logical (Lx/Ly) properly. Dx = (Lx - xWO) * xVE/xWE + xVO Dy = (Ly - yWO) * yVE/yWE + yVO Lx = (Dx - xVO) * xWE/xVE + xWO Ly = (Dy - yVO) * yWE/yVE + yWO If the resolution is @default, ->resolution sets the scaling such that the physical dimensions on the screen are the same as on the paper based on the information on printer- and screen resolution. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static status resolutionWinPrinter(WinPrinter prt, Any resolution) { if ( prt->resolution != resolution ) { assign(prt, resolution, resolution); if ( isDefault(resolution) || isInteger(resolution) ) mapModeWinPrinter(prt, NAME_isotropic); else mapModeWinPrinter(prt, NAME_anisotropic); if ( prt->ws_ref->hdc ) { int w, h; HDC hdc = prt->ws_ref->hdc; HDC shdc = GetDC(NULL); int rx = GetDeviceCaps(hdc, LOGPIXELSX); int ry = GetDeviceCaps(hdc, LOGPIXELSY); int srx = GetDeviceCaps(shdc, LOGPIXELSX); int sry = GetDeviceCaps(shdc, LOGPIXELSY); int ox = valInt(prt->origin->x); int oy = valInt(prt->origin->y); ReleaseDC(NULL, shdc); DEBUG(NAME_print, Cprintf("Resolution = %d x %d, Screen = %d x %d\n", rx, ry, srx, sry)); SetWindowOrgEx(hdc, ox, oy, NULL); if ( isDefault(resolution) ) { SetViewportOrgEx(hdc, 0, 0, NULL); SetWindowExtEx(hdc, srx, sry, NULL); SetViewportExtEx(hdc, rx, ry, NULL); } else { int pw = GetDeviceCaps(hdc, PHYSICALWIDTH); int ph = GetDeviceCaps(hdc, PHYSICALHEIGHT); if ( isInteger(resolution) ) { int pw = GetDeviceCaps(hdc, PHYSICALWIDTH); int ph = GetDeviceCaps(hdc, PHYSICALHEIGHT); h = valInt(resolution); w = (h*pw)/ph; } else { Size sz = resolution; h = valInt(sz->h); w = valInt(sz->w); } SetViewportOrgEx(hdc, 0, 0, NULL); SetWindowExtEx(hdc, w, h, NULL); SetViewportExtEx(hdc, pw, ph, NULL); } } } succeed; } static status originWinPrinter(WinPrinter prt, Point origin) { if ( !equalPoint(origin, prt->origin) ) { copyPoint(prt->origin, origin); if ( prt->ws_ref->hdc ) { int x = valInt(prt->origin->x); int y = valInt(prt->origin->y); HDC hdc = prt->ws_ref->hdc; SetWindowOrgEx(hdc, x, y, NULL); SetViewportOrgEx(hdc, 0, 0, NULL); /* needed? */ } } succeed; } static status viewportWinPrinter(WinPrinter prt, Area vp) { HDC hdc = prt->ws_ref->hdc; if ( hdc ) { int x = valInt(vp->x); int y = valInt(vp->y); int w = valInt(vp->w); int h = valInt(vp->h); SetViewportOrgEx(hdc, x, y, NULL); SetViewportExtEx(hdc, w, h, NULL); succeed; } fail; } static status windowWinPrinter(WinPrinter prt, Area ww) { HDC hdc = prt->ws_ref->hdc; if ( hdc ) { int x = valInt(ww->x); int y = valInt(ww->y); int w = valInt(ww->w); int h = valInt(ww->h); SetWindowOrgEx(hdc, x, y, NULL); SetWindowExtEx(hdc, w, h, NULL); succeed; } fail; } static status rangeWinPrinter(WinPrinter prt, Tuple range) { if ( isNil(range) || (isInteger(range->first) && isInteger(range->second)) ) { assign(prt, range, range); succeed; } return errorPce(range, NAME_unexpectedType, nameToType(NAME_tuple)); } /******************************* * INFO * *******************************/ static Size getSizeWinPrinter(WinPrinter prt) { if ( prt->ws_ref->hdc ) { int w = GetDeviceCaps(prt->ws_ref->hdc, PHYSICALWIDTH); int h = GetDeviceCaps(prt->ws_ref->hdc, PHYSICALHEIGHT); answer(answerObject(ClassSize, toInt(w), toInt(h), EAV)); } fail; } static Point getOffsetWinPrinter(WinPrinter prt) { if ( prt->ws_ref->hdc ) { int x = GetDeviceCaps(prt->ws_ref->hdc, PHYSICALOFFSETX); int y = GetDeviceCaps(prt->ws_ref->hdc, PHYSICALOFFSETY); answer(answerObject(ClassPoint, toInt(x), toInt(y), EAV)); } fail; } static Size getDotsPerInchWinPrinter(WinPrinter prt) { if ( prt->ws_ref->hdc ) { int x = GetDeviceCaps(prt->ws_ref->hdc, LOGPIXELSX); int y = GetDeviceCaps(prt->ws_ref->hdc, LOGPIXELSY); answer(answerObject(ClassSize, toInt(x), toInt(y), EAV)); } fail; } typedef struct { int mode; Name name; } mapmode; static mapmode map[] = { { MM_ANISOTROPIC, NAME_anisotropic }, { MM_HIENGLISH, NAME_hienglish }, { MM_HIMETRIC, NAME_himetric }, { MM_ISOTROPIC, NAME_isotropic }, { MM_LOENGLISH, NAME_loenglish }, { MM_TEXT, NAME_text }, { MM_TWIPS, NAME_twips }, { -1, NULL } }; static int nameToMapMode(Name name) { mapmode *m = map; for(; m->name; m++) { if ( m->name == name ) return m->mode; } return -1; } static Name mapModeToName(int mode) { mapmode *m = map; for(; m->name; m++) { if ( m->mode == mode ) return m->name; } return NULL; } static status mapModeWinPrinter(WinPrinter prt, Name mode) { if ( prt->map_mode != mode ) { assign(prt, map_mode, mode); if ( prt->ws_ref->hdc ) { if ( !SetMapMode(prt->ws_ref->hdc, nameToMapMode(mode)) ) { Cprintf("Failed SetMapMode(%s): %s\n", strName(mode), WinError()); fail; } } } succeed; } /******************************* * DRAWING * *******************************/ static status drawInWinPrinter(WinPrinter prt, Any obj, Point pos) { Int oldx, oldy; Device dev; Area bb; if ( notDefault(pos) && instanceOfObject(obj, ClassGraphical) ) { Graphical gr = obj; oldx = gr->area->x; oldy = gr->area->y; dev = gr->device; gr->device = NIL; setGraphical(gr, pos->x, pos->y, DEFAULT, DEFAULT); } else { oldx = oldy = DEFAULT; dev = NIL; /* keep compiler happy */ } if ( instanceOfObject(obj, ClassGraphical) ) { Graphical gr = obj; ComputeGraphical(getRootGraphical(gr)); bb = gr->area; } else /* if ( instanceOfObject(obj, ClassChain) ) */ { Cell cell; bb = answerObject(ClassArea, EAV); for_cell(cell, (Chain)obj) { Graphical gr = cell->value; if ( instanceOfObject(gr, ClassGraphical) ) { ComputeGraphical(getRootGraphical(gr)); unionNormalisedArea(bb, gr->area); } } } d_hdc(prt->ws_ref->hdc, DEFAULT, DEFAULT, FALSE); d_offset(0,0); /* needed for proper init */ if ( instanceOfObject(obj, ClassGraphical) ) { Graphical gr = obj; RedrawArea(gr, gr->area); } else { Chain ch = obj; Cell cell; for_cell(cell, ch) { if ( instanceOfObject(cell->value, ClassGraphical) ) { Graphical gr = cell->value; RedrawArea(gr, gr->area); } } } d_done(); doneObject(bb); if ( notDefault(oldx) ) { Graphical gr = obj; setGraphical(gr, oldx, oldy, DEFAULT, DEFAULT); gr->device = dev; } succeed; } /******************************* * CLASS DECLARATION * *******************************/ /* Type declarations */ static char *T_drawIn[] = { "graphical|chain", "at=[point]" }; /* Instance Variables */ static vardecl var_winprinter[] = { SV(NAME_mapMode, "[{anisotropic,hienglish,himetric,isotropic,loenglish,text,twips}]", IV_GET|IV_STORE, mapModeWinPrinter, NAME_dimension, "Resolution and coordinate system"), IV(NAME_jobName, "name", IV_BOTH, NAME_name, "Public name of the print job"), IV(NAME_job, "int*", IV_GET, NAME_identifier, "Windows job-id for this print job"), SV(NAME_range, "tuple*", IV_GET|IV_STORE, rangeWinPrinter, NAME_selection, "Range of pages to print"), SV(NAME_resolution, "[int|size]", IV_GET|IV_STORE, resolutionWinPrinter, NAME_dimension, "Height/Size of the page in logical units"), SV(NAME_origin, "point", IV_GET|IV_STORE, originWinPrinter, NAME_dimension, "Origin for drawing"), IV(NAME_device, "[name|file]", IV_BOTH, NAME_output, "Default output device"), IV(NAME_wsRef, "alien:PRINTDLG *", IV_GET, NAME_internal, "Associated Windows data") }; /* Send Methods */ static senddecl send_winprinter[] = { SM(NAME_initialise, 1, "job_name=[name]", initialiseWinPrinter, DEFAULT, "Create Windows printer interface"), SM(NAME_unlink, 0, NULL, unlinkWinPrinter, DEFAULT, "Remove from @win_printers"), SM(NAME_setup, 1, "frame=[frame]", setupWinPrinter, NAME_property, "Query properties using standard dialog"), SM(NAME_viewport, 1, "area", viewportWinPrinter, NAME_dimension, "Set the physical area"), SM(NAME_window, 1, "area", windowWinPrinter, NAME_dimension, "Set the logical area"), SM(NAME_open, 0, NULL, openWinPrinter, NAME_open, "Start a document"), SM(NAME_close, 0, NULL, closeWinPrinter, NAME_open, "End a document"), SM(NAME_nextPage, 0, NULL, nextPageWinPrinter, NAME_print, "Advance to the next page"), SM(NAME_drawIn, 2, T_drawIn, drawInWinPrinter, NAME_print, "Paint graphical on printer") }; /* Get Methods */ static getdecl get_winprinter[] = { GM(NAME_size, 0, "size", NULL, getSizeWinPrinter, NAME_dimension, "Size of page in device units"), GM(NAME_offset, 0, "point", NULL, getOffsetWinPrinter, NAME_dimension, "Top-left-most position that can be printed"), GM(NAME_dotsPerInch, 0, "size", NULL, getDotsPerInchWinPrinter, NAME_dimension, "Resolution in dots per inch") }; /* Resources */ #define rc_winprinter NULL /* static classvardecl rc_winprinter[] = { }; */ /* Class Declaration */ static Name winprinter_termnames[] = { NAME_file }; ClassDecl(winprinter_decls, var_winprinter, send_winprinter, get_winprinter, rc_winprinter, 1, winprinter_termnames, "$Rev$"); status makeClassWinPrinter(Class class) { declareClass(class, &winprinter_decls); WinPrinters = globalObject(CtoName("win_printers"), ClassChain, EAV); at_pce_exit(closeAllWinPrinters, ATEXIT_FIFO); succeed; }