Back to index

tetex-bin  3.0
mapfile.c
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 1996-2005 Han The Thanh, <thanh@pdftex.org>
00003 
00004 This file is part of pdfTeX.
00005 
00006 pdfTeX is free software; you can redistribute it and/or modify
00007 it under the terms of the GNU General Public License as published by
00008 the Free Software Foundation; either version 2 of the License, or
00009 (at your option) any later version.
00010 
00011 pdfTeX is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 GNU General Public License for more details.
00015 
00016 You should have received a copy of the GNU General Public License
00017 along with pdfTeX; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/mapfile.c#28 $
00021 */
00022 
00023 #include <math.h>
00024 #include "ptexlib.h"
00025 #include <kpathsea/c-auto.h>
00026 #include <kpathsea/c-memstr.h>
00027 #include "avlstuff.h"
00028 
00029 static const char perforce_id[] =
00030     "$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/mapfile.c#28 $";
00031 
00032 #define FM_BUF_SIZE     1024
00033 
00034 static FILE *fm_file;
00035 
00036 #define fm_open()       \
00037     open_input (&fm_file, kpse_fontmap_format, FOPEN_RBIN_MODE)
00038 #define fm_close()      xfclose(fm_file, cur_file_name)
00039 #define fm_getchar()    xgetc(fm_file)
00040 #define fm_eof()        feof(fm_file)
00041 
00042 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
00043 enum _ltype { MAPFILE, MAPLINE };
00044 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND};
00045 
00046 typedef struct mitem {
00047     int mode;            /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */
00048     int type;            /* mapfile or map line */
00049     char *line;            /* pointer to map line */
00050     struct mitem *next;        /* pointer to next item, or NULL */
00051 } mapitem;
00052 mapitem *miptr, *mapitems = NULL;
00053 
00054 fm_entry *fm_cur;
00055 static const char nontfm[] = "<nontfm>";
00056 static fm_entry *loaded_tfm_found;
00057 static fm_entry *avail_tfm_found;
00058 static fm_entry *non_tfm_found;
00059 static fm_entry *not_avail_tfm_found;
00060 
00061 static const char *basefont_names[] = {
00062     "Courier",
00063     "Courier-Bold",
00064     "Courier-Oblique",
00065     "Courier-BoldOblique",
00066     "Helvetica",
00067     "Helvetica-Bold",
00068     "Helvetica-Oblique",
00069     "Helvetica-BoldOblique",
00070     "Symbol",
00071     "Times-Roman",
00072     "Times-Bold",
00073     "Times-Italic",
00074     "Times-BoldItalic",
00075     "ZapfDingbats",
00076     NULL
00077 };
00078 
00079 #define read_field(r, q, buf) do {                     \
00080     for (q = buf; *r != ' ' && *r != 10; *q++ = *r++); \
00081     *q = 0;                                            \
00082     skip (r, ' ');                                     \
00083 } while (0)
00084 
00085 #define set_field(F) do {                              \
00086     if (q > buf)                                       \
00087         fm_ptr->F = xstrdup(buf);                      \
00088     if (*r == 10)                                      \
00089         goto done;                                     \
00090 } while (0)
00091 
00092 static fm_entry *new_fm_entry(void)
00093 {
00094     fm_entry *fm;
00095     fm = xtalloc(1, fm_entry);
00096     fm->tfm_name = NULL;
00097     fm->ps_name = NULL;
00098     fm->flags = 0;
00099     fm->ff_name = NULL;
00100     fm->subset_tag = NULL;
00101     fm->encoding = NULL;
00102     fm->tfm_num = getnullfont();
00103     fm->tfm_avail = TFM_UNCHECKED;
00104     fm->type = 0;
00105     fm->slant = 0;
00106     fm->extend = 0;
00107     fm->ff_objnum = 0;
00108     fm->fn_objnum = 0;
00109     fm->fd_objnum = 0;
00110     fm->charset = NULL;
00111     fm->all_glyphs = false;
00112     fm->links = 0;
00113     return fm;
00114 }
00115 
00116 static void delete_fm_entry(fm_entry * fm)
00117 {
00118     xfree(fm->tfm_name);
00119     xfree(fm->ps_name);
00120     xfree(fm->ff_name);
00121     xfree(fm->subset_tag);
00122     xfree(fm->charset);
00123     xfree(fm);
00124 }
00125 
00126 static ff_entry *new_ff_entry(void)
00127 {
00128     ff_entry *ff;
00129     ff = xtalloc(1, ff_entry);
00130     ff->ff_name = NULL;
00131     ff->ff_path = NULL;
00132     return ff;
00133 }
00134 
00135 static void delete_ff_entry(ff_entry * ff)
00136 {
00137     xfree(ff->ff_name);
00138     xfree(ff->ff_path);
00139     xfree(ff);
00140 }
00141 
00142 static fm_entry *dummy_fm_entry()
00143 {
00144     static const fm_entry const_fm_entry;
00145     return (fm_entry*) &const_fm_entry;
00146 }
00147 
00148 char *mk_base_tfm(char *tfmname, int *i)
00149 {
00150     static char buf[SMALL_BUF_SIZE];
00151     char *p = tfmname, *r = strend(p) - 1, *q = r;
00152     while (q > p && isdigit(*q))
00153         --q;
00154     if (!(q > p) || q == r || (*q != '+' && *q != '-'))
00155         return NULL;
00156     check_buf(q - p + 1, SMALL_BUF_SIZE);
00157     strncpy(buf, p, (unsigned) (q - p));
00158     buf[q - p] = 0;
00159     *i = atoi(q);
00160     return buf;
00161 }
00162 
00163 static fmentryptr fmlookup(internalfontnumber);
00164 
00165 boolean hasfmentry(internalfontnumber f)
00166 {
00167     if (pdffontmap[f] == NULL)
00168         pdffontmap[f] = fmlookup(f);
00169     assert(pdffontmap[f] != NULL);
00170     return pdffontmap[f] != (fmentryptr) dummy_fm_entry();
00171 }
00172 
00173 /**********************************************************************/
00174 
00175 struct avl_table *tfm_tree = NULL;
00176 struct avl_table *ps_tree = NULL;
00177 struct avl_table *ff_tree = NULL;
00178 
00179 /* AVL sort fm_entry into tfm_tree by tfm_name */
00180 
00181 static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p)
00182 {
00183     return strcmp(((const fm_entry *) pa)->tfm_name,
00184                   ((const fm_entry *) pb)->tfm_name);
00185 }
00186 
00187 #define cmp_return(a, b)    \
00188     if (a > b)              \
00189         return 1;           \
00190     if (a < b)              \
00191         return -1
00192 
00193 /* AVL sort fm_entry into ps_tree by ps_name, slant, and extend */
00194 static int comp_fm_entry_ps(const void *pa, const void *pb, void *p)
00195 {
00196     const fm_entry *p1 = (const fm_entry *) pa,
00197                    *p2 = (const fm_entry *) pb;
00198     int i;
00199     assert(p1->ps_name != NULL && p2->ps_name != NULL);
00200     if ((i = strcmp(p1->ps_name, p2->ps_name)) != 0)
00201         return i;
00202     cmp_return(p1->slant, p2->slant);
00203     cmp_return(p1->extend, p2->extend);
00204     if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
00205         (i = strcmp(p1->tfm_name, p2->tfm_name)) != 0)
00206         return i;
00207     return 0;
00208 }
00209 
00210 /* AVL sort ff_entry into ff_tree by ff_name */
00211 
00212 static int comp_ff_entry(const void *pa, const void *pb, void *p)
00213 {
00214     return strcmp(((const ff_entry *) pa)->ff_name,
00215                  ((const ff_entry *) pb)->ff_name);
00216 }
00217 
00218 static void create_avl_trees()
00219 {
00220     if (tfm_tree == NULL) {
00221         tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator);
00222         assert(tfm_tree != NULL);
00223     }
00224     if (ps_tree == NULL) {
00225         ps_tree = avl_create(comp_fm_entry_ps, NULL, &avl_xallocator);
00226         assert(ps_tree != NULL);
00227     }
00228     if (ff_tree == NULL) {
00229         ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator);
00230         assert(ff_tree != NULL);
00231     }
00232 }
00233 
00234 /*
00235 The function avl_do_entry() is not completely symmetrical with regards
00236 to tfm_name and ps_name handling, e. g. a duplicate tfm_name gives a
00237 "goto exit", and no ps_name link is tried. This is to keep it compatible
00238 with the original version.
00239 */
00240 
00241 static int avl_do_entry(fm_entry * fp, int mode)
00242 {
00243     fm_entry *p, *p2;
00244     void *a;
00245     void **aa;
00246     struct avl_traverser trav;
00247 
00248     /* handle tfm_name link */
00249 
00250     if (strcmp(fp->tfm_name, nontfm) != 0) {
00251         p = (fm_entry *) avl_find(tfm_tree, fp);
00252         if (p != NULL) {
00253             if (mode == FM_DUPIGNORE) {
00254                 pdftex_warn("fontmap entry for `%s' already exists, duplicates ignored",
00255                             fp->tfm_name);
00256                 goto exit;
00257             } else { /* mode == FM_REPLACE / FM_DELETE */
00258                 if (fontused[p->tfm_num]) {
00259                     pdftex_warn("fontmap entry for `%s' has been used, replace/delete not allowed",
00260                                 fp->tfm_name);
00261                     goto exit;        
00262                 }
00263                 a = avl_delete(tfm_tree, p);
00264                 assert(a != NULL);
00265                 unset_tfmlink(p);
00266                 if (!has_pslink(p))
00267                     delete_fm_entry(p);
00268             }
00269         }
00270         if (mode != FM_DELETE) {
00271             aa = avl_probe(tfm_tree, fp);
00272             assert(aa != NULL);
00273             set_tfmlink(fp);
00274         }
00275     }
00276 
00277     /* handle ps_name link */
00278 
00279     if (fp->ps_name != NULL) {
00280         assert(fp->tfm_name != NULL);
00281         p = (fm_entry *) avl_find(ps_tree, fp);
00282         if (p != NULL) {
00283             if (mode == FM_DUPIGNORE) {
00284                 pdftex_warn("ps_name entry for `%s' already exists, duplicates ignored",
00285                             fp->ps_name);
00286                 goto exit;
00287             } else { /* mode == FM_REPLACE / FM_DELETE */
00288                 if (fontused[p->tfm_num]) {
00289                     /* REPLACE/DELETE not allowed */
00290                     pdftex_warn("fontmap entry for `%s' has been used, replace/delete not allowed",
00291                                 p->tfm_name);
00292                     goto exit;        
00293                 }
00294                 a = avl_delete(ps_tree, p);
00295                 assert(a != NULL);
00296                 unset_pslink(p);
00297                 if (!has_tfmlink(p))
00298                     delete_fm_entry(p);
00299             }
00300         }
00301         if (mode != FM_DELETE) {
00302             aa = avl_probe(ps_tree, fp);
00303             assert(aa != NULL);
00304             set_pslink(fp);
00305         }
00306     }
00307 exit:
00308     if (!has_tfmlink(fp) && !has_pslink(fp))        /* e. g. after FM_DELETE */
00309         return 1;                /* deallocation of fm_entry structure required */
00310     else
00311         return 0;
00312 }
00313 
00314 /**********************************************************************/
00315 
00316 static void fm_scan_line(mapitem * mitem)
00317 {
00318     int a, b, c, j, u = 0, v = 0;
00319     float d;
00320     char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
00321     char **pn;
00322     fm_entry *fm_ptr = NULL;
00323     char *p, *q, *r, *s, *t = NULL;
00324     p = fm_line;
00325     switch (mitem->type) {
00326     case MAPFILE:
00327         do {
00328             c = fm_getchar();
00329             append_char_to_buf(c, p, fm_line, FM_BUF_SIZE);
00330         }
00331         while (c != 10);
00332         break;
00333     case MAPLINE:
00334         t = mitem->line;
00335         while ((c = *t++) != 0)
00336             append_char_to_buf(c, p, fm_line, FM_BUF_SIZE);
00337         break;
00338     default:
00339         assert(0);
00340     }
00341     append_eol(p, fm_line, FM_BUF_SIZE);
00342     if (is_cfg_comment(*fm_line))
00343         return;
00344     r = fm_line;
00345     read_field(r, q, buf);
00346     fm_ptr = new_fm_entry();
00347     set_field(tfm_name);
00348     if (*r == 10)
00349         goto done;
00350     p = r;
00351     read_field(r, q, buf);
00352     if (*buf != '<' && *buf != '"')
00353         set_field(ps_name);
00354     else
00355         r = p;                        /* unget the field */
00356     if (isdigit(*r)) {                /* font flags given */
00357         fm_ptr->flags = atoi(r);
00358         while (isdigit(*r))
00359             r++;
00360     } else
00361         fm_ptr->flags = 4;      /* treat as Symbol font */
00362     while (1) {                 /* this was former label reswitch: */
00363         skip(r, ' ');
00364         switch (*r) {
00365         case 10:
00366             goto done;
00367         case '"':                /* opening quote */
00368             r++;
00369             u = v = 0;
00370             do {
00371                 skip(r, ' ');
00372                 if (sscanf(r, "%f %n", &d, &j) > 0) {
00373                     s = r + j;        /* jump behind number, eat also blanks, if any */
00374                     if (*(s - 1) == 'E' || *(s - 1) == 'e')
00375                         s--;        /* e. g. 0.5ExtendFont: %f = 0.5E */
00376                     if (strncmp(s, "SlantFont", strlen("SlantFont")) == 0) {
00377                         d *= 1000.0;        /* correct rounding also for neg. numbers */
00378                         fm_ptr->slant =
00379                             (integer) (d > 0 ? d + 0.5 : d - 0.5);
00380                         r = s + strlen("SlantFont");
00381                     } else if (strncmp(s, "ExtendFont", strlen("ExtendFont")) == 0) {
00382                         d *= 1000.0;
00383                         fm_ptr->extend =
00384                             (integer) (d > 0 ? d + 0.5 : d - 0.5);
00385                         if (fm_ptr->extend == 1000)
00386                             fm_ptr->extend = 0;
00387                         r = s + strlen("ExtendFont");
00388                     } else {
00389                         pdftex_warn
00390                             ("invalid entry for `%s': unknown name `%s' ignored",
00391                              fm_ptr->tfm_name, s);
00392                         for (r = s; *r != ' ' && *r != '"' && *r != 10;
00393                              r++);
00394                     }
00395                 } else
00396                     for (; *r != ' ' && *r != '"' && *r != 10; r++);
00397             } while (*r == ' ');
00398             if (*r == '"')        /* closing quote */
00399                 r++;
00400             else {
00401                 pdftex_warn("invalid entry for `%s': unknown line format",
00402                             fm_ptr->tfm_name);
00403                 goto bad_line;
00404             }
00405             break;
00406         default: /* encoding or font file specification */
00407             a = b = 0;
00408             if (*r == '<') {
00409                 a = *r++;
00410                 if (*r == '<' || *r == '[')
00411                     b = *r++;
00412             }
00413             read_field(r, q, buf);
00414                 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
00415             if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) {
00416                 fm_ptr->encoding = add_enc(buf);
00417                 u = 0;                      /* u, v used if intervening blank: "<< foo" */
00418                 v = 0;
00419             } else if (strlen(buf) > 0) {
00420                 /* fontfile, formats:
00421                  * subsetting:    '<cmr10.pfa' 
00422                  * no subsetting: '<<cmr10.pfa' 
00423                  * no embedding:  'cmr10.pfa' 
00424                  */
00425                 if (a == '<' || u == '<') {
00426                     set_included(fm_ptr);
00427                     if ((a == '<' && b == 0) || (a == 0 && v == 0))
00428                         set_subsetted(fm_ptr);
00429                     /* otherwise b == '<' (or '[') => no subsetting */
00430                 }
00431                 set_field(ff_name);
00432                 u = 0;
00433                 v = 0;
00434             } else {
00435                 u = a;
00436                 v = b;
00437             }
00438         }
00439     }
00440   done:
00441     if (fm_ptr->ps_name != NULL) {
00442         /* check whether this is a base font */
00443         for (pn = (char**)basefont_names; *pn != NULL; ++pn)
00444             if (strcmp(*pn, fm_ptr->ps_name) == 0) {
00445                 set_basefont(fm_ptr);
00446                 break;
00447             }
00448         /* when no font file is given and this is not a base font, drop this entry */
00449         if (!is_fontfile(fm_ptr) && !is_basefont(fm_ptr)) {
00450             pdftex_warn("invalid entry for `%s': font file missing",
00451                     fm_ptr->tfm_name);
00452             goto bad_line;
00453         }
00454         if (is_fontfile(fm_ptr) && is_basefont(fm_ptr) && !is_included(fm_ptr)) {
00455             pdftex_warn(
00456                 "invalid entry for `%s': font file must be included or omitted for base fonts",
00457                 fm_ptr->tfm_name);
00458             goto bad_line;
00459         }
00460     }
00461     if (is_fontfile(fm_ptr) && 
00462         strcasecmp(strend(fm_fontfile(fm_ptr)) - 4, ".ttf") == 0)
00463         set_truetype(fm_ptr);
00464     if ((fm_ptr->slant != 0 || fm_ptr->extend != 0) &&
00465         (!is_included(fm_ptr) || is_truetype(fm_ptr))) {
00466         pdftex_warn
00467             ("invalid entry for `%s': SlantFont/ExtendFont can be used only with embedded T1 fonts",
00468              fm_ptr->tfm_name);
00469         goto bad_line;
00470     }
00471     if (is_truetype(fm_ptr) && (is_reencoded(fm_ptr)) && !is_subsetted(fm_ptr)) {
00472         pdftex_warn
00473             ("invalid entry for `%s': only subsetted TrueType font can be reencoded",
00474              fm_ptr->tfm_name);
00475         goto bad_line;
00476     }
00477     if (abs(fm_ptr->slant) >= 1000) {
00478         pdftex_warn
00479             ("invalid entry for `%s': too big value of SlantFont (%g)",
00480              fm_ptr->tfm_name, fm_ptr->slant / 1000.0);
00481         goto bad_line;
00482     }
00483     if (abs(fm_ptr->extend) >= 2000) {
00484         pdftex_warn
00485             ("invalid entry for `%s': too big value of ExtendFont (%g)",
00486              fm_ptr->tfm_name, fm_ptr->extend / 1000.0);
00487         goto bad_line;
00488     }
00489 
00490     /*
00491        Until here the map line has been completely scanned without errors;
00492        fm_ptr points to a valid, freshly filled-out fm_entry structure.
00493        Now follows the actual work of registering/deleting.
00494      */
00495 
00496     if (avl_do_entry(fm_ptr, mitem->mode) == 1) /* if no link to fm_entry */
00497         goto bad_line;
00498     return;
00499   bad_line:
00500     delete_fm_entry(fm_ptr);
00501 }
00502 
00503 void fm_read_info()
00504 {
00505     mapitem *tmp;
00506     create_avl_trees();
00507     while (mapitems != NULL) {
00508         assert(mapitems->line != NULL);
00509         switch (mapitems->type) {
00510         case MAPFILE:
00511             set_cur_file_name(mapitems->line);
00512             if (!fm_open()) {
00513                 pdftex_warn("cannot open font map file");
00514             } else {
00515                 cur_file_name = (char *) nameoffile + 1;
00516                 tex_printf("{%s", cur_file_name);
00517                 while (!fm_eof())
00518                     fm_scan_line(mapitems);
00519                 fm_close();
00520                 tex_printf("}");
00521                 fm_file = NULL;
00522             }
00523             break;
00524         case MAPLINE:
00525             cur_file_name = NULL;        /* makes pdftex_warn() shorter */
00526             fm_scan_line(mapitems);
00527             break;
00528         default:
00529             assert(0);
00530         }
00531         tmp = mapitems;
00532         mapitems = mapitems->next;
00533         xfree(tmp->line);
00534         xfree(tmp);
00535     }
00536     cur_file_name = NULL;
00537     return;
00538 }
00539 
00540 /**********************************************************************/
00541 
00542 /*
00543 char *mk_exname(char *basename, int e)
00544 {
00545     static char buf[SMALL_BUF_SIZE];
00546     char *p = basename, *r;
00547     if ((r = strrchr(p, '.')) == NULL)
00548         r = strend(p);
00549     check_buf(r - p + strlen(r) + 10, SMALL_BUF_SIZE);
00550     strncpy(buf, p, (unsigned) (r - p));
00551     sprintf(buf + (r - p), "%+i", e);
00552     strcat(buf, r);
00553     return buf;
00554 }
00555 */
00556 
00557 internalfontnumber tfmoffm(fmentryptr fm_pt)
00558 {
00559     return ((fm_entry *) fm_pt)->tfm_num;
00560 }
00561 
00562 static fm_entry *mk_ex_fm(internalfontnumber f, fm_entry * basefm, int ex)
00563 {
00564     fm_entry *fm;
00565     integer e = basefm->extend;
00566     if (e == 0)
00567         e = 1000;
00568     fm = new_fm_entry();
00569     fm->flags = basefm->flags;
00570     fm->encoding = basefm->encoding;
00571     fm->type = basefm->type;
00572     fm->slant = basefm->slant;
00573     fm->extend = roundxnoverd(e, 1000 + ex, 1000); /* modify ExtentFont to simulate expansion */
00574     if (fm->extend == 1000)
00575         fm->extend = 0;
00576     fm->tfm_name = xstrdup(makecstring(fontname[f]));
00577     if (basefm->ps_name != NULL)
00578         fm->ps_name = xstrdup(basefm->ps_name);
00579     fm->ff_name = xstrdup(basefm->ff_name);
00580     fm->ff_objnum = pdfnewobjnum();
00581     fm->tfm_num = f;
00582     fm->tfm_avail = TFM_FOUND;
00583     assert(strcmp(fm->tfm_name, nontfm) != 0);
00584     return fm;
00585 }
00586 
00587 static void init_fm(fm_entry * fm, internalfontnumber f)
00588 {
00589     if (fm->fd_objnum == 0)
00590         fm->fd_objnum = pdfnewobjnum();
00591     if (fm->ff_objnum == 0 && is_included(fm))
00592         fm->ff_objnum = pdfnewobjnum();
00593     if (fm->tfm_num == getnullfont()) {
00594         fm->tfm_num = f;
00595         fm->tfm_avail = TFM_FOUND;
00596     }
00597 }
00598 
00599 static fmentryptr fmlookup(internalfontnumber f)
00600 {
00601     char *tfm, *p;
00602     fm_entry *fm, *exfm;
00603     fm_entry tmp;
00604     int ai, e;
00605     if (tfm_tree == NULL || mapitems != NULL)
00606         fm_read_info();
00607     tfm = makecstring(fontname[f]);
00608     assert(strcmp(tfm, nontfm) != 0);
00609 
00610     /* Look up for full <tfmname>[+-]<expand> */
00611     tmp.tfm_name = tfm;
00612     fm = (fm_entry *) avl_find(tfm_tree, &tmp);
00613     if (fm != NULL) {
00614         init_fm(fm, f);
00615         return (fmentryptr) fm;
00616     }
00617     tfm = mk_base_tfm(makecstring(fontname[f]), &e);
00618     if (tfm == NULL) /* not an expanded font, nothing to do */
00619         return (fmentryptr) dummy_fm_entry();
00620 
00621     tmp.tfm_name = tfm;
00622     fm = (fm_entry *) avl_find(tfm_tree, &tmp);
00623     if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
00624         if (!is_t1fontfile(fm) || !is_included(fm)) {
00625             pdftex_warn(
00626                 "font %s cannot be expanded (not an included Type1 font)",
00627                 tfm);
00628             return (fmentryptr) dummy_fm_entry();
00629         }
00630         exfm = mk_ex_fm(f, fm, e); /* copies all fields from fm except tfm name */
00631         init_fm(exfm, f);
00632         ai = avl_do_entry(exfm, FM_DUPIGNORE);
00633         assert(ai == 0);
00634         return (fmentryptr) exfm;
00635     }
00636     return (fmentryptr) dummy_fm_entry();
00637 }
00638 
00639 
00640 /**********************************************************************/
00641 
00642 /*
00643    Early check whether a font file exists. Used e. g. for replacing fonts
00644    of embedded PDF files: Without font file, the font within the embedded
00645    PDF-file is used. Search tree ff_tree is used in 1st instance, as it
00646    may be faster than the kpse_find_file(), and kpse_find_file() is called
00647    only once per font file name + expansion parameter. This might help
00648    keeping speed, if many PDF pages with same fonts are to be embedded.
00649 
00650    The ff_tree contains only font files, which are actually needed,
00651    so this tree typically is much smaller than the tfm_tree or ps_tree.
00652 */
00653 
00654 ff_entry *check_ff_exist(fm_entry * fm)
00655 {
00656     ff_entry *ff;
00657     ff_entry tmp;
00658     char *ex_ffname;
00659     void **aa;
00660 
00661     assert(fm->ff_name != NULL);
00662     tmp.ff_name = fm->ff_name;
00663     ff = (ff_entry *) avl_find(ff_tree, &tmp);
00664     if (ff == NULL) {                /* not yet in database */
00665         ff = new_ff_entry();
00666         ff->ff_name = xstrdup(fm->ff_name);
00667         if (is_truetype(fm))
00668             ff->ff_path =
00669                 kpse_find_file(fm->ff_name, kpse_truetype_format, 0);
00670         else
00671             ff->ff_path =
00672                 kpse_find_file(fm->ff_name, kpse_type1_format, 0);
00673         aa = avl_probe(ff_tree, ff);
00674         assert(aa != NULL);
00675     }
00676     return ff;
00677 }
00678 
00679 /**********************************************************************/
00680 
00681 static boolean used_tfm(fm_entry *p)
00682 {
00683     internalfontnumber f;
00684     strnumber s;
00685     ff_entry *ff;
00686 
00687     /* check if the font file is not a TrueType font */
00688     /* font replacement makes sense only for included Type1 files */
00689     if (is_truetype(p) || !is_included(p))
00690         return false;
00691 
00692     /* check if the font file is available */
00693     ff = check_ff_exist(p); 
00694     if (ff->ff_path == NULL)
00695         return false;
00696 
00697     /* check whether this font has been used */
00698     if (fontused[p->tfm_num])
00699         return true;
00700     assert(p->tfm_name != NULL);
00701 
00702     /* check whether we didn't find a loaded font yet,
00703      * and this font has been loaded */
00704     if (loaded_tfm_found == NULL && strcmp(p->tfm_name, nontfm) != 0) {
00705         s = maketexstring(p->tfm_name);
00706         if ((f = tfmlookup(s, 0)) != getnullfont()) {
00707             loaded_tfm_found = p; 
00708             if (pdffontmap[f] == NULL)
00709                 pdffontmap[f] = (fmentryptr) p;
00710             if (p->tfm_num == getnullfont())
00711                 p->tfm_num = f;
00712             assert(p->tfm_num == f);
00713             /* don't call flushstr() here as it has been called by tfmlookup() */
00714         }
00715         else
00716             flushstr(s);
00717     }
00718 
00719     /* check whether we didn't find either a loaded or a loadable font yet,
00720      * and this font is loadable */
00721     if (avail_tfm_found == NULL && loaded_tfm_found == NULL &&
00722         strcmp(p->tfm_name, nontfm) != 0) {
00723         if (p->tfm_avail == TFM_UNCHECKED) {
00724             if (kpse_find_file(p->tfm_name, kpse_tfm_format, 0) != NULL) {
00725                 avail_tfm_found = p;
00726                 p->tfm_avail = TFM_FOUND;
00727             }
00728             else {
00729                 p->tfm_avail = TFM_NOTFOUND;
00730                 if (not_avail_tfm_found == NULL)
00731                     not_avail_tfm_found = p;
00732             }
00733         }
00734     }
00735 
00736     /* check whether the current entry is a <nontfm> entry */
00737     if (non_tfm_found == NULL && strcmp(p->tfm_name, nontfm) == 0)
00738         non_tfm_found = p;
00739 
00740     return false;
00741 }
00742 
00743 /* lookup_ps_name looks for an entry with a given ps name + slant + extend.
00744  * As there may exist several such entries, we need to select the `right'
00745  * one. We do so by checking all such entries and return the first one that
00746  * fulfils the following criteria (in descending priority):
00747  *
00748  * - the tfm has been used (some char from this font has been typeset)
00749  * - the tfm has been loaded (but not used yet)
00750  * - the tfm can be loaded (but not loaded yet)
00751  * - the tfm is present in map files, but cannot be loaded. In this case a
00752  *   dummy tfm can be loaded instead, and a warning should be written out
00753  */
00754 static fm_entry *lookup_ps_name(fm_entry *fm)
00755 {
00756     fm_entry *p, *p2;
00757     struct avl_traverser t, t2;
00758     strnumber s;
00759     int a;
00760 
00761     loaded_tfm_found = NULL;
00762     avail_tfm_found = NULL;
00763     non_tfm_found = NULL;
00764     not_avail_tfm_found = NULL;
00765 
00766     assert(fm->tfm_name == NULL);
00767     p = (fm_entry *) avl_t_find(&t, ps_tree, fm);
00768     if (p == NULL)
00769         return NULL;
00770     t2 = t;
00771     p2 = avl_t_prev(&t2);
00772 
00773     /* search forward */
00774     do {
00775         if (used_tfm(p))
00776             return p;
00777         p = avl_t_next(&t);
00778     } while (p != NULL && comp_fm_entry_ps(fm, p, NULL) == 0);
00779 
00780     /* search backward */
00781     while (p2 != NULL && comp_fm_entry_ps(fm, p2, NULL) == 0) {
00782         if (used_tfm(p2))
00783             return p2;
00784         p2 = avl_t_prev(&t2);
00785     }
00786 
00787     if (loaded_tfm_found != NULL)
00788         p = loaded_tfm_found;
00789     else if (avail_tfm_found != NULL) {
00790         p = avail_tfm_found;
00791         p->tfm_num = readfontinfo(getnullcs(), maketexstring(p->tfm_name), 
00792                                   getnullstr(), -1000);
00793         p->tfm_avail = TFM_FOUND;
00794     } else if (non_tfm_found != NULL) {
00795         p = non_tfm_found;
00796         p->tfm_num = newdummyfont();
00797         p->tfm_avail = TFM_FOUND;
00798     } else if (not_avail_tfm_found != NULL) {
00799         p = not_avail_tfm_found;
00800         pdftex_warn("`%s' not loadable, use a dummy tfm instead", p->tfm_name);
00801         p2 = new_fm_entry();
00802         p2->flags = p->flags;
00803         p2->encoding = p->encoding;
00804         p2->type = p->type;
00805         p2->slant = p->slant;
00806         p2->extend = p->extend;
00807         p2->tfm_name = xstrdup(nontfm);
00808         p2->ps_name = xstrdup(p->ps_name);
00809         if (p->ff_name != NULL)
00810             p2->ff_name = xstrdup(p->ff_name);
00811         p2->tfm_num = newdummyfont();
00812         p2->tfm_avail = TFM_FOUND;
00813         a = avl_do_entry(p2, FM_DUPIGNORE);
00814         assert(a == 0);
00815         p = p2;
00816     } else
00817         return NULL;
00818     assert(p->tfm_num != getnullfont());
00819     return p;
00820 }
00821 
00822 /* Lookup fontmap for /BaseFont entries of embedded PDF-files */
00823 
00824 fm_entry *lookup_fontmap(char *bname)
00825 {
00826     fm_entry *fm, *fmx;
00827     fm_entry tmp, tmpx;
00828     ff_entry *ff;
00829     char buf[SMALL_BUF_SIZE];
00830     char *a, *b, *c, *d, *e, *s;
00831     strnumber str;
00832     int i, sl, ex, ai;
00833     if (tfm_tree == NULL || mapitems != NULL)
00834         fm_read_info();
00835     if (bname == NULL)
00836         return dummy_fm_entry();
00837     if (strlen(bname) >= SMALL_BUF_SIZE)
00838         pdftex_fail("Font name too long: `%s'", bname);
00839     strcpy(buf, bname);         /* keep bname untouched for later */
00840     s = buf; 
00841     if (strlen(buf) > 7) {        /* check for subsetted name tag */
00842         for (i = 0; i < 6; i++, s++)
00843             if (*s < 'A' || *s > 'Z')
00844                 break;
00845         if (i == 6 && *s == '+')
00846             s++;                /* if name tag found, skip behind it */
00847         else
00848             s = buf;
00849     }
00850 
00851     /*
00852        Scan -Slant_<slant> and -Extend_<extend> font name extensions;
00853        three valid formats:
00854        <fontname>-Slant_<slant>
00855        <fontname>-Slant_<slant>-Extend_<extend>
00856        <fontname>-Extend_<extend>
00857        Slant entry must come _before_ Extend entry
00858      */
00859 
00860     tmp.slant = 0;
00861     tmp.extend = 0;
00862     if ((a = strstr(s, "-Slant_")) != NULL) {
00863         b = a + strlen("-Slant_");
00864         sl = (int) strtol(b, &e, 10);
00865         if ((e != b) && (e == strend(b))) {
00866             tmp.slant = sl;
00867             *a = 0;                /* bname string ends before "-Slant_" */
00868         } else {
00869             if (e != b) {        /* only if <slant> is valid number */
00870                 if ((c = strstr(e, "-Extend_")) != NULL) {
00871                     d = c + strlen("-Extend_");
00872                     ex = (int) strtol(d, &e, 10);
00873                     if ((e != d) && (e == strend(d))) {
00874                         tmp.slant = sl;
00875                         tmp.extend = ex;
00876                         *a = 0;        /* bname string ends before "-Slant_" */
00877                     }
00878                 }
00879             }
00880         }
00881     } else {
00882         if ((a = strstr(s, "-Extend_")) != NULL) {
00883             b = a + strlen("-Extend_");
00884             ex = (int) strtol(b, &e, 10);
00885             if ((e != b) && (e == strend(b))) {
00886                 tmp.extend = ex;
00887                 *a = 0;                /* bname string ends before "-Extend_" */
00888             }
00889         }
00890     }
00891     tmp.ps_name = s;
00892     tmp.tfm_name = NULL;
00893     fm = lookup_ps_name(&tmp);
00894     if (fm != NULL) {
00895         i = fm->tfm_num;
00896         assert(i != getnullfont());
00897         if (pdffontmap[i] == NULL)
00898             pdffontmap[i] = (fmentryptr) fm;
00899         if (fm->ff_objnum == 0 && is_included(fm))
00900             fm->ff_objnum = pdfnewobjnum();
00901         if (!fontused[i])
00902             pdfinitfont(i);
00903         return fm;
00904     }
00905 /*
00906    The following code snipplet handles fonts with "Slant" and "Extend"
00907    name extensions in embedded PDF files, which don't yet have an
00908    fm_entry. If such a font is found (e. g. CMR10-Extend_1020), and no
00909    fm_entry for this is found in the ps_tree (e. g. ps_name = "CMR10",
00910    extend = 1020), and if an unextended base font (e. g. CMR10) is found,
00911    a new <nontfm> fm_entry is created and put into the ps_tree. Then
00912    the lookup_fontmap() function is (recursively) called again, which
00913    then should find the new fm_entry. The same can be done manually by
00914    a map entry e. g.:
00915 
00916    \pdfmapline{+<nontfm> CMR10 "1.02 ExtendFont" <cmr10.pfb}
00917 
00918    This would also match the embedded font CMR10-Extend_1020, and replace
00919    it by an extended copy of cmr10.pfb. -- But not by an expanded version;
00920    no MM files (e.g. cmr10+20.pfb) would be used.
00921 */
00922 
00923     tmpx.ps_name = s;
00924     tmpx.tfm_name = NULL;
00925     tmpx.slant = 0;
00926     tmpx.extend = 0;
00927 /*     fm = (fm_entry *) avl_find(ps_tree, &tmpx); */
00928     fm = lookup_ps_name(&tmpx);
00929     if (fm != NULL) {
00930         if (is_truetype(fm) || !is_included(fm))
00931             return dummy_fm_entry();
00932         ff = check_ff_exist(fm);
00933         if (ff->ff_path == NULL)
00934             return dummy_fm_entry();
00935         fmx = new_fm_entry();
00936         fmx->flags = fm->flags;
00937         fmx->encoding = fm->encoding;
00938         fmx->type = fm->type;
00939         fmx->slant = tmp.slant;
00940         fmx->extend = tmp.extend;
00941         fmx->tfm_name = xstrdup(nontfm);
00942         fmx->ps_name = xstrdup(s);
00943         fmx->ff_name = xstrdup(fm->ff_name);
00944         ai = avl_do_entry(fmx, FM_DUPIGNORE);
00945         assert(ai == 0);
00946         fm = lookup_fontmap(bname);        /* new try */
00947         assert(fm != NULL);
00948         return fm;
00949     }
00950     return dummy_fm_entry();
00951 }
00952 
00953 
00954 /**********************************************************************/
00955 /* cleaning up... */
00956 
00957 static void destroy_fm_entry_tfm(void *pa, void *pb)
00958 {
00959     fm_entry *fm;
00960     fm = (fm_entry *) pa;
00961     if (!has_pslink(fm))
00962         delete_fm_entry(fm);
00963     else
00964         unset_tfmlink(fm);
00965 }
00966 
00967 static void destroy_fm_entry_ps(void *pa, void *pb)
00968 {
00969     fm_entry *fm;
00970     fm = (fm_entry *) pa;
00971     if (!has_tfmlink(fm))
00972         delete_fm_entry(fm);
00973     else
00974         unset_pslink(fm);
00975 }
00976 
00977 static void destroy_ff_entry(void *pa, void *pb)
00978 {
00979     ff_entry *ff;
00980     ff = (ff_entry *) pa;
00981     delete_ff_entry(ff);
00982 }
00983 
00984 void fm_free(void)
00985 {
00986     if (tfm_tree != NULL)
00987         avl_destroy(tfm_tree, destroy_fm_entry_tfm);
00988     if (ps_tree != NULL)
00989         avl_destroy(ps_tree, destroy_fm_entry_ps);
00990     if (ff_tree != NULL)
00991         avl_destroy(ff_tree, destroy_ff_entry);
00992 }
00993 
00994 /**********************************************************************/
00995 
00996 /*
00997 Add mapfile name or mapline contents to the linked list "mapitems". Items
00998 not beginning with [+-=] flush list with pending items. Leading blanks
00999 and blanks immediately following [+-=] are ignored.
01000 */
01001 
01002 char *add_map_item(char *s, int type)
01003 {
01004     char *p;
01005     int l;            /* length of map item (without [+-=]) */
01006     mapitem *tmp;
01007     int mode;
01008     for (; *s == ' '; s++);    /* ignore leading blanks */
01009     switch (*s) {
01010     case '+':            /* +mapfile.map, +mapline */
01011     mode = FM_DUPIGNORE;    /* insert entry, if it is not duplicate */
01012     s++;
01013     break;
01014     case '=':            /* =mapfile.map, =mapline */
01015     mode = FM_REPLACE;        /* try to replace earlier entry */
01016     s++;
01017     break;
01018     case '-':            /* -mapfile.map, -mapline */
01019     mode = FM_DELETE;        /* try to delete entry */
01020     s++;
01021     break;
01022     default:
01023     mode = FM_DUPIGNORE;    /* also flush pending list */
01024     while (mapitems != NULL) {
01025         tmp = mapitems;
01026         mapitems = mapitems->next;
01027         xfree(tmp->line);
01028         xfree(tmp);
01029     }
01030     }
01031     for (; *s == ' '; s++); /* ignore blanks after [+-=] */
01032     p = s;                  /* map item starts here */
01033     switch (type) {         /* find end of map item */
01034     case MAPFILE:
01035     for (; *p != 0 && *p != ' ' && *p != 10; p++);
01036     break;
01037     case MAPLINE:        /* blanks allowed */
01038     for (; *p != 0 && *p != 10; p++);
01039     break;
01040     default:
01041     assert(0);
01042     }
01043     l = p - s;
01044     if (l > 0) {        /* only if real item to add */
01045     tmp = xtalloc(1, mapitem);
01046     if (mapitems == NULL)
01047         mapitems = tmp;    /* start new list */
01048     else
01049         miptr->next = tmp;
01050     miptr = tmp;
01051     miptr->mode = mode;
01052     miptr->type = type;
01053     miptr->line = xtalloc(l + 1, char);
01054     *(miptr->line) = 0;
01055     strncat(miptr->line, s, l);
01056     miptr->next = NULL;
01057     }
01058     return p;
01059 }
01060 
01061 void pdfmapfile(integer t)
01062 {
01063     add_map_item(makecstring(tokenstostring(t)), MAPFILE);
01064     flushstr(lasttokensstring);
01065 }
01066 
01067 void pdfmapline(integer t)
01068 {
01069     add_map_item(makecstring(tokenstostring(t)), MAPLINE);
01070     flushstr(lasttokensstring);
01071 }
01072 
01073 void pdfinitmapfile(string map_name)
01074 {
01075     if (mapitems == NULL) {
01076         mapitems = xtalloc(1, mapitem);
01077         miptr = mapitems;
01078         miptr->mode = FM_DUPIGNORE;
01079         miptr->type = MAPFILE;
01080         miptr->line = xstrdup(map_name);
01081         miptr->next = NULL;
01082     }
01083 }
01084 
01085 /**********************************************************************/
01086 
01087 /* end of mapfile.c */