| /* | 
 |  *	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; | 
 | } |