/* 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. Copyright (C) 1998, 1999 by Mark A. Wicks 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. */ /* * References: * * Unicode and Glyph Names, ver. 2.3., Adobe Solution Network * http://partners.adobe.com/asn/tech/type/unicodegn.jsp */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "system.h" #include "mem.h" #include "error.h" #include "mfileio.h" #include "pdfparse.h" #include "dpxconf.h" /* Hash */ #include "dpxutil.h" #include "dpxfile.h" #include "unicode.h" #include "agl.h" static int agl_load_listfile (const char *filename, int format); static agl_name * agl_new_name (void) { agl_name *agln; agln = NEW(1, agl_name); agln->name = NULL; agln->suffix = NULL; agln->n_components = 0; agln->alternate = NULL; agln->is_predef = 0; return agln; } static void agl_release_name (agl_name *agln) { agl_name *next; while (agln) { next = agln->alternate; if (agln->name) RELEASE(agln->name); if (agln->suffix) RELEASE(agln->suffix); agln->name = NULL; RELEASE(agln); agln = next; } } char * agl_chop_suffix (const char *glyphname, char **suffix) { char *name, *p; int len; ASSERT(glyphname && suffix); p = strchr(glyphname, '.'); if (p) { len = strlen(glyphname) - strlen(p); if (len < 1) { name = NULL; *suffix = NEW(strlen(glyphname), char); strcpy(*suffix, glyphname+1); } else { p++; name = NEW(len + 1, char); strncpy(name, glyphname, len); name[len] = '\0'; if (p[0] == '\0') { *suffix = NULL; } else { *suffix = NEW(strlen(p) + 1, char); strcpy(*suffix, p); } } } else { name = NEW(strlen(glyphname) + 1, char); strcpy(name, glyphname); *suffix = NULL; } return name; } static const char * const modifiers[] = { "acute", "breve", "caron", "cedilla", "circumflex", "dieresis", "dotaccent", "grave", "hungarumlaut", "macron", "ogonek", "ring", "tilde", "commaaccent", "slash", /* The following entries are not accent nor something * but PS font may have those "small" version... */ "ampersand", "exclam", "exclamdown", "question","questiondown", NULL }; static int skip_capital (const char **p, const char *endptr) { int slen = 0, len; len = (int) (endptr - (*p)); if (len >= 2 && ((**p == 'A' && *(*p+1) == 'E') || (**p == 'O' && *(*p+1) == 'E'))) { *p += 2; slen = 2; } else if (len >= 3 && **p == 'E' && *(*p+1) == 't' && *(*p+2) == 'h') { *p += 3; slen = 3; } else if (len >= 5 && **p == 'T' && *(*p+1) == 'h' && *(*p+2) == 'o' && *(*p+3) == 'r' && *(*p+4) == 'n') { *p += 5; slen = 5; } else if (len >= 1 && **p >= 'A' && **p <= 'Z') { *p += 1; slen = 1; } return slen; } static int skip_modifier (const char **p, const char *endptr) { int slen = 0, len; int i; len = (int) (endptr - (*p)); for (i = 0; modifiers[i] != NULL; i++) { if ((len >= strlen(modifiers[i]) && !memcmp(*p, modifiers[i], len))) { slen = strlen(modifiers[i]); *p += slen; break; } } return slen; } static int is_smallcap (const char *glyphname) { int len, slen; const char *p, *endptr; if (!glyphname) return 0; p = glyphname; len = strlen(glyphname); if (len < 6 || strcmp(p + len - 5, "small")) return 0; endptr = p + len - 5; len -= 5; slen = skip_modifier(&p, endptr); if (slen == len) return 1; /* Acutesmall, Gravesmall, etc */ else if (slen > 0) { /* ??? */ return 0; } len -= skip_capital(&p, endptr); if (len == 0) { return 1; /* Asmall, AEsmall, etc */ } while (len > 0) { /* allow multiple accent */ slen = skip_modifier(&p, endptr); if (slen == 0) return 0; len -= slen; } return 1; } #define SUFFIX_LIST_MAX 16 #define AGL_VAR_SMCP_IDX 0 static struct { const char *key; const char *otl_tag; const char *suffixes[SUFFIX_LIST_MAX]; } var_list[] = { {"small" , "smcp", {"sc", NULL}}, {"swash" , "swsh", {NULL}}, {"superior" , "sups", {NULL}}, {"inferior" , "sinf", {NULL}}, {"numerator" , "numr", {NULL}}, {"denominator" , "dnom", {NULL}}, {"oldstyle" , "onum", {NULL}}, /* The following only used by TeX, there are no * corresponding OTL feat. tag. */ {"display" , NULL, {NULL}}, {"text" , NULL, {NULL}}, {"big" , NULL, {NULL}}, {"bigg" , NULL, {NULL}}, {"Big" , NULL, {NULL}}, {"Bigg" , NULL, {NULL}}, {NULL, NULL, {NULL}} }; const char * agl_suffix_to_otltag (const char *suffix) { int i, j; for (i = 0; var_list[i].key; i++) { for (j = 0; var_list[i].suffixes[j]; j++) { if (!strcmp(suffix, var_list[i].suffixes[j])) return var_list[i].otl_tag; } if (!strcmp(suffix, var_list[i].key)) return var_list[i].otl_tag; if (var_list[i].otl_tag && !strcmp(suffix, var_list[i].otl_tag)) return var_list[i].otl_tag; } return NULL; } static int agl_guess_name (const char *glyphname) { int i, len; if (is_smallcap(glyphname)) return AGL_VAR_SMCP_IDX; len = strlen(glyphname); for (i = 1; var_list[i].key != NULL; i++) { if (len > strlen(var_list[i].key) && !strcmp(glyphname+len-strlen(var_list[i].key), var_list[i].key) ) { return i; } } return -1; } static agl_name * agl_normalized_name (char *glyphname) { agl_name *agln; char *suffix; int i, n; if (!glyphname) return NULL; agln = agl_new_name(); suffix = strchr(glyphname, '.'); if (suffix) { n = strlen(glyphname) - strlen(suffix); if (suffix[1] != '\0') { agln->suffix = NEW(strlen(suffix), char); strcpy(agln->suffix, suffix+1); } agln->name = NEW(n+1, char); memcpy(agln->name, glyphname, n); agln->name[n] = '\0'; } else if (is_smallcap(glyphname)) { n = strlen(glyphname) - 5; agln->suffix = NEW(3, char); strcpy(agln->suffix, "sc"); agln->name = NEW(n+1, char); for (i = 0; i < n; i++) { agln->name[i] = isupper((unsigned char)glyphname[i]) ? (glyphname[i] + 32) : glyphname[i]; } agln->name[n] = '\0'; } else { int var_idx; #define SET_STRING(p,s) do {\ (p) = NEW(strlen((s))+1, char);\ strcpy((p),(s));\ } while (0) var_idx = agl_guess_name(glyphname); if (var_idx < 0 || !var_list[var_idx].key) { n = strlen(glyphname); } else { n = strlen(glyphname) - strlen(var_list[var_idx].key); if (var_list[var_idx].suffixes[0]) SET_STRING(agln->suffix, var_list[var_idx].suffixes[0]); else { SET_STRING(agln->suffix, var_list[var_idx].key); } } agln->name = NEW(n+1, char); memcpy(agln->name, glyphname, n); agln->name[n] = '\0'; } return agln; } static struct ht_table aglmap; static inline void hval_free (void *hval) { agl_release_name((struct agl_name *) hval); } void agl_init_map (void) { ht_init_table(&aglmap, hval_free); agl_load_listfile(AGL_EXTRA_LISTFILE, 0); if (agl_load_listfile(AGL_PREDEF_LISTFILE, 1) < 0) { WARN("Failed to load AGL file \"%s\"...", AGL_PREDEF_LISTFILE); } if (agl_load_listfile(AGL_DEFAULT_LISTFILE, 0) < 0) { WARN("Failed to load AGL file \"%s\"...", AGL_DEFAULT_LISTFILE); } } void agl_close_map (void) { ht_clear_table(&aglmap); } #define WBUF_SIZE 1024 static int agl_load_listfile (const char *filename, int is_predef) { int count = 0; const char *p, *endptr; char *nextptr; char wbuf[WBUF_SIZE]; FILE *fp; if (!filename) return -1; fp = DPXFOPEN(filename, DPX_RES_TYPE_AGL); if (!fp) { return -1; } if (dpx_conf.verbose_level > 0) MESG("= endptr) continue; nextptr = strchr(p, ';'); if (!nextptr || nextptr == p) continue; name = parse_ident(&p, nextptr); skip_white(&p, endptr); if (!name || p[0] != ';') { WARN("Invalid AGL entry: %s", wbuf); if (name) RELEASE(name); continue; } p++; skip_white(&p, endptr); n_unicodes = 0; while (p < endptr && ((p[0] >= '0' && p[0] <= '9') || (p[0] >= 'A' && p[0] <= 'F')) ) { if (n_unicodes >= AGL_MAX_UNICODES) { WARN("Too many Unicode values"); break; } unicodes[n_unicodes++] = strtol(p, &nextptr, 16); p = nextptr; skip_white(&p, endptr); } if (n_unicodes == 0) { WARN("AGL entry ignored (no mapping): %s", wbuf); RELEASE(name); continue; } agln = agl_normalized_name(name); agln->is_predef = is_predef; agln->n_components = n_unicodes; for (i = 0; i < n_unicodes; i++) { agln->unicodes[i] = unicodes[i]; } duplicate = ht_lookup_table(&aglmap, name, strlen(name)); if (!duplicate) ht_append_table(&aglmap, name, strlen(name), agln); else { while (duplicate->alternate) duplicate = duplicate->alternate; duplicate->alternate = agln; } if (dpx_conf.verbose_level > 5) { if (agln->suffix) MESG("agl: %s [%s.%s] -->", name, agln->name, agln->suffix); else MESG("agl: %s [%s] -->", name, agln->name); for (i = 0; i < agln->n_components; i++) { if (agln->unicodes[i] > 0xffff) { MESG(" U+%06X", agln->unicodes[i]); } else { MESG(" U+%04X", agln->unicodes[i]); } } MESG("\n"); } RELEASE(name); count++; } DPXFCLOSE(fp); if (dpx_conf.verbose_level > 0) MESG(">"); return count; } agl_name * agl_lookup_list (const char *glyphname) { agl_name *agln; if (!glyphname) return NULL; agln = ht_lookup_table(&aglmap, glyphname, strlen(glyphname)); return agln; } int agl_name_is_unicode (const char *glyphname) { char c, *suffix; int i, len; if (!glyphname) return 0; suffix = strchr(glyphname, '.'); len = (int) (suffix ? suffix - glyphname : strlen(glyphname)); /* * uni02ac is invalid glyph name and mapped to th empty string. */ if (len >= 7 && (len - 3) % 4 == 0 && !strncmp(glyphname, "uni", 3)) { c = glyphname[3]; /* * Check if the 4th character is uppercase hexadecimal digit. * "union" should not be treated as Unicode glyph name. */ if (isdigit((unsigned char)c) || (c >= 'A' && c <= 'F')) return 1; else return 0; } else if (len <= 7 && len >= 5 && glyphname[0] == 'u') { for (i = 1; i < len - 1; i++) { c = glyphname[i]; if (!isdigit((unsigned char)c) && (c < 'A' || c > 'F')) return 0; } return 1; } return 0; } int32_t agl_name_convert_unicode (const char *glyphname) { int32_t ucv = -1; const char *p; if (!agl_name_is_unicode(glyphname)) return -1; if (strlen(glyphname) > 7 && *(glyphname+7) != '.') { WARN("Mapping to multiple Unicode characters not supported."); return -1; } if (glyphname[1] == 'n') p = glyphname + 3; else p = glyphname + 1; ucv = 0; while (*p != '\0' && *p != '.') { if (!isdigit((unsigned char)*p) && (*p < 'A' || *p > 'F')) { WARN("Invalid char %c in Unicode glyph name %s.", *p, glyphname); return -1; } ucv <<= 4; ucv += isdigit((unsigned char)*p) ? *p - '0' : *p - 'A' + 10; p++; } if (!UC_is_valid(ucv)) { if (ucv < 0x10000) { WARN("Invalid Unicode code value U+%04X.", ucv); } else { WARN("Invalid Unicode code value U+%06X.", ucv); } ucv = -1; } return ucv; } static int xtol (const char *start, int len) { int v = 0; while (len-- > 0) { v <<= 4; if (isdigit((unsigned char)*start)) { v += *start - '0'; } else if (*start >= 'A' && *start <= 'F') { v += *start - 'A' + 10; } else { return -1; } start++; } return v; } #define IS_PUA(u) (((u) >= 0x00E000L && (u) <= 0x00F8FFL) || \ ((u) >= 0x0F0000L && (u) <= 0x0FFFFDL) || \ ((u) >= 0x100000L && (u) <= 0x10FFFDL) \ ) static int32_t put_unicode_glyph (const char *name, unsigned char **dstpp, unsigned char *limptr) { const char *p; int32_t len = 0, ucv; p = name; ucv = 0; if (p[1] != 'n') { p += 1; ucv = xtol(p, strlen(p)); len += UC_UTF16BE_encode_char(ucv, dstpp, limptr); } else { p += 3; while (*p != '\0') { ucv = xtol(p, 4); len += UC_UTF16BE_encode_char(ucv, dstpp, limptr); p += 4; } } return len; } int32_t agl_sput_UTF16BE (const char *glyphstr, unsigned char **dstpp, unsigned char *limptr, int *fail_count) { int32_t len = 0; int count = 0; const char *p, *endptr; ASSERT(glyphstr && dstpp); p = glyphstr; endptr = strchr(p, '.'); if (!endptr) endptr = p + strlen(p); while (p < endptr) { char *name; const char *delim; int32_t sub_len; int i; agl_name *agln0, *agln1 = NULL; delim = strchr(p, '_'); if (delim == p) { /* * Glyph names starting with a underscore or two subsequent * underscore in glyph name not allowed? */ WARN("Invalid glyph name component in \"%s\".", glyphstr); count++; if (fail_count) *fail_count = count; return len; /* Cannot continue */ } else if (!delim || delim > endptr) { delim = endptr; } sub_len = (int32_t) (delim - p); name = NEW(sub_len+1, char); memcpy(name, p, sub_len); name[sub_len] = '\0'; if (agl_name_is_unicode(name)) { sub_len = put_unicode_glyph(name, dstpp, limptr); if (sub_len > 0) len += sub_len; else { count++; } } else { agln1 = agl_lookup_list(name); if (!agln1 || (agln1->n_components == 1 && IS_PUA(agln1->unicodes[0]))) { agln0 = agl_normalized_name(name); if (agln0) { if (dpx_conf.verbose_level > 1 && agln0->suffix) { WARN("agl: fix %s --> %s.%s", name, agln0->name, agln0->suffix); } agln1 = agl_lookup_list(agln0->name); agl_release_name(agln0); } } if (agln1) { for (i = 0; i < agln1->n_components; i++) { len += UC_UTF16BE_encode_char(agln1->unicodes[i], dstpp, limptr); } } else { if (dpx_conf.verbose_level > 0) { WARN("No Unicode mapping for glyph name \"%s\" found.", name); } count++; } } RELEASE(name); p = delim + 1; } if (fail_count) *fail_count = count; return len; } int agl_get_unicodes (const char *glyphstr, int32_t *unicodes, int max_unicodes) { int count = 0; const char *p, *endptr; p = glyphstr; endptr = strchr(p, '.'); if (!endptr) endptr = p + strlen(p); while (p < endptr) { char *name; const char *delim; int32_t sub_len; int i; agl_name *agln0, *agln1 = NULL; delim = strchr(p, '_'); if (delim == p) { /* * Glyph names starting with a underscore or two subsequent * underscore in glyph name not allowed? */ WARN("Invalid glyph name component in \"%s\".", glyphstr); return -1; /* Cannot continue */ } else if (!delim || delim > endptr) { delim = endptr; } sub_len = (int32_t) (delim - p); name = NEW(sub_len+1, char); memcpy(name, p, sub_len); name[sub_len] = '\0'; if (agl_name_is_unicode(name)) { p = name; if (p[1] != 'n') { /* uXXXXXXXX */ if (count >= max_unicodes) { RELEASE(name); return -1; } p++; unicodes[count++] = xtol(p, strlen(p)); } else { p += 3; while (*p != '\0') { if (count >= max_unicodes) { RELEASE(name); return -1; } unicodes[count++] = xtol(p, 4); p += 4; } } } else { agln1 = agl_lookup_list(name); if (!agln1 || (agln1->n_components == 1 && IS_PUA(agln1->unicodes[0]))) { agln0 = agl_normalized_name(name); if (agln0) { if (dpx_conf.verbose_level > 1 && agln0->suffix) { WARN("agl: fix %s --> %s.%s", name, agln0->name, agln0->suffix); } agln1 = agl_lookup_list(agln0->name); agl_release_name(agln0); } } if (agln1) { if (count + agln1->n_components > max_unicodes) { RELEASE(name); return -1; } for (i = 0; i < agln1->n_components; i++) { unicodes[count++] = agln1->unicodes[i]; } } else { if (dpx_conf.verbose_level > 1) WARN("No Unicode mapping for glyph name \"%s\" found.", name); RELEASE(name); return -1; } } RELEASE(name); p = delim + 1; } return count; }