/src/ghostpdl/xps/xpscff.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* XPS interpreter - cff font support */ |
18 | | |
19 | | #include "ghostxps.h" |
20 | | |
21 | 0 | #define CFF_ARGS_SIZE 48 |
22 | | |
23 | | /* |
24 | | * Big-endian memory accessor functions |
25 | | */ |
26 | | |
27 | | static inline int s16(byte *p) |
28 | 0 | { |
29 | 0 | return (signed short)( (p[0] << 8) | p[1] ); |
30 | 0 | } |
31 | | |
32 | | static inline int u16(byte *p) |
33 | 0 | { |
34 | 0 | return (p[0] << 8) | p[1]; |
35 | 0 | } |
36 | | |
37 | | static inline int u24(byte *p) |
38 | 0 | { |
39 | 0 | return (p[0] << 16) | (p[1] << 8) | p[2]; |
40 | 0 | } |
41 | | |
42 | | static inline int u32(byte *p) |
43 | 0 | { |
44 | 0 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; |
45 | 0 | } |
46 | | |
47 | | /* |
48 | | * OpenType Tables |
49 | | * |
50 | | * Required: cmap, head, hhea, hmtx, maxp, name, OS/2, post |
51 | | * TrueType: cvt, fpgm, glyf, loca, prep |
52 | | * Postscript: CFF, VORG |
53 | | * Typographic: BASE, GDEF, GPOS, GSUB, JSTF |
54 | | * Other: DSIG, gasp, hdmx, kern, LTSH, PCLT, VDMX, vhea, vmtx |
55 | | */ |
56 | | |
57 | | static byte * xps_count_cff_index(byte *p, byte *e, int *countp); |
58 | | static byte * xps_find_cff_index(byte *p, byte *e, int idx, byte **pp, byte **ep); |
59 | | |
60 | | static int subrbias(int count) |
61 | 0 | { |
62 | 0 | return count < 1240 ? 107 : count < 33900 ? 1131 : 32768; |
63 | 0 | } |
64 | | |
65 | | static int uofs(byte *p, int offsize) |
66 | 0 | { |
67 | 0 | if (offsize == 1) return p[0]; |
68 | 0 | if (offsize == 2) return u16(p); |
69 | 0 | if (offsize == 3) return u24(p); |
70 | 0 | if (offsize == 4) return u32(p); |
71 | 0 | return 0; |
72 | 0 | } |
73 | | |
74 | | static byte * |
75 | | xps_read_cff_real(byte *p, byte *e, float *val) |
76 | 0 | { |
77 | 0 | char buf[64]; |
78 | 0 | char *txt = buf; |
79 | | |
80 | | /* b0 was 30 */ |
81 | |
|
82 | 0 | while (txt < buf + (sizeof buf) - 4 && p < e) |
83 | 0 | { |
84 | 0 | int b, n; |
85 | |
|
86 | 0 | b = *p++; |
87 | |
|
88 | 0 | n = (b >> 4) & 0xf; |
89 | 0 | if (n < 0xA) { *txt++ = n + '0'; } |
90 | 0 | else if (n == 0xA) { *txt++ = '.'; } |
91 | 0 | else if (n == 0xB) { *txt++ = 'E'; } |
92 | 0 | else if (n == 0xC) { *txt++ = 'E'; *txt++ = '-'; } |
93 | 0 | else if (n == 0xE) { *txt++ = '-'; } |
94 | 0 | else if (n == 0xF) { break; } |
95 | | |
96 | 0 | n = b & 0xf; |
97 | 0 | if (n < 0xA) { *txt++ = n + '0'; } |
98 | 0 | else if (n == 0xA) { *txt++ = '.'; } |
99 | 0 | else if (n == 0xB) { *txt++ = 'E'; } |
100 | 0 | else if (n == 0xC) { *txt++ = 'E'; *txt++ = '-'; } |
101 | 0 | else if (n == 0xE) { *txt++ = '-'; } |
102 | 0 | else if (n == 0xF) { break; } |
103 | 0 | } |
104 | |
|
105 | 0 | *txt = 0; |
106 | |
|
107 | 0 | *val = atof(buf); |
108 | |
|
109 | 0 | return p; |
110 | 0 | } |
111 | | |
112 | | static byte * |
113 | | xps_read_cff_integer(byte *p, byte *e, int b0, int *val) |
114 | 0 | { |
115 | 0 | int b1, b2, b3, b4; |
116 | |
|
117 | 0 | if (b0 == 28) |
118 | 0 | { |
119 | 0 | if (p + 2 > e) |
120 | 0 | { |
121 | 0 | gs_throw(-1, "corrupt dictionary (integer)"); |
122 | 0 | return 0; |
123 | 0 | } |
124 | 0 | b1 = *p++; |
125 | 0 | b2 = *p++; |
126 | 0 | *val = (b1 << 8) | b2; |
127 | 0 | } |
128 | | |
129 | 0 | else if (b0 == 29) |
130 | 0 | { |
131 | 0 | if (p + 4 > e) |
132 | 0 | { |
133 | 0 | gs_throw(-1, "corrupt dictionary (integer)"); |
134 | 0 | return 0; |
135 | 0 | } |
136 | 0 | b1 = *p++; |
137 | 0 | b2 = *p++; |
138 | 0 | b3 = *p++; |
139 | 0 | b4 = *p++; |
140 | 0 | *val = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; |
141 | 0 | } |
142 | | |
143 | 0 | else if (b0 < 247) |
144 | 0 | { |
145 | 0 | *val = b0 - 139; |
146 | 0 | } |
147 | | |
148 | 0 | else if (b0 < 251) |
149 | 0 | { |
150 | 0 | if (p + 1 > e) |
151 | 0 | { |
152 | 0 | gs_throw(-1, "corrupt dictionary (integer)"); |
153 | 0 | return 0; |
154 | 0 | } |
155 | 0 | b1 = *p++; |
156 | 0 | *val = (b0 - 247) * 256 + b1 + 108; |
157 | 0 | } |
158 | | |
159 | 0 | else |
160 | 0 | { |
161 | 0 | if (p + 1 > e) |
162 | 0 | { |
163 | 0 | gs_throw(-1, "corrupt dictionary (integer)"); |
164 | 0 | return 0; |
165 | 0 | } |
166 | 0 | b1 = *p++; |
167 | 0 | *val = -(b0 - 251) * 256 - b1 - 108; |
168 | 0 | } |
169 | | |
170 | 0 | return p; |
171 | 0 | } |
172 | | |
173 | | static int |
174 | | xps_read_cff_dict(byte *p, byte *e, xps_font_t *font, gs_font_type1 *pt1, int depth) |
175 | 0 | { |
176 | 0 | struct { int ival; float fval; } args[CFF_ARGS_SIZE]; |
177 | 0 | int offset; |
178 | 0 | int b0, n; |
179 | |
|
180 | 0 | int privatelen = 0; |
181 | 0 | int privateofs = 0; |
182 | |
|
183 | 0 | if (depth > 16) |
184 | 0 | return gs_throw(-1, "too many nested dicts"); |
185 | | |
186 | 0 | memset(args, 0x00, sizeof(args)); |
187 | |
|
188 | 0 | offset = p - font->cffdata; |
189 | |
|
190 | 0 | n = 0; |
191 | 0 | while (p < e) |
192 | 0 | { |
193 | 0 | b0 = *p++; |
194 | |
|
195 | 0 | if (b0 < 22) |
196 | 0 | { |
197 | 0 | if (b0 == 12) |
198 | 0 | { |
199 | 0 | if (p + 1 > e) |
200 | 0 | { |
201 | 0 | return gs_throw(-1, "corrupt dictionary (operator)"); |
202 | 0 | } |
203 | 0 | b0 = 0x100 | *p++; |
204 | 0 | } |
205 | | |
206 | | /* some CFF file offsets */ |
207 | | |
208 | 0 | if (b0 == 17) |
209 | 0 | { |
210 | 0 | if (args[0].ival < 0) |
211 | 0 | return gs_throw(-1, "corrupt cff file offset"); |
212 | 0 | font->charstrings = font->cffdata + args[0].ival; |
213 | 0 | } |
214 | | |
215 | 0 | if (b0 == 18) |
216 | 0 | { |
217 | 0 | if (args[0].ival < 0 || args[1].ival < 0) |
218 | 0 | return gs_throw(-1, "corrupt cff file offset"); |
219 | 0 | privatelen = args[0].ival; |
220 | 0 | privateofs = args[1].ival; |
221 | 0 | if ((font->cffdata + privateofs + privatelen) > font->cffend) |
222 | 0 | return gs_throw(-1, "corrupt cff file offset"); |
223 | 0 | } |
224 | | |
225 | 0 | if (b0 == 19) |
226 | 0 | { |
227 | 0 | if (args[0].ival < 0) |
228 | 0 | return gs_throw(-1, "corrupt cff file offset"); |
229 | 0 | font->subrs = font->cffdata + offset + args[0].ival; |
230 | 0 | } |
231 | | |
232 | 0 | if (b0 == (256 | 36)) |
233 | 0 | gs_warn("cid cff fonts not supported yet"); |
234 | 0 | if (b0 == (256 | 37)) |
235 | 0 | gs_warn("cid cff fonts not supported yet"); |
236 | | |
237 | | /* Type1 stuff that need to be set for the pt1 struct */ |
238 | |
|
239 | 0 | if (b0 == (256 | 6)) |
240 | 0 | { |
241 | 0 | if (args[0].ival == 1) |
242 | 0 | { |
243 | 0 | pt1->data.interpret = gs_type1_interpret; |
244 | 0 | pt1->data.lenIV = -1; /* FIXME */ |
245 | 0 | } |
246 | 0 | } |
247 | |
|
248 | 0 | if (b0 == (256 | 7)) |
249 | 0 | { |
250 | 0 | pt1->FontMatrix.xx = args[0].fval; |
251 | 0 | pt1->FontMatrix.xy = args[1].fval; |
252 | 0 | pt1->FontMatrix.yx = args[2].fval; |
253 | 0 | pt1->FontMatrix.yy = args[3].fval; |
254 | 0 | pt1->FontMatrix.tx = args[4].fval; |
255 | 0 | pt1->FontMatrix.ty = args[5].fval; |
256 | 0 | } |
257 | |
|
258 | 0 | if (b0 == 5) |
259 | 0 | { |
260 | 0 | pt1->FontBBox.p.x = args[0].fval; |
261 | 0 | pt1->FontBBox.p.y = args[1].fval; |
262 | 0 | pt1->FontBBox.q.x = args[2].fval; |
263 | 0 | pt1->FontBBox.q.y = args[3].fval; |
264 | 0 | } |
265 | |
|
266 | 0 | if (b0 == 20) |
267 | 0 | pt1->data.defaultWidthX = float2fixed(args[0].fval); |
268 | |
|
269 | 0 | if (b0 == 21) |
270 | 0 | pt1->data.nominalWidthX = float2fixed(args[0].fval); |
271 | |
|
272 | 0 | if (b0 == (256 | 19)) |
273 | 0 | pt1->data.initialRandomSeed = args[0].ival; |
274 | | |
275 | | /* Monday morning blues */ |
276 | | #if 0 |
277 | | if (b0 == 6) |
278 | | { |
279 | | pt1->data.BlueValues.count = n / 2; |
280 | | for (f = 0, i = 0; i < n; f += args[i].fval, i++) |
281 | | pt1->data.BlueValues.values[i] = f; |
282 | | } |
283 | | |
284 | | if (b0 == 7) |
285 | | { |
286 | | pt1->data.OtherBlues.count = n / 2; |
287 | | for (f = 0, i = 0; i < n; f += args[i].fval, i++) |
288 | | pt1->data.OtherBlues.values[i] = f; |
289 | | } |
290 | | |
291 | | if (b0 == 8) |
292 | | { |
293 | | pt1->data.FamilyBlues.count = n / 2; |
294 | | for (f = 0, i = 0; i < n; f += args[i].fval, i++) |
295 | | pt1->data.FamilyBlues.values[i] = f; |
296 | | } |
297 | | |
298 | | if (b0 == 9) |
299 | | { |
300 | | pt1->data.FamilyOtherBlues.count = n / 2; |
301 | | for (f = 0, i = 0; i < n; f += args[i].fval, i++) |
302 | | pt1->data.FamilyOtherBlues.values[i] = f; |
303 | | } |
304 | | |
305 | | if (b0 == 10) |
306 | | { |
307 | | pt1->data.StdHW.count = 1; |
308 | | pt1->data.StdHW.values[0] = args[0].fval; |
309 | | } |
310 | | |
311 | | if (b0 == 11) |
312 | | { |
313 | | pt1->data.StdVW.count = 1; |
314 | | pt1->data.StdVW.values[0] = args[0].fval; |
315 | | } |
316 | | |
317 | | if (b0 == (256 | 9)) |
318 | | pt1->data.BlueScale = args[0].fval; |
319 | | |
320 | | if (b0 == (256 | 10)) |
321 | | pt1->data.BlueShift = args[0].fval; |
322 | | |
323 | | if (b0 == (256 | 11)) |
324 | | pt1->data.BlueFuzz = args[0].fval; |
325 | | |
326 | | if (b0 == (256 | 12)) |
327 | | { |
328 | | pt1->data.StemSnapH.count = n; |
329 | | for (f = 0, i = 0; i < n; f += args[i].fval, i++) |
330 | | pt1->data.StemSnapH.values[i] = f; |
331 | | } |
332 | | |
333 | | if (b0 == (256 | 13)) |
334 | | { |
335 | | pt1->data.StemSnapV.count = n; |
336 | | for (f = 0, i = 0; i < n; f += args[i].fval, i++) |
337 | | pt1->data.StemSnapV.values[i] = f; |
338 | | } |
339 | | |
340 | | if (b0 == (256 | 14)) |
341 | | pt1->data.ForceBold = args[0].ival; |
342 | | |
343 | | if (b0 == (256 | 17)) |
344 | | pt1->data.LanguageGroup = args[0].ival; |
345 | | |
346 | | if (b0 == (256 | 18)) |
347 | | pt1->data.ExpansionFactor = args[0].fval; |
348 | | |
349 | | #endif |
350 | |
|
351 | 0 | n = 0; |
352 | 0 | } |
353 | | |
354 | 0 | else |
355 | 0 | { |
356 | 0 | if (b0 == 30) |
357 | 0 | { |
358 | 0 | if (n >= CFF_ARGS_SIZE) |
359 | 0 | return gs_throw(-1, "overflow in cff dict"); |
360 | 0 | p = xps_read_cff_real(p, e, &args[n].fval); |
361 | 0 | if (!p) |
362 | 0 | return gs_throw(-1, "corrupt dictionary operand"); |
363 | 0 | args[n].ival = (int) args[n].fval; |
364 | 0 | n++; |
365 | 0 | } |
366 | 0 | else if (b0 == 28 || b0 == 29 || (b0 >= 32 && b0 <= 254)) |
367 | 0 | { |
368 | 0 | if (n >= CFF_ARGS_SIZE) |
369 | 0 | return gs_throw(-1, "overflow in cff dict"); |
370 | 0 | p = xps_read_cff_integer(p, e, b0, &args[n].ival); |
371 | 0 | if (!p) |
372 | 0 | return gs_throw(-1, "corrupt dictionary operand"); |
373 | 0 | args[n].fval = (float) args[n].ival; |
374 | 0 | n++; |
375 | 0 | } |
376 | 0 | else |
377 | 0 | { |
378 | 0 | return gs_throw1(-1, "corrupt dictionary operand (b0 = %d)", b0); |
379 | 0 | } |
380 | 0 | } |
381 | 0 | } |
382 | | |
383 | | /* recurse for the private dictionary */ |
384 | 0 | if (privatelen) |
385 | 0 | { |
386 | 0 | int code = xps_read_cff_dict( |
387 | 0 | font->cffdata + privateofs, |
388 | 0 | font->cffdata + privateofs + privatelen, |
389 | 0 | font, pt1, depth+1); |
390 | 0 | if (code < 0) |
391 | 0 | return gs_rethrow(code, "cannot read private dictionary"); |
392 | 0 | } |
393 | | |
394 | 0 | return 0; |
395 | 0 | } |
396 | | |
397 | | /* |
398 | | * Get the number of items in an INDEX, and return |
399 | | * a pointer to the end of the INDEX or NULL on |
400 | | * failure. |
401 | | */ |
402 | | static byte * |
403 | | xps_count_cff_index(byte *p, byte *e, int *countp) |
404 | 0 | { |
405 | 0 | int count, offsize, last; |
406 | |
|
407 | 0 | if (p + 3 > e) |
408 | 0 | { |
409 | 0 | gs_throw(-1, "not enough data for index header"); |
410 | 0 | return 0; |
411 | 0 | } |
412 | | |
413 | 0 | count = u16(p); p += 2; |
414 | 0 | *countp = count; |
415 | |
|
416 | 0 | if (count == 0) |
417 | 0 | return p; |
418 | | |
419 | 0 | offsize = *p++; |
420 | |
|
421 | 0 | if (offsize < 1 || offsize > 4) |
422 | 0 | { |
423 | 0 | gs_throw(-1, "corrupt index header"); |
424 | 0 | return 0; |
425 | 0 | } |
426 | | |
427 | 0 | if (p + count * offsize > e) |
428 | 0 | { |
429 | 0 | gs_throw(-1, "not enough data for index offset table"); |
430 | 0 | return 0; |
431 | 0 | } |
432 | | |
433 | 0 | p += count * offsize; |
434 | 0 | last = uofs(p, offsize); |
435 | 0 | if (last < 0 || p + last > e) |
436 | 0 | { |
437 | 0 | gs_throw(-1, "corrupt index header"); |
438 | 0 | return 0; |
439 | 0 | } |
440 | | |
441 | 0 | p += offsize; |
442 | 0 | p --; /* stupid offsets */ |
443 | |
|
444 | 0 | if (p + last > e) |
445 | 0 | { |
446 | 0 | gs_throw(-1, "not enough data for index data"); |
447 | 0 | return 0; |
448 | 0 | } |
449 | | |
450 | 0 | p += last; |
451 | |
|
452 | 0 | return p; |
453 | 0 | } |
454 | | |
455 | | /* |
456 | | * Locate and store pointers to the data of an |
457 | | * item in the index that starts at 'p'. |
458 | | * Return pointer to the end of the index, |
459 | | * or NULL on failure. |
460 | | */ |
461 | | static byte * |
462 | | xps_find_cff_index(byte *p, byte *e, int idx, byte **pp, byte **ep) |
463 | 0 | { |
464 | 0 | int count, offsize, sofs, eofs, last; |
465 | |
|
466 | 0 | if (p == NULL) |
467 | 0 | return 0; |
468 | | |
469 | 0 | if (p + 3 > e) |
470 | 0 | { |
471 | 0 | gs_throw(-1, "not enough data for index header"); |
472 | 0 | return 0; |
473 | 0 | } |
474 | | |
475 | 0 | count = u16(p); p += 2; |
476 | 0 | if (count == 0) |
477 | 0 | return 0; |
478 | | |
479 | 0 | offsize = *p++; |
480 | |
|
481 | 0 | if (offsize < 1 || offsize > 4) |
482 | 0 | { |
483 | 0 | gs_throw(-1, "corrupt index header"); |
484 | 0 | return 0; |
485 | 0 | } |
486 | | |
487 | 0 | if (p + count * offsize > e) |
488 | 0 | { |
489 | 0 | gs_throw(-1, "not enough data for index offset table"); |
490 | 0 | return 0; |
491 | 0 | } |
492 | | |
493 | 0 | if (idx < 0 || idx >= count) |
494 | 0 | { |
495 | 0 | gs_throw(-1, "tried to access non-existing index item"); |
496 | 0 | return 0; |
497 | 0 | } |
498 | | |
499 | 0 | sofs = uofs(p + idx * offsize, offsize); |
500 | 0 | eofs = uofs(p + (idx + 1) * offsize, offsize); |
501 | 0 | last = uofs(p + count * offsize, offsize); |
502 | |
|
503 | 0 | p += count * offsize; |
504 | 0 | p += offsize; |
505 | 0 | p --; /* stupid offsets */ |
506 | |
|
507 | 0 | if (p + last > e) |
508 | 0 | { |
509 | 0 | gs_throw(-1, "not enough data for index data"); |
510 | 0 | return 0; |
511 | 0 | } |
512 | | |
513 | 0 | if (sofs < 0 || eofs < 0 || sofs > eofs || eofs > last) |
514 | 0 | { |
515 | 0 | gs_throw(-1, "corrupt index offset table"); |
516 | 0 | return 0; |
517 | 0 | } |
518 | | |
519 | 0 | *pp = p + sofs; |
520 | 0 | *ep = p + eofs; |
521 | |
|
522 | 0 | return p + last; |
523 | 0 | } |
524 | | |
525 | | /* |
526 | | * Scan the CFF file structure and extract important data. |
527 | | */ |
528 | | |
529 | | static int |
530 | | xps_read_cff_file(xps_font_t *font, gs_font_type1 *pt1) |
531 | 0 | { |
532 | 0 | byte *p = font->cffdata; |
533 | 0 | byte *e = font->cffend; |
534 | 0 | byte *dictp, *dicte; |
535 | 0 | int ngsubrs; |
536 | 0 | int nsubrs; |
537 | 0 | int count; |
538 | 0 | int code; |
539 | | |
540 | | /* CFF header */ |
541 | 0 | { |
542 | 0 | int major, minor, hdrsize; |
543 | |
|
544 | 0 | if (p + 4 > e) |
545 | 0 | return gs_throw(-1, "not enough data for header"); |
546 | | |
547 | 0 | major = *p++; |
548 | 0 | minor = *p++; |
549 | 0 | hdrsize = *p++; |
550 | 0 | /*offsize = **/p++; |
551 | |
|
552 | 0 | if (major != 1 || minor != 0) |
553 | 0 | return gs_throw(-1, "not a CFF 1.0 file"); |
554 | | |
555 | 0 | if (p + hdrsize - 4 > e) |
556 | 0 | return gs_throw(-1, "not enough data for extended header"); |
557 | 0 | } |
558 | | |
559 | | /* Name INDEX */ |
560 | 0 | p = xps_count_cff_index(p, e, &count); |
561 | 0 | if (!p) |
562 | 0 | return gs_throw(-1, "cannot read name index"); |
563 | 0 | if (count != 1) |
564 | 0 | return gs_throw(-1, "file did not contain exactly one font"); |
565 | | |
566 | | /* Top Dict INDEX */ |
567 | 0 | p = xps_find_cff_index(p, e, 0, &dictp, &dicte); |
568 | 0 | if (!p) |
569 | 0 | return gs_throw(-1, "cannot read top dict index"); |
570 | | |
571 | | /* String index */ |
572 | 0 | p = xps_count_cff_index(p, e, &count); |
573 | 0 | if (!p) |
574 | 0 | return gs_throw(-1, "cannot read string index"); |
575 | | |
576 | | /* Global Subr INDEX */ |
577 | 0 | font->gsubrs = p; |
578 | 0 | p = xps_count_cff_index(p, e, &ngsubrs); |
579 | 0 | if (!p) |
580 | 0 | return gs_throw(-1, "cannot read gsubr index"); |
581 | | |
582 | | /* Read the top and private dictionaries */ |
583 | 0 | code = xps_read_cff_dict(dictp, dicte, font, pt1, 0); |
584 | 0 | if (code < 0) |
585 | 0 | return gs_rethrow(code, "cannot read top dictionary"); |
586 | | |
587 | | /* Check the subrs index */ |
588 | 0 | nsubrs = 0; |
589 | 0 | if (font->subrs) |
590 | 0 | { |
591 | 0 | p = xps_count_cff_index(font->subrs, e, &nsubrs); |
592 | 0 | if (!p) |
593 | 0 | return gs_rethrow(-1, "cannot read subrs index"); |
594 | 0 | } |
595 | | |
596 | | /* Check the charstrings index */ |
597 | 0 | if (font->charstrings) |
598 | 0 | { |
599 | 0 | p = xps_count_cff_index(font->charstrings, e, &count); |
600 | 0 | if (!p) |
601 | 0 | return gs_rethrow(-1, "cannot read charstrings index"); |
602 | 0 | } |
603 | | |
604 | 0 | pt1->data.subroutineNumberBias = subrbias(nsubrs); |
605 | 0 | pt1->data.gsubrNumberBias = subrbias(ngsubrs); |
606 | | /* nominal and defaultWidthX */ |
607 | |
|
608 | 0 | return 0; |
609 | 0 | } |
610 | | |
611 | | /* |
612 | | * Ghostscript font machinery callbacks. |
613 | | */ |
614 | | |
615 | | static gs_glyph |
616 | | xps_post_callback_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t spc) |
617 | 0 | { |
618 | 0 | xps_font_t *xf = pfont->client_data; |
619 | 0 | int value; |
620 | 0 | value = xps_encode_font_char(xf, chr); |
621 | 0 | if (value == 0) |
622 | 0 | return GS_NO_GLYPH; |
623 | 0 | return value; |
624 | 0 | } |
625 | | |
626 | | static int |
627 | | xps_post_callback_decode_glyph(gs_font *p42, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length) |
628 | 0 | { |
629 | 0 | return 0; |
630 | 0 | } |
631 | | |
632 | | static int |
633 | | xps_post_callback_glyph_name(gs_font *pf, gs_glyph glyph, gs_const_string *pstr) |
634 | 0 | { |
635 | 0 | dmprintf1(pf->memory, "asking for CFF glyph name %lu\n", (ulong)glyph); |
636 | 0 | return -1; |
637 | 0 | } |
638 | | |
639 | | static int |
640 | | xps_post_callback_glyph_info(gs_font *font, gs_glyph glyph, |
641 | | const gs_matrix *pmat, int members, gs_glyph_info_t *info) |
642 | 0 | { |
643 | 0 | dmprintf1(font->memory, "asking for CFF glyph info %lu\n", (ulong)glyph); |
644 | 0 | return -1; |
645 | 0 | } |
646 | | |
647 | | static int |
648 | | xps_post_callback_glyph_outline(gs_font *font, int wmode, gs_glyph glyph, |
649 | | const gs_matrix *pmat, gx_path *ppath, double sbw[4]) |
650 | 0 | { |
651 | 0 | dmprintf1(font->memory, "asking for CFF glyph outline %lu\n", (ulong)glyph); |
652 | 0 | return -1; |
653 | 0 | } |
654 | | |
655 | | typedef struct gs_type1exec_state_s |
656 | | { |
657 | | gs_type1_state cis; /* must be first */ |
658 | | /* i_ctx_t *i_ctx_p; */ /* so push/pop can access o-stack */ |
659 | | double sbw[4]; |
660 | | gs_rect char_bbox; |
661 | | /* |
662 | | * The following elements are only used locally to make the stack clean |
663 | | * for OtherSubrs: they don't need to be declared for the garbage |
664 | | * collector. |
665 | | */ |
666 | | void * save_args[6]; |
667 | | int num_args; |
668 | | bool AlignToPixels; |
669 | | } gs_type1exec_state; |
670 | | |
671 | | static int |
672 | | xps_post_callback_glyph_data(gs_font_type1 * pfont, gs_glyph glyph, gs_glyph_data_t *pgd) |
673 | 0 | { |
674 | 0 | xps_font_t *font = pfont->client_data; |
675 | 0 | byte *s, *e; |
676 | 0 | byte *p; |
677 | |
|
678 | 0 | p = xps_find_cff_index(font->charstrings, font->cffend, glyph, &s, &e); |
679 | 0 | if (!p) |
680 | 0 | return gs_rethrow(gs_error_rangecheck, "cannot find charstring"); |
681 | | |
682 | 0 | gs_glyph_data_from_string(pgd, s, e - s, NULL); |
683 | |
|
684 | 0 | return 0; |
685 | 0 | } |
686 | | |
687 | | static int |
688 | | xps_post_callback_subr_data(gs_font_type1 * pfont, |
689 | | int subr_num, bool global, gs_glyph_data_t *pgd) |
690 | 0 | { |
691 | 0 | xps_font_t *font = pfont->client_data; |
692 | 0 | byte *s, *e; |
693 | 0 | byte *p; |
694 | |
|
695 | 0 | if (global) |
696 | 0 | { |
697 | 0 | p = xps_find_cff_index(font->gsubrs, font->cffend, subr_num, &s, &e); |
698 | 0 | if (!p) |
699 | 0 | return gs_rethrow(gs_error_rangecheck, "cannot find gsubr"); |
700 | 0 | } |
701 | 0 | else |
702 | 0 | { |
703 | 0 | p = xps_find_cff_index(font->subrs, font->cffend, subr_num, &s, &e); |
704 | 0 | if (!p) |
705 | 0 | return gs_rethrow(gs_error_rangecheck, "cannot find subr"); |
706 | 0 | } |
707 | | |
708 | 0 | gs_glyph_data_from_string(pgd, s, e - s, NULL); |
709 | |
|
710 | 0 | return 0; |
711 | 0 | } |
712 | | |
713 | | static int |
714 | | xps_post_callback_seac_data(gs_font_type1 * pfont, int ccode, gs_glyph * pglyph, |
715 | | gs_const_string *gstr, gs_glyph_data_t *pgd) |
716 | 0 | { |
717 | 0 | return gs_throw(-1, "seac is deprecated in CFF fonts"); |
718 | 0 | } |
719 | | |
720 | | static int |
721 | | xps_post_callback_push(void *callback_data, const fixed *values, int count) |
722 | 0 | { |
723 | 0 | return gs_throw(-1, "push not implemented"); |
724 | 0 | } |
725 | | |
726 | | static int |
727 | | xps_post_callback_pop(void *callback_data, fixed *value) |
728 | 0 | { |
729 | 0 | return gs_throw(-1, "pop not implemented"); |
730 | 0 | } |
731 | | |
732 | | static int |
733 | | xps_cff_append(gs_gstate *pgs, gs_font_type1 *pt1, gs_glyph glyph, int donthint) |
734 | 0 | { |
735 | 0 | int code, value; |
736 | 0 | gs_type1exec_state cxs; |
737 | 0 | gs_glyph_data_t gd; |
738 | 0 | gs_type1_state *const pcis = &cxs.cis; |
739 | 0 | gs_glyph_data_t *pgd = &gd; |
740 | 0 | double sbw[4]; |
741 | 0 | gs_matrix mtx; |
742 | | |
743 | | /* get charstring data */ |
744 | 0 | code = xps_post_callback_glyph_data(pt1, glyph, pgd); |
745 | 0 | if (code < 0) |
746 | 0 | return gs_rethrow(code, "cannot get glyph data"); |
747 | | |
748 | 0 | mtx = ctm_only(pgs); |
749 | 0 | gs_matrix_scale(&mtx, 0.001, 0.001, &mtx); |
750 | 0 | mtx.tx = 0; |
751 | 0 | mtx.ty = 0; |
752 | 0 | gs_matrix_fixed_from_matrix(&pgs->ctm, &mtx); |
753 | 0 | pgs->flatness = 0; |
754 | |
|
755 | 0 | code = gs_type1_interp_init(&cxs.cis, pgs, pgs->path, NULL, NULL, donthint, 0, pt1); |
756 | 0 | if (code < 0) |
757 | 0 | return gs_throw(code, "cannot init type1 interpreter"); |
758 | | |
759 | 0 | gs_type1_set_callback_data(pcis, &cxs); |
760 | | |
761 | | /* TODO: check if this is set in the font dict |
762 | | gs_type1_set_lsb(pcis, &mpt); |
763 | | gs_type1_set_width(pcis, &mpt); |
764 | | |
765 | | ... |
766 | | */ |
767 | 0 | while (1) |
768 | 0 | { |
769 | 0 | code = pt1->data.interpret(pcis, pgd, &value); |
770 | 0 | switch (code) |
771 | 0 | { |
772 | 0 | case type1_result_callothersubr: /* unknown OtherSubr */ |
773 | 0 | return_error(-15); /* can't handle it */ |
774 | 0 | case type1_result_sbw: /* [h]sbw, just continue */ |
775 | 0 | type1_cis_get_metrics(pcis, cxs.sbw); |
776 | 0 | type1_cis_get_metrics(pcis, sbw); |
777 | 0 | pgd = 0; |
778 | 0 | break; |
779 | 0 | case 0: /* all done */ |
780 | 0 | return 0; |
781 | 0 | default: /* code < 0, error */ |
782 | 0 | return gs_throw(code, "cannot interpret type1 data"); |
783 | 0 | } |
784 | 0 | } |
785 | 0 | } |
786 | | |
787 | | static int |
788 | | xps_post_callback_build_char(gs_show_enum *penum, gs_gstate *pgs, |
789 | | gs_font *pfont, gs_char chr, gs_glyph glyph) |
790 | 0 | { |
791 | 0 | gs_font_type1 *pt1 = (gs_font_type1*)pfont; |
792 | 0 | const gs_rect *pbbox; |
793 | 0 | float w2[6]; |
794 | 0 | int code; |
795 | | |
796 | | /* get the metrics */ |
797 | 0 | w2[0] = 0; |
798 | 0 | w2[1] = 1; |
799 | |
|
800 | 0 | pbbox = &pt1->FontBBox; |
801 | 0 | w2[2] = pbbox->p.x * 0.001; |
802 | 0 | w2[3] = pbbox->p.y * 0.001; |
803 | 0 | w2[4] = pbbox->q.x * 0.001; |
804 | 0 | w2[5] = pbbox->q.y * 0.001; |
805 | | |
806 | | /* Expand the bbox when stroking */ |
807 | 0 | if ( pfont->PaintType ) |
808 | 0 | { |
809 | 0 | float expand = max(1.415, gs_currentmiterlimit(pgs)) * gs_currentlinewidth(pgs) / 2; |
810 | 0 | w2[2] -= expand, w2[3] -= expand; |
811 | 0 | w2[4] += expand, w2[5] += expand; |
812 | 0 | } |
813 | |
|
814 | 0 | if ( (code = gs_moveto(pgs, 0.0, 0.0)) < 0 ) |
815 | 0 | return code; |
816 | | |
817 | 0 | if ( (code = gs_setcachedevice(penum, pgs, w2)) < 0 ) |
818 | 0 | return code; |
819 | | |
820 | 0 | code = xps_cff_append(pgs, pt1, glyph, |
821 | 0 | gs_show_in_charpath(penum) != cpm_show); |
822 | 0 | if (code < 0) |
823 | 0 | return code; |
824 | | |
825 | 0 | code = (pfont->PaintType ? gs_stroke(pgs) : gs_fill(pgs)); |
826 | 0 | if (code < 0) |
827 | 0 | return code; |
828 | | |
829 | 0 | return 0; |
830 | 0 | } |
831 | | |
832 | | int |
833 | | xps_init_postscript_font(xps_context_t *ctx, xps_font_t *font) |
834 | 0 | { |
835 | 0 | gs_font_type1 *pt1; |
836 | 0 | int cffofs; |
837 | 0 | int cfflen; |
838 | 0 | int cffend; |
839 | 0 | int code; |
840 | | |
841 | | /* Find the CFF table and parse it to create a charstring based font */ |
842 | | /* We don't need to support CFF files with multiple fonts */ |
843 | | /* Find the VORG table for easier vertical metrics */ |
844 | |
|
845 | 0 | cffofs = xps_find_sfnt_table(font, "CFF ", &cfflen); |
846 | 0 | if (cffofs < 0) |
847 | 0 | return gs_throw(-1, "cannot find CFF table"); |
848 | | |
849 | | /* check the table is within the buffer and no integer overflow occurs */ |
850 | 0 | cffend = cffofs + cfflen; |
851 | 0 | if (cffend < cffofs || cfflen < 0 || cffend > font->length) |
852 | 0 | return gs_throw(-1, "corrupt CFF table location"); |
853 | | |
854 | 0 | font->cffdata = font->data + cffofs; |
855 | 0 | font->cffend = font->data + cffend; |
856 | |
|
857 | 0 | font->gsubrs = 0; |
858 | 0 | font->subrs = 0; |
859 | 0 | font->charstrings = 0; |
860 | |
|
861 | 0 | pt1 = (void*) gs_alloc_struct(ctx->memory, gs_font_type1, &st_gs_font_type1, "xps_font type1"); |
862 | 0 | if (!pt1) |
863 | 0 | return gs_throw(-1, "out of memory"); |
864 | | |
865 | 0 | font->font = (void*) pt1; |
866 | | |
867 | | /* Common to all fonts */ |
868 | |
|
869 | 0 | pt1->next = 0; |
870 | 0 | pt1->prev = 0; |
871 | 0 | pt1->memory = ctx->memory; |
872 | |
|
873 | 0 | pt1->dir = ctx->fontdir; /* NB also set by gs_definefont later */ |
874 | 0 | pt1->base = font->font; /* NB also set by gs_definefont later */ |
875 | 0 | pt1->is_resource = false; |
876 | 0 | gs_notify_init(&pt1->notify_list, gs_memory_stable(ctx->memory)); |
877 | 0 | pt1->id = gs_next_ids(ctx->memory, 1); |
878 | |
|
879 | 0 | pt1->client_data = font; /* that's us */ |
880 | |
|
881 | 0 | gs_make_identity(&pt1->FontMatrix); |
882 | 0 | gs_make_identity(&pt1->orig_FontMatrix); |
883 | |
|
884 | 0 | pt1->FontType = ft_encrypted2; |
885 | 0 | pt1->BitmapWidths = true; |
886 | 0 | pt1->ExactSize = fbit_use_outlines; |
887 | 0 | pt1->InBetweenSize = fbit_use_outlines; |
888 | 0 | pt1->TransformedChar = fbit_use_outlines; |
889 | 0 | pt1->WMode = 0; |
890 | 0 | pt1->PaintType = 0; |
891 | 0 | pt1->StrokeWidth = 0; |
892 | |
|
893 | 0 | pt1->procs.define_font = gs_no_define_font; |
894 | 0 | pt1->procs.make_font = gs_no_make_font; |
895 | 0 | pt1->procs.font_info = gs_default_font_info; |
896 | 0 | pt1->procs.same_font = gs_default_same_font; |
897 | 0 | pt1->procs.encode_char = xps_post_callback_encode_char; |
898 | 0 | pt1->procs.decode_glyph = xps_post_callback_decode_glyph; |
899 | 0 | pt1->procs.enumerate_glyph = gs_no_enumerate_glyph; |
900 | 0 | pt1->procs.glyph_info = xps_post_callback_glyph_info; |
901 | 0 | pt1->procs.glyph_outline = xps_post_callback_glyph_outline; |
902 | 0 | pt1->procs.glyph_name = xps_post_callback_glyph_name; |
903 | 0 | pt1->procs.init_fstack = gs_default_init_fstack; |
904 | 0 | pt1->procs.next_char_glyph = gs_default_next_char_glyph; |
905 | 0 | pt1->procs.build_char = xps_post_callback_build_char; |
906 | |
|
907 | 0 | strcpy((char*)pt1->font_name.chars, "PostScriptFont"); |
908 | 0 | pt1->font_name.size = strlen((char*)pt1->font_name.chars); |
909 | |
|
910 | 0 | pt1->key_name.size = 0; |
911 | | |
912 | | /* Base font specific */ |
913 | |
|
914 | 0 | pt1->FontBBox.p.x = 0; /* -0.5; */ |
915 | 0 | pt1->FontBBox.p.y = 0; /* -0.5; */ |
916 | 0 | pt1->FontBBox.q.x = 0; /* 1.5; */ |
917 | 0 | pt1->FontBBox.q.y = 0; /* 1.5; */ |
918 | |
|
919 | 0 | uid_set_UniqueID(&pt1->UID, pt1->id); |
920 | |
|
921 | 0 | pt1->encoding_index = ENCODING_INDEX_UNKNOWN; |
922 | 0 | pt1->nearest_encoding_index = ENCODING_INDEX_UNKNOWN; |
923 | |
|
924 | 0 | pt1->FAPI = 0; |
925 | 0 | pt1->FAPI_font_data = 0; |
926 | | |
927 | | /* Type 1/2 specific */ |
928 | | /* defaults from the CFF spec */ |
929 | |
|
930 | 0 | pt1->data.procs.glyph_data = xps_post_callback_glyph_data; |
931 | 0 | pt1->data.procs.subr_data = xps_post_callback_subr_data; |
932 | 0 | pt1->data.procs.seac_data = xps_post_callback_seac_data; |
933 | 0 | pt1->data.procs.push_values = xps_post_callback_push; |
934 | 0 | pt1->data.procs.pop_value = xps_post_callback_pop; |
935 | |
|
936 | 0 | pt1->data.interpret = gs_type2_interpret; |
937 | 0 | pt1->data.proc_data = font; |
938 | 0 | pt1->data.parent = NULL; |
939 | 0 | pt1->data.lenIV = -1; /* DEFAULT_LENIV_2 */ |
940 | |
|
941 | 0 | pt1->data.subroutineNumberBias = 0; |
942 | 0 | pt1->data.gsubrNumberBias = 0; |
943 | 0 | pt1->data.initialRandomSeed = 0; |
944 | 0 | pt1->data.defaultWidthX = 0; |
945 | 0 | pt1->data.nominalWidthX = 0; |
946 | |
|
947 | 0 | pt1->data.BlueFuzz = 1; |
948 | 0 | pt1->data.BlueScale = 0.039625f; |
949 | 0 | pt1->data.BlueShift = 7; |
950 | 0 | pt1->data.BlueValues.count = 0; |
951 | 0 | pt1->data.ExpansionFactor = 0.06f; |
952 | 0 | pt1->data.ForceBold = 0; |
953 | 0 | pt1->data.FamilyBlues.count = 0; |
954 | 0 | pt1->data.FamilyOtherBlues.count = 0; |
955 | 0 | pt1->data.LanguageGroup = 0; |
956 | 0 | pt1->data.OtherBlues.count = 0; |
957 | |
|
958 | 0 | pt1->data.RndStemUp = 0; |
959 | 0 | memset(&pt1->data.StdHW, 0, sizeof(pt1->data.StdHW)); |
960 | 0 | memset(&pt1->data.StdVW, 0, sizeof(pt1->data.StdVW)); |
961 | 0 | memset(&pt1->data.StemSnapH, 0, sizeof(pt1->data.StemSnapH)); |
962 | 0 | memset(&pt1->data.StemSnapV, 0, sizeof(pt1->data.StemSnapH)); |
963 | 0 | memset(&pt1->data.WeightVector, 0, sizeof(pt1->data.WeightVector)); |
964 | |
|
965 | 0 | code = xps_read_cff_file(font, pt1); |
966 | 0 | if (code < 0) |
967 | 0 | { |
968 | | /* TODO free pt1 here? */ |
969 | 0 | return gs_rethrow(code, "cannot read cff file structure"); |
970 | 0 | } |
971 | | |
972 | 0 | if ((code = gs_definefont(ctx->fontdir, font->font)) < 0) { |
973 | 0 | return(code); |
974 | 0 | } |
975 | | |
976 | 0 | code = xps_fapi_passfont (font->font, NULL, NULL, font->data, font->length); |
977 | 0 | return code; |
978 | 0 | } |