/* t1dotlessj.cc -- driver for creating dotlessj characters from Type 1 fonts * * Copyright (c) 2003-2009 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CTIME # include #endif #if defined(_MSDOS) || defined(_WIN32) # include # include #endif // also see otftotfm/automatic.cc enum { EXIT_NORMAL = 0, EXIT_DOTLESSJ_EXISTS = 1, EXIT_J_NODOT = 2, EXIT_NO_J = 3, EXIT_ERROR = 4 }; using namespace Efont; #define VERSION_OPT 301 #define HELP_OPT 302 #define QUIET_OPT 303 #define PFA_OPT 304 #define PFB_OPT 305 #define OUTPUT_OPT 306 #define NAME_OPT 307 const Clp_Option options[] = { { "help", 'h', HELP_OPT, 0, 0 }, { "name", 'n', NAME_OPT, Clp_ValString, 0 }, { "output", 'o', OUTPUT_OPT, Clp_ValString, 0 }, { "pfa", 'a', PFA_OPT, 0, 0 }, { "pfb", 'b', PFA_OPT, 0, 0 }, { "quiet", 'q', QUIET_OPT, 0, Clp_Negate }, { "version", 0, VERSION_OPT, 0, 0 }, }; static const char *program_name; void usage_error(ErrorHandler *errh, const char *error_message, ...) { va_list val; va_start(val, error_message); if (!error_message) errh->message("Usage: %s [OPTIONS] [FONTFILE [OUTPUTFILE]]", program_name); else errh->vxmessage(ErrorHandler::e_error, error_message, val); errh->message("Type %s --help for more information.", program_name); exit(EXIT_ERROR); } void usage() { FileErrorHandler uerrh(stdout); uerrh.message("\ % reads a PostScript Type 1 font, derives a new PostScript Type 1\n\ font containing just a dotlessj character (by chopping the dot from the j),\n\ and writes it to the standard output.\n\ \n\ Usage: %s [OPTIONS] [FONTFILE [OUTPUTFILE]]\n\ \n\ Options:\n\ -a, --pfa Output PFA font.\n\ -b, --pfb Output PFB font. This is the default.\n\ -o, --output=FILE Write output to FILE instead of standard output.\n\ -n, --name=NAME Set output font%,s PostScript name.\n\ -h, --help Print this message and exit.\n\ -q, --quiet Do not report errors to standard error.\n\ --version Print version number and exit.\n\ \n\ Report bugs to .\n", program_name); } // Sectioner class Sectioner : public Type1CharstringGenInterp { public: Sectioner(int precision); void act_line(int, const Point &, const Point &); void act_curve(int, const Point &, const Point &, const Point &, const Point &); void act_closepath(int); void act_flex(int, const Point &, const Point &, const Point &, const Point &, const Point &, const Point &, const Point &, double); void run(const CharstringContext &g); void undot(PermString, ErrorHandler *); Type1Charstring gen(Type1Font *); private: CharstringBounds _boundser; Vector _sections; Vector _bounds; }; Sectioner::Sectioner(int precision) : Type1CharstringGenInterp(precision) { set_direct_hint_replacement(true); } void Sectioner::act_line(int cmd, const Point &p0, const Point &p1) { Type1CharstringGenInterp::act_line(cmd, p0, p1); _boundser.act_line(cmd, p0, p1); } void Sectioner::act_curve(int cmd, const Point &p0, const Point &p1, const Point &p2, const Point &p3) { Type1CharstringGenInterp::act_curve(cmd, p0, p1, p2, p3); _boundser.act_curve(cmd, p0, p1, p2, p3); } void Sectioner::act_flex(int cmd, const Point &p0, const Point &p1, const Point &p2, const Point &p3_4, const Point &p5, const Point &p6, const Point &p7, double flex_depth) { Type1CharstringGenInterp::act_flex(cmd, p0, p1, p2, p3_4, p5, p6, p7, flex_depth); _boundser.act_flex(cmd, p0, p1, p2, p3_4, p5, p6, p7, flex_depth); } void Sectioner::act_closepath(int cmd) { Type1CharstringGenInterp::act_closepath(cmd); Type1Charstring result; Type1CharstringGenInterp::intermediate_output(result); _sections.push_back(result.data_string()); _bounds.resize(_bounds.size() + 4); int width_crap; _boundser.output(_bounds.end() - 4, width_crap); _boundser.clear(); } void Sectioner::run(const CharstringContext &g) { _boundser.clear(); Type1Charstring last_section; Type1CharstringGenInterp::run(g, last_section); _sections.push_back(last_section.data_string()); _bounds.resize(_bounds.size() + 4); int width_crap; _boundser.output(_bounds.end() - 4, width_crap); } void Sectioner::undot(PermString font_name, ErrorHandler *errh) { //for (String *s = _sections.begin(); s < _sections.end(); s++) // fprintf(stderr, "%d %s\n", s - _sections.begin(), CharstringUnparser::unparse(Type1Charstring(*s)).c_str()); if (_sections.size() < 3) errh->fatal("<%d>%s: % is already dotless", -EXIT_J_NODOT, font_name.c_str()); int topmost = -1; for (int i = 0; i < _sections.size() - 1; i++) if (topmost < 0 || _bounds[i*4 + 1] > _bounds[topmost*4 + 1]) topmost = i; // check if any sections are below this for (int i = 0; i < _sections.size() - 1; i++) if (_bounds[i*4 + 1] < _bounds[topmost*4 + 1]) goto found_below; errh->fatal("<%d>%s: % is already dotless", -EXIT_J_NODOT, font_name.c_str()); found_below: _sections[topmost] = String(); } Type1Charstring Sectioner::gen(Type1Font *font) { StringAccum sa; for (String *s = _sections.begin(); s < _sections.end(); s++) sa << *s; Type1Charstring in(sa.take_string()), out; Type1CharstringGenInterp gen(precision()); gen.set_hint_replacement_storage(font); gen.run(CharstringContext(program(), &in), out); return out; } // MAIN static Type1Font * do_file(const char *filename, PsresDatabase *psres, ErrorHandler *errh) { FILE *f; if (!filename || strcmp(filename, "-") == 0) { f = stdin; filename = ""; #if defined(_MSDOS) || defined(_WIN32) _setmode(_fileno(f), _O_BINARY); #endif } else f = fopen(filename, "rb"); if (!f) { // check for PostScript name Filename fn = psres->filename_value("FontOutline", filename); f = fn.open_read(); } if (!f) errh->fatal("<%d>%s: %s", -EXIT_ERROR, filename, strerror(errno)); Type1Reader *reader; int c = getc(f); ungetc(c, f); if (c == EOF) errh->fatal("<%d>%s: empty file", -EXIT_ERROR, filename); if (c == 128) reader = new Type1PFBReader(f); else reader = new Type1PFAReader(f); Type1Font *font = new Type1Font(*reader); if (!font->ok()) errh->fatal("<%d>%s: no glyphs in font", -EXIT_ERROR, filename); delete reader; return font; } int main(int argc, char *argv[]) { PsresDatabase *psres = new PsresDatabase; psres->add_psres_path(getenv("PSRESOURCEPATH"), 0, false); Clp_Parser *clp = Clp_NewParser(argc, (const char * const *)argv, sizeof(options) / sizeof(options[0]), options); program_name = Clp_ProgramName(clp); ErrorHandler *errh = ErrorHandler::static_initialize(new FileErrorHandler(stderr)); const char *input_file = 0; FILE *outputf = 0; const char *private_use_dotlessj = "uniF6BE"; bool binary = true; const char *font_name = 0; while (1) { int opt = Clp_Next(clp); switch (opt) { case QUIET_OPT: if (clp->negated) errh = ErrorHandler::default_handler(); else errh = new SilentErrorHandler; break; case NAME_OPT: font_name = clp->vstr; break; case PFA_OPT: binary = false; break; case PFB_OPT: binary = true; break; case OUTPUT_OPT: output_file: if (outputf) usage_error(errh, "output file specified twice"); if (strcmp(clp->vstr, "-") == 0) outputf = stdout; else if (!(outputf = fopen(clp->vstr, "wb"))) errh->fatal("<%d>%s: %s", -EXIT_ERROR, clp->vstr, strerror(errno)); break; case VERSION_OPT: printf("t1dotlessj (LCDF typetools) %s\n", VERSION); printf("Copyright (C) 2003-2009 Eddie Kohler\n\ This is free software; see the source for copying conditions.\n\ There is NO warranty, not even for merchantability or fitness for a\n\ particular purpose.\n"); exit(0); break; case HELP_OPT: usage(); exit(0); break; case Clp_NotOption: if (input_file && outputf) usage_error(errh, "too many arguments"); else if (input_file) goto output_file; else input_file = clp->vstr; break; case Clp_Done: goto done; case Clp_BadOption: usage_error(errh, 0); break; default: break; } } done: Type1Font *font = do_file(input_file, psres, errh); if (!input_file || strcmp(input_file, "-") == 0) input_file = ""; // check for existing dotlessj if (font->glyph("dotlessj")) errh->fatal("<%d>%s: already has a % glyph", -EXIT_DOTLESSJ_EXISTS, font->font_name().c_str()); else if (font->glyph("uni0237")) errh->fatal("<%d>%s: already has a dotlessj glyph at %", -EXIT_DOTLESSJ_EXISTS, font->font_name().c_str()); else if (font->glyph("u0237")) errh->fatal("<%d>%s: already has a dotlessj glyph at %", -EXIT_DOTLESSJ_EXISTS, font->font_name().c_str()); else if (private_use_dotlessj && font->glyph(private_use_dotlessj)) errh->fatal("<%d>%s: already has a dotlessj glyph at %<%s%>", -EXIT_DOTLESSJ_EXISTS, font->font_name().c_str(), private_use_dotlessj); // check for j Type1Charstring *j_cs = font->glyph("j"); if (!j_cs) j_cs = font->glyph("uni006A"); if (!j_cs) j_cs = font->glyph("u006A"); if (!j_cs) errh->fatal("<%d>%s: has no % glyph to make dotless", -EXIT_NO_J, font->font_name().c_str()); // make new font String actual_font_name = (font_name ? String(font_name) : font->font_name() + String("LCDFJ")); if (actual_font_name.length() > 29 && !font_name) { errh->warning("derived font name %<%s%> longer than 29 characters", actual_font_name.c_str()); errh->message("(Use the %<--name%> option to supply your own name.)"); } Vector xuid_extension; xuid_extension.push_back(0x00237237); Type1Font *dotless_font = Type1Font::skeleton_make_copy(font, actual_font_name, &xuid_extension); dotless_font->skeleton_common_subrs(); // copy space and .notdef if (Type1Charstring *notdef = font->glyph(".notdef")) dotless_font->add_glyph(Type1Subr::make_glyph(".notdef", *notdef, " |-")); if (Type1Charstring *space = font->glyph("space")) { dotless_font->add_glyph(Type1Subr::make_glyph("space", *space, " |-")); dotless_font->type1_encoding()->put(' ', "space"); } // create dotless j Sectioner sec(5); sec.run(CharstringContext(font, j_cs)); sec.undot(font->font_name(), errh); Type1Subr *dotlessj = Type1Subr::make_glyph("uni0237", sec.gen(dotless_font), " |-"); dotless_font->add_glyph(dotlessj); //fprintf(stderr, "! %s\n", CharstringUnparser::unparse(dotlessj->t1cs()).c_str()); // encode dotless j dotless_font->type1_encoding()->clear(); dotless_font->type1_encoding()->put('j', "uni0237"); // write it to output if (!outputf) outputf = stdout; if (binary) { #if defined(_MSDOS) || defined(_WIN32) _setmode(_fileno(outputf), _O_BINARY); #endif Type1PFBWriter w(outputf); dotless_font->write(w); } else { Type1PFAWriter w(outputf); dotless_font->write(w); } return (errh->nerrors() == 0 ? EXIT_NORMAL : EXIT_ERROR); }