|  | /* | 
|  | *	PostScript driver Escape function | 
|  | * | 
|  | *	Copyright 1998  Huw D M Davies | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library 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 | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "wine/wingdi16.h" | 
|  | #include "wine/winuser16.h" | 
|  | #include "wownt32.h" | 
|  | #include "psdrv.h" | 
|  | #include "wine/debug.h" | 
|  | #include "winspool.h" | 
|  | #include "heap.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(psdrv); | 
|  |  | 
|  | static const char psbegindocument[] = | 
|  | "%%BeginDocument: Wine passthrough\n"; | 
|  |  | 
|  | /********************************************************************** | 
|  | *           ExtEscape  (WINEPS.@) | 
|  | */ | 
|  | INT PSDRV_ExtEscape( PSDRV_PDEVICE *physDev, INT nEscape, INT cbInput, LPCVOID in_data, | 
|  | INT cbOutput, LPVOID out_data ) | 
|  | { | 
|  | switch(nEscape) | 
|  | { | 
|  | case QUERYESCSUPPORT: | 
|  | if(cbInput < sizeof(INT)) | 
|  | { | 
|  | WARN("cbInput < sizeof(INT) (=%d) for QUERYESCSUPPORT\n", cbInput); | 
|  | return 0; | 
|  | } else { | 
|  | UINT num = *(const UINT *)in_data; | 
|  | TRACE("QUERYESCSUPPORT for %d\n", num); | 
|  |  | 
|  | switch(num) { | 
|  | case NEXTBAND: | 
|  | case SETCOPYCOUNT: | 
|  | case GETTECHNOLOGY: | 
|  | case SETLINECAP: | 
|  | case SETLINEJOIN: | 
|  | case SETMITERLIMIT: | 
|  | case SETCHARSET: | 
|  | case EXT_DEVICE_CAPS: | 
|  | case SET_BOUNDS: | 
|  | case EPSPRINTING: | 
|  | case POSTSCRIPT_DATA: | 
|  | case PASSTHROUGH: | 
|  | case POSTSCRIPT_PASSTHROUGH: | 
|  | case POSTSCRIPT_IGNORE: | 
|  | case BEGIN_PATH: | 
|  | case CLIP_TO_PATH: | 
|  | case END_PATH: | 
|  | return TRUE; | 
|  |  | 
|  | default: | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | case NEXTBAND: | 
|  | { | 
|  | RECT *r = out_data; | 
|  | if(!physDev->job.banding) { | 
|  | physDev->job.banding = TRUE; | 
|  | r->left   = 0; | 
|  | r->top    = 0; | 
|  | r->right  = physDev->horzRes; | 
|  | r->bottom = physDev->vertRes; | 
|  | TRACE("NEXTBAND returning %ld,%ld - %ld,%ld\n", r->left, r->top, r->right, r->bottom ); | 
|  | return 1; | 
|  | } | 
|  | r->left   = 0; | 
|  | r->top    = 0; | 
|  | r->right  = 0; | 
|  | r->bottom = 0; | 
|  | TRACE("NEXTBAND rect to 0,0 - 0,0\n" ); | 
|  | physDev->job.banding = FALSE; | 
|  | return EndPage( physDev->hdc ); | 
|  | } | 
|  |  | 
|  | case SETCOPYCOUNT: | 
|  | { | 
|  | const INT *NumCopies = in_data; | 
|  | INT *ActualCopies = out_data; | 
|  | if(cbInput != sizeof(INT)) { | 
|  | WARN("cbInput != sizeof(INT) (=%d) for SETCOPYCOUNT\n", cbInput); | 
|  | return 0; | 
|  | } | 
|  | TRACE("SETCOPYCOUNT %d\n", *NumCopies); | 
|  | *ActualCopies = 1; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | case GETTECHNOLOGY: | 
|  | { | 
|  | LPSTR p = out_data; | 
|  | strcpy(p, "PostScript"); | 
|  | *(p + strlen(p) + 1) = '\0'; /* 2 '\0's at end of string */ | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | case SETLINECAP: | 
|  | { | 
|  | INT newCap = *(const INT *)in_data; | 
|  | if(cbInput != sizeof(INT)) { | 
|  | WARN("cbInput != sizeof(INT) (=%d) for SETLINECAP\n", cbInput); | 
|  | return 0; | 
|  | } | 
|  | TRACE("SETLINECAP %d\n", newCap); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case SETLINEJOIN: | 
|  | { | 
|  | INT newJoin = *(const INT *)in_data; | 
|  | if(cbInput != sizeof(INT)) { | 
|  | WARN("cbInput != sizeof(INT) (=%d) for SETLINEJOIN\n", cbInput); | 
|  | return 0; | 
|  | } | 
|  | TRACE("SETLINEJOIN %d\n", newJoin); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case SETMITERLIMIT: | 
|  | { | 
|  | INT newLimit = *(const INT *)in_data; | 
|  | if(cbInput != sizeof(INT)) { | 
|  | WARN("cbInput != sizeof(INT) (=%d) for SETMITERLIMIT\n", cbInput); | 
|  | return 0; | 
|  | } | 
|  | TRACE("SETMITERLIMIT %d\n", newLimit); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case SETCHARSET: | 
|  | /* Undocumented escape used by winword6. | 
|  | Switches between ANSI and a special charset. | 
|  | If *lpInData == 1 we require that | 
|  | 0x91 is quoteleft | 
|  | 0x92 is quoteright | 
|  | 0x93 is quotedblleft | 
|  | 0x94 is quotedblright | 
|  | 0x95 is bullet | 
|  | 0x96 is endash | 
|  | 0x97 is emdash | 
|  | 0xa0 is non break space - yeah right. | 
|  |  | 
|  | If *lpInData == 0 we get ANSI. | 
|  | Since there's nothing else there, let's just make these the default | 
|  | anyway and see what happens... | 
|  | */ | 
|  | return 1; | 
|  |  | 
|  | case EXT_DEVICE_CAPS: | 
|  | { | 
|  | UINT cap = *(const UINT *)in_data; | 
|  | if(cbInput != sizeof(UINT)) { | 
|  | WARN("cbInput != sizeof(UINT) (=%d) for EXT_DEVICE_CAPS\n", cbInput); | 
|  | return 0; | 
|  | } | 
|  | TRACE("EXT_DEVICE_CAPS %d\n", cap); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case SET_BOUNDS: | 
|  | { | 
|  | const RECT *r = in_data; | 
|  | if(cbInput != sizeof(RECT)) { | 
|  | WARN("cbInput != sizeof(RECT) (=%d) for SET_BOUNDS\n", cbInput); | 
|  | return 0; | 
|  | } | 
|  | TRACE("SET_BOUNDS (%ld,%ld) - (%ld,%ld)\n", r->left, r->top, | 
|  | r->right, r->bottom); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case EPSPRINTING: | 
|  | { | 
|  | UINT epsprint = *(const UINT*)in_data; | 
|  | /* FIXME: In this mode we do not need to send page intros and page | 
|  | * ends according to the doc. But I just ignore that detail | 
|  | * for now. | 
|  | */ | 
|  | TRACE("EPS Printing support %sable.\n",epsprint?"en":"dis"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | case POSTSCRIPT_DATA: | 
|  | case PASSTHROUGH: | 
|  | case POSTSCRIPT_PASSTHROUGH: | 
|  | { | 
|  | /* Write directly to spool file, bypassing normal PS driver | 
|  | * processing that is done along with writing PostScript code | 
|  | * to the spool. | 
|  | * We have a WORD before the data counting the size, but | 
|  | * cbInput is just this +2. | 
|  | * However Photoshop 7 has a bug that sets cbInput to 2 less than the | 
|  | * length of the string, rather than 2 more.  So we'll use the WORD at | 
|  | * in_data[0] instead. | 
|  | */ | 
|  | if(!physDev->job.in_passthrough) { | 
|  | WriteSpool16(physDev->job.hJob, (LPSTR)psbegindocument, sizeof(psbegindocument)-1); | 
|  | physDev->job.in_passthrough = TRUE; | 
|  | } | 
|  | return WriteSpool16(physDev->job.hJob,((char*)in_data)+2,*(const WORD*)in_data); | 
|  | } | 
|  |  | 
|  | case POSTSCRIPT_IGNORE: | 
|  | { | 
|  | BOOL ret = physDev->job.quiet; | 
|  | TRACE("POSTSCRIPT_IGNORE %d\n", *(const short*)in_data); | 
|  | physDev->job.quiet = *(const short*)in_data; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | case GETSETPRINTORIENT: | 
|  | { | 
|  | /* If lpInData is present, it is a 20 byte structure, first 32 | 
|  | * bit LONG value is the orientation. if lpInData is NULL, it | 
|  | * returns the current orientation. | 
|  | */ | 
|  | FIXME("GETSETPRINTORIENT not implemented (data %p)!\n",in_data); | 
|  | return 1; | 
|  | } | 
|  | case BEGIN_PATH: | 
|  | TRACE("BEGIN_PATH\n"); | 
|  | if(physDev->pathdepth) | 
|  | FIXME("Nested paths not yet handled\n"); | 
|  | return ++physDev->pathdepth; | 
|  |  | 
|  | case END_PATH: | 
|  | { | 
|  | const struct PATH_INFO *info = (const struct PATH_INFO*)in_data; | 
|  |  | 
|  | TRACE("END_PATH\n"); | 
|  | if(!physDev->pathdepth) { | 
|  | ERR("END_PATH called without a BEIGN_PATH\n"); | 
|  | return -1; | 
|  | } | 
|  | TRACE("RenderMode = %d, FillMode = %d, BkMode = %d\n", | 
|  | info->RenderMode, info->FillMode, info->BkMode); | 
|  | switch(info->RenderMode) { | 
|  | case RENDERMODE_NO_DISPLAY: | 
|  | PSDRV_WriteClosePath(physDev); /* not sure if this is necessary, but it can't hurt */ | 
|  | break; | 
|  | case RENDERMODE_OPEN: | 
|  | case RENDERMODE_CLOSED: | 
|  | default: | 
|  | FIXME("END_PATH: RenderMode %d, not yet supported\n", info->RenderMode); | 
|  | break; | 
|  | } | 
|  | return --physDev->pathdepth; | 
|  | } | 
|  |  | 
|  | case CLIP_TO_PATH: | 
|  | { | 
|  | WORD mode = *(const WORD*)in_data; | 
|  |  | 
|  | switch(mode) { | 
|  | case CLIP_SAVE: | 
|  | TRACE("CLIP_TO_PATH: CLIP_SAVE\n"); | 
|  | PSDRV_WriteGSave(physDev); | 
|  | return 1; | 
|  | case CLIP_RESTORE: | 
|  | TRACE("CLIP_TO_PATH: CLIP_RESTORE\n"); | 
|  | PSDRV_WriteGRestore(physDev); | 
|  | return 1; | 
|  | case CLIP_INCLUSIVE: | 
|  | TRACE("CLIP_TO_PATH: CLIP_INCLUSIVE\n"); | 
|  | /* FIXME to clip or eoclip ? (see PATH_INFO.FillMode) */ | 
|  | PSDRV_WriteClip(physDev); | 
|  | PSDRV_WriteNewPath(physDev); | 
|  | return 1; | 
|  | case CLIP_EXCLUSIVE: | 
|  | FIXME("CLIP_EXCLUSIVE: not implemented\n"); | 
|  | return 0; | 
|  | default: | 
|  | FIXME("Unknown CLIP_TO_PATH mode %d\n", mode); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | default: | 
|  | FIXME("Unimplemented code 0x%x\n", nEscape); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *           PSDRV_StartPage | 
|  | */ | 
|  | INT PSDRV_StartPage( PSDRV_PDEVICE *physDev ) | 
|  | { | 
|  | if(!physDev->job.OutOfPage) { | 
|  | FIXME("Already started a page?\n"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if(physDev->job.PageNo++ == 0) { | 
|  | if(!PSDRV_WriteHeader( physDev, physDev->job.DocName )) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if(!PSDRV_WriteNewPage( physDev )) | 
|  | return 0; | 
|  | physDev->job.OutOfPage = FALSE; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************ | 
|  | *           PSDRV_EndPage | 
|  | */ | 
|  | INT PSDRV_EndPage( PSDRV_PDEVICE *physDev ) | 
|  | { | 
|  | if(physDev->job.OutOfPage) { | 
|  | FIXME("Already ended a page?\n"); | 
|  | return 1; | 
|  | } | 
|  | if(!PSDRV_WriteEndPage( physDev )) | 
|  | return 0; | 
|  | PSDRV_EmptyDownloadList(physDev, FALSE); | 
|  | physDev->job.OutOfPage = TRUE; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************ | 
|  | *           PSDRV_StartDocA | 
|  | */ | 
|  | INT PSDRV_StartDocA( PSDRV_PDEVICE *physDev, const DOCINFOA *doc ) | 
|  | { | 
|  | LPCSTR output = "LPT1:"; | 
|  | BYTE buf[300]; | 
|  | HANDLE hprn = INVALID_HANDLE_VALUE; | 
|  | PRINTER_INFO_5A *pi5 = (PRINTER_INFO_5A*)buf; | 
|  | DWORD needed; | 
|  |  | 
|  | if(physDev->job.hJob) { | 
|  | FIXME("hJob != 0. Now what?\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if(doc->lpszOutput) | 
|  | output = doc->lpszOutput; | 
|  | else if(physDev->job.output) | 
|  | output = physDev->job.output; | 
|  | else { | 
|  | if(OpenPrinterA(physDev->pi->FriendlyName, &hprn, NULL) && | 
|  | GetPrinterA(hprn, 5, buf, sizeof(buf), &needed)) { | 
|  | output = pi5->pPortName; | 
|  | } | 
|  | if(hprn != INVALID_HANDLE_VALUE) | 
|  | ClosePrinter(hprn); | 
|  | } | 
|  |  | 
|  | physDev->job.hJob = OpenJob16(output,  doc->lpszDocName, HDC_16(physDev->hdc) ); | 
|  | if(!physDev->job.hJob) { | 
|  | WARN("OpenJob failed\n"); | 
|  | return 0; | 
|  | } | 
|  | physDev->job.banding = FALSE; | 
|  | physDev->job.OutOfPage = TRUE; | 
|  | physDev->job.PageNo = 0; | 
|  | physDev->job.quiet = FALSE; | 
|  | physDev->job.in_passthrough = FALSE; | 
|  | physDev->job.had_passthrough_rect = FALSE; | 
|  | if(doc->lpszDocName) { | 
|  | physDev->job.DocName = HeapAlloc(GetProcessHeap(), 0, strlen(doc->lpszDocName)+1); | 
|  | strcpy(physDev->job.DocName, doc->lpszDocName); | 
|  | } else | 
|  | physDev->job.DocName = NULL; | 
|  |  | 
|  | return physDev->job.hJob; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *           PSDRV_StartDoc | 
|  | */ | 
|  | INT PSDRV_StartDoc( PSDRV_PDEVICE *physDev, const DOCINFOW *doc ) | 
|  | { | 
|  | DOCINFOA docA; | 
|  | INT ret; | 
|  |  | 
|  | docA.cbSize = doc->cbSize; | 
|  | docA.lpszDocName = doc->lpszDocName ? | 
|  | HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDocName ) : NULL; | 
|  | docA.lpszOutput = doc->lpszOutput ? | 
|  | HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszOutput ) : NULL; | 
|  | docA.lpszDatatype = doc->lpszDatatype ? | 
|  | HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDatatype ) : NULL; | 
|  | docA.fwType = doc->fwType; | 
|  |  | 
|  | ret = PSDRV_StartDocA(physDev, &docA); | 
|  |  | 
|  | HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDocName ); | 
|  | HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszOutput ); | 
|  | HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDatatype ); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *           PSDRV_EndDoc | 
|  | */ | 
|  | INT PSDRV_EndDoc( PSDRV_PDEVICE *physDev ) | 
|  | { | 
|  | INT ret = 1; | 
|  | if(!physDev->job.hJob) { | 
|  | FIXME("hJob == 0. Now what?\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if(!physDev->job.OutOfPage) { | 
|  | WARN("Somebody forgot an EndPage\n"); | 
|  | PSDRV_EndPage( physDev ); | 
|  | } | 
|  | PSDRV_WriteFooter( physDev ); | 
|  |  | 
|  | if( CloseJob16( physDev->job.hJob ) == SP_ERROR ) { | 
|  | WARN("CloseJob error\n"); | 
|  | ret = 0; | 
|  | } | 
|  | physDev->job.hJob = 0; | 
|  | HeapFree(GetProcessHeap(), 0, physDev->job.DocName); | 
|  | physDev->job.DocName = NULL; | 
|  |  | 
|  | return ret; | 
|  | } |