1 /*---------------------------------------------------------------------------*
2  |              PDFlib - A library for generating PDF on the fly             |
3  +---------------------------------------------------------------------------+
4  | Copyright (c) 1997-2006 Thomas Merz and PDFlib GmbH. All rights reserved. |
5  +---------------------------------------------------------------------------+
6  |                                                                           |
7  |    This software is subject to the PDFlib license. It is NOT in the       |
8  |    public domain. Extended versions and commercial licenses are           |
9  |    available, please check http://www.pdflib.com.                         |
10  |                                                                           |
11  *---------------------------------------------------------------------------*/
12 
13 /* $Id: pc_output.c,v 1.144.2.29 2009/10/23 11:28:02 stm Exp $
14  *
15  * PDFlib output routines
16  *
17  */
18 
19 #include "pc_util.h"
20 #include "pc_file.h"
21 #include "pc_md5.h"
22 
23 #if (defined(WIN32) || defined(OS2)) && !defined(WINCE)
24 #include <fcntl.h>
25 #include <io.h>
26 #endif
27 
28 /* for time_t, timer().
29 */
30 #ifndef WINCE
31 #include <time.h>
32 #else
33 #include <winbase.h>
34 #endif
35 
36 #if !defined(PDF_MAC_NOCORESERVICES) && (defined(MAC) || defined (MACOSX))
37 
38 /*
39  * Setting the file type requires either Carbon or InterfaceLib/Classic.
40  * If we have neither (i.e. a Mach-O build without Carbon) we suppress
41  * the code for setting the file type and creator.
42  */
43 
44 #if !defined(MACOSX) || (defined(PDF_TARGET_API_MAC_CARBON) && \
45      defined(PDF_ALLOW_MAC_DEPR_FUNCS))
46 #define PDF_FILETYPE_SUPPORTED
47 #include <Files.h>
48 #endif
49 
50 #endif /* !PDF_MAC_NOCORESERVICES && (MAC || MACOSX) */
51 
52 #ifdef HAVE_LIBZ
53 #include "zlib.h"
54 #endif
55 
56 #ifdef HAVE_LIBZ
57 #define PDF_DEFAULT_COMPRESSION	6		/* default zlib level */
58 #else
59 #define PDF_DEFAULT_COMPRESSION	0		/* no compression */
60 #endif
61 
62 #define STREAM_BUFSIZE	65536		/* initial output buffer size */
63 #define STREAM_MAXINCR	1048576		/* max. output buffer increment */
64 #define ID_CHUNKSIZE	2048		/* object ids */
65 
66 struct pdc_output_s {
67     pdc_core	*pdc;			/* core context */
68 
69     pdc_bool    open;                   /* file open */
70     pdc_byte	*basepos;		/* start of this chunk */
71     pdc_byte	*curpos;		/* next write position */
72     pdc_byte	*maxpos;		/* maximum position of chunk */
73     int		buf_incr;		/* current buffer increment */
74     pdc_off_t	base_offset;		/* base offset of this chunk */
75     pdc_bool	compressing;		/* in compression state */
76     pdc_flush_state flush;
77 #ifdef HAVE_LIBZ
78     z_stream	z;			/* zlib compression stream */
79 #endif
80 
81     FILE	*fp;			/* output file stream */
82 #if defined(MVS) || defined(MVS_TEST)
83     const char *fopenparams;            /* additional fopen() parameters */
84     int		recordsize;		/* file record size */
85 #endif
86 
87     /* client-supplied data sink procedure */
88     size_t	(*writeproc)(pdc_output *out, void *data, size_t size);
89 
90     int		compresslevel;		/* zlib compression level */
91     pdc_bool	compr_changed;		/* compress level has been changed */
92     pdc_off_t	length;			/* length of stream */
93 
94     pdc_off_t	*file_offset;		/* the objects' file offsets */
95     int		file_offset_capacity;
96     pdc_id	lastobj;		/* highest object id */
97 
98     pdc_off_t	start_pos;		/* stream start position */
99     pdc_off_t	xref_pos;		/* xref table start position */
100 
101     MD5_CTX	md5;			/* MD5 digest context for file ID */
102     unsigned char id[2][MD5_DIGEST_LENGTH];
103     void	*opaque;		/* this will be used to store PDF *p */
104 
105 
106     /* ------------- extended output control ------------- */
107 };
108 
109 /* ----------- service function to get PDF version string  -------------- */
110 
111 const char *
pdc_get_pdfversion(pdc_core * pdc,int compatibility)112 pdc_get_pdfversion(pdc_core *pdc, int compatibility)
113 {
114     return pdc_errprintf(pdc, "%d.%d", compatibility / 10, compatibility % 10);
115 }
116 
117 /* --------------------- PDFlib stream handling ----------------------- */
118 
119 void *
pdc_get_opaque(pdc_output * out)120 pdc_get_opaque(pdc_output *out)
121 {
122     return out->opaque;
123 }
124 
125 #ifdef HAVE_LIBZ
126 /* wrapper for pdc_malloc for use in zlib */
127 static voidpf
pdc_zlib_alloc(voidpf pdc,uInt items,uInt size)128 pdc_zlib_alloc(voidpf pdc, uInt items, uInt size)
129 {
130     return (voidpf) pdc_malloc((pdc_core *) pdc, items * size, "zlib");
131 }
132 
133 #endif	/* HAVE_LIBZ */
134 
135 pdc_bool
pdc_stream_not_empty(pdc_output * out)136 pdc_stream_not_empty(pdc_output *out)
137 {
138     return (!out->writeproc && out->curpos != out->basepos);
139 }
140 
141 char *
pdc_get_stream_contents(pdc_output * out,pdc_off_t * size)142 pdc_get_stream_contents(pdc_output *out, pdc_off_t *size)
143 {
144     pdc_core *pdc = out->pdc;
145 
146     if (out->writeproc)
147 	pdc_error(pdc, PDC_E_IO_NOBUFFER, 0, 0, 0, 0);
148 
149     if (size)
150 	*size = (pdc_off_t) (out->curpos - out->basepos);
151 
152     out->base_offset += (out->curpos - out->basepos);
153     out->curpos = out->basepos;
154 
155     return (char *) out->basepos;
156 }
157 
158 static size_t
pdc_writeproc_file(pdc_output * out,void * data,size_t size)159 pdc_writeproc_file(pdc_output *out, void *data, size_t size)
160 {
161     return pdc__fwrite(data, 1, size, out->fp);
162 }
163 
164 #if defined(_MSC_VER) && defined(_MANAGED)
165 #pragma unmanaged
166 #endif
167 static pdc_bool
pdc_init_stream(pdc_core * pdc,pdc_output * out,const char * filename,FILE * fp,size_t (* writeproc)(pdc_output * out,void * data,size_t size))168 pdc_init_stream(
169     pdc_core *pdc,
170     pdc_output *out,
171     const char *filename,
172     FILE *fp,
173     size_t (*writeproc)(pdc_output *out, void *data, size_t size))
174 {
175     static const char fn[] = "pdc_init_stream";
176 
177 #if (defined(MAC) || defined(MACOSX)) && defined(PDF_FILETYPE_SUPPORTED)
178 #if !defined(TARGET_API_MAC_CARBON) || defined(__MWERKS__)
179     FCBPBRec	fcbInfo;
180     Str32	name;
181 #endif	/* TARGET_API_MAC_CARBON */
182     FInfo	fInfo;
183     FSSpec	fSpec;
184 #endif  /* (MAC || MACOSX) && PDF_FILETYPE_SUPPORTED */
185 
186     /*
187      * This may be left over from the previous run. We deliberately
188      * don't reuse the previous buffer in order to avoid potentially
189      * unwanted growth of the allocated buffer due to a single large
190      * document in a longer series of documents.
191      */
192     if (out->basepos)
193 	pdc_free(pdc, (void *) out->basepos);
194 
195     out->basepos	= (pdc_byte *) pdc_malloc(pdc, STREAM_BUFSIZE, fn);
196     out->curpos		= out->basepos;
197     out->maxpos		= out->basepos + STREAM_BUFSIZE;
198     out->buf_incr	= STREAM_BUFSIZE;
199 
200     out->base_offset	= 0;
201     out->compressing	= pdc_false;
202 
203 #ifdef HAVE_LIBZ
204     /* zlib sometimes reads uninitialized memory where it shouldn't... */
205     memset(&out->z, 0, sizeof out->z);
206 
207     out->z.zalloc	= (alloc_func) pdc_zlib_alloc;
208     out->z.zfree	= (free_func) pdc_free;
209     out->z.opaque	= (voidpf) pdc;
210 
211     if (deflateInit(&out->z, pdc_get_compresslevel(out)) != Z_OK)
212 	pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateInit", 0, 0, 0);
213 
214     out->compr_changed = pdc_false;
215 #endif
216 
217     /* Defaults */
218     out->fp		= (FILE *) NULL;
219     out->writeproc	= pdc_writeproc_file;
220 
221     if (fp)
222     {
223 	out->fp	= fp;
224     }
225     else if (writeproc)
226     {
227 	out->writeproc	= writeproc;		/* PDF_open_mem */
228     }
229     else if (filename == NULL || *filename == '\0')
230     {
231 	/* PDF_open_file with in-core output */
232 	out->writeproc = NULL;
233     }
234     else
235     {
236 	/* PDF_open_file with file output */
237 #if !((defined(MAC) || defined (MACOSX)) && defined(__MWERKS__))
238 	if (filename && !strcmp(filename, "-"))
239         {
240 	    out->fp = stdout;
241 #if !defined(__MWERKS__) && (defined(WIN32) || defined(OS2))
242 #if !defined(__BORLANDC__) && !defined(OS2)
243 	    _setmode(_fileno(stdout), _O_BINARY);
244 #else
245 	    setmode(fileno(stdout), O_BINARY);
246 #endif /* !__BORLANDC__ && !OS2 */
247 #endif
248 	}
249         else
250         {
251 #endif /* !MAC */
252             char fopenparams[200]; /* sufficient */
253 
254 #if defined(MVS) || defined(MVS_TEST)
255             if (out->fopenparams != (char *) 0)
256             {
257                 strcpy(fopenparams, WRITEMODE);
258                 strcat(fopenparams, ",");
259                 strcat(fopenparams, out->fopenparams);
260             }
261             else if (out->recordsize <= 1)
262             {
263                 strcpy(fopenparams, WRITEMODE_V);
264             }
265             else
266             {
267                 strcpy(fopenparams, WRITEMODE);
268             }
269 #else
270             strcpy(fopenparams, WRITEMODE);
271 #endif
272 
273             out->fp = pdc_fopen_logg(out->pdc, filename, fopenparams);
274 	    if (out->fp == NULL)
275 		return pdc_false;
276 
277 #if (defined(MAC) || defined(MACOSX)) && defined(PDF_FILETYPE_SUPPORTED)
278             if (!pdc->ptfrun)
279             {
280                 /* set the proper type and creator for the output file */
281 #if TARGET_API_MAC_CARBON && !defined(__MWERKS__)
282 
283                 if (FSPathMakeFSSpec((const UInt8 *) filename, &fSpec) == noErr)
284                 {
285                     FSpGetFInfo(&fSpec, &fInfo);
286                     fInfo.fdType = 'PDF ';
287                     fInfo.fdCreator = 'CARO';
288                     FSpSetFInfo(&fSpec, &fInfo);
289                 }
290 
291 #else
292 
293                 memset(&fcbInfo, 0, sizeof(FCBPBRec));
294                 fcbInfo.ioRefNum = (short) out->fp->handle;
295                 fcbInfo.ioNamePtr = name;
296 
297                 if (!PBGetFCBInfoSync(&fcbInfo) &&
298                     FSMakeFSSpec(fcbInfo.ioFCBVRefNum, fcbInfo.ioFCBParID,
299                     name, &fSpec) == noErr)
300                 {
301                         FSpGetFInfo(&fSpec, &fInfo);
302                         fInfo.fdType = 'PDF ';
303                         fInfo.fdCreator = 'CARO';
304                         FSpSetFInfo(&fSpec, &fInfo);
305                 }
306 #endif  /* !defined(TARGET_API_MAC_CARBON) || defined(__MWERKS__) */
307             }
308 #endif	/* (MAC || MACOSX) && PDF_FILETYPE_SUPPORTED */
309 
310 #if !((defined(MAC) || defined (MACOSX)) && defined(__MWERKS__))
311 	}
312 #endif /* !MAC */
313     }
314 
315     return pdc_true;
316 }
317 #if defined(_MSC_VER) && defined(_MANAGED)
318 #pragma managed
319 #endif
320 
321 /* close the output file, if opened with PDF_open_file();
322  * close the output stream if opened
323  */
324 
325 static void
pdc_close_stream(pdc_output * out)326 pdc_close_stream(pdc_output *out)
327 {
328     /* this time we MUST flush the stream -
329     ** even if (flush == pdc_flush_none)
330     */
331     out->flush = pdc_flush_heavy;
332     pdc_flush_stream(out);
333 
334 #ifdef HAVE_LIBZ
335     /*
336      * This is delicate: we must ignore the return value because of the
337      * following reasoning: We are called in two situations:
338      * - end of document
339      * - exception
340      * In the first case compression is inactive, so deflateEnd() will
341      * fail only in the second case. However, when an exception occurs
342      * the document is definitely unusable, so we avoid recursive exceptions
343      * or an (unallowed) exception in PDF_delete().
344      */
345 
346     (void) deflateEnd(&out->z);
347 #endif
348 
349     /* close the output file if writing to file, but do not close the
350      * in-core output stream since the caller will have to
351      * fetch the buffer after PDF_close().
352      */
353     if (out->fp)
354     {
355         pdc_fclose_logg(out->pdc, out->fp);
356 
357 	/* mark fp as dead in case the error handler jumps in later */
358 	out->fp = NULL;
359     }
360 }
361 
362 static void
pdc_check_stream(pdc_output * out,size_t len)363 pdc_check_stream(pdc_output *out, size_t len)
364 {
365     size_t newsize;
366     size_t cur;
367     pdc_core *pdc = out->pdc;
368 
369     if (out->curpos + len <= out->maxpos)
370 	return;
371 
372     pdc_flush_stream(out);
373 
374     if (out->curpos + len <= out->maxpos)
375 	return;
376 
377     do
378     {
379 	out->maxpos += out->buf_incr;
380 
381 	if (out->buf_incr < STREAM_MAXINCR)
382 	    out->buf_incr *= 2;
383     } while (out->curpos + len > out->maxpos);
384 
385     cur = (size_t) (out->curpos - out->basepos);
386     newsize = (size_t) (out->maxpos - out->basepos);
387 
388     out->basepos = (pdc_byte *)
389 	pdc_realloc(pdc, (void *) out->basepos, newsize, "pdc_check_stream");
390     out->maxpos = out->basepos + newsize;
391     out->curpos = out->basepos + cur;
392 }
393 
394 void
pdc_flush_stream(pdc_output * out)395 pdc_flush_stream(pdc_output *out)
396 {
397     size_t size;
398     pdc_core *pdc = out->pdc;
399 
400     if (!out->writeproc || out->flush == pdc_flush_none)
401 	return;
402 
403     size = (size_t) (out->curpos - out->basepos);
404 
405     if (size == 0)
406 	return;
407 
408     if (out->writeproc(out, (void *) out->basepos, size) != size) {
409 	pdc_free(pdc, out->basepos);
410 	out->basepos = NULL;
411 	out->writeproc = NULL;
412 	pdc_error(pdc, PDC_E_IO_NOWRITE, 0, 0, 0, 0);
413     }
414 
415     out->base_offset += (out->curpos - out->basepos);
416     out->curpos = out->basepos;
417 }
418 
419 pdc_off_t
pdc_tell_out(pdc_output * out)420 pdc_tell_out(pdc_output *out)
421 {
422     return (out->base_offset + (out->curpos - out->basepos));
423 }
424 
425 /* --------------------- compression handling ----------------------- */
426 
427 static void
pdc_begin_compress(pdc_output * out)428 pdc_begin_compress(pdc_output *out)
429 {
430     pdc_core *pdc = out->pdc;
431 
432     if (!pdc_get_compresslevel(out)) {
433 	out->compressing = pdc_false;
434 	return;
435     }
436 
437 #ifdef HAVE_LIBZ
438     if (out->compr_changed)
439     {
440 	if (deflateEnd(&out->z) != Z_OK)
441 	    pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateEnd", 0, 0, 0);
442 
443 	if (deflateInit(&out->z, pdc_get_compresslevel(out)) != Z_OK)
444 	    pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateInit", 0, 0, 0);
445 
446 	out->compr_changed = pdc_false;
447     }
448     else
449     {
450 	if (deflateReset(&out->z) != Z_OK)
451 	    pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateReset", 0, 0, 0);
452     }
453 
454     out->z.avail_in = 0;
455 #endif /* HAVE_LIBZ */
456 
457     out->compressing = pdc_true;
458 }
459 
460 
461 static void
pdc_end_compress(pdc_output * out)462 pdc_end_compress(pdc_output *out)
463 {
464     int status;
465     pdc_core *pdc = out->pdc;
466 
467     /* this may happen during cleanup triggered by an exception handler */
468     if (!out->compressing)
469 	return;
470 
471     if (!pdc_get_compresslevel(out)) {
472 	out->compressing = pdc_false;
473 	return;
474     }
475 
476 
477 #ifdef HAVE_LIBZ
478     /* Finish the stream */
479     do {
480 	pdc_check_stream(out, 128);
481 	out->z.next_out = (Bytef *) out->curpos;
482 	out->z.avail_out = (uInt) (out->maxpos - out->curpos);
483 
484 	status = deflate(&(out->z), Z_FINISH);
485 	out->curpos = out->z.next_out;
486 
487 	if (status != Z_STREAM_END && status != Z_OK)
488 	    pdc_error(pdc, PDC_E_IO_COMPRESS, "Z_FINISH", 0, 0, 0);
489 
490     } while (status != Z_STREAM_END);
491 
492     out->compressing = pdc_false;
493 #endif /* HAVE_LIBZ */
494 }
495 
496 /* ---------------------- Low-level output function ---------------------- */
497 /*
498  * Write binary data to the output without any modification,
499  * and apply compression if we are currently in compression mode.
500  */
501 
502 
503 void
pdc_write(pdc_output * out,const void * data,size_t size)504 pdc_write(pdc_output *out, const void *data, size_t size)
505 {
506     int estimate = 0;
507     pdc_core *pdc = out->pdc;
508 
509 #ifdef HAVE_LIBZ
510     if (out->compressing) {
511 	out->z.avail_in		= (uInt) size;
512 	out->z.next_in		= (Bytef *) data;
513 	out->z.avail_out	= 0;
514 
515 	while (out->z.avail_in > 0) {
516 	    if (out->z.avail_out == 0) {
517 		/* estimate output buffer size */
518 		estimate = (int) (out->z.avail_in/4 + 16);
519 		pdc_check_stream(out, (size_t) estimate);
520 		out->z.next_out = (Bytef *) out->curpos;
521 		out->z.avail_out = (uInt) (out->maxpos - out->curpos);
522 	    }
523 
524 	    if (deflate(&(out->z), Z_NO_FLUSH) != Z_OK)
525 		pdc_error(pdc, PDC_E_IO_COMPRESS, "Z_NO_FLUSH", 0, 0, 0);
526 
527 	    out->curpos = out->z.next_out;
528 	}
529     } else {
530 #endif /* HAVE_LIBZ */
531 
532 	pdc_check_stream(out, size);
533 	memcpy(out->curpos, data, size);
534 	out->curpos += size;
535 
536 #ifdef HAVE_LIBZ
537     }
538 #endif /* HAVE_LIBZ */
539 }
540 
541 
542 /* --------------------------- Setup --------------------------- */
543 
544 pdc_output *
pdc_boot_output(pdc_core * pdc)545 pdc_boot_output(pdc_core *pdc)
546 {
547     static const char *fn = "pdc_boot_output";
548     pdc_output *out;
549 
550     out = (pdc_output*)pdc_malloc(pdc, sizeof(pdc_output), fn);
551     out->pdc = pdc;
552 
553     out->file_offset	= NULL;
554 
555     /* curpos must be initialized here so that the check for empty
556      * buffer in PDF_delete() also works in the degenerate case of
557      * no output produced.
558      */
559     out->basepos = out->curpos = NULL;
560 
561     out->open = pdc_false;
562 
563     return out;
564 }
565 
566 void
pdc_init_outctl(pdc_outctl * oc)567 pdc_init_outctl(pdc_outctl *oc)
568 {
569     oc->filename	= 0;
570     oc->fp		= 0;
571     oc->writeproc	= 0;
572     oc->flush		= pdc_flush_page;
573 #if defined(MVS) || defined(MVS_TEST)
574     oc->fopenparams     = 0;
575     oc->recordsize      = 0;
576 #endif
577 
578 
579 } /* pdc_init_outctl */
580 
581 /*
582  * Initialize the PDF output.
583  * Note that the caller is responsible for supplying sensible arguments.
584  */
585 #if defined(_MSC_VER) && defined(_MANAGED)
586 #pragma unmanaged
587 #endif
588 pdc_bool
pdc_init_output(void * opaque,pdc_output * out,int compatibility,pdc_outctl * oc)589 pdc_init_output(
590     void *opaque,
591     pdc_output *out,
592     int compatibility,
593     pdc_outctl *oc)
594 {
595     static const char *fn = "pdc_init_output";
596     pdc_core *pdc = out->pdc;
597     int i;
598 
599     pdc_cleanup_output(out, pdc_false);
600 
601     out->opaque		= opaque;
602 
603     out->lastobj	= 0;
604 #if defined(MVS) || defined(MVS_TEST)
605     out->fopenparams    = oc->fopenparams;
606     out->recordsize     = oc->recordsize;
607 #endif
608 
609     if (out->file_offset == NULL) {
610 	out->file_offset_capacity = ID_CHUNKSIZE;
611 
612 	out->file_offset = (pdc_off_t *) pdc_malloc(pdc,
613 		sizeof(pdc_off_t) * out->file_offset_capacity, fn);
614     }
615 
616     for (i = 1; i < out->file_offset_capacity; ++i)
617 	out->file_offset[i] = PDC_BAD_ID;
618 
619     out->compresslevel	= PDF_DEFAULT_COMPRESSION;
620     out->compr_changed	= pdc_false;
621     out->flush = oc->flush;
622 
623     memcpy(out->id[0], out->id[1], MD5_DIGEST_LENGTH);
624 
625 
626     if (!pdc_init_stream(pdc, out, oc->filename, oc->fp, oc->writeproc))
627 	return pdc_false;
628 
629 
630     {
631 	/* Write the document header */
632 	pdc_printf(out, "%%PDF-%s\n", pdc_get_pdfversion(pdc, compatibility));
633 
634 #define PDC_MAGIC_BINARY "\045\344\343\317\322\012"
635 	pdc_write(out, PDC_MAGIC_BINARY, sizeof(PDC_MAGIC_BINARY) - 1);
636     }
637 
638     out->open = pdc_true;
639 
640     return pdc_true;
641 }
642 #if defined(_MSC_VER) && defined(_MANAGED)
643 #pragma managed
644 #endif
645 
646 void
pdc_close_output(pdc_output * out)647 pdc_close_output(pdc_output *out)
648 {
649     if (out->open)
650     {
651         out->open = pdc_false;
652 
653         pdc_close_stream(out);
654 
655         if (out->file_offset)
656         {
657             pdc_free(out->pdc, out->file_offset);
658             out->file_offset = 0;
659         }
660 
661     }
662 }
663 
664 void
pdc_cleanup_output(pdc_output * out,pdc_bool keep_buf)665 pdc_cleanup_output(pdc_output *out, pdc_bool keep_buf)
666 {
667     pdc_core *pdc = out->pdc;
668 
669     if (out->file_offset)
670     {
671 	pdc_free(pdc, out->file_offset);
672 	out->file_offset = NULL;
673     }
674 
675 
676     if (!keep_buf && out->basepos)
677     {
678 	pdc_free(pdc, (void *) out->basepos);
679 	out->basepos = NULL;
680         out->curpos = NULL;
681     }
682 
683 }
684 
685 /* --------------------------- Digest --------------------------- */
686 
687 void
pdc_init_digest(pdc_output * out)688 pdc_init_digest(pdc_output *out)
689 {
690     MD5_Init(&out->md5);
691 }
692 
693 void
pdc_update_digest(pdc_output * out,unsigned char * input,unsigned int len)694 pdc_update_digest(pdc_output *out, unsigned char *input,
695     unsigned int len)
696 {
697     MD5_Update(&out->md5, input, len);
698 }
699 
700 void
pdc_finish_digest(pdc_output * out,pdc_bool settime)701 pdc_finish_digest(pdc_output *out, pdc_bool settime)
702 {
703     if (settime)
704     {
705         time_t timer;
706 
707         time(&timer);
708         MD5_Update(&out->md5, (unsigned char *) &timer, sizeof timer);
709     }
710 
711     MD5_Final(out->id[1], &out->md5);
712 }
713 
714 unsigned char *
pdc_get_digest(pdc_output * out)715 pdc_get_digest(pdc_output *out)
716 {
717     return out->id[1];
718 }
719 
720 /* --------------------------- Objects and ids --------------------------- */
721 
722 pdc_id
pdc_begin_obj(pdc_output * out,pdc_id obj_id)723 pdc_begin_obj(pdc_output *out, pdc_id obj_id)
724 {
725     if (obj_id == PDC_NEW_ID)
726 	obj_id = pdc_alloc_id(out);
727 
728     out->file_offset[obj_id] = pdc_tell_out(out);
729     pdc_printf(out, "%ld 0 obj\n", obj_id);
730 
731 
732     return obj_id;
733 }
734 
735 pdc_id
pdc_alloc_id(pdc_output * out)736 pdc_alloc_id(pdc_output *out)
737 {
738 
739 
740     out->lastobj++;
741 
742     if (out->lastobj > PDF_MAXINDOBJS)
743         pdc_error(out->pdc, PDC_E_INT_TOOMUCH_INDOBJS,
744                   pdc_errprintf(out->pdc, "%d", PDF_MAXINDOBJS), 0, 0, 0);
745 
746     if (out->lastobj >= out->file_offset_capacity) {
747 	out->file_offset_capacity *= 2;
748 	out->file_offset = (pdc_off_t *)
749 	    pdc_realloc(out->pdc, out->file_offset,
750 		sizeof(pdc_off_t) * out->file_offset_capacity, "pdc_alloc_id");
751     }
752 
753     /* only needed for verifying obj table in PDF_close() */
754     out->file_offset[out->lastobj] = PDC_BAD_ID;
755 
756     return out->lastobj;
757 }
758 
759 
760 /* --------------------------- Strings --------------------------- */
761 
762 void
pdc_put_pdfstring(pdc_output * out,const char * text,int len)763 pdc_put_pdfstring(pdc_output *out, const char *text, int len)
764 {
765     const unsigned char *goal, *s;
766 
767     if (!len)
768         len = (int) strlen(text);
769 
770     if (out->pdc->compatibility <= PDC_1_5 && len > PDF_MAXSTRINGSIZE)
771         pdc_error(out->pdc, PDC_E_INT_TOOLONG_TEXTSTR,
772                   pdc_errprintf(out->pdc, "%d", PDF_MAXSTRINGSIZE), 0, 0, 0);
773 
774 
775     pdc_putc(out, PDF_PARENLEFT);
776 
777     goal = (const unsigned char *) text + len;
778 
779     for (s = (const unsigned char *) text; s < goal; s++)
780     {
781 	switch (*s)
782         {
783 	    case PDF_RETURN:
784 		pdc_putc(out, PDF_BACKSLASH);
785 		pdc_putc(out, PDF_r);
786 		break;
787 
788             case PDF_NEWLINE:
789 		pdc_putc(out, PDF_BACKSLASH);
790 		pdc_putc(out, PDF_n);
791 		break;
792 
793 	    default:
794 		if (*s == PDF_PARENLEFT || *s == PDF_PARENRIGHT ||
795 		    *s == PDF_BACKSLASH)
796 		    pdc_putc(out, PDF_BACKSLASH);
797 		pdc_putc(out, (char) *s);
798 	}
799     }
800 
801     pdc_putc(out, PDF_PARENRIGHT);
802 }
803 
804 /* normalized file name according PDF specification */
805 void
pdc_put_pdffilename(pdc_output * out,const char * text,int len)806 pdc_put_pdffilename(pdc_output *out, const char *text, int len)
807 {
808     static const char *fn = "pdc_put_pdffilename";
809     pdc_byte *btext = (pdc_byte *) text;
810     pdc_bool isuni = pdc_is_utf16be_unicode(btext);
811     char *ttext;
812     pdc_byte c, cp, cpp;
813     int i, ia = 0, j = 0;
814 
815     ttext = (char *) pdc_calloc(out->pdc, (size_t) (len + 4), fn);
816 
817     if (isuni)
818     {
819         ttext[0] = PDF_BOM0;
820         ttext[1] = PDF_BOM1;
821         ia = 2;
822         j = 2;
823     }
824 
825     /* absolute path name:
826      * r:\pdfdocs\spec.pdf -> /r/pdfdocs/spec.pdf
827      * pclib/eng:\pdfdocs\spec.pdf -> /pclib/eng/pdfdocs/spec.pdf
828      */
829     cp = 0x7F;
830     for (i = ia; i < len; i++)
831     {
832         c = btext[i];
833         if (c == PDF_COLON && (!isuni || cp == 0))
834         {
835             if (isuni)
836             {
837                 ttext[j] = 0;
838                 j++;
839             }
840             ttext[j] = PDF_SLASH;
841             j++;
842 
843             break;
844         }
845         cp = c;
846     }
847 
848     cp = 0x7F;
849     cpp = 0x7F;
850     for (i = ia; i < len; i++)
851     {
852         c = btext[i];
853 
854         if ((c == PDF_BACKSLASH || c == PDF_SLASH || c == PDF_COLON) &&
855             (!isuni || cp == 0))
856         {
857             /* convert to slash, but avoid multiple slashes */
858             if (cpp != PDF_SLASH)
859             {
860                 c = PDF_SLASH;
861             }
862             else
863             {
864                 if (isuni)
865                     j--;
866                 continue;
867             }
868         }
869 
870         ttext[j] = c;
871         j++;
872 
873         cp = c;
874         if (c)
875             cpp = c;
876     }
877 
878     len = j;
879 
880     pdc_put_pdfstring(out, ttext, len);
881 
882     pdc_free(out->pdc, ttext);
883 }
884 
885 
886 /* --------------------------- Streams --------------------------- */
887 
888 void
pdc_begin_pdfstream(pdc_output * out)889 pdc_begin_pdfstream(pdc_output *out)
890 {
891     pdc_puts(out, "stream\n");
892 
893     out->start_pos = pdc_tell_out(out);
894 
895     if (out->compresslevel)
896 	pdc_begin_compress(out);
897 }
898 
899 void
pdc_end_pdfstream(pdc_output * out)900 pdc_end_pdfstream(pdc_output *out)
901 {
902     if (out->compresslevel)
903 	pdc_end_compress(out);
904 
905     out->length = pdc_tell_out(out) - out->start_pos;
906 
907     /* some PDF consumers seem to need the additional "\n" before "endstream",
908     ** the PDF reference allows it, and Acrobat's "repair" feature relies on it.
909     */
910     pdc_puts(out, "\nendstream\n");
911 }
912 
913 pdc_off_t
pdc_get_pdfstreamlength(pdc_output * out)914 pdc_get_pdfstreamlength(pdc_output *out)
915 {
916     return out->length;
917 }
918 
919 void
pdc_put_pdfstreamlength(pdc_output * out,pdc_id length_id)920 pdc_put_pdfstreamlength(pdc_output *out, pdc_id length_id)
921 {
922 
923     pdc_begin_obj(out, length_id);	/* Length object */
924     pdc_printf(out, "%lld\n", out->length);
925     pdc_end_obj(out);
926 }
927 
928 void
pdc_set_compresslevel(pdc_output * out,int compresslevel)929 pdc_set_compresslevel(pdc_output *out, int compresslevel)
930 {
931     out->compresslevel = compresslevel;
932     out->compr_changed = pdc_true;
933 }
934 
935 int
pdc_get_compresslevel(pdc_output * out)936 pdc_get_compresslevel(pdc_output *out)
937 {
938     return out->compresslevel;
939 }
940 
941 
942 /* --------------------------- Names --------------------------- */
943 
944 /* characters illegal in PDF names: "()<>[]{}/%#" */
945 #define PDF_ILL_IN_NAMES "\050\051\074\076\133\135\173\175\057\045\043"
946 
947 #define PDF_NEEDS_QUOTE(c) \
948 	((c) < 33 || (c) > 126 || strchr(PDF_ILL_IN_NAMES, (c)) != (char *) 0)
949 
950 void
pdc_put_pdfname(pdc_output * out,const char * text,size_t len)951 pdc_put_pdfname(pdc_output *out, const char *text, size_t len)
952 {
953     const unsigned char *goal, *s;
954     static const char BinToHex[] = PDF_STRING_0123456789ABCDEF;
955 
956     if (!len)
957         len = strlen(text);
958 
959     goal = (const unsigned char *) text + len;
960 
961     pdc_putc(out, PDF_SLASH);
962 
963     for (s = (const unsigned char *) text; s < goal; s++) {
964 	if (PDF_NEEDS_QUOTE(*s)) {
965 	    pdc_putc(out, PDF_HASH);
966 	    pdc_putc(out, BinToHex[*s >> 4]);	/* first nibble  */
967 	    pdc_putc(out, BinToHex[*s & 0x0F]);	/* second nibble  */
968 	} else
969 	    pdc_putc(out, (char) *s);
970     }
971 }
972 
973 /* --------------------------- Document sections  --------------------------- */
974 
975 void
pdc_mark_free(pdc_output * out,pdc_id obj_id)976 pdc_mark_free(pdc_output *out, pdc_id obj_id)
977 {
978     out->file_offset[obj_id] = PDC_FREE_ID;
979 }
980 
981 
982 void
pdc_write_xref(pdc_output * out)983 pdc_write_xref(pdc_output *out)
984 {
985     pdc_id	start = 1;
986     pdc_id	i;
987     pdc_id	free_id;
988     pdc_core *	pdc = out->pdc;
989 
990 
991 
992     /* Don't write any object after this check! */
993 
994     for (i = start; i <= out->lastobj; i++) {
995 	if (out->file_offset[i] == PDC_BAD_ID) {
996 	    pdc_warning(pdc, PDC_E_INT_UNUSEDOBJ,
997 		pdc_errprintf(pdc, "%ld", i), 0, 0, 0);
998 	    /* write a dummy object */
999 	    pdc_begin_obj(out, i);
1000 	    pdc_printf(out, "null %% unused object\n");
1001 	    pdc_end_obj(out);
1002 	}
1003     }
1004 
1005 
1006     out->xref_pos = pdc_tell_out(out);
1007     pdc_puts(out, "xref\n");
1008     pdc_printf(out, "0 %ld\n", out->lastobj + 1);
1009 
1010     /* find the last free entry in the xref table.
1011     */
1012     out->file_offset[0] = PDC_FREE_ID;
1013     for (free_id = out->lastobj;
1014 	out->file_offset[free_id] != PDC_FREE_ID;
1015 	--free_id)
1016 	;
1017 
1018     pdc_printf(out, "%010ld 65535 f \n", free_id);
1019     free_id = 0;
1020 
1021 #define PDF_FLUSH_AFTER_MANY_OBJS	3000    /* ca. 60 KB */
1022     for (i = 1; i <= out->lastobj; i++) {
1023 	/* Avoid spike in memory usage at the end of the document */
1024 	if (i % PDF_FLUSH_AFTER_MANY_OBJS == 0)
1025 	    pdc_flush_stream(out);
1026 
1027 	if (out->file_offset[i] == PDC_FREE_ID)
1028 	{
1029 	    pdc_printf(out, "%010ld 00001 f \n", free_id);
1030 	    free_id = i;
1031 	}
1032 	else
1033 	{
1034 	    pdc_printf(out, "%010lld 00000 n \n", out->file_offset[i]);
1035 	}
1036     }
1037 }
1038 
1039 void
pdc_write_digest(pdc_output * out)1040 pdc_write_digest(pdc_output *out)
1041 {
1042     static const char bin2hex[] = PDF_STRING_0123456789ABCDEF;
1043 
1044     int i;
1045 
1046     pdc_puts(out, "/ID[<");
1047     for (i = 0; i < MD5_DIGEST_LENGTH; ++i)
1048     {
1049 	pdc_putc(out, bin2hex[out->id[0][i] >> 4]);
1050 	pdc_putc(out, bin2hex[out->id[0][i] & 0x0F]);
1051     }
1052     pdc_puts(out, "><");
1053     for (i = 0; i < MD5_DIGEST_LENGTH; ++i)
1054     {
1055 	pdc_putc(out, bin2hex[out->id[1][i] >> 4]);
1056 	pdc_putc(out, bin2hex[out->id[1][i] & 0x0F]);
1057     }
1058     pdc_puts(out, ">]\n");
1059 }
1060 
1061 void
pdc_write_eof(pdc_output * out)1062 pdc_write_eof(pdc_output *out)
1063 {
1064 #if defined(MVS) || defined(MVS_TEST)
1065     int i, k;
1066 
1067     if (out->recordsize > 1)
1068     {
1069 	if ((i = (pdc_tell_out(out) + 6) % out->recordsize) != 0)
1070 	{
1071 	    for (k = 0; k < out->recordsize - i - 1; ++k)
1072 		pdc_putc(out, PDF_SPACE);
1073 
1074 	    pdc_putc(out, PDF_NEWLINE);
1075 	}
1076     }
1077 #endif /* MVS */
1078     pdc_puts(out, "%%EOF\n");
1079 }
1080 
1081 void
pdc_write_trailer(pdc_output * out,pdc_id info_id,pdc_id root_id,int root_gen,long xref_size,pdc_off_t xref_prev,pdc_off_t xref_pos)1082 pdc_write_trailer(
1083     pdc_output *out,
1084     pdc_id info_id,
1085     pdc_id root_id,
1086     int root_gen,
1087     long xref_size,
1088     pdc_off_t xref_prev,
1089     pdc_off_t xref_pos)
1090 {
1091     if (xref_size == -1)
1092     {
1093 	xref_size = out->lastobj + 1;
1094     }
1095 
1096     if (xref_pos == -1)
1097     {
1098 	xref_pos = out->xref_pos;
1099     }
1100 
1101     /* we've seen PDF consumers that need the linefeed...
1102     */
1103     pdc_puts(out, "trailer\n");
1104 
1105     pdc_begin_dict(out);				/* trailer */
1106     pdc_printf(out, "/Size %ld\n", xref_size);
1107 
1108     if (xref_prev != -1)
1109 	pdc_printf(out, "/Prev %lld\n", xref_prev);
1110 
1111     pdc_printf(out, "/Root %ld %d R\n", root_id, root_gen);
1112 
1113     if (info_id != PDC_BAD_ID)
1114 	pdc_printf(out, "/Info %ld 0 R\n", info_id);
1115 
1116     pdc_write_digest(out);
1117     pdc_end_dict(out);				/* trailer */
1118 
1119     pdc_puts(out, "startxref\n");
1120     pdc_printf(out, "%lld\n", xref_pos);
1121     pdc_write_eof(out);
1122 }
1123 
1124 /* ---------------------- High-level output functions ---------------------- */
1125 
1126 /*
1127  * Write a native encoded string to the output.
1128  */
1129 
1130 static void
pdc_puts_internal(pdc_output * out,char * s,pdc_bool tocopy)1131 pdc_puts_internal(pdc_output *out, char *s, pdc_bool tocopy)
1132 {
1133     char *scp = s;
1134     (void) tocopy;
1135 
1136     pdc_write(out, (void *) scp, strlen(scp));
1137 }
1138 
1139 void
pdc_puts(pdc_output * out,const char * s)1140 pdc_puts(pdc_output *out, const char *s)
1141 {
1142     pdc_puts_internal(out, (char *) s, pdc_true);
1143 }
1144 
1145 /*
1146  * Write a ASCII character to the output.
1147  */
1148 
1149 void
pdc_putc(pdc_output * out,const char c)1150 pdc_putc(pdc_output *out, const char c)
1151 {
1152     pdc_write(out, (void *) &c, (size_t) 1);
1153 }
1154 
1155 /*
1156  * Write a formatted string (native encoded) to the output.
1157  */
1158 
1159 void
pdc_printf(pdc_output * out,const char * fmt,...)1160 pdc_printf(pdc_output *out, const char *fmt, ...)
1161 {
1162     char buf[PDC_GEN_BUFSIZE];
1163     va_list ap;
1164 
1165     va_start(ap, fmt);
1166 
1167     pdc_vsprintf(out->pdc, pdc_true, buf, fmt, ap);
1168     pdc_puts_internal(out, buf, pdc_false);
1169 
1170     va_end(ap);
1171 }
1172 
1173