aboutsummaryrefslogtreecommitdiffstats
path: root/z48.c
diff options
context:
space:
mode:
authorJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
committerJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
commit71bdb35d52747e6d7d9f55df4524d57c2966be94 (patch)
tree480ee5eefccc40d5f3331cc52d66f722fd19bfb9 /z48.c
parentb41263ea7578fa9742486135c762803b52794105 (diff)
downloadlout-71bdb35d52747e6d7d9f55df4524d57c2966be94.tar.gz
Lout 3.17.
git-svn-id: http://svn.savannah.nongnu.org/svn/lout/trunk@2 9365b830-b601-4143-9ba8-b4a8e2c3339c
Diffstat (limited to 'z48.c')
-rw-r--r--z48.c3706
1 files changed, 3706 insertions, 0 deletions
diff --git a/z48.c b/z48.c
new file mode 100644
index 0000000..6c66e43
--- /dev/null
+++ b/z48.c
@@ -0,0 +1,3706 @@
+/*@z48.c:PDF back end@********************************************************/
+/* */
+/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */
+/* COPYRIGHT (C) 1991, 1999 Jeffrey H. Kingston */
+/* */
+/* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */
+/* Basser Department of Computer Science */
+/* The University of Sydney 2006 */
+/* AUSTRALIA */
+/* */
+/* This PDF Back End module written by Vincent Tan, March 1998. */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either Version 2, or (at your option) */
+/* any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
+/* */
+/* FILE: z48.c */
+/* MODULE: PDF back end */
+/* EXTERNS: PDFFile_Init(), PDFFile_BeginFontEncoding(), */
+/* PDFFile_EndFontEncoding(), PDFFile_Cleanup(), */
+/* PDFPage_Init(), PDFPage_Cleanup(), PDFPage_Write(), */
+/* PDFPage_Push(), PDFPage_Pop(), PDFPage_Scale(), */
+/* PDFPage_Translate(), PDFPage_Rotate(), PDFPage_SetVars(), */
+/* PDFPage_WriteGraphic(), PDFPage_PrintUnderline(), */
+/* PDFFont_AddFont(), PDFFont_Set(), PDFText_OpenXY(), */
+/* PDFText_OpenX(), PDFText_Open(), PDFText_Kern(), */
+/* PDFText_Close(), PDFHasValidTextMatrix() */
+/* */
+/*****************************************************************************/
+#define PI 3.1415926535897931160
+#include "externs.h"
+
+
+/* ANSI headers */
+#include <ctype.h>
+#include <math.h>
+#include <time.h>
+
+/* zlib headers: define PDF_COMPRESSION = 0 if you don't have zlib library */
+#if PDF_COMPRESSION
+#include "zlib.h"
+#endif
+
+static void Assert(BOOLEAN condition, FILE_POS *inFilePos)
+{
+ if (!condition) /* allows me to set a breakpoint here */
+ assert(condition, inFilePos);
+}
+
+/* #define's and typedefs */
+#undef USE_MATRICES
+#undef _CALC_LARGEST_PAGE_OBJECT_
+
+enum {
+ kBase14FontCount = 14, /* there are 14 base PDF fonts */
+ kBufferSize = 1024 /* size of buffer for skipping non-marking commands */
+};
+
+#if PDF_COMPRESSION
+enum {
+ kRawOutputBufferSize = 4096, /* arbitrary choice */
+ kCompressedOutputBufferSize = 4096 /* arbitrary choice */
+};
+#endif
+
+enum {
+ kNumberOfObjectsPerBlock = 256, /* arbitrary choice */
+ kNumberOfPagesPerBlock = 64 /* arbitrary choice */
+};
+
+typedef enum {
+ kFitNoChange = 0, /* special default case */
+ kFit, /* [ /Fit ]: fit the page to the window */
+ kFitH, /* [ /FitH top ]: fit the width of the page to window; */
+ /* top specifies y-coord of the top edge of the window */
+ kFitV, /* [ /FitV left ]: fit the height of the page to the */
+ /* window. left specifies x-coord of left edge of win. */
+ kFitR, /* [ /FitR left bottom right top ]: fit the rectangle */
+ /* specified by left bottom right top in the window. */
+ /* If the height (top - bottom) and width (right-left) */
+ /* imply different zoom factors, the numerically */
+ /* smaller zoom factor is used, to ensure that the */
+ /* specified rectangle fits in the window */
+ kFitB, /* [ /FitB ]: fit the page's bounding box to window */
+ kFitBH, /* [ /FitBH top ]: fit the width of the page's bound. */
+ /* box to the window. top specifies the y-coordinate */
+ /* of the top edge of the window */
+ kFitBV, /* [ /FitBV left ]: fit the height of the page's */
+ /* bounding box to the window. left specifies the */
+ /* x-coordinate of the left edge of the window */
+
+ kNumberOfDestLinkOptions
+} PDF_LINK_DEST_OPTION;
+
+enum eUnitsKeywords {
+ k_in = 0,
+ k_cm,
+ k_pt,
+ k_em,
+ k_loutf,
+ k_loutv,
+ k_louts,
+ kNumberOfUnitKeywords
+};
+
+enum eGraphicsKeywords {
+ k_xsize = 0,
+ k_ysize,
+ k_xmark,
+ k_ymark,
+ kNumberOfGraphicsKeywords
+};
+
+enum eArithmeticKeywords {
+ k_add = 0,
+ k_sub,
+ k_mul,
+ k_div,
+ k_sin,
+ k_cos,
+ k_pick,
+ kNumberOfArithmeticKeywords
+};
+
+typedef enum {
+ k_link_source = 0, /* source of a link to an internal document target */
+ k_link_external, /* source of a link to an external document target */
+ k_link_URI, /* source of a link to an (external) URI target */
+ k_link_target, /* internal document target */
+ k_link_target_for_export, /* external document target */
+ kNumberOfLinkKeywords
+} PDF_LINK_KEYWORD;
+
+enum {
+ k_author = 0,
+ k_title,
+ k_subject,
+ k_keywords,
+ kNumberOfDocInfoKeywords
+};
+
+
+/* basic types */
+#ifdef USE_MATRICES
+typedef double t_matrix[9];
+#endif
+
+typedef char t_tempbuf[512];
+typedef unsigned int PDF_OBJECT_NUM;
+typedef PDF_OBJECT_NUM PDF_PAGE_OBJECT_NUM; /* an object number that can */
+ /* refer ONLY to page object */
+typedef unsigned int PDF_FONT_NUM;
+typedef unsigned int PDF_PAGE_NUM;
+typedef unsigned int PDF_FILE_OFFSET;
+
+
+/* font list */
+struct t_font_list_entry {
+ struct t_font_list_entry *m_next_font_entry;
+ FULL_CHAR *m_PDF_font_name;
+ FULL_CHAR *m_short_font_name;
+ FULL_CHAR *m_actual_font_name;
+ PDF_OBJECT_NUM m_font_encoding_obj; /* valid for entire PDF file */
+ PDF_OBJECT_NUM m_pdf_object_number; /* valid for entire PDF file */
+ BOOLEAN m_font_resource_in_pdf; /* TRUE when PDF file has */
+ /* /Type /Font resource */
+ BOOLEAN m_in_use; /* used on a per-page basis */
+};
+
+typedef struct t_font_list_entry t_font_list_entry, *t_font_list_entry_ptr;
+
+
+/* offsets of all objects (for xref list) */
+typedef PDF_FILE_OFFSET t_offset_array[kNumberOfObjectsPerBlock];
+
+struct t_offset_block {
+ struct t_offset_block *m_next_block;
+ t_offset_array m_block;
+};
+
+typedef struct t_offset_block t_offset_block, *t_offset_block_ptr;
+
+
+/* for /Pages object */
+typedef PDF_PAGE_OBJECT_NUM t_page_array[kNumberOfPagesPerBlock];
+
+struct t_page_block {
+ struct t_page_block *m_next_block;
+ t_page_array m_block;
+};
+
+typedef struct t_page_block t_page_block, *t_page_block_ptr;
+
+
+/* for font encodings */
+struct t_font_encoding_entry {
+ struct t_font_encoding_entry* m_next_entry;
+ PDF_OBJECT_NUM m_object_num;
+ FULL_CHAR *m_font_encoding;
+};
+
+typedef struct t_font_encoding_entry
+ t_font_encoding_entry, *t_font_encoding_entry_ptr;
+
+
+/* for qsave/qrestore [see PDFPage_Push()] */
+struct t_qsave_entry {
+ struct t_qsave_entry *m_next_entry;
+ int m_page_h_origin, m_page_v_origin;
+ float m_page_h_scale_factor, m_page_v_scale_factor;
+};
+
+typedef struct t_qsave_entry t_qsave_entry, *t_qsave_entry_ptr;
+
+
+/* for qsave/qrestore [see PDFPage_Push()] */
+struct t_qsave_marking_entry {
+ struct t_qsave_marking_entry* m_next_entry;
+ unsigned int m_buffer_pos;
+};
+
+typedef struct t_qsave_marking_entry t_qsave_marking_entry, *t_qsave_marking_entry_ptr;
+
+
+/* target of link annotations */
+struct t_target_annot_entry {
+ struct t_target_annot_entry* m_next_entry;
+
+ /* all of the following are always defined */
+ FULL_CHAR *m_name;
+ PDF_PAGE_OBJECT_NUM m_page_object_num;
+
+ /* these are in PDF's default user space coordinates */
+ int m_ll_x;
+ int m_ll_y;
+ int m_ur_x;
+ int m_ur_y;
+
+ BOOLEAN m_for_export;
+};
+
+typedef struct t_target_annot_entry t_target_annot_entry, *t_target_annot_entry_ptr;
+
+/* source of link annotations */
+struct t_source_annot_entry {
+ struct t_source_annot_entry* m_next_entry;
+
+ t_target_annot_entry* m_target; /* if is a link and this is NULL then */
+ /* the link is a fwd link and remains */
+ /* unresolvable until the page is */
+ /* encountered - instead, the m_name */
+ /* field is defined; m_target will be */
+ /* NULL for URI type links */
+
+ FULL_CHAR *m_name; /* this string is defined if m_target */
+ /* is NULL otherwise it is null */
+ /* for URI links, this contains the */
+ /* URI to link to */
+ FULL_CHAR *m_file_spec; /* only defined for link_type == */
+ /* k_link_external */
+
+ /* all of the following are always defined */
+ /* these are in PDF's default user space coordinates */
+ int m_ll_x;
+ int m_ll_y;
+ int m_ur_x;
+ int m_ur_y;
+
+ PDF_OBJECT_NUM m_this_object_num; /* obj num of this "/Type /Annot" obj */
+ PDF_PAGE_OBJECT_NUM m_this_page_object_num; /* obj num of the page that */
+ /* this annot lies in */
+ PDF_LINK_DEST_OPTION m_dest_option;
+ PDF_LINK_KEYWORD m_link_type;
+
+ BOOLEAN m_written_to_PDF_file;
+};
+
+typedef struct t_source_annot_entry t_source_annot_entry, *t_source_annot_entry_ptr;
+
+
+/* matrices */
+#ifdef USE_MATRICES
+struct t_matrix_entry {
+ struct t_matrix_entry* m_next_entry;
+ t_matrix m_matrix;
+};
+
+typedef struct t_matrix_entry t_matrix_entry, *t_matrix_entry_ptr;
+#endif
+
+
+/* statics */
+
+/* general */
+static BOOLEAN g_PDF_debug;
+
+/* objects */
+static PDF_OBJECT_NUM g_next_objnum;
+static t_offset_block_ptr g_obj_offset_list; /* first block */
+static t_offset_block_ptr g_cur_obj_offset_block;
+
+/* fonts */
+static t_font_list_entry_ptr g_font_list; /* backwards */
+static t_font_encoding_entry_ptr g_font_encoding_list; /* backwards */
+
+/* pages */
+static PDF_PAGE_NUM g_page_count; /* current page num, */
+ /* starting at 1 */
+static PDF_PAGE_OBJECT_NUM g_page_object_num; /* obj num of current*/
+ /* "/Type /Page" obj,*/
+ /* corr. to page */
+ /* num g_page_count */
+static t_page_block_ptr g_page_block_list; /* first block */
+static t_page_block_ptr g_cur_page_block;
+static PDF_OBJECT_NUM g_pages_root;
+
+/* document */
+static int g_doc_h_bound;
+static int g_doc_v_bound;
+static FULL_CHAR* g_doc_author;
+static FULL_CHAR* g_doc_title;
+static FULL_CHAR* g_doc_subject;
+static FULL_CHAR* g_doc_keywords;
+
+/* link annotations */
+static t_target_annot_entry_ptr g_target_annot_list;
+static BOOLEAN g_has_exported_targets;
+
+/* globals for each page */
+/* these indicate what kind of content the page has */
+static BOOLEAN g_page_uses_fonts;
+static BOOLEAN g_page_has_text;
+static BOOLEAN g_page_has_graphics;
+
+/* these are only defined when the page has some content */
+static PDF_OBJECT_NUM g_page_contents_obj_num;
+static PDF_OBJECT_NUM g_page_length_obj_num;
+static PDF_FILE_OFFSET g_page_start_offset;
+
+/* valid after a PDF_Push and PDF_Pop */
+static t_qsave_entry_ptr g_qsave_stack;
+
+static t_qsave_marking_entry_ptr g_qsave_marking_stack; /* implemented as a */
+ /* linked list; pts */
+ /* to top of stack */
+static BOOLEAN g_in_buffering_mode;
+static char g_buffer[kBufferSize]; /* this buffer is used*/
+ /* for removing redundant operations */
+static unsigned int g_buffer_pos;
+
+/* valid after a link annotation has been defined */
+static t_source_annot_entry_ptr g_source_annot_list;
+
+#ifdef USE_MATRICES
+static t_matrix g_cur_matrix;
+static t_matrix_entry_ptr g_matrix_stack;
+#endif
+
+/* track these values in case they are ever required */
+static float g_page_h_scale_factor, g_page_v_scale_factor;
+static int g_page_h_origin, g_page_v_origin;
+static int g_page_line_width;
+
+/* magic keywords (actually they will appear in Lout documents as "__in", "__cm", etc.) */
+static char *g_unit_keywords[kNumberOfUnitKeywords] =
+{
+ "in", "cm", "pt", "em", "loutf", "loutv", "louts" /* MUST be followed by a fp number */
+};
+
+static char *g_graphic_keywords[kNumberOfGraphicsKeywords] =
+{
+ "xsize", "ysize", "xmark", "ymark" /* like macros, these expand to the actual value */
+};
+
+static char *g_arithmetic_keywords[kNumberOfArithmeticKeywords] =
+{
+ /* syntax: "__mul(x, y)" emits (x * y) to 2 decimal places */
+ /* */
+ /* Notes: */
+ /* */
+ /* sin and cos expect their arguments in degrees */
+ /* */
+ /* for negation, use "__sub(0, arg)" */
+ /* */
+ /* __pick(i, expr1, expr2, expr3...) picks the ith expr from the */
+ /* list of expr the "," are optional (if they are not used, you */
+ /* should separate values with whitespace) */
+
+ "add", "sub", "mul", "div", "sin", "cos", "pick" /* like macros, these expand to the actual value */
+};
+
+static char *g_link_keywords[kNumberOfLinkKeywords] =
+{
+ /* syntax: "__link_source=<<name_of_target_link [dest_link_option]>>" */
+ /* */
+ /* example: "__link_source=<<chapter6>>" */
+ /* example: "__link_source=<<part7 __FitH>>" */
+
+ "link_source=<<",
+
+ /* syntax: "__link_external=<<name_of_target_link __link_to=file_spec>>"*/
+ /* syntax: "__link_external=<<name_of_target_link __link_to=<< /FS /URL /F (url)>>>>" */
+ /* */
+ /* ** note the special format required for URL links ** */
+ /* */
+ /* example: "__link_external=<<chapter6 __link_to=/usr/bin/file.pdf>>" */
+ /* example: "__link_external=<<chapter6 __link_to=<< /FS /URL /F */
+ /* (ftp://ftp.cs.usyd.edu.au/jeff/lout/user.pdf) >>>>" */
+
+ "link_external=<<",
+
+ /* syntax: "__link_URI=<<URL>>" */
+ /* */
+ /* example: "__link_URI=<<http://www.adobe.com>>" */
+
+ "link_URI=<<",
+
+ /* syntax: "__link_target=<<name_of_target_link>>" where */
+ /* name_of_target_link is in this PDF file; name_of_target_link CANNOT */
+ /* be accessed by external documents in links */
+ /* */
+ /* example: "__link_target=<<my_internal_target>>" */
+
+ "link_target=<<",
+
+ /* syntax: "__link_target_for_export=<<name_of_target_link>>" where */
+ /* name_of_target_link is in this file; name_of_target_link can be */
+ /* accessed by external documents in links */
+ /* */
+ /* example: "__link_target_for_export=<<my_exported_target>>" */
+
+ "link_target_for_export=<<"
+};
+
+static char *g_dest_link_options[kNumberOfDestLinkOptions] =
+{
+ /* see PDF_LINK_DEST_OPTION for descriptions of the meanings of these */
+ "__FitNoChange",
+ "__Fit",
+ "__FitH",
+ "__FitV",
+ "__FitR",
+ "__FitB",
+ "__FitBH",
+ "__FitBV"
+};
+
+static char* g_external_file_spec_keyword[1] =
+{
+ "__link_to="
+};
+
+static char* g_doc_info_keywords[kNumberOfDocInfoKeywords] =
+{
+ "author=", "title=", "subject=", "keywords="
+};
+
+static int g_units[kNumberOfUnitKeywords];
+
+static int g_graphics_vars[kNumberOfGraphicsKeywords];
+
+/* text state */
+static BOOLEAN g_TJ_pending;
+static BOOLEAN g_ET_pending;
+static BOOLEAN g_valid_text_matrix; /* true when BT...ET block open */
+
+
+/* expressions */
+static int g_expr_depth = 0;
+static int g_expr_index;
+static t_tempbuf g_expr;
+
+/* links */
+static int g_link_depth = 0;
+static int g_link_index;
+static t_tempbuf g_link;
+static PDF_LINK_KEYWORD g_link_keyword;
+
+/* the 14 base fonts */
+static char *g_standard_base_14_fonts[kBase14FontCount] = {
+ "Courier",
+ "Courier-Bold",
+ "Courier-Oblique",
+ "Courier-BoldOblique",
+ "Helvetica",
+ "Helvetica-Bold",
+ "Helvetica-Oblique",
+ "Helvetica-BoldOblique",
+ "Symbol",
+ "Times",
+ "Times-Bold",
+ "Times-Italic",
+ "Times-BoldItalic",
+ "ZapfDingbats"
+};
+
+
+#if PDF_COMPRESSION
+static BOOLEAN g_apply_compression;
+static z_stream g_comp_stream; /* zlib compression stream */
+static unsigned char* g_raw_buffer_ptr;
+
+/* compression buffers */
+static unsigned char g_raw_output[kRawOutputBufferSize];
+static unsigned char g_compressed_output[kCompressedOutputBufferSize];
+#endif
+
+/* for calculating largest page object */
+#ifdef _CALC_LARGEST_PAGE_OBJECT_
+static PDF_FILE_OFFSET g_max_page_length = 0;
+#endif
+
+BOOLEAN PDFHasValidTextMatrix(void) /* this is called from z24.c */
+{
+ return g_valid_text_matrix;
+}
+
+
+/*****************************************************************************/
+/* */
+/* t_offset_block_ptr PDFObject_FindOffsetBlock(PDF_OBJECT_NUM in_obj_num) */
+/* */
+/* Find the offset block for the given object number. */
+/* */
+/*****************************************************************************/
+
+static t_offset_block_ptr PDFObject_FindOffsetBlock(PDF_OBJECT_NUM in_obj_num,
+ unsigned int* out_block_pos)
+{
+ int wanted_block_num = (in_obj_num - 1) / kNumberOfObjectsPerBlock;
+ int block_pos = (in_obj_num - 1) % kNumberOfObjectsPerBlock;
+ t_offset_block_ptr the_block = g_obj_offset_list;
+
+ Assert((in_obj_num > 0) && (in_obj_num < g_next_objnum), no_fpos);
+
+ /* find block */
+ while (wanted_block_num != 0) {
+ Assert(the_block != NULL, no_fpos);
+ the_block = the_block->m_next_block;
+ wanted_block_num--;
+ }
+ Assert(the_block != NULL, no_fpos);
+
+ if (out_block_pos != NULL)
+ *out_block_pos = block_pos;
+ return the_block;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDF_OBJECT_NUM PDFObject_New(FILE* in_fp) */
+/* */
+/* Return the next available object number. */
+/* */
+/*****************************************************************************/
+
+static PDF_OBJECT_NUM PDFObject_New(/* FILE* in_fp */)
+{
+ int wanted_block_num = (g_next_objnum - 1) / kNumberOfObjectsPerBlock;
+ int block_pos = (g_next_objnum - 1) % kNumberOfObjectsPerBlock;
+ t_offset_block_ptr the_block = g_cur_obj_offset_block;
+
+ /* if first obj in a block then allocate the block */
+ if (block_pos == 0)
+ {
+ the_block = (t_offset_block_ptr) malloc(sizeof(t_offset_block));
+ if (the_block == NULL)
+ Error(48, 1, "PDFObject_New: run out of memory", FATAL, no_fpos);
+ if (wanted_block_num == 0) /* if first block in file */
+ {
+ Assert(g_obj_offset_list == NULL, no_fpos);
+ g_obj_offset_list = the_block;
+ }
+ else
+ {
+ Assert(g_cur_obj_offset_block != NULL, no_fpos);
+ g_cur_obj_offset_block->m_next_block = the_block;
+ }
+ the_block->m_next_block = NULL; /* don't forget to init this! */
+ g_cur_obj_offset_block = the_block;
+ }
+ else
+ {
+ Assert(the_block != NULL, no_fpos);
+ }
+
+ /* initialise the offset of this object to zero (it hasn't been written */
+ /* to the PDF file yet) */
+ the_block->m_block[block_pos] = 0; /* ftell(in_fp); */
+
+ return g_next_objnum++;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFObject_WriteRef(FILE* in_fp, PDF_OBJECT_NUM in_object_number) */
+/* */
+/* Return the next available object number and write a reference to it. */
+/* */
+/*****************************************************************************/
+
+static void PDFObject_WriteRef(FILE* in_fp, PDF_OBJECT_NUM in_object_number)
+{
+ fprintf(in_fp, "%u 0 R", in_object_number);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFObject_WriteObj(FILE* in_fp, PDF_OBJECT_NUM in_object_number) */
+/* */
+/* Write the object's definition (and remember its file position). */
+/* */
+/*****************************************************************************/
+
+static void PDFObject_WriteObj(FILE* in_fp, PDF_OBJECT_NUM in_object_number)
+{
+ unsigned int block_pos;
+ t_offset_block_ptr block =
+ PDFObject_FindOffsetBlock(in_object_number, &block_pos);
+ Assert(block->m_block[block_pos]==0, no_fpos); /* offset shd be "unknown" */
+ block->m_block[block_pos] = ftell(in_fp); /* remember offset for xref */
+
+ fprintf(in_fp, "%u 0 obj\n", in_object_number);
+}
+
+/*****************************************************************************/
+/* */
+/* PDF_OBJECT_NUM PDFObject_WriteNewObj(FILE* in_fp) */
+/* */
+/* Return the next available object number and write its definition. */
+/* */
+/*****************************************************************************/
+
+static PDF_OBJECT_NUM PDFObject_WriteNewObj(FILE* in_fp)
+{
+ PDF_OBJECT_NUM next_ref = PDFObject_New(/* in_fp */);
+ PDFObject_WriteObj(in_fp, next_ref);
+ return next_ref;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDF_OBJECT_NUM PDFObject_WriteNextRef(FILE* in_fp) */
+/* */
+/* Return the next available object number and write a reference to it. */
+/* */
+/*****************************************************************************/
+
+/* ***
+static PDF_OBJECT_NUM PDFObject_WriteNextRef(FILE* in_fp)
+{
+ PDF_OBJECT_NUM next_ref = PDFObject_New(in_fp);
+ PDFObject_WriteRef(in_fp, next_ref);
+ return next_ref;
+}
+*** */
+
+
+/*****************************************************************************/
+/* */
+/* void PDFFile_BeginFontEncoding(FILE* in_fp, const char* in_encoding_name) */
+/* */
+/* Begin font encoding. */
+/* */
+/*****************************************************************************/
+
+void PDFFile_BeginFontEncoding(FILE* in_fp, const char* in_encoding_name)
+{
+ PDF_OBJECT_NUM encoding_num;
+ t_font_encoding_entry_ptr encoding_entry;
+
+ /* TO FILL IN: create entry in font-encoding list and add this encoding */
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% font encoding '%s':\n%%\n", in_encoding_name);
+
+ encoding_num = PDFObject_WriteNewObj(in_fp);
+ fputs("<<\n/Type /Encoding\n/Differences [ 0\n", in_fp);
+
+ /* add font encoding to list of encodings (assume we don't get passed the */
+ /* same encoding more than once) */
+ encoding_entry =
+ (t_font_encoding_entry_ptr) malloc(sizeof(t_font_encoding_entry));
+ if (encoding_entry == NULL)
+ Error(48, 2, "PDFFile_BeginFontEncoding: run out of memory",FATAL,no_fpos);
+
+ encoding_entry->m_font_encoding =
+ (FULL_CHAR*) malloc(strlen(in_encoding_name) + 1);
+ if (encoding_entry->m_font_encoding == NULL)
+ Error(48, 3, "PDFFile_BeginFontEncoding: out of memory", FATAL, no_fpos);
+
+ encoding_entry->m_next_entry = g_font_encoding_list;
+ encoding_entry->m_object_num = encoding_num;
+ strcpy((char*) encoding_entry->m_font_encoding, (char*) in_encoding_name);
+ g_font_encoding_list = encoding_entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFFile_EndFontEncoding(FILE* in_fp) */
+/* */
+/* End of font encoding. */
+/* */
+/*****************************************************************************/
+
+void PDFFile_EndFontEncoding(FILE* in_fp)
+{
+ fputs("]\n>>\nendobj\n", in_fp);
+}
+
+
+/*****************************************************************************/
+/* */
+/* const FULL_CHAR* PDFFont_FindListEntry(const FULL_CHAR* in_real_font_name)*/
+/* */
+/* Try to find the font list entry with the specified real font name; */
+/* return the entry's reference if found else return NULL. */
+/* */
+/*****************************************************************************/
+
+static t_font_list_entry_ptr
+ PDFFont_FindListEntry(const FULL_CHAR* in_real_font_name)
+{
+ t_font_list_entry_ptr entry = g_font_list;
+
+ while (entry != NULL) {
+ if (strcmp((char*)in_real_font_name, (char*)entry->m_actual_font_name)==0)
+ break;
+ entry = entry->m_next_font_entry;
+ }
+ return entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* const FULL_CHAR* */
+/* PDFFont_FindListEntry_Short(const FULL_CHAR* in_short_font_name) */
+/* */
+/* Try to find the font list entry with the specified real font name; */
+/* return the entry's reference if found else return NULL. */
+/* */
+/*****************************************************************************/
+
+static t_font_list_entry_ptr
+ PDFFont_FindListEntry_Short(const FULL_CHAR* in_short_font_name)
+{
+ t_font_list_entry_ptr entry = g_font_list;
+
+ while (entry != NULL) {
+ if (strcmp((char*)in_short_font_name, (char*)entry->m_short_font_name)==0)
+ break;
+ entry = entry->m_next_font_entry;
+ }
+ return entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* const t_font_list_entry_ptr PDFFont_NewListEntry( */
+/* const FULL_CHAR* in_short_font_name, const FULL_CHAR* in_real_font_name)*/
+/* */
+/* Create a new font entry and return the short name of the font. */
+/* */
+/*****************************************************************************/
+
+static t_font_list_entry_ptr
+ PDFFont_NewListEntry(const FULL_CHAR* in_short_font_name,
+ const FULL_CHAR* in_real_font_name,
+ PDF_OBJECT_NUM in_font_encoding_obj)
+{
+ PDF_FONT_NUM next_font_num = 0;
+ t_font_list_entry_ptr new_entry = g_font_list;
+ /* t_font_list_entry_ptr last_font_list_entry = NULL; */
+
+ /* find next available font number */
+ {
+ while (new_entry != NULL) {
+ next_font_num++;
+ new_entry = new_entry->m_next_font_entry;
+ }
+ }
+
+ /* make a new font list entry */
+ {
+ char PDF_font_name[64] = "/F";
+ char num[32];
+
+ new_entry = (t_font_list_entry_ptr) malloc(sizeof(t_font_list_entry));
+ if (new_entry == NULL)
+ Error(48, 4, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
+
+ sprintf(num, "%u", next_font_num);
+ strcat(PDF_font_name, num);
+
+ new_entry->m_PDF_font_name =
+ (FULL_CHAR*) malloc(strlen((char*) PDF_font_name) + 1);
+ if (new_entry->m_PDF_font_name == NULL)
+ Error(48, 5, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
+ strcpy((char*) new_entry->m_PDF_font_name, PDF_font_name);
+
+ new_entry->m_short_font_name =
+ (FULL_CHAR*) malloc(strlen((char*) in_short_font_name) + 1);
+ if (new_entry->m_short_font_name == NULL)
+ Error(48, 6, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
+ strcpy((char*) new_entry->m_short_font_name, (char*) in_short_font_name);
+
+ new_entry->m_actual_font_name =
+ (FULL_CHAR*) malloc(strlen((char*) in_real_font_name) + 1);
+ if (new_entry->m_actual_font_name == NULL)
+ Error(48, 7, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
+ strcpy((char*) new_entry->m_actual_font_name, (char*) in_real_font_name);
+
+ new_entry->m_font_encoding_obj = in_font_encoding_obj;
+
+ new_entry->m_pdf_object_number = 0; /* don't give this font resource an */
+ /* object number until needed */
+ /* new_entry->m_in_use = TRUE; */ /* should be cleared after each page */
+ /* g_page_uses_fonts = TRUE; */
+ new_entry->m_font_resource_in_pdf = FALSE; /* not in PDF file yet */
+
+ new_entry->m_next_font_entry = g_font_list;
+ g_font_list = new_entry;
+ }
+ debug1(DPD, D, "new PDF font entry with short name %s",
+ new_entry->m_short_font_name);
+ return new_entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* const t_font_list_entry_ptr PDFGetFont(const char* in_real_font_name) */
+/* */
+/* Return the reference of a font entry. Never returns NULL. */
+/* */
+/*****************************************************************************/
+/*
+static t_font_list_entry_ptr PDFGetFont(const FULL_CHAR* in_real_font_name)
+{
+ t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
+ if (entry == NULL)
+ entry = PDFFont_NewListEntry(in_real_font_name);
+ return entry;
+}
+*/
+
+/*****************************************************************************/
+/* */
+/* PDFFont_WriteObjectRef(FILE* in_fp, t_font_list_entry_ptr in_font_entry) */
+/* */
+/* Write a reference to the object to the file. */
+/* */
+/*****************************************************************************/
+
+static void PDFFont_WriteObjectRef(FILE* in_fp,
+ const t_font_list_entry* in_font_entry)
+{
+ Assert(in_font_entry->m_pdf_object_number != 0, no_fpos);
+ PDFObject_WriteRef(in_fp, in_font_entry->m_pdf_object_number);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFFont_WriteObject(FILE* in_fp, */
+/* t_font_list_entry_ptr in_font_entry) */
+/* */
+/* Write a reference to the object to the file. */
+/* */
+/*****************************************************************************/
+
+static void PDFFont_WriteObject(FILE* in_fp, t_font_list_entry_ptr in_font_entry)
+{
+ if (in_font_entry->m_pdf_object_number == 0)
+ in_font_entry->m_pdf_object_number = PDFObject_New(/* in_fp */);
+ PDFObject_WriteObj(in_fp, in_font_entry->m_pdf_object_number);
+}
+
+
+/*****************************************************************************/
+/* */
+/* BOOLEAN PDFFont_IsOneOfTheBase14Fonts(const FULL_CHAR* in_real_font_name)*/
+/* */
+/* Returns true if given font is one of the base 14 fonts. */
+/* */
+/*****************************************************************************/
+
+static BOOLEAN PDFFont_IsOneOfTheBase14Fonts(const FULL_CHAR* in_real_font_name)
+{
+ int i;
+ for (i = 0; i < kBase14FontCount; i++)
+ if (strcmp(g_standard_base_14_fonts[i], (char*) in_real_font_name) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFFont_WriteFontResource(FILE* in_fp, */
+/* t_font_list_entry_ptr in_font_entry) */
+/* */
+/* Writes out the PDF idea of a Font resource. */
+/* */
+/*****************************************************************************/
+
+static void PDFFont_WriteFontResource(FILE* in_fp,
+ t_font_list_entry_ptr in_font_entry)
+{
+ if (! in_font_entry->m_font_resource_in_pdf)
+ {
+ in_font_entry->m_font_resource_in_pdf = TRUE;
+
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% declare use of font %s:\n%%\n",
+ in_font_entry->m_actual_font_name);
+
+ PDFFont_WriteObject(in_fp, in_font_entry);
+ fputs("<<\n/Type /Font\n/Subtype /Type1\n", in_fp);
+ fprintf(in_fp, "/Name %s\n", (char*) in_font_entry->m_PDF_font_name);
+ fprintf(in_fp, "/BaseFont /%s\n", (char*) in_font_entry->m_actual_font_name);
+ if (! PDFFont_IsOneOfTheBase14Fonts(in_font_entry->m_actual_font_name))
+ {
+ /* ***
+ fputs("/FirstChar 0"\n, in_fp); - we don't do first chars (yet)
+ fputs("/LastChar 255\n", in_fp); - we don't do last chars (yet)
+ fputs("/Widths ", in_fp); - we don't do last chars (yet)
+ fputs("/FontDescriptor ", in_fp); - we don't do font descriptors (yet)
+ *** */
+ }
+
+ if (in_font_entry->m_font_encoding_obj != 0)
+ {
+ fputs("/Encoding ", in_fp);
+ PDFObject_WriteRef(in_fp, in_font_entry->m_font_encoding_obj);
+ fputs("\n", in_fp);
+ }
+
+ /* ***
+ else
+ Error(48, 8, "PDFFont_WriteFontResource: a font has no encoding",
+ WARN, no_fpos);
+ *** */
+ fputs(">>\nendobj\n", in_fp);
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFFont_WriteFontResource_name(FILE* in_fp, */
+/* const FULL_CHAR* in_real_font_name) */
+/* */
+/* Writes out the PDF idea of a Font resource. */
+/* */
+/*****************************************************************************/
+
+/* ***
+static void PDFFont_WriteFontResource_name(FILE* in_fp,
+ const FULL_CHAR* in_real_font_name)
+{
+ t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
+ Assert(entry != NULL, no_fpos);
+ PDFFont_WriteFontResource(in_fp, entry);
+}
+*** */
+
+
+/*****************************************************************************/
+/* */
+/* const FULL_CHAR* PDFGetPDFFontName(const FULL_CHAR* in_real_font_name) */
+/* */
+/* Return the short name of a font. */
+/* */
+/*****************************************************************************/
+
+/* ***
+static const FULL_CHAR* PDFGetPDFFontName(const FULL_CHAR* in_real_font_name)
+{
+ t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
+ Assert(entry != NULL, no_fpos);
+ return entry->m_PDF_font_name;
+}
+*** */
+
+
+/*****************************************************************************/
+/* */
+/* PDF_OBJECT_NUM PDFFont_FindFontEncoding(FULL_CHAR* in_font_encoding_name) */
+/* */
+/* Return the object number of a given font encoding. */
+/* */
+/*****************************************************************************/
+
+static PDF_OBJECT_NUM PDFFont_FindFontEncoding(
+ const FULL_CHAR* in_font_encoding_name)
+{
+ t_font_encoding_entry_ptr entry = g_font_encoding_list;
+
+ while (entry != NULL)
+ {
+ if (strcmp((char*)in_font_encoding_name, (char*)entry->m_font_encoding)==0)
+ break;
+ entry = entry->m_next_entry;
+ }
+ return (entry != NULL) ? entry->m_object_num : 0;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFFont_AddFont( */
+/* FILE* in_fp, const FULL_CHAR* in_short_font_name, */
+/* const FULL_CHAR* in_real_font_name) */
+/* */
+/* Add this font to the list of fonts that the document uses. Also remember */
+/* that this font is "in use" (output when page resources are written). */
+/* */
+/*****************************************************************************/
+
+void PDFFont_AddFont(FILE* in_fp, const FULL_CHAR* in_short_font_name,
+ const FULL_CHAR* in_real_font_name, const FULL_CHAR* in_font_encoding_name)
+{
+ t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
+ debug4(DPD, D, "PDFFont_AddFont(-, %s, %s, %s) [new = %s]",
+ in_short_font_name, in_real_font_name, in_font_encoding_name,
+ bool(entry == NULL));
+ /* *** this attempted bug fix by Jeff K. problem may be multiple font
+ entries for the same font
+ if (entry == NULL)
+ *** */
+ if (TRUE)
+ entry = PDFFont_NewListEntry(in_short_font_name, in_real_font_name,
+ PDFFont_FindFontEncoding(in_font_encoding_name));
+
+ /* ***
+ entry->m_in_use = TRUE;
+ g_page_uses_fonts = TRUE;
+ *** */
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_SetVars(int xsize, int ysize, int xmark, int ymark, */
+/* int loutf, int loutv, int louts) */
+/* */
+/* Writes a string to the page's stream. */
+/* */
+/*****************************************************************************/
+
+void PDFPage_SetVars(int xsize, int ysize, int xmark, int ymark,
+ int loutf, int loutv, int louts)
+{
+ g_graphics_vars[k_xsize] = xsize;
+ g_graphics_vars[k_ysize] = ysize;
+ g_graphics_vars[k_xmark] = xmark;
+ g_graphics_vars[k_ymark] = ymark;
+
+ g_units[k_loutf] = loutf;
+ g_units[k_loutv] = loutv;
+ g_units[k_louts] = louts;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_FlushCompressedBuffer(FILE* in_fp) */
+/* */
+/* Flushes the compressed output buffer to the page's stream. */
+/* */
+/*****************************************************************************/
+
+#if PDF_COMPRESSION
+static void PDFPage_FlushCompressedBuffer(FILE* in_fp)
+{
+ int err;
+
+ Assert(g_apply_compression, no_fpos);
+ do {
+ err = deflate(&g_comp_stream, Z_FINISH);
+
+ fwrite(g_compressed_output,
+ sizeof(g_compressed_output) - g_comp_stream.avail_out, 1, in_fp);
+ g_comp_stream.next_out = g_compressed_output;
+ g_comp_stream.avail_out = sizeof(g_compressed_output);
+ } while (err == Z_OK);
+
+ if (err != Z_STREAM_END)
+ Error(48, 9, "PDFPage_FlushCompressedBuffer: zlib error occurred",
+ FATAL, no_fpos);
+
+ err = deflateEnd(&g_comp_stream);
+ if (err != Z_OK)
+ Error(48, 10, "PDFPage_FlushCompressedBuffer: zlib error occurred",
+ FATAL, no_fpos);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_FlushRawBuffer(FILE* in_fp) */
+/* */
+/* Attempts to compress the raw buffer; also flushes the compressed output */
+/* buffer to the page's stream if it is full. */
+/* */
+/*****************************************************************************/
+
+static void PDFPage_FlushRawBuffer(FILE* in_fp)
+{
+ int err;
+
+ /* compress the raw buffer */
+ Assert(g_apply_compression, no_fpos);
+ g_comp_stream.next_in = g_raw_output;
+ g_comp_stream.avail_in = (uInt) (g_raw_buffer_ptr - g_raw_output);
+ Assert(g_comp_stream.avail_out != 0, no_fpos);
+
+ /* always compress to the point where the raw buffer is empty */
+ do {
+ err = deflate(&g_comp_stream, Z_NO_FLUSH);
+ if (err != Z_OK)
+ Error(48, 11, "PDFPage_FlushRawBuffer: zlib error occurred",FATAL,no_fpos);
+
+ /* IF compressed output buffer is full THEN flush it to disk and reset it */
+ if (g_comp_stream.avail_out == 0)
+ {
+ if (fwrite(g_compressed_output, sizeof(g_compressed_output), 1, in_fp)!=1)
+ Error(48, 12, "PDFPage_FlushRawBuffer: write error occurred",
+ FATAL, no_fpos);
+ g_comp_stream.next_out = g_compressed_output;
+ g_comp_stream.avail_out = sizeof(g_compressed_output);
+ }
+ } while (g_comp_stream.avail_in != 0);
+
+ /* reset raw buffer for next call */
+ g_raw_buffer_ptr = g_raw_output;
+}
+#endif
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_WriteStream(FILE* in_fp, char* in_str) */
+/* */
+/* Writes a string to the page's stream. */
+/* */
+/*****************************************************************************/
+
+static void PDFPage_WriteStream(FILE* in_fp, char* in_str)
+{
+ if (*in_str == 0)
+ return;
+
+#if PDF_COMPRESSION
+ if (g_apply_compression)
+ {
+ unsigned int total = strlen(in_str);
+ char *ptr = in_str;
+
+ while (total != 0)
+ {
+ unsigned int len = total;
+ BOOLEAN needToFlush =
+ ((g_raw_buffer_ptr + len) > (g_raw_output + sizeof(g_raw_output)));
+ if (needToFlush)
+ len = g_raw_output + sizeof(g_raw_output) - g_raw_buffer_ptr;
+ memcpy(g_raw_buffer_ptr, ptr, len);
+
+ ptr += len;
+ g_raw_buffer_ptr += len;
+ total -= len;
+
+ /* IF need to flush raw buffer THEN do so */
+ if (needToFlush) PDFPage_FlushRawBuffer(in_fp);
+ } /* while still have bytes to process */
+ }
+ else
+#endif
+ fputs(in_str, in_fp);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Begin(FILE* in_fp) */
+/* */
+/* Begins the page's stream. */
+/* */
+/*****************************************************************************/
+
+static void PDFPage_Begin(FILE* in_fp)
+{
+ if (g_page_contents_obj_num == 0)
+ {
+ t_tempbuf str;
+
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% page %u's contents:\n%%\n", g_page_count);
+
+ g_page_contents_obj_num = PDFObject_WriteNewObj(in_fp);
+ g_page_length_obj_num = PDFObject_New(/* in_fp */);
+ fputs("<< /Length ", in_fp);
+ PDFObject_WriteRef(in_fp, g_page_length_obj_num);
+#if PDF_COMPRESSION
+ if (g_apply_compression) fputs(" /Filter /FlateDecode", in_fp);
+#endif
+ fputs(" >>\nstream\n", in_fp);
+ g_page_start_offset = ftell(in_fp);
+
+#if PDF_COMPRESSION
+ if (g_apply_compression)
+ {
+ int err;
+
+ g_raw_buffer_ptr = g_raw_output;
+ g_comp_stream.zalloc = (alloc_func) Z_NULL;
+ g_comp_stream.zfree = (free_func) Z_NULL;
+ g_comp_stream.opaque = (voidpf) Z_NULL;
+
+ err = deflateInit(&g_comp_stream, Z_DEFAULT_COMPRESSION);
+ if (err != Z_OK)
+ Error(48, 13, "PDFPage_Begin: zlib error occurred", FATAL, no_fpos);
+
+ g_comp_stream.next_out = g_compressed_output;
+ g_comp_stream.avail_out = sizeof(g_compressed_output);
+ }
+#endif
+
+#ifndef USE_MATRICES
+ sprintf(str, "%.2f 0 0 %.2f 0 0 cm\n",
+ g_page_h_scale_factor, g_page_v_scale_factor);
+ PDFPage_WriteStream(in_fp, str);
+#endif
+ sprintf(str, "%u w\n", g_page_line_width);
+ PDFPage_WriteStream(in_fp, str);
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_FlushBuffer(FILE* in_fp) */
+/* */
+/* Flush the buffer to the page's stream and turn off buffering mode. */
+/* */
+/*****************************************************************************/
+
+static void PDFPage_FlushBuffer(FILE* in_fp)
+{
+ if (g_in_buffering_mode)
+ {
+ g_in_buffering_mode = FALSE;
+
+ /* empty the stack since it's no longer needed */
+ while (g_qsave_marking_stack != NULL)
+ {
+ t_qsave_marking_entry_ptr entry = g_qsave_marking_stack;
+ g_qsave_marking_stack = entry->m_next_entry;
+ free(entry);
+ }
+
+ /* output the buffer */
+ PDFPage_WriteStream(in_fp, g_buffer);
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDF_FILE_OFFSET PDFPage_End(FILE* in_fp) */
+/* */
+/* Ends the page's stream. */
+/* */
+/*****************************************************************************/
+
+static PDF_FILE_OFFSET PDFPage_End(FILE* in_fp)
+{
+
+ /* if page has no marks on it then write out an empty stream */
+ if (g_in_buffering_mode)
+ {
+ g_buffer_pos = 0;
+ g_buffer[0] = '\0'; /* force empty string to be written */
+ PDFPage_FlushBuffer(in_fp); /* all I really want is to empty the stack */
+ }
+
+ /* IF applying compression THEN first flush the raw buffer and then flush */
+ /* the compressed buffer (must be performed in that order!) */
+#if PDF_COMPRESSION
+ if (g_apply_compression)
+ {
+ if ((g_raw_buffer_ptr > g_raw_output) && (g_raw_buffer_ptr[-1] == '\n'))
+ g_raw_buffer_ptr--; /* remove last linefeed */
+
+ PDFPage_FlushRawBuffer(in_fp);
+ PDFPage_FlushCompressedBuffer(in_fp);
+ fputs("\n", in_fp);
+ }
+#endif
+
+ /* close page's stream */
+ Assert(g_page_contents_obj_num != 0, no_fpos);
+ {
+ PDF_FILE_OFFSET page_length = ftell(in_fp) - g_page_start_offset;
+
+ /* close page's stream */
+ fputs("endstream\nendobj\n", in_fp);
+ return page_length;
+ }
+ return 0;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Write(FILE* in_fp, char* in_str) */
+/* */
+/* Writes a string to the page's stream. */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Write(FILE* in_fp, char* in_str)
+{
+ if (*in_str == 0)
+ return;
+
+ PDFPage_Begin(in_fp); /* write page content's hdr "<< /Length >> stream"...*/
+
+ /* IF trying to remove redundant operations THEN */
+ if (g_in_buffering_mode)
+ {
+
+ /* if buffer will overflow then turn off buffering and flush buffer */
+ unsigned int len = strlen(in_str);
+ if ( (g_buffer_pos + len) > (kBufferSize-1) ) /* -1 for NULL char */
+ {
+ PDFPage_FlushBuffer(in_fp);
+ PDFPage_WriteStream(in_fp, in_str);
+ }
+ else
+ {
+ strcpy(g_buffer + g_buffer_pos, in_str); /* save into buffer */
+ g_buffer_pos += len;
+ }
+ }
+ else
+ {
+ if (g_TJ_pending)
+ {
+ g_TJ_pending = FALSE; /* clear it */
+ PDFPage_WriteStream(in_fp, ")]TJ\n");
+ }
+
+ if (g_ET_pending)
+ {
+ g_ET_pending = FALSE; /* clear it */
+ PDFPage_WriteStream(in_fp, "ET\n");
+ g_valid_text_matrix = FALSE; /* Td is not allowed */
+ }
+
+ PDFPage_WriteStream(in_fp, in_str);
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFPage_Push(FILE* in_fp) */
+/* */
+/* Saves the current graphics state. */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Push(FILE* in_fp)
+{
+
+ /* push origin coords */
+ {
+ t_qsave_entry_ptr entry = (t_qsave_entry_ptr) malloc(sizeof(t_qsave_entry));
+ if (entry == NULL)
+ Error(48, 14, "PDFPage_Push: run out of memory", FATAL, no_fpos);
+
+ entry->m_page_h_origin = g_page_h_origin;
+ entry->m_page_v_origin = g_page_v_origin;
+ /* entry->m_page_h_scale_factor = g_page_h_scale_factor; */
+ /* entry->m_page_v_scale_factor = g_page_v_scale_factor; */
+ entry->m_next_entry = g_qsave_stack;
+ g_qsave_stack = entry;
+ }
+
+ /* if buffering text */
+ if (g_in_buffering_mode)
+ {
+
+ /* push current state */
+ t_qsave_marking_entry_ptr entry =
+ (t_qsave_marking_entry_ptr) malloc(sizeof(t_qsave_marking_entry));
+ if (entry == NULL)
+ Error(48, 15, "PDFPage_Push: run out of memory", FATAL, no_fpos);
+
+ entry->m_next_entry = g_qsave_marking_stack; /* next-to-top-of-stack */
+ entry->m_buffer_pos = g_buffer_pos;
+ g_qsave_marking_stack = entry; /* new TOS */
+ /* g_in_buffering_mode = TRUE; */
+ }
+
+ /* write out push op */
+ PDFPage_Write(in_fp, "q\n");
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFPage_Pop(FILE* in_fp) */
+/* */
+/* Restores the current graphics state. */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Pop(FILE* in_fp)
+{
+
+ /* pop origin coords */
+ {
+ t_qsave_entry_ptr entry = g_qsave_stack;
+
+ g_page_h_origin = entry->m_page_h_origin;
+ g_page_v_origin = entry->m_page_v_origin;
+ /* g_page_h_scale_factor = entry->m_page_h_scale_factor; */
+ /* g_page_v_scale_factor = entry->m_page_v_scale_factor; */
+
+ g_qsave_stack = entry->m_next_entry;
+
+ free(entry);
+ }
+
+ /* if no marks on page since last push (and thus there should be a stack) */
+ if (g_in_buffering_mode)
+ {
+
+ /* pop state: behave as if the q...Q never existed */
+ t_qsave_marking_entry_ptr entry = g_qsave_marking_stack;
+
+ Assert(entry != NULL, no_fpos);
+
+ g_qsave_marking_stack = entry->m_next_entry; /* new TOS */
+ g_buffer_pos = entry->m_buffer_pos;
+ g_buffer[g_buffer_pos] = '\0'; /* chop off unwanted text */
+
+ free(entry);
+ }
+ else
+ {
+ Assert(g_qsave_marking_stack == NULL, no_fpos);
+ PDFPage_Write(in_fp, "\nQ\n");
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFFont_Set(FILE* in_fp, FULL_LENGTH in_font_size, */
+/* FULL_CHAR* in_font_name) */
+/* */
+/* Sets the font name and size for subsequent text write statements. */
+/* */
+/*****************************************************************************/
+
+void PDFFont_Set(FILE* in_fp, FULL_LENGTH in_font_size,
+ FULL_CHAR *in_short_font_name)
+{
+ t_tempbuf str;
+
+ t_font_list_entry_ptr entry = PDFFont_FindListEntry_Short(in_short_font_name);
+ if( entry == NULL )
+ {
+ Error(48, 42, "cannot find font entry for name %s", FATAL, no_fpos,
+ in_short_font_name);
+ }
+ /* Assert(entry != NULL, no_fpos); */
+#ifdef USE_MATRICES
+ sprintf(str, "%s %u Tf\n", entry->m_PDF_font_name,
+ (int) (g_page_v_scale_factor * in_font_size));
+#else
+ sprintf(str, "%s %u Tf\n", entry->m_PDF_font_name, in_font_size);
+ /* g_text_font_size_in_ems = g_page_v_scale_factor * in_font_size; */
+#endif
+
+#if 1
+
+ /* font changes can occur within BT...ET blocks, so temporarily turn off */
+ /* g_ET_pending. I do it this way so that the qsave_marking_stack */
+ /* optimisation can still be applied (this avoids output such as */
+ /* "/F0 240 Tf /F0 240 Tf /F1 600 Tf" and instead produces "") */
+ if (g_TJ_pending)
+ {
+ g_TJ_pending = FALSE; /* clear it */
+ PDFPage_WriteStream(in_fp, ")]TJ\n");
+ }
+
+ {
+ BOOLEAN cur_ET_pending = g_ET_pending;
+
+ g_ET_pending = FALSE; /* clear it */
+ PDFPage_Write(in_fp, str);
+ g_ET_pending = cur_ET_pending; /* restore it */
+ }
+#else
+ /* font changes can occur within BT...ET blocks, so bypass PDFPage_Write() */
+ PDFPage_WriteStream(in_fp, str);
+#endif
+ entry->m_in_use = TRUE;
+ g_page_uses_fonts = TRUE;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_RMove(FILE* in_fp, int hdelta, int vdelta) */
+/* */
+/* Offsets text pen by the given offsets. */
+/* */
+/*****************************************************************************/
+
+/* ***
+void PDFText_RMove(FILE* in_fp, int hdelta, int vdelta)
+{
+ t_tempbuf str;
+
+ g_tx_hpos += hdelta;
+ g_tx_vpos += vdelta;
+#if 1
+ sprintf(str, "ET\n1 0 0 1 %d %d cm\nBT\n", hdelta, vdelta);
+#else
+ sprintf(str, "1 0 0 1 %d %d Tm\n", g_tx_hpos, g_tx_vpos);
+#endif
+ PDFPage_Write(in_fp, str);
+}
+*** */
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_MoveTo(FILE* in_fp, int hpos, int vpos) */
+/* */
+/* Move text pen to the given coords. */
+/* */
+/*****************************************************************************/
+
+/* ***
+static void PDFText_MoveTo(FILE* in_fp, int hpos, int vpos)
+{
+ g_tx_hpos = 0;
+ g_tx_vpos = 0;
+#if 1
+ PDFText_RMove(in_fp, hpos, vpos);
+#else
+ {
+ t_tempbuf str;
+ sprintf(str, "1 0 0 1 %d %d Tm\n", hpos, vpos);
+ PDFPage_Write(in_fp, str);
+ }
+#endif
+}
+*** */
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_OpenString(FILE* in_fp) */
+/* */
+/* Open TJ block */
+/* */
+/*****************************************************************************/
+
+static void PDFText_OpenString(FILE* in_fp)
+{
+ if (g_TJ_pending)
+ g_TJ_pending = FALSE; /* clear it */
+ else
+ PDFPage_Write(in_fp, "[(");
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_MoveToXYAndOpen(FILE* in_fp, int hpos, int vpos) */
+/* */
+/* Move text pen to the given coords. */
+/* */
+/*****************************************************************************/
+
+static void PDFText_MoveToXYAndOpen(FILE* in_fp, int hpos, int vpos)
+{
+#if 1
+ t_tempbuf str;
+ sprintf(str, "1 0 0 1 %d %d Tm\n", hpos, vpos);
+ PDFPage_Write(in_fp, str);
+#else
+ PDFText_MoveTo(in_fp, hpos, vpos);
+#endif
+ PDFText_OpenString(in_fp);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_MoveToXAndOpen(FILE* in_fp, int hpos, int vpos) */
+/* */
+/* Move text pen to the given coords. */
+/* */
+/*****************************************************************************/
+
+static void PDFText_MoveToXAndOpen(FILE* in_fp, int hpos)
+{
+#if 1
+ t_tempbuf str;
+ sprintf(str, "%d 0 Td\n", hpos);
+ PDFPage_Write(in_fp, str);
+#else
+ PDFText_MoveTo(in_fp, hpos, vpos);
+#endif
+ PDFText_OpenString(in_fp);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_OpenBT(FILE* in_fp) */
+/* */
+/* Opens a text object at the given coords. */
+/* */
+/*****************************************************************************/
+
+static void PDFText_OpenBT(FILE* in_fp)
+{
+ PDFPage_FlushBuffer(in_fp); /* about to mark page: flush buffered PDF */
+
+ g_page_has_text = TRUE;
+
+ if (g_TJ_pending)
+ {
+ g_TJ_pending = FALSE; /* clear it */
+ PDFPage_WriteStream(in_fp, ")]TJ\n");
+ }
+
+ if (g_ET_pending)
+ g_ET_pending = FALSE; /* clear it */
+ else
+ {
+ PDFPage_Write(in_fp, "BT\n");
+ g_valid_text_matrix = TRUE; /* Td is allowed */
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_OpenXY(FILE* in_fp, int hpos, int vpos) */
+/* */
+/* Opens a text object at the given coords. */
+/* */
+/*****************************************************************************/
+
+void PDFText_OpenXY(FILE* in_fp, int hpos, int vpos)
+{
+ PDFText_OpenBT(in_fp);
+ PDFText_MoveToXYAndOpen(in_fp, hpos, vpos);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_OpenX(FILE* in_fp, int hpos) */
+/* */
+/* Opens a text object at the given coords. */
+/* */
+/*****************************************************************************/
+
+void PDFText_OpenX(FILE* in_fp, int hpos)
+{
+ PDFText_OpenBT(in_fp);
+ PDFText_MoveToXAndOpen(in_fp, hpos);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_Open(FILE* in_fp) */
+/* */
+/* Opens a text object. */
+/* */
+/*****************************************************************************/
+
+void PDFText_Open(FILE* in_fp)
+{
+ if (g_TJ_pending)
+ {
+ g_TJ_pending = FALSE; /* clear it */
+ Assert(g_ET_pending == TRUE, no_fpos);
+ g_ET_pending = FALSE; /* clear it */
+ }
+ else
+ {
+ PDFText_OpenBT(in_fp);
+ PDFText_OpenString(in_fp);
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_Kern(FILE* in_fp, int in_kern) */
+/* */
+/* Apply kerning to a text string. */
+/* */
+/* Note: in_kern is in 1/1000 of font size */
+/* */
+/*****************************************************************************/
+
+void PDFText_Kern(FILE* in_fp, int in_kern)
+{
+ t_tempbuf str;
+
+ /* sprintf(str, ")%d(", -in_kern * 1000 / g_text_font_size_in_ems); */
+ sprintf(str, ")%d(", -in_kern);
+ PDFPage_Write(in_fp, str);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFText_Close(FILE* in_fp) */
+/* */
+/* Closes a previously opened text object. */
+/* */
+/*****************************************************************************/
+
+void PDFText_Close(FILE* in_fp)
+{
+ /* PDFPage_Begin(in_fp); - shouldn't be needed */
+ Assert(g_page_contents_obj_num != 0, no_fpos);
+
+ g_TJ_pending = TRUE;
+ /* PDFPage_Write(in_fp, ")] TJ\n"); */
+ g_ET_pending = TRUE;
+}
+
+
+#ifdef USE_MATRICES
+
+/*****************************************************************************/
+/* */
+/* void PDF_Matrix_XY(double* in_out_x, double* in_out_y) */
+/* */
+/* Returns (x, y) after applying the current matrix: */
+/* */
+/* [ a b 0 ] */
+/* [ x y 1 ] x [ c d 0 ] = [ ax+cy+e bx+dy+f 1 ] */
+/* [ e f 1 ] */
+/* */
+/*****************************************************************************/
+
+static void PDF_Matrix_XY(double* in_out_x, double* in_out_y)
+{
+ double result_x, result_y;
+
+ result_x = g_cur_matrix[0] * *in_out_x + g_cur_matrix[3] * *in_out_y +
+ g_cur_matrix[6];
+ result_y = g_cur_matrix[1] * *in_out_x + g_cur_matrix[4] * *in_out_y +
+ g_cur_matrix[7];
+ *in_out_x = result_x;
+ *in_out_y = result_y;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDF_Matrix_Mul(t_matrix in_left, t_matrix in_right, t_matrix out_result) */
+/* */
+/* Multiplies the given matrices. */
+/* */
+/* [ a b 0 ] [ g h 0 ] [ ag+bi ah+bj 0 ] */
+/* [ c d 0 ] x [ i j 0 ] = [ cg+di ch+dj 0 ] */
+/* [ e f 1 ] [ k l 1 ] [ eg+fi+k eh+fj+l 1 ] */
+/* */
+/*****************************************************************************/
+
+static void PDF_Matrix_Mul(t_matrix in_left, t_matrix in_right,
+ t_matrix out_result)
+{
+ t_matrix result;
+ result[0] = in_left[0] * in_right[0] + in_left[1] * in_right[3];
+ result[1] = in_left[0] * in_right[1] + in_left[1] * in_right[4];
+ result[2] = 0;
+ result[3] = in_left[3] * in_right[0] + in_left[4] * in_right[3];
+ result[4] = in_left[3] * in_right[1] + in_left[4] * in_right[4];
+ result[5] = 0;
+ result[6] = in_left[6] * in_right[0] + in_left[7] * in_right[3] + in_right[6];
+ result[7] = in_left[6] * in_right[1] + in_left[7] * in_right[4] + in_right[7];
+ result[8] = 1;
+
+ memcpy(out_result, result, sizeof(t_matrix));
+}
+#endif
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Scale(float in_h_scale_factor, float in_v_scale_factor) */
+/* */
+/* Changes CTM by scale factor: */
+/* */
+/* [ sh 0 0 ] */
+/* [ 0 sv 0 ] */
+/* [ 0 0 1 ] */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Scale(FILE* in_fp, float in_h_scale_factor, float in_v_scale_factor)
+{
+#ifdef USE_MATRICES
+ t_matrix m = { 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+ m[0] = in_h_scale_factor;
+ m[4] = in_v_scale_factor;
+
+ PDF_Matrix_Mul(m, g_cur_matrix, g_cur_matrix);
+#else
+ t_tempbuf str;
+
+ sprintf(str, "%.2f 0 0 %.2f 0 0 cm\n", in_h_scale_factor, in_v_scale_factor);
+ PDFPage_Write(in_fp, str);
+#endif
+ g_page_h_scale_factor *= in_h_scale_factor;
+ g_page_v_scale_factor *= in_v_scale_factor;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Rotate(FILE* in_fp, float in_angle_in_radians) */
+/* */
+/* Changes CTM by rotation factor. */
+/* */
+/* [ cos a sin a 0 ] */
+/* [ -sin a cos a 0 ] */
+/* [ 0 0 1 ] */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Rotate(FILE* in_fp, float in_angle_in_radians)
+{
+ float cos_radians = cos(in_angle_in_radians);
+ float sin_radians = sin(in_angle_in_radians);
+#ifdef USE_MATRICES
+ t_matrix m = { 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+ m[0] = m[4] = cos_radians;
+ m[1] = sin_radians;
+ m[3] = -sin_radians;
+ PDF_Matrix_Mul(m, g_cur_matrix, g_cur_matrix);
+#else
+ t_tempbuf str;
+
+ sprintf(str, "%.2f %.2f %.2f %.2f 0 0 cm\n", cos_radians, sin_radians,
+ -sin_radians, cos_radians);
+ PDFPage_Write(in_fp, str);
+#endif
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Translate(FILE* in_fp, float in_delta_h, float in_delta_v) */
+/* */
+/* Changes CTM by translation: */
+/* */
+/* [ 1 0 0 ] */
+/* [ 0 1 0 ] */
+/* [ dh dv 1 ] */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Translate(FILE* in_fp, float in_delta_h, float in_delta_v)
+{
+#ifdef USE_MATRICES
+ t_matrix m = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
+ m[6] = in_delta_h;
+ m[7] = in_delta_v;
+ PDF_Matrix_Mul(m, g_cur_matrix, g_cur_matrix);
+#else
+ t_tempbuf str;
+
+ sprintf(str, "1 0 0 1 %.2f %.2f cm\n", in_delta_h, in_delta_v);
+ PDFPage_Write(in_fp, str);
+#endif
+ g_page_h_origin += in_delta_h;
+ g_page_v_origin += in_delta_v;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFTargetAnnot_New(FULL_CHAR* in_annot_name, ...) */
+/* */
+/* Create a new target annotation entry. */
+/* */
+/*****************************************************************************/
+
+static void PDFTargetAnnot_New(FULL_CHAR* in_annot_name,
+ unsigned int in_annot_name_length, int in_ll_x, int in_ll_y, int in_ur_x,
+ int in_ur_y, BOOLEAN in_for_export)
+{
+ t_target_annot_entry_ptr entry =
+ (t_target_annot_entry_ptr) malloc(sizeof(t_target_annot_entry));
+
+ if (entry == NULL)
+ Error(48, 16, "PDFTargetAnnot_New: run out of memory", FATAL, no_fpos);
+
+ entry->m_name = (FULL_CHAR*) malloc(in_annot_name_length + 1);
+ if (entry->m_name == NULL)
+ Error(48, 17, "PDFTargetAnnot_New: run out of memory", FATAL, no_fpos);
+ memcpy(entry->m_name, in_annot_name, in_annot_name_length);
+ entry->m_name[in_annot_name_length] = '\0';
+
+ Assert(g_page_contents_obj_num != 0, no_fpos);
+ entry->m_page_object_num = g_page_object_num;
+
+ entry->m_ll_x = in_ll_x;
+ entry->m_ll_y = in_ll_y;
+ entry->m_ur_x = in_ur_x;
+ entry->m_ur_y = in_ur_y;
+ entry->m_for_export = in_for_export;
+
+ entry->m_next_entry = g_target_annot_list;
+ g_target_annot_list = entry;
+
+ if (in_for_export)
+ g_has_exported_targets = in_for_export;
+}
+
+
+/*****************************************************************************/
+/* */
+/* t_target_annot_entry_ptr PDFTargetAnnot_Find(FULL_CHAR* in_annot_name) */
+/* */
+/* Finds an annotation. Returns NULL if not found. */
+/* */
+/*****************************************************************************/
+
+static t_target_annot_entry_ptr PDFTargetAnnot_Find(FULL_CHAR* in_annot_name)
+{
+ t_target_annot_entry_ptr entry = g_target_annot_list;
+
+ /* this takes O(n) time; may need re-implementing if speed is a factor */
+ while (entry != NULL)
+ {
+ if (strcmp((char*) in_annot_name, (char*) entry->m_name) == 0)
+ break;
+ entry = entry->m_next_entry;
+ }
+
+ return entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFSourceAnnot_Write(FILE* in_fp, */
+/* t_source_annot_entry_ptr in_source_entry) */
+/* */
+/* Write an annot which specifies the source and target of the link. */
+/* */
+/*****************************************************************************/
+
+static void PDFSourceAnnot_Write(FILE* in_fp, t_source_annot_entry_ptr in_entry)
+{
+ t_target_annot_entry_ptr target;
+
+ Assert(in_entry != NULL, no_fpos);
+
+ target = in_entry->m_target;
+
+ /* if it is an unresolved forward link then exit */
+ if ( (in_entry->m_link_type == k_link_source) && (target == NULL) )
+ return;
+
+ /* green light: write it out */
+ if (g_PDF_debug)
+ {
+ fprintf(in_fp, "%%\n%% annotation in page object # %u to %s:\n%%\n",
+ in_entry->m_this_page_object_num, in_entry->m_target->m_name);
+ }
+
+ PDFObject_WriteObj(in_fp, in_entry->m_this_object_num);
+ fprintf(in_fp, "<<\n/Type /Annot\n/Subtype /Link\n"
+ /* this is what Adobe does (it's also more flexible) */
+ "/Rect [ %d %d %d %d ]\n/Border [ 0 0 0 ]\n",
+ /* "/BS << /Type /Border /S /U >>\n" */
+ /* border appearance is "underline" */
+ in_entry->m_ll_x, in_entry->m_ll_y, in_entry->m_ur_x, in_entry->m_ur_y);
+
+ switch (in_entry->m_link_type)
+ {
+ case k_link_source:
+
+ fprintf(in_fp, "/Dest [ ");
+ PDFObject_WriteRef(in_fp, in_entry->m_target->m_page_object_num);
+ switch (in_entry->m_dest_option)
+ {
+ case kFitNoChange:
+
+ fprintf(in_fp, " /XYZ null null null");
+ /* NB NO BREAK */
+
+ case kFit:
+
+ fprintf(in_fp, " /Fit");
+ break;
+
+
+ case kFitH:
+
+ /* [ /FitH top ]: fit the width of the page to the window; top */
+ /* specifies the y-coordinate of the top edge of the window */
+ fprintf(in_fp, " /FitH %u", target->m_ur_y);
+ break;
+
+
+ case kFitV:
+
+ /* [ /FitV left ]: fit the height of the page to the window; */
+ /* left specifies the x-coordinate of the left edge of the window */
+ fprintf(in_fp, " /FitV %u", target->m_ll_x);
+ break;
+
+
+ case kFitR:
+
+ /* [ /FitR left bottom right top ]: fit the rectangle specified */
+ /* by left bottom right top in the window. If the height (top - */
+ /* bottom) and width (right - left) imply different zoom factors, */
+ /* the numerically smaller zoom factor is used, to ensure that */
+ /* the specified rectangle fits in the window */
+ fprintf(in_fp, " /FitR %u %u %u %u", target->m_ll_x, target->m_ll_y,
+ target->m_ur_x, target->m_ur_y);
+ break;
+
+
+ case kFitB:
+
+ /* [ /FitB ]: fit the page's bounding box to the window */
+ fprintf(in_fp, " /FitB");
+ break;
+
+
+ case kFitBH:
+
+ /* [ /FitBH top ]: fit the width of the page's bounding box to */
+ /* the window. top specifies the y-coord of top edge of window */
+ fprintf(in_fp, " /FitBH %u", target->m_ur_y);
+ break;
+
+
+ case kFitBV:
+
+ /* [ /FitBV left ]: fit the height of the page' bounding box to */
+ /* the window. left specifies the x-coordinate of the left edge */
+ /* of the window */
+ fprintf(in_fp, " /FitBV %u", target->m_ll_x);
+ break;
+
+
+ default:
+
+ Error(48, 18, "PDFSourceAnnot_Write: invalid link dest option",
+ FATAL, no_fpos);
+ }
+ fprintf(in_fp, " ]\n");
+ break;
+
+
+ case k_link_external:
+
+#if 1 /* required wrapper for URLs is now in the Lout libraries */
+ fprintf(in_fp, "/A << /Type /Action /S /GoToR /D (%s) /F\n"
+ /* <= split across lines for LONG file specs */
+ "(%s) >>\n", in_entry->m_name, in_entry->m_file_spec);
+#else
+ if (in_entry->m_file_spec[0] != '<')
+ {
+ /* note: destination/target is specified as a string, as is file spec */
+ fprintf(in_fp, "/A << /Type /Action /S /GoToR /D (%s) /F\n"
+ /* <= split across lines for LONG file specs */
+ "(%s) >>\n", in_entry->m_name, in_entry->m_file_spec);
+ }
+ else
+ {
+ /* if file spec starts with '<' then URL, eg <http://www.adobe.com> */
+ Assert(in_entry->m_file_spec[strlen((char*) in_entry->m_file_spec)-1]
+ == '>', no_fpos);
+ fprintf(in_fp, "/A << /Type /Action /S /GoToR /D (%s) /F\n"
+ /* <= split across lines for LONG file specs */
+ "<< /FS /URL /F (%s) >> >>\n", in_entry->m_name, in_entry->m_file_spec);
+ }
+#endif
+ break;
+
+
+ case k_link_URI:
+
+ fprintf(in_fp, "/A << /Type /Action /S /URI /URI\n"
+ /* <= split across lines for LONG URI's */
+ "(%s) >>\n", in_entry->m_name);
+ break;
+
+
+ case k_link_target:
+ case k_link_target_for_export:
+ case kNumberOfLinkKeywords:
+
+ break;
+ }
+
+ fprintf(in_fp, ">>\nendobj\n");
+ in_entry->m_written_to_PDF_file = TRUE;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFSourceAnnot_New(FULL_CHAR* in_annot_name) */
+/* */
+/* Create an entry in the g_source_annot_list which links to in_annot_name. */
+/* */
+/*****************************************************************************/
+
+static t_source_annot_entry_ptr
+PDFSourceAnnot_New(PDF_LINK_KEYWORD in_link_type, FULL_CHAR* in_annot_name,
+ unsigned int in_annot_name_length, int in_ll_x, int in_ll_y, int in_ur_x,
+ int in_ur_y, PDF_LINK_DEST_OPTION in_link_dest_option)
+{
+ t_target_annot_entry_ptr target = NULL;
+ t_source_annot_entry_ptr entry =
+ (t_source_annot_entry_ptr) malloc(sizeof(t_source_annot_entry));
+
+ if (entry == NULL)
+ Error(48, 19, "PDFSourceAnnot_New: run out of memory", FATAL, no_fpos);
+
+ entry->m_ll_x = in_ll_x;
+ entry->m_ll_y = in_ll_y;
+ entry->m_ur_x = in_ur_x;
+ entry->m_ur_y = in_ur_y;
+
+ entry->m_this_object_num = PDFObject_New(/* in_fp */);
+ entry->m_this_page_object_num = g_page_object_num;
+ entry->m_link_type = in_link_type;
+ Assert((in_link_dest_option >= kFitNoChange) &&
+ (in_link_dest_option <= kFitBV), no_fpos);
+ entry->m_dest_option = in_link_dest_option;
+ entry->m_file_spec = NULL;
+ entry->m_written_to_PDF_file = FALSE;
+
+ if (in_link_type == k_link_source)
+ target = PDFTargetAnnot_Find(in_annot_name);
+
+ if (target != NULL)
+ {
+ entry->m_target = target;
+ entry->m_name = NULL;
+ }
+ else
+ {
+ entry->m_target = NULL; /* fwd link */
+ entry->m_name = (FULL_CHAR*) malloc(in_annot_name_length + 1);
+ if (entry->m_name == NULL)
+ Error(48, 20, "PDFSourceAnnot_New: run out of memory", FATAL, no_fpos);
+ memcpy(entry->m_name, in_annot_name, in_annot_name_length);
+ entry->m_name[in_annot_name_length] = '\0';
+ }
+
+ entry->m_next_entry = g_source_annot_list;
+ g_source_annot_list = entry;
+
+ return entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFSourceAnnot_Dispose(t_source_annot_entry_ptr in_source_annot) */
+/* */
+/* Dispose of a source annot entry; returns the next entry in the list. */
+/* */
+/*****************************************************************************/
+
+static t_source_annot_entry_ptr
+ PDFSourceAnnot_Dispose(t_source_annot_entry_ptr in_source_annot)
+{
+ t_source_annot_entry_ptr next_entry = in_source_annot->m_next_entry;
+
+ if (in_source_annot->m_name != NULL)
+ free(in_source_annot->m_name);
+ if (in_source_annot->m_file_spec != NULL)
+ free(in_source_annot->m_file_spec);
+ free(in_source_annot);
+ return next_entry;
+}
+
+
+/*****************************************************************************/
+/* */
+/* float PDFPage_GetFloat(FULL_CHAR* in_str) */
+/* */
+/* Outputs an appropriate PDF string for drawing a graphic element. */
+/* */
+/*****************************************************************************/
+
+static FULL_CHAR *PDFPage_GetFloat(FULL_CHAR* in_str, float* out_value)
+{
+ if (sscanf((char*) in_str, "%f", out_value) == 1)
+ {
+ /* skip (presumed) floating point number: [ ]*[+|-][0-9.]* */
+ while (isspace(*in_str))
+ in_str++;
+ if ( (*in_str == '-') || (*in_str == '+') )
+ in_str++;
+ while (isdigit(*in_str) || (*in_str == '.'))
+ in_str++;
+ }
+ else Error(48, 21, "PDFPage_GetFloat: unable to evaluate number for Lout graphic keyword processing",
+ FATAL, no_fpos);
+ return in_str;
+}
+
+
+/*****************************************************************************/
+/* */
+/* int PDFKeyword_Find(int in_number_of_array_elements, */
+/* char* in_keyword_array[], FULL_CHAR* in_str) */
+/* */
+/* Return index into keyword array if an element matches the given string. */
+/* Returns -1 if not found. */
+/* */
+/*****************************************************************************/
+
+static int PDFKeyword_Find(int in_number_of_array_elements,
+ char* in_keyword_array[], FULL_CHAR* in_str)
+{
+ unsigned int i;
+
+ /* look for keyword */
+ for (i = 0; i < in_number_of_array_elements; i++)
+ {
+ unsigned int len = strlen(in_keyword_array[i]);
+
+ if (memcmp(in_keyword_array[i], in_str, len) == 0)
+ break;
+ }
+
+ return (i < in_number_of_array_elements) ? i : -1;
+}
+
+
+/*****************************************************************************/
+/* */
+/* FULL_CHAR *PDFPage_ProcessGraphicsKeyword(FULL_CHAR* charPtr, int i) */
+/* */
+/* Processes a link keyword. */
+/* */
+/*****************************************************************************/
+
+#if 0 /* this function is no longer used */
+
+static FULL_CHAR *PDFPage_ProcessGraphicsKeyword(FULL_CHAR* charPtr, int i,
+ char** strPtr)
+{
+ float value;
+
+ /* if need be, expand this later to a full blown expression evaluator (ugh) */
+ switch (*charPtr)
+ {
+ case '+':
+
+ Assert(FALSE, no_fpos);
+ charPtr = PDFPage_GetFloat(++charPtr, &value);
+ sprintf(*strPtr, "%.2f", g_graphics_vars[i] + value);
+ break;
+
+
+ case '-':
+
+ Assert(FALSE, no_fpos);
+ charPtr = PDFPage_GetFloat(++charPtr, &value);
+ sprintf(*strPtr, "%.2f", g_graphics_vars[i] - value);
+ break;
+
+
+ case '*':
+
+ Assert(FALSE, no_fpos);
+ charPtr = PDFPage_GetFloat(++charPtr, &value);
+ sprintf(*strPtr, "%.2f", g_graphics_vars[i] * value);
+ break;
+
+
+ case '/':
+
+ Assert(FALSE, no_fpos);
+ charPtr = PDFPage_GetFloat(++charPtr, &value);
+ Assert(value != 0, no_fpos); /* not great since value is a float... */
+ sprintf(*strPtr, "%.2f", g_graphics_vars[i] / value);
+ break;
+
+
+ default:
+
+ sprintf(*strPtr, "%d", g_graphics_vars[i]);
+ break;
+ }
+ *strPtr += strlen(*strPtr);
+ return charPtr;
+}
+
+#endif
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_ProcessLinkKeyword(void) */
+/* */
+/* Processes a link keyword. */
+/* */
+/*****************************************************************************/
+
+static void PDFPage_ProcessLinkKeyword(void)
+{
+ FULL_CHAR* charPtr = (FULL_CHAR*) g_link;
+ PDF_LINK_KEYWORD keyword = g_link_keyword;
+ unsigned int link_len = 0;
+ FULL_CHAR* link_name = charPtr;
+
+ /* scan for name of link; scan until end of string or until ' __' reached */
+ /* (scan for name of link; scan until end of string or whitespace reached) */
+#if 1
+
+ FULL_CHAR* parm = NULL;
+ debug1(DPD, D, "PDFPage_ProcessLinkKeyword(g_link = %s", g_link);
+
+ while ((*charPtr != '\0') &&
+ !(isspace(charPtr[0]) && (charPtr[1] == '_') && (charPtr[2] == '_')))
+ {
+ link_len++;
+ charPtr++;
+ }
+
+ if (*charPtr != '\0')
+ parm = ++charPtr;
+
+ while (*charPtr != '\0')
+ charPtr++;
+#else
+ while ((*charPtr != '\0') && ! isspace(*charPtr))
+ {
+ link_len++;
+ charPtr++;
+ }
+#endif
+ if (link_len == 0)
+ Error(48, 22, "PDFPage_ProcessLinkKeyword: empty link-name / URI; ignored.",
+ WARN, no_fpos);
+ else
+ {
+
+ /* see documentaton for @Graphic for the meaning of the x, y parms */
+ /* translate the object's box into PDF's default user space */
+ int ll_x = g_page_h_origin * g_page_h_scale_factor;
+ int ll_y = g_page_v_origin * g_page_v_scale_factor;
+ int ur_x = (g_page_h_origin + g_graphics_vars[k_xsize]) * g_page_h_scale_factor;
+ int ur_y = (g_page_v_origin + g_graphics_vars[k_ysize]) * g_page_v_scale_factor;
+
+ /* remove this block later (it produces debugging output): */
+#if 0
+ {
+ t_tempbuf strz = "PDFPage_ProcessLinkKeyword: ";
+ switch (keyword)
+ {
+ case k_link_source:
+
+ strcat(strz, "link_source =");
+ break;
+
+
+ case k_link_external:
+
+ strcat(strz, "link_external =");
+ break;
+
+
+ case k_link_URI:
+
+ strcat(strz, "link_URI =");
+ break;
+
+
+ case k_link_target:
+
+ strcat(strz, "link_target =");
+ break;
+
+
+ case k_link_target_for_export:
+
+ strcat(strz, "link_target_for_export=");
+ break;
+ }
+ strcat(strz, (char*) link_name);
+ fprintf(stderr, "%s", strz);
+ /* Err or(48, 23, strz, WARN, no_fpos); */
+ }
+#endif
+ switch (keyword)
+ {
+ case k_link_source:
+
+ {
+ int j;
+
+ /* if there is a dest option specified then get it */
+ if (parm != NULL)
+ {
+ j = PDFKeyword_Find(kNumberOfDestLinkOptions, g_dest_link_options,
+ charPtr);
+ if (j >= 0) /* note signed comparison */
+ charPtr += strlen(g_dest_link_options[j]);
+ else
+ {
+ j = (int) kFitNoChange; /* default */
+ /* must consume the rest of the string */
+ while (*charPtr != '\0')
+ charPtr++;
+ link_len = charPtr - link_name;
+ }
+ }
+ else
+ j = (int) kFitNoChange; /* default */
+
+ PDFSourceAnnot_New(keyword, link_name, link_len,
+ ll_x, ll_y, ur_x, ur_y, (PDF_LINK_DEST_OPTION) j);
+ break;
+ }
+
+
+ case k_link_external:
+ case k_link_URI:
+
+ {
+ t_source_annot_entry_ptr source;
+ source = PDFSourceAnnot_New(keyword, link_name, link_len, ll_x,
+ ll_y, ur_x, ur_y, (PDF_LINK_DEST_OPTION) 0 /* doesn't matter */);
+ if (keyword == k_link_external)
+ {
+ int j;
+
+ link_len = 0;
+
+ if (parm != NULL)
+ {
+ j = PDFKeyword_Find(1, g_external_file_spec_keyword, parm);
+ if (j == 0)
+ {
+ parm += strlen(g_external_file_spec_keyword[0]);
+ link_len = strlen((char*) parm);
+#if 0
+ /* scan for name of file spec; scan until end of string or */
+ /* until whitespace reached */
+ link_name = charPtr;
+ while ((*charPtr != '\0') && ! isspace(*charPtr))
+ {
+ link_len++;
+ charPtr++;
+ }
+#endif
+ }
+ }
+
+ if (link_len == 0)
+ Error(48, 24, "PDFPage_ProcessLinkKeyword: empty file spec",
+ FATAL, no_fpos);
+ else
+ {
+ source->m_file_spec = (FULL_CHAR*) malloc(link_len + 1);
+ if (source->m_file_spec == NULL)
+ Error(48, 25, "PDFPage_ProcessLinkKeyword: out of memory",
+ FATAL, no_fpos);
+#if 1
+ strcpy((char*) source->m_file_spec, (char*) parm);
+#else
+ memcpy(source->m_file_spec, link_name, link_len);
+ source->m_file_spec[link_len] = '\0';
+#endif
+ }
+ }
+ break;
+ }
+
+
+ case k_link_target:
+ case k_link_target_for_export:
+
+ PDFTargetAnnot_New(link_name, link_len, ll_x, ll_y, ur_x, ur_y,
+ keyword == k_link_target_for_export);
+ break;
+
+
+ case kNumberOfLinkKeywords:
+ break;
+
+ } /* switch */
+ } /* else */
+
+ debug0(DPD, D, "PDFPage_ProcessLinkKeyword returning");
+ /* return charPtr; */
+} /* PDFPage_ProcessLinkKeyword */
+
+
+/*****************************************************************************/
+/* */
+/* FULL_CHAR* PDFPage_ProcessDocInfoKeyword(FULL_CHAR* charPtr, int i) */
+/* */
+/* Processes a document info keyword. */
+/* */
+/*****************************************************************************/
+
+static FULL_CHAR *PDFPage_ProcessDocInfoKeyword(FULL_CHAR* charPtr, int i)
+{
+ switch (i)
+ {
+ case k_author:
+
+ if (g_doc_author != NULL)
+ free(g_doc_author);
+ g_doc_author = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
+ if (g_doc_author == NULL)
+ Error(48, 26, "PDFPage_ProcessDocInfoKeyword: no memory for __author=",
+ WARN, no_fpos);
+ else
+ strcpy((char*) g_doc_author, (char*) charPtr);
+ break;
+
+
+ case k_title:
+
+ if (g_doc_title != NULL)
+ free(g_doc_title);
+ g_doc_title = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
+ if (g_doc_title == NULL)
+ Error(48, 27, "PDFPage_ProcessDocInfoKeyword: no memory for __title=",
+ WARN, no_fpos);
+ else
+ strcpy((char*) g_doc_title, (char*) charPtr);
+ break;
+
+
+ case k_subject:
+
+ if (g_doc_subject != NULL)
+ free(g_doc_subject);
+ g_doc_subject = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
+ if (g_doc_subject == NULL)
+ Error(47, 28, "PDFPage_ProcessDocInfoKeyword: no memory for __subject=",
+ WARN, no_fpos);
+ else
+ strcpy((char*) g_doc_subject, (char*) charPtr);
+ break;
+
+
+ case k_keywords:
+ if (g_doc_keywords != NULL)
+ free(g_doc_keywords);
+ g_doc_keywords = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
+ if (g_doc_keywords == NULL)
+ Error(48, 29, "PDFPage_ProcessDocInfoKeyword: no memory for __keywords=",
+ WARN, no_fpos);
+ else
+ strcpy((char*) g_doc_keywords, (char*) charPtr);
+ break;
+
+ }
+ return (charPtr + strlen((char*) charPtr));
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_EvalExpr(char* inExpr) */
+/* */
+/* Evaluate collected expression in the given expression buffer. */
+/* */
+/*****************************************************************************/
+
+static char *PDFPage_EvalExpr(char* inExpr, float* outValue)
+{
+ int i;
+ char* chp = inExpr;
+
+ while (isspace( (int) *chp)) /* ignore leading white space */
+ chp++;
+
+ while (*chp == '_')
+ chp++;
+
+ while (*chp == '+') /* ignore unary + */
+ chp++;
+
+ if (isdigit((int) *chp) || (*chp == '.'))
+ {
+ chp = (char*) PDFPage_GetFloat((FULL_CHAR*) chp, outValue);
+ }
+ else if (*chp == '-') /* handle unary negation */
+ {
+ float val;
+
+ chp = PDFPage_EvalExpr(++chp, &val);
+ *outValue = -val;
+ }
+ else
+ {
+ i = PDFKeyword_Find(kNumberOfArithmeticKeywords,
+ g_arithmetic_keywords, (FULL_CHAR*) chp);
+ if (i >= 0)
+ {
+ float val1, val2;
+
+ chp += strlen(g_arithmetic_keywords[i]);
+
+ while (isspace( (int) *chp))
+ chp++;
+ if (*chp != '(')
+ Error(48, 30, "PDFPage_EvalExpr: '(' expected", FATAL, no_fpos);
+ chp = PDFPage_EvalExpr(++chp, &val1);
+ if ( (i <= k_div) || (i == k_pick) )
+ {
+ int count;
+
+ if (i == k_pick)
+ {
+ count = floor(val1);
+ Assert(count != 0, no_fpos);
+ }
+ else
+ count = 1;
+
+ if (*chp != ',')
+ Error(48, 31, "PDFPage_EvalExpr: ',' expected", FATAL, no_fpos);
+
+ do {
+ chp = PDFPage_EvalExpr(++chp, &val2);
+ if ((count != 1) && (*chp == ','))
+ ++chp;
+ } while (--count != 0);
+ }
+ if (*chp != ')')
+ Error(48, 32, "PDFPage_EvalExpr: ')' expected", FATAL, no_fpos);
+ ++chp;
+ switch (i)
+ {
+
+ case k_add:
+ *outValue = val1 + val2;
+ break;
+
+
+ case k_sub:
+
+ *outValue = val1 - val2;
+ break;
+
+
+ case k_mul:
+
+ *outValue = val1 * val2;
+ break;
+
+
+ case k_div:
+
+ Assert(val2 != 0, no_fpos); /* not great since value is a float... */
+ *outValue = val1 / val2;
+ break;
+
+
+ case k_sin:
+
+ *outValue = sin((double) val1 * (double) PI / (double) 180.0);
+ break;
+
+
+ case k_cos:
+
+ *outValue = cos((double) val1 * (double) PI / (double) 180.0);
+ break;
+
+
+ case k_pick:
+
+ *outValue = val2;
+ break;
+ }
+ }
+ else
+ {
+ i = PDFKeyword_Find(kNumberOfGraphicsKeywords, g_graphic_keywords,
+ (FULL_CHAR*) chp);
+ if (i >= 0)
+ {
+ chp += strlen(g_graphic_keywords[i]);
+ *outValue = g_graphics_vars[i];
+ }
+ else
+ {
+ i = PDFKeyword_Find(kNumberOfUnitKeywords, g_unit_keywords, (FULL_CHAR*) chp);
+ if (i >= 0)
+ {
+ chp += strlen(g_unit_keywords[i]);
+ *outValue = g_units[i];
+ }
+ else
+ {
+ Error(48, 33, "PDFPage_EvalExpr: __add, __sub, __mul, __div, or a unit keyword was expected",
+ FATAL, no_fpos);
+ }
+ }
+ }
+ }
+ return chp;
+}
+
+
+/*****************************************************************************/
+/* */
+/* FULL_CHAR *PDFPage_CollectExpr(FULL_CHAR* charPtr) */
+/* */
+/* Collect expression into the expression buffer. */
+/* */
+/*****************************************************************************/
+
+static FULL_CHAR *PDFPage_CollectExpr(FULL_CHAR* charPtr, BOOLEAN* outHasResult,
+ float* outResult)
+{
+ *outHasResult = FALSE;
+ while (*charPtr != 0)
+ {
+ char ch;
+
+ if ( g_expr_index >= sizeof(g_expr) )
+ Error(48, 34, "PDFPage_CollectExpr: expression too long (max. 512 chars)",
+ FATAL, no_fpos);
+
+ g_expr[g_expr_index++] = ch = *charPtr++;
+ if (ch == '(')
+ g_expr_depth++;
+ else if (ch == ')')
+ {
+ Assert(g_expr_depth != 0, no_fpos);
+ g_expr_depth--;
+ if (g_expr_depth == 0)
+ {
+ g_expr[g_expr_index] = '\0'; /* terminate the string */
+ (char*) PDFPage_EvalExpr(g_expr, outResult);
+ *outHasResult = TRUE;
+ break; /* exit while */
+ }
+ }
+ }
+ return charPtr;
+}
+
+
+/*****************************************************************************/
+/* */
+/* FULL_CHAR *PDFPage_CollectLink(FULL_CHAR* charPtr) */
+/* */
+/* Collect link into the link buffer. */
+/* */
+/*****************************************************************************/
+
+static FULL_CHAR *PDFPage_CollectLink(FULL_CHAR* charPtr
+ /*, BOOLEAN* outHasResult, float* outResult*/)
+{
+ debug1(DPD, D, "PDFPage_CollectLink(\"%s\")", charPtr);
+ while (*charPtr != 0)
+ {
+ char ch;
+
+ if ( g_link_index >= sizeof(g_link) )
+ Error(48, 35, "PDFPage_CollectLink: link too long (max. 512 chars)",
+ FATAL, no_fpos);
+
+ g_link[g_link_index++] = ch = *charPtr++;
+ if ((ch == '<') && (*charPtr == '<'))
+ {
+ g_link[g_link_index++] = *charPtr++;
+ g_link_depth++;
+ }
+ else if ((ch == '>') && (*charPtr == '>'))
+ {
+ Assert(g_link_depth != 0, no_fpos);
+
+ g_link_depth--;
+ if (g_link_depth == 0)
+ {
+ /* I don't want the outermost '<<' '>>' pair */
+ g_link[--g_link_index] = '\0'; /* terminate the string */
+
+ PDFPage_ProcessLinkKeyword();
+
+ charPtr++;
+ break; /* exit while */
+ }
+ else
+ g_link[g_link_index++] = *charPtr++;
+ }
+ }
+ debug0(DPD, D, "PDFPage_CollectLink returning");
+ return charPtr;
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_WriteGraphic(FILE* in_fp, FULL_CHAR* in_str) */
+/* */
+/* Outputs an appropriate PDF string for drawing a graphic element. */
+/* */
+/*****************************************************************************/
+
+void PDFPage_WriteGraphic(FILE* in_fp, FULL_CHAR* in_str)
+{
+ t_tempbuf str;
+
+ FULL_CHAR *charPtr = in_str;
+ char *strPtr = str;
+
+ if (*charPtr == 0)
+ return;
+
+ /* if in collecting an expression mode then collect until terminating ')' */
+ if (g_expr_depth != 0)
+ {
+ BOOLEAN hasResult;
+ float value;
+
+ charPtr = PDFPage_CollectExpr(charPtr, &hasResult, &value);
+ if (hasResult)
+ {
+ sprintf(strPtr, "%.2f", value);
+ strPtr += strlen(strPtr);
+ }
+ }
+
+ /* if in collecting-a-link mode then collect until terminating '>>' */
+ if (g_link_depth != 0)
+ charPtr = PDFPage_CollectLink(charPtr);
+
+ /* scan the string for '__' otherwise output it */
+ while (*charPtr != 0)
+ {
+ int i;
+ float value;
+
+ Assert(strPtr < (str + sizeof(t_tempbuf)), no_fpos);
+
+ /* look for "__" (double underline) */
+ if ( (charPtr[0] == '_') && (charPtr[1] == '_') )
+ {
+ charPtr += 2;
+
+ /* "in", "cm", "pt", "em", "loutf", "loutv", "louts" */
+#if 0
+ i = PDFKeyword_Find(kNumberOfUnitKeywords, g_unit_keywords, charPtr);
+ if (i >= 0)
+ {
+ Assert(FALSE, no_fpos);
+ charPtr += strlen(g_unit_keywords[i]); /* skip keyword */
+ charPtr = PDFPage_GetFloat(charPtr, &value); /* get value */
+ sprintf(strPtr, "%.2f", g_units[i] * value); /* output it */
+ strPtr += strlen(strPtr);
+ }
+ else
+#endif
+ {
+
+ /* "xsize", "ysize", "xmark", "ymark" */
+ i = PDFKeyword_Find(kNumberOfGraphicsKeywords, g_graphic_keywords, charPtr);
+ if (i >= 0)
+ {
+ charPtr += strlen(g_graphic_keywords[i]);
+#if 1
+ sprintf(strPtr, "%d", g_graphics_vars[i]);
+ strPtr += strlen(strPtr);
+#else
+ charPtr = PDFPage_ProcessGraphicsKeyword(charPtr, i, &strPtr);
+#endif
+ }
+ else
+ {
+ /* "link_source=<<", "link_target=<<", "link_target_for_export=<<", "link_URI=<<" */
+ i = PDFKeyword_Find(kNumberOfLinkKeywords, g_link_keywords, charPtr);
+ if (i >= 0)
+ {
+ charPtr += strlen(g_link_keywords[i]);
+#if 1
+ while (isspace(*charPtr))
+ charPtr++;
+
+ g_link_index = 0;
+ g_link_depth++;
+ g_link_keyword = (PDF_LINK_KEYWORD) i;
+ charPtr = PDFPage_CollectLink(charPtr);
+#else
+ charPtr = PDFPage_ProcessLinkKeyword(charPtr, (PDF_LINK_KEYWORD) i);
+#endif
+ } /* if */
+ else
+ {
+
+ /* "author=", "title=", "subject=", "keywords=" */
+ i = PDFKeyword_Find(kNumberOfDocInfoKeywords, g_doc_info_keywords, charPtr);
+ if (i >= 0)
+ {
+ charPtr += strlen(g_doc_info_keywords[i]);
+ charPtr = PDFPage_ProcessDocInfoKeyword(charPtr, i);
+ }
+ else
+ {
+
+ /* "add" "sub" "mul" "div", "sin", "cos" */
+ i = PDFKeyword_Find(kNumberOfArithmeticKeywords, g_arithmetic_keywords, charPtr);
+ if (i >= 0)
+ {
+ strcpy(g_expr, g_arithmetic_keywords[i]);
+ charPtr += strlen(g_arithmetic_keywords[i]);
+ while (isspace(*charPtr))
+ charPtr++;
+ if (*charPtr != '(')
+ Error(48, 36, "PDFPage_WriteGraphic: '(' expected", FATAL, no_fpos);
+
+ strcat(g_expr, "(");
+ g_expr_index = strlen(g_expr);
+ g_expr_depth++;
+ {
+ BOOLEAN hasResult;
+
+ charPtr = PDFPage_CollectExpr(++charPtr, &hasResult, &value);
+ if (hasResult)
+ {
+ sprintf(strPtr, "%.2f", value);
+ strPtr += strlen(strPtr);
+ }
+ }
+ }
+ else
+ {
+ /* alert user in case there was a spelling mistake */
+ Error(48, 37, "PDFPage_WriteGraphic: '__' encountered while processing @Graphic", WARN, no_fpos);
+ *strPtr++ = '_';
+ *strPtr++ = '_';
+ } /* else */
+ } /* else */
+ } /* else */
+ } /* else */
+ } /* else */
+ } /* if */
+ else
+ {
+ *strPtr++ = *charPtr++;
+ }
+ }
+
+ *strPtr = 0;
+
+ PDFPage_FlushBuffer(in_fp); /* this is a marking operation, so flush */
+ PDFPage_Write(in_fp, str);
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDFPage_PrintUnderline(FILE* in_fp, int x1, int x2, int y, int thickness) */
+/* */
+/* Implements underlining (just draws a horizontal line). */
+/* */
+/*****************************************************************************/
+
+void PDFPage_PrintUnderline(FILE* in_fp, int in_x1, int in_x2, int in_y,
+ int in_thickness)
+{
+ t_tempbuf str;
+
+ /* this is a marking operation, so flush and turn off buffering */
+ PDFPage_FlushBuffer(in_fp);
+
+ /* fprintf(out_fp, "/ul { gsave setlinewidth dup 3 1 roll\n"); */
+ /* fprintf(out_fp, " moveto lineto stroke grestore } bind def\n"); */
+
+ sprintf(str, "q %d w %d %d m %d %d l s Q\n",in_thickness,in_x1,in_y,in_x2,in_y);
+ PDFPage_Write(in_fp, str);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Init(FILE* in_fp, float in_scale_factor, int in_line_width) */
+/* */
+/* Inits the vars for the start of processing a new page. */
+/* */
+/* [ 0 1 2 ] [ s 0 0 ] */
+/* [ 3 4 5 ] = [ 0 s 0 ] */
+/* [ 6 7 8 ] [ 0 0 1 ] */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Init(FILE* in_fp, float in_scale_factor, int in_line_width)
+{
+
+#ifdef USE_MATRICES
+ g_cur_matrix[0] = g_cur_matrix[4] = in_scale_factor;
+ g_cur_matrix[1] = g_cur_matrix[2] = g_cur_matrix[3] =
+ g_cur_matrix[5] = g_cur_matrix[6] = g_cur_matrix[7] = 0;
+ g_cur_matrix[8] = 1;
+ g_matrix_stack = NULL;
+#endif
+
+ /* clear/init page vars */
+ g_page_uses_fonts = FALSE;
+ g_page_has_text = FALSE;
+ g_page_has_graphics = FALSE;
+
+ g_page_contents_obj_num = 0; /* undefined */
+ g_page_length_obj_num = 0; /* undefined */
+ g_page_start_offset = 0; /* undefined */
+ /* g_text_font_size_in_ems = 0; */ /* undefined */
+
+ g_page_h_scale_factor = g_page_v_scale_factor = in_scale_factor;
+ g_page_h_origin = g_page_v_origin = 0;
+ g_page_line_width = in_line_width;
+
+ /* ***
+ g_graphics_vars[k_in] = IN;
+ g_graphics_vars[k_cm] = CM;
+ g_graphics_vars[k_pt] = PT;
+ g_graphics_vars[k_em] = EM;
+ *** */
+ g_graphics_vars[k_xsize] = 0; /* undefined */
+ g_graphics_vars[k_ysize] = 0; /* undefined */
+ g_graphics_vars[k_xmark] = 0; /* undefined */
+ g_graphics_vars[k_ymark] = 0; /* undefined */
+ /* ***
+ g_graphics_vars[k_loutf] = 0;
+ g_graphics_vars[k_loutv] = 0;
+ g_graphics_vars[k_louts] = 0;
+ *** */
+
+ /* No need to touch k_in other constant units */
+ g_units[k_loutf] = 0; /* undefined */
+ g_units[k_loutv] = 0; /* undefined */
+ g_units[k_louts] = 0; /* undefined */
+
+ g_ET_pending = FALSE;
+ g_TJ_pending = FALSE;
+ g_valid_text_matrix = FALSE; /* Td is not allowed */
+
+ /* mark all fonts "not in use" */
+ {
+ t_font_list_entry_ptr entry = g_font_list;
+ while (entry != NULL) {
+ entry->m_in_use = FALSE; /* set the "in use" state to "not in use" */
+ entry = entry->m_next_font_entry;
+ }
+ }
+
+ /* init qsave stack */
+ g_qsave_stack = NULL;
+
+ /* init qsave_marking stack */
+ g_qsave_marking_stack = NULL;
+ g_buffer_pos = 0;
+ /* buffer contains empty string */
+ g_buffer[0] = '\0';
+ /* try to chop entire stream if possible! Originally: FALSE; */
+ /* turn on buffering only AFTER a save request */
+ g_in_buffering_mode = FALSE;
+ /* try to chop entire stream if possible! Originally: FALSE; */
+ /* turn on buffering only AFTER a save request */
+ g_in_buffering_mode = TRUE;
+
+ /* bump page number */
+ ++g_page_count;
+ g_page_object_num = PDFObject_New(/* in_fp */);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFPage_Cleanup(FILE* in_fp) */
+/* */
+/* Cleans up the processing after a page's contents have been written out. */
+/* */
+/*****************************************************************************/
+
+void PDFPage_Cleanup(FILE* in_fp)
+{
+ BOOLEAN hasAnnot = FALSE;
+
+ Assert(g_qsave_stack == NULL, no_fpos);
+
+ /* if page has some content then close its stream object */
+ if (g_page_contents_obj_num != 0)
+ {
+ PDF_FILE_OFFSET page_length = PDFPage_End(in_fp);
+
+#ifdef _CALC_LARGEST_PAGE_OBJECT_
+ if (page_length > g_max_page_length)
+ g_max_page_length = page_length;
+#endif
+
+ /* write page's length object */
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% length object for page %u:\n%%\n", g_page_count);
+
+ PDFObject_WriteObj(in_fp, g_page_length_obj_num);
+ fprintf(in_fp, "%u\nendobj\n", page_length);
+
+ /* write out any used font resources */
+ {
+ t_font_list_entry_ptr entry = g_font_list;
+ while (entry != NULL) {
+ PDFFont_WriteFontResource(in_fp, entry);
+ entry = entry->m_next_font_entry;
+ }
+ }
+ }
+
+ /* write out annotations */
+ {
+ t_source_annot_entry_ptr source = g_source_annot_list;
+
+ while (source != NULL)
+ {
+ if (source->m_this_page_object_num == g_page_object_num)
+ {
+
+ /* even if the annotation(s) cannot be written out now, flag the */
+ /* fact that this page has annotations */
+ hasAnnot = TRUE;
+
+ /* attempt to write out annotation */
+ PDFSourceAnnot_Write(in_fp, source);
+ } /* if annot entry belongs to this page */
+ source = source->m_next_entry;
+ } /* while */
+ }
+
+ /* start writing page object ("/Type /Page"); remember its number */
+ {
+ unsigned int wanted_block_num = (g_page_count - 1) / kNumberOfPagesPerBlock;
+ unsigned int block_pos = (g_page_count - 1) % kNumberOfPagesPerBlock;
+ t_page_block_ptr the_block = g_cur_page_block;
+
+ /* if first obj in a block then allocate the block */
+ if (block_pos == 0)
+ {
+ the_block = (t_page_block_ptr) malloc(sizeof(t_page_block));
+ if (the_block == NULL)
+ Error(48, 38, "PDFPage_Cleanup: run out of memory", FATAL, no_fpos);
+ if (wanted_block_num == 0) /* if first block in file */
+ {
+ Assert(g_page_block_list == NULL, no_fpos);
+ g_page_block_list = the_block;
+ }
+ else
+ {
+ Assert(g_cur_page_block != NULL, no_fpos);
+ g_cur_page_block->m_next_block = the_block;
+ }
+ the_block->m_next_block = NULL; /* don't forget to init this! */
+ g_cur_page_block = the_block;
+ }
+ else
+ {
+ Assert(the_block != NULL, no_fpos);
+ }
+
+ /* save object number of this page for later use in the /Pages list */
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% page number %u:\n%%\n", g_page_count);
+ the_block->m_block[block_pos] = g_page_object_num;
+ PDFObject_WriteObj(in_fp, g_page_object_num);
+ /* PDFObject_WriteNewObj(in_fp); */
+ }
+
+ /* write out /Page ID */
+ fputs("<<\n/Type /Page\n", in_fp);
+
+ /* write out page size and orientation */
+ fprintf(in_fp, "/CropBox [ 0 0 %u %u ]\n",g_doc_h_bound,g_doc_v_bound);
+
+ /* write out parent object ref */
+ fputs("/Parent ", in_fp);
+ PDFObject_WriteRef(in_fp, g_pages_root);
+ fputs("\n", in_fp);
+
+ /* write out contents object ref (if it exists) */
+ if (g_page_contents_obj_num != 0)
+ {
+ fputs("/Contents ", in_fp);
+ PDFObject_WriteRef(in_fp, g_page_contents_obj_num);
+ fputs("\n", in_fp);
+ }
+
+ /* open resources dictionary */
+ if (g_page_uses_fonts || g_page_has_text || g_page_has_graphics)
+ fputs("/Resources\n<<\n", in_fp);
+
+ /* write out font resources used */
+ if (g_page_uses_fonts)
+ {
+ t_font_list_entry_ptr entry = g_font_list;
+ fputs("/Font <<", in_fp);
+ while (entry != NULL) {
+ if (entry->m_in_use) {
+ fprintf(in_fp, " %s ", entry->m_PDF_font_name);
+ PDFFont_WriteObjectRef(in_fp, entry);
+ }
+ entry = entry->m_next_font_entry;
+ }
+ fputs(" >>\n", in_fp);
+ }
+
+ /* write out any procsets used */
+ if (g_page_has_text || g_page_has_graphics)
+ {
+ fputs("/ProcSet [ /PDF", in_fp);
+ if (g_page_has_text)
+ fputs(" /Text", in_fp);
+ fputs(" ]\n", in_fp);
+ }
+
+ /* close resources dictionary */
+ if (g_page_uses_fonts || g_page_has_text || g_page_has_graphics)
+ fputs(">>\n", in_fp);
+
+ /* write out annot array */
+ if (hasAnnot)
+ {
+ t_source_annot_entry_ptr entry = g_source_annot_list;
+ t_source_annot_entry_ptr previous_entry = NULL;
+
+ /* write out annotations */
+ fputs("/Annots [", in_fp);
+ while (entry != NULL)
+ {
+ if (entry->m_this_page_object_num == g_page_object_num)
+ {
+ fputs(" ", in_fp);
+ PDFObject_WriteRef(in_fp, entry->m_this_object_num);
+
+ /* if the annotation has just been written out above then delete it */
+ if (entry->m_written_to_PDF_file)
+ {
+ t_source_annot_entry_ptr next_entry = entry->m_next_entry;
+ if (g_source_annot_list == entry)
+ g_source_annot_list = next_entry;
+ if (previous_entry != NULL)
+ previous_entry->m_next_entry = next_entry;
+ PDFSourceAnnot_Dispose(entry);
+ entry = next_entry;
+ }
+ else /* annot is a fwd referring one: defer deleting it */
+ {
+ previous_entry = entry;
+ entry = entry->m_next_entry;
+ }
+ } /* if annot entry belongs to this page */
+ else /* annot does not belong to this page; skip it */
+ {
+ previous_entry = entry;
+ entry = entry->m_next_entry;
+ }
+ } /* while */
+ fputs(" ]\n", in_fp);
+ } /* if */
+
+ /* close object */
+ fputs(">>\nendobj\n", in_fp);
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFFile_Init(FILE* in_fp, int in_h_bound, int in_v_bound) */
+/* */
+/* Initialize this module. */
+/* */
+/*****************************************************************************/
+
+void PDFFile_Init(FILE* in_fp, int in_h_bound, int in_v_bound,
+ int in_IN, int in_CM, int in_PT, int in_EM)
+{
+ /* write PDF header */
+ fputs("%PDF-1.2\n", in_fp); /* identifies this as PDF */
+ fputs("\045\342\343\317\323\n", in_fp); /* 0x25 0xE2 0xE3 0xCF 0xD3 */
+
+ /* set debugging status */
+#if DEBUG_ON
+ g_PDF_debug = dbg[DPD].on[D] || dbg[DPD].on[DD] || dbg[DPD].on[DDD];
+#else
+ g_PDF_debug = FALSE;
+#endif
+
+#if PDF_COMPRESSION
+ g_apply_compression = !g_PDF_debug;
+#endif
+
+ /* objects */
+ g_next_objnum = 1; /* object numbers start at one */
+ g_obj_offset_list = NULL;
+ g_cur_obj_offset_block = NULL;
+
+ /* fonts */
+ g_font_list = NULL;
+ g_font_encoding_list = NULL;
+
+ /* pages */
+ g_page_count = 0;
+ g_page_block_list = NULL;
+ g_cur_page_block = NULL;
+ g_pages_root = PDFObject_New(/* in_fp */);
+
+ /* doc */
+ g_doc_h_bound = in_h_bound;
+ g_doc_v_bound = in_v_bound;
+ g_doc_author = NULL;
+ g_doc_title = NULL;
+ g_doc_subject = NULL;
+ g_doc_keywords = NULL;
+
+ /* link annotations */
+ g_target_annot_list = NULL;
+ g_has_exported_targets = FALSE;
+ g_source_annot_list = NULL;
+
+ /* units */
+ g_units[k_in] = in_IN;
+ g_units[k_cm] = in_CM;
+ g_units[k_pt] = in_PT;
+ g_units[k_em] = in_EM;
+
+}
+
+
+/*****************************************************************************/
+/* */
+/* void PDFFile_WritePagesObject(FILE* in_fp) */
+/* */
+/* Cleans up processing after all pages has been written out. */
+/* */
+/*****************************************************************************/
+
+static void PDFFile_WritePagesObject(FILE* in_fp)
+{
+ unsigned int i;
+ t_page_block_ptr the_block = g_page_block_list;
+
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% root of pages tree:\n%%\n");
+
+ /* write out the root of the Pages tree */
+ PDFObject_WriteObj(in_fp, g_pages_root);
+ fputs("<<\n", in_fp);
+ fputs("/Type /Pages\n", in_fp);
+ fputs("/Kids [ ", in_fp);
+ for (i = 0; i < g_page_count; i++)
+ {
+ int block_pos = i % kNumberOfPagesPerBlock;
+ PDFObject_WriteRef(in_fp, the_block->m_block[block_pos]);
+ if (block_pos == (kNumberOfPagesPerBlock - 1))
+ {
+ the_block = the_block->m_next_block;
+ /* Assert(the_block != NULL, no_fpos); not always true! */
+ }
+ fputs(" ", in_fp);
+ }
+ fprintf(in_fp, " ]\n/Count %u\n", g_page_count);
+ /* ***
+ fprintf(in_fp, "/MediaBox [ 0 0 612 792 ]\n");
+ fprintf(in_fp, "/MediaBox [ 0 0 %u %u ]\n",g_doc_h_bound,g_doc_v_bound);
+ *** */
+ fprintf(in_fp, "/MediaBox [ 0 0 %u %u ]\n", g_doc_h_bound, g_doc_v_bound);
+ fputs(">>\nendobj\n", in_fp);
+}
+
+
+/*****************************************************************************/
+/* */
+/* PDF_FILE_OFFSET PDFFile_WriteXREF(FILE* in_fp) */
+/* */
+/* Writes out the XREF table. */
+/* */
+/*****************************************************************************/
+
+static PDF_FILE_OFFSET PDFFile_WriteXREF(FILE* in_fp)
+{
+ int i;
+ PDF_FILE_OFFSET xref_start;
+ t_offset_block_ptr the_block = g_obj_offset_list;
+
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% xref table:\n%%\n");
+
+ xref_start = ftell(in_fp);
+ fputs("xref\n", in_fp);
+ fprintf(in_fp, "0 %u\n", g_next_objnum);
+ fputs("0000000000 65535 f \n", in_fp); /* object 0 is a deleted obj */
+ Assert( (g_next_objnum == 1) || (the_block != NULL), no_fpos);
+ for (i = 1; i < g_next_objnum; i++) /* write out list of object offsets */
+ {
+ int block_pos = (i - 1) % kNumberOfObjectsPerBlock;
+
+ /* always write an entry (even if the object doesn't exist) */
+ fprintf(in_fp, "%010u 00000 n \n", the_block->m_block[block_pos]);
+
+ if (the_block->m_block[block_pos] == 0)
+ {
+ t_tempbuf str;
+
+ strcpy(str, "PDFFile_WriteXREF: undefined object number: ");
+ sprintf(str + strlen(str), "%u", i);
+ Error(48, 39, "%s", WARN, no_fpos, str);
+ }
+
+ if (block_pos == (kNumberOfObjectsPerBlock - 1))
+ {
+ the_block = the_block->m_next_block;
+ /* Assert(the_block != NULL, no_fpos); not always true! */
+ }
+ }
+ return xref_start;
+}
+
+/*****************************************************************************/
+/* */
+/* void PDFFile_Cleanup(FILE* in_fp) */
+/* */
+/* Cleans up processing after all pages has been written out. */
+/* */
+/*****************************************************************************/
+
+void PDFFile_Cleanup(FILE* in_fp)
+{
+ PDF_FILE_OFFSET xref_start; /* file offset of start of xref table */
+ PDF_OBJECT_NUM catalog_obj_num;
+ PDF_OBJECT_NUM info_obj_num;
+ PDF_OBJECT_NUM dests_obj_num;
+
+ /* write out any unresolved link annotations. This could be done earlier */
+ /* (in fact, it can be done as each new target is defined) but I've */
+ /* arbitrarily decided to do it here. */
+ {
+ t_source_annot_entry_ptr source = g_source_annot_list;
+
+ while (source != NULL)
+ {
+ t_target_annot_entry_ptr target;
+
+ Assert(source->m_target == NULL, no_fpos);
+ target = PDFTargetAnnot_Find(source->m_name);
+ if (target != NULL)
+ {
+ source->m_target = target;
+ PDFSourceAnnot_Write(in_fp, source);
+ }
+ source = source->m_next_entry;
+ }
+ }
+
+ /* write out pages object */
+ PDFFile_WritePagesObject(in_fp);
+
+ /* if file has exported targets for links then write out /Dests dictionary */
+ if (g_has_exported_targets)
+ {
+ t_target_annot_entry_ptr entry = g_target_annot_list;
+
+ Assert(entry != NULL, no_fpos); /* should be at least an entry! */
+
+ /* write PDF 1.1 style /Dests dictionary */
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% /Dests dictionary (exported links):\n%%\n");
+
+ dests_obj_num = PDFObject_WriteNewObj(in_fp);
+ fputs("<<\n", in_fp);
+
+ while (entry != NULL)
+ {
+ if (entry->m_for_export)
+ {
+ fprintf(in_fp, "/%s [ ", entry->m_name);
+ PDFObject_WriteRef(in_fp, entry->m_page_object_num);
+ fprintf(in_fp, " /XYZ null null null ]\n");
+ }
+ entry = entry->m_next_entry;
+ }
+ fputs(">>\nendobj\n", in_fp);
+ }
+
+ /* write out catalog object */
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% catalog:\n%%\n");
+
+ catalog_obj_num = PDFObject_WriteNewObj(in_fp);
+ fputs("<<\n", in_fp);
+ fputs("/Type /Catalog\n", in_fp);
+ fputs("/Pages ", in_fp);
+ PDFObject_WriteRef(in_fp, g_pages_root);
+ fputs("\n", in_fp);
+
+ /* if file has exported targets for links then write out a /Dest dictionary */
+ if (g_has_exported_targets)
+ {
+ fputs("/Dests ", in_fp);
+ PDFObject_WriteRef(in_fp, dests_obj_num);
+ fputs("\n", in_fp);
+ }
+
+ /* ***
+ fputs("/PageMode ", in_fp);
+ switch ()
+ {
+ }
+ fputs("\n", in_fp);
+ *** */
+
+ fputs(">>\nendobj\n", in_fp);
+
+ /* write out info object */
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% Info object:\n%%\n");
+
+ /* ***
+ Author string (Optional) The name of the person who created the document.
+ CreationDate Date (Optional) The date the document was created.
+ ModDate Date (Optional) The date the document was last modified.
+ Creator string (Optional) If the document was converted into a PDF document from another
+ form, this is the name of the application that created the original document.
+ Producer string (Optional) The name of the application that converted the document from its native
+ format to PDF.
+ Title string (Optional) The documentÕs title.
+ Subject string (Optional) The subject of the document.
+ Keywords string (Optional) Keywords associated with the document.
+
+ example:
+
+ /Creator (Adobe Illustrator)
+ /CreationDate (D:19930204080603-08'00')
+ /Author (Werner Heisenberg)
+ /Producer (Acrobat Network Distiller 1.0 for Macintosh)
+ *** */
+
+ info_obj_num = PDFObject_WriteNewObj(in_fp);
+ fputs("<<\n", in_fp);
+
+ fprintf(in_fp, "/Creator (%s)\n", LOUT_VERSION);
+ fprintf(in_fp, "/Producer (%s)\n", LOUT_VERSION);
+
+ {
+ time_t now;
+ struct tm *date;
+
+ /* I will presume that localtime() is Y2K compliant. If it isn't */
+ /* on your system, feel free to tweak this code. :-) */
+
+ now = time( NULL );
+ date = localtime( &now );
+ fprintf(in_fp, "/CreationDate (D:%.4d%.2d%.2d%.2d%.2d%.2d)\n",
+ date->tm_year + 1900, date->tm_mon + 1, date->tm_mday,
+ date->tm_hour, date->tm_min, date->tm_sec);
+ }
+
+ if (g_doc_author != NULL)
+ fprintf(in_fp, "/Author (%s)\n", g_doc_author);
+
+ if (g_doc_title != NULL)
+ fprintf(in_fp, "/Title (%s)\n", g_doc_title);
+
+ if (g_doc_subject != NULL)
+ fprintf(in_fp, "/Subject (%s)\n", g_doc_subject);
+
+ if (g_doc_keywords != NULL)
+ fprintf(in_fp, "/Keywords (%s)\n", g_doc_keywords);
+
+ fputs(">>\nendobj\n", in_fp);
+
+ /* write out xref table */
+ xref_start = PDFFile_WriteXREF(in_fp);
+
+ /* write out trailer */
+ /* *** uwe: comments can appear in the body only.
+ if (g_PDF_debug)
+ fprintf(in_fp, "%%\n%% trailer:\n%%\n");
+ *** */
+
+ fputs("trailer\n<<\n", in_fp);
+ fprintf(in_fp, "/Size %u\n", g_next_objnum);
+ fputs("/Root ", in_fp);
+ PDFObject_WriteRef(in_fp, catalog_obj_num);
+ fputs("\n/Info ", in_fp);
+ PDFObject_WriteRef(in_fp, info_obj_num);
+
+ fprintf(in_fp, " >>\nstartxref\n%u\n", xref_start);
+ fputs("%%EOF\n", in_fp);
+
+ /* memory deallocation (no need to dispose of the qsave_marking_stack */
+ /* because it's always empty after a page has been processed) */
+
+ while (g_obj_offset_list != NULL)
+ {
+ t_offset_block_ptr the_block = g_obj_offset_list;
+ g_obj_offset_list = the_block->m_next_block;
+ free(the_block);
+ }
+
+ while (g_font_encoding_list != NULL)
+ {
+ t_font_encoding_entry_ptr the_block = g_font_encoding_list;
+ g_font_encoding_list = the_block->m_next_entry;
+ free(the_block->m_font_encoding);
+ free(the_block);
+ }
+
+ while (g_font_list != NULL)
+ {
+ t_font_list_entry_ptr the_block = g_font_list;
+ g_font_list = the_block->m_next_font_entry;
+ free(the_block->m_PDF_font_name);
+ free(the_block->m_short_font_name);
+ free(the_block->m_actual_font_name);
+ free(the_block);
+ }
+
+ while (g_page_block_list != NULL)
+ {
+ t_page_block_ptr the_block = g_page_block_list;
+ g_page_block_list = the_block->m_next_block;
+ free(the_block);
+ }
+
+ while (g_source_annot_list != NULL)
+ {
+ t_source_annot_entry_ptr entry = g_source_annot_list;
+
+ if (entry->m_target == NULL)
+ {
+ t_tempbuf str;
+ strcpy(str, "PDFFile_Cleanup: unresolved link annotation named ");
+ strcat(str, (char*) entry->m_name);
+ Error(48, 40, "%s", WARN, no_fpos, str);
+ }
+
+ g_source_annot_list = PDFSourceAnnot_Dispose(entry);
+ }
+
+ while (g_target_annot_list != NULL)
+ {
+ t_target_annot_entry_ptr entry = g_target_annot_list;
+ g_target_annot_list = entry->m_next_entry;
+ free(entry->m_name);
+ free(entry);
+ }
+
+#ifdef _CALC_LARGEST_PAGE_OBJECT_
+ /* display largest page object */
+ {
+ t_tempbuf str;
+ /* JK sprintf(str, "The largest page object is %u bytes long.", g_max_page_length); */
+ Error(48, 41, "The largest page object is %u bytes long.", WARN, no_fpos, g_max_page_length);
+ }
+#endif
+}