c434cad322
Files correlati : Ricompilazione Demo : [ ] Commento : Riportata la versione 3.1 patch 650 git-svn-id: svn://10.65.10.50/trunk@14148 c028cbd2-c16b-5b4b-a496-9718f37d4682
1091 lines
24 KiB
C
Executable File
1091 lines
24 KiB
C
Executable File
/*---------------------------------------------------------------------------*
|
|
| PDFlib - A library for generating PDF on the fly |
|
|
+---------------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2005 Thomas Merz and PDFlib GmbH. All rights reserved. |
|
|
+---------------------------------------------------------------------------+
|
|
| |
|
|
| This software is subject to the PDFlib license. It is NOT in the |
|
|
| public domain. Extended versions and commercial licenses are |
|
|
| available, please check http://www.pdflib.com. |
|
|
| |
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
/* $Id: pc_output.c,v 1.2 2006-07-11 13:10:33 alex Exp $
|
|
*
|
|
* PDFlib output routines
|
|
*
|
|
*/
|
|
|
|
#include "pc_util.h"
|
|
#include "pc_file.h"
|
|
#include "pc_md5.h"
|
|
|
|
#if (defined(WIN32) || defined(OS2)) && !defined(WINCE)
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
/* for time_t, timer().
|
|
*/
|
|
#ifndef WINCE
|
|
#include <time.h>
|
|
#else
|
|
#include <winbase.h>
|
|
#endif
|
|
|
|
#if defined(MAC) || defined (MACOSX)
|
|
|
|
/*
|
|
* Setting the file type requires either Carbon or InterfaceLib/Classic.
|
|
* If we have neither (i.e. a Mach-O build without Carbon) we suppress
|
|
* the code for setting the file type and creator.
|
|
*/
|
|
|
|
#if !defined(MACOSX) || defined(PDF_TARGET_API_MAC_CARBON)
|
|
#define PDF_FILETYPE_SUPPORTED
|
|
#endif
|
|
|
|
#ifdef PDF_FILETYPE_SUPPORTED
|
|
#include <Files.h>
|
|
#endif
|
|
|
|
#endif /* defined(MAC) || defined (MACOSX) */
|
|
|
|
#ifdef HAVE_LIBZ
|
|
#include "zlib.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBZ
|
|
#define PDF_DEFAULT_COMPRESSION 6 /* default zlib level */
|
|
#else
|
|
#define PDF_DEFAULT_COMPRESSION 0 /* no compression */
|
|
#endif
|
|
|
|
#define STREAM_CHUNKSIZE 65536 /* output buffer for in-core */
|
|
#define ID_CHUNKSIZE 2048 /* object ids */
|
|
|
|
struct pdc_output_s {
|
|
pdc_core *pdc; /* core context */
|
|
|
|
pdc_bool open; /* file open */
|
|
pdc_byte *basepos; /* start of this chunk */
|
|
pdc_byte *curpos; /* next write position */
|
|
pdc_byte *maxpos; /* maximum position of chunk */
|
|
pdc_off_t base_offset; /* base offset of this chunk */
|
|
pdc_bool compressing; /* in compression state */
|
|
#ifdef HAVE_LIBZ
|
|
z_stream z; /* zlib compression stream */
|
|
#endif
|
|
|
|
FILE *fp; /* output file stream */
|
|
#if defined(MVS) || defined(MVS_TEST)
|
|
int blocksize; /* file record size */
|
|
#endif
|
|
|
|
/* client-supplied data sink procedure */
|
|
size_t (*writeproc)(pdc_output *out, void *data, size_t size);
|
|
|
|
int compresslevel; /* zlib compression level */
|
|
pdc_bool compr_changed; /* compress level has been changed */
|
|
pdc_off_t length; /* length of stream */
|
|
|
|
pdc_off_t *file_offset; /* the objects' file offsets */
|
|
int file_offset_capacity;
|
|
pdc_id lastobj; /* highest object id */
|
|
|
|
pdc_off_t start_pos; /* stream start position */
|
|
pdc_off_t xref_pos; /* xref table start position */
|
|
|
|
MD5_CTX md5; /* MD5 digest context for file ID */
|
|
unsigned char id[2][MD5_DIGEST_LENGTH];
|
|
void *opaque; /* this will be used to store PDF *p */
|
|
};
|
|
|
|
/* --------------------- PDFlib stream handling ----------------------- */
|
|
|
|
void *
|
|
pdc_get_opaque(pdc_output *out)
|
|
{
|
|
return out->opaque;
|
|
}
|
|
|
|
#ifdef HAVE_LIBZ
|
|
/* wrapper for pdc_malloc for use in zlib */
|
|
static voidpf
|
|
pdc_zlib_alloc(voidpf pdc, uInt items, uInt size)
|
|
{
|
|
return (voidpf) pdc_malloc((pdc_core *) pdc, items * size, "zlib");
|
|
}
|
|
|
|
#endif /* HAVE_LIBZ */
|
|
|
|
pdc_bool
|
|
pdc_stream_not_empty(pdc_output *out)
|
|
{
|
|
return (!out->writeproc && out->curpos != out->basepos);
|
|
}
|
|
|
|
char *
|
|
pdc_get_stream_contents(pdc_output *out, pdc_off_t *size)
|
|
{
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
if (out->writeproc)
|
|
pdc_error(pdc, PDC_E_IO_NOBUFFER, 0, 0, 0, 0);
|
|
|
|
if (size)
|
|
*size = (pdc_off_t) (out->curpos - out->basepos);
|
|
|
|
out->base_offset += (out->curpos - out->basepos);
|
|
out->curpos = out->basepos;
|
|
|
|
return (char *) out->basepos;
|
|
}
|
|
|
|
static size_t
|
|
pdc_writeproc_file(pdc_output *out, void *data, size_t size)
|
|
{
|
|
return fwrite(data, 1, (size_t) size, out->fp);
|
|
}
|
|
|
|
#if defined(_MSC_VER) && defined(_MANAGED)
|
|
#pragma unmanaged
|
|
#endif
|
|
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))
|
|
{
|
|
static const char fn[] = "pdc_init_stream";
|
|
|
|
#if defined(MAC) || defined(MACOSX)
|
|
#if !defined(TARGET_API_MAC_CARBON) || defined(__MWERKS__)
|
|
FCBPBRec fcbInfo;
|
|
Str32 name;
|
|
#endif /* TARGET_API_MAC_CARBON */
|
|
FInfo fInfo;
|
|
FSSpec fSpec;
|
|
#endif /* defined(MAC) || defined(MACOSX) */
|
|
|
|
/*
|
|
* This may be left over from the previous run. We deliberately
|
|
* don't reuse the previous buffer in order to avoid potentially
|
|
* unwanted growth of the allocated buffer due to a single large
|
|
* document in a longer series of documents.
|
|
*/
|
|
if (out->basepos)
|
|
pdc_free(pdc, (void *) out->basepos);
|
|
|
|
out->basepos = (pdc_byte *)pdc_malloc(pdc, STREAM_CHUNKSIZE, fn);
|
|
out->curpos = out->basepos;
|
|
out->maxpos = out->basepos + STREAM_CHUNKSIZE;
|
|
|
|
out->base_offset = 0;
|
|
out->compressing = pdc_false;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
/* zlib sometimes reads uninitialized memory where it shouldn't... */
|
|
memset(&out->z, 0, sizeof out->z);
|
|
|
|
out->z.zalloc = (alloc_func) pdc_zlib_alloc;
|
|
out->z.zfree = (free_func) pdc_free;
|
|
out->z.opaque = (voidpf) pdc;
|
|
|
|
if (deflateInit(&out->z, pdc_get_compresslevel(out)) != Z_OK)
|
|
pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateInit", 0, 0, 0);
|
|
|
|
out->compr_changed = pdc_false;
|
|
#endif
|
|
|
|
/* Defaults */
|
|
out->fp = (FILE *) NULL;
|
|
out->writeproc = pdc_writeproc_file;
|
|
|
|
if (fp) {
|
|
out->fp = fp;
|
|
|
|
} else if (writeproc) {
|
|
out->writeproc = writeproc; /* PDF_open_mem */
|
|
|
|
} else if (filename == NULL || *filename == '\0') {
|
|
/* PDF_open_file with in-core output */
|
|
out->writeproc = NULL;
|
|
|
|
} else {
|
|
/* PDF_open_file with file output */
|
|
#if !((defined(MAC) || defined (MACOSX)) && defined(__MWERKS__))
|
|
if (filename && !strcmp(filename, "-")) {
|
|
out->fp = stdout;
|
|
#if !defined(__MWERKS__) && (defined(WIN32) || defined(OS2))
|
|
#if defined WINCE
|
|
_setmode(fileno(stdout), _O_BINARY);
|
|
#else
|
|
setmode(fileno(stdout), O_BINARY);
|
|
#endif /* !WINCE */
|
|
#endif
|
|
} else {
|
|
#endif /* !MAC */
|
|
|
|
#if defined(MVS) || defined(MVS_TEST)
|
|
out->fp = pdc_wfopen(out->pdc, filename,
|
|
(out->blocksize <= 1) ? WRITEMODE_V : WRITEMODE);
|
|
#else
|
|
out->fp = pdc_wfopen(out->pdc, filename, WRITEMODE);
|
|
#endif
|
|
|
|
if (out->fp == NULL)
|
|
return pdc_false;
|
|
|
|
#ifdef PDF_FILETYPE_SUPPORTED
|
|
|
|
#if defined(MAC) || defined(MACOSX)
|
|
/* set the proper type and creator for the output file */
|
|
#if TARGET_API_MAC_CARBON && !defined(__MWERKS__)
|
|
|
|
if (FSPathMakeFSSpec((const UInt8 *) filename, &fSpec) == noErr) {
|
|
FSpGetFInfo(&fSpec, &fInfo);
|
|
|
|
fInfo.fdType = 'PDF ';
|
|
fInfo.fdCreator = 'CARO';
|
|
FSpSetFInfo(&fSpec, &fInfo);
|
|
}
|
|
|
|
#else
|
|
|
|
memset(&fcbInfo, 0, sizeof(FCBPBRec));
|
|
fcbInfo.ioRefNum = (short) out->fp->handle;
|
|
fcbInfo.ioNamePtr = name;
|
|
|
|
if (!PBGetFCBInfoSync(&fcbInfo) &&
|
|
FSMakeFSSpec(fcbInfo.ioFCBVRefNum, fcbInfo.ioFCBParID,
|
|
name, &fSpec) == noErr) {
|
|
FSpGetFInfo(&fSpec, &fInfo);
|
|
fInfo.fdType = 'PDF ';
|
|
fInfo.fdCreator = 'CARO';
|
|
FSpSetFInfo(&fSpec, &fInfo);
|
|
}
|
|
#endif /* !defined(TARGET_API_MAC_CARBON) || defined(__MWERKS__) */
|
|
#endif /* defined(MAC) || defined(MACOSX) */
|
|
|
|
#endif /* PDF_FILETYPE_SUPPORTED */
|
|
|
|
#if !((defined(MAC) || defined (MACOSX)) && defined(__MWERKS__))
|
|
}
|
|
#endif /* !MAC */
|
|
}
|
|
|
|
return pdc_true;
|
|
}
|
|
#if defined(_MSC_VER) && defined(_MANAGED)
|
|
#pragma managed
|
|
#endif
|
|
|
|
/* close the output file, if opened with PDF_open_file();
|
|
* close the output stream if opened
|
|
*/
|
|
|
|
static void
|
|
pdc_close_stream(pdc_output *out)
|
|
{
|
|
pdc_flush_stream(out);
|
|
|
|
#ifdef HAVE_LIBZ
|
|
/*
|
|
* This is delicate: we must ignore the return value because of the
|
|
* following reasoning: We are called in two situations:
|
|
* - end of document
|
|
* - exception
|
|
* In the first case compression is inactive, so deflateEnd() will
|
|
* fail only in the second case. However, when an exception occurs
|
|
* the document is definitely unusable, so we avoid recursive exceptions
|
|
* or an (unallowed) exception in PDF_delete().
|
|
*/
|
|
|
|
(void) deflateEnd(&out->z);
|
|
#endif
|
|
|
|
/* close the output file if writing to file, but do not close the
|
|
* in-core output stream since the caller will have to
|
|
* fetch the buffer after PDF_close().
|
|
*/
|
|
if (out->fp)
|
|
{
|
|
pdc_wfclose(out->pdc, out->fp);
|
|
|
|
/* mark fp as dead in case the error handler jumps in later */
|
|
out->fp = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pdc_check_stream(pdc_output *out, size_t len)
|
|
{
|
|
size_t max;
|
|
int cur;
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
if (out->curpos + len <= out->maxpos)
|
|
return;
|
|
|
|
pdc_flush_stream(out);
|
|
|
|
if (out->curpos + len <= out->maxpos)
|
|
return;
|
|
|
|
max = (size_t) (2 * (out->maxpos - out->basepos));
|
|
cur = out->curpos - out->basepos;
|
|
|
|
out->basepos = (pdc_byte *)
|
|
pdc_realloc(pdc, (void *) out->basepos, max, "pdc_check_stream");
|
|
out->maxpos = out->basepos + max;
|
|
out->curpos = out->basepos + cur;
|
|
|
|
pdc_check_stream(out, len);
|
|
}
|
|
|
|
void
|
|
pdc_flush_stream(pdc_output *out)
|
|
{
|
|
size_t size;
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
if (!out->writeproc || out->compressing)
|
|
return;
|
|
|
|
size = (size_t) (out->curpos - out->basepos);
|
|
|
|
if (size == 0)
|
|
return;
|
|
|
|
if (out->writeproc(out, (void *) out->basepos, size) != size) {
|
|
pdc_free(pdc, out->basepos);
|
|
out->basepos = NULL;
|
|
out->writeproc = NULL;
|
|
pdc_error(pdc, PDC_E_IO_NOWRITE, 0, 0, 0, 0);
|
|
}
|
|
|
|
out->base_offset += (out->curpos - out->basepos);
|
|
out->curpos = out->basepos;
|
|
}
|
|
|
|
pdc_off_t
|
|
pdc_tell_out(pdc_output *out)
|
|
{
|
|
return (out->base_offset + (out->curpos - out->basepos));
|
|
}
|
|
|
|
/* --------------------- compression handling ----------------------- */
|
|
|
|
static void
|
|
pdc_begin_compress(pdc_output *out)
|
|
{
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
if (!pdc_get_compresslevel(out)) {
|
|
out->compressing = pdc_false;
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if (out->compr_changed)
|
|
{
|
|
if (deflateEnd(&out->z) != Z_OK)
|
|
pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateEnd", 0, 0, 0);
|
|
|
|
if (deflateInit(&out->z, pdc_get_compresslevel(out)) != Z_OK)
|
|
pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateInit", 0, 0, 0);
|
|
|
|
out->compr_changed = pdc_false;
|
|
}
|
|
else
|
|
{
|
|
if (deflateReset(&out->z) != Z_OK)
|
|
pdc_error(pdc, PDC_E_IO_COMPRESS, "deflateReset", 0, 0, 0);
|
|
}
|
|
|
|
out->z.avail_in = 0;
|
|
#endif /* HAVE_LIBZ */
|
|
|
|
out->compressing = pdc_true;
|
|
}
|
|
|
|
|
|
static void
|
|
pdc_end_compress(pdc_output *out)
|
|
{
|
|
int status;
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
/* this may happen during cleanup triggered by an exception handler */
|
|
if (!out->compressing)
|
|
return;
|
|
|
|
if (!pdc_get_compresslevel(out)) {
|
|
out->compressing = pdc_false;
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LIBZ
|
|
/* Finish the stream */
|
|
do {
|
|
pdc_check_stream(out, 128);
|
|
out->z.next_out = (Bytef *) out->curpos;
|
|
out->z.avail_out = (uInt) (out->maxpos - out->curpos);
|
|
|
|
status = deflate(&(out->z), Z_FINISH);
|
|
out->curpos = out->z.next_out;
|
|
|
|
if (status != Z_STREAM_END && status != Z_OK)
|
|
pdc_error(pdc, PDC_E_IO_COMPRESS, "Z_FINISH", 0, 0, 0);
|
|
|
|
} while (status != Z_STREAM_END);
|
|
|
|
out->compressing = pdc_false;
|
|
#endif /* HAVE_LIBZ */
|
|
}
|
|
|
|
/* ---------------------- Low-level output function ---------------------- */
|
|
/*
|
|
* Write binary data to the output without any modification,
|
|
* and apply compression if we are currently in compression mode.
|
|
*/
|
|
|
|
|
|
void
|
|
pdc_write(pdc_output *out, const void *data, size_t size)
|
|
{
|
|
int estimate = 0;
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if (out->compressing) {
|
|
out->z.avail_in = (uInt) size;
|
|
out->z.next_in = (Bytef *) data;
|
|
out->z.avail_out = 0;
|
|
|
|
while (out->z.avail_in > 0) {
|
|
if (out->z.avail_out == 0) {
|
|
/* estimate output buffer size */
|
|
estimate = (int) (out->z.avail_in/4 + 16);
|
|
pdc_check_stream(out, (size_t) estimate);
|
|
out->z.next_out = (Bytef *) out->curpos;
|
|
out->z.avail_out = (uInt) (out->maxpos - out->curpos);
|
|
}
|
|
|
|
if (deflate(&(out->z), Z_NO_FLUSH) != Z_OK)
|
|
pdc_error(pdc, PDC_E_IO_COMPRESS, "Z_NO_FLUSH", 0, 0, 0);
|
|
|
|
out->curpos = out->z.next_out;
|
|
}
|
|
} else {
|
|
#endif /* HAVE_LIBZ */
|
|
|
|
pdc_check_stream(out, size);
|
|
memcpy(out->curpos, data, size);
|
|
out->curpos += size;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
}
|
|
#endif /* HAVE_LIBZ */
|
|
}
|
|
|
|
|
|
/* --------------------------- Setup --------------------------- */
|
|
|
|
pdc_output *
|
|
pdc_boot_output(pdc_core *pdc)
|
|
{
|
|
static const char *fn = "pdc_boot_output";
|
|
pdc_output *out;
|
|
|
|
out = (pdc_output*)pdc_malloc(pdc, sizeof(pdc_output), fn);
|
|
out->pdc = pdc;
|
|
|
|
out->file_offset = NULL;
|
|
|
|
/* curpos must be initialized here so that the check for empty
|
|
* buffer in PDF_delete() also works in the degenerate case of
|
|
* no output produced.
|
|
*/
|
|
out->basepos = out->curpos = NULL;
|
|
|
|
out->open = pdc_false;
|
|
|
|
return out;
|
|
}
|
|
|
|
void
|
|
pdc_init_outctl(pdc_outctl *oc)
|
|
{
|
|
oc->filename = 0;
|
|
oc->fp = 0;
|
|
oc->writeproc = 0;
|
|
#if defined(MVS) || defined(MVS_TEST)
|
|
oc->blocksize = 0;
|
|
#endif
|
|
} /* pdc_init_outctl */
|
|
|
|
/*
|
|
* Initialize the PDF output.
|
|
* Note that the caller is responsible for supplying sensible arguments.
|
|
*/
|
|
#if defined(_MSC_VER) && defined(_MANAGED)
|
|
#pragma unmanaged
|
|
#endif
|
|
pdc_bool
|
|
pdc_init_output(
|
|
void *opaque,
|
|
pdc_output *out,
|
|
int compatibility,
|
|
pdc_outctl *oc)
|
|
{
|
|
static const char *fn = "pdc_init_output";
|
|
pdc_core *pdc = out->pdc;
|
|
int i;
|
|
|
|
pdc_cleanup_output(out, pdc_false);
|
|
|
|
out->opaque = opaque;
|
|
out->lastobj = 0;
|
|
#if defined(MVS) || defined(MVS_TEST)
|
|
out->blocksize = oc->blocksize;
|
|
#endif
|
|
|
|
if (out->file_offset == NULL) {
|
|
out->file_offset_capacity = ID_CHUNKSIZE;
|
|
|
|
out->file_offset = (pdc_off_t *) pdc_malloc(pdc,
|
|
sizeof(pdc_off_t) * out->file_offset_capacity, fn);
|
|
}
|
|
|
|
for (i = 1; i < out->file_offset_capacity; ++i)
|
|
out->file_offset[i] = PDC_BAD_ID;
|
|
|
|
out->compresslevel = PDF_DEFAULT_COMPRESSION;
|
|
out->compr_changed = pdc_false;
|
|
|
|
memcpy(out->id[0], out->id[1], MD5_DIGEST_LENGTH);
|
|
|
|
|
|
if (!pdc_init_stream(pdc, out, oc->filename, oc->fp, oc->writeproc))
|
|
return pdc_false;
|
|
|
|
/* Write the document header */
|
|
if (compatibility == PDC_1_6)
|
|
pdc_puts(out, "%PDF-1.6\n");
|
|
if (compatibility == PDC_1_5)
|
|
pdc_puts(out, "%PDF-1.5\n");
|
|
else if (compatibility == PDC_1_4)
|
|
pdc_puts(out, "%PDF-1.4\n");
|
|
else
|
|
pdc_puts(out, "%PDF-1.3\n");
|
|
|
|
#define PDC_MAGIC_BINARY "\045\344\343\317\322\012"
|
|
pdc_write(out, PDC_MAGIC_BINARY, sizeof(PDC_MAGIC_BINARY) - 1);
|
|
|
|
out->open = pdc_true;
|
|
|
|
return pdc_true;
|
|
}
|
|
#if defined(_MSC_VER) && defined(_MANAGED)
|
|
#pragma managed
|
|
#endif
|
|
|
|
void
|
|
pdc_close_output(pdc_output *out)
|
|
{
|
|
if (out->open)
|
|
{
|
|
out->open = pdc_false;
|
|
|
|
pdc_close_stream(out);
|
|
|
|
if (out->file_offset)
|
|
{
|
|
pdc_free(out->pdc, out->file_offset);
|
|
out->file_offset = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
pdc_cleanup_output(pdc_output *out, pdc_bool keep_buf)
|
|
{
|
|
pdc_core *pdc = out->pdc;
|
|
|
|
if (out->file_offset)
|
|
{
|
|
pdc_free(pdc, out->file_offset);
|
|
out->file_offset = NULL;
|
|
}
|
|
|
|
|
|
if (!keep_buf && out->basepos)
|
|
{
|
|
pdc_free(pdc, (void *) out->basepos);
|
|
out->basepos = NULL;
|
|
out->curpos = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/* --------------------------- Digest --------------------------- */
|
|
|
|
void
|
|
pdc_init_digest(pdc_output *out)
|
|
{
|
|
MD5_Init(&out->md5);
|
|
}
|
|
|
|
void
|
|
pdc_update_digest(pdc_output *out, unsigned char *input,
|
|
unsigned int len)
|
|
{
|
|
MD5_Update(&out->md5, input, len);
|
|
}
|
|
|
|
void
|
|
pdc_finish_digest(pdc_output *out, pdc_bool settime)
|
|
{
|
|
if (settime)
|
|
{
|
|
time_t timer;
|
|
|
|
time(&timer);
|
|
MD5_Update(&out->md5, (unsigned char *) &timer, sizeof timer);
|
|
}
|
|
|
|
MD5_Final(out->id[1], &out->md5);
|
|
}
|
|
|
|
unsigned char *
|
|
pdc_get_digest(pdc_output *out)
|
|
{
|
|
return out->id[1];
|
|
}
|
|
|
|
/* --------------------------- Objects and ids --------------------------- */
|
|
|
|
pdc_id
|
|
pdc_begin_obj(pdc_output *out, pdc_id obj_id)
|
|
{
|
|
if (obj_id == PDC_NEW_ID)
|
|
obj_id = pdc_alloc_id(out);
|
|
|
|
out->file_offset[obj_id] = pdc_tell_out(out);
|
|
pdc_printf(out, "%ld 0 obj\n", obj_id);
|
|
|
|
return obj_id;
|
|
}
|
|
|
|
pdc_id
|
|
pdc_alloc_id(pdc_output *out)
|
|
{
|
|
|
|
out->lastobj++;
|
|
|
|
if (out->lastobj >= out->file_offset_capacity) {
|
|
out->file_offset_capacity *= 2;
|
|
out->file_offset = (pdc_off_t *)
|
|
pdc_realloc(out->pdc, out->file_offset,
|
|
sizeof(pdc_off_t) * out->file_offset_capacity, "pdc_alloc_id");
|
|
}
|
|
|
|
/* only needed for verifying obj table in PDF_close() */
|
|
out->file_offset[out->lastobj] = PDC_BAD_ID;
|
|
|
|
return out->lastobj;
|
|
}
|
|
|
|
|
|
/* --------------------------- Strings --------------------------- */
|
|
|
|
void
|
|
pdc_put_pdfstring(pdc_output *out, const char *text, int len)
|
|
{
|
|
const unsigned char *goal, *s;
|
|
|
|
if (!len)
|
|
len = (int) strlen(text);
|
|
|
|
|
|
pdc_putc(out, PDF_PARENLEFT);
|
|
|
|
goal = (const unsigned char *) text + len;
|
|
|
|
for (s = (const unsigned char *) text; s < goal; s++)
|
|
{
|
|
switch (*s)
|
|
{
|
|
case PDF_RETURN:
|
|
pdc_putc(out, PDF_BACKSLASH);
|
|
pdc_putc(out, PDF_r);
|
|
break;
|
|
|
|
case PDF_NEWLINE:
|
|
pdc_putc(out, PDF_BACKSLASH);
|
|
pdc_putc(out, PDF_n);
|
|
break;
|
|
|
|
default:
|
|
if (*s == PDF_PARENLEFT || *s == PDF_PARENRIGHT ||
|
|
*s == PDF_BACKSLASH)
|
|
pdc_putc(out, PDF_BACKSLASH);
|
|
pdc_putc(out, (char) *s);
|
|
}
|
|
}
|
|
|
|
pdc_putc(out, PDF_PARENRIGHT);
|
|
}
|
|
|
|
/* normalized file name according PDF specification */
|
|
void
|
|
pdc_put_pdffilename(pdc_output *out, const char *text, int len)
|
|
{
|
|
static const char *fn = "pdc_put_pdffilename";
|
|
char *ttext;
|
|
|
|
#if defined(WIN32) || defined(MAC)
|
|
int i, j = 0, k = 0;
|
|
#endif
|
|
|
|
if (!len)
|
|
len = (int) strlen(text);
|
|
|
|
ttext = (char *) pdc_malloc(out->pdc, (size_t) (len + 4), fn);
|
|
strcpy(ttext, text);
|
|
|
|
#if defined(WIN32)
|
|
|
|
/* absolute path name */
|
|
if (strchr(ttext, PDF_COLON) != NULL || text[0] == PDF_BACKSLASH)
|
|
{
|
|
ttext[j] = PDF_SLASH;
|
|
j++;
|
|
}
|
|
for (i = k; i < len; i++)
|
|
{
|
|
if (text[i] == PDF_BACKSLASH)
|
|
ttext[j] = PDF_SLASH;
|
|
else if (text[i] == PDF_COLON)
|
|
continue;
|
|
else
|
|
ttext[j] = text[i];
|
|
j++;
|
|
}
|
|
len = j;
|
|
|
|
#elif defined(MAC)
|
|
|
|
/* absolute path name */
|
|
if (text[0] != PDF_COLON)
|
|
{
|
|
ttext[j] = PDF_SLASH;
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
k = 1;
|
|
}
|
|
for (i = k; i < len; i++)
|
|
{
|
|
if (text[i] == PDF_COLON)
|
|
ttext[j] = PDF_SLASH;
|
|
else
|
|
ttext[j] = text[i];
|
|
j++;
|
|
}
|
|
len = j;
|
|
|
|
#endif
|
|
|
|
pdc_put_pdfstring(out, ttext, len);
|
|
|
|
pdc_free(out->pdc, ttext);
|
|
}
|
|
|
|
/* --------------------------- Streams --------------------------- */
|
|
|
|
void
|
|
pdc_begin_pdfstream(pdc_output *out)
|
|
{
|
|
pdc_puts(out, "stream\n");
|
|
|
|
out->start_pos = pdc_tell_out(out);
|
|
|
|
if (out->compresslevel)
|
|
pdc_begin_compress(out);
|
|
}
|
|
|
|
void
|
|
pdc_end_pdfstream(pdc_output *out)
|
|
{
|
|
if (out->compresslevel)
|
|
pdc_end_compress(out);
|
|
|
|
out->length = pdc_tell_out(out) - out->start_pos;
|
|
|
|
/* some PDF consumers seem to need the additional "\n" before "endstream",
|
|
** the PDF reference allows it, and Acrobat's "repair" feature relies on it.
|
|
*/
|
|
pdc_puts(out, "\nendstream\n");
|
|
}
|
|
|
|
pdc_off_t
|
|
pdc_get_pdfstreamlength(pdc_output *out)
|
|
{
|
|
return out->length;
|
|
}
|
|
|
|
void
|
|
pdc_put_pdfstreamlength(pdc_output *out, pdc_id length_id)
|
|
{
|
|
|
|
pdc_begin_obj(out, length_id); /* Length object */
|
|
pdc_printf(out, "%lld\n", out->length);
|
|
pdc_end_obj(out);
|
|
}
|
|
|
|
void
|
|
pdc_set_compresslevel(pdc_output *out, int compresslevel)
|
|
{
|
|
out->compresslevel = compresslevel;
|
|
out->compr_changed = pdc_true;
|
|
}
|
|
|
|
int
|
|
pdc_get_compresslevel(pdc_output *out)
|
|
{
|
|
return out->compresslevel;
|
|
}
|
|
|
|
/* --------------------------- Names --------------------------- */
|
|
|
|
/* characters illegal in PDF names: "()<>[]{}/%#" */
|
|
#define PDF_ILL_IN_NAMES "\050\051\074\076\133\135\173\175\057\045\043"
|
|
|
|
#define PDF_NEEDS_QUOTE(c) \
|
|
((c) < 33 || (c) > 126 || strchr(PDF_ILL_IN_NAMES, (c)) != (char *) 0)
|
|
|
|
void
|
|
pdc_put_pdfname(pdc_output *out, const char *text, size_t len)
|
|
{
|
|
const unsigned char *goal, *s;
|
|
static const char BinToHex[] = PDF_STRING_0123456789ABCDEF;
|
|
|
|
if (!len)
|
|
len = strlen(text);
|
|
|
|
goal = (const unsigned char *) text + len;
|
|
|
|
pdc_putc(out, PDF_SLASH);
|
|
|
|
for (s = (const unsigned char *) text; s < goal; s++) {
|
|
if (PDF_NEEDS_QUOTE(*s)) {
|
|
pdc_putc(out, PDF_HASH);
|
|
pdc_putc(out, BinToHex[*s >> 4]); /* first nibble */
|
|
pdc_putc(out, BinToHex[*s & 0x0F]); /* second nibble */
|
|
} else
|
|
pdc_putc(out, (char) *s);
|
|
}
|
|
}
|
|
|
|
/* --------------------------- Document sections --------------------------- */
|
|
|
|
void
|
|
pdc_mark_free(pdc_output *out, pdc_id obj_id)
|
|
{
|
|
out->file_offset[obj_id] = PDC_FREE_ID;
|
|
}
|
|
|
|
|
|
void
|
|
pdc_write_xref(pdc_output *out)
|
|
{
|
|
pdc_id i;
|
|
pdc_id free_id;
|
|
pdc_core * pdc = out->pdc;
|
|
|
|
/* Don't write any object after this check! */
|
|
|
|
for (i = 1; i <= out->lastobj; i++) {
|
|
if (out->file_offset[i] == PDC_BAD_ID) {
|
|
pdc_warning(pdc, PDC_E_INT_UNUSEDOBJ,
|
|
pdc_errprintf(pdc, "%ld", i), 0, 0, 0);
|
|
/* write a dummy object */
|
|
pdc_begin_obj(out, i);
|
|
pdc_printf(out, "null %% unused object\n");
|
|
pdc_end_obj(out);
|
|
}
|
|
}
|
|
|
|
out->xref_pos = pdc_tell_out(out); /* xref table */
|
|
pdc_puts(out, "xref\n");
|
|
pdc_printf(out, "0 %ld\n", out->lastobj + 1);
|
|
|
|
/* find the last free entry in the xref table.
|
|
*/
|
|
out->file_offset[0] = PDC_FREE_ID;
|
|
for (free_id = out->lastobj;
|
|
out->file_offset[free_id] != PDC_FREE_ID;
|
|
--free_id)
|
|
;
|
|
|
|
pdc_printf(out, "%010ld 65535 f \n", free_id);
|
|
free_id = 0;
|
|
|
|
#define PDF_FLUSH_AFTER_MANY_OBJS 3000 /* ca. 60 KB */
|
|
for (i = 1; i <= out->lastobj; i++) {
|
|
/* Avoid spike in memory usage at the end of the document */
|
|
if (i % PDF_FLUSH_AFTER_MANY_OBJS == 0)
|
|
pdc_flush_stream(out);
|
|
|
|
if (out->file_offset[i] == PDC_FREE_ID)
|
|
{
|
|
pdc_printf(out, "%010ld 00001 f \n", free_id);
|
|
free_id = i;
|
|
}
|
|
else
|
|
{
|
|
pdc_printf(out, "%010lld 00000 n \n", out->file_offset[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
pdc_write_digest(pdc_output *out)
|
|
{
|
|
static const char bin2hex[] = PDF_STRING_0123456789ABCDEF;
|
|
|
|
int i;
|
|
|
|
pdc_puts(out, "/ID[<");
|
|
for (i = 0; i < MD5_DIGEST_LENGTH; ++i)
|
|
{
|
|
pdc_putc(out, bin2hex[out->id[0][i] >> 4]);
|
|
pdc_putc(out, bin2hex[out->id[0][i] & 0x0F]);
|
|
}
|
|
pdc_puts(out, "><");
|
|
for (i = 0; i < MD5_DIGEST_LENGTH; ++i)
|
|
{
|
|
pdc_putc(out, bin2hex[out->id[1][i] >> 4]);
|
|
pdc_putc(out, bin2hex[out->id[1][i] & 0x0F]);
|
|
}
|
|
pdc_puts(out, ">]\n");
|
|
}
|
|
|
|
void
|
|
pdc_write_eof(pdc_output *out)
|
|
{
|
|
#if defined(MVS) || defined(MVS_TEST)
|
|
int i, k;
|
|
|
|
if (out->blocksize > 1)
|
|
{
|
|
if ((i = (pdc_tell_out(out) + 6) % out->blocksize) != 0)
|
|
{
|
|
for (k = 0; k < out->blocksize - i - 1; ++k)
|
|
pdc_putc(out, PDF_SPACE);
|
|
|
|
pdc_putc(out, PDF_NEWLINE);
|
|
}
|
|
}
|
|
#endif /* MVS */
|
|
pdc_puts(out, "%%EOF\n");
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (xref_size == -1)
|
|
{
|
|
xref_size = out->lastobj + 1;
|
|
xref_pos = out->xref_pos;
|
|
}
|
|
|
|
/* we've seen PDF consumers that need the linefeed...
|
|
*/
|
|
pdc_puts(out, "trailer\n");
|
|
|
|
pdc_begin_dict(out); /* trailer */
|
|
pdc_printf(out, "/Size %ld\n", xref_size);
|
|
|
|
if (xref_prev != -1)
|
|
pdc_printf(out, "/Prev %lld\n", xref_prev);
|
|
|
|
pdc_printf(out, "/Root %ld %d R\n", root_id, root_gen);
|
|
|
|
if (info_id != PDC_BAD_ID)
|
|
pdc_printf(out, "/Info %ld 0 R\n", info_id);
|
|
|
|
pdc_write_digest(out);
|
|
pdc_end_dict(out); /* trailer */
|
|
|
|
pdc_puts(out, "startxref\n");
|
|
pdc_printf(out, "%lld\n", xref_pos);
|
|
pdc_write_eof(out);
|
|
}
|
|
|
|
/* ---------------------- High-level output functions ---------------------- */
|
|
|
|
#define PDC_LINEBUFLEN 4048 /* len of line output buffer */
|
|
|
|
/*
|
|
* Write a native encoded string to the output.
|
|
*/
|
|
|
|
static void
|
|
pdc_puts_internal(pdc_output *out, char *s, pdc_bool tocopy)
|
|
{
|
|
char *scp = s;
|
|
(void) tocopy;
|
|
|
|
pdc_write(out, (void *) scp, strlen(scp));
|
|
}
|
|
|
|
void
|
|
pdc_puts(pdc_output *out, const char *s)
|
|
{
|
|
pdc_puts_internal(out, (char *) s, pdc_true);
|
|
}
|
|
|
|
/*
|
|
* Write a ASCII character to the output.
|
|
*/
|
|
|
|
void
|
|
pdc_putc(pdc_output *out, const char c)
|
|
{
|
|
pdc_write(out, (void *) &c, (size_t) 1);
|
|
}
|
|
|
|
/*
|
|
* Write a formatted string (native encoded) to the output.
|
|
*/
|
|
|
|
void
|
|
pdc_printf(pdc_output *out, const char *fmt, ...)
|
|
{
|
|
char buf[PDC_LINEBUFLEN];
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
pdc_vsprintf(out->pdc, pdc_true, buf, fmt, ap);
|
|
pdc_puts_internal(out, buf, pdc_false);
|
|
|
|
va_end(ap);
|
|
}
|