summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/t1font.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/t1font.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/t1font.cc916
1 files changed, 916 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/t1font.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/t1font.cc
new file mode 100644
index 00000000000..19aacc3c985
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/t1font.cc
@@ -0,0 +1,916 @@
+// -*- related-file-name: "../include/efont/t1font.hh" -*-
+
+/* t1font.{cc,hh} -- Type 1 font
+ *
+ * Copyright (c) 1998-2014 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 <efont/t1font.hh>
+#include <efont/t1item.hh>
+#include <efont/t1rw.hh>
+#include <efont/t1mm.hh>
+#include <lcdf/error.hh>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+namespace Efont {
+
+static PermString::Initializer initializer;
+static PermString lenIV_str = "lenIV";
+static PermString FontInfo_str = "FontInfo";
+
+Type1Font::Type1Font(PermString name)
+ : CharstringProgram(1000),
+ _cached_defs(false), _built(true), _font_name(name), _glyph_map(-1),
+ _encoding(0), _cached_mmspace(0), _mmspace(0), _synthetic_item(0)
+{
+ _dict = new HashMap<PermString, Type1Definition *>[dLast];
+ for (int i = 0; i < dLast; i++) {
+ _index[i] = -1;
+ _dict_deltas[i] = 0;
+ _dict[i].set_default_value((Type1Definition *)0);
+ }
+}
+
+Type1Font::Type1Font(Type1Reader &reader)
+ : CharstringProgram(1000),
+ _cached_defs(false), _built(false), _glyph_map(-1), _encoding(0),
+ _cached_mmspace(0), _mmspace(0), _synthetic_item(0)
+{
+ _dict = new HashMap<PermString, Type1Definition *>[dLast];
+ for (int i = 0; i < dLast; i++) {
+ _index[i] = -1;
+ _dict_deltas[i] = 0;
+ _dict[i].set_default_value((Type1Definition *)0);
+ }
+ read(reader);
+}
+
+Type1Font::~Type1Font()
+{
+ delete[] _dict;
+ for (int i = 0; i < _items.size(); i++)
+ delete _items[i];
+ delete _mmspace;
+ for (int i = 0; i < _subrs.size(); i++)
+ delete _subrs[i];
+ if (!_synthetic_item)
+ for (int i = 0; i < _glyphs.size(); i++)
+ delete _glyphs[i];
+}
+
+void
+Type1Font::set_item(int i, Type1Item *it)
+{
+ delete _items[i];
+ _items[i] = it;
+}
+
+static const char * const dict_starters[] = {
+ "0 dict begin", "/FontInfo 0 dict dup begin", "dup /Private 0 dict dup begin", "/Blend 0 dict dup begin", 0, 0
+};
+
+void
+Type1Font::add_definition(int dict, Type1Definition *t1d)
+{
+ if (_index[dict] < 0) {
+ if (_built && dict_starters[dict])
+ add_item(new Type1CopyItem(dict_starters[dict]));
+ _index[dict] = _items.size();
+ }
+ add_item(t1d);
+ set_dict(dict, t1d->name(), t1d);
+}
+
+void
+Type1Font::add_type1_encoding(Type1Encoding *e)
+{
+ if (_encoding) {
+ for (Type1Item** t1i = _items.begin(); t1i < _items.end(); t1i++)
+ if (*t1i == _encoding) {
+ delete _encoding;
+ *t1i = _encoding = e;
+ return;
+ }
+ }
+ _encoding = e;
+ add_item(e);
+}
+
+void
+Type1Font::add_glyph(Type1Subr *s)
+{
+ int &g = _glyph_map.find_force(s->name(), _glyphs.size());
+ if (g == _glyphs.size())
+ _glyphs.push_back(s);
+ else {
+ delete _glyphs[g];
+ _glyphs[g] = s;
+ }
+}
+
+int
+Type1Font::read(Type1Reader &reader)
+{
+ Dict cur_dict = dFont;
+ int eexec_state = 0;
+ bool have_subrs = false;
+ bool have_charstrings = false;
+ int lenIV = 4;
+ Type1SubrGroupItem *cur_group = 0;
+ int cur_group_count = 0;
+
+ StringAccum accum;
+ while (reader.next_line(accum)) {
+
+ // check for NULL STRING
+ int x_length = accum.length();
+ if (!x_length)
+ continue;
+ const char *x = accum.c_str(); // ensure we don't run off the string
+
+ // check for CHARSTRINGS
+ if (reader.was_charstring()) {
+ Type1Subr *fcs = Type1Subr::make(x, x_length, reader.charstring_start(), reader.charstring_length(), lenIV);
+
+ if (fcs->is_subr()) {
+ if (fcs->subrno() >= _subrs.size())
+ _subrs.resize(fcs->subrno() + 30, (Type1Subr *)0);
+ if (_subrs[fcs->subrno()]) // hybrid font program
+ delete _subrs[fcs->subrno()];
+ _subrs[fcs->subrno()] = fcs;
+ if (!have_subrs && _items.size()) {
+ if (Type1CopyItem *item = _items.back()->cast_copy()) {
+ cur_group = new Type1SubrGroupItem
+ (this, true, item->value());
+ cur_group_count = 0;
+ _items.back() = cur_group;
+ delete item;
+ }
+ have_subrs = true;
+ }
+
+ } else {
+ add_glyph(fcs);
+ if (!have_charstrings && _items.size()) {
+ if (Type1CopyItem *item = _items.back()->cast_copy()) {
+ cur_group = new Type1SubrGroupItem
+ (this, false, item->value());
+ cur_group_count = 0;
+ _items.back() = cur_group;
+ delete item;
+ }
+ have_charstrings = true;
+ }
+ }
+
+ accum.clear();
+ continue;
+ }
+
+ // check for COMMENTS
+ if (x[0] == '%') {
+ add_item(new Type1CopyItem(accum.take_string()));
+ continue;
+ }
+
+ // check for CHARSTRING START
+ // 5/29/1999: beware of charstring start-like things that don't have
+ // `readstring' in them!
+ if (!_charstring_definer
+ && strstr(x, "string currentfile") != 0
+ && strstr(x, "readstring") != 0) {
+ const char *sb = x;
+ while (*sb && *sb != '/')
+ sb++;
+ const char *se = sb + 1;
+ while (*sb && *se && *se != ' ' && *se != '{')
+ se++;
+ if (*sb && *se) {
+ _charstring_definer = permprintf(" %*s ", se - sb - 1, sb + 1);
+ reader.set_charstring_definer(_charstring_definer);
+ add_item(new Type1CopyItem(accum.take_string()));
+ continue;
+ }
+ }
+
+ // check for ENCODING
+ if (!_encoding && strncmp(x, "/Encoding ", 10) == 0) {
+ read_encoding(reader, x + 10);
+ accum.clear();
+ continue;
+ }
+
+ // check for a DEFINITION
+ if (x[0] == '/') {
+ definition_succeed:
+ Type1Definition *t1d = Type1Definition::make(accum, &reader);
+ if (!t1d)
+ goto definition_fail;
+ if (t1d->name() == lenIV_str)
+ t1d->value_int(lenIV);
+ add_definition(cur_dict, t1d);
+ accum.clear();
+ continue;
+ } else if (x[0] == ' ') {
+ const char *y;
+ for (y = x; y[0] == ' '; y++)
+ ;
+ if (y[0] == '/')
+ goto definition_succeed;
+ }
+
+ definition_fail:
+
+ // check for ZEROS special case
+ if (eexec_state == 2) {
+ // In eexec_state 2 (right after turning off eexec), the opening
+ // part of the string will have some 0 bytes followed by '0's.
+ // Change the 0 bytes into textual '0's.
+ int zeros = 0;
+ while (x[zeros] == 0 && x_length > 0)
+ zeros++, x_length--;
+ add_item(new Type1CopyItem(String::make_fill('0', zeros * 2 + x_length)));
+ eexec_state = 3;
+ accum.clear();
+ continue;
+ }
+
+ // check for MODIFIED FONT
+ if (eexec_state == 1 && strstr(x, "FontDirectory") != 0
+ && read_synthetic_font(reader, x, accum)) {
+ accum.clear();
+ continue;
+ }
+
+ // check for END-OF-CHARSTRING-GROUP TEXT
+ if (cur_group) {
+ if (cur_group_count == 0
+ || ((strstr(x, "end") != 0 || strstr(x, "put") != 0)
+ && strchr(x, '/') == 0)) {
+ cur_group->add_end_text(x);
+ cur_group_count++;
+ accum.clear();
+ continue;
+ }
+ cur_group = 0;
+ }
+
+ // add COPY ITEM
+ String s = accum.take_string();
+ add_item(new Type1CopyItem(s));
+ x = s.data();
+
+ if (eexec_state == 0 && strncmp(x, "currentfile eexec", 17) == 0 && (isspace((unsigned char) x[17]) || !x[17])) {
+ // allow arbitrary whitespace after "currentfile eexec".
+ // note: strlen("currentfile eexec") == 17
+ for (x += 17; isspace((unsigned char) *x); x++)
+ /* nada */;
+ reader.switch_eexec(true, (unsigned char *)x, (s.data() + s.length()) - x);
+ set_item(nitems() - 1, new Type1EexecItem(true));
+ eexec_state = 1;
+ } else if (eexec_state == 1 && strstr(x, "currentfile closefile") != 0) {
+ reader.switch_eexec(false, 0, 0);
+ add_item(new Type1EexecItem(false));
+ eexec_state = 2;
+ } else if (strstr(x, "begin") != 0) {
+ // 30.Sep.2002: NuevaMM's BlendFontInfo dict starts with a simple
+ // "/FontInfo ... begin" inside a "/Blend ... begin".
+ Dict was_dict = cur_dict;
+ if (strstr(x, "/Private") != 0)
+ cur_dict = dPrivate;
+ else if (strstr(x, "/FontInfo") != 0)
+ cur_dict = dFontInfo;
+ else
+ cur_dict = dFont;
+ if (strstr(x, "/Blend") != 0)
+ cur_dict = (Dict)(cur_dict + dBlend);
+ else if (was_dict == dBlend && cur_dict == dFontInfo)
+ cur_dict = (Dict)(cur_dict + dBlend);
+ } else if (cur_dict == dFontInfo && strstr(x, "end") != 0)
+ cur_dict = dFont;
+ }
+
+ // set dictionary deltas
+ for (int i = dFI; i < dLast; i++)
+ _dict_deltas[i] = get_dict_size(i) - _dict[i].size();
+ // borrow glyphs and glyph map from _synthetic_item
+ if (!_glyphs.size() && _synthetic_item) {
+ _glyphs = _synthetic_item->included_font()->_glyphs;
+ _glyph_map = _synthetic_item->included_font()->_glyph_map;
+ }
+
+ return (ok() ? 0 : -1);
+}
+
+bool
+Type1Font::ok() const
+{
+ return font_name() && _glyphs.size() > 0;
+}
+
+static char *skip_comment_space(char *s)
+{
+ while (1) {
+ if (isspace((unsigned char) *s))
+ ++s;
+ else if (*s == '%') {
+ for (++s; *s != '\r' && *s != '\n' && *s != '\0'; ++s)
+ /* nada */;
+ } else
+ return s;
+ }
+}
+
+void
+Type1Font::read_encoding(Type1Reader &reader, const char *first_line)
+{
+ while (isspace((unsigned char) *first_line))
+ first_line++;
+ if (strncmp(first_line, "StandardEncoding", 16) == 0) {
+ add_type1_encoding(Type1Encoding::standard_encoding());
+ return;
+ }
+
+ add_type1_encoding(new Type1Encoding);
+
+ bool got_any = false;
+ StringAccum accum;
+
+ while (reader.next_line(accum)) {
+
+ // check for NULL STRING
+ if (!accum.length())
+ continue;
+ accum.append('\0'); // ensure we don't run off the string
+ char *pos = accum.data();
+
+ // skip to first `dup' token
+ if (!got_any) {
+ if (!(pos = strstr(pos, "dup"))) {
+ pos = accum.data();
+ goto check_done;
+ }
+ }
+
+ // parse as many `dup INDEX */CHARNAME put' as there are in the line
+ while (1) {
+ // skip spaces, look for `dup '
+ while (isspace((unsigned char) pos[0]))
+ ++pos;
+ if (pos[0] == '%')
+ pos = skip_comment_space(pos);
+ if (pos[0] != 'd' || pos[1] != 'u' || pos[2] != 'p' || !isspace((unsigned char) pos[3]))
+ break;
+
+ // look for `INDEX */'
+ char *scan;
+ int char_value = strtol(pos + 4, &scan, 10);
+ if (scan[0] == '#' && char_value > 0 && char_value < 37
+ && isalnum((unsigned char) scan[1]))
+ char_value = strtol(scan + 1, &scan, char_value);
+ while (isspace((unsigned char) scan[0]))
+ scan++;
+ if (char_value < 0 || char_value >= 256 || scan[0] != '/')
+ break;
+
+ // look for `CHARNAME put'
+ scan++;
+ char *name_pos = scan;
+ while (!isspace((unsigned char) scan[0]) && scan[0] != '\0')
+ ++scan;
+ char *name_end = scan;
+ while (isspace((unsigned char) scan[0]))
+ ++scan;
+ if (scan[0] != 'p' || scan[1] != 'u' || scan[2] != 't')
+ break;
+
+ _encoding->put(char_value, PermString(name_pos, name_end - name_pos));
+ got_any = true;
+ pos = scan + 3;
+ }
+
+ check_done:
+ // check for end of encoding section
+ // if not over, add COPY ITEM for leftovers we didn't parse
+ if ((strstr(pos, "readonly") != 0 || strstr(pos, "def") != 0)
+ && (got_any || strstr(pos, "for") == 0)) {
+ _encoding->set_definer(String(pos));
+ return;
+ } else if (got_any && *pos)
+ add_item(new Type1CopyItem(String(pos)));
+
+ accum.clear();
+ }
+}
+
+static bool
+read_synthetic_string(Type1Reader &reader, StringAccum &wrong_accum,
+ const char *format, int *value)
+{
+ StringAccum accum;
+ if (!reader.next_line(accum))
+ return false;
+ wrong_accum << accum;
+ accum.append('\0'); // ensure we don't run off the string
+ int n = 0;
+ if (value)
+ sscanf(accum.data(), format, value, &n);
+ else
+ sscanf(accum.data(), format, &n);
+ return (n != 0 && (isspace((unsigned char) accum[n]) || accum[n] == '\0'));
+}
+
+bool
+Type1Font::read_synthetic_font(Type1Reader &reader, const char *first_line,
+ StringAccum &wrong_accum)
+{
+ // read font name
+ PermString font_name;
+ {
+ char *x = new char[strlen(first_line) + 1];
+ int n = 0;
+ sscanf(first_line, "FontDirectory /%[^] \t\r\n[{}/] known {%n", x, &n);
+ if (n && (isspace((unsigned char) first_line[n]) || first_line[n] == 0))
+ font_name = x;
+ delete[] x;
+ if (!font_name)
+ return false;
+ }
+
+ // check UniqueID
+ int unique_id;
+ {
+ StringAccum accum;
+ if (!reader.next_line(accum))
+ return false;
+ wrong_accum << accum;
+ accum.c_str(); // ensure we don't run off the string
+ const char *y = accum.data();
+ if (*y != '/' || strncmp(y + 1, font_name.c_str(), font_name.length()) != 0)
+ return false;
+ int n = 0;
+ sscanf(y + font_name.length() + 1, " findfont%n", &n);
+ y = strstr(y, "/UniqueID get ");
+ if (n == 0 || y == 0)
+ return false;
+ n = 0;
+ sscanf(y + 14, "%d%n", &unique_id, &n);
+ if (n == 0)
+ return false;
+ }
+
+ // check lines that say how much text
+ int multiplier;
+ if (!read_synthetic_string(reader, wrong_accum, "save userdict /fbufstr %d string put%n", &multiplier))
+ return false;
+
+ int multiplicand;
+ if (!read_synthetic_string(reader, wrong_accum, "%d {currentfile fbufstr readstring { pop } { clear currentfile%n", &multiplicand))
+ return false;
+
+ if (!read_synthetic_string(reader, wrong_accum, "closefile /fontdownload /unexpectedEOF /.error cvx exec } ifelse } repeat%n", 0))
+ return false;
+
+ int extra;
+ if (!read_synthetic_string(reader, wrong_accum, "currentfile %d string readstring { pop } { clear currentfile%n", &extra))
+ return false;
+
+ if (!read_synthetic_string(reader, wrong_accum, "closefile /fontdownload /unexpectedEOF /.error cvx exec } ifelse%n", 0))
+ return false;
+
+ if (!read_synthetic_string(reader, wrong_accum, "restore } if } if%n", 0))
+ return false;
+
+ Type1SubsetReader subreader(&reader, multiplier*multiplicand + extra);
+ Type1Font *synthetic = new Type1Font(subreader);
+ if (!synthetic->ok())
+ delete synthetic;
+ else {
+ _synthetic_item = new Type1IncludedFont(synthetic, unique_id);
+ add_item(_synthetic_item);
+ }
+ return true;
+}
+
+
+void
+Type1Font::undo_synthetic()
+{
+ // A synthetic font doesn't share arbitrary code with its base font; it
+ // shares just the CharStrings dictionary, according to Adobe Type 1 Font
+ // Format. We take advantage of this.
+
+ if (!_synthetic_item)
+ return;
+
+ int mod_ii;
+ for (mod_ii = nitems() - 1; mod_ii >= 0; mod_ii--)
+ if (_items[mod_ii] == _synthetic_item)
+ break;
+ if (mod_ii < 0)
+ return;
+
+ // remove synthetic item and the reference to the included font
+ _items[mod_ii] = new Type1NullItem;
+ if (Type1CopyItem *copy = _items[mod_ii+1]->cast_copy())
+ if (copy->value().find_left("findfont") >= 0) {
+ delete copy;
+ _items[mod_ii+1] = new Type1NullItem;
+ }
+
+ Type1Font *f = _synthetic_item->included_font();
+ // its glyphs are already stored in our _glyphs array
+
+ // copy SubrGroupItem from `f' into `this'
+ Type1SubrGroupItem *oth_subrs = 0, *oth_glyphs = 0;
+ for (int i = 0; i < f->nitems(); i++)
+ if (Type1SubrGroupItem *subr_grp = f->_items[i]->cast_subr_group()) {
+ if (subr_grp->is_subrs())
+ oth_subrs = subr_grp;
+ else
+ oth_glyphs = subr_grp;
+ }
+
+ assert(oth_glyphs != 0);
+
+ for (int i = nitems() - 1; i >= 0; i--)
+ if (Type1SubrGroupItem *subr_grp = _items[i]->cast_subr_group()) {
+ assert(subr_grp->is_subrs());
+ if (oth_subrs)
+ subr_grp->set_end_text(oth_subrs->end_text());
+ shift_indices(i + 1, 1);
+ Type1SubrGroupItem *nsubr = new Type1SubrGroupItem(*oth_glyphs, this);
+ _items[i + 1] = nsubr;
+ break;
+ }
+
+ // delete included font
+ f->_glyphs.clear(); // don't delete glyphs; we've stolen them
+ delete _synthetic_item;
+ _synthetic_item = 0;
+}
+
+
+Type1Charstring *
+Type1Font::subr(int e) const
+{
+ if (e >= 0 && e < _subrs.size() && _subrs[e])
+ return &_subrs[e]->t1cs();
+ else
+ return 0;
+}
+
+PermString
+Type1Font::glyph_name(int i) const
+{
+ if (i >= 0 && i < _glyphs.size() && _glyphs[i])
+ return _glyphs[i]->name();
+ else
+ return PermString();
+}
+
+Type1Charstring *
+Type1Font::glyph(int i) const
+{
+ if (i >= 0 && i < _glyphs.size() && _glyphs[i])
+ return &_glyphs[i]->t1cs();
+ else
+ return 0;
+}
+
+Type1Charstring *
+Type1Font::glyph(PermString name) const
+{
+ int i = _glyph_map[name];
+ if (i >= 0)
+ return &_glyphs[i]->t1cs();
+ else
+ return 0;
+}
+
+
+bool
+Type1Font::set_subr(int e, const Type1Charstring &t1cs, PermString definer)
+{
+ if (e < 0)
+ return false;
+ if (e >= _subrs.size())
+ _subrs.resize(e + 1, (Type1Subr *)0);
+
+ if (!definer) {
+ Type1Subr *pattern_subr = _subrs[e];
+ for (int i = 0; i < _subrs.size() && !pattern_subr; i++)
+ pattern_subr = _subrs[i];
+ if (!pattern_subr)
+ return false;
+ definer = pattern_subr->definer();
+ }
+
+ delete _subrs[e];
+ _subrs[e] = Type1Subr::make_subr(e, t1cs, definer);
+ return true;
+}
+
+bool
+Type1Font::remove_subr(int e)
+{
+ if (e < 0 || e >= _subrs.size())
+ return false;
+ delete _subrs[e];
+ _subrs[e] = 0;
+ return true;
+}
+
+void
+Type1Font::fill_in_subrs()
+{
+ while (_subrs.size() && _subrs.back() == 0)
+ _subrs.pop_back();
+ for (int i = 0; i < _subrs.size(); i++)
+ if (!_subrs[i])
+ set_subr(i, Type1Charstring(String::make_stable("\013", 1)));
+}
+
+void
+Type1Font::renumber_subrs(const Vector<int> &renumbering)
+{
+ Vector<Type1Subr *> old_subrs;
+ old_subrs.swap(_subrs);
+ for (int i = 0; i < old_subrs.size() && i < renumbering.size(); i++) {
+ int r = renumbering[i];
+ Type1Subr *s = old_subrs[i];
+ if (r >= 0 && s)
+ set_subr(r, s->t1cs(), s->definer());
+ else
+ delete s;
+ }
+ for (int i = renumbering.size(); i < old_subrs.size(); i++)
+ delete old_subrs[i];
+}
+
+
+void
+Type1Font::shift_indices(int move_index, int delta)
+{
+ if (delta > 0) {
+ _items.resize(_items.size() + delta, (Type1Item *)0);
+ memmove(&_items[move_index + delta], &_items[move_index],
+ sizeof(Type1Item *) * (_items.size() - move_index - delta));
+
+ for (int i = dFont; i < dLast; i++)
+ if (_index[i] > move_index)
+ _index[i] += delta;
+
+ } else {
+ memmove(&_items[move_index], &_items[move_index - delta],
+ sizeof(Type1Item *) * (_items.size() - (move_index - delta)));
+ _items.resize(_items.size() + delta);
+
+ for (int i = dFont; i < dLast; i++)
+ if (_index[i] >= move_index) {
+ if (_index[i] < move_index - delta)
+ _index[i] = move_index;
+ else
+ _index[i] += delta;
+ }
+ }
+}
+
+Type1Definition *
+Type1Font::ensure(Dict dict, PermString name)
+{
+ assert(_index[dict] >= 0);
+ Type1Definition *def = _dict[dict][name];
+ if (!def) {
+ def = new Type1Definition(name, 0, "def");
+ int move_index = _index[dict];
+ shift_indices(move_index, 1);
+ _items[move_index] = def;
+ set_dict(dict, name, def);
+ }
+ return def;
+}
+
+void
+Type1Font::add_header_comment(const String &comment)
+{
+ int i;
+ for (i = 0; i < _items.size(); i++) {
+ Type1CopyItem *copy = _items[i]->cast_copy();
+ if (!copy || copy->value()[0] != '%')
+ break;
+ }
+ shift_indices(i, 1);
+ _items[i] = new Type1CopyItem(comment);
+}
+
+
+Type1Item *
+Type1Font::dict_size_item(int d) const
+{
+ switch (d) {
+
+ case dF:
+ if (_built && _index[d] > 0)
+ return _items[_index[d] - 1];
+ break;
+
+ case dFI: case dP: case dB:
+ if (_index[d] > 0)
+ return _items[_index[d] - 1];
+ break;
+
+ case dBFI:
+ if (Type1Item *t1i = b_dict("FontInfo"))
+ return t1i;
+ else if (_index[dBFI] > 0)
+ return _items[_index[dBFI] - 1];
+ break;
+
+ case dBP:
+ if (Type1Item *t1i = b_dict("Private"))
+ return t1i;
+ else if (_index[dBP] > 0)
+ return _items[_index[dBP] - 1];
+ break;
+
+ }
+ return 0;
+}
+
+int
+Type1Font::get_dict_size(int d) const
+{
+ Type1Item *item = dict_size_item(d);
+ if (!item)
+ /* nada */;
+ else if (Type1Definition *t1d = item->cast_definition()) {
+ int num;
+ if (strstr(t1d->definer().c_str(), "dict") && t1d->value_int(num))
+ return num;
+ } else if (Type1CopyItem *copy = item->cast_copy()) {
+ String value = copy->value();
+ int pos = value.find_left(" dict");
+ if (pos >= 1 && isdigit((unsigned char) value[pos - 1])) {
+ while (pos >= 1 && isdigit((unsigned char) value[pos - 1]))
+ pos--;
+ return strtol(value.data() + pos, 0, 10);
+ }
+ }
+ return -1;
+}
+
+void
+Type1Font::set_dict_size(int d, int size)
+{
+ Type1Item *item = dict_size_item(d);
+ if (!item)
+ return;
+ if (Type1Definition *t1d = item->cast_definition()) {
+ int num;
+ if (strstr(t1d->definer().c_str(), "dict") && t1d->value_int(num))
+ t1d->set_int(size);
+ } else if (Type1CopyItem *copy = item->cast_copy()) {
+ String value = copy->value();
+ int pos = value.find_left(" dict");
+ if (pos >= 1 && isdigit((unsigned char) value[pos - 1])) {
+ int numpos = pos - 1;
+ while (numpos >= 1 && isdigit((unsigned char) value[numpos - 1]))
+ numpos--;
+ StringAccum accum;
+ accum << value.substring(0, numpos) << size << value.substring(pos);
+ copy->set_value(accum.take_string());
+ }
+ }
+}
+
+void
+Type1Font::write(Type1Writer &w)
+{
+ Type1Definition *lenIV_def = p_dict("lenIV");
+ int lenIV = 4;
+ if (lenIV_def)
+ lenIV_def->value_int(lenIV);
+ w.set_charstring_start(_charstring_definer);
+ w.set_lenIV(lenIV);
+
+ // change dict sizes
+ for (int i = dF; i < dLast; i++)
+ set_dict_size(i, _dict[i].size() + _dict_deltas[i]);
+ // XXX what if dict had nothing, but now has something?
+
+ for (int i = 0; i < _items.size(); i++)
+ _items[i]->gen(w);
+
+ w.flush();
+}
+
+void
+Type1Font::cache_defs() const
+{
+ Type1Definition *t1d = dict("FontName");
+ if (t1d)
+ t1d->value_name(_font_name);
+ _cached_defs = true;
+}
+
+MultipleMasterSpace *
+Type1Font::mmspace() const
+{
+ if (!_cached_mmspace)
+ create_mmspace();
+ return _mmspace;
+}
+
+MultipleMasterSpace *
+Type1Font::create_mmspace(ErrorHandler *errh) const
+{
+ if (_cached_mmspace)
+ return _mmspace;
+ _cached_mmspace = 1;
+
+ Type1Definition *t1d;
+
+ Vector< Vector<double> > master_positions;
+ t1d = fi_dict("BlendDesignPositions");
+ if (!t1d || !t1d->value_numvec_vec(master_positions))
+ return 0;
+
+ int nmasters = master_positions.size();
+ if (nmasters <= 0) {
+ errh->error("bad BlendDesignPositions");
+ return 0;
+ }
+ int naxes = master_positions[0].size();
+ _mmspace = new MultipleMasterSpace(font_name(), naxes, nmasters);
+ _mmspace->set_master_positions(master_positions);
+
+ Vector< Vector<double> > normalize_in, normalize_out;
+ t1d = fi_dict("BlendDesignMap");
+ if (t1d && t1d->value_normalize(normalize_in, normalize_out))
+ _mmspace->set_normalize(normalize_in, normalize_out);
+
+ Vector<PermString> axis_types;
+ t1d = fi_dict("BlendAxisTypes");
+ if (t1d && t1d->value_namevec(axis_types) && axis_types.size() == naxes)
+ for (int a = 0; a < naxes; a++)
+ _mmspace->set_axis_type(a, axis_types[a]);
+
+ int ndv, cdv;
+ Type1Charstring *cs;
+ t1d = p_dict("NDV");
+ if (t1d && t1d->value_int(ndv) && (cs = subr(ndv)))
+ _mmspace->set_ndv(*cs);
+ t1d = p_dict("CDV");
+ if (t1d && t1d->value_int(cdv) && (cs = subr(cdv)))
+ _mmspace->set_cdv(*cs);
+
+ Vector<double> design_vector;
+ t1d = dict("DesignVector");
+ if (t1d && t1d->value_numvec(design_vector))
+ _mmspace->set_design_vector(design_vector);
+
+ Vector<double> weight_vector;
+ t1d = dict("WeightVector");
+ if (t1d && t1d->value_numvec(weight_vector))
+ _mmspace->set_weight_vector(weight_vector);
+
+ if (!_mmspace->check(errh)) {
+ delete _mmspace;
+ _mmspace = 0;
+ }
+ return _mmspace;
+}
+
+void
+Type1Font::uncache_defs()
+{
+ _cached_defs = false;
+}
+
+void
+Type1Font::font_matrix(double matrix[6]) const
+{
+ Vector<double> t1d_matrix;
+ Type1Definition *t1d = dict("FontMatrix");
+ if (t1d && t1d->value_numvec(t1d_matrix) && t1d_matrix.size() == 6)
+ memcpy(&matrix[0], &t1d_matrix[0], sizeof(double) * 6);
+ else {
+ matrix[0] = matrix[3] = 0.001;
+ matrix[1] = matrix[2] = matrix[4] = matrix[5] = 0;
+ }
+}
+
+}