| /* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <string.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <locale.h> |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wine/debug.h" |
| #include "psdrv.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 <xx> 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 == ':') { /* <key>: */ |
| gotoption = FALSE; |
| } else { |
| while(isspace(*cp)) |
| cp++; |
| if(*cp == ':') { /* <key> : */ |
| gotoption = FALSE; |
| } else { /* <key> <option> */ |
| opt = cp; |
| } |
| } |
| |
| tuple->key = HeapAlloc( PSDRV_Heap, 0, endkey - line + 1 ); |
| if(!tuple->key) return FALSE; |
| |
| memcpy(tuple->key, line, endkey - line); |
| tuple->key[endkey - line] = '\0'; |
| |
| if(gotoption) { /* opt points to 1st non-space character of the option */ |
| cp = strpbrk(opt, ":/"); |
| if(!cp) { |
| ERR("Error in line '%s'?\n", line); |
| HeapFree(GetProcessHeap(), 0, tuple->key); |
| goto start; |
| } |
| tuple->option = HeapAlloc( PSDRV_Heap, 0, cp - opt + 1 ); |
| if(!tuple->option) return FALSE; |
| memcpy(tuple->option, opt, cp - opt); |
| tuple->option[cp - opt] = '\0'; |
| if(*cp == '/') { |
| char *buf; |
| trans = cp + 1; |
| cp = strchr(trans, ':'); |
| if(!cp) { |
| ERR("Error in line '%s'?\n", line); |
| HeapFree(GetProcessHeap(), 0, tuple->option); |
| HeapFree(GetProcessHeap(), 0, tuple->key); |
| goto start; |
| } |
| buf = HeapAlloc( PSDRV_Heap, 0, cp - trans + 1 ); |
| if(!buf) return FALSE; |
| memcpy(buf, trans, cp - trans); |
| buf[cp - trans] = '\0'; |
| tuple->opttrans = PSDRV_PPDDecodeHex(buf); |
| HeapFree( PSDRV_Heap, 0, buf ); |
| } |
| } |
| |
| /* cp should point to a ':', so we increment past it */ |
| cp++; |
| |
| while(isspace(*cp)) |
| cp++; |
| |
| switch(*cp) { |
| case '"': |
| if( (!gotoption && strncmp(tuple->key, "*?", 2) ) || |
| !strncmp(tuple->key, "*JCL", 4)) |
| PSDRV_PPDGetQuotedValue(fp, cp, tuple); |
| else |
| PSDRV_PPDGetInvocationValue(fp, cp, tuple); |
| break; |
| |
| case '^': |
| PSDRV_PPDGetSymbolValue(cp, tuple); |
| break; |
| |
| default: |
| PSDRV_PPDGetStringValue(cp, tuple); |
| } |
| return TRUE; |
| } |
| |
| /********************************************************************* |
| * |
| * PSDRV_PPDGetPageSizeInfo |
| * |
| * Searches ppd PageSize list to return entry matching name or creates new |
| * entry which is appended to the list if name is not found. |
| * |
| */ |
| static PAGESIZE *PSDRV_PPDGetPageSizeInfo(PPD *ppd, char *name) |
| { |
| PAGESIZE *page; |
| |
| LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry) |
| { |
| if(!strcmp(page->Name, name)) |
| return page; |
| } |
| |
| page = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(*page) ); |
| list_add_tail(&ppd->PageSizes, &page->entry); |
| return page; |
| } |
| |
| /********************************************************************** |
| * |
| * PSDRV_PPDGetWord |
| * |
| * Returns ptr alloced from heap to first word in str. Strips leading spaces. |
| * Puts ptr to next word in next |
| */ |
| static char *PSDRV_PPDGetWord(char *str, char **next) |
| { |
| char *start, *end, *ret; |
| |
| start = str; |
| while(start && *start && isspace(*start)) |
| start++; |
| if(!start || !*start) return FALSE; |
| |
| end = start; |
| while(*end && !isspace(*end)) |
| end++; |
| |
| ret = HeapAlloc( PSDRV_Heap, 0, end - start + 1 ); |
| memcpy(ret, start, end - start ); |
| *(ret + (end - start)) = '\0'; |
| |
| while(*end && isspace(*end)) |
| end++; |
| if(*end) |
| *next = end; |
| else |
| *next = NULL; |
| |
| return ret; |
| } |
| |
| /************************************************************ |
| * parse_resolution |
| * |
| * Helper to extract x and y resolutions from a resolution entry. |
| * Returns TRUE on successful parsing, otherwise FALSE. |
| * |
| * The ppd spec says that entries can either be: |
| * "nnndpi" |
| * or |
| * "nnnxmmmdpi" |
| * in the former case return sz.cx == cx.cy == nnn. |
| * |
| * There are broken ppd files out there (notably from Samsung) that |
| * use the form "nnnmmmdpi", so we have to work harder to spot these. |
| * We use a transition from a zero to a non-zero digit as separator |
| * (and fail if we find more than one of these). We also don't bother |
| * trying to parse "dpi" in case that's missing. |
| */ |
| static BOOL parse_resolution(const char *str, SIZE *sz) |
| { |
| int tmp[2]; |
| int *cur; |
| BOOL had_zero; |
| const char *c; |
| |
| if(sscanf(str, "%dx%d", tmp, tmp + 1) == 2) |
| { |
| sz->cx = tmp[0]; |
| sz->cy = tmp[1]; |
| return TRUE; |
| } |
| |
| tmp[0] = 0; |
| tmp[1] = -1; |
| cur = tmp; |
| had_zero = FALSE; |
| for(c = str; isdigit(*c); c++) |
| { |
| if(!had_zero || *c == '0') |
| { |
| *cur *= 10; |
| *cur += *c - '0'; |
| if(*c == '0') |
| had_zero = TRUE; |
| } |
| else if(cur != tmp) |
| return FALSE; |
| else |
| { |
| cur++; |
| *cur = *c - '0'; |
| had_zero = FALSE; |
| } |
| } |
| if(tmp[0] == 0) return FALSE; |
| |
| sz->cx = tmp[0]; |
| if(tmp[1] != -1) |
| sz->cy = tmp[1]; |
| else |
| sz->cy = sz->cx; |
| return TRUE; |
| } |
| |
| /******************************************************************************* |
| * PSDRV_AddSlot |
| * |
| */ |
| static BOOL PSDRV_AddSlot(PPD *ppd, LPCSTR szName, LPCSTR szFullName, |
| LPSTR szInvocationString, WORD wWinBin) |
| { |
| INPUTSLOT *slot = HeapAlloc( PSDRV_Heap, 0, sizeof(INPUTSLOT) ); |
| if (!slot) return FALSE; |
| |
| slot->Name = szName; |
| slot->FullName = szFullName; |
| slot->InvocationString = szInvocationString; |
| slot->WinBin = wWinBin; |
| |
| list_add_tail( &ppd->InputSlots, &slot->entry ); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * |
| * PSDRV_ParsePPD |
| * |
| * |
| */ |
| PPD *PSDRV_ParsePPD(char *fname) |
| { |
| FILE *fp; |
| PPD *ppd; |
| PPDTuple tuple; |
| char *default_pagesize = NULL, *default_duplex = NULL; |
| PAGESIZE *page, *page_cursor2; |
| |
| TRACE("file '%s'\n", fname); |
| |
| if((fp = fopen(fname, "r")) == NULL) { |
| WARN("Couldn't open ppd file '%s'\n", fname); |
| return NULL; |
| } |
| |
| ppd = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(*ppd)); |
| if(!ppd) { |
| ERR("Unable to allocate memory for ppd\n"); |
| fclose(fp); |
| return NULL; |
| } |
| |
| ppd->ColorDevice = CD_NotSpecified; |
| |
| list_init( &ppd->InstalledFonts ); |
| list_init( &ppd->PageSizes ); |
| list_init( &ppd->Constraints ); |
| list_init( &ppd->InputSlots ); |
| list_init( &ppd->Duplexes ); |
| |
| /* Some gimp-print ppd files don't contain a DefaultResolution line |
| so default to 300 */ |
| ppd->DefaultResolution = 300; |
| |
| /* |
| * The Windows PostScript drivers create the following "virtual bin" for |
| * every PostScript printer |
| */ |
| if (!PSDRV_AddSlot( ppd, NULL, "Automatically Select", NULL, DMBIN_FORMSOURCE )) |
| { |
| HeapFree (PSDRV_Heap, 0, ppd); |
| fclose(fp); |
| return NULL; |
| } |
| |
| while( PSDRV_PPDGetNextTuple(fp, &tuple)) { |
| |
| if(!strcmp("*NickName", tuple.key)) { |
| ppd->NickName = tuple.value; |
| tuple.value = NULL; |
| TRACE("NickName = '%s'\n", ppd->NickName); |
| } |
| |
| else if(!strcmp("*LanguageLevel", tuple.key)) { |
| sscanf(tuple.value, "%d", &(ppd->LanguageLevel)); |
| TRACE("LanguageLevel = %d\n", ppd->LanguageLevel); |
| } |
| |
| else if(!strcmp("*ColorDevice", tuple.key)) { |
| if(!strcasecmp(tuple.value, "true")) |
| ppd->ColorDevice = CD_True; |
| else |
| ppd->ColorDevice = CD_False; |
| TRACE("ColorDevice = %s\n", ppd->ColorDevice == CD_True ? "True" : "False"); |
| } |
| |
| else if((!strcmp("*DefaultResolution", tuple.key)) || |
| (!strcmp("*DefaultJCLResolution", tuple.key))) { |
| SIZE sz; |
| if(parse_resolution(tuple.value, &sz)) |
| { |
| TRACE("DefaultResolution %dx%d\n", sz.cx, sz.cy); |
| ppd->DefaultResolution = sz.cx; |
| } |
| else |
| WARN("failed to parse DefaultResolution %s\n", debugstr_a(tuple.value)); |
| } |
| |
| else if(!strcmp("*Font", tuple.key)) |
| { |
| FONTNAME *fn = HeapAlloc( PSDRV_Heap, 0, sizeof(*fn) ); |
| fn->Name = tuple.option; |
| tuple.option = NULL; |
| list_add_tail( &ppd->InstalledFonts, &fn->entry ); |
| } |
| |
| else if(!strcmp("*DefaultFont", tuple.key)) { |
| ppd->DefaultFont = tuple.value; |
| tuple.value = NULL; |
| } |
| |
| else if(!strcmp("*JCLBegin", tuple.key)) { |
| ppd->JCLBegin = tuple.value; |
| tuple.value = NULL; |
| } |
| |
| else if(!strcmp("*JCLToPSInterpreter", tuple.key)) { |
| ppd->JCLToPSInterpreter = tuple.value; |
| tuple.value = NULL; |
| } |
| |
| else if(!strcmp("*JCLEnd", tuple.key)) { |
| ppd->JCLEnd = tuple.value; |
| tuple.value = NULL; |
| } |
| |
| else if(!strcmp("*PageSize", tuple.key)) { |
| page = PSDRV_PPDGetPageSizeInfo(ppd, tuple.option); |
| |
| if(!page->Name) { |
| int i; |
| |
| page->Name = tuple.option; |
| tuple.option = NULL; |
| |
| for(i = 0; PageTrans[i].PSName; i++) { |
| if(!strcmp(PageTrans[i].PSName, page->Name)) { /* case ? */ |
| page->WinPage = PageTrans[i].WinPage; |
| break; |
| } |
| } |
| if(!page->WinPage) { |
| TRACE("Can't find Windows page type for '%s' - using %u\n", |
| page->Name, UserPageType); |
| page->WinPage = UserPageType++; |
| } |
| } |
| if(!page->FullName) { |
| if(tuple.opttrans) { |
| page->FullName = tuple.opttrans; |
| tuple.opttrans = NULL; |
| } else |
| { |
| page->FullName = HeapAlloc( PSDRV_Heap, 0, strlen(page->Name)+1 ); |
| strcpy( page->FullName, page->Name ); |
| } |
| } |
| if(!page->InvocationString) { |
| page->InvocationString = tuple.value; |
| tuple.value = NULL; |
| } |
| } |
| |
| else if(!strcmp("*DefaultPageSize", tuple.key)) { |
| if(default_pagesize) { |
| WARN("Already set default pagesize\n"); |
| } else { |
| default_pagesize = tuple.value; |
| tuple.value = NULL; |
| } |
| } |
| |
| else if(!strcmp("*ImageableArea", tuple.key)) { |
| page = PSDRV_PPDGetPageSizeInfo(ppd, tuple.option); |
| |
| if(!page->Name) { |
| page->Name = tuple.option; |
| tuple.option = NULL; |
| } |
| if(!page->FullName) { |
| page->FullName = tuple.opttrans; |
| tuple.opttrans = NULL; |
| } |
| |
| #define PIA page->ImageableArea |
| if(!PIA) { |
| PIA = HeapAlloc( PSDRV_Heap, 0, sizeof(*PIA) ); |
| push_lc_numeric("C"); |
| sscanf(tuple.value, "%f%f%f%f", &PIA->llx, &PIA->lly, |
| &PIA->urx, &PIA->ury); |
| pop_lc_numeric(); |
| } |
| #undef PIA |
| } |
| |
| |
| else if(!strcmp("*PaperDimension", tuple.key)) { |
| page = PSDRV_PPDGetPageSizeInfo(ppd, tuple.option); |
| |
| if(!page->Name) { |
| page->Name = tuple.option; |
| tuple.option = NULL; |
| } |
| if(!page->FullName) { |
| page->FullName = tuple.opttrans; |
| tuple.opttrans = NULL; |
| } |
| |
| #define PD page->PaperDimension |
| if(!PD) { |
| PD = HeapAlloc( PSDRV_Heap, 0, sizeof(*PD) ); |
| push_lc_numeric("C"); |
| sscanf(tuple.value, "%f%f", &PD->x, &PD->y); |
| pop_lc_numeric(); |
| } |
| #undef PD |
| } |
| |
| else if(!strcmp("*LandscapeOrientation", tuple.key)) { |
| if(!strcmp(tuple.value, "Plus90")) |
| ppd->LandscapeOrientation = 90; |
| else if(!strcmp(tuple.value, "Minus90")) |
| ppd->LandscapeOrientation = -90; |
| |
| /* anything else, namely 'any', leaves value at 0 */ |
| |
| TRACE("LandscapeOrientation = %d\n", |
| ppd->LandscapeOrientation); |
| } |
| |
| else if(!strcmp("*UIConstraints", tuple.key)) |
| { |
| char *start; |
| CONSTRAINT *con = HeapAlloc( PSDRV_Heap, 0, sizeof(*con) ); |
| |
| start = tuple.value; |
| con->Feature1 = PSDRV_PPDGetWord(start, &start); |
| con->Value1 = PSDRV_PPDGetWord(start, &start); |
| con->Feature2 = PSDRV_PPDGetWord(start, &start); |
| con->Value2 = PSDRV_PPDGetWord(start, &start); |
| |
| list_add_tail( &ppd->Constraints, &con->entry ); |
| } |
| |
| else if (!strcmp("*InputSlot", tuple.key)) |
| { |
| |
| if (!tuple.opttrans) |
| tuple.opttrans = tuple.option; |
| |
| TRACE("Using Windows bin type %u for '%s'\n", UserBinType, |
| tuple.option); |
| |
| /* FIXME - should check for failure */ |
| PSDRV_AddSlot(ppd, tuple.option, tuple.opttrans, tuple.value, |
| UserBinType++); |
| |
| tuple.option = tuple.opttrans = tuple.value = NULL; |
| } |
| |
| /* |
| * Windows treats "manual feed" as another paper source. Most PPD |
| * files, however, treat it as a separate option which is either on or |
| * off. |
| */ |
| else if (!strcmp("*ManualFeed", tuple.key) && tuple.option && |
| !strcmp ("True", tuple.option)) |
| { |
| /* FIXME - should check for failure */ |
| PSDRV_AddSlot(ppd, "Manual Feed", "Manual Feed", tuple.value, DMBIN_MANUAL); |
| tuple.value = NULL; |
| } |
| |
| else if(!strcmp("*TTRasterizer", tuple.key)) { |
| if(!strcasecmp("None", tuple.value)) |
| ppd->TTRasterizer = RO_None; |
| else if(!strcasecmp("Accept68K", tuple.value)) |
| ppd->TTRasterizer = RO_Accept68K; |
| else if(!strcasecmp("Type42", tuple.value)) |
| ppd->TTRasterizer = RO_Type42; |
| else if(!strcasecmp("TrueImage", tuple.value)) |
| ppd->TTRasterizer = RO_TrueImage; |
| else { |
| FIXME("Unknown option %s for *TTRasterizer\n", |
| tuple.value); |
| ppd->TTRasterizer = RO_None; |
| } |
| TRACE("*TTRasterizer = %d\n", ppd->TTRasterizer); |
| } |
| |
| else if(!strcmp("*Duplex", tuple.key)) |
| { |
| DUPLEX *duplex = HeapAlloc( GetProcessHeap(), 0, sizeof(*duplex) ); |
| duplex->Name = tuple.option; |
| duplex->FullName = tuple.opttrans; |
| duplex->InvocationString = tuple.value; |
| if(!strcasecmp("None", tuple.option) || !strcasecmp("False", tuple.option) |
| || !strcasecmp("Simplex", tuple.option)) |
| duplex->WinDuplex = DMDUP_SIMPLEX; |
| else if(!strcasecmp("DuplexNoTumble", tuple.option)) |
| duplex->WinDuplex = DMDUP_VERTICAL; |
| else if(!strcasecmp("DuplexTumble", tuple.option)) |
| duplex->WinDuplex = DMDUP_HORIZONTAL; |
| else if(!strcasecmp("Notcapable", tuple.option)) |
| duplex->WinDuplex = 0; |
| else { |
| FIXME("Unknown option %s for *Duplex defaulting to simplex\n", tuple.option); |
| duplex->WinDuplex = DMDUP_SIMPLEX; |
| } |
| tuple.option = tuple.opttrans = tuple.value = NULL; |
| list_add_tail( &ppd->Duplexes, &duplex->entry ); |
| } |
| |
| else if(!strcmp("*DefaultDuplex", tuple.key)) { |
| if(default_duplex) { |
| WARN("Already set default duplex\n"); |
| } else { |
| default_duplex = tuple.value; |
| tuple.value = NULL; |
| } |
| } |
| |
| HeapFree(PSDRV_Heap, 0, tuple.key); |
| HeapFree(PSDRV_Heap, 0, tuple.option); |
| HeapFree(PSDRV_Heap, 0, tuple.value); |
| HeapFree(PSDRV_Heap, 0, tuple.opttrans); |
| HeapFree(PSDRV_Heap, 0, tuple.valtrans); |
| |
| } |
| |
| /* Remove any partial page size entries, that is any without a PageSize or a PaperDimension (we can |
| cope with a missing ImageableArea). */ |
| LIST_FOR_EACH_ENTRY_SAFE(page, page_cursor2, &ppd->PageSizes, PAGESIZE, entry) |
| { |
| if(!page->InvocationString || !page->PaperDimension) |
| { |
| WARN("Removing page %s since it has a missing %s entry\n", debugstr_a(page->FullName), |
| page->InvocationString ? "PaperDimension" : "InvocationString"); |
| HeapFree(PSDRV_Heap, 0, page->Name); |
| HeapFree(PSDRV_Heap, 0, page->FullName); |
| HeapFree(PSDRV_Heap, 0, page->InvocationString); |
| HeapFree(PSDRV_Heap, 0, page->ImageableArea); |
| HeapFree(PSDRV_Heap, 0, page->PaperDimension); |
| list_remove(&page->entry); |
| } |
| } |
| |
| ppd->DefaultPageSize = NULL; |
| if(default_pagesize) { |
| LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry) { |
| if(!strcmp(page->Name, default_pagesize)) { |
| ppd->DefaultPageSize = page; |
| TRACE("DefaultPageSize: %s\n", page->Name); |
| break; |
| } |
| } |
| HeapFree(PSDRV_Heap, 0, default_pagesize); |
| } |
| if(!ppd->DefaultPageSize) { |
| ppd->DefaultPageSize = LIST_ENTRY(list_head(&ppd->PageSizes), PAGESIZE, entry); |
| TRACE("Setting DefaultPageSize to first in list\n"); |
| } |
| |
| ppd->DefaultDuplex = NULL; |
| if (default_duplex) |
| { |
| DUPLEX *duplex; |
| LIST_FOR_EACH_ENTRY( duplex, &ppd->Duplexes, DUPLEX, entry ) |
| { |
| if (!strcmp(duplex->Name, default_duplex)) |
| { |
| ppd->DefaultDuplex = duplex; |
| TRACE("DefaultDuplex: %s\n", duplex->Name); |
| break; |
| } |
| } |
| HeapFree(PSDRV_Heap, 0, default_duplex); |
| } |
| if (!ppd->DefaultDuplex) |
| { |
| ppd->DefaultDuplex = LIST_ENTRY( list_head( &ppd->Duplexes ), DUPLEX, entry ); |
| TRACE("Setting DefaultDuplex to first in list\n"); |
| } |
| |
| |
| { |
| FONTNAME *fn; |
| PAGESIZE *page; |
| CONSTRAINT *con; |
| INPUTSLOT *slot; |
| |
| LIST_FOR_EACH_ENTRY( fn, &ppd->InstalledFonts, FONTNAME, entry ) |
| TRACE("'%s'\n", fn->Name); |
| |
| LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry) { |
| TRACE("'%s' aka '%s' (%d) invoked by '%s'\n", page->Name, |
| page->FullName, page->WinPage, page->InvocationString); |
| if(page->ImageableArea) |
| TRACE("Area = %.2f,%.2f - %.2f, %.2f\n", |
| page->ImageableArea->llx, page->ImageableArea->lly, |
| page->ImageableArea->urx, page->ImageableArea->ury); |
| if(page->PaperDimension) |
| TRACE("Dimension = %.2f x %.2f\n", |
| page->PaperDimension->x, page->PaperDimension->y); |
| } |
| |
| LIST_FOR_EACH_ENTRY( con, &ppd->Constraints, CONSTRAINT, entry ) |
| TRACE("CONSTRAINTS@ %s %s %s %s\n", con->Feature1, |
| con->Value1, con->Feature2, con->Value2); |
| |
| LIST_FOR_EACH_ENTRY( slot, &ppd->InputSlots, INPUTSLOT, entry ) |
| TRACE("INPUTSLOTS '%s' Name '%s' (%d) Invocation '%s'\n", |
| debugstr_a(slot->Name), slot->FullName, slot->WinBin, |
| debugstr_a(slot->InvocationString)); |
| } |
| |
| fclose(fp); |
| return ppd; |
| } |