1 // -*- related-file-name: "../include/efont/otfpost.hh" -*-
2
3 /* otfpost.{cc,hh} -- OpenType post table
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/otfpost.hh>
20 #include <lcdf/error.hh>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <efont/otfdata.hh> // for ntohl()
25
26 #define USHORT_AT(d) (Data::u16_aligned(d))
27 #define SHORT_AT(d) (Data::s16_aligned(d))
28 #define ULONG_AT(d) (Data::u32_aligned(d))
29 #define LONG_AT(d) (Data::s32_aligned(d))
30
31 namespace Efont { namespace OpenType {
32
33 static const char * const mac_names[] = {
34 ".notdef", ".null", "nonmarkingreturn", "space", // 0-3
35 "exclam", "quotedbl", "numbersign", "dollar", // 4-7
36 "percent", "ampersand", "quotesingle", "parenleft", // 8-11
37 "parenright", "asterisk", "plus", "comma", // 12-15
38 "hyphen", "period", "slash", "zero", // 16-19
39 "one", "two", "three", "four", // 20-23
40 "five", "six", "seven", "eight", // 24-27
41 "nine", "colon", "semicolon", "less", // 28-31
42 "equal", "greater", "question", "at", // 32-35
43 "A", "B", "C", "D", // 36-39
44 "E", "F", "G", "H", // 40-43
45 "I", "J", "K", "L", // 44-47
46 "M", "N", "O", "P", // 48-51
47 "Q", "R", "S", "T", // 52-55
48 "U", "V", "W", "X", // 56-59
49 "Y", "Z", "bracketleft", "backslash", // 60-63
50 "bracketright", "asciicircum", "underscore", "grave", // 64-67
51 "a", "b", "c", "d", // 68-71
52 "e", "f", "g", "h", // 72-75
53 "i", "j", "k", "l", // 76-79
54 "m", "n", "o", "p", // 80-83
55 "q", "r", "s", "t", // 84-87
56 "u", "v", "w", "x", // 88-91
57 "y", "z", "braceleft", "bar", // 92-95
58 "braceright", "asciitilde", "Adieresis", "Aring", // 96-99
59 "Ccedilla", "Eacute", "Ntilde", "Odieresis", // 100-103
60 "Udieresis", "aacute", "agrave", "acircumflex", // 104-107
61 "adieresis", "atilde", "aring", "ccedilla", // 108-111
62 "eacute", "egrave", "ecircumflex", "edieresis", // 112-115
63 "iacute", "igrave", "icircumflex", "idieresis", // 116-119
64 "ntilde", "oacute", "ograve", "ocircumflex", // 120-123
65 "odieresis", "otilde", "uacute", "ugrave", // 124-127
66 "ucircumflex", "udieresis", "dagger", "degree", // 128-131
67 "cent", "sterling", "section", "bullet", // 132-135
68 "paragraph", "germandbls", "registered", "copyright", // 136-139
69 "trademark", "acute", "dieresis", "notequal", // 140-143
70 "AE", "Oslash", "infinity", "plusminus", // 144-147
71 "lessequal", "greaterequal", "yen", "mu", // 148-151
72 "partialdiff", "summation", "product", "pi", // 152-155
73 "integral", "ordfeminine", "ordmasculine", "Omega", // 156-159
74 "ae", "oslash", "questiondown", "exclamdown", // 160-163
75 "logicalnot", "radical", "florin", "approxequal", // 164-167
76 "Delta", "guillemotleft", "guillemotright", "ellipsis", // 168-171
77 "nonbreakingspace", "Agrave", "Atilde", "Otilde", // 172-175
78 "OE", "oe", "endash", "emdash", // 176-179
79 "quotedblleft", "quotedblright", "quoteleft", "quoteright", // 180-183
80 "divide", "lozenge", "ydieresis", "Ydieresis", // 184-187
81 "fraction", "currency", "guilsinglleft", "guilsinglright", // 188-191
82 "fi", "fl", "daggerdbl", "periodcentered", // 192-195
83 "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", // 196-199
84 "Ecircumflex", "Aacute", "Edieresis", "Egrave", // 200-203
85 "Iacute", "Icircumflex", "Idieresis", "Igrave", // 204-207
86 "Oacute", "Ocircumflex", "apple", "Ograve", // 208-211
87 "Uacute", "Ucircumflex", "Ugrave", "dotlessi", // 212-215
88 "circumflex", "tilde", "macron", "breve", // 216-219
89 "dotaccent", "ring", "cedilla", "hungarumlaut", // 220-223
90 "ogonek", "caron", "Lslash", "lslash", // 224-227
91 "Scaron", "scaron", "Zcaron", "zcaron", // 228-231
92 "brokenbar", "Eth", "eth", "Yacute", // 232-235
93 "yacute", "Thorn", "thorn", "minus", // 236-239
94 "multiply", "onesuperior", "twosuperior", "threesuperior", // 240-243
95 "onehalf", "onequarter", "threequarters", "franc", // 244-247
96 "Gbreve", "gbreve", "Idotaccent", "Scedilla", // 248-251
97 "scedilla", "Cacute", "cacute", "Ccaron", // 252-255
98 "ccaron", "dcroat" // 256-257
99 };
100
101
Post(const String & s,ErrorHandler * errh)102 Post::Post(const String &s, ErrorHandler *errh)
103 : _str(s), _version(0)
104 {
105 _str.align_long();
106 _error = parse_header(errh ? errh : ErrorHandler::silent_handler());
107 }
108
109 int
parse_header(ErrorHandler * errh)110 Post::parse_header(ErrorHandler *errh)
111 {
112 // HEADER FORMAT:
113 // 0 FIXED version
114 // 4 FIXED italicAngle
115 // 8 FWORD underlinePosition
116 // 10 FWORD underlineThickness
117 // 12 ULONG isFixedPitch
118 // 16 ULONG minMemType42
119 // 20 ULONG maxMemType42
120 // 24 ULONG minMemType1
121 // 28 ULONG maxMemType1
122 int len = _str.length();
123 const uint8_t *data = _str.udata();
124 if (HEADER_SIZE > len)
125 return errh->error("OTF post table too small"), -EFAULT;
126 _version = USHORT_AT(data); // ignore minor version number
127 // except that version 2.5 isn't compatible
128 if (_version < 1 || _version > 3
129 || (_version == 2 && USHORT_AT(data + 2) == 0x5000))
130 return errh->error("bad post version number"), -ERANGE;
131 if (_version == 2) {
132 // VERSION 2.0 GLYPH NAMES FORMAT:
133 // 32 USHORT numberOfGlyphs
134 // 34 USHORT glyphNameIndex[mumberOfGlyphs]
135 // CHAR names[...]
136 if (HEADER_SIZE + 2 > len
137 || ((_nglyphs = USHORT_AT(data + HEADER_SIZE)),
138 HEADER_SIZE + 2 + 2 * _nglyphs > len))
139 return errh->error("OTF post table too small for glyph map"), -EFAULT;
140 int pos = HEADER_SIZE + 2 + 2 * _nglyphs;
141 while (pos < len && pos + data[pos] < len) {
142 _extend_glyph_names.push_back(pos);
143 pos += 1 + data[pos];
144 }
145 const uint8_t *gni = data + HEADER_SIZE + 2;
146 for (int i = 0, g; i < _nglyphs; ++i, gni += 2)
147 if ((g = USHORT_AT(gni)) >= _extend_glyph_names.size() + N_MAC_GLYPHS)
148 return errh->error("bad glyph name index in post");
149 } else if (_version == 1)
150 _nglyphs = N_MAC_GLYPHS;
151 else
152 _nglyphs = -1;
153
154 return 0;
155 }
156
157 double
italic_angle() const158 Post::italic_angle() const
159 {
160 if (error() < 0)
161 return -1;
162 return (double) LONG_AT(_str.udata() + 4) / 65536.;
163 }
164
165 bool
is_fixed_pitch() const166 Post::is_fixed_pitch() const
167 {
168 if (error() < 0)
169 return false;
170 return ULONG_AT(_str.udata() + 12) != 0;
171 }
172
173 bool
glyph_names(Vector<PermString> & gnames) const174 Post::glyph_names(Vector<PermString> &gnames) const
175 {
176 gnames.clear();
177 if (error() < 0)
178 return false;
179 if (_version == 1) {
180 for (int i = 0; i < N_MAC_GLYPHS; i++)
181 gnames.push_back(mac_names[i]);
182 return true;
183 } else if (_version == 2) {
184 const uint8_t *data = _str.udata();
185 const uint8_t *gni = data + HEADER_SIZE + 2;
186 for (int i = 0; i < _nglyphs; i++, gni += 2) {
187 int g = USHORT_AT(gni);
188 if (g < N_MAC_GLYPHS)
189 gnames.push_back(mac_names[g]);
190 else {
191 const uint8_t *n = data + _extend_glyph_names[g - N_MAC_GLYPHS];
192 gnames.push_back(PermString((const char *) n + 1, *n));
193 }
194 }
195 return true;
196 } else
197 return false;
198 }
199
200 }}
201