diff options
Diffstat (limited to 'contrib/groff/hpftodit/hpftodit.cc')
-rw-r--r-- | contrib/groff/hpftodit/hpftodit.cc | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/contrib/groff/hpftodit/hpftodit.cc b/contrib/groff/hpftodit/hpftodit.cc new file mode 100644 index 0000000..c11f8cbb --- /dev/null +++ b/contrib/groff/hpftodit/hpftodit.cc @@ -0,0 +1,779 @@ +// -*- C++ -*- +/* Copyright (C) 1994 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff 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, or (at your option) any later +version. + +groff 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. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* +TODO +put human readable font name in device file +devise new names for useful characters +use --- for unnamed characters +option to specify symbol sets to look in +make it work with TrueType fonts +put filename in error messages (or fix lib) +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <errno.h> +#include "assert.h" +#include "lib.h" +#include "posix.h" +#include "errarg.h" +#include "error.h" +#include "cset.h" + +#define SIZEOF(v) (sizeof(v)/sizeof(v[0])) + +const int MULTIPLIER = 3; + +inline +int scale(int n) +{ + return n * MULTIPLIER; +} + +// tags in TFM file + +enum tag_type { + min_tag = 400, + type_tag = 400, + symbol_set_tag = 404, + msl_tag = 403, + inches_per_point_tag = 406, + design_units_per_em_tag = 408, + posture_tag = 409, + stroke_weight_tag = 411, + spacing_tag = 412, + slant_tag = 413, + appearance_width_tag = 414, + word_spacing_tag = 421, + x_height_tag = 424, + lower_ascent_tag = 427, + lower_descent_tag = 428, + width_tag = 433, + left_extent_tag = 435, + right_extent_tag = 436, + ascent_tag = 437, + descent_tag = 438, + pair_kern_tag = 439, + typeface_tag = 442, + max_tag = 443 + }; + +// types in TFM file + +enum { + ENUM_TYPE = 1, + BYTE_TYPE = 2, + USHORT_TYPE = 3, + FLOAT_TYPE = 5, + SIGNED_SHORT_TYPE = 17 + }; + + +typedef unsigned char byte; +typedef unsigned short uint16; +typedef short int16; +typedef unsigned int uint32; + +class File { +public: + File(const char *); + void skip(int n); + byte get_byte(); + uint16 get_uint16(); + uint32 get_uint32(); + void seek(uint32 n); +private: + unsigned char *buf_; + const unsigned char *ptr_; + const unsigned char *end_; +}; + +struct entry { + char present; + uint16 type; + uint32 count; + uint32 value; + entry() : present(0) { } +}; + +struct char_info { + uint16 msl; + uint16 width; + uint16 ascent; + int16 descent; + int16 left_extent; + uint16 right_extent; + uint16 symbol_set; + unsigned char code; +}; + +const uint16 NO_SYMBOL_SET = 0; + +struct name_list { + char *name; + name_list *next; + name_list(const char *s, name_list *p) : name(strsave(s)), next(p) { } + ~name_list() { a_delete name; } +}; + +struct symbol_set { + uint16 select; + uint16 index[256]; +}; + +#define SYMBOL_SET(n, c) ((n) * 32 + ((c) - 64)) + +uint16 text_symbol_sets[] = { + SYMBOL_SET(0, 'N'), // Latin 1 + SYMBOL_SET(6, 'J'), // Microsoft Publishing + SYMBOL_SET(2, 'N'), // Latin 2 + 0 + }; + +uint16 special_symbol_sets[] = { + SYMBOL_SET(8, 'M'), + SYMBOL_SET(5, 'M'), + SYMBOL_SET(15, 'U'), + 0 + }; + +entry tags[max_tag + 1 - min_tag]; + +char_info *char_table; +uint32 nchars; + +int msl_name_table_size = 0; +name_list **msl_name_table = 0; + +int n_symbol_sets; +symbol_set *symbol_set_table; + +static int special_flag = 0; +static int italic_flag = 0; +static int italic_sep; + +static void usage(); +static const char *xbasename(const char *); +static void read_tags(File &); +static void check_type(); +static void check_units(File &); +static int read_map(const char *); +static void require_tag(tag_type); +static void dump_tags(File &f); +static void output_spacewidth(); +static void output_pclweight(); +static void output_pclproportional(); +static void read_and_output_pcltypeface(File &); +static void output_pclstyle(); +static void output_slant(); +static void output_ligatures(); +static void read_symbol_sets(File &); +static void read_and_output_kernpairs(File &); +static void output_charset(); +static void read_char_table(File &f); + +inline +entry &tag_info(tag_type t) +{ + return tags[t - min_tag]; +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + + int opt; + int debug_flag = 0; + + while ((opt = getopt(argc, argv, "dsvi:")) != EOF) { + switch (opt) { + case 'd': + debug_flag = 1; + break; + case 's': + special_flag = 1; + break; + case 'i': + italic_flag = 1; + italic_sep = atoi(optarg); + break; + case 'v': + { + extern const char *version_string; + fprintf(stderr, "hpftodit version %s\n", version_string); + fflush(stderr); + break; + } + break; + case '?': + usage(); + default: + assert(0); + } + } + if (argc - optind != 3) + usage(); + File f(argv[optind]); + if (!read_map(argv[optind + 1])) + exit(1); + current_filename = 0; + current_lineno = -1; // no line numbers + if (freopen(argv[optind + 2], "w", stdout) == 0) + fatal("cannot open `%1': %2", argv[optind + 2], strerror(errno)); + current_filename = argv[optind]; + printf("name %s\n", xbasename(argv[optind + 2])); + if (special_flag) + printf("special\n"); + read_tags(f); + check_type(); + check_units(f); + if (debug_flag) + dump_tags(f); + read_char_table(f); + output_spacewidth(); + output_slant(); + read_and_output_pcltypeface(f); + output_pclproportional(); + output_pclweight(); + output_pclstyle(); + read_symbol_sets(f); + output_ligatures(); + read_and_output_kernpairs(f); + output_charset(); + return 0; +} + +static +void usage() +{ + fprintf(stderr, "usage: %s [-s] [-i n] tfm_file map_file output_font\n", + program_name); + exit(1); +} + +File::File(const char *s) +{ + int fd = open(s, O_RDONLY); + if (fd < 0) + fatal("cannot open `%1': %2", s, strerror(errno)); + current_filename = s; + struct stat sb; + if (fstat(fd, &sb) < 0) + fatal("cannot stat: %1", strerror(errno)); + if (!S_ISREG(sb.st_mode)) + fatal("not a regular file"); + buf_ = new unsigned char[sb.st_size]; + long nread = read(fd, buf_, sb.st_size); + if (nread < 0) + fatal("read error: %1", strerror(errno)); + if (nread != sb.st_size) + fatal("read unexpected number of bytes"); + ptr_ = buf_; + end_ = buf_ + sb.st_size; +} + +void File::skip(int n) +{ + if (end_ - ptr_ < n) + fatal("unexpected end of file"); + ptr_ += n; +} + +void File::seek(uint32 n) +{ + if (end_ - buf_ < n) + fatal("unexpected end of file"); + ptr_ = buf_ + n; +} + +byte File::get_byte() +{ + if (ptr_ >= end_) + fatal("unexpected end of file"); + return *ptr_++; +} + +uint16 File::get_uint16() +{ + if (end_ - ptr_ < 2) + fatal("unexpected end of file"); + uint16 n = *ptr_++; + return n + (*ptr_++ << 8); +} + +uint32 File::get_uint32() +{ + if (end_ - ptr_ < 4) + fatal("unexpected end of file"); + uint32 n = *ptr_++; + for (int i = 0; i < 3; i++) + n += *ptr_++ << (i + 1)*8; + return n; +} + +static +void read_tags(File &f) +{ + if (f.get_byte() != 'I' || f.get_byte() != 'I') + fatal("not an Intel format TFM file"); + f.skip(6); + uint16 ntags = f.get_uint16(); + entry dummy; + for (uint16 i = 0; i < ntags; i++) { + uint16 tag = f.get_uint16(); + entry *p; + if (min_tag <= tag && tag <= max_tag) + p = tags + (tag - min_tag); + else + p = &dummy; + p->present = 1; + p->type = f.get_uint16(); + p->count = f.get_uint32(); + p->value = f.get_uint32(); + } +} + +static +void check_type() +{ + require_tag(type_tag); + if (tag_info(type_tag).value != 0) { + if (tag_info(type_tag).value == 2) + fatal("cannot handle TrueType tfm files"); + fatal("unknown type tag %1", int(tag_info(type_tag).value)); + } +} + +static +void check_units(File &f) +{ + require_tag(design_units_per_em_tag); + f.seek(tag_info(design_units_per_em_tag).value); + uint32 num = f.get_uint32(); + uint32 den = f.get_uint32(); + if (num != 8782 || den != 1) + fatal("design units per em != 8782/1"); + require_tag(inches_per_point_tag); + f.seek(tag_info(inches_per_point_tag).value); + num = f.get_uint32(); + den = f.get_uint32(); + if (num != 100 || den != 7231) + fatal("inches per point not 100/7231"); +} + +static +void require_tag(tag_type t) +{ + if (!tag_info(t).present) + fatal("tag %1 missing", int(t)); +} + +static +void output_spacewidth() +{ + require_tag(word_spacing_tag); + printf("spacewidth %d\n", scale(tag_info(word_spacing_tag).value)); +} + +static +void read_symbol_sets(File &f) +{ + uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count; + n_symbol_sets = symbol_set_dir_length/14; + symbol_set_table = new symbol_set[n_symbol_sets]; + int i; + for (i = 0; i < n_symbol_sets; i++) { + f.seek(tag_info(symbol_set_tag).value + i*14); + (void)f.get_uint32(); + uint32 off1 = f.get_uint32(); + uint32 off2 = f.get_uint32(); + (void)f.get_uint16(); // what's this for? + f.seek(off1); + int j; + uint16 kind = 0; + for (j = 0; j < off2 - off1; j++) { + unsigned char c = f.get_byte(); + if ('0' <= c && c <= '9') + kind = kind*10 + (c - '0'); + else if ('A' <= c && c <= 'Z') + kind = kind*32 + (c - 64); + } + symbol_set_table[i].select = kind; + for (j = 0; j < 256; j++) + symbol_set_table[i].index[j] = f.get_uint16(); + } + for (i = 0; i < nchars; i++) + char_table[i].symbol_set = NO_SYMBOL_SET; + + uint16 *symbol_set_selectors = (special_flag + ? special_symbol_sets + : text_symbol_sets); + for (i = 0; symbol_set_selectors[i] != 0; i++) { + int j; + for (j = 0; j < n_symbol_sets; j++) + if (symbol_set_table[j].select == symbol_set_selectors[i]) + break; + if (j < n_symbol_sets) { + for (int k = 0; k < 256; k++) { + uint16 index = symbol_set_table[j].index[k]; + if (index != 0xffff + && char_table[index].symbol_set == NO_SYMBOL_SET) { + char_table[index].symbol_set = symbol_set_table[j].select; + char_table[index].code = k; + } + } + } + } +} + +static +void read_char_table(File &f) +{ + require_tag(msl_tag); + nchars = tag_info(msl_tag).count; + char_table = new char_info[nchars]; + + f.seek(tag_info(msl_tag).value); + uint32 i; + for (i = 0; i < nchars; i++) + char_table[i].msl = f.get_uint16(); + + require_tag(width_tag); + f.seek(tag_info(width_tag).value); + for (i = 0; i < nchars; i++) + char_table[i].width = f.get_uint16(); + + require_tag(ascent_tag); + f.seek(tag_info(ascent_tag).value); + for (i = 0; i < nchars; i++) { + char_table[i].ascent = f.get_uint16(); + } + + require_tag(descent_tag); + f.seek(tag_info(descent_tag).value); + for (i = 0; i < nchars; i++) { + char_table[i].descent = f.get_uint16(); + if (char_table[i].descent > 0) + char_table[i].descent = 0; + } + + require_tag(left_extent_tag); + f.seek(tag_info(left_extent_tag).value); + for (i = 0; i < nchars; i++) + char_table[i].left_extent = int16(f.get_uint16()); + + require_tag(right_extent_tag); + f.seek(tag_info(right_extent_tag).value); + for (i = 0; i < nchars; i++) + char_table[i].right_extent = f.get_uint16(); +} + +static +void output_pclweight() +{ + require_tag(stroke_weight_tag); + int stroke_weight = tag_info(stroke_weight_tag).value; + int pcl_stroke_weight; + if (stroke_weight < 128) + pcl_stroke_weight = -3; + else if (stroke_weight == 128) + pcl_stroke_weight = 0; + else if (stroke_weight <= 145) + pcl_stroke_weight = 1; + else if (stroke_weight <= 179) + pcl_stroke_weight = 3; + else + pcl_stroke_weight = 4; + printf("pclweight %d\n", pcl_stroke_weight); +} + +static +void output_pclproportional() +{ + require_tag(spacing_tag); + printf("pclproportional %d\n", tag_info(spacing_tag).value == 0); +} + +static +void read_and_output_pcltypeface(File &f) +{ + printf("pcltypeface "); + require_tag(typeface_tag); + f.seek(tag_info(typeface_tag).value); + for (uint32 i = 0; i < tag_info(typeface_tag).count; i++) { + unsigned char c = f.get_byte(); + if (c == '\0') + break; + putchar(c); + } + printf("\n"); +} + +static +void output_pclstyle() +{ + unsigned pcl_style = 0; + // older tfms don't have the posture tag + if (tag_info(posture_tag).present) { + if (tag_info(posture_tag).value) + pcl_style |= 1; + } + else { + require_tag(slant_tag); + if (tag_info(slant_tag).value != 0) + pcl_style |= 1; + } + require_tag(appearance_width_tag); + if (tag_info(appearance_width_tag).value < 100) // guess + pcl_style |= 4; + printf("pclstyle %d\n", pcl_style); +} + +static +void output_slant() +{ + require_tag(slant_tag); + int slant = int16(tag_info(slant_tag).value); + if (slant != 0) + printf("slant %f\n", slant/100.0); +} + +static +void output_ligatures() +{ + // don't use ligatures for fixed space font + require_tag(spacing_tag); + if (tag_info(spacing_tag).value != 0) + return; + static const char *ligature_names[] = { + "fi", "fl", "ff", "ffi", "ffl" + }; + + static const char *ligature_chars[] = { + "fi", "fl", "ff", "Fi", "Fl" + }; + + unsigned ligature_mask = 0; + int i; + for (i = 0; i < nchars; i++) { + uint16 msl = char_table[i].msl; + if (msl < msl_name_table_size + && char_table[i].symbol_set != NO_SYMBOL_SET) { + for (name_list *p = msl_name_table[msl]; p; p = p->next) + for (int j = 0; j < SIZEOF(ligature_chars); j++) + if (strcmp(p->name, ligature_chars[j]) == 0) { + ligature_mask |= 1 << j; + break; + } + } + } + if (ligature_mask) { + printf("ligatures"); + for (i = 0; i < SIZEOF(ligature_names); i++) + if (ligature_mask & (1 << i)) + printf(" %s", ligature_names[i]); + printf(" 0\n"); + } +} + +static +void read_and_output_kernpairs(File &f) +{ + if (tag_info(pair_kern_tag).present) { + printf("kernpairs\n"); + f.seek(tag_info(pair_kern_tag).value); + uint16 n_pairs = f.get_uint16(); + for (int i = 0; i < n_pairs; i++) { + uint16 i1 = f.get_uint16(); + uint16 i2 = f.get_uint16(); + int16 val = int16(f.get_uint16()); + if (char_table[i1].symbol_set != NO_SYMBOL_SET + && char_table[i2].symbol_set != NO_SYMBOL_SET + && char_table[i1].msl < msl_name_table_size + && char_table[i2].msl < msl_name_table_size) { + for (name_list *p = msl_name_table[char_table[i1].msl]; + p; + p = p->next) + for (name_list *q = msl_name_table[char_table[i2].msl]; + q; + q = q->next) + printf("%s %s %d\n", p->name, q->name, scale(val)); + } + } + } +} + +static +void output_charset() +{ + require_tag(slant_tag); + double slant_angle = int16(tag_info(slant_tag).value)*PI/18000.0; + double slant = sin(slant_angle)/cos(slant_angle); + + require_tag(x_height_tag); + require_tag(lower_ascent_tag); + require_tag(lower_descent_tag); + + printf("charset\n"); + int i; + for (i = 0; i < nchars; i++) { + uint16 msl = char_table[i].msl; + if (msl < msl_name_table_size + && msl_name_table[msl]) { + if (char_table[i].symbol_set != NO_SYMBOL_SET) { + printf("%s\t%d,%d", + msl_name_table[msl]->name, + scale(char_table[i].width), + scale(char_table[i].ascent)); + int depth = scale(- char_table[i].descent); + if (depth < 0) + depth = 0; + int italic_correction = 0; + int left_italic_correction = 0; + int subscript_correction = 0; + if (italic_flag) { + italic_correction = scale(char_table[i].right_extent + - char_table[i].width + + italic_sep); + if (italic_correction < 0) + italic_correction = 0; + subscript_correction = int((tag_info(x_height_tag).value + * slant * .8) + .5); + if (subscript_correction > italic_correction) + subscript_correction = italic_correction; + left_italic_correction = scale(italic_sep + - char_table[i].left_extent); + } + if (subscript_correction != 0) + printf(",%d,%d,%d,%d", + depth, italic_correction, left_italic_correction, + subscript_correction); + else if (left_italic_correction != 0) + printf(",%d,%d,%d", depth, italic_correction, left_italic_correction); + else if (italic_correction != 0) + printf(",%d,%d", depth, italic_correction); + else if (depth != 0) + printf(",%d", depth); + // This is fairly arbitrary. Fortunately it doesn't much matter. + unsigned type = 0; + if (char_table[i].ascent > (tag_info(lower_ascent_tag).value*9)/10) + type |= 2; + if (char_table[i].descent < (int16(tag_info(lower_descent_tag).value)*9)/10) + type |= 1; + printf("\t%d\t%d\n", + type, + char_table[i].symbol_set*256 + char_table[i].code); + for (name_list *p = msl_name_table[msl]->next; p; p = p->next) + printf("%s\t\"\n", p->name); + } + else + warning("MSL %1 not in any of the searched symbol sets", msl); + } + } +} + +static +void dump_tags(File &f) +{ + int i; + for (i = min_tag; i <= max_tag; i++) { + enum tag_type t = tag_type(i); + if (tag_info(t).present) { + fprintf(stderr, + "%d %d %d %d\n", i, tag_info(t).type, tag_info(t).count, + tag_info(t).value); + if (tag_info(t).type == FLOAT_TYPE + && tag_info(t).count == 1) { + f.seek(tag_info(t).value); + uint32 num = f.get_uint32(); + uint32 den = f.get_uint32(); + fprintf(stderr, "(%u/%u = %g)\n", num, den, (double)num/den); + } + } + } +} + +static +int read_map(const char *file) +{ + errno = 0; + FILE *fp = fopen(file, "r"); + if (!fp) { + error("can't open `%1': %2", file, strerror(errno)); + return 0; + } + current_filename = file; + char buf[512]; + current_lineno = 0; + while (fgets(buf, int(sizeof(buf)), fp)) { + current_lineno++; + char *ptr = buf; + while (csspace(*ptr)) + ptr++; + if (*ptr == '\0' || *ptr == '#') + continue; + ptr = strtok(ptr, " \n\t"); + if (!ptr) + continue; + int n; + if (sscanf(ptr, "%d", &n) != 1) { + error("bad map file"); + fclose(fp); + return 0; + } + if (n < 0) { + error("negative code"); + fclose(fp); + return 0; + } + if (n >= msl_name_table_size) { + size_t old_size = msl_name_table_size; + name_list **old_table = msl_name_table; + msl_name_table_size = n + 256; + msl_name_table = new name_list *[msl_name_table_size]; + if (old_table) { + memcpy(msl_name_table, old_table, old_size*sizeof(name_list *)); + a_delete old_table; + } + for (size_t i = old_size; i < msl_name_table_size; i++) + msl_name_table[i] = 0; + } + ptr = strtok(0, " \n\t"); + if (!ptr) { + error("missing names"); + fclose(fp); + return 0; + } + for (; ptr; ptr = strtok(0, " \n\t")) + msl_name_table[n] = new name_list(ptr, msl_name_table[n]); + } + fclose(fp); + return 1; +} + +static +const char *xbasename(const char *s) +{ + const char *b = strrchr(s, '/'); + return b ? b + 1 : s; +} |