1 /*******************************************************************************
2 * Adobe Font Metric (AFM) file parsing functions for Wine PostScript driver.
3 * See http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf.
4 *
5 * Copyright 2001 Ian Pilcher
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * 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 * NOTE: Many of the functions in this file can return either fatal errors
22 * (memory allocation failure) or non-fatal errors (unusable AFM file).
23 * Fatal errors are indicated by returning FALSE; see individual function
24 * descriptions for how they indicate non-fatal errors.
25 *
26 */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #ifdef HAVE_DIRENT_H
36 # include <dirent.h>
37 #endif
38 #include <errno.h>
39 #include <ctype.h>
40 #include <limits.h> /* INT_MIN */
41
42 #ifdef HAVE_FLOAT_H
43 #include <float.h> /* FLT_MAX */
44 #endif
45
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winerror.h"
49 #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 * ReadLine
58 *
59 * Reads a line from a text file into the buffer and trims trailing whitespace.
60 * Can handle DOS and Unix text files, including weird DOS EOF. Returns FALSE
61 * for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
62 * either the number of characters in the returned string or one of the
63 * following:
64 *
65 * 0: Blank (or all whitespace) line. This is just a special case
66 * of the normal behavior.
67 *
68 * EOF: End of file has been reached.
69 *
70 * INT_MIN: Buffer overflow. Returned string is truncated (duh!) and
71 * trailing whitespace is *not* trimmed. Remaining text in
72 * line is discarded. (I.e. the file pointer is positioned at
73 * the beginning of the next line.)
74 *
75 */
76 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
77 {
78 CHAR *cp;
79 INT i;
80
81 if (fgets(buffer, bufsize, file) == NULL)
82 {
83 if (feof(file) == 0) /* EOF or error? */
84 {
85 ERR("%s\n", strerror(errno));
86 return FALSE;
87 }
88
89 *p_result = EOF;
90 return TRUE;
91 }
92
93 cp = strchr(buffer, '\n');
94 if (cp == NULL)
95 {
96 i = strlen(buffer);
97
98 if (i == bufsize - 1) /* max possible; was line truncated? */
99 {
100 do
101 i = fgetc(file); /* find the newline or EOF */
102 while (i != '\n' && i != EOF);
103
104 if (i == EOF)
105 {
106 if (feof(file) == 0) /* EOF or error? */
107 {
108 ERR("%s\n", strerror(errno));
109 return FALSE;
110 }
111
112 WARN("No newline at EOF\n");
113 }
114
115 *p_result = INT_MIN;
116 return TRUE;
117 }
118 else /* no newline and not truncated */
119 {
120 if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */
121 {
122 *p_result = EOF;
123 return TRUE;
124 }
125
126 WARN("No newline at EOF\n");
127 cp = buffer + i; /* points to \0 where \n should have been */
128 }
129 }
130
131 do
132 {
133 *cp = '\0'; /* trim trailing whitespace */
134 if (cp == buffer)
135 break; /* don't underflow buffer */
136 --cp;
137 }
138 while (isspace(*cp));
139
140 *p_result = strlen(buffer);
141 return TRUE;
142 }
143
144 /*******************************************************************************
145 * FindLine
146 *
147 * Finds a line in the file that begins with the given string. Returns FALSE
148 * for unexpected I/O errors; returns an empty (zero character) string if the
149 * requested line is not found.
150 *
151 * NOTE: The file pointer *MUST* be positioned at the beginning of a line when
152 * this function is called. Otherwise, an infinite loop can result.
153 *
154 */
155 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
156 {
157 INT len = strlen(key);
158 LONG start = ftell(file);
159
160 do
161 {
162 INT result;
163 BOOL ok;
164
165 ok = ReadLine(file, buffer, bufsize, &result);
166 if (ok == FALSE)
167 return FALSE;
168
169 if (result > 0 && strncmp(buffer, key, len) == 0)
170 return TRUE;
171
172 if (result == EOF)
173 {
174 rewind(file);
175 }
176 else if (result == INT_MIN)
177 {
178 WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
179 }
180 }
181 while (ftell(file) != start);
182
183 WARN("Couldn't find line '%s...' in AFM file\n", key);
184 buffer[0] = '\0';
185 return TRUE;
186 }
187
188 /*******************************************************************************
189 * DoubleToFloat
190 *
191 * Utility function to convert double to float while checking for overflow.
192 * Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
193 * Linux x86/gcc).
194 *
195 */
196 static inline BOOL DoubleToFloat(float *p_f, double d)
197 {
198 if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
199 return FALSE;
200
201 *p_f = (float)d;
202 return TRUE;
203 }
204
205 /*******************************************************************************
206 * Round
207 *
208 * Utility function to add or subtract 0.5 before converting to integer type.
209 *
210 */
211 static inline float Round(float f)
212 {
213 return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
214 }
215
216 /*******************************************************************************
217 * ReadFloat
218 *
219 * Finds and parses a line of the form '<key> <value>', where value is a
220 * number. Sets *p_found to FALSE if a corresponding line cannot be found, or
221 * it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
222 * skip the check of *p_found if the item is not required.
223 *
224 */
225 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
226 FLOAT *p_ret, BOOL *p_found)
227 {
228 CHAR *cp, *end_ptr;
229 double d;
230
231 if (FindLine(file, buffer, bufsize, key) == FALSE)
232 return FALSE;
233
234 if (buffer[0] == '\0') /* line not found */
235 {
236 *p_found = FALSE;
237 *p_ret = 0.0;
238 return TRUE;
239 }
240
241 cp = buffer + strlen(key); /* first char after key */
242 errno = 0;
243 d = strtod(cp, &end_ptr);
244
245 if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
246 {
247 WARN("Error parsing line '%s'\n", buffer);
248 *p_found = FALSE;
249 *p_ret = 0.0;
250 return TRUE;
251 }
252
253 *p_found = TRUE;
254 return TRUE;
255 }
256
257 /*******************************************************************************
258 * ReadInt
259 *
260 * See description of ReadFloat.
261 *
262 */
263 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
264 INT *p_ret, BOOL *p_found)
265 {
266 BOOL retval;
267 FLOAT f;
268
269 retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
270 if (retval == FALSE || *p_found == FALSE)
271 {
272 *p_ret = 0;
273 return retval;
274 }
275
276 f = Round(f);
277
278 if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
279 {
280 WARN("Error parsing line '%s'\n", buffer);
281 *p_ret = 0;
282 *p_found = FALSE;
283 return TRUE;
284 }
285
286 *p_ret = (INT)f;
287 return TRUE;
288 }
289
290 /*******************************************************************************
291 * ReadString
292 *
293 * Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
294 * if line cannot be found or can't be parsed.
295 *
296 */
297 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
298 LPSTR *p_str)
299 {
300 CHAR *cp;
301
302 if (FindLine(file, buffer, bufsize, key) == FALSE)
303 return FALSE;
304
305 if (buffer[0] == '\0')
306 {
307 *p_str = NULL;
308 return TRUE;
309 }
310
311 cp = buffer + strlen(key); /* first char after key */
312 if (*cp == '\0')
313 {
314 *p_str = NULL;
315 return TRUE;
316 }
317
318 while (isspace(*cp)) /* find first non-whitespace char */
319 ++cp;
320
321 *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
322 if (*p_str == NULL)
323 return FALSE;
324
325 strcpy(*p_str, cp);
326 return TRUE;
327 }
328
329 /*******************************************************************************
330 * ReadBBox
331 *
332 * Similar to ReadFloat above.
333 *
334 */
335 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
336 BOOL *p_found)
337 {
338 CHAR *cp, *end_ptr;
339 double d;
340
341 if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
342 return FALSE;
343
344 if (buffer[0] == '\0')
345 {
346 *p_found = FALSE;
347 return TRUE;
348 }
349
350 errno = 0;
351
352 cp = buffer + sizeof("FontBBox");
353 d = strtod(cp, &end_ptr);
354 if (end_ptr == cp || errno != 0 ||
355 DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
356 goto parse_error;
357
358 cp = end_ptr;
359 d = strtod(cp, &end_ptr);
360 if (end_ptr == cp || errno != 0 ||
361 DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
362 goto parse_error;
363
364 cp = end_ptr;
365 d = strtod(cp, &end_ptr);
366 if (end_ptr == cp || errno != 0
367 || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
368 goto parse_error;
369
370 cp = end_ptr;
371 d = strtod(cp, &end_ptr);
372 if (end_ptr == cp || errno != 0
373 || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
374 goto parse_error;
375
376 *p_found = TRUE;
377 return TRUE;
378
379 parse_error:
380 WARN("Error parsing line '%s'\n", buffer);
381 *p_found = FALSE;
382 return TRUE;
383 }
384
385 /*******************************************************************************
386 * ReadWeight
387 *
388 * Finds and parses the 'Weight' line of an AFM file. Only tries to determine
389 * if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
390 * little FW_* typedefs in the Win32 doc. AFAICT, this is what the Windows
391 * PostScript driver does.
392 *
393 */
394 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
395 {
396 { "REGULAR", FW_NORMAL },
397 { "NORMAL", FW_NORMAL },
398 { "ROMAN", FW_NORMAL },
399 { "BOLD", FW_BOLD },
400 { "BOOK", FW_NORMAL },
401 { "MEDIUM", FW_NORMAL },
402 { "LIGHT", FW_NORMAL },
403 { "BLACK", FW_BOLD },
404 { "HEAVY", FW_BOLD },
405 { "DEMI", FW_BOLD },
406 { "ULTRA", FW_BOLD },
407 { "SUPER" , FW_BOLD },
408 { NULL, 0 }
409 };
410
411 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
412 BOOL *p_found)
413 {
414 LPSTR sz;
415 CHAR *cp;
416 INT i;
417
418 if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
419 return FALSE;
420
421 if (sz == NULL)
422 {
423 *p_found = FALSE;
424 return TRUE;
425 }
426
427 for (cp = sz; *cp != '\0'; ++cp)
428 *cp = toupper(*cp);
429
430 for (i = 0; afm_weights[i].keyword != NULL; ++i)
431 {
432 if (strstr(sz, afm_weights[i].keyword) != NULL)
433 {
434 afm->Weight = afm_weights[i].weight;
435 *p_found = TRUE;
436 HeapFree(PSDRV_Heap, 0, sz);
437 return TRUE;
438 }
439 }
440
441 WARN("Unknown weight '%s'; treating as Roman\n", sz);
442
443 afm->Weight = FW_NORMAL;
444 *p_found = TRUE;
445 HeapFree(PSDRV_Heap, 0, sz);
446 return TRUE;
447 }
448
449 /*******************************************************************************
450 * ReadFixedPitch
451 *
452 */
453 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
454 BOOL *p_found)
455 {
456 LPSTR sz;
457
458 if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
459 return FALSE;
460
461 if (sz == NULL)
462 {
463 *p_found = FALSE;
464 return TRUE;
465 }
466
467 if (strcasecmp(sz, "false") == 0)
468 {
469 afm->IsFixedPitch = FALSE;
470 *p_found = TRUE;
471 HeapFree(PSDRV_Heap, 0, sz);
472 return TRUE;
473 }
474
475 if (strcasecmp(sz, "true") == 0)
476 {
477 afm->IsFixedPitch = TRUE;
478 *p_found = TRUE;
479 HeapFree(PSDRV_Heap, 0, sz);
480 return TRUE;
481 }
482
483 WARN("Can't parse line '%s'\n", buffer);
484
485 *p_found = FALSE;
486 HeapFree(PSDRV_Heap, 0, sz);
487 return TRUE;
488 }
489
490 /*******************************************************************************
491 * ReadFontMetrics
492 *
493 * Allocates space for the AFM on the driver heap and reads basic font metrics.
494 * Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
495 * is unusable.
496 *
497 */
498 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
499 {
500 AFM *afm;
501 BOOL retval, found;
502
503 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
504 if (afm == NULL)
505 return FALSE;
506
507 retval = ReadWeight(file, buffer, bufsize, afm, &found);
508 if (retval == FALSE || found == FALSE)
509 goto cleanup_afm;
510
511 retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
512 &(afm->ItalicAngle), &found);
513 if (retval == FALSE || found == FALSE)
514 goto cleanup_afm;
515
516 retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
517 if (retval == FALSE || found == FALSE)
518 goto cleanup_afm;
519
520 retval = ReadBBox(file, buffer, bufsize, afm, &found);
521 if (retval == FALSE || found == FALSE)
522 goto cleanup_afm;
523
524 retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
525 &(afm->UnderlinePosition), &found);
526 if (retval == FALSE || found == FALSE)
527 goto cleanup_afm;
528
529 retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
530 &(afm->UnderlineThickness), &found);
531 if (retval == FALSE || found == FALSE)
532 goto cleanup_afm;
533
534 retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */
535 &(afm->Ascender), &found);
536 if (retval == FALSE)
537 goto cleanup_afm;
538
539 retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */
540 &(afm->Descender), &found);
541 if (retval == FALSE)
542 goto cleanup_afm;
543
544 afm->WinMetrics.usUnitsPerEm = 1000;
545 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
546 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
547
548 if (afm->WinMetrics.sTypoAscender == 0)
549 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
550
551 if (afm->WinMetrics.sTypoDescender == 0)
552 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
553
554 afm->WinMetrics.sTypoLineGap = 1200 -
555 (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
556 if (afm->WinMetrics.sTypoLineGap < 0)
557 afm->WinMetrics.sTypoLineGap = 0;
558
559 return TRUE;
560
561 cleanup_afm: /* handle fatal or non-fatal errors */
562 HeapFree(PSDRV_Heap, 0, afm);
563 *p_afm = NULL;
564 return retval;
565 }
566
567 /*******************************************************************************
568 * ParseC
569 *
570 * Fatal error: return FALSE (none defined)
571 *
572 * Non-fatal error: leave metrics->C set to INT_MAX
573 *
574 */
575 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
576 {
577 int base = 10;
578 long l;
579 CHAR *cp, *end_ptr;
580
581 cp = sz + 1;
582
583 if (*cp == 'H')
584 {
585 base = 16;
586 ++cp;
587 }
588
589 errno = 0;
590 l = strtol(cp, &end_ptr, base);
591 if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
592 {
593 WARN("Error parsing character code '%s'\n", sz);
594 return TRUE;
595 }
596
597 metrics->C = (INT)l;
598 return TRUE;
599 }
600
601 /*******************************************************************************
602 * ParseW
603 *
604 * Fatal error: return FALSE (none defined)
605 *
606 * Non-fatal error: leave metrics->WX set to FLT_MAX
607 *
608 */
609 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
610 {
611 CHAR *cp, *end_ptr;
612 BOOL vector = TRUE;
613 double d;
614
615 cp = sz + 1;
616
617 if (*cp == '')
618 ++cp;
619
620 if (*cp == 'X')
621 {
622 vector = FALSE;
623 ++cp;
624 }
625
626 if (!isspace(*cp))
627 goto parse_error;
628
629 errno = 0;
630 d = strtod(cp, &end_ptr);
631 if (end_ptr == cp || errno != 0 ||
632 DoubleToFloat(&(metrics->WX), d) == FALSE)
633 goto parse_error;
634
635 if (vector == FALSE)
636 return TRUE;
637
638 /* Make sure that Y component of vector is zero */
639
640 d = strtod(cp, &end_ptr); /* errno == 0 */
641 if (end_ptr == cp || errno != 0 || d != 0.0)
642 {
643 metrics->WX = FLT_MAX;
644 goto parse_error;
645 }
646
647 return TRUE;
648
649 parse_error:
650 WARN("Error parsing character width '%s'\n", sz);
651 return TRUE;
652 }
653
654 /*******************************************************************************
655 *
656 * ParseB
657 *
658 * Fatal error: return FALSE (none defined)
659 *
660 * Non-fatal error: leave metrics->B.ury set to FLT_MAX
661 *
662 */
663 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
664 {
665 CHAR *cp, *end_ptr;
666 double d;
667
668 errno = 0;
669
670 cp = sz + 1;
671 d = strtod(cp, &end_ptr);
672 if (end_ptr == cp || errno != 0 ||
673 DoubleToFloat(&(metrics->B.llx), d) == FALSE)
674 goto parse_error;
675
676 cp = end_ptr;
677 d = strtod(cp, &end_ptr);
678 if (end_ptr == cp || errno != 0 ||
679 DoubleToFloat(&(metrics->B.lly), d) == FALSE)
680 goto parse_error;
681
682 cp = end_ptr;
683 d = strtod(cp, &end_ptr);
684 if (end_ptr == cp || errno != 0 ||
685 DoubleToFloat(&(metrics->B.urx), d) == FALSE)
686 goto parse_error;
687
688 cp = end_ptr;
689 d = strtod(cp, &end_ptr);
690 if (end_ptr == cp || errno != 0 ||
691 DoubleToFloat(&(metrics->B.ury), d) == FALSE)
692 goto parse_error;
693
694 return TRUE;
695
696 parse_error:
697 WARN("Error parsing glyph bounding box '%s'\n", sz);
698 return TRUE;
699 }
700
701 /*******************************************************************************
702 * ParseN
703 *
704 * Fatal error: return FALSE (PSDRV_GlyphName failure)
705 *
706 * Non-fatal error: leave metrics-> set to NULL
707 *
708 */
709 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
710 {
711 CHAR save, *cp, *end_ptr;
712
713 cp = sz + 1;
714
715 while (isspace(*cp))
716 ++cp;
717
718 end_ptr = cp;
719
720 while (*end_ptr != '\0' && !isspace(*end_ptr))
721 ++end_ptr;
722
723 if (end_ptr == cp)
724 {
725 WARN("Error parsing glyph name '%s'\n", sz);
726 return TRUE;
727 }
728
729 save = *end_ptr;
730 *end_ptr = '\0';
731
732 metrics->N = PSDRV_GlyphName(cp);
733 if (metrics->N == NULL)
734 return FALSE;
735
736 *end_ptr = save;
737 return TRUE;
738 }
739
740 /*******************************************************************************
741 * ParseCharMetrics
742 *
743 * Parses the metrics line for a single glyph in an AFM file. Returns FALSE on
744 * fatal error; sets *metrics to 'badmetrics' on non-fatal error.
745 *
746 */
747 static const OLD_AFMMETRICS badmetrics =
748 {
749 INT_MAX, /* C */
750 INT_MAX, /* UV */
751 FLT_MAX, /* WX */
752 NULL, /* N */
753 { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, /* B */
754 NULL /* L */
755 };
756
757 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
758 {
759 CHAR *cp = buffer;
760
761 *metrics = badmetrics;
762
763 while (*cp != '\0')
764 {
765 while (isspace(*cp))
766 ++cp;
767
768 switch(*cp)
769 {
770 case 'C': if (ParseC(cp, metrics) == FALSE)
771 return FALSE;
772 break;
773
774 case 'W': if (ParseW(cp, metrics) == FALSE)
775 return FALSE;
776 break;
777
778 case 'N': if (ParseN(cp, metrics) == FALSE)
779 return FALSE;
780 break;
781
782 case 'B': if (ParseB(cp, metrics) == FALSE)
783 return FALSE;
784 break;
785 }
786
787 cp = strchr(cp, ';');
788 if (cp == NULL)
789 {
790 WARN("No terminating semicolon\n");
791 break;
792 }
793
794 ++cp;
795 }
796
797 if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
798 metrics->B.ury == FLT_MAX)
799 {
800 *metrics = badmetrics;
801 return TRUE;
802 }
803
804 return TRUE;
805 }
806
807 /*******************************************************************************
808 * IsWinANSI
809 *
810 * Checks whether Unicode value is part of Microsoft code page 1252
811 *
812 */
813 static const LONG ansiChars[21] =
814 {
815 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
816 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
817 0x20ac, 0x2122, 0x2219
818 };
819
820 static int cmpUV(const void *a, const void *b)
821 {
822 return (int)(*((const LONG *)a) - *((const LONG *)b));
823 }
824
825 static inline BOOL IsWinANSI(LONG uv)
826 {
827 if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
828 (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
829 (0x2020 <= uv && uv <= 0x2022))
830 return TRUE;
831
832 if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
833 return TRUE;
834
835 return FALSE;
836 }
837
838 /*******************************************************************************
839 * Unicodify
840 *
841 * Determines Unicode value (UV) for each glyph, based on font encoding.
842 *
843 * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the
844 * Unicode private use range U+F020 - U+F0FF.
845 *
846 * other: UV determined by glyph name, based on Adobe Glyph List.
847 *
848 * Also does some font metric calculations that require UVs to be known.
849 *
850 */
851 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
852 {
853 return ((const UNICODEGLYPH *)a)->name->index -
854 ((const UNICODEGLYPH *)b)->name->index;
855 }
856
857 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
858 {
859 INT i;
860
861 if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
862 {
863 for (i = 0; i < afm->NumofMetrics; ++i)
864 {
865 if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
866 {
867 metrics[i].UV = metrics[i].C | 0xf000L;
868 }
869 else
870 {
871 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
872 metrics[i].UV = -1L;
873 }
874 }
875
876 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
877 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
878 }
879 else /* non-FontSpecific encoding */
880 {
881 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 for (i = 0; i < afm->NumofMetrics; ++i)
888 {
889 ug.name = metrics[i].N;
890 p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
891 sizeof(ug), UnicodeGlyphByNameIndex);
892 if (p_ug == NULL)
893 {
894 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
895 metrics[i].UV = -1L;
896 }
897 else
898 {
899 metrics[i].UV = p_ug->UV;
900
901 if (IsWinANSI(p_ug->UV))
902 {
903 SHORT ury = (SHORT)Round(metrics[i].B.ury);
904 SHORT lly = (SHORT)Round(metrics[i].B.lly);
905
906 if (ury > afm->WinMetrics.sAscender)
907 afm->WinMetrics.sAscender = ury;
908 if (lly < afm->WinMetrics.sDescender)
909 afm->WinMetrics.sDescender = lly;
910 }
911 }
912 }
913
914 if (afm->WinMetrics.sAscender == 0)
915 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
916 if (afm->WinMetrics.sDescender == 0)
917 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
918 }
919
920 afm->WinMetrics.sLineGap =
921 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
922 if (afm->WinMetrics.sLineGap < 0)
923 afm->WinMetrics.sLineGap = 0;
924
925 afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
926 afm->WinMetrics.sAscender : 0;
927 afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
928 -(afm->WinMetrics.sDescender) : 0;
929 }
930
931 /*******************************************************************************
932 * ReadCharMetrics
933 *
934 * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error.
935 *
936 */
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 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
943 AFMMETRICS **p_metrics)
944 {
945 BOOL retval, found;
946 OLD_AFMMETRICS *old_metrics, *encoded_metrics;
947 AFMMETRICS *metrics;
948 INT i, len;
949
950 retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
951 &(afm->NumofMetrics), &found);
952 if (retval == FALSE || found == FALSE)
953 {
954 *p_metrics = NULL;
955 return retval;
956 }
957
958 old_metrics = HeapAlloc(PSDRV_Heap, 0,
959 afm->NumofMetrics * sizeof(*old_metrics));
960 if (old_metrics == NULL)
961 return FALSE;
962
963 for (i = 0; i < afm->NumofMetrics; ++i)
964 {
965 retval = ReadLine(file, buffer, bufsize, &len);
966 if (retval == FALSE)
967 goto cleanup_old_metrics;
968
969 if(len > 0)
970 {
971 retval = ParseCharMetrics(buffer, len, old_metrics + i);
972 if (retval == FALSE || old_metrics[i].C == INT_MAX)
973 goto cleanup_old_metrics;
974
975 continue;
976 }
977
978 switch (len)
979 {
980 case 0: --i;
981 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 qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
994 OldAFMMetricsByUV);
995
996 for (i = 0; old_metrics[i].UV == -1; ++i); /* count unencoded glyphs */
997
998 afm->NumofMetrics -= i;
999 encoded_metrics = old_metrics + i;
1000
1001 afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
1002 afm->NumofMetrics * sizeof(*metrics));
1003 if (afm->Metrics == NULL)
1004 goto cleanup_old_metrics; /* retval == TRUE */
1005
1006 for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1007 {
1008 metrics->C = encoded_metrics->C;
1009 metrics->UV = encoded_metrics->UV;
1010 metrics->WX = encoded_metrics->WX;
1011 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 cleanup_old_metrics: /* handle fatal or non-fatal errors */
1021 HeapFree(PSDRV_Heap, 0, old_metrics);
1022 *p_metrics = NULL;
1023 return retval;
1024 }
1025
1026 /*******************************************************************************
1027 * BuildAFM
1028 *
1029 * 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 static BOOL BuildAFM(FILE *file)
1034 {
1035 CHAR buffer[258]; /* allow for <cr>, <lf>, and <nul> */
1036 AFM *afm;
1037 AFMMETRICS *metrics;
1038 LPSTR font_name, full_name, family_name, encoding_scheme;
1039 BOOL retval, added;
1040
1041 retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1042 if (retval == FALSE || afm == NULL)
1043 return retval;
1044
1045 retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1046 if (retval == FALSE || font_name == NULL)
1047 goto cleanup_afm;
1048
1049 retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1050 if (retval == FALSE || full_name == NULL)
1051 goto cleanup_font_name;
1052
1053 retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1054 &family_name);
1055 if (retval == FALSE || family_name == NULL)
1056 goto cleanup_full_name;
1057
1058 retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1059 &encoding_scheme);
1060 if (retval == FALSE || encoding_scheme == NULL)
1061 goto cleanup_family_name;
1062
1063 afm->FontName = font_name;
1064 afm->FullName = full_name;
1065 afm->FamilyName = family_name;
1066 afm->EncodingScheme = encoding_scheme;
1067
1068 retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1069 if (retval == FALSE || metrics == FALSE)
1070 goto cleanup_encoding_scheme;
1071
1072 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1073 if (retval == FALSE || added == FALSE)
1074 goto cleanup_encoding_scheme;
1075
1076 return TRUE;
1077
1078 /* clean up after fatal or non-fatal errors */
1079
1080 cleanup_encoding_scheme:
1081 HeapFree(PSDRV_Heap, 0, encoding_scheme);
1082 cleanup_family_name:
1083 HeapFree(PSDRV_Heap, 0, family_name);
1084 cleanup_full_name:
1085 HeapFree(PSDRV_Heap, 0, full_name);
1086 cleanup_font_name:
1087 HeapFree(PSDRV_Heap, 0, font_name);
1088 cleanup_afm:
1089 HeapFree(PSDRV_Heap, 0, afm);
1090
1091 return retval;
1092 }
1093
1094 /*******************************************************************************
1095 * ReadAFMFile
1096 *
1097 * Reads font metrics from Type 1 AFM file. Only returns FALSE for
1098 * unexpected errors (memory allocation or I/O).
1099 *
1100 */
1101 static BOOL ReadAFMFile(LPCSTR filename)
1102 {
1103 FILE *f;
1104 BOOL retval;
1105
1106 TRACE("%s\n", filename);
1107
1108 f = fopen(filename, "r");
1109 if (f == NULL)
1110 {
1111 WARN("%s: %s\n", filename, strerror(errno));
1112 return TRUE;
1113 }
1114
1115 retval = BuildAFM(f);
1116
1117 fclose(f);
1118 return retval;
1119 }
1120
1121 /*******************************************************************************
1122 * ReadAFMDir
1123 *
1124 * Reads all Type 1 AFM files in a directory.
1125 *
1126 */
1127 static BOOL ReadAFMDir(LPCSTR dirname)
1128 {
1129 struct dirent *dent;
1130 DIR *dir;
1131 CHAR filename[256];
1132
1133 dir = opendir(dirname);
1134 if (dir == NULL)
1135 {
1136 WARN("%s: %s\n", dirname, strerror(errno));
1137 return TRUE;
1138 }
1139
1140 while ((dent = readdir(dir)) != NULL)
1141 {
1142 CHAR *file_extension = strchr(dent->d_name, '.');
1143 int fn_len;
1144
1145 if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1146 continue;
1147
1148 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1149 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1150 {
1151 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1152 continue;
1153 }
1154
1155 if (ReadAFMFile(filename) == FALSE)
1156 {
1157 closedir(dir);
1158 return FALSE;
1159 }
1160 }
1161
1162 closedir(dir);
1163 return TRUE;
1164 }
1165
1166 /*******************************************************************************
1167 * PSDRV_GetType1Metrics
1168 *
1169 * 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 *
1172 * If this function fails (returns FALSE), the driver will fail to initialize
1173 * and the driver heap will be destroyed, so it's not necessary to HeapFree
1174 * everything in that event.
1175 *
1176 */
1177 BOOL PSDRV_GetType1Metrics(void)
1178 {
1179 static const WCHAR pathW[] = {'A','F','M','P','a','t','h',0};
1180 HKEY hkey;
1181 DWORD len;
1182 LPWSTR valueW;
1183 LPSTR valueA, ptr;
1184
1185 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1186 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
1187 return TRUE;
1188
1189 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1190 {
1191 len += sizeof(WCHAR);
1192 valueW = HeapAlloc( PSDRV_Heap, 0, len );
1193 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
1194 {
1195 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
1196 valueA = HeapAlloc( PSDRV_Heap, 0, len );
1197 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
1198 TRACE( "got AFM font path %s\n", debugstr_a(valueA) );
1199 ptr = valueA;
1200 while (ptr)
1201 {
1202 LPSTR next = strchr( ptr, ':' );
1203 if (next) *next++ = 0;
1204 if (!ReadAFMDir( ptr ))
1205 {
1206 RegCloseKey(hkey);
1207 return FALSE;
1208 }
1209 ptr = next;
1210 }
1211 HeapFree( PSDRV_Heap, 0, valueA );
1212 }
1213 HeapFree( PSDRV_Heap, 0, valueW );
1214 }
1215
1216 RegCloseKey(hkey);
1217 return TRUE;
1218 }
1219
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.