/* Copyright (C) 2001 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gp_msprn.c,v 1.1.2.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* %printer% IODevice */ #include "windows_.h" #include "errno_.h" #include "stdio_.h" #include "string_.h" #include "ctype_.h" #include "fcntl_.h" #include #include "gp.h" #include "gscdefs.h" #include "gserrors.h" #include "gserror.h" #include "gstypes.h" #include "gsmemory.h" /* for gxiodev.h */ #include "gxiodev.h" /* The MS-Windows printer IODevice */ /* * This allows a MS-Windows printer to be specified as an * output using * -sOutputFile="%printer%HP DeskJet 500" * * To write to a remote printer on another server * -sOutputFile="%printer%\\server\printer name" * * If you don't supply a printer name you will get * Error: /undefinedfilename in --.outputpage-- * If the printer name is invalid you will get * Error: /invalidfileaccess in --.outputpage-- * * This is implemented by returning the file pointer * for the write end of a pipe, and starting a thread * which reads the pipe and writes to a Windows printer. * This will not work in Win32s. * * The old method provided by gp_open_printer() * -sOutputFile="\\spool\HP DeskJet 500" * should not be used except on Win32s. * The "\\spool\" is not a UNC name and causes confusion. */ private iodev_proc_init(mswin_printer_init); private iodev_proc_fopen(mswin_printer_fopen); private iodev_proc_fclose(mswin_printer_fclose); const gx_io_device gs_iodev_printer = { "%printer%", "FileSystem", {mswin_printer_init, iodev_no_open_device, NULL /*iodev_os_open_file */ , mswin_printer_fopen, mswin_printer_fclose, iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status, iodev_no_enumerate_files, NULL, NULL, iodev_no_get_params, iodev_no_put_params } }; typedef struct tid_s { unsigned long tid; } tid_t; void mswin_printer_thread(void *arg) { int fd = (int)arg; char pname[gp_file_name_sizeof]; char data[4096]; HANDLE hprinter = INVALID_HANDLE_VALUE; int count; DWORD written; DOC_INFO_1 di; /* Read from pipe and write to Windows printer. * First gp_file_name_sizeof bytes are name of the printer. */ if (read(fd, pname, sizeof(pname)) != sizeof(pname)) { /* Didn't get the printer name */ close(fd); return; } while ( (count = read(fd, data, sizeof(data))) > 0 ) { if (hprinter == INVALID_HANDLE_VALUE) { if (!OpenPrinter(pname, &hprinter, NULL)) { close(fd); return; } di.pDocName = (LPTSTR)gs_product; di.pOutputFile = NULL; di.pDatatype = "RAW"; if (!StartDocPrinter(hprinter, 1, (LPBYTE) & di)) { AbortPrinter(hprinter); close(fd); return; } } if (!WritePrinter(hprinter, (LPVOID) data, count, &written)) { AbortPrinter(hprinter); close(fd); return; } } if (hprinter != INVALID_HANDLE_VALUE) { if (count == 0) { /* EOF */ EndDocPrinter(hprinter); ClosePrinter(hprinter); } else { /* Error */ AbortPrinter(hprinter); } } close(fd); } /* The file device procedures */ private int mswin_printer_init(gx_io_device * iodev, gs_memory_t * mem) { /* state -> structure containing thread handle */ iodev->state = gs_alloc_bytes(mem, sizeof(tid_t), "mswin_printer_init"); if (iodev->state == NULL) return_error(gs_error_VMerror); ((tid_t *)iodev->state)->tid = -1; return 0; } private int mswin_printer_fopen(gx_io_device * iodev, const char *fname, const char *access, FILE ** pfile, char *rfname, uint rnamelen) { DWORD version = GetVersion(); HANDLE hprinter; int pipeh[2]; unsigned long tid; char pname[gp_file_name_sizeof]; unsigned long *ptid = &((tid_t *)(iodev->state))->tid; /* Win32s supports neither pipes nor Win32 printers. */ if (((HIWORD(version) & 0x8000) != 0) && ((HIWORD(version) & 0x4000) == 0)) return_error(gs_error_invalidfileaccess); /* Make sure that printer exists. */ if (!OpenPrinter((LPTSTR)fname, &hprinter, NULL)) return_error(gs_error_invalidfileaccess); ClosePrinter(hprinter); if (iodev->state == NULL) return_error(gs_error_invalidfileaccess); /* Create a pipe to connect a FILE pointer to a Windows printer. */ if (_pipe(pipeh, 4096, _O_BINARY) != 0) return_error(gs_fopen_errno_to_code(errno)); *pfile = fdopen(pipeh[1], (char *)access); if (*pfile == NULL) { close(pipeh[0]); close(pipeh[1]); return_error(gs_fopen_errno_to_code(errno)); } /* start a thread to read the pipe */ *ptid = _beginthread(&mswin_printer_thread, 32768, pipeh[0]); if (*ptid == -1) { fclose(*pfile); close(pipeh[0]); return_error(gs_error_invalidfileaccess); } /* Give the name of the printer to the thread by writing * it to the pipe. This is avoids elaborate thread * synchronisation code. */ strncpy(pname, fname, sizeof(pname)); fwrite(pname, 1, sizeof(pname), *pfile); return 0; } private int mswin_printer_fclose(gx_io_device * iodev, FILE * file) { unsigned long *ptid = &((tid_t *)(iodev->state))->tid; HANDLE hthread; fclose(file); if ((iodev->state != NULL) && (*ptid != -1)) { /* Wait until the print thread finishes before continuing */ hthread = (HANDLE)*ptid; WaitForSingleObject(hthread, 60000); CloseHandle(hthread); *ptid = -1; } return 0; }