1 // -*- related-file-name: "../include/efont/ttfcs.hh" -*-
2
3 /* ttfcs.{cc,hh} -- TrueType "charstring" emulation
4 *
5 * Copyright (c) 2006-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/ttfcs.hh>
20 #include <efont/ttfhead.hh>
21 #include <efont/t1csgen.hh>
22 #include <efont/otfpost.hh>
23 #include <efont/otfcmap.hh>
24 #include <lcdf/hashmap.hh>
25 #include <algorithm>
26 namespace Efont {
27 typedef OpenType::Glyph Glyph;
28
TrueTypeBoundsCharstringProgram(const OpenType::Font * otf)29 TrueTypeBoundsCharstringProgram::TrueTypeBoundsCharstringProgram(const OpenType::Font* otf)
30 : CharstringProgram(otf->units_per_em()),
31 _otf(otf), _nglyphs(-1), _loca_long(false),
32 _loca(otf->table("loca")), _glyf(otf->table("glyf")),
33 _hmtx(otf->table("hmtx")), _got_glyph_names(false), _got_unicodes(false)
34 {
35 OpenType::Data maxp(otf->table("maxp"));
36 if (maxp.length() >= 6)
37 _nglyphs = maxp.u16(4);
38
39 OpenType::Head head(otf->table("head"), 0);
40 if (head.ok())
41 _loca_long = head.index_to_loc_format() != 0;
42 if (_loca_long)
43 _loca.align_long();
44 int loca_onesize = (_loca_long ? 4 : 2);
45 if (_nglyphs >= _loca.length() / loca_onesize)
46 _nglyphs = (_loca.length() / loca_onesize) - 1;
47
48 // horizontal metrics
49 OpenType::Data hhea(_otf->table("hhea"));
50 // HHEA format:
51 // 0 Fixed Table version number 0x00010000 for version 1.0.
52 // 4 FWORD Ascender
53 // 6 FWORD Descender
54 // 8 FWORD LineGap
55 // 10 UFWORD advanceWidthMax
56 // 12 FWORD minLeftSideBearing
57 // 14 FWORD minRightSideBearing
58 // 16 FWORD xMaxExtent
59 // 18 SHORT caretSlopeRise
60 // 20 SHORT caretSlopeRun
61 // 22 SHORT caretOffset
62 // 24 SHORT (reserved)
63 // 26 SHORT (reserved)
64 // 28 SHORT (reserved)
65 // 30 SHORT (reserved)
66 // 32 SHORT metricDataFormat
67 // 34 USHORT numberOfHMetrics
68 if (hhea.length() >= 36
69 && hhea.u32(0) == 0x10000)
70 _nhmtx = hhea.u16(34);
71 if (_nhmtx * 4 > _hmtx.length())
72 _nhmtx = _hmtx.length() / 4;
73 }
74
~TrueTypeBoundsCharstringProgram()75 TrueTypeBoundsCharstringProgram::~TrueTypeBoundsCharstringProgram()
76 {
77 for (Charstring **cs = _charstrings.begin(); cs < _charstrings.end(); cs++)
78 delete *cs;
79 }
80
font_matrix(double matrix[6]) const81 void TrueTypeBoundsCharstringProgram::font_matrix(double matrix[6]) const {
82 matrix[0] = matrix[3] = 1.0 / _otf->units_per_em();
83 matrix[1] = matrix[2] = matrix[4] = matrix[5] = 0;
84 }
85
86 int
nglyphs() const87 TrueTypeBoundsCharstringProgram::nglyphs() const
88 {
89 return _nglyphs;
90 }
91
92 PermString
glyph_name(int gi) const93 TrueTypeBoundsCharstringProgram::glyph_name(int gi) const
94 {
95 // generate glyph names based on what pdftex can understand
96 if (gi == 0)
97 return PermString(".notdef");
98
99 // try 'post' table glyph names
100 if (!_got_glyph_names) {
101 OpenType::Post post(_otf->table("post"));
102 if (post.ok())
103 post.glyph_names(_glyph_names);
104 HashMap<PermString, int> name2glyph(-1);
105 // some 'post' tables are bogus, reject multiply-encoded names
106 for (int gi = 0; gi < _glyph_names.size(); ++gi) {
107 int& xgi = name2glyph.find_force(_glyph_names[gi]);
108 if (xgi == -1)
109 xgi = gi;
110 else if (xgi == 0)
111 _glyph_names[gi] = PermString();
112 else
113 _glyph_names[gi] = _glyph_names[xgi] = PermString();
114 }
115 _got_glyph_names = true;
116 }
117 if (gi >= 0 && gi < _glyph_names.size() && _glyph_names[gi])
118 return _glyph_names[gi];
119
120 // try 'uniXXXX' names
121 if (!_got_unicodes) {
122 OpenType::Cmap cmap(_otf->table("cmap"));
123 if (cmap.ok()) {
124 Vector<std::pair<uint32_t, Glyph> > ugp;
125 cmap.unmap_all(ugp);
126 std::sort(ugp.begin(), ugp.end());
127 for (Vector<std::pair<uint32_t, Glyph> >::iterator it = ugp.begin();
128 it != ugp.end(); ) {
129 Vector<std::pair<uint32_t, Glyph> >::iterator nit = it + 1;
130 // ignore code points with multiple glyph mappings
131 if (nit == ugp.end() || nit->first != it->first) {
132 if (it->second >= _unicodes.size())
133 _unicodes.resize(it->second + 1, 0);
134 if (!_unicodes[it->second])
135 _unicodes[it->second] = it->first;
136 } else
137 while (nit != ugp.end() && nit->first == it->first)
138 ++nit;
139 it = nit;
140 }
141 }
142 _got_unicodes = true;
143 }
144
145 if (gi >= 0 && gi < _unicodes.size() && _unicodes[gi] > 0 && _unicodes[gi] <= 0xFFFF) {
146 char buf[10];
147 sprintf(buf, "uni%04X", _unicodes[gi]);
148 return PermString(buf);
149 } else
150 return permprintf("index%d", gi);
151 }
152
153 void
glyph_names(Vector<PermString> & gn) const154 TrueTypeBoundsCharstringProgram::glyph_names(Vector<PermString> &gn) const
155 {
156 gn.clear();
157 for (int gi = 0; gi < _nglyphs; gi++)
158 gn.push_back(glyph_name(gi));
159 }
160
161 Charstring *
glyph(int gi) const162 TrueTypeBoundsCharstringProgram::glyph(int gi) const
163 {
164 if (gi < 0 || gi >= _nglyphs)
165 return 0;
166 if (_charstrings.size() <= gi)
167 _charstrings.resize(gi + 1, (Charstring *) 0);
168 if (!_charstrings[gi]) {
169 // calculate glyf offsets
170 uint32_t offset, end_offset;
171 if (_loca_long) {
172 offset = _loca.u32(gi * 4);
173 end_offset = _loca.u32(gi * 4 + 4);
174 } else {
175 offset = _loca.u16(gi * 2) * 2;
176 end_offset = _loca.u16(gi * 2 + 2) * 2;
177 }
178
179 // fetch bounding box from glyf
180 int ncontours, xmin, ymin, xmax, ymax;
181 if (offset != end_offset) {
182 if (offset > end_offset || offset + 10 > end_offset
183 || end_offset > (uint32_t) _glyf.length())
184 return 0;
185
186 ncontours = _glyf.s16(offset);
187 xmin = _glyf.s16(offset + 2);
188 ymin = _glyf.s16(offset + 4);
189 xmax = _glyf.s16(offset + 6);
190 ymax = _glyf.s16(offset + 8);
191 } else
192 ncontours = xmin = ymin = xmax = ymax = 0;
193
194 // fetch horizontal metrics
195 int advance_width, lsb;
196 if (gi >= _nhmtx) {
197 advance_width = (_nhmtx ? _hmtx.u16((_nhmtx - 1) * 4) : 0);
198 int hmtx_offset = _nhmtx * 4 + (gi - _nhmtx) * 2;
199 lsb = (hmtx_offset + 2 <= _hmtx.length() ? _hmtx.s16(hmtx_offset) : 0);
200 } else {
201 advance_width = _hmtx.u16(gi * 4);
202 lsb = _hmtx.s16(gi * 4 + 2);
203 }
204
205 // make charstring
206 Type1CharstringGen gen;
207 if (ncontours == 0) {
208 gen.gen_number(0, 'X');
209 gen.gen_number(advance_width);
210 gen.gen_command(Charstring::cHsbw);
211 } else {
212 gen.gen_number(lsb, 'X');
213 gen.gen_number(advance_width);
214 gen.gen_command(Charstring::cHsbw);
215 gen.gen_moveto(Point(xmin, ymin), false, false);
216 if (xmax != xmin || ymax == ymin)
217 gen.gen_number(xmax - xmin, 'x');
218 if (ymax != ymin)
219 gen.gen_number(ymax - ymin, 'y');
220 gen.gen_command(ymax == ymin ? Charstring::cHlineto
221 : (xmax == xmin ? Charstring::cVlineto
222 : Charstring::cRlineto));
223 gen.gen_command(Charstring::cClosepath);
224 }
225 gen.gen_command(Charstring::cEndchar);
226
227 _charstrings[gi] = gen.output();
228 }
229 return _charstrings[gi];
230 }
231
232 }
233