1 /* Copyright (C) 2001-2012 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.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* Output utilities for PDF-writing driver */
18 #include "memory_.h"
19 #include "jpeglib_.h"		/* for sdct.h */
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gscdefs.h"
23 #include "gsdsrc.h"
24 #include "gsfunc.h"
25 #include "gsfunc3.h"
26 #include "gdevpdfx.h"
27 #include "gdevpdfo.h"
28 #include "gdevpdfg.h"
29 #include "gdevpdtd.h"
30 #include "strimpl.h"
31 #include "sa85x.h"
32 #include "scfx.h"
33 #include "sdct.h"
34 #include "slzwx.h"
35 #include "spngpx.h"
36 #include "srlx.h"
37 #include "sarc4.h"
38 #include "smd5.h"
39 #include "sstring.h"
40 #include "strmio.h"
41 #include "szlibx.h"
42 #ifdef USE_LDF_JB2
43 #include "sjbig2_luratech.h"
44 #endif
45 #ifdef USE_LWF_JP2
46 #include "sjpx_luratech.h"
47 #endif
48 
49 #include "opdfread.h"
50 #include "gdevagl.h"
51 #include "gs_mgl_e.h"
52 #include "gs_mro_e.h"
53 
54 extern single_glyph_list_t *SingleGlyphList;
55 
56     /* Define the size of internal stream buffers. */
57 /* (This is not a limitation, it only affects performance.) */
58 #define sbuf_size 512
59 
60 /* Optionally substitute other filters for FlateEncode for debugging. */
61 #if 1
62 #  define compression_filter_name "FlateDecode"
63 #  define compression_filter_template s_zlibE_template
64 #  define compression_filter_state stream_zlib_state
65 #else
66 #  define compression_filter_name "LZWDecode"
67 #  define compression_filter_template s_LZWE_template
68 #  define compression_filter_state stream_LZW_state
69 #endif
70 
71 /* Import procedures for writing filter parameters. */
72 extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
73 extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state);
74 
75 #define CHECK(expr)\
76   BEGIN if ((code = (expr)) < 0) return code; END
77 
78 /* GC descriptors */
79 extern_st(st_pdf_color_space);
80 extern_st(st_pdf_font_resource);
81 extern_st(st_pdf_char_proc);
82 extern_st(st_pdf_font_descriptor);
83 public_st_pdf_resource();
84 private_st_pdf_x_object();
85 private_st_pdf_pattern();
86 
87 /* ---------------- Utilities ---------------- */
88 
89 #ifdef PS2WRITE_USES_ROMFS
90 /*
91  * Strip whitespace and comments from a line of PostScript code as possible.
92  * Return a pointer to any string that remains, or NULL if none.
93  * Note that this may store into the string.
94  */
95 /* This function copied from geninit.c . */
96 static char *
doit(char * line,bool intact)97 doit(char *line, bool intact)
98 {
99     char *str = line;
100     char *from;
101     char *to;
102     int in_string = 0;
103 
104     if (intact)
105         return str;
106     while (*str == ' ' || *str == '\t')		/* strip leading whitespace */
107         ++str;
108     if (*str == 0)		/* all whitespace */
109         return NULL;
110     if (!strncmp(str, "%END", 4))	/* keep these for .skipeof */
111         return str;
112     if (str[0] == '%')    /* comment line */
113         return NULL;
114     /*
115      * Copy the string over itself removing:
116      *  - All comments not within string literals;
117      *  - Whitespace adjacent to '[' ']' '{' '}';
118      *  - Whitespace before '/' '(' '<';
119      *  - Whitespace after ')' '>'.
120      */
121     for (to = from = str; (*to = *from) != 0; ++from, ++to) {
122         switch (*from) {
123             case '%':
124                 if (!in_string)
125                     break;
126                 continue;
127             case ' ':
128             case '\t':
129                 if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
130                     --to;
131                 continue;
132             case '(':
133             case '<':
134             case '/':
135             case '[':
136             case ']':
137             case '{':
138             case '}':
139                 if (to > str && !in_string && strchr(" \t", to[-1]))
140                     *--to = *from;
141                 if (*from == '(')
142                     ++in_string;
143                 continue;
144             case ')':
145                 --in_string;
146                 continue;
147             case '\\':
148                 if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
149                     *++to = *++from;
150                 continue;
151             default:
152                 continue;
153         }
154         break;
155     }
156     /* Strip trailing whitespace. */
157     while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
158         --to;
159     *to = 0;
160     return str;
161 }
162 
163 static int
copy_ps_file_stripping_all(stream * s,const char * fname,bool HaveTrueTypes)164 copy_ps_file_stripping_all(stream *s, const char *fname, bool HaveTrueTypes)
165 {
166     stream *f;
167     char buf[1024], *p, *q  = buf;
168     int n, l = 0, m = sizeof(buf) - 1, outl = 0;
169     bool skipping = false;
170 
171     f = sfopen(fname, "rb", s->memory);
172     if (f == NULL)
173         return_error(gs_error_undefinedfilename);
174     n = sfread(buf, 1, m, f);
175     buf[n] = 0;
176     do {
177         if (*q == '\r' || *q == '\n') {
178             q++;
179             continue;
180         }
181         p = strchr(q, '\r');
182         if (p == NULL)
183             p = strchr(q, '\n');
184         if (p == NULL) {
185             if (n < m)
186                 p = buf + n;
187             else {
188                 strcpy(buf, q);
189                 l = strlen(buf);
190                 m = sizeof(buf) - 1 - l;
191                 if (!m) {
192                     sfclose(f);
193                     emprintf1(s->memory,
194                               "The procset %s contains a too long line.",
195                               fname);
196                     return_error(gs_error_ioerror);
197                 }
198                 n = sfread(buf + l, 1, m, f);
199                 n += l;
200                 m += l;
201                 buf[n] = 0;
202                 q = buf;
203                 continue;
204             }
205         }
206         *p = 0;
207         if (q[0] == '%')
208             l = 0;
209         else {
210             q = doit(q, false);
211             if (q == NULL)
212                 l = 0;
213             else
214                 l = strlen(q);
215         }
216         if (l) {
217             if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
218                 skipping = true;
219             if (!skipping) {
220                 outl += l + 1;
221                 if (outl > 100) {
222                     q[l] = '\r';
223                     outl = 0;
224                 } else
225                     q[l] = ' ';
226                 stream_write(s, q, l + 1);
227             }
228             if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
229                 skipping = false;
230         }
231         q = p + 1;
232     } while (n == m || q < buf + n);
233     if (outl)
234         stream_write(s, "\r", 1);
235     sfclose(f);
236     return 0;
237 }
238 
239 static int
copy_ps_file_strip_comments(stream * s,const char * fname,bool HaveTrueTypes)240 copy_ps_file_strip_comments(stream *s, const char *fname, bool HaveTrueTypes)
241 {
242     stream *f;
243     char buf[1024], *p, *q  = buf;
244     int n, l = 0, m = sizeof(buf) - 1, outl = 0;
245     bool skipping = false;
246 
247     f = sfopen(fname, "rb", s->memory);
248     if (f == NULL)
249         return_error(gs_error_undefinedfilename);
250     n = sfread(buf, 1, m, f);
251     buf[n] = 0;
252     do {
253         if (*q == '\r' || *q == '\n') {
254             q++;
255             continue;
256         }
257         p = strchr(q, '\r');
258         if (p == NULL)
259             p = strchr(q, '\n');
260         if (p == NULL) {
261             if (n < m)
262                 p = buf + n;
263             else {
264                 strcpy(buf, q);
265                 l = strlen(buf);
266                 m = sizeof(buf) - 1 - l;
267                 if (!m) {
268                     sfclose(f);
269                     emprintf1(s->memory,
270                               "The procset %s contains a too long line.",
271                               fname);
272                     return_error(gs_error_ioerror);
273                 }
274                 n = sfread(buf + l, 1, m, f);
275                 n += l;
276                 m += l;
277                 buf[n] = 0;
278                 q = buf;
279                 continue;
280             }
281         }
282         *p = 0;
283         if (q[0] == '%')
284             l = 0;
285         else {
286             q = doit(q, false);
287             if (q == NULL)
288                 l = 0;
289             else
290                 l = strlen(q);
291         }
292         if (l) {
293             if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
294                 skipping = true;
295             if (!skipping) {
296                 outl += l + 1;
297                 if (outl > 100) {
298                     q[l] = '\n';
299                     outl = 0;
300                 } else
301                     q[l] = '\n';
302                 stream_write(s, q, l + 1);
303             }
304             if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
305                 skipping = false;
306         }
307         q = p + 1;
308     } while (n == m || q < buf + n);
309     if (outl)
310         stream_write(s, "\r", 1);
311     sfclose(f);
312     return 0;
313 }
314 #endif
315 
write_opdfread(stream * s)316 static int write_opdfread(stream *s)
317 {
318     int index = 0;
319 
320     do {
321         if (opdfread_ps[index] == 0x00)
322             break;
323         stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
324         index++;
325     } while (1);
326     return 0;
327 }
328 
write_tt_encodings(stream * s,bool HaveTrueTypes)329 static int write_tt_encodings(stream *s, bool HaveTrueTypes)
330 {
331     int index = 0;
332 
333     do {
334         if (gs_mro_e_ps[index] == 0x00)
335             break;
336         stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
337         index++;
338     } while (1);
339 
340     if (HaveTrueTypes) {
341         char Buffer[256];
342         single_glyph_list_t *entry = (single_glyph_list_t *)&SingleGlyphList;
343 
344         sprintf(Buffer, "/AdobeGlyphList mark\n");
345         stream_write(s, Buffer, strlen(Buffer));
346         while (entry->Glyph) {
347             sprintf(Buffer, "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
348             stream_write(s, Buffer, strlen(Buffer));
349             entry++;
350         };
351         sprintf(Buffer, ".dicttomark readonly def\n");
352         stream_write(s, Buffer, strlen(Buffer));
353 
354         index = 0;
355         do {
356             if (gs_mgl_e_ps[index] == 0x00)
357                 break;
358             stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
359             index++;
360         } while (1);
361     }
362     return 0;
363 }
364 
365 static int
copy_procsets(stream * s,bool HaveTrueTypes,bool stripping)366 copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
367 {
368     int code;
369 
370     code = write_opdfread(s);
371     if (code < 0)
372         return code;
373 
374     code = write_tt_encodings(s, HaveTrueTypes);
375     return code;
376 
377 }
378 
379 static int
encode(stream ** s,const stream_template * t,gs_memory_t * mem)380 encode(stream **s, const stream_template *t, gs_memory_t *mem)
381 {
382     stream_state *st = s_alloc_state(mem, t->stype, "pdfwrite_pdf_open_document.encode");
383 
384     if (st == 0)
385         return_error(gs_error_VMerror);
386     if (t->set_defaults)
387         t->set_defaults(st);
388     if (s_add_filter(s, t, st, mem) == 0) {
389         gs_free_object(mem, st, "pdfwrite_pdf_open_document.encode");
390         return_error(gs_error_VMerror);
391     }
392     return 0;
393 }
394 
395 /* ------ Document ------ */
396 
ps2write_dsc_header(gx_device_pdf * pdev,int pages)397 int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
398 {
399     stream *s = pdev->strm;
400 
401     if (pdev->ForOPDFRead) {
402         char cre_date_time[41];
403         int code, status, cre_date_time_len;
404         char BBox[256];
405 
406         stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
407         /* We need to calculate the document BoundingBox which is a 'high water'
408          * mark derived from the BoundingBox of all the individual pages.
409          */
410         {
411             int pagecount = 1;
412             int urx=0, ury=0, j;
413 
414             for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
415                 pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
416 
417                 for (; pres != 0; pres = pres->next)
418                     if ((!pres->named || pdev->ForOPDFRead)
419                         && !pres->object->written) {
420                         pdf_page_t *page = &pdev->pages[pagecount - 1];
421                         if ((int)ceil(page->MediaBox.x) > urx)
422                             urx = page->MediaBox.x;
423                         if ((int)ceil(page->MediaBox.y) > urx)
424                             ury = page->MediaBox.y;
425                         pagecount++;
426                     }
427             }
428             sprintf(BBox, "%%%%BoundingBox: 0 0 %d %d\n", urx, ury);
429         }
430         stream_write(s, (byte *)BBox, strlen(BBox));
431         cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
432         cre_date_time[cre_date_time_len] = 0;
433         sprintf(BBox, "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
434                 pdev->dname);
435         stream_write(s, (byte *)BBox, strlen(BBox));
436         stream_puts(s, "%%LanguageLevel: 2\n");
437         sprintf(BBox, "%%%%CreationDate: %s\n", cre_date_time);
438         stream_write(s, (byte *)BBox, strlen(BBox));
439         sprintf(BBox, "%%%%Pages: %d\n", pages);
440         stream_write(s, (byte *)BBox, strlen(BBox));
441         sprintf(BBox, "%%%%EndComments\n");
442         stream_write(s, (byte *)BBox, strlen(BBox));
443         sprintf(BBox, "%%%%BeginProlog\n");
444         stream_write(s, (byte *)BBox, strlen(BBox));
445         if (pdev->params.CompressPages) {
446             /*  When CompressEntireFile is true and ASCII85EncodePages is false,
447                 the ASCII85Encode filter is applied, rather one may expect the opposite.
448                 Keeping it so due to no demand for this mode.
449                 A right implementation should compute the length of the compressed procset,
450                 write out an invocation of SubFileDecode filter, and write the length to
451                 there assuming the output file is positionable. */
452             stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
453             code = encode(&s, &s_A85E_template, pdev->pdf_memory);
454             if (code < 0)
455                 return code;
456             code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
457             if (code < 0)
458                 return code;
459         }
460         stream_puts(s, "/DSC_OPDFREAD true def\n");
461         stream_puts(s, "/SetPageSize true def\n");
462 
463         code = copy_procsets(s, pdev->HaveTrueTypes, false);
464         if (code < 0)
465             return code;
466         status = s_close_filters(&s, pdev->strm);
467         if (status < 0)
468             return_error(gs_error_ioerror);
469         stream_puts(s, "\n");
470         pdev->OPDFRead_procset_length = stell(s);
471     }
472     return 0;
473 }
474 
475 /* Open the document if necessary. */
476 int
pdfwrite_pdf_open_document(gx_device_pdf * pdev)477 pdfwrite_pdf_open_document(gx_device_pdf * pdev)
478 {
479     if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
480         stream *s = pdev->strm;
481         int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
482 
483         pdev->binary_ok = !pdev->params.ASCII85EncodePages;
484         if (pdev->ForOPDFRead) {
485             int code, status;
486             char BBox[256];
487             int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
488             int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
489 
490             if (pdev->ProduceDSC)
491                 pdev->CompressEntireFile = 0;
492             else {
493                 stream_write(s, (byte *)"%!\r", 3);
494                 sprintf(BBox, "%%%%BoundingBox: 0 0 %d %d\n", width, height);
495                 stream_write(s, (byte *)BBox, strlen(BBox));
496                 if (pdev->params.CompressPages || pdev->CompressEntireFile) {
497                     /*  When CompressEntireFile is true and ASCII85EncodePages is false,
498                         the ASCII85Encode filter is applied, rather one may expect the opposite.
499                         Keeping it so due to no demand for this mode.
500                         A right implementation should compute the length of the compressed procset,
501                         write out an invocation of SubFileDecode filter, and write the length to
502                         there assuming the output file is positionable. */
503                     stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
504                     code = encode(&s, &s_A85E_template, pdev->pdf_memory);
505                     if (code < 0)
506                         return code;
507                     code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
508                     if (code < 0)
509                         return code;
510                 }
511                 stream_puts(s, "/DSC_OPDFREAD false def\n");
512                 code = copy_procsets(s, pdev->HaveTrueTypes, true);
513                 if (code < 0)
514                     return code;
515                 if (!pdev->CompressEntireFile) {
516                     status = s_close_filters(&s, pdev->strm);
517                     if (status < 0)
518                         return_error(gs_error_ioerror);
519                 } else
520                     pdev->strm = s;
521                 if(pdev->SetPageSize)
522                     stream_puts(s, "/SetPageSize true def\n");
523                 if(pdev->RotatePages)
524                     stream_puts(s, "/RotatePages true def\n");
525                 if(pdev->FitPages)
526                     stream_puts(s, "/FitPages true def\n");
527                 if(pdev->CenterPages)
528                     stream_puts(s, "/CenterPages true def\n");
529                 pdev->OPDFRead_procset_length = stell(s);
530             }
531         }
532         if (!(pdev->ForOPDFRead)) {
533             pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
534             if (pdev->binary_ok)
535                 stream_puts(s, "%\307\354\217\242\n");
536         }
537     }
538     /*
539      * Determine the compression method.  Currently this does nothing.
540      * It also isn't clear whether the compression method can now be
541      * changed in the course of the document.
542      *
543      * Flate compression is available starting in PDF 1.2.  Since we no
544      * longer support any older PDF versions, we ignore UseFlateCompression
545      * and always use Flate compression.
546      */
547     if (!pdev->params.CompressPages)
548         pdev->compression = pdf_compress_none;
549     else
550         pdev->compression = pdf_compress_Flate;
551     return 0;
552 }
553 
554 /* ------ Objects ------ */
555 
556 /* Allocate an object ID. */
557 static long
pdf_next_id(gx_device_pdf * pdev)558 pdf_next_id(gx_device_pdf * pdev)
559 {
560     return (pdev->next_id)++;
561 }
562 
563 /*
564  * Return the current position in the output.  Note that this may be in the
565  * main output file, the asides file, or the pictures file.  If the current
566  * file is the pictures file, positions returned by pdf_stell must only be
567  * used locally (for computing lengths or patching), since there is no way
568  * to map them later to the eventual position in the output file.
569  */
570 long
pdf_stell(gx_device_pdf * pdev)571 pdf_stell(gx_device_pdf * pdev)
572 {
573     stream *s = pdev->strm;
574     long pos = stell(s);
575 
576     if (s == pdev->asides.strm)
577         pos += ASIDES_BASE_POSITION;
578     return pos;
579 }
580 
581 /* Allocate an ID for a future object.
582  * pdf_obj_ref below allocates an object and assigns it a position assuming
583  * it will be written at the current location in the PDF file. But we want
584  * some way to notice when writing the PDF file if some kinds of objects have
585  * never been written out (eg pages allocated for /Dest targets). Setting the
586  * position to 0 is good, because we always write the header, which is 15
587  * bytes. However, pdf_obj_ref is so wisely used its no longer possible to
588  * tell whether writing the object out has been deferred (in which case the
589  * pos is updated by pdf_open_obj) or not. Adding this function to allow us
590  * to create an object whose writing is definitely deferred, in which case
591  * we know it will be updated later. This allows setting the pos to 0,
592  * and we can detect that when writing the xref, and set the object to
593  * 'unused'.
594  */
pdf_obj_forward_ref(gx_device_pdf * pdev)595 long pdf_obj_forward_ref(gx_device_pdf * pdev)
596 {
597     long id = pdf_next_id(pdev);
598     long pos = 0;
599 
600     fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
601     return id;
602 }
603 
604 /* Allocate an ID for a future object. */
605 long
pdf_obj_ref(gx_device_pdf * pdev)606 pdf_obj_ref(gx_device_pdf * pdev)
607 {
608     long id = pdf_next_id(pdev);
609     long pos = pdf_stell(pdev);
610 
611     fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
612     return id;
613 }
614 
615 /* Begin an object, optionally allocating an ID. */
616 long
pdf_open_obj(gx_device_pdf * pdev,long id,pdf_resource_type_t type)617 pdf_open_obj(gx_device_pdf * pdev, long id, pdf_resource_type_t type)
618 {
619     stream *s = pdev->strm;
620 
621     if (id <= 0) {
622         id = pdf_obj_ref(pdev);
623     } else {
624         long pos = pdf_stell(pdev);
625         FILE *tfile = pdev->xref.file;
626         int64_t tpos = gp_ftell_64(tfile);
627 
628         gp_fseek_64 (tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos),
629               SEEK_SET);
630         fwrite(&pos, sizeof(pos), 1, tfile);
631         gp_fseek_64(tfile, tpos, SEEK_SET);
632     }
633     if (pdev->ForOPDFRead && pdev->ProduceDSC) {
634         switch(type) {
635             case resourceNone:
636                 /* Used when outputting usage of a previously defined resource
637                  * Does not want comments around its use
638                  */
639                 break;
640             case resourcePage:
641                 /* We *don't* want resource comments around pages */
642                 break;
643             case resourceColorSpace:
644                 pprintld1(s, "%%%%BeginResource: file (PDF Color Space obj_%ld)\n", id);
645                 break;
646             case resourceExtGState:
647                 pprintld1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%ld)\n", id);
648                 break;
649             case resourcePattern:
650                 pprintld1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%ld)\n", id);
651                 break;
652             case resourceShading:
653                 pprintld1(s, "%%%%BeginResource: file (PDF Shading obj_%ld)\n", id);
654                 break;
655             case resourceCIDFont:
656             case resourceFont:
657                 /* Ought to write the font name here */
658                 pprintld1(s, "%%%%BeginResource: font (PDF Font obj_%ld)\n", id);
659                 break;
660             case resourceCharProc:
661                 pprintld1(s, "%%%%BeginResource: file (PDF CharProc obj_%ld)\n", id);
662                 break;
663             case resourceCMap:
664                 pprintld1(s, "%%%%BeginResource: file (PDF CMap obj_%ld)\n", id);
665                 break;
666             case resourceFontDescriptor:
667                 pprintld1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%ld)\n", id);
668                 break;
669             case resourceGroup:
670                 pprintld1(s, "%%%%BeginResource: file (PDF Group obj_%ld)\n", id);
671                 break;
672             case resourceFunction:
673                 pprintld1(s, "%%%%BeginResource: file (PDF Function obj_%ld)\n", id);
674                 break;
675             case resourceEncoding:
676                 pprintld1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%ld)\n", id);
677                 break;
678             case resourceCIDSystemInfo:
679                 pprintld1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%ld)\n", id);
680                 break;
681             case resourceHalftone:
682                 pprintld1(s, "%%%%BeginResource: file (PDF Halftone obj_%ld)\n", id);
683                 break;
684             case resourceLength:
685                 pprintld1(s, "%%%%BeginResource: file (PDF Length obj_%ld)\n", id);
686                 break;
687             case resourceSoftMaskDict:
688                 /* This should not be possible, not valid in PostScript */
689                 pprintld1(s, "%%%%BeginResource: file (PDF SoftMask obj_%ld)\n", id);
690                 break;
691             case resourceXObject:
692                 /* This should not be possible, we write these inline */
693                 pprintld1(s, "%%%%BeginResource: file (PDF XObject obj_%ld)\n", id);
694                 break;
695             case resourceStream:
696                 /* Possibly we should not add comments to this type */
697                 pprintld1(s, "%%%%BeginResource: file (PDF stream obj_%ld)\n", id);
698                 break;
699             case resourceOutline:
700                 /* This should not be possible, not valid in PostScript */
701                 pprintld1(s, "%%%%BeginResource: file (PDF Outline obj_%ld)\n", id);
702                 break;
703             case resourceArticle:
704                 /* This should not be possible, not valid in PostScript */
705                 pprintld1(s, "%%%%BeginResource: file (PDF Article obj_%ld)\n", id);
706                 break;
707             case resourceDests:
708                 /* This should not be possible, not valid in PostScript */
709                 pprintld1(s, "%%%%BeginResource: file (PDF Dests obj_%ld)\n", id);
710                 break;
711             case resourceLabels:
712                 /* This should not be possible, not valid in PostScript */
713                 pprintld1(s, "%%%%BeginResource: file (PDF Page Labels obj_%ld)\n", id);
714                 break;
715             case resourceThread:
716                 /* This should not be possible, not valid in PostScript */
717                 pprintld1(s, "%%%%BeginResource: file (PDF Thread obj_%ld)\n", id);
718                 break;
719             case resourceCatalog:
720                 /* This should not be possible, not valid in PostScript */
721                 pprintld1(s, "%%%%BeginResource: file (PDF Catalog obj_%ld)\n", id);
722                 break;
723             case resourceEncrypt:
724                 /* This should not be possible, not valid in PostScript */
725                 pprintld1(s, "%%%%BeginResource: file (PDF Encryption obj_%ld)\n", id);
726                 break;
727             case resourcePagesTree:
728                 /* This should not be possible, not valid in PostScript */
729                 pprintld1(s, "%%%%BeginResource: file (PDF Pages Tree obj_%ld)\n", id);
730                 break;
731             case resourceMetadata:
732                 /* This should not be possible, not valid in PostScript */
733                 pprintld1(s, "%%%%BeginResource: file (PDF Metadata obj_%ld)\n", id);
734                 break;
735             case resourceICC:
736                 /* This should not be possible, not valid in PostScript */
737                 pprintld1(s, "%%%%BeginResource: file (PDF ICC Profile obj_%ld)\n", id);
738                 break;
739             case resourceAnnotation:
740                 /* This should not be possible, not valid in PostScript */
741                 pprintld1(s, "%%%%BeginResource: file (PDF Annotation obj_%ld)\n", id);
742                 break;
743             default:
744                 pprintld1(s, "%%%%BeginResource: file (PDF object obj_%ld)\n", id);
745                 break;
746         }
747     }
748     pprintld1(s, "%ld 0 obj\n", id);
749     return id;
750 }
751 long
pdf_begin_obj(gx_device_pdf * pdev,pdf_resource_type_t type)752 pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
753 {
754     return pdf_open_obj(pdev, 0L, type);
755 }
756 
757 /* End an object. */
758 int
pdf_end_obj(gx_device_pdf * pdev,pdf_resource_type_t type)759 pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
760 {
761     stream_puts(pdev->strm, "endobj\n");
762     if (pdev->ForOPDFRead && pdev->ProduceDSC) {
763         switch(type) {
764             case resourcePage:
765                 break;
766             default:
767             stream_puts(pdev->strm, "%%EndResource\n");
768             break;
769         }
770     }
771     return 0;
772 }
773 
774 /* ------ Page contents ------ */
775 
776 /* Handle transitions between contexts. */
777 static int
778     none_to_stream(gx_device_pdf *), stream_to_text(gx_device_pdf *),
779     string_to_text(gx_device_pdf *), text_to_stream(gx_device_pdf *),
780     stream_to_none(gx_device_pdf *);
781 typedef int (*context_proc) (gx_device_pdf *);
782 static const context_proc context_procs[4][4] =
783 {
784     {0, none_to_stream, none_to_stream, none_to_stream},
785     {stream_to_none, 0, stream_to_text, stream_to_text},
786     {text_to_stream, text_to_stream, 0, 0},
787     {string_to_text, string_to_text, string_to_text, 0}
788 };
789 
790 /* Compute an object encryption key. */
791 static int
pdf_object_key(const gx_device_pdf * pdev,gs_id object_id,byte key[16])792 pdf_object_key(const gx_device_pdf * pdev, gs_id object_id, byte key[16])
793 {
794     gs_md5_state_t md5;
795     gs_md5_byte_t zero[2] = {0, 0}, t;
796     int KeySize = pdev->KeyLength / 8;
797 
798     gs_md5_init(&md5);
799     gs_md5_append(&md5, pdev->EncryptionKey, KeySize);
800     t = (byte)(object_id >>  0);  gs_md5_append(&md5, &t, 1);
801     t = (byte)(object_id >>  8);  gs_md5_append(&md5, &t, 1);
802     t = (byte)(object_id >> 16);  gs_md5_append(&md5, &t, 1);
803     gs_md5_append(&md5, zero, 2);
804     gs_md5_finish(&md5, key);
805     return min(KeySize + 5, 16);
806 }
807 
808 /* Initialize encryption. */
809 int
pdf_encrypt_init(const gx_device_pdf * pdev,gs_id object_id,stream_arcfour_state * psarc4)810 pdf_encrypt_init(const gx_device_pdf * pdev, gs_id object_id, stream_arcfour_state *psarc4)
811 {
812     byte key[16];
813 
814     return s_arcfour_set_key(psarc4, key, pdf_object_key(pdev, object_id, key));
815 }
816 
817 /* Add the encryption filter. */
818 int
pdf_begin_encrypt(gx_device_pdf * pdev,stream ** s,gs_id object_id)819 pdf_begin_encrypt(gx_device_pdf * pdev, stream **s, gs_id object_id)
820 {
821     gs_memory_t *mem = pdev->v_memory;
822     stream_arcfour_state *ss;
823     gs_md5_byte_t key[16];
824     int code, keylength;
825 
826     if (!pdev->KeyLength)
827         return 0;
828     keylength = pdf_object_key(pdev, object_id, key);
829     ss = gs_alloc_struct(mem, stream_arcfour_state,
830                     s_arcfour_template.stype, "psdf_encrypt");
831     if (ss == NULL)
832         return_error(gs_error_VMerror);
833     code = s_arcfour_set_key(ss, key, keylength);
834     if (code < 0)
835         return code;
836     if (s_add_filter(s, &s_arcfour_template, (stream_state *)ss, mem) == 0)
837         return_error(gs_error_VMerror);
838     return 0;
839     /* IMPORTANT NOTE :
840        We don't encrypt streams written into temporary files,
841        because they can be used for comparizon
842        (for example, for merging equal images).
843        Instead that the encryption is applied in pdf_copy_data,
844        when the stream is copied to the output file.
845      */
846 }
847 
848 /* Remove the encryption filter. */
849 void
pdf_end_encrypt(gx_device_pdf * pdev)850 pdf_end_encrypt(gx_device_pdf * pdev)
851 {
852     if (pdev->KeyLength) {
853         stream *s = pdev->strm;
854         stream *fs = s->strm;
855 
856         sclose(s);
857         gs_free_object(pdev->pdf_memory, s->cbuf, "encrypt buffer");
858         gs_free_object(pdev->pdf_memory, s, "encrypt stream");
859         pdev->strm = fs;
860     }
861 }
862 
863 /* Enter stream context. */
864 static int
none_to_stream(gx_device_pdf * pdev)865 none_to_stream(gx_device_pdf * pdev)
866 {
867     stream *s;
868     int code;
869 
870     if (pdev->contents_id != 0)
871         return_error(gs_error_Fatal);	/* only 1 contents per page */
872     pdev->compression_at_page_start = pdev->compression;
873     if (pdev->ResourcesBeforeUsage) {
874         pdf_resource_t *pres;
875 
876         code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
877                     true, pdev->params.CompressPages);
878         if (code < 0)
879             return code;
880         pdev->contents_id = pres->object->id;
881         pdev->contents_length_id = gs_no_id; /* inapplicable */
882         pdev->contents_pos = -1; /* inapplicable */
883         s = pdev->strm;
884     } else {
885         pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
886         pdev->contents_length_id = pdf_obj_ref(pdev);
887         s = pdev->strm;
888         pprintld1(s, "<</Length %ld 0 R", pdev->contents_length_id);
889         if (pdev->compression == pdf_compress_Flate) {
890             if (pdev->binary_ok)
891                 pprints1(s, "/Filter /%s", compression_filter_name);
892             else
893                 pprints1(s, "/Filter [/ASCII85Decode /%s]", compression_filter_name);
894         }
895         stream_puts(s, ">>\nstream\n");
896         pdev->contents_pos = pdf_stell(pdev);
897         code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
898         if (code < 0)
899             return code;
900         pdev->strm = s;
901         if (pdev->compression == pdf_compress_Flate) {	/* Set up the Flate filter. */
902             const stream_template *templat;
903             stream *es;
904             byte *buf;
905             compression_filter_state *st;
906 
907             if (!pdev->binary_ok) {	/* Set up the A85 filter */
908                 const stream_template *templat2 = &s_A85E_template;
909                 stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
910                 byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
911                                            "PDF contents buffer");
912                 stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
913                                 templat2->stype, "PDF contents state");
914                 if (as == 0 || ast == 0 || buf == 0)
915                     return_error(gs_error_VMerror);
916                 s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
917                            s_mode_write);
918                 ast->memory = pdev->pdf_memory;
919                 ast->templat = templat2;
920                 as->state = (stream_state *) ast;
921                 as->procs.process = templat2->process;
922                 as->strm = s;
923                 (*templat2->init) ((stream_state *) ast);
924                 pdev->strm = s = as;
925             }
926             templat = &compression_filter_template;
927             es = s_alloc(pdev->pdf_memory, "PDF compression stream");
928             buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
929                                        "PDF compression buffer");
930             st = gs_alloc_struct(pdev->pdf_memory, compression_filter_state,
931                                  templat->stype, "PDF compression state");
932             if (es == 0 || st == 0 || buf == 0)
933                 return_error(gs_error_VMerror);
934             s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
935                        s_mode_write);
936             st->memory = pdev->pdf_memory;
937             st->templat = templat;
938             es->state = (stream_state *) st;
939             es->procs.process = templat->process;
940             es->strm = s;
941             (*templat->set_defaults) ((stream_state *) st);
942             (*templat->init) ((stream_state *) st);
943             pdev->strm = s = es;
944         }
945     }
946     /*
947      * Scale the coordinate system.  Use an extra level of q/Q for the
948      * sake of poorly designed PDF tools that assume that the contents
949      * stream restores the CTM.
950      */
951     pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
952              72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
953     if (pdev->CompatibilityLevel >= 1.3) {
954         /* Set the default rendering intent. */
955         if (pdev->params.DefaultRenderingIntent != ri_Default) {
956             static const char *const ri_names[] = { psdf_ri_names };
957 
958             pprints1(s, "/%s ri\n",
959                      ri_names[(int)pdev->params.DefaultRenderingIntent]);
960         }
961     }
962     pdev->AR4_save_bug = false;
963     return PDF_IN_STREAM;
964 }
965 /* Enter text context from stream context. */
966 static int
stream_to_text(gx_device_pdf * pdev)967 stream_to_text(gx_device_pdf * pdev)
968 {
969     int code;
970 
971     /*
972      * Bizarrely enough, Acrobat Reader cares how the final font size is
973      * obtained -- the CTM (cm), text matrix (Tm), and font size (Tf)
974      * are *not* all equivalent.  In particular, it seems to use the
975      * product of the text matrix and font size to decide how to
976      * anti-alias characters.  Therefore, we have to temporarily patch
977      * the CTM so that the scale factors are unity.  What a nuisance!
978      */
979     code = pdf_save_viewer_state(pdev, pdev->strm);
980     if (code < 0)
981         return 0;
982     pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
983              pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
984     pdev->procsets |= Text;
985     code = pdf_from_stream_to_text(pdev);
986     return (code < 0 ? code : PDF_IN_TEXT);
987 }
988 /* Exit string context to text context. */
989 static int
string_to_text(gx_device_pdf * pdev)990 string_to_text(gx_device_pdf * pdev)
991 {
992     int code = pdf_from_string_to_text(pdev);
993 
994     return (code < 0 ? code : PDF_IN_TEXT);
995 }
996 /* Exit text context to stream context. */
997 static int
text_to_stream(gx_device_pdf * pdev)998 text_to_stream(gx_device_pdf * pdev)
999 {
1000     int code;
1001 
1002     stream_puts(pdev->strm, "ET\n");
1003     code = pdf_restore_viewer_state(pdev, pdev->strm);
1004     if (code < 0)
1005         return code;
1006     pdf_reset_text(pdev);	/* because of Q */
1007     return PDF_IN_STREAM;
1008 }
1009 /* Exit stream context. */
1010 static int
stream_to_none(gx_device_pdf * pdev)1011 stream_to_none(gx_device_pdf * pdev)
1012 {
1013     stream *s = pdev->strm;
1014     long length;
1015     int code;
1016 
1017     if (pdev->ResourcesBeforeUsage) {
1018         int code = pdf_exit_substream(pdev);
1019 
1020         if (code < 0)
1021             return code;
1022     } else {
1023         if (pdev->vgstack_depth) {
1024             code = pdf_restore_viewer_state(pdev, s);
1025             if (code < 0)
1026                 return code;
1027         }
1028         if (pdev->compression_at_page_start == pdf_compress_Flate) {	/* Terminate the filters. */
1029             stream *fs = s->strm;
1030 
1031             if (!pdev->binary_ok) {
1032                 sclose(s);	/* Terminate the ASCII85 filter. */
1033                 gs_free_object(pdev->pdf_memory, s->cbuf, "A85E contents buffer");
1034                 gs_free_object(pdev->pdf_memory, s, "A85E contents stream");
1035                 pdev->strm = s = fs;
1036                 fs = s->strm;
1037             }
1038             sclose(s);		/* Next terminate the compression filter */
1039             gs_free_object(pdev->pdf_memory, s->cbuf, "zlib buffer");
1040             gs_free_object(pdev->pdf_memory, s, "zlib stream");
1041             pdev->strm = fs;
1042         }
1043         pdf_end_encrypt(pdev);
1044         s = pdev->strm;
1045         length = pdf_stell(pdev) - pdev->contents_pos;
1046         if (pdev->PDFA != 0)
1047             stream_puts(s, "\n");
1048         stream_puts(s, "endstream\n");
1049         pdf_end_obj(pdev, resourceStream);
1050         pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1051         pprintld1(s, "%ld\n", length);
1052         pdf_end_obj(pdev, resourceLength);
1053     }
1054     return PDF_IN_NONE;
1055 }
1056 
1057 /* Begin a page contents part. */
1058 int
pdf_open_contents(gx_device_pdf * pdev,pdf_context_t context)1059 pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1060 {
1061     int (*proc) (gx_device_pdf *);
1062 
1063     while ((proc = context_procs[pdev->context][context]) != 0) {
1064         int code = (*proc) (pdev);
1065 
1066         if (code < 0)
1067             return code;
1068         pdev->context = (pdf_context_t) code;
1069     }
1070     pdev->context = context;
1071     return 0;
1072 }
1073 
1074 /* Close the current contents part if we are in one. */
1075 int
pdf_close_contents(gx_device_pdf * pdev,bool last)1076 pdf_close_contents(gx_device_pdf * pdev, bool last)
1077 {
1078     if (pdev->context == PDF_IN_NONE)
1079         return 0;
1080     if (last) {			/* Exit from the clipping path gsave. */
1081         int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1082 
1083         if (code < 0)
1084             return code;
1085         stream_puts(pdev->strm, "Q\n");	/* See none_to_stream. */
1086         pdf_close_text_contents(pdev);
1087     }
1088     return pdf_open_contents(pdev, PDF_IN_NONE);
1089 }
1090 
1091 /* ------ Resources et al ------ */
1092 
1093 /* Define the allocator descriptors for the resource types. */
1094 const char *const pdf_resource_type_names[] = {
1095     PDF_RESOURCE_TYPE_NAMES
1096 };
1097 const gs_memory_struct_type_t *const pdf_resource_type_structs[] = {
1098     PDF_RESOURCE_TYPE_STRUCTS
1099 };
1100 
1101 /* Cancel a resource (do not write it into PDF). */
1102 int
pdf_cancel_resource(gx_device_pdf * pdev,pdf_resource_t * pres,pdf_resource_type_t rtype)1103 pdf_cancel_resource(gx_device_pdf * pdev, pdf_resource_t *pres, pdf_resource_type_t rtype)
1104 {
1105     /* fixme : Remove *pres from resource chain. */
1106     pres->where_used = 0;
1107     if (pres->object) {
1108         pres->object->written = true;
1109         if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1110             || rtype > NUM_RESOURCE_TYPES) {
1111             int code = cos_stream_release_pieces((cos_stream_t *)pres->object);
1112 
1113             if (code < 0)
1114                 return code;
1115         }
1116         cos_release(pres->object, "pdf_cancel_resource");
1117         gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1118         pres->object = 0;
1119     }
1120     return 0;
1121 }
1122 
1123 /* Remove a resource. */
1124 void
pdf_forget_resource(gx_device_pdf * pdev,pdf_resource_t * pres1,pdf_resource_type_t rtype)1125 pdf_forget_resource(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1126 {   /* fixme : optimize. */
1127     pdf_resource_t **pchain = pdev->resources[rtype].chains;
1128     pdf_resource_t *pres;
1129     pdf_resource_t **pprev = &pdev->last_resource;
1130     int i;
1131 
1132     for (; (pres = *pprev) != 0; pprev = &pres->prev)
1133         if (pres == pres1) {
1134             *pprev = pres->prev;
1135             break;
1136         }
1137     for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1138         pprev = pchain + i;
1139         for (; (pres = *pprev) != 0; pprev = &pres->next)
1140             if (pres == pres1) {
1141                 *pprev = pres->next;
1142                 if (pres->object) {
1143                     COS_RELEASE(pres->object, "pdf_forget_resource");
1144                     gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1145                     pres->object = 0;
1146                 }
1147                 gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1148                 break;
1149             }
1150     }
1151 }
1152 
1153 static int
nocheck(gx_device_pdf * pdev,pdf_resource_t * pres0,pdf_resource_t * pres1)1154 nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1155 {
1156     return 1;
1157 }
1158 
1159 /* Substitute a resource with a same one. */
1160 int
pdf_substitute_resource(gx_device_pdf * pdev,pdf_resource_t ** ppres,pdf_resource_type_t rtype,int (* eq)(gx_device_pdf * pdev,pdf_resource_t * pres0,pdf_resource_t * pres1),bool write)1161 pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
1162         pdf_resource_type_t rtype,
1163         int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
1164         bool write)
1165 {
1166     pdf_resource_t *pres1 = *ppres;
1167     int code;
1168 
1169     code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1170     if (code < 0)
1171         return code;
1172     if (code != 0) {
1173         code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1174         if (code < 0)
1175             return code;
1176         pdf_forget_resource(pdev, pres1, rtype);
1177         return 0;
1178     } else {
1179         pdf_reserve_object_id(pdev, pres1, gs_no_id);
1180         if (write) {
1181             code = cos_write_object(pres1->object, pdev, rtype);
1182             if (code < 0)
1183                 return code;
1184             pres1->object->written = 1;
1185         }
1186         return 1;
1187     }
1188 }
1189 
1190 /* Find a resource of a given type by gs_id. */
1191 pdf_resource_t *
pdf_find_resource_by_gs_id(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id rid)1192 pdf_find_resource_by_gs_id(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1193                            gs_id rid)
1194 {
1195     pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1196     pdf_resource_t **pprev = pchain;
1197     pdf_resource_t *pres;
1198 
1199     for (; (pres = *pprev) != 0; pprev = &pres->next)
1200         if (pres->rid == rid) {
1201             if (pprev != pchain) {
1202                 *pprev = pres->next;
1203                 pres->next = *pchain;
1204                 *pchain = pres;
1205             }
1206             return pres;
1207         }
1208     return 0;
1209 }
1210 
1211 /* Find resource by resource id. */
1212 pdf_resource_t *
pdf_find_resource_by_resource_id(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id id)1213 pdf_find_resource_by_resource_id(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id id)
1214 {
1215     pdf_resource_t **pchain = pdev->resources[rtype].chains;
1216     pdf_resource_t *pres;
1217     int i;
1218 
1219     for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1220         for (pres = pchain[i]; pres != 0; pres = pres->next) {
1221             if (pres->object->id == id)
1222                 return pres;
1223         }
1224     }
1225     return 0;
1226 }
1227 
1228 /* Find same resource. */
1229 int
pdf_find_same_resource(gx_device_pdf * pdev,pdf_resource_type_t rtype,pdf_resource_t ** ppres,int (* eq)(gx_device_pdf * pdev,pdf_resource_t * pres0,pdf_resource_t * pres1))1230 pdf_find_same_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, pdf_resource_t **ppres,
1231         int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1))
1232 {
1233     pdf_resource_t **pchain = pdev->resources[rtype].chains;
1234     pdf_resource_t *pres;
1235     cos_object_t *pco0 = (*ppres)->object;
1236     int i;
1237 
1238     for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1239         for (pres = pchain[i]; pres != 0; pres = pres->next) {
1240             if (*ppres != pres) {
1241                 int code;
1242                 cos_object_t *pco1 = pres->object;
1243 
1244                 if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1245                     continue;	    /* don't compare different types */
1246                 code = pco0->cos_procs->equal(pco0, pco1, pdev);
1247                 if (code < 0)
1248                     return code;
1249                 if (code > 0) {
1250                     code = eq(pdev, *ppres, pres);
1251                     if (code < 0)
1252                         return code;
1253                     if (code > 0) {
1254                         *ppres = pres;
1255                         return 1;
1256                     }
1257                 }
1258             }
1259         }
1260     }
1261     return 0;
1262 }
1263 
1264 /* Drop resources by a condition. */
1265 void
pdf_drop_resources(gx_device_pdf * pdev,pdf_resource_type_t rtype,int (* cond)(gx_device_pdf * pdev,pdf_resource_t * pres))1266 pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1267         int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1268 {
1269     pdf_resource_t **pchain = pdev->resources[rtype].chains;
1270     pdf_resource_t **pprev;
1271     pdf_resource_t *pres;
1272     int i;
1273 
1274     for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1275         pprev = pchain + i;
1276         for (; (pres = *pprev) != 0; ) {
1277             if (cond(pdev, pres)) {
1278                 *pprev = pres->next;
1279                 pres->next = pres; /* A temporary mark - see below */
1280             } else
1281                 pprev = &pres->next;
1282         }
1283     }
1284     pprev = &pdev->last_resource;
1285     for (; (pres = *pprev) != 0; )
1286         if (pres->next == pres) {
1287             *pprev = pres->prev;
1288             if (pres->object) {
1289                 COS_RELEASE(pres->object, "pdf_drop_resources");
1290                 gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1291                 pres->object = 0;
1292             }
1293             gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1294         } else
1295             pprev = &pres->prev;
1296 }
1297 
1298 /* Print resource statistics. */
1299 void
pdf_print_resource_statistics(gx_device_pdf * pdev)1300 pdf_print_resource_statistics(gx_device_pdf * pdev)
1301 {
1302 
1303     int rtype;
1304 
1305     for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1306         pdf_resource_t **pchain = pdev->resources[rtype].chains;
1307         pdf_resource_t *pres;
1308         const char *name = pdf_resource_type_names[rtype];
1309         int i, n = 0;
1310 
1311         for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1312             for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1313         }
1314         dprintf3("Resource type %d (%s) has %d instances.\n", rtype,
1315                 (name ? name : ""), n);
1316     }
1317 }
1318 
1319 /* Begin an object logically separate from the contents. */
1320 long
pdf_open_separate(gx_device_pdf * pdev,long id,pdf_resource_type_t type)1321 pdf_open_separate(gx_device_pdf * pdev, long id, pdf_resource_type_t type)
1322 {
1323     int code;
1324     code = pdfwrite_pdf_open_document(pdev);
1325     if (code < 0)
1326         return code;
1327     pdev->asides.save_strm = pdev->strm;
1328     pdev->strm = pdev->asides.strm;
1329     return pdf_open_obj(pdev, id, type);
1330 }
1331 long
pdf_begin_separate(gx_device_pdf * pdev,pdf_resource_type_t type)1332 pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1333 {
1334     return pdf_open_separate(pdev, 0L, type);
1335 }
1336 
1337 void
pdf_reserve_object_id(gx_device_pdf * pdev,pdf_resource_t * pres,long id)1338 pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, long id)
1339 {
1340     pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1341     sprintf(pres->rname, "R%ld", pres->object->id);
1342 }
1343 
1344 /* Begin an aside (resource, annotation, ...). */
1345 int
pdf_alloc_aside(gx_device_pdf * pdev,pdf_resource_t ** plist,const gs_memory_struct_type_t * pst,pdf_resource_t ** ppres,long id)1346 pdf_alloc_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1347                 const gs_memory_struct_type_t * pst, pdf_resource_t **ppres,
1348                 long id)
1349 {
1350     pdf_resource_t *pres;
1351     cos_object_t *object;
1352 
1353     if (pst == NULL)
1354         pst = &st_pdf_resource;
1355     pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1356                            "pdf_alloc_aside(resource)");
1357     if (pres == 0)
1358         return_error(gs_error_VMerror);
1359     object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1360     if (object == 0)
1361         return_error(gs_error_VMerror);
1362     memset(pres + 1, 0, pst->ssize - sizeof(*pres));
1363     pres->object = object;
1364     if (id < 0) {
1365         object->id = -1L;
1366         pres->rname[0] = 0;
1367     } else
1368         pdf_reserve_object_id(pdev, pres, id);
1369     pres->next = *plist;
1370     pres->rid = 0;
1371     *plist = pres;
1372     pres->prev = pdev->last_resource;
1373     pdev->last_resource = pres;
1374     pres->named = false;
1375     pres->global = false;
1376     pres->where_used = pdev->used_mask;
1377     *ppres = pres;
1378     return 0;
1379 }
1380 int
pdf_begin_aside(gx_device_pdf * pdev,pdf_resource_t ** plist,const gs_memory_struct_type_t * pst,pdf_resource_t ** ppres,pdf_resource_type_t type)1381 pdf_begin_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1382                 const gs_memory_struct_type_t * pst, pdf_resource_t ** ppres,
1383                 pdf_resource_type_t type)
1384 {
1385     long id = pdf_begin_separate(pdev, type);
1386 
1387     if (id < 0)
1388         return (int)id;
1389     return pdf_alloc_aside(pdev, plist, pst, ppres, id);
1390 }
1391 
1392 /* Begin a resource of a given type. */
1393 int
pdf_begin_resource_body(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id rid,pdf_resource_t ** ppres)1394 pdf_begin_resource_body(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1395                         gs_id rid, pdf_resource_t ** ppres)
1396 {
1397     int code;
1398 
1399     if (rtype > NUM_RESOURCE_TYPES)
1400         rtype = resourceOther;
1401 
1402     code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1403                                pdf_resource_type_structs[rtype], ppres, rtype);
1404 
1405     if (code >= 0)
1406         (*ppres)->rid = rid;
1407     return code;
1408 }
1409 int
pdf_begin_resource(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id rid,pdf_resource_t ** ppres)1410 pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1411                    pdf_resource_t ** ppres)
1412 {
1413     int code;
1414 
1415     if (rtype > NUM_RESOURCE_TYPES)
1416         rtype = resourceOther;
1417 
1418     code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1419 
1420     if (code >= 0 && pdf_resource_type_names[rtype] != 0) {
1421         stream *s = pdev->strm;
1422 
1423         pprints1(s, "<</Type%s", pdf_resource_type_names[rtype]);
1424         pprintld1(s, "/Name/R%ld", (*ppres)->object->id);
1425     }
1426     return code;
1427 }
1428 
1429 /* Allocate a resource, but don't open the stream. */
1430 int
pdf_alloc_resource(gx_device_pdf * pdev,pdf_resource_type_t rtype,gs_id rid,pdf_resource_t ** ppres,long id)1431 pdf_alloc_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1432                    pdf_resource_t ** ppres, long id)
1433 {
1434     int code;
1435 
1436     if (rtype > NUM_RESOURCE_TYPES)
1437         rtype = resourceOther;
1438 
1439     code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1440                                pdf_resource_type_structs[rtype], ppres, id);
1441 
1442     if (code >= 0)
1443         (*ppres)->rid = rid;
1444     return code;
1445 }
1446 
1447 /* Get the object id of a resource. */
1448 long
pdf_resource_id(const pdf_resource_t * pres)1449 pdf_resource_id(const pdf_resource_t *pres)
1450 {
1451     return pres->object->id;
1452 }
1453 
1454 /* End an aside or other separate object. */
1455 int
pdf_end_separate(gx_device_pdf * pdev,pdf_resource_type_t type)1456 pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1457 {
1458     int code = pdf_end_obj(pdev, type);
1459 
1460     pdev->strm = pdev->asides.save_strm;
1461     pdev->asides.save_strm = 0;
1462     return code;
1463 }
1464 int
pdf_end_aside(gx_device_pdf * pdev,pdf_resource_type_t type)1465 pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1466 {
1467     return pdf_end_separate(pdev, type);
1468 }
1469 
1470 /* End a resource. */
1471 int
pdf_end_resource(gx_device_pdf * pdev,pdf_resource_type_t type)1472 pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
1473 {
1474     return pdf_end_aside(pdev, type);
1475 }
1476 
1477 /*
1478  * Write the Cos objects for resources local to a content stream.  Formerly,
1479  * this procedure also freed such objects, but this doesn't work, because
1480  * resources of one type might refer to resources of another type.
1481  */
1482 int
pdf_write_resource_objects(gx_device_pdf * pdev,pdf_resource_type_t rtype)1483 pdf_write_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
1484 {
1485     int j, code = 0;
1486 
1487     for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
1488         pdf_resource_t *pres = pdev->resources[rtype].chains[j];
1489 
1490         for (; pres != 0; pres = pres->next)
1491             if ((!pres->named || pdev->ForOPDFRead)
1492                 && pres->object && !pres->object->written) {
1493                     code = cos_write_object(pres->object, pdev, rtype);
1494             }
1495     }
1496     return code;
1497 }
1498 
1499 /*
1500  * Reverse resource chains.
1501  * ps2write uses it with page resources.
1502  * Assuming only the 0th chain contauns something.
1503  */
1504 void
pdf_reverse_resource_chain(gx_device_pdf * pdev,pdf_resource_type_t rtype)1505 pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype)
1506 {
1507     pdf_resource_t *pres = pdev->resources[rtype].chains[0];
1508     pdf_resource_t *pres1, *pres0 = pres, *pres2;
1509 
1510     if (pres == NULL)
1511         return;
1512     pres1 = pres->next;
1513     for (;;) {
1514         if (pres1 == NULL)
1515             break;
1516         pres2 = pres1->next;
1517         pres1->next = pres;
1518         pres = pres1;
1519         pres1 = pres2;
1520     }
1521     pres0->next = NULL;
1522     pdev->resources[rtype].chains[0] = pres;
1523 }
1524 
1525 /*
1526  * Free unnamed Cos objects for resources local to a content stream,
1527  * since they can't be used again.
1528  */
1529 int
pdf_free_resource_objects(gx_device_pdf * pdev,pdf_resource_type_t rtype)1530 pdf_free_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
1531 {
1532     int j;
1533 
1534     for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
1535         pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
1536         pdf_resource_t *pres;
1537 
1538         while ((pres = *prev) != 0) {
1539             if (pres->named) {	/* named, don't free */
1540                 prev = &pres->next;
1541             } else {
1542                 if (pres->object) {
1543                     cos_free(pres->object, "pdf_free_resource_objects");
1544                     pres->object = 0;
1545                 }
1546                 *prev = pres->next;
1547             }
1548         }
1549     }
1550     return 0;
1551 }
1552 
1553 #ifdef DEPRECATED_906
1554 /* Write and free all resource objects. */
1555 
1556 int
pdf_write_and_free_all_resource_objects(gx_device_pdf * pdev)1557 pdf_write_and_free_all_resource_objects(gx_device_pdf *pdev)
1558 {
1559     int i, code = 0, code1;
1560 
1561     for (i = 0; i < NUM_RESOURCE_TYPES; ++i) {
1562         code1 = pdf_write_resource_objects(pdev, i);
1563         if (code >= 0)
1564             code = code1;
1565     }
1566     code1 = pdf_finish_resources(pdev, resourceFontDescriptor,
1567                         pdf_release_FontDescriptor_components);
1568     if (code >= 0)
1569         code = code1;
1570     for (i = 0; i < NUM_RESOURCE_TYPES; ++i) {
1571         code1 = pdf_free_resource_objects(pdev, i);
1572         if (code >= 0)
1573             code = code1;
1574     }
1575     return code;
1576 }
1577 #endif
1578 
1579 /*
1580  * Store the resource sets for a content stream (page or XObject).
1581  * Sets page->{procsets, resource_ids[]}.
1582  */
1583 int
pdf_store_page_resources(gx_device_pdf * pdev,pdf_page_t * page,bool clear_usage)1584 pdf_store_page_resources(gx_device_pdf *pdev, pdf_page_t *page, bool clear_usage)
1585 {
1586     int i;
1587 
1588     /* Write any resource dictionaries. */
1589 
1590     for (i = 0; i <= resourceFont; ++i) {
1591         stream *s = 0;
1592         int j;
1593 
1594         if (i == resourceOther || i > NUM_RESOURCE_TYPES)
1595             continue;
1596         page->resource_ids[i] = 0;
1597         for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
1598             pdf_resource_t *pres = pdev->resources[i].chains[j];
1599 
1600             for (; pres != 0; pres = pres->next) {
1601                 if (pres->where_used & pdev->used_mask) {
1602                     long id = pdf_resource_id(pres);
1603 
1604                     if (id == -1L)
1605                         continue;
1606                     if (s == 0) {
1607                         page->resource_ids[i] = pdf_begin_separate(pdev, i);
1608                         s = pdev->strm;
1609                         stream_puts(s, "<<");
1610                     }
1611                     pprints1(s, "/%s\n", pres->rname);
1612                     pprintld1(s, "%ld 0 R", id);
1613                     if (clear_usage)
1614                         pres->where_used -= pdev->used_mask;
1615                 }
1616             }
1617         }
1618         if (s) {
1619             stream_puts(s, ">>\n");
1620             pdf_end_separate(pdev, i);
1621             if (i != resourceFont)
1622                 pdf_write_resource_objects(pdev, i);
1623         }
1624     }
1625     page->procsets = pdev->procsets;
1626     return 0;
1627 }
1628 
1629 /* Copy data from a temporary file to a stream. */
1630 void
pdf_copy_data(stream * s,FILE * file,long count,stream_arcfour_state * ss)1631 pdf_copy_data(stream *s, FILE *file, long count, stream_arcfour_state *ss)
1632 {
1633     long r, left = count, code;
1634     byte buf[sbuf_size];
1635 
1636     while (left > 0) {
1637         uint copy = min(left, sbuf_size);
1638 
1639         r = fread(buf, 1, copy, file);
1640         if (r < 1) {
1641             code = gs_note_error(gs_error_ioerror);
1642             return;
1643         }
1644         if (ss)
1645             s_arcfour_process_buffer(ss, buf, copy);
1646         stream_write(s, buf, copy);
1647         left -= copy;
1648     }
1649 }
1650 
1651 /* Copy data from a temporary file to a stream,
1652    which may be targetted to the same file. */
1653 void
pdf_copy_data_safe(stream * s,FILE * file,int64_t position,long count)1654 pdf_copy_data_safe(stream *s, FILE *file, int64_t position, long count)
1655 {
1656     long r, left = count, code;
1657 
1658     while (left > 0) {
1659         byte buf[sbuf_size];
1660         long copy = min(left, (long)sbuf_size);
1661         int64_t end_pos = gp_ftell_64(file);
1662 
1663         gp_fseek_64(file, position + count - left, SEEK_SET);
1664         r = fread(buf, 1, copy, file);
1665         if (r < 1) {
1666             code = gs_note_error(gs_error_ioerror);
1667             return;
1668         }
1669         gp_fseek_64(file, end_pos, SEEK_SET);
1670         stream_write(s, buf, copy);
1671         sflush(s);
1672         left -= copy;
1673     }
1674 }
1675 
1676 /* ------ Pages ------ */
1677 
1678 /* Get or assign the ID for a page. */
1679 /* Returns 0 if the page number is out of range. */
1680 long
pdf_page_id(gx_device_pdf * pdev,int page_num)1681 pdf_page_id(gx_device_pdf * pdev, int page_num)
1682 {
1683     cos_dict_t *Page;
1684 
1685     if (page_num < 1)
1686         return 0;
1687     if (page_num >= pdev->num_pages) {	/* Grow the pages array. */
1688         uint new_num_pages =
1689             max(page_num + 10, pdev->num_pages << 1);
1690         pdf_page_t *new_pages =
1691             gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
1692                              "pdf_page_id(resize pages)");
1693 
1694         if (new_pages == 0)
1695             return 0;
1696         memset(&new_pages[pdev->num_pages], 0,
1697                (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
1698         pdev->pages = new_pages;
1699         pdev->num_pages = new_num_pages;
1700     }
1701     if ((Page = pdev->pages[page_num - 1].Page) == 0) {
1702         pdev->pages[page_num - 1].Page = Page =
1703             cos_dict_alloc(pdev, "pdf_page_id");
1704         Page->id = pdf_obj_forward_ref(pdev);
1705     }
1706     return Page->id;
1707 }
1708 
1709 /* Get the page structure for the current page. */
1710 pdf_page_t *
pdf_current_page(gx_device_pdf * pdev)1711 pdf_current_page(gx_device_pdf *pdev)
1712 {
1713     return &pdev->pages[pdev->next_page];
1714 }
1715 
1716 /* Get the dictionary object for the current page. */
1717 cos_dict_t *
pdf_current_page_dict(gx_device_pdf * pdev)1718 pdf_current_page_dict(gx_device_pdf *pdev)
1719 {
1720     if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
1721         return 0;
1722     return pdev->pages[pdev->next_page].Page;
1723 }
1724 
1725 /* Write saved page- or document-level information. */
1726 int
pdf_write_saved_string(gx_device_pdf * pdev,gs_string * pstr)1727 pdf_write_saved_string(gx_device_pdf * pdev, gs_string * pstr)
1728 {
1729     if (pstr->data != 0) {
1730         stream_write(pdev->strm, pstr->data, pstr->size);
1731         gs_free_string(pdev->pdf_memory, pstr->data, pstr->size,
1732                        "pdf_write_saved_string");
1733         pstr->data = 0;
1734     }
1735     return 0;
1736 }
1737 
1738 /* Open a page for writing. */
1739 int
pdf_open_page(gx_device_pdf * pdev,pdf_context_t context)1740 pdf_open_page(gx_device_pdf * pdev, pdf_context_t context)
1741 {
1742     if (!is_in_page(pdev)) {
1743         int code;
1744 
1745         if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
1746             return_error(gs_error_VMerror);
1747         code = pdfwrite_pdf_open_document(pdev);
1748         if (code < 0)
1749             return code;
1750     }
1751     /* Note that context may be PDF_IN_NONE here. */
1752     return pdf_open_contents(pdev, context);
1753 }
1754 
1755 /*  Go to the unclipped stream context. */
1756 int
pdf_unclip(gx_device_pdf * pdev)1757 pdf_unclip(gx_device_pdf * pdev)
1758 {
1759     const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
1760     /* When ResourcesBeforeUsage != 0, one sbstack element
1761        appears from the page contents stream. */
1762 
1763     if (pdev->sbstack_depth <= bottom) {
1764         int code = pdf_open_page(pdev, PDF_IN_STREAM);
1765 
1766         if (code < 0)
1767             return code;
1768     }
1769     if (pdev->context > PDF_IN_STREAM) {
1770         int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1771 
1772         if (code < 0)
1773             return code;
1774     }
1775     if (pdev->vgstack_depth > pdev->vgstack_bottom) {
1776         int code = pdf_restore_viewer_state(pdev, pdev->strm);
1777 
1778         if (code < 0)
1779             return code;
1780         code = pdf_remember_clip_path(pdev, NULL);
1781         if (code < 0)
1782             return code;
1783         pdev->clip_path_id = pdev->no_clip_path_id;
1784     }
1785     return 0;
1786 }
1787 
1788 /* ------ Miscellaneous output ------ */
1789 
1790 /* Generate the default Producer string. */
1791 void
pdf_store_default_Producer(char buf[PDF_MAX_PRODUCER])1792 pdf_store_default_Producer(char buf[PDF_MAX_PRODUCER])
1793 {
1794     if ((gs_revision % 100) == 0)
1795         sprintf(buf, "(%s %1.1f)", gs_product, gs_revision / 100.0);
1796     else
1797         sprintf(buf, "(%s %1.2f)", gs_product, gs_revision / 100.0);
1798 }
1799 
1800 /* Write matrix values. */
1801 void
pdf_put_matrix(gx_device_pdf * pdev,const char * before,const gs_matrix * pmat,const char * after)1802 pdf_put_matrix(gx_device_pdf * pdev, const char *before,
1803                const gs_matrix * pmat, const char *after)
1804 {
1805     stream *s = pdev->strm;
1806 
1807     if (before)
1808         stream_puts(s, before);
1809     pprintg6(s, "%g %g %g %g %g %g ",
1810              pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
1811     if (after)
1812         stream_puts(s, after);
1813 }
1814 
1815 /*
1816  * Write a name, with escapes for unusual characters.  Since we only support
1817  * PDF 1.2 and above, we can use an escape sequence for anything except a
1818  * null <00>, and the machinery for selecting the put_name_chars procedure
1819  * depending on CompatibilityLevel is no longer needed.
1820  */
1821 static int
pdf_put_name_chars_1_2(stream * s,const byte * nstr,uint size)1822 pdf_put_name_chars_1_2(stream *s, const byte *nstr, uint size)
1823 {
1824     uint i;
1825 
1826     for (i = 0; i < size; ++i) {
1827         uint c = nstr[i];
1828         char hex[4];
1829 
1830         switch (c) {
1831             default:
1832                 if (c >= 0x21 && c <= 0x7e) {
1833                     stream_putc(s, (byte)c);
1834                     break;
1835                 }
1836                 /* falls through */
1837             case '#':
1838             case '%':
1839             case '(': case ')':
1840             case '<': case '>':
1841             case '[': case ']':
1842             case '{': case '}':
1843             case '/':
1844                 sprintf(hex, "#%02x", c);
1845                 stream_puts(s, hex);
1846                 break;
1847             case 0:
1848                 stream_puts(s, "BnZr"); /* arbitrary */
1849         }
1850     }
1851     return 0;
1852 }
1853 pdf_put_name_chars_proc_t
pdf_put_name_chars_proc(const gx_device_pdf * pdev)1854 pdf_put_name_chars_proc(const gx_device_pdf *pdev)
1855 {
1856     return &pdf_put_name_chars_1_2;
1857 }
1858 int
pdf_put_name_chars(const gx_device_pdf * pdev,const byte * nstr,uint size)1859 pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
1860 {
1861     return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
1862 }
1863 int
pdf_put_name(const gx_device_pdf * pdev,const byte * nstr,uint size)1864 pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
1865 {
1866     stream_putc(pdev->strm, '/');
1867     return pdf_put_name_chars(pdev, nstr, size);
1868 }
1869 
1870 /* Write an encoded string with encryption. */
1871 static int
pdf_encrypt_encoded_string(const gx_device_pdf * pdev,const byte * str,uint size,gs_id object_id)1872 pdf_encrypt_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
1873 {
1874     stream sinp, sstr, sout;
1875     stream_PSSD_state st;
1876     stream_state so;
1877     byte buf[100], bufo[100];
1878     stream_arcfour_state sarc4;
1879 
1880     if (pdf_encrypt_init(pdev, object_id, &sarc4) < 0) {
1881         /* The interface can't pass an error. */
1882         stream_write(pdev->strm, str, size);
1883         return size;
1884     }
1885     s_init(&sinp, NULL);
1886     sread_string(&sinp, str + 1, size);
1887     s_init(&sstr, NULL);
1888     sstr.close_at_eod = false;
1889     s_init_state((stream_state *)&st, &s_PSSD_template, NULL);
1890     s_init_filter(&sstr, (stream_state *)&st, buf, sizeof(buf), &sinp);
1891     s_init(&sout, NULL);
1892     s_init_state(&so, &s_PSSE_template, NULL);
1893     s_init_filter(&sout, &so, bufo, sizeof(bufo), pdev->strm);
1894     stream_putc(pdev->strm, '(');
1895     for (;;) {
1896         uint n;
1897         int code = sgets(&sstr, buf, sizeof(buf), &n);
1898 
1899         if (n > 0) {
1900             s_arcfour_process_buffer(&sarc4, buf, n);
1901             stream_write(&sout, buf, n);
1902         }
1903         if (code == EOFC)
1904             break;
1905         if (code < 0 || n < sizeof(buf)) {
1906             /* The interface can't pass an error. */
1907             break;
1908         }
1909     }
1910     sclose(&sout); /* Writes ')'. */
1911     return stell(&sinp) + 1;
1912 }
1913 
1914 /* Write an encoded string with possible encryption. */
1915 static int
pdf_put_encoded_string(const gx_device_pdf * pdev,const byte * str,uint size,gs_id object_id)1916 pdf_put_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
1917 {
1918     if (!pdev->KeyLength || object_id == (gs_id)-1) {
1919         stream_write(pdev->strm, str, size);
1920         return 0;
1921     } else
1922         return pdf_encrypt_encoded_string(pdev, str, size, object_id);
1923 }
1924 /* Write an encoded string with possible encryption. */
1925 static int
pdf_put_encoded_string_as_hex(const gx_device_pdf * pdev,const byte * str,uint size,gs_id object_id)1926 pdf_put_encoded_string_as_hex(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
1927 {
1928     if (!pdev->KeyLength || object_id == (gs_id)-1) {
1929         int i, oct, width = 0;
1930         char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
1931 
1932         if (pdev->ForOPDFRead && pdev->ProduceDSC)
1933             stream_write(pdev->strm, "\n", 1);
1934         stream_write(pdev->strm, "<", 1);
1935         width++;
1936         for (i = 1; i < size - 1; i++) {
1937             if (str[i] == '\\') {
1938                 if (str[i + 1] >= '0' && str[i + 1] <= '9') {
1939                     oct = (str[i+1] - 0x30) * 64;
1940                     oct += (str[i+2] - 0x30) *8;
1941                     oct += str[i+3] - 0x30;
1942                     i+=3;
1943                 } else {
1944                     switch (str[++i]) {
1945                         case 'b' :
1946                             oct = 8;
1947                             break;
1948                         case 't' :
1949                             oct = 9;
1950                             break;
1951                         case 'n' :
1952                             oct = 10;
1953                             break;
1954                         case 'f' :
1955                             oct = 12;
1956                             break;
1957                         case 'r' :
1958                             oct = 13;
1959                             break;
1960                         default:
1961                             oct = str[i];
1962                             break;
1963                     }
1964                 }
1965                 if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
1966                     stream_write(pdev->strm, "\n", 1);
1967                     width = 0;
1968                 }
1969                 stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
1970                 stream_write(pdev->strm, &hex[oct & 0x0f], 1);
1971                 width += 2;
1972             } else {
1973                 if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
1974                     stream_write(pdev->strm, "\n", 1);
1975                     width = 0;
1976                 }
1977                 stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
1978                 stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
1979                 width += 2;
1980             }
1981         }
1982         stream_write(pdev->strm, ">", 1);
1983         if (pdev->ForOPDFRead && pdev->ProduceDSC)
1984             stream_write(pdev->strm, "\n", 1);
1985         return 0;
1986     } else
1987         return pdf_encrypt_encoded_string(pdev, str, size, object_id);
1988 }
1989 
1990 /* Write an encoded hexadecimal string with possible encryption. */
1991 static int
pdf_put_encoded_hex_string(const gx_device_pdf * pdev,const byte * str,uint size,gs_id object_id)1992 pdf_put_encoded_hex_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
1993 {
1994     emprintf(pdev->memory,
1995              "Unimplemented function : pdf_put_encoded_hex_string\n");
1996     stream_write(pdev->strm, str, size);
1997     return_error(gs_error_unregistered);
1998 }
1999 /*  Scan an item in a serialized array or dictionary.
2000     This is a very simplified Postscript lexical scanner.
2001     It assumes the serialization with pdf===only defined in gs/lib/gs_pdfwr.ps .
2002     We only need to select strings and encrypt them.
2003     Other items are passed identically.
2004     Note we don't reconstruct the nesting of arrays|dictionaries.
2005 */
2006 static int
pdf_scan_item(const gx_device_pdf * pdev,const byte * p,uint l,gs_id object_id)2007 pdf_scan_item(const gx_device_pdf * pdev, const byte * p, uint l, gs_id object_id)
2008 {
2009     const byte *q = p;
2010     int n = l;
2011 
2012     if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2013         return (l > 0 ? 1 : 0);
2014     for (q++, n--; n; q++, n--) {
2015         if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2016             return q - p;
2017         if (*q == '/' || *q == '[' || *q == ']' || *q == '{' || *q == '}' || *q == '(' || *q == '<')
2018             return q - p;
2019         /* Note : immediate names are not allowed in PDF. */
2020     }
2021     return l;
2022 }
2023 
2024 /* Write a serialized array or dictionary with possible encryption. */
2025 static int
pdf_put_composite(const gx_device_pdf * pdev,const byte * vstr,uint size,gs_id object_id)2026 pdf_put_composite(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2027 {
2028     if (!pdev->KeyLength || object_id == (gs_id)-1) {
2029         if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2030             stream_putc(pdev->strm, (byte)'\n');
2031             if (size > 255) {
2032                 const byte *start, *p, *end, *save;
2033                 int width = 0;
2034 
2035                 end = vstr + size;
2036                 start = p = vstr;
2037                 while (p < end) {
2038                     if(*p == '\r' || *p == '\n') {
2039                         width = 0;
2040                         p++;
2041                         continue;
2042                     }
2043                     if (width > 254) {
2044                         save = p;
2045                         /* search backwards for a point to split */
2046                         while (p > start) {
2047                             if (*p == '/' || *p == '[' || *p == '{' || *p == '(' || *p == ' ') {
2048                                 stream_write(pdev->strm, start, p - start);
2049                                 stream_putc(pdev->strm, (byte)'\n');
2050                                 start = p;
2051                             }
2052                             else p--;
2053                         }
2054                         if (p == start) {
2055                             stream_write(pdev->strm, start, save - start);
2056                             stream_putc(pdev->strm, (byte)'\n');
2057                             start = save;
2058                         }
2059                     } else {
2060                         width++;
2061                         p++;
2062                     }
2063                 }
2064             } else {
2065                 stream_write(pdev->strm, vstr, size);
2066             }
2067         } else {
2068             stream_write(pdev->strm, vstr, size);
2069         }
2070     } else {
2071         const byte *p = vstr;
2072         int l = size, n;
2073 
2074         for (;l > 0 ;) {
2075             if (*p == '(')
2076                 n = pdf_encrypt_encoded_string(pdev, p, l, object_id);
2077             else {
2078                 n = pdf_scan_item(pdev, p, l, object_id);
2079                 stream_write(pdev->strm, p, n);
2080             }
2081             l -= n;
2082             p += n;
2083         }
2084     }
2085     return 0;
2086 }
2087 
2088 /*
2089  * Write a string in its shortest form ( () or <> ).  Note that
2090  * this form is different depending on whether binary data are allowed.
2091  * We wish PDF supported ASCII85 strings ( <~ ~> ), but it doesn't.
2092  */
2093 int
pdf_put_string(const gx_device_pdf * pdev,const byte * str,uint size)2094 pdf_put_string(const gx_device_pdf * pdev, const byte * str, uint size)
2095 {
2096     psdf_write_string(pdev->strm, str, size,
2097                       (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2098     return 0;
2099 }
2100 
2101 /* Write a value, treating names specially. */
2102 int
pdf_write_value(const gx_device_pdf * pdev,const byte * vstr,uint size,gs_id object_id)2103 pdf_write_value(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2104 {
2105     if (size > 0 && vstr[0] == '/')
2106         return pdf_put_name(pdev, vstr + 1, size - 1);
2107     else if (size > 3 && vstr[0] == 0 && vstr[1] == 0 && vstr[size - 1] == 0)
2108         return pdf_put_name(pdev, vstr + 3, size - 4);
2109     else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2110         return pdf_put_composite(pdev, vstr, size, object_id);
2111     else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2112         return pdf_put_composite(pdev, vstr, size, object_id);
2113     else if (size > 1 && vstr[0] == '(') {
2114         if (pdev->ForOPDFRead)
2115             return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2116         else
2117             return pdf_put_encoded_string(pdev, vstr, size, object_id);
2118     }
2119     else if (size > 1 && vstr[0] == '<')
2120         return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2121     stream_write(pdev->strm, vstr, size);
2122     return 0;
2123 }
2124 
2125 /* Store filters for a stream. */
2126 /* Currently this only saves parameters for CCITTFaxDecode. */
2127 int
pdf_put_filters(cos_dict_t * pcd,gx_device_pdf * pdev,stream * s,const pdf_filter_names_t * pfn)2128 pdf_put_filters(cos_dict_t *pcd, gx_device_pdf *pdev, stream *s,
2129                 const pdf_filter_names_t *pfn)
2130 {
2131     const char *filter_name = 0;
2132     bool binary_ok = true;
2133     stream *fs = s;
2134     cos_dict_t *decode_parms = 0;
2135     int code;
2136 
2137     for (; fs != 0; fs = fs->strm) {
2138         const stream_state *st = fs->state;
2139         const stream_template *templat = st->templat;
2140 
2141 #define TEMPLATE_IS(atemp)\
2142   (templat->process == (atemp).process)
2143         if (TEMPLATE_IS(s_A85E_template))
2144             binary_ok = false;
2145         else if (TEMPLATE_IS(s_CFE_template)) {
2146             cos_param_list_writer_t writer;
2147             stream_CF_state cfs;
2148 
2149             decode_parms =
2150                 cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2151             if (decode_parms == 0)
2152                 return_error(gs_error_VMerror);
2153             CHECK(cos_param_list_writer_init(&writer, decode_parms, 0));
2154             /*
2155              * If EndOfBlock is true, we mustn't write a Rows value.
2156              * This is a hack....
2157              */
2158             cfs = *(const stream_CF_state *)st;
2159             if (cfs.EndOfBlock)
2160                 cfs.Rows = 0;
2161             CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2162             filter_name = pfn->CCITTFaxDecode;
2163         } else if (TEMPLATE_IS(s_DCTE_template))
2164             filter_name = pfn->DCTDecode;
2165         else if (TEMPLATE_IS(s_zlibE_template))
2166             filter_name = pfn->FlateDecode;
2167         else if (TEMPLATE_IS(s_LZWE_template))
2168             filter_name = pfn->LZWDecode;
2169 #ifdef USE_LDF_JB2
2170         else if (TEMPLATE_IS(s_jbig2encode_template))
2171             filter_name = pfn->JBIG2Decode;
2172 #endif
2173 #ifdef USE_LWF_JP2
2174         else if (TEMPLATE_IS(s_jpxe_template))
2175             filter_name = pfn->JPXDecode;
2176 #endif
2177         else if (TEMPLATE_IS(s_PNGPE_template)) {
2178             /* This is a predictor for FlateDecode or LZWEncode. */
2179             const stream_PNGP_state *const ss =
2180                 (const stream_PNGP_state *)st;
2181 
2182             decode_parms =
2183                 cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2184             if (decode_parms == 0)
2185                 return_error(gs_error_VMerror);
2186             CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2187                                          ss->Predictor));
2188             CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2189                                          ss->Columns));
2190             if (ss->Colors != 1)
2191                 CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2192                                              ss->Colors));
2193             if (ss->BitsPerComponent != 8)
2194                 CHECK(cos_dict_put_c_key_int(decode_parms,
2195                                              "/BitsPerComponent",
2196                                              ss->BitsPerComponent));
2197         } else if (TEMPLATE_IS(s_RLE_template))
2198             filter_name = pfn->RunLengthDecode;
2199 #undef TEMPLATE_IS
2200     }
2201     if (filter_name) {
2202         if (binary_ok) {
2203             CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2204             if (decode_parms)
2205                 CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2206                                                 COS_OBJECT(decode_parms)));
2207         } else {
2208             cos_array_t *pca =
2209                 cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2210 
2211             if (pca == 0)
2212                 return_error(gs_error_VMerror);
2213             CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2214             CHECK(cos_array_add_c_string(pca, filter_name));
2215             CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2216                                             COS_OBJECT(pca)));
2217             if (decode_parms) {
2218                 pca = cos_array_alloc(pdev,
2219                                       "pdf_put_image_filters(DecodeParms)");
2220                 if (pca == 0)
2221                     return_error(gs_error_VMerror);
2222                 CHECK(cos_array_add_c_string(pca, "null"));
2223                 CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2224                 CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2225                                                 COS_OBJECT(pca)));
2226             }
2227         }
2228     } else if (!binary_ok)
2229         CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2230     return 0;
2231 }
2232 
2233 /* Add a Flate compression filter to a binary writer. */
2234 static int
pdf_flate_binary(gx_device_pdf * pdev,psdf_binary_writer * pbw)2235 pdf_flate_binary(gx_device_pdf *pdev, psdf_binary_writer *pbw)
2236 {
2237     const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2238                     &s_LZWE_template : &s_zlibE_template);
2239     stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2240                                      "pdf_write_function");
2241 
2242     if (st == 0)
2243         return_error(gs_error_VMerror);
2244     if (templat->set_defaults)
2245         templat->set_defaults(st);
2246     return psdf_encode_binary(pbw, templat, st);
2247 }
2248 
2249 /*
2250  * Begin a data stream.  The client has opened the object and written
2251  * the << and any desired dictionary keys.
2252  */
2253 int
pdf_begin_data(gx_device_pdf * pdev,pdf_data_writer_t * pdw)2254 pdf_begin_data(gx_device_pdf *pdev, pdf_data_writer_t *pdw)
2255 {
2256     return pdf_begin_data_stream(pdev, pdw,
2257                                  DATA_STREAM_BINARY | DATA_STREAM_COMPRESS, 0);
2258 }
2259 
2260 int
pdf_append_data_stream_filters(gx_device_pdf * pdev,pdf_data_writer_t * pdw,int orig_options,gs_id object_id)2261 pdf_append_data_stream_filters(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2262                       int orig_options, gs_id object_id)
2263 {
2264     stream *s = pdev->strm;
2265     int options = orig_options;
2266 #define USE_ASCII85 1
2267 #define USE_FLATE 2
2268     static const char *const fnames[4] = {
2269         "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2270         "/Filter[/ASCII85Decode/FlateDecode]"
2271     };
2272     static const char *const fnames1_2[4] = {
2273         "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2274         "/Filter[/ASCII85Decode/LZWDecode]"
2275     };
2276     int filters = 0;
2277     int code;
2278 
2279     if (options & DATA_STREAM_COMPRESS) {
2280         filters |= USE_FLATE;
2281         options |= DATA_STREAM_BINARY;
2282     }
2283     if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2284         filters |= USE_ASCII85;
2285     if (!(options & DATA_STREAM_NOLENGTH)) {
2286         stream_puts(s, (pdev->CompatibilityLevel < 1.3 ?
2287             fnames1_2[filters] : fnames[filters]));
2288         if (pdev->ResourcesBeforeUsage) {
2289             pdw->length_pos = stell(s) + 8;
2290             stream_puts(s, "/Length             >>stream\n");
2291             pdw->length_id = -1;
2292         } else {
2293             pdw->length_pos = -1;
2294             pdw->length_id = pdf_obj_ref(pdev);
2295             pprintld1(s, "/Length %ld 0 R>>stream\n", pdw->length_id);
2296         }
2297     }
2298     if (options & DATA_STREAM_ENCRYPT) {
2299         code = pdf_begin_encrypt(pdev, &s, object_id);
2300         if (code < 0)
2301             return code;
2302         pdev->strm = s;
2303         pdw->encrypted = true;
2304     } else
2305         pdw->encrypted = false;
2306     if (options & DATA_STREAM_BINARY) {
2307         code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2308         if (code < 0)
2309             return code;
2310     } else {
2311         code = 0;
2312         pdw->binary.target = pdev->strm;
2313         pdw->binary.dev = (gx_device_psdf *)pdev;
2314         pdw->binary.strm = pdev->strm;
2315     }
2316     pdw->start = stell(s);
2317     if (filters & USE_FLATE)
2318         code = pdf_flate_binary(pdev, &pdw->binary);
2319     return code;
2320 #undef USE_ASCII85
2321 #undef USE_FLATE
2322 }
2323 
2324 int
pdf_begin_data_stream(gx_device_pdf * pdev,pdf_data_writer_t * pdw,int options,gs_id object_id)2325 pdf_begin_data_stream(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2326                       int options, gs_id object_id)
2327 {   int code;
2328     /* object_id is an unused rudiment from the old code,
2329        when the encription was applied when creating the stream.
2330        The new code encrypts than copying stream from the temporary file. */
2331     pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2332     pdw->binary.target = pdev->strm;
2333     pdw->binary.dev = (gx_device_psdf *)pdev;
2334     pdw->binary.strm = 0;		/* for GC in case of failure */
2335     code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2336                 options);
2337     if (object_id != 0)
2338         pdf_reserve_object_id(pdev, pdw->pres, object_id);
2339     pdw->binary.strm = pdev->strm;
2340     return code;
2341 }
2342 
2343 /* End a data stream. */
2344 int
pdf_end_data(pdf_data_writer_t * pdw)2345 pdf_end_data(pdf_data_writer_t *pdw)
2346 {   int code;
2347 
2348     code = pdf_close_aside(pdw->pdev);
2349     if (code < 0)
2350         return code;
2351     code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2352     if (code < 0)
2353         return code;
2354     return 0;
2355 }
2356 
2357 /* Create a Function object. */
2358 static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
2359                                const gs_function_info_t *pinfo);
2360 int
pdf_function_scaled(gx_device_pdf * pdev,const gs_function_t * pfn,const gs_range_t * pranges,cos_value_t * pvalue)2361 pdf_function_scaled(gx_device_pdf *pdev, const gs_function_t *pfn,
2362                     const gs_range_t *pranges, cos_value_t *pvalue)
2363 {
2364     if (pranges == NULL)
2365         return pdf_function(pdev, pfn, pvalue);
2366     {
2367         /*
2368          * Create a temporary scaled function.  Note that the ranges
2369          * represent the inverse scaling from what gs_function_make_scaled
2370          * expects.
2371          */
2372         gs_memory_t *mem = pdev->pdf_memory;
2373         gs_function_t *psfn;
2374         gs_range_t *ranges = (gs_range_t *)
2375             gs_alloc_byte_array(mem, pfn->params.n, sizeof(gs_range_t),
2376                                 "pdf_function_scaled");
2377         int i, code;
2378 
2379         if (ranges == 0)
2380             return_error(gs_error_VMerror);
2381         for (i = 0; i < pfn->params.n; ++i) {
2382             double rbase = pranges[i].rmin;
2383             double rdiff = pranges[i].rmax - rbase;
2384             double invbase = -rbase / rdiff;
2385 
2386             ranges[i].rmin = invbase;
2387             ranges[i].rmax = invbase + 1.0 / rdiff;
2388         }
2389         code = gs_function_make_scaled(pfn, &psfn, ranges, mem);
2390         if (code >= 0) {
2391             code = pdf_function(pdev, psfn, pvalue);
2392             gs_function_free(psfn, true, mem);
2393         }
2394         gs_free_object(mem, ranges, "pdf_function_scaled");
2395         return code;
2396     }
2397 }
2398 static int
pdf_function_aux(gx_device_pdf * pdev,const gs_function_t * pfn,pdf_resource_t ** ppres)2399 pdf_function_aux(gx_device_pdf *pdev, const gs_function_t *pfn,
2400              pdf_resource_t **ppres)
2401 {
2402     gs_function_info_t info;
2403     cos_param_list_writer_t rlist;
2404     pdf_resource_t *pres;
2405     cos_object_t *pcfn;
2406     cos_dict_t *pcd;
2407     int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2408 
2409     if (code < 0) {
2410         *ppres = 0;
2411         return code;
2412     }
2413     *ppres = pres;
2414     pcfn = pres->object;
2415     gs_function_get_info(pfn, &info);
2416     if (FunctionType(pfn) == function_type_ArrayedOutput) {
2417         /*
2418          * Arrayed Output Functions are used internally to represent
2419          * Shading Function entries that are arrays of Functions.
2420          * They require special handling.
2421          */
2422         cos_array_t *pca;
2423 
2424         cos_become(pcfn, cos_type_array);
2425         pca = (cos_array_t *)pcfn;
2426         return pdf_function_array(pdev, pca, &info);
2427     }
2428     if (info.DataSource != 0) {
2429         psdf_binary_writer writer;
2430         stream *save = pdev->strm;
2431         cos_stream_t *pcos;
2432         stream *s;
2433 
2434         cos_become(pcfn, cos_type_stream);
2435         pcos = (cos_stream_t *)pcfn;
2436         pcd = cos_stream_dict(pcos);
2437         s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
2438         if (s == 0)
2439             return_error(gs_error_VMerror);
2440         pdev->strm = s;
2441         code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
2442         if (code >= 0 && info.data_size > 30	/* 30 is arbitrary */
2443             )
2444             code = pdf_flate_binary(pdev, &writer);
2445         if (code >= 0) {
2446             static const pdf_filter_names_t fnames = {
2447                 PDF_FILTER_NAMES
2448             };
2449 
2450             code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
2451         }
2452         if (code >= 0) {
2453             byte buf[100];		/* arbitrary */
2454             ulong pos;
2455             uint count;
2456             const byte *ptr;
2457 
2458             for (pos = 0; pos < info.data_size; pos += count) {
2459                 count = min(sizeof(buf), info.data_size - pos);
2460                 data_source_access_only(info.DataSource, pos, count, buf,
2461                                         &ptr);
2462                 stream_write(writer.strm, ptr, count);
2463             }
2464             code = psdf_end_binary(&writer);
2465             sclose(s);
2466         }
2467         pdev->strm = save;
2468         if (code < 0)
2469             return code;
2470     } else {
2471         cos_become(pcfn, cos_type_dict);
2472         pcd = (cos_dict_t *)pcfn;
2473     }
2474     if (info.Functions != 0) {
2475         cos_array_t *functions =
2476             cos_array_alloc(pdev, "pdf_function(Functions)");
2477         cos_value_t v;
2478 
2479         if (functions == 0)
2480             return_error(gs_error_VMerror);
2481         if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
2482             (code = cos_dict_put_c_key(pcd, "/Functions",
2483                                        COS_OBJECT_VALUE(&v, functions))) < 0
2484             ) {
2485             COS_FREE(functions, "pdf_function(Functions)");
2486             return code;
2487         }
2488     }
2489     code = cos_param_list_writer_init(&rlist, pcd, PRINT_BINARY_OK);
2490     if (code < 0)
2491         return code;
2492     return gs_function_get_params(pfn, (gs_param_list *)&rlist);
2493 }
2494 static int
functions_equal(gx_device_pdf * pdev,pdf_resource_t * pres0,pdf_resource_t * pres1)2495 functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
2496 {
2497     return true;
2498 }
2499 int
pdf_function(gx_device_pdf * pdev,const gs_function_t * pfn,cos_value_t * pvalue)2500 pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
2501 {
2502     pdf_resource_t *pres;
2503     int code = pdf_function_aux(pdev, pfn, &pres);
2504 
2505     if (code < 0)
2506         return code;
2507     code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
2508     if (code < 0)
2509         return code;
2510     pres->where_used |= pdev->used_mask;
2511     COS_OBJECT_VALUE(pvalue, pres->object);
2512     return 0;
2513 }
pdf_function_array(gx_device_pdf * pdev,cos_array_t * pca,const gs_function_info_t * pinfo)2514 static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
2515                                const gs_function_info_t *pinfo)
2516 {
2517     int i, code = 0;
2518     cos_value_t v;
2519 
2520     for (i = 0; i < pinfo->num_Functions; ++i) {
2521         if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
2522             (code = cos_array_add(pca, &v)) < 0
2523             ) {
2524             break;
2525         }
2526     }
2527     return code;
2528 }
2529 
2530 /* Write a Function object. */
2531 int
pdf_write_function(gx_device_pdf * pdev,const gs_function_t * pfn,long * pid)2532 pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn, long *pid)
2533 {
2534     cos_value_t value;
2535     int code = pdf_function(pdev, pfn, &value);
2536 
2537     if (code < 0)
2538         return code;
2539     *pid = value.contents.object->id;
2540     return 0;
2541 }
2542 
2543 /* Write a FontBBox dictionary element. */
2544 int
pdf_write_font_bbox(gx_device_pdf * pdev,const gs_int_rect * pbox)2545 pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
2546 {
2547     stream *s = pdev->strm;
2548     /*
2549      * AR 4 doesn't like fonts with empty FontBBox, which
2550      * happens when the font contains only space characters.
2551      * Small bbox causes AR 4 to display a hairline. So we use
2552      * the full BBox.
2553      */
2554     int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
2555     int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
2556 
2557     pprintd4(s, "/FontBBox[%d %d %d %d]",
2558              pbox->p.x, pbox->p.y, x, y);
2559     return 0;
2560 }
2561 
2562 /* Write a FontBBox dictionary element using floats for the values. */
2563 int
pdf_write_font_bbox_float(gx_device_pdf * pdev,const gs_rect * pbox)2564 pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
2565 {
2566     stream *s = pdev->strm;
2567     /*
2568      * AR 4 doesn't like fonts with empty FontBBox, which
2569      * happens when the font contains only space characters.
2570      * Small bbox causes AR 4 to display a hairline. So we use
2571      * the full BBox.
2572      */
2573     float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
2574     float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
2575 
2576     pprintg4(s, "/FontBBox[%g %g %g %g]",
2577              pbox->p.x, pbox->p.y, x, y);
2578     return 0;
2579 }
2580