1 /*
2 * Copyright � 1999, 2001 Free Software Foundation, Inc.
3 *
4 * $Id: afm.c,v 1.10 2001/02/13 23:38:05 danny Exp $
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this software; see the file COPYING. If not, write to
18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22 * This is Oleo's AFM module.
23 *
24 * Its purpose is to locate and interpret AFM (Adobe Font Metric) files,
25 * and to determine string lengths based on that information.
26 *
27 * This implementation is based on Adobe's document "5004.AFM_Spec.pdf".
28 *
29 * The return value of AfmStringWidth is of course a number that represents
30 * the string's width in points.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #ifdef WITH_DMALLOC
38 #include <dmalloc.h>
39 #endif
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <sys/param.h>
46
47 #include "afm.h"
48 #include "cmd.h"
49
50 #define AFM_PATH "/share/afm"
51 #define USRLOCAL_PATH "/usr/local"
52 #define USR_PATH "/usr"
53 #define GS_PATH "/share/ghostscript/fonts"
54
55 #define LINE_LEN 256
56
57 static char *CurrentFontName = NULL,
58 *CurrentFontSlant = NULL;
59 static int CurrentFontSize = 10;
60 FILE *fp;
61
62 struct charmetrics {
63 unsigned int code;
64 char *name;
65 int width;
66 };
67
68 struct afm {
69 int IsFixedPitch, ItalicAngle,
70 FontBBox1, FontBBox2, FontBBox3, FontBBox4,
71 UnderlinePosition, UnderlineThickness, CapHeight,
72 XHeight, Ascender, Descender,
73 StartCharMetrics;
74 struct charmetrics * charMetrics;
75 int charIndex[256];
76 };
77
78 static struct afm *afm = 0;
79
changed(char * a,char * b)80 static int changed(char *a, char *b)
81 {
82 if (a == 0 && b == 0)
83 return 0;
84 if (a != 0 && b != 0 && strcmp(a, b) == 0)
85 return 0;
86 return 1;
87 }
88
89 static char *Where, *line;
90
IsCommand(char * literal)91 static int IsCommand(char *literal)
92 {
93 int len;
94
95 len = strlen(literal);
96
97 Where = line + len + 1;
98
99 return (strncmp(literal, line, len) == 0);
100 }
101
Value(void)102 static int Value(void)
103 {
104 int v;
105
106 while (isspace(*Where))
107 Where++;
108 sscanf(Where, "%d", &v);
109 return v;
110 }
111
ValueBoolean(void)112 static int ValueBoolean(void)
113 {
114 while (isspace(*Where))
115 Where++;
116 if (strncmp(Where, "True", 4) == 0)
117 return 1;
118 if (strncmp(Where, "true", 4) == 0)
119 return 1;
120 if (strncmp(Where, "TRUE", 4) == 0)
121 return 1;
122 return 0;
123 }
124
skip(void)125 static void skip(void)
126 {
127 while (*Where != ';' && *Where != '\n' && *Where != '\0')
128 Where++;
129 }
130
namelen(void)131 static int namelen(void)
132 {
133 char *p;
134 int i;
135
136 for (i=0, p=Where; !isspace(*p); i++, p++) ;
137 return i;
138 }
139
ReadCharMetrics(int n)140 static void ReadCharMetrics(int n)
141 {
142 int i, l;
143
144 afm->StartCharMetrics = n;
145
146 if (afm->charMetrics)
147 free(afm->charMetrics);
148 afm->charMetrics = (struct charmetrics *)calloc(n, sizeof(struct charmetrics));
149
150 for (i=0; i<n; i++) {
151 if (fgets(line, LINE_LEN, fp) == 0)
152 return; /* FIX ME */
153 Where = line;
154 while (*Where != '\n') {
155 if (isspace(*Where)) {
156 Where++;
157 continue;
158 }
159 if (*Where == 'C' && isspace(*(Where+1))) {
160 afm->charMetrics[i].code = atoi(Where+2);
161 skip();
162 } else if (*Where == 'C' && *(Where+1) == 'X' && isspace(*(Where+2))) {
163 sscanf(Where+3, "%x", &afm->charMetrics[i].code);
164 skip();
165 } else if (*Where == 'N' && isspace(*(Where+1))) {
166 Where += 2;
167 l = namelen();
168 afm->charMetrics[i].name = malloc(l+1);
169 afm->charMetrics[i].name[l] = '\0';
170 strncpy(afm->charMetrics[i].name, Where, l);
171 skip();
172 } else if (*Where == 'W' && *(Where+1) == 'X' && isspace(*(Where+2))) {
173 afm->charMetrics[i].width = atoi(Where+3);
174 skip();
175 } else {
176 skip();
177 /* FIX ME */
178 }
179
180 Where++;
181 }
182 }
183 }
184
185 static void
ReadAfmLine(void)186 ReadAfmLine(void)
187 {
188 if (afm == 0) {
189 afm = (struct afm *)malloc(sizeof(struct afm));
190 memset(afm, 0, sizeof(struct afm));
191 }
192
193 if (IsCommand("IsFixedPitch")) afm->IsFixedPitch = ValueBoolean();
194 if (IsCommand("ItalicAngle")) afm->ItalicAngle = Value();
195 if (IsCommand("FontBBox"))
196 sscanf(Where, "%d %d %d %d", &afm->FontBBox1, &afm->FontBBox2,
197 &afm->FontBBox3, &afm->FontBBox4);
198 if (IsCommand("StartCharMetrics")) {
199 int n = atoi(Where);
200 ReadCharMetrics(n);
201 }
202 }
203
204 char *
GSName(char * name,char * slant)205 GSName(char *name, char *slant)
206 {
207 if (slant == NULL)
208 slant = "";
209
210 if (strcmp(name, "Courier") == 0 && strcmp(slant, "") == 0)
211 return "n022003l"; /* NimbusMonL-Regu */
212 if (strcmp(name, "CGTimes") == 0 && strcmp(slant, "") == 0)
213 return "n021003l"; /* NimbusRomNo9L-Regu */
214 return NULL;
215 }
216
217 FILE *
OpenAfmFile(char * name,char * slant)218 OpenAfmFile(char *name, char *slant)
219 {
220 FILE *fp;
221 char *fn, *alias;
222
223 fn = malloc(MAXPATHLEN);
224
225 sprintf(fn, "%s/%s%s.afm", BUILD_PREFIX AFM_PATH, name, slant ? slant : "");
226 fp = fopen(fn, "r");
227 if (fp)
228 return fp;
229 #ifdef VERBOSE
230 else
231 MessageAppend(0, "Failed to open %s\n", fn);
232 #endif
233
234 sprintf(fn, "%s/%s%s.afm", USR_PATH GS_PATH, name, slant ? slant : "");
235 fp = fopen(fn, "r");
236 if (fp)
237 return fp;
238 #ifdef VERBOSE
239 else
240 MessageAppend(0, "Failed to open %s\n", fn);
241 #endif
242
243 sprintf(fn, "%s/%s%s.afm", USRLOCAL_PATH GS_PATH, name, slant ? slant : "");
244 fp = fopen(fn, "r");
245 if (fp)
246 return fp;
247 #ifdef VERBOSE
248 else
249 MessageAppend(0, "Failed to open %s\n", fn);
250 #endif
251
252 if ((alias = GSName(name, slant)) != NULL) {
253 sprintf(fn, "%s/%s.afm", USR_PATH GS_PATH, alias);
254 fp = fopen(fn, "r");
255 if (fp)
256 return fp;
257 #ifdef VERBOSE
258 else
259 MessageAppend(0, "Failed to open %s\n", fn);
260 #endif
261 sprintf(fn, "%s/%s.afm", USRLOCAL_PATH GS_PATH, alias);
262 fp = fopen(fn, "r");
263 if (fp)
264 return fp;
265 #ifdef VERBOSE
266 else
267 MessageAppend(0, "Failed to open %s\n", fn);
268 #endif
269 }
270
271 if (fp == 0) {
272 io_info_msg(PACKAGE ": couldn't open AFM file for %s%s\n", name, slant);
273 free(fn);
274 return NULL;
275 }
276 free(fn);
277
278 return fp;
279 }
280
281 void
AfmSetFont(char * name,char * slant,int size)282 AfmSetFont(char *name, char *slant, int size)
283 {
284 /* Have the font name/slant changed ? */
285 CurrentFontSize = size;
286
287 if ( ( ! changed(name, CurrentFontName)) &&
288 ( ! changed(slant, CurrentFontSlant)))
289 return;
290
291 /*
292 * Something's changed, need to read another file.
293 *
294 * First however remember current state.
295 */
296 if (CurrentFontName) {
297 free(CurrentFontName);
298 CurrentFontName = 0;
299 }
300 if (CurrentFontSlant) {
301 free(CurrentFontSlant);
302 CurrentFontSlant = 0;
303 }
304
305 if (name)
306 CurrentFontName = strdup(name);
307 if (slant)
308 CurrentFontSlant = strdup(slant);
309
310 /*
311 * Start reading file.
312 */
313 fp = OpenAfmFile(name, slant);
314 if (fp) {
315 line = malloc(LINE_LEN + 3);
316
317 while (fgets(line, LINE_LEN, fp) != NULL) {
318 ReadAfmLine();
319 }
320
321 fclose(fp);
322 free(line);
323 }
324
325 AfmSetEncoding("ISOLatin1");
326 }
327
328 int
AfmFontHeight(void)329 AfmFontHeight(void)
330 {
331 if (! afm) {
332 /* FIX ME */
333 io_info_msg("AfmFontHeight: failed\n");
334 return 0;
335 }
336 return afm->FontBBox4 * CurrentFontSize / 1000;
337 }
338
339 int
AfmFontWidth(void)340 AfmFontWidth(void)
341 {
342 if (! afm) {
343 /* FIX ME */
344 io_info_msg("AfmFontWidth: failed\n");
345 return 8;
346 }
347 return afm->FontBBox3 * CurrentFontSize / 1000;
348 }
349
350 int
AfmPitch(void)351 AfmPitch(void)
352 {
353 float p;
354
355 if (! afm) {
356 io_info_msg("AfmPitch: failed\n");
357 return 10; /* FIX ME this is the fallback value */
358 }
359 p = 72.0 * 1000 / (afm->FontBBox3 * CurrentFontSize);
360 return p;
361 }
362
363 int
AfmStringWidth(char * s)364 AfmStringWidth(char *s)
365 {
366 int r, i, cw;
367 unsigned int pc;
368 char *p;
369
370 if (s == NULL || afm == 0)
371 return 0;
372
373 if (afm->IsFixedPitch)
374 r = strlen(s) * afm->FontBBox3 * CurrentFontSize / 1000;
375 else {
376 if (afm->charMetrics == 0) {
377 return 0; /* FIX ME */
378 }
379 for (r=0, p=s; *p; p++) {
380 pc = 255 & *p;
381 cw = 0;
382 #if 1 /* Use quick access */
383 if (afm->charIndex[pc] >= 0)
384 cw = afm->charMetrics[afm->charIndex[pc]].width;
385 #else
386 for (i=0; cw == 0 && i<afm->StartCharMetrics; i++)
387 if (afm->charMetrics[i].code == pc) {
388 cw = afm->charMetrics[i].width;
389 break;
390 }
391 #endif
392 if (cw == 0) {
393 fprintf(stderr, "Width of char(%c) not found\n", *p);
394 /* return 0; */
395 }
396 r += cw;
397 }
398 r = r * CurrentFontSize / 1000;
399 }
400
401 #if 0
402 fprintf(stderr, "AfmStringWidth(%s) -> %d\n", s, r);
403 #endif
404
405 return r;
406 }
407
408 static char *IsoLatin1Encoding[256] = {
409 /* 0x00 */ "non-printable",
410 /* 0x01 */ "non-printable",
411 /* 0x02 */ "non-printable",
412 /* 0x03 */ "non-printable",
413 /* 0x04 */ "non-printable",
414 /* 0x05 */ "non-printable",
415 /* 0x06 */ "non-printable",
416 /* 0x07 */ "non-printable",
417 /* 0x08 */ "non-printable",
418 /* 0x09 */ "non-printable",
419 /* 0x0a */ "non-printable",
420 /* 0x0b */ "non-printable",
421 /* 0x0c */ "non-printable",
422 /* 0x0d */ "non-printable",
423 /* 0x0e */ "non-printable",
424 /* 0x0f */ "non-printable",
425 /* 0x10 */ "non-printable",
426 /* 0x11 */ "non-printable",
427 /* 0x12 */ "non-printable",
428 /* 0x13 */ "non-printable",
429 /* 0x14 */ "non-printable",
430 /* 0x15 */ "non-printable",
431 /* 0x16 */ "non-printable",
432 /* 0x17 */ "non-printable",
433 /* 0x18 */ "non-printable",
434 /* 0x19 */ "non-printable",
435 /* 0x1a */ "non-printable",
436 /* 0x1b */ "non-printable",
437 /* 0x1c */ "non-printable",
438 /* 0x1d */ "non-printable",
439 /* 0x1e */ "non-printable",
440 /* 0x1f */ "non-printable",
441 /* 0x20 */ "/space",
442 /* 0x21 */ "/exclam",
443 /* 0x22 */ "/quotedbl",
444 /* 0x23 */ "/numbersign",
445 /* 0x24 */ "/dollar",
446 /* 0x25 */ "/percent",
447 /* 0x26 */ "/ampersand",
448 /* 0x27 */ "/quoteright",
449 /* 0x28 */ "/parenleft",
450 /* 0x29 */ "/parenright",
451 /* 0x2a */ "/asterisk",
452 /* 0x2b */ "/plus",
453 /* 0x2c */ "/comma",
454 /* 0x2d */ "/hyphen",
455 /* 0x2e */ "/period",
456 /* 0x2f */ "/slash",
457 /* 0x30 */ "/zero",
458 /* 0x31 */ "/one",
459 /* 0x32 */ "/two",
460 /* 0x33 */ "/three",
461 /* 0x34 */ "/four",
462 /* 0x35 */ "/five",
463 /* 0x36 */ "/six",
464 /* 0x37 */ "/seven",
465 /* 0x38 */ "/eight",
466 /* 0x39 */ "/nine",
467 /* 0x3a */ "/colon",
468 /* 0x3b */ "/semicolon",
469 /* 0x3c */ "/less",
470 /* 0x3d */ "/equal",
471 /* 0x3e */ "/greater",
472 /* 0x3f */ "/question",
473 /* 0x40 */ "/at",
474 /* 0x41 */ "/A",
475 /* 0x42 */ "/B",
476 /* 0x43 */ "/C",
477 /* 0x44 */ "/D",
478 /* 0x45 */ "/E",
479 /* 0x46 */ "/F",
480 /* 0x47 */ "/G",
481 /* 0x48 */ "/H",
482 /* 0x49 */ "/I",
483 /* 0x4a */ "/J",
484 /* 0x4b */ "/K",
485 /* 0x4c */ "/L",
486 /* 0x4d */ "/M",
487 /* 0x4e */ "/N",
488 /* 0x4f */ "/O",
489 /* 0x50 */ "/P",
490 /* 0x51 */ "/Q",
491 /* 0x52 */ "/R",
492 /* 0x53 */ "/S",
493 /* 0x54 */ "/T",
494 /* 0x55 */ "/U",
495 /* 0x56 */ "/V",
496 /* 0x57 */ "/W",
497 /* 0x58 */ "/X",
498 /* 0x59 */ "/Y",
499 /* 0x5a */ "/Z",
500 /* 0x5b */ "/bracketleft",
501 /* 0x5c */ "/backslash",
502 /* 0x5d */ "/bracketright",
503 /* 0x5e */ "/asciicircum",
504 /* 0x5f */ "/underscore",
505 /* 0x60 */ "/quoteleft",
506 /* 0x61 */ "/a",
507 /* 0x62 */ "/b",
508 /* 0x63 */ "/c",
509 /* 0x64 */ "/d",
510 /* 0x65 */ "/e",
511 /* 0x66 */ "/f",
512 /* 0x67 */ "/g",
513 /* 0x68 */ "/h",
514 /* 0x69 */ "/i",
515 /* 0x6a */ "/j",
516 /* 0x6b */ "/k",
517 /* 0x6c */ "/l",
518 /* 0x6d */ "/m",
519 /* 0x6e */ "/n",
520 /* 0x6f */ "/o",
521 /* 0x70 */ "/p",
522 /* 0x71 */ "/q",
523 /* 0x72 */ "/r",
524 /* 0x73 */ "/s",
525 /* 0x74 */ "/t",
526 /* 0x75 */ "/u",
527 /* 0x76 */ "/v",
528 /* 0x77 */ "/w",
529 /* 0x78 */ "/x",
530 /* 0x79 */ "/y",
531 /* 0x7a */ "/z",
532 /* 0x7b */ "/braceleft",
533 /* 0x7c */ "/bar",
534 /* 0x7d */ "/braceright",
535 /* 0x7e */ "/tilde",
536 /* 0x7f */ "non-printable",
537 /* 0x80 */ "non-printable",
538 /* 0x81 */ "non-printable",
539 /* 0x82 */ "non-printable",
540 /* 0x83 */ "non-printable",
541 /* 0x84 */ "non-printable",
542 /* 0x85 */ "non-printable",
543 /* 0x86 */ "non-printable",
544 /* 0x87 */ "non-printable",
545 /* 0x88 */ "non-printable",
546 /* 0x89 */ "non-printable",
547 /* 0x8a */ "non-printable",
548 /* 0x8b */ "non-printable",
549 /* 0x8c */ "non-printable",
550 /* 0x8d */ "non-printable",
551 /* 0x8e */ "non-printable",
552 /* 0x8f */ "non-printable",
553 /* 0x90 */ "non-printable",
554 /* 0x91 */ "non-printable",
555 /* 0x92 */ "non-printable",
556 /* 0x93 */ "non-printable",
557 /* 0x94 */ "non-printable",
558 /* 0x95 */ "non-printable",
559 /* 0x96 */ "non-printable",
560 /* 0x97 */ "non-printable",
561 /* 0x98 */ "non-printable",
562 /* 0x99 */ "non-printable",
563 /* 0x9a */ "non-printable",
564 /* 0x9b */ "non-printable",
565 /* 0x9c */ "non-printable",
566 /* 0x9d */ "non-printable",
567 /* 0x9e */ "non-printable",
568 /* 0x9f */ "non-printable",
569 /* 0xa0 */ "/space",
570 /* 0xa1 */ "/exclamdown",
571 /* 0xa2 */ "/cent",
572 /* 0xa3 */ "/sterling",
573 /* 0xa4 */ "/currency",
574 /* 0xa5 */ "/yen",
575 /* 0xa6 */ "/brokenbar",
576 /* 0xa7 */ "/section",
577 /* 0xa8 */ "/dieresis",
578 /* 0xa9 */ "/copyright",
579 /* 0xaa */ "/ordfeminine",
580 /* 0xab */ "/guillemotleft",
581 /* 0xac */ "/logicalnot",
582 /* 0xad */ "/hyphen",
583 /* 0xae */ "/registered",
584 /* 0xaf */ "/macron",
585 /* 0xb0 */ "/degree",
586 /* 0xb1 */ "/plusminus",
587 /* 0xb2 */ "/twosuperior",
588 /* 0xb3 */ "/threesuperior",
589 /* 0xb4 */ "/acute",
590 /* 0xb5 */ "/mu",
591 /* 0xb6 */ "/paragraph",
592 /* 0xb7 */ "/bullet",
593 /* 0xb8 */ "/cedilla",
594 /* 0xb9 */ "/dotlessi",
595 /* 0xba */ "/ordmasculine",
596 /* 0xbb */ "/guillemotright",
597 /* 0xbc */ "/onequarter",
598 /* 0xbd */ "/onehalf",
599 /* 0xbe */ "/threequarters",
600 /* 0xbf */ "/questiondown",
601 /* 0xc0 */ "/Agrave",
602 /* 0xc1 */ "/Aacute",
603 /* 0xc2 */ "/Acircumflex",
604 /* 0xc3 */ "/Atilde",
605 /* 0xc4 */ "/Adieresis",
606 /* 0xc5 */ "/Aring",
607 /* 0xc6 */ "/AE",
608 /* 0xc7 */ "/Ccedilla",
609 /* 0xc8 */ "/Egrave",
610 /* 0xc9 */ "/Eacute",
611 /* 0xca */ "/Ecircumflex",
612 /* 0xcb */ "/Edieresis",
613 /* 0xcc */ "/Igrave",
614 /* 0xcd */ "/Iacute",
615 /* 0xce */ "/Icircumflex",
616 /* 0xcf */ "/Idieresis",
617 /* 0xd0 */ "/Eth",
618 /* 0xd1 */ "/Ntilde",
619 /* 0xd2 */ "/Ograve",
620 /* 0xd3 */ "/Oacute",
621 /* 0xd4 */ "/Ocircumflex",
622 /* 0xd5 */ "/Otilde",
623 /* 0xd6 */ "/Odieresis",
624 /* 0xd7 */ "/multiply",
625 /* 0xd8 */ "/Oslash",
626 /* 0xd9 */ "/Ugrave",
627 /* 0xda */ "/Uacute",
628 /* 0xdb */ "/Ucircumflex",
629 /* 0xdc */ "/Udieresis",
630 /* 0xdd */ "/Yacute",
631 /* 0xde */ "/Thorn",
632 /* 0xdf */ "/germandbls",
633 /* 0xe0 */ "/agrave",
634 /* 0xe1 */ "/aacute",
635 /* 0xe2 */ "/acircumflex",
636 /* 0xe3 */ "/atilde",
637 /* 0xe4 */ "/adieresis",
638 /* 0xe5 */ "/aring",
639 /* 0xe6 */ "/ae",
640 /* 0xe7 */ "/ccedilla",
641 /* 0xe8 */ "/egrave",
642 /* 0xe9 */ "/eacute",
643 /* 0xea */ "/ecircumflex",
644 /* 0xeb */ "/edieresis",
645 /* 0xec */ "/igrave",
646 /* 0xed */ "/iacute",
647 /* 0xee */ "/icircumflex",
648 /* 0xef */ "/idieresis",
649 /* 0xf0 */ "/eth",
650 /* 0xf1 */ "/ntilde",
651 /* 0xf2 */ "/ograve",
652 /* 0xf3 */ "/oacute",
653 /* 0xf4 */ "/ocircumflex",
654 /* 0xf5 */ "/otilde",
655 /* 0xf6 */ "/odieresis",
656 /* 0xf7 */ "/divide",
657 /* 0xf8 */ "/oslash",
658 /* 0xf9 */ "/ugrave",
659 /* 0xfa */ "/uacute",
660 /* 0xfb */ "/ucircumflex",
661 /* 0xfc */ "/udieresis",
662 /* 0xfd */ "/yacute",
663 /* 0xfe */ "/thorn",
664 /* 0xff */ "/ydieresis"
665 };
666
667 /*
668 * Process the encoding
669 */
670 static void
DoEncoding(void)671 DoEncoding(void)
672 {
673 int i, j;
674
675 if (! afm)
676 return;
677
678 for (i=0; i<afm->StartCharMetrics; i++) {
679 afm->charMetrics[i].code = -1;
680
681 for (j=0; j<256; j++) {
682 if (strcmp(IsoLatin1Encoding[j] + 1, afm->charMetrics[i].name) == 0) {
683 afm->charMetrics[i].code = j;
684 break;
685 }
686 }
687 }
688
689 /* Build the quick access */
690 for (i=0; i<256; i++)
691 afm->charIndex[i] = -1;
692 for (i=0; i<afm->StartCharMetrics; i++)
693 if (afm->charMetrics[i].code > 0 && afm->charMetrics[i].code < 256)
694 afm->charIndex[afm->charMetrics[i].code] = i;
695 }
696
697 void
AfmSetEncoding(const char * enc)698 AfmSetEncoding(const char *enc)
699 {
700 #if 0
701 fprintf(stderr, "AfmSetEncoding(%s)\n", enc);
702 #endif
703 DoEncoding();
704
705 /* FIX ME */
706 }
707