| /* | |
| * 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" | |
| 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 = *(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 = *(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 = *(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 = *(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 = *(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 = *(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,*(WORD*)in_data); | |
| } | |
| case POSTSCRIPT_IGNORE: | |
| { | |
| BOOL ret = physDev->job.quiet; | |
| TRACE("POSTSCRIPT_IGNORE %d\n", *(short*)in_data); | |
| physDev->job.quiet = *(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: | |
| { | |
| struct PATH_INFO *info = (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 = *(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; | |
| } | |
| 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_StartDoc | |
| */ | |
| INT PSDRV_StartDoc( 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_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 a 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; | |
| if(physDev->job.DocName) { | |
| HeapFree(GetProcessHeap(), 0, physDev->job.DocName); | |
| physDev->job.DocName = NULL; | |
| } | |
| return ret; | |
| } |