/* maket1font.{cc,hh} -- translate CFF fonts to Type 1 fonts * * Copyright (c) 2002-2011 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 "maket1font.hh" #include #include #include #include #include #include #include using namespace Efont; typedef unsigned CsRef; enum { CSR_GLYPH = 0x00000000, CSR_SUBR = 0x80000000, CSR_GSUBR = 0xC0000000, CSR_TYPE = 0xC0000000, CSR_NUM = 0x3FFFFFFF }; class MakeType1CharstringInterp : public Type1CharstringGenInterp { public: MakeType1CharstringInterp(int precision = 5); ~MakeType1CharstringInterp(); Type1Font *output() const { return _output; } void run(const CharstringProgram *, Type1Font *, PermString glyph_definer, ErrorHandler *); void run(const CharstringContext &, Type1Charstring &, ErrorHandler *); bool type2_command(int, const uint8_t *, int *); String landmark(ErrorHandler *errh) const; class Subr; private: // output Type1Font *_output; bool _flex_message; // subroutines int _subr_bias; int _gsubr_bias; mutable Vector _glyphs; mutable Vector _subrs; mutable Vector _gsubrs; Subr *_cur_subr; int _cur_glyph; Subr *csr_subr(CsRef, bool force) const; Type1Charstring *csr_charstring(CsRef) const; }; class MakeType1CharstringInterp::Subr { public: Subr(CsRef csr) : _csr(csr), _output_subrno(-1), _stamp(0) { } bool up_to_date() const { return _stamp == max_stamp; } void update() { _stamp = max_stamp; } static void bump_date() { max_stamp++; } //String name(const MakeType1CharstringInterp *) const; Type1Charstring *charstring(const MakeType1CharstringInterp *) const; int ncalls() const { return _calls.size(); } Subr *call(int i) const { return _calls[i]; } bool has_call(Subr *) const; struct Caller { Subr *subr; int pos; int len; String s; Caller(Subr *s, int p, int l) : subr(s), pos(p), len(l) { } String charstring(MakeType1CharstringInterp *mcsi) const { return subr->charstring(mcsi)->substring(pos, len); } }; int ncallers() const { return _callers.size(); } const Caller &caller(int i) const { return _callers[i]; } void add_call(Subr *s) { _calls.push_back(s); } void add_caller(Subr *, int, int); int output_subrno() const { return _output_subrno; } void set_output_subrno(int n) { _output_subrno = n; } void transfer_nested_calls(int pos, int length, Subr *new_caller) const; void change_callers(Subr *, int pos, int length, int new_length); bool unify(MakeType1CharstringInterp *); void caller_data(bool assign, MakeType1CharstringInterp *mcsi); private: CsRef _csr; Vector _calls; Vector _callers; int _output_subrno; int _stamp; static int max_stamp; friend class MakeType1CharstringInterp; }; int MakeType1CharstringInterp::Subr::max_stamp = 1; inline void MakeType1CharstringInterp::Subr::add_caller(Subr *s, int pos, int len) { _callers.push_back(Caller(s, pos, len)); } bool MakeType1CharstringInterp::Subr::has_call(Subr *s) const { for (int i = 0; i < _calls.size(); i++) if (_calls[i] == s) return true; return false; } /***** * MakeType1CharstringInterp **/ MakeType1CharstringInterp::MakeType1CharstringInterp(int precision) : Type1CharstringGenInterp(precision), _flex_message(false) { } MakeType1CharstringInterp::~MakeType1CharstringInterp() { for (int i = 0; i < _glyphs.size(); i++) delete _glyphs[i]; for (int i = 0; i < _subrs.size(); i++) delete _subrs[i]; for (int i = 0; i < _gsubrs.size(); i++) delete _gsubrs[i]; } String MakeType1CharstringInterp::landmark(ErrorHandler *errh) const { if (_cur_glyph >= 0 && _cur_glyph < program()->nglyphs()) return errh->format("glyph %<%s%>", program()->glyph_name(_cur_glyph).c_str()); else return String(); } // subroutines MakeType1CharstringInterp::Subr * MakeType1CharstringInterp::csr_subr(CsRef csr, bool force) const { Vector *vp; if ((csr & CSR_TYPE) == CSR_SUBR) vp = &_subrs; else if ((csr & CSR_TYPE) == CSR_GSUBR) vp = &_gsubrs; else if ((csr & CSR_TYPE) == CSR_GLYPH) vp = &_glyphs; else return 0; int n = (csr & CSR_NUM); if (n >= vp->size()) return 0; Subr *&what = (*vp)[n]; if (!what && force) what = new Subr(csr); return what; } #if 0 String MakeType1CharstringInterp::Subr::name(const MakeType1CharstringInterp *mcsi) const { int n = (_csr & CSR_NUM); switch (_csr & CSR_TYPE) { case CSR_SUBR: return "subr" + String(n); case CSR_GSUBR: return "gsubr" + String(n); case CSR_GLYPH: return mcsi->output()->glyph_name(n); default: return ""; } } #endif Type1Charstring * MakeType1CharstringInterp::Subr::charstring(const MakeType1CharstringInterp *mcsi) const { int n = (_csr & CSR_NUM); switch (_csr & CSR_TYPE) { case CSR_SUBR: case CSR_GSUBR: if (_output_subrno >= 0) return static_cast(mcsi->output()->subr(_output_subrno)); return 0; case CSR_GLYPH: return static_cast(mcsi->output()->glyph(n)); default: return 0; } } void MakeType1CharstringInterp::Subr::transfer_nested_calls(int pos, int length, Subr *new_caller) const { int right = pos + length; for (int i = 0; i < _calls.size(); i++) { Subr *cs = _calls[i]; // 11.Jul.2006 - remember not to shift the new caller's records! (Michael Zedler) if (cs != new_caller) for (int j = 0; j < cs->_callers.size(); j++) { Caller &c = cs->_callers[j]; if (c.subr == this && pos <= c.pos && c.pos + c.len <= right) { // shift caller to point at the subroutine c.subr = new_caller; c.pos -= pos; new_caller->add_call(cs); } } } } void MakeType1CharstringInterp::Subr::change_callers(Subr *caller, int pos, int length, int new_length) { if (up_to_date()) return; update(); int right = pos + length; int delta = new_length - length; for (int i = 0; i < _callers.size(); i++) { Caller &c = _callers[i]; if (c.subr != caller) /* nada */; else if (pos <= c.pos && c.pos + c.len <= right) { // erase //fprintf(stderr, " ERASE caller %08x:%d+%d\n", c.subr->_csr, c.pos, c.len); c.subr = 0; } else if (right <= c.pos) { //fprintf(stderr, " ADJUST caller %08x:%d+%d -> %d+%d\n", c.subr->_csr, c.pos, c.len, c.pos+delta, c.len); c.pos += delta; } else if (c.pos <= pos && right <= c.pos + c.len) { c.len += delta; } else c.subr = 0; } } void MakeType1CharstringInterp::Subr::caller_data(bool assign, MakeType1CharstringInterp *mcsi) { for (int i = 0; i < _callers.size(); i++) { Caller &c = _callers[i]; if (!c.subr) continue; String s = c.subr->charstring(mcsi)->substring(c.pos, c.len); if (assign) c.s = s; else if (c.s != s) { //fprintf(stderr, "FAIL %08x caller %08x:%d+%d\n", _csr, c.subr->_csr, c.pos, c.len); //assert(0); } assert(c.subr->has_call(this)); } } bool MakeType1CharstringInterp::Subr::unify(MakeType1CharstringInterp *mcsi) { // clean up caller list for (int i = 0; i < _callers.size(); i++) if (!_callers[i].subr) { _callers[i] = _callers.back(); _callers.pop_back(); i--; } if (!_callers.size()) return false; assert(!_calls.size()); // because this hasn't been unified yet // Find the smallest shared substring. String substr = _callers[0].charstring(mcsi); int suboff = 0; for (int i = 1; i < _callers.size(); i++) { String substr1 = _callers[i].charstring(mcsi); const char *d = substr.data() + suboff, *d1 = substr1.data(); const char *dx = substr.data() + substr.length(), *d1x = d1 + substr1.length(); while (dx > d && d1x > d1 && dx[-1] == d1x[-1]) dx--, d1x--; suboff = dx - substr.data(); } substr = substr.substring(Type1Charstring(substr).first_caret_after(suboff)); if (!substr.length()) return false; for (int i = 0; i < _callers.size(); i++) { Caller &c = _callers[i]; if (int delta = c.len - substr.length()) { c.pos += delta; c.len -= delta; } } // otherwise, success _output_subrno = mcsi->output()->nsubrs(); mcsi->output()->set_subr(_output_subrno, Type1Charstring(substr + "\013")); // This subr has become real, so it is suitable for later unifications. // Mark it as having called any subroutines contained completely within itself. // How to do this? Look at one caller, and go over all its calls. _callers[0].subr->transfer_nested_calls(_callers[0].pos, _callers[0].len, this); // adapt callers String callsubr_string = Type1CharstringGen::callsubr_string(_output_subrno); for (int i = 0; i < _callers.size(); i++) // 13.Jun.2003 - must check whether _callers[i].subr exists: if we // called a subroutine more than once, change_callers() might have // zeroed it out. if (_callers[i].subr && _callers[i].subr != this) { Subr::Caller c = _callers[i]; c.subr->charstring(mcsi)->assign_substring(c.pos, c.len, callsubr_string); Subr::bump_date(); for (int j = 0; j < c.subr->ncalls(); j++) c.subr->call(j)->change_callers(c.subr, c.pos, c.len, callsubr_string.length()); assert(!_callers[i].subr); } // this subr is no longer "called"/interpolated from anywhere _callers.clear(); //fprintf(stderr, "Succeeded %x\n", _csr); return true; } // running bool MakeType1CharstringInterp::type2_command(int cmd, const uint8_t *data, int *left) { switch (cmd) { case Cs::cCallsubr: case Cs::cCallgsubr: if (subr_depth() < MAX_SUBR_DEPTH && size() == 1) { //fprintf(stderr, "succeeded %d\n", (int) top()); bool g = (cmd == Cs::cCallgsubr); CsRef csref = ((int)top() + program()->xsubr_bias(g)) | (g ? CSR_GSUBR : CSR_SUBR); Subr *callee = csr_subr(csref, true); if (callee) _cur_subr->add_call(callee); int left = csgen().length(); bool more = callxsubr_command(g); int right = csgen().length(); if (error() >= 0 && callee) callee->add_caller(_cur_subr, left, right - left); return more; } else { //fprintf(stderr, "failed %d\n", (int) top()); goto normal; } normal: default: return CharstringInterp::type2_command(cmd, data, left); } } void MakeType1CharstringInterp::run(const CharstringContext &g, Type1Charstring &out, ErrorHandler *errh) { Type1CharstringGenInterp::run(g, out); if (Type1CharstringGenInterp::bad_flex() && !_flex_message) { errh->lwarning(landmark(errh), "complex flex hint replaced with curves"); errh->message("(This Type 2 format font contains flex hints prohibited by Type 1.\nI%,ve safely replaced them with ordinary curves.)"); _flex_message = true; } } void MakeType1CharstringInterp::run(const CharstringProgram *program, Type1Font *output, PermString glyph_definer, ErrorHandler *errh) { _output = output; set_hint_replacement_storage(output); _glyphs.assign(program->nglyphs(), 0); _subrs.assign(program->nsubrs(), 0); _subr_bias = program->subr_bias(); _gsubrs.assign(program->ngsubrs(), 0); _gsubr_bias = program->gsubr_bias(); // run over the glyphs int nglyphs = program->nglyphs(); Type1Charstring receptacle; for (int i = 0; i < nglyphs; i++) { _cur_subr = _glyphs[i] = new Subr(CSR_GLYPH | i); _cur_glyph = i; run(program->glyph_context(i), receptacle, errh); #if 0 PermString n = program->glyph_name(i); if (n == "integral.disp") { fprintf(stderr, "%s was %s\n", n.c_str(), CharstringUnparser::unparse(*program->glyph(i)).c_str()); fprintf(stderr, "%s == %s\n", n.c_str(), CharstringUnparser::unparse(receptacle).c_str()); } #endif output->add_glyph(Type1Subr::make_glyph(program->glyph_name(i), receptacle, glyph_definer)); } // unify Subrs for (int i = 0; i < _subrs.size(); i++) if (_subrs[i]) _subrs[i]->unify(this); for (int i = 0; i < _gsubrs.size(); i++) if (_gsubrs[i]) _gsubrs[i]->unify(this); } /***** * main **/ static void add_number_def(Type1Font *output, int dict, PermString name, const Cff::Font *font, Cff::DictOperator op) { double v; if (font->dict_value(op, &v)) output->add_definition(dict, Type1Definition::make(name, v, "def")); } static void add_delta_def(Type1Font *output, int dict, PermString name, const Cff::Font *font, Cff::DictOperator op) { Vector vec; if (font->dict_value(op, vec)) { for (int i = 1; i < vec.size(); i++) vec[i] += vec[i - 1]; StringAccum sa; for (int i = 0; i < vec.size(); i++) sa << (i ? ' ' : '[') << vec[i]; sa << ']'; output->add_definition(dict, Type1Definition::make_literal(name, sa.take_string(), (dict == Type1Font::dP ? "|-" : "readonly def"))); } } Type1Font * create_type1_font(Cff::Font *font, ErrorHandler *errh) { String version = font->dict_string(Cff::oVersion); Type1Font *output = Type1Font::skeleton_make(font->font_name(), version); output->skeleton_comments_end(); StringAccum sa; // FontInfo dictionary if (version) output->add_definition(Type1Font::dFI, Type1Definition::make_string("version", version, "readonly def")); if (String s = font->dict_string(Cff::oNotice)) output->add_definition(Type1Font::dFI, Type1Definition::make_string("Notice", s, "readonly def")); if (String s = font->dict_string(Cff::oCopyright)) output->add_definition(Type1Font::dFI, Type1Definition::make_string("Copyright", s, "readonly def")); if (String s = font->dict_string(Cff::oFullName)) output->add_definition(Type1Font::dFI, Type1Definition::make_string("FullName", s, "readonly def")); if (String s = font->dict_string(Cff::oFamilyName)) output->add_definition(Type1Font::dFI, Type1Definition::make_string("FamilyName", s, "readonly def")); if (String s = font->dict_string(Cff::oWeight)) output->add_definition(Type1Font::dFI, Type1Definition::make_string("Weight", s, "readonly def")); double v; if (font->dict_value(Cff::oIsFixedPitch, &v)) output->add_definition(Type1Font::dFI, Type1Definition::make_literal("isFixedPitch", (v ? "true" : "false"), "def")); add_number_def(output, Type1Font::dFI, "ItalicAngle", font, Cff::oItalicAngle); add_number_def(output, Type1Font::dFI, "UnderlinePosition", font, Cff::oUnderlinePosition); add_number_def(output, Type1Font::dFI, "UnderlineThickness", font, Cff::oUnderlineThickness); output->skeleton_fontinfo_end(); // Encoding, other font dictionary entries output->add_item(font->type1_encoding_copy()); font->dict_value(Cff::oPaintType, &v); output->add_definition(Type1Font::dF, Type1Definition::make("PaintType", v, "def")); output->add_definition(Type1Font::dF, Type1Definition::make("FontType", 1.0, "def")); Vector vec; if (font->dict_value(Cff::oFontMatrix, vec) && vec.size() == 6) { sa << '[' << vec[0] << ' ' << vec[1] << ' ' << vec[2] << ' ' << vec[3] << ' ' << vec[4] << ' ' << vec[5] << ']'; output->add_definition(Type1Font::dF, Type1Definition::make_literal("FontMatrix", sa.take_string(), "readonly def")); } else output->add_definition(Type1Font::dF, Type1Definition::make_literal("FontMatrix", "[0.001 0 0 0.001 0 0]", "readonly def")); add_number_def(output, Type1Font::dF, "StrokeWidth", font, Cff::oStrokeWidth); add_number_def(output, Type1Font::dF, "UniqueID", font, Cff::oUniqueID); if (font->dict_value(Cff::oXUID, vec) && vec.size()) { for (int i = 0; i < vec.size(); i++) sa << (i ? ' ' : '[') << vec[i]; sa << ']'; output->add_definition(Type1Font::dF, Type1Definition::make_literal("XUID", sa.take_string(), "readonly def")); } if (font->dict_value(Cff::oFontBBox, vec) && vec.size() == 4) { sa << '{' << vec[0] << ' ' << vec[1] << ' ' << vec[2] << ' ' << vec[3] << '}'; output->add_definition(Type1Font::dF, Type1Definition::make_literal("FontBBox", sa.take_string(), "readonly def")); } else output->add_definition(Type1Font::dF, Type1Definition::make_literal("FontBBox", "{0 0 0 0}", "readonly def")); output->skeleton_fontdict_end(); // Private dictionary add_delta_def(output, Type1Font::dP, "BlueValues", font, Cff::oBlueValues); add_delta_def(output, Type1Font::dP, "OtherBlues", font, Cff::oOtherBlues); add_delta_def(output, Type1Font::dP, "FamilyBlues", font, Cff::oFamilyBlues); add_delta_def(output, Type1Font::dP, "FamilyOtherBlues", font, Cff::oFamilyOtherBlues); add_number_def(output, Type1Font::dP, "BlueScale", font, Cff::oBlueScale); add_number_def(output, Type1Font::dP, "BlueShift", font, Cff::oBlueShift); add_number_def(output, Type1Font::dP, "BlueFuzz", font, Cff::oBlueFuzz); if (font->dict_value(Cff::oStdHW, &v)) output->add_definition(Type1Font::dP, Type1Definition::make_literal("StdHW", String("[") + String(v) + "]", "|-")); if (font->dict_value(Cff::oStdVW, &v)) output->add_definition(Type1Font::dP, Type1Definition::make_literal("StdVW", String("[") + String(v) + "]", "|-")); add_delta_def(output, Type1Font::dP, "StemSnapH", font, Cff::oStemSnapH); add_delta_def(output, Type1Font::dP, "StemSnapV", font, Cff::oStemSnapV); if (font->dict_value(Cff::oForceBold, &v)) output->add_definition(Type1Font::dP, Type1Definition::make_literal("ForceBold", (v ? "true" : "false"), "def")); add_number_def(output, Type1Font::dP, "LanguageGroup", font, Cff::oLanguageGroup); add_number_def(output, Type1Font::dP, "ExpansionFactor", font, Cff::oExpansionFactor); add_number_def(output, Type1Font::dP, "UniqueID", font, Cff::oUniqueID); output->add_definition(Type1Font::dP, Type1Definition::make_literal("MinFeature", "{16 16}", "|-")); output->add_definition(Type1Font::dP, Type1Definition::make_literal("password", "5839", "def")); output->add_definition(Type1Font::dP, Type1Definition::make_literal("lenIV", "0", "def")); output->skeleton_private_end(); output->skeleton_common_subrs(); // add glyphs MakeType1CharstringInterp maker(5); maker.run(font, output, " |-", errh); return output; } #include