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