1 // -*- related-file-name: "../include/efont/t1csgen.hh" -*-
2
3 /* t1csgen.{cc,hh} -- Type 1 charstring generation
4 *
5 * Copyright (c) 1998-2019 Eddie Kohler
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version. This program is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 * Public License for more details.
14 */
15
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
19 #include <efont/t1csgen.hh>
20 #include <efont/t1font.hh>
21 #include <efont/t1item.hh>
22 #include <math.h>
23 namespace Efont {
24
25 static const char * const command_desc[] = {
26 0, 0, 0, 0, "y",
27 "xy", "x", "y", "xyxyxy", 0,
28
29 0, 0, 0, 0, 0,
30 0, 0, 0, 0, 0,
31
32 0, "xy", "x", 0, 0,
33 0, 0, 0, 0, 0,
34
35 "yxyx", "xxyy", 0, 0, 0,
36 0, 0, 0, 0, 0,
37
38 0, 0, 0, 0, 0,
39 0, 0, 0, 0, 0,
40
41 0, 0, 0, 0, 0,
42 0, 0, 0, 0, 0,
43
44 0, 0, 0, 0, 0,
45 "XY", 0, 0, 0, 0
46 };
47
Type1CharstringGen(int precision)48 Type1CharstringGen::Type1CharstringGen(int precision)
49 {
50 if (precision >= 1 && precision <= 107)
51 _precision = precision;
52 else
53 _precision = 5;
54 _f_precision = _precision;
55 clear();
56 }
57
58 void
clear()59 Type1CharstringGen::clear()
60 {
61 _ncs.clear();
62 _true = _false = Point(0, 0);
63 _state = S_INITIAL;
64 }
65
66
67 void
gen_rational(int big_val,int divisor)68 Type1CharstringGen::gen_rational(int big_val, int divisor)
69 {
70 int frac = big_val % divisor;
71 int val = (frac == 0 ? big_val / divisor : big_val);
72
73 if (val >= -107 && val <= 107)
74 _ncs.append((char)(val + 139));
75
76 else if (val >= -1131 && val <= 1131) {
77 int base = val < 0 ? 251 : 247;
78 if (val < 0) val = -val;
79 val -= 108;
80 int w = val % 256;
81 val = (val - w) / 256;
82 _ncs.append((char)(val + base));
83 _ncs.append((char)w);
84
85 } else {
86 _ncs.append('\377');
87 long l = val;
88 _ncs.append((char)((l >> 24) & 0xFF));
89 _ncs.append((char)((l >> 16) & 0xFF));
90 _ncs.append((char)((l >> 8) & 0xFF));
91 _ncs.append((char)((l >> 0) & 0xFF));
92 }
93
94 if (frac != 0) {
95 _ncs.append((char)(divisor + 139));
96 _ncs.append((char)Charstring::cEscape);
97 _ncs.append((char)(Charstring::cDiv - Charstring::cEscapeDelta));
98 }
99 }
100
101 void
gen_number(double float_val,int kind)102 Type1CharstringGen::gen_number(double float_val, int kind)
103 {
104 switch (kind) {
105 case 'x':
106 _true.x += float_val;
107 float_val = _true.x - _false.x;
108 break;
109 case 'y':
110 _true.y += float_val;
111 float_val = _true.y - _false.y;
112 break;
113 case 'X':
114 _true.x = float_val;
115 break;
116 case 'Y':
117 _true.y = float_val;
118 break;
119 }
120
121 // 30.Jul.2003 - Avoid rounding differences between platforms with the
122 // extra 0.00001.
123 int big_val = (int)floor(float_val * _f_precision + 0.50001);
124
125 gen_rational(big_val, _precision);
126
127 float_val = big_val / _f_precision;
128 switch (kind) {
129 case 'x':
130 _false.x += float_val;
131 break;
132 case 'y':
133 _false.y += float_val;
134 break;
135 case 'X':
136 _false.x = float_val;
137 break;
138 case 'Y':
139 _false.y = float_val;
140 break;
141 }
142 }
143
144
145 void
gen_command(int command)146 Type1CharstringGen::gen_command(int command)
147 {
148 if (command >= Charstring::cEscapeDelta) {
149 _ncs.append((char)Charstring::cEscape);
150 _ncs.append((char)(command - Charstring::cEscapeDelta));
151 if (command != Charstring::cSbw)
152 _state = S_GEN;
153 } else {
154 _ncs.append((char)command);
155 if (command > Charstring::cVmoveto && command != Charstring::cHsbw)
156 _state = S_GEN;
157 }
158 }
159
160 bool
gen_stem3_stack(CharstringInterp & interp)161 Type1CharstringGen::gen_stem3_stack(CharstringInterp &interp)
162 {
163 // special handling to ensure rounding doesn't generate an invalid stem3
164 // hint
165 if (interp.size() < 6)
166 return false;
167
168 // sort hints
169 int i0, i1, i2;
170 if (interp.at(0) > interp.at(2))
171 i0 = 2, i1 = 0;
172 else
173 i0 = 0, i1 = 2;
174 if (interp.at(4) < interp.at(i0))
175 i2 = i1, i1 = i0, i0 = 4;
176 else if (interp.at(4) < interp.at(i1))
177 i2 = i1, i1 = 4;
178 else
179 i2 = 4;
180
181 // check constraints. count "almost equal" as equal
182 double stemw0 = interp.at(i0+1), stemw2 = interp.at(i2+1);
183 if ((int)(1024*(stemw0 - stemw2) + .5) != 0)
184 return false;
185
186 double c0 = interp.at(i0) + interp.at(i0+1)/2;
187 double c1 = interp.at(i1) + interp.at(i1+1)/2;
188 double c2 = interp.at(i2) + interp.at(i2+1)/2;
189 if ((int)(1024*((c1 - c0) - (c2 - c1)) + .5) != 0)
190 return false;
191
192 // if all constraints are satisfied now, make sure they are also satisfied
193 // after rounding
194 int big_v0 = (int)floor(interp.at(i0) * _f_precision + 0.50001);
195 int big_v2 = (int)floor(interp.at(i2) * _f_precision + 0.50001);
196 int big_stemw0 = (int)floor(stemw0 * _f_precision + 0.50001);
197 int big_stemw1 = (int)floor(interp.at(i1+1) * _f_precision + 0.50001);
198
199 int big_v1_times2 = big_v0 + big_v2 + big_stemw0 - big_stemw1;
200
201 gen_rational(big_v0, _precision);
202 gen_rational(big_stemw0, _precision);
203 if (big_v1_times2 % 2)
204 gen_rational(big_v1_times2, 2 * _precision);
205 else
206 gen_rational(big_v1_times2 / 2, _precision);
207 gen_rational(big_stemw1, _precision);
208 gen_rational(big_v2, _precision);
209 gen_rational(big_stemw0, _precision);
210
211 interp.clear();
212 return true;
213 }
214
215 void
gen_stack(CharstringInterp & interp,int for_cmd)216 Type1CharstringGen::gen_stack(CharstringInterp &interp, int for_cmd)
217 {
218 const char *str = ((unsigned)for_cmd <= Charstring::cLastCommand ? command_desc[for_cmd] : (const char *)0);
219 if ((for_cmd == Charstring::cHstem3 || for_cmd == Charstring::cVstem3)
220 && gen_stem3_stack(interp))
221 return;
222
223 int i;
224 for (i = 0; str && *str && i < interp.size(); i++, str++)
225 gen_number(interp.at(i), *str);
226 for (; i < interp.size(); i++)
227 gen_number(interp.at(i));
228 interp.clear();
229 }
230
231 void
gen_moveto(const Point & p,bool closepath,bool always)232 Type1CharstringGen::gen_moveto(const Point &p, bool closepath, bool always)
233 {
234 // make sure we generate some moveto on the first command
235
236 Point d = p - _true;
237 int big_dx = (int)floor(d.x * _f_precision + 0.50001);
238 int big_dy = (int)floor(d.y * _f_precision + 0.50001);
239
240 if (big_dx == 0 && big_dy == 0 && _state != S_INITIAL && !always)
241 /* do nothing */;
242 else {
243 if (closepath)
244 gen_command(Charstring::cClosepath);
245 if (big_dy == 0) {
246 gen_number(d.x, 'x');
247 gen_command(Charstring::cHmoveto);
248 } else if (big_dx == 0) {
249 gen_number(d.y, 'y');
250 gen_command(Charstring::cVmoveto);
251 } else {
252 gen_number(d.x, 'x');
253 gen_number(d.y, 'y');
254 gen_command(Charstring::cRmoveto);
255 }
256 }
257
258 _true = p;
259 }
260
261 void
append_charstring(const String & s)262 Type1CharstringGen::append_charstring(const String &s)
263 {
264 _ncs << s;
265 }
266
267 Type1Charstring *
output()268 Type1CharstringGen::output()
269 {
270 return new Type1Charstring(take_string());
271 }
272
273 void
output(Type1Charstring & cs)274 Type1CharstringGen::output(Type1Charstring &cs)
275 {
276 cs.assign(take_string());
277 }
278
279 String
callsubr_string(int subr)280 Type1CharstringGen::callsubr_string(int subr)
281 {
282 Type1CharstringGen csg;
283 csg.gen_number(subr);
284 csg.gen_command(Charstring::cCallsubr);
285 return csg._ncs.take_string();
286 }
287
288
289 /*****
290 * Type1CharstringGenInterp
291 **/
292
Type1CharstringGenInterp(int precision)293 Type1CharstringGenInterp::Type1CharstringGenInterp(int precision)
294 : _csgen(precision), _hint_csgen(precision),
295 _direct_hr(false), _hr_storage(0),
296 _max_flex_height(0),
297 _had_flex(false), _had_bad_flex(false), _had_hr(false)
298 {
299 }
300
301 void
set_hint_replacement_storage(Type1Font * font)302 Type1CharstringGenInterp::set_hint_replacement_storage(Type1Font *font)
303 {
304 _hr_storage = font;
305 _hr_firstsubr = font->nsubrs();
306 }
307
308
309 // generating charstring commands
310
311 inline void
gen_number(double n,int what)312 Type1CharstringGenInterp::gen_number(double n, int what)
313 {
314 _csgen.gen_number(n, what);
315 }
316
317 inline void
gen_command(int what)318 Type1CharstringGenInterp::gen_command(int what)
319 {
320 _csgen.gen_command(what);
321 }
322
323 void
gen_sbw(bool hints_follow)324 Type1CharstringGenInterp::gen_sbw(bool hints_follow)
325 {
326 if (!hints_follow && nhints())
327 act_hintmask(Cs::cHintmask, 0, nhints());
328 else if (left_sidebearing().y == 0 && _width.y == 0) {
329 gen_number(left_sidebearing().x, 'X');
330 gen_number(_width.x);
331 gen_command(Cs::cHsbw);
332 } else {
333 gen_number(left_sidebearing().x, 'X');
334 gen_number(left_sidebearing().y, 'Y');
335 gen_number(_width.x);
336 gen_number(_width.y);
337 gen_command(Cs::cSbw);
338 }
339 _state = S_CLOSED;
340 }
341
342 void
act_width(int,const Point & p)343 Type1CharstringGenInterp::act_width(int, const Point &p)
344 {
345 _width = p;
346 }
347
348 void
act_seac(int,double asb,double adx,double ady,int bchar,int achar)349 Type1CharstringGenInterp::act_seac(int, double asb, double adx, double ady, int bchar, int achar)
350 {
351 if (_state == S_INITIAL)
352 gen_sbw(false);
353 gen_number(asb);
354 gen_number(adx);
355 gen_number(ady);
356 gen_number(bchar);
357 gen_number(achar);
358 gen_command(Cs::cSeac);
359 _state = S_SEAC;
360 }
361
362 void
swap_stem_hints()363 Type1CharstringGenInterp::swap_stem_hints()
364 {
365 _stem_pos.clear();
366 _stem_width.clear();
367 _stem_hstem.clear();
368 _in_hr = true;
369 }
370
371 void
act_hstem(int,double pos,double width)372 Type1CharstringGenInterp::act_hstem(int, double pos, double width)
373 {
374 if (_state != S_INITIAL && !_in_hr)
375 swap_stem_hints();
376 _stem_pos.push_back(pos);
377 _stem_width.push_back(width);
378 _stem_hstem.push_back(1);
379 }
380
381 void
act_vstem(int,double pos,double width)382 Type1CharstringGenInterp::act_vstem(int, double pos, double width)
383 {
384 if (_state != S_INITIAL && !_in_hr)
385 swap_stem_hints();
386 _stem_pos.push_back(pos);
387 _stem_width.push_back(width);
388 _stem_hstem.push_back(0);
389 }
390
391 String
gen_hints(const unsigned char * data,int nhints) const392 Type1CharstringGenInterp::gen_hints(const unsigned char *data, int nhints) const
393 {
394 _hint_csgen.clear();
395 unsigned char mask = 0x80;
396 for (int i = 0; i < nhints; i++) {
397 if (*data & mask) {
398 double offset = (_stem_hstem[i] ? left_sidebearing().y : left_sidebearing().x);
399 _hint_csgen.gen_number(_stem_pos[i] - offset);
400 _hint_csgen.gen_number(_stem_width[i]);
401 _hint_csgen.gen_command(_stem_hstem[i] ? Cs::cHstem : Cs::cVstem);
402 }
403 if ((mask >>= 1) == 0)
404 data++, mask = 0x80;
405 }
406 return _hint_csgen.take_string();
407 }
408
409 void
act_hintmask(int cmd,const unsigned char * data,int nhints)410 Type1CharstringGenInterp::act_hintmask(int cmd, const unsigned char *data, int nhints)
411 {
412 if (cmd == Cs::cCntrmask || nhints > Type1CharstringGenInterp::nhints())
413 return;
414
415 String data_holder;
416 if (!data) {
417 data_holder = String::make_fill('\377', ((nhints - 1) >> 3) + 1);
418 data = data_holder.udata();
419 }
420
421 String hints = gen_hints(data, nhints);
422 _in_hr = false;
423
424 if (_state == S_INITIAL || _direct_hr) {
425 _last_hints = hints;
426 if (_state == S_INITIAL)
427 gen_sbw(true);
428 _csgen.append_charstring(hints);
429 } else if (_hr_storage && hints != _last_hints) {
430 _last_hints = hints;
431 hints += (char)(Cs::cReturn);
432
433 int subrno = -1, nsubrs = _hr_storage->nsubrs();
434 for (int i = _hr_firstsubr; i < nsubrs; i++)
435 if (Type1Subr *s = _hr_storage->subr_x(i))
436 if (s->t1cs() == hints) {
437 subrno = i;
438 break;
439 }
440
441 if (subrno < 0 && _hr_storage->set_subr(nsubrs, Type1Charstring(hints)))
442 subrno = nsubrs;
443
444 if (subrno >= 0) {
445 _had_hr = true;
446 _csgen.gen_number(subrno);
447 _csgen.gen_number(4);
448 _csgen.gen_command(Cs::cCallsubr);
449 }
450 }
451 }
452
453 void
act_line(int cmd,const Point & a,const Point & b)454 Type1CharstringGenInterp::act_line(int cmd, const Point &a, const Point &b)
455 {
456 if (_state == S_INITIAL)
457 gen_sbw(false);
458 else if (_in_hr)
459 act_hintmask(cmd, 0, nhints());
460 _csgen.gen_moveto(a, _state == S_OPEN, false);
461 _state = S_OPEN;
462 if (a.x == b.x) {
463 gen_number(b.y - a.y, 'y');
464 gen_command(Cs::cVlineto);
465 } else if (a.y == b.y) {
466 gen_number(b.x - a.x, 'x');
467 gen_command(Cs::cHlineto);
468 } else {
469 gen_number(b.x - a.x, 'x');
470 gen_number(b.y - a.y, 'y');
471 gen_command(Cs::cRlineto);
472 }
473 }
474
475 void
act_curve(int cmd,const Point & a,const Point & b,const Point & c,const Point & d)476 Type1CharstringGenInterp::act_curve(int cmd, const Point &a, const Point &b, const Point &c, const Point &d)
477 {
478 if (_state == S_INITIAL)
479 gen_sbw(false);
480 else if (_in_hr)
481 act_hintmask(cmd, 0, nhints());
482 _csgen.gen_moveto(a, _state == S_OPEN, false);
483 _state = S_OPEN;
484 if (b.y == a.y && d.x == c.x) {
485 gen_number(b.x - a.x, 'x');
486 gen_number(c.x - b.x, 'x');
487 gen_number(c.y - b.y, 'y');
488 gen_number(d.y - c.y, 'y');
489 gen_command(Cs::cHvcurveto);
490 } else if (b.x == a.x && d.y == c.y) {
491 gen_number(b.y - a.y, 'y');
492 gen_number(c.x - a.x, 'x');
493 gen_number(c.y - b.y, 'y');
494 gen_number(d.x - c.x, 'x');
495 gen_command(Cs::cVhcurveto);
496 } else {
497 gen_number(b.x - a.x, 'x');
498 gen_number(b.y - a.y, 'y');
499 gen_number(c.x - b.x, 'x');
500 gen_number(c.y - b.y, 'y');
501 gen_number(d.x - c.x, 'x');
502 gen_number(d.y - c.y, 'y');
503 gen_command(Cs::cRrcurveto);
504 }
505 }
506
507 void
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)508 Type1CharstringGenInterp::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)
509 {
510 if (_state == S_INITIAL)
511 gen_sbw(false);
512 else if (_in_hr)
513 act_hintmask(cmd, 0, nhints());
514 _csgen.gen_moveto(p0, _state == S_OPEN, false);
515 _state = S_OPEN;
516
517 // 1. Outer endpoints must have same x (or y) coordinate
518 bool v_ok = (p0.x == p7.x);
519 bool h_ok = (p0.y == p7.y);
520
521 // 2. Join point and its neighboring controls must be at an extreme
522 if (v_ok && p2.x == p3_4.x && p3_4.x == p5.x) {
523 double distance = fabs(p3_4.x - p0.x);
524 int sign = (p3_4.x < p0.x ? -1 : 1);
525 if (sign * (p1.x - p0.x) < 0 || sign * (p1.x - p0.x) > distance
526 || sign * (p6.x - p0.x) < 0 || sign * (p6.x - p0.x) > distance)
527 v_ok = false;
528 } else
529 v_ok = false;
530
531 if (h_ok && p2.y == p3_4.y && p3_4.y == p5.y) {
532 double distance = fabs(p3_4.y - p0.y);
533 int sign = (p3_4.y < p0.y ? -1 : 1);
534 if (sign * (p1.y - p0.y) < 0 || sign * (p1.y - p0.y) > distance
535 || sign * (p6.y - p0.y) < 0 || sign * (p6.y - p0.y) > distance)
536 h_ok = false;
537 } else
538 h_ok = false;
539
540 // 3. Flex height <= 20
541 if (v_ok && fabs(p3_4.x - p0.x) > 20)
542 v_ok = false;
543 if (h_ok && fabs(p3_4.y - p0.y) > 20)
544 h_ok = false;
545
546 // generate flex commands
547 if (v_ok || h_ok) {
548 _had_flex = true;
549 Point p_reference = (h_ok ? Point(p3_4.x, p0.y) : Point(p0.x, p3_4.y));
550
551 _csgen.gen_number(1);
552 _csgen.gen_command(Cs::cCallsubr);
553
554 _csgen.gen_moveto(p_reference, false, true);
555 _csgen.gen_number(2);
556 _csgen.gen_command(Cs::cCallsubr);
557
558 _csgen.gen_moveto(p1, false, true);
559 _csgen.gen_number(2);
560 _csgen.gen_command(Cs::cCallsubr);
561
562 _csgen.gen_moveto(p2, false, true);
563 _csgen.gen_number(2);
564 _csgen.gen_command(Cs::cCallsubr);
565
566 _csgen.gen_moveto(p3_4, false, true);
567 _csgen.gen_number(2);
568 _csgen.gen_command(Cs::cCallsubr);
569
570 _csgen.gen_moveto(p5, false, true);
571 _csgen.gen_number(2);
572 _csgen.gen_command(Cs::cCallsubr);
573
574 _csgen.gen_moveto(p6, false, true);
575 _csgen.gen_number(2);
576 _csgen.gen_command(Cs::cCallsubr);
577
578 _csgen.gen_moveto(p7, false, true);
579 _csgen.gen_number(2);
580 _csgen.gen_command(Cs::cCallsubr);
581
582 _csgen.gen_number(flex_depth);
583 _csgen.gen_number(p7.x, 'X');
584 _csgen.gen_number(p7.y, 'Y');
585 _csgen.gen_number(0);
586 _csgen.gen_command(Cs::cCallsubr);
587
588 double flex_height = fabs(h_ok ? p3_4.y - p0.y : p3_4.x - p0.x);
589 if (flex_height > _max_flex_height)
590 _max_flex_height = flex_height;
591 } else {
592 _had_bad_flex = true;
593 act_curve(cmd, p0, p1, p2, p3_4);
594 act_curve(cmd, p3_4, p5, p6, p7);
595 }
596 }
597
598 void
act_closepath(int cmd)599 Type1CharstringGenInterp::act_closepath(int cmd)
600 {
601 if (_in_hr)
602 act_hintmask(cmd, 0, nhints());
603 gen_command(Cs::cClosepath);
604 _state = S_CLOSED;
605 }
606
607 void
intermediate_output(Type1Charstring & out)608 Type1CharstringGenInterp::intermediate_output(Type1Charstring &out)
609 {
610 _csgen.output(out);
611 _state = S_INITIAL;
612 act_hintmask(Cs::cEndchar, 0, nhints());
613 }
614
615 void
run(const CharstringContext & g,Type1Charstring & out)616 Type1CharstringGenInterp::run(const CharstringContext &g, Type1Charstring &out)
617 {
618 _width = Point(0, 0);
619 _csgen.clear();
620 swap_stem_hints();
621 _state = S_INITIAL;
622 _in_hr = false;
623
624 CharstringInterp::interpret(g);
625
626 if (_state == S_INITIAL)
627 gen_sbw(false);
628 else if (_in_hr)
629 act_hintmask(Cs::cEndchar, 0, nhints());
630 if (_state != S_SEAC)
631 _csgen.gen_command(Cs::cEndchar);
632
633 _csgen.output(out);
634 }
635
636 }
637