type1afm.c 30.6 KB
Newer Older
1
/*******************************************************************************
2
 *  Adobe Font Metric (AFM) file parsing functions for Wine PostScript driver.
3 4 5 6
 *  See http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf.
 *
 *  Copyright 2001  Ian Pilcher
 *
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
20 21 22 23 24 25 26
 *
 *  NOTE:  Many of the functions in this file can return either fatal errors
 *  	(memory allocation failure) or non-fatal errors (unusable AFM file).
 *  	Fatal errors are indicated by returning FALSE; see individual function
 *  	descriptions for how they indicate non-fatal errors.
 *
 */
27

28
#include "config.h"
29
#include "wine/port.h"
30 31 32

#include <string.h>
#include <stdlib.h>
33
#include <stdarg.h>
34
#include <stdio.h>
35 36 37
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
38 39 40 41 42 43 44 45
#include <errno.h>
#include <ctype.h>
#include <limits.h> 	    /* INT_MIN */

#ifdef HAVE_FLOAT_H
#include <float.h>  	    /* FLT_MAX */
#endif

46
#include "windef.h"
47
#include "winbase.h"
48 49
#include "winerror.h"
#include "winreg.h"
50
#include "winnls.h"
51
#include "psdrv.h"
52
#include "wine/debug.h"
53

54
WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

/*******************************************************************************
 *  ReadLine
 *
 *  Reads a line from a text file into the buffer and trims trailing whitespace.
 *  Can handle DOS and Unix text files, including weird DOS EOF.  Returns FALSE
 *  for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
 *  either the number of characters in the returned string or one of the
 *  following:
 *
 *  	0:  	    Blank (or all whitespace) line.  This is just a special case
 *  	    	    of the normal behavior.
 *
 *  	EOF:	    End of file has been reached.
 *
 *  	INT_MIN:    Buffer overflow.  Returned string is truncated (duh!) and
 *  	    	    trailing whitespace is *not* trimmed.  Remaining text in
 *  	    	    line is discarded.  (I.e. the file pointer is positioned at
 *  	    	    the beginning of the next line.)
 *
 */
static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
{
     CHAR   *cp;
     INT    i;
80

81 82 83 84 85 86 87
     if (fgets(buffer, bufsize, file) == NULL)
     {
     	if (feof(file) == 0)	    	    	    	/* EOF or error? */
	{
	    ERR("%s\n", strerror(errno));
    	    return FALSE;
	}
88

89 90 91
	*p_result = EOF;
	return TRUE;
    }
92

93 94 95 96
    cp = strchr(buffer, '\n');
    if (cp == NULL)
    {
    	i = strlen(buffer);
97

98 99 100 101 102
	if (i == bufsize - 1)	    /* max possible; was line truncated? */
	{
	    do
	    	i = fgetc(file);    	    	/* find the newline or EOF */
	    while (i != '\n' && i != EOF);
103

104 105 106 107 108 109 110
	    if (i == EOF)
	    {
	    	if (feof(file) == 0)	    	    	/* EOF or error? */
		{
		    ERR("%s\n", strerror(errno));
		    return FALSE;
		}
111

112 113
		WARN("No newline at EOF\n");
	    }
114

115 116 117 118 119 120 121 122 123 124
	    *p_result = INT_MIN;
	    return TRUE;
	}
	else	    	    	    	/* no newline and not truncated */
	{
	    if (strcmp(buffer, "\x1a") == 0)	    /* test for DOS EOF */
	    {
	    	*p_result = EOF;
		return TRUE;
	    }
125

126 127 128 129
	    WARN("No newline at EOF\n");
	    cp = buffer + i;	/* points to \0 where \n should have been */
	}
    }
130

131 132 133 134 135 136 137 138
    do
    {
    	*cp = '\0'; 	    	    	    	/* trim trailing whitespace */
	if (cp == buffer)
	    break;  	    	    	    	/* don't underflow buffer */
	--cp;
    }
    while (isspace(*cp));
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    *p_result = strlen(buffer);
    return TRUE;
}

/*******************************************************************************
 *  FindLine
 *
 *  Finds a line in the file that begins with the given string.  Returns FALSE
 *  for unexpected I/O errors; returns an empty (zero character) string if the
 *  requested line is not found.
 *
 *  NOTE:  The file pointer *MUST* be positioned at the beginning of a line when
 *  	this function is called.  Otherwise, an infinite loop can result.
 *
 */
static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
{
    INT     len = strlen(key);
    LONG    start = ftell(file);
159

160 161 162 163
    do
    {
	INT 	result;
	BOOL	ok;
164

165 166 167
	ok = ReadLine(file, buffer, bufsize, &result);
	if (ok == FALSE)
	    return FALSE;
168

169 170
	if (result > 0 && strncmp(buffer, key, len) == 0)
	    return TRUE;
171

172 173 174 175 176 177 178 179 180 181
	if (result == EOF)
	{
	    rewind(file);
	}
	else if (result == INT_MIN)
	{
	    WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
	}
    }
    while (ftell(file) != start);
182

183 184 185 186 187 188 189 190 191 192 193 194 195
    WARN("Couldn't find line '%s...' in AFM file\n", key);
    buffer[0] = '\0';
    return TRUE;
}

/*******************************************************************************
 *  DoubleToFloat
 *
 *  Utility function to convert double to float while checking for overflow.
 *  Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
 *  Linux x86/gcc).
 *
 */
196
static inline BOOL DoubleToFloat(float *p_f, double d)
197 198 199
{
    if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
    	return FALSE;
200

201 202 203 204 205 206 207 208 209 210
    *p_f = (float)d;
    return TRUE;
}

/*******************************************************************************
 *  Round
 *
 *  Utility function to add or subtract 0.5 before converting to integer type.
 *
 */
211
static inline float Round(float f)
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
{
    return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
}

/*******************************************************************************
 *  ReadFloat
 *
 *  Finds and parses a line of the form '<key> <value>', where value is a
 *  number.  Sets *p_found to FALSE if a corresponding line cannot be found, or
 *  it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
 *  skip the check of *p_found if the item is not required.
 *
 */
static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
    	FLOAT *p_ret, BOOL *p_found)
{
    CHAR    *cp, *end_ptr;
    double  d;

    if (FindLine(file, buffer, bufsize, key) == FALSE)
    	return FALSE;
233

234 235 236 237 238 239
    if (buffer[0] == '\0')  	    /* line not found */
    {
    	*p_found = FALSE;
	*p_ret = 0.0;
	return TRUE;
    }
240

241 242 243
    cp = buffer + strlen(key);	    	    	    /* first char after key */
    errno = 0;
    d = strtod(cp, &end_ptr);
244

245 246
    if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
    {
247
    	WARN("Error parsing line '%s'\n", buffer);
248 249 250 251
    	*p_found = FALSE;
	*p_ret = 0.0;
	return TRUE;
    }
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    *p_found = TRUE;
    return TRUE;
}

/*******************************************************************************
 *  ReadInt
 *
 *  See description of ReadFloat.
 *
 */
static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
    	INT *p_ret, BOOL *p_found)
{
    BOOL    retval;
    FLOAT   f;

    retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
    if (retval == FALSE || *p_found == FALSE)
    {
    	*p_ret = 0;
    	return retval;
    }
275

276
    f = Round(f);
277

278 279 280 281 282 283 284
    if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
    {
    	WARN("Error parsing line '%s'\n", buffer);
    	*p_ret = 0;
	*p_found = FALSE;
	return TRUE;
    }
285

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    *p_ret = (INT)f;
    return TRUE;
}

/*******************************************************************************
 *  ReadString
 *
 *  Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
 *  if line cannot be found or can't be parsed.
 *
 */
static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
    	LPSTR *p_str)
{
    CHAR    *cp;

    if (FindLine(file, buffer, bufsize, key) == FALSE)
    	return FALSE;
304

305 306 307 308 309
    if (buffer[0] == '\0')
    {
    	*p_str = NULL;
	return TRUE;
    }
310

311 312 313 314 315 316
    cp = buffer + strlen(key);	    	    	    /* first char after key */
    if (*cp == '\0')
    {
    	*p_str = NULL;
	return TRUE;
    }
317

318 319
    while (isspace(*cp))    	    	/* find first non-whitespace char */
    	++cp;
320

321 322 323
    *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
    if (*p_str == NULL)
    	return FALSE;
324

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    strcpy(*p_str, cp);
    return TRUE;
}

/*******************************************************************************
 *  ReadBBox
 *
 *  Similar to ReadFloat above.
 *
 */
static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
    	BOOL *p_found)
{
    CHAR    *cp, *end_ptr;
    double  d;

    if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
    	return FALSE;
343

344 345 346 347 348
    if (buffer[0] == '\0')
    {
    	*p_found = FALSE;
	return TRUE;
    }
349

350
    errno = 0;
351

352 353 354 355 356 357 358 359 360 361 362
    cp = buffer + sizeof("FontBBox");
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
    	goto parse_error;

    cp = end_ptr;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
    	goto parse_error;
363

364 365 366 367 368
    cp = end_ptr;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0
    	    || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
    	goto parse_error;
369

370 371 372 373 374
    cp = end_ptr;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0
    	    || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
    	goto parse_error;
375

376 377
    *p_found = TRUE;
    return TRUE;
378

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
    parse_error:
    	WARN("Error parsing line '%s'\n", buffer);
	*p_found = FALSE;
	return TRUE;
}

/*******************************************************************************
 *  ReadWeight
 *
 *  Finds and parses the 'Weight' line of an AFM file.  Only tries to determine
 *  if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
 *  little FW_* typedefs in the Win32 doc.  AFAICT, this is what the Windows
 *  PostScript driver does.
 *
 */
static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
{
    { "REGULAR",	FW_NORMAL },
397
    { "NORMAL",         FW_NORMAL },
398 399 400 401 402 403 404 405 406 407 408 409
    { "ROMAN",	    	FW_NORMAL },
    { "BOLD",	    	FW_BOLD },
    { "BOOK",	    	FW_NORMAL },
    { "MEDIUM",     	FW_NORMAL },
    { "LIGHT",	    	FW_NORMAL },
    { "BLACK",	    	FW_BOLD },
    { "HEAVY",	    	FW_BOLD },
    { "DEMI",	    	FW_BOLD },
    { "ULTRA",	    	FW_BOLD },
    { "SUPER" ,     	FW_BOLD },
    { NULL, 	    	0 }
};
410

411 412 413 414 415 416
static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
    	BOOL *p_found)
{
    LPSTR   sz;
    CHAR    *cp;
    INT     i;
417

418 419 420 421 422 423 424 425
    if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
    	return FALSE;

    if (sz == NULL)
    {
    	*p_found = FALSE;
	return TRUE;
    }
426

427 428
    for (cp = sz; *cp != '\0'; ++cp)
    	*cp = toupper(*cp);
429

430 431 432 433 434 435 436 437 438 439
    for (i = 0; afm_weights[i].keyword != NULL; ++i)
    {
    	if (strstr(sz, afm_weights[i].keyword) != NULL)
	{
	    afm->Weight = afm_weights[i].weight;
	    *p_found = TRUE;
	    HeapFree(PSDRV_Heap, 0, sz);
	    return TRUE;
	}
    }
440

441
    WARN("Unknown weight '%s'; treating as Roman\n", sz);
442

443 444 445 446 447 448 449 450 451 452 453 454 455 456
    afm->Weight = FW_NORMAL;
    *p_found = TRUE;
    HeapFree(PSDRV_Heap, 0, sz);
    return TRUE;
}

/*******************************************************************************
 *  ReadFixedPitch
 *
 */
static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
    	BOOL *p_found)
{
    LPSTR   sz;
457

458 459
    if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
    	return FALSE;
460

461 462 463 464 465 466 467 468 469 470 471 472 473
    if (sz == NULL)
    {
    	*p_found = FALSE;
	return TRUE;
    }

    if (strcasecmp(sz, "false") == 0)
    {
    	afm->IsFixedPitch = FALSE;
	*p_found = TRUE;
	HeapFree(PSDRV_Heap, 0, sz);
	return TRUE;
    }
474

475 476 477 478 479 480 481
    if (strcasecmp(sz, "true") == 0)
    {
    	afm->IsFixedPitch = TRUE;
	*p_found = TRUE;
	HeapFree(PSDRV_Heap, 0, sz);
	return TRUE;
    }
482

483
    WARN("Can't parse line '%s'\n", buffer);
484

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
    *p_found = FALSE;
    HeapFree(PSDRV_Heap, 0, sz);
    return TRUE;
}

/*******************************************************************************
 *  ReadFontMetrics
 *
 *  Allocates space for the AFM on the driver heap and reads basic font metrics.
 *  Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
 *  is unusable.
 *
 */
static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
{
    AFM     *afm;
    BOOL    retval, found;

    *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
    if (afm == NULL)
    	return FALSE;
506

507 508 509
    retval = ReadWeight(file, buffer, bufsize, afm, &found);
    if (retval == FALSE || found == FALSE)
    	goto cleanup_afm;
510

511 512 513 514
    retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
    	    &(afm->ItalicAngle), &found);
    if (retval == FALSE || found == FALSE)
    	goto cleanup_afm;
515

516 517 518
    retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
    if (retval == FALSE || found == FALSE)
    	goto cleanup_afm;
519

520 521 522
    retval = ReadBBox(file, buffer, bufsize, afm, &found);
    if (retval == FALSE || found == FALSE)
    	goto cleanup_afm;
523

524 525 526 527
    retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
    	    &(afm->UnderlinePosition), &found);
    if (retval == FALSE || found == FALSE)
    	goto cleanup_afm;
528

529 530 531 532
    retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
    	    &(afm->UnderlineThickness), &found);
    if (retval == FALSE || found == FALSE)
    	goto cleanup_afm;
533

534 535 536 537
    retval = ReadFloat(file, buffer, bufsize, "Ascender",    	/* optional */
    	    &(afm->Ascender), &found);
    if (retval == FALSE)
    	goto cleanup_afm;
538

539 540 541 542
    retval = ReadFloat(file, buffer, bufsize, "Descender",   	/* optional */
    	    &(afm->Descender), &found);
    if (retval == FALSE)
    	goto cleanup_afm;
543

544 545 546
    afm->WinMetrics.usUnitsPerEm = 1000;
    afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
    afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
547

548 549
    if (afm->WinMetrics.sTypoAscender == 0)
    	afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
550

551 552
    if (afm->WinMetrics.sTypoDescender == 0)
    	afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
553

554 555 556 557
    afm->WinMetrics.sTypoLineGap = 1200 -
    	    (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
    if (afm->WinMetrics.sTypoLineGap < 0)
    	afm->WinMetrics.sTypoLineGap = 0;
558

559
    return TRUE;
560

561 562 563 564 565 566 567 568 569 570 571 572 573 574
    cleanup_afm:    	    	    	/* handle fatal or non-fatal errors */
    	HeapFree(PSDRV_Heap, 0, afm);
	*p_afm = NULL;
	return retval;
}

/*******************************************************************************
 *  ParseC
 *
 *  Fatal error:    	return FALSE (none defined)
 *
 *  Non-fatal error:	leave metrics->C set to INT_MAX
 *
 */
575
static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
576 577 578 579 580 581
{
    int     base = 10;
    long    l;
    CHAR    *cp, *end_ptr;

    cp = sz + 1;
582

583 584 585 586 587
    if (*cp == 'H')
    {
    	base = 16;
	++cp;
    }
588

589 590 591 592 593 594 595
    errno = 0;
    l = strtol(cp, &end_ptr, base);
    if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
    {
    	WARN("Error parsing character code '%s'\n", sz);
	return TRUE;
    }
596

597 598 599 600 601 602 603 604 605 606 607 608
    metrics->C = (INT)l;
    return TRUE;
}

/*******************************************************************************
 *  ParseW
 *
 *  Fatal error:    	return FALSE (none defined)
 *
 *  Non-fatal error:	leave metrics->WX set to FLT_MAX
 *
 */
609
static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
610 611 612 613 614 615
{
    CHAR    *cp, *end_ptr;
    BOOL    vector = TRUE;
    double  d;

    cp = sz + 1;
616

617 618
    if (*cp == '0')
    	++cp;
619

620 621 622 623 624
    if (*cp == 'X')
    {
    	vector = FALSE;
	++cp;
    }
625

626 627
    if (!isspace(*cp))
    	goto parse_error;
628

629 630 631 632 633
    errno = 0;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(metrics->WX), d) == FALSE)
    	goto parse_error;
634

635 636
    if (vector == FALSE)
    	return TRUE;
637

638
    /*	Make sure that Y component of vector is zero */
639

640 641 642 643 644 645
    d = strtod(cp, &end_ptr);	    	    	    	    /* errno == 0 */
    if (end_ptr == cp || errno != 0 || d != 0.0)
    {
    	metrics->WX = FLT_MAX;
    	goto parse_error;
    }
646

647
    return TRUE;
648

649 650 651 652 653 654 655 656 657 658 659 660 661 662
    parse_error:
    	WARN("Error parsing character width '%s'\n", sz);
	return TRUE;
}

/*******************************************************************************
 *
 *  ParseB
 *
 *  Fatal error:    	return FALSE (none defined)
 *
 *  Non-fatal error:	leave metrics->B.ury set to FLT_MAX
 *
 */
663
static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
664 665 666
{
    CHAR    *cp, *end_ptr;
    double  d;
667

668
    errno = 0;
669

670 671 672 673 674
    cp = sz + 1;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(metrics->B.llx), d) == FALSE)
	goto parse_error;
675

676 677 678 679 680
    cp = end_ptr;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(metrics->B.lly), d) == FALSE)
	goto parse_error;
681

682 683 684 685 686 687 688 689 690 691 692 693 694
    cp = end_ptr;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(metrics->B.urx), d) == FALSE)
	goto parse_error;

    cp = end_ptr;
    d = strtod(cp, &end_ptr);
    if (end_ptr == cp || errno != 0 ||
    	    DoubleToFloat(&(metrics->B.ury), d) == FALSE)
	goto parse_error;

    return TRUE;
695

696 697 698 699 700 701 702 703 704 705 706 707 708
    parse_error:
    	WARN("Error parsing glyph bounding box '%s'\n", sz);
	return TRUE;
}

/*******************************************************************************
 *  ParseN
 *
 *  Fatal error:    	return FALSE (PSDRV_GlyphName failure)
 *
 *  Non-fatal error:	leave metrics-> set to NULL
 *
 */
709
static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
710 711
{
    CHAR    save, *cp, *end_ptr;
712

713
    cp = sz + 1;
714

715 716
    while (isspace(*cp))
    	++cp;
717

718
    end_ptr = cp;
719

720 721
    while (*end_ptr != '\0' && !isspace(*end_ptr))
    	++end_ptr;
722

723 724 725 726 727
    if (end_ptr == cp)
    {
    	WARN("Error parsing glyph name '%s'\n", sz);
	return TRUE;
    }
728

729 730
    save = *end_ptr;
    *end_ptr = '\0';
731

732 733 734
    metrics->N = PSDRV_GlyphName(cp);
    if (metrics->N == NULL)
    	return FALSE;
735

736 737 738 739 740 741 742 743 744 745 746
    *end_ptr = save;
    return TRUE;
}

/*******************************************************************************
 *  ParseCharMetrics
 *
 *  Parses the metrics line for a single glyph in an AFM file.  Returns FALSE on
 *  fatal error; sets *metrics to 'badmetrics' on non-fatal error.
 *
 */
747
static const OLD_AFMMETRICS badmetrics =
748 749
{
    INT_MAX,	    	    	    	    	    /* C */
750
    INT_MAX,	    	    	    	    	    /* UV */
751 752 753 754 755
    FLT_MAX,	    	    	    	    	    /* WX */
    NULL,   	    	    	    	    	    /* N */
    { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, 	    /* B */
    NULL    	    	    	    	    	    /* L */
};
756

757
static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
758 759 760 761
{
    CHAR    *cp = buffer;

    *metrics = badmetrics;
762

763 764 765 766
    while (*cp != '\0')
    {
    	while (isspace(*cp))
	    ++cp;
767

768 769 770 771 772
	switch(*cp)
	{
	    case 'C':	if (ParseC(cp, metrics) == FALSE)
	    	    	    return FALSE;
	    	    	break;
773

774 775 776
	    case 'W':	if (ParseW(cp, metrics) == FALSE)
	    	    	    return FALSE;
	    	    	break;
777

778 779 780
	    case 'N':	if (ParseN(cp, metrics) == FALSE)
	    	    	    return FALSE;
	    	    	break;
781

782 783 784 785
	    case 'B':	if (ParseB(cp, metrics) == FALSE)
	    	    	    return FALSE;
	    	    	break;
	}
786

787 788 789 790 791 792
	cp = strchr(cp, ';');
	if (cp == NULL)
	{
	    WARN("No terminating semicolon\n");
	    break;
	}
793

794 795
	++cp;
    }
796

797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
    if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
    	    metrics->B.ury == FLT_MAX)
    {
    	*metrics = badmetrics;
	return TRUE;
    }

    return TRUE;
}

/*******************************************************************************
 *  IsWinANSI
 *
 *  Checks whether Unicode value is part of Microsoft code page 1252
 *
 */
static const LONG ansiChars[21] =
{
    0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
    0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
    0x20ac, 0x2122, 0x2219
};

static int cmpUV(const void *a, const void *b)
{
    return (int)(*((const LONG *)a) - *((const LONG *)b));
}
824

825
static inline BOOL IsWinANSI(LONG uv)
826 827 828 829 830
{
    if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
    	    (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
	    (0x2020 <= uv && uv <= 0x2022))
    	return TRUE;
831

832 833
    if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
    	return TRUE;
834

835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
    return FALSE;
}

/*******************************************************************************
 *  Unicodify
 *
 *  Determines Unicode value (UV) for each glyph, based on font encoding.
 *
 *  	FontSpecific:	Usable encodings (0x20 - 0xff) are mapped into the
 *  	    	    	Unicode private use range U+F020 - U+F0FF.
 *
 *  	other:	    	UV determined by glyph name, based on Adobe Glyph List.
 *
 *  Also does some font metric calculations that require UVs to be known.
 *
 */
static int UnicodeGlyphByNameIndex(const void *a, const void *b)
{
    return ((const UNICODEGLYPH *)a)->name->index -
    	    ((const UNICODEGLYPH *)b)->name->index;
}
856

857
static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
858 859
{
    INT     i;
860

861 862 863 864 865 866
    if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
    {
    	for (i = 0; i < afm->NumofMetrics; ++i)
	{
	    if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
	    {
867
		metrics[i].UV = metrics[i].C | 0xf000L;
868 869 870 871 872 873 874
	    }
	    else
	    {
	    	TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
		metrics[i].UV = -1L;
	    }
	}
875

876 877 878 879 880 881
	afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
	afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
    }
    else    	    	    	    	    	/* non-FontSpecific encoding */
    {
    	UNICODEGLYPH	ug, *p_ug;
882

883
	PSDRV_IndexGlyphList();     	/* for fast searching of glyph names */
884

885
	afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
886

887 888 889 890 891 892 893 894 895 896 897 898 899
	for (i = 0; i < afm->NumofMetrics; ++i)
	{
	    ug.name = metrics[i].N;
	    p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
	    	    sizeof(ug), UnicodeGlyphByNameIndex);
	    if (p_ug == NULL)
	    {
	    	TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
		metrics[i].UV = -1L;
	    }
	    else
	    {
	    	metrics[i].UV = p_ug->UV;
900

901 902 903 904
		if (IsWinANSI(p_ug->UV))
		{
		    SHORT   ury = (SHORT)Round(metrics[i].B.ury);
		    SHORT   lly = (SHORT)Round(metrics[i].B.lly);
905

906 907 908 909 910 911 912
		    if (ury > afm->WinMetrics.sAscender)
		    	afm->WinMetrics.sAscender = ury;
		    if (lly < afm->WinMetrics.sDescender)
		    	afm->WinMetrics.sDescender = lly;
		}
	    }
	}
913

914 915 916 917 918
	if (afm->WinMetrics.sAscender == 0)
	    afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
	if (afm->WinMetrics.sDescender == 0)
	    afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
    }
919

920 921 922 923
    afm->WinMetrics.sLineGap =
    	    1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
    if (afm->WinMetrics.sLineGap < 0)
    	afm->WinMetrics.sLineGap = 0;
924

925 926 927 928 929 930 931 932 933 934 935 936
    afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
    	    afm->WinMetrics.sAscender : 0;
    afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
    	    -(afm->WinMetrics.sDescender) : 0;
}

/*******************************************************************************
 *  ReadCharMetrics
 *
 *  Reads metrics for all glyphs.  *p_metrics will be NULL on non-fatal error.
 *
 */
937
static int OldAFMMetricsByUV(const void *a, const void *b)
938
{
939
    return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
940 941
}

942 943 944
static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
    	AFMMETRICS **p_metrics)
{
945 946 947 948
    BOOL    	    retval, found;
    OLD_AFMMETRICS  *old_metrics, *encoded_metrics;
    AFMMETRICS	    *metrics;
    INT     	    i, len;
949

950 951 952 953 954 955 956
    retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
    	    &(afm->NumofMetrics), &found);
    if (retval == FALSE || found == FALSE)
    {
    	*p_metrics = NULL;
	return retval;
    }
957

958 959 960
    old_metrics = HeapAlloc(PSDRV_Heap, 0,
    	    afm->NumofMetrics * sizeof(*old_metrics));
    if (old_metrics == NULL)
961
    	return FALSE;
962

963 964 965 966
    for (i = 0; i < afm->NumofMetrics; ++i)
    {
    	retval = ReadLine(file, buffer, bufsize, &len);
    	if (retval == FALSE)
967
	    goto cleanup_old_metrics;
968

969 970
	if(len > 0)
	{
971 972 973
	    retval = ParseCharMetrics(buffer, len, old_metrics + i);
	    if (retval == FALSE || old_metrics[i].C == INT_MAX)
	    	goto cleanup_old_metrics;
974

975 976
	    continue;
	}
977

978 979 980 981
	switch (len)
	{
	    case 0: 	    --i;
		    	    continue;
982

983
	    case INT_MIN:   WARN("Ignoring long line '%32s...'\n", buffer);
984
			    goto cleanup_old_metrics;	    /* retval == TRUE */
985

986
	    case EOF:	    WARN("Unexpected EOF\n");
987
	    	    	    goto cleanup_old_metrics;  	    /* retval == TRUE */
988 989
	}
    }
990

991
    Unicodify(afm, old_metrics);    /* wait until glyph names have been read */
992

993 994
    qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
    	    OldAFMMetricsByUV);
995

996
    for (i = 0; old_metrics[i].UV == -1; ++i);      /* count unencoded glyphs */
997

998 999
    afm->NumofMetrics -= i;
    encoded_metrics = old_metrics + i;
1000

1001 1002 1003 1004
    afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
    	    afm->NumofMetrics * sizeof(*metrics));
    if (afm->Metrics == NULL)
    	goto cleanup_old_metrics;   	    	    	    /* retval == TRUE */
1005

1006 1007 1008 1009 1010 1011
    for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
    {
    	metrics->C = encoded_metrics->C;
	metrics->UV = encoded_metrics->UV;
	metrics->WX = encoded_metrics->WX;
	metrics->N = encoded_metrics->N;
1012
    }
1013

1014
    HeapFree(PSDRV_Heap, 0, old_metrics);
1015

1016
    afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1017

1018
    return TRUE;
1019

1020 1021
    cleanup_old_metrics:    	    	/* handle fatal or non-fatal errors */
    	HeapFree(PSDRV_Heap, 0, old_metrics);
1022 1023 1024 1025 1026 1027 1028 1029
	*p_metrics = NULL;
	return retval;
}

/*******************************************************************************
 *  BuildAFM
 *
 *  Builds the AFM for a PostScript font and adds it to the driver font list.
1030
 *  Returns FALSE only on an unexpected error (memory allocation or I/O error).
1031 1032 1033 1034 1035 1036 1037 1038 1039
 *
 */
static BOOL BuildAFM(FILE *file)
{
    CHAR    	buffer[258];    	/* allow for <cr>, <lf>, and <nul> */
    AFM     	*afm;
    AFMMETRICS	*metrics;
    LPSTR   	font_name, full_name, family_name, encoding_scheme;
    BOOL    	retval, added;
1040

1041 1042 1043
    retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
    if (retval == FALSE || afm == NULL)
    	return retval;
1044

1045 1046 1047
    retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
    if (retval == FALSE || font_name == NULL)
    	goto cleanup_afm;
1048

1049 1050 1051
    retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
    if (retval == FALSE || full_name == NULL)
    	goto cleanup_font_name;
1052

1053 1054 1055 1056
    retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
    	    &family_name);
    if (retval == FALSE || family_name == NULL)
    	goto cleanup_full_name;
1057

1058 1059 1060 1061
    retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
    	    &encoding_scheme);
    if (retval == FALSE || encoding_scheme == NULL)
    	goto cleanup_family_name;
1062

1063 1064 1065 1066
    afm->FontName = font_name;
    afm->FullName = full_name;
    afm->FamilyName = family_name;
    afm->EncodingScheme = encoding_scheme;
1067

1068 1069 1070
    retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
    if (retval == FALSE || metrics == FALSE)
    	goto cleanup_encoding_scheme;
1071

1072 1073 1074
    retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
    if (retval == FALSE || added == FALSE)
    	goto cleanup_encoding_scheme;
1075

1076
    return TRUE;
1077

1078
    /* clean up after fatal or non-fatal errors */
1079

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
    cleanup_encoding_scheme:
    	HeapFree(PSDRV_Heap, 0, encoding_scheme);
    cleanup_family_name:
    	HeapFree(PSDRV_Heap, 0, family_name);
    cleanup_full_name:
    	HeapFree(PSDRV_Heap, 0, full_name);
    cleanup_font_name:
    	HeapFree(PSDRV_Heap, 0, font_name);
    cleanup_afm:
    	HeapFree(PSDRV_Heap, 0, afm);
1090

1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
    return retval;
}

/*******************************************************************************
 *  ReadAFMFile
 *
 *  Reads font metrics from Type 1 AFM file.  Only returns FALSE for
 *  unexpected errors (memory allocation or I/O).
 *
 */
static BOOL ReadAFMFile(LPCSTR filename)
{
    FILE    *f;
    BOOL    retval;

    TRACE("%s\n", filename);
1107

1108 1109 1110 1111 1112 1113
    f = fopen(filename, "r");
    if (f == NULL)
    {
    	WARN("%s: %s\n", filename, strerror(errno));
	return TRUE;
    }
1114

1115
    retval = BuildAFM(f);
1116

1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
    fclose(f);
    return retval;
}

/*******************************************************************************
 *  ReadAFMDir
 *
 *  Reads all Type 1 AFM files in a directory.
 *
 */
static BOOL ReadAFMDir(LPCSTR dirname)
{
    struct dirent   *dent;
    DIR     	    *dir;
    CHAR    	    filename[256];
1132

1133 1134 1135 1136 1137 1138
    dir = opendir(dirname);
    if (dir == NULL)
    {
    	WARN("%s: %s\n", dirname, strerror(errno));
	return TRUE;
    }
1139

1140 1141 1142 1143
    while ((dent = readdir(dir)) != NULL)
    {
    	CHAR	*file_extension = strchr(dent->d_name, '.');
	int 	fn_len;
1144

1145 1146
	if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
	    continue;
1147

1148 1149 1150 1151 1152 1153
	fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
	if (fn_len < 0 || fn_len > sizeof(filename) - 1)
	{
	    WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
	    continue;
	}
1154

1155 1156 1157 1158 1159 1160
	if (ReadAFMFile(filename) == FALSE)
	{
	    closedir(dir);
	    return FALSE;
	}
    }
1161

1162 1163 1164 1165 1166 1167 1168 1169
    closedir(dir);
    return TRUE;
}

/*******************************************************************************
 *  PSDRV_GetType1Metrics
 *
 *  Reads font metrics from Type 1 AFM font files in directories listed in the
1170
 *  HKEY_CURRENT_USER\\Software\\Wine\\Fonts\\AFMPath registry string.
1171
 *
Austin English's avatar
Austin English committed
1172
 *  If this function fails (returns FALSE), the driver will fail to initialize
1173 1174 1175 1176 1177 1178
 *  and the driver heap will be destroyed, so it's not necessary to HeapFree
 *  everything in that event.
 *
 */
BOOL PSDRV_GetType1Metrics(void)
{
1179 1180 1181 1182 1183
    static const WCHAR pathW[] = {'A','F','M','P','a','t','h',0};
    HKEY hkey;
    DWORD len;
    LPWSTR valueW;
    LPSTR valueA, ptr;
1184

1185 1186 1187
    /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
    if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
        return TRUE;
1188

1189
    if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1190
    {
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
        len += sizeof(WCHAR);
        valueW = HeapAlloc( PSDRV_Heap, 0, len );
        if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
        {
            len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
            valueA = HeapAlloc( PSDRV_Heap, 0, len );
            WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
            TRACE( "got AFM font path %s\n", debugstr_a(valueA) );
            ptr = valueA;
            while (ptr)
            {
                LPSTR next = strchr( ptr, ':' );
                if (next) *next++ = 0;
                if (!ReadAFMDir( ptr ))
                {
                    RegCloseKey(hkey);
                    return FALSE;
                }
                ptr = next;
            }
            HeapFree( PSDRV_Heap, 0, valueA );
        }
        HeapFree( PSDRV_Heap, 0, valueW );
1214
    }
1215

1216 1217 1218
    RegCloseKey(hkey);
    return TRUE;
}