ppd.c 33.4 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4 5
/*	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
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 */

22
#include "config.h"
23
#include "wine/port.h"
24

Alexandre Julliard's avatar
Alexandre Julliard committed
25
#include <string.h>
26
#include <stdarg.h>
27
#include <stdio.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
28
#include <ctype.h>
29
#include <locale.h>
30
#include <assert.h>
31 32
#include "windef.h"
#include "winbase.h"
33
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
34
#include "psdrv.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
35

36
WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
37

Alexandre Julliard's avatar
Alexandre Julliard committed
38 39 40 41 42 43 44 45
typedef struct {
char	*key;
char	*option;
char	*opttrans;
char	*value;
char	*valtrans;
} PPDTuple;

46 47 48 49
struct map_context
{
    const char *ptr, *pos, *end;
};
Alexandre Julliard's avatar
Alexandre Julliard committed
50 51 52

/* map of page names in ppd file to Windows paper constants */

Dmitry Timoshkov's avatar
Dmitry Timoshkov committed
53 54
static const struct {
  const char *PSName;
Alexandre Julliard's avatar
Alexandre Julliard committed
55 56
  WORD WinPage;
} PageTrans[] = {
57 58
  {"10x11",                   DMPAPER_10X11},
  {"10x14",                   DMPAPER_10X14},
59
  {"11x17",		      DMPAPER_11X17},	/* not in Adobe PPD file spec */
60 61 62
  {"12x11",                   DMPAPER_12X11},
  {"15x11",                   DMPAPER_15X11},
  {"9x11",                    DMPAPER_9X11},
63 64
  {"A2",                      DMPAPER_A2},
  {"A3",                      DMPAPER_A3},
65 66 67 68
  {"A3.Transverse",           DMPAPER_A3_TRANSVERSE},
  {"A3Extra",                 DMPAPER_A3_EXTRA},
  {"A3Extra.Transverse",      DMPAPER_A3_EXTRA_TRANSVERSE},
  {"A3Rotated",               DMPAPER_A3_ROTATED},
69
  {"A4",                      DMPAPER_A4},
70 71 72 73 74
  {"A4.Transverse",           DMPAPER_A4_TRANSVERSE},
  {"A4Extra",                 DMPAPER_A4_EXTRA},
  {"A4Plus",                  DMPAPER_A4_PLUS},
  {"A4Rotated",               DMPAPER_A4_ROTATED},
  {"A4Small",                 DMPAPER_A4SMALL},
75
  {"A5",                      DMPAPER_A5},
76 77 78 79 80 81 82 83
  {"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},
84
  {"B4",                      DMPAPER_B4},
85
  {"B4Rotated",               DMPAPER_B4_JIS_ROTATED},
86
  {"B5",                      DMPAPER_B5},
87 88 89 90
  {"B5.Transverse",           DMPAPER_B5_TRANSVERSE},
  {"B5Rotated",               DMPAPER_B5_JIS_ROTATED},
  {"B6",                      DMPAPER_B6_JIS},
  {"B6Rotated",               DMPAPER_B6_JIS_ROTATED},
91 92 93 94 95
  {"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 */
96
  {"DoublePostcard",          DMPAPER_DBL_JAPANESE_POSTCARD},
97
  {"DoublePostcardRotated",   DMPAPER_DBL_JAPANESE_POSTCARD_ROTATED},
98 99 100 101
  {"Env10",                   DMPAPER_ENV_10},
  {"Env11",                   DMPAPER_ENV_11},
  {"Env12",                   DMPAPER_ENV_12},
  {"Env14",                   DMPAPER_ENV_14},
102
  {"Env9",		      DMPAPER_ENV_9},
103
  {"EnvC3",                   DMPAPER_ENV_C3},
104 105 106
  {"EnvC4",                   DMPAPER_ENV_C4},
  {"EnvC5",                   DMPAPER_ENV_C5},
  {"EnvC6",                   DMPAPER_ENV_C6},
107 108 109 110 111
  {"EnvC65",                  DMPAPER_ENV_C65},
  {"EnvChou3",                DMPAPER_JENV_CHOU3},
  {"EnvChou3Rotated",         DMPAPER_JENV_CHOU3_ROTATED},
  {"EnvChou4",                DMPAPER_JENV_CHOU4},
  {"EnvChou4Rotated",         DMPAPER_JENV_CHOU4_ROTATED},
112 113 114 115
  {"EnvDL",                   DMPAPER_ENV_DL},
  {"EnvISOB4",                DMPAPER_ENV_B4},
  {"EnvISOB5",                DMPAPER_ENV_B5},
  {"EnvISOB6",                DMPAPER_ENV_B6},
116
  {"EnvInvite",		      DMPAPER_ENV_INVITE},
117 118 119 120 121
  {"EnvItalian",              DMPAPER_ENV_ITALY},
  {"EnvKaku2",                DMPAPER_JENV_KAKU2},
  {"EnvKaku2Rotated",         DMPAPER_JENV_KAKU2_ROTATED},
  {"EnvKaku3",                DMPAPER_JENV_KAKU3},
  {"EnvKaku3Rotated",         DMPAPER_JENV_KAKU3_ROTATED},
122
  {"EnvMonarch",              DMPAPER_ENV_MONARCH},
123
  {"EnvPRC1",                 DMPAPER_PENV_1},
124 125
  {"EnvPRC10",		      DMPAPER_PENV_10},
  {"EnvPRC10Rotated",         DMPAPER_PENV_10_ROTATED},
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
  {"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},
143
  {"EnvPersonal",	      DMPAPER_ENV_PERSONAL},
144 145
  {"EnvYou4",                 DMPAPER_JENV_YOU4},
  {"EnvYou4Rotated",          DMPAPER_JENV_YOU4_ROTATED},
146 147 148
  {"Executive",               DMPAPER_EXECUTIVE},
  {"FanFoldGerman",           DMPAPER_FANFOLD_STD_GERMAN},
  {"FanFoldGermanLegal",      DMPAPER_FANFOLD_LGL_GERMAN},
149
  {"FanFoldUS",               DMPAPER_FANFOLD_US},
150 151 152 153
  {"Folio",                   DMPAPER_FOLIO},
  {"ISOB4",                   DMPAPER_ISO_B4},
  {"ISOB5Extra",              DMPAPER_B5_EXTRA},
  {"Ledger",                  DMPAPER_LEDGER},
154
  {"Legal",                   DMPAPER_LEGAL},
155
  {"LegalExtra",              DMPAPER_LEGAL_EXTRA},
156
  {"Letter",                  DMPAPER_LETTER},
157 158 159 160 161 162
  {"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},
163
  {"Monarch",                 DMPAPER_ENV_MONARCH},	/* use EnvMonarch */
164 165 166 167 168 169 170
  {"Note",                    DMPAPER_NOTE},
  {"PRC16K",                  DMPAPER_P16K},
  {"PRC16KRotated",           DMPAPER_P16K_ROTATED},
  {"PRC32K",                  DMPAPER_P32K},
  {"PRC32KBig",               DMPAPER_P32KBIG},
  {"PRC32KBigRotated",        DMPAPER_P32KBIG_ROTATED},
  {"PRC32KRotated",           DMPAPER_P32K_ROTATED},
171 172
  {"Postcard",                DMPAPER_JAPANESE_POSTCARD},
  {"PostcardRotated",         DMPAPER_JAPANESE_POSTCARD_ROTATED},
173 174 175 176 177 178
  {"Quarto",                  DMPAPER_QUARTO},
  {"Statement",               DMPAPER_STATEMENT},
  {"SuperA",                  DMPAPER_A_PLUS},
  {"SuperB",                  DMPAPER_B_PLUS},
  {"Tabloid",                 DMPAPER_TABLOID},
  {"TabloidExtra",            DMPAPER_TABLOID_EXTRA},
179
  {NULL,                      0}
Alexandre Julliard's avatar
Alexandre Julliard committed
180 181 182 183 184 185
};

/***********************************************************************
 *
 *		PSDRV_PPDDecodeHex
 *
186
 * Copies str into a newly allocated string from the process heap substituting
Alexandre Julliard's avatar
Alexandre Julliard committed
187 188 189 190 191 192
 * hex strings enclosed in '<' and '>' for their byte codes.
 *
 */
static char *PSDRV_PPDDecodeHex(char *str)
{
    char *buf, *in, *out;
193
    BOOL inhex = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

    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))) {
215
		    ERR("Invalid hex char in hex string\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
		    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
 *
 */
241
static BOOL PSDRV_PPDGetTransValue(const char *start, PPDTuple *tuple)
Alexandre Julliard's avatar
Alexandre Julliard committed
242
{
243 244
    char *buf;
    const char *end;
Alexandre Julliard's avatar
Alexandre Julliard committed
245 246 247 248 249 250 251 252 253 254 255 256

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

257 258 259 260 261 262 263 264 265
static BOOL get_line( char *buf, int size, struct map_context *ctx )
{
    int i;
    if (ctx->pos > ctx->end) return FALSE;

    for (i = 0; i < size - 1; i++)
    {
        if (ctx->pos > ctx->end) break;
        buf[i] = *ctx->pos++;
266 267 268 269 270 271 272 273 274

        /* \r\n -> \n */
        if (buf[i] == '\r' && ctx->pos <= ctx->end && *ctx->pos == '\n')
        {
            ctx->pos++;
            buf[i] = '\n';
        }

        if (buf[i] == '\n' || buf[i] == '\r')
275 276 277 278 279 280 281 282
        {
            i++;
            break;
        }
    }
    buf[i] = '\0';
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
283 284 285 286 287 288 289 290

/***********************************************************************
 *
 *		PSDRV_PPDGetInvocationValue
 *
 * Passed string that should be surrounded by `"'s, return string alloced
 * from process heap.
 */
291
static BOOL PSDRV_PPDGetInvocationValue(struct map_context *ctx, PPDTuple *tuple)
Alexandre Julliard's avatar
Alexandre Julliard committed
292
{
293 294
    const char *start;
    char *buf, line[257];
Alexandre Julliard's avatar
Alexandre Julliard committed
295

296
    assert( *ctx->pos == '"' );
Alexandre Julliard's avatar
Alexandre Julliard committed
297

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
    ctx->pos++;
    for (start = ctx->pos; ctx->pos <= ctx->end; ctx->pos++)
        if (*ctx->pos == '"') break;
    if (ctx->pos > ctx->end) return FALSE;
    ctx->pos++;

    buf = HeapAlloc( PSDRV_Heap, 0, ctx->pos - start );
    memcpy( buf, start, ctx->pos - start - 1 );
    buf[ctx->pos - start  - 1] = '\0';
    tuple->value = buf;

    if (get_line( line, sizeof(line), ctx ))
    {
        start = strchr( line, '/' );
        if (start) return PSDRV_PPDGetTransValue( start + 1, tuple );
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
315 316 317 318 319 320 321 322 323 324
}


/***********************************************************************
 *
 *		PSDRV_PPDGetQuotedValue
 *
 * Passed string that should be surrounded by `"'s. Expand <xx> as hex
 * return string alloced from process heap.
 */
325
static BOOL PSDRV_PPDGetQuotedValue(struct map_context *ctx, PPDTuple *tuple)
Alexandre Julliard's avatar
Alexandre Julliard committed
326 327 328
{
    char *buf;

329
    if(!PSDRV_PPDGetInvocationValue(ctx, tuple))
Alexandre Julliard's avatar
Alexandre Julliard committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343
        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.
 */
344
static BOOL PSDRV_PPDGetStringValue(char *str, PPDTuple *tuple)
Alexandre Julliard's avatar
Alexandre Julliard committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
{
    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.
 */
368
static BOOL PSDRV_PPDGetSymbolValue(char *pos, PPDTuple *tuple)
Alexandre Julliard's avatar
Alexandre Julliard committed
369
{
370
    FIXME("Stub\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
371 372 373 374 375 376 377 378 379 380 381
    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.
 */
382
static BOOL PSDRV_PPDGetNextTuple(struct map_context *ctx, PPDTuple *tuple)
Alexandre Julliard's avatar
Alexandre Julliard committed
383
{
384 385
    char line[257], *opt, *cp, *trans, *endkey;
    BOOL gotoption;
386
    struct map_context save;
Alexandre Julliard's avatar
Alexandre Julliard committed
387

388 389 390 391
 start:

    gotoption = TRUE;
    opt = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
392 393 394
    memset(tuple, 0, sizeof(*tuple));

    do {
395 396
        save = *ctx;
        if(!get_line(line, sizeof(line), ctx))
Alexandre Julliard's avatar
Alexandre Julliard committed
397 398 399 400 401
            return FALSE;
	if(line[0] == '*' && line[1] != '%' && strncmp(line, "*End", 4))
	    break;
    } while(1);

402 403 404
    cp = line + strlen(line) - 1;
    if (*cp != '\n' && *cp != '\r')
    {
405
        ERR("Line too long.\n");
406
        goto start;
Alexandre Julliard's avatar
Alexandre Julliard committed
407 408
    }

409
    for(cp = line; !isspace(*cp) && *cp != ':'; cp++)
Alexandre Julliard's avatar
Alexandre Julliard committed
410 411
        ;

412
    endkey = cp;
413 414
    while (isspace(*cp)) cp++;
    if (*cp == ':') /* <key>: */
Alexandre Julliard's avatar
Alexandre Julliard committed
415
        gotoption = FALSE;
416 417
    else /* <key> <option> */
        opt = cp;
Alexandre Julliard's avatar
Alexandre Julliard committed
418

419
    tuple->key = HeapAlloc( PSDRV_Heap, 0, endkey - line + 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
420 421
    if(!tuple->key) return FALSE;

422 423
    memcpy(tuple->key, line, endkey - line);
    tuple->key[endkey - line] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
424

425
    if(gotoption) { /* opt points to 1st non-space character of the option */
Alexandre Julliard's avatar
Alexandre Julliard committed
426 427
        cp = strpbrk(opt, ":/");
	if(!cp) {
428
	    ERR("Error in line '%s'?\n", line);
429 430
            HeapFree(GetProcessHeap(), 0, tuple->key);
	    goto start;
Alexandre Julliard's avatar
Alexandre Julliard committed
431 432 433 434 435 436 437 438 439 440
	}
	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) {
441
	        ERR("Error in line '%s'?\n", line);
442 443 444
                HeapFree(GetProcessHeap(), 0, tuple->option);
                HeapFree(GetProcessHeap(), 0, tuple->key);
		goto start;
Alexandre Julliard's avatar
Alexandre Julliard committed
445 446 447 448 449 450 451 452 453
	    }
	    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 );
	}
    }
454 455

    /* cp should point to a ':', so we increment past it */
456
    cp++;
Alexandre Julliard's avatar
Alexandre Julliard committed
457 458 459 460 461 462

    while(isspace(*cp))
        cp++;

    switch(*cp) {
    case '"':
463 464
        /* update the context pos so that it points to the opening quote */
        ctx->pos = save.pos + (cp - line);
Alexandre Julliard's avatar
Alexandre Julliard committed
465 466
        if( (!gotoption && strncmp(tuple->key, "*?", 2) ) ||
	     !strncmp(tuple->key, "*JCL", 4))
467
	    PSDRV_PPDGetQuotedValue(ctx, tuple);
Alexandre Julliard's avatar
Alexandre Julliard committed
468
        else
469
	    PSDRV_PPDGetInvocationValue(ctx, tuple);
Alexandre Julliard's avatar
Alexandre Julliard committed
470 471 472 473 474 475 476 477 478 479 480 481 482
	break;

    case '^':
        PSDRV_PPDGetSymbolValue(cp, tuple);
	break;

    default:
        PSDRV_PPDGetStringValue(cp, tuple);
    }
    return TRUE;
}

/*********************************************************************
483
 *                       get_pagesize
Alexandre Julliard's avatar
Alexandre Julliard committed
484
 *
485
 * Searches ppd PageSize list to return entry matching name or optionally creates new
Alexandre Julliard's avatar
Alexandre Julliard committed
486 487
 * entry which is appended to the list if name is not found.
 */
488
static PAGESIZE *get_pagesize( PPD *ppd, char *name, BOOL create )
Alexandre Julliard's avatar
Alexandre Julliard committed
489
{
490
    PAGESIZE *page;
491

492 493 494 495
    LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry)
    {
        if(!strcmp(page->Name, name))
            return page;
Alexandre Julliard's avatar
Alexandre Julliard committed
496
    }
497

498 499
    if (!create) return NULL;

500 501 502
    page = HeapAlloc( PSDRV_Heap,  HEAP_ZERO_MEMORY, sizeof(*page) );
    list_add_tail(&ppd->PageSizes, &page->entry);
    return page;
Alexandre Julliard's avatar
Alexandre Julliard committed
503 504
}

505 506 507 508 509 510 511 512 513 514 515 516 517
static DUPLEX *get_duplex( PPD *ppd, const char *name )
{
    DUPLEX *duplex;

    LIST_FOR_EACH_ENTRY( duplex, &ppd->Duplexes, DUPLEX, entry )
    {
        if (!strcmp( duplex->Name, name ))
            return duplex;
    }

    return NULL;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
/**********************************************************************
 *
 *		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;
}

552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
/************************************************************
 *           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;
}

616 617 618 619
/*******************************************************************************
 *	PSDRV_AddSlot
 *
 */
620
static BOOL PSDRV_AddSlot(PPD *ppd, LPCSTR szName, LPCSTR szFullName,
621 622
	LPSTR szInvocationString, WORD wWinBin)
{
623 624
    INPUTSLOT *slot = HeapAlloc( PSDRV_Heap, 0, sizeof(INPUTSLOT) );
    if (!slot) return FALSE;
625 626 627 628 629 630

    slot->Name = szName;
    slot->FullName = szFullName;
    slot->InvocationString = szInvocationString;
    slot->WinBin = wWinBin;

631 632
    list_add_tail( &ppd->InputSlots, &slot->entry );
    return TRUE;
633 634
}

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
static char *get_ppd_override( HANDLE printer, const char *value )
{
    DWORD err, type, needed;
    char *data;

    err = GetPrinterDataExA( printer, "PPD Overrides", value, &type, NULL, 0, &needed );
    if (err != ERROR_MORE_DATA || type != REG_SZ || needed == 0) return NULL;

    data = HeapAlloc( PSDRV_Heap, 0, needed );
    if (data)
    {
        GetPrinterDataExA( printer, "PPD Overrides", value, &type, (BYTE*)data, needed, &needed );
        TRACE( "got override %s: %s\n", value, data );
    }
    return data;
}

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
static BOOL map_file( const WCHAR *filename, struct map_context *c )
{
    HANDLE file, mapping;
    LARGE_INTEGER size;

    file = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
    if (file == INVALID_HANDLE_VALUE) return FALSE;

    if (!GetFileSizeEx( file, &size ) || size.u.HighPart)
    {
        CloseHandle( file );
        return FALSE;
    }

    mapping = CreateFileMappingW( file, NULL, PAGE_READONLY, 0, 0, NULL );
    CloseHandle( file );
    if (!mapping) return FALSE;

    c->pos = c->ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
    c->end = c->ptr + size.u.LowPart - 1;
    CloseHandle( mapping );
    return TRUE;
}

static void unmap_file( struct map_context *c )
{
    UnmapViewOfFile( c->ptr );
}

Alexandre Julliard's avatar
Alexandre Julliard committed
681 682 683 684 685 686
/***********************************************************************
 *
 *		PSDRV_ParsePPD
 *
 *
 */
687
PPD *PSDRV_ParsePPD( const WCHAR *fname, HANDLE printer )
Alexandre Julliard's avatar
Alexandre Julliard committed
688 689 690
{
    PPD *ppd;
    PPDTuple tuple;
691
    char *default_pagesize = NULL, *default_duplex = NULL;
692
    char *def_pagesize_override = NULL, *def_duplex_override = NULL;
693
    PAGESIZE *page, *page_cursor2;
694
    struct map_context c;
695 696
    WORD UserPageType = DMPAPER_USER;
    WORD UserBinType = DMBIN_USER;
Alexandre Julliard's avatar
Alexandre Julliard committed
697

698
    TRACE("file %s\n", debugstr_w(fname));
Alexandre Julliard's avatar
Alexandre Julliard committed
699

700 701 702
    if (!map_file( fname, &c ))
    {
        WARN("Couldn't open ppd file %s\n", debugstr_w(fname));
Alexandre Julliard's avatar
Alexandre Julliard committed
703
        return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
704
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
705 706 707

    ppd = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(*ppd));
    if(!ppd) {
708
        ERR("Unable to allocate memory for ppd\n");
709
	unmap_file( &c );
Alexandre Julliard's avatar
Alexandre Julliard committed
710 711 712
	return NULL;
    }

713
    ppd->ColorDevice = CD_NotSpecified;
714

715
    list_init( &ppd->Resolutions );
716 717
    list_init( &ppd->InstalledFonts );
    list_init( &ppd->PageSizes );
718
    list_init( &ppd->Constraints );
719
    list_init( &ppd->InputSlots );
720
    list_init( &ppd->Duplexes );
721

722 723 724 725
    /* Some gimp-print ppd files don't contain a DefaultResolution line
       so default to 300 */
    ppd->DefaultResolution = 300;

726 727 728 729
    /*
     *	The Windows PostScript drivers create the following "virtual bin" for
     *	every PostScript printer
     */
730
    if (!PSDRV_AddSlot( ppd, NULL, "Automatically Select", NULL, DMBIN_FORMSOURCE ))
731 732
    {
	HeapFree (PSDRV_Heap, 0, ppd);
733
	unmap_file( &c );
734 735
	return NULL;
    }
736

737 738
    while (PSDRV_PPDGetNextTuple( &c, &tuple ))
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
739 740 741
	if(!strcmp("*NickName", tuple.key)) {
	    ppd->NickName = tuple.value;
	    tuple.value = NULL;
742
	    TRACE("NickName = '%s'\n", ppd->NickName);
Alexandre Julliard's avatar
Alexandre Julliard committed
743 744 745 746
	}

	else if(!strcmp("*LanguageLevel", tuple.key)) {
	    sscanf(tuple.value, "%d", &(ppd->LanguageLevel));
747
	    TRACE("LanguageLevel = %d\n", ppd->LanguageLevel);
Alexandre Julliard's avatar
Alexandre Julliard committed
748 749 750 751
	}

	else if(!strcmp("*ColorDevice", tuple.key)) {
	    if(!strcasecmp(tuple.value, "true"))
752 753 754 755
                ppd->ColorDevice = CD_True;
            else
                ppd->ColorDevice = CD_False;
            TRACE("ColorDevice = %s\n", ppd->ColorDevice == CD_True ? "True" : "False");
Alexandre Julliard's avatar
Alexandre Julliard committed
756 757
	}

758 759
	else if((!strcmp("*DefaultResolution", tuple.key)) ||
		(!strcmp("*DefaultJCLResolution", tuple.key))) {
760 761 762 763 764 765 766 767
            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));
Alexandre Julliard's avatar
Alexandre Julliard committed
768 769
	}

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
        else if(!strcmp("*Resolution", tuple.key))
        {
            SIZE sz;
            if (parse_resolution(tuple.option, &sz))
            {
                RESOLUTION *res;

                TRACE("Resolution %dx%d, invocation %s\n", sz.cx, sz.cy, tuple.value);

                res = HeapAlloc( GetProcessHeap(), 0, sizeof(*res) );
                res->resx = sz.cx;
                res->resy = sz.cy;
                res->InvocationString = tuple.value;
                tuple.value = NULL;
                list_add_tail( &ppd->Resolutions, &res->entry );
            }
            else
                WARN("failed to parse Resolution %s\n", debugstr_a(tuple.option));
        }

790 791 792 793 794 795 796
        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 );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817

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

818 819
        else if(!strcmp("*PageSize", tuple.key))
        {
820
            page = get_pagesize( ppd, tuple.option, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
821 822 823 824 825 826

	    if(!page->Name) {
	        page->Name = tuple.option;
		tuple.option = NULL;
	    }
	    if(!page->FullName) {
827 828 829
	        if(tuple.opttrans) {
		    page->FullName = tuple.opttrans;
		    tuple.opttrans = NULL;
830 831 832 833
		} else
                {
                    page->FullName = HeapAlloc( PSDRV_Heap, 0, strlen(page->Name)+1 );
                    strcpy( page->FullName, page->Name );
834
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
835 836 837 838 839
	    }
	    if(!page->InvocationString) {
		page->InvocationString = tuple.value;
	        tuple.value = NULL;
	    }
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
            if (!page->WinPage)
            {
                int i;
                for (i = 0; PageTrans[i].PSName; i++)
                {
                    if (!strcmp( PageTrans[i].PSName, page->Name ))
                    {
                        page->WinPage = PageTrans[i].WinPage;
                        break;
                    }
                }
                if (!page->WinPage)
                {
                    TRACE( "Can't find Windows page type for %s - using %u\n", debugstr_a(page->Name), UserPageType );
                    page->WinPage = UserPageType++;
                }
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
858

859 860 861 862
        else if(!strcmp("*DefaultPageSize", tuple.key))
        {
            if (!default_pagesize)
            {
863 864
                default_pagesize = tuple.value;
                tuple.value = NULL;
865
            }
866 867 868
        }

        else if(!strcmp("*ImageableArea", tuple.key)) {
869
            page = get_pagesize( ppd, tuple.option, TRUE );
870

Alexandre Julliard's avatar
Alexandre Julliard committed
871 872 873 874 875 876 877 878 879 880 881 882
	    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) );
883
                push_lc_numeric("C");
Alexandre Julliard's avatar
Alexandre Julliard committed
884 885
		sscanf(tuple.value, "%f%f%f%f", &PIA->llx, &PIA->lly,
						&PIA->urx, &PIA->ury);
886
                pop_lc_numeric();
Alexandre Julliard's avatar
Alexandre Julliard committed
887 888 889 890 891
	    }
#undef PIA
	}

	else if(!strcmp("*PaperDimension", tuple.key)) {
892
            page = get_pagesize( ppd, tuple.option, TRUE );
893

Alexandre Julliard's avatar
Alexandre Julliard committed
894 895 896 897 898 899 900 901 902 903 904 905
	    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) );
906
                push_lc_numeric("C");
Alexandre Julliard's avatar
Alexandre Julliard committed
907
		sscanf(tuple.value, "%f%f", &PD->x, &PD->y);
908
                pop_lc_numeric();
Alexandre Julliard's avatar
Alexandre Julliard committed
909 910 911 912 913 914 915 916
	    }
#undef PD
	}

	else if(!strcmp("*LandscapeOrientation", tuple.key)) {
	    if(!strcmp(tuple.value, "Plus90"))
	        ppd->LandscapeOrientation = 90;
	    else if(!strcmp(tuple.value, "Minus90"))
Alexandre Julliard's avatar
Alexandre Julliard committed
917
	        ppd->LandscapeOrientation = -90;
Alexandre Julliard's avatar
Alexandre Julliard committed
918 919 920

	    /* anything else, namely 'any', leaves value at 0 */

921
	    TRACE("LandscapeOrientation = %d\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
922 923
		  ppd->LandscapeOrientation);
	}
924

925 926
        else if(!strcmp("*UIConstraints", tuple.key))
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
927
	    char *start;
928
            CONSTRAINT *con = HeapAlloc( PSDRV_Heap, 0, sizeof(*con) );
Alexandre Julliard's avatar
Alexandre Julliard committed
929 930 931 932 933 934

	    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);
935 936 937

            list_add_tail( &ppd->Constraints, &con->entry );
        }
938

939 940
	else if (!strcmp("*InputSlot", tuple.key))
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
941

942 943
	    if (!tuple.opttrans)
		tuple.opttrans = tuple.option;
Alexandre Julliard's avatar
Alexandre Julliard committed
944

945 946
	    TRACE("Using Windows bin type %u for '%s'\n", UserBinType,
		    tuple.option);
Alexandre Julliard's avatar
Alexandre Julliard committed
947

948 949 950
	    /* FIXME - should check for failure */
	    PSDRV_AddSlot(ppd, tuple.option, tuple.opttrans, tuple.value,
		    UserBinType++);
Alexandre Julliard's avatar
Alexandre Julliard committed
951

952
	    tuple.option = tuple.opttrans = tuple.value = NULL;
953
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
954

955 956 957 958 959 960 961 962 963
	/*
	 *  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 */
964
	    PSDRV_AddSlot(ppd, "Manual Feed", "Manual Feed", tuple.value, DMBIN_MANUAL);
965 966 967
	    tuple.value = NULL;
	}

968 969 970 971 972 973 974 975 976 977 978 979 980 981
	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;
	    }
982
	    TRACE("*TTRasterizer = %d\n", ppd->TTRasterizer);
983 984
	}

985 986 987 988 989 990
        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;
991 992
            if(!strcasecmp("None", tuple.option) || !strcasecmp("False", tuple.option)
               || !strcasecmp("Simplex", tuple.option))
993
                duplex->WinDuplex = DMDUP_SIMPLEX;
994
            else if(!strcasecmp("DuplexNoTumble", tuple.option))
995
                duplex->WinDuplex = DMDUP_VERTICAL;
996
            else if(!strcasecmp("DuplexTumble", tuple.option))
997
                duplex->WinDuplex = DMDUP_HORIZONTAL;
998
            else if(!strcasecmp("Notcapable", tuple.option))
999
                duplex->WinDuplex = 0;
1000 1001
            else {
                FIXME("Unknown option %s for *Duplex defaulting to simplex\n", tuple.option);
1002
                duplex->WinDuplex = DMDUP_SIMPLEX;
1003 1004
            }
            tuple.option = tuple.opttrans = tuple.value = NULL;
1005
            list_add_tail( &ppd->Duplexes, &duplex->entry );
1006 1007
        }

1008 1009 1010 1011
        else if (!strcmp("*DefaultDuplex", tuple.key))
        {
            if (!default_duplex)
            {
1012 1013
                default_duplex = tuple.value;
                tuple.value = NULL;
1014
            }
1015 1016
        }

1017 1018 1019 1020 1021
        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);
1022

Alexandre Julliard's avatar
Alexandre Julliard committed
1023 1024
    }

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    /* 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);
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1041

1042
    ppd->DefaultPageSize = NULL;
1043 1044 1045 1046
    def_pagesize_override = get_ppd_override( printer, "DefaultPageSize" );
    if (def_pagesize_override)
        ppd->DefaultPageSize = get_pagesize( ppd, def_pagesize_override, FALSE );
    if (!ppd->DefaultPageSize && default_pagesize)
1047 1048 1049 1050 1051 1052
        ppd->DefaultPageSize = get_pagesize( ppd, default_pagesize, FALSE );

    if (!ppd->DefaultPageSize)
    {
        struct list *head = list_head( &ppd->PageSizes );
        if (head) ppd->DefaultPageSize = LIST_ENTRY( head, PAGESIZE, entry );
1053 1054
        TRACE("Setting DefaultPageSize to first in list\n");
    }
1055 1056
    TRACE( "DefaultPageSize: %s\n", ppd->DefaultPageSize ? ppd->DefaultPageSize->Name : "<not set>" );

1057
    HeapFree( PSDRV_Heap, 0, def_pagesize_override );
1058
    HeapFree( PSDRV_Heap, 0, default_pagesize );
1059

1060
    ppd->DefaultDuplex = NULL;
1061 1062 1063 1064
    def_duplex_override = get_ppd_override( printer, "DefaultDuplex" );
    if (def_duplex_override)
        ppd->DefaultDuplex = get_duplex( ppd, def_duplex_override );
    if (!ppd->DefaultDuplex && default_duplex)
1065 1066
        ppd->DefaultDuplex = get_duplex( ppd, default_duplex );

1067 1068
    if (!ppd->DefaultDuplex)
    {
1069 1070
        struct list *head = list_head( &ppd->Duplexes );
        if (head) ppd->DefaultDuplex = LIST_ENTRY( head, DUPLEX, entry );
1071 1072
        TRACE("Setting DefaultDuplex to first in list\n");
    }
1073 1074
    TRACE( "DefaultDuplex: %s\n", ppd->DefaultDuplex ? ppd->DefaultDuplex->Name : "<not set>" );

1075
    HeapFree( PSDRV_Heap, 0, def_duplex_override );
1076
    HeapFree( PSDRV_Heap, 0, default_duplex );
1077 1078


Alexandre Julliard's avatar
Alexandre Julliard committed
1079 1080 1081 1082 1083 1084
    {
        FONTNAME *fn;
	PAGESIZE *page;
	CONSTRAINT *con;
	INPUTSLOT *slot;

1085 1086
        LIST_FOR_EACH_ENTRY( fn, &ppd->InstalledFonts, FONTNAME, entry )
            TRACE("'%s'\n", fn->Name);
1087

1088
	LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry) {
1089
	    TRACE("'%s' aka '%s' (%d) invoked by '%s'\n", page->Name,
Alexandre Julliard's avatar
Alexandre Julliard committed
1090 1091
	      page->FullName, page->WinPage, page->InvocationString);
	    if(page->ImageableArea)
1092
	        TRACE("Area = %.2f,%.2f - %.2f, %.2f\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1093 1094 1095
		      page->ImageableArea->llx, page->ImageableArea->lly,
		      page->ImageableArea->urx, page->ImageableArea->ury);
	    if(page->PaperDimension)
1096
	        TRACE("Dimension = %.2f x %.2f\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1097 1098 1099
		      page->PaperDimension->x, page->PaperDimension->y);
	}

1100
        LIST_FOR_EACH_ENTRY( con, &ppd->Constraints, CONSTRAINT, entry )
1101
	    TRACE("CONSTRAINTS@ %s %s %s %s\n", con->Feature1,
1102 1103
		  con->Value1, con->Feature2, con->Value2);

1104
        LIST_FOR_EACH_ENTRY( slot, &ppd->InputSlots, INPUTSLOT, entry )
1105
	    TRACE("INPUTSLOTS '%s' Name '%s' (%d) Invocation '%s'\n",
1106 1107
		  debugstr_a(slot->Name), slot->FullName, slot->WinBin,
		  debugstr_a(slot->InvocationString));
Alexandre Julliard's avatar
Alexandre Julliard committed
1108 1109
    }

1110
    unmap_file( &c );
Alexandre Julliard's avatar
Alexandre Julliard committed
1111 1112
    return ppd;
}