summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/dvipsencoding.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/dvipsencoding.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/dvipsencoding.cc979
1 files changed, 979 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/dvipsencoding.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/dvipsencoding.cc
new file mode 100644
index 00000000000..2c4e605ed86
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/dvipsencoding.cc
@@ -0,0 +1,979 @@
+/* dvipsencoding.{cc,hh} -- store a DVIPS encoding
+ *
+ * Copyright (c) 2003-2012 Eddie Kohler
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "dvipsencoding.hh"
+#include "metrics.hh"
+#include "secondary.hh"
+#include <lcdf/error.hh>
+#include <lcdf/straccum.hh>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <algorithm>
+#include "util.hh"
+
+enum { GLYPHLIST_ALTERNATIVE = 0x40000000,
+ GLYPHLIST_USEMAP = GLYPHLIST_ALTERNATIVE,
+ U_EMPTYSLOT = 0xD801,
+ U_ALTSELECTOR = 0xD802 };
+static HashMap<String, uint32_t> glyphlist((uint32_t) -1);
+static Vector<uint32_t> glyphmap;
+static PermString::Initializer perm_initializer;
+PermString DvipsEncoding::dot_notdef(".notdef");
+
+#define NEXT_GLYPH_NAME(gn) ("/" + (gn))
+
+void
+DvipsEncoding::add_glyphlist(String text)
+{
+ const char *s = text.begin(), *end = text.end();
+ while (s != end) {
+ // move to first nonblank
+ while (s != end && isspace((unsigned char) *s))
+ ++s;
+ // ignore comments
+ if (s != end && *s == '#') {
+ skip_to_end_of_line:
+ while (s != end && *s != '\n' && *s != '\r')
+ ++s;
+ continue;
+ }
+ // parse glyph name
+ const char *name_start = s;
+ while (s != end && !isspace((unsigned char) *s) && *s != ';')
+ ++s;
+ if (s == name_start)
+ goto skip_to_end_of_line;
+ String glyph_name = text.substring(name_start, s).compact();
+ int map_pos = glyphmap.size();
+ // parse Unicodes
+ while (1) {
+ while (s != end && (*s == ' ' || *s == '\t'))
+ ++s;
+ if (s == end || *s == '\n' || *s == '\r' || *s == '#'
+ || (map_pos == glyphmap.size() && *s != ';' && *s != ','))
+ break;
+ if (*s == ';' || *s == ',') {
+ ++s;
+ while (s != end && (*s == ' ' || *s == '\t'))
+ ++s;
+ if (s == end || !isxdigit((unsigned char) *s))
+ goto skip_to_end_of_line;
+ if (map_pos != glyphmap.size())
+ glyphmap.push_back(GLYPHLIST_ALTERNATIVE);
+ }
+ uint32_t u = 0;
+ while (s != end && isxdigit((unsigned char) *s)) {
+ if (*s >= '0' && *s <= '9')
+ u = (u << 4) + *s - '0';
+ else if (*s >= 'A' && *s <= 'F')
+ u = (u << 4) + *s - 'A' + 10;
+ else
+ u = (u << 4) + *s - 'a' + 10;
+ ++s;
+ }
+ if (u == 0 || u > 0x10FFFF)
+ goto skip_to_end_of_line;
+ glyphmap.push_back(u);
+ if (s != end && !isspace((unsigned char) *s) && *s != ',' && *s != ';')
+ break;
+ }
+ // store result
+ if (map_pos == glyphmap.size() - 1) {
+ glyphlist.insert(glyph_name, glyphmap.back());
+ glyphmap.pop_back();
+ } else {
+ glyphlist.insert(glyph_name, map_pos | GLYPHLIST_USEMAP);
+ glyphmap.push_back(0);
+ }
+ goto skip_to_end_of_line;
+ }
+}
+
+static void
+unicode_add_suffix(Vector<uint32_t> &prefix,
+ int prefix_starting_from,
+ const Vector<uint32_t> &suffix)
+{
+ int prefix_size = prefix.size();
+ for (Vector<uint32_t>::const_iterator it = suffix.begin();
+ it != suffix.end() && *it != 0;
+ ++it)
+ if (*it == GLYPHLIST_ALTERNATIVE) {
+ prefix.push_back(*it);
+ for (int i = prefix_starting_from; i < prefix_size; ++i)
+ prefix.push_back(prefix[i]);
+ } else
+ prefix.push_back(*it);
+}
+
+bool
+DvipsEncoding::glyphname_unicode(String gn, Vector<uint32_t> &unis)
+{
+ int unis_first_size = unis.size();
+
+ // drop all characters to the right of the first dot
+ String::iterator dot = std::find(gn.begin(), gn.end(), '.');
+ if (dot > gn.begin() && dot < gn.end())
+ gn = gn.substring(gn.begin(), dot);
+
+ // map the first component, handle later components recursively
+ String::iterator underscore = std::find(gn.begin(), gn.end(), '_');
+ String component = gn.substring(gn.begin(), underscore);
+ int prefix_start = 0;
+ Vector<uint32_t> suffix;
+ if (String gn_suffix = gn.substring(underscore + 1, gn.end())) {
+ if (!glyphname_unicode(gn_suffix, suffix))
+ return false;
+ }
+
+ // check glyphlist
+ int value = glyphlist[component];
+ uint32_t uval;
+ if (value >= 0 && !(value & GLYPHLIST_USEMAP))
+ unis.push_back(value);
+ else if (value >= 0) {
+ for (int i = (value & ~GLYPHLIST_USEMAP);
+ glyphmap[i];
+ ++i)
+ if (glyphmap[i] == GLYPHLIST_ALTERNATIVE) {
+ unicode_add_suffix(unis, prefix_start, suffix);
+ unis.push_back(GLYPHLIST_ALTERNATIVE);
+ prefix_start = unis.size();
+ } else
+ unis.push_back(glyphmap[i]);
+ } else if (component.length() >= 7
+ && (component.length() % 4) == 3
+ && (memcmp(component.data(), "uni", 3) == 0
+ // 16.Aug.2008: Some texnansx.enc have incorrect "Uni"
+ // prefix, but we might as well understand it.
+ || memcmp(component.data(), "Uni", 3) == 0)) {
+ for (const char *s = component.begin() + 3;
+ s < component.end();
+ s += 4)
+ if (parse_unicode_number(s, s + 4, -1, uval))
+ unis.push_back(uval);
+ else {
+ unis.resize(unis_first_size);
+ return false;
+ }
+ } else if (component.length() >= 5
+ && component.length() <= 7
+ && component[0] == 'u'
+ && parse_unicode_number(component.begin() + 1, component.end(), -1, uval))
+ unis.push_back(uval);
+ else
+ return false;
+
+ unicode_add_suffix(unis, prefix_start, suffix);
+ return true;
+}
+
+
+DvipsEncoding::DvipsEncoding()
+ : _boundary_char(-1), _altselector_char(-1), _unicoding_map(-1),
+ _warn_missing(false)
+{
+}
+
+void
+DvipsEncoding::encode(int e, PermString what)
+{
+ if (e >= _e.size())
+ _e.resize(e + 1, dot_notdef);
+ _e[e] = what;
+}
+
+int
+DvipsEncoding::encoding_of(PermString what, bool encoding_required)
+{
+ int slot = -1;
+ for (int i = 0; i < _e.size(); i++)
+ if (_e[i] == what) {
+ slot = i;
+ goto use_slot;
+ } else if (!_e[i] || _e[i] == dot_notdef)
+ slot = i;
+ if (what == "||")
+ return _boundary_char;
+ else if (!encoding_required || slot < 0)
+ return -1;
+ use_slot:
+ if (encoding_required) {
+ if (slot >= _encoding_required.size())
+ _encoding_required.resize(slot + 1, false);
+ _encoding_required[slot] = true;
+ this->encode(slot, what);
+ }
+ return slot;
+}
+
+static String
+tokenize(const String &s, int &pos_in, int &line)
+{
+ const char *data = s.data();
+ int len = s.length();
+ int pos = pos_in;
+ while (1) {
+ // skip whitespace
+ while (pos < len && isspace((unsigned char) data[pos])) {
+ if (data[pos] == '\n')
+ line++;
+ else if (data[pos] == '\r' && (pos + 1 == len || data[pos+1] != '\n'))
+ line++;
+ pos++;
+ }
+
+ if (pos >= len) {
+ pos_in = len;
+ return String();
+ } else if (data[pos] == '%') {
+ for (pos++; pos < len && data[pos] != '\n' && data[pos] != '\r'; pos++)
+ /* nada */;
+ } else if (data[pos] == '[' || data[pos] == ']' || data[pos] == '{' || data[pos] == '}') {
+ pos_in = pos + 1;
+ return s.substring(pos, 1);
+ } else if (data[pos] == '(') {
+ int first = pos, nest = 0;
+ for (pos++; pos < len && (data[pos] != ')' || nest); pos++)
+ switch (data[pos]) {
+ case '(': nest++; break;
+ case ')': nest--; break;
+ case '\\':
+ if (pos + 1 < len)
+ pos++;
+ break;
+ case '\n': line++; break;
+ case '\r':
+ if (pos + 1 == len || data[pos+1] != '\n')
+ line++;
+ break;
+ }
+ pos_in = (pos < len ? pos + 1 : len);
+ return s.substring(first, pos_in - first);
+ } else {
+ int first = pos;
+ while (pos < len && data[pos] == '/')
+ pos++;
+ while (pos < len && data[pos] != '/' && !isspace((unsigned char) data[pos]) && data[pos] != '[' && data[pos] != ']' && data[pos] != '%' && data[pos] != '(' && data[pos] != '{' && data[pos] != '}')
+ pos++;
+ pos_in = pos;
+ return s.substring(first, pos - first);
+ }
+ }
+}
+
+
+static String
+comment_tokenize(const String &s, int &pos_in, int &line)
+{
+ const char *data = s.data();
+ int len = s.length();
+ int pos = pos_in;
+ while (1) {
+ while (pos < len && data[pos] != '%' && data[pos] != '(') {
+ if (data[pos] == '\n')
+ line++;
+ else if (data[pos] == '\r' && (pos + 1 == len || data[pos+1] != '\n'))
+ line++;
+ pos++;
+ }
+
+ if (pos >= len) {
+ pos_in = len;
+ return String();
+ } else if (data[pos] == '%') {
+ for (pos++; pos < len && (data[pos] == ' ' || data[pos] == '\t'); pos++)
+ /* nada */;
+ int first = pos;
+ for (; pos < len && data[pos] != '\n' && data[pos] != '\r'; pos++)
+ /* nada */;
+ pos_in = pos;
+ if (pos > first)
+ return s.substring(first, pos - first);
+ } else {
+ int nest = 0;
+ for (pos++; pos < len && (data[pos] != ')' || nest); pos++)
+ switch (data[pos]) {
+ case '(': nest++; break;
+ case ')': nest--; break;
+ case '\\':
+ if (pos + 1 < len)
+ pos++;
+ break;
+ case '\n': line++; break;
+ case '\r':
+ if (pos + 1 == len || data[pos+1] != '\n')
+ line++;
+ break;
+ }
+ }
+ }
+}
+
+static bool
+retokenize_isword(char c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9') || c == '.' || c == '_';
+}
+
+
+static struct { const char *s; int v; } ligkern_ops[] = {
+ { "=:", DvipsEncoding::JL_LIG }, { "|=:", DvipsEncoding::JL_CLIG },
+ { "|=:>", DvipsEncoding::JL_CLIG_S }, { "=:|", DvipsEncoding::JL_LIGC },
+ { "=:|>", DvipsEncoding::JL_LIGC_S }, { "|=:|", DvipsEncoding::JL_CLIGC },
+ { "|=:>", DvipsEncoding::JL_CLIGC_S }, { "|=:|>>", DvipsEncoding::JL_CLIGC_SS },
+ { "{}", DvipsEncoding::JT_KERN }, { "{K}", DvipsEncoding::JT_KERN },
+ { "{L}", DvipsEncoding::JT_LIG }, { "{LK}", DvipsEncoding::JT_NOLIGKERN },
+ { "{KL}", DvipsEncoding::JT_NOLIGKERN }, { "{k}", DvipsEncoding::JT_KERN },
+ { "{l}", DvipsEncoding::JT_LIG }, { "{lk}", DvipsEncoding::JT_NOLIGKERN },
+ { "{kl}", DvipsEncoding::JT_NOLIGKERN },
+ // some encodings have @{@} instead of {}
+ { "@{@}", DvipsEncoding::JT_KERN },
+ { 0, 0 }
+};
+
+static int
+find_ligkern_op(const String &s)
+{
+ for (int i = 0; ligkern_ops[i].s; i++)
+ if (ligkern_ops[i].s == s)
+ return ligkern_ops[i].v;
+ return 0;
+}
+
+inline bool
+operator==(const DvipsEncoding::Ligature& l1, const DvipsEncoding::Ligature& l2)
+{
+ return l1.c1 == l2.c1 && l1.c2 == l2.c2;
+}
+
+void
+DvipsEncoding::add_ligkern(const Ligature &l, int override)
+{
+ Ligature *old = std::find(_lig.begin(), _lig.end(), l);
+ if (old == _lig.end())
+ _lig.push_back(l);
+ else {
+ if ((l.join & JT_KERN) && (override > 0 || !(old->join & JT_KERN))) {
+ old->join |= JT_KERN;
+ old->k = l.k;
+ }
+ if ((l.join & JT_LIG) && (override > 0 || !(old->join & JT_LIG))) {
+ old->join = (old->join & JT_KERN) | (l.join & JT_LIGALL);
+ old->d = l.d;
+ }
+ }
+}
+
+int
+DvipsEncoding::parse_ligkern_words(Vector<String> &v, int override, ErrorHandler *errh)
+{
+ _file_had_ligkern = true;
+ int op;
+ long l;
+ char *endptr;
+ if (v.size() == 3) {
+ // empty string fails
+ if (!v[0])
+ return -1;
+ // boundary char setting
+ if (v[0] == "||" && v[1] == "=") {
+ char *endptr;
+ if (override > 0 || _boundary_char < 0)
+ _boundary_char = strtol(v[2].c_str(), &endptr, 10);
+ if (*endptr == 0 && _boundary_char < _e.size())
+ return 0;
+ else
+ return errh->error("parse error in boundary character assignment");
+ }
+ // altselector char setting
+ if (v[0] == "^^" && v[1] == "=") {
+ char *endptr;
+ if (override > 0 || _altselector_char < 0)
+ _altselector_char = strtol(v[2].c_str(), &endptr, 10);
+ if (*endptr == 0 && _altselector_char < _e.size())
+ return 0;
+ else
+ return errh->error("parse error in altselector character assignment");
+ }
+ // encoding
+ l = strtol(v[0].c_str(), &endptr, 0);
+ if (endptr == v[0].end() && v[1] == "=") {
+ if (l >= 0 && l < 256) {
+ if (override > 0 || !_e[l])
+ encode(l, v[2]);
+ return 0;
+ } else
+ return errh->error("encoding value %<%d%> out of range", l);
+ }
+
+ // kern operation
+ if (v[1].length() >= 3 && v[1][0] == '{' && v[1].back() == '}') {
+ String middle = v[1].substring(1, v[1].length() - 2);
+ l = strtol(middle.c_str(), &endptr, 0);
+ if (endptr == middle.end()) {
+ op = JT_KERN;
+ goto found_kernop;
+ }
+ }
+ op = find_ligkern_op(v[1]);
+ if (!op || (op & JT_ADDLIG))
+ return -1;
+ found_kernop:
+ int av = (v[0] == "*" ? J_ALL : encoding_of(v[0]));
+ if (av < 0)
+ return errh->warning("bad %<%s%> (%<%s%> has no encoding)", v[1].c_str(), v[0].c_str());
+ int bv = (v[2] == "*" ? J_ALL : encoding_of(v[2]));
+ if (bv < 0)
+ return errh->warning("bad %<%s%> (%<%s%> has no encoding)", v[1].c_str(), v[2].c_str());
+ if ((op & JT_KERN) && l && (av == J_ALL || bv == J_ALL))
+ return errh->warning("%<%s %s %s%> illegal, only {0} works with *", v[0].c_str(), v[1].c_str(), v[2].c_str());
+ Ligature lig = { av, bv, op, static_cast<int>(l), 0 };
+ add_ligkern(lig, override);
+ return 0;
+
+ } else if (v.size() == 4 && ((op = find_ligkern_op(v[2])) & JT_ADDLIG)) {
+ int av = encoding_of(v[0], override > 0);
+ if (av < 0)
+ return (override > 0 ? errh->warning("bad ligature (%<%s%> has no encoding)", v[0].c_str()) : -1);
+ int bv = encoding_of(v[1], override > 0);
+ if (bv < 0)
+ return (override > 0 ? errh->warning("bad ligature (%<%s%> has no encoding)", v[1].c_str()) : -1);
+ int cv = encoding_of(v[3], override > 0);
+ if (cv < 0)
+ return (override > 0 ? errh->warning("bad ligature (%<%s%> has no encoding)", v[3].c_str()) : -1);
+ Ligature lig = { av, bv, op, 0, cv };
+ add_ligkern(lig, override);
+ return 0;
+
+ } else
+ return -EPARSE;
+}
+
+int
+DvipsEncoding::parse_position_words(Vector<String> &v, int override, ErrorHandler *errh)
+{
+ if (v.size() != 4)
+ return -EPARSE;
+
+ int c = encoding_of(v[0]);
+ if (c < 0)
+ return (override > 0 ? errh->warning("bad positioning (%<%s%> has no encoding)", v[0].c_str()) : -1);
+
+ char *endptr;
+ int pdx, pdy, adx;
+ if (!v[1] || !v[2] || !v[3]
+ || (pdx = strtol(v[1].c_str(), &endptr, 10), *endptr)
+ || (pdy = strtol(v[2].c_str(), &endptr, 10), *endptr)
+ || (adx = strtol(v[3].c_str(), &endptr, 10), *endptr))
+ return errh->error("parse error in POSITION");
+
+ Ligature l = { c, pdx, pdy, adx, 0 };
+ Ligature *old = std::find(_pos.begin(), _pos.end(), l);
+ if (old == _pos.end())
+ _pos.push_back(l);
+ else if (override > 0)
+ *old = l;
+ return 0;
+}
+
+int
+DvipsEncoding::parse_unicoding_words(Vector<String> &v, int override, ErrorHandler *errh)
+{
+ int av;
+ if (v.size() < 2 || (v[1] != "=" && v[1] != "=:" && v[1] != ":="))
+ return -EPARSE;
+ else if (v[0] == "||" || (av = encoding_of(v[0])) < 0)
+ return errh->warning("bad UNICODING (%<%s%> has no encoding)", v[0].c_str());
+
+ int original_size = _unicoding.size();
+
+ if (v.size() == 2 || (v.size() == 3 && v[2] == dot_notdef))
+ /* no warnings to delete a glyph */;
+ else {
+ for (int i = 2; i < v.size(); i++) {
+ if (_unicoding.size() != original_size)
+ _unicoding.push_back(GLYPHLIST_ALTERNATIVE);
+ if (!glyphname_unicode(v[i], _unicoding)) {
+ errh->warning("can%,t map %<%s%> to Unicode", v[i].c_str());
+ if (i == v.size() - 1 && _unicoding.size() == original_size)
+ errh->warning("target %<%s%> will be deleted from encoding", v[0].c_str());
+ else if (_unicoding.size() != original_size)
+ _unicoding.pop_back();
+ }
+ }
+ }
+
+ _unicoding.push_back(0);
+ if (override > 0 || _unicoding_map[v[0]] < 0)
+ _unicoding_map.insert(v[0], original_size);
+ return 0;
+}
+
+const DvipsEncoding::WordType DvipsEncoding::word_types[] = {
+ { "LIGKERN", &DvipsEncoding::parse_ligkern_words },
+ { "POSITION", &DvipsEncoding::parse_position_words },
+ { "UNICODING", &DvipsEncoding::parse_unicoding_words }
+};
+
+void
+DvipsEncoding::parse_word_group(Vector<String> &words, int override, int wt, ErrorHandler *errh)
+{
+ if (words.size() > 0) {
+ int (DvipsEncoding::*method)(Vector<String> &, int, ErrorHandler *) = word_types[wt].parsefunc;
+ if ((this->*method)(words, override, errh) == -EPARSE) {
+ Vector<String> rewords;
+ for (String *sp = words.begin(); sp != words.end(); sp++) {
+ const char *s = sp->begin(), *ends = sp->end();
+ while (s != ends) {
+ const char *word = s;
+ if (*s == '{') {
+ for (s++; s != ends && *s != '}'; s++)
+ /* nada */;
+ if (s != ends)
+ s++;
+ } else {
+ bool x = retokenize_isword(*s);
+ for (s++; s != ends && x == retokenize_isword(*s); s++)
+ /* nada */;
+ }
+ rewords.push_back(sp->substring(word, s));
+ }
+ }
+ if ((this->*method)(rewords, override, errh) == -EPARSE)
+ errh->error("parse error in %s", word_types[wt].name);
+
+ }
+ words.clear();
+ }
+}
+
+int
+DvipsEncoding::parse_words(const String &s, int override, int wt, ErrorHandler *errh)
+{
+ Vector<String> words;
+ const char *data = s.data();
+ const char *end = s.end();
+ while (data < end) {
+ while (data < end && isspace((unsigned char) *data))
+ data++;
+ const char *first = data;
+ while (data < end && !isspace((unsigned char) *data) && *data != ';')
+ data++;
+ if (data == first) {
+ data++; // step past semicolon (or harmlessly past EOS)
+ parse_word_group(words, override, wt, errh);
+ } else
+ words.push_back(s.substring(first, data));
+ }
+ parse_word_group(words, override, wt, errh);
+ return 0;
+}
+
+String
+DvipsEncoding::landmark(int line) const
+{
+ StringAccum sa;
+ sa << _printable_filename << ':' << line;
+ return sa.take_string();
+}
+
+static String
+trim_space(const String &s, int pos)
+{
+ while (pos < s.length() && isspace((unsigned char) s[pos]))
+ pos++;
+ int epos = s.length();
+ for (int x = 0; x < 2; x++) {
+ while (epos > pos && isspace((unsigned char) s[epos - 1]))
+ epos--;
+ if (epos == pos || s[epos - 1] != ';')
+ break;
+ epos--;
+ }
+ return s.substring(pos, epos - pos);
+}
+
+int
+DvipsEncoding::parse(String filename, bool ignore_ligkern, bool ignore_other, ErrorHandler *errh)
+{
+ int before = errh->nerrors();
+ String s = read_file(filename, errh);
+ if (errh->nerrors() != before)
+ return -1;
+ _filename = filename;
+ _printable_filename = printable_filename(filename);
+ _file_had_ligkern = false;
+ int pos = 0, line = 1;
+
+ // parse text
+ String token = tokenize(s, pos, line);
+ if (!token || token[0] != '/')
+ return errh->lerror(landmark(line), "parse error, expected name");
+ _name = token.substring(1);
+ _initial_comment = s.substring(0, pos - token.length());
+
+ if (tokenize(s, pos, line) != "[")
+ return errh->lerror(landmark(line), "parse error, expected [");
+
+ while ((token = tokenize(s, pos, line)) && token[0] == '/')
+ _e.push_back(token.substring(1));
+
+ _final_text = token + s.substring(pos);
+
+ // now parse comments
+ pos = 0, line = 1;
+ Vector<String> words;
+ LandmarkErrorHandler lerrh(errh, "");
+ while ((token = comment_tokenize(s, pos, line)))
+ if (token.length() >= 8
+ && memcmp(token.data(), "LIGKERN", 7) == 0
+ && isspace((unsigned char) token[7])
+ && !ignore_ligkern) {
+ lerrh.set_landmark(landmark(line));
+ parse_words(token.substring(8), 1, WT_LIGKERN, &lerrh);
+
+ } else if (token.length() >= 9
+ && memcmp(token.data(), "LIGKERNX", 8) == 0
+ && isspace((unsigned char) token[8])
+ && !ignore_ligkern) {
+ lerrh.set_landmark(landmark(line));
+ parse_words(token.substring(9), 1, WT_LIGKERN, &lerrh);
+
+ } else if (token.length() >= 10
+ && memcmp(token.data(), "UNICODING", 9) == 0
+ && isspace((unsigned char) token[9])
+ && !ignore_other) {
+ lerrh.set_landmark(landmark(line));
+ parse_words(token.substring(10), 1, WT_UNICODING, &lerrh);
+
+ } else if (token.length() >= 9
+ && memcmp(token.data(), "POSITION", 8) == 0
+ && isspace((unsigned char) token[8])
+ && !ignore_other) {
+ lerrh.set_landmark(landmark(line));
+ parse_words(token.substring(9), 1, WT_POSITION, &lerrh);
+
+ } else if (token.length() >= 13
+ && memcmp(token.data(), "CODINGSCHEME", 12) == 0
+ && isspace((unsigned char) token[12])
+ && !ignore_other) {
+ _coding_scheme = trim_space(token, 13);
+ if (_coding_scheme.length() > 39)
+ lerrh.lwarning(landmark(line), "only first 39 chars of CODINGSCHEME are significant");
+ if (std::find(_coding_scheme.begin(), _coding_scheme.end(), '(') < _coding_scheme.end()
+ || std::find(_coding_scheme.begin(), _coding_scheme.end(), ')') < _coding_scheme.end()) {
+ lerrh.lerror(landmark(line), "CODINGSCHEME cannot contain parentheses");
+ _coding_scheme = String();
+ }
+
+ } else if (token.length() >= 11
+ && memcmp(token.data(), "WARNMISSING", 11) == 0
+ && (token.length() == 11 || isspace((unsigned char) token[11]))
+ && !ignore_other) {
+ String value = trim_space(token, 11);
+ if (value == "1" || value == "yes" || value == "true" || !value)
+ _warn_missing = true;
+ else if (value == "0" || value == "no" || value == "false")
+ _warn_missing = false;
+ else
+ lerrh.lerror(landmark(line), "WARNMISSING command not understood");
+ }
+
+ return 0;
+}
+
+int
+DvipsEncoding::parse_ligkern(const String &ligkern_text, int override, ErrorHandler *errh)
+{
+ return parse_words(ligkern_text, override, WT_LIGKERN, errh);
+}
+
+int
+DvipsEncoding::parse_position(const String &position_text, int override, ErrorHandler *errh)
+{
+ return parse_words(position_text, override, WT_POSITION, errh);
+}
+
+int
+DvipsEncoding::parse_unicoding(const String &unicoding_text, int override, ErrorHandler *errh)
+{
+ return parse_words(unicoding_text, override, WT_UNICODING, errh);
+}
+
+void
+DvipsEncoding::bad_codepoint(int code, Metrics &metrics, HashMap<PermString, int> &unencoded)
+{
+ for (int i = 0; i < _lig.size(); i++) {
+ Ligature &l = _lig[i];
+ if (l.c1 == code || l.c2 == code)
+ l.join = 0;
+ else if ((l.join & JT_ADDLIG) && l.d == code)
+ l.join &= ~JT_LIGALL;
+ }
+
+ if (_warn_missing) {
+ Vector<uint32_t> garbage;
+ bool unicodes_explicit = x_unicodes(_e[code], garbage);
+ if (!unicodes_explicit || garbage.size() > 0) {
+ Vector<Setting> v;
+ v.push_back(Setting(Setting::RULE, 500, 500));
+ v.push_back(Setting(Setting::SPECIAL, String("Warning: missing glyph '") + _e[code] + "'"));
+ metrics.encode_virtual(code, _e[code], 0, v, true);
+ unencoded.insert(_e[code], 1);
+ }
+ }
+}
+
+static inline Efont::OpenType::Glyph
+map_uni(uint32_t uni, const Efont::OpenType::Cmap &cmap, const Metrics &m)
+{
+ if (uni == U_EMPTYSLOT)
+ return m.emptyslot_glyph();
+ else
+ return cmap.map_uni(uni);
+}
+
+bool
+DvipsEncoding::x_unicodes(PermString chname, Vector<uint32_t> &unicodes) const
+{
+ int i = _unicoding_map[chname];
+ if (i >= 0) {
+ for (; _unicoding[i] > 0; i++)
+ unicodes.push_back(_unicoding[i]);
+ return true;
+ } else {
+ glyphname_unicode(chname, unicodes);
+ return false;
+ }
+}
+
+
+void
+DvipsEncoding::make_metrics(Metrics &metrics, const FontInfo &finfo, Secondary *secondary, bool literal, ErrorHandler *errh)
+{
+ // first pass: without secondaries
+ for (int code = 0; code < _e.size(); code++) {
+ PermString chname = _e[code];
+
+ // common case: skip .notdef
+ if (chname == dot_notdef)
+ continue;
+
+ // find first single Unicode glyph supported by the font
+ Efont::OpenType::Glyph glyph = 0;
+ uint32_t glyph_uni = 0;
+ {
+ Vector<uint32_t> unicodes;
+ (void) x_unicodes(chname, unicodes);
+ Vector<uint32_t>::iterator u = unicodes.begin();
+ while (u != unicodes.end() && glyph <= 0) {
+ uint32_t this_uni = u[0];
+ ++u;
+ if (u != unicodes.end()) {
+ if (*u != GLYPHLIST_ALTERNATIVE)
+ break;
+ ++u;
+ }
+
+ glyph = map_uni(this_uni, *finfo.cmap, metrics);
+ if (glyph_uni == 0 || glyph > 0)
+ glyph_uni = this_uni;
+ }
+ }
+
+ // find named glyph, if any
+ Efont::OpenType::Glyph named_glyph = finfo.glyphid(chname);
+#if 0
+ // 2.May.2008: ff, fi, fl, ffi, and ffl might map to f_f, f_i, f_l,
+ // f_f_i, and f_f_l
+ if (!named_glyph && chname.length() > 0 && chname.length() <= 3
+ && chname[0] == 'f') {
+ if (chname.equals("ff", 2))
+ named_glyph = finfo.glyphid("f_f");
+ else if (chname.equals("fi", 2))
+ named_glyph = finfo.glyphid("f_i");
+ else if (chname.equals("fl", 2))
+ named_glyph = finfo.glyphid("f_l");
+ else if (chname.equals("ffi", 2))
+ named_glyph = finfo.glyphid("f_f_i");
+ else if (chname.equals("ffl", 2))
+ named_glyph = finfo.glyphid("f_f_l");
+ }
+#endif
+ // do not use a Unicode-mapped glyph if literal
+ if (literal)
+ glyph = named_glyph;
+
+ // If we found a glyph, maybe use its named_glyph variant.
+ if (glyph > 0 && named_glyph > 0
+ && std::find(chname.begin(), chname.end(), '.') < chname.end())
+ glyph = named_glyph;
+
+ // assign slot
+ if (glyph > 0)
+ metrics.encode(code, glyph_uni, glyph);
+ }
+
+ // second pass: with secondaries
+ for (int code = 0; code < _e.size(); code++) {
+ // skip already-encoded characters and .notdef
+ if (literal || metrics.glyph(code) > 0 || _e[code] == dot_notdef)
+ continue;
+
+ PermString chname = _e[code];
+
+ // find all Unicodes
+ Vector<uint32_t> unicodes;
+ bool unicodes_explicit = x_unicodes(chname, unicodes);
+
+ // find named glyph, if any
+ Efont::OpenType::Glyph named_glyph = finfo.glyphid(chname);
+
+ // 1. We were not able to find the glyph using Unicode.
+ // 2. There might be a named_glyph.
+ // May need to try secondaries later. Store this slot.
+ // Try secondaries, if there's explicit unicoding or no named_glyph.
+ if (unicodes_explicit || named_glyph <= 0)
+ for (uint32_t *u = unicodes.begin(); u != unicodes.end(); ) {
+ uint32_t *endu = u + 1;
+ while (endu != unicodes.end() && *endu != GLYPHLIST_ALTERNATIVE)
+ ++endu;
+ if (secondary->encode_uni(code, chname, u, endu, metrics, errh))
+ goto encoded;
+ u = (endu == unicodes.end() ? endu : endu + 1);
+ }
+
+ // 1. We were not able to find the glyph using Unicode or secondaries.
+ // 2. There might be a named_glyph.
+ // Use named glyph, if any. Special case for "UNICODING foo =: ;",
+ // which should turn off the character (even if a named_glyph exists),
+ // UNLESS the glyph was explicitly requested.
+ if (named_glyph > 0
+ && (!unicodes_explicit
+ || unicodes.size() > 0
+ || (_encoding_required.size() > code && _encoding_required[code]))) {
+ uint32_t uni = 0;
+ if (unicodes.size() == 1 || (unicodes.size() > 0 && unicodes[1] == GLYPHLIST_ALTERNATIVE))
+ uni = unicodes[0];
+ metrics.encode(code, uni, named_glyph);
+ }
+
+ encoded:
+ /* all set */;
+ }
+
+ // add altselector
+ if (_altselector_char >= 0 && _altselector_char < _e.size()) {
+ metrics.add_altselector_code(_altselector_char, 0);
+ if (metrics.glyph(_altselector_char) <= 0 && !literal)
+ (void) secondary->encode_uni(_altselector_char, "altselector", U_ALTSELECTOR, metrics, errh);
+ }
+
+ // final pass: complain
+ HashMap<PermString, int> unencoded_map;
+ for (int code = 0; code < _e.size(); code++)
+ if (_e[code] != dot_notdef && metrics.glyph(code) <= 0)
+ bad_codepoint(code, metrics, unencoded_map);
+ Vector<String> unencoded;
+ for (HashMap<PermString, int>::iterator it = unencoded_map.begin(); it; ++it)
+ unencoded.push_back(it.key());
+
+ if (unencoded.size() == 1) {
+ errh->warning("%<%s%> glyph not found in font", unencoded[0].c_str());
+ errh->message("(This glyph will appear as a blot and cause warnings if used.)");
+ } else if (unencoded.size() > 1) {
+ std::sort(unencoded.begin(), unencoded.end());
+ StringAccum sa;
+ for (const String* a = unencoded.begin(); a < unencoded.end(); a++)
+ sa << *a << ' ';
+ sa.pop_back();
+ sa.append_break_lines(sa.take_string(), 68, " ");
+ sa.pop_back();
+ errh->warning("%d glyphs not found in font:", unencoded.size());
+ errh->message("%s\n(These glyphs will appear as blots and cause warnings if used.)", sa.c_str());
+ }
+
+ metrics.set_coding_scheme(_coding_scheme);
+}
+
+void
+DvipsEncoding::make_base_mappings(Vector<int> &mappings, const FontInfo &finfo)
+{
+ mappings.clear();
+ for (int code = 0; code < _e.size(); code++) {
+ PermString chname = _e[code];
+
+ // common case: skip .notdef
+ if (chname == dot_notdef)
+ continue;
+
+ // find named glyph
+ Efont::OpenType::Glyph named_glyph = finfo.glyphid(chname);
+ if (named_glyph > 0) {
+ if (mappings.size() <= named_glyph)
+ mappings.resize(named_glyph + 1, -1);
+ mappings[named_glyph] = code;
+ }
+ }
+}
+
+
+void
+DvipsEncoding::apply_ligkern_lig(Metrics &metrics, ErrorHandler *errh) const
+{
+ assert((int)J_ALL == (int)Metrics::CODE_ALL);
+ for (const Ligature *l = _lig.begin(); l < _lig.end(); l++) {
+ if (l->c1 < 0 || l->c2 < 0 || l->join < 0 || !(l->join & JT_LIG))
+ continue;
+ metrics.remove_ligatures(l->c1, l->c2);
+ if (!(l->join & JT_ADDLIG))
+ /* nada */;
+ else if ((l->join & JT_LIGALL) == JL_LIG)
+ metrics.add_ligature(l->c1, l->c2, l->d);
+ else if ((l->join & JT_LIGALL) == JL_LIGC)
+ metrics.add_ligature(l->c1, l->c2, metrics.pair_code(l->d, l->c2));
+ else if ((l->join & JT_LIGALL) == JL_CLIG)
+ metrics.add_ligature(l->c1, l->c2, metrics.pair_code(l->c1, l->d));
+ else {
+ static int complex_join_warning = 0;
+ if (!complex_join_warning) {
+ errh->warning("complex LIGKERN ligature removed (I only support %<=:%>, %<=:|%>, and %<|=:%>)");
+ complex_join_warning = 1;
+ }
+ }
+ }
+}
+
+void
+DvipsEncoding::apply_ligkern_kern(Metrics &metrics, ErrorHandler *) const
+{
+ assert((int)J_ALL == (int)Metrics::CODE_ALL);
+ for (const Ligature *l = _lig.begin(); l < _lig.end(); l++)
+ if (l->c1 >= 0 && l->c2 >= 0 && (l->join & JT_KERN))
+ metrics.set_kern(l->c1, l->c2, l->k);
+}
+
+void
+DvipsEncoding::apply_position(Metrics &metrics, ErrorHandler *) const
+{
+ for (const Ligature *l = _pos.begin(); l < _pos.end(); l++)
+ if (l->c1 >= 0)
+ metrics.add_single_positioning(l->c1, l->c2, l->join, l->k);
+}