summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/mmpfb/t1rewrit.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/mmpfb/t1rewrit.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/mmpfb/t1rewrit.cc924
1 files changed, 924 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/mmpfb/t1rewrit.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/mmpfb/t1rewrit.cc
new file mode 100644
index 00000000000..86382ca239b
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/mmpfb/t1rewrit.cc
@@ -0,0 +1,924 @@
+/* t1rewrit.cc -- routines for multiple- to single-master charstring conversion
+ *
+ * Copyright (c) 1997-2012 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 "t1rewrit.hh"
+#include <efont/t1item.hh>
+#include <efont/t1csgen.hh>
+#include <lcdf/error.hh>
+#include <lcdf/straccum.hh>
+#include <stdio.h>
+#include <math.h>
+
+using namespace Efont;
+
+static bool itc_complained = false;
+static ErrorHandler *itc_errh;
+
+void
+itc_complain()
+{
+ //itc_errh->warning("strange %<callothersubr%>; is this an ITC font?");
+ itc_complained = true;
+}
+
+
+/*****
+ * HintReplacementDetector
+ **/
+
+class HintReplacementDetector : public CharstringInterp { public:
+
+ HintReplacementDetector(Type1Font *, int);
+ HintReplacementDetector(Type1Font *, const Vector<double> &, int);
+
+ bool is_hint_replacement(int i) const { return _hint_replacements[i] != 0; }
+ int call_count(int i) const { return _call_counts[i]; }
+
+ bool type1_command(int);
+
+ bool run(Type1Font *, Type1Charstring &);
+
+ private:
+
+ Vector<int> _hint_replacements;
+ Vector<int> _call_counts;
+ int _subr_level;
+ int _count_calls_below;
+
+};
+
+HintReplacementDetector::HintReplacementDetector(Type1Font *f, int b)
+ : CharstringInterp(),
+ _hint_replacements(f->nsubrs(), 0), _call_counts(f->nsubrs(), 0),
+ _count_calls_below(b)
+{
+}
+
+HintReplacementDetector::HintReplacementDetector(Type1Font *f, const Vector<double> &wv, int b)
+ : CharstringInterp(wv),
+ _hint_replacements(f->nsubrs(), 0), _call_counts(f->nsubrs(), 0),
+ _count_calls_below(b)
+{
+}
+
+bool
+HintReplacementDetector::type1_command(int cmd)
+{
+ switch (cmd) {
+
+ case Cs::cCallothersubr: {
+ if (size() < 2)
+ goto unknown;
+ int command = (int)top(0);
+ int n = (int)top(1);
+ if (command == Cs::othcReplacehints && n == 1) {
+ pop(2);
+ _hint_replacements[(int)top()] = 1;
+ ps_clear();
+ ps_push(top());
+ pop();
+ break;
+ } else if (command >= Cs::othcMM1 && command <= Cs::othcMM6) {
+ pop(2);
+ return mm_command(command, n);
+ } else if (command >= Cs::othcITC_load && command <= Cs::othcITC_random) {
+ pop(2);
+ return itc_command(command, n);
+ } else
+ goto unknown;
+ }
+
+ case Cs::cCallsubr: {
+ if (size() < 1)
+ return error(errUnderflow, cmd);
+ int which = (int)pop();
+ if (!_count_calls_below || _subr_level < _count_calls_below)
+ _call_counts[which]++;
+
+ Charstring *subr_cs = get_subr(which);
+ if (!subr_cs)
+ return error(errSubr, which);
+
+ _subr_level++;
+ subr_cs->process(*this);
+ _subr_level--;
+
+ if (error() != errOK)
+ return false;
+ return !done();
+ }
+
+ case Cs::cEndchar:
+ case Cs::cReturn:
+ return CharstringInterp::type1_command(cmd);
+
+ case Cs::cBlend:
+ case Cs::cAbs:
+ case Cs::cAdd:
+ case Cs::cSub:
+ case Cs::cDiv:
+ case Cs::cNeg:
+ case Cs::cRandom:
+ case Cs::cMul:
+ case Cs::cSqrt:
+ case Cs::cDrop:
+ case Cs::cExch:
+ case Cs::cIndex:
+ case Cs::cRoll:
+ case Cs::cDup:
+ case Cs::cAnd:
+ case Cs::cOr:
+ case Cs::cNot:
+ case Cs::cEq:
+ case Cs::cIfelse:
+ return arith_command(cmd);
+
+ case Cs::cPop:
+ if (ps_size() >= 1)
+ push(ps_pop());
+ break;
+
+ default:
+ unknown:
+ clear();
+ break;
+
+ }
+ return true;
+}
+
+bool
+HintReplacementDetector::run(Type1Font *f, Type1Charstring &cs)
+{
+ _subr_level = 0;
+ CharstringInterp::interpret(f, &cs);
+ return error() == errOK;
+}
+
+
+/*****
+ * Type1OneMMRemover
+ **/
+
+class Type1OneMMRemover: public CharstringInterp { public:
+
+ Type1OneMMRemover(Type1MMRemover *);
+
+ bool type1_command(int);
+
+ inline bool run_fresh_subr(const Type1Charstring &, bool);
+ inline bool run_fresh_glyph(const Type1Charstring &);
+ inline bool rerun_subr(const Type1Charstring &);
+
+ Type1Charstring *output_prefix();
+ void output_main(Type1Charstring &);
+
+ private:
+
+ Type1MMRemover *_remover;
+ Type1CharstringGen _prefix_gen;
+ Type1CharstringGen _main_gen;
+
+ int _subr_level;
+ bool _in_subr;
+ bool _in_prefix;
+ bool _must_expand;
+
+ inline void run_subr(Type1Charstring *);
+ bool itc_command(int command, int on_stack);
+
+ bool run(const Type1Charstring &, bool, bool, bool);
+
+};
+
+/* For version 1.1
+ *
+ * Problem: Sometimes a charstring will call one subroutine, which will call
+ * another, etc., which finally does a multiple master CallOtherSubr! The
+ * required arguments might build up only gradually over all the subrs. This
+ * makes it hard to remove the eventual CallOtherSubr!
+ *
+ * Partial solution: Divide each subroutine into two parts: the initial
+ * "prefix" contains the closure of any initial multiple-master commands
+ * (including those from sub-subroutines), the following "main" part has all
+ * the other commands. In a situation like this:
+ *
+ * subr-1 = 8 15 callothersubr pop pop pop return
+ * subr-2 = 4 5 6 1 callsubr return
+ * subr-3 = 1 2 3 2 callsubr return
+ *
+ * we'll divide it up like this: (prefix || main)
+ *
+ * subr-1 = 8 15 callothersubr pop pop pop || (nothing)
+ * subr-2 = 4 5 6 8 15 callothersubr pop pop pop || (nothing)
+ * subr-3 = 1 2 3 4 5 6 8 15 callothersubr pop pop pop || (nothing)
+ *
+ * Now, when we call a subroutine, we EXECUTE its prefix part. Then, if its
+ * main part is nonempty, we output the original call to the subroutine to
+ * take care of the main part. */
+
+
+Type1OneMMRemover::Type1OneMMRemover(Type1MMRemover *remover)
+ : CharstringInterp(remover->weight_vector()),
+ _remover(remover), _prefix_gen(remover->precision()),
+ _main_gen(remover->precision())
+{
+}
+
+inline void
+Type1OneMMRemover::run_subr(Type1Charstring *cs)
+{
+ _subr_level++;
+ cs->process(*this);
+ _subr_level--;
+}
+
+bool
+Type1OneMMRemover::itc_command(int command, int on_stack)
+{
+ const Vector<double> &weight = weight_vector();
+ assert(weight.size());
+ Vector<double> *scratch = scratch_vector();
+ Type1CharstringGen *gen =
+ (_in_prefix ? &_prefix_gen : (_in_subr ? &_main_gen : 0));
+
+ int base = size() - on_stack - 2;
+ switch (command) {
+
+ case Cs::othcITC_load: {
+ if (on_stack != 1)
+ return false;
+ int offset = (int)at(base);
+ for (int i = 0; i < weight.size(); i++)
+ vec(scratch, offset+i) = weight.at_u(i);
+ // save load command, so we expand its effects into the scratch
+ // vector
+ if (gen) {
+ gen->gen_number(offset);
+ gen->gen_number(1);
+ gen->gen_number(Cs::othcITC_load);
+ gen->gen_command(Cs::cCallothersubr);
+ }
+ break;
+ }
+
+ case Cs::othcITC_put: {
+ if (on_stack != 2)
+ return false;
+ int offset = (int)at(base+1);
+ vec(scratch, offset) = at(base);
+ // save put command, so we expand its effects into the scratch
+ // vector
+ if (gen) {
+ gen->gen_number(at(base));
+ gen->gen_number(offset);
+ gen->gen_number(2);
+ gen->gen_number(Cs::othcITC_put);
+ gen->gen_command(Cs::cCallothersubr);
+ }
+ break;
+ }
+
+ case Cs::othcITC_get: {
+ if (on_stack != 1)
+ return false;
+ int offset = (int)at(base);
+ double d = vec(scratch, offset);
+ if (!KNOWN(d)) {
+ _must_expand = true;
+ return false;
+ }
+ ps_push(d);
+ break;
+ }
+
+ case Cs::othcITC_add: {
+ if (on_stack != 2)
+ return false;
+ ps_push(at(base) + at(base+1));
+ break;
+ }
+
+ case Cs::othcITC_sub: {
+ if (on_stack != 2)
+ return false;
+ ps_push(at(base) - at(base+1));
+ break;
+ }
+
+ case Cs::othcITC_mul: {
+ if (on_stack != 2)
+ return false;
+ ps_push(at(base) * at(base+1));
+ break;
+ }
+
+ case Cs::othcITC_div: {
+ if (on_stack != 2)
+ return false;
+ ps_push(at(base) / at(base+1));
+ break;
+ }
+
+ case Cs::othcITC_ifelse: {
+ if (on_stack != 4)
+ return false;
+ if (at(base+2) <= at(base+3))
+ ps_push(at(base));
+ else
+ ps_push(at(base+1));
+ break;
+ }
+
+ default:
+ return false;
+
+ }
+
+ pop(on_stack + 2);
+ return true;
+}
+
+bool
+Type1OneMMRemover::type1_command(int cmd)
+{
+ switch (cmd) {
+
+ case Cs::cCallothersubr: {
+ // Expand known othersubr calls. If we cannot expand the othersubr
+ // call completely, then write it to the expander.
+ if (size() < 2)
+ goto partial_othersubr;
+ int command = (int)top(0);
+ int n = (int)top(1);
+ if (command >= Cs::othcITC_load && command <= Cs::othcITC_random) {
+ if (!itc_complained)
+ itc_complain();
+ if (size() < 2 + n || !itc_command(command, n))
+ goto partial_othersubr;
+ } else if (command >= Cs::othcMM1 && command <= Cs::othcMM6) {
+ if (size() < 2 + n)
+ goto partial_othersubr;
+ pop(2);
+ mm_command(command, n);
+ } else
+ goto normal;
+ break;
+ }
+
+ partial_othersubr: {
+ if (!_in_prefix) {
+ _must_expand = true;
+ goto normal;
+ }
+ _prefix_gen.gen_stack(*this, 0);
+ _prefix_gen.gen_command(Cs::cCallothersubr);
+ break;
+ }
+
+ case Cs::cCallsubr: {
+ // expand subroutines in line if necessary
+ if (size() < 1)
+ goto normal;
+ int subrno = (int)pop();
+ if (_subr_level < 1) { // otherwise, have already included prefix
+ if (Type1Charstring *cs = _remover->subr_prefix(subrno))
+ run_subr(cs);
+ }
+ if (Type1Charstring *cs = _remover->subr_expander(subrno))
+ run_subr(cs);
+ else {
+ push(subrno);
+ goto normal;
+ }
+ break;
+ }
+
+ case Cs::cPop:
+ if (ps_size() >= 1)
+ push(ps_pop());
+ else if (_in_prefix && ps_size() == 0) {
+ _prefix_gen.gen_stack(*this, 0);
+ _prefix_gen.gen_command(Cs::cPop);
+ } else
+ goto normal;
+ break;
+
+ case Cs::cDiv:
+ if (size() < 2)
+ goto normal;
+ top(1) /= top(0);
+ pop();
+ break;
+
+ case Cs::cReturn:
+ return false;
+
+ normal:
+ default:
+ _main_gen.gen_stack(*this, cmd);
+ _main_gen.gen_command(cmd);
+ _in_prefix = 0;
+ return (cmd != Cs::cEndchar);
+
+ }
+ return true;
+}
+
+
+bool
+Type1OneMMRemover::run(const Type1Charstring &cs,
+ bool in_subr, bool do_prefix, bool fresh)
+{
+ _prefix_gen.clear();
+ _main_gen.clear();
+ _in_subr = in_subr;
+ _in_prefix = do_prefix;
+ _subr_level = (fresh ? 0 : 1);
+ _must_expand = false;
+ Vector<double> *scratch = scratch_vector();
+ scratch->assign(scratch->size(), UNKDOUBLE);
+
+ CharstringInterp::interpret(_remover->program(), &cs);
+
+ if (in_subr) {
+ _main_gen.gen_stack(*this, Cs::cReturn);
+ _main_gen.gen_command(Cs::cReturn);
+ }
+ if (_must_expand)
+ return true;
+ if (fresh && in_subr) {
+ if (_main_gen.length() == 0
+ || (_main_gen.length() == 1 && _main_gen.data()[0] == Cs::cReturn))
+ return true;
+ }
+ return false;
+}
+
+inline bool
+Type1OneMMRemover::run_fresh_subr(const Type1Charstring &cs, bool do_prefix)
+{
+ return run(cs, true, do_prefix, true);
+}
+
+inline bool
+Type1OneMMRemover::run_fresh_glyph(const Type1Charstring &cs)
+{
+ return run(cs, false, false, true);
+}
+
+inline bool
+Type1OneMMRemover::rerun_subr(const Type1Charstring &cs)
+{
+ return run(cs, true, false, false);
+}
+
+Type1Charstring *
+Type1OneMMRemover::output_prefix()
+{
+ if (_prefix_gen.length() > 0) {
+ _prefix_gen.gen_command(Cs::cReturn);
+ return _prefix_gen.output();
+ } else
+ return 0;
+}
+
+void
+Type1OneMMRemover::output_main(Type1Charstring &cs)
+{
+ _main_gen.output(cs);
+}
+
+
+/*****
+ * Type1BadCallRemover
+ **/
+
+class Type1BadCallRemover: public CharstringInterp { public:
+
+ Type1BadCallRemover(Type1MMRemover *);
+
+ bool type1_command(int);
+
+ bool run(Type1Charstring &, bool is_subr);
+
+ private:
+
+ Type1CharstringGen _gen;
+ Type1MMRemover *_remover;
+ bool _is_subr;
+
+};
+
+Type1BadCallRemover::Type1BadCallRemover(Type1MMRemover *remover)
+ : CharstringInterp(remover->weight_vector()),
+ _gen(remover->precision()), _remover(remover)
+{
+}
+
+bool
+Type1BadCallRemover::type1_command(int cmd)
+{
+ switch (cmd) {
+
+ case Cs::cCallsubr: {
+ if (size() < 1)
+ goto normal;
+ int subrno = (int)top();
+ if (!get_subr(subrno)) {
+ pop();
+ return false;
+ } else
+ goto normal;
+ }
+
+ normal:
+ default:
+ _gen.gen_stack(*this, 0);
+ _gen.gen_command(cmd);
+ return (cmd != Cs::cEndchar || _is_subr) && cmd != Cs::cReturn;
+
+ }
+}
+
+bool
+Type1BadCallRemover::run(Type1Charstring &cs, bool is_subr)
+{
+ _is_subr = is_subr;
+ _gen.clear();
+ CharstringInterp::interpret(_remover->program(), &cs);
+ _gen.output(cs);
+ return error() == errOK;
+}
+
+
+/*****
+ * Type1MMRemover
+ **/
+
+Type1MMRemover::Type1MMRemover(Type1Font *font, const Vector<double> &wv,
+ int precision, ErrorHandler *errh)
+ : _font(font), _weight_vector(wv), _precision(precision),
+ _nsubrs(font->nsubrs()),
+ _subr_done(_nsubrs, 0),
+ _subr_prefix(_nsubrs, (Type1Charstring *)0),
+ _must_expand_subr(_nsubrs, 0),
+ _hint_replacement_subr(_nsubrs, 0),
+ _expand_all_subrs(false), _errh(errh)
+{
+ itc_errh = _errh;
+
+ // find subroutines needed for hint replacement
+ HintReplacementDetector hr(font, wv, 0);
+ for (int i = 0; i < _font->nglyphs(); i++)
+ if (Type1Subr *g = _font->glyph_x(i))
+ hr.run(font, g->t1cs());
+ for (int i = 0; i < _nsubrs; i++)
+ if (hr.is_hint_replacement(i))
+ _hint_replacement_subr[i] = 1;
+
+ // don't get rid of first 4 subrs
+ for (int i = 0; i < _nsubrs && i < 4; i++)
+ _subr_done[i] = 1;
+}
+
+Type1MMRemover::~Type1MMRemover()
+{
+ for (int i = 0; i < _nsubrs; i++)
+ if (_subr_prefix[i])
+ delete _subr_prefix[i];
+}
+
+
+Type1Charstring *
+Type1MMRemover::subr_prefix(int subrno)
+{
+ if (subrno < 0 || subrno >= _nsubrs)
+ return 0;
+
+ if (!_subr_done[subrno]) {
+ _subr_done[subrno] = 1;
+
+ Type1Charstring *subr = _font->subr(subrno);
+ if (!subr)
+ return 0;
+
+ Type1OneMMRemover one(this);
+ if (one.run_fresh_subr(*subr, !_hint_replacement_subr[subrno]))
+ _must_expand_subr[subrno] = true;
+ _subr_prefix[subrno] = one.output_prefix();
+ one.output_main(*subr);
+ }
+
+ return _subr_prefix[subrno];
+}
+
+Type1Charstring *
+Type1MMRemover::subr_expander(int subrno)
+{
+ if (subrno < 0 || subrno >= _nsubrs)
+ return 0;
+ if (!_subr_done[subrno])
+ (void)subr_prefix(subrno);
+ if (!_expand_all_subrs && !_must_expand_subr[subrno])
+ return 0;
+ return _font->subr(subrno);
+}
+
+extern "C" {
+static int CDECL
+sort_permstring_compare(const void *v1, const void *v2)
+{
+ const PermString *s1 = (const PermString *)v1;
+ const PermString *s2 = (const PermString *)v2;
+ return strcmp(s1->c_str(), s2->c_str());
+}
+}
+
+void
+Type1MMRemover::run()
+{
+ Type1OneMMRemover one(this);
+
+ // check subroutines
+ for (int subrno = 0; subrno < _nsubrs; subrno++)
+ (void)subr_prefix(subrno);
+
+ // expand glyphs
+ Vector<PermString> bad_glyphs;
+ for (int i = 0; i < _font->nglyphs(); i++) {
+ Type1Subr *g = _font->glyph_x(i);
+ if (g) {
+ if (one.run_fresh_glyph(g->t1cs())) {
+ // Every glyph should be fully expandable without encountering
+ // a MM command. If we fail the first time, try again,
+ // expanding ALL subroutines. This catches, for example, SUBR
+ // 1 { 1 0 return }; GLYPH g { 1 callsubr 2 blend }; This will
+ // fail the first time, because `1 callsubr' will be left as a
+ // subroutine call, so `1 0' (required arguments to `blend')
+ // won't be visible.
+ _expand_all_subrs = true;
+ if (one.run_fresh_glyph(g->t1cs()))
+ bad_glyphs.push_back(g->name());
+ _expand_all_subrs = false;
+ }
+ one.output_main(g->t1cs());
+ }
+ }
+
+ // remove uncalled subroutines, expand hint replacement subroutines
+ HintReplacementDetector hr(_font, _weight_vector, 0);
+ for (int i = 0; i < _font->nglyphs(); i++)
+ if (Type1Subr *g = _font->glyph_x(i))
+ hr.run(_font, g->t1cs());
+ // don't remove first four subroutines!
+ for (int subrno = 4; subrno < _nsubrs; subrno++)
+ if (hr.call_count(subrno) || _hint_replacement_subr[subrno]) {
+ Type1Charstring *cs = _font->subr(subrno);
+ if (one.rerun_subr(*cs)) {
+ _expand_all_subrs = true;
+ if (one.rerun_subr(*cs))
+ bad_glyphs.push_back(permprintf("subr %d", subrno));
+ _expand_all_subrs = false;
+ }
+ one.output_main(*cs);
+ } else
+ _font->remove_subr(subrno);
+
+ // remove calls to removed subroutines
+ Type1BadCallRemover bcr(this);
+ for (int i = 0; i < _font->nglyphs(); i++)
+ if (Type1Subr *g = _font->glyph_x(i))
+ bcr.run(g->t1cs(), false);
+ for (int subrno = 4; subrno < _nsubrs; subrno++)
+ if (Type1Charstring *cs = _font->subr(subrno))
+ bcr.run(*cs, true);
+
+ // report warnings
+ if (bad_glyphs.size()) {
+ qsort(&bad_glyphs[0], bad_glyphs.size(), sizeof(PermString), sort_permstring_compare);
+ _errh->error("could not fully interpolate the following glyphs:");
+ StringAccum sa;
+ for (int i = 0; i < bad_glyphs.size(); i++) {
+ PermString n = bad_glyphs[i];
+ bool comma = (i < bad_glyphs.size() - 1);
+ if (sa.length() && sa.length() + 1 + n.length() + comma > 70) {
+ _errh->message(" %s", sa.c_str());
+ sa.clear();
+ }
+ sa << (sa.length() ? " " : "") << n << (comma ? "," : "");
+ }
+ _errh->message(" %s", sa.c_str());
+ }
+}
+
+
+/*****
+ * SubrExpander
+ **/
+
+class SubrExpander : public CharstringInterp { public:
+
+ SubrExpander();
+
+ void set_renumbering(const Vector<int> *v) { _renumbering = v; }
+
+ bool type1_command(int);
+
+ bool run(Type1Font *, Type1Charstring &);
+
+ private:
+
+ Type1CharstringGen _gen;
+ const Vector<int> *_renumbering;
+ int _subr_level;
+
+};
+
+SubrExpander::SubrExpander()
+ : CharstringInterp(), _gen(0), _renumbering(0)
+{
+}
+
+bool
+SubrExpander::type1_command(int cmd)
+{
+ switch (cmd) {
+
+ case Cs::cCallsubr: {
+ if (size() < 1)
+ goto unknown;
+ int subrno = (int)top(0);
+ int renumber_subrno = (subrno >= 0 && subrno < _renumbering->size() ? (*_renumbering)[subrno] : subrno);
+ if (renumber_subrno >= 0) {
+ top(0) = renumber_subrno;
+ goto unknown;
+ }
+ pop();
+ if (Charstring *subr_cs = get_subr(subrno)) {
+ _subr_level++;
+ subr_cs->process(*this);
+ _subr_level--;
+ }
+ return !done();
+ }
+
+ case Cs::cEndchar:
+ set_done();
+ goto end_cs;
+
+ case Cs::cReturn:
+ if (_subr_level)
+ return false;
+ goto end_cs;
+
+ end_cs:
+ _gen.gen_stack(*this, cmd);
+ _gen.gen_command(cmd);
+ return false;
+
+ default:
+ unknown:
+ _gen.gen_stack(*this, cmd);
+ _gen.gen_command(cmd);
+ break;
+
+ }
+ return true;
+}
+
+bool
+SubrExpander::run(Type1Font *font, Type1Charstring &cs)
+{
+ _gen.clear();
+ _subr_level = 0;
+ CharstringInterp::interpret(font, &cs);
+ _gen.output(cs);
+ return error() == errOK;
+}
+
+
+/*****
+ * Type1SubrRemover
+ **/
+
+Type1SubrRemover::Type1SubrRemover(Type1Font *font, ErrorHandler *errh)
+ : _font(font), _nsubrs(font->nsubrs()),
+ _renumbering(_nsubrs, REMOVABLE), _cost(_nsubrs, 0),
+ _save_count(0), _nonexist_count(0), _errh(errh)
+{
+ // find subroutines needed for hint replacement
+ HintReplacementDetector hr(font, 2);
+ for (int i = 0; i < _font->nglyphs(); i++) {
+ Type1Subr *g = _font->glyph_x(i);
+ if (g)
+ hr.run(_font, g->t1cs());
+ }
+
+ // save necessary subroutines
+ for (int i = 0; i < 4; i++) {
+ _renumbering[i] = i;
+ _save_count++;
+ }
+ // save hint-replacement subroutines
+ for (int i = 0; i < _nsubrs; i++) {
+ Type1Subr *cs = _font->subr_x(i);
+ if (!cs) {
+ _renumbering[i] = DEAD;
+ _nonexist_count++;
+ } else if (hr.is_hint_replacement(i)) {
+ _renumbering[i] = i;
+ _save_count++;
+ } else
+ _cost[i] = hr.call_count(i) * (cs->t1cs().length() - (i <= 107 ? 2 : 3));
+ }
+}
+
+Type1SubrRemover::~Type1SubrRemover()
+{
+}
+
+static Vector<int> *sort_keys;
+
+extern "C" {
+static int CDECL
+sort_permute_compare(const void *v1, const void *v2)
+{
+ const int *i1 = (const int *)v1;
+ const int *i2 = (const int *)v2;
+ return (*sort_keys)[*i1] - (*sort_keys)[*i2];
+}
+}
+
+bool
+Type1SubrRemover::run(int lower_to)
+{
+ if (lower_to < 0)
+ lower_to = _nsubrs;
+ if (lower_to < _save_count) {
+ _errh->warning("reducing %s to minimum number of subroutines (%d)",
+ _font->font_name().c_str(), _save_count - _nonexist_count);
+ lower_to = _save_count;
+ }
+ int to_remove = _nsubrs - _nonexist_count - lower_to;
+ if (to_remove < 0)
+ to_remove = 0;
+
+ // multiply by lost bytes per call
+ Vector<int> permute;
+ for (int i = 0; i < _nsubrs; i++)
+ permute.push_back(i);
+
+ // sort them by least frequent use -> most frequent use
+ sort_keys = &_cost;
+ qsort(&permute[0], _nsubrs, sizeof(int), sort_permute_compare);
+
+ // mark first portion of `permute' to be removed
+ int removed = 0;
+ for (int i = 0; i < _nsubrs; i++) {
+ int p = permute[i];
+ if (_renumbering[p] == REMOVABLE && removed < to_remove) {
+ _renumbering[p] = DEAD;
+ removed++;
+ }
+ }
+
+ // renumber the rest
+ int renumber_pos = 0;
+ for (int i = 0; i < _nsubrs; i++)
+ if (_renumbering[i] == REMOVABLE) { // save it
+ while (_renumbering[renumber_pos] >= 0)
+ renumber_pos++;
+ _renumbering[i] = renumber_pos++;
+ }
+ SubrExpander rem0;
+ rem0.set_renumbering(&_renumbering);
+
+ // go through and change them all
+ for (int i = 0; i < _nsubrs; i++) {
+ Type1Subr *s = _font->subr_x(i);
+ if (s && _renumbering[i] >= 0)
+ rem0.run(_font, s->t1cs());
+ }
+ for (int i = 0; i < _font->nglyphs(); i++)
+ if (Type1Subr *g = _font->glyph_x(i))
+ rem0.run(_font, g->t1cs());
+
+ // actually remove subroutines
+ _font->renumber_subrs(_renumbering);
+
+ return true;
+}