1 /* tfmfile.c -- readers for TFM, AFM, OTFM-0 and OTFM-1 files */
3 * Copyright (C) 2000, Matias Atria
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <stdio.h> /* tex-file.h needs this */
37 typedef struct tfmpool {
45 static ListHead tfmpool = {NULL, NULL, 0};
46 static DviHashTable tfmhash;
48 #define TFM_HASH_SIZE 31
50 #ifdef WORD_LITTLE_ENDIAN
51 static inline void swap_array(Uint32 *ptr, int n)
57 *ptr++ = ((i & 0xff000000) >> 24)
58 | ((i & 0x00ff0000) >> 8)
59 | ((i & 0x0000ff00) << 8)
60 | ((i & 0x000000ff) << 24);
67 static int __PROTO(ofm_load_file(const char *filename, TFMInfo *info));
69 /* reading of AFM files */
70 /* macro to convert between AFM and TFM units */
71 #define AFM2TFM(x) FROUND((double)(x) * 0x100000 / 1000)
72 int afm_load_file(const char *filename, TFMInfo *info)
74 /* the information we want is:
84 in = fopen(filename, "rb");
87 status = afm_parse_file(in, &fi, P_GM);
91 mdvi_error(_("%s: Error reading AFM data\n"), filename);
96 info->chars = xnalloc(TFMChar, 256);
99 info->design = 0xa00000; /* fake -- 10pt */
100 info->checksum = 0; /* no checksum */
101 info->type = DviFontAFM;
102 mdvi_strncpy(info->coding, fi->gfi->encodingScheme, 63);
103 mdvi_strncpy(info->family, fi->gfi->familyName, 63);
105 /* now get the data */
106 for(cm = fi->cmi; cm < fi->cmi + fi->numOfChars; cm++) {
111 if(code < 0 || code > 255)
112 continue; /* ignore it */
113 ch = &info->chars[code];
119 ch->advance = AFM2TFM(cm->wx);
120 /* this is the `leftSideBearing' */
121 ch->left = AFM2TFM(cm->charBBox.llx);
122 /* this is the height (ascent - descent) -- the sign is to follow
123 * TeX conventions, as opposed to Adobe's ones */
124 ch->depth = -AFM2TFM(cm->charBBox.lly);
125 /* this is the width (rightSideBearing - leftSideBearing) */
126 ch->right = AFM2TFM(cm->charBBox.urx);
127 /* this is the `ascent' */
128 ch->height = AFM2TFM(cm->charBBox.ury);
131 /* we don't need this anymore */
132 afm_free_fontinfo(fi);
134 /* optimize storage */
135 if(info->loc > 0 || info->hic < 256) {
136 memmove(&info->chars[0],
137 &info->chars[info->loc],
138 (info->hic - info->loc + 1) * sizeof(TFMChar));
139 info->chars = mdvi_realloc(info->chars,
140 (info->hic - info->loc + 1) * sizeof(TFMChar));
147 #endif /* WITH_AFM_FILES */
149 int tfm_load_file(const char *filename, TFMInfo *info)
151 int lf, lh, bc, ec, nw, nh, nd, ne;
165 in = fopen(filename, "rb");
170 DEBUG((DBG_FONTS, "(mt) reading TFM file `%s'\n",
172 /* We read the entire TFM file into core */
173 if(fstat(fileno(in), &st) < 0)
175 /* according to the spec, TFM files are smaller than 16K */
176 if(st.st_size == 0 || st.st_size >= 16384)
179 /* allocate a word-aligned buffer to hold the file */
180 size = 4 * ROUND(st.st_size, 4);
181 if(size != st.st_size)
182 mdvi_warning(_("Warning: TFM file `%s' has suspicious size\n"),
184 tfm = (Uchar *)mdvi_malloc(size);
185 if(fread(tfm, st.st_size, 1, in) != 1)
187 /* we don't need this anymore */
191 /* not a checksum, but serves a similar purpose */
195 /* get the counters */
197 lh = muget2(ptr); checksum += 6 + lh;
199 ec = muget2(ptr); checksum += ec - bc + 1;
200 nw = muget2(ptr); checksum += nw;
201 nh = muget2(ptr); checksum += nh;
202 nd = muget2(ptr); checksum += nd;
203 checksum += muget2(ptr); /* skip italics correction count */
204 checksum += muget2(ptr); /* skip lig/kern table size */
205 checksum += muget2(ptr); /* skip kern table size */
206 ne = muget2(ptr); checksum += ne;
207 checksum += muget2(ptr); /* skip # of font parameters */
210 cb = (Int32 *)tfm; cb += 6 + lh;
211 charinfo = cb; cb += size;
212 widths = cb; cb += nw;
213 heights = cb; cb += nh;
216 if(widths[0] || heights[0] || depths[0] ||
217 checksum != lf || bc - 1 > ec || ec > 255 || ne > 256)
220 /* from this point on, no error checking is done */
222 /* now we're at the header */
223 /* get the checksum */
224 info->checksum = muget4(ptr);
225 /* get the design size */
226 info->design = muget4(ptr);
227 /* get the coding scheme */
229 /* get the coding scheme */
231 if(n < 0 || n > 39) {
232 mdvi_warning(_("%s: font coding scheme truncated to 40 bytes\n"),
236 memcpy(info->coding, ptr, n);
240 strcpy(info->coding, "FontSpecific");
241 /* get the font family */
246 memcpy(info->family, ptr, i);
249 strcpy(info->family, "unspecified");
252 /* now we don't read from `ptr' anymore */
256 info->type = DviFontTFM;
258 /* allocate characters */
259 info->chars = xnalloc(TFMChar, size);
262 #ifdef WORD_LITTLE_ENDIAN
263 /* byte-swap the three arrays at once (they are consecutive in memory) */
264 swap_array((Uint32 *)widths, nw + nh + nd);
267 /* get the relevant data */
268 ptr = (Uchar *)charinfo;
269 for(i = bc; i <= ec; ptr += 3, i++) {
272 ndx = (int)*ptr; ptr++;
273 info->chars[i-bc].advance = widths[ndx];
274 /* TFM files lack this information */
275 info->chars[i-bc].left = 0;
276 info->chars[i-bc].right = widths[ndx];
277 info->chars[i-bc].present = (ndx != 0);
279 ndx = ((*ptr >> 4) & 0xf);
280 info->chars[i-bc].height = heights[ndx];
282 info->chars[i-bc].depth = depths[ndx];
286 /* free everything */
292 mdvi_error(_("%s: File corrupted, or not a TFM file\n"), filename);
294 if(tfm) mdvi_free(tfm);
299 static int ofm1_load_file(FILE *in, TFMInfo *info)
301 int lf, lh, bc, ec, nw, nh, nd;
320 fuget4(in); /* italics */
321 fuget4(in); /* lig-kern */
322 fuget4(in); /* kern */
323 fuget4(in); /* extensible recipe */
324 fuget4(in); /* parameters */
325 fuget4(in); /* direction */
330 /* get the checksum */
331 info->checksum = fuget4(in);
332 /* the design size */
333 info->design = fuget4(in);
334 /* get the coding scheme */
336 /* get the coding scheme */
340 fread(info->coding, 39, 1, in);
343 strcpy(info->coding, "FontSpecific");
344 /* get the font family */
349 fread(info->family, i, 1, in);
352 strcpy(info->family, "unspecified");
356 /* jump to the beginning of the char-info table */
357 fseek(in, 4L*nco, SEEK_SET);
362 info->chars = xnalloc(TFMChar, size);
363 end = info->chars + size;
365 for(tch = info->chars, i = 0; i < ncw; i++) {
369 /* in the characters we store the actual indices */
370 ch.advance = fuget2(in);
371 ch.height = fuget1(in);
372 ch.depth = fuget1(in);
375 /* get # of repeats */
377 /* skip parameters */
378 fseek(in, (long)npc * 2, SEEK_CUR);
379 /* if npc is odd, skip padding */
380 if(npc & 1) fuget2(in);
382 /* now repeat the character */
383 while(nr-- >= 0 && tch < end)
384 memcpy(tch++, &ch, sizeof(TFMChar));
389 /* I wish we were done, but we aren't */
391 /* get the widths, heights and depths */
393 tfm = xnalloc(Int32, size);
394 /* read them in one sweep */
395 if(fread(tfm, 4, size, in) != size) {
400 /* byte-swap things if necessary */
401 #ifdef WORD_LITTLE_ENDIAN
402 swap_array((Uint32 *)tfm, size);
405 heights = widths + nw;
406 depths = heights + nh;
408 if(widths[0] || heights[0] || depths[0])
411 /* now fix the characters */
413 for(tch = info->chars; tch < end; tch++) {
414 tch->present = (tch->advance != 0);
415 tch->advance = widths[tch->advance];
416 tch->height = heights[tch->height];
417 tch->depth = depths[tch->depth];
419 tch->right = tch->advance;
427 if(tfm) mdvi_free(tfm);
431 /* we don't read OFM files into memory, because they can potentially be large */
432 static int ofm_load_file(const char *filename, TFMInfo *info)
434 int lf, lh, bc, ec, nw, nh, nd;
449 in = fopen(filename, "rb");
453 /* not a checksum, but serves a similar purpose */
456 /* get the counters */
463 DEBUG((DBG_FONTS, "(mt) reading Level-1 OFM file `%s'\n",
465 /* we handle level-1 files separately */
466 if(ofm1_load_file(in, info) < 0)
471 DEBUG((DBG_FONTS, "(mt) reading Level-0 OFM file `%s'\n", filename));
473 lf = fuget4(in); checksum = nwords;
474 lh = fuget4(in); checksum += lh;
476 ec = fuget4(in); checksum += 2 * (ec - bc + 1);
477 nw = fuget4(in); checksum += nw;
478 nh = fuget4(in); checksum += nh;
479 nd = fuget4(in); checksum += nd;
480 checksum += fuget4(in); /* skip italics correction count */
481 checksum += 2*fuget4(in); /* skip lig/kern table size */
482 checksum += fuget4(in); /* skip kern table size */
483 checksum += 2*fuget4(in); /* skip extensible recipe count */
484 checksum += fuget4(in); /* skip # of font parameters */
486 /* I have found several .ofm files that seem to have the
487 * font-direction word missing, so we try to detect that here */
488 if(checksum == lf + 1) {
489 DEBUG((DBG_FONTS, "(mt) font direction missing in `%s'\n",
494 /* skip font direction */
498 if(checksum != lf || bc > ec + 1 || ec > 65535)
501 /* now we're at the header */
503 /* get the checksum */
504 info->checksum = fuget4(in);
505 /* get the design size */
506 info->design = fuget4(in);
508 /* get the coding scheme */
510 /* get the coding scheme */
512 if(n < 0 || n > 39) {
513 mdvi_warning(_("%s: font coding scheme truncated to 40 bytes\n"),
517 fread(info->coding, 39, 1, in);
520 strcpy(info->coding, "FontSpecific");
521 /* get the font family */
526 fread(info->family, i, 1, in);
529 strcpy(info->family, "unspecified");
532 /* now skip anything else in the header */
533 fseek(in, 4L*(nwords + lh), SEEK_SET);
534 /* and read everything at once */
535 size = 2*(ec - bc + 1) + nw + nh + nd;
536 tfm = xnalloc(Int32, size * sizeof(Int32));
537 if(fread(tfm, 4, size, in) != size) {
541 /* byte-swap all the tables at once */
542 #ifdef WORD_LITTLE_ENDIAN
543 swap_array((Uint32 *)tfm, size);
546 charinfo = cb; cb += 2*(ec - bc + 1);
547 widths = cb; cb += nw;
548 heights = cb; cb += nh;
551 if(widths[0] || heights[0] || depths[0]) {
556 /* from this point on, no error checking is done */
558 /* we don't need this anymore */
561 /* now we don't read from `ptr' anymore */
565 info->type = DviFontTFM;
567 /* allocate characters */
568 info->chars = xnalloc(TFMChar, size);
570 /* get the relevant data */
571 ptr = (Uchar *)charinfo;
572 for(i = bc; i <= ec; ptr += 4, i++) {
576 info->chars[i-bc].advance = widths[ndx];
577 /* TFM files lack this information */
578 info->chars[i-bc].left = 0;
579 info->chars[i-bc].right = widths[ndx];
580 info->chars[i-bc].present = (ndx != 0);
582 info->chars[i-bc].height = heights[ndx];
584 info->chars[i-bc].depth = depths[ndx];
591 mdvi_error(_("%s: File corrupted, or not a TFM file\n"), filename);
596 char *lookup_font_metrics(const char *name, int *type)
601 #ifndef WITH_AFM_FILES
605 file = kpse_find_tfm(name);
609 file = kpse_find_ofm(name);
610 /* we may have gotten a TFM back */
612 const char *ext = file_extension(file);
613 if(ext && STREQ(ext, "tfm"))
618 #ifdef WITH_AFM_FILES
620 file = kpse_find_file(name, kpse_afm_format, 0);
623 file = kpse_find_file(name, kpse_afm_format, 0);
626 file = kpse_find_tfm(name);
639 * The next two functions are just wrappers for the font metric loaders,
640 * and use the pool of TFM data
643 /* this is how we interpret arguments:
644 * - if filename is NULL, we look for files of the given type,
645 * unless type is DviFontAny, in which case we try all the
647 * - if filename is not NULL, we look at `type' to decide
648 * how to read the file. If type is DviFontAny, we just
651 TFMInfo *get_font_metrics(const char *short_name, int type, const char *filename)
658 tfm = (TFMPool *)mdvi_hash_lookup(&tfmhash,
659 MDVI_KEY(short_name));
661 DEBUG((DBG_FONTS, "(mt) reusing metric file `%s' (%d links)\n",
662 short_name, tfm->links));
664 return &tfm->tfminfo;
668 file = filename ? (char *)filename : lookup_font_metrics(short_name, &type);
672 tfm = xalloc(TFMPool);
673 DEBUG((DBG_FONTS, "(mt) loading font metric data from `%s'\n", file, file));
676 status = tfm_load_file(file, &tfm->tfminfo);
679 status = ofm_load_file(file, &tfm->tfminfo);
681 #ifdef WITH_AFM_FILES
683 status = afm_load_file(file, &tfm->tfminfo);
696 tfm->short_name = mdvi_strdup(short_name);
698 /* add it to the pool */
699 if(tfmpool.count == 0)
700 mdvi_hash_create(&tfmhash, TFM_HASH_SIZE);
701 mdvi_hash_add(&tfmhash, MDVI_KEY(tfm->short_name),
702 tfm, MDVI_HASH_UNCHECKED);
703 listh_prepend(&tfmpool, LIST(tfm));
706 return &tfm->tfminfo;
709 void free_font_metrics(TFMInfo *info)
713 if(tfmpool.count == 0)
715 /* get the entry -- can't use the hash table for this, because
716 * we don't have the short name */
717 for(tfm = (TFMPool *)tfmpool.head; tfm; tfm = tfm->next)
718 if(info == &tfm->tfminfo)
722 if(--tfm->links > 0) {
723 DEBUG((DBG_FONTS, "(mt) %s not removed, still in use\n",
727 mdvi_hash_remove_ptr(&tfmhash, MDVI_KEY(tfm->short_name));
729 DEBUG((DBG_FONTS, "(mt) removing unused TFM data for `%s'\n", tfm->short_name));
730 listh_remove(&tfmpool, LIST(tfm));
731 mdvi_free(tfm->short_name);
732 mdvi_free(tfm->tfminfo.chars);
736 void flush_font_metrics(void)
740 for(; (ptr = (TFMPool *)tfmpool.head); ) {
741 tfmpool.head = LIST(ptr->next);
743 mdvi_free(ptr->short_name);
744 mdvi_free(ptr->tfminfo.chars);
747 mdvi_hash_reset(&tfmhash, 0);