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

afmparse.cc

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

/* afmparse.{cc,hh} -- Adobe Font Metrics parsing
 *
 * 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/afmparse.hh>
#include <efont/t1cs.hh>
#include <efont/metrics.hh>
#include <lcdf/strtonum.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdlib.h>
#ifdef BROKEN_STRTOD
# define strtod good_strtod
#endif
namespace Efont {

static bool name_enders[256];
static char xvalue[256];


AfmParser::AfmParser(Slurper &slurp)
    : _slurper(slurp),
      _line(0), _pos(0), _length(0)
{
    static_initialize();
}


void
AfmParser::trim_end()
{
    int l = _length - 1;
    while (l >= 0 && isspace(_line[l])) {
      _line[l] = 0;
      l--;
    }
    _length = l + 1;
}


void
AfmParser::static_initialize()
{
    if (name_enders[(uint8_t)'('])
      return;
    name_enders[(uint8_t)' '] = name_enders[(uint8_t)'\t']
      = name_enders[(uint8_t)'\r'] = name_enders[(uint8_t)'\f']
      = name_enders[(uint8_t)'\v'] = name_enders[(uint8_t)'\n']
      = name_enders[(uint8_t)'\0'] = name_enders[(uint8_t)'[']
      = name_enders[(uint8_t)']'] = name_enders[(uint8_t)'/']
      = name_enders[(uint8_t)'('] = name_enders[(uint8_t)')']
      = name_enders[(uint8_t)';'] = true;

    xvalue[(uint8_t)'0'] = 0;
    xvalue[(uint8_t)'1'] = 1;
    xvalue[(uint8_t)'2'] = 2;
    xvalue[(uint8_t)'3'] = 3;
    xvalue[(uint8_t)'4'] = 4;
    xvalue[(uint8_t)'5'] = 5;
    xvalue[(uint8_t)'6'] = 6;
    xvalue[(uint8_t)'7'] = 7;
    xvalue[(uint8_t)'8'] = 8;
    xvalue[(uint8_t)'9'] = 9;
    xvalue[(uint8_t)'a'] = 10;
    xvalue[(uint8_t)'b'] = 11;
    xvalue[(uint8_t)'c'] = 12;
    xvalue[(uint8_t)'d'] = 13;
    xvalue[(uint8_t)'e'] = 14;
    xvalue[(uint8_t)'f'] = 15;
    xvalue[(uint8_t)'A'] = 10;
    xvalue[(uint8_t)'B'] = 11;
    xvalue[(uint8_t)'C'] = 12;
    xvalue[(uint8_t)'D'] = 13;
    xvalue[(uint8_t)'E'] = 14;
    xvalue[(uint8_t)'F'] = 15;
}


// isall == is total == is all of the string

// %s   whitespace-terminated PermString - non-zero-length
// %/s  name-terminated PermString - non-zero-length
// %+s  rest of line PermString
// %= rest of line/until next `=' PermString; beginning & ending spaces
//    elided
// %d   integer
// %g   number
// %x   hex integer
// %b   boolean
// %c   character
// %<   hex string; argument is String *stored
// %.   any non-alphanumeric
// ???????????? EOS vs. don't-skip-to-EOS ???????????


#define FAIL(s)         do { _message = s; return 0; } while (0)

unsigned char *
AfmParser::vis(const char *formatsigned, va_list valist)
{
    const unsigned char *format = (const unsigned char *)formatsigned;
    unsigned char *str = _pos;
  
    // Oftentimes, we'll get a keyword first. So handle that simple case
    // with a tight loop for (possibly) better performance and slightly different
    // semantics. (A keyword that comes first in the format must be followed
    // by a nonalphanumeric in the input to match.)
    if (isalpha(*format)) {
      bool ok = true;
      for (; *format && *format != ' ' && ok; format++, str++) {
          assert(isalnum(*format));
          if (*format != *str) ok = false;
      }
      if (!ok || isalnum(*str)) {
          if (_message) return 0;
          _fail_field = -1;
          FAIL("keyword mismatch");
      }
    }
  
    int fplus, fslash;
    _fail_field = 0;
    _message = PermString();
  
    while (1) {
    
      switch (*format) {
      
          /* - - - - - - - - percent specifications - - - - - - - */
        case '%':
          fplus = fslash = 0;
          _fail_field++;
      
        percentspec:
          switch (*++format) {
      
            // FLAGS
            case '+':
            fplus++;
            goto percentspec;
            case '/':
            fslash++;
            goto percentspec;
      
            case '%':
            goto matchchar;
      
            case 'b':
            case 's': {
              int len;
         
              if (fplus)
                  len = strlen((char *)str);
              else if (fslash) {
                  for (len = 0; !name_enders[ str[len] ]; len++)
                    ;
                  if (len == 0)
                    FAIL("should be a string");
              } else {
                  for (len = 0; !isspace(str[len]) && str[len]; len++)
                    ;
                  if (len == 0)
                    FAIL("should be a string");
              }
         
              PermString s = PermString((char *)str, len);
              str += len;
         
              // Now we have the string. What to do with it? Depends on format.
              if (*format == 'b') {
           
                  bool *bstore = va_arg(valist, bool *);
                  if (s == "true") {
                    if (bstore) *bstore = 1;
                  } else if (s == "false") {
                    if (bstore) *bstore = 0;
                  } else
                    FAIL("should be `true' or `false'");
           
              } else {
                  PermString *sstore = va_arg(valist, PermString *);
                  if (sstore) *sstore = s;
              }
              break;
            }
       
            case '(': {
              if (*str++ != '(') FAIL("should be a parenthesized string");
              unsigned char *last = str;
              int paren_level = 0;
              while (*last && paren_level >= 0) {
                  if (*last == '(') paren_level++;
                  if (*last == ')') paren_level--;
                  last++;
              }
              if (paren_level >= 0) FAIL("had unbalanced parentheses");
         
              PermString *sstore = va_arg(valist, PermString *);
              if (sstore) *sstore = PermString((char *)str, last - str - 1);
              str = last;
              break;
            }
       
            case 'd':
            case 'i': {
              unsigned char *new_str;
              int v = strtol((char *)str, (char **)&new_str, 10);
              if (new_str == str) FAIL("should be an integer");
         
              str = new_str;
              int *istore = va_arg(valist, int *);
              if (istore) *istore = v;
              break;
            }
       
            case 'x': {
              unsigned char *new_str;
              int v = strtol((char *)str, (char **)&new_str, 16);
              if (new_str == str) FAIL("should be a hex integer");
         
              str = new_str;
              int *istore = va_arg(valist, int *);
              if (istore) *istore = v;
              break;
            }
       
            case 'e':
            case 'f':
            case 'g': {
              unsigned char *new_str;
              double v = strtonumber((char *)str, (char **)&new_str);
              if (v < MIN_KNOWN_DOUBLE) v = MIN_KNOWN_DOUBLE;
              if (new_str == str) FAIL("should be a real number");
         
              str = new_str;
              double *dstore = va_arg(valist, double *);
              if (dstore) *dstore = v;
              break;
            }
       
            case '<': {
              unsigned char *endbrack =
                  (unsigned char *)strchr((char *)str, '>');
              int n = (endbrack ? endbrack - (str + 1) : 0);
              if (!endbrack || n % 2 != 0)
                  FAIL("should be hex values in <angle brackets>");
              String s = String::garbage_string(n/2);
              unsigned char *data = s.mutable_udata();
         
              str++;
              while (*str != '>') {
                  if (isxdigit(str[0]) && isxdigit(str[1])) {
                    *data++ = xvalue[str[0]] * 16 + xvalue[str[1]];
                    str += 2;
                  } else
                    FAIL("had non-hex digits in the angle brackets");
              }
              str++;
         
              if (String *datastore = va_arg(valist, String *))
                  *datastore = s;
              break;
            }

            default:
            assert(0 && "internal error: bad %");
            FAIL("");
      
          }
          break;
          /* - - - - - - - - end percent specifications - - - - - - - */
      
        case ' ':
          if (!isspace(*str))
            FAIL("should be followed by whitespace");
          /* FALLTHRU */
      
        case '-':
          while (isspace(*str)) str++;
          break;
      
        case 0:
          // always eat space at end of format
          while (isspace(*str)) str++;
          return str;
      
        default:
        matchchar:
          if (*str++ != *format)
            FAIL(permprintf("- expected `%c'", *format));
          break;
      
      }
    
      format++;
    }
}


bool
AfmParser::isall(const char *format, ...)
{
    va_list valist;
    va_start(valist, format);
    unsigned char *new_pos = vis(format, valist);
    va_end(valist);
    if (new_pos && *new_pos == 0) {
      _pos = new_pos;
      return 1;
    } else
      return 0;
}


bool
AfmParser::is(const char *format, ...)
{
    va_list valist;
    va_start(valist, format);
    unsigned char *new_pos = vis(format, valist);
    va_end(valist);
    if (new_pos) {
      _pos = new_pos;
      return 1;
    } else
      return 0;
}


PermString
AfmParser::keyword() const
{
    const char *f = (const char *)_pos;
    while (isspace(*f))
      f++;
    const char *l = f;
    while (isalnum(*l) || *l == '_')
      l++;
    return PermString(f, l - f);
}


void
AfmParser::skip_until(unsigned char c)
{
    while (*_pos && *_pos != c)
      _pos++;
}

}

Generated by  Doxygen 1.6.0   Back to index