1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | Object.defineProperty(exports, "__esModule", {
|
25 | value: true
|
26 | });
|
27 | exports.Type1Font = void 0;
|
28 |
|
29 | var _cff_parser = require("./cff_parser.js");
|
30 |
|
31 | var _fonts_utils = require("./fonts_utils.js");
|
32 |
|
33 | var _core_utils = require("./core_utils.js");
|
34 |
|
35 | var _stream = require("./stream.js");
|
36 |
|
37 | var _type1_parser = require("./type1_parser.js");
|
38 |
|
39 | var _util = require("../shared/util.js");
|
40 |
|
41 | function findBlock(streamBytes, signature, startIndex) {
|
42 | const streamBytesLength = streamBytes.length;
|
43 | const signatureLength = signature.length;
|
44 | const scanLength = streamBytesLength - signatureLength;
|
45 | let i = startIndex,
|
46 | found = false;
|
47 |
|
48 | while (i < scanLength) {
|
49 | let j = 0;
|
50 |
|
51 | while (j < signatureLength && streamBytes[i + j] === signature[j]) {
|
52 | j++;
|
53 | }
|
54 |
|
55 | if (j >= signatureLength) {
|
56 | i += j;
|
57 |
|
58 | while (i < streamBytesLength && (0, _core_utils.isWhiteSpace)(streamBytes[i])) {
|
59 | i++;
|
60 | }
|
61 |
|
62 | found = true;
|
63 | break;
|
64 | }
|
65 |
|
66 | i++;
|
67 | }
|
68 |
|
69 | return {
|
70 | found,
|
71 | length: i
|
72 | };
|
73 | }
|
74 |
|
75 | function getHeaderBlock(stream, suggestedLength) {
|
76 | const EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63];
|
77 | const streamStartPos = stream.pos;
|
78 | let headerBytes, headerBytesLength, block;
|
79 |
|
80 | try {
|
81 | headerBytes = stream.getBytes(suggestedLength);
|
82 | headerBytesLength = headerBytes.length;
|
83 | } catch (ex) {}
|
84 |
|
85 | if (headerBytesLength === suggestedLength) {
|
86 | block = findBlock(headerBytes, EEXEC_SIGNATURE, suggestedLength - 2 * EEXEC_SIGNATURE.length);
|
87 |
|
88 | if (block.found && block.length === suggestedLength) {
|
89 | return {
|
90 | stream: new _stream.Stream(headerBytes),
|
91 | length: suggestedLength
|
92 | };
|
93 | }
|
94 | }
|
95 |
|
96 | (0, _util.warn)('Invalid "Length1" property in Type1 font -- trying to recover.');
|
97 | stream.pos = streamStartPos;
|
98 | const SCAN_BLOCK_LENGTH = 2048;
|
99 | let actualLength;
|
100 |
|
101 | while (true) {
|
102 | const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);
|
103 | block = findBlock(scanBytes, EEXEC_SIGNATURE, 0);
|
104 |
|
105 | if (block.length === 0) {
|
106 | break;
|
107 | }
|
108 |
|
109 | stream.pos += block.length;
|
110 |
|
111 | if (block.found) {
|
112 | actualLength = stream.pos - streamStartPos;
|
113 | break;
|
114 | }
|
115 | }
|
116 |
|
117 | stream.pos = streamStartPos;
|
118 |
|
119 | if (actualLength) {
|
120 | return {
|
121 | stream: new _stream.Stream(stream.getBytes(actualLength)),
|
122 | length: actualLength
|
123 | };
|
124 | }
|
125 |
|
126 | (0, _util.warn)('Unable to recover "Length1" property in Type1 font -- using as is.');
|
127 | return {
|
128 | stream: new _stream.Stream(stream.getBytes(suggestedLength)),
|
129 | length: suggestedLength
|
130 | };
|
131 | }
|
132 |
|
133 | function getEexecBlock(stream, suggestedLength) {
|
134 | const eexecBytes = stream.getBytes();
|
135 | return {
|
136 | stream: new _stream.Stream(eexecBytes),
|
137 | length: eexecBytes.length
|
138 | };
|
139 | }
|
140 |
|
141 | class Type1Font {
|
142 | constructor(name, file, properties) {
|
143 | const PFB_HEADER_SIZE = 6;
|
144 | let headerBlockLength = properties.length1;
|
145 | let eexecBlockLength = properties.length2;
|
146 | let pfbHeader = file.peekBytes(PFB_HEADER_SIZE);
|
147 | const pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01;
|
148 |
|
149 | if (pfbHeaderPresent) {
|
150 | file.skip(PFB_HEADER_SIZE);
|
151 | headerBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
|
152 | }
|
153 |
|
154 | const headerBlock = getHeaderBlock(file, headerBlockLength);
|
155 | const headerBlockParser = new _type1_parser.Type1Parser(headerBlock.stream, false, _fonts_utils.SEAC_ANALYSIS_ENABLED);
|
156 | headerBlockParser.extractFontHeader(properties);
|
157 |
|
158 | if (pfbHeaderPresent) {
|
159 | pfbHeader = file.getBytes(PFB_HEADER_SIZE);
|
160 | eexecBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
|
161 | }
|
162 |
|
163 | const eexecBlock = getEexecBlock(file, eexecBlockLength);
|
164 | const eexecBlockParser = new _type1_parser.Type1Parser(eexecBlock.stream, true, _fonts_utils.SEAC_ANALYSIS_ENABLED);
|
165 | const data = eexecBlockParser.extractFontProgram(properties);
|
166 |
|
167 | for (const key in data.properties) {
|
168 | properties[key] = data.properties[key];
|
169 | }
|
170 |
|
171 | const charstrings = data.charstrings;
|
172 | const type2Charstrings = this.getType2Charstrings(charstrings);
|
173 | const subrs = this.getType2Subrs(data.subrs);
|
174 | this.charstrings = charstrings;
|
175 | this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties);
|
176 | this.seacs = this.getSeacs(data.charstrings);
|
177 | }
|
178 |
|
179 | get numGlyphs() {
|
180 | return this.charstrings.length + 1;
|
181 | }
|
182 |
|
183 | getCharset() {
|
184 | const charset = [".notdef"];
|
185 |
|
186 | for (const {
|
187 | glyphName
|
188 | } of this.charstrings) {
|
189 | charset.push(glyphName);
|
190 | }
|
191 |
|
192 | return charset;
|
193 | }
|
194 |
|
195 | getGlyphMapping(properties) {
|
196 | const charstrings = this.charstrings;
|
197 |
|
198 | if (properties.composite) {
|
199 | const charCodeToGlyphId = Object.create(null);
|
200 |
|
201 | for (let glyphId = 0, charstringsLen = charstrings.length; glyphId < charstringsLen; glyphId++) {
|
202 | const charCode = properties.cMap.charCodeOf(glyphId);
|
203 | charCodeToGlyphId[charCode] = glyphId + 1;
|
204 | }
|
205 |
|
206 | return charCodeToGlyphId;
|
207 | }
|
208 |
|
209 | const glyphNames = [".notdef"];
|
210 | let builtInEncoding, glyphId;
|
211 |
|
212 | for (glyphId = 0; glyphId < charstrings.length; glyphId++) {
|
213 | glyphNames.push(charstrings[glyphId].glyphName);
|
214 | }
|
215 |
|
216 | const encoding = properties.builtInEncoding;
|
217 |
|
218 | if (encoding) {
|
219 | builtInEncoding = Object.create(null);
|
220 |
|
221 | for (const charCode in encoding) {
|
222 | glyphId = glyphNames.indexOf(encoding[charCode]);
|
223 |
|
224 | if (glyphId >= 0) {
|
225 | builtInEncoding[charCode] = glyphId;
|
226 | }
|
227 | }
|
228 | }
|
229 |
|
230 | return (0, _fonts_utils.type1FontGlyphMapping)(properties, builtInEncoding, glyphNames);
|
231 | }
|
232 |
|
233 | hasGlyphId(id) {
|
234 | if (id < 0 || id >= this.numGlyphs) {
|
235 | return false;
|
236 | }
|
237 |
|
238 | if (id === 0) {
|
239 | return true;
|
240 | }
|
241 |
|
242 | const glyph = this.charstrings[id - 1];
|
243 | return glyph.charstring.length > 0;
|
244 | }
|
245 |
|
246 | getSeacs(charstrings) {
|
247 | const seacMap = [];
|
248 |
|
249 | for (let i = 0, ii = charstrings.length; i < ii; i++) {
|
250 | const charstring = charstrings[i];
|
251 |
|
252 | if (charstring.seac) {
|
253 | seacMap[i + 1] = charstring.seac;
|
254 | }
|
255 | }
|
256 |
|
257 | return seacMap;
|
258 | }
|
259 |
|
260 | getType2Charstrings(type1Charstrings) {
|
261 | const type2Charstrings = [];
|
262 |
|
263 | for (let i = 0, ii = type1Charstrings.length; i < ii; i++) {
|
264 | type2Charstrings.push(type1Charstrings[i].charstring);
|
265 | }
|
266 |
|
267 | return type2Charstrings;
|
268 | }
|
269 |
|
270 | getType2Subrs(type1Subrs) {
|
271 | let bias = 0;
|
272 | const count = type1Subrs.length;
|
273 |
|
274 | if (count < 1133) {
|
275 | bias = 107;
|
276 | } else if (count < 33769) {
|
277 | bias = 1131;
|
278 | } else {
|
279 | bias = 32768;
|
280 | }
|
281 |
|
282 | const type2Subrs = [];
|
283 | let i;
|
284 |
|
285 | for (i = 0; i < bias; i++) {
|
286 | type2Subrs.push([0x0b]);
|
287 | }
|
288 |
|
289 | for (i = 0; i < count; i++) {
|
290 | type2Subrs.push(type1Subrs[i]);
|
291 | }
|
292 |
|
293 | return type2Subrs;
|
294 | }
|
295 |
|
296 | wrap(name, glyphs, charstrings, subrs, properties) {
|
297 | const cff = new _cff_parser.CFF();
|
298 | cff.header = new _cff_parser.CFFHeader(1, 0, 4, 4);
|
299 | cff.names = [name];
|
300 | const topDict = new _cff_parser.CFFTopDict();
|
301 | topDict.setByName("version", 391);
|
302 | topDict.setByName("Notice", 392);
|
303 | topDict.setByName("FullName", 393);
|
304 | topDict.setByName("FamilyName", 394);
|
305 | topDict.setByName("Weight", 395);
|
306 | topDict.setByName("Encoding", null);
|
307 | topDict.setByName("FontMatrix", properties.fontMatrix);
|
308 | topDict.setByName("FontBBox", properties.bbox);
|
309 | topDict.setByName("charset", null);
|
310 | topDict.setByName("CharStrings", null);
|
311 | topDict.setByName("Private", null);
|
312 | cff.topDict = topDict;
|
313 | const strings = new _cff_parser.CFFStrings();
|
314 | strings.add("Version 0.11");
|
315 | strings.add("See original notice");
|
316 | strings.add(name);
|
317 | strings.add(name);
|
318 | strings.add("Medium");
|
319 | cff.strings = strings;
|
320 | cff.globalSubrIndex = new _cff_parser.CFFIndex();
|
321 | const count = glyphs.length;
|
322 | const charsetArray = [".notdef"];
|
323 | let i, ii;
|
324 |
|
325 | for (i = 0; i < count; i++) {
|
326 | const glyphName = charstrings[i].glyphName;
|
327 |
|
328 | const index = _cff_parser.CFFStandardStrings.indexOf(glyphName);
|
329 |
|
330 | if (index === -1) {
|
331 | strings.add(glyphName);
|
332 | }
|
333 |
|
334 | charsetArray.push(glyphName);
|
335 | }
|
336 |
|
337 | cff.charset = new _cff_parser.CFFCharset(false, 0, charsetArray);
|
338 | const charStringsIndex = new _cff_parser.CFFIndex();
|
339 | charStringsIndex.add([0x8b, 0x0e]);
|
340 |
|
341 | for (i = 0; i < count; i++) {
|
342 | charStringsIndex.add(glyphs[i]);
|
343 | }
|
344 |
|
345 | cff.charStrings = charStringsIndex;
|
346 | const privateDict = new _cff_parser.CFFPrivateDict();
|
347 | privateDict.setByName("Subrs", null);
|
348 | const fields = ["BlueValues", "OtherBlues", "FamilyBlues", "FamilyOtherBlues", "StemSnapH", "StemSnapV", "BlueShift", "BlueFuzz", "BlueScale", "LanguageGroup", "ExpansionFactor", "ForceBold", "StdHW", "StdVW"];
|
349 |
|
350 | for (i = 0, ii = fields.length; i < ii; i++) {
|
351 | const field = fields[i];
|
352 |
|
353 | if (!(field in properties.privateData)) {
|
354 | continue;
|
355 | }
|
356 |
|
357 | const value = properties.privateData[field];
|
358 |
|
359 | if (Array.isArray(value)) {
|
360 | for (let j = value.length - 1; j > 0; j--) {
|
361 | value[j] -= value[j - 1];
|
362 | }
|
363 | }
|
364 |
|
365 | privateDict.setByName(field, value);
|
366 | }
|
367 |
|
368 | cff.topDict.privateDict = privateDict;
|
369 | const subrIndex = new _cff_parser.CFFIndex();
|
370 |
|
371 | for (i = 0, ii = subrs.length; i < ii; i++) {
|
372 | subrIndex.add(subrs[i]);
|
373 | }
|
374 |
|
375 | privateDict.subrsIndex = subrIndex;
|
376 | const compiler = new _cff_parser.CFFCompiler(cff);
|
377 | return compiler.compile();
|
378 | }
|
379 |
|
380 | }
|
381 |
|
382 | exports.Type1Font = Type1Font; |
\ | No newline at end of file |