/src/ghostpdl/devices/vector/gdevpdti.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 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 | | /* Bitmap font implementation for pdfwrite */ |
18 | | #include "memory_.h" |
19 | | #include "gx.h" |
20 | | #include "gxpath.h" |
21 | | #include "gserrors.h" |
22 | | #include "gsutil.h" |
23 | | #include "gdevpdfx.h" |
24 | | #include "gdevpdfg.h" |
25 | | #include "gdevpdtf.h" |
26 | | #include "gdevpdti.h" |
27 | | #include "gdevpdts.h" |
28 | | #include "gdevpdtw.h" |
29 | | #include "gdevpdtt.h" |
30 | | #include "gdevpdfo.h" |
31 | | #include "gxchar.h" /* For gs_show_enum */ |
32 | | |
33 | | /* ---------------- Private ---------------- */ |
34 | | |
35 | | /* Define the structure for a CharProc pseudo-resource. */ |
36 | | /*typedef struct pdf_char_proc_s pdf_char_proc_t;*/ /* gdevpdfx.h */ |
37 | | struct pdf_char_proc_s { |
38 | | pdf_resource_common(pdf_char_proc_t); |
39 | | pdf_char_proc_ownership_t *owner_fonts; /* fonts using this charproc. */ |
40 | | int y_offset; /* of character (0,0) */ |
41 | | int x_offset; /* of character (0,0) */ |
42 | | gs_point real_width; /* Not used with synthesised bitmap fonts. */ |
43 | | gs_point v; /* Not used with synthesised bitmap fonts. */ |
44 | | }; |
45 | | |
46 | | /* The descriptor is public for pdf_resource_type_structs. */ |
47 | | gs_public_st_suffix_add1(st_pdf_char_proc, pdf_char_proc_t, |
48 | | "pdf_char_proc_t", pdf_char_proc_enum_ptrs, pdf_char_proc_reloc_ptrs, |
49 | | st_pdf_resource, owner_fonts); |
50 | | |
51 | | struct pdf_char_proc_ownership_s { |
52 | | pdf_char_proc_t *char_proc; |
53 | | pdf_char_proc_ownership_t *font_next; /* next char_proc for same font */ |
54 | | pdf_char_proc_ownership_t *char_next; /* next char_proc for same charproc */ |
55 | | pdf_font_resource_t *font; |
56 | | gs_char char_code; /* Character code in PDF font. */ |
57 | | gs_glyph glyph; /* Glyph id in Postscript font. */ |
58 | | gs_string char_name; |
59 | | bool duplicate_char_name; |
60 | | }; |
61 | | gs_private_st_strings1_ptrs4(st_pdf_char_proc_ownership, pdf_char_proc_ownership_t, |
62 | | "pdf_char_proc_ownership_t", pdf_char_proc_ownership_enum_ptrs, |
63 | | pdf_char_proc_ownership_reloc_ptrs, char_name, char_proc, char_next, font_next, font); |
64 | | |
65 | | gs_private_st_ptrs1(st_pdf_bitmap_fonts, pdf_bitmap_fonts_t, |
66 | | "pdf_bitmap_fonts_t", pdf_bitmap_fonts_enum_ptrs, |
67 | | pdf_bitmap_fonts_reloc_ptrs, open_font); |
68 | | |
69 | | static inline int64_t |
70 | | pdf_char_proc_id(const pdf_char_proc_t *pcp) |
71 | 295k | { |
72 | 295k | return pdf_resource_id((const pdf_resource_t *)pcp); |
73 | 295k | } |
74 | | |
75 | | /* Assign a code for a char_proc. */ |
76 | | static int |
77 | | assign_char_code(gx_device_pdf * pdev, gs_text_enum_t *pte) |
78 | 134k | { |
79 | 134k | pdf_bitmap_fonts_t *pbfs = pdev->text->bitmap_fonts; |
80 | 134k | pdf_font_resource_t *pdfont = pbfs->open_font; /* Type 3 */ |
81 | 134k | int i, c = 0, code; |
82 | 134k | uint operation = pte->text.operation; |
83 | | |
84 | 134k | if (pbfs->bitmap_encoding_id == 0) |
85 | 1.80k | pbfs->bitmap_encoding_id = pdf_obj_ref(pdev); |
86 | 134k | if (pdfont == 0 || pdfont->u.simple.LastChar == 255 || |
87 | 134k | !pbfs->use_open_font |
88 | 134k | ) { |
89 | | /* Start a new synthesized font. */ |
90 | 2.38k | char *pc; |
91 | | |
92 | 2.38k | code = pdf_font_type3_alloc(pdev, &pdfont, pdf_write_contents_bitmap); |
93 | 2.38k | if (code < 0) |
94 | 0 | return code; |
95 | 2.38k | pdfont->u.simple.s.type3.bitmap_font = true; |
96 | 2.38k | if (pbfs->open_font == 0) |
97 | 1.80k | pdfont->rname[0] = 0; |
98 | 581 | else |
99 | 581 | strcpy(pdfont->rname, pbfs->open_font->rname); |
100 | 2.38k | pdfont->u.simple.s.type3.FontBBox.p.x = 0; |
101 | 2.38k | pdfont->u.simple.s.type3.FontBBox.p.y = 0; |
102 | 2.38k | pdfont->u.simple.s.type3.FontBBox.q.x = 0; |
103 | 2.38k | pdfont->u.simple.s.type3.FontBBox.q.y = 0; |
104 | 2.38k | pdfont->mark_glyph = NULL; |
105 | 2.38k | gs_make_identity(&pdfont->u.simple.s.type3.FontMatrix); |
106 | | /* |
107 | | * We "increment" the font name as a radix-26 "number". |
108 | | * This cannot possibly overflow. |
109 | | */ |
110 | 2.38k | for (pc = pdfont->rname; *pc == 'Z'; ++pc) |
111 | 0 | *pc = '@'; |
112 | 2.38k | if ((*pc)++ == 0) |
113 | 1.80k | *pc = 'A', pc[1] = 0; |
114 | 2.38k | pbfs->open_font = pdfont; |
115 | 2.38k | pbfs->use_open_font = true; |
116 | 2.38k | pdfont->u.simple.FirstChar = 255; |
117 | 2.38k | } |
118 | 134k | if ((operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) || |
119 | 134k | (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR))) { |
120 | 134k | unsigned char p = *pte->text.data.bytes; |
121 | 134k | unsigned char index = p / 8, bit = 0x01 << (p % 8); |
122 | | |
123 | 134k | if (pdfont->used[index] & bit) { |
124 | 10.6M | for (i = 0;i < 256;i++) { |
125 | 10.6M | index = i / 8; |
126 | 10.6M | bit = 0x01 << (i % 8); |
127 | 10.6M | if (!(pdfont->used[index] & bit)) { |
128 | 121k | c = i; |
129 | 121k | break; |
130 | 121k | } |
131 | 10.6M | } |
132 | 121k | } else |
133 | 12.2k | c = p; |
134 | 134k | pdfont->used[index] |= bit; |
135 | 134k | if (c > pdfont->u.simple.LastChar) |
136 | 86.2k | pdfont->u.simple.LastChar = c; |
137 | | |
138 | 134k | } else { |
139 | 0 | unsigned char index, bit; |
140 | 0 | c = ++(pdfont->u.simple.LastChar); |
141 | 0 | index = c / 8; |
142 | 0 | bit = 0x01 << (c % 8); |
143 | 0 | pdfont->used[index] |= bit; |
144 | 0 | } |
145 | 134k | if (c < pdfont->u.simple.FirstChar) |
146 | 3.83k | pdfont->u.simple.FirstChar = c; |
147 | | |
148 | 134k | pdfont->Widths[c] = psdf_round(pdev->char_width.x, 100, 10); /* See |
149 | | pdf_write_Widths about rounding. We need to provide |
150 | | a compatible data for Tj. */ |
151 | 134k | if (c > pbfs->max_embedded_code) |
152 | 61.4k | pbfs->max_embedded_code = c; |
153 | | |
154 | 134k | return c; |
155 | 134k | } |
156 | | |
157 | | /* Write the contents of a Type 3 bitmap or vector font resource. */ |
158 | | int |
159 | | pdf_write_contents_bitmap(gx_device_pdf *pdev, pdf_font_resource_t *pdfont) |
160 | 3.29k | { |
161 | 3.29k | stream *s = pdev->strm; |
162 | 3.29k | const pdf_char_proc_ownership_t *pcpo; |
163 | 3.29k | int64_t diff_id = 0; |
164 | 3.29k | int code; |
165 | | |
166 | 3.29k | if (pdfont->u.simple.s.type3.bitmap_font) |
167 | 2.38k | diff_id = pdev->text->bitmap_fonts->bitmap_encoding_id; |
168 | 915 | else { |
169 | | /* See comment in pdf_write_encoding. */ |
170 | 915 | diff_id = pdf_obj_ref(pdev); |
171 | 915 | } |
172 | 3.29k | code = pdf_write_encoding_ref(pdev, pdfont, diff_id); |
173 | 3.29k | if (code < 0) |
174 | 0 | return code; |
175 | 3.29k | stream_puts(s, "/CharProcs <<"); |
176 | | /* Write real characters. */ |
177 | 151k | for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo; |
178 | 147k | pcpo = pcpo->char_next |
179 | 147k | ) { |
180 | 147k | if (pdfont->u.simple.s.type3.bitmap_font) |
181 | 134k | pprinti64d2(s, "/a%"PRId64" %"PRId64" 0 R\n", pcpo->char_code, |
182 | 134k | pdf_char_proc_id(pcpo->char_proc)); |
183 | 13.7k | else if (!pcpo-> duplicate_char_name) { |
184 | 13.6k | pdf_put_name(pdev, pcpo->char_name.data, pcpo->char_name.size); |
185 | 13.6k | pprinti64d1(s, " %"PRId64" 0 R\n", pdf_char_proc_id(pcpo->char_proc)); |
186 | 13.6k | } |
187 | 147k | pdf_record_usage_by_parent(pdev, pdf_char_proc_id(pcpo->char_proc), pdfont->object->id); |
188 | 147k | } |
189 | 3.29k | stream_puts(s, ">>"); |
190 | 3.29k | pprintg6(s, "/FontMatrix[%g %g %g %g %g %g]", |
191 | 3.29k | (float)pdfont->u.simple.s.type3.FontMatrix.xx, |
192 | 3.29k | (float)pdfont->u.simple.s.type3.FontMatrix.xy, |
193 | 3.29k | (float)pdfont->u.simple.s.type3.FontMatrix.yx, |
194 | 3.29k | (float)pdfont->u.simple.s.type3.FontMatrix.yy, |
195 | 3.29k | (float)pdfont->u.simple.s.type3.FontMatrix.tx, |
196 | 3.29k | (float)pdfont->u.simple.s.type3.FontMatrix.ty); |
197 | 3.29k | code = pdf_finish_write_contents_type3(pdev, pdfont); |
198 | 3.29k | if (code < 0) |
199 | 0 | return code; |
200 | 3.29k | if (!pdfont->u.simple.s.type3.bitmap_font && diff_id > 0) { |
201 | 915 | code = pdf_write_encoding(pdev, pdfont, diff_id, 0); |
202 | 915 | if (code < 0) |
203 | 0 | return code; |
204 | 915 | } |
205 | 3.29k | return 0; |
206 | 3.29k | } |
207 | | |
208 | | /* ---------------- Public ---------------- */ |
209 | | |
210 | | /* |
211 | | * Allocate and initialize bookkeeping for bitmap fonts. |
212 | | */ |
213 | | pdf_bitmap_fonts_t * |
214 | | pdf_bitmap_fonts_alloc(gs_memory_t *mem) |
215 | 34.0k | { |
216 | 34.0k | pdf_bitmap_fonts_t *pbfs = |
217 | 34.0k | gs_alloc_struct(mem, pdf_bitmap_fonts_t, &st_pdf_bitmap_fonts, |
218 | 34.0k | "pdf_bitmap_fonts_alloc"); |
219 | | |
220 | 34.0k | if (pbfs == 0) |
221 | 0 | return 0; |
222 | 34.0k | memset(pbfs, 0, sizeof(*pbfs)); |
223 | 34.0k | pbfs->max_embedded_code = -1; |
224 | 34.0k | return pbfs; |
225 | 34.0k | } |
226 | | |
227 | | /* |
228 | | * Update text state at the end of a page. |
229 | | */ |
230 | | void |
231 | | pdf_close_text_page(gx_device_pdf *pdev) |
232 | 45.3k | { |
233 | | /* |
234 | | * When Acrobat Reader 3 prints a file containing a Type 3 font with a |
235 | | * non-standard Encoding, it apparently only emits the subset of the |
236 | | * font actually used on the page. Thus, if the "Download Fonts Once" |
237 | | * option is selected, characters not used on the page where the font |
238 | | * first appears will not be defined, and hence will print as blank if |
239 | | * used on subsequent pages. Thus, we can't allow a Type 3 font to |
240 | | * add additional characters on subsequent pages. |
241 | | */ |
242 | 45.3k | if (pdev->CompatibilityLevel <= 1.2) |
243 | 33.1k | pdev->text->bitmap_fonts->use_open_font = false; |
244 | 45.3k | } |
245 | | |
246 | | int |
247 | | pdf_charproc_y_offset(pdf_char_proc_t *pcp) |
248 | 1.67M | { |
249 | 1.67M | return pcp->y_offset; |
250 | 1.67M | } |
251 | | |
252 | | int |
253 | | pdf_charproc_x_offset(pdf_char_proc_t *pcp) |
254 | 1.67M | { |
255 | 1.67M | return pcp->x_offset; |
256 | 1.67M | } |
257 | | |
258 | | /* Attach a CharProc to a font. */ |
259 | | static int |
260 | | pdf_attach_charproc(gx_device_pdf * pdev, pdf_font_resource_t *pdfont, pdf_char_proc_t *pcp, |
261 | | gs_glyph glyph, gs_char char_code, const gs_const_string *gnstr) |
262 | 232k | { |
263 | 232k | pdf_char_proc_ownership_t *pcpo; |
264 | 232k | bool duplicate_char_name = false; |
265 | | |
266 | 14.2M | for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
267 | 14.1M | if (pcpo->glyph == glyph && pcpo->char_code == char_code) |
268 | 81.0k | return 0; |
269 | 14.1M | } |
270 | 151k | if (!pdfont->u.simple.s.type3.bitmap_font) { |
271 | 616k | for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
272 | 598k | if (!bytes_compare(pcpo->char_name.data, pcpo->char_name.size, gnstr->data, gnstr->size)) { |
273 | 37 | duplicate_char_name = true; |
274 | 37 | break; |
275 | 37 | } |
276 | 598k | } |
277 | 17.5k | } |
278 | 151k | pcpo = gs_alloc_struct(pdev->pdf_memory, |
279 | 151k | pdf_char_proc_ownership_t, &st_pdf_char_proc_ownership, "pdf_attach_charproc"); |
280 | | |
281 | 151k | if (pcpo == NULL) |
282 | 0 | return_error(gs_error_VMerror); |
283 | 151k | pcpo->font = pdfont; |
284 | 151k | pcpo->char_next = pdfont->u.simple.s.type3.char_procs; |
285 | 151k | pdfont->u.simple.s.type3.char_procs = pcpo; |
286 | 151k | pcpo->char_proc = pcp; |
287 | 151k | pcpo->font_next = pcp->owner_fonts; |
288 | 151k | pcp->owner_fonts = pcpo; |
289 | 151k | pcpo->char_code = char_code; |
290 | 151k | pcpo->glyph = glyph; |
291 | 151k | if (gnstr == NULL) { |
292 | 134k | pcpo->char_name.data = 0; |
293 | 134k | pcpo->char_name.size = 0; |
294 | 134k | } else { |
295 | 17.5k | if (gnstr->size > 0) { |
296 | 17.5k | pcpo->char_name.data = gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, gnstr->size, "storage for charproc name"); |
297 | 17.5k | if (pcpo->char_name.data == NULL) |
298 | 0 | return_error(gs_error_VMerror); |
299 | 17.5k | memcpy(pcpo->char_name.data, gnstr->data, gnstr->size); |
300 | 17.5k | } |
301 | 17.5k | pcpo->char_name.size = gnstr->size; |
302 | 17.5k | } |
303 | 151k | pcpo->duplicate_char_name = duplicate_char_name; |
304 | 151k | return 0; |
305 | 151k | } |
306 | | |
307 | | int |
308 | | pdf_free_charproc_ownership(gx_device_pdf * pdev, pdf_resource_t *pres) |
309 | 3.34k | { |
310 | 3.34k | pdf_char_proc_ownership_t *next, *pcpo = (pdf_char_proc_ownership_t *)pres; |
311 | | |
312 | 155k | while (pcpo) { |
313 | 151k | next = pcpo->char_next; |
314 | 151k | if(pcpo->char_name.size != 0 && pcpo->char_name.data) { |
315 | 17.5k | gs_free_object(pdev->pdf_memory->non_gc_memory, pcpo->char_name.data, "free storage for charproc naem"); |
316 | | /* This causes PCL some trouble, don't know why yet FIXME-MEMORY |
317 | | gs_free_string(pdev->pdf_memory, (byte *)pcpo->char_name.data, pcpo->char_name.size, "Free CharProc name");*/ |
318 | 17.5k | pcpo->char_name.data = (byte *)0L; |
319 | 17.5k | pcpo->char_name.size = 0; |
320 | 17.5k | } |
321 | 151k | gs_free_object(pdev->pdf_memory, pcpo, "Free CharProc"); |
322 | 151k | pcpo = next; |
323 | 151k | } |
324 | 3.34k | return 0; |
325 | 3.34k | } |
326 | | |
327 | | /* Begin a CharProc for a synthesized (bitmap) font. */ |
328 | | int |
329 | | pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width, |
330 | | int y_offset, int x_offset, gs_id id, pdf_char_proc_t ** ppcp, |
331 | | pdf_stream_position_t * ppos) |
332 | 134k | { |
333 | 134k | gs_char char_code = 0; |
334 | 134k | pdf_bitmap_fonts_t *const pbfs = pdev->text->bitmap_fonts; |
335 | 134k | pdf_font_resource_t *font; |
336 | 134k | pdf_resource_t *pres; |
337 | 134k | pdf_char_proc_t *pcp; |
338 | 134k | int code; |
339 | | /* This code added to store PCL bitmap glyphs in type 3 fonts where possible */ |
340 | 134k | gs_glyph glyph = GS_NO_GLYPH; |
341 | 134k | gs_const_string str2, *str = NULL; |
342 | 134k | gs_show_enum *show_enum = (gs_show_enum *)pdev->pte; |
343 | 134k | pdf_encoding_element_t *pet = 0; |
344 | | /* Since this is for text searching, its only useful if the character code |
345 | | * lies in an ASCII range, so we only handle some kinds of text layout. |
346 | | */ |
347 | 134k | int allowed_op = (show_enum->text.operation & |
348 | 134k | (TEXT_FROM_STRING | TEXT_FROM_BYTES | TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR)); |
349 | | |
350 | | /* Check to see the current font is a type 3. We can get here if pdfwrite decides |
351 | | * it can't handle a font type, and renders to a bitmap instead. If that's the |
352 | | * case then we can't add the bitmap to the existing font (its not a type 3 font) |
353 | | * and must fall back to holding it in our fallback type 3 font 'collection'. |
354 | | */ |
355 | | /* Because the bitmaps are stored directly in the cache they already have any |
356 | | * effects caused by non-identity FontMatrix entries applied. So if the type 3 |
357 | | * font we created has a non-identity FontMatrix we can't use it and must |
358 | | * go back to collecting the bitmap into our fallback font. |
359 | | */ |
360 | 134k | if ((show_enum->current_font->FontType == ft_user_defined || |
361 | 134k | show_enum->current_font->FontType == ft_PDF_user_defined || |
362 | 134k | show_enum->current_font->FontType == ft_PCL_user_defined || |
363 | 134k | show_enum->current_font->FontType == ft_MicroType || |
364 | 134k | show_enum->current_font->FontType == ft_GL2_stick_user_defined || |
365 | 134k | show_enum->current_font->FontType == ft_GL2_531) && allowed_op && |
366 | 134k | show_enum->current_font->FontMatrix.xx == 1 && show_enum->current_font->FontMatrix.xy == 0 && |
367 | 134k | show_enum->current_font->FontMatrix.yx == 0 && show_enum->current_font->FontMatrix.yy == 1) { |
368 | 0 | pdf_char_proc_ownership_t *pcpo; |
369 | |
|
370 | 0 | gs_font_base *base = (gs_font_base *)show_enum->current_font; |
371 | 0 | code = pdf_attached_font_resource(pdev, show_enum->current_font, &font, NULL, NULL, NULL, NULL); |
372 | 0 | if (code < 0) |
373 | 0 | return code; |
374 | 0 | if (font == NULL) |
375 | 0 | return_error(gs_error_invalidfont); |
376 | | |
377 | | /* The text processing will have run past the glyph, so we need to 'back up' |
378 | | * by one and get it again in order to get the character code and glyph, and update |
379 | | * the pointer correctly. |
380 | | */ |
381 | 0 | show_enum->index--; |
382 | 0 | code = gs_default_next_char_glyph((gs_text_enum_t *)show_enum, (gs_char *)&char_code, &glyph); |
383 | 0 | if (code < 0) |
384 | 0 | return code; |
385 | | |
386 | | /* If the returned character code is outside the possible Encoding for |
387 | | * a type 3 font, then set pet to NULL, this means we will fall back to |
388 | | * the 'collection' font, as pet is checked below. |
389 | | */ |
390 | 0 | if ((int)char_code >= 0 && (int)char_code <= 255) { |
391 | 0 | pet = &font->u.simple.Encoding[char_code]; |
392 | 0 | if (pet) { |
393 | | /* Check to see if we *already* have this glyph in this font. If |
394 | | * we do then we can't add it to this font. Setting pet to 0 |
395 | | * falls back to the collection method. |
396 | | */ |
397 | 0 | for (pcpo = font->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
398 | 0 | if (pcpo->glyph == pet->glyph && pcpo->char_code == char_code) { |
399 | 0 | pet = 0x00; |
400 | 0 | break; |
401 | 0 | } |
402 | 0 | } |
403 | 0 | } |
404 | 0 | } |
405 | 0 | else |
406 | 0 | pet = 0x00; |
407 | | |
408 | | /* We need a glyph name for the type 3 font's Encoding, if we haven't got one |
409 | | * then we need to give up, something about the font or text is not acceptable |
410 | | * (see various comments above). |
411 | | */ |
412 | 0 | if (pet && pet->glyph != GS_NO_GLYPH && !(pet->size == 7 && |
413 | 0 | !strncmp((const char *)pet->data, ".notdef", 7))) { |
414 | 0 | if (char_code < font->u.simple.FirstChar) |
415 | 0 | font->u.simple.FirstChar = char_code; |
416 | 0 | if ((int)char_code > font->u.simple.LastChar) |
417 | 0 | font->u.simple.LastChar = char_code; |
418 | 0 | base->FontBBox.q.x = max(base->FontBBox.q.x, w); |
419 | 0 | base->FontBBox.q.y = max(base->FontBBox.q.y, y_offset + h); |
420 | 0 | str2.data = pet->data; |
421 | 0 | str2.size = pet->size; |
422 | 0 | str = &str2; |
423 | 0 | glyph = pet->glyph; |
424 | | /* This is to work around a weird Acrobat bug. If the Encoding of a type 3 |
425 | | * (possibly other types) is simply a standard encoding (eg WinAnsiEncoding) |
426 | | * then Acrobat 4 & 8 just ignore the glyphs altogether. This forces us to write |
427 | | * all the used glyphs as /Differencess, and that makes it work <sigh> |
428 | | */ |
429 | 0 | pet->is_difference = 1; |
430 | 0 | font->Widths[char_code] = psdf_round(pdev->char_width.x, 100, 10); /* See |
431 | | pdf_write_Widths about rounding. We need to provide |
432 | | a compatible data for Tj. */ |
433 | 0 | } else { |
434 | 0 | char_code = assign_char_code(pdev, pdev->pte); |
435 | 0 | font = pbfs->open_font; /* Type 3 */ |
436 | 0 | } |
437 | 134k | } else { |
438 | 134k | char_code = assign_char_code(pdev, pdev->pte); |
439 | 134k | font = pbfs->open_font; /* Type 3 */ |
440 | 134k | } |
441 | | |
442 | 134k | code = pdf_begin_resource(pdev, resourceCharProc, id, &pres); |
443 | 134k | if (code < 0) |
444 | 0 | return code; |
445 | 134k | pcp = (pdf_char_proc_t *) pres; |
446 | 134k | code = pdf_attach_charproc(pdev, font, pcp, glyph, char_code, str); |
447 | 134k | if (code < 0) |
448 | 0 | return code; |
449 | 134k | pres->object->written = true; |
450 | 134k | { |
451 | 134k | stream *s = pdev->strm; |
452 | | |
453 | | /* |
454 | | * The resource file is positionable, so rather than use an |
455 | | * object reference for the length, we'll go back and fill it in |
456 | | * at the end of the definition. Take 1M as the longest |
457 | | * definition we can handle. (This used to be 10K, but there was |
458 | | * a real file that exceeded this limit.) |
459 | | */ |
460 | 134k | stream_puts(s, "<</Length >>stream\n"); |
461 | 134k | ppos->start_pos = stell(s); |
462 | 134k | } |
463 | 134k | code = pdf_begin_encrypt(pdev, &pdev->strm, pres->object->id); |
464 | 134k | if (code < 0) |
465 | 0 | return code; |
466 | 134k | pcp->y_offset = y_offset; |
467 | 134k | pcp->x_offset = x_offset; |
468 | 134k | font->u.simple.s.type3.FontBBox.q.x = |
469 | 134k | max(font->u.simple.s.type3.FontBBox.q.x, w); |
470 | 134k | font->u.simple.s.type3.FontBBox.q.y = |
471 | 134k | max(font->u.simple.s.type3.FontBBox.q.y, y_offset + h); |
472 | 134k | font->u.simple.s.type3.max_y_offset = |
473 | 134k | max(font->u.simple.s.type3.max_y_offset, h + (h >> 2)); |
474 | 134k | pcp->real_width.x = w; |
475 | 134k | pcp->real_width.y = y_offset + h; |
476 | 134k | *ppcp = pcp; |
477 | 134k | return 0; |
478 | 134k | } |
479 | | |
480 | | /* End a CharProc. */ |
481 | | int |
482 | | pdf_end_char_proc(gx_device_pdf * pdev, pdf_stream_position_t * ppos) |
483 | 134k | { |
484 | 134k | stream *s; |
485 | 134k | gs_offset_t start_pos, end_pos, length; |
486 | | |
487 | 134k | if (pdf_end_encrypt(pdev)) |
488 | 0 | s_close_filters(&pdev->strm, pdev->strm->strm); |
489 | | |
490 | 134k | s = pdev->strm; |
491 | 134k | start_pos = ppos->start_pos; |
492 | 134k | end_pos = stell(s); |
493 | 134k | length = end_pos - start_pos; |
494 | 134k | if (length > 999999) |
495 | 0 | return_error(gs_error_limitcheck); |
496 | 134k | sseek(s, start_pos - 15); |
497 | 134k | pprintd1(s, "%d", length); |
498 | 134k | sseek(s, end_pos); |
499 | 134k | if (pdev->PDFA != 0) |
500 | 0 | stream_puts(s, "\n"); |
501 | 134k | stream_puts(s, "endstream\n"); |
502 | 134k | pdf_end_separate(pdev, resourceCharProc); |
503 | 134k | return 0; |
504 | 134k | } |
505 | | |
506 | | /* Mark glyph names for garbager. */ |
507 | | void |
508 | | pdf_mark_glyph_names(const pdf_font_resource_t *pdfont, const gs_memory_t *memory) |
509 | 35.1k | { |
510 | 35.1k | if (pdfont->mark_glyph == NULL) { |
511 | | /* Synthesised bitmap fonts pass here. */ |
512 | 33.1k | return; |
513 | 33.1k | } |
514 | 2.03k | if (pdfont->u.simple.Encoding != NULL) { |
515 | 2.03k | int i; |
516 | | |
517 | 522k | for (i = 0; i < 256; i++) |
518 | 520k | if (pdfont->u.simple.Encoding[i].glyph != GS_NO_GLYPH) |
519 | 83.7k | pdfont->mark_glyph(memory, pdfont->u.simple.Encoding[i].glyph, pdfont->mark_glyph_data); |
520 | 2.03k | } |
521 | 2.03k | if (pdfont->FontType == ft_user_defined || |
522 | 2.03k | pdfont->FontType == ft_PDF_user_defined || |
523 | 2.03k | pdfont->FontType == ft_PCL_user_defined || |
524 | 2.03k | pdfont->FontType == ft_MicroType || |
525 | 2.03k | pdfont->FontType == ft_GL2_stick_user_defined || |
526 | 2.03k | pdfont->FontType == ft_GL2_531) { |
527 | 320 | const pdf_char_proc_ownership_t *pcpo = pdfont->u.simple.s.type3.char_procs; |
528 | | |
529 | 6.58k | for (; pcpo != NULL; pcpo = pcpo->font_next) |
530 | 6.26k | pdfont->mark_glyph(memory, pcpo->glyph, pdfont->mark_glyph_data); |
531 | 320 | } |
532 | 2.03k | } |
533 | | |
534 | | /* Put out a reference to an image as a character in a synthesized font. */ |
535 | | int |
536 | | pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp, |
537 | | const gs_matrix * pimat) |
538 | 1.81M | { |
539 | | /* We need to choose a font, which use the charproc. |
540 | | In most cases it is the last font, which the charproc is attached to. |
541 | | If the charproc is substituted, it causes a font change. */ |
542 | 1.81M | const pdf_char_proc_ownership_t * pcpo = pcp->owner_fonts; |
543 | 1.81M | pdf_font_resource_t *pdfont = pcpo->font; |
544 | 1.81M | byte ch = pcpo->char_code; |
545 | 1.81M | pdf_text_state_values_t values; |
546 | | |
547 | 1.81M | values.character_spacing = 0; |
548 | 1.81M | values.pdfont = pdfont; |
549 | 1.81M | values.size = 1; |
550 | 1.81M | values.matrix = *pimat; |
551 | 1.81M | values.render_mode = pdev->pte->pgs->text_rendering_mode; |
552 | 1.81M | values.word_spacing = 0; |
553 | 1.81M | pdf_set_text_state_values(pdev, &values); |
554 | 1.81M | pdf_bitmap_char_update_bbox(pdev, pcp->x_offset, pcp->y_offset, pcp->real_width.x, pcp->real_width.y); |
555 | 1.81M | pdf_append_chars(pdev, &ch, 1, pdfont->Widths[ch] * pimat->xx, 0.0, false); |
556 | 1.81M | return 0; |
557 | 1.81M | } |
558 | | |
559 | | /* |
560 | | * Write the Encoding for bitmap fonts, if needed. |
561 | | */ |
562 | | int |
563 | | pdf_write_bitmap_fonts_Encoding(gx_device_pdf *pdev) |
564 | 34.0k | { |
565 | 34.0k | pdf_bitmap_fonts_t *pbfs; |
566 | | |
567 | 34.0k | if (pdev->text == NULL || pdev->text->bitmap_fonts == NULL) |
568 | 0 | return 0; |
569 | | |
570 | 34.0k | pbfs = pdev->text->bitmap_fonts; |
571 | | |
572 | 34.0k | if (pbfs->bitmap_encoding_id) { |
573 | 1.80k | stream *s; |
574 | 1.80k | int i; |
575 | | |
576 | 1.80k | pdf_open_separate(pdev, pbfs->bitmap_encoding_id, resourceEncoding); |
577 | 1.80k | s = pdev->strm; |
578 | | /* |
579 | | * Even though the PDF reference documentation says that a |
580 | | * BaseEncoding key is required unless the encoding is |
581 | | * "based on the base font's encoding" (and there is no base |
582 | | * font in this case), Acrobat 2.1 gives an error if the |
583 | | * BaseEncoding key is present. |
584 | | */ |
585 | 1.80k | stream_puts(s, "<</Type/Encoding/Differences[0"); |
586 | 137k | for (i = 0; i <= pbfs->max_embedded_code; ++i) { |
587 | 135k | if (!(i & 15)) |
588 | 9.36k | stream_puts(s, "\n"); |
589 | 135k | pprintd1(s, "/a%d", i); |
590 | 135k | } |
591 | 1.80k | stream_puts(s, "\n] >>\n"); |
592 | 1.80k | pdf_end_separate(pdev, resourceEncoding); |
593 | 1.80k | pbfs->bitmap_encoding_id = 0; |
594 | 1.80k | } |
595 | 34.0k | return 0; |
596 | 34.0k | } |
597 | | |
598 | | /* |
599 | | * Start charproc accumulation for a Type 3 font. |
600 | | */ |
601 | | int |
602 | | pdf_start_charproc_accum(gx_device_pdf *pdev) |
603 | 98.6k | { |
604 | 98.6k | pdf_char_proc_t *pcp; |
605 | 98.6k | pdf_resource_t *pres; |
606 | 98.6k | int id = gs_next_ids(pdev->memory, 1); |
607 | 98.6k | int code = pdf_enter_substream(pdev, resourceCharProc, id, |
608 | 98.6k | &pres, false, pdev->CompressFonts); |
609 | | |
610 | 98.6k | if (code < 0) |
611 | 0 | return code; |
612 | 98.6k | pres->rid = id; |
613 | 98.6k | pcp = (pdf_char_proc_t *)pres; |
614 | 98.6k | pcp->owner_fonts = NULL; |
615 | 98.6k | return 0; |
616 | 98.6k | } |
617 | | |
618 | | /* |
619 | | * Install charproc accumulator for a Type 3 font. |
620 | | */ |
621 | | int |
622 | | pdf_set_charproc_attrs(gx_device_pdf *pdev, gs_font *font, double *pw, int narg, |
623 | | gs_text_cache_control_t control, gs_char ch, bool scale_100) |
624 | 83.2k | { |
625 | 83.2k | pdf_font_resource_t *pdfont; |
626 | 83.2k | pdf_resource_t *pres = pdev->accumulating_substream_resource; |
627 | 83.2k | pdf_char_proc_t *pcp; |
628 | 83.2k | int code; |
629 | | |
630 | 83.2k | if (pres == NULL) |
631 | 0 | return_error(gs_error_undefined); |
632 | 83.2k | code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL); |
633 | 83.2k | if (code < 0) |
634 | 0 | return code; |
635 | 83.2k | pcp = (pdf_char_proc_t *)pres; |
636 | 83.2k | pcp->owner_fonts = NULL; |
637 | 83.2k | pcp->real_width.x = pw[font->WMode && narg > 6 ? 6 : 0]; |
638 | 83.2k | pcp->real_width.y = pw[font->WMode && narg > 6 ? 7 : 1]; |
639 | 83.2k | pcp->v.x = (narg > 8 ? pw[8] : 0); |
640 | 83.2k | pcp->v.y = (narg > 8 ? pw[9] : 0); |
641 | 83.2k | if (control == TEXT_SET_CHAR_WIDTH) { |
642 | | /* PLRM 5.7.1 "BuildGlyph" reads : "Normally, it is unnecessary and |
643 | | undesirable to initialize the current color parameter, because show |
644 | | is defined to paint glyphs with the current color." |
645 | | However comparefiles/Bug687044.ps doesn't follow that. */ |
646 | 74.4k | pdev->skip_colors = false; |
647 | 74.4k | pprintg1(pdev->strm, "%g 0 d0\n", (float)pw[0]); |
648 | | /* The colour change described above can't affect PCL fonts and we need |
649 | | * all glyphs to be noted as cached in order for the bitmap font cache |
650 | | * probing to work properly. |
651 | | */ |
652 | 74.4k | if (font->FontType == ft_PCL_user_defined || font->FontType == ft_GL2_stick_user_defined |
653 | 74.4k | || font->FontType == ft_GL2_531 || font->FontType == ft_MicroType) |
654 | 0 | pdfont->u.simple.s.type3.cached[ch >> 3] |= 0x80 >> (ch & 7); |
655 | 74.4k | } else { |
656 | 8.81k | double d; |
657 | 8.81k | pdev->skip_colors = true; |
658 | 8.81k | if (pw[4] < pw[2]) { |
659 | 17 | d = pw[2]; |
660 | 17 | pw[2] = pw[4]; |
661 | 17 | pw[4] = d; |
662 | 17 | } |
663 | 8.81k | if (pw[5] < pw[3]) { |
664 | 31 | d = pw[5]; |
665 | 31 | pw[5] = pw[3]; |
666 | 31 | pw[3] = d; |
667 | 31 | } |
668 | 8.81k | pprintg6(pdev->strm, "%g %g %g %g %g %g d1\n", |
669 | 8.81k | (float)pw[0], (float)0.0, (float)pw[2], |
670 | 8.81k | (float)pw[3], (float)pw[4], (float)pw[5]); |
671 | 8.81k | pdfont->u.simple.s.type3.cached[ch >> 3] |= 0x80 >> (ch & 7); |
672 | 8.81k | } |
673 | | /* See comments in pdf_text_process regarding type 3 CharProc accumulation |
674 | | * Initially this matrix was emitted there, at the start of the accumulator |
675 | | * but if we do that then GS incorrectly applied the matrix to the 'd1' |
676 | | * operator. We write the scale matrix here because this is *after* the |
677 | | * 'd1' has been emitted above, and so does not affect it. |
678 | | */ |
679 | 83.2k | if (scale_100) { |
680 | 83.2k | code = stream_puts(pdev->strm, "0.01 0 0 0.01 0 0 cm\n"); |
681 | 83.2k | if (code < 0) |
682 | 0 | return code; |
683 | 83.2k | } |
684 | 83.2k | return 0; |
685 | 83.2k | } |
686 | | |
687 | | /* |
688 | | * Open a stream object in the temporary file. |
689 | | */ |
690 | | |
691 | | int |
692 | | pdf_open_aside(gx_device_pdf *pdev, pdf_resource_type_t rtype, |
693 | | gs_id id, pdf_resource_t **ppres, bool reserve_object_id, int options) |
694 | 179k | { |
695 | 179k | int code; |
696 | 179k | pdf_resource_t *pres; |
697 | 179k | stream *s, *save_strm = pdev->strm; |
698 | 179k | pdf_data_writer_t writer; |
699 | 179k | static const pdf_filter_names_t fnames = { |
700 | 179k | PDF_FILTER_NAMES |
701 | 179k | }; |
702 | | |
703 | 179k | pdev->streams.save_strm = pdev->strm; |
704 | | |
705 | 179k | if (rtype >= NUM_RESOURCE_TYPES) |
706 | 42.5k | rtype = resourceOther; |
707 | 179k | code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, id), |
708 | 179k | pdf_resource_type_structs[rtype], &pres, reserve_object_id ? 0 : -1); |
709 | 179k | if (code < 0) |
710 | 0 | return code; |
711 | 179k | cos_become(pres->object, cos_type_stream); |
712 | 179k | s = cos_write_stream_alloc((cos_stream_t *)pres->object, pdev, "pdf_enter_substream"); |
713 | 179k | if (s == 0) |
714 | 0 | return_error(gs_error_VMerror); |
715 | 179k | pdev->strm = s; |
716 | 179k | code = pdf_append_data_stream_filters(pdev, &writer, |
717 | 179k | options | DATA_STREAM_NOLENGTH, pres->object->id); |
718 | 179k | if (code < 0) { |
719 | 0 | pdev->strm = save_strm; |
720 | 0 | return code; |
721 | 0 | } |
722 | 179k | code = pdf_put_filters((cos_dict_t *)pres->object, pdev, writer.binary.strm, &fnames); |
723 | 179k | if (code < 0) { |
724 | 0 | pdev->strm = save_strm; |
725 | 0 | return code; |
726 | 0 | } |
727 | 179k | pdev->strm = writer.binary.strm; |
728 | 179k | *ppres = pres; |
729 | 179k | return 0; |
730 | 179k | } |
731 | | |
732 | | /* |
733 | | * Close a stream object in the temporary file. |
734 | | */ |
735 | | int |
736 | | pdf_close_aside(gx_device_pdf *pdev) |
737 | 179k | { |
738 | | /* We should call pdf_end_data here, but we don't want to put pdf_data_writer_t |
739 | | into pdf_substream_save stack to simplify garbager descriptors. |
740 | | Use a lower level functions instead that. */ |
741 | 179k | stream *s = pdev->strm; |
742 | 179k | cos_stream_t *pcs = cos_stream_from_pipeline(s); |
743 | 179k | int status = s_close_filters(&s, NULL); |
744 | | |
745 | 179k | pdev->strm = pdev->streams.save_strm; |
746 | 179k | if (status < 0) |
747 | 0 | return(gs_note_error(gs_error_ioerror)); |
748 | | |
749 | 179k | if (!pcs) |
750 | 0 | return gs_note_error(gs_error_ioerror); |
751 | | |
752 | 179k | pcs->is_open = false; |
753 | 179k | return 0; |
754 | 179k | } |
755 | | |
756 | | /* |
757 | | * Enter the substream accumulation mode. |
758 | | */ |
759 | | int |
760 | | pdf_enter_substream(gx_device_pdf *pdev, pdf_resource_type_t rtype, |
761 | | gs_id id, pdf_resource_t **ppres, bool reserve_object_id, bool compress) |
762 | 136k | { |
763 | 136k | int sbstack_ptr = pdev->sbstack_depth; |
764 | 136k | pdf_resource_t *pres; |
765 | 136k | stream *save_strm = pdev->strm; |
766 | 136k | int code; |
767 | | |
768 | 136k | if (pdev->sbstack_depth >= pdev->sbstack_size) |
769 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
770 | 136k | if (pdev->sbstack[sbstack_ptr].text_state == 0) { |
771 | 136k | pdev->sbstack[sbstack_ptr].text_state = pdf_text_state_alloc(pdev->pdf_memory); |
772 | 136k | if (pdev->sbstack[sbstack_ptr].text_state == 0) |
773 | 0 | return_error(gs_error_VMerror); |
774 | 136k | } |
775 | 136k | code = pdf_open_aside(pdev, rtype, id, &pres, reserve_object_id, |
776 | 136k | (compress ? DATA_STREAM_COMPRESS : 0)); |
777 | 136k | if (code < 0) |
778 | 0 | return code; |
779 | 136k | code = pdf_save_viewer_state(pdev, NULL); |
780 | 136k | if (code < 0) { |
781 | 0 | pdev->strm = save_strm; |
782 | 0 | return code; |
783 | 0 | } |
784 | 136k | pdev->sbstack[sbstack_ptr].context = pdev->context; |
785 | 136k | pdf_text_state_copy(pdev->sbstack[sbstack_ptr].text_state, pdev->text->text_state); |
786 | 136k | pdf_set_text_state_default(pdev->text->text_state); |
787 | 136k | pdev->sbstack[sbstack_ptr].clip_path = pdev->clip_path; |
788 | 136k | pdev->clip_path = 0; |
789 | 136k | pdev->sbstack[sbstack_ptr].clip_path_id = pdev->clip_path_id; |
790 | 136k | pdev->clip_path_id = pdev->no_clip_path_id; |
791 | 136k | pdev->sbstack[sbstack_ptr].vgstack_bottom = pdev->vgstack_bottom; |
792 | 136k | pdev->vgstack_bottom = pdev->vgstack_depth; |
793 | 136k | pdev->sbstack[sbstack_ptr].strm = save_strm; |
794 | 136k | pdev->sbstack[sbstack_ptr].procsets = pdev->procsets; |
795 | 136k | pdev->sbstack[sbstack_ptr].substream_Resources = pdev->substream_Resources; |
796 | 136k | pdev->sbstack[sbstack_ptr].skip_colors = pdev->skip_colors; |
797 | 136k | pdev->sbstack[sbstack_ptr].font3 = pdev->font3; |
798 | 136k | pdev->sbstack[sbstack_ptr].accumulating_substream_resource = pdev->accumulating_substream_resource; |
799 | 136k | pdev->sbstack[sbstack_ptr].charproc_just_accumulated = pdev->charproc_just_accumulated; |
800 | 136k | pdev->sbstack[sbstack_ptr].accumulating_a_global_object = pdev->accumulating_a_global_object; |
801 | 136k | pdev->sbstack[sbstack_ptr].pres_soft_mask_dict = pdev->pres_soft_mask_dict; |
802 | 136k | pdev->sbstack[sbstack_ptr].objname = pdev->objname; |
803 | 136k | pdev->sbstack[sbstack_ptr].last_charpath_op = pdev->last_charpath_op; |
804 | 136k | pdev->skip_colors = false; |
805 | 136k | pdev->charproc_just_accumulated = false; |
806 | 136k | pdev->pres_soft_mask_dict = NULL; |
807 | 136k | pdev->objname.data = NULL; |
808 | 136k | pdev->objname.size = 0; |
809 | | /* Do not reset pdev->accumulating_a_global_object - it inherits. */ |
810 | 136k | pdev->sbstack_depth++; |
811 | 136k | pdev->procsets = 0; |
812 | 136k | pdev->font3 = 0; |
813 | 136k | pdev->context = PDF_IN_STREAM; |
814 | 136k | pdev->accumulating_substream_resource = pres; |
815 | 136k | pdev->last_charpath_op = 0; |
816 | | /* Do not alter type3charpath, inherit the current value. We need to know if */ |
817 | | /* we are inside a charpath operation, and only reset this when the charpath */ |
818 | | /* is complete */ |
819 | 136k | if (rtype != resourceXObject) |
820 | 135k | pdf_reset_graphics(pdev); |
821 | 1.30k | else { |
822 | 1.30k | if (pdev->vg_initial_set) |
823 | 1.30k | pdev->state.blend_mode = pdev->vg_initial.blend_mode; |
824 | 1.30k | } |
825 | 136k | *ppres = pres; |
826 | 136k | return 0; |
827 | 136k | } |
828 | | |
829 | | /* |
830 | | * Exit the substream accumulation mode. |
831 | | */ |
832 | | int |
833 | | pdf_exit_substream(gx_device_pdf *pdev) |
834 | 136k | { |
835 | 136k | int code, code1; |
836 | 136k | int sbstack_ptr; |
837 | | |
838 | 136k | if (pdev->sbstack_depth <= 0) |
839 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
840 | 136k | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
841 | 136k | sbstack_ptr = pdev->sbstack_depth - 1; |
842 | 138k | while (pdev->vgstack_depth > pdev->vgstack_bottom) { |
843 | 1.82k | code1 = pdf_restore_viewer_state(pdev, pdev->strm); |
844 | 1.82k | if (code >= 0) |
845 | 1.82k | code = code1; |
846 | 1.82k | } |
847 | 136k | if (pdev->clip_path != 0) |
848 | 1.82k | gx_path_free(pdev->clip_path, "pdf_end_charproc_accum"); |
849 | 136k | code1 = pdf_close_aside(pdev); |
850 | 136k | if (code1 < 0 && code >= 0) |
851 | 0 | code = code1; |
852 | 136k | pdev->context = pdev->sbstack[sbstack_ptr].context; |
853 | 136k | pdf_text_state_copy(pdev->text->text_state, pdev->sbstack[sbstack_ptr].text_state); |
854 | 136k | gs_free_object(pdev->pdf_memory, pdev->sbstack[sbstack_ptr].text_state, "free text state for stream"); |
855 | 136k | pdev->sbstack[sbstack_ptr].text_state = 0; |
856 | 136k | pdev->clip_path = pdev->sbstack[sbstack_ptr].clip_path; |
857 | 136k | pdev->sbstack[sbstack_ptr].clip_path = 0; |
858 | 136k | pdev->clip_path_id = pdev->sbstack[sbstack_ptr].clip_path_id; |
859 | 136k | pdev->vgstack_bottom = pdev->sbstack[sbstack_ptr].vgstack_bottom; |
860 | 136k | pdev->strm = pdev->sbstack[sbstack_ptr].strm; |
861 | 136k | pdev->sbstack[sbstack_ptr].strm = 0; |
862 | 136k | pdev->procsets = pdev->sbstack[sbstack_ptr].procsets; |
863 | 136k | pdev->substream_Resources = pdev->sbstack[sbstack_ptr].substream_Resources; |
864 | 136k | pdev->sbstack[sbstack_ptr].substream_Resources = 0; |
865 | 136k | pdev->skip_colors = pdev->sbstack[sbstack_ptr].skip_colors; |
866 | 136k | pdev->font3 = pdev->sbstack[sbstack_ptr].font3; |
867 | 136k | pdev->sbstack[sbstack_ptr].font3 = 0; |
868 | 136k | pdev->accumulating_substream_resource = pdev->sbstack[sbstack_ptr].accumulating_substream_resource; |
869 | 136k | pdev->sbstack[sbstack_ptr].accumulating_substream_resource = 0; |
870 | 136k | pdev->charproc_just_accumulated = pdev->sbstack[sbstack_ptr].charproc_just_accumulated; |
871 | 136k | pdev->accumulating_a_global_object = pdev->sbstack[sbstack_ptr].accumulating_a_global_object; |
872 | 136k | pdev->pres_soft_mask_dict = pdev->sbstack[sbstack_ptr].pres_soft_mask_dict; |
873 | 136k | pdev->objname = pdev->sbstack[sbstack_ptr].objname; |
874 | 136k | pdev->last_charpath_op = pdev->sbstack[sbstack_ptr].last_charpath_op; |
875 | 136k | pdev->sbstack_depth = sbstack_ptr; |
876 | 136k | code1 = pdf_restore_viewer_state(pdev, NULL); |
877 | 136k | if (code1 < 0 && code >= 0) |
878 | 0 | code = code1; |
879 | 136k | return code; |
880 | 136k | } |
881 | | |
882 | | static bool |
883 | | pdf_is_same_charproc_attrs1(gx_device_pdf *pdev, pdf_char_proc_t *pcp0, pdf_char_proc_t *pcp1) |
884 | 271k | { |
885 | 271k | if (pcp0->real_width.x != pcp1->real_width.x) |
886 | 165k | return false; |
887 | 105k | if (pcp0->real_width.y != pcp1->real_width.y) |
888 | 0 | return false; |
889 | 105k | if (pcp0->v.x != pcp1->v.x) |
890 | 0 | return false; |
891 | 105k | if (pcp0->v.y != pcp1->v.y) |
892 | 0 | return false; |
893 | 105k | return true; |
894 | 105k | } |
895 | | |
896 | | typedef struct charproc_compatibility_data_s { |
897 | | const pdf_char_glyph_pairs_t *cgp; |
898 | | pdf_font_resource_t *pdfont; |
899 | | gs_char char_code; |
900 | | gs_glyph glyph; |
901 | | gs_font *font; |
902 | | } charproc_compatibility_data_t; |
903 | | |
904 | | static bool |
905 | | is_char_code_used(pdf_font_resource_t *pdfont, gs_char char_code) |
906 | 155 | { |
907 | 155 | pdf_char_proc_ownership_t *pcpo; |
908 | | |
909 | 1.21k | for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
910 | 1.06k | if (pcpo->char_code == char_code) { |
911 | 2 | return true; |
912 | 2 | } |
913 | 1.06k | } |
914 | 153 | return false; |
915 | 155 | } |
916 | | |
917 | | static int |
918 | | pdf_is_charproc_compatible(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1) |
919 | 155 | { |
920 | 155 | charproc_compatibility_data_t *data = (charproc_compatibility_data_t *)pdev->find_resource_param; |
921 | 155 | pdf_char_proc_t *pcp0 = (pdf_char_proc_t *)pres0; |
922 | 155 | pdf_char_proc_t *pcp1 = (pdf_char_proc_t *)pres1; |
923 | 155 | pdf_font_resource_t *pdfont = data->pdfont; |
924 | 155 | pdf_char_proc_ownership_t *pcpo; |
925 | 155 | pdf_font_cache_elem_t **e; |
926 | 155 | bool can_add_to_current_font = false, computed_can_add_to_current_font = false; |
927 | | |
928 | | /* Does it have same attributes ? */ |
929 | 155 | if (!pdf_is_same_charproc_attrs1(pdev, pcp0, pcp1)) |
930 | 0 | return 0; |
931 | | /* Is it from same font ? */ |
932 | 1.95k | for (pcpo = pcp1->owner_fonts; pcpo != NULL; pcpo = pcpo->char_next) { |
933 | 1.80k | if (pdfont == pcpo->font) { |
934 | | /* Check for encoding conflict. */ |
935 | 0 | if (pcpo->char_code == data->char_code && pcpo->glyph == data->glyph) |
936 | 0 | return 1; /* Same char code. */ |
937 | 0 | if (!computed_can_add_to_current_font) { |
938 | 0 | can_add_to_current_font = !is_char_code_used(pdfont, data->char_code); |
939 | 0 | computed_can_add_to_current_font = true; |
940 | 0 | } |
941 | 0 | if (can_add_to_current_font) |
942 | 0 | return 1; /* No conflict. */ |
943 | 0 | } |
944 | 1.80k | } |
945 | | /* Look for another font with same encoding, |
946 | | because we want to reduce the number of new fonts. |
947 | | We also restrict with ones attached to same PS font, |
948 | | otherwise it creates too mixed fonts and disturbs word breaks. |
949 | | */ |
950 | 155 | e = pdf_locate_font_cache_elem(pdev, data->font); |
951 | 155 | if (e != NULL) { |
952 | 1.95k | for (pcpo = pcp1->owner_fonts; pcpo != NULL; pcpo = pcpo->char_next) { |
953 | 1.80k | if (pcpo->char_code != data->char_code || pcpo->glyph != data->glyph) |
954 | 1.73k | continue; /* Need same Encoding to generate a proper ToUnicode. */ |
955 | 74 | if (pdfont->u.simple.s.type3.bitmap_font != pcpo->font->u.simple.s.type3.bitmap_font) |
956 | 0 | continue; |
957 | 74 | if (gs_matrix_compare(&pdfont->u.simple.s.type3.FontMatrix, &pcpo->font->u.simple.s.type3.FontMatrix)) |
958 | 25 | continue; |
959 | 49 | if (data->cgp != NULL) { |
960 | 47 | if (!pdf_check_encoding_compatibility(pcpo->font, data->cgp->s, data->cgp->num_all_chars)) |
961 | 0 | continue; |
962 | 47 | } |
963 | 49 | if ((*e)->pdfont != pcpo->font) |
964 | 49 | continue; |
965 | 0 | data->pdfont = pcpo->font; /* Switch to the other font. */ |
966 | 0 | return 1; |
967 | 49 | } |
968 | 155 | } |
969 | | /* Check whether it can be added into the current font. */ |
970 | 155 | if (!computed_can_add_to_current_font) |
971 | 155 | can_add_to_current_font = !is_char_code_used(pdfont, data->char_code); |
972 | 155 | if (!can_add_to_current_font) { |
973 | | /* Can't substitute due to encoding conflict. */ |
974 | 2 | return 0; |
975 | 2 | } |
976 | | /* The current font will share it with another font. */ |
977 | 153 | return 1; |
978 | 155 | } |
979 | | |
980 | | static int |
981 | | pdf_find_same_charproc_aux(gx_device_pdf *pdev, |
982 | | pdf_font_resource_t **ppdfont, pdf_char_proc_t **ppcp) |
983 | 98.5k | { |
984 | 98.5k | pdf_char_proc_ownership_t *pcpo; |
985 | 98.5k | int code; |
986 | | |
987 | | /* fixme: this passes parameters to pdf_is_charproc_compatible |
988 | | through special gx_device_pdf field pdev->find_resource_param |
989 | | due to prototype limitation of pdf_find_same_resource. |
990 | | It would be better to change the client data argument type in there to void. */ |
991 | 280k | for (pcpo = (*ppdfont)->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
992 | 271k | pdf_char_proc_t *pcp = pcpo->char_proc; |
993 | | |
994 | 271k | if (*ppcp != pcp && pdf_is_same_charproc_attrs1(pdev, *ppcp, pcp)) { |
995 | 105k | cos_object_t *pco0 = pcp->object; |
996 | 105k | cos_object_t *pco1 = (*ppcp)->object; |
997 | | |
998 | 105k | code = pco0->cos_procs->equal(pco0, pco1, pdev); |
999 | 105k | if (code < 0) { |
1000 | 0 | return code; |
1001 | 0 | } |
1002 | 105k | if (code) { |
1003 | 89.1k | *ppcp = pcp; |
1004 | 89.1k | return 1; |
1005 | 89.1k | } |
1006 | 105k | } |
1007 | 271k | } |
1008 | 9.40k | return pdf_find_same_resource(pdev, resourceCharProc, (pdf_resource_t **)ppcp, pdf_is_charproc_compatible); |
1009 | 98.5k | } |
1010 | | static int |
1011 | | pdf_find_same_charproc(gx_device_pdf *pdev, |
1012 | | pdf_font_resource_t **ppdfont, const pdf_char_glyph_pairs_t *cgp, |
1013 | | pdf_char_proc_t **ppcp, gs_glyph glyph, gs_char char_code, |
1014 | | gs_font *font) |
1015 | 98.5k | { |
1016 | 98.5k | charproc_compatibility_data_t data; |
1017 | 98.5k | int code; |
1018 | | |
1019 | 98.5k | data.cgp = cgp; |
1020 | 98.5k | data.pdfont = *ppdfont; |
1021 | 98.5k | data.char_code = char_code; |
1022 | 98.5k | data.glyph = glyph; |
1023 | 98.5k | data.font = font; |
1024 | 98.5k | pdev->find_resource_param = &data; |
1025 | 98.5k | code = pdf_find_same_charproc_aux(pdev, ppdfont, ppcp); |
1026 | 98.5k | pdev->find_resource_param = NULL; |
1027 | 98.5k | *ppdfont = data.pdfont; |
1028 | 98.5k | return code; |
1029 | 98.5k | } |
1030 | | |
1031 | | static bool |
1032 | | pdf_is_charproc_defined(gx_device_pdf *pdev, pdf_font_resource_t *pdfont, gs_char ch) |
1033 | 9.24k | { |
1034 | 9.24k | pdf_char_proc_ownership_t *pcpo; |
1035 | | |
1036 | 131k | for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
1037 | 122k | if (pcpo->char_code == ch) |
1038 | 20 | return true; |
1039 | 122k | } |
1040 | 9.22k | return false; |
1041 | 9.24k | } |
1042 | | |
1043 | | static int |
1044 | | complete_adding_char(gx_device_pdf *pdev, gs_font *font, |
1045 | | gs_glyph glyph, gs_char ch, pdf_char_proc_t *pcp, |
1046 | | const gs_const_string *gnstr) |
1047 | 98.6k | { |
1048 | 98.6k | pdf_font_resource_t *pdfont; |
1049 | 98.6k | double *real_widths; |
1050 | 98.6k | byte *glyph_usage; |
1051 | 98.6k | int char_cache_size, width_cache_size; |
1052 | 98.6k | pdf_encoding_element_t *pet; |
1053 | 98.6k | int code; |
1054 | | |
1055 | 98.6k | code = pdf_attached_font_resource(pdev, font, &pdfont, |
1056 | 98.6k | &glyph_usage, &real_widths, &char_cache_size, &width_cache_size); |
1057 | 98.6k | if (code < 0) |
1058 | 0 | return code; |
1059 | 98.6k | code = pdf_attach_charproc(pdev, pdfont, pcp, glyph, ch, gnstr); |
1060 | 98.6k | if (code < 0) |
1061 | 0 | return code; |
1062 | 98.6k | if (ch >= char_cache_size || ch >= width_cache_size) |
1063 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1064 | 98.6k | pet = &pdfont->u.simple.Encoding[ch]; |
1065 | 98.6k | pdfont->Widths[ch] = pcp->real_width.x; |
1066 | 98.6k | real_widths[ch * 2 ] = pcp->real_width.x; |
1067 | 98.6k | real_widths[ch * 2 + 1] = pcp->real_width.y; |
1068 | 98.6k | glyph_usage[ch / 8] |= 0x80 >> (ch & 7); |
1069 | 98.6k | pdfont->used[ch >> 3] |= 0x80 >> (ch & 7); |
1070 | 98.6k | if (pdfont->u.simple.v != NULL && font->WMode) { |
1071 | 0 | pdfont->u.simple.v[ch].x = pcp->v.x; |
1072 | 0 | pdfont->u.simple.v[ch].y = pcp->v.x; |
1073 | 0 | } |
1074 | 98.6k | pet->glyph = glyph; |
1075 | 98.6k | pet->is_difference = true; |
1076 | 98.6k | if (pdfont->u.simple.LastChar < (int)ch) |
1077 | 3.25k | pdfont->u.simple.LastChar = (int)ch; |
1078 | 98.6k | if (pdfont->u.simple.FirstChar > (int)ch) |
1079 | 2.06k | pdfont->u.simple.FirstChar = (int)ch; |
1080 | 98.6k | return pdf_copy_string_to_encoding(pdev, (gs_const_string *)gnstr, pet); |
1081 | 98.6k | } |
1082 | | |
1083 | | static int |
1084 | | pdf_char_widths_from_charprocs(gx_device_pdf *pdev, gs_font *font) |
1085 | 0 | { |
1086 | 0 | pdf_font_resource_t *pdfont; |
1087 | 0 | double *real_widths; |
1088 | 0 | byte *glyph_usage; |
1089 | 0 | int char_cache_size, width_cache_size; |
1090 | 0 | pdf_char_proc_ownership_t *pcpo; |
1091 | 0 | int code; |
1092 | |
|
1093 | 0 | code = pdf_attached_font_resource(pdev, font, &pdfont, |
1094 | 0 | &glyph_usage, &real_widths, &char_cache_size, &width_cache_size); |
1095 | 0 | if (code < 0) |
1096 | 0 | return code; |
1097 | 0 | for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) { |
1098 | 0 | pdf_char_proc_t *pcp = pcpo->char_proc; |
1099 | 0 | gs_char ch = pcpo->char_code; |
1100 | |
|
1101 | 0 | real_widths[ch * 2 ] = pcp->real_width.x; |
1102 | 0 | real_widths[ch * 2 + 1] = pcp->real_width.y; |
1103 | 0 | glyph_usage[ch / 8] |= 0x80 >> (ch & 7); |
1104 | 0 | } |
1105 | 0 | return 0; |
1106 | 0 | } |
1107 | | |
1108 | | /* |
1109 | | * Complete charproc accumulation for a Type 3 font. |
1110 | | */ |
1111 | | int |
1112 | | pdf_end_charproc_accum(gx_device_pdf *pdev, gs_font *font, const pdf_char_glyph_pairs_t *cgp, |
1113 | | gs_glyph glyph, gs_char output_char_code, const gs_const_string *gnstr) |
1114 | 98.6k | { |
1115 | 98.6k | int code; |
1116 | 98.6k | pdf_resource_t *pres = (pdf_resource_t *)pdev->accumulating_substream_resource; |
1117 | | /* We could use pdfont->u.simple.s.type3.char_procs insted the thing above |
1118 | | unless the font is defined recursively. |
1119 | | But we don't want such assumption. */ |
1120 | 98.6k | pdf_char_proc_t *pcp = (pdf_char_proc_t *)pres; |
1121 | 98.6k | pdf_font_resource_t *pdfont; |
1122 | 98.6k | gs_char ch = output_char_code; |
1123 | 98.6k | bool checking_glyph_variation = false; |
1124 | | |
1125 | 98.6k | if (ch == GS_NO_CHAR) |
1126 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1127 | 98.6k | if (ch >= 256) |
1128 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1129 | 98.6k | if (pres == NULL) |
1130 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1131 | 98.6k | code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL); |
1132 | 98.6k | if (code < 0) |
1133 | 0 | return code; |
1134 | 98.6k | if (pdfont != (pdf_font_resource_t *)pdev->font3) |
1135 | 11 | return_error(gs_error_unregistered); /* Must not happen. */ |
1136 | 98.6k | code = pdf_exit_substream(pdev); |
1137 | 98.6k | if (code < 0) |
1138 | 0 | return code; |
1139 | 98.6k | if (!(pdfont->used[ch >> 3] & (0x80 >> (ch & 7))) || |
1140 | 98.6k | !(pdfont->u.simple.s.type3.cached[ch >> 3] & (0x80 >> (ch & 7)))) { |
1141 | | /* First appearence or not cached - check for duplicates. */ |
1142 | 98.5k | pdf_font_resource_t *pdfont1 = pdfont; |
1143 | | |
1144 | 98.5k | checking_glyph_variation = true; |
1145 | | /* CAUTION : a possible font change. */ |
1146 | 98.5k | code = pdf_find_same_charproc(pdev, &pdfont, cgp, &pcp, glyph, ch, font); |
1147 | 98.5k | if (code < 0) |
1148 | 0 | return code; |
1149 | 98.5k | if (code != 0) { |
1150 | 89.3k | code = pdf_cancel_resource(pdev, pres, resourceCharProc); |
1151 | 89.3k | if (code < 0) |
1152 | 0 | return code; |
1153 | 89.3k | pdf_forget_resource(pdev, pres, resourceCharProc); |
1154 | 89.3k | if (pdfont1 != pdfont) { |
1155 | 0 | code = pdf_attach_font_resource(pdev, font, pdfont); |
1156 | 0 | if (code < 0) |
1157 | 0 | return code; |
1158 | 0 | code = pdf_char_widths_from_charprocs(pdev, font); |
1159 | 0 | if (code < 0) |
1160 | 0 | return code; |
1161 | 0 | } |
1162 | 89.3k | pdev->charproc_just_accumulated = true; |
1163 | 89.3k | return complete_adding_char(pdev, font, glyph, ch, pcp, gnstr); |
1164 | 89.3k | } |
1165 | 9.24k | if (pdf_is_charproc_defined(pdev, pdfont, ch)) { |
1166 | | /* Encoding conflict after a font change. */ |
1167 | 20 | gs_font *base_font = font, *below; |
1168 | | |
1169 | 20 | while ((below = base_font->base) != base_font && |
1170 | 20 | base_font->procs.same_font(base_font, below, FONT_SAME_OUTLINES)) |
1171 | 0 | base_font = below; |
1172 | 20 | code = pdf_make_font3_resource(pdev, base_font, &pdfont); |
1173 | 20 | if (code < 0) |
1174 | 0 | return code; |
1175 | 20 | code = pdf_attach_font_resource(pdev, font, pdfont); |
1176 | 20 | if (code < 0) |
1177 | 0 | return code; |
1178 | 20 | } |
1179 | 9.24k | } |
1180 | 9.25k | pdf_reserve_object_id(pdev, pres, 0); |
1181 | 9.25k | if (checking_glyph_variation) |
1182 | 9.24k | pdev->charproc_just_accumulated = true; |
1183 | 9.25k | return complete_adding_char(pdev, font, glyph, ch, pcp, gnstr); |
1184 | 98.6k | } |
1185 | | |
1186 | | /* Add procsets to substream Resources. */ |
1187 | | int |
1188 | | pdf_add_procsets(cos_dict_t *pcd, pdf_procset_t procsets) |
1189 | 3.83k | { |
1190 | 3.83k | char str[5 + 7 + 7 + 7 + 5 + 2]; |
1191 | 3.83k | cos_value_t v; |
1192 | | |
1193 | 3.83k | strcpy(str, "[/PDF"); |
1194 | 3.83k | if (procsets & ImageB) |
1195 | 0 | strcat(str, "/ImageB"); |
1196 | 3.83k | if (procsets & ImageC) |
1197 | 2.05k | strcat(str, "/ImageC"); |
1198 | 3.83k | if (procsets & ImageI) |
1199 | 5 | strcat(str, "/ImageI"); |
1200 | 3.83k | if (procsets & Text) |
1201 | 18 | strcat(str, "/Text"); |
1202 | 3.83k | strcat(str, "]"); |
1203 | 3.83k | cos_string_value(&v, (byte *)str, strlen(str)); |
1204 | 3.83k | return cos_dict_put_c_key(pcd, "/ProcSet", &v); |
1205 | 3.83k | } |
1206 | | |
1207 | | /* Add a resource to substream Resources. */ |
1208 | | int |
1209 | | pdf_add_resource(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key, pdf_resource_t *pres) |
1210 | 1.57M | { |
1211 | 1.57M | if (pcd != 0) { |
1212 | 39.1k | const cos_value_t *v = cos_dict_find(pcd, (const byte *)key, strlen(key)); |
1213 | 39.1k | cos_dict_t *list; |
1214 | 39.1k | int code; |
1215 | 39.1k | char buf[10 + (sizeof(long) * 8 / 3 + 1)], buf1[sizeof(pres->rname) + 1]; |
1216 | | |
1217 | 39.1k | if (pdev->ForOPDFRead && !pres->global && pdev->accumulating_a_global_object) { |
1218 | 0 | pres->global = true; |
1219 | 0 | code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true); |
1220 | 0 | if (code < 0) |
1221 | 0 | return code; |
1222 | 0 | } |
1223 | 39.1k | gs_snprintf(buf, sizeof(buf), "%"PRId64" 0 R\n", pres->object->id); |
1224 | 39.1k | if (v != NULL) { |
1225 | 32.0k | if (v->value_type != COS_VALUE_OBJECT && |
1226 | 32.0k | v->value_type != COS_VALUE_RESOURCE) |
1227 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1228 | 32.0k | list = (cos_dict_t *)v->contents.object; |
1229 | 32.0k | if (list->cos_procs != &cos_dict_procs) |
1230 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1231 | 32.0k | } else { |
1232 | 7.10k | list = cos_dict_alloc(pdev, "pdf_add_resource"); |
1233 | 7.10k | if (list == NULL) |
1234 | 0 | return_error(gs_error_VMerror); |
1235 | 7.10k | code = cos_dict_put_c_key_object((cos_dict_t *)pcd, key, (cos_object_t *)list); |
1236 | 7.10k | if (code < 0) |
1237 | 0 | return code; |
1238 | 7.10k | } |
1239 | 39.1k | buf1[0] = '/'; |
1240 | 39.1k | strcpy(buf1 + 1, pres->rname); |
1241 | 39.1k | return cos_dict_put_string(list, (const byte *)buf1, strlen(buf1), |
1242 | 39.1k | (const byte *)buf, strlen(buf)); |
1243 | 39.1k | } |
1244 | 1.53M | return 0; |
1245 | 1.57M | } |