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

t1item.cc

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

/* t1item.{cc,hh} -- items in a 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/t1item.hh>
#include <efont/t1rw.hh>
#include <efont/t1interp.hh>
#include <efont/t1font.hh>
#include <lcdf/strtonum.h>
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
#ifndef WIN32
# include <unistd.h>
#endif
#ifdef BROKEN_STRTOD
# define strtod good_strtod
#endif
namespace Efont {

/*****
 * Type1NullItem
 **/

void
Type1NullItem::gen(Type1Writer &)
{
}

/*****
 * Type1CopyItem
 **/

void
Type1CopyItem::gen(Type1Writer &w)
{
    w << _value << '\n';
}


/*****
 * Type1EexecItem
 **/

void
Type1EexecItem::gen(Type1Writer &w)
{
    if (_eexec_on)
      w << "currentfile eexec\n";
    w.switch_eexec(_eexec_on);
}


/*****
 * Type1Definition
 **/

typedef Vector<double> NumVector;


Type1Definition::Type1Definition(PermString n, const String &v, PermString d)
    : _name(n), _val(v), _definer(d)
{
    _val.c_str();       // ensure it ends with '\0'
}

Type1Definition *
Type1Definition::make_string(PermString n, const String &v, PermString d)
{
    Type1Definition *t1d = new Type1Definition(n, "", d);
    t1d->set_string(v);
    return t1d;
}

int
Type1Definition::slurp_string(StringAccum &accum, int pos, Type1Reader *reader)
{
    int paren_level = 0;
    char *s = accum.data() + pos;
  
    do {
      switch (*s++) {
        case '(': paren_level++; break;
        case ')': paren_level--; break;
        case '\\': if (paren_level && *s) s++; break;
        case 0:
          if (!reader) return -1;
          pos = s - accum.data();
          accum.append('\n'); // add \n
          if (!reader->next_line(accum)) return -1;
          accum.c_str();      // ensure null termination
          s = accum.data() + pos;
          break;
      }
    } while (paren_level);
  
    return s - accum.data();
}

int
Type1Definition::slurp_proc(StringAccum &accum, int pos, Type1Reader *reader)
{
    int paren_level = 0;
    int brace_level = 0;
    char *s = accum.data() + pos;
  
    do {
      switch (*s++) {
        case '{': if (!paren_level) brace_level++; break;
        case '}': if (!paren_level) brace_level--; break;
        case '(': paren_level++; break;
        case ')': paren_level--; break;
        case '\\': if (paren_level && *s) s++; break;
        case '%':
          if (!paren_level)
            while (*s != '\n' && *s != '\r' && *s)
                s++;
          break;
        case 0:
          if (!reader) return -1;
          pos = s - accum.data();
          accum.append('\n'); // add \n
          if (!reader->next_line(accum)) return -1;
          accum.c_str();      // ensure null termination
          s = accum.data() + pos;
          break;
      }
    } while (brace_level);
  
    return s - accum.data();
}

Type1Definition *
Type1Definition::make(StringAccum &accum, Type1Reader *reader,
                  bool force_definition)
{
    char *s = accum.data();
    while (isspace(*s))
      s++;
    if (*s != '/')
      return 0;
    s++;
    int name_start_pos = s - accum.data();
  
    // find NAME LENGTH
    while (!isspace(*s) && *s != '[' && *s != '{' && *s != '('
         && *s != ']' && *s != '}' && *s != ')' && *s)
      s++;
    if (!*s)
      return 0;
    int name_end_pos = s - accum.data();
  
    while (isspace(*s))
      s++;
    int val_pos = s - accum.data();
    int val_end_pos = -1;
    bool check_def = false;
  
    if (*s == '}' || *s == ']' || *s == ')' || *s == 0)
      return 0;
  
    else if (*s == '(')
      val_end_pos = slurp_string(accum, val_pos, reader);
  
    else if (*s == '{')
      val_end_pos = slurp_proc(accum, val_pos, reader);
  
    else if (*s == '[') {
      int brack_level = 0;
      do {
          switch (*s++) {
            case '[': brack_level++; break;
            case ']': brack_level--; break;
            case '(': case ')': case 0: return 0;
          }
      } while (brack_level);
      val_end_pos = s - accum.data();
    
    } else {
      while (!isspace(*s) && *s)
          s++;
      val_end_pos = s - accum.data();
      if (!force_definition) check_def = true;
    }
  
    if (val_end_pos < 0)
      return 0;
    s = accum.data() + val_end_pos;
    while (isspace(*s))
      s++;
    if (check_def && (s[0] != 'd' || s[1] != 'e' || s[2] != 'f'))
      if (strncmp(s, "dict def", 8) != 0)
          return 0;
  
    PermString name(accum.data()+name_start_pos, name_end_pos - name_start_pos);
    PermString def(s, accum.length() - (s - accum.data()));
    String value = String(accum.data() + val_pos, val_end_pos - val_pos);
    return new Type1Definition(name, value, def);
}

void
Type1Definition::gen(Type1Writer &w)
{
    w << '/' << _name << ' ' << _val << ' ' << _definer << '\n';
}

void
Type1Definition::gen(StringAccum &sa)
{
    sa << '/' << _name << ' ' << _val << ' ' << _definer;
}


bool
Type1Definition::value_bool(bool &b) const
{
    if (_val == "true") {
      b = true;
      return true;
    } else if (_val == "false") {
      b = false;
      return true;
    } else
      return false;
}

bool
Type1Definition::value_int(int &i) const
{
    char *s;
    i = strtol(_val.data(), &s, 10);
    return (*s == 0);
}

bool
Type1Definition::value_num(double &d) const
{
    char *s;
    d = strtonumber(_val.data(), &s);
    return (*s == 0);
}

bool
Type1Definition::value_string(String &str) const
{
    if (_val.length() == 0 || _val[0] != '(' || _val.back() != ')')
      return false;
    StringAccum sa;
    int pos, first_pos = 1, len = _val.length() - 1;
    for (pos = 1; pos < len; pos++)
      if (_val[pos] == '\\') {
          sa.append(_val.data() + first_pos, pos - first_pos);
          pos++;
          switch (pos < len ? _val[pos] : -1) {
            case '\r':
            pos++;
            if (pos < len && _val[pos] == '\n')
                pos++;
            break;
            case '\n':
            pos++;
            break;
            case '0': case '1': case '2': case '3':
            case '4': case '5': case '6': case '7': {
              int c = _val[pos++] - '0';
              for (int i = 1; pos < len && i < 3 && _val[pos] >= '0' && _val[pos] <= '7'; i++, pos++)
                  c = (c << 3) | (_val[pos] - '0');
              sa.append((char) c);
              break;
            }
            case 'n':
            sa << '\n';
            pos++;
            break;
            case 'r':
            sa << '\r';
            pos++;
            break;
            case 't':
            sa << '\t';
            pos++;
            break;
            case 'b':
            sa << '\b';
            pos++;
            break;
            case 'f':
            sa << '\f';
            pos++;
            break;
            default:
            sa << _val[pos];
            pos++;
            break;
          }
          first_pos = pos;
      }
    sa.append(_val.data() + first_pos, len - first_pos);
    str = sa.take_string();
    return true;
}

bool
Type1Definition::value_name(PermString &str) const
{
    if (_val.length() == 0 || _val[0] != '/')
      return false;
    int pos;
    for (pos = 1; pos < _val.length(); pos++)
      if (isspace(_val[pos]) || _val[pos] == '/')
          return false;
    str = PermString(_val.data() + 1, pos - 1);
    return true;
}

static bool
strtonumvec(const char *f, const char **endf, NumVector &v)
{
    v.clear();
    char *s = (char *)f;
    if (*s != '[' && *s != '{')
      return false;
    s++;
    while (1) {
      while (isspace(*s))
          s++;
      if (isdigit(*s) || *s == '.' || *s == '-')
          v.push_back( strtonumber(s, &s) );
      else {
          if (endf)
            *endf = s + 1;
          return (*s == ']' || *s == '}');
      }
    }
}

bool
Type1Definition::value_numvec(NumVector &v) const
{
    return strtonumvec(_val.data(), 0, v);
}

static bool
strtonumvec_vec(const char *f, const char **endf, Vector<NumVector> &v)
{
    v.clear();
    const char *s = f;
    if (*s != '[' && *s != '{')
      return false;
    s++;
    while (1) {
      while (isspace(*s))
          s++;
      if (*s == '[' || *s == '{') {
          NumVector subv;
          if (!strtonumvec(s, &s, subv))
            return false;
          v.push_back(subv);
      } else {
          if (endf)
            *endf = s + 1;
          return (*s == ']' || *s == '}');
      }
    }
}

bool
Type1Definition::value_numvec_vec(Vector<NumVector> &v) const
{
    return strtonumvec_vec(_val.data(), 0, v);
}

bool
Type1Definition::value_normalize(Vector<NumVector> &in,
                         Vector<NumVector> &out) const
{
    in.clear();
    out.clear();
    const char *s = _val.data();
    if (*s++ != '[')
      return false;
    while (1) {
      while (isspace(*s))
          s++;
      if (*s == '[') {
          Vector<NumVector> sub;
          if (!strtonumvec_vec(s, &s, sub))
            return false;
      
          NumVector subin;
          NumVector subout;
          for (int i = 0; i < sub.size(); i++)
            if (sub[i].size() == 2) {
                subin.push_back(sub[i][0]);
                subout.push_back(sub[i][1]);
            } else
                return false;
          in.push_back(subin);
          out.push_back(subout);
      } else
          return (*s == ']');
    }
}

bool
Type1Definition::value_namevec(Vector<PermString> &v) const
{
    v.clear();
    const char *s = _val.data();
    if (*s++ != '[')
      return false;
    while (1) {
      while (isspace(*s))
          s++;
      if (*s == '/')
          s++;
      if (isalnum(*s)) {
          const char *start = s;
          while (*s && !isspace(*s) && *s != ']' && *s != '/')
            s++;
          v.push_back(PermString(start, s - start));
      } else
          return (*s == ']');
    }
}


void
Type1Definition::set_bool(bool b)
{
    set_val(b ? "true" : "false");
}

void
Type1Definition::set_int(int i)
{
    set_val(String(i));
}

void
Type1Definition::set_num(double n)
{
    set_val(String(n));
}

void
Type1Definition::set_string(const String &v)
{
    const char *s = v.data();
    int len = v.length();
    int left = 0;
    StringAccum sa;
    sa << '(';
    for (int pos = 0; pos < len; pos++)
      if ((s[pos] < ' ' && !isspace(s[pos])) || ((unsigned char)s[pos]) > 0176 || s[pos] == '(' || s[pos] == ')' || s[pos] == '\\') {
          sa << v.substring(left, pos - left) << '\\';
          if (s[pos] == '(' || s[pos] == ')' || s[pos] == '\\')
            sa << s[pos];
          else
            sprintf(sa.reserve(8), "%03o", (unsigned char) (s[pos]));
          left = pos + 1;
      }
    sa << v.substring(left) << ')';
    _val = sa.take_string();
}

void
Type1Definition::set_name(PermString str, bool name)
{
    StringAccum sa;
    if (name)
      sa << '/';
    sa << str;
    set_val(sa);
}

static void
accum_numvec(StringAccum &sa, const NumVector &nv, bool executable)
{
    char open = (executable ? '{' : '[');
    for (int i = 0; i < nv.size(); i++)
      sa << (i ? ' ' : open) << nv[i];
    sa << (executable ? '}' : ']');
}

void
Type1Definition::set_numvec(const NumVector &nv, bool executable)
{
    StringAccum sa;
    accum_numvec(sa, nv, executable);
    set_val(sa);
}

void
Type1Definition::set_numvec_vec(const Vector<NumVector> &nv)
{
    StringAccum sa;
    sa << '[';
    for (int i = 0; i < nv.size(); i++)
      accum_numvec(sa, nv[i], false);
    sa << ']';
    set_val(sa);
}

void
Type1Definition::set_normalize(const Vector<NumVector> &vin,
                         const Vector<NumVector> &vout)
{
    StringAccum sa;
    sa << '[';
    for (int i = 0; i < vin.size(); i++) {
      const NumVector &vini = vin[i];
      const NumVector &vouti = vout[i];
      sa << '[';
      for (int j = 0; j < vini.size(); j++)
          sa << '[' << vini[j] << ' ' << vouti[j] << ']';
      sa << ']';
    }
    sa << ']';
    set_val(sa);
}

void
Type1Definition::set_namevec(const Vector<PermString> &v, bool executable)
{
    StringAccum sa;
    sa << '[';
    for (int i = 0; i < v.size(); i++) {
      if (i) sa << ' ';
      if (executable) sa << '/';
      sa << v[i];
    }
    sa << ']';
    set_val(sa);
}


/*****
 * Type1Encoding
 **/

static PermString::Initializer initializer;
static PermString dot_notdef(".notdef");


Type1Encoding::Type1Encoding()
    : _v(new PermString[256]), _copy_of(0), _definer("readonly def")
{
    for (int i = 0; i < 256; i++)
      _v[i] = dot_notdef;
}

Type1Encoding::Type1Encoding(const Type1Encoding &o)
    : Type1Item(), _definer(o._definer)
{
    if (o._copy_of) {
      _v = o._v;
      _copy_of = o._copy_of;
    } else {
      _v = new PermString[256];
      _copy_of = 0;
      for (int i = 0; i < 256; i++)
          _v[i] = o._v[i];
    }
}

Type1Encoding::Type1Encoding(Type1Encoding *copy_of)
    : _v(copy_of->_v), _copy_of(copy_of), _definer(copy_of->_definer)
{
}

Type1Encoding::~Type1Encoding()
{
    if (!_copy_of)
      delete[] _v;
}

void
Type1Encoding::unshare()
{
    if (_copy_of) {
      PermString *new_v = new PermString[256];
      memcpy(new_v, _v, sizeof(PermString) * 256);
      _v = new_v;
      _copy_of = 0;
    }
}

void
Type1Encoding::clear()
{
    unshare();
    for (int i = 0; i < 256; i++)
      _v[i] = dot_notdef;
}


static Type1Encoding *canonical_standard_encoding;

Type1Encoding *
Type1Encoding::standard_encoding()
{
    if (!canonical_standard_encoding) {
      canonical_standard_encoding = new Type1Encoding;
      for (int i = 0; i < 256; i++)
          if (Charstring::standard_encoding[i])
            canonical_standard_encoding->put(i, Charstring::standard_encoding[i]);
    }
    // Return a copy of the cached encoding. When it's deleted, we won't be.
    return new Type1Encoding(canonical_standard_encoding);
}


void
Type1Encoding::gen(Type1Writer &w)
{
    if (_copy_of && _copy_of == canonical_standard_encoding)
      w << "/Encoding StandardEncoding def\n";
    else {
      w << "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n";
      for (int i = 0; i < 256; i++)
          if (_v[i] != dot_notdef)
            w << "dup " << i << " /" << _v[i] << " put\n";
      w << _definer << '\n';
    }
}


/*****
 * Type1Subr
 **/

Type1Subr::Type1Subr(PermString n, int num, PermString definer,
                 int lenIV, const String &s)
    : _name(n), _subrno(num), _definer(definer), _cs(lenIV, s)
{
}

Type1Subr::Type1Subr(PermString n, int num, PermString definer,
                 const Type1Charstring &t1cs)
    : _name(n), _subrno(num), _definer(definer), _cs(t1cs)
{
}

Type1Subr *
Type1Subr::make(const char *s_in, int s_len, int cs_pos, int cs_len, int lenIV)
{
    /* USAGE NOTE: You must ensure that s_in contains a valid subroutine string
       before calling Type1Subr::make. Type1Reader::was_charstring() is a good
       guarantee of this.
       A valid subroutine string is one of the following:
       /[char_name] ### charstring_start ........
       dup [subrno] ### charstring_start .... */
  
    const char *s = s_in;
    PermString name;
    int subrno = 0;
  
    // Force literal spaces rather than isspace().
    if (*s == '/') {
      const char *nstart = ++s;
      while (!isspace(*s) && *s)
          s++;
      name = PermString(nstart, s - nstart);
    
    } else {
      // dup [subrno] ...
      s += 3;
      while (isspace(*s))
          s++;
      subrno = strtol(s, (char **)&s, 10);
    }
  
    s = s_in + cs_pos;

    // Lazily decrypt the charstring.
    PermString definer = PermString(s + cs_len, s_len - cs_len - cs_pos);
    return new Type1Subr(name, subrno, definer, lenIV, String(s, cs_len));
}

Type1Subr *
Type1Subr::make_subr(int subrno, const Type1Charstring &cs, PermString definer)
{
    return new Type1Subr(PermString(), subrno, definer, cs);
}

Type1Subr *
Type1Subr::make_glyph(PermString glyph, const Type1Charstring &cs, PermString definer)
{
    return new Type1Subr(glyph, -1, definer, cs);
}


void
Type1Subr::gen(Type1Writer &w)
{
    int len = _cs.length();
    const unsigned char *data = _cs.data();
  
    if (is_subr())
      w << "dup " << _subrno << ' ' << len + w.lenIV() << w.charstring_start();
    else
      w << '/' << _name << ' ' << len + w.lenIV() << w.charstring_start();
  
    if (w.lenIV() < 0) {
      // lenIV < 0 means charstrings are unencrypted
      w.print((const char *)data, len);
    
    } else {
      // PERFORMANCE NOTE: Putting the charstring in a buffer of known length
      // and printing that buffer rather than one char at a time is an OK
      // optimization. (around 10%)
      unsigned char *buf = new unsigned char[len + w.lenIV()];
      unsigned char *t = buf;
    
      int r = t1R_cs;
      for (int i = 0; i < w.lenIV(); i++) {
          unsigned char c = (unsigned char)(r >> 8);
          *t++ = c;
          r = ((c + r) * t1C1 + t1C2) & 0xFFFF;
      }
      for (int i = 0; i < len; i++, data++) {
          unsigned char c = (*data ^ (r >> 8));
          *t++ = c;
          r = ((c + r) * t1C1 + t1C2) & 0xFFFF;
      }
    
      w.print((char *)buf, len + w.lenIV());
      delete[] buf;
    }
  
    w << _definer << '\n';
}

/*****
 * Type1SubrGroupItem
 **/

Type1SubrGroupItem::Type1SubrGroupItem(Type1Font *font, bool is_subrs, const String &value)
    : _font(font), _is_subrs(is_subrs), _value(value)
{
}

Type1SubrGroupItem::Type1SubrGroupItem(const Type1SubrGroupItem &from, Type1Font *font)
    : _font(font), _is_subrs(from._is_subrs),
      _value(from._value), _end_text(from._end_text)
{
}

void
Type1SubrGroupItem::add_end_text(const String &s)
{
    _end_text += s + "\n";
}

void
Type1SubrGroupItem::gen(Type1Writer &w)
{
    Type1Font *font = _font;
  
    int pos = _value.find_left(_is_subrs ? " array" : " dict");
    if (pos >= 1 && isdigit(_value[pos - 1])) {
      int numpos = pos - 1;
      while (numpos >= 1 && isdigit(_value[numpos - 1]))
          numpos--;
    
      int n;
      if (_is_subrs) {
          n = font->nsubrs();
          while (n && !font->subr(n - 1))
            n--;
      } else
          n = font->nglyphs();

      w << _value.substring(0, numpos) << n << _value.substring(pos);
    } else
      w << _value;
  
    w << '\n';
  
    if (_is_subrs) {
      int count = font->nsubrs();
      for (int i = 0; i < count; i++)
          if (Type1Subr *g = font->subr_x(i))
            g->gen(w);
    } else {
      int count = font->nglyphs();
      for (int i = 0; i < count; i++)
          if (Type1Subr *g = font->glyph_x(i))
            g->gen(w);
    }

    w << _end_text;
}

/*****
 * Type1IncludedFont
 **/

Type1IncludedFont::Type1IncludedFont(Type1Font *font, int unique_id)
    : _included_font(font), _unique_id(unique_id)
{
}

Type1IncludedFont::~Type1IncludedFont()
{
    delete _included_font;
}

void
Type1IncludedFont::gen(Type1Writer &w)
{
    FILE *f = tmpfile();
    if (!f)
      return;

    // write included font
    Type1PFAWriter new_w(f);
    _included_font->write(new_w);
    fflush(f);

    struct stat s;
    fstat(fileno(f), &s);

    w << "FontDirectory /" << _included_font->font_name() << " known{\n"
      << "/" << _included_font->font_name()
      << " findfont dup /UniqueID known {dup /UniqueID get "
      << _unique_id
      << " eq exch /FontType get 1 eq and}{pop false}ifelse {\nsave userdict/fbufstr 512 string put\n"
      << (int)(s.st_size / 512)
      << "{currentfile fbufstr readstring{pop}{clear currentfile\nclosefile/fontdownload/unexpectedEOF/.error cvx exec}ifelse}repeat\ncurrentfile "
      << (int)(s.st_size % 512)
      << " string readstring{pop}{clear currentfile\nclosefile/fontdownload/unexpectedEOF/.error cvx exec}ifelse\nrestore}if}if\n";

    rewind(f);
    char str[2048];
    while (1) {
      int r = fread(str, 1, 2048, f);
      if (r <= 0)
          break;
      w.print(str, r);
    }

    fclose(f);
}

}

Generated by  Doxygen 1.6.0   Back to index