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