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

t1rw.cc

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

/* t1rw.{cc,hh} -- Type 1 font reading and writing
 *
 * 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/t1rw.hh>
#include <efont/t1cs.hh>
#include <lcdf/straccum.hh>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
namespace Efont {

unsigned char Type1Reader::xvalue_store[257];
unsigned char *Type1Reader::xvalue = &Type1Reader::xvalue_store[1];

void
Type1Reader::static_initialize()
{
    if (xvalue['A'])
      return;
    // rely on static data being initialized to 0
    xvalue['0'] = 0;  xvalue['1'] = 1;  xvalue['2'] = 2;  xvalue['3'] = 3;
    xvalue['4'] = 4;  xvalue['5'] = 5;  xvalue['6'] = 6;  xvalue['7'] = 7;
    xvalue['8'] = 8;  xvalue['9'] = 9;
    xvalue['A'] = 10; xvalue['B'] = 11; xvalue['C'] = 12; xvalue['D'] = 13;
    xvalue['E'] = 14; xvalue['F'] = 15;
    xvalue['a'] = 10; xvalue['b'] = 11; xvalue['c'] = 12; xvalue['d'] = 13;
    xvalue['e'] = 14; xvalue['f'] = 15;
}


Type1Reader::Type1Reader()
    : _data(new unsigned char[DATA_SIZE]), _len(0), _pos(0),
      _ungot(-1), _eexec(false)
{
    static_initialize();
}

Type1Reader::~Type1Reader()
{
    delete[] _data;
}


void
Type1Reader::set_charstring_definer(PermString x)
{
    _charstring_definer = x;
}

void
Type1Reader::switch_eexec(bool on, unsigned char *data, int len)
{
    if (on) {
      if (_pos < len + 3) {
          unsigned char *new_data = new unsigned char[len + 3 + DATA_SIZE];
          assert(_len <= DATA_SIZE);
          memcpy(new_data + len + 3, _data + _pos, _len - _pos);
          _len = len + 3 + _len - _pos;
          _pos = len + 3;
          delete[] _data;
          _data = new_data;
      }
      int original_pos = _pos;
      // don't forget _ungot!!
      if (_ungot >= 0)
          _data[--_pos] = _ungot, _ungot = -1;
      if (_crlf == 0 || _crlf == 2)
          _data[--_pos] = '\n';
      if (_crlf == 1 || _crlf == 2)
          _data[--_pos] = '\r';
      memcpy(_data + _pos - len, data, len);
      _pos -= len;
      start_eexec(original_pos - _pos);
    }
    _eexec = on;
}


int
Type1Reader::more_data()
{
    _pos = 0;
    _len = more_data(_data, DATA_SIZE);
    if (_len < 0)
      return -1;
    else
      return _data[_pos++];
}


inline int
Type1Reader::get_base()
{
    if (_pos >= _len)
      return more_data();
    else
      return _data[_pos++];
}


inline int
Type1Reader::eexec(int c)
{
    unsigned char answer = (unsigned char)(c ^ (_r >> 8));
    _r = (((unsigned char)c + _r) * t1C1 + t1C2) & 0xFFFF;
    return answer;
}


int
Type1Reader::ascii_eexec_get()
{
    int d1 = get_base();
    while (isspace(d1))
      d1 = get_base();
  
    int d2 = get_base();
    while (isspace(d2))
      d2 = get_base();
    if (d2 < 0)
      return -1;
  
    return eexec((xvalue[d1] << 4) | (xvalue[d2]));
}


inline int
Type1Reader::get()
{
    if (!_eexec)
      return get_base();
  
    else if (_binary_eexec) {
      int c = get_base();
      return c < 0 ? c : eexec(c);
    
    } else
      return ascii_eexec_get();
}


void
Type1Reader::start_eexec(int initial_ascii)
{
    /* God damn this _ungot thing!! It makes sense not to check it on every
       char fetch, since it can only be set at the end of next_line; therefore,
       only the first get() into a user-visible function might need to check
       _ungot. The problem is I forgot start_eexec() was such a function! */
    int c = _ungot < 0 ? get_base() : _ungot;
    initial_ascii--;
    _ungot = -1;
  
    /* Strictly speaking, I should look for whitespace even in binary sections
       of PFB fonts; but it turns out some PFBs would be unreadable with that
       pedantic rule. */
    while (isspace(c) && (initial_ascii >= 0 || !preserve_whitespace()))
      c = get_base(), initial_ascii--;
  
    /* Differentiate between ASCII eexec and binary eexec. */
    int rand_bytes[4];
    _binary_eexec = false;
    for (int i = 0; i < 4; i++) {
      if (i)
          c = get_base();
      rand_bytes[i] = c;
      if (!isxdigit(c))
          _binary_eexec = true;
    }
  
    _r = t1R_ee;
    if (_binary_eexec)
      for (int i = 0; i < 4; i++)
          eexec(rand_bytes[i]);
    else {
      for (int i = 0; i < 2; i++) {
          c = xvalue[rand_bytes[i*2]] * 16 + xvalue[rand_bytes[i*2+1]];
          eexec(c);
      }
      ascii_eexec_get();
      ascii_eexec_get();
    }
}


bool
Type1Reader::test_charstring(StringAccum &str)
{
    /* PERFORMANCE NOTE: This function is definitely a bottleneck. Making it
       more efficient cut time by about 40%. */
  
    if (!_charstring_definer)
      return false;
    if (_charstring_len >= 0)
      return str.length() <= _charstring_start + _charstring_len;
  
    str.append('\0');         // protect against running off end of string
    char *s = str.data();
    char *start;
    while (*s == ' ')
      s++;
  
    if (s[0] == '/')
      s++;
    else if (s[0] == 'd' && s[1] == 'u' && s[2] == 'p' && isspace(s[3])) {
      s += 4;
      // 17.Jan.2000 -- some fonts have extra space here.
      while (isspace(*s))
          s++;
    } else
      goto not_charstring;

    // Force one literal space rather than isspace().
    // Why? Consistency: we force 1 literal space around _charstring_definer.
    while (*s != ' ' && *s)
      s++;
    if (*s++ != ' ' || !isdigit(*s))
      goto not_charstring;
    start = s;
    s++;
    while (*s != ' ' && *s)
      s++;
    if (strncmp(s, _charstring_definer.c_str(), _charstring_definer.length()) != 0)
      goto not_charstring;
  
    _charstring_len = strtol(start, 0, 10);
    _charstring_start = (s - str.data()) + _charstring_definer.length();
    str.pop_back();
    return str.length() <= _charstring_start + _charstring_len;
  
  not_charstring:
    str.pop_back();
    return false;
}


/* PERFORMANCE NOTE: All kinds of speedup tricks tried with eexec -- buffering
   (doing eexec processing at more_data time), etc., and all this stuff -- and
   found to have essentially zero effect for PFB fonts. Now, it might be
   effective for PFAs; but who cares? I hate PFAs. */

bool
Type1Reader::next_line(StringAccum &s)
{
    if (_len < 0)
      return false;
    
    // Can't be a charstring if incoming accumulator has nonzero length.
    _charstring_start = 0;
    _charstring_len = (s.length() > 0 ? 0 : -1);

    int first_char = _ungot < 0 ? get() : _ungot;
    _ungot = -1;

    for (int c = first_char; c >= 0; c = get())
      switch (c) {
      
        case '\n':
          if (test_charstring(s))
            goto normal;
          else {
            _crlf = 0;
            goto done;
          }
      
        case '\r':
          // check for \r\n (counts as only one line ending)
          if (test_charstring(s))
            goto normal;
          c = get();
          if (c != '\n' || preserve_whitespace())
            _ungot = c, _crlf = 1;
          else
            _crlf = 2;
          goto done;
      
        normal:
        default:
          s.append((char)c);
          break;
      
      }
  
  done:
    return true;
}


int
Type1Reader::get_data(unsigned char *data, int len)
{
    if (_len < 0)
      return -1;
    if (len <= 0)
      return 0;

    int pos = 0;
    if (_ungot >= 0) {
      *data++ = _ungot;
      pos++;
      _ungot = -1;
    }

    for (; pos < len; pos++) {
      int c = get();
      if (c < 0)
          break;
      *data++ = c;
    }

    return pos;
}


/*****
 * Type1PFAReader
 **/

Type1PFAReader::Type1PFAReader(FILE *f)
    : _f(f)
{
}

int
Type1PFAReader::more_data(unsigned char *data, int len)
{
    size_t size = fread(data, 1, len, _f);
    return size ? (int)size : -1;
}


/*****
 * Type1PFBReader
 **/

Type1PFBReader::Type1PFBReader(FILE *f)
    : _f(f), _binary(false), _left(0)
{
}

int
Type1PFBReader::more_data(unsigned char *data, int len)
{
    while (_left == 0) {
      int c = getc(_f);
      if (c != 128)
          return -1;
    
      c = getc(_f);
      if (c == 3 || c < 1 || c > 3)
          return -1;
      _binary = (c == 2);
    
      _left = getc(_f);
      _left |= getc(_f) << 8;
      _left |= getc(_f) << 16;
      _left |= getc(_f) << 24;
    }
  
    if (_left < 0)
      return -1;
  
    if (len > _left)
      len = _left;
    _left -= len;
  
    return fread(data, 1, len, _f);
}

bool
Type1PFBReader::preserve_whitespace() const
{
    return _binary;
}


/*****
 * Type1SubsetReader
 **/

Type1SubsetReader::Type1SubsetReader(Type1Reader *r, int left)
    : _reader(r), _left(left)
{
}

int
Type1SubsetReader::more_data(unsigned char *data, int len)
{
    if (_left <= 0)
      return -1;
    if (len > _left)
      len = _left;
    int how_much = _reader->get_data(data, len);
    if (how_much > 0)
      _left -= how_much;
    return how_much;
}

bool
Type1SubsetReader::preserve_whitespace() const
{
    return _reader->preserve_whitespace();
}


/*****
 * Writer
 **/

Type1Writer::Type1Writer()
    : _buf(new unsigned char[BufSize]), _pos(0),
      _eexec(false), _eexec_start(-1), _eexec_end(-1), _lenIV(4)
{
}


Type1Writer::~Type1Writer()
{
    assert(!_pos);
    delete[] _buf;
}


inline unsigned char
Type1Writer::eexec(int p)
{
    unsigned char c = ((unsigned char)p ^ (_r >> 8)) & 0xFF;
    _r = ((c + _r) * t1C1 + t1C2) & 0xFFFF;
    return c;
}


void
Type1Writer::flush()
{
    local_flush();
}


/* PERFORMANCE NOTE: Doing eexec processing during flush -- which streamlines
   code for the print() methods -- seems to save some time (4-5%). It also
   makes write performance more consistent. But I have mixed feelings. */

void
Type1Writer::local_flush()
{
    if (_eexec_start >= 0 && _eexec_end < 0)
      _eexec_end = _pos;
    for (int p = _eexec_start; p < _eexec_end; p++)
      _buf[p] = eexec(_buf[p]);
    print0(_buf, _pos);
    _pos = 0;
    _eexec_start = _eexec ? 0 : -1;
    _eexec_end = -1;
}


void
Type1Writer::print(const char *s, int n)
{
    while (n > 0) {
      if (_pos >= BufSize)
          local_flush();
      int copy = BufSize - _pos;
      if (copy > n) copy = n;
      memcpy(_buf + _pos, s, copy);
      _pos += copy;
      s += copy;
      n -= copy;
    }
}


Type1Writer &
Type1Writer::operator<<(int x)
{
    char str[128];
    sprintf(str, "%d", x);
    print(str, strlen(str));
    return *this;
}


Type1Writer &
Type1Writer::operator<<(double x)
{
    char str[256];
    sprintf(str, "%g", x);
    print(str, strlen(str));
    return *this;
}


void
Type1Writer::switch_eexec(bool on)
{
    _eexec = on;
    if (_eexec) {
      _eexec_start = _pos;
      _r = t1R_ee;
      print("SUCK", 4);
    } else
      _eexec_end = _pos;
}


/*****
 * Type1PFAWriter
 **/

Type1PFAWriter::Type1PFAWriter(FILE *f)
    : _f(f), _hex_line(0)
{
}

Type1PFAWriter::~Type1PFAWriter()
{
    flush();
}


void
Type1PFAWriter::switch_eexec(bool on)
{
    flush();
    _hex_line = 0;
    Type1Writer::switch_eexec(on);
}

void
Type1PFAWriter::print0(const unsigned char *c, int l)
{
    if (eexecing()) {
      const char *hex = "0123456789ABCDEF";
      for (; l; c++, l--) {
          putc(hex[*c / 16], _f);
          putc(hex[*c % 16], _f);
          if (++_hex_line == 39) {
            putc('\n', _f);
            _hex_line = 0;
          }
      }
    } else
      fwrite(c, 1, l, _f);
}


/*****
 * Type1PFBWriter
 **/

Type1PFBWriter::Type1PFBWriter(FILE *f)
    : _f(f), _binary(false)
{
}

Type1PFBWriter::~Type1PFBWriter()
{
    flush();
    fputc(128, _f);
    fputc(3, _f);
}

void
Type1PFBWriter::flush()
{
    Type1Writer::flush();
    if (_save.length()) {
      fputc(128, _f);
      fputc(_binary ? 2 : 1, _f);
      long l = _save.length();
      fputc(l & 0xFF, _f);
      fputc((l >> 8) & 0xFF, _f);
      fputc((l >> 16) & 0xFF, _f);
      fputc((l >> 24) & 0xFF, _f);
      fwrite(_save.data(), 1, _save.length(), _f);
      _save.clear();
    }
}

void
Type1PFBWriter::switch_eexec(bool on)
{
    flush();
    Type1Writer::switch_eexec(on);
    _binary = on;
}

void
Type1PFBWriter::print0(const unsigned char *c, int l)
{
    char *m = _save.extend(l);
    memcpy(m, c, l);
}

}

Generated by  Doxygen 1.6.0   Back to index