Logo Search packages:      
Sourcecode: lcdf-typetools version File versions

t1font.cc

// -*- 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;
    }
}

}

Generated by  Doxygen 1.6.0   Back to index