Back to index

texmacs  1.0.7.15
fontmap.c
Go to the documentation of this file.
00001 /*  $Header: /home/cvsroot/dvipdfmx/src/fontmap.c,v 1.42 2009/09/18 23:56:02 matthias Exp $
00002     
00003     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
00004 
00005     Copyright (C) 2007 by Jin-Hwan Cho and Shunsaku Hirata,
00006     the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
00007     
00008     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014     
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019     
00020     You should have received a copy of the GNU General Public License
00021     along with this program; if not, write to the Free Software
00022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00023 */
00024 
00025 #if HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif
00028 
00029 #include "system.h"
00030 #include "mem.h"
00031 #include "error.h"
00032 
00033 #include "dpxfile.h"
00034 #include "dpxutil.h"
00035 
00036 #include "subfont.h"
00037 
00038 #include "fontmap.h"
00039 
00040 /* CIDFont */
00041 static char *strip_options (const char *map_name, fontmap_opt *opt);
00042 
00043 static int verbose = 0;
00044 void
00045 pdf_fontmap_set_verbose (void)
00046 {
00047   verbose++;
00048 }
00049 
00050 
00051 void
00052 pdf_init_fontmap_record (fontmap_rec *mrec) 
00053 {
00054   ASSERT(mrec);
00055 
00056   mrec->map_name   = NULL;
00057 
00058   /* SFD char mapping */
00059   mrec->charmap.sfd_name   = NULL;
00060   mrec->charmap.subfont_id = NULL;
00061   /* for OFM */
00062   mrec->opt.mapc   = -1; /* compatibility */
00063 
00064   mrec->font_name  = NULL;
00065   mrec->enc_name   = NULL;
00066 
00067   mrec->opt.slant  = 0.0;
00068   mrec->opt.extend = 1.0;
00069   mrec->opt.bold   = 0.0;
00070 
00071   mrec->opt.flags  = 0;
00072 
00073   mrec->opt.design_size = -1.0;
00074 
00075   mrec->opt.tounicode = NULL;
00076   mrec->opt.otl_tags  = NULL; /* deactivated */
00077   mrec->opt.index     = 0;
00078   mrec->opt.charcoll  = NULL;
00079   mrec->opt.style     = FONTMAP_STYLE_NONE;
00080   mrec->opt.stemv     = -1; /* not given explicitly by an option */
00081 }
00082 
00083 void
00084 pdf_clear_fontmap_record (fontmap_rec *mrec)
00085 {
00086   ASSERT(mrec);
00087 
00088   if (mrec->map_name)
00089     RELEASE(mrec->map_name);
00090   if (mrec->charmap.sfd_name)
00091     RELEASE(mrec->charmap.sfd_name);
00092   if (mrec->charmap.subfont_id)
00093     RELEASE(mrec->charmap.subfont_id);
00094   if (mrec->enc_name)
00095     RELEASE(mrec->enc_name);
00096   if (mrec->font_name)
00097     RELEASE(mrec->font_name);
00098 
00099   if (mrec->opt.tounicode)
00100     RELEASE(mrec->opt.tounicode);
00101   if (mrec->opt.otl_tags)
00102     RELEASE(mrec->opt.otl_tags);
00103   if (mrec->opt.charcoll)
00104     RELEASE(mrec->opt.charcoll);
00105   pdf_init_fontmap_record(mrec);
00106 }
00107 
00108 /* strdup: just returns NULL for NULL */
00109 static char *
00110 mstrdup (const char *s)
00111 {
00112   char  *r;
00113   if (!s)
00114     return  NULL;
00115   r = NEW(strlen(s) + 1, char);
00116   strcpy(r, s);
00117   return  r;
00118 }
00119 
00120 static void
00121 pdf_copy_fontmap_record (fontmap_rec *dst, const fontmap_rec *src)
00122 {
00123   ASSERT( dst && src );
00124 
00125   dst->map_name   = mstrdup(src->map_name);
00126 
00127   dst->charmap.sfd_name   = mstrdup(src->charmap.sfd_name);
00128   dst->charmap.subfont_id = mstrdup(src->charmap.subfont_id);
00129 
00130   dst->font_name  = mstrdup(src->font_name);
00131   dst->enc_name   = mstrdup(src->enc_name);
00132 
00133   dst->opt.slant  = src->opt.slant;
00134   dst->opt.extend = src->opt.extend;
00135   dst->opt.bold   = src->opt.bold;
00136 
00137   dst->opt.flags  = src->opt.flags;
00138   dst->opt.mapc   = src->opt.mapc;
00139 
00140   dst->opt.tounicode = mstrdup(src->opt.tounicode);
00141   dst->opt.otl_tags  = mstrdup(src->opt.otl_tags);
00142   dst->opt.index     = src->opt.index;
00143   dst->opt.charcoll  = mstrdup(src->opt.charcoll);
00144   dst->opt.style     = src->opt.style;
00145   dst->opt.stemv     = src->opt.stemv;
00146 }
00147 
00148 
00149 static void
00150 hval_free (void *vp)
00151 {
00152   fontmap_rec *mrec = (fontmap_rec *) vp;
00153   pdf_clear_fontmap_record(mrec);
00154   RELEASE(mrec);
00155 }
00156 
00157 
00158 static void
00159 fill_in_defaults (fontmap_rec *mrec, const char *tex_name)
00160 {
00161   if (mrec->enc_name &&
00162       (!strcmp(mrec->enc_name, "default") ||
00163        !strcmp(mrec->enc_name, "none"))) {
00164     RELEASE(mrec->enc_name);
00165     mrec->enc_name = NULL;
00166   }
00167   if (mrec->font_name && 
00168       (!strcmp(mrec->font_name, "default") ||
00169        !strcmp(mrec->font_name, "none"))) {
00170     RELEASE(mrec->font_name);
00171     mrec->font_name = NULL;
00172   }
00173   /* We *must* fill font_name either explicitly or by default */
00174   if (!mrec->font_name) {
00175     mrec->font_name = NEW(strlen(tex_name)+1, char);
00176     strcpy(mrec->font_name, tex_name);
00177   }
00178 
00179   mrec->map_name = NEW(strlen(tex_name)+1, char);
00180   strcpy(mrec->map_name, tex_name);
00181 
00182 #ifndef WITHOUT_COMPAT
00183   /* Use "UCS" character collection for Unicode SFD
00184    * and Identity CMap combination. For backward
00185    * compatibility.
00186    */
00187   if (mrec->charmap.sfd_name && mrec->enc_name &&
00188       !mrec->opt.charcoll) {
00189     if ((!strcmp(mrec->enc_name, "Identity-H") ||
00190          !strcmp(mrec->enc_name, "Identity-V"))
00191           &&
00192          (strstr(mrec->charmap.sfd_name, "Uni")  ||
00193           strstr(mrec->charmap.sfd_name, "UBig") ||
00194           strstr(mrec->charmap.sfd_name, "UBg")  ||
00195           strstr(mrec->charmap.sfd_name, "UGB")  ||
00196           strstr(mrec->charmap.sfd_name, "UKS")  ||
00197           strstr(mrec->charmap.sfd_name, "UJIS"))) {
00198       mrec->opt.charcoll = NEW(strlen("UCS")+1, char);
00199       strcpy(mrec->opt.charcoll, "UCS");
00200     }
00201   }
00202 #endif /* WITHOUT_COMPAT */
00203 
00204   return;
00205 }
00206 
00207 static char *
00208 readline (char *buf, int buf_len, FILE *fp)
00209 {
00210   char  *p, *q;
00211   ASSERT( buf && buf_len > 0 && fp );
00212   p = mfgets(buf, buf_len, fp);
00213   if (!p)
00214     return  NULL;
00215   q = strchr(p, '%'); /* we don't have quoted string */
00216   if (q)
00217     *q = '\0';
00218   return  p;
00219 }
00220 
00221 #ifndef ISBLANK
00222 #  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
00223 #endif
00224 static void
00225 skip_blank (char **pp, char *endptr)
00226 {
00227   char  *p = *pp;
00228   if (!p || p >= endptr)
00229     return;
00230   for ( ; p < endptr && ISBLANK(*p); p++);
00231   *pp = p;
00232 }
00233 
00234 static char *
00235 parse_string_value (char **pp, char *endptr)
00236 {
00237   char  *q = NULL, *p = *pp;
00238   int    n;
00239 
00240   if (!p || p >= endptr)
00241     return  NULL;
00242   if (*p == '"')
00243     q = parse_c_string(&p, endptr);
00244   else {
00245     for (n = 0; p < endptr && !isspace(*p); p++, n++);
00246     if (n == 0)
00247       return  NULL;
00248     q = NEW(n + 1, char);
00249     memcpy(q, *pp, n); q[n] = '\0';
00250   }
00251 
00252   *pp = p;
00253   return  q;
00254 }
00255 
00256 /* no preceeding spaces allowed */
00257 static char *
00258 parse_integer_value (char **pp, char *endptr, int base)
00259 {
00260   char  *q, *p = *pp;
00261   int    has_sign = 0, has_prefix = 0, n;
00262 
00263   ASSERT( base == 0 || (base >= 2 && base <= 36) );
00264 
00265   if (!p || p >= endptr)
00266     return  NULL;
00267 
00268   if (*p == '-' || *p == '+') {
00269     p++; has_sign = 1;
00270   }
00271   if ((base == 0 || base == 16) &&
00272       p + 2 <= endptr &&
00273       p[0] == '0' && p[1] == 'x') {
00274     p += 2; has_prefix = 1;
00275   }
00276   if (base == 0) {
00277     if (has_prefix)
00278       base = 16;
00279     else if (p < endptr && *p == '0')
00280       base = 8;
00281     else {
00282       base = 10;
00283     }
00284   }
00285 #define ISDIGIT_WB(c,b) ( \
00286   ((b) <= 10 && (c) >= '0' && (c) < '0' + (b)) || \
00287   ((b) >  10 && ( \
00288       ((c) >= '0' && (c) <= '9') || \
00289       ((c) >= 'a' && (c) < 'a' + ((b) - 10)) || \
00290       ((c) >= 'A' && (c) < 'A' + ((b) - 10)) \
00291     ) \
00292   ) \
00293 )
00294   for (n = 0; p < endptr && ISDIGIT_WB(*p, base); p++, n++);
00295   if (n == 0)
00296     return  NULL;
00297   if (has_sign)
00298     n += 1;
00299   if (has_prefix)
00300     n += 2;
00301 
00302   q = NEW(n + 1, char);
00303   memcpy(q, *pp, n); q[n] = '\0';
00304 
00305   *pp = p;
00306   return  q;
00307 }
00308 
00309 static int
00310 fontmap_parse_mapdef_dpm (fontmap_rec *mrec,
00311                           const char *mapdef, char *endptr)
00312 {
00313   char  *p = (char *) mapdef;
00314 
00315   /*
00316    * Parse record line in map file.  First two fields (after TeX font
00317    * name) are position specific.  Arguments start at the first token
00318    * beginning with a  '-'.
00319    *
00320    * NOTE:
00321    *   Dvipdfm basically uses parse_ident() for parsing enc_name,
00322    *   font_name, and other string values which assumes PostScript-like
00323    *   syntax.
00324    *   skip_white() skips '\r' and '\n' but they should terminate
00325    *   fontmap line here.
00326    */
00327 
00328   skip_blank(&p, endptr);
00329   /* encoding field */
00330   if (p < endptr && *p != '-') { /* May be NULL */
00331     mrec->enc_name = parse_string_value(&p, endptr);
00332     skip_blank(&p, endptr);
00333   }
00334 
00335   /* fontname or font filename field */
00336   if (p < endptr && *p != '-') { /* May be NULL */
00337     mrec->font_name = parse_string_value(&p, endptr);
00338     skip_blank(&p, endptr);
00339   }
00340   if (mrec->font_name) {
00341     char  *tmp;
00342     /* Several options are encoded in font_name for
00343      * compatibility with dvipdfm.
00344      */
00345     tmp = strip_options(mrec->font_name, &mrec->opt);
00346     if (tmp) {
00347       RELEASE(mrec->font_name);
00348       mrec->font_name = tmp;
00349     }
00350   }
00351 
00352   skip_blank(&p, endptr);
00353   /* Parse any remaining arguments */
00354   while (p + 1 < endptr &&
00355          *p != '\r' && *p != '\n' && *p == '-') {
00356     char  *q, mopt = p[1];
00357     long   v;
00358 
00359     p += 2; skip_blank(&p, endptr);
00360     switch (mopt) {
00361 
00362     case  's': /* Slant option */
00363       q = parse_float_decimal(&p, endptr);
00364       if (!q) {
00365         WARN("Missing a number value for 's' option.");
00366         return  -1;
00367       }
00368       mrec->opt.slant = atof(q);
00369       RELEASE(q);
00370       break;
00371 
00372     case  'e': /* Extend option */
00373       q = parse_float_decimal(&p, endptr);
00374       if (!q) {
00375         WARN("Missing a number value for 'e' option.");
00376         return  -1;
00377       }
00378       mrec->opt.extend = atof(q);
00379       if (mrec->opt.extend <= 0.0) {
00380         WARN("Invalid value for 'e' option: %s", q);
00381         return  -1;
00382       }
00383       RELEASE(q);
00384       break;
00385 
00386     case  'b': /* Fake-bold option */
00387       q = parse_float_decimal(&p, endptr);
00388       if (!q) {
00389         WARN("Missing a number value for 'b' option.");
00390         return  -1;
00391       }
00392       mrec->opt.bold = atof(q);
00393       if (mrec->opt.bold <= 0.0) {
00394         WARN("Invalid value for 'b' option: %s", q);
00395         return  -1;
00396       }
00397       RELEASE(q);
00398       break;
00399 
00400     case  'r': /* Remap option; obsolete; just ignore */
00401       break;
00402 
00403     case  'i':  /* TTC index */
00404       q = parse_integer_value(&p, endptr, 10);
00405       if (!q) {
00406         WARN("Missing TTC index number...");
00407         return  -1;
00408       }
00409       mrec->opt.index = atoi(q);
00410       if (mrec->opt.index < 0) {
00411         WARN("Invalid TTC index number: %s", q);
00412         return  -1;
00413       }
00414       RELEASE(q);
00415       break;
00416 
00417     case  'p': /* UCS plane: just for testing */
00418       q = parse_integer_value(&p, endptr, 0);
00419       if (!q) {
00420         WARN("Missing a number for 'p' option.");
00421         return  -1;
00422       }
00423       v = strtol(q, NULL, 0);
00424       if (v < 0 || v > 16)
00425         WARN("Invalid value for option 'p': %s", q);
00426       else {
00427         mrec->opt.mapc = v << 16;
00428       }
00429       RELEASE(q);
00430       break;
00431 
00432     case  'u': /* ToUnicode */
00433       q = parse_string_value(&p, endptr);
00434       if (q)
00435         mrec->opt.tounicode = q;
00436       else {
00437         WARN("Missing string value for option 'u'.");
00438         return  -1;
00439       }
00440       break;
00441 
00442     case  'v': /* StemV */
00443       q = parse_integer_value(&p, endptr, 10);
00444       if (!q) {
00445         WARN("Missing a number for 'v' option.");
00446         return  -1;
00447       }
00448       mrec->opt.stemv = strtol(q, NULL, 0);
00449       RELEASE(q);
00450       break;
00451 
00452     /* Omega uses both single-byte and double-byte set_char command
00453      * even for double-byte OFMs. This confuses CMap decoder.
00454      */
00455     case  'm':
00456       /* Map single bytes char 0xab to double byte char 0xcdab  */
00457       if (p + 4 <= endptr &&
00458           p[0] == '<' && p[3] == '>') {
00459         p++;
00460         q = parse_integer_value(&p, endptr, 16);
00461         if (!q) {
00462           WARN("Invalid value for option 'm'.");
00463           return  -1;
00464         } else if (p < endptr && *p != '>') {
00465           WARN("Invalid value for option 'm': %s", q);
00466           RELEASE(q);
00467           return  -1;
00468         }
00469         v = strtol(q, NULL, 16);
00470         mrec->opt.mapc = ((v << 8) & 0x0000ff00L);
00471         RELEASE(q); p++;
00472       } else if (p + 4 <= endptr &&
00473                  !memcmp(p, "sfd:", strlen("sfd:"))) {
00474         char  *r;
00475         /* SFD mapping: sfd:Big5,00 */
00476         p += 4; skip_blank(&p, endptr);
00477         q  = parse_string_value(&p, endptr);
00478         if (!q) {
00479           WARN("Missing value for option 'm'.");
00480           return  -1;
00481         }
00482         r  = strchr(q, ',');
00483         if (!r) {
00484           WARN("Invalid value for option 'm': %s", q);
00485           RELEASE(q);
00486           return  -1;
00487         }
00488         *r = 0; r++; skip_blank(&r, r + strlen(r));
00489         if (*r == '\0') {
00490           WARN("Invalid value for option 'm': %s,", q);
00491           RELEASE(q);
00492           return  -1;
00493         }
00494         mrec->charmap.sfd_name   = mstrdup(q);
00495         mrec->charmap.subfont_id = mstrdup(r);
00496         RELEASE(q);
00497       } else if (p + 4 < endptr &&
00498                  !memcmp(p, "pad:", strlen("pad:"))) {
00499         p += 4; skip_blank(&p, endptr);
00500         q  = parse_integer_value(&p, endptr, 16);
00501         if (!q) {
00502           WARN("Invalid value for option 'm'.");
00503           return  -1;
00504         } else if (p < endptr && !isspace(*p)) {
00505           WARN("Invalid value for option 'm': %s", q);
00506           RELEASE(q);
00507           return  -1;
00508         }
00509         v = strtol(q, NULL, 16);
00510         mrec->opt.mapc = ((v << 8) & 0x0000ff00L);
00511         RELEASE(q);
00512       } else {
00513         WARN("Invalid value for option 'm'.");
00514         return  -1;
00515       }
00516       break;
00517 
00518     case 'w': /* Writing mode (for unicode encoding) */
00519       if (!mrec->enc_name ||
00520            strcmp(mrec->enc_name, "unicode")) {
00521         WARN("Fontmap option 'w' meaningless for encoding other than \"unicode\".");
00522         return  -1;
00523       }
00524       q  = parse_integer_value(&p, endptr, 10);
00525       if (!q) {
00526         WARN("Missing wmode value...");
00527         return  -1;
00528       }
00529       if (atoi(q) == 1)
00530         mrec->opt.flags |= FONTMAP_OPT_VERT;
00531       else if (atoi(q) == 0)
00532         mrec->opt.flags &= ~FONTMAP_OPT_VERT;
00533       else {
00534         WARN("Invalid value for option 'w': %s", q);
00535       }
00536       RELEASE(q);
00537       break;
00538 
00539     default:
00540       WARN("Unrecognized font map option: '%c'", mopt);
00541       return  -1;
00542       break;
00543     }
00544     skip_blank(&p, endptr);
00545   }
00546 
00547   if (p < endptr && *p != '\r' && *p != '\n') {
00548     WARN("Invalid char in fontmap line: %c", *p);
00549     return  -1;
00550   }
00551 
00552   return  0;
00553 }
00554 
00555 
00556 /* Parse record line in map file of DVIPS/pdfTeX format. */
00557 static int
00558 fontmap_parse_mapdef_dps (fontmap_rec *mrec,
00559                           const char *mapdef, char *endptr)
00560 {
00561   char *p = (char *)mapdef, *q;
00562 
00563   skip_blank(&p, endptr);
00564 
00565   /* The first field (after TFM name) must be PostScript name. */
00566   if (p < endptr) {
00567     q = parse_string_value(&p, endptr);
00568     if (q) RELEASE(q);
00569     skip_blank(&p, endptr);
00570   } else {
00571     WARN("Missing a PostScript font name.");
00572     return -1;
00573   }
00574 
00575   if (p >= endptr) return 0;
00576 
00577   /* Parse any remaining arguments */
00578   while (p < endptr && *p != '\r' && *p != '\n' && (*p == '<' || *p == '"')) {
00579     switch (*p) {
00580     case '<': /* encoding or fontfile field */
00581       if (++p < endptr && *p == '[') p++; /*skip */
00582       skip_blank(&p, endptr);
00583       if ((q = parse_string_value(&p, endptr))) {
00584         int n = strlen(q);
00585         if (n > 4 && strncmp(q+n-4, ".enc", 4) == 0)
00586           mrec->enc_name = q;
00587         else
00588           mrec->font_name = q;
00589       }
00590       skip_blank(&p, endptr);
00591       break;
00592 
00593     case '"': /* Options */
00594       if ((q = parse_string_value(&p, endptr))) {
00595         char *r = q, *e = q+strlen(q), *s, *t;
00596         skip_blank(&r, e);
00597         while (r < e) {
00598           if ((s = parse_float_decimal(&r, e))) {
00599             skip_blank(&r, e);
00600             if ((t = parse_string_value(&r, e))) {
00601               if (strcmp(t, "SlantFont") == 0)
00602                 mrec->opt.slant = atof(s);
00603               else if (strcmp(r, "ExtendFont") == 0)
00604                 mrec->opt.extend = atof(s);
00605               RELEASE(t);
00606             }
00607             RELEASE(s);
00608           } else if ((s = parse_string_value(&r, e))) { /* skip */
00609             RELEASE(s);
00610           }
00611           skip_blank(&r, e);
00612         }
00613         RELEASE(q);
00614       }
00615       skip_blank(&p, endptr);
00616       break;
00617     
00618     default:
00619       WARN("Found an invalid entry: %s", p);
00620       return -1;
00621       break;
00622     }
00623     skip_blank(&p, endptr);
00624   }
00625 
00626   if (p < endptr && *p != '\r' && *p != '\n') {
00627     WARN("Invalid char in fontmap line: %c", *p);
00628     return -1;
00629   }
00630 
00631   return  0;
00632 }
00633 
00634 
00635 static struct ht_table *fontmap = NULL;
00636 
00637 #define fontmap_invalid(m) (!(m) || !(m)->map_name || !(m)->font_name)
00638 static char *
00639 chop_sfd_name (const char *tex_name, char **sfd_name)
00640 {
00641   char  *fontname;
00642   char  *p, *q;
00643   int    m, n, len;
00644 
00645   *sfd_name = NULL;
00646 
00647   p = strchr((char *) tex_name, '@');
00648   if (!p ||
00649       p[1] == '\0' || p == tex_name) {
00650     return  NULL;
00651   }
00652   m = (int) (p - tex_name);
00653   p++;
00654   q = strchr(p, '@');
00655   if (!q || q == p) {
00656     return NULL;
00657   }
00658   n = (int) (q - p);
00659   q++;
00660 
00661   len = strlen(tex_name) - n;
00662   fontname = NEW(len+1, char);
00663   memcpy(fontname, tex_name, m);
00664   fontname[m] = '\0';
00665   if (*q)
00666     strcat(fontname, q);
00667 
00668   *sfd_name = NEW(n+1, char);
00669   memcpy(*sfd_name, p, n);
00670   (*sfd_name)[n] = '\0';
00671 
00672   return  fontname;
00673 }
00674 
00675 static char *
00676 make_subfont_name (const char *map_name, const char *sfd_name, const char *sub_id)
00677 {
00678   char  *tfm_name;
00679   int    n, m;
00680   char  *p, *q;
00681 
00682   p = strchr(map_name, '@');
00683   if (!p || p == map_name)
00684     return  NULL;
00685   m = (int) (p - map_name);
00686   q = strchr(p + 1, '@');
00687   if (!q || q == p + 1)
00688     return  NULL;
00689   n = (int) (q - p) + 1; /* including two '@' */
00690   if (strlen(sfd_name) != n - 2 ||
00691       memcmp(p + 1, sfd_name, n - 2))
00692     return  NULL;
00693   tfm_name = NEW(strlen(map_name) - n + strlen(sub_id) + 1, char);
00694   memcpy(tfm_name, map_name, m);
00695   tfm_name[m] = '\0';
00696   strcat(tfm_name, sub_id);
00697   if (q[1]) /* not ending with '@' */
00698     strcat(tfm_name, q + 1);
00699 
00700   return  tfm_name;
00701 }
00702 
00703 /* "foo@A@ ..." is expanded to
00704  *   fooab ... -m sfd:A,ab
00705  *   ...
00706  *   fooyz ... -m sfd:A,yz
00707  * where 'ab' ... 'yz' is subfont IDs in SFD 'A'.
00708  */
00709 int
00710 pdf_append_fontmap_record (const char *kp, const fontmap_rec *vp)
00711 {
00712   fontmap_rec *mrec;
00713   char        *fnt_name, *sfd_name = NULL;
00714 
00715   if (!kp || fontmap_invalid(vp)) {
00716     WARN("Invalid fontmap record...");
00717     return -1;
00718   }
00719 
00720   if (verbose > 3)
00721     MESG("fontmap>> append key=\"%s\"...", kp);
00722 
00723   fnt_name = chop_sfd_name(kp, &sfd_name);
00724   if (fnt_name && sfd_name) {
00725     char  *tfm_name;
00726     char **subfont_ids;
00727     int    n = 0;
00728     subfont_ids = sfd_get_subfont_ids(sfd_name, &n);
00729     if (!subfont_ids)
00730       return  -1;
00731     while (n-- > 0) {
00732       tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]);
00733       if (!tfm_name)
00734         continue;
00735       mrec = ht_lookup_table(fontmap, tfm_name, strlen(tfm_name));
00736       if (!mrec) {
00737         mrec = NEW(1, fontmap_rec);
00738         pdf_init_fontmap_record(mrec);
00739         mrec->map_name = mstrdup(kp); /* link */
00740         mrec->charmap.sfd_name   = mstrdup(sfd_name);
00741         mrec->charmap.subfont_id = mstrdup(subfont_ids[n]);
00742         ht_insert_table(fontmap, tfm_name, strlen(tfm_name), mrec);
00743       }
00744       RELEASE(tfm_name);
00745     }
00746     RELEASE(fnt_name);
00747     RELEASE(sfd_name);
00748   }
00749 
00750   mrec = ht_lookup_table(fontmap, kp, strlen(kp));
00751   if (!mrec) {
00752     mrec = NEW(1, fontmap_rec);
00753     pdf_copy_fontmap_record(mrec, vp);
00754     if (mrec->map_name && !strcmp(kp, mrec->map_name)) {
00755       RELEASE(mrec->map_name);
00756       mrec->map_name = NULL;
00757     }
00758     ht_insert_table(fontmap, kp, strlen(kp), mrec);
00759   }
00760   if (verbose > 3)
00761     MESG("\n");
00762 
00763   return  0;
00764 }
00765 
00766 int
00767 pdf_remove_fontmap_record (const char *kp)
00768 {
00769   char  *fnt_name, *sfd_name = NULL;
00770 
00771   if (!kp)
00772     return  -1;
00773 
00774   if (verbose > 3)
00775     MESG("fontmap>> remove key=\"%s\"...", kp);
00776 
00777   fnt_name = chop_sfd_name(kp, &sfd_name);
00778   if (fnt_name && sfd_name) {
00779     char  *tfm_name;
00780     char **subfont_ids;
00781     int    n = 0;
00782     subfont_ids = sfd_get_subfont_ids(sfd_name, &n);
00783     if (!subfont_ids)
00784       return  -1;
00785     if (verbose > 3)
00786       MESG("\nfontmap>> Expand @%s@:", sfd_name);
00787     while (n-- > 0) {
00788       tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]);
00789       if (!tfm_name)
00790         continue;
00791       if (verbose > 3)
00792         MESG(" %s", tfm_name);
00793       ht_remove_table(fontmap, tfm_name, strlen(tfm_name));
00794       RELEASE(tfm_name);
00795     }
00796     RELEASE(fnt_name);
00797     RELEASE(sfd_name);
00798   }
00799 
00800   ht_remove_table(fontmap, kp, strlen(kp));
00801 
00802   if (verbose > 3)
00803     MESG("\n");
00804 
00805   return  0;
00806 }
00807 
00808 int
00809 pdf_insert_fontmap_record (const char *kp, const fontmap_rec *vp)
00810 {
00811   fontmap_rec *mrec;
00812   char        *fnt_name, *sfd_name;
00813 
00814   if (!kp || fontmap_invalid(vp)) {
00815     WARN("Invalid fontmap record...");
00816     return -1;
00817   }
00818 
00819   if (verbose > 3)
00820     MESG("fontmap>> insert key=\"%s\"...", kp);
00821 
00822   fnt_name = chop_sfd_name(kp, &sfd_name);
00823   if (fnt_name && sfd_name) {
00824     char  *tfm_name;
00825     char **subfont_ids;
00826     int    n = 0;
00827     subfont_ids = sfd_get_subfont_ids(sfd_name, &n);
00828     if (!subfont_ids) {
00829       RELEASE(fnt_name);
00830       RELEASE(sfd_name);
00831       return  -1;
00832     }
00833     if (verbose > 3)
00834       MESG("\nfontmap>> Expand @%s@:", sfd_name);
00835     while (n-- > 0) {
00836       tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]);
00837       if (!tfm_name)
00838         continue;
00839       if (verbose > 3)
00840         MESG(" %s", tfm_name);
00841       mrec = NEW(1, fontmap_rec);
00842       pdf_init_fontmap_record(mrec);
00843       mrec->map_name = mstrdup(kp); /* link to this entry */
00844       mrec->charmap.sfd_name   = mstrdup(sfd_name);
00845       mrec->charmap.subfont_id = mstrdup(subfont_ids[n]);
00846       ht_insert_table(fontmap, tfm_name, strlen(tfm_name), mrec);
00847       RELEASE(tfm_name);
00848     }
00849     RELEASE(fnt_name);
00850     RELEASE(sfd_name);
00851   }
00852 
00853   mrec = NEW(1, fontmap_rec);
00854   pdf_copy_fontmap_record(mrec, vp);
00855   if (mrec->map_name && !strcmp(kp, mrec->map_name)) {
00856     RELEASE(mrec->map_name);
00857     mrec->map_name = NULL;
00858   }
00859   ht_insert_table(fontmap, kp, strlen(kp), mrec);
00860 
00861   if (verbose > 3)
00862     MESG("\n");
00863 
00864   return  0;
00865 }
00866 
00867 
00868 int
00869 pdf_read_fontmap_line (fontmap_rec *mrec, const char *mline, long mline_len, int format)
00870 {
00871   int    error;
00872   char  *q, *p, *endptr;
00873 
00874   ASSERT(mrec);
00875 
00876   p      = (char *) mline;
00877   endptr = p + mline_len;
00878 
00879   skip_blank(&p, endptr);
00880   if (p >= endptr)
00881     return -1;
00882 
00883   q = parse_string_value(&p, endptr);
00884   if (!q)
00885     return -1;
00886 
00887   if (format > 0) /* DVIPDFM format */
00888     error = fontmap_parse_mapdef_dpm(mrec, p, endptr);
00889   else /* DVIPS/pdfTeX format */
00890     error = fontmap_parse_mapdef_dps(mrec, p, endptr);
00891   if (!error) {
00892     char  *fnt_name, *sfd_name = NULL;
00893     fnt_name = chop_sfd_name(q, &sfd_name);
00894     if (fnt_name && sfd_name) {
00895       if (!mrec->font_name) {
00896       /* In the case of subfonts, the base name (before the character '@')
00897        * will be used as a font_name by default.
00898        * Otherwise tex_name will be used as a font_name by default.
00899        */
00900         mrec->font_name = fnt_name;
00901       } else {
00902         RELEASE(fnt_name);
00903       }
00904       if (mrec->charmap.sfd_name)
00905         RELEASE(mrec->charmap.sfd_name);
00906       mrec->charmap.sfd_name = sfd_name ;
00907     }
00908     fill_in_defaults(mrec, q);
00909   }
00910   RELEASE(q);
00911 
00912   return  error;
00913 }
00914 
00915 /* DVIPS/pdfTeX fontmap line if one of the following three cases found:
00916  *
00917  * (1) any line including the character '"'
00918  * (2) any line including the character '<'
00919  * (3) if the line consists of two entries (tfmname and psname)
00920  *
00921  * DVIPDFM fontmap line otherwise.
00922  */
00923 int
00924 is_pdfm_mapline (const char *mline) /* NULL terminated. */
00925 {
00926   int   n = 0;
00927   char *p, *endptr;
00928 
00929   if (strchr(mline, '"') || strchr(mline, '<'))
00930     return -1; /* DVIPS/pdfTeX format */
00931 
00932   p      = (char *) mline;
00933   endptr = p + strlen(mline);
00934 
00935   skip_blank(&p, endptr);
00936 
00937   while (p < endptr) {
00938     /* Break if '-' preceeded by blanks is found. (DVIPDFM format) */
00939     if (*p == '-') return 1;
00940     for (n++; p < endptr && !ISBLANK(*p); p++);
00941     skip_blank(&p, endptr);
00942   }
00943 
00944   /* Two entries: TFM_NAME PS_NAME only (DVIPS format)
00945    * Otherwise (DVIPDFM format) */
00946   return (n == 2 ? 0 : 1);
00947 }
00948 
00949 int
00950 pdf_load_fontmap_file (const char *filename, int mode)
00951 {
00952   fontmap_rec *mrec;
00953   FILE        *fp;
00954   char        *p = NULL, *endptr;
00955   long         llen, lpos  = 0;
00956   int          error = 0, format = 0;
00957 
00958   ASSERT(filename);
00959   ASSERT(fontmap) ;
00960 
00961   if (verbose)
00962     MESG("<FONTMAP:%s", filename);
00963 
00964   fp = DPXFOPEN(filename, DPX_RES_TYPE_FONTMAP);
00965   if (!fp) {
00966     WARN("Couldn't open font map file \"%s\".", filename);
00967     return  -1;
00968   }
00969 
00970   while (!error &&
00971          (p = readline(work_buffer, WORK_BUFFER_SIZE, fp)) != NULL) {
00972     int m;
00973 
00974     lpos++;
00975     llen   = strlen(work_buffer);
00976     endptr = p + llen;
00977 
00978     skip_blank(&p, endptr);
00979     if (p == endptr)
00980       continue;
00981 
00982     m = is_pdfm_mapline(p);
00983 
00984     if (format * m < 0) { /* mismatch */
00985       WARN("Found a mismatched fontmap line %d from %s.", lpos, filename);
00986       WARN("-- Ignore the current input buffer: %s", p);
00987       continue;
00988     } else
00989       format += m;
00990 
00991     mrec  = NEW(1, fontmap_rec);
00992     pdf_init_fontmap_record(mrec);
00993 
00994     /* format > 0: DVIPDFM, format <= 0: DVIPS/pdfTeX */
00995     error = pdf_read_fontmap_line(mrec, p, llen, format);
00996     if (error) {
00997       WARN("Invalid map record in fontmap line %d from %s.", lpos, filename);
00998       WARN("-- Ignore the current input buffer: %s", p);
00999       pdf_clear_fontmap_record(mrec);
01000       RELEASE(mrec);
01001       continue;
01002     } else {
01003       switch (mode) {
01004       case FONTMAP_RMODE_REPLACE:
01005         pdf_insert_fontmap_record(mrec->map_name, mrec);
01006         break;
01007       case FONTMAP_RMODE_APPEND:
01008         pdf_append_fontmap_record(mrec->map_name, mrec);
01009         break;
01010       case FONTMAP_RMODE_REMOVE:
01011         pdf_remove_fontmap_record(mrec->map_name);
01012         break;
01013       }
01014     }
01015     pdf_clear_fontmap_record(mrec);
01016     RELEASE(mrec);
01017   }
01018   DPXFCLOSE(fp);
01019 
01020   if (verbose)
01021     MESG(">");
01022 
01023   return  error;
01024 }
01025 
01026 #if  0
01027 /* tfm_name="dmjhira10", map_name="dmj@DNP@10", sfd_name="DNP"
01028  *  --> sub_id="hira"
01029  * Test if tfm_name can be really considered as subfont.
01030  */
01031 static int
01032 test_subfont (const char *tfm_name, const char *map_name, const char *sfd_name)
01033 {
01034   int    r = 0;
01035   char **ids;
01036   int    n, m;
01037   char  *p = (char *) map_name;
01038   char  *q = (char *) tfm_name;
01039 
01040   ASSERT( tfm_name && map_name && sfd_name );
01041 
01042   /* until first occurence of '@' */
01043   for ( ; *p && *q && *p == *q && *p != '@'; p++, q++);
01044   if (*p != '@')
01045     return  0;
01046   p++;
01047   /* compare sfd_name (should be always true here) */
01048   if (strlen(p) <= strlen(sfd_name) ||
01049       memcmp(p, sfd_name, strlen(sfd_name)) ||
01050       p[strlen(sfd_name)] != '@')
01051     return  0;
01052   /* check tfm_name follows second '@' */
01053   p += strlen(sfd_name) + 1;
01054   if (*p) {
01055     char  *r = (char *) tfm_name;
01056     r += strlen(tfm_name) - strlen(p);
01057     if (strcmp(r, p))
01058       return  0;
01059   }
01060   /* Now 'p' is located at next to SFD name terminator
01061    * (second '@') in map_name and 'q' is at first char
01062    * of subfont_id substring in tfm_name.
01063    */
01064   n  = strlen(q) - strlen(p); /* length of subfont_id string */
01065   if (n <= 0)
01066     return  0;
01067   /* check if n-length substring 'q' is valid as subfont ID */
01068   ids = sfd_get_subfont_ids(sfd_name, &m);
01069   if (!ids)
01070     return  0;
01071   while (!r && m-- > 0) {
01072     if (strlen(ids[m]) == n &&
01073         !memcmp(q, ids[m], n)) {
01074       r = 1;
01075     }
01076   }
01077 
01078   return  r;
01079 }
01080 #endif  /* 0 */
01081 
01082 
01083 fontmap_rec *
01084 pdf_lookup_fontmap_record (const char *tfm_name)
01085 {
01086   fontmap_rec *mrec = NULL;
01087 
01088   if (fontmap && tfm_name)
01089     mrec = ht_lookup_table(fontmap, tfm_name, strlen(tfm_name));
01090 
01091   return  mrec;
01092 }
01093 
01094 
01095 void
01096 pdf_init_fontmaps (void)
01097 {
01098   fontmap = NEW(1, struct ht_table);
01099   ht_init_table(fontmap, hval_free);
01100 }
01101 
01102 void
01103 pdf_close_fontmaps (void)
01104 {
01105   if (fontmap) {
01106     ht_clear_table(fontmap);
01107     RELEASE(fontmap);
01108   }
01109   fontmap = NULL;
01110 
01111   release_sfd_record();
01112 }
01113 
01114 #if 0
01115 void
01116 pdf_clear_fontmaps (void)
01117 {
01118   pdf_close_fontmaps();
01119   pdf_init_fontmaps();
01120 }
01121 #endif
01122 
01123 /* CIDFont options
01124  *
01125  * FORMAT:
01126  *
01127  *   (:int:)?!?string(/string)?(,string)?
01128  */
01129 
01130 static char *
01131 substr (char **str, char stop)
01132 {
01133   char *sstr, *endptr;
01134 
01135   endptr = strchr(*str, stop);
01136   if (!endptr || endptr == *str)
01137     return NULL;
01138   sstr = NEW(endptr-(*str)+1, char);
01139   memcpy(sstr, *str, endptr-(*str));
01140   sstr[endptr-(*str)] = '\0';
01141 
01142   *str = endptr+1;
01143   return sstr;
01144 }
01145 
01146 #include <ctype.h>
01147 #define CID_MAPREC_CSI_DELIM '/'
01148 
01149 static char *
01150 strip_options (const char *map_name, fontmap_opt *opt)
01151 {
01152   char *font_name;
01153   char *p, *next = NULL;
01154   int   have_csi = 0, have_style = 0;
01155 
01156   ASSERT(opt);
01157 
01158   p = (char *) map_name;
01159   font_name      = NULL;
01160   opt->charcoll  = NULL;
01161   opt->index     = 0;
01162   opt->style     = FONTMAP_STYLE_NONE;
01163   opt->flags     = 0;
01164 
01165   if (*p == ':' && isdigit(*(p+1))) {
01166     opt->index = (int) strtoul(p+1, &next, 10);
01167     if (*next == ':')
01168       p = next + 1;
01169     else {
01170       opt->index = 0;
01171     }
01172   }
01173   if (*p == '!') { /* no-embedding */
01174     if (*(++p) == '\0')
01175       ERROR("Invalid map record: %s (--> %s)", map_name, p);
01176     opt->flags |= FONTMAP_OPT_NOEMBED;
01177   }
01178 
01179   if ((next = strchr(p, CID_MAPREC_CSI_DELIM)) != NULL) {
01180     if (next == p)
01181       ERROR("Invalid map record: %s (--> %s)", map_name, p);
01182     font_name = substr(&p, CID_MAPREC_CSI_DELIM);
01183     have_csi  = 1;
01184   } else if ((next = strchr(p, ',')) != NULL) {
01185     if (next == p)
01186       ERROR("Invalid map record: %s (--> %s)", map_name, p);
01187     font_name = substr(&p, ',');
01188     have_style = 1;
01189   } else {
01190     font_name = NEW(strlen(p)+1, char);
01191     strcpy(font_name, p);
01192   }
01193 
01194   if (have_csi) {
01195     if ((next = strchr(p, ',')) != NULL) {
01196       opt->charcoll = substr(&p, ',');
01197       have_style = 1;
01198     } else if (p[0] == '\0') {
01199       ERROR("Invalid map record: %s.", map_name);
01200     } else {
01201       opt->charcoll = NEW(strlen(p)+1, char);
01202       strcpy(opt->charcoll, p);
01203     }
01204   }
01205 
01206   if (have_style) {
01207     if (!strncmp(p, "BoldItalic", 10)) {
01208       if (*(p+10))
01209         ERROR("Invalid map record: %s (--> %s)", map_name, p);
01210       opt->style = FONTMAP_STYLE_BOLDITALIC;
01211     } else if (!strncmp(p, "Bold", 4)) {
01212       if (*(p+4))
01213         ERROR("Invalid map record: %s (--> %s)", map_name, p);
01214       opt->style = FONTMAP_STYLE_BOLD;
01215     } else if (!strncmp(p, "Italic", 6)) {
01216       if (*(p+6))
01217         ERROR("Invalid map record: %s (--> %s)", map_name, p);
01218       opt->style = FONTMAP_STYLE_ITALIC;
01219     }
01220   }
01221 
01222   return font_name;
01223 }
01224 
01225 #if  DPXTEST
01226 static void
01227 dump_fontmap_rec (const char *key, const fontmap_rec *mrec)
01228 {
01229   fontmap_opt *opt = (fontmap_opt *) &mrec->opt;
01230 
01231   if (mrec->map_name)
01232     fprintf(stdout, "  <!-- subfont");
01233   else
01234     fprintf(stdout, "  <insert");
01235   fprintf(stdout, " id=\"%s\"", key);
01236   if (mrec->map_name)
01237     fprintf(stdout, " map-name=\"%s\"", mrec->map_name);
01238   if (mrec->enc_name)
01239     fprintf(stdout, " enc-name=\"%s\"",  mrec->enc_name);
01240   if (mrec->font_name)
01241     fprintf(stdout, " font-name=\"%s\"", mrec->font_name);
01242   if (mrec->charmap.sfd_name && mrec->charmap.subfont_id) {
01243     fprintf(stdout, " charmap=\"sfd:%s,%s\"",
01244             mrec->charmap.sfd_name, mrec->charmap.subfont_id);
01245   }
01246   if (opt->slant != 0.0)
01247     fprintf(stdout, " font-slant=\"%g\"", opt->slant);
01248   if (opt->extend != 1.0)
01249     fprintf(stdout, " font-extend=\"%g\"", opt->extend);
01250   if (opt->charcoll)
01251     fprintf(stdout, " glyph-order=\"%s\"", opt->charcoll);
01252   if (opt->tounicode)
01253     fprintf(stdout, " tounicode=\"%s\"", opt->tounicode);
01254   if (opt->index != 0)
01255     fprintf(stdout, " ttc-index=\"%d\"", opt->index);
01256   if (opt->flags & FONTMAP_OPT_NOEMBED)
01257     fprintf(stdout, " embedding=\"no\"");
01258   if (opt->mapc >= 0) {
01259     fprintf(stdout, " charmap=\"pad:");
01260     if (opt->mapc > 0xffff)
01261       fprintf(stdout, "%02x %02x", (opt->mapc >> 16) & 0xff, (opt->mapc >> 8) & 0xff);
01262     else
01263       fprintf(stdout, "%02x", (opt->mapc >> 8) & 0xff);
01264     fprintf(stdout, "\"");
01265   }
01266   if (opt->flags & FONTMAP_OPT_VERT)
01267     fprintf(stdout, " writing-mode=\"vertical\"");
01268   if (opt->style != FONTMAP_STYLE_NONE) {
01269     fprintf(stdout, " font-style=\"");
01270     switch (opt->style) {
01271     case FONTMAP_STYLE_BOLD:
01272       fprintf(stdout, "bold");
01273       break;
01274     case FONTMAP_STYLE_ITALIC:
01275       fprintf(stdout, "italic");
01276       break;
01277     case FONTMAP_STYLE_BOLDITALIC:
01278       fprintf(stdout, "bolditalic");
01279       break;
01280     }
01281     fprintf(stdout, "\"");
01282   }
01283   if (mrec->map_name)
01284     fprintf(stdout, " / -->\n");
01285   else
01286     fprintf(stdout, " />\n");
01287 }
01288 
01289 void
01290 dump_fontmaps (void)
01291 {
01292   struct ht_iter iter;
01293   fontmap_rec   *mrec;
01294   char           key[128], *kp;
01295   int            kl;
01296 
01297   if (!fontmap)
01298     return;
01299 
01300   fprintf(stdout, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
01301   fprintf(stdout, "<!DOCTYPE fontmap SYSTEM \"fontmap.dtd\">\n");
01302   fprintf(stdout, "<fontmap id=\"%s\">\n", "foo");
01303   if (ht_set_iter(fontmap, &iter) == 0) {
01304     do {
01305       kp   = ht_iter_getkey(&iter, &kl);
01306       mrec = ht_iter_getval(&iter);
01307       if (kl > 127)
01308         continue;
01309       memcpy(key, kp, kl); key[kl] = 0;
01310       dump_fontmap_rec(key, mrec);
01311     } while (!ht_iter_next(&iter));
01312   }
01313   ht_clear_iter(&iter);
01314   fprintf(stdout, "</fontmap>\n");
01315 
01316   return;
01317 }
01318 
01319 void
01320 test_fontmap_help (void)
01321 {
01322   fprintf(stdout, "usage: fontmap [options] [mapfile...]\n");
01323   fprintf(stdout, "-l, --lookup string\n");
01324   fprintf(stdout, "  Lookup fontmap entry for 'string' after loading mapfile(s).\n");
01325 }
01326 
01327 int
01328 test_fontmap_main (int argc, char *argv[])
01329 {
01330   int    i;
01331   char  *key = NULL;
01332 
01333   for (;;) {
01334     int  c, optidx = 0;
01335     static struct option long_options[] = {
01336       {"lookup", 1, 0, 'l'},
01337       {"help",   0, 0, 'h'},
01338       {0, 0, 0, 0}
01339     };
01340     c = getopt_long(argc, argv, "l:h", long_options, &optidx);
01341     if (c == -1)
01342       break;
01343 
01344     switch (c) {
01345     case  'l':
01346       key = optarg;
01347       break;
01348     case  'h':
01349       test_fontmap_help();
01350       return  0;
01351       break;
01352     default:
01353       test_fontmap_help();
01354       return  -1;
01355       break;
01356     }
01357   }
01358 
01359   pdf_init_fontmaps();
01360   for (i = optind; i < argc; i++)
01361     pdf_load_fontmap_file(argv[i], FONTMAP_RMODE_REPLACE);
01362 
01363   if (key == NULL)
01364     dump_fontmaps();
01365   else {
01366     fontmap_rec *mrec;
01367     mrec = pdf_lookup_fontmap_record(key);
01368     if (mrec)
01369       dump_fontmap_rec(key, mrec);
01370     else {
01371       WARN("Fontmap entry \"%s\" not found.", key);
01372     }
01373   }
01374   pdf_close_fontmaps();
01375 
01376   return  0;
01377 }
01378 #endif /* DPXTEST */