/* PostScript Printer Description (PPD) file parser * * See http://www.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf * * 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 "config.h" #include #include #include #include #include #include "windef.h" #include "winbase.h" #include "wine/debug.h" #include "psdrv.h" #include "winspool.h" WINE_DEFAULT_DEBUG_CHANNEL(psdrv); typedef struct { char *key; char *option; char *opttrans; char *value; char *valtrans; } PPDTuple; /* map of page names in ppd file to Windows paper constants */ static const struct { const char *PSName; WORD WinPage; } PageTrans[] = { {"10x11", DMPAPER_10X11}, {"10x14", DMPAPER_10X14}, {"11x17", DMPAPER_11X17}, /* not in Adobe PPD file spec */ {"12x11", DMPAPER_12X11}, {"15x11", DMPAPER_15X11}, {"9x11", DMPAPER_9X11}, {"A2", DMPAPER_A2}, {"A3", DMPAPER_A3}, {"A3.Transverse", DMPAPER_A3_TRANSVERSE}, {"A3Extra", DMPAPER_A3_EXTRA}, {"A3Extra.Transverse", DMPAPER_A3_EXTRA_TRANSVERSE}, {"A3Rotated", DMPAPER_A3_ROTATED}, {"A4", DMPAPER_A4}, {"A4.Transverse", DMPAPER_A4_TRANSVERSE}, {"A4Extra", DMPAPER_A4_EXTRA}, {"A4Plus", DMPAPER_A4_PLUS}, {"A4Rotated", DMPAPER_A4_ROTATED}, {"A4Small", DMPAPER_A4SMALL}, {"A5", DMPAPER_A5}, {"A5.Transverse", DMPAPER_A5_TRANSVERSE}, {"A5Extra", DMPAPER_A5_EXTRA}, {"A5Rotated", DMPAPER_A5_ROTATED}, {"A6", DMPAPER_A6}, {"A6Rotated", DMPAPER_A6_ROTATED}, {"ARCHC", DMPAPER_CSHEET}, {"ARCHD", DMPAPER_DSHEET}, {"ARCHE", DMPAPER_ESHEET}, {"B4", DMPAPER_B4}, {"B4Rotated", DMPAPER_B4_JIS_ROTATED}, {"B5", DMPAPER_B5}, {"B5.Transverse", DMPAPER_B5_TRANSVERSE}, {"B5Rotated", DMPAPER_B5_JIS_ROTATED}, {"B6", DMPAPER_B6_JIS}, {"B6Rotated", DMPAPER_B6_JIS_ROTATED}, {"C4", DMPAPER_ENV_C4}, /* use EnvC4 */ {"C5", DMPAPER_ENV_C5}, /* use EnvC5 */ {"C6", DMPAPER_ENV_C6}, /* use EnvC6 */ {"Comm10", DMPAPER_ENV_10}, /* use Env10 */ {"DL", DMPAPER_ENV_DL}, /* use EnvDL */ {"DoublePostcard", DMPAPER_DBL_JAPANESE_POSTCARD}, {"DoublePostcardRotated", DMPAPER_DBL_JAPANESE_POSTCARD_ROTATED}, {"Env10", DMPAPER_ENV_10}, {"Env11", DMPAPER_ENV_11}, {"Env12", DMPAPER_ENV_12}, {"Env14", DMPAPER_ENV_14}, {"Env9", DMPAPER_ENV_9}, {"EnvC3", DMPAPER_ENV_C3}, {"EnvC4", DMPAPER_ENV_C4}, {"EnvC5", DMPAPER_ENV_C5}, {"EnvC6", DMPAPER_ENV_C6}, {"EnvC65", DMPAPER_ENV_C65}, {"EnvChou3", DMPAPER_JENV_CHOU3}, {"EnvChou3Rotated", DMPAPER_JENV_CHOU3_ROTATED}, {"EnvChou4", DMPAPER_JENV_CHOU4}, {"EnvChou4Rotated", DMPAPER_JENV_CHOU4_ROTATED}, {"EnvDL", DMPAPER_ENV_DL}, {"EnvISOB4", DMPAPER_ENV_B4}, {"EnvISOB5", DMPAPER_ENV_B5}, {"EnvISOB6", DMPAPER_ENV_B6}, {"EnvInvite", DMPAPER_ENV_INVITE}, {"EnvItalian", DMPAPER_ENV_ITALY}, {"EnvKaku2", DMPAPER_JENV_KAKU2}, {"EnvKaku2Rotated", DMPAPER_JENV_KAKU2_ROTATED}, {"EnvKaku3", DMPAPER_JENV_KAKU3}, {"EnvKaku3Rotated", DMPAPER_JENV_KAKU3_ROTATED}, {"EnvMonarch", DMPAPER_ENV_MONARCH}, {"EnvPRC1", DMPAPER_PENV_1}, {"EnvPRC10", DMPAPER_PENV_10}, {"EnvPRC10Rotated", DMPAPER_PENV_10_ROTATED}, {"EnvPRC1Rotated", DMPAPER_PENV_1_ROTATED}, {"EnvPRC2", DMPAPER_PENV_2}, {"EnvPRC2Rotated", DMPAPER_PENV_2_ROTATED}, {"EnvPRC3", DMPAPER_PENV_3}, {"EnvPRC3Rotated", DMPAPER_PENV_3_ROTATED}, {"EnvPRC4", DMPAPER_PENV_4}, {"EnvPRC4Rotated", DMPAPER_PENV_4_ROTATED}, {"EnvPRC5", DMPAPER_PENV_5}, {"EnvPRC5Rotated", DMPAPER_PENV_5_ROTATED}, {"EnvPRC6", DMPAPER_PENV_6}, {"EnvPRC6Rotated", DMPAPER_PENV_6_ROTATED}, {"EnvPRC7", DMPAPER_PENV_7}, {"EnvPRC7Rotated", DMPAPER_PENV_7_ROTATED}, {"EnvPRC8", DMPAPER_PENV_8}, {"EnvPRC8Rotated", DMPAPER_PENV_8_ROTATED}, {"EnvPRC9", DMPAPER_PENV_9}, {"EnvPRC9Rotated", DMPAPER_PENV_9_ROTATED}, {"EnvPersonal", DMPAPER_ENV_PERSONAL}, {"EnvYou4", DMPAPER_JENV_YOU4}, {"EnvYou4Rotated", DMPAPER_JENV_YOU4_ROTATED}, {"Executive", DMPAPER_EXECUTIVE}, {"FanFoldGerman", DMPAPER_FANFOLD_STD_GERMAN}, {"FanFoldGermanLegal", DMPAPER_FANFOLD_LGL_GERMAN}, {"FanFoldUS", DMPAPER_FANFOLD_US}, {"Folio", DMPAPER_FOLIO}, {"ISOB4", DMPAPER_ISO_B4}, {"ISOB5Extra", DMPAPER_B5_EXTRA}, {"Ledger", DMPAPER_LEDGER}, {"Legal", DMPAPER_LEGAL}, {"LegalExtra", DMPAPER_LEGAL_EXTRA}, {"Letter", DMPAPER_LETTER}, {"Letter.Transverse", DMPAPER_LETTER_TRANSVERSE}, {"LetterExtra", DMPAPER_LETTER_EXTRA}, {"LetterExtra.Transverse", DMPAPER_LETTER_EXTRA_TRANSVERSE}, {"LetterPlus", DMPAPER_LETTER_PLUS}, {"LetterRotated", DMPAPER_LETTER_ROTATED}, {"LetterSmall", DMPAPER_LETTERSMALL}, {"Monarch", DMPAPER_ENV_MONARCH}, /* use EnvMonarch */ {"Note", DMPAPER_NOTE}, {"PRC16K", DMPAPER_P16K}, {"PRC16KRotated", DMPAPER_P16K_ROTATED}, {"PRC32K", DMPAPER_P32K}, {"PRC32KBig", DMPAPER_P32KBIG}, {"PRC32KBigRotated", DMPAPER_P32KBIG_ROTATED}, {"PRC32KRotated", DMPAPER_P32K_ROTATED}, {"Postcard", DMPAPER_JAPANESE_POSTCARD}, {"PostcardRotated", DMPAPER_JAPANESE_POSTCARD_ROTATED}, {"Quarto", DMPAPER_QUARTO}, {"Statement", DMPAPER_STATEMENT}, {"SuperA", DMPAPER_A_PLUS}, {"SuperB", DMPAPER_B_PLUS}, {"Tabloid", DMPAPER_TABLOID}, {"TabloidExtra", DMPAPER_TABLOID_EXTRA}, {NULL, 0} }; static WORD UserPageType = DMPAPER_USER; static WORD UserBinType = DMBIN_USER; /*********************************************************************** * * PSDRV_PPDDecodeHex * * Copies str into a newly allocated string from the process heap subsituting * hex strings enclosed in '<' and '>' for their byte codes. * */ static char *PSDRV_PPDDecodeHex(char *str) { char *buf, *in, *out; BOOL inhex = FALSE; buf = HeapAlloc(PSDRV_Heap, 0, strlen(str) + 1); if(!buf) return NULL; for(in = str, out = buf; *in; in++) { if(!inhex) { if(*in != '<') *out++ = *in; else inhex = TRUE; } else { if(*in == '>') { inhex = FALSE; continue; } else if(isspace(*in)) continue; else { int i; if(!isxdigit(*in) || !isxdigit(*(in + 1))) { ERR("Invalid hex char in hex string\n"); HeapFree(PSDRV_Heap, 0, buf); return NULL; } *out = 0; for(i = 0; i < 2; i++) { if(isdigit(*(in + i))) *out |= (*(in + i) - '0') << ((1-i) * 4); else *out |= (toupper(*(in + i)) - 'A' + 10) << ((1-i) * 4); } out++; in++; } } } *out = '\0'; return buf; } /*********************************************************************** * * PSDRV_PPDGetTransValue * */ static BOOL PSDRV_PPDGetTransValue(char *start, PPDTuple *tuple) { char *buf, *end; end = strpbrk(start, "\r\n"); if(end == start) return FALSE; if(!end) end = start + strlen(start); buf = HeapAlloc( PSDRV_Heap, 0, end - start + 1 ); memcpy(buf, start, end - start); *(buf + (end - start)) = '\0'; tuple->valtrans = PSDRV_PPDDecodeHex(buf); HeapFree( PSDRV_Heap, 0, buf ); return TRUE; } /*********************************************************************** * * PSDRV_PPDGetInvocationValue * * Passed string that should be surrounded by `"'s, return string alloced * from process heap. */ static BOOL PSDRV_PPDGetInvocationValue(FILE *fp, char *pos, PPDTuple *tuple) { char *start, *end, *buf; char line[257]; int len; start = pos + 1; buf = HeapAlloc( PSDRV_Heap, 0, strlen(start) + 1 ); len = 0; do { end = strchr(start, '"'); if(end) { buf = HeapReAlloc( PSDRV_Heap, 0, buf, len + (end - start) + 1 ); memcpy(buf + len, start, end - start); *(buf + len + (end - start)) = '\0'; tuple->value = buf; start = strchr(end, '/'); if(start) return PSDRV_PPDGetTransValue(start + 1, tuple); return TRUE; } else { int sl = strlen(start); buf = HeapReAlloc( PSDRV_Heap, 0, buf, len + sl + 1 ); strcpy(buf + len, start); len += sl; } } while( fgets((start = line), sizeof(line), fp) ); tuple->value = NULL; HeapFree( PSDRV_Heap, 0, buf ); return FALSE; } /*********************************************************************** * * PSDRV_PPDGetQuotedValue * * Passed string that should be surrounded by `"'s. Expand as hex * return string alloced from process heap. */ static BOOL PSDRV_PPDGetQuotedValue(FILE *fp, char *pos, PPDTuple *tuple) { char *buf; if(!PSDRV_PPDGetInvocationValue(fp, pos, tuple)) return FALSE; buf = PSDRV_PPDDecodeHex(tuple->value); HeapFree(PSDRV_Heap, 0, tuple->value); tuple->value = buf; return TRUE; } /*********************************************************************** * * PSDRV_PPDGetStringValue * * Just strip leading white space. */ static BOOL PSDRV_PPDGetStringValue(char *str, PPDTuple *tuple) { char *start = str, *end; while(*start != '\0' && isspace(*start)) start++; end = strpbrk(start, "/\r\n"); if(!end) end = start + strlen(start); tuple->value = HeapAlloc( PSDRV_Heap, 0, (end - start) + 1 ); memcpy(tuple->value, start, end - start); *(tuple->value + (end - start)) = '\0'; if(*end == '/') PSDRV_PPDGetTransValue(end + 1, tuple); return TRUE; } /*********************************************************************** * * PSDRV_PPDSymbolValue * * Not implemented yet. */ static BOOL PSDRV_PPDGetSymbolValue(char *pos, PPDTuple *tuple) { FIXME("Stub\n"); return FALSE; } /********************************************************************* * * PSDRV_PPDGetNextTuple * * Gets the next Keyword Option Value tuple from the file. Allocs space off * the process heap which should be free()ed by the caller if not needed. */ static BOOL PSDRV_PPDGetNextTuple(FILE *fp, PPDTuple *tuple) { char line[257], *opt, *cp, *trans, *endkey; BOOL gotoption; start: gotoption = TRUE; opt = NULL; memset(tuple, 0, sizeof(*tuple)); do { if(!fgets(line, sizeof(line), fp)) return FALSE; if(line[0] == '*' && line[1] != '%' && strncmp(line, "*End", 4)) break; } while(1); if(line[strlen(line)-1] != '\n') { ERR("Line too long.\n"); goto start; } for(cp = line; !isspace(*cp) && *cp != ':'; cp++) ; endkey = cp; if(*cp == ':') { /* : */ gotoption = FALSE; } else { while(isspace(*cp)) cp++; if(*cp == ':') { /* : */ gotoption = FALSE; } else { /*