1 /* t1rewrit.cc -- routines for multiple- to single-master charstring conversion
2  *
3  * Copyright (c) 1997-2012 Eddie Kohler
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version. This program is distributed in the hope that it will be
9  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11  * Public License for more details.
12  */
13 
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
17 #include "t1rewrit.hh"
18 #include <efont/t1item.hh>
19 #include <efont/t1csgen.hh>
20 #include <lcdf/error.hh>
21 #include <lcdf/straccum.hh>
22 #include <stdio.h>
23 #include <math.h>
24 
25 using namespace Efont;
26 
27 static bool itc_complained = false;
28 static ErrorHandler *itc_errh;
29 
30 void
itc_complain()31 itc_complain()
32 {
33     //itc_errh->warning("strange %<callothersubr%>; is this an ITC font?");
34     itc_complained = true;
35 }
36 
37 
38 /*****
39  * HintReplacementDetector
40  **/
41 
42 class HintReplacementDetector : public CharstringInterp { public:
43 
44     HintReplacementDetector(Type1Font *, int);
45     HintReplacementDetector(Type1Font *, const Vector<double> &, int);
46 
is_hint_replacement(int i) const47     bool is_hint_replacement(int i) const { return _hint_replacements[i] != 0; }
call_count(int i) const48     int call_count(int i) const		{ return _call_counts[i]; }
49 
50     bool type1_command(int);
51 
52     bool run(Type1Font *, Type1Charstring &);
53 
54   private:
55 
56     Vector<int> _hint_replacements;
57     Vector<int> _call_counts;
58     int _subr_level;
59     int _count_calls_below;
60 
61 };
62 
HintReplacementDetector(Type1Font * f,int b)63 HintReplacementDetector::HintReplacementDetector(Type1Font *f, int b)
64     : CharstringInterp(),
65       _hint_replacements(f->nsubrs(), 0), _call_counts(f->nsubrs(), 0),
66       _count_calls_below(b)
67 {
68 }
69 
HintReplacementDetector(Type1Font * f,const Vector<double> & wv,int b)70 HintReplacementDetector::HintReplacementDetector(Type1Font *f, const Vector<double> &wv, int b)
71     : CharstringInterp(wv),
72       _hint_replacements(f->nsubrs(), 0), _call_counts(f->nsubrs(), 0),
73       _count_calls_below(b)
74 {
75 }
76 
77 bool
type1_command(int cmd)78 HintReplacementDetector::type1_command(int cmd)
79 {
80     switch (cmd) {
81 
82       case Cs::cCallothersubr: {
83 	  if (size() < 2)
84 	      goto unknown;
85 	  int command = (int)top(0);
86 	  int n = (int)top(1);
87 	  if (command == Cs::othcReplacehints && n == 1) {
88 	      pop(2);
89 	      _hint_replacements[(int)top()] = 1;
90 	      ps_clear();
91 	      ps_push(top());
92 	      pop();
93 	      break;
94 	  } else if (command >= Cs::othcMM1 && command <= Cs::othcMM6) {
95 	      pop(2);
96 	      return mm_command(command, n);
97 	  } else if (command >= Cs::othcITC_load && command <= Cs::othcITC_random) {
98 	      pop(2);
99 	      return itc_command(command, n);
100 	  } else
101 	      goto unknown;
102       }
103 
104       case Cs::cCallsubr: {
105 	  if (size() < 1)
106 	      return error(errUnderflow, cmd);
107 	  int which = (int)pop();
108 	  if (!_count_calls_below || _subr_level < _count_calls_below)
109 	      _call_counts[which]++;
110 
111 	  Charstring *subr_cs = get_subr(which);
112 	  if (!subr_cs)
113 	      return error(errSubr, which);
114 
115 	  _subr_level++;
116 	  subr_cs->process(*this);
117 	  _subr_level--;
118 
119 	  if (error() != errOK)
120 	      return false;
121 	  return !done();
122       }
123 
124       case Cs::cEndchar:
125       case Cs::cReturn:
126 	return CharstringInterp::type1_command(cmd);
127 
128       case Cs::cBlend:
129       case Cs::cAbs:
130       case Cs::cAdd:
131       case Cs::cSub:
132       case Cs::cDiv:
133       case Cs::cNeg:
134       case Cs::cRandom:
135       case Cs::cMul:
136       case Cs::cSqrt:
137       case Cs::cDrop:
138       case Cs::cExch:
139       case Cs::cIndex:
140       case Cs::cRoll:
141       case Cs::cDup:
142       case Cs::cAnd:
143       case Cs::cOr:
144       case Cs::cNot:
145       case Cs::cEq:
146       case Cs::cIfelse:
147 	return arith_command(cmd);
148 
149       case Cs::cPop:
150 	if (ps_size() >= 1)
151 	    push(ps_pop());
152 	break;
153 
154       default:
155       unknown:
156 	clear();
157 	break;
158 
159     }
160     return true;
161 }
162 
163 bool
run(Type1Font * f,Type1Charstring & cs)164 HintReplacementDetector::run(Type1Font *f, Type1Charstring &cs)
165 {
166     _subr_level = 0;
167     CharstringInterp::interpret(f, &cs);
168     return error() == errOK;
169 }
170 
171 
172 /*****
173  * Type1OneMMRemover
174  **/
175 
176 class Type1OneMMRemover: public CharstringInterp { public:
177 
178     Type1OneMMRemover(Type1MMRemover *);
179 
180     bool type1_command(int);
181 
182     inline bool run_fresh_subr(const Type1Charstring &, bool);
183     inline bool run_fresh_glyph(const Type1Charstring &);
184     inline bool rerun_subr(const Type1Charstring &);
185 
186     Type1Charstring *output_prefix();
187     void output_main(Type1Charstring &);
188 
189   private:
190 
191     Type1MMRemover *_remover;
192     Type1CharstringGen _prefix_gen;
193     Type1CharstringGen _main_gen;
194 
195     int _subr_level;
196     bool _in_subr;
197     bool _in_prefix;
198     bool _must_expand;
199 
200     inline void run_subr(Type1Charstring *);
201     bool itc_command(int command, int on_stack);
202 
203     bool run(const Type1Charstring &, bool, bool, bool);
204 
205 };
206 
207 /* For version 1.1
208  *
209  * Problem: Sometimes a charstring will call one subroutine, which will call
210  * another, etc., which finally does a multiple master CallOtherSubr! The
211  * required arguments might build up only gradually over all the subrs. This
212  * makes it hard to remove the eventual CallOtherSubr!
213  *
214  * Partial solution: Divide each subroutine into two parts: the initial
215  * "prefix" contains the closure of any initial multiple-master commands
216  * (including those from sub-subroutines), the following "main" part has all
217  * the other commands. In a situation like this:
218  *
219  * subr-1 = 8 15 callothersubr pop pop pop return
220  * subr-2 = 4 5 6  1 callsubr return
221  * subr-3 = 1 2 3  2 callsubr return
222  *
223  * we'll divide it up like this: (prefix || main)
224  *
225  * subr-1 =             8 15 callothersubr pop pop pop || (nothing)
226  * subr-2 =       4 5 6 8 15 callothersubr pop pop pop || (nothing)
227  * subr-3 = 1 2 3 4 5 6 8 15 callothersubr pop pop pop || (nothing)
228  *
229  * Now, when we call a subroutine, we EXECUTE its prefix part. Then, if its
230  * main part is nonempty, we output the original call to the subroutine to
231  * take care of the main part. */
232 
233 
Type1OneMMRemover(Type1MMRemover * remover)234 Type1OneMMRemover::Type1OneMMRemover(Type1MMRemover *remover)
235     : CharstringInterp(remover->weight_vector()),
236       _remover(remover), _prefix_gen(remover->precision()),
237       _main_gen(remover->precision())
238 {
239 }
240 
241 inline void
run_subr(Type1Charstring * cs)242 Type1OneMMRemover::run_subr(Type1Charstring *cs)
243 {
244     _subr_level++;
245     cs->process(*this);
246     _subr_level--;
247 }
248 
249 bool
itc_command(int command,int on_stack)250 Type1OneMMRemover::itc_command(int command, int on_stack)
251 {
252     const Vector<double> &weight = weight_vector();
253     assert(weight.size());
254     Vector<double> *scratch = scratch_vector();
255     Type1CharstringGen *gen =
256 	(_in_prefix ? &_prefix_gen : (_in_subr ? &_main_gen : 0));
257 
258     int base = size() - on_stack - 2;
259     switch (command) {
260 
261       case Cs::othcITC_load: {
262 	  if (on_stack != 1)
263 	      return false;
264 	  int offset = (int)at(base);
265 	  for (int i = 0; i < weight.size(); i++)
266 	      vec(scratch, offset+i) = weight.at_u(i);
267 	  // save load command, so we expand its effects into the scratch
268 	  // vector
269 	  if (gen) {
270 	      gen->gen_number(offset);
271 	      gen->gen_number(1);
272 	      gen->gen_number(Cs::othcITC_load);
273 	      gen->gen_command(Cs::cCallothersubr);
274 	  }
275 	  break;
276       }
277 
278       case Cs::othcITC_put: {
279 	  if (on_stack != 2)
280 	      return false;
281 	  int offset = (int)at(base+1);
282 	  vec(scratch, offset) = at(base);
283 	  // save put command, so we expand its effects into the scratch
284 	  // vector
285 	  if (gen) {
286 	      gen->gen_number(at(base));
287 	      gen->gen_number(offset);
288 	      gen->gen_number(2);
289 	      gen->gen_number(Cs::othcITC_put);
290 	      gen->gen_command(Cs::cCallothersubr);
291 	  }
292 	  break;
293       }
294 
295       case Cs::othcITC_get: {
296 	  if (on_stack != 1)
297 	      return false;
298 	  int offset = (int)at(base);
299 	  double d = vec(scratch, offset);
300 	  if (!KNOWN(d)) {
301 	      _must_expand = true;
302 	      return false;
303 	  }
304 	  ps_push(d);
305 	  break;
306       }
307 
308       case Cs::othcITC_add: {
309 	  if (on_stack != 2)
310 	      return false;
311 	  ps_push(at(base) + at(base+1));
312 	  break;
313       }
314 
315       case Cs::othcITC_sub: {
316 	  if (on_stack != 2)
317 	      return false;
318 	  ps_push(at(base) - at(base+1));
319 	  break;
320       }
321 
322       case Cs::othcITC_mul: {
323 	  if (on_stack != 2)
324 	      return false;
325 	  ps_push(at(base) * at(base+1));
326 	  break;
327       }
328 
329       case Cs::othcITC_div: {
330 	  if (on_stack != 2)
331 	      return false;
332 	  ps_push(at(base) / at(base+1));
333 	  break;
334       }
335 
336       case Cs::othcITC_ifelse: {
337 	  if (on_stack != 4)
338 	      return false;
339 	  if (at(base+2) <= at(base+3))
340 	      ps_push(at(base));
341 	  else
342 	      ps_push(at(base+1));
343 	  break;
344       }
345 
346       default:
347 	return false;
348 
349     }
350 
351     pop(on_stack + 2);
352     return true;
353 }
354 
355 bool
type1_command(int cmd)356 Type1OneMMRemover::type1_command(int cmd)
357 {
358     switch (cmd) {
359 
360       case Cs::cCallothersubr: {
361 	  // Expand known othersubr calls. If we cannot expand the othersubr
362 	  // call completely, then write it to the expander.
363 	  if (size() < 2)
364 	      goto partial_othersubr;
365 	  int command = (int)top(0);
366 	  int n = (int)top(1);
367 	  if (command >= Cs::othcITC_load && command <= Cs::othcITC_random) {
368 	      if (!itc_complained)
369 		  itc_complain();
370 	      if (size() < 2 + n || !itc_command(command, n))
371 		  goto partial_othersubr;
372 	  } else if (command >= Cs::othcMM1 && command <= Cs::othcMM6) {
373 	      if (size() < 2 + n)
374 		  goto partial_othersubr;
375 	      pop(2);
376 	      mm_command(command, n);
377 	  } else
378 	      goto normal;
379 	  break;
380       }
381 
382       partial_othersubr: {
383 	  if (!_in_prefix) {
384 	      _must_expand = true;
385 	      goto normal;
386 	  }
387 	  _prefix_gen.gen_stack(*this, 0);
388 	  _prefix_gen.gen_command(Cs::cCallothersubr);
389 	  break;
390       }
391 
392       case Cs::cCallsubr: {
393 	  // expand subroutines in line if necessary
394 	  if (size() < 1)
395 	      goto normal;
396 	  int subrno = (int)pop();
397 	  if (_subr_level < 1) { // otherwise, have already included prefix
398 	      if (Type1Charstring *cs = _remover->subr_prefix(subrno))
399 		  run_subr(cs);
400 	  }
401 	  if (Type1Charstring *cs = _remover->subr_expander(subrno))
402 	      run_subr(cs);
403 	  else {
404 	      push(subrno);
405 	      goto normal;
406 	  }
407 	  break;
408       }
409 
410       case Cs::cPop:
411 	if (ps_size() >= 1)
412 	    push(ps_pop());
413 	else if (_in_prefix && ps_size() == 0) {
414 	    _prefix_gen.gen_stack(*this, 0);
415 	    _prefix_gen.gen_command(Cs::cPop);
416 	} else
417 	    goto normal;
418 	break;
419 
420       case Cs::cDiv:
421 	if (size() < 2)
422 	    goto normal;
423 	top(1) /= top(0);
424 	pop();
425 	break;
426 
427       case Cs::cReturn:
428 	return false;
429 
430       normal:
431       default:
432 	_main_gen.gen_stack(*this, cmd);
433 	_main_gen.gen_command(cmd);
434 	_in_prefix = 0;
435 	return (cmd != Cs::cEndchar);
436 
437     }
438     return true;
439 }
440 
441 
442 bool
run(const Type1Charstring & cs,bool in_subr,bool do_prefix,bool fresh)443 Type1OneMMRemover::run(const Type1Charstring &cs,
444 		       bool in_subr, bool do_prefix, bool fresh)
445 {
446     _prefix_gen.clear();
447     _main_gen.clear();
448     _in_subr = in_subr;
449     _in_prefix = do_prefix;
450     _subr_level = (fresh ? 0 : 1);
451     _must_expand = false;
452     Vector<double> *scratch = scratch_vector();
453     scratch->assign(scratch->size(), UNKDOUBLE);
454 
455     CharstringInterp::interpret(_remover->program(), &cs);
456 
457     if (in_subr) {
458 	_main_gen.gen_stack(*this, Cs::cReturn);
459 	_main_gen.gen_command(Cs::cReturn);
460     }
461     if (_must_expand)
462 	return true;
463     if (fresh && in_subr) {
464 	if (_main_gen.length() == 0
465 	    || (_main_gen.length() == 1 && _main_gen.data()[0] == Cs::cReturn))
466 	    return true;
467     }
468     return false;
469 }
470 
471 inline bool
run_fresh_subr(const Type1Charstring & cs,bool do_prefix)472 Type1OneMMRemover::run_fresh_subr(const Type1Charstring &cs, bool do_prefix)
473 {
474     return run(cs, true, do_prefix, true);
475 }
476 
477 inline bool
run_fresh_glyph(const Type1Charstring & cs)478 Type1OneMMRemover::run_fresh_glyph(const Type1Charstring &cs)
479 {
480     return run(cs, false, false, true);
481 }
482 
483 inline bool
rerun_subr(const Type1Charstring & cs)484 Type1OneMMRemover::rerun_subr(const Type1Charstring &cs)
485 {
486     return run(cs, true, false, false);
487 }
488 
489 Type1Charstring *
output_prefix()490 Type1OneMMRemover::output_prefix()
491 {
492     if (_prefix_gen.length() > 0) {
493 	_prefix_gen.gen_command(Cs::cReturn);
494 	return _prefix_gen.output();
495     } else
496 	return 0;
497 }
498 
499 void
output_main(Type1Charstring & cs)500 Type1OneMMRemover::output_main(Type1Charstring &cs)
501 {
502     _main_gen.output(cs);
503 }
504 
505 
506 /*****
507  * Type1BadCallRemover
508  **/
509 
510 class Type1BadCallRemover: public CharstringInterp { public:
511 
512     Type1BadCallRemover(Type1MMRemover *);
513 
514     bool type1_command(int);
515 
516     bool run(Type1Charstring &, bool is_subr);
517 
518   private:
519 
520     Type1CharstringGen _gen;
521     Type1MMRemover *_remover;
522     bool _is_subr;
523 
524 };
525 
Type1BadCallRemover(Type1MMRemover * remover)526 Type1BadCallRemover::Type1BadCallRemover(Type1MMRemover *remover)
527     : CharstringInterp(remover->weight_vector()),
528       _gen(remover->precision()), _remover(remover)
529 {
530 }
531 
532 bool
type1_command(int cmd)533 Type1BadCallRemover::type1_command(int cmd)
534 {
535     switch (cmd) {
536 
537       case Cs::cCallsubr: {
538 	  if (size() < 1)
539 	      goto normal;
540 	  int subrno = (int)top();
541 	  if (!get_subr(subrno)) {
542 	      pop();
543 	      return false;
544 	  } else
545 	      goto normal;
546       }
547 
548       normal:
549       default:
550 	_gen.gen_stack(*this, 0);
551 	_gen.gen_command(cmd);
552 	return (cmd != Cs::cEndchar || _is_subr) && cmd != Cs::cReturn;
553 
554     }
555 }
556 
557 bool
run(Type1Charstring & cs,bool is_subr)558 Type1BadCallRemover::run(Type1Charstring &cs, bool is_subr)
559 {
560     _is_subr = is_subr;
561     _gen.clear();
562     CharstringInterp::interpret(_remover->program(), &cs);
563     _gen.output(cs);
564     return error() == errOK;
565 }
566 
567 
568 /*****
569  * Type1MMRemover
570  **/
571 
Type1MMRemover(Type1Font * font,const Vector<double> & wv,int precision,ErrorHandler * errh)572 Type1MMRemover::Type1MMRemover(Type1Font *font, const Vector<double> &wv,
573 			       int precision, ErrorHandler *errh)
574     : _font(font), _weight_vector(wv), _precision(precision),
575       _nsubrs(font->nsubrs()),
576       _subr_done(_nsubrs, 0),
577       _subr_prefix(_nsubrs, (Type1Charstring *)0),
578       _must_expand_subr(_nsubrs, 0),
579       _hint_replacement_subr(_nsubrs, 0),
580       _expand_all_subrs(false), _errh(errh)
581 {
582     itc_errh = _errh;
583 
584     // find subroutines needed for hint replacement
585     HintReplacementDetector hr(font, wv, 0);
586     for (int i = 0; i < _font->nglyphs(); i++)
587 	if (Type1Subr *g = _font->glyph_x(i))
588 	    hr.run(font, g->t1cs());
589     for (int i = 0; i < _nsubrs; i++)
590 	if (hr.is_hint_replacement(i))
591 	    _hint_replacement_subr[i] = 1;
592 
593     // don't get rid of first 4 subrs
594     for (int i = 0; i < _nsubrs && i < 4; i++)
595 	_subr_done[i] = 1;
596 }
597 
~Type1MMRemover()598 Type1MMRemover::~Type1MMRemover()
599 {
600     for (int i = 0; i < _nsubrs; i++)
601 	if (_subr_prefix[i])
602 	    delete _subr_prefix[i];
603 }
604 
605 
606 Type1Charstring *
subr_prefix(int subrno)607 Type1MMRemover::subr_prefix(int subrno)
608 {
609     if (subrno < 0 || subrno >= _nsubrs)
610 	return 0;
611 
612     if (!_subr_done[subrno]) {
613 	_subr_done[subrno] = 1;
614 
615 	Type1Charstring *subr = _font->subr(subrno);
616 	if (!subr)
617 	    return 0;
618 
619 	Type1OneMMRemover one(this);
620 	if (one.run_fresh_subr(*subr, !_hint_replacement_subr[subrno]))
621 	    _must_expand_subr[subrno] = true;
622 	_subr_prefix[subrno] = one.output_prefix();
623 	one.output_main(*subr);
624     }
625 
626     return _subr_prefix[subrno];
627 }
628 
629 Type1Charstring *
subr_expander(int subrno)630 Type1MMRemover::subr_expander(int subrno)
631 {
632     if (subrno < 0 || subrno >= _nsubrs)
633 	return 0;
634     if (!_subr_done[subrno])
635 	(void)subr_prefix(subrno);
636     if (!_expand_all_subrs && !_must_expand_subr[subrno])
637 	return 0;
638     return _font->subr(subrno);
639 }
640 
641 extern "C" {
642 static int CDECL
sort_permstring_compare(const void * v1,const void * v2)643 sort_permstring_compare(const void *v1, const void *v2)
644 {
645     const PermString *s1 = (const PermString *)v1;
646     const PermString *s2 = (const PermString *)v2;
647     return strcmp(s1->c_str(), s2->c_str());
648 }
649 }
650 
651 void
run()652 Type1MMRemover::run()
653 {
654     Type1OneMMRemover one(this);
655 
656     // check subroutines
657     for (int subrno = 0; subrno < _nsubrs; subrno++)
658 	(void)subr_prefix(subrno);
659 
660     // expand glyphs
661     Vector<PermString> bad_glyphs;
662     for (int i = 0; i < _font->nglyphs(); i++) {
663 	Type1Subr *g = _font->glyph_x(i);
664 	if (g) {
665 	    if (one.run_fresh_glyph(g->t1cs())) {
666 		// Every glyph should be fully expandable without encountering
667 		// a MM command. If we fail the first time, try again,
668 		// expanding ALL subroutines. This catches, for example, SUBR
669 		// 1 { 1 0 return }; GLYPH g { 1 callsubr 2 blend }; This will
670 		// fail the first time, because `1 callsubr' will be left as a
671 		// subroutine call, so `1 0' (required arguments to `blend')
672 		// won't be visible.
673 		_expand_all_subrs = true;
674 		if (one.run_fresh_glyph(g->t1cs()))
675 		    bad_glyphs.push_back(g->name());
676 		_expand_all_subrs = false;
677 	    }
678 	    one.output_main(g->t1cs());
679 	}
680     }
681 
682     // remove uncalled subroutines, expand hint replacement subroutines
683     HintReplacementDetector hr(_font, _weight_vector, 0);
684     for (int i = 0; i < _font->nglyphs(); i++)
685 	if (Type1Subr *g = _font->glyph_x(i))
686 	    hr.run(_font, g->t1cs());
687     // don't remove first four subroutines!
688     for (int subrno = 4; subrno < _nsubrs; subrno++)
689 	if (hr.call_count(subrno) || _hint_replacement_subr[subrno]) {
690 	    Type1Charstring *cs = _font->subr(subrno);
691 	    if (one.rerun_subr(*cs)) {
692 		_expand_all_subrs = true;
693 		if (one.rerun_subr(*cs))
694 		    bad_glyphs.push_back(permprintf("subr %d", subrno));
695 		_expand_all_subrs = false;
696 	    }
697 	    one.output_main(*cs);
698 	} else
699 	    _font->remove_subr(subrno);
700 
701     // remove calls to removed subroutines
702     Type1BadCallRemover bcr(this);
703     for (int i = 0; i < _font->nglyphs(); i++)
704 	if (Type1Subr *g = _font->glyph_x(i))
705 	    bcr.run(g->t1cs(), false);
706     for (int subrno = 4; subrno < _nsubrs; subrno++)
707 	if (Type1Charstring *cs = _font->subr(subrno))
708 	    bcr.run(*cs, true);
709 
710     // report warnings
711     if (bad_glyphs.size()) {
712 	qsort(&bad_glyphs[0], bad_glyphs.size(), sizeof(PermString), sort_permstring_compare);
713 	_errh->error("could not fully interpolate the following glyphs:");
714 	StringAccum sa;
715 	for (int i = 0; i < bad_glyphs.size(); i++) {
716 	    PermString n = bad_glyphs[i];
717 	    bool comma = (i < bad_glyphs.size() - 1);
718 	    if (sa.length() && sa.length() + 1 + n.length() + comma > 70) {
719 		_errh->message("  %s", sa.c_str());
720 		sa.clear();
721 	    }
722 	    sa << (sa.length() ? " " : "") << n << (comma ? "," : "");
723 	}
724 	_errh->message("  %s", sa.c_str());
725     }
726 }
727 
728 
729 /*****
730  * SubrExpander
731  **/
732 
733 class SubrExpander : public CharstringInterp { public:
734 
735     SubrExpander();
736 
set_renumbering(const Vector<int> * v)737     void set_renumbering(const Vector<int> *v) { _renumbering = v; }
738 
739     bool type1_command(int);
740 
741     bool run(Type1Font *, Type1Charstring &);
742 
743   private:
744 
745     Type1CharstringGen _gen;
746     const Vector<int> *_renumbering;
747     int _subr_level;
748 
749 };
750 
SubrExpander()751 SubrExpander::SubrExpander()
752     : CharstringInterp(), _gen(0), _renumbering(0)
753 {
754 }
755 
756 bool
type1_command(int cmd)757 SubrExpander::type1_command(int cmd)
758 {
759     switch (cmd) {
760 
761       case Cs::cCallsubr: {
762 	  if (size() < 1)
763 	      goto unknown;
764 	  int subrno = (int)top(0);
765 	  int renumber_subrno = (subrno >= 0 && subrno < _renumbering->size() ? (*_renumbering)[subrno] : subrno);
766 	  if (renumber_subrno >= 0) {
767 	      top(0) = renumber_subrno;
768 	      goto unknown;
769 	  }
770 	  pop();
771 	  if (Charstring *subr_cs = get_subr(subrno)) {
772 	      _subr_level++;
773 	      subr_cs->process(*this);
774 	      _subr_level--;
775 	  }
776 	  return !done();
777       }
778 
779       case Cs::cEndchar:
780 	set_done();
781 	goto end_cs;
782 
783       case Cs::cReturn:
784 	if (_subr_level)
785 	    return false;
786 	goto end_cs;
787 
788       end_cs:
789 	_gen.gen_stack(*this, cmd);
790 	_gen.gen_command(cmd);
791 	return false;
792 
793       default:
794       unknown:
795 	_gen.gen_stack(*this, cmd);
796 	_gen.gen_command(cmd);
797 	break;
798 
799     }
800     return true;
801 }
802 
803 bool
run(Type1Font * font,Type1Charstring & cs)804 SubrExpander::run(Type1Font *font, Type1Charstring &cs)
805 {
806     _gen.clear();
807     _subr_level = 0;
808     CharstringInterp::interpret(font, &cs);
809     _gen.output(cs);
810     return error() == errOK;
811 }
812 
813 
814 /*****
815  * Type1SubrRemover
816  **/
817 
Type1SubrRemover(Type1Font * font,ErrorHandler * errh)818 Type1SubrRemover::Type1SubrRemover(Type1Font *font, ErrorHandler *errh)
819     : _font(font), _nsubrs(font->nsubrs()),
820       _renumbering(_nsubrs, REMOVABLE), _cost(_nsubrs, 0),
821       _save_count(0), _nonexist_count(0), _errh(errh)
822 {
823     // find subroutines needed for hint replacement
824     HintReplacementDetector hr(font, 2);
825     for (int i = 0; i < _font->nglyphs(); i++) {
826 	Type1Subr *g = _font->glyph_x(i);
827 	if (g)
828 	    hr.run(_font, g->t1cs());
829     }
830 
831     // save necessary subroutines
832     for (int i = 0; i < 4; i++) {
833 	_renumbering[i] = i;
834 	_save_count++;
835     }
836     // save hint-replacement subroutines
837     for (int i = 0; i < _nsubrs; i++) {
838 	Type1Subr *cs = _font->subr_x(i);
839 	if (!cs) {
840 	    _renumbering[i] = DEAD;
841 	    _nonexist_count++;
842 	} else if (hr.is_hint_replacement(i)) {
843 	    _renumbering[i] = i;
844 	    _save_count++;
845 	} else
846 	    _cost[i] = hr.call_count(i) * (cs->t1cs().length() - (i <= 107 ? 2 : 3));
847     }
848 }
849 
~Type1SubrRemover()850 Type1SubrRemover::~Type1SubrRemover()
851 {
852 }
853 
854 static Vector<int> *sort_keys;
855 
856 extern "C" {
857 static int CDECL
sort_permute_compare(const void * v1,const void * v2)858 sort_permute_compare(const void *v1, const void *v2)
859 {
860     const int *i1 = (const int *)v1;
861     const int *i2 = (const int *)v2;
862     return (*sort_keys)[*i1] - (*sort_keys)[*i2];
863 }
864 }
865 
866 bool
run(int lower_to)867 Type1SubrRemover::run(int lower_to)
868 {
869     if (lower_to < 0)
870 	lower_to = _nsubrs;
871     if (lower_to < _save_count) {
872 	_errh->warning("reducing %s to minimum number of subroutines (%d)",
873 		       _font->font_name().c_str(), _save_count - _nonexist_count);
874 	lower_to = _save_count;
875     }
876     int to_remove = _nsubrs - _nonexist_count - lower_to;
877     if (to_remove < 0)
878 	to_remove = 0;
879 
880     // multiply by lost bytes per call
881     Vector<int> permute;
882     for (int i = 0; i < _nsubrs; i++)
883 	permute.push_back(i);
884 
885     // sort them by least frequent use -> most frequent use
886     sort_keys = &_cost;
887     qsort(&permute[0], _nsubrs, sizeof(int), sort_permute_compare);
888 
889     // mark first portion of `permute' to be removed
890     int removed = 0;
891     for (int i = 0; i < _nsubrs; i++) {
892 	int p = permute[i];
893 	if (_renumbering[p] == REMOVABLE && removed < to_remove) {
894 	    _renumbering[p] = DEAD;
895 	    removed++;
896 	}
897     }
898 
899     // renumber the rest
900     int renumber_pos = 0;
901     for (int i = 0; i < _nsubrs; i++)
902 	if (_renumbering[i] == REMOVABLE) { // save it
903 	    while (_renumbering[renumber_pos] >= 0)
904 		renumber_pos++;
905 	    _renumbering[i] = renumber_pos++;
906 	}
907     SubrExpander rem0;
908     rem0.set_renumbering(&_renumbering);
909 
910     // go through and change them all
911     for (int i = 0; i < _nsubrs; i++) {
912 	Type1Subr *s = _font->subr_x(i);
913 	if (s && _renumbering[i] >= 0)
914 	    rem0.run(_font, s->t1cs());
915     }
916     for (int i = 0; i < _font->nglyphs(); i++)
917 	if (Type1Subr *g = _font->glyph_x(i))
918 	    rem0.run(_font, g->t1cs());
919 
920     // actually remove subroutines
921     _font->renumber_subrs(_renumbering);
922 
923     return true;
924 }
925