/src/ghostpdl/devices/vector/gdevpsf1.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Write an embedded Type 1 font */ |
18 | | #include "math.h" |
19 | | #include "memory_.h" |
20 | | #include "gx.h" |
21 | | #include "gserrors.h" |
22 | | #include "gsccode.h" |
23 | | #include "gsmatrix.h" |
24 | | #include "gxfixed.h" |
25 | | #include "gxfont.h" |
26 | | #include "gxfont1.h" |
27 | | #include "gxmatrix.h" /* for gxtype1.h */ |
28 | | #include "gxtype1.h" |
29 | | #include "strimpl.h" /* required by Watcom compiler (why?) */ |
30 | | #include "stream.h" |
31 | | #include "sfilter.h" |
32 | | #include "spsdf.h" |
33 | | #include "sstring.h" |
34 | | #include "spprint.h" |
35 | | #include "gdevpsf.h" |
36 | | |
37 | | /* ------ Utilities shared with CFF writer ------ */ |
38 | | |
39 | | /* Gather glyph information for a Type 1 or Type 2 font. */ |
40 | | int |
41 | | psf_type1_glyph_data(gs_font_base *pbfont, gs_glyph glyph, |
42 | | gs_glyph_data_t *pgd, gs_font_type1 **ppfont) |
43 | 1.06M | { |
44 | 1.06M | gs_font_type1 *const pfont = (gs_font_type1 *)pbfont; |
45 | | |
46 | 1.06M | *ppfont = pfont; |
47 | 1.06M | return pfont->data.procs.glyph_data(pfont, glyph, pgd); |
48 | 1.06M | } |
49 | | int |
50 | | psf_get_type1_glyphs(psf_outline_glyphs_t *pglyphs, gs_font_type1 *pfont, |
51 | | gs_glyph *subset_glyphs, uint subset_size) |
52 | 7.65k | { |
53 | 7.65k | return psf_get_outline_glyphs(pglyphs, (gs_font_base *)pfont, |
54 | 7.65k | subset_glyphs, subset_size, |
55 | 7.65k | psf_type1_glyph_data); |
56 | 7.65k | } |
57 | | |
58 | | /* ------ Main program ------ */ |
59 | | |
60 | | /* Write a (named) array of floats. */ |
61 | | static int |
62 | | write_float_array(gs_param_list *plist, const char *key, const float *values, |
63 | | int count) |
64 | 24.2k | { |
65 | 24.2k | if (count != 0) { |
66 | 12.0k | gs_param_float_array fa; |
67 | | |
68 | 12.0k | fa.persistent = false; |
69 | 12.0k | fa.size = count; |
70 | 12.0k | fa.data = values; |
71 | 12.0k | return param_write_float_array(plist, key, &fa); |
72 | 12.0k | } |
73 | 12.2k | return 0; |
74 | 24.2k | } |
75 | | |
76 | | /* Write a UniqueID and/or XUID. */ |
77 | | static void |
78 | | write_uid(stream *s, const gs_uid *puid, int options) |
79 | 6.05k | { |
80 | 6.05k | if (uid_is_UniqueID(puid)) |
81 | 0 | pprintld1(s, "/UniqueID %ld def\n", puid->id); |
82 | 6.05k | else if (uid_is_XUID(puid) && (options & WRITE_TYPE1_XUID) != 0) { |
83 | 0 | uint i, n = uid_XUID_size(puid); |
84 | | |
85 | | /* Adobe products (specifically Acrobat but the same limitation is mentioned |
86 | | * in the PLRM) cannot handle XUIDs > 16 entries. |
87 | | */ |
88 | 0 | if (n > 16) |
89 | 0 | n = 16; |
90 | |
|
91 | 0 | stream_puts(s, "/XUID ["); |
92 | 0 | for (i = 0; i < n; ++i) |
93 | 0 | pprintld1(s, "%ld ", uid_XUID_values(puid)[i]); |
94 | 0 | stream_puts(s, "] readonly def\n"); |
95 | 0 | } |
96 | 6.05k | } |
97 | | |
98 | | /* Write the font name. */ |
99 | | static void |
100 | | write_font_name(stream *s, const gs_font_type1 *pfont, |
101 | | const gs_const_string *alt_font_name, bool as_name) |
102 | 6.05k | { |
103 | 6.05k | const byte *c; |
104 | 6.05k | const byte *name = (alt_font_name ? alt_font_name->data : pfont->font_name.chars); |
105 | 6.05k | int n = (alt_font_name ? alt_font_name->size : pfont->font_name.size); |
106 | | |
107 | 6.05k | if (n == 0) |
108 | | /* empty name, may need to write it as empty string */ |
109 | 0 | stream_puts(s, (as_name ? "/" : "()")); |
110 | 6.05k | else { |
111 | 114k | for (c = (byte *)"()<>[]{}/% \n\r\t\b\f\004\033"; *c; c++) |
112 | 108k | if (memchr(name, *c, n)) |
113 | 16 | break; |
114 | 6.05k | if (*c || memchr(name, 0, n)) { |
115 | | /* name contains whitespace (NUL included) or a PostScript separator */ |
116 | 16 | byte pssebuf[1 + 4 * gs_font_name_max + 1]; /* "(" + "\ooo" * gs_font_name_max + ")" */ |
117 | 16 | stream_cursor_read r; |
118 | 16 | stream_cursor_write w; |
119 | | |
120 | 16 | pssebuf[0] = '('; |
121 | 16 | r.limit = (r.ptr = name - 1) + n; |
122 | 16 | w.limit = (w.ptr = pssebuf) + sizeof pssebuf - 1; |
123 | 16 | s_PSSE_template.process(NULL, &r, &w, true); |
124 | 16 | stream_write(s, pssebuf, w.ptr - pssebuf + 1); |
125 | 16 | if (as_name) |
126 | 8 | stream_puts(s, " cvn"); |
127 | 6.04k | } else { |
128 | | /* name without any special characters */ |
129 | 6.04k | if (as_name) |
130 | 3.02k | stream_putc(s, '/'); |
131 | 6.04k | stream_write(s, name, n); |
132 | 6.04k | } |
133 | 6.05k | } |
134 | 6.05k | } |
135 | | /* |
136 | | * Write the Encoding array. This is a separate procedure only for |
137 | | * readability. |
138 | | */ |
139 | | static int |
140 | | write_Encoding(stream *s, gs_font_type1 *pfont, int options, |
141 | | gs_glyph *subset_glyphs, uint subset_size, gs_glyph notdef) |
142 | 3.02k | { |
143 | 3.02k | stream_puts(s, "/Encoding "); |
144 | 3.02k | switch (pfont->encoding_index) { |
145 | 0 | case ENCODING_INDEX_STANDARD: |
146 | 0 | stream_puts(s, "StandardEncoding"); |
147 | 0 | break; |
148 | 0 | case ENCODING_INDEX_ISOLATIN1: |
149 | | /* ATM only recognizes StandardEncoding. */ |
150 | 0 | if (options & WRITE_TYPE1_POSTSCRIPT) { |
151 | 0 | stream_puts(s, "ISOLatin1Encoding"); |
152 | 0 | break; |
153 | 0 | } |
154 | 3.02k | default:{ |
155 | 3.02k | gs_char i; |
156 | | |
157 | 3.02k | stream_puts(s, "256 array\n"); |
158 | 3.02k | stream_puts(s, "0 1 255 {1 index exch /.notdef put} for\n"); |
159 | 778k | for (i = 0; i < 256; ++i) { |
160 | 775k | gs_glyph glyph = |
161 | 775k | (*pfont->procs.encode_char) |
162 | 775k | ((gs_font *)pfont, (gs_char)i, GLYPH_SPACE_NAME); |
163 | 775k | gs_const_string namestr; |
164 | | |
165 | 775k | if (subset_glyphs && subset_size) { |
166 | | /* |
167 | | * Only write Encoding entries for glyphs in the |
168 | | * subset. Use binary search to check each glyph, |
169 | | * since subset_glyphs are sorted. |
170 | | */ |
171 | 0 | if (!psf_sorted_glyphs_include(subset_glyphs, |
172 | 0 | subset_size, glyph)) |
173 | 0 | continue; |
174 | 0 | } |
175 | 775k | if (glyph != GS_NO_GLYPH && glyph != notdef && |
176 | 775k | pfont->procs.glyph_name((gs_font *)pfont, glyph, |
177 | 103k | &namestr) >= 0 |
178 | 775k | ) { |
179 | 103k | pprintd1(s, "dup %d /", (int)i); |
180 | 103k | stream_write(s, namestr.data, namestr.size); |
181 | 103k | stream_puts(s, " put\n"); |
182 | 103k | } |
183 | 775k | } |
184 | 3.02k | stream_puts(s, "readonly"); |
185 | 3.02k | } |
186 | 3.02k | } |
187 | 3.02k | stream_puts(s, " def\n"); |
188 | 3.02k | return 0; |
189 | 3.02k | } |
190 | | |
191 | | static int WriteNumber (byte *dest, int value) |
192 | 0 | { |
193 | 0 | if (value >= -107 && value <= 107) { |
194 | 0 | *dest = value + 139; |
195 | 0 | return 1; |
196 | 0 | } else { |
197 | 0 | if (value >= 108 && value <= 1131) { |
198 | 0 | int quotient = (int)floor((value - 108) / (double)256); |
199 | 0 | dest[0] = quotient + 247; |
200 | 0 | dest[1] = value - 108 - quotient * 256; |
201 | 0 | return 2; |
202 | 0 | } else { |
203 | 0 | if (value <= -108 && value >= -1131) { |
204 | 0 | int quotient = (int)floor((value + 108) / -256); |
205 | 0 | int newval = value + 256 * quotient + 108; |
206 | 0 | dest[0] = quotient + 251; |
207 | 0 | dest[1] = newval * -1; |
208 | 0 | return 2; |
209 | 0 | } else { |
210 | 0 | dest[0] = 255; |
211 | 0 | dest[1] = value >> 24; |
212 | 0 | dest[2] = (value & 0xFF0000) >> 16; |
213 | 0 | dest[3] = (value & 0xFF00) >> 8; |
214 | 0 | dest[4] = value & 0xFF; |
215 | 0 | return 5; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | } |
219 | 0 | return 0; |
220 | 0 | } |
221 | | |
222 | | /* The following 2 routines attempt to parse out Multiple Master 'OtherSubrs' |
223 | | * calls, and replace the multiple arguments to $Blend with the two 'base' |
224 | | * parameters. This works reasonably well but can be defeated. FOr example a |
225 | | * CharString which puts some parameters on the operand stack, then calls a |
226 | | * Subr which puts the remaining parameters on the stack, and calls a MM |
227 | | * OtherSubr (constructions like this have been observed). In general we |
228 | | * work around this by storing the operands on the stack, but it is possible |
229 | | * that the values are calculated (eg x y div) which is a common way to get |
230 | | * float values into the interpreter. This will defeat the code below. |
231 | | * |
232 | | * The only way to solve this is to actually fully interpret the CharString |
233 | | * and any /Subrs it calls, and then emit the result as a non-MM CharString |
234 | | * by blending the values. This would mean writing a new routine like |
235 | | * 'psf_convert_type1_to_type2' (see gdevpsfx.c) or modifying that routine |
236 | | * so that it outputs type 1 CharStrings (which is probably simpler to do). |
237 | | */ |
238 | | static int CheckSubrForMM (gs_glyph_data_t *gdata, gs_font_type1 *pfont) |
239 | 0 | { |
240 | 0 | crypt_state state = crypt_charstring_seed; |
241 | 0 | int code = 0; |
242 | 0 | gs_bytestring *data = (gs_bytestring *)&gdata->bits; |
243 | 0 | byte *source = data->data, *end = source + data->size; |
244 | 0 | int CurrentNumberIndex = 0, Stack[32]; |
245 | |
|
246 | 0 | memset(Stack, 0x00, sizeof(Stack)); |
247 | 0 | gs_type1_decrypt(source, source, data->size, &state); |
248 | |
|
249 | 0 | if(pfont->data.lenIV) |
250 | 0 | source += pfont->data.lenIV; |
251 | |
|
252 | 0 | while (source < end) { |
253 | 0 | if (*source < 32) { |
254 | | /* Command */ |
255 | 0 | switch (*source) { |
256 | 0 | case 12: |
257 | 0 | if (*(source + 1) == 16) { |
258 | 0 | if (CurrentNumberIndex < 1) |
259 | 0 | return_error(gs_error_rangecheck); |
260 | 0 | switch(Stack[CurrentNumberIndex-1]) { |
261 | 0 | case 18: |
262 | 0 | code = 6; |
263 | 0 | break; |
264 | 0 | case 17: |
265 | 0 | code = 4; |
266 | 0 | break; |
267 | 0 | case 16: |
268 | 0 | code = 3; |
269 | 0 | break; |
270 | 0 | case 15: |
271 | 0 | code = 2; |
272 | 0 | break; |
273 | 0 | case 14: |
274 | 0 | code = 1; |
275 | 0 | break; |
276 | 0 | default: |
277 | 0 | code = 0; |
278 | 0 | break; |
279 | 0 | } |
280 | 0 | source += 2; |
281 | 0 | } else { |
282 | 0 | source +=2; |
283 | 0 | } |
284 | 0 | break; |
285 | 0 | default: |
286 | 0 | source++; |
287 | 0 | break; |
288 | 0 | } |
289 | 0 | CurrentNumberIndex = 0; |
290 | 0 | } else { |
291 | | /* Number */ |
292 | 0 | if (*source < 247) { |
293 | 0 | Stack[CurrentNumberIndex++] = *source++ - 139; |
294 | 0 | } else { |
295 | 0 | if (*source < 251) { |
296 | 0 | Stack[CurrentNumberIndex] = ((*source++ - 247) * 256) + 108; |
297 | 0 | Stack[CurrentNumberIndex++] += *source++; |
298 | 0 | } else { |
299 | 0 | if (*source < 255) { |
300 | 0 | Stack[CurrentNumberIndex] = ((*source++ - 251) * -256) - 108; |
301 | 0 | Stack[CurrentNumberIndex++] -= *source++; |
302 | 0 | } else { |
303 | 0 | Stack[CurrentNumberIndex] = *source++ << 24; |
304 | 0 | Stack[CurrentNumberIndex] += *source++ << 16; |
305 | 0 | Stack[CurrentNumberIndex] += *source++ << 8; |
306 | 0 | Stack[CurrentNumberIndex] += *source++; |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } |
310 | 0 | } |
311 | 0 | } |
312 | 0 | state = crypt_charstring_seed; |
313 | 0 | source = data->data; |
314 | 0 | gs_type1_encrypt(source, source, data->size, &state); |
315 | 0 | return code; |
316 | 0 | } |
317 | | |
318 | | static int strip_othersubrs(gs_glyph_data_t *gdata, gs_font_type1 *pfont, byte *stripped, byte *SubrsWithMM) |
319 | 0 | { |
320 | 0 | crypt_state state = crypt_charstring_seed; |
321 | 0 | gs_bytestring *data = (gs_bytestring *)&gdata->bits; |
322 | 0 | byte *source = data->data, *dest = stripped, *end = source + data->size; |
323 | 0 | int i, dest_length = 0, CurrentNumberIndex = 0, Stack[64], written; |
324 | 0 | int OnlyCalcLength = 0; |
325 | 0 | char Buffer[16]; |
326 | |
|
327 | 0 | memset(Stack, 0x00, 64 * sizeof(int)); |
328 | 0 | if (stripped == NULL) { |
329 | 0 | OnlyCalcLength = 1; |
330 | 0 | dest = (byte *)&Buffer; |
331 | 0 | } |
332 | |
|
333 | 0 | gs_type1_decrypt(source, source, data->size, &state); |
334 | |
|
335 | 0 | if(pfont->data.lenIV >= 0) { |
336 | 0 | for (i=0;i<pfont->data.lenIV;i++) { |
337 | 0 | if (!OnlyCalcLength) |
338 | 0 | *dest++ = *source++; |
339 | 0 | } |
340 | 0 | dest_length += pfont->data.lenIV; |
341 | 0 | } |
342 | 0 | while (source < end) { |
343 | 0 | if (*source < 32) { |
344 | | /* Command */ |
345 | 0 | switch (*source) { |
346 | 0 | case 12: |
347 | 0 | if (*(source + 1) == 16) { |
348 | | /* Callothersubsr, the only thing we care about */ |
349 | 0 | switch(Stack[CurrentNumberIndex-1]) { |
350 | | /* If we find a Multiple Master call, remove all but the |
351 | | * first set of arguments. Mimics the result of a call. |
352 | | * Adobe 'encourages' the use of Subrs to do MM, but |
353 | | * the spec doens't say you have to, so we need to be |
354 | | * prepared, just in case. I doubt we will ever execute |
355 | | * this code. |
356 | | */ |
357 | 0 | case 14: |
358 | 0 | CurrentNumberIndex -= pfont->data.WeightVector.count - 1; |
359 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
360 | 0 | written = WriteNumber(dest, Stack[i]); |
361 | 0 | dest_length += written; |
362 | 0 | if (!OnlyCalcLength) |
363 | 0 | dest += written; |
364 | 0 | } |
365 | 0 | source += 2; |
366 | 0 | break; |
367 | 0 | case 15: |
368 | 0 | CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 2; |
369 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
370 | 0 | written = WriteNumber(dest, Stack[i]); |
371 | 0 | dest_length += written; |
372 | 0 | if (!OnlyCalcLength) |
373 | 0 | dest += written; |
374 | 0 | } |
375 | 0 | source += 2; |
376 | 0 | break; |
377 | 0 | case 16: |
378 | 0 | CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 3; |
379 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
380 | 0 | written = WriteNumber(dest, Stack[i]); |
381 | 0 | dest_length += written; |
382 | 0 | if (!OnlyCalcLength) |
383 | 0 | dest += written; |
384 | 0 | } |
385 | 0 | source += 2; |
386 | 0 | break; |
387 | 0 | case 17: |
388 | 0 | CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 4; |
389 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
390 | 0 | written = WriteNumber(dest, Stack[i]); |
391 | 0 | dest_length += written; |
392 | 0 | if (!OnlyCalcLength) |
393 | 0 | dest += written; |
394 | 0 | } |
395 | 0 | source += 2; |
396 | 0 | break; |
397 | 0 | case 18: |
398 | 0 | CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 6; |
399 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
400 | 0 | written = WriteNumber(dest, Stack[i]); |
401 | 0 | dest_length += written; |
402 | 0 | if (!OnlyCalcLength) |
403 | 0 | dest += written; |
404 | 0 | } |
405 | 0 | source += 2; |
406 | 0 | break; |
407 | 0 | default: |
408 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
409 | 0 | written = WriteNumber(dest, Stack[i]); |
410 | 0 | dest_length += written; |
411 | 0 | if (!OnlyCalcLength) |
412 | 0 | dest += written; |
413 | 0 | } |
414 | 0 | if (!OnlyCalcLength) { |
415 | 0 | *dest++ = *source++; |
416 | 0 | *dest++ = *source++; |
417 | 0 | } else { |
418 | 0 | source += 2; |
419 | 0 | } |
420 | 0 | dest_length += 2; |
421 | 0 | break; |
422 | 0 | } |
423 | 0 | } else { |
424 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
425 | 0 | written = WriteNumber(dest, Stack[i]); |
426 | 0 | dest_length += written; |
427 | 0 | if (!OnlyCalcLength) |
428 | 0 | dest += written; |
429 | 0 | } |
430 | 0 | if (!OnlyCalcLength) { |
431 | 0 | *dest++ = *source++; |
432 | 0 | *dest++ = *source++; |
433 | 0 | } else { |
434 | 0 | source += 2; |
435 | 0 | } |
436 | 0 | dest_length += 2; |
437 | 0 | } |
438 | 0 | break; |
439 | 0 | case 10: |
440 | 0 | if (CurrentNumberIndex != 0 && SubrsWithMM[Stack[CurrentNumberIndex - 1]] != 0) { |
441 | 0 | int index = Stack[CurrentNumberIndex - 1]; |
442 | 0 | int StackBase = CurrentNumberIndex - 1 - pfont->data.WeightVector.count * SubrsWithMM[index]; |
443 | |
|
444 | 0 | CurrentNumberIndex--; /* Remove the subr index */ |
445 | |
|
446 | 0 | for (i=0;i < StackBase; i++) { |
447 | 0 | written = WriteNumber(dest, Stack[i]); |
448 | 0 | dest_length += written; |
449 | 0 | if (!OnlyCalcLength) |
450 | 0 | dest += written; |
451 | 0 | } |
452 | 0 | for (i=0;i<SubrsWithMM[index];i++) { |
453 | | /* See above, it may be that we don't have enough numbers on the stack |
454 | | * (due to constructs such as x y div), if we don't have enough parameters |
455 | | * just write a 0 instead. We know this is incorrect..... |
456 | | */ |
457 | 0 | if (StackBase + i >= 0) |
458 | 0 | written = WriteNumber(dest, Stack[StackBase + i]); |
459 | 0 | else |
460 | 0 | written = WriteNumber(dest, 0); |
461 | 0 | dest_length += written; |
462 | 0 | if (!OnlyCalcLength) |
463 | 0 | dest += written; |
464 | 0 | } |
465 | 0 | source++; |
466 | 0 | } else { |
467 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
468 | 0 | written = WriteNumber(dest, Stack[i]); |
469 | 0 | dest_length += written; |
470 | 0 | if (!OnlyCalcLength) |
471 | 0 | dest += written; |
472 | 0 | } |
473 | 0 | if (!OnlyCalcLength) |
474 | 0 | *dest++ = *source++; |
475 | 0 | else |
476 | 0 | source++; |
477 | 0 | dest_length++; |
478 | 0 | } |
479 | 0 | break; |
480 | 0 | default: |
481 | 0 | for (i = 0;i < CurrentNumberIndex;i++) { |
482 | 0 | written = WriteNumber(dest, Stack[i]); |
483 | 0 | dest_length += written; |
484 | 0 | if (!OnlyCalcLength) |
485 | 0 | dest += written; |
486 | 0 | } |
487 | 0 | if (!OnlyCalcLength) |
488 | 0 | *dest++ = *source++; |
489 | 0 | else |
490 | 0 | source++; |
491 | 0 | dest_length++; |
492 | 0 | } |
493 | 0 | CurrentNumberIndex = 0; |
494 | 0 | } else { |
495 | | /* Number */ |
496 | 0 | if (*source < 247) { |
497 | 0 | Stack[CurrentNumberIndex++] = *source++ - 139; |
498 | 0 | } else { |
499 | 0 | if (*source < 251) { |
500 | 0 | Stack[CurrentNumberIndex] = ((*source++ - 247) * 256) + 108; |
501 | 0 | Stack[CurrentNumberIndex++] += *source++; |
502 | 0 | } else { |
503 | 0 | if (*source < 255) { |
504 | 0 | Stack[CurrentNumberIndex] = ((*source++ - 251) * -256) - 108; |
505 | 0 | Stack[CurrentNumberIndex++] -= *source++; |
506 | 0 | } else { |
507 | 0 | source++; |
508 | 0 | Stack[CurrentNumberIndex] = *source++ << 24; |
509 | 0 | Stack[CurrentNumberIndex] += *source++ << 16; |
510 | 0 | Stack[CurrentNumberIndex] += *source++ << 8; |
511 | 0 | Stack[CurrentNumberIndex++] += *source++; |
512 | 0 | } |
513 | 0 | } |
514 | 0 | } |
515 | 0 | } |
516 | 0 | } |
517 | 0 | source = data->data; |
518 | 0 | state = crypt_charstring_seed; |
519 | 0 | gs_type1_encrypt(source, source, data->size, &state); |
520 | |
|
521 | 0 | if (!OnlyCalcLength) { |
522 | 0 | state = crypt_charstring_seed; |
523 | 0 | gs_type1_encrypt(stripped, stripped, dest_length, &state); |
524 | 0 | } |
525 | 0 | return dest_length; |
526 | 0 | } |
527 | | |
528 | | /* |
529 | | * Write the Private dictionary. This is a separate procedure only for |
530 | | * readability. write_CharString is a parameter so that we can encrypt |
531 | | * Subrs and CharStrings when the font's lenIV == -1 but we are writing |
532 | | * the font with lenIV = 0. |
533 | | */ |
534 | | static int |
535 | | write_Private(stream *s, gs_font_type1 *pfont, |
536 | | gs_glyph *subset_glyphs, uint subset_size, |
537 | | gs_glyph notdef, int lenIV, |
538 | | int (*write_CharString)(stream *, const void *, uint), |
539 | | const param_printer_params_t *ppp, int options) |
540 | 3.02k | { |
541 | 3.02k | const gs_type1_data *const pdata = &pfont->data; |
542 | 3.02k | printer_param_list_t rlist; |
543 | 3.02k | gs_param_list *const plist = (gs_param_list *)&rlist; |
544 | 3.02k | int code = s_init_param_printer(&rlist, ppp, s); |
545 | 3.02k | byte *SubrsWithMM = 0; |
546 | | |
547 | 3.02k | if (code < 0) |
548 | 0 | return 0; |
549 | 3.02k | stream_puts(s, "dup /Private 17 dict dup begin\n"); |
550 | 3.02k | stream_puts(s, "/-|{string currentfile exch readstring pop}executeonly def\n"); |
551 | 3.02k | stream_puts(s, "/|-{noaccess def}executeonly def\n"); |
552 | 3.02k | stream_puts(s, "/|{noaccess put}executeonly def\n"); |
553 | 3.02k | { |
554 | 3.02k | static const gs_param_item_t private_items[] = { |
555 | 3.02k | {"BlueFuzz", gs_param_type_int, |
556 | 3.02k | offset_of(gs_type1_data, BlueFuzz)}, |
557 | 3.02k | {"BlueScale", gs_param_type_float, |
558 | 3.02k | offset_of(gs_type1_data, BlueScale)}, |
559 | 3.02k | {"BlueShift", gs_param_type_float, |
560 | 3.02k | offset_of(gs_type1_data, BlueShift)}, |
561 | 3.02k | {"ExpansionFactor", gs_param_type_float, |
562 | 3.02k | offset_of(gs_type1_data, ExpansionFactor)}, |
563 | 3.02k | {"ForceBold", gs_param_type_bool, |
564 | 3.02k | offset_of(gs_type1_data, ForceBold)}, |
565 | 3.02k | {"LanguageGroup", gs_param_type_int, |
566 | 3.02k | offset_of(gs_type1_data, LanguageGroup)}, |
567 | 3.02k | {"RndStemUp", gs_param_type_bool, |
568 | 3.02k | offset_of(gs_type1_data, RndStemUp)}, |
569 | 3.02k | gs_param_item_end |
570 | 3.02k | }; |
571 | 3.02k | gs_type1_data defaults; |
572 | | |
573 | 3.02k | defaults.BlueFuzz = 1; |
574 | 3.02k | defaults.BlueScale = (float)0.039625; |
575 | 3.02k | defaults.BlueShift = 7.0; |
576 | 3.02k | defaults.ExpansionFactor = (float)0.06; |
577 | 3.02k | defaults.ForceBold = false; |
578 | 3.02k | defaults.LanguageGroup = 0; |
579 | 3.02k | defaults.RndStemUp = true; |
580 | 3.02k | code = gs_param_write_items(plist, pdata, &defaults, private_items); |
581 | 3.02k | if (code < 0) |
582 | 0 | return code; |
583 | 3.02k | if (lenIV != 4) { |
584 | 5 | code = param_write_int(plist, "lenIV", &lenIV); |
585 | 5 | if (code < 0) |
586 | 0 | return code; |
587 | 5 | } |
588 | 3.02k | write_float_array(plist, "BlueValues", pdata->BlueValues.values, |
589 | 3.02k | pdata->BlueValues.count); |
590 | 3.02k | write_float_array(plist, "OtherBlues", pdata->OtherBlues.values, |
591 | 3.02k | pdata->OtherBlues.count); |
592 | 3.02k | write_float_array(plist, "FamilyBlues", pdata->FamilyBlues.values, |
593 | 3.02k | pdata->FamilyBlues.count); |
594 | 3.02k | write_float_array(plist, "FamilyOtherBlues", pdata->FamilyOtherBlues.values, |
595 | 3.02k | pdata->FamilyOtherBlues.count); |
596 | 3.02k | write_float_array(plist, "StdHW", pdata->StdHW.values, |
597 | 3.02k | pdata->StdHW.count); |
598 | 3.02k | write_float_array(plist, "StdVW", pdata->StdVW.values, |
599 | 3.02k | pdata->StdVW.count); |
600 | 3.02k | write_float_array(plist, "StemSnapH", pdata->StemSnapH.values, |
601 | 3.02k | pdata->StemSnapH.count); |
602 | 3.02k | write_float_array(plist, "StemSnapV", pdata->StemSnapV.values, |
603 | 3.02k | pdata->StemSnapV.count); |
604 | 3.02k | } |
605 | 0 | write_uid(s, &pfont->UID, options); |
606 | 3.02k | stream_puts(s, "/MinFeature{16 16} def\n"); |
607 | 3.02k | stream_puts(s, "/password 5839 def\n"); |
608 | | |
609 | | /* |
610 | | * Write the Subrs. We always write them all, even for subsets. |
611 | | * (We will fix this someday.) |
612 | | */ |
613 | | |
614 | 3.02k | { |
615 | 3.02k | int n, i; |
616 | 3.02k | gs_glyph_data_t gdata; |
617 | 3.02k | int code; |
618 | | |
619 | 3.02k | gdata.memory = pfont->memory; |
620 | 3.02k | for (n = 0; |
621 | 46.9k | (code = pdata->procs.subr_data(pfont, n, false, &gdata)) != |
622 | 46.9k | gs_error_rangecheck; |
623 | 43.8k | ) { |
624 | 43.8k | ++n; |
625 | 43.8k | if (code >= 0) |
626 | 43.8k | gs_glyph_data_free(&gdata, "write_Private(Subrs)"); |
627 | 43.8k | } |
628 | 3.02k | if (pfont->data.WeightVector.count != 0) |
629 | 0 | SubrsWithMM = gs_alloc_bytes(pfont->memory, n, "Subrs record"); |
630 | | |
631 | 3.02k | pprintd1(s, "/Subrs %d array\n", n); |
632 | | |
633 | | /* prescan the /Subrs array to see if any of the Subrs call out to OtherSubrs */ |
634 | 3.02k | if (pfont->data.WeightVector.count != 0) { |
635 | 0 | for (i = 0; i < n; ++i) { |
636 | 0 | if ((code = pdata->procs.subr_data(pfont, i, false, &gdata)) >= 0) { |
637 | 0 | code = CheckSubrForMM(&gdata, pfont); |
638 | 0 | if (code < 0) { |
639 | 0 | if (SubrsWithMM != 0) |
640 | 0 | gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record"); |
641 | 0 | return code; |
642 | 0 | } |
643 | 0 | if (SubrsWithMM != 0) |
644 | 0 | SubrsWithMM[i] = code; |
645 | 0 | } |
646 | 0 | } |
647 | 0 | } |
648 | | |
649 | 46.9k | for (i = 0; i < n; ++i) |
650 | 43.8k | if ((code = pdata->procs.subr_data(pfont, i, false, &gdata)) >= 0) { |
651 | 43.8k | char buf[50]; |
652 | | |
653 | 43.8k | if (gdata.bits.size) { |
654 | 43.8k | if (pfont->data.WeightVector.count != 0) { |
655 | 0 | byte *stripped; |
656 | 0 | int length; |
657 | |
|
658 | 0 | length = strip_othersubrs(&gdata, pfont, NULL, SubrsWithMM); |
659 | 0 | stripped = gs_alloc_bytes(pfont->memory, length, "Subrs copy for OtherSubrs"); |
660 | 0 | code = strip_othersubrs(&gdata, pfont, stripped, SubrsWithMM); |
661 | 0 | if (code < 0) { |
662 | 0 | if (SubrsWithMM != 0) |
663 | 0 | gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record"); |
664 | 0 | return code; |
665 | 0 | } |
666 | 0 | gs_snprintf(buf, sizeof(buf), "dup %d %u -| ", i, code); |
667 | 0 | stream_puts(s, buf); |
668 | 0 | write_CharString(s, stripped, code); |
669 | 0 | gs_free_object(pfont->memory, stripped, "free Subrs copy for OtherSubrs"); |
670 | 43.8k | } else { |
671 | 43.8k | gs_snprintf(buf, sizeof(buf), "dup %d %u -| ", i, gdata.bits.size); |
672 | 43.8k | stream_puts(s, buf); |
673 | 43.8k | write_CharString(s, gdata.bits.data, gdata.bits.size); |
674 | 43.8k | } |
675 | 43.8k | stream_puts(s, " |\n"); |
676 | 43.8k | } |
677 | 43.8k | gs_glyph_data_free(&gdata, "write_Private(Subrs)"); |
678 | 43.8k | } |
679 | 3.02k | stream_puts(s, "|-\n"); |
680 | 3.02k | } |
681 | | |
682 | | /* We don't write OtherSubrs -- there had better not be any! */ |
683 | | |
684 | | /* Write the CharStrings. */ |
685 | | |
686 | 0 | { |
687 | 3.02k | int num_chars = 0; |
688 | 3.02k | gs_glyph glyph; |
689 | 3.02k | psf_glyph_enum_t genum; |
690 | 3.02k | gs_glyph_data_t gdata; |
691 | 3.02k | int code; |
692 | | |
693 | 3.02k | gdata.memory = pfont->memory; |
694 | 3.02k | psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, subset_glyphs, |
695 | 3.02k | (subset_glyphs ? subset_size : 0), |
696 | 3.02k | GLYPH_SPACE_NAME); |
697 | 3.02k | for (glyph = GS_NO_GLYPH; |
698 | 109k | (code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1; |
699 | 3.02k | ) |
700 | 106k | if (code == 0 && |
701 | 106k | (code = pdata->procs.glyph_data(pfont, glyph, &gdata)) >= 0 |
702 | 106k | ) { |
703 | 106k | ++num_chars; |
704 | 106k | gs_glyph_data_free(&gdata, "write_Private(CharStrings)"); |
705 | 106k | } |
706 | 3.02k | pprintd1(s, "2 index /CharStrings %d dict dup begin\n", num_chars); |
707 | 3.02k | psf_enumerate_glyphs_reset(&genum); |
708 | 3.02k | for (glyph = GS_NO_GLYPH; |
709 | 109k | (code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1; |
710 | 3.02k | ) |
711 | 106k | if (code == 0 && |
712 | 106k | (code = pdata->procs.glyph_data(pfont, glyph, &gdata)) >= 0 |
713 | 106k | ) { |
714 | 106k | gs_const_string gstr; |
715 | 106k | int code; |
716 | 106k | byte *stripped; |
717 | | |
718 | 106k | code = pfont->procs.glyph_name((gs_font *)pfont, glyph, &gstr); |
719 | 106k | if (code < 0) { |
720 | 0 | if (SubrsWithMM != 0) |
721 | 0 | gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record"); |
722 | 0 | return code; |
723 | 0 | } |
724 | | |
725 | 106k | stream_puts(s, "/"); |
726 | 106k | stream_write(s, gstr.data, gstr.size); |
727 | | |
728 | 106k | if (pfont->data.WeightVector.count != 0) { |
729 | 0 | gs_bytestring *data = (gs_bytestring *)&gdata.bits; |
730 | |
|
731 | 0 | stripped = gs_alloc_bytes(pfont->memory, data->size, "CharStrings copy for OtherSubrs"); |
732 | 0 | code = strip_othersubrs(&gdata, pfont, stripped, SubrsWithMM); |
733 | 0 | if (code < 0) { |
734 | 0 | if (SubrsWithMM != 0) |
735 | 0 | gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record"); |
736 | 0 | return code; |
737 | 0 | } |
738 | 0 | pprintd1(s, " %d -| ", code); |
739 | 0 | write_CharString(s, stripped, code); |
740 | 0 | gs_free_object(pfont->memory, stripped, "free CharStrings copy for OtherSubrs"); |
741 | 106k | } else { |
742 | 106k | pprintd1(s, " %d -| ", gdata.bits.size); |
743 | 106k | write_CharString(s, gdata.bits.data, gdata.bits.size); |
744 | 106k | } |
745 | | |
746 | 106k | stream_puts(s, " |-\n"); |
747 | 106k | gs_glyph_data_free(&gdata, "write_Private(CharStrings)"); |
748 | 106k | } |
749 | 3.02k | } |
750 | 3.02k | if (SubrsWithMM != 0) |
751 | 0 | gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record"); |
752 | | |
753 | | /* Wrap up. */ |
754 | | |
755 | 3.02k | stream_puts(s, "end\nend\nreadonly put\nnoaccess put\n"); |
756 | 3.02k | s_release_param_printer(&rlist); |
757 | 3.02k | return 0; |
758 | 3.02k | } |
759 | | |
760 | | /* Encrypt and write a CharString. */ |
761 | | static int |
762 | | stream_write_encrypted(stream *s, const void *ptr, uint count) |
763 | 0 | { |
764 | 0 | const byte *const data = ptr; |
765 | 0 | crypt_state state = crypt_charstring_seed; |
766 | 0 | byte buf[50]; /* arbitrary */ |
767 | 0 | uint left, n; |
768 | 0 | int code = 0; |
769 | |
|
770 | 0 | for (left = count; left > 0; left -= n) { |
771 | 0 | n = min(left, sizeof(buf)); |
772 | 0 | gs_type1_encrypt(buf, data + count - left, n, &state); |
773 | 0 | code = stream_write(s, buf, n); |
774 | 0 | } |
775 | 0 | return code; |
776 | 0 | } |
777 | | |
778 | | /* Write one FontInfo entry. */ |
779 | | static void |
780 | | write_font_info(stream *s, const char *key, const gs_const_string *pvalue, |
781 | | int do_write) |
782 | 12.1k | { |
783 | 12.1k | if (do_write) { |
784 | 188 | pprints1(s, "\n/%s ", key); |
785 | 188 | s_write_ps_string(s, pvalue->data, pvalue->size, PRINT_HEX_NOT_OK); |
786 | 188 | stream_puts(s, " def"); |
787 | 188 | } |
788 | 12.1k | } |
789 | | |
790 | | /* Write the definition of a Type 1 font. */ |
791 | | int |
792 | | psf_write_type1_font(stream *s, gs_font_type1 *pfont, int options, |
793 | | gs_glyph *orig_subset_glyphs, uint orig_subset_size, |
794 | | const gs_const_string *alt_font_name, int lengths[3]) |
795 | 3.02k | { |
796 | 3.02k | stream *es = s; |
797 | 3.02k | gs_offset_t start = stell(s); |
798 | 3.02k | param_printer_params_t ppp; |
799 | 3.02k | printer_param_list_t rlist; |
800 | 3.02k | gs_param_list *const plist = (gs_param_list *)&rlist; |
801 | 3.02k | stream AXE_stream; |
802 | 3.02k | stream_AXE_state AXE_state; |
803 | 3.02k | byte AXE_buf[200]; /* arbitrary */ |
804 | 3.02k | stream exE_stream; |
805 | 3.02k | stream_exE_state exE_state; |
806 | 3.02k | byte exE_buf[200]; /* arbitrary */ |
807 | 3.02k | psf_outline_glyphs_t glyphs; |
808 | 3.02k | int lenIV = pfont->data.lenIV; |
809 | 3.02k | int (*write_CharString)(stream *, const void *, uint) = stream_write; |
810 | 3.02k | int code = psf_get_type1_glyphs(&glyphs, pfont, orig_subset_glyphs, |
811 | 3.02k | orig_subset_size); |
812 | | |
813 | 3.02k | if (code < 0) |
814 | 0 | return code; |
815 | | |
816 | | /* Initialize the parameter printer. */ |
817 | | |
818 | 3.02k | ppp = param_printer_params_default; |
819 | 3.02k | ppp.item_suffix = " def\n"; |
820 | 3.02k | ppp.print_ok = |
821 | 3.02k | (options & WRITE_TYPE1_ASCIIHEX ? 0 : PRINT_BINARY_OK) | |
822 | 3.02k | PRINT_HEX_NOT_OK; |
823 | 3.02k | code = s_init_param_printer(&rlist, &ppp, s); |
824 | 3.02k | if (code < 0) |
825 | 0 | return code; |
826 | | |
827 | | /* Write the font header. */ |
828 | | |
829 | 3.02k | stream_puts(s, "%!FontType1-1.0: "); |
830 | 3.02k | write_font_name(s, pfont, alt_font_name, false); |
831 | 3.02k | stream_puts(s, "\n11 dict begin\n"); |
832 | | |
833 | | /* Write FontInfo. */ |
834 | | |
835 | 3.02k | stream_puts(s, "/FontInfo 5 dict dup begin"); |
836 | 3.02k | { |
837 | 3.02k | gs_font_info_t info; |
838 | 3.02k | int code = pfont->procs.font_info((gs_font *)pfont, NULL, |
839 | 3.02k | (FONT_INFO_COPYRIGHT | FONT_INFO_NOTICE | |
840 | 3.02k | FONT_INFO_FAMILY_NAME | FONT_INFO_FULL_NAME), |
841 | 3.02k | &info); |
842 | | |
843 | 3.02k | if (code >= 0) { |
844 | 3.02k | write_font_info(s, "Copyright", &info.Copyright, |
845 | 3.02k | info.members & FONT_INFO_COPYRIGHT); |
846 | 3.02k | write_font_info(s, "Notice", &info.Notice, |
847 | 3.02k | info.members & FONT_INFO_NOTICE); |
848 | 3.02k | write_font_info(s, "FamilyName", &info.FamilyName, |
849 | 3.02k | info.members & FONT_INFO_FAMILY_NAME); |
850 | 3.02k | write_font_info(s, "FullName", &info.FullName, |
851 | 3.02k | info.members & FONT_INFO_FULL_NAME); |
852 | 3.02k | } |
853 | 3.02k | } |
854 | 3.02k | stream_puts(s, "\nend readonly def\n"); |
855 | | |
856 | | /* Write the main font dictionary. */ |
857 | | |
858 | 3.02k | stream_puts(s, "/FontName "); |
859 | 3.02k | write_font_name(s, pfont, alt_font_name, true); |
860 | 3.02k | stream_puts(s, " def\n"); |
861 | 3.02k | code = write_Encoding(s, pfont, options, glyphs.subset_glyphs, |
862 | 3.02k | glyphs.subset_size, glyphs.notdef); |
863 | 3.02k | if (code < 0) |
864 | 0 | return code; |
865 | 3.02k | pprintg6(s, "/FontMatrix [%g %g %g %g %g %g] readonly def\n", |
866 | 3.02k | pfont->FontMatrix.xx, pfont->FontMatrix.xy, |
867 | 3.02k | pfont->FontMatrix.yx, pfont->FontMatrix.yy, |
868 | 3.02k | pfont->FontMatrix.tx, pfont->FontMatrix.ty); |
869 | 3.02k | write_uid(s, &pfont->UID, options); |
870 | 3.02k | pprintg4(s, "/FontBBox {%g %g %g %g} readonly def\n", |
871 | 3.02k | pfont->FontBBox.p.x, pfont->FontBBox.p.y, |
872 | 3.02k | pfont->FontBBox.q.x, pfont->FontBBox.q.y); |
873 | 3.02k | { |
874 | 3.02k | static const gs_param_item_t font_items[] = { |
875 | 3.02k | {"FontType", gs_param_type_int, |
876 | 3.02k | offset_of(gs_font_type1, FontType)}, |
877 | 3.02k | {"PaintType", gs_param_type_int, |
878 | 3.02k | offset_of(gs_font_type1, PaintType)}, |
879 | 3.02k | {"StrokeWidth", gs_param_type_float, |
880 | 3.02k | offset_of(gs_font_type1, StrokeWidth)}, |
881 | 3.02k | gs_param_item_end |
882 | 3.02k | }; |
883 | | |
884 | 3.02k | code = gs_param_write_items(plist, pfont, NULL, font_items); |
885 | 3.02k | if (code < 0) |
886 | 0 | return code; |
887 | 3.02k | } |
888 | | |
889 | | /* |
890 | | * This is nonsense. We cna't write the WeightVector alonr from a Multiple |
891 | | * Master and expect any sensible results. Since its useless alone, there's |
892 | | * no point in emitting it at all. Leaving the code in place in case we |
893 | | * decide to write MM fonts one day. |
894 | | { |
895 | | const gs_type1_data *const pdata = &pfont->data; |
896 | | |
897 | | write_float_array(plist, "WeightVector", pdata->WeightVector.values, |
898 | | pdata->WeightVector.count); |
899 | | } |
900 | | */ |
901 | 3.02k | stream_puts(s, "currentdict end\n"); |
902 | | |
903 | | /* Write the Private dictionary. */ |
904 | | |
905 | 3.02k | if (lenIV < 0 && (options & WRITE_TYPE1_WITH_LENIV)) { |
906 | | /* We'll have to encrypt the CharStrings. */ |
907 | 0 | lenIV = 0; |
908 | 0 | write_CharString = stream_write_encrypted; |
909 | 0 | } |
910 | 3.02k | if (options & WRITE_TYPE1_EEXEC) { |
911 | 3.02k | stream_puts(s, "currentfile eexec\n"); |
912 | 3.02k | lengths[0] = (int)(stell(s) - start); |
913 | 3.02k | start = stell(s); |
914 | 3.02k | if (options & WRITE_TYPE1_ASCIIHEX) { |
915 | 3.02k | s_init(&AXE_stream, s->memory); |
916 | 3.02k | s_init_state((stream_state *)&AXE_state, &s_AXE_template, NULL); |
917 | 3.02k | s_init_filter(&AXE_stream, (stream_state *)&AXE_state, |
918 | 3.02k | AXE_buf, sizeof(AXE_buf), es); |
919 | | /* We have to set this after s_init_filter() as that function |
920 | | * sets it to true. |
921 | | */ |
922 | 3.02k | AXE_state.EndOfData = false; |
923 | 3.02k | es = &AXE_stream; |
924 | 3.02k | } |
925 | 3.02k | s_init(&exE_stream, s->memory); |
926 | 3.02k | s_init_state((stream_state *)&exE_state, &s_exE_template, NULL); |
927 | 3.02k | exE_state.cstate = 55665; |
928 | 3.02k | s_init_filter(&exE_stream, (stream_state *)&exE_state, |
929 | 3.02k | exE_buf, sizeof(exE_buf), es); |
930 | 3.02k | es = &exE_stream; |
931 | | /* |
932 | | * Note: eexec encryption always writes/skips 4 initial bytes, not |
933 | | * the number of initial bytes given by pdata->lenIV. |
934 | | */ |
935 | 3.02k | stream_puts(es, "****"); |
936 | 3.02k | } |
937 | 3.02k | code = write_Private(es, pfont, glyphs.subset_glyphs, glyphs.subset_size, |
938 | 3.02k | glyphs.notdef, lenIV, write_CharString, &ppp, options); |
939 | 3.02k | if (code < 0) |
940 | 0 | return code; |
941 | 3.02k | stream_puts(es, "dup/FontName get exch definefont pop\n"); |
942 | 3.02k | if (options & WRITE_TYPE1_EEXEC) { |
943 | 3.02k | if (options & (WRITE_TYPE1_EEXEC_PAD | WRITE_TYPE1_EEXEC_MARK)) |
944 | 3.02k | stream_puts(es, "mark "); |
945 | 3.02k | stream_puts(es, "currentfile closefile\n"); |
946 | 3.02k | s_close_filters(&es, s); |
947 | 3.02k | lengths[1] = (int)(stell(s) - start); |
948 | 3.02k | start = stell(s); |
949 | 3.02k | if (options & WRITE_TYPE1_EEXEC_PAD) { |
950 | 3.02k | int i; |
951 | | |
952 | 27.2k | for (i = 0; i < 8; ++i) |
953 | 24.2k | stream_puts(s, "\n0000000000000000000000000000000000000000000000000000000000000000"); |
954 | 3.02k | stream_puts(s, "\ncleartomark\n"); |
955 | 3.02k | } |
956 | 3.02k | lengths[2] = (int)(stell(s) - start); |
957 | 3.02k | } else { |
958 | 0 | lengths[0] = (int)(stell(s) - start); |
959 | 0 | lengths[1] = lengths[2] = 0; |
960 | 0 | } |
961 | | |
962 | | /* Wrap up. */ |
963 | | |
964 | 3.02k | s_release_param_printer(&rlist); |
965 | 3.02k | return 0; |
966 | 3.02k | } |