UNPKG

10.2 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * JavaScript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.Type1Font = void 0;
28
29var _cff_parser = require("./cff_parser.js");
30
31var _fonts_utils = require("./fonts_utils.js");
32
33var _core_utils = require("./core_utils.js");
34
35var _stream = require("./stream.js");
36
37var _type1_parser = require("./type1_parser.js");
38
39var _util = require("../shared/util.js");
40
41function 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
75function 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
133function getEexecBlock(stream, suggestedLength) {
134 const eexecBytes = stream.getBytes();
135 return {
136 stream: new _stream.Stream(eexecBytes),
137 length: eexecBytes.length
138 };
139}
140
141class 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
382exports.Type1Font = Type1Font;
\No newline at end of file