blob: 31ca224dcb545e9df1a0346a6fb2ba038455ab71 [file] [log] [blame]
/*@z48.c:PDF back end@********************************************************/
/* */
/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.24) */
/* COPYRIGHT (C) 1991, 2000 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;
/* these two lines are Uwe patch Jul 18, 2000 */
if (in_font_encoding_name == NULL)
return 0;
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 ? 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);
/* bug fix from newman-andy@yale.edu Feb 23 2000 if (err != Z_OK) */
if ( err != Z_OK && g_comp_stream.avail_in != 0 )
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