diff --git a/Makefile b/Makefile index 6ca861a..699895f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # t2p: build a PDF file out of one or more TIFF Class F Group 4 files # Makefile -# $Id: Makefile,v 1.16 2003/02/22 02:02:06 eric Exp $ +# $Id: Makefile,v 1.17 2003/03/04 17:58:36 eric Exp $ # Copyright 2001 Eric Smith # # This program is free software; you can redistribute it and/or modify @@ -43,7 +43,7 @@ PACKAGE = t2p TARGETS = t2p bitblt_test CSRCS = t2p.c semantics.c bitblt.c bitblt_test.c bitblt_table_gen.c \ - pdf.c pdf_util.c pdf_prim.c pdf_g4.c pdf_g4_table_gen.c + pdf.c pdf_util.c pdf_prim.c pdf_bookmark.c pdf_g4.c pdf_g4_table_gen.c OSRCS = scanner.l parser.y HDRS = t2p.h semantics.h bitblt.h \ pdf.h pdf_private.h pdf_util.h pdf_prim.h @@ -62,7 +62,7 @@ all: $(TARGETS) t2p: t2p.o scanner.o semantics.o parser.tab.o bitblt.o \ - pdf.o pdf_util.o pdf_prim.o pdf_g4.o + pdf.o pdf_util.o pdf_prim.o pdf_bookmark.o pdf_g4.o bitblt_tables.h: bitblt_table_gen ./bitblt_table_gen >bitblt_tables.h diff --git a/TODO b/TODO index b5787e9..f17ef34 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,5 @@ t2p TODO list -$Id: TODO,v 1.9 2003/02/21 01:25:47 eric Exp $ +$Id: TODO,v 1.10 2003/03/04 17:58:36 eric Exp $ No particular order. @@ -70,6 +70,7 @@ Adobe Systems Incorporated, Addison-Wesley, 1993.] * proper output of real numbers - variable precision, no exponent * bookmarks (outline) + * alternate destination specs (only is /Fit currently supported) * name trees, number trees diff --git a/pdf.h b/pdf.h index 9e69615..9489605 100644 --- a/pdf.h +++ b/pdf.h @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * PDF routines - * $Id: pdf.h,v 1.4 2003/02/21 01:25:47 eric Exp $ + * $Id: pdf.h,v 1.5 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,8 @@ typedef struct pdf_file *pdf_file_handle; typedef struct pdf_page *pdf_page_handle; +typedef struct pdf_bookmark *pdf_bookmark_handle; + void pdf_init (void); @@ -68,6 +70,11 @@ void pdf_write_g4_fax_image (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); +/* Create a new bookmark, under the specified parent, or at the top + level if parent is NULL. */ +pdf_bookmark_handle pdf_new_bookmark (pdf_bookmark_handle parent, + char *title, + bool open, + pdf_page_handle pdf_page); void pdf_insert_tiff_image (pdf_page_handle pdf_page, char *filename); diff --git a/pdf_bookmark.c b/pdf_bookmark.c new file mode 100644 index 0000000..89f956e --- /dev/null +++ b/pdf_bookmark.c @@ -0,0 +1,132 @@ +/* + * t2p: Create a PDF file from the contents of one or more TIFF + * bilevel image files. The images in the resulting PDF file + * will be compressed using ITU-T T.6 (G4) fax encoding. + * + * PDF routines + * $Id: pdf_bookmark.c,v 1.1 2003/03/04 17:58:36 eric Exp $ + * Copyright 2001, 2002, 2003 Eric Smith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. Note that permission is + * not granted to redistribute this program under the terms of any + * other version of the General Public License. + * + * 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 USA + */ + + +#include +#include +#include +#include +#include + + +#include "bitblt.h" +#include "pdf.h" +#include "pdf_util.h" +#include "pdf_prim.h" +#include "pdf_private.h" + + +static void pdf_bookmark_update_count (pdf_bookmark_handle entry) +{ + struct pdf_obj *count_obj; + + while (entry) + { + count_obj = pdf_get_dict_entry (entry->dict, "Count"); + if (! count_obj) + { + count_obj = pdf_new_integer (0); + pdf_set_dict_entry (entry->dict, "Count", count_obj); + } + pdf_set_integer (count_obj, + pdf_get_integer (count_obj) + + ((entry->open) ? 1 : -1)); + entry = entry->parent; + } +} + + +/* Create a new bookmark, under the specified parent, or at the top + level if parent is NULL. */ +pdf_bookmark_handle pdf_new_bookmark (pdf_bookmark_handle parent, + char *title, + bool open, + pdf_page_handle pdf_page) +{ + pdf_file_handle pdf_file = pdf_page->pdf_file; + struct pdf_bookmark *root; + struct pdf_bookmark *entry; + + struct pdf_obj *dest_array; + + root = pdf_file->outline_root; + if (! root) + { + root = pdf_calloc (1, sizeof (struct pdf_bookmark)); + root->dict = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + pdf_set_dict_entry (root->dict, "Count", pdf_new_integer (0)); + + pdf_file->outline_root = root; + pdf_set_dict_entry (pdf_file->catalog, "Outlines", root->dict); + } + + entry = pdf_calloc (1, sizeof (struct pdf_bookmark)); + entry->dict = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); + entry->open = open; + + pdf_set_dict_entry (entry->dict, "Title", pdf_new_string (title)); + + dest_array = pdf_new_obj (PT_ARRAY); + pdf_add_array_elem (dest_array, pdf_page->page_dict); + pdf_add_array_elem (dest_array, pdf_new_name ("Fit")); + pdf_set_dict_entry (entry->dict, "Dest", dest_array); + + if (parent) + { + entry->parent = parent; + entry->prev = parent->last; + } + else + { + parent = root; + entry->parent = root; + entry->prev = root->last; + } + + pdf_set_dict_entry (entry->dict, "Parent", parent->dict); + + if (entry->prev) + { + pdf_set_dict_entry (entry->dict, "Prev", entry->prev->dict); + + entry->prev->next = entry; + pdf_set_dict_entry (entry->prev->dict, "Next", entry->dict); + + parent->last = entry; + pdf_set_dict_entry (parent->dict, "Last", entry->dict); + } + else + { + parent->first = entry; + pdf_set_dict_entry (parent->dict, "First", entry->dict); + + parent->last = entry; + pdf_set_dict_entry (parent->dict, "Last", entry->dict); + } + + pdf_bookmark_update_count (parent); + + return (entry); +} diff --git a/pdf_prim.c b/pdf_prim.c index e0f2c3c..a1f76bc 100644 --- a/pdf_prim.c +++ b/pdf_prim.c @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * PDF routines - * $Id: pdf_prim.c,v 1.5 2003/02/21 02:49:11 eric Exp $ + * $Id: pdf_prim.c,v 1.6 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -102,7 +102,7 @@ struct pdf_obj bool boolean; char *name; char *string; - unsigned long integer; + long integer; double real; struct pdf_obj *ind_ref; struct pdf_dict dict; @@ -233,7 +233,7 @@ struct pdf_obj *pdf_new_string (char *str) } -struct pdf_obj *pdf_new_integer (unsigned long val) +struct pdf_obj *pdf_new_integer (long val) { struct pdf_obj *obj = pdf_new_obj (PT_INTEGER); obj->val.integer = val; @@ -315,7 +315,7 @@ struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj) } -unsigned long pdf_get_integer (struct pdf_obj *obj) +long pdf_get_integer (struct pdf_obj *obj) { if (obj->type == PT_IND_REF) obj = pdf_deref_ind_obj (obj); @@ -325,7 +325,7 @@ unsigned long pdf_get_integer (struct pdf_obj *obj) return (obj->val.integer); } -void pdf_set_integer (struct pdf_obj *obj, unsigned long val) +void pdf_set_integer (struct pdf_obj *obj, long val) { if (obj->type == PT_IND_REF) obj = pdf_deref_ind_obj (obj); diff --git a/pdf_prim.h b/pdf_prim.h index e3fc073..6bce5f6 100644 --- a/pdf_prim.h +++ b/pdf_prim.h @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * PDF routines - * $Id: pdf_prim.h,v 1.4 2003/02/21 02:49:11 eric Exp $ + * $Id: pdf_prim.h,v 1.5 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -68,7 +68,7 @@ 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_integer (long val); struct pdf_obj *pdf_new_real (double val); @@ -80,8 +80,8 @@ struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj); 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); +long pdf_get_integer (struct pdf_obj *obj); +void pdf_set_integer (struct pdf_obj *obj, long val); double pdf_get_real (struct pdf_obj *obj); diff --git a/pdf_private.h b/pdf_private.h index e316771..954ab4f 100644 --- a/pdf_private.h +++ b/pdf_private.h @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * PDF routines - * $Id: pdf_private.h,v 1.2 2003/02/20 04:44:17 eric Exp $ + * $Id: pdf_private.h,v 1.3 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -45,14 +45,32 @@ struct pdf_pages }; +struct pdf_bookmark +{ + struct pdf_obj *dict; /* indirect reference */ + bool open; + + struct pdf_bookmark *first; + struct pdf_bookmark *last; + + /* the following fields don't appear in the root */ + /* title and dest are in the dictionary but don't have + explicit fields in the C structure */ + struct pdf_bookmark *parent; + struct pdf_bookmark *prev; + struct pdf_bookmark *next; +}; + + 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; + 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_bookmark *outline_root; + struct pdf_obj *trailer_dict; }; diff --git a/pdf_util.h b/pdf_util.h index 971ae70..6873242 100644 --- a/pdf_util.h +++ b/pdf_util.h @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * PDF routines - * $Id: pdf_util.h,v 1.3 2003/02/21 02:49:11 eric Exp $ + * $Id: pdf_util.h,v 1.4 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -24,14 +24,21 @@ */ +#include + + void pdf_fatal (char *fmt, ...); void *pdf_calloc (size_t nmemb, size_t size); char *pdf_strdup (char *s); +#if 1 +#define pdf_assert(cond) assert(cond) +#else #define pdf_assert(cond) do \ { \ if (! (cond)) \ pdf_fatal ("assert at %s(%d)\n", __FILE__, __LINE__); \ } while (0) +#endif diff --git a/t2p.c b/t2p.c index 5267089..e2a7e43 100644 --- a/t2p.c +++ b/t2p.c @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * Main program - * $Id: t2p.c,v 1.24 2003/02/21 01:25:47 eric Exp $ + * $Id: t2p.c,v 1.25 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -88,7 +88,7 @@ void usage (void) fprintf (stderr, " -v verbose\n"); fprintf (stderr, " -b fmt create bookmarks\n"); fprintf (stderr, "bookmark format:\n"); - fprintf (stderr, " %%F file name\n"); + fprintf (stderr, " %%F file name (sans suffix)\n"); fprintf (stderr, " %%p page number\n"); } @@ -311,7 +311,7 @@ bool process_page (int image, /* range 1 .. n */ points */ Rect rect; - Bitmap *bitmap; + Bitmap *bitmap = NULL; int row; @@ -464,6 +464,7 @@ bool process_page (int image, /* range 1 .. n */ page = pdf_new_page (out->pdf, width_points, height_points); +#if 0 pdf_write_g4_fax_image (page, 0, 0, /* x, y */ width_points, height_points, @@ -471,21 +472,102 @@ bool process_page (int image, /* range 1 .. n */ 0, /* ImageMask */ 0, 0, 0, /* r, g, b */ 0); /* BlackIs1 */ +#endif - free_bitmap (bitmap); + while (bookmarks) + { + /* $$$ need to handle level here */ + pdf_new_bookmark (NULL, bookmarks->name, 0, page); + bookmarks = bookmarks->next; + } result = 1; fail: + if (bitmap) + free_bitmap (bitmap); + return (result); } -void main_args (char *out_fn, int inf_count, char **in_fn) +#define MAX_BOOKMARK_NAME_LEN 500 + + +static int filename_length_without_suffix (char *in_fn) +{ + char *p; + int len = strlen (in_fn); + + p = strrchr (in_fn, '.'); + if (p && ((strcasecmp (p, ".tif") == 0) || + (strcasecmp (p, ".tiff") == 0))) + return (p - in_fn); + return (len); +} + + +/* $$$ this function should ensure that it doesn't overflow the name string! */ +static void generate_bookmark_name (char *name, + char *bookmark_fmt, + char *in_fn, + int page) +{ + bool meta = 0; + int len; + + while (*bookmark_fmt) + { + if (meta) + { + meta = 0; + switch (*bookmark_fmt) + { + case '%': + *(name++) = '%'; + break; + case 'F': + len = filename_length_without_suffix (in_fn); + strncpy (name, in_fn, len); + name += len; + break; + case 'p': + sprintf (name, "%d", page); + name += strlen (name); + break; + default: + break; + } + } + else + switch (*bookmark_fmt) + { + case '%': + meta = 1; + break; + default: + *(name++) = *bookmark_fmt; + } + bookmark_fmt++; + } + *bookmark_fmt = '\0'; +} + + +void main_args (char *out_fn, + int inf_count, + char **in_fn, + char *bookmark_fmt) { int i, ip; input_attributes_t input_attributes; pdf_file_attributes_t output_attributes; + bookmark_t bookmark; + char bookmark_name [MAX_BOOKMARK_NAME_LEN]; + + bookmark.next = NULL; + bookmark.level = 1; + bookmark.name = & bookmark_name [0]; memset (& input_attributes, 0, sizeof (input_attributes)); memset (& output_attributes, 0, sizeof (output_attributes)); @@ -499,7 +581,13 @@ void main_args (char *out_fn, int inf_count, char **in_fn) for (ip = 1;; ip++) { fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]); - if (! process_page (ip, input_attributes, NULL)) + if (bookmark_fmt) + generate_bookmark_name (& bookmark_name [0], + bookmark_fmt, + in_fn [i], + ip); + if (! process_page (ip, input_attributes, + bookmark_fmt ? & bookmark : NULL)) fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]); if (last_tiff_page ()) break; @@ -596,7 +684,7 @@ int main (int argc, char *argv[]) if (spec_fn) main_spec (spec_fn); else - main_args (out_fn, inf_count, in_fn); + main_args (out_fn, inf_count, in_fn, bookmark_fmt); close_tiff_input_file (); close_pdf_output_files (); diff --git a/tumble.c b/tumble.c index 56a8b9d..7e3cac5 100644 --- a/tumble.c +++ b/tumble.c @@ -4,7 +4,7 @@ * will be compressed using ITU-T T.6 (G4) fax encoding. * * Main program - * $Id: tumble.c,v 1.24 2003/02/21 01:25:47 eric Exp $ + * $Id: tumble.c,v 1.25 2003/03/04 17:58:36 eric Exp $ * Copyright 2001, 2002, 2003 Eric Smith * * This program is free software; you can redistribute it and/or modify @@ -88,7 +88,7 @@ void usage (void) fprintf (stderr, " -v verbose\n"); fprintf (stderr, " -b fmt create bookmarks\n"); fprintf (stderr, "bookmark format:\n"); - fprintf (stderr, " %%F file name\n"); + fprintf (stderr, " %%F file name (sans suffix)\n"); fprintf (stderr, " %%p page number\n"); } @@ -311,7 +311,7 @@ bool process_page (int image, /* range 1 .. n */ points */ Rect rect; - Bitmap *bitmap; + Bitmap *bitmap = NULL; int row; @@ -464,6 +464,7 @@ bool process_page (int image, /* range 1 .. n */ page = pdf_new_page (out->pdf, width_points, height_points); +#if 0 pdf_write_g4_fax_image (page, 0, 0, /* x, y */ width_points, height_points, @@ -471,21 +472,102 @@ bool process_page (int image, /* range 1 .. n */ 0, /* ImageMask */ 0, 0, 0, /* r, g, b */ 0); /* BlackIs1 */ +#endif - free_bitmap (bitmap); + while (bookmarks) + { + /* $$$ need to handle level here */ + pdf_new_bookmark (NULL, bookmarks->name, 0, page); + bookmarks = bookmarks->next; + } result = 1; fail: + if (bitmap) + free_bitmap (bitmap); + return (result); } -void main_args (char *out_fn, int inf_count, char **in_fn) +#define MAX_BOOKMARK_NAME_LEN 500 + + +static int filename_length_without_suffix (char *in_fn) +{ + char *p; + int len = strlen (in_fn); + + p = strrchr (in_fn, '.'); + if (p && ((strcasecmp (p, ".tif") == 0) || + (strcasecmp (p, ".tiff") == 0))) + return (p - in_fn); + return (len); +} + + +/* $$$ this function should ensure that it doesn't overflow the name string! */ +static void generate_bookmark_name (char *name, + char *bookmark_fmt, + char *in_fn, + int page) +{ + bool meta = 0; + int len; + + while (*bookmark_fmt) + { + if (meta) + { + meta = 0; + switch (*bookmark_fmt) + { + case '%': + *(name++) = '%'; + break; + case 'F': + len = filename_length_without_suffix (in_fn); + strncpy (name, in_fn, len); + name += len; + break; + case 'p': + sprintf (name, "%d", page); + name += strlen (name); + break; + default: + break; + } + } + else + switch (*bookmark_fmt) + { + case '%': + meta = 1; + break; + default: + *(name++) = *bookmark_fmt; + } + bookmark_fmt++; + } + *bookmark_fmt = '\0'; +} + + +void main_args (char *out_fn, + int inf_count, + char **in_fn, + char *bookmark_fmt) { int i, ip; input_attributes_t input_attributes; pdf_file_attributes_t output_attributes; + bookmark_t bookmark; + char bookmark_name [MAX_BOOKMARK_NAME_LEN]; + + bookmark.next = NULL; + bookmark.level = 1; + bookmark.name = & bookmark_name [0]; memset (& input_attributes, 0, sizeof (input_attributes)); memset (& output_attributes, 0, sizeof (output_attributes)); @@ -499,7 +581,13 @@ void main_args (char *out_fn, int inf_count, char **in_fn) for (ip = 1;; ip++) { fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]); - if (! process_page (ip, input_attributes, NULL)) + if (bookmark_fmt) + generate_bookmark_name (& bookmark_name [0], + bookmark_fmt, + in_fn [i], + ip); + if (! process_page (ip, input_attributes, + bookmark_fmt ? & bookmark : NULL)) fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]); if (last_tiff_page ()) break; @@ -596,7 +684,7 @@ int main (int argc, char *argv[]) if (spec_fn) main_spec (spec_fn); else - main_args (out_fn, inf_count, in_fn); + main_args (out_fn, inf_count, in_fn, bookmark_fmt); close_tiff_input_file (); close_pdf_output_files ();