// -*- related-file-name: "../include/efont/t1font.hh" -*-

/* t1font.{cc,hh} -- Type 1 font
 *
 * Copyright (c) 1998-2004 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)
    : _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)
    : _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)
{
    _glyphs.push_back(s);
    _glyph_map.insert(s->name(), _glyphs.size() - 1);
}

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);
		_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::fill_string('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(x[17]) || !x[17])) {
	    // allow arbitrary whitespace after "currentfile eexec".
	    // note: strlen("currentfile eexec") == 17
	    for (x += 17; isspace(*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;
}


void
Type1Font::read_encoding(Type1Reader &reader, const char *first_line)
{
    while (isspace(*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(pos[0]))
		pos++;
	    if (pos[0] != 'd' || pos[1] != 'u' || pos[2] != 'p' || !isspace(pos[3]))
		break;
      
	    // look for `INDEX */'
	    char *scan;
	    int char_value = strtol(pos + 4, &scan, 10);
	    while (scan[0] == ' ') scan++;
	    if (char_value < 0 || char_value >= 256 || scan[0] != '/')
		break;
      
	    // look for `CHARNAME put'
	    scan++;
	    char *name_pos = scan;
	    while (scan[0] != ' ' && scan[0]) scan++;
	    char *name_end = scan;
	    while (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(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(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::stable_string("\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(value[pos - 1])) {
	    while (pos >= 1 && isdigit(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(value[pos - 1])) {
	    int numpos = pos - 1;
	    while (numpos >= 1 && isdigit(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;
    }
}

}
