diff options
Diffstat (limited to 'z48.c')
-rw-r--r-- | z48.c | 3706 |
1 files changed, 3706 insertions, 0 deletions
@@ -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 +} |