diff options
Diffstat (limited to 'gs/base/gdevpdf.c')
-rw-r--r-- | gs/base/gdevpdf.c | 1391 |
1 files changed, 1391 insertions, 0 deletions
diff --git a/gs/base/gdevpdf.c b/gs/base/gdevpdf.c new file mode 100644 index 000000000..b3fc7b812 --- /dev/null +++ b/gs/base/gdevpdf.c @@ -0,0 +1,1391 @@ +/* Copyright (C) 2001-2007 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* $Id$ */ +/* PDF-writing driver */ +#include "fcntl_.h" +#include "memory_.h" +#include "string_.h" +#include "time_.h" +#include "unistd_.h" +#include "gx.h" +#include "gp.h" /* for gp_get_realtime */ +#include "gserrors.h" +#include "gxdevice.h" +#include "gdevpdfx.h" +#include "gdevpdfg.h" /* only for pdf_reset_graphics */ +#include "gdevpdfo.h" +#include "gdevpdt.h" +#include "smd5.h" +#include "sarc4.h" + +/* Define the default language level and PDF compatibility level. */ +/* Acrobat 4 (PDF 1.3) is the default. */ +#define PSDF_VERSION_INITIAL psdf_version_ll3 +#define PDF_COMPATIBILITY_LEVEL_INITIAL 1.4 + +/* Define the size of internal stream buffers. */ +/* (This is not a limitation, it only affects performance.) */ +#define sbuf_size 512 + +/* GC descriptors */ +private_st_pdf_page(); +gs_private_st_element(st_pdf_page_element, pdf_page_t, "pdf_page_t[]", + pdf_page_elt_enum_ptrs, pdf_page_elt_reloc_ptrs, + st_pdf_page); +private_st_device_pdfwrite(); +private_st_pdf_substream_save(); +private_st_pdf_substream_save_element(); + +/* GC procedures */ +static +ENUM_PTRS_WITH(device_pdfwrite_enum_ptrs, gx_device_pdf *pdev) +{ + index -= gx_device_pdf_num_ptrs + gx_device_pdf_num_param_strings; + if (index < NUM_RESOURCE_TYPES * NUM_RESOURCE_CHAINS) + ENUM_RETURN(pdev->resources[index / NUM_RESOURCE_CHAINS].chains[index % NUM_RESOURCE_CHAINS]); + index -= NUM_RESOURCE_TYPES * NUM_RESOURCE_CHAINS; + if (index <= pdev->outline_depth) + ENUM_RETURN(pdev->outline_levels[index].first.action); + index -= pdev->outline_depth + 1; + if (index <= pdev->outline_depth) + ENUM_RETURN(pdev->outline_levels[index].last.action); + index -= pdev->outline_depth + 1; + ENUM_PREFIX(st_device_psdf, 0); +} +#define e1(i,elt) ENUM_PTR(i, gx_device_pdf, elt); +gx_device_pdf_do_ptrs(e1) +#undef e1 +#define e1(i,elt) ENUM_PARAM_STRING_PTR(i + gx_device_pdf_num_ptrs, gx_device_pdf, elt); +gx_device_pdf_do_param_strings(e1) +#undef e1 +#define e1(i,elt) ENUM_STRING_PTR(i + gx_device_pdf_num_ptrs + gx_device_pdf_num_param_strings,\ + gx_device_pdf, elt); +gx_device_pdf_do_const_strings(e1) +#undef e1 +ENUM_PTRS_END +static RELOC_PTRS_WITH(device_pdfwrite_reloc_ptrs, gx_device_pdf *pdev) +{ + RELOC_PREFIX(st_device_psdf); +#define r1(i,elt) RELOC_PTR(gx_device_pdf,elt); + gx_device_pdf_do_ptrs(r1) +#undef r1 +#define r1(i,elt) RELOC_PARAM_STRING_PTR(gx_device_pdf,elt); + gx_device_pdf_do_param_strings(r1) +#undef r1 +#define r1(i,elt) RELOC_CONST_STRING_PTR(gx_device_pdf,elt); + gx_device_pdf_do_const_strings(r1) +#undef r1 + { + int i, j; + + for (i = 0; i < NUM_RESOURCE_TYPES; ++i) + for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) + RELOC_PTR(gx_device_pdf, resources[i].chains[j]); + for (i = 0; i <= pdev->outline_depth; ++i) { + RELOC_PTR(gx_device_pdf, outline_levels[i].first.action); + RELOC_PTR(gx_device_pdf, outline_levels[i].last.action); + } + } +} +RELOC_PTRS_END +/* Even though device_pdfwrite_finalize is the same as gx_device_finalize, */ +/* we need to implement it separately because st_composite_final */ +/* declares all 3 procedures as private. */ +static void +device_pdfwrite_finalize(void *vpdev) +{ + gx_device_finalize(vpdev); +} + +/* Driver procedures */ +static dev_proc_open_device(pdf_open); +static dev_proc_output_page(pdf_output_page); +static dev_proc_close_device(pdf_close); +/* Driver procedures defined in other files are declared in gdevpdfx.h. */ + +#ifndef X_DPI +# define X_DPI 720 +#endif +#ifndef Y_DPI +# define Y_DPI 720 +#endif + +/* ---------------- Device prototype ---------------- */ + +#define PDF_DEVICE_NAME "pdfwrite" +#define PDF_DEVICE_IDENT gs_pdfwrite_device +#define PDF_DEVICE_MaxInlineImageSize 4000 +#define PDF_FOR_OPDFREAD 0 + +#include "gdevpdfb.h" + +#undef PDF_DEVICE_NAME +#undef PDF_DEVICE_IDENT +#undef PDF_DEVICE_MaxInlineImageSize +#undef PDF_FOR_OPDFREAD + +#define PDF_DEVICE_NAME "ps2write" +#define PDF_DEVICE_IDENT gs_ps2write_device +#define PDF_DEVICE_MaxInlineImageSize max_long +#define PDF_FOR_OPDFREAD 1 + +#include "gdevpdfb.h" + +#undef PDF_DEVICE_NAME +#undef PDF_DEVICE_IDENT +#undef PDF_DEVICE_MaxInlineImageSize +#undef PDF_FOR_OPDFREAD +/* ---------------- Device open/close ---------------- */ + +/* Close and remove temporary files. */ +static int +pdf_close_temp_file(gx_device_pdf *pdev, pdf_temp_file_t *ptf, int code) +{ + int err = 0; + FILE *file = ptf->file; + + /* + * ptf->strm == 0 or ptf->file == 0 is only possible if this procedure + * is called to clean up during initialization failure, but ptf->strm + * might not be open if it was finalized before the device was closed. + */ + if (ptf->strm) { + if (s_is_valid(ptf->strm)) { + sflush(ptf->strm); + /* Prevent freeing the stream from closing the file. */ + ptf->strm->file = 0; + } else + ptf->file = file = 0; /* file was closed by finalization */ + gs_free_object(pdev->pdf_memory, ptf->strm_buf, + "pdf_close_temp_file(strm_buf)"); + ptf->strm_buf = 0; + gs_free_object(pdev->pdf_memory, ptf->strm, + "pdf_close_temp_file(strm)"); + ptf->strm = 0; + } + if (file) { + err = ferror(file) | fclose(file); + unlink(ptf->file_name); + ptf->file = 0; + } + ptf->save_strm = 0; + return + (code < 0 ? code : err != 0 ? gs_note_error(gs_error_ioerror) : code); +} +static int +pdf_close_files(gx_device_pdf * pdev, int code) +{ + code = pdf_close_temp_file(pdev, &pdev->pictures, code); + code = pdf_close_temp_file(pdev, &pdev->streams, code); + code = pdf_close_temp_file(pdev, &pdev->asides, code); + return pdf_close_temp_file(pdev, &pdev->xref, code); +} + +/* Reset the state of the current page. */ +static void +pdf_reset_page(gx_device_pdf * pdev) +{ + pdev->page_dsc_info = gs_pdfwrite_device.page_dsc_info; + pdev->contents_id = 0; + pdf_reset_graphics(pdev); + pdev->procsets = NoMarks; + memset(pdev->cs_Patterns, 0, sizeof(pdev->cs_Patterns)); /* simplest to create for each page */ + pdf_reset_text_page(pdev->text); + pdf_remember_clip_path(pdev, 0); + pdev->clip_path_id = pdev->no_clip_path_id; +} + +/* Open a temporary file, with or without a stream. */ +static int +pdf_open_temp_file(gx_device_pdf *pdev, pdf_temp_file_t *ptf) +{ + char fmode[4]; + + strcpy(fmode, "w+"); + strcat(fmode, gp_fmode_binary_suffix); + ptf->file = + gp_open_scratch_file(gp_scratch_file_name_prefix, + ptf->file_name, fmode); + if (ptf->file == 0) + return_error(gs_error_invalidfileaccess); + return 0; +} +static int +pdf_open_temp_stream(gx_device_pdf *pdev, pdf_temp_file_t *ptf) +{ + int code = pdf_open_temp_file(pdev, ptf); + + if (code < 0) + return code; + ptf->strm = s_alloc(pdev->pdf_memory, "pdf_open_temp_stream(strm)"); + if (ptf->strm == 0) + return_error(gs_error_VMerror); + ptf->strm_buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size, + "pdf_open_temp_stream(strm_buf)"); + if (ptf->strm_buf == 0) { + gs_free_object(pdev->pdf_memory, ptf->strm, + "pdf_open_temp_stream(strm)"); + ptf->strm = 0; + return_error(gs_error_VMerror); + } + swrite_file(ptf->strm, ptf->file, ptf->strm_buf, sbuf_size); + return 0; +} + +/* Initialize the IDs allocated at startup. */ +void +pdf_initialize_ids(gx_device_pdf * pdev) +{ + gs_param_string nstr; + + pdev->next_id = pdev->FirstObjectNumber; + + /* Initialize the Catalog. */ + + param_string_from_string(nstr, "{Catalog}"); + pdf_create_named_dict(pdev, &nstr, &pdev->Catalog, 0L); + + /* Initialize the Info dictionary. */ + + param_string_from_string(nstr, "{DocInfo}"); + pdf_create_named_dict(pdev, &nstr, &pdev->Info, 0L); + { + char buf[PDF_MAX_PRODUCER]; + + pdf_store_default_Producer(buf); + cos_dict_put_c_key_string(pdev->Info, "/Producer", (byte *)buf, + strlen(buf)); + } + /* + * Acrobat Distiller sets CreationDate and ModDate to the current + * date and time, rather than (for example) %%CreationDate from the + * PostScript file. We think this is wrong, but we do the same. + */ + { + struct tm tms; + time_t t; + char buf[1+2+4+2+2+2+2+2+1+2+1+2+1+1+1]; /* (D:yyyymmddhhmmssZhh'mm')\0 */ + int timeoffset; + char timesign; + + time(&t); + tms = *gmtime(&t); + tms.tm_isdst = -1; + timeoffset = (int)difftime(t, mktime(&tms)); /* tz+dst in seconds */ + timesign = (timeoffset == 0 ? 'Z' : timeoffset < 0 ? '-' : '+'); + timeoffset = any_abs(timeoffset) / 60; + tms = *localtime(&t); + + sprintf(buf, "(D:%04d%02d%02d%02d%02d%02d%c%02d\'%02d\')", + tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, + tms.tm_hour, tms.tm_min, tms.tm_sec, + timesign, timeoffset / 60, timeoffset % 60); + + cos_dict_put_c_key_string(pdev->Info, "/CreationDate", (byte *)buf, + strlen(buf)); + cos_dict_put_c_key_string(pdev->Info, "/ModDate", (byte *)buf, + strlen(buf)); + } + + /* Allocate the root of the pages tree. */ + + pdf_create_named_dict(pdev, NULL, &pdev->Pages, 0L); +} + +static int +pdf_compute_fileID(gx_device_pdf * pdev) +{ + /* We compute a file identifier when beginning a document + to allow its usage with PDF encryption. Due to that, + in contradiction to the Adobe recommendation, our + ID doesn't depend on the document size. + */ + gs_memory_t *mem = pdev->pdf_memory; + stream *strm = pdev->strm; + uint ignore; + int code; + stream *s = s_MD5E_make_stream(mem, pdev->fileID, sizeof(pdev->fileID)); + long secs_ns[2]; + uint KeyLength = pdev->KeyLength; + + if (s == NULL) + return_error(gs_error_VMerror); + pdev->KeyLength = 0; /* Disable encryption. Not so important though. */ + gp_get_usertime(secs_ns); + sputs(s, (byte *)secs_ns, sizeof(secs_ns), &ignore); + sputs(s, (const byte *)pdev->fname, strlen(pdev->fname), &ignore); + pdev->strm = s; + code = cos_dict_elements_write(pdev->Info, pdev); + pdev->strm = strm; + pdev->KeyLength = KeyLength; + if (code < 0) + return code; + sclose(s); + gs_free_object(mem, s, "pdf_compute_fileID"); +#if 0 + memcpy(pdev->fileID, "xxxxxxxxxxxxxxxx", sizeof(pdev->fileID)); /* Debug */ +#endif + return 0; +} + +static const byte pad[32] = { 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, + 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, + 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A}; + +static inline void +copy_padded(byte buf[32], gs_param_string *str) +{ + memcpy(buf, str->data, min(str->size, 32)); + if (32 > str->size) + memcpy(buf + str->size, pad, 32 - str->size); +} + +static void +Adobe_magic_loop_50(byte digest[16], int key_length) +{ + gs_md5_state_t md5; + int i; + + for (i = 0; i < 50; i++) { + gs_md5_init(&md5); + gs_md5_append(&md5, digest, key_length); + gs_md5_finish(&md5, digest); + } +} + +static void +Adobe_magic_loop_19(byte *data, int data_size, const byte *key, int key_size) +{ + stream_arcfour_state sarc4; + byte key_buf[16]; + int i, j; + + for (i = 1; i <= 19; i++) { + for (j = 0; j < key_size; j++) + key_buf[j] = key[j] ^ (byte)i; + s_arcfour_set_key(&sarc4, key_buf, key_size); + s_arcfour_process_buffer(&sarc4, data, data_size); + } +} + +static int +pdf_compute_encryption_data(gx_device_pdf * pdev) +{ + gs_md5_state_t md5; + byte digest[16], buf[32], t; + stream_arcfour_state sarc4; + + if (pdev->PDFX && pdev->KeyLength != 0) { + eprintf("Encryption is not allowed in a PDF/X doucment.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->KeyLength == 0) + pdev->KeyLength = 40; + if (pdev->EncryptionV == 0 && pdev->KeyLength == 40) + pdev->EncryptionV = 1; + if (pdev->EncryptionV == 0 && pdev->KeyLength > 40) + pdev->EncryptionV = 2; + if (pdev->EncryptionV > 1 && pdev->CompatibilityLevel < 1.4) { + eprintf("PDF 1.3 only supports 40 bits keys.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->EncryptionR == 0) + pdev->EncryptionR = 2; + if (pdev->EncryptionR < 2 || pdev->EncryptionR > 3) { + eprintf("Encryption revisions 2 and 3 are only supported.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->EncryptionR > 2 && pdev->CompatibilityLevel < 1.4) { + eprintf("PDF 1.3 only supports the encryption revision 2.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->KeyLength > 128) { + eprintf("The maximal length of PDF encryption key is 128 bits.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->KeyLength % 8) { + eprintf("PDF encryption key length must be a multiple of 8.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->EncryptionR == 2 && + ((pdev->Permissions & (0xFFFFFFC3)) != 0xFFFFFFC0)) { + eprintf("Some of Permissions are not allowed with R=2.\n"); + return_error(gs_error_rangecheck); + } + if (pdev->EncryptionV == 2 && pdev->EncryptionR == 2 && pdev->KeyLength > 40) { + eprintf("Encryption version 2 revision 2 with KeyLength > 40 appears incompatible to some viewers. With long keys use revision 3.\n"); + return_error(gs_error_rangecheck); + } + /* Compute O : */ + gs_md5_init(&md5); + copy_padded(buf, &pdev->OwnerPassword); + gs_md5_append(&md5, buf, sizeof(buf)); + gs_md5_finish(&md5, digest); + if (pdev->EncryptionR == 3) + Adobe_magic_loop_50(digest, pdev->KeyLength / 8); + copy_padded(buf, &pdev->UserPassword); + s_arcfour_set_key(&sarc4, digest, pdev->KeyLength / 8); + s_arcfour_process_buffer(&sarc4, buf, sizeof(buf)); + if (pdev->EncryptionR == 3) + Adobe_magic_loop_19(buf, sizeof(buf), digest, pdev->KeyLength / 8); + memcpy(pdev->EncryptionO, buf, sizeof(pdev->EncryptionO)); + /* Compute Key : */ + gs_md5_init(&md5); + copy_padded(buf, &pdev->UserPassword); + gs_md5_append(&md5, buf, sizeof(buf)); + gs_md5_append(&md5, pdev->EncryptionO, sizeof(pdev->EncryptionO)); + t = (byte)(pdev->Permissions >> 0); gs_md5_append(&md5, &t, 1); + t = (byte)(pdev->Permissions >> 8); gs_md5_append(&md5, &t, 1); + t = (byte)(pdev->Permissions >> 16); gs_md5_append(&md5, &t, 1); + t = (byte)(pdev->Permissions >> 24); gs_md5_append(&md5, &t, 1); + gs_md5_append(&md5, pdev->fileID, sizeof(pdev->fileID)); + if (pdev->EncryptionR == 3) + if (!pdev->EncryptMetadata) { + const byte v[4] = {0xFF, 0xFF, 0xFF, 0xFF}; + + gs_md5_append(&md5, v, 4); + } + gs_md5_finish(&md5, digest); + if (pdev->EncryptionR == 3) + Adobe_magic_loop_50(digest, pdev->KeyLength / 8); + memcpy(pdev->EncryptionKey, digest, pdev->KeyLength / 8); + /* Compute U : */ + if (pdev->EncryptionR == 3) { + gs_md5_init(&md5); + gs_md5_append(&md5, pad, sizeof(pad)); + gs_md5_append(&md5, pdev->fileID, sizeof(pdev->fileID)); + gs_md5_finish(&md5, digest); + s_arcfour_set_key(&sarc4, pdev->EncryptionKey, pdev->KeyLength / 8); + s_arcfour_process_buffer(&sarc4, digest, sizeof(digest)); + Adobe_magic_loop_19(digest, sizeof(digest), pdev->EncryptionKey, pdev->KeyLength / 8); + memcpy(pdev->EncryptionU, digest, sizeof(digest)); + memcpy(pdev->EncryptionU + sizeof(digest), pad, + sizeof(pdev->EncryptionU) - sizeof(digest)); + } else { + memcpy(pdev->EncryptionU, pad, sizeof(pdev->EncryptionU)); + s_arcfour_set_key(&sarc4, pdev->EncryptionKey, pdev->KeyLength / 8); + s_arcfour_process_buffer(&sarc4, pdev->EncryptionU, sizeof(pdev->EncryptionU)); + } + return 0; +} + +#ifdef __DECC +/* The ansi alias rules are violated in this next routine. Tell the compiler + to ignore this. + */ +#pragma optimize save +#pragma optimize ansi_alias=off +#endif +/* + * Update the color mapping procedures after setting ProcessColorModel. + * + * The 'index' value indicates the ProcessColorModel. + * 0 = DeviceGray + * 1 = DeviceRGB + * 2 = DeviceCMYK + * 3 = DeviceN (treat like CMYK except for color model name) + */ +void +pdf_set_process_color_model(gx_device_pdf * pdev, int index) +{ + + const static gx_device_color_info pcm_color_info[] = { + dci_values(1, 8, 255, 0, 256, 0), /* Gray */ + dci_values(3, 24, 255, 255, 256, 256), /* RGB */ + dci_values(4, 32, 255, 255, 256, 256), /* CMYK */ + dci_values(4, 32, 255, 255, 256, 256) /* Treat DeviceN like CMYK */ + }; + + pdev->pcm_color_info_index = index; + pdev->color_info = pcm_color_info[index]; + /* Set the separable and linear shift, masks, bits. */ + set_linear_color_bits_mask_shift((gx_device *)pdev); + pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN; + /* + * The conversion from PS to PDF should be transparent as possible. + * Particularly it should not change representation of colors. + * Perhaps due to historical reasons the source color information + * sometimes isn't accessible from device methods, and + * therefore they perform a mapping of colors to + * an output color model. Here we handle some color models, + * which were selected almost due to antique reasons. + */ + switch (index) { + case 0: /* DeviceGray */ + set_dev_proc(pdev, map_rgb_color, gx_default_gray_map_rgb_color); + set_dev_proc(pdev, map_color_rgb, gx_default_gray_map_color_rgb); + set_dev_proc(pdev, map_cmyk_color, NULL); + set_dev_proc(pdev, get_color_mapping_procs, + gx_default_DevGray_get_color_mapping_procs); + set_dev_proc(pdev, get_color_comp_index, + gx_default_DevGray_get_color_comp_index); + set_dev_proc(pdev, encode_color, gx_default_gray_encode); + set_dev_proc(pdev, decode_color, gx_default_decode_color); + break; + case 1: /* DeviceRGB */ + set_dev_proc(pdev, map_rgb_color, gx_default_rgb_map_rgb_color); + set_dev_proc(pdev, map_color_rgb, gx_default_rgb_map_color_rgb); + set_dev_proc(pdev, map_cmyk_color, NULL); + set_dev_proc(pdev, get_color_mapping_procs, + gx_default_DevRGB_get_color_mapping_procs); + set_dev_proc(pdev, get_color_comp_index, + gx_default_DevRGB_get_color_comp_index); + set_dev_proc(pdev, encode_color, gx_default_rgb_map_rgb_color); + set_dev_proc(pdev, decode_color, gx_default_rgb_map_color_rgb); + break; + case 3: /* DeviceN - treat like DeviceCMYK except for cm_name */ + pdev->color_info.cm_name = "DeviceN"; + case 2: /* DeviceCMYK */ + set_dev_proc(pdev, map_rgb_color, NULL); + set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb); + /* possible problems with aliassing on next statement */ + set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color); + set_dev_proc(pdev, get_color_mapping_procs, + gx_default_DevCMYK_get_color_mapping_procs); + set_dev_proc(pdev, get_color_comp_index, + gx_default_DevCMYK_get_color_comp_index); + set_dev_proc(pdev, encode_color, cmyk_8bit_map_cmyk_color); + set_dev_proc(pdev, decode_color, cmyk_8bit_map_color_rgb); + break; + default: /* can't happen - see the call from gdev_pdf_put_params. */ + DO_NOTHING; + } +} +#ifdef __DECC +#pragma optimize restore +#endif + +/* + * Reset the text state parameters to initial values. + */ +void +pdf_reset_text(gx_device_pdf * pdev) +{ + pdf_reset_text_state(pdev->text); +} + +/* Open the device. */ +static int +pdf_open(gx_device * dev) +{ + gx_device_pdf *const pdev = (gx_device_pdf *) dev; + gs_memory_t *mem = pdev->pdf_memory = gs_memory_stable(pdev->memory); + int code; + + if ((code = pdf_open_temp_file(pdev, &pdev->xref)) < 0 || + (code = pdf_open_temp_stream(pdev, &pdev->asides)) < 0 || + (code = pdf_open_temp_stream(pdev, &pdev->streams)) < 0 || + (code = pdf_open_temp_stream(pdev, &pdev->pictures)) < 0 + ) + goto fail; + code = gdev_vector_open_file((gx_device_vector *) pdev, sbuf_size); + if (code < 0) + goto fail; + if (pdev->ComputeDocumentDigest) { + stream *s = s_MD5C_make_stream(pdev->pdf_memory, pdev->strm); + + if (s == NULL) + return_error(gs_error_VMerror); + pdev->strm = s; + } + gdev_vector_init((gx_device_vector *) pdev); + gp_get_realtime(pdev->uuid_time); + pdev->vec_procs = &pdf_vector_procs; + pdev->fill_options = pdev->stroke_options = gx_path_type_optimize; + /* Set in_page so the vector routines won't try to call */ + /* any vector implementation procedures. */ + pdev->in_page = true; + /* + * pdf_initialize_ids allocates some (global) named objects, so we must + * initialize the named objects dictionary now. + */ + pdev->local_named_objects = + pdev->global_named_objects = + cos_dict_alloc(pdev, "pdf_open(global_named_objects)"); + /* Initialize internal structures that don't have IDs. */ + pdev->NI_stack = cos_array_alloc(pdev, "pdf_open(NI stack)"); + pdev->Namespace_stack = cos_array_alloc(pdev, "pdf_open(Namespace stack)"); + pdf_initialize_ids(pdev); + code = pdf_compute_fileID(pdev); + if (code < 0) + goto fail; + if (pdev->OwnerPassword.size > 0) { + code = pdf_compute_encryption_data(pdev); + if (code < 0) + goto fail; + } else if(pdev->UserPassword.size > 0) { + eprintf("User password is specified. Need an Owner password or both.\n"); + return_error(gs_error_rangecheck); + } else if (pdev->KeyLength) { + eprintf("Can't accept encryption options without a password.\n"); + return_error(gs_error_rangecheck); + } + /* Now create a new dictionary for the local named objects. */ + pdev->local_named_objects = + cos_dict_alloc(pdev, "pdf_open(local_named_objects)"); + pdev->outlines_id = 0; + pdev->next_page = 0; + pdev->text = pdf_text_data_alloc(mem); + pdev->sbstack_size = count_of(pdev->vgstack); /* Overestimated a few. */ + pdev->sbstack = gs_alloc_struct_array(mem, pdev->sbstack_size, pdf_substream_save, + &st_pdf_substream_save_element, "pdf_open"); + pdev->pages = + gs_alloc_struct_array(mem, initial_num_pages, pdf_page_t, + &st_pdf_page_element, "pdf_open(pages)"); + if (pdev->text == 0 || pdev->pages == 0 || pdev->sbstack == 0) { + code = gs_error_VMerror; + goto fail; + } + memset(pdev->sbstack, 0, pdev->sbstack_size * sizeof(pdf_substream_save)); + memset(pdev->pages, 0, initial_num_pages * sizeof(pdf_page_t)); + pdev->num_pages = initial_num_pages; + { + int i, j; + + for (i = 0; i < NUM_RESOURCE_TYPES; ++i) + for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) + pdev->resources[i].chains[j] = 0; + } + pdev->outline_levels[0].first.id = 0; + pdev->outline_levels[0].left = max_int; + pdev->outline_levels[0].first.action = 0; + pdev->outline_levels[0].last.action = 0; + pdev->outline_depth = 0; + pdev->closed_outline_depth = 0; + pdev->outlines_open = 0; + pdev->articles = 0; + pdev->Dests = 0; + /* {global,local}_named_objects was initialized above */ + pdev->PageLabels = 0; + pdev->PageLabels_current_page = 0; + pdev->PageLabels_current_label = 0; + pdev->pte = NULL; + pdf_reset_page(pdev); + return 0; + fail: + gdev_vector_close_file((gx_device_vector *) pdev); + return pdf_close_files(pdev, code); +} + +/* Detect I/O errors. */ +static int +pdf_ferror(gx_device_pdf *pdev) +{ + fflush(pdev->file); + fflush(pdev->xref.file); + sflush(pdev->strm); + sflush(pdev->asides.strm); + sflush(pdev->streams.strm); + sflush(pdev->pictures.strm); + return ferror(pdev->file) || ferror(pdev->xref.file) || + ferror(pdev->asides.file) || ferror(pdev->streams.file) || + ferror(pdev->pictures.file); +} + +/* Compute the dominant text orientation of a page. */ +static int +pdf_dominant_rotation(const pdf_text_rotation_t *ptr) +{ + int i, imax = -1; + long max_count = 0; + static const int angles[] = { pdf_text_rotation_angle_values }; + + for (i = 0; i < countof(ptr->counts); ++i) { + long count = ptr->counts[i]; + + if (count > max_count) + imax = i, max_count = count; + } + return (imax < 0 ? imax : angles[imax]); +} + +/* Print a Rotate command, if requested and possible. */ +static void +pdf_print_orientation(gx_device_pdf * pdev, pdf_page_t *page) +{ + stream *s = pdev->strm; + int dsc_orientation = -1; + const pdf_page_dsc_info_t *ppdi; + + if (pdev->params.AutoRotatePages == arp_None) + return; /* Not requested. */ + + ppdi = (page != NULL ? &page->dsc_info : &pdev->doc_dsc_info); + + /* Determine DSC orientation : */ + if (ppdi->viewing_orientation >= 0) + dsc_orientation = ppdi->viewing_orientation; + else if (ppdi->orientation >= 0) + dsc_orientation = ppdi->orientation; + if ((page == NULL && pdev->params.AutoRotatePages == arp_All) || /* document */ + (page != NULL && page->text_rotation.Rotate >= 0) || /* page */ + dsc_orientation >= 0 /* have DSC */) { + const pdf_text_rotation_t *ptr = + (page != NULL ? &page->text_rotation : &pdev->text_rotation); + int angle = -1; + +#define Bug687800 +#ifndef Bug687800 /* Bug 687800 together with Bug687489.ps . */ + const gs_point *pbox = &(page != NULL ? page : &pdev->pages[0])->MediaBox; + + if (dsc_orientation >= 0 && pbox->x > pbox->y) { + /* The page is in landscape format. Adjust the rotation accordingly. */ + dsc_orientation ^= 1; + } +#endif + + /* Combine DSC rotation with text rotation : */ + if (dsc_orientation == 0) { + if (ptr->Rotate == 0 || ptr->Rotate == 180) + angle = ptr->Rotate; + } else if (dsc_orientation == 1) { + if (ptr->Rotate == 90 || ptr->Rotate == 270) + angle = ptr->Rotate; + else + angle = 90; + } + + if (angle < 0) { +#define Bug688793 +#ifdef Bug688793 + /* If not combinable, prefer dsc rotation : */ + if (dsc_orientation >= 0) + angle = dsc_orientation * 90; + else + angle = ptr->Rotate; +#else + /* If not combinable, prefer text rotation : */ + if (ptr->Rotate >= 0) + angle = ptr->Rotate; +#ifdef Bug687800 + else + angle = dsc_orientation * 90; +#endif +#endif + } + + /* If got some, write it out : */ + if (angle >= 0) + pprintd1(s, "/Rotate %d", angle); + } +} + + +/* Close the current page. */ +static int +pdf_close_page(gx_device_pdf * pdev) +{ + int page_num = ++(pdev->next_page); + pdf_page_t *page; + int code; + + /* + * If the very first page is blank, we need to open the document + * before doing anything else. + */ + + code = pdf_open_document(pdev); + if (code < 0) + return code; + if (pdev->ForOPDFRead && pdev->context == PDF_IN_NONE) { + /* Must create a context stream for empty pages. */ + code = pdf_open_contents(pdev, PDF_IN_STREAM); + if (code < 0) + return code; + } + pdf_close_contents(pdev, true); + + /* + * We can't write the page object or the annotations array yet, because + * later pdfmarks might add elements to them. Write the other objects + * that the page references, and record what we'll need later. + * + * Start by making sure the pages array element exists. + */ + + pdf_page_id(pdev, page_num); + page = &pdev->pages[page_num - 1]; + page->MediaBox.x = pdev->MediaSize[0]; + page->MediaBox.y = pdev->MediaSize[1]; + page->contents_id = pdev->contents_id; + page->NumCopies_set = pdev->NumCopies_set; + page->NumCopies = pdev->NumCopies; + /* pdf_store_page_resources sets procsets, resource_ids[]. */ + code = pdf_store_page_resources(pdev, page); + if (code < 0) + return code; + + /* Write the Functions. */ + + pdf_write_resource_objects(pdev, resourceFunction); + + /* Save viewer's memory with cleaning resources. */ + + if (pdev->MaxViewerMemorySize < 10000000) { + /* fixme: the condition above and the cleaning algorithm + may be improved with counting stored resource size + and creating multiple streams per page. */ + + if (pdev->ForOPDFRead) { + pdf_resource_t *pres = pdf_find_resource_by_resource_id(pdev, resourcePage, pdev->contents_id); + + if (pres != NULL) { + code = cos_dict_put_c_strings((cos_dict_t *)pres->object, "/.CleanResources", "/All"); + if (code < 0) + return code; + } + } + code = pdf_close_text_document(pdev); + if (code < 0) + return code; + code = pdf_write_and_free_all_resource_objects(pdev); + if (code < 0) + return code; + } + + /* Close use of text on the page. */ + + pdf_close_text_page(pdev); + + /* Accumulate text rotation. */ + + page->text_rotation.Rotate = + (pdev->params.AutoRotatePages == arp_PageByPage ? + pdf_dominant_rotation(&page->text_rotation) : -1); + { + int i; + + for (i = 0; i < countof(page->text_rotation.counts); ++i) + pdev->text_rotation.counts[i] += page->text_rotation.counts[i]; + } + + /* Record information from DSC comments. */ + + page->dsc_info = pdev->page_dsc_info; + if (page->dsc_info.orientation < 0) + page->dsc_info.orientation = pdev->doc_dsc_info.orientation; +#ifdef Bug688793 + if (page->dsc_info.viewing_orientation < 0) + page->dsc_info.viewing_orientation = + pdev->doc_dsc_info.viewing_orientation; +#endif + if (page->dsc_info.bounding_box.p.x >= page->dsc_info.bounding_box.q.x || + page->dsc_info.bounding_box.p.y >= page->dsc_info.bounding_box.q.y + ) + page->dsc_info.bounding_box = pdev->doc_dsc_info.bounding_box; + + /* Finish up. */ + + pdf_reset_page(pdev); + return (pdf_ferror(pdev) ? gs_note_error(gs_error_ioerror) : 0); +} + +/* Write the page object. */ +static double +round_box_coord(floatp xy) +{ + return (int)(xy * 100 + 0.5) / 100.0; +} +static int +pdf_write_page(gx_device_pdf *pdev, int page_num) +{ + long page_id = pdf_page_id(pdev, page_num); + pdf_page_t *page = &pdev->pages[page_num - 1]; + floatp mediabox[4] = {0, 0}; + stream *s; + + mediabox[2] = round_box_coord(page->MediaBox.x); + mediabox[3] = round_box_coord(page->MediaBox.y); + pdf_open_obj(pdev, page_id); + s = pdev->strm; + pprintg2(s, "<</Type/Page/MediaBox [0 0 %g %g]\n", + mediabox[2], mediabox[3]); + if (pdev->PDFX) { + const cos_value_t *v_trimbox = cos_dict_find_c_key(page->Page, "/TrimBox"); + floatp trimbox[4] = {0, 0}, bleedbox[4] = {0, 0}; + bool print_bleedbox = false; + + trimbox[2] = bleedbox[2] = mediabox[2]; + trimbox[3] = bleedbox[3] = mediabox[3]; + /* Offsets are [left right top bottom] according to the Acrobat 7.0 + distiller parameters manual, 12/7/2004, pp. 102-103. */ + if (v_trimbox != NULL && v_trimbox->value_type == COS_VALUE_SCALAR) { + const byte *p = v_trimbox->contents.chars.data; + char buf[100]; + int l = min (v_trimbox->contents.chars.size, sizeof(buf) - 1); + float temp[4]; /* the type is float for sscanf. */ + + memcpy(buf, p, l); + buf[l] = 0; + if (sscanf(buf, "[ %g %g %g %g ]", + &temp[0], &temp[1], &temp[2], &temp[3]) == 4) { + trimbox[0] = temp[0]; + trimbox[1] = temp[1]; + trimbox[2] = temp[2]; + trimbox[3] = temp[3]; + } + } else if (pdev->PDFXTrimBoxToMediaBoxOffset.size >= 4 && + pdev->PDFXTrimBoxToMediaBoxOffset.data[0] >= 0 && + pdev->PDFXTrimBoxToMediaBoxOffset.data[1] >= 0 && + pdev->PDFXTrimBoxToMediaBoxOffset.data[2] >= 0 && + pdev->PDFXTrimBoxToMediaBoxOffset.data[3] >= 0) { + trimbox[0] = mediabox[0] + pdev->PDFXTrimBoxToMediaBoxOffset.data[0]; + trimbox[1] = mediabox[1] + pdev->PDFXTrimBoxToMediaBoxOffset.data[3]; + trimbox[2] = mediabox[2] - pdev->PDFXTrimBoxToMediaBoxOffset.data[1]; + trimbox[3] = mediabox[3] - pdev->PDFXTrimBoxToMediaBoxOffset.data[2]; + } + if (pdev->PDFXSetBleedBoxToMediaBox) + print_bleedbox = true; + else if (pdev->PDFXBleedBoxToTrimBoxOffset.size >= 4 && + pdev->PDFXBleedBoxToTrimBoxOffset.data[0] >= 0 && + pdev->PDFXBleedBoxToTrimBoxOffset.data[1] >= 0 && + pdev->PDFXBleedBoxToTrimBoxOffset.data[2] >= 0 && + pdev->PDFXBleedBoxToTrimBoxOffset.data[3] >= 0) { + bleedbox[0] = trimbox[0] - pdev->PDFXBleedBoxToTrimBoxOffset.data[0]; + bleedbox[1] = trimbox[1] - pdev->PDFXBleedBoxToTrimBoxOffset.data[3]; + bleedbox[2] = trimbox[2] + pdev->PDFXBleedBoxToTrimBoxOffset.data[1]; + bleedbox[3] = trimbox[3] + pdev->PDFXBleedBoxToTrimBoxOffset.data[2]; + print_bleedbox = true; + } + if (cos_dict_find_c_key(page->Page, "/TrimBox") == NULL && + cos_dict_find_c_key(page->Page, "/ArtBox") == NULL) + pprintg4(s, "/TrimBox [%g %g %g %g]\n", + trimbox[0], trimbox[1], trimbox[2], trimbox[3]); + if (print_bleedbox && + cos_dict_find_c_key(page->Page, "/BleedBox") == NULL) + pprintg4(s, "/BleedBox [%g %g %g %g]\n", + bleedbox[0], bleedbox[1], bleedbox[2], bleedbox[3]); + } + pdf_print_orientation(pdev, page); + pprintld1(s, "/Parent %ld 0 R\n", pdev->Pages->id); + if (pdev->ForOPDFRead) { + if (page->NumCopies_set) + pprintld1(s, "/NumCopies %ld\n", page->NumCopies); + } + if (page->group_id > 0) { + pprintld1(s, "/Group %ld 0 R\n", page->group_id); + } + stream_puts(s, "/Resources<</ProcSet[/PDF"); + if (page->procsets & ImageB) + stream_puts(s, " /ImageB"); + if (page->procsets & ImageC) + stream_puts(s, " /ImageC"); + if (page->procsets & ImageI) + stream_puts(s, " /ImageI"); + if (page->procsets & Text) + stream_puts(s, " /Text"); + stream_puts(s, "]\n"); + { + int i; + + for (i = 0; i < countof(page->resource_ids); ++i) + if (page->resource_ids[i] && pdf_resource_type_names[i]) { + stream_puts(s, pdf_resource_type_names[i]); + pprintld1(s, " %ld 0 R\n", page->resource_ids[i]); + } + } + stream_puts(s, ">>\n"); + + /* Write the annotations array if any. */ + + if (page->Annots) { + stream_puts(s, "/Annots"); + COS_WRITE(page->Annots, pdev); + COS_FREE(page->Annots, "pdf_write_page(Annots)"); + page->Annots = 0; + } + /* + * The PDF documentation allows, and this code formerly emitted, + * a Contents entry whose value was an empty array. Acrobat Reader + * 3 and 4 accept this, but Acrobat Reader 5.0 rejects it. + * Fortunately, the Contents entry is optional. + */ + if (page->contents_id != 0) + pprintld1(s, "/Contents %ld 0 R\n", page->contents_id); + + /* Write any elements stored by pdfmarks. */ + + cos_dict_elements_write(page->Page, pdev); + + stream_puts(s, ">>\n"); + pdf_end_obj(pdev); + return 0; +} + +/* Wrap up ("output") a page. */ +static int +pdf_output_page(gx_device * dev, int num_copies, int flush) +{ + gx_device_pdf *const pdev = (gx_device_pdf *) dev; + int code = pdf_close_page(pdev); + + return (code < 0 ? code : + pdf_ferror(pdev) ? gs_note_error(gs_error_ioerror) : + gx_finish_output_page(dev, num_copies, flush)); +} + +/* Close the device. */ +static int +pdf_close(gx_device * dev) +{ + gx_device_pdf *const pdev = (gx_device_pdf *) dev; + gs_memory_t *mem = pdev->pdf_memory; + stream *s; + FILE *tfile = pdev->xref.file; + long xref; + long resource_pos; + long Catalog_id = pdev->Catalog->id, Info_id = pdev->Info->id, + Pages_id = pdev->Pages->id, Encrypt_id = 0; + long Threads_id = 0; + bool partial_page = (pdev->contents_id != 0 && pdev->next_page != 0); + int code = 0, code1; + + /* + * If this is an EPS file, or if the file didn't end with a showpage for + * some other reason, or if the file has produced no marks at all, we + * need to tidy up a little so as not to produce illegal PDF. However, + * if there is at least one complete page, we discard any leftover + * marks. + */ + if (pdev->next_page == 0) { + code = pdf_open_page(pdev, PDF_IN_STREAM); + + if (code < 0) + return code; + } + if (pdev->contents_id != 0) + pdf_close_page(pdev); + + /* Write the page objects. */ + + { + int i; + + for (i = 1; i <= pdev->next_page; ++i) + pdf_write_page(pdev, i); + } + + if (pdev->PrintStatistics) + pdf_print_resource_statistics(pdev); + + /* Write the font resources and related resources. */ + code1 = pdf_write_resource_objects(pdev, resourceXObject); + if (code >= 0) + code = code1; + code1 = pdf_free_resource_objects(pdev, resourceXObject); + if (code >= 0) + code = code1; + code1 = pdf_write_resource_objects(pdev, resourceGroup); + if (code >= 0) + code = code1; + code1 = pdf_free_resource_objects(pdev, resourceGroup); + if (code >= 0) + code = code1; + code1 = pdf_write_resource_objects(pdev, resourceSoftMaskDict); + if (code >= 0) + code = code1; + code1 = pdf_free_resource_objects(pdev, resourceSoftMaskDict); + if (code >= 0) + code = code1; + code1 = pdf_close_text_document(pdev); + if (code >= 0) + code = code1; + code1 = pdf_write_resource_objects(pdev, resourceCMap); + if (code >= 0) + code = code1; + code1 = pdf_free_resource_objects(pdev, resourceCMap); + if (code >= 0) + code = code1; + if (pdev->ResourcesBeforeUsage) + pdf_reverse_resource_chain(pdev, resourcePage); + code1 = pdf_write_resource_objects(pdev, resourcePage); + if (code >= 0) + code = code1; + code1 = pdf_free_resource_objects(pdev, resourcePage); + if (code >= 0) + code = code1; + + code1 = pdf_free_resource_objects(pdev, resourceOther); + if (code >= 0) + code = code1; + + + /* Create the Pages tree. */ + + pdf_open_obj(pdev, Pages_id); + s = pdev->strm; + stream_puts(s, "<< /Type /Pages /Kids [\n"); + /* Omit the last page if it was incomplete. */ + if (partial_page) + --(pdev->next_page); + { + int i; + + for (i = 0; i < pdev->next_page; ++i) + pprintld1(s, "%ld 0 R\n", pdev->pages[i].Page->id); + } + pprintd1(s, "] /Count %d\n", pdev->next_page); + pdev->text_rotation.Rotate = pdf_dominant_rotation(&pdev->text_rotation); + pdf_print_orientation(pdev, NULL); + cos_dict_elements_write(pdev->Pages, pdev); + stream_puts(s, ">>\n"); + pdf_end_obj(pdev); + + /* Close outlines and articles. */ + + if (pdev->outlines_id != 0) { + /* depth > 0 is only possible for an incomplete outline tree. */ + while (pdev->outline_depth > 0) { + code1 = pdfmark_close_outline(pdev); + if (code >= 0) + code = code1; + } + code = pdfmark_close_outline(pdev); + if (code >= 0) + code = code1; + pdf_open_obj(pdev, pdev->outlines_id); + pprintd1(s, "<< /Count %d", pdev->outlines_open); + pprintld2(s, " /First %ld 0 R /Last %ld 0 R >>\n", + pdev->outline_levels[0].first.id, + pdev->outline_levels[0].last.id); + pdf_end_obj(pdev); + } + if (pdev->articles != 0) { + pdf_article_t *part; + + /* Write the remaining information for each article. */ + for (part = pdev->articles; part != 0; part = part->next) + pdfmark_write_article(pdev, part); + } + + /* Write named destinations. (We can't free them yet.) */ + + if (pdev->Dests) + COS_WRITE_OBJECT(pdev->Dests, pdev); + + /* Write the PageLabel array */ + pdfmark_end_pagelabels(pdev); + if (pdev->PageLabels) { + COS_WRITE_OBJECT(pdev->PageLabels, pdev); + } + + /* Write the document metadata. */ + code1 = pdf_document_metadata(pdev); + if (code >= 0) + code = code1; + + /* Write the Catalog. */ + + /* + * The PDF specification requires Threads to be an indirect object. + * Write the threads now, if any. + */ + if (pdev->articles != 0) { + pdf_article_t *part; + + Threads_id = pdf_begin_obj(pdev); + s = pdev->strm; + stream_puts(s, "[ "); + while ((part = pdev->articles) != 0) { + pdev->articles = part->next; + pprintld1(s, "%ld 0 R\n", part->contents->id); + COS_FREE(part->contents, "pdf_close(article contents)"); + gs_free_object(mem, part, "pdf_close(article)"); + } + stream_puts(s, "]\n"); + pdf_end_obj(pdev); + } + pdf_open_obj(pdev, Catalog_id); + s = pdev->strm; + stream_puts(s, "<<"); + pprintld1(s, "/Type /Catalog /Pages %ld 0 R\n", Pages_id); + if (pdev->outlines_id != 0) + pprintld1(s, "/Outlines %ld 0 R\n", pdev->outlines_id); + if (Threads_id) + pprintld1(s, "/Threads %ld 0 R\n", Threads_id); + if (pdev->Dests) + pprintld1(s, "/Dests %ld 0 R\n", pdev->Dests->id); + if (pdev->PageLabels) + pprintld1(s, "/PageLabels << /Nums %ld 0 R >>\n", + pdev->PageLabels->id); + cos_dict_elements_write(pdev->Catalog, pdev); + stream_puts(s, ">>\n"); + pdf_end_obj(pdev); + if (pdev->Dests) { + COS_FREE(pdev->Dests, "pdf_close(Dests)"); + pdev->Dests = 0; + } + if (pdev->PageLabels) { + COS_FREE(pdev->PageLabels, "pdf_close(PageLabels)"); + pdev->PageLabels = 0; + pdev->PageLabels_current_label = 0; + } + + /* Prevent writing special named objects twice. */ + + pdev->Catalog->id = 0; + /*pdev->Info->id = 0;*/ /* Info should get written */ + pdev->Pages->id = 0; + { + int i; + + for (i = 0; i < pdev->num_pages; ++i) + if (pdev->pages[i].Page) + pdev->pages[i].Page->id = 0; + } + + /* + * Write the definitions of the named objects. + * Note that this includes Form XObjects created by BP/EP, named PS + * XObjects, and images named by NI. + */ + + do { + cos_dict_objects_write(pdev->local_named_objects, pdev); + } while (pdf_pop_namespace(pdev) >= 0); + cos_dict_objects_write(pdev->global_named_objects, pdev); + + /* Copy the resources into the main file. */ + + s = pdev->strm; + resource_pos = stell(s); + sflush(pdev->asides.strm); + { + FILE *rfile = pdev->asides.file; + long res_end = ftell(rfile); + + fseek(rfile, 0L, SEEK_SET); + pdf_copy_data(s, rfile, res_end, NULL); + } + + /* Write Encrypt. */ + if (pdev->OwnerPassword.size > 0) { + Encrypt_id = pdf_obj_ref(pdev); + + pdf_open_obj(pdev, Encrypt_id); + s = pdev->strm; + stream_puts(s, "<<"); + stream_puts(s, "/Filter /Standard "); + pprintld1(s, "/V %ld ", pdev->EncryptionV); + pprintld1(s, "/Length %ld ", pdev->KeyLength); + pprintld1(s, "/R %ld ", pdev->EncryptionR); + pprintld1(s, "/P %ld ", pdev->Permissions); + stream_puts(s, "/O "); + pdf_put_string(pdev, pdev->EncryptionO, sizeof(pdev->EncryptionO)); + stream_puts(s, "\n/U "); + pdf_put_string(pdev, pdev->EncryptionU, sizeof(pdev->EncryptionU)); + stream_puts(s, ">>\n"); + pdf_end_obj(pdev); + s = pdev->strm; + } + + /* Write the cross-reference section. */ + + xref = pdf_stell(pdev) - pdev->OPDFRead_procset_length; + if (pdev->FirstObjectNumber == 1) + pprintld1(s, "xref\n0 %ld\n0000000000 65535 f \n", + pdev->next_id); + else + pprintld2(s, "xref\n0 1\n0000000000 65535 f \n%ld %ld\n", + pdev->FirstObjectNumber, + pdev->next_id - pdev->FirstObjectNumber); + fseek(tfile, 0L, SEEK_SET); + { + long i; + + for (i = pdev->FirstObjectNumber; i < pdev->next_id; ++i) { + ulong pos; + char str[21]; + + fread(&pos, sizeof(pos), 1, tfile); + if (pos & ASIDES_BASE_POSITION) + pos += resource_pos - ASIDES_BASE_POSITION; + pos -= pdev->OPDFRead_procset_length; + sprintf(str, "%010ld 00000 n \n", pos); + stream_puts(s, str); + } + } + + /* Write the trailer. */ + + stream_puts(s, "trailer\n"); + pprintld3(s, "<< /Size %ld /Root %ld 0 R /Info %ld 0 R\n", + pdev->next_id, Catalog_id, Info_id); + stream_puts(s, "/ID ["); + psdf_write_string(pdev->strm, pdev->fileID, sizeof(pdev->fileID), 0); + psdf_write_string(pdev->strm, pdev->fileID, sizeof(pdev->fileID), 0); + stream_puts(s, "]\n"); + if (pdev->OwnerPassword.size > 0) { + pprintld1(s, "/Encrypt %ld 0 R ", Encrypt_id); + } + stream_puts(s, ">>\n"); + pprintld1(s, "startxref\n%ld\n%%%%EOF\n", xref); + + /* Release the resource records. */ + + { + pdf_resource_t *pres; + pdf_resource_t *prev; + + for (prev = pdev->last_resource; (pres = prev) != 0;) { + prev = pres->prev; + gs_free_object(mem, pres, "pdf_resource_t"); + } + pdev->last_resource = 0; + } + + /* Free named objects. */ + + cos_dict_objects_delete(pdev->local_named_objects); + COS_FREE(pdev->local_named_objects, "pdf_close(local_named_objects)"); + pdev->local_named_objects = 0; + cos_dict_objects_delete(pdev->global_named_objects); + COS_FREE(pdev->global_named_objects, "pdf_close(global_named_objects)"); + pdev->global_named_objects = 0; + + /* Wrap up. */ + + gs_free_object(mem, pdev->pages, "pages"); + pdev->pages = 0; + pdev->num_pages = 0; + + if (pdev->ForOPDFRead && pdev->OPDFReadProcsetPath.size) { + /* pdf_open_dcument could set up filters for entire document. + Removing them now. */ + int status; + + stream_putc(s, 0x04); + while (s->strm) { + s = s->strm; + } + status = s_close_filters(&pdev->strm, s); + if (status < 0 && code == 0) + code = gs_error_ioerror; + } + code1 = gdev_vector_close_file((gx_device_vector *) pdev); + if (code >= 0) + code = code1; + if (pdev->max_referred_page >= pdev->next_page + 1) { + /* Note : pdev->max_referred_page counts from 1, + and pdev->next_page counts from 0. */ + eprintf2("ERROR: A pdfmark destination page %d points beyond the last page %d.\n", + pdev->max_referred_page, pdev->next_page); +#if 0 /* Temporary disabled due to Bug 687686. */ + if (code >= 0) + code = gs_note_error(gs_error_rangecheck); +#endif + } + return pdf_close_files(pdev, code); +} |