1
2 /* Copyright (C) 2001-2006 Artifex Software, Inc.
3 All Rights Reserved.
4
5 This software is provided AS-IS with no warranty, either express or
6 implied.
7
8 This software is distributed under license and may not be copied, modified
9 or distributed except as expressly authorized under the terms of that
10 license. Refer to licensing information at http://www.artifex.com/
11 or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
12 San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
13 */
14
15 /* $Id: gdevpdti.c 9811 2009-06-22 07:41:34Z ken $ */
16 /* Bitmap font implementation for pdfwrite */
17 #include "memory_.h"
18 #include "gx.h"
19 #include "gxpath.h"
20 #include "gserrors.h"
21 #include "gsutil.h"
22 #include "gdevpdfx.h"
23 #include "gdevpdfg.h"
24 #include "gdevpdtf.h"
25 #include "gdevpdti.h"
26 #include "gdevpdts.h"
27 #include "gdevpdtw.h"
28 #include "gdevpdtt.h"
29 #include "gdevpdfo.h"
30
31 /* ---------------- Private ---------------- */
32
33 /* Define the structure for a CharProc pseudo-resource. */
34 /*typedef struct pdf_char_proc_s pdf_char_proc_t;*/ /* gdevpdfx.h */
35 struct pdf_char_proc_s {
36 pdf_resource_common(pdf_char_proc_t);
37 pdf_char_proc_ownership_t *owner_fonts; /* fonts using this charproc. */
38 int y_offset; /* of character (0,0) */
39 int x_offset; /* of character (0,0) */
40 gs_point real_width; /* Not used with synthesised bitmap fonts. */
41 gs_point v; /* Not used with synthesised bitmap fonts. */
42 };
43
44 /* The descriptor is public for pdf_resource_type_structs. */
45 gs_public_st_suffix_add1(st_pdf_char_proc, pdf_char_proc_t,
46 "pdf_char_proc_t", pdf_char_proc_enum_ptrs, pdf_char_proc_reloc_ptrs,
47 st_pdf_resource, owner_fonts);
48
49 struct pdf_char_proc_ownership_s {
50 pdf_char_proc_t *char_proc;
51 pdf_char_proc_ownership_t *font_next; /* next char_proc for same font */
52 pdf_char_proc_ownership_t *char_next; /* next char_proc for same charproc */
53 pdf_font_resource_t *font;
54 gs_char char_code; /* Character code in PDF font. */
55 gs_glyph glyph; /* Glyph id in Postscript font. */
56 gs_const_string char_name;
57 bool duplicate_char_name;
58 };
59 gs_private_st_strings1_ptrs4(st_pdf_char_proc_ownership, pdf_char_proc_ownership_t,
60 "pdf_char_proc_ownership_t", pdf_char_proc_ownership_enum_ptrs,
61 pdf_char_proc_ownership_reloc_ptrs, char_name, char_proc, char_next, font_next, font);
62
63 gs_private_st_ptrs1(st_pdf_bitmap_fonts, pdf_bitmap_fonts_t,
64 "pdf_bitmap_fonts_t", pdf_bitmap_fonts_enum_ptrs,
65 pdf_bitmap_fonts_reloc_ptrs, open_font);
66
67 static inline long
pdf_char_proc_id(const pdf_char_proc_t * pcp)68 pdf_char_proc_id(const pdf_char_proc_t *pcp)
69 {
70 return pdf_resource_id((const pdf_resource_t *)pcp);
71 }
72
73 /* Assign a code for a char_proc. */
74 static int
assign_char_code(gx_device_pdf * pdev,gs_text_enum_t * pte)75 assign_char_code(gx_device_pdf * pdev, gs_text_enum_t *pte)
76 {
77 pdf_bitmap_fonts_t *pbfs = pdev->text->bitmap_fonts;
78 pdf_font_resource_t *pdfont = pbfs->open_font; /* Type 3 */
79 int i, c = 0, code;
80 uint operation = pte->text.operation;
81
82 if (pbfs->bitmap_encoding_id == 0)
83 pbfs->bitmap_encoding_id = pdf_obj_ref(pdev);
84 if (pdfont == 0 || pdfont->u.simple.LastChar == 255 ||
85 !pbfs->use_open_font
86 ) {
87 /* Start a new synthesized font. */
88 char *pc;
89
90 code = pdf_font_type3_alloc(pdev, &pdfont, pdf_write_contents_bitmap);
91 if (code < 0)
92 return code;
93 pdfont->u.simple.s.type3.bitmap_font = true;
94 if (pbfs->open_font == 0)
95 pdfont->rname[0] = 0;
96 else
97 strcpy(pdfont->rname, pbfs->open_font->rname);
98 pdfont->u.simple.s.type3.FontBBox.p.x = 0;
99 pdfont->u.simple.s.type3.FontBBox.p.y = 0;
100 pdfont->u.simple.s.type3.FontBBox.q.x = 0;
101 pdfont->u.simple.s.type3.FontBBox.q.y = 0;
102 pdfont->mark_glyph = NULL;
103 gs_make_identity(&pdfont->u.simple.s.type3.FontMatrix);
104 /*
105 * We "increment" the font name as a radix-26 "number".
106 * This cannot possibly overflow.
107 */
108 for (pc = pdfont->rname; *pc == 'Z'; ++pc)
109 *pc = '@';
110 if ((*pc)++ == 0)
111 *pc = 'A', pc[1] = 0;
112 pbfs->open_font = pdfont;
113 pbfs->use_open_font = true;
114 pdfont->u.simple.FirstChar = 255;
115 }
116 if ((operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) ||
117 (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR))) {
118 unsigned char p = *pte->text.data.bytes;
119 unsigned char index = p / 8, bit = 0x01 << (p % 8);
120
121 if (pdfont->used[index] & bit) {
122 for (i = 0;i < 256;i++) {
123 index = i / 8;
124 bit = 0x01 << (i % 8);
125 if (!(pdfont->used[index] & bit)) {
126 c = i;
127 break;
128 }
129 }
130 } else
131 c = p;
132 pdfont->used[index] |= bit;
133 if (c > pdfont->u.simple.LastChar)
134 pdfont->u.simple.LastChar = c;
135
136 } else {
137 unsigned char index, bit;
138 c = ++(pdfont->u.simple.LastChar);
139 index = c / 8;
140 bit = 0x01 << (c % 8);
141 pdfont->used[index] |= bit;
142 }
143 if (c < pdfont->u.simple.FirstChar)
144 pdfont->u.simple.FirstChar = c;
145
146 pdfont->Widths[c] = psdf_round(pdev->char_width.x, 100, 10); /* See
147 pdf_write_Widths about rounding. We need to provide
148 a compatible data for Tj. */
149 if (c > pbfs->max_embedded_code)
150 pbfs->max_embedded_code = c;
151
152 return c;
153 }
154
155 /* Write the contents of a Type 3 bitmap or vector font resource. */
156 int
pdf_write_contents_bitmap(gx_device_pdf * pdev,pdf_font_resource_t * pdfont)157 pdf_write_contents_bitmap(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
158 {
159 stream *s = pdev->strm;
160 const pdf_char_proc_ownership_t *pcpo;
161 long diff_id = 0;
162 int code;
163
164 if (pdfont->u.simple.s.type3.bitmap_font)
165 diff_id = pdev->text->bitmap_fonts->bitmap_encoding_id;
166 else {
167 /* See comment in pdf_write_encoding. */
168 diff_id = pdf_obj_ref(pdev);
169 }
170 code = pdf_write_encoding_ref(pdev, pdfont, diff_id);
171 if (code < 0)
172 return code;
173 stream_puts(s, "/CharProcs <<");
174 /* Write real characters. */
175 for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo;
176 pcpo = pcpo->char_next
177 ) {
178 if (pdfont->u.simple.s.type3.bitmap_font)
179 pprintld2(s, "/a%ld %ld 0 R\n", (long)pcpo->char_code,
180 pdf_char_proc_id(pcpo->char_proc));
181 else if (!pcpo-> duplicate_char_name) {
182 pdf_put_name(pdev, pcpo->char_name.data, pcpo->char_name.size);
183 pprintld1(s, " %ld 0 R\n", pdf_char_proc_id(pcpo->char_proc));
184 }
185 }
186 stream_puts(s, ">>");
187 pprintg6(s, "/FontMatrix[%g %g %g %g %g %g]",
188 (float)pdfont->u.simple.s.type3.FontMatrix.xx,
189 (float)pdfont->u.simple.s.type3.FontMatrix.xy,
190 (float)pdfont->u.simple.s.type3.FontMatrix.yx,
191 (float)pdfont->u.simple.s.type3.FontMatrix.yy,
192 (float)pdfont->u.simple.s.type3.FontMatrix.tx,
193 (float)pdfont->u.simple.s.type3.FontMatrix.ty);
194 code = pdf_finish_write_contents_type3(pdev, pdfont);
195 if (code < 0)
196 return code;
197 s = pdev->strm; /* pdf_finish_write_contents_type3 changes pdev->strm . */
198 if (!pdfont->u.simple.s.type3.bitmap_font && diff_id > 0) {
199 code = pdf_write_encoding(pdev, pdfont, diff_id, 0);
200 if (code < 0)
201 return code;
202 }
203 return 0;
204 }
205
206 /* ---------------- Public ---------------- */
207
208 /*
209 * Allocate and initialize bookkeeping for bitmap fonts.
210 */
211 pdf_bitmap_fonts_t *
pdf_bitmap_fonts_alloc(gs_memory_t * mem)212 pdf_bitmap_fonts_alloc(gs_memory_t *mem)
213 {
214 pdf_bitmap_fonts_t *pbfs =
215 gs_alloc_struct(mem, pdf_bitmap_fonts_t, &st_pdf_bitmap_fonts,
216 "pdf_bitmap_fonts_alloc");
217
218 if (pbfs == 0)
219 return 0;
220 memset(pbfs, 0, sizeof(*pbfs));
221 pbfs->max_embedded_code = -1;
222 return pbfs;
223 }
224
225 /*
226 * Update text state at the end of a page.
227 */
228 void
pdf_close_text_page(gx_device_pdf * pdev)229 pdf_close_text_page(gx_device_pdf *pdev)
230 {
231 /*
232 * When Acrobat Reader 3 prints a file containing a Type 3 font with a
233 * non-standard Encoding, it apparently only emits the subset of the
234 * font actually used on the page. Thus, if the "Download Fonts Once"
235 * option is selected, characters not used on the page where the font
236 * first appears will not be defined, and hence will print as blank if
237 * used on subsequent pages. Thus, we can't allow a Type 3 font to
238 * add additional characters on subsequent pages.
239 */
240 if (pdev->CompatibilityLevel <= 1.2)
241 pdev->text->bitmap_fonts->use_open_font = false;
242 }
243
244 int
pdf_charproc_y_offset(pdf_char_proc_t * pcp)245 pdf_charproc_y_offset(pdf_char_proc_t *pcp)
246 {
247 return pcp->y_offset;
248 }
249
250 int
pdf_charproc_x_offset(pdf_char_proc_t * pcp)251 pdf_charproc_x_offset(pdf_char_proc_t *pcp)
252 {
253 return pcp->x_offset;
254 }
255
256 /* Attach a CharProc to a font. */
257 static int
pdf_attach_charproc(gx_device_pdf * pdev,pdf_font_resource_t * pdfont,pdf_char_proc_t * pcp,gs_glyph glyph,gs_char char_code,const gs_const_string * gnstr)258 pdf_attach_charproc(gx_device_pdf * pdev, pdf_font_resource_t *pdfont, pdf_char_proc_t *pcp,
259 gs_glyph glyph, gs_char char_code, const gs_const_string *gnstr)
260 {
261 pdf_char_proc_ownership_t *pcpo;
262 bool duplicate_char_name = false;
263
264 for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
265 if (pcpo->glyph == glyph && pcpo->char_code == char_code)
266 return 0;
267 }
268 if (!pdfont->u.simple.s.type3.bitmap_font) {
269 for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
270 if (!bytes_compare(pcpo->char_name.data, pcpo->char_name.size, gnstr->data, gnstr->size)) {
271 duplicate_char_name = true;
272 break;
273 }
274 }
275 }
276 pcpo = gs_alloc_struct(pdev->pdf_memory,
277 pdf_char_proc_ownership_t, &st_pdf_char_proc_ownership, "pdf_attach_charproc");
278
279 if (pcpo == NULL)
280 return_error(gs_error_VMerror);
281 pcpo->font = pdfont;
282 pcpo->char_next = pdfont->u.simple.s.type3.char_procs;
283 pdfont->u.simple.s.type3.char_procs = pcpo;
284 pcpo->char_proc = pcp;
285 pcpo->font_next = pcp->owner_fonts;
286 pcp->owner_fonts = pcpo;
287 pcpo->char_code = char_code;
288 pcpo->glyph = glyph;
289 if (gnstr == NULL) {
290 pcpo->char_name.data = 0;
291 pcpo->char_name.size = 0;
292 } else
293 pcpo->char_name = *gnstr;
294 pcpo->duplicate_char_name = duplicate_char_name;
295 return 0;
296 }
297
298 /* Begin a CharProc for a synthesized (bitmap) font. */
299 int
pdf_begin_char_proc(gx_device_pdf * pdev,int w,int h,int x_width,int y_offset,int x_offset,gs_id id,pdf_char_proc_t ** ppcp,pdf_stream_position_t * ppos)300 pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width,
301 int y_offset, int x_offset, gs_id id, pdf_char_proc_t ** ppcp,
302 pdf_stream_position_t * ppos)
303 {
304 int char_code = 0;
305 pdf_bitmap_fonts_t *const pbfs = pdev->text->bitmap_fonts;
306 pdf_font_resource_t *font;
307 pdf_resource_t *pres;
308 pdf_char_proc_t *pcp;
309 int code;
310
311 char_code = assign_char_code(pdev, pdev->pte);
312 font = pbfs->open_font; /* Type 3 */
313 code = pdf_begin_resource(pdev, resourceCharProc, id, &pres);
314 if (code < 0)
315 return code;
316 pcp = (pdf_char_proc_t *) pres;
317 code = pdf_attach_charproc(pdev, font, pcp, GS_NO_GLYPH, char_code, NULL);
318 if (code < 0)
319 return code;
320 pres->object->written = true;
321 {
322 stream *s = pdev->strm;
323
324 /*
325 * The resource file is positionable, so rather than use an
326 * object reference for the length, we'll go back and fill it in
327 * at the end of the definition. Take 1M as the longest
328 * definition we can handle. (This used to be 10K, but there was
329 * a real file that exceeded this limit.)
330 */
331 stream_puts(s, "<</Length >>stream\n");
332 ppos->start_pos = stell(s);
333 }
334 code = pdf_begin_encrypt(pdev, &pdev->strm, pres->object->id);
335 if (code < 0)
336 return code;
337 pcp->y_offset = y_offset;
338 pcp->x_offset = x_offset;
339 font->u.simple.s.type3.FontBBox.q.x =
340 max(font->u.simple.s.type3.FontBBox.q.x, w);
341 font->u.simple.s.type3.FontBBox.q.y =
342 max(font->u.simple.s.type3.FontBBox.q.y, y_offset + h);
343 font->u.simple.s.type3.max_y_offset =
344 max(font->u.simple.s.type3.max_y_offset, h + (h >> 2));
345 *ppcp = pcp;
346 return 0;
347 }
348
349 /* End a CharProc. */
350 int
pdf_end_char_proc(gx_device_pdf * pdev,pdf_stream_position_t * ppos)351 pdf_end_char_proc(gx_device_pdf * pdev, pdf_stream_position_t * ppos)
352 {
353 stream *s;
354 long start_pos, end_pos, length;
355
356 pdf_end_encrypt(pdev);
357 s = pdev->strm;
358 start_pos = ppos->start_pos;
359 end_pos = stell(s);
360 length = end_pos - start_pos;
361 if (length > 999999)
362 return_error(gs_error_limitcheck);
363 sseek(s, start_pos - 15);
364 pprintd1(s, "%d", length);
365 sseek(s, end_pos);
366 if (pdev->PDFA)
367 stream_puts(s, "\n");
368 stream_puts(s, "endstream\n");
369 pdf_end_separate(pdev);
370 return 0;
371 }
372
373 /* Mark glyph names for garbager. */
374 void
pdf_mark_glyph_names(const pdf_font_resource_t * pdfont,const gs_memory_t * memory)375 pdf_mark_glyph_names(const pdf_font_resource_t *pdfont, const gs_memory_t *memory)
376 {
377 if (pdfont->mark_glyph == NULL) {
378 /* Synthesised bitmap fonts pass here. */
379 return;
380 }
381 if (pdfont->u.simple.Encoding != NULL) {
382 int i;
383
384 for (i = 0; i < 256; i++)
385 if (pdfont->u.simple.Encoding[i].glyph != GS_NO_GLYPH)
386 pdfont->mark_glyph(memory, pdfont->u.simple.Encoding[i].glyph, pdfont->mark_glyph_data);
387 }
388 if (pdfont->FontType == ft_user_defined) {
389 const pdf_char_proc_ownership_t *pcpo = pdfont->u.simple.s.type3.char_procs;
390
391 for (; pcpo != NULL; pcpo = pcpo->font_next)
392 pdfont->mark_glyph(memory, pcpo->glyph, pdfont->mark_glyph_data);
393 }
394 }
395
396 /* Put out a reference to an image as a character in a synthesized font. */
397 int
pdf_do_char_image(gx_device_pdf * pdev,const pdf_char_proc_t * pcp,const gs_matrix * pimat)398 pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp,
399 const gs_matrix * pimat)
400 {
401 /* We need to choose a font, which use the charproc.
402 In most cases it is the last font, which the charproc is attached to.
403 If the charproc is substituted, it causes a font change. */
404 const pdf_char_proc_ownership_t * pcpo = pcp->owner_fonts;
405 pdf_font_resource_t *pdfont = pcpo->font;
406 byte ch = pcpo->char_code;
407 pdf_text_state_values_t values;
408
409 values.character_spacing = 0;
410 values.pdfont = pdfont;
411 values.size = 1;
412 values.matrix = *pimat;
413 values.render_mode = 0;
414 values.word_spacing = 0;
415 pdf_set_text_state_values(pdev, &values);
416 pdf_append_chars(pdev, &ch, 1, pdfont->Widths[ch] * pimat->xx, 0.0, false);
417 return 0;
418 }
419
420 /*
421 * Write the Encoding for bitmap fonts, if needed.
422 */
423 int
pdf_write_bitmap_fonts_Encoding(gx_device_pdf * pdev)424 pdf_write_bitmap_fonts_Encoding(gx_device_pdf *pdev)
425 {
426 pdf_bitmap_fonts_t *pbfs = pdev->text->bitmap_fonts;
427
428 if (pbfs->bitmap_encoding_id) {
429 stream *s;
430 int i;
431
432 pdf_open_separate(pdev, pbfs->bitmap_encoding_id);
433 s = pdev->strm;
434 /*
435 * Even though the PDF reference documentation says that a
436 * BaseEncoding key is required unless the encoding is
437 * "based on the base font's encoding" (and there is no base
438 * font in this case), Acrobat 2.1 gives an error if the
439 * BaseEncoding key is present.
440 */
441 stream_puts(s, "<</Type/Encoding/Differences[0");
442 for (i = 0; i <= pbfs->max_embedded_code; ++i) {
443 if (!(i & 15))
444 stream_puts(s, "\n");
445 pprintd1(s, "/a%d", i);
446 }
447 stream_puts(s, "\n] >>\n");
448 pdf_end_separate(pdev);
449 pbfs->bitmap_encoding_id = 0;
450 }
451 return 0;
452 }
453
454 /*
455 * Start charproc accumulation for a Type 3 font.
456 */
457 int
pdf_start_charproc_accum(gx_device_pdf * pdev)458 pdf_start_charproc_accum(gx_device_pdf *pdev)
459 {
460 pdf_char_proc_t *pcp;
461 pdf_resource_t *pres;
462 int code = pdf_enter_substream(pdev, resourceCharProc, gs_next_ids(pdev->memory, 1),
463 &pres, false, pdev->CompressFonts);
464
465 if (code < 0)
466 return code;
467 pcp = (pdf_char_proc_t *)pres;
468 pcp->owner_fonts = NULL;
469 return 0;
470 }
471
472 /*
473 * Install charproc accumulator for a Type 3 font.
474 */
475 int
pdf_set_charproc_attrs(gx_device_pdf * pdev,gs_font * font,const double * pw,int narg,gs_text_cache_control_t control,gs_char ch)476 pdf_set_charproc_attrs(gx_device_pdf *pdev, gs_font *font, const double *pw, int narg,
477 gs_text_cache_control_t control, gs_char ch)
478 {
479 pdf_font_resource_t *pdfont;
480 pdf_resource_t *pres = pdev->accumulating_substream_resource;
481 pdf_char_proc_t *pcp;
482 int code;
483
484 code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
485 if (code < 0)
486 return code;
487 pcp = (pdf_char_proc_t *)pres;
488 pcp->owner_fonts = NULL;
489 pcp->real_width.x = pw[font->WMode && narg > 6 ? 6 : 0];
490 pcp->real_width.y = pw[font->WMode && narg > 6 ? 7 : 1];
491 pcp->v.x = (narg > 8 ? pw[8] : 0);
492 pcp->v.y = (narg > 8 ? pw[9] : 0);
493 if (control == TEXT_SET_CHAR_WIDTH) {
494 /* PLRM 5.7.1 "BuildGlyph" reads : "Normally, it is unnecessary and
495 undesirable to initialize the current color parameter, because show
496 is defined to paint glyphs with the current color."
497 However comparefiles/Bug687044.ps doesn't follow that. */
498 pdev->skip_colors = false;
499 pprintg1(pdev->strm, "%g 0 d0\n", (float)pw[0]);
500 } else {
501 pdev->skip_colors = true;
502 pprintg6(pdev->strm, "%g %g %g %g %g %g d1\n",
503 (float)pw[0], (float)0.0, (float)pw[2],
504 (float)pw[3], (float)pw[4], (float)pw[5]);
505 pdfont->u.simple.s.type3.cached[ch >> 3] |= 0x80 >> (ch & 7);
506 }
507 return 0;
508 }
509
510 /*
511 * Open a stream object in the temporary file.
512 */
513
514 int
pdf_open_aside(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id id,pdf_resource_t ** ppres,bool reserve_object_id,int options)515 pdf_open_aside(gx_device_pdf *pdev, pdf_resource_type_t rtype,
516 gs_id id, pdf_resource_t **ppres, bool reserve_object_id, int options)
517 {
518 int code;
519 pdf_resource_t *pres;
520 stream *s, *save_strm = pdev->strm;
521 pdf_data_writer_t writer;
522 static const pdf_filter_names_t fnames = {
523 PDF_FILTER_NAMES
524 };
525
526 pdev->streams.save_strm = pdev->strm;
527 code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, id),
528 pdf_resource_type_structs[rtype], &pres, reserve_object_id ? 0 : -1);
529 if (code < 0)
530 return code;
531 cos_become(pres->object, cos_type_stream);
532 s = cos_write_stream_alloc((cos_stream_t *)pres->object, pdev, "pdf_enter_substream");
533 if (s == 0)
534 return_error(gs_error_VMerror);
535 pdev->strm = s;
536 code = pdf_append_data_stream_filters(pdev, &writer,
537 options | DATA_STREAM_NOLENGTH, pres->object->id);
538 if (code < 0) {
539 pdev->strm = save_strm;
540 return code;
541 }
542 code = pdf_put_filters((cos_dict_t *)pres->object, pdev, writer.binary.strm, &fnames);
543 if (code < 0) {
544 pdev->strm = save_strm;
545 return code;
546 }
547 pdev->strm = writer.binary.strm;
548 *ppres = pres;
549 return 0;
550 }
551
552 /*
553 * Close a stream object in the temporary file.
554 */
555 int
pdf_close_aside(gx_device_pdf * pdev)556 pdf_close_aside(gx_device_pdf *pdev)
557 {
558 /* We should call pdf_end_data here, but we don't want to put pdf_data_writer_t
559 into pdf_substream_save stack to simplify garbager descriptors.
560 Use a lower level functions instead that. */
561 stream *s = pdev->strm;
562 int status = s_close_filters(&s, cos_write_stream_from_pipeline(s));
563 cos_stream_t *pcs = cos_stream_from_pipeline(s);
564 int code = 0;
565
566 if (status < 0)
567 code = gs_note_error(gs_error_ioerror);
568 pcs->is_open = false;
569 sclose(s);
570 pdev->strm = pdev->streams.save_strm;
571 return code;
572 }
573
574 /*
575 * Enter the substream accumulation mode.
576 */
577 int
pdf_enter_substream(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id id,pdf_resource_t ** ppres,bool reserve_object_id,bool compress)578 pdf_enter_substream(gx_device_pdf *pdev, pdf_resource_type_t rtype,
579 gs_id id, pdf_resource_t **ppres, bool reserve_object_id, bool compress)
580 {
581 int sbstack_ptr = pdev->sbstack_depth;
582 pdf_resource_t *pres;
583 stream *save_strm = pdev->strm;
584 int code;
585
586 if (pdev->sbstack_depth >= pdev->sbstack_size)
587 return_error(gs_error_unregistered); /* Must not happen. */
588 if (pdev->sbstack[sbstack_ptr].text_state == 0) {
589 pdev->sbstack[sbstack_ptr].text_state = pdf_text_state_alloc(pdev->pdf_memory);
590 if (pdev->sbstack[sbstack_ptr].text_state == 0)
591 return_error(gs_error_VMerror);
592 }
593 code = pdf_open_aside(pdev, rtype, id, &pres, reserve_object_id,
594 (compress ? DATA_STREAM_COMPRESS : 0));
595 if (code < 0)
596 return code;
597 code = pdf_save_viewer_state(pdev, NULL);
598 if (code < 0) {
599 pdev->strm = save_strm;
600 return code;
601 }
602 pdev->sbstack[sbstack_ptr].context = pdev->context;
603 pdf_text_state_copy(pdev->sbstack[sbstack_ptr].text_state, pdev->text->text_state);
604 pdf_set_text_state_default(pdev->text->text_state);
605 pdev->sbstack[sbstack_ptr].clip_path = pdev->clip_path;
606 pdev->clip_path = 0;
607 pdev->sbstack[sbstack_ptr].clip_path_id = pdev->clip_path_id;
608 pdev->clip_path_id = pdev->no_clip_path_id;
609 pdev->sbstack[sbstack_ptr].vgstack_bottom = pdev->vgstack_bottom;
610 pdev->vgstack_bottom = pdev->vgstack_depth;
611 pdev->sbstack[sbstack_ptr].strm = save_strm;
612 pdev->sbstack[sbstack_ptr].procsets = pdev->procsets;
613 pdev->sbstack[sbstack_ptr].substream_Resources = pdev->substream_Resources;
614 pdev->sbstack[sbstack_ptr].skip_colors = pdev->skip_colors;
615 pdev->sbstack[sbstack_ptr].font3 = pdev->font3;
616 pdev->sbstack[sbstack_ptr].accumulating_substream_resource = pdev->accumulating_substream_resource;
617 pdev->sbstack[sbstack_ptr].charproc_just_accumulated = pdev->charproc_just_accumulated;
618 pdev->sbstack[sbstack_ptr].accumulating_a_global_object = pdev->accumulating_a_global_object;
619 pdev->sbstack[sbstack_ptr].pres_soft_mask_dict = pdev->pres_soft_mask_dict;
620 pdev->sbstack[sbstack_ptr].objname = pdev->objname;
621 pdev->sbstack[sbstack_ptr].last_charpath_op = pdev->last_charpath_op;
622 pdev->skip_colors = false;
623 pdev->charproc_just_accumulated = false;
624 pdev->pres_soft_mask_dict = NULL;
625 pdev->objname.data = NULL;
626 pdev->objname.size = 0;
627 /* Do not reset pdev->accumulating_a_global_object - it inherits. */
628 pdev->sbstack_depth++;
629 pdev->procsets = 0;
630 pdev->font3 = 0;
631 pdev->context = PDF_IN_STREAM;
632 pdev->accumulating_substream_resource = pres;
633 pdev->last_charpath_op = 0;
634 /* Do not alter type3charpath, inherit the current value. We need to know if */
635 /* we are inside a charpath operation, and only reset this when the charpath */
636 /* is complete */
637 pdf_reset_graphics(pdev);
638 *ppres = pres;
639 return 0;
640 }
641
642 /*
643 * Exit the substream accumulation mode.
644 */
645 int
pdf_exit_substream(gx_device_pdf * pdev)646 pdf_exit_substream(gx_device_pdf *pdev)
647 {
648 int code, code1;
649 int sbstack_ptr;
650
651 if (pdev->sbstack_depth <= 0)
652 return_error(gs_error_unregistered); /* Must not happen. */
653 code = pdf_open_contents(pdev, PDF_IN_STREAM);
654 sbstack_ptr = pdev->sbstack_depth - 1;
655 while (pdev->vgstack_depth > pdev->vgstack_bottom) {
656 code1 = pdf_restore_viewer_state(pdev, pdev->strm);
657 if (code >= 0)
658 code = code1;
659 }
660 if (pdev->clip_path != 0)
661 gx_path_free(pdev->clip_path, "pdf_end_charproc_accum");
662 code1 = pdf_close_aside(pdev);
663 if (code1 < 0 && code >= 0)
664 code = code1;
665 pdev->context = pdev->sbstack[sbstack_ptr].context;
666 pdf_text_state_copy(pdev->text->text_state, pdev->sbstack[sbstack_ptr].text_state);
667 pdev->clip_path = pdev->sbstack[sbstack_ptr].clip_path;
668 pdev->sbstack[sbstack_ptr].clip_path = 0;
669 pdev->clip_path_id = pdev->sbstack[sbstack_ptr].clip_path_id;
670 pdev->vgstack_bottom = pdev->sbstack[sbstack_ptr].vgstack_bottom;
671 pdev->strm = pdev->sbstack[sbstack_ptr].strm;
672 pdev->sbstack[sbstack_ptr].strm = 0;
673 pdev->procsets = pdev->sbstack[sbstack_ptr].procsets;
674 pdev->substream_Resources = pdev->sbstack[sbstack_ptr].substream_Resources;
675 pdev->sbstack[sbstack_ptr].substream_Resources = 0;
676 pdev->skip_colors = pdev->sbstack[sbstack_ptr].skip_colors;
677 pdev->font3 = pdev->sbstack[sbstack_ptr].font3;
678 pdev->sbstack[sbstack_ptr].font3 = 0;
679 pdev->accumulating_substream_resource = pdev->sbstack[sbstack_ptr].accumulating_substream_resource;
680 pdev->sbstack[sbstack_ptr].accumulating_substream_resource = 0;
681 pdev->charproc_just_accumulated = pdev->sbstack[sbstack_ptr].charproc_just_accumulated;
682 pdev->accumulating_a_global_object = pdev->sbstack[sbstack_ptr].accumulating_a_global_object;
683 pdev->pres_soft_mask_dict = pdev->sbstack[sbstack_ptr].pres_soft_mask_dict;
684 pdev->objname = pdev->sbstack[sbstack_ptr].objname;
685 pdev->last_charpath_op = pdev->sbstack[sbstack_ptr].last_charpath_op;
686 pdev->sbstack_depth = sbstack_ptr;
687 code1 = pdf_restore_viewer_state(pdev, NULL);
688 if (code1 < 0 && code >= 0)
689 code = code1;
690 return code;
691 }
692
693 static bool
pdf_is_same_charproc_attrs1(gx_device_pdf * pdev,pdf_char_proc_t * pcp0,pdf_char_proc_t * pcp1)694 pdf_is_same_charproc_attrs1(gx_device_pdf *pdev, pdf_char_proc_t *pcp0, pdf_char_proc_t *pcp1)
695 {
696 if (pcp0->real_width.x != pcp1->real_width.x)
697 return false;
698 if (pcp0->real_width.y != pcp1->real_width.y)
699 return false;
700 if (pcp0->v.x != pcp1->v.x)
701 return false;
702 if (pcp0->v.y != pcp1->v.y)
703 return false;
704 return true;
705 }
706
707 typedef struct charproc_compatibility_data_s {
708 const pdf_char_glyph_pairs_t *cgp;
709 pdf_font_resource_t *pdfont;
710 gs_char char_code;
711 gs_glyph glyph;
712 gs_font *font;
713 } charproc_compatibility_data_t;
714
715 static bool
is_char_code_used(pdf_font_resource_t * pdfont,gs_char char_code)716 is_char_code_used(pdf_font_resource_t *pdfont, gs_char char_code)
717 {
718 pdf_char_proc_ownership_t *pcpo;
719
720 for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
721 if (pcpo->char_code == char_code) {
722 return true;
723 }
724 }
725 return false;
726 }
727
728 static int
pdf_is_charproc_compatible(gx_device_pdf * pdev,pdf_resource_t * pres0,pdf_resource_t * pres1)729 pdf_is_charproc_compatible(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
730 {
731 charproc_compatibility_data_t *data = (charproc_compatibility_data_t *)pdev->find_resource_param;
732 pdf_char_proc_t *pcp0 = (pdf_char_proc_t *)pres0;
733 pdf_char_proc_t *pcp1 = (pdf_char_proc_t *)pres1;
734 pdf_font_resource_t *pdfont = data->pdfont;
735 pdf_char_proc_ownership_t *pcpo;
736 pdf_font_cache_elem_t **e;
737 bool can_add_to_current_font = false, computed_can_add_to_current_font = false;
738
739 /* Does it have same attributes ? */
740 if (!pdf_is_same_charproc_attrs1(pdev, pcp0, pcp1))
741 return 0;
742 /* Is it from same font ? */
743 for (pcpo = pcp1->owner_fonts; pcpo != NULL; pcpo = pcpo->char_next) {
744 if (pdfont == pcpo->font) {
745 /* Check for encoding conflict. */
746 if (pcpo->char_code == data->char_code && pcpo->glyph == data->glyph)
747 return 1; /* Same char code. */
748 if (!computed_can_add_to_current_font) {
749 can_add_to_current_font = !is_char_code_used(pdfont, data->char_code);
750 computed_can_add_to_current_font = true;
751 }
752 if (can_add_to_current_font)
753 return 1; /* No conflict. */
754 }
755 }
756 /* Look for another font with same encoding,
757 because we want to reduce the number of new fonts.
758 We also restrict with ones attached to same PS font,
759 otherwise it creates too mixed fonts and disturbs word breaks.
760 */
761 e = pdf_locate_font_cache_elem(pdev, data->font);
762 if (e != NULL) {
763 for (pcpo = pcp1->owner_fonts; pcpo != NULL; pcpo = pcpo->char_next) {
764 if (pcpo->char_code != data->char_code || pcpo->glyph != data->glyph)
765 continue; /* Need same Encoding to generate a proper ToUnicode. */
766 if (pdfont->u.simple.s.type3.bitmap_font != pcpo->font->u.simple.s.type3.bitmap_font)
767 continue;
768 if (memcmp(&pdfont->u.simple.s.type3.FontMatrix, &pcpo->font->u.simple.s.type3.FontMatrix,
769 sizeof(pdfont->u.simple.s.type3.FontMatrix)))
770 continue;
771 if (data->cgp != NULL) {
772 if (!pdf_check_encoding_compatibility(pcpo->font, data->cgp->s, data->cgp->num_all_chars))
773 continue;
774 }
775 if ((*e)->pdfont != pcpo->font)
776 continue;
777 data->pdfont = pcpo->font; /* Switch to the other font. */
778 return 1;
779 }
780 }
781 /* Check whether it can be added into the current font. */
782 if (!computed_can_add_to_current_font)
783 can_add_to_current_font = !is_char_code_used(pdfont, data->char_code);
784 if (!can_add_to_current_font) {
785 /* Can't substitute due to encoding conflict. */
786 return 0;
787 }
788 /* The current font will share it with another font. */
789 return 1;
790 }
791
792 static int
pdf_find_same_charproc_aux(gx_device_pdf * pdev,pdf_font_resource_t ** ppdfont,pdf_char_proc_t ** ppcp)793 pdf_find_same_charproc_aux(gx_device_pdf *pdev,
794 pdf_font_resource_t **ppdfont, pdf_char_proc_t **ppcp)
795 {
796 pdf_char_proc_ownership_t *pcpo;
797 int code;
798
799 /* fixme: this passes parameters to pdf_is_charproc_compatible
800 through special gx_device_pdf field pdev->find_resource_param
801 due to prototype limitation of pdf_find_same_resource.
802 It would be better to change the client data argument type in there to void. */
803 for (pcpo = (*ppdfont)->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
804 pdf_char_proc_t *pcp = pcpo->char_proc;
805
806 if (*ppcp != pcp && pdf_is_same_charproc_attrs1(pdev, *ppcp, pcp)) {
807 cos_object_t *pco0 = pcp->object;
808 cos_object_t *pco1 = (*ppcp)->object;
809
810 code = pco0->cos_procs->equal(pco0, pco1, pdev);
811 if (code < 0) {
812 return code;
813 }
814 if (code) {
815 *ppcp = pcp;
816 return 1;
817 }
818 }
819 }
820 return pdf_find_same_resource(pdev, resourceCharProc, (pdf_resource_t **)ppcp, pdf_is_charproc_compatible);
821 }
822 static int
pdf_find_same_charproc(gx_device_pdf * pdev,pdf_font_resource_t ** ppdfont,const pdf_char_glyph_pairs_t * cgp,pdf_char_proc_t ** ppcp,gs_glyph glyph,gs_char char_code,gs_font * font)823 pdf_find_same_charproc(gx_device_pdf *pdev,
824 pdf_font_resource_t **ppdfont, const pdf_char_glyph_pairs_t *cgp,
825 pdf_char_proc_t **ppcp, gs_glyph glyph, gs_char char_code,
826 gs_font *font)
827 {
828 charproc_compatibility_data_t data;
829 int code;
830
831 data.cgp = cgp;
832 data.pdfont = *ppdfont;
833 data.char_code = char_code;
834 data.glyph = glyph;
835 data.font = font;
836 pdev->find_resource_param = &data;
837 code = pdf_find_same_charproc_aux(pdev, ppdfont, ppcp);
838 pdev->find_resource_param = NULL;
839 *ppdfont = data.pdfont;
840 return code;
841 }
842
843 static bool
pdf_is_charproc_defined(gx_device_pdf * pdev,pdf_font_resource_t * pdfont,gs_char ch)844 pdf_is_charproc_defined(gx_device_pdf *pdev, pdf_font_resource_t *pdfont, gs_char ch)
845 {
846 pdf_char_proc_ownership_t *pcpo;
847
848 for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
849 if (pcpo->char_code == ch)
850 return true;
851 }
852 return false;
853 }
854
855 static int
complete_adding_char(gx_device_pdf * pdev,gs_font * font,gs_glyph glyph,gs_char ch,pdf_char_proc_t * pcp,const gs_const_string * gnstr)856 complete_adding_char(gx_device_pdf *pdev, gs_font *font,
857 gs_glyph glyph, gs_char ch, pdf_char_proc_t *pcp,
858 const gs_const_string *gnstr)
859 {
860 pdf_font_resource_t *pdfont;
861 double *real_widths;
862 byte *glyph_usage;
863 int char_cache_size, width_cache_size;
864 pdf_encoding_element_t *pet;
865 int code;
866
867 code = pdf_attached_font_resource(pdev, font, &pdfont,
868 &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
869 if (code < 0)
870 return code;
871 code = pdf_attach_charproc(pdev, pdfont, pcp, glyph, ch, gnstr);
872 if (code < 0)
873 return code;
874 if (ch >= char_cache_size || ch >= width_cache_size)
875 return_error(gs_error_unregistered); /* Must not happen. */
876 pet = &pdfont->u.simple.Encoding[ch];
877 pdfont->Widths[ch] = pcp->real_width.x;
878 real_widths[ch * 2 ] = pcp->real_width.x;
879 real_widths[ch * 2 + 1] = pcp->real_width.y;
880 glyph_usage[ch / 8] |= 0x80 >> (ch & 7);
881 pdfont->used[ch >> 3] |= 0x80 >> (ch & 7);
882 if (pdfont->u.simple.v != NULL && font->WMode) {
883 pdfont->u.simple.v[ch].x = pcp->v.x;
884 pdfont->u.simple.v[ch].y = pcp->v.x;
885 }
886 pet->glyph = glyph;
887 pet->str = *gnstr;
888 pet->is_difference = true;
889 if (pdfont->u.simple.LastChar < (int)ch)
890 pdfont->u.simple.LastChar = (int)ch;
891 if (pdfont->u.simple.FirstChar > (int)ch)
892 pdfont->u.simple.FirstChar = (int)ch;
893 return 0;
894 }
895
896 static int
pdf_char_widths_from_charprocs(gx_device_pdf * pdev,gs_font * font)897 pdf_char_widths_from_charprocs(gx_device_pdf *pdev, gs_font *font)
898 {
899 pdf_font_resource_t *pdfont;
900 double *real_widths;
901 byte *glyph_usage;
902 int char_cache_size, width_cache_size;
903 pdf_char_proc_ownership_t *pcpo;
904 int code;
905
906 code = pdf_attached_font_resource(pdev, font, &pdfont,
907 &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
908 if (code < 0)
909 return code;
910 for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
911 pdf_char_proc_t *pcp = pcpo->char_proc;
912 gs_char ch = pcpo->char_code;
913
914 real_widths[ch * 2 ] = pcp->real_width.x;
915 real_widths[ch * 2 + 1] = pcp->real_width.y;
916 glyph_usage[ch / 8] |= 0x80 >> (ch & 7);
917 }
918 return 0;
919 }
920
921
922 /*
923 * Complete charproc accumulation for a Type 3 font.
924 */
925 int
pdf_end_charproc_accum(gx_device_pdf * pdev,gs_font * font,const pdf_char_glyph_pairs_t * cgp,gs_glyph glyph,gs_char output_char_code,const gs_const_string * gnstr)926 pdf_end_charproc_accum(gx_device_pdf *pdev, gs_font *font, const pdf_char_glyph_pairs_t *cgp,
927 gs_glyph glyph, gs_char output_char_code, const gs_const_string *gnstr)
928 {
929 int code;
930 pdf_resource_t *pres = (pdf_resource_t *)pdev->accumulating_substream_resource;
931 /* We could use pdfont->u.simple.s.type3.char_procs insted the thing above
932 unless the font is defined recursively.
933 But we don't want such assumption. */
934 pdf_char_proc_t *pcp = (pdf_char_proc_t *)pres;
935 pdf_font_resource_t *pdfont;
936 gs_char ch = output_char_code;
937 bool checking_glyph_variation = false;
938
939 if (ch == GS_NO_CHAR)
940 return_error(gs_error_unregistered); /* Must not happen. */
941 if (ch >= 256)
942 return_error(gs_error_unregistered); /* Must not happen. */
943 code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
944 if (code < 0)
945 return code;
946 if (pdfont != (pdf_font_resource_t *)pdev->font3)
947 return_error(gs_error_unregistered); /* Must not happen. */
948 code = pdf_exit_substream(pdev);
949 if (code < 0)
950 return code;
951 if (!(pdfont->used[ch >> 3] & (0x80 >> (ch & 7))) ||
952 !(pdfont->u.simple.s.type3.cached[ch >> 3] & (0x80 >> (ch & 7)))) {
953 /* First appearence or not cached - check for duplicates. */
954 pdf_font_resource_t *pdfont1 = pdfont;
955
956 checking_glyph_variation = true;
957 /* CAUTION : a possible font change. */
958 code = pdf_find_same_charproc(pdev, &pdfont, cgp, &pcp, glyph, ch, font);
959 if (code < 0)
960 return code;
961 if (code != 0) {
962 code = pdf_cancel_resource(pdev, pres, resourceCharProc);
963 if (code < 0)
964 return code;
965 pdf_forget_resource(pdev, pres, resourceCharProc);
966 if (pdfont1 != pdfont) {
967 code = pdf_attach_font_resource(pdev, font, pdfont);
968 if (code < 0)
969 return code;
970 code = pdf_char_widths_from_charprocs(pdev, font);
971 if (code < 0)
972 return code;
973 }
974 pdev->charproc_just_accumulated = true;
975 return complete_adding_char(pdev, font, glyph, ch, pcp, gnstr);
976 }
977 if (pdf_is_charproc_defined(pdev, pdfont, ch)) {
978 /* Encoding conflict after a font change. */
979 gs_font *base_font = font, *below;
980
981 while ((below = base_font->base) != base_font &&
982 base_font->procs.same_font(base_font, below, FONT_SAME_OUTLINES))
983 base_font = below;
984 code = pdf_make_font3_resource(pdev, base_font, &pdfont);
985 if (code < 0)
986 return code;
987 code = pdf_attach_font_resource(pdev, font, pdfont);
988 if (code < 0)
989 return code;
990 }
991 }
992 pdf_reserve_object_id(pdev, pres, 0);
993 if (checking_glyph_variation)
994 pdev->charproc_just_accumulated = true;
995 return complete_adding_char(pdev, font, glyph, ch, pcp, gnstr);
996 }
997
998 /* Add procsets to substream Resources. */
999 int
pdf_add_procsets(cos_dict_t * pcd,pdf_procset_t procsets)1000 pdf_add_procsets(cos_dict_t *pcd, pdf_procset_t procsets)
1001 {
1002 char str[5 + 7 + 7 + 7 + 5 + 2];
1003 cos_value_t v;
1004
1005 strcpy(str, "[/PDF");
1006 if (procsets & ImageB)
1007 strcat(str, "/ImageB");
1008 if (procsets & ImageC)
1009 strcat(str, "/ImageC");
1010 if (procsets & ImageI)
1011 strcat(str, "/ImageI");
1012 if (procsets & Text)
1013 strcat(str, "/Text");
1014 strcat(str, "]");
1015 cos_string_value(&v, (byte *)str, strlen(str));
1016 return cos_dict_put_c_key(pcd, "/ProcSet", &v);
1017 }
1018
1019 /* Add a resource to substream Resources. */
1020 int
pdf_add_resource(gx_device_pdf * pdev,cos_dict_t * pcd,const char * key,pdf_resource_t * pres)1021 pdf_add_resource(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key, pdf_resource_t *pres)
1022 {
1023 if (pcd != 0) {
1024 const cos_value_t *v = cos_dict_find(pcd, (const byte *)key, strlen(key));
1025 cos_dict_t *list;
1026 int code;
1027 char buf[10 + (sizeof(long) * 8 / 3 + 1)], buf1[sizeof(pres->rname) + 1];
1028
1029 if (pdev->ForOPDFRead && !pres->global && pdev->accumulating_a_global_object) {
1030 pres->global = true;
1031 code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
1032 if (code < 0)
1033 return code;
1034 }
1035 sprintf(buf, "%ld 0 R\n", pres->object->id);
1036 if (v != NULL) {
1037 if (v->value_type != COS_VALUE_OBJECT &&
1038 v->value_type != COS_VALUE_RESOURCE)
1039 return_error(gs_error_unregistered); /* Must not happen. */
1040 list = (cos_dict_t *)v->contents.object;
1041 if (list->cos_procs != &cos_dict_procs)
1042 return_error(gs_error_unregistered); /* Must not happen. */
1043 } else {
1044 list = cos_dict_alloc(pdev, "pdf_add_resource");
1045 if (list == NULL)
1046 return_error(gs_error_VMerror);
1047 code = cos_dict_put_c_key_object((cos_dict_t *)pcd, key, (cos_object_t *)list);
1048 if (code < 0)
1049 return code;
1050 }
1051 buf1[0] = '/';
1052 strcpy(buf1 + 1, pres->rname);
1053 return cos_dict_put_string(list, (const byte *)buf1, strlen(buf1),
1054 (const byte *)buf, strlen(buf));
1055 }
1056 return 0;
1057 }
1058
1059