/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002-2020 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "system.h" #include "mem.h" #include "error.h" #include "dpxconf.h" #include "dpxfile.h" #include "subfont.h" /* Don't forget fontmap reading now requires information * from SFD files. You must initialize at least sfd_file_ * cache before starting loading of fontmaps. */ /* Subfont Definition File: * struct sfd_file_ is for storing subfont identifiers * contained in a SFD file and for mapping string pair * to internal code mapping table * ID which is index within an array of struct sfd_rec_. * We store code mapping tables in different place than * struct sfd_file_. */ struct sfd_file_ { char *ident; /* SFD file name */ char **sub_id; /* Subfont IDs */ int *rec_id; /* indices within struct sfd_rec_ array "sfd_record" */ int max_subfonts; int num_subfonts; }; /* Mapping table */ struct sfd_rec_ { /* unsigned char misbit[32]; */ unsigned short vector[256]; /* 0 for undefined */ }; static void init_sfd_file_ (struct sfd_file_ *sfd) { sfd->ident = NULL; sfd->sub_id = NULL; sfd->rec_id = NULL; sfd->max_subfonts = sfd->num_subfonts = 0; } static void clean_sfd_file_ (struct sfd_file_ *sfd) { int i; if (sfd->ident) RELEASE(sfd->ident); if (sfd->sub_id) { for (i = 0; i < sfd->num_subfonts; i++) { if (sfd->sub_id[i]) RELEASE(sfd->sub_id[i]); } RELEASE(sfd->sub_id); } if (sfd->rec_id) RELEASE(sfd->rec_id); init_sfd_file_(sfd); } static struct sfd_file_ *sfd_files = NULL; static int num_sfd_files = 0, max_sfd_files = 0; static struct sfd_rec_ *sfd_record = NULL; static int num_sfd_records = 0, max_sfd_records = 0; /* Another buffer... * We want buffer size at least 7 x 256 + a * 4096 is usually enough. */ #define LINE_BUF_SIZE 4096 static char line_buf[LINE_BUF_SIZE]; /* Each lines describes character code mapping for each * subfonts. '#' is start of comment. * SFD file format uses a '\' before newline sequence * for line-continuation. */ static char * readline (char *buf, int buf_len, FILE *fp) { char *r, *q, *p = buf; int n = 0, c = 0; while (buf_len - n > 0 && (q = mfgets(p, buf_len - n, fp))) { c++; r = strchr(q, '#'); /* Comment is converted to single wsp (followed by a newline). */ if (r) { *r = ' '; *(r + 1) = '\0'; } if (strlen(q) == 0) break; /* empty line */ n += strlen(q); q += strlen(q) - 1; if (*q != '\\') break; else { /* line continued */ n -= 1; p = buf + n; } } if (n >= buf_len - 1) { WARN("Possible buffer overflow in reading SFD file (buffer full, size=%d bytes)", buf_len - 1); } return (c > 0 ? buf : NULL); } #define clear_vector(v) if ((v)) { \ int __i; \ for (__i = 0; __i < 256; __i++) \ (v)[__i] = 0; \ } /* subfont_id ( integer ':' | integer '_' integer | integer )* * * 0x32: ==> Subsequent integers are place into slots starting at 0x32. * 0x32: 0xA1A1 0xA1A2 ... ==> 0x32 is mappned to 0xA1A1, 0x33 to 0xA1A2 * 0xA1A1_0xA1A5 ==> Expanded to 0xA1A1 0xA1A2 ... 0xA1A5 */ /* subfont_id is already consumed here. */ static int read_sfd_record (struct sfd_rec_ *rec, const char *lbuf) { const char *p = lbuf, *q; char *r; int repos = 0; int c, v1 = 0, v2 = 0; int curpos = 0; int error = 0; #define IS_TOKSEP(c) ((c) == '\0' || isspace((unsigned char)(c))) for ( ; *p && isspace((unsigned char)*p); p++); while (!error && *p) { repos = 0; q = p; v1 = strtol(p, &r, 0); q = r; if (q == p || (!IS_TOKSEP(*q) && *q != ':' && *q != '_')) { WARN("Unknown token in subfont mapping table: %c", *q); return -1; } switch (*q) { case ':': if (v1 < 0 || v1 > 0xff) { WARN("Invalud value for subfont table offset: %ld", v1); return -1; } repos = 1; q++; break; case '_': p = q + 1; v2 = strtol(p, &r, 0); q = r; if (v1 < 0 || v1 > 0xffffL || v2 < 0 || v2 > 0xffffL) { WARN("Invalid value in subfont mapping table: 0x%x_0x%x", v1, v2); return -1; } else if (q == p || !IS_TOKSEP(*q)) { WARN("Invalid char in subfont mapping table: %c", *q); return -1; } break; default: if (v1 < 0 || v1 > 0xffffL) { WARN("Invalid character code in subfont mapping table: 0x%x", v1); return -1; } v2 = v1; break; } if (repos) curpos = v1; else { if (v2 < v1 || curpos + (v2 - v1) > 0xff) { WARN("Invalid range in subfont mapping: curpos=\"0x%02x\" range=\"0x%04x,0x%04x\"", curpos, v1, v2); return -1; } for (c = v1; c <= v2; c++) { if (rec->vector[curpos] != 0) { WARN("Subfont mapping for slot=\"0x%02x\" already defined...", curpos); return -1; } ASSERT( curpos >= 0 && curpos <= 255 ); rec->vector[curpos++] = (unsigned short) c; } } for (p = q; *p && isspace((unsigned char)*p); p++); } return error; } /* Scan for subfont IDs */ static int scan_sfd_file (struct sfd_file_ *sfd, FILE *fp) { char *id; char *q, *p; int n, lpos = 0; ASSERT( sfd && fp ); if (dpx_conf.verbose_level > 3) { MESG("\nsubfont>> Scanning SFD file \"%s\"...\n", sfd->ident); } rewind(fp); sfd->max_subfonts = sfd->num_subfonts = 0; while ((p = readline(line_buf, LINE_BUF_SIZE, fp)) != NULL) { lpos++; for ( ; *p && isspace((unsigned char)*p); p++); if (*p == 0) continue; /* empty */ /* Saw non-wsp here */ for (n = 0, q = p; *p && !isspace((unsigned char)*p); p++, n++); id = NEW(n + 1, char); memcpy(id, q, n); id[n] = '\0'; if (sfd->num_subfonts >= sfd->max_subfonts) { sfd->max_subfonts += 16; sfd->sub_id = RENEW(sfd->sub_id, sfd->max_subfonts, char *); } if (dpx_conf.verbose_level > 3) { MESG("subfont>> id=\"%s\" at line=\"%d\"\n", id, lpos); } sfd->sub_id[sfd->num_subfonts] = id; sfd->num_subfonts++; } sfd->rec_id = NEW(sfd->num_subfonts, int); for (n = 0; n < sfd->num_subfonts; n++) { sfd->rec_id[n] = -1; /* Not loaded yet. We do lazy loading of map definitions. */ } if (dpx_conf.verbose_level > 3) { MESG("subfont>> %d entries found in SFD file \"%s\".\n", sfd->num_subfonts, sfd->ident); } return 0; } /* Open SFD file and gather subfont IDs. We do not read mapping tables * here but only read subfont IDs used in SFD file. */ static int find_sfd_file (const char *sfd_name) { int id = -1; int i, error = -1; /* Check if we already opened SFD file */ for (i = 0; i < num_sfd_files; i++) { if (!strcmp(sfd_files[i].ident, sfd_name)) { id = i; break; } } if (id < 0) { struct sfd_file_ *sfd = NULL; FILE *fp; if (num_sfd_files >= max_sfd_files) { max_sfd_files += 8; sfd_files = RENEW(sfd_files, max_sfd_files, struct sfd_file_); } sfd = &sfd_files[num_sfd_files]; init_sfd_file_(sfd); sfd->ident = NEW(strlen(sfd_name) + 1, char); strcpy(sfd->ident, sfd_name); fp = DPXFOPEN(sfd->ident, DPX_RES_TYPE_SFD); if (!fp) { clean_sfd_file_(sfd); return -1; } error = scan_sfd_file(sfd, fp); DPXFCLOSE(fp); if (!error) id = num_sfd_files++; else { WARN("Error occured while reading SFD file \"%s\"", sfd_name); clean_sfd_file_(sfd); id = -1; } } return id; } char ** sfd_get_subfont_ids (const char *sfd_name, int *num_ids) { int sfd_id; if (!sfd_name) return NULL; sfd_id = find_sfd_file(sfd_name); if (sfd_id < 0) return NULL; if (num_ids) *num_ids = sfd_files[sfd_id].num_subfonts; return sfd_files[sfd_id].sub_id; } /* Make sure that sfd_name does not have the extension '.sfd'. * Mapping tables are actually read here. */ int sfd_load_record (const char *sfd_name, const char *subfont_id) { int rec_id = -1; struct sfd_file_ *sfd; FILE *fp; int sfd_id, i, error = 0; char *p, *q; if (!sfd_name || !subfont_id) return -1; sfd_id = find_sfd_file(sfd_name); if (sfd_id < 0) return -1; sfd = &sfd_files[sfd_id]; /* Check if we already loaded mapping table. */ for (i = 0; i < sfd->num_subfonts && strcmp(sfd->sub_id[i], subfont_id); i++); if (i == sfd->num_subfonts) { WARN("Subfont id=\"%s\" not exist in SFD file \"%s\"...", subfont_id, sfd->ident); return -1; } else if (sfd->rec_id[i] >= 0) { return sfd->rec_id[i]; } if (dpx_conf.verbose_level > 3) { MESG("\nsubfont>> Loading SFD mapping table for <%s,%s>...", sfd->ident, subfont_id); } /* reopen */ fp = DPXFOPEN(sfd->ident, DPX_RES_TYPE_SFD); if (!fp) { return -1; /* ERROR("Could not open SFD file \"%s\"", sfd_name); */ } /* Seek to record for 'sub_name'. */ while ((p = readline(line_buf, LINE_BUF_SIZE, fp))) { for ( ; *p && isspace((unsigned char)*p); p++); if (*p == 0) continue; /* empty line */ /* q = parse_ident(&p, p + strlen(p)); */ for (q = p; *p && !isspace((unsigned char)*p); p++); *p = '\0'; p++; if (!strcmp(q, subfont_id)) { if (num_sfd_records >= max_sfd_records) { max_sfd_records += 16; sfd_record = RENEW(sfd_record, max_sfd_records, struct sfd_rec_); } clear_vector(sfd_record[num_sfd_records].vector); error = read_sfd_record(&sfd_record[num_sfd_records], p); if (error) WARN("Error occured while reading SFD file: file=\"%s\" subfont_id=\"%s\"", sfd->ident, subfont_id); else { rec_id = num_sfd_records++; } } } if (rec_id < 0) { WARN("Failed to load subfont mapping table for SFD=\"%s\" subfont_id=\"%s\"", sfd->ident, subfont_id); } sfd->rec_id[i] = rec_id; DPXFCLOSE(fp); if (dpx_conf.verbose_level > 3) { int __i; if (rec_id >= 0) { MESG(" at id=\"%d\"", rec_id); MESG("\nsubfont>> Content of mapping table:"); for (__i = 0; __i < 256; __i++) { if (__i % 16 == 0) MESG("\nsubfont>> "); MESG(" %04x", sfd_record[rec_id].vector[__i]); } } MESG("\n"); } return rec_id; } /* Lookup mapping table */ unsigned short lookup_sfd_record (int rec_id, unsigned char c) { if (!sfd_record || rec_id < 0 || rec_id >= num_sfd_records) ERROR("Invalid subfont_id: %d", rec_id); return sfd_record[rec_id].vector[c]; } void release_sfd_record (void) { int i; if (sfd_record) { RELEASE(sfd_record); } if (sfd_files) { for (i = 0; i < num_sfd_files; i++) { clean_sfd_file_(&sfd_files[i]); } RELEASE(sfd_files); } sfd_record = NULL; sfd_files = NULL; num_sfd_records = max_sfd_records = 0; num_sfd_files = max_sfd_files = 0; } #if DPXTEST /* SFD file dumper */ #ifdef HAVE_ICONV #include #else typedef int iconv_t; #endif #include static void dump_table (const char *sfd_name, const char *sub_name, iconv_t cd) { int rec_id, i; rec_id = sfd_load_record(sfd_name, sub_name); if (rec_id < 0) { WARN("Could not load SFD mapping for \"%s\"", sub_name); return; } fprintf(stdout, " \n", sub_name); for (i = 0; i < 256; i++) { unsigned short c = lookup_sfd_record(rec_id, i); char *p, inbuf[2]; char *q, outbuf[32]; size_t r, inbufleft = 2, outbufleft = 32; if (c == 0) fprintf(stdout, " ", i); else { fprintf(stdout, " > 8) & 0xff, c & 0xff); if (cd != (iconv_t) -1) { p = inbuf; q = outbuf; inbuf[0] = (c >> 8) & 0xff; inbuf[1] = c & 0xff; #ifdef HAVE_ICONV r = iconv(cd, &p, &inbufleft, &q, &outbufleft); if (r == -1) { if (dpx_conf.verbose_level > 0) { WARN("Conversion to Unicode failed for subfont-id=\"%s\" code=\"0x%02x\"", sub_name, i); WARN(">> with: %s", strerror(errno)); } } else { outbuf[32-outbufleft] = 0; fprintf(stdout, " uc=\"%s\"", outbuf); } #endif /* HAVE_ICONV */ } fprintf(stdout, " />"); } fprintf(stdout, "\n"); } fprintf(stdout, " \n"); return; } #define subfontDefinition_DTD "\ \n\ \n\ \n\ \n\ \n\ \ " void test_subfont_help (void) { fprintf(stdout, "usage: subfont [options] SFD_name\n"); fprintf(stdout, "-e, --encoding string\n"); fprintf(stdout, " Target (output) encoding of SFD mapping is 'string'.\n"); fprintf(stdout, " It must be an encoding name recognized by iconv.\n"); fprintf(stdout, " With this option write Unicode character in auxiliary attribute 'uc'.\n"); fprintf(stdout, "-s, --subfont-id string\n"); fprintf(stdout, " Load and dump mapping table only for subfont 'string'.\n"); } int test_subfont_main (int argc, char *argv[]) { char *sfd_name = NULL, *sub_name = NULL; char *from = NULL; int i; iconv_t cd = (iconv_t) -1; for (;;) { int c, optidx = 0; static struct option long_options[] = { {"encoding", 1, 0, 'e'}, /* for to-Unicode conversion */ {"subfont-id", 1, 0, 's'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "e:s:h", long_options, &optidx); if (c == -1) break; switch (c) { case 'e': from = optarg; break; case 's': sub_name = optarg; break; case 'h': test_subfont_help(); return 0; break; default: test_subfont_help(); return -1; break; } } if (optind < argc) { sfd_name = argv[optind++]; } if (sfd_name == NULL) { WARN("No SFD file name specified."); test_subfont_help(); return -1; } if (!from) cd = (iconv_t) -1; else { #ifdef HAVE_ICONV cd = iconv_open("utf-8", from); if (cd == (iconv_t) -1) { WARN("Can't open iconv conversion descriptor for %s --> utf-8", from); return -1; } #else WARN("Your system doesn't have iconv() in libc!"); #endif } fprintf(stdout, "\n"); fprintf(stdout, "\n", subfontDefinition_DTD); fprintf(stdout, "\n", sfd_name, from ? from : "unknown"); if (sub_name == NULL || !strcmp(sub_name, "all")) { char **sub_id; int num_ids = 0; sub_id = sfd_get_subfont_ids(sfd_name, &num_ids); if (!sub_id) WARN("Could not open SFD file: %s", sfd_name); else { for (i = 0; i < num_ids; i++) dump_table(sfd_name, sub_id[i], cd); } } else { dump_table(sfd_name, sub_name, cd); } fprintf(stdout, "\n"); #ifdef HAVE_ICONV if (cd != (iconv_t) -1) iconv_close(cd); #endif return 0; } #endif /* DPXTEST */