1
// -*- related-file-name: "../include/efont/t1csgen.hh" -*-
3
/* t1csgen.{cc,hh} -- Type 1 charstring generation
5
* Copyright (c) 1998-2005 Eddie Kohler
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.
19
#include <efont/t1csgen.hh>
20
#include <efont/t1font.hh>
21
#include <efont/t1item.hh>
25
static const char * const command_desc[] = {
27
"xy", "x", "y", "xyxyxy", 0,
35
"yxyx", "xxyy", 0, 0, 0,
48
Type1CharstringGen::Type1CharstringGen(int precision)
50
if (precision >= 1 && precision <= 107)
51
_precision = precision;
54
_f_precision = _precision;
58
Type1CharstringGen::clear()
61
_true = _false = Point(0, 0);
67
Type1CharstringGen::gen_number(double float_val, int kind)
72
float_val = _true.x - _false.x;
76
float_val = _true.y - _false.y;
86
// 30.Jul.2003 - Avoid rounding differences between platforms with the
88
int big_val = (int)floor(float_val * _f_precision + 0.50001);
89
int frac = big_val % _precision;
90
int val = (frac == 0 ? big_val / _precision : big_val);
92
if (val >= -107 && val <= 107)
93
_ncs.append((char)(val + 139));
95
else if (val >= -1131 && val <= 1131) {
96
int base = val < 0 ? 251 : 247;
97
if (val < 0) val = -val;
100
val = (val - w) / 256;
101
_ncs.append((char)(val + base));
102
_ncs.append((char)w);
107
_ncs.append((char)((l >> 24) & 0xFF));
108
_ncs.append((char)((l >> 16) & 0xFF));
109
_ncs.append((char)((l >> 8) & 0xFF));
110
_ncs.append((char)((l >> 0) & 0xFF));
114
_ncs.append((char)(_precision + 139));
115
_ncs.append((char)Charstring::cEscape);
116
_ncs.append((char)(Charstring::cDiv - Charstring::cEscapeDelta));
119
float_val = big_val / _f_precision;
122
_false.x += float_val;
125
_false.y += float_val;
128
_false.x = float_val;
131
_false.y = float_val;
138
Type1CharstringGen::gen_command(int command)
140
if (command >= Charstring::cEscapeDelta) {
141
_ncs.append((char)Charstring::cEscape);
142
_ncs.append((char)(command - Charstring::cEscapeDelta));
143
if (command != Charstring::cSbw)
146
_ncs.append((char)command);
147
if (command > Charstring::cVmoveto && command != Charstring::cHsbw)
153
Type1CharstringGen::gen_stack(CharstringInterp &interp, int for_cmd)
155
const char *str = ((unsigned)for_cmd <= Charstring::cLastCommand ? command_desc[for_cmd] : (const char *)0);
157
for (i = 0; str && *str && i < interp.size(); i++, str++)
158
gen_number(interp.at(i), *str);
159
for (; i < interp.size(); i++)
160
gen_number(interp.at(i));
165
Type1CharstringGen::gen_moveto(const Point &p, bool closepath)
167
// make sure we generate some moveto on the first command
169
double dx = p.x - _false.x;
170
double dy = p.y - _false.y;
171
int big_dx = (int)floor(dx * _f_precision + 0.50001);
172
int big_dy = (int)floor(dy * _f_precision + 0.50001);
174
if (big_dx == 0 && big_dy == 0 && _state != S_INITIAL)
178
gen_command(Charstring::cClosepath);
181
gen_command(Charstring::cHmoveto);
182
} else if (big_dx == 0) {
184
gen_command(Charstring::cVmoveto);
188
gen_command(Charstring::cRmoveto);
197
Type1CharstringGen::append_charstring(const String &s)
203
Type1CharstringGen::output()
205
return new Type1Charstring(take_string());
209
Type1CharstringGen::output(Type1Charstring &cs)
211
cs.assign(take_string());
215
Type1CharstringGen::callsubr_string(int subr)
217
Type1CharstringGen csg;
218
csg.gen_number(subr);
219
csg.gen_command(Charstring::cCallsubr);
220
return csg._ncs.take_string();
225
* Type1CharstringGenInterp
228
Type1CharstringGenInterp::Type1CharstringGenInterp(int precision)
229
: _csgen(precision), _hint_csgen(precision),
230
_direct_hr(false), _hr_storage(0),
231
_max_flex_height(0), _bad_flex(false)
236
Type1CharstringGenInterp::set_hint_replacement_storage(Type1Font *font)
239
_hr_firstsubr = font->nsubrs();
243
// generating charstring commands
246
Type1CharstringGenInterp::gen_number(double n, int what)
248
_csgen.gen_number(n, what);
252
Type1CharstringGenInterp::gen_command(int what)
254
_csgen.gen_command(what);
258
Type1CharstringGenInterp::gen_sbw(bool hints_follow)
260
if (!hints_follow && nhints())
261
act_hintmask(Cs::cHintmask, 0, nhints());
262
else if (left_sidebearing().y == 0 && _width.y == 0) {
263
gen_number(left_sidebearing().x, 'X');
264
gen_number(_width.x);
265
gen_command(Cs::cHsbw);
267
gen_number(left_sidebearing().x, 'X');
268
gen_number(left_sidebearing().y, 'Y');
269
gen_number(_width.x);
270
gen_number(_width.y);
271
gen_command(Cs::cSbw);
277
Type1CharstringGenInterp::act_width(int, const Point &p)
283
Type1CharstringGenInterp::act_seac(int, double asb, double adx, double ady, int bchar, int achar)
285
if (_state == S_INITIAL)
292
gen_command(Cs::cSeac);
297
Type1CharstringGenInterp::swap_stem_hints()
306
Type1CharstringGenInterp::act_hstem(int, double pos, double width)
308
if (_state != S_INITIAL && !_in_hr)
310
_stem_pos.push_back(pos);
311
_stem_width.push_back(width);
312
_stem_hstem.push_back(1);
316
Type1CharstringGenInterp::act_vstem(int, double pos, double width)
318
if (_state != S_INITIAL && !_in_hr)
320
_stem_pos.push_back(pos);
321
_stem_width.push_back(width);
322
_stem_hstem.push_back(0);
326
Type1CharstringGenInterp::gen_hints(const unsigned char *data, int nhints) const
329
unsigned char mask = 0x80;
330
for (int i = 0; i < nhints; i++) {
332
double offset = (_stem_hstem[i] ? left_sidebearing().y : left_sidebearing().x);
333
_hint_csgen.gen_number(_stem_pos[i] - offset);
334
_hint_csgen.gen_number(_stem_width[i]);
335
_hint_csgen.gen_command(_stem_hstem[i] ? Cs::cHstem : Cs::cVstem);
337
if ((mask >>= 1) == 0)
340
return _hint_csgen.take_string();
344
Type1CharstringGenInterp::act_hintmask(int cmd, const unsigned char *data, int nhints)
346
if (cmd == Cs::cCntrmask || nhints > Type1CharstringGenInterp::nhints())
351
data_holder = String::fill_string('\377', ((nhints - 1) >> 3) + 1);
352
data = data_holder.udata();
355
String hints = gen_hints(data, nhints);
358
if (_state == S_INITIAL || _direct_hr) {
360
if (_state == S_INITIAL)
362
_csgen.append_charstring(hints);
363
} else if (_hr_storage && hints != _last_hints) {
365
hints += (char)(Cs::cReturn);
367
int subrno = -1, nsubrs = _hr_storage->nsubrs();
368
for (int i = _hr_firstsubr; i < nsubrs; i++)
369
if (Type1Subr *s = _hr_storage->subr_x(i))
370
if (s->t1cs() == hints) {
375
if (subrno < 0 && _hr_storage->set_subr(nsubrs, Type1Charstring(hints)))
379
_csgen.gen_number(subrno);
380
_csgen.gen_number(4);
381
_csgen.gen_command(Cs::cCallsubr);
387
Type1CharstringGenInterp::act_line(int cmd, const Point &a, const Point &b)
389
if (_state == S_INITIAL)
392
act_hintmask(cmd, 0, nhints());
393
_csgen.gen_moveto(a, _state == S_OPEN);
396
gen_number(b.y - a.y, 'y');
397
gen_command(Cs::cVlineto);
398
} else if (a.y == b.y) {
399
gen_number(b.x - a.x, 'x');
400
gen_command(Cs::cHlineto);
402
gen_number(b.x - a.x, 'x');
403
gen_number(b.y - a.y, 'y');
404
gen_command(Cs::cRlineto);
409
Type1CharstringGenInterp::act_curve(int cmd, const Point &a, const Point &b, const Point &c, const Point &d)
411
if (_state == S_INITIAL)
414
act_hintmask(cmd, 0, nhints());
415
_csgen.gen_moveto(a, _state == S_OPEN);
417
if (b.y == a.y && d.x == c.x) {
418
gen_number(b.x - a.x, 'x');
419
gen_number(c.x - b.x, 'x');
420
gen_number(c.y - b.y, 'y');
421
gen_number(d.y - c.y, 'y');
422
gen_command(Cs::cHvcurveto);
423
} else if (b.x == a.x && d.y == c.y) {
424
gen_number(b.y - a.y, 'y');
425
gen_number(c.x - a.x, 'x');
426
gen_number(c.y - b.y, 'y');
427
gen_number(d.x - c.x, 'x');
428
gen_command(Cs::cVhcurveto);
430
gen_number(b.x - a.x, 'x');
431
gen_number(b.y - a.y, 'y');
432
gen_number(c.x - b.x, 'x');
433
gen_number(c.y - b.y, 'y');
434
gen_number(d.x - c.x, 'x');
435
gen_number(d.y - c.y, 'y');
436
gen_command(Cs::cRrcurveto);
441
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)
443
if (_state == S_INITIAL)
446
act_hintmask(cmd, 0, nhints());
447
_csgen.gen_moveto(p0, _state == S_OPEN);
450
// 1. Outer endpoints must have same x (or y) coordinate
451
bool v_ok = (p0.x == p7.x);
452
bool h_ok = (p0.y == p7.y);
454
// 2. Join point and its neighboring controls must be at an extreme
455
if (v_ok && p2.x == p3_4.x && p3_4.x == p5.x) {
456
double distance = fabs(p3_4.x - p0.x);
457
int sign = (p3_4.x < p0.x ? -1 : 1);
458
if (sign * (p1.x - p0.x) < 0 || sign * (p1.x - p0.x) > distance
459
|| sign * (p6.x - p0.x) < 0 || sign * (p6.x - p0.x) > distance)
464
if (h_ok && p2.y == p3_4.y && p3_4.y == p5.y) {
465
double distance = fabs(p3_4.y - p0.y);
466
int sign = (p3_4.y < p0.y ? -1 : 1);
467
if (sign * (p1.y - p0.y) < 0 || sign * (p1.y - p0.y) > distance
468
|| sign * (p6.y - p0.y) < 0 || sign * (p6.y - p0.y) > distance)
473
// 3. Flex height <= 20
474
if (v_ok && fabs(p3_4.x - p0.x) > 20)
476
if (h_ok && fabs(p3_4.y - p0.y) > 20)
479
// generate flex commands
481
Point p_reference = (h_ok ? Point(p3_4.x, p0.y) : Point(p0.x, p3_4.y));
483
_csgen.gen_number(1);
484
_csgen.gen_command(Cs::cCallsubr);
486
_csgen.gen_moveto(p_reference, false);
487
_csgen.gen_number(2);
488
_csgen.gen_command(Cs::cCallsubr);
490
_csgen.gen_moveto(p1, false);
491
_csgen.gen_number(2);
492
_csgen.gen_command(Cs::cCallsubr);
494
_csgen.gen_moveto(p2, false);
495
_csgen.gen_number(2);
496
_csgen.gen_command(Cs::cCallsubr);
498
_csgen.gen_moveto(p3_4, false);
499
_csgen.gen_number(2);
500
_csgen.gen_command(Cs::cCallsubr);
502
_csgen.gen_moveto(p5, false);
503
_csgen.gen_number(2);
504
_csgen.gen_command(Cs::cCallsubr);
506
_csgen.gen_moveto(p6, false);
507
_csgen.gen_number(2);
508
_csgen.gen_command(Cs::cCallsubr);
510
_csgen.gen_moveto(p7, false);
511
_csgen.gen_number(2);
512
_csgen.gen_command(Cs::cCallsubr);
514
_csgen.gen_number(flex_depth);
515
_csgen.gen_number(p7.x, 'X');
516
_csgen.gen_number(p7.y, 'Y');
517
_csgen.gen_number(0);
518
_csgen.gen_command(Cs::cCallsubr);
520
double flex_height = fabs(h_ok ? p3_4.y - p0.y : p3_4.x - p0.x);
521
if (flex_height > _max_flex_height)
522
_max_flex_height = flex_height;
525
act_curve(cmd, p0, p1, p2, p3_4);
526
act_curve(cmd, p3_4, p5, p6, p7);
531
Type1CharstringGenInterp::act_closepath(int cmd)
534
act_hintmask(cmd, 0, nhints());
535
gen_command(Cs::cClosepath);
540
Type1CharstringGenInterp::intermediate_output(Type1Charstring &out)
544
act_hintmask(Cs::cEndchar, 0, nhints());
548
Type1CharstringGenInterp::run(const CharstringContext &g, Type1Charstring &out)
550
_width = Point(0, 0);
556
CharstringInterp::interpret(g);
558
if (_state == S_INITIAL)
561
act_hintmask(Cs::cEndchar, 0, nhints());
562
if (_state != S_SEAC)
563
_csgen.gen_command(Cs::cEndchar);