diff --git a/bitblt_g4.c b/bitblt_g4.c new file mode 100644 index 0000000..8516335 --- /dev/null +++ b/bitblt_g4.c @@ -0,0 +1,179 @@ +#include +#include + + +#include "libpdf.h" +#include "libpdf_util.h" +#include "libpdf_prim.h" +#include "libpdf_private.h" + + +struct pdf_g4_image +{ + unsigned long Columns; + unsigned long Rows; + unsigned long rowbytes; + int BlackIs1; + unsigned char *data; + unsigned long len; + char XObject_name [4]; +}; + + +char pdf_new_XObject (pdf_page_handle pdf_page, struct pdf_obj *ind_ref) +{ + char XObject_name [4] = "Im "; + + XObject_name [2] = ++pdf_page->last_XObject_name; + + if (! pdf_page->XObject_dict) + { + pdf_page->XObject_dict = pdf_new_obj (PT_DICTIONARY); + pdf_set_dict_entry (pdf_page->resources, "XObject", pdf_page->XObject_dict); + } + + pdf_set_dict_entry (pdf_page->XObject_dict, & XObject_name [0], ind_ref); + + return (pdf_page->last_XObject_name); +} + + +void pdf_write_g4_content_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + unsigned long width = (8.5 * 72); /* full width of page */ + unsigned long height = (11 * 72); /* full height of page */ + unsigned long x = 0; /* 0 is left edge */ + unsigned long y = 0; /* 0 is bottom edge */ + struct pdf_g4_image *image = app_data; + + char str1 [100]; + char *str2 = "/"; + char *str3 = " Do\r\n"; + + /* width 0 0 height x y cm */ + sprintf (str1, "q %ld 0 0 %ld %ld %ld cm\r\n", width, height, x, y); + + pdf_stream_write_data (pdf_file, stream, str1, strlen (str1)); + pdf_stream_write_data (pdf_file, stream, str2, strlen (str2)); + pdf_stream_write_data (pdf_file, stream, & image->XObject_name [0], + strlen (& image->XObject_name [0])); + pdf_stream_write_data (pdf_file, stream, str3, strlen (str3)); +} + + +void pdf_write_g4_fax_image_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + struct pdf_g4_image *image = app_data; + +#if 1 + pdf_stream_write_data (pdf_file, stream, image->data, image->len); +#else + unsigned long row = 0; + unsigned char *ref; + unsigned char *raw; + + ref = NULL; + raw = image->data; + + while (row < image->Rows) + { + pdf_stream_write_data (pdf_file, stream, raw, image->rowbytes); + + row++; + ref = raw; + raw += image->rowbytes; + } + /* $$$ generate and write EOFB code */ + /* $$$ flush any remaining buffered bits */ +#endif +} + + +void pdf_write_g4_fax_image (pdf_page_handle pdf_page, + unsigned long Columns, + unsigned long Rows, + unsigned long rowbytes, + int ImageMask, + int BlackIs1, /* boolean, typ. false */ + unsigned char *data, + unsigned long len) +{ + struct pdf_g4_image *image; + + struct pdf_obj *stream; + struct pdf_obj *stream_dict; + struct pdf_obj *decode_parms; + + struct pdf_obj *content_stream; + + image = pdf_calloc (sizeof (struct pdf_g4_image)); + + image->Columns = Columns; + image->Rows = Rows; + image->rowbytes = rowbytes; + image->BlackIs1 = BlackIs1; + image->data = data; + image->len = len; + + stream_dict = pdf_new_obj (PT_DICTIONARY); + + stream = pdf_new_ind_ref (pdf_page->pdf_file, + pdf_new_stream (pdf_page->pdf_file, + stream_dict, + & pdf_write_g4_fax_image_callback, + image)); + + strcpy (& image->XObject_name [0], "Im "); + image->XObject_name [2] = pdf_new_XObject (pdf_page, stream); + + pdf_set_dict_entry (stream_dict, "Type", pdf_new_name ("XObject")); + pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image")); + pdf_set_dict_entry (stream_dict, "Name", pdf_new_name (& image->XObject_name [0])); + pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (Columns)); + pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (Rows)); + pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (1)); + if (ImageMask) + pdf_set_dict_entry (stream_dict, "ImageMask", pdf_new_bool (ImageMask)); + else + pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name ("DeviceGray")); + + decode_parms = pdf_new_obj (PT_DICTIONARY); + + pdf_set_dict_entry (decode_parms, + "K", + pdf_new_integer (-1)); + + pdf_set_dict_entry (decode_parms, + "Columns", + pdf_new_integer (Columns)); + + pdf_set_dict_entry (decode_parms, + "Rows", + pdf_new_integer (Rows)); + + if (BlackIs1) + pdf_set_dict_entry (decode_parms, + "BlackIs1", + pdf_new_bool (BlackIs1)); + + pdf_stream_add_filter (stream, "CCITTFaxDecode", decode_parms); + + /* the following will write the stream, using our callback function to + get the actual data */ + pdf_write_ind_obj (pdf_page->pdf_file, stream); + + content_stream = pdf_new_ind_ref (pdf_page->pdf_file, + pdf_new_stream (pdf_page->pdf_file, + pdf_new_obj (PT_DICTIONARY), + & pdf_write_g4_content_callback, + image)); + + pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); + + pdf_write_ind_obj (pdf_page->pdf_file, content_stream); +} + diff --git a/pdf.c b/pdf.c new file mode 100644 index 0000000..e7a328b --- /dev/null +++ b/pdf.c @@ -0,0 +1,171 @@ +#include +#include + + +#include "libpdf.h" +#include "libpdf_util.h" +#include "libpdf_prim.h" +#include "libpdf_private.h" + + +static void pdf_set_info (pdf_file_handle pdf_file, char *key, char *val) +{ + if (! pdf_file->info) + pdf_file->info = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + + pdf_set_dict_entry (pdf_file->info, key, pdf_new_string (val)); +} + + +void pdf_init (void) +{ +} + + +struct pdf_pages *pdf_new_pages (pdf_file_handle pdf_file) +{ + struct pdf_pages *pages = pdf_calloc (sizeof (struct pdf_pages)); + pages->kids = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_ARRAY)); + pages->count = pdf_new_integer (0); + pages->pages_dict = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + pdf_set_dict_entry (pages->pages_dict, "Type", pdf_new_name ("Pages")); + pdf_set_dict_entry (pages->pages_dict, "Kids", pages->kids); + pdf_set_dict_entry (pages->pages_dict, "Count", pages->count); + return (pages); +} + + +pdf_file_handle pdf_create (char *filename) +{ + pdf_file_handle pdf_file; + + pdf_file = pdf_calloc (sizeof (struct pdf_file)); + + pdf_file->f = fopen (filename, "wb"); + if (! pdf_file->f) + { + pdf_fatal ("error opening output file\n"); + } + + pdf_file->root = pdf_new_pages (pdf_file); + + pdf_file->catalog = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + pdf_set_dict_entry (pdf_file->catalog, "Type", pdf_new_name ("Catalog")); + pdf_set_dict_entry (pdf_file->catalog, "Pages", pdf_file->root->pages_dict); + /* Outlines dictionary will be created later if needed*/ + pdf_set_dict_entry (pdf_file->catalog, "PageMode", pdf_new_name ("UseNone")); + + pdf_file->info = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + pdf_set_info (pdf_file, "Producer", "libpdf, Copyright 2003 Eric Smith "); + + pdf_file->trailer_dict = pdf_new_obj (PT_DICTIONARY); + /* Size key will be added later */ + pdf_set_dict_entry (pdf_file->trailer_dict, "Root", pdf_file->catalog); + pdf_set_dict_entry (pdf_file->trailer_dict, "Info", pdf_file->info); + + /* write file header */ + fprintf (pdf_file->f, "%%PDF-1.2\r\n"); + + return (pdf_file); +} + + +void pdf_close (pdf_file_handle pdf_file) +{ + /* write body */ + pdf_write_all_ind_obj (pdf_file); + + /* write cross reference table and get maximum object number */ + pdf_set_dict_entry (pdf_file->trailer_dict, "Size", pdf_new_integer (pdf_write_xref (pdf_file))); + + /* write trailer */ + fprintf (pdf_file->f, "trailer\r\n"); + pdf_write_obj (pdf_file, pdf_file->trailer_dict); + fprintf (pdf_file->f, "startxref\r\n"); + fprintf (pdf_file->f, "%ld\r\n", pdf_file->xref_offset); + fprintf (pdf_file->f, "%%%%EOF\r\n"); + + fclose (pdf_file->f); + /* should free stuff here */ +} + + +void pdf_set_author (pdf_file_handle pdf_file, char *author) +{ + pdf_set_info (pdf_file, "Author", author); +} + +void pdf_set_creator (pdf_file_handle pdf_file, char *creator) +{ + pdf_set_info (pdf_file, "Creator", creator); +} + +void pdf_set_producer (pdf_file_handle pdf_file, char *producer) +{ + pdf_set_info (pdf_file, "Producer", producer); +} + +void pdf_set_title (pdf_file_handle pdf_file, char *title) +{ + pdf_set_info (pdf_file, "Title", title); +} + +void pdf_set_subject (pdf_file_handle pdf_file, char *subject) +{ + pdf_set_info (pdf_file, "Subject", subject); +} + +void pdf_set_keywords (pdf_file_handle pdf_file, char *keywords) +{ + pdf_set_info (pdf_file, "Keywords", keywords); +} + + +pdf_page_handle pdf_new_page (pdf_file_handle pdf_file, + double width, + double height) +{ + pdf_page_handle page = pdf_calloc (sizeof (struct pdf_page)); + + page->pdf_file = pdf_file; + + page->media_box = pdf_new_obj (PT_ARRAY); + pdf_add_array_elem (page->media_box, pdf_new_real (0)); + pdf_add_array_elem (page->media_box, pdf_new_real (0)); + pdf_add_array_elem (page->media_box, pdf_new_real (width)); + pdf_add_array_elem (page->media_box, pdf_new_real (height)); + + page->procset = pdf_new_obj (PT_ARRAY); + pdf_add_array_elem (page->procset, pdf_new_name ("PDF")); + + page->resources = pdf_new_obj (PT_DICTIONARY); + pdf_set_dict_entry (page->resources, "ProcSet", page->procset); + + page->page_dict = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + pdf_set_dict_entry (page->page_dict, "Type", pdf_new_name ("Page")); + pdf_set_dict_entry (page->page_dict, "MediaBox", page->media_box); + pdf_set_dict_entry (page->page_dict, "Resources", page->resources); + + /* $$$ currently only support a single-level pages tree */ + pdf_set_dict_entry (page->page_dict, "Parent", pdf_file->root->pages_dict); + pdf_add_array_elem (pdf_file->root->kids, page->page_dict); + pdf_set_integer (pdf_file->root->count, + pdf_get_integer (pdf_file->root->count) + 1); + + page->last_XObject_name = '@'; /* first name will be "ImA" */ + + return (page); +} + +void pdf_close_page (pdf_page_handle pdf_page) +{ +} + + +void pdf_set_page_number (pdf_page_handle pdf_page, char *page_number) +{ +} + +void pdf_bookmark (pdf_page_handle pdf_page, int level, char *name) +{ +} diff --git a/pdf.h b/pdf.h new file mode 100644 index 0000000..42fc2db --- /dev/null +++ b/pdf.h @@ -0,0 +1,44 @@ +typedef struct pdf_file *pdf_file_handle; + +typedef struct pdf_page *pdf_page_handle; + + +void pdf_init (void); + +pdf_file_handle pdf_create (char *filename); + +void pdf_close (pdf_file_handle pdf_file); + +void pdf_set_author (pdf_file_handle pdf_file, char *author); +void pdf_set_creator (pdf_file_handle pdf_file, char *author); +void pdf_set_title (pdf_file_handle pdf_file, char *author); +void pdf_set_subject (pdf_file_handle pdf_file, char *author); +void pdf_set_keywords (pdf_file_handle pdf_file, char *author); + + +/* width and height in units of 1/72 inch */ +pdf_page_handle pdf_new_page (pdf_file_handle pdf_file, + double width, + double height); + +void pdf_close_page (pdf_page_handle pdf_page); + + +/* The length of the data must be Rows * rowbytes. + Note that rowbytes must be at least (Columns+7)/8, but may be arbitrarily + large. */ +void pdf_write_g4_fax_image (pdf_page_handle pdf_page, + unsigned long Columns, + unsigned long Rows, + unsigned long rowbytes, + int ImageMask, + int BlackIs1, /* boolean, typ. false */ + unsigned char *data, + unsigned long len); + + +void pdf_set_page_number (pdf_page_handle pdf_page, char *page_number); + +void pdf_bookmark (pdf_page_handle pdf_page, int level, char *name); + +void pdf_insert_tiff_image (pdf_page_handle pdf_page, char *filename); diff --git a/pdf_g4.c b/pdf_g4.c new file mode 100644 index 0000000..8516335 --- /dev/null +++ b/pdf_g4.c @@ -0,0 +1,179 @@ +#include +#include + + +#include "libpdf.h" +#include "libpdf_util.h" +#include "libpdf_prim.h" +#include "libpdf_private.h" + + +struct pdf_g4_image +{ + unsigned long Columns; + unsigned long Rows; + unsigned long rowbytes; + int BlackIs1; + unsigned char *data; + unsigned long len; + char XObject_name [4]; +}; + + +char pdf_new_XObject (pdf_page_handle pdf_page, struct pdf_obj *ind_ref) +{ + char XObject_name [4] = "Im "; + + XObject_name [2] = ++pdf_page->last_XObject_name; + + if (! pdf_page->XObject_dict) + { + pdf_page->XObject_dict = pdf_new_obj (PT_DICTIONARY); + pdf_set_dict_entry (pdf_page->resources, "XObject", pdf_page->XObject_dict); + } + + pdf_set_dict_entry (pdf_page->XObject_dict, & XObject_name [0], ind_ref); + + return (pdf_page->last_XObject_name); +} + + +void pdf_write_g4_content_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + unsigned long width = (8.5 * 72); /* full width of page */ + unsigned long height = (11 * 72); /* full height of page */ + unsigned long x = 0; /* 0 is left edge */ + unsigned long y = 0; /* 0 is bottom edge */ + struct pdf_g4_image *image = app_data; + + char str1 [100]; + char *str2 = "/"; + char *str3 = " Do\r\n"; + + /* width 0 0 height x y cm */ + sprintf (str1, "q %ld 0 0 %ld %ld %ld cm\r\n", width, height, x, y); + + pdf_stream_write_data (pdf_file, stream, str1, strlen (str1)); + pdf_stream_write_data (pdf_file, stream, str2, strlen (str2)); + pdf_stream_write_data (pdf_file, stream, & image->XObject_name [0], + strlen (& image->XObject_name [0])); + pdf_stream_write_data (pdf_file, stream, str3, strlen (str3)); +} + + +void pdf_write_g4_fax_image_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + struct pdf_g4_image *image = app_data; + +#if 1 + pdf_stream_write_data (pdf_file, stream, image->data, image->len); +#else + unsigned long row = 0; + unsigned char *ref; + unsigned char *raw; + + ref = NULL; + raw = image->data; + + while (row < image->Rows) + { + pdf_stream_write_data (pdf_file, stream, raw, image->rowbytes); + + row++; + ref = raw; + raw += image->rowbytes; + } + /* $$$ generate and write EOFB code */ + /* $$$ flush any remaining buffered bits */ +#endif +} + + +void pdf_write_g4_fax_image (pdf_page_handle pdf_page, + unsigned long Columns, + unsigned long Rows, + unsigned long rowbytes, + int ImageMask, + int BlackIs1, /* boolean, typ. false */ + unsigned char *data, + unsigned long len) +{ + struct pdf_g4_image *image; + + struct pdf_obj *stream; + struct pdf_obj *stream_dict; + struct pdf_obj *decode_parms; + + struct pdf_obj *content_stream; + + image = pdf_calloc (sizeof (struct pdf_g4_image)); + + image->Columns = Columns; + image->Rows = Rows; + image->rowbytes = rowbytes; + image->BlackIs1 = BlackIs1; + image->data = data; + image->len = len; + + stream_dict = pdf_new_obj (PT_DICTIONARY); + + stream = pdf_new_ind_ref (pdf_page->pdf_file, + pdf_new_stream (pdf_page->pdf_file, + stream_dict, + & pdf_write_g4_fax_image_callback, + image)); + + strcpy (& image->XObject_name [0], "Im "); + image->XObject_name [2] = pdf_new_XObject (pdf_page, stream); + + pdf_set_dict_entry (stream_dict, "Type", pdf_new_name ("XObject")); + pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image")); + pdf_set_dict_entry (stream_dict, "Name", pdf_new_name (& image->XObject_name [0])); + pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (Columns)); + pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (Rows)); + pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (1)); + if (ImageMask) + pdf_set_dict_entry (stream_dict, "ImageMask", pdf_new_bool (ImageMask)); + else + pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name ("DeviceGray")); + + decode_parms = pdf_new_obj (PT_DICTIONARY); + + pdf_set_dict_entry (decode_parms, + "K", + pdf_new_integer (-1)); + + pdf_set_dict_entry (decode_parms, + "Columns", + pdf_new_integer (Columns)); + + pdf_set_dict_entry (decode_parms, + "Rows", + pdf_new_integer (Rows)); + + if (BlackIs1) + pdf_set_dict_entry (decode_parms, + "BlackIs1", + pdf_new_bool (BlackIs1)); + + pdf_stream_add_filter (stream, "CCITTFaxDecode", decode_parms); + + /* the following will write the stream, using our callback function to + get the actual data */ + pdf_write_ind_obj (pdf_page->pdf_file, stream); + + content_stream = pdf_new_ind_ref (pdf_page->pdf_file, + pdf_new_stream (pdf_page->pdf_file, + pdf_new_obj (PT_DICTIONARY), + & pdf_write_g4_content_callback, + image)); + + pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); + + pdf_write_ind_obj (pdf_page->pdf_file, content_stream); +} + diff --git a/pdf_prim.c b/pdf_prim.c new file mode 100644 index 0000000..2ba81e8 --- /dev/null +++ b/pdf_prim.c @@ -0,0 +1,525 @@ +#include +#include +#include + +#include "libpdf.h" +#include "libpdf_util.h" +#include "libpdf_prim.h" +#include "libpdf_private.h" + + +struct pdf_array_elem +{ + struct pdf_array_elem *next; + struct pdf_obj *val; +}; + + +struct pdf_array +{ + struct pdf_array_elem *first; + struct pdf_array_elem *last; +}; + + +struct pdf_dict_entry +{ + struct pdf_dict_entry *next; + char *key; + struct pdf_obj *val; +}; + + +struct pdf_dict +{ + struct pdf_dict_entry *first; +}; + + +struct pdf_stream +{ + struct pdf_obj *stream_dict; + struct pdf_obj *length; + pdf_stream_write_callback callback; + void *app_data; /* arg to pass to callback */ + struct pdf_obj *filters; /* name or array of names */ + struct pdf_obj *decode_parms; +}; + + +struct pdf_obj +{ + /* these fields only apply to indirectly referenced objects */ + struct pdf_obj *prev; + struct pdf_obj *next; + unsigned long obj_num; + unsigned long obj_gen; + long int file_offset; + + /* these fields apply to all objects */ + unsigned long ref_count; + pdf_obj_type type; + union { + int bool; + char *name; + char *string; + unsigned long integer; + double real; + struct pdf_obj *ind_ref; + struct pdf_dict dict; + struct pdf_array array; + struct pdf_stream stream; + } val; +}; + + +struct pdf_obj *ref (struct pdf_obj *obj) +{ + obj->ref_count++; + return (obj); +} + + +void unref (struct pdf_obj *obj) +{ + if ((--obj->ref_count) == 0) + { + /* $$$ free the object */ + } +} + + +struct pdf_obj *pdf_deref_ind_obj (struct pdf_obj *ind_obj) +{ + pdf_assert (ind_obj->type == PT_IND_REF); + return (ind_obj->val.ind_ref); +} + + +void pdf_set_dict_entry (struct pdf_obj *dict_obj, char *key, struct pdf_obj *val) +{ + struct pdf_dict_entry *entry; + + if (dict_obj->type == PT_IND_REF) + dict_obj = pdf_deref_ind_obj (dict_obj); + + pdf_assert (dict_obj->type == PT_DICTIONARY); + + /* replacing existing entry? */ + for (entry = dict_obj->val.dict.first; entry; entry = entry->next) + if (strcmp (entry->key, key) == 0) + { + unref (entry->val); + entry->val = ref (val); + return; + } + + /* new entry */ + entry = pdf_calloc (sizeof (struct pdf_dict_entry)); + + entry->next = dict_obj->val.dict.first; + dict_obj->val.dict.first = entry; + + entry->key = pdf_strdup (key); + entry->val = ref (val); +} + + +struct pdf_obj *pdf_get_dict_entry (struct pdf_obj *dict_obj, char *key) +{ + struct pdf_dict_entry *entry; + + if (dict_obj->type == PT_IND_REF) + dict_obj = pdf_deref_ind_obj (dict_obj); + + pdf_assert (dict_obj->type == PT_DICTIONARY); + + for (entry = dict_obj->val.dict.first; entry; entry = entry->next) + if (strcmp (entry->key, key) == 0) + return (entry->val); + + return (NULL); +} + + +void pdf_add_array_elem (struct pdf_obj *array_obj, struct pdf_obj *val) +{ + struct pdf_array_elem *elem = pdf_calloc (sizeof (struct pdf_array_elem)); + + if (array_obj->type == PT_IND_REF) + array_obj = pdf_deref_ind_obj (array_obj); + + pdf_assert (array_obj->type == PT_ARRAY); + + elem->val = ref (val); + + if (! array_obj->val.array.first) + array_obj->val.array.first = elem; + else + array_obj->val.array.last->next = elem; + + array_obj->val.array.last = elem; +} + + +struct pdf_obj *pdf_new_obj (pdf_obj_type type) +{ + struct pdf_obj *obj = pdf_calloc (sizeof (struct pdf_obj)); + obj->type = type; + return (obj); +} + + +struct pdf_obj *pdf_new_bool (int bool) +{ + struct pdf_obj *obj = pdf_new_obj (PT_BOOL); + obj->val.bool = bool; + return (obj); +} + + +struct pdf_obj *pdf_new_name (char *name) +{ + struct pdf_obj *obj = pdf_new_obj (PT_NAME); + obj->val.name = pdf_strdup (name); + return (obj); +} + + +struct pdf_obj *pdf_new_string (char *str) +{ + struct pdf_obj *obj = pdf_new_obj (PT_STRING); + obj->val.string = pdf_strdup (str); + return (obj); +} + + +struct pdf_obj *pdf_new_integer (unsigned long val) +{ + struct pdf_obj *obj = pdf_new_obj (PT_INTEGER); + obj->val.integer = val; + return (obj); +} + + +struct pdf_obj *pdf_new_real (double val) +{ + struct pdf_obj *obj = pdf_new_obj (PT_REAL); + obj->val.real = val; + return (obj); +} + + +struct pdf_obj *pdf_new_stream (pdf_file_handle pdf_file, + struct pdf_obj *stream_dict, + pdf_stream_write_callback callback, + void *app_data) +{ + struct pdf_obj *obj = pdf_new_obj (PT_STREAM); + + obj->val.stream.stream_dict = stream_dict; + obj->val.stream.length = pdf_new_ind_ref (pdf_file, pdf_new_integer (0)); + pdf_set_dict_entry (obj->val.stream.stream_dict, "Length", obj->val.stream.length); + + obj->val.stream.callback = callback; + obj->val.stream.app_data = app_data; + return (obj); +} + + +/* $$$ currently limited to one filter per stream */ +void pdf_stream_add_filter (struct pdf_obj *stream, + char *filter_name, + struct pdf_obj *decode_parms) +{ + if (stream->type == PT_IND_REF) + stream = pdf_deref_ind_obj (stream); + + pdf_assert (stream->type == PT_STREAM); + + pdf_set_dict_entry (stream->val.stream.stream_dict, "Filter", pdf_new_name (filter_name)); + if (decode_parms) + pdf_set_dict_entry (stream->val.stream.stream_dict, "DecodeParms", decode_parms); +} + + +struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj) +{ + struct pdf_obj *ind_obj; + + pdf_assert (obj->type != PT_IND_REF); + + ind_obj = pdf_new_obj (PT_IND_REF); + + ind_obj->type = PT_IND_REF; + ind_obj->val.ind_ref = obj; + + /* is there already an indirect reference to this object? */ + if (! obj->obj_num) + { + /* no, assign object number/generation and add to linked list */ + if (! pdf_file->first_ind_obj) + { + obj->obj_num = 1; + pdf_file->first_ind_obj = pdf_file->last_ind_obj = obj; + } + else + { + obj->obj_num = pdf_file->last_ind_obj->obj_num + 1; + pdf_file->last_ind_obj->next = obj; + obj->prev = pdf_file->last_ind_obj; + pdf_file->last_ind_obj = obj; + } + } + + return (ind_obj); +} + + +unsigned long pdf_get_integer (struct pdf_obj *obj) +{ + if (obj->type == PT_IND_REF) + obj = pdf_deref_ind_obj (obj); + + pdf_assert (obj->type == PT_INTEGER); + + return (obj->val.integer); +} + +void pdf_set_integer (struct pdf_obj *obj, unsigned long val) +{ + if (obj->type == PT_IND_REF) + obj = pdf_deref_ind_obj (obj); + + pdf_assert (obj->type == PT_INTEGER); + + obj->val.integer = val; +} + + +double pdf_get_real (struct pdf_obj *obj) +{ + if (obj->type == PT_IND_REF) + obj = pdf_deref_ind_obj (obj); + + pdf_assert (obj->type == PT_REAL); + + return (obj->val.real); +} + +void pdf_set_real (struct pdf_obj *obj, double val) +{ + if (obj->type == PT_IND_REF) + obj = pdf_deref_ind_obj (obj); + + pdf_assert (obj->type == PT_REAL); + + obj->val.real = val; +} + + +static int name_char_needs_quoting (char c) +{ + return ((c < '!') || (c > '~') || (c == '/') || (c == '\\') || + (c == '(') || (c == ')') || (c == '<') || (c == '>') || + (c == '[') || (c == ']') || (c == '{') || (c == '}') || + (c == '%')); +} + + +void pdf_write_name (pdf_file_handle pdf_file, char *s) +{ + fprintf (pdf_file->f, "/"); + while (*s) + if (name_char_needs_quoting (*s)) + fprintf (pdf_file->f, "#%02x", 0xff & *(s++)); + else + fprintf (pdf_file->f, "%c", *(s++)); + fprintf (pdf_file->f, " "); +} + + +static int string_char_needs_quoting (char c) +{ + return ((c < ' ') || (c > '~') || (c == '\\') || + (c == '(') || (c == ')')); +} + + +void pdf_write_string (pdf_file_handle pdf_file, char *s) +{ + fprintf (pdf_file->f, "("); + while (*s) + if (string_char_needs_quoting (*s)) + fprintf (pdf_file->f, "\\%03o", 0xff & *(s++)); + else + fprintf (pdf_file->f, "%c", *(s++)); + fprintf (pdf_file->f, ") "); +} + + +void pdf_write_real (pdf_file_handle pdf_file, double num) +{ + /* $$$ not actually good enough, precision needs to be variable, + and no exponent is allowed */ + fprintf (pdf_file->f, "%0f ", num); +} + + +void pdf_write_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *ind_obj) +{ + struct pdf_obj *obj = pdf_deref_ind_obj (ind_obj); + fprintf (pdf_file->f, "%ld %ld R ", obj->obj_num, obj->obj_gen); +} + + +void pdf_write_array (pdf_file_handle pdf_file, struct pdf_obj *array_obj) +{ + struct pdf_array_elem *elem; + + pdf_assert (array_obj->type == PT_ARRAY); + + fprintf (pdf_file->f, "[ "); + for (elem = array_obj->val.array.first; elem; elem = elem->next) + { + pdf_write_obj (pdf_file, elem->val); + fprintf (pdf_file->f, " "); + } + fprintf (pdf_file->f, "] "); +} + + +void pdf_write_dict (pdf_file_handle pdf_file, struct pdf_obj *dict_obj) +{ + struct pdf_dict_entry *entry; + + pdf_assert (dict_obj->type == PT_DICTIONARY); + + fprintf (pdf_file->f, "<<\r\n"); + for (entry = dict_obj->val.dict.first; entry; entry = entry->next) + { + pdf_write_name (pdf_file, entry->key); + fprintf (pdf_file->f, " "); + pdf_write_obj (pdf_file, entry->val); + fprintf (pdf_file->f, "\r\n"); + } + fprintf (pdf_file->f, ">>\r\n"); +} + + +void pdf_stream_write_data (pdf_file_handle pdf_file, + struct pdf_obj *stream, + char *data, + unsigned long len) +{ + while (len) + { + unsigned long l2 = fwrite (data, 1, len, pdf_file->f); + data += l2; + len -= l2; + if (ferror (pdf_file->f)) + pdf_fatal ("error writing stream data\n"); + } +} + + +void pdf_write_stream (pdf_file_handle pdf_file, struct pdf_obj *stream) +{ + unsigned long begin_pos, end_pos; + + pdf_assert (stream->type == PT_STREAM); + + pdf_write_dict (pdf_file, stream->val.stream.stream_dict); + fprintf (pdf_file->f, "stream\r\n"); + begin_pos = ftell (pdf_file->f); + stream->val.stream.callback (pdf_file, + stream, + stream->val.stream.app_data); + end_pos = ftell (pdf_file->f); + fprintf (pdf_file->f, "\r\nendstream\r\n"); + + pdf_set_integer (stream->val.stream.length, end_pos - begin_pos); +} + + +void pdf_write_obj (pdf_file_handle pdf_file, struct pdf_obj *obj) +{ + switch (obj->type) + { + case PT_NULL: + fprintf (pdf_file->f, "null "); + break; + case PT_BOOL: + if (obj->val.bool) + fprintf (pdf_file->f, "true "); + else + fprintf (pdf_file->f, "false "); + break; + case PT_NAME: + pdf_write_name (pdf_file, obj->val.name); + break; + case PT_STRING: + pdf_write_string (pdf_file, obj->val.string); + break; + case PT_INTEGER: + fprintf (pdf_file->f, "%ld ", obj->val.integer); + break; + case PT_REAL: + pdf_write_real (pdf_file, obj->val.real); + break; + case PT_IND_REF: + pdf_write_ind_ref (pdf_file, obj); + break; + case PT_DICTIONARY: + pdf_write_dict (pdf_file, obj); + break; + case PT_ARRAY: + pdf_write_array (pdf_file, obj); + break; + case PT_STREAM: + pdf_write_stream (pdf_file, obj); + break; + default: + pdf_fatal ("bad object type\n"); + } +} + + +void pdf_write_ind_obj (pdf_file_handle pdf_file, struct pdf_obj *ind_obj) +{ + struct pdf_obj *obj; + + if (ind_obj->type == PT_IND_REF) + obj = pdf_deref_ind_obj (ind_obj); + else + obj = ind_obj; + + obj->file_offset = ftell (pdf_file->f); + fprintf (pdf_file->f, "%ld %ld obj\r\n", obj->obj_num, obj->obj_gen); + pdf_write_obj (pdf_file, obj); + fprintf (pdf_file->f, "endobj\r\n"); +} + + +void pdf_write_all_ind_obj (pdf_file_handle pdf_file) +{ + struct pdf_obj *ind_obj; + for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next) + if (! ind_obj->file_offset) + pdf_write_ind_obj (pdf_file, ind_obj); +} + + +unsigned long pdf_write_xref (pdf_file_handle pdf_file) +{ + struct pdf_obj *ind_obj; + pdf_file->xref_offset = ftell (pdf_file->f); + fprintf (pdf_file->f, "xref\r\n"); + fprintf (pdf_file->f, "0 %ld\r\n", pdf_file->last_ind_obj->obj_num + 1); + fprintf (pdf_file->f, "0000000000 65535 f\r\n"); + for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next) + fprintf (pdf_file->f, "%010ld 00000 n\r\n", ind_obj->file_offset); + return (pdf_file->last_ind_obj->obj_num + 1); +} + + diff --git a/pdf_prim.h b/pdf_prim.h new file mode 100644 index 0000000..fc86e00 --- /dev/null +++ b/pdf_prim.h @@ -0,0 +1,100 @@ +typedef enum +{ + PT_BAD, + + /* scalar */ + PT_NULL, + PT_BOOL, + PT_NAME, + PT_STRING, + PT_INTEGER, + PT_REAL, + PT_IND_REF, + + /* composite */ + PT_DICTIONARY, + PT_ARRAY, + PT_STREAM +} pdf_obj_type; + + +struct pdf_obj; + + +typedef void (*pdf_stream_write_callback)(pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data); + + +void pdf_set_dict_entry (struct pdf_obj *dict_obj, char *key, struct pdf_obj *val); +struct pdf_obj *pdf_get_dict_entry (struct pdf_obj *dict_obj, char *key); + + +void pdf_add_array_elem (struct pdf_obj *array_obj, struct pdf_obj *val); + + +/* Create a new object that will NOT be used indirectly */ +struct pdf_obj *pdf_new_obj (pdf_obj_type type); + +struct pdf_obj *pdf_new_bool (int bool); + +struct pdf_obj *pdf_new_name (char *name); + +struct pdf_obj *pdf_new_string (char *str); + +struct pdf_obj *pdf_new_integer (unsigned long val); + +struct pdf_obj *pdf_new_real (double val); + + +/* Create a new indirect object */ +struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj); + +/* get the object referenced by an indirect reference */ +struct pdf_obj *pdf_deref_ind_obj (struct pdf_obj *ind_obj); + + +unsigned long pdf_get_integer (struct pdf_obj *obj); +void pdf_set_integer (struct pdf_obj *obj, unsigned long val); + + +double pdf_get_real (struct pdf_obj *obj); +void pdf_set_real (struct pdf_obj *obj, double val); + + +/* The callback will be called when the stream data is to be written to the + file. app_data will be passed as an argument to the callback. */ +struct pdf_obj *pdf_new_stream (pdf_file_handle pdf_file, + struct pdf_obj *stream_dict, + pdf_stream_write_callback callback, + void *app_data); + +/* The callback should call pdf_stream_write_data to write the actual + stream data. */ +void pdf_stream_write_data (pdf_file_handle pdf_file, + struct pdf_obj *stream, + char *data, + unsigned long len); + +void pdf_stream_add_filter (struct pdf_obj *stream, + char *filter_name, + struct pdf_obj *decode_parms); + + +/* Write the object to the file */ +void pdf_write_obj (pdf_file_handle pdf_file, struct pdf_obj *obj); + + +/* Write the indirect object to the file. For most objects this should + be done by pdf_write_all_ind_obj() when the file is being closed, but for + large objects such as streams, it's probably better to do it as soon as the + object is complete. */ +void pdf_write_ind_obj (pdf_file_handle pdf_file, struct pdf_obj *ind_obj); + + +/* Write all indirect objects that haven't already been written to the file. */ +void pdf_write_all_ind_obj (pdf_file_handle pdf_file); + + +/* Write the cross reference table, and return the maximum object number */ +unsigned long pdf_write_xref (pdf_file_handle pdf_file); diff --git a/pdf_private.h b/pdf_private.h new file mode 100644 index 0000000..d4467e4 --- /dev/null +++ b/pdf_private.h @@ -0,0 +1,32 @@ +struct pdf_page +{ + pdf_file_handle pdf_file; + struct pdf_obj *page_dict; + struct pdf_obj *media_box; + struct pdf_obj *procset; + struct pdf_obj *resources; + + char last_XObject_name; + struct pdf_obj *XObject_dict; +}; + + +struct pdf_pages +{ + struct pdf_obj *pages_dict; + struct pdf_obj *kids; + struct pdf_obj *count; +}; + + +struct pdf_file +{ + FILE *f; + struct pdf_obj *first_ind_obj; + struct pdf_obj *last_ind_obj; + long int xref_offset; + struct pdf_obj *catalog; + struct pdf_obj *info; + struct pdf_pages *root; + struct pdf_obj *trailer_dict; +}; diff --git a/pdf_util.c b/pdf_util.c new file mode 100644 index 0000000..a07241d --- /dev/null +++ b/pdf_util.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "libpdf.h" +#include "libpdf_util.h" + + +void pdf_fatal (char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + + exit (2); +} + + +void *pdf_calloc (long int size) +{ + void *m = calloc (1, size); + if (! m) + pdf_fatal ("failed to allocate memory\n"); + return (m); +} + + +char *pdf_strdup (char *s) +{ + unsigned long len = strlen (s); + char *s2 = pdf_calloc (len + 1); + strcpy (s2, s); + return (s2); +} diff --git a/pdf_util.h b/pdf_util.h new file mode 100644 index 0000000..2045cd6 --- /dev/null +++ b/pdf_util.h @@ -0,0 +1,11 @@ +void pdf_fatal (char *fmt, ...); + +void *pdf_calloc (long int size); + +char *pdf_strdup (char *s); + +#define pdf_assert(cond) do \ + { \ + if (! (cond)) \ + pdf_fatal ("assert at %s(%d)\n", __FILE__, __LINE__); \ + } while (0)