diff --git a/Makefile b/Makefile index 8bd1364..2e1ecbf 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,11 @@ # 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 +# +# 2010-09-02 [JDB] Allow building in a directory separate from the source and +# add PNG and blank-page support files. Also change the +# "include" of dependencies to suppress an error if the +# dependency files are not present. # Conditionals: uncomment the following defines as nessary. Note that a @@ -29,7 +34,7 @@ CTL_LANG=1 -CFLAGS = -Wall +CFLAGS = -Wall -Wno-unused-function -Wno-unused-but-set-variable LDFLAGS = LDLIBS = -ltiff -ljpeg -lnetpbm -lz -lm @@ -49,6 +54,7 @@ LDLIBS := -Wl,-static $(LDLIBS) endif +LEX = flex YACC = bison YFLAGS = -d -v @@ -58,18 +64,18 @@ YFLAGS = -d -v # let me know why so I can improve this Makefile. # ----------------------------------------------------------------------------- -VERSION = 0.33 +VERSION = 0.34 PACKAGE = tumble TARGETS = tumble -CSRCS = tumble.c semantics.c \ - tumble_input.c tumble_tiff.c tumble_jpeg.c tumble_pbm.c \ +CSRCS = tumble.c semantics.c tumble_input.c \ + tumble_tiff.c tumble_jpeg.c tumble_pbm.c tumble_png.c tumble_blank.c \ bitblt.c bitblt_table_gen.c bitblt_g4.c g4_table_gen.c \ pdf.c pdf_util.c pdf_prim.c pdf_name_tree.c \ pdf_bookmark.c pdf_page_label.c \ - pdf_text.c pdf_g4.c pdf_jpeg.c + pdf_text.c pdf_g4.c pdf_jpeg.c pdf_png.c OSRCS = scanner.l parser.y HDRS = tumble.h tumble_input.h semantics.h bitblt.h bitblt_tables.h \ pdf.h pdf_private.h pdf_util.h pdf_prim.h pdf_name_tree.h @@ -106,12 +112,12 @@ CFLAGS := $(CFLAGS) $(CDEFINES) all: $(TARGETS) $(TEST_TARGETS) -TUMBLE_OBJS = tumble.o semantics.o \ - tumble_input.o tumble_tiff.o tumble_jpeg.o tumble_pbm.o \ +TUMBLE_OBJS = tumble.o semantics.o tumble_input.o \ + tumble_tiff.o tumble_jpeg.o tumble_pbm.o tumble_png.o tumble_blank.o \ bitblt.o bitblt_g4.o bitblt_tables.o g4_tables.o \ pdf.o pdf_util.o pdf_prim.o pdf_name_tree.o \ pdf_bookmark.o pdf_page_label.o \ - pdf_text.o pdf_g4.o pdf_jpeg.o + pdf_text.o pdf_g4.o pdf_jpeg.o pdf_png.o ifdef CTL_LANG TUMBLE_OBJS += scanner.o parser.tab.o @@ -119,9 +125,6 @@ endif tumble: $(TUMBLE_OBJS) $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ -ifndef DEBUG - strip $@ -endif bitblt_tables.h: bitblt_table_gen @@ -178,6 +181,6 @@ ALL_CSRCS = $(CSRCS) $(AUTO_CSRCS) $(TEST_CSRCS) DEPENDS = $(ALL_CSRCS:.c=.d) %.d: %.c - $(CC) -M -MG $(CFLAGS) $< | sed -e 's@ /[^ ]*@@g' -e 's@^\(.*\)\.o:@\1.d \1.o:@' > $@ + $(CC) -M -MG $(CFLAGS) $< | sed -e 's@ \([A-Za-z]\):/@ /\1/@g' -e 's@^\(.*\)\.o:@\1.d \1.o:@' > $@ -include $(DEPENDS) +-include $(DEPENDS) diff --git a/bitblt.h b/bitblt.h index a0fd668..762684a 100644 --- a/bitblt.h +++ b/bitblt.h @@ -19,8 +19,15 @@ * 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 + * + * 2009-03-13 [JDB] pm_config.h (part of NETPBM) defines BITS_PER_WORD but + * apparently doesn't use it externally. We undefine it here + * so that our version takes precedence and warnings are not + * generated. */ +#undef BITS_PER_WORD + typedef struct Point { diff --git a/bitblt_g4.c b/bitblt_g4.c index b709ca0..7f64ef5 100644 --- a/bitblt_g4.c +++ b/bitblt_g4.c @@ -19,6 +19,10 @@ * 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 + * + * 2007-06-20 [JDB] Fixed a bug wherein "g4_get_pixel" is called with pixel + * index == width, causing an index off the end of the array + * if width % BITS_PER_WORD == 0. */ @@ -303,7 +307,7 @@ void bitblt_write_g4 (Bitmap *bitmap, FILE *f) word_t *cur_line; word_t *ref_line; /* reference (previous) row */ - temp_buffer = pdf_calloc ((width + BITS_PER_WORD - 1) / BITS_PER_WORD, + temp_buffer = pdf_calloc (width / BITS_PER_WORD + 1, sizeof (word_t)); cur_line = bitmap->bits; diff --git a/parser.y b/parser.y index 1b0841c..29d6eda 100644 --- a/parser.y +++ b/parser.y @@ -19,15 +19,23 @@ * 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 -*/ + * + * 2009-03-13 [JDB] Add support for blank pages, overlay images, color + * mapping, color-key masking, and push/pop of input + * contexts. + */ %{ #include #include #include #include "semantics.h" + + extern int yylex (void); %} +%error-verbose + %union { int integer; char character; @@ -36,6 +44,9 @@ page_size_t size; range_t range; page_label_t page_label; + overlay_t overlay; + rgb_t rgb; + rgb_range_t rgb_range; } %token INTEGER @@ -63,9 +74,13 @@ %token CROP %token SIZE %token RESOLUTION +%token BLANK %token INPUT +%token TRANSPARENT +%token COLORMAP %token LABEL +%token OVERLAY %token PAGE %token PAGES %token BOOKMARK @@ -89,6 +104,12 @@ %type page_size +%type rgb + +%type rgb_range +%type gray_range +%type color_range + %% statements: @@ -157,8 +178,21 @@ size_clause: resolution_clause: RESOLUTION FLOAT unit ; +rgb_range: + '(' range range range ')' { $$.red = $2; $$.green = $3; $$.blue = $4; } + +gray_range: + '(' range ')' { $$.red = $2; $$.green = $2; $$.blue = $2; } ; + +color_range: + rgb_range + | gray_range; + +transparency_clause: + TRANSPARENT color_range ';' { input_set_transparency ($2); } ; + modifier_clause: - rotate_clause | crop_clause | size_clause | resolution_clause; + rotate_clause | crop_clause | size_clause | resolution_clause | transparency_clause; modifier_clauses: modifier_clause @@ -175,12 +209,16 @@ part_clause: modifier_clause_list ';' { input_set_modifier_context (INPUT_MODIFIER_ALL); } ; +blank_page_clause: + BLANK { input_set_file (NULL); } size_clause ; + input_clause: input_file_clause | image_clause | images_clause | part_clause | modifier_clause + | blank_page_clause | input_clause_list ; input_clauses: @@ -188,7 +226,8 @@ input_clauses: | input_clauses input_clause ; input_clause_list: - '{' input_clauses '}' ; + '{' { input_push_context (); } + input_clauses '}' { input_pop_context (); } ; input_statement: INPUT input_clauses ; @@ -218,12 +257,23 @@ label_clause: | LABEL CHARACTER ';' { page_label_t label = { NULL, $2 }; output_set_page_label (label); } | LABEL STRING ',' CHARACTER ';' { page_label_t label = { $2, $4 }; output_set_page_label (label); } ; +overlay_clause_list: + /* empty */ + | '{' overlay_clauses '}' ; + +overlay_clauses: + overlay_clause + | overlay_clauses overlay_clause ; + +overlay_clause: + OVERLAY length ',' length ';' { overlay_t overlay = { $2, $4 }; output_overlay (overlay); } ; + page_ranges: range { output_pages ($1); } | page_ranges ',' range { output_pages ($3); } ; page_clause: - PAGE INTEGER ';' { range_t range = { $2, $2 }; output_pages (range); } ; + PAGE INTEGER { range_t range = { $2, $2 }; output_pages (range); } overlay_clause_list ';' ; pages_clause: PAGES page_ranges ';' ; @@ -240,8 +290,15 @@ bookmark_clause: bookmark_name_list output_clause_list ';' { bookmark_level--; output_pop_context (); } ; +rgb: + '(' INTEGER INTEGER INTEGER ')' { $$.red = $2; $$.green = $3; $$.blue = $4; } ; + +colormap_clause: + COLORMAP rgb ',' rgb ';' { output_set_colormap ($2, $4); } ; + output_clause: output_file_clause + | colormap_clause | label_clause | page_clause | pages_clause | bookmark_clause diff --git a/pdf.c b/pdf.c index 7861b04..db12ac2 100644 --- a/pdf.c +++ b/pdf.c @@ -19,6 +19,9 @@ * 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 + * + * 2005-05-03 [JDB] Add CreationDate and ModDate PDF headers. + * 2014-02-18 [JDB] Use PDF_PRODUCER definition. */ @@ -26,6 +29,7 @@ #include #include #include +#include #include "bitblt.h" @@ -70,6 +74,11 @@ struct pdf_pages *pdf_new_pages (pdf_file_handle pdf_file) pdf_file_handle pdf_create (char *filename) { pdf_file_handle pdf_file; + time_t current_time, adjusted_time; + struct tm *time_and_date; + int gmt_diff; + char tm_string[18], gmt_string[24]; + const char tm_format[] = "D:%Y%m%d%H%M%S"; pdf_file = pdf_calloc (1, sizeof (struct pdf_file)); @@ -88,7 +97,23 @@ pdf_file_handle pdf_create (char *filename) pdf_set_dict_entry (pdf_file->catalog, "PageLayout", pdf_new_name ("SinglePage")); pdf_file->info = pdf_new_ind_ref (pdf_file, pdf_new_obj (PT_DICTIONARY)); - pdf_set_info (pdf_file, "Producer", "tumble by Eric Smith -- http://tumble.brouhaha.com/"); + pdf_set_info (pdf_file, "Producer", PDF_PRODUCER); + + /* Generate CreationDate and ModDate */ + + current_time = time (NULL); + time_and_date = gmtime (¤t_time); + adjusted_time = mktime (time_and_date); + gmt_diff = (int) difftime (current_time, adjusted_time); + + time_and_date = localtime (¤t_time); + + if (strftime (tm_string, sizeof (tm_string), tm_format, time_and_date)) + { + sprintf (gmt_string, "%s%+03d'%02d'", tm_string, gmt_diff / 3600, gmt_diff % 60); + pdf_set_info (pdf_file, "CreationDate", gmt_string); + pdf_set_info (pdf_file, "ModDate", gmt_string); + } pdf_file->trailer_dict = pdf_new_obj (PT_DICTIONARY); /* Size key will be added later */ diff --git a/pdf.h b/pdf.h index f66d06e..5cfa02b 100644 --- a/pdf.h +++ b/pdf.h @@ -19,14 +19,46 @@ * 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 + * + * 2007-06-28 [JDB] Increase page limits from 45" to 200" square. + * 2010-09-02 [JDB] Added support for min-is-black TIFF images. + * 2014-02-18 [JDB] Added PDF_PRODUCER definition. */ +#if !defined(SEMANTICS) +typedef struct +{ + int first; + int last; + } range_t; + +typedef struct +{ + int red; + int green; + int blue; +} rgb_t; + +typedef struct +{ + range_t red; + range_t green; + range_t blue; +} rgb_range_t; + +typedef struct +{ + rgb_t black_map; + rgb_t white_map; +} colormap_t; +#endif /* Acrobat default units aren't really points, but they're close. */ #define POINTS_PER_INCH 72.0 -/* page size limited by Acrobat Reader to 45 inches on a side */ -#define PAGE_MAX_INCHES 45 +/* Page size for Acrobat Reader 4.0 and later is 200 x 200 inches. + Old limit of 45 inches applied to Acrobat Reader 3.x only. */ +#define PAGE_MAX_INCHES 200 #define PAGE_MAX_POINTS (PAGE_MAX_INCHES * POINTS_PER_INCH) @@ -48,11 +80,18 @@ pdf_file_handle pdf_create (char *filename); void pdf_close (pdf_file_handle pdf_file, int page_mode); +#define AS_STR(S) #S +#define TO_STR(S) AS_STR(S) + +#define PDF_PRODUCER "tumble " TO_STR(TUMBLE_VERSION) \ + " by Eric Smith (modified by J. David Bryan)" + 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); +void pdf_set_creator (pdf_file_handle pdf_file, char *creator); +void pdf_set_producer (pdf_file_handle pdf_file, char *producer); +void pdf_set_title (pdf_file_handle pdf_file, char *title); +void pdf_set_subject (pdf_file_handle pdf_file, char *subject); +void pdf_set_keywords (pdf_file_handle pdf_file, char *keywords); /* width and height in units of 1/72 inch */ @@ -74,12 +113,10 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, double y, double width, double height, + bool negative, Bitmap *bitmap, - bool ImageMask, - double r, /* RGB fill color, only for ImageMask */ - double g, - double b, - bool BlackIs1); /* boolean, typ. false */ + colormap_t *colormap, + rgb_range_t *transparency); void pdf_write_jpeg_image (pdf_page_handle pdf_page, @@ -90,9 +127,25 @@ void pdf_write_jpeg_image (pdf_page_handle pdf_page, bool color, uint32_t width_samples, uint32_t height_samples, + rgb_range_t *transparency, FILE *f); +void pdf_write_png_image (pdf_page_handle pdf_page, + double x, + double y, + double width, + double height, + int color, + char *pal, + int palent, + int bpp, + uint32_t width_samples, + uint32_t height_samples, + rgb_range_t *transparency, + FILE *f); + + void pdf_set_page_number (pdf_page_handle pdf_page, char *page_number); /* Create a new bookmark, under the specified parent, or at the top diff --git a/pdf_g4.c b/pdf_g4.c index ccfc447..566d053 100644 --- a/pdf_g4.c +++ b/pdf_g4.c @@ -19,6 +19,12 @@ * 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 + * + * 2008-12-30 [JDB] Fixed bug wherein "pdf_write_g4_content_callback" called + * "pdf_stream_printf" to write XObject name without escaping + * restricted characters. Now calls "pdf_write_name". + * + * 2010-09-02 [JDB] Added support for min-is-black TIFF images. */ @@ -40,11 +46,8 @@ struct pdf_g4_image { double width, height; double x, y; - double r, g, b; /* fill color, only for ImageMask */ unsigned long Columns; unsigned long Rows; - bool ImageMask; - bool BlackIs1; Bitmap *bitmap; char XObject_name [4]; }; @@ -60,12 +63,9 @@ static void pdf_write_g4_content_callback (pdf_file_handle pdf_file, pdf_stream_printf (pdf_file, stream, "q %g 0 0 %g %g %g cm ", image->width, image->height, image->x, image->y); - if (image->ImageMask) - pdf_stream_printf (pdf_file, stream, "%g %g %g rg ", - image->r, image->g, image->b); - pdf_stream_printf (pdf_file, stream, "/%s Do Q\r\n", - image->XObject_name); + pdf_write_name (pdf_file, image->XObject_name); + pdf_stream_printf (pdf_file, stream, "Do Q\r\n"); } @@ -84,12 +84,10 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, double y, double width, double height, + bool negative, Bitmap *bitmap, - bool ImageMask, - double r, /* RGB fill color, only for ImageMask */ - double g, - double b, - bool BlackIs1) /* boolean, typ. false */ + colormap_t *colormap, + rgb_range_t *transparency) { struct pdf_g4_image *image; @@ -99,6 +97,16 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, struct pdf_obj *content_stream; + struct pdf_obj *contents; + struct pdf_obj *mask; + + typedef char MAP_STRING[6]; + + MAP_STRING color_index; + static MAP_STRING last_color_index; + static struct pdf_obj *color_space; + + pdf_add_array_elem_unique (pdf_page->procset, pdf_new_name ("ImageB")); image = pdf_calloc (1, sizeof (struct pdf_g4_image)); @@ -107,15 +115,10 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, image->height = height; image->x = x; image->y = y; - image->r = r; - image->g = g; - image->b = b; image->bitmap = bitmap; image->Columns = bitmap->rect.max.x - bitmap->rect.min.x; image->Rows = bitmap->rect.max.y - bitmap->rect.min.y; - image->ImageMask = ImageMask; - image->BlackIs1 = BlackIs1; stream_dict = pdf_new_obj (PT_DICTIONARY); @@ -134,8 +137,42 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (image->Columns)); pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (image->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)); + + if (transparency) + { + mask = pdf_new_obj (PT_ARRAY); + + pdf_add_array_elem (mask, pdf_new_integer (transparency->red.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->red.last)); + + pdf_set_dict_entry (stream_dict, "Mask", mask); + } + + if (colormap) + { + color_index [0] = (char) colormap->black_map.red; + color_index [1] = (char) colormap->black_map.green; + color_index [2] = (char) colormap->black_map.blue; + color_index [3] = (char) colormap->white_map.red; + color_index [4] = (char) colormap->white_map.green; + color_index [5] = (char) colormap->white_map.blue; + + if ((color_space == NULL) || + (memcmp (color_index, last_color_index, sizeof (MAP_STRING)) != 0)) + { + memcpy (last_color_index, color_index, sizeof (MAP_STRING)); + + color_space = pdf_new_obj (PT_ARRAY); + pdf_add_array_elem (color_space, pdf_new_name ("Indexed")); + pdf_add_array_elem (color_space, pdf_new_name ("DeviceRGB")); + pdf_add_array_elem (color_space, pdf_new_integer (1)); + pdf_add_array_elem (color_space, pdf_new_string_n (color_index, 6)); + + color_space = pdf_new_ind_ref (pdf_page->pdf_file, color_space); + } + + pdf_set_dict_entry (stream_dict, "ColorSpace", color_space); + } else pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name ("DeviceGray")); @@ -153,10 +190,10 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, "Rows", pdf_new_integer (image->Rows)); - if (BlackIs1) + if (negative) pdf_set_dict_entry (decode_parms, "BlackIs1", - pdf_new_bool (BlackIs1)); + pdf_new_bool (true)); pdf_stream_add_filter (stream, "CCITTFaxDecode", decode_parms); @@ -170,7 +207,13 @@ void pdf_write_g4_fax_image (pdf_page_handle pdf_page, & pdf_write_g4_content_callback, image)); - pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); + contents = pdf_get_dict_entry (pdf_page->page_dict, "Contents"); + + if (! contents) + contents = pdf_new_obj (PT_ARRAY); + + pdf_add_array_elem (contents, content_stream); + pdf_set_dict_entry (pdf_page->page_dict, "Contents", contents); pdf_write_ind_obj (pdf_page->pdf_file, content_stream); } diff --git a/pdf_jpeg.c b/pdf_jpeg.c index 0978840..8d57c81 100644 --- a/pdf_jpeg.c +++ b/pdf_jpeg.c @@ -19,6 +19,10 @@ * 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 + * + * 2008-12-30 [JDB] Fixed bug wherein "pdf_write_jpeg_content_callback" called + * "pdf_stream_printf" to write XObject name without escaping + * restricted characters. Now calls "pdf_write_name". */ @@ -57,8 +61,8 @@ static void pdf_write_jpeg_content_callback (pdf_file_handle pdf_file, pdf_stream_printf (pdf_file, stream, "q %g 0 0 %g %g %g cm ", image->width, image->height, image->x, image->y); - pdf_stream_printf (pdf_file, stream, "/%s Do Q\r\n", - image->XObject_name); + pdf_write_name (pdf_file, image->XObject_name); + pdf_stream_printf (pdf_file, stream, "Do Q\r\n"); } @@ -101,6 +105,7 @@ void pdf_write_jpeg_image (pdf_page_handle pdf_page, bool color, uint32_t width_samples, uint32_t height_samples, + rgb_range_t *transparency, FILE *f) { struct pdf_jpeg_image *image; @@ -110,6 +115,9 @@ void pdf_write_jpeg_image (pdf_page_handle pdf_page, struct pdf_obj *content_stream; + struct pdf_obj *contents; + struct pdf_obj *mask; + image = pdf_calloc (1, sizeof (struct pdf_jpeg_image)); image->width = width; @@ -139,13 +147,28 @@ void pdf_write_jpeg_image (pdf_page_handle pdf_page, pdf_set_dict_entry (stream_dict, "Type", pdf_new_name ("XObject")); pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image")); -// Name is required in PDF 1.0 but obsoleted in later PDF versions -// pdf_set_dict_entry (stream_dict, "Name", pdf_new_name (& image->XObject_name [0])); pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (image->width_samples)); pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (image->height_samples)); pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name (image->color ? "DeviceRGB" : "DeviceGray")); pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (8)); + if (transparency) + { + mask = pdf_new_obj (PT_ARRAY); + + pdf_add_array_elem (mask, pdf_new_integer (transparency->red.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->red.last)); + + if (image->color) { + pdf_add_array_elem (mask, pdf_new_integer (transparency->green.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->green.last)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->blue.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->blue.last)); + } + + pdf_set_dict_entry (stream_dict, "Mask", mask); + } + pdf_stream_add_filter (stream, "DCTDecode", NULL); /* the following will write the stream, using our callback function to @@ -158,7 +181,13 @@ void pdf_write_jpeg_image (pdf_page_handle pdf_page, & pdf_write_jpeg_content_callback, image)); - pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); + contents = pdf_get_dict_entry (pdf_page->page_dict, "Contents"); + + if (! contents) + contents = pdf_new_obj (PT_ARRAY); + + pdf_add_array_elem (contents, content_stream); + pdf_set_dict_entry (pdf_page->page_dict, "Contents", contents); pdf_write_ind_obj (pdf_page->pdf_file, content_stream); } diff --git a/pdf_page_label.c b/pdf_page_label.c index 903c447..9d10699 100644 --- a/pdf_page_label.c +++ b/pdf_page_label.c @@ -19,6 +19,11 @@ * 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 + * + * 2007-05-07 [JDB] Fixed a bug wherein a page label specifying prefix without + * a style (e.g., LABEL ) produced bad PDF (no labels + * were displayed). Should have output "/P " but + * instead output "/S /P ". */ @@ -53,10 +58,14 @@ void pdf_new_page_label (pdf_file_handle pdf_file, } label_dict = pdf_new_obj (PT_DICTIONARY); - pdf_set_dict_entry (label_dict, "S", pdf_new_name (style_str)); + + if (style) + pdf_set_dict_entry (label_dict, "S", pdf_new_name (style_str)); + if (prefix) pdf_set_dict_entry (label_dict, "P", pdf_new_string (prefix)); - if (base != 1) + + if (base > 1) pdf_set_dict_entry (label_dict, "St", pdf_new_integer (base)); pdf_add_number_tree_element (pdf_file->page_label_tree, diff --git a/pdf_png.c b/pdf_png.c new file mode 100644 index 0000000..a1f3ce5 --- /dev/null +++ b/pdf_png.c @@ -0,0 +1,233 @@ +/* + * tumble: build a PDF file from image files + * + * PDF routines + * Copyright 2004 Daniel Gloeckner + * + * Derived from pdf_jpeg.c written 2003 by 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 + * + * 2008-12-30 [JDB] Fixed bug wherein "pdf_write_png_content_callback" called + * "pdf_stream_printf" to write XObject name without escaping + * restricted characters. Now calls "pdf_write_name". + */ + + +#include +#include +#include +#include +#include + + +#include "bitblt.h" +#include "pdf.h" +#include "pdf_util.h" +#include "pdf_prim.h" +#include "pdf_private.h" + + +struct pdf_png_image +{ + double width, height; + double x, y; + bool color; /* false for grayscale */ + uint32_t width_samples, height_samples; + FILE *f; + char XObject_name [4]; +}; + + +static void pdf_write_png_content_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + struct pdf_png_image *image = app_data; + + /* transformation matrix is: width 0 0 height x y cm */ + pdf_stream_printf (pdf_file, stream, "q %g 0 0 %g %g %g cm ", + image->width, image->height, + image->x, image->y); + pdf_write_name (pdf_file, image->XObject_name); + pdf_stream_printf (pdf_file, stream, "Do Q\r\n"); +} + + +static void pdf_write_png_image_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + struct pdf_png_image *image = app_data; + int rlen, wlen; + uint8_t *wp; + uint8_t buffer [8192]; + + while (! feof (image->f)) + { + uint32_t clen; + rlen = fread (buffer, 1, 8, image->f); + if (rlen != 8) + pdf_fatal ("unexpected EOF on input file\n"); + clen=(buffer[0]<<24)+(buffer[1]<<16)+(buffer[2]<<8)+buffer[3]; + if (!memcmp(buffer+4,"IEND",4)) + break; + if (memcmp(buffer+4,"IDAT",4)) { + fseek(image->f, clen+4, SEEK_CUR); + continue; + } + while (clen) + { + rlen = fread (buffer, 1, (clenf); + if(!rlen) + pdf_fatal ("unexpected EOF on input file\n"); + clen -= rlen; + wp = buffer; + while (rlen) + { + wlen = fwrite (wp, 1, rlen, pdf_file->f); + if (feof (pdf_file->f)) + pdf_fatal ("unexpected EOF on output file\n"); + if (ferror (pdf_file->f)) + pdf_fatal ("error on output file\n"); + rlen -= wlen; + wp += wlen; + } + if (ferror (image->f)) + pdf_fatal ("error on input file\n"); + } + fseek(image->f, 4, SEEK_CUR); + } +} + + +void pdf_write_png_image (pdf_page_handle pdf_page, + double x, + double y, + double width, + double height, + int color, + char *indexed, + int palent, + int bpp, + uint32_t width_samples, + uint32_t height_samples, + rgb_range_t *transparency, + FILE *f) +{ + struct pdf_png_image *image; + + struct pdf_obj *stream; + struct pdf_obj *stream_dict; + struct pdf_obj *flateparams; + + struct pdf_obj *content_stream; + + struct pdf_obj *contents; + struct pdf_obj *mask; + + image = pdf_calloc (1, sizeof (struct pdf_png_image)); + + image->width = width; + image->height = height; + image->x = x; + image->y = y; + + image->f = f; + + image->color = color; + image->width_samples = width_samples; + image->height_samples = height_samples; + + pdf_add_array_elem_unique (pdf_page->procset, + pdf_new_name (palent ? "ImageI" : image->color ? "ImageC" : "ImageB")); + + 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_png_image_callback, + image)); + + strcpy (& image->XObject_name [0], "Im "); + image->XObject_name [2] = pdf_new_XObject (pdf_page, stream); + + flateparams = pdf_new_obj (PT_DICTIONARY); + + pdf_set_dict_entry (stream_dict, "Type", pdf_new_name ("XObject")); + pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image")); +// Name is required in PDF 1.0 but obsoleted in later PDF versions +// pdf_set_dict_entry (stream_dict, "Name", pdf_new_name (& image->XObject_name [0])); + pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (image->width_samples)); + pdf_set_dict_entry (flateparams, "Columns", pdf_new_integer (image->width_samples)); + pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (image->height_samples)); + + if (transparency) + { + mask = pdf_new_obj (PT_ARRAY); + + pdf_add_array_elem (mask, pdf_new_integer (transparency->red.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->red.last)); + + if (!palent && image->color) { + pdf_add_array_elem (mask, pdf_new_integer (transparency->green.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->green.last)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->blue.first)); + pdf_add_array_elem (mask, pdf_new_integer (transparency->blue.last)); + } + + pdf_set_dict_entry (stream_dict, "Mask", mask); + } + + if(palent) { + struct pdf_obj *space; + space = pdf_new_obj (PT_ARRAY); + pdf_add_array_elem (space, pdf_new_name ("Indexed")); + pdf_add_array_elem (space, pdf_new_name ("DeviceRGB")); + pdf_add_array_elem (space, pdf_new_integer (palent-1)); + pdf_add_array_elem (space, pdf_new_string_n (indexed,3*palent)); + pdf_set_dict_entry (stream_dict, "ColorSpace", space); + } else + pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name (image->color ? "DeviceRGB" : "DeviceGray")); + + pdf_set_dict_entry (flateparams, "Colors", pdf_new_integer ((!indexed && image->color) ? 3 : 1)); + pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (bpp)); + pdf_set_dict_entry (flateparams, "BitsPerComponent", pdf_new_integer (bpp)); + pdf_set_dict_entry (flateparams, "Predictor", pdf_new_integer (15)); + + pdf_stream_add_filter (stream, "FlateDecode", flateparams); + + /* 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_png_content_callback, + image)); + + contents = pdf_get_dict_entry (pdf_page->page_dict, "Contents"); + + if (! contents) + contents = pdf_new_obj (PT_ARRAY); + + pdf_add_array_elem (contents, content_stream); + pdf_set_dict_entry (pdf_page->page_dict, "Contents", contents); + + pdf_write_ind_obj (pdf_page->pdf_file, content_stream); +} diff --git a/pdf_prim.c b/pdf_prim.c index b7f9c23..d76bd2c 100644 --- a/pdf_prim.c +++ b/pdf_prim.c @@ -19,6 +19,9 @@ * 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 + * + * 2007-05-07 [JDB] Allow embedded nulls in strings by storing as character + * arrays plus length words. */ @@ -90,7 +93,10 @@ struct pdf_obj union { bool boolean; char *name; - char *string; + struct { + char *content; + int length; + } string; long integer; double real; struct pdf_obj *ind_ref; @@ -243,7 +249,18 @@ struct pdf_obj *pdf_new_name (char *name) struct pdf_obj *pdf_new_string (char *str) { struct pdf_obj *obj = pdf_new_obj (PT_STRING); - obj->val.string = pdf_strdup (str); + obj->val.string.content = pdf_strdup (str); + obj->val.string.length = strlen(str); + return (obj); +} + + +struct pdf_obj *pdf_new_string_n (char *str, int n) +{ + struct pdf_obj *obj = pdf_new_obj (PT_STRING); + obj->val.string.length = n; + obj->val.string.content = pdf_calloc (1,n); + memcpy(obj->val.string.content, str, n); return (obj); } @@ -397,7 +414,16 @@ int pdf_compare_obj (struct pdf_obj *o1, struct pdf_obj *o2) return (1); return (0); case PT_STRING: - return (strcmp (o1->val.string, o2->val.string)); + { + int l; + l = o1->val.string.length; + if(l > o2->val.string.length) + l = o2->val.string.length; + l = memcmp (o1->val.string.content, o2->val.string.content, l); + if (l) + return l; + return o1->val.string.length - o2->val.string.length; + } case PT_NAME: return (strcmp (o1->val.name, o2->val.name)); default: @@ -427,22 +453,70 @@ void pdf_write_name (pdf_file_handle pdf_file, char *s) } -static int string_char_needs_quoting (char c) +static int pdf_write_literal_string (pdf_file_handle pdf_file, char *s, int n) { - return ((c < ' ') || (c > '~') || (c == '\\') || - (c == '(') || (c == ')')); + int i, p; + + if (pdf_file) + fprintf (pdf_file->f, "("); + + for (i = p = 0; n; n--) + { + int j, k; + + k = 0; + + switch (*s) + { + case '\\': + k = 1; + break; + + case '(': + for (j = k =1; k && j < n; j++) + k += (s[j] == '(') ? 1 : (s[j] == ')') ? -1 : 0; + p += !k; + break; + + case ')': + if (p) + p--; + else + k = 1; + break; + } + + if (k) + { + i++; + if (pdf_file) + fprintf (pdf_file->f, "\\"); + } + + i++; + + if (pdf_file) + fprintf (pdf_file->f, "%c", *(s++)); + } + + if (pdf_file) + fprintf (pdf_file->f, ") "); + return i; } -void pdf_write_string (pdf_file_handle pdf_file, char *s) +void pdf_write_string (pdf_file_handle pdf_file, char *s, int n) { - 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, ") "); + if (pdf_write_literal_string (NULL, s, n) < 2 * n) + pdf_write_literal_string (pdf_file, s, n); + else + { + fprintf (pdf_file->f, "<"); + + for( ; n--; ) + fprintf (pdf_file->f, "%.2X",*(s++)); + fprintf (pdf_file->f, "> "); + } } @@ -560,7 +634,7 @@ void pdf_write_obj (pdf_file_handle pdf_file, struct pdf_obj *obj) pdf_write_name (pdf_file, obj->val.name); break; case PT_STRING: - pdf_write_string (pdf_file, obj->val.string); + pdf_write_string (pdf_file, obj->val.string.content, obj->val.string.length); break; case PT_INTEGER: fprintf (pdf_file->f, "%ld ", obj->val.integer); diff --git a/pdf_prim.h b/pdf_prim.h index a40116f..95ef2cd 100644 --- a/pdf_prim.h +++ b/pdf_prim.h @@ -19,6 +19,9 @@ * 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 + * + * 2007-05-07 [JDB] Add declarations for pdf_new_string_n() and + * pdf_write_name(). */ @@ -79,6 +82,8 @@ struct pdf_obj *pdf_new_name (char *name); struct pdf_obj *pdf_new_string (char *str); +struct pdf_obj *pdf_new_string_n (char *str, int n); + struct pdf_obj *pdf_new_integer (long val); struct pdf_obj *pdf_new_real (double val); @@ -146,5 +151,9 @@ void pdf_write_all_ind_obj (pdf_file_handle pdf_file); unsigned long pdf_write_xref (pdf_file_handle pdf_file); +/* Write a name, escaping reserved characters */ +void pdf_write_name (pdf_file_handle pdf_file, char *s); + + /* this isn't really a PDF primitive data type */ char pdf_new_XObject (pdf_page_handle pdf_page, struct pdf_obj *ind_ref); diff --git a/scanner.l b/scanner.l index 7b4b255..664cd4a 100644 --- a/scanner.l +++ b/scanner.l @@ -19,10 +19,16 @@ * 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 -*/ + * + * 2009-03-13 [JDB] Add support for blank pages, overlay images, color + * mapping, color-key masking, and push/pop of input + * contexts. + */ %option case-insensitive %option noyywrap +%option nounput +%option noinput %{ #include @@ -46,7 +52,7 @@ dot [\.] %% -[\,;{}] { return (yytext [0]); } +[\,;{}()] { return (yytext [0]); } {dot}{dot} { LDBG(("elipsis\n")); return (ELIPSIS); } /* decimal integer */ @@ -75,8 +81,10 @@ e { yylval.size.width = 34.0; all { return (ALL); } author { return (AUTHOR); } +blank { return (BLANK); } bookmark { return (BOOKMARK); } cm { return (CM); } +colormap { return (COLORMAP); } creator { return (CREATOR); } crop { return (CROP); } even { return (EVEN); } @@ -90,6 +98,7 @@ label { return (LABEL); } landscape { return (LANDSCAPE); } odd { return (ODD); } output { return (OUTPUT); } +overlay { return (OVERLAY); } page { return (PAGE); } pages { return (PAGES); } portrait { return (PORTRAIT) ; } @@ -98,6 +107,7 @@ rotate { return (ROTATE); } size { return (SIZE); } subject { return (SUBJECT); } title { return (TITLE); } +transparent { return (TRANSPARENT); } '[^\n']' { yylval.character = yytext [1]; diff --git a/semantics.c b/semantics.c index dc9b8c1..160ee4e 100644 --- a/semantics.c +++ b/semantics.c @@ -19,6 +19,10 @@ * 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 + * + * 2009-03-13 [JDB] Add support for blank pages, overlay images, color + * mapping, color-key masking, and push/pop of input + * contexts. */ @@ -47,6 +51,9 @@ typedef struct bool has_crop; crop_t crop; + + bool has_transparency; + rgb_range_t transparency; } input_modifiers_t; @@ -59,6 +66,7 @@ typedef struct input_context_t including those from subcontexts */ char *input_file; + bool is_blank; input_modifiers_t modifiers [INPUT_MODIFIER_TYPE_COUNT]; } input_context_t; @@ -88,6 +96,9 @@ typedef struct output_context_t bool has_page_label; page_label_t page_label; + + bool has_colormap; + colormap_t colormap; } output_context_t; @@ -97,11 +108,11 @@ typedef struct output_page_t output_context_t *output_context; range_t range; bookmark_t *bookmark_list; + bool has_overlay; + overlay_t overlay; } output_page_t; -#undef SEMANTIC_DEBUG - #ifdef SEMANTIC_DEBUG #define SDBG(x) printf x #else @@ -117,7 +128,7 @@ int bookmark_level; input_context_t *first_input_context; input_context_t *last_input_context; -input_modifier_type_t current_modifier_context; +input_modifier_type_t current_modifier_context = INPUT_MODIFIER_ALL; input_image_t *first_input_image; input_image_t *last_input_image; @@ -206,6 +217,7 @@ void input_set_file (char *name) { input_clone (); last_input_context->input_file = name; + last_input_context->is_blank = (name == NULL); }; void input_set_rotation (int rotation) @@ -222,6 +234,12 @@ void input_set_page_size (page_size_t size) SDBG(("page size %f, %f\n", size.width, size.height)); } +void input_set_transparency (rgb_range_t rgb_range) +{ + last_input_context->modifiers [current_modifier_context].has_transparency = 1; + last_input_context->modifiers [current_modifier_context].transparency = rgb_range; +} + static void increment_input_image_count (int count) { input_context_t *context; @@ -446,16 +464,31 @@ void output_pages (range_t range) } +void output_overlay (overlay_t overlay) +{ + output_pages (last_output_page->range); + last_output_page->has_overlay = 1; + last_output_page->overlay.left = overlay.left; + last_output_page->overlay.top = overlay.top; +} + +void output_set_colormap (rgb_t black_color, rgb_t white_color) +{ + output_clone (); + last_output_context->has_colormap = 1; + last_output_context->colormap.black_map = black_color; + last_output_context->colormap.white_map = white_color; +} + void yyerror (char *s) { fprintf (stderr, "%d: %s\n", line, s); } - static char *get_input_filename (input_context_t *context) { for (; context; context = context->parent) - if (context->input_file) + if ((context->input_file) || (context->is_blank)) return (context->input_file); fprintf (stderr, "no input file name found\n"); exit (2); @@ -481,6 +514,23 @@ static bool get_input_rotation (input_context_t *context, return (0); /* default */ } +static rgb_range_t *get_input_transparency (input_context_t *context, + input_modifier_type_t type) +{ + for (; context; context = context->parent) + { + if (context->modifiers [type].has_transparency) + { + return & (context->modifiers [type].transparency); + } + if (context->modifiers [INPUT_MODIFIER_ALL].has_transparency) + { + return & (context->modifiers [INPUT_MODIFIER_ALL].transparency); + } + } + return NULL; /* default */ +} + static bool get_input_page_size (input_context_t *context, input_modifier_type_t type, page_size_t *page_size) @@ -527,12 +577,21 @@ static page_label_t *get_output_page_label (output_context_t *context) return (NULL); /* default */ } +static colormap_t *get_output_colormap (output_context_t *context) +{ + for (; context; context = context->parent) + if (context->has_colormap) + return (& context->colormap); + return (NULL); /* default */ +} + #ifdef SEMANTIC_DEBUG void dump_input_tree (void) { input_image_t *image; int i; + char *fn; printf ("input images:\n"); for (image = first_input_image; image; image = image->next) @@ -542,6 +601,7 @@ void dump_input_tree (void) bool has_rotation, has_page_size; int rotation; page_size_t page_size; + rgb_range_t *transparency; has_rotation = get_input_rotation (image->input_context, parity, @@ -549,11 +609,19 @@ void dump_input_tree (void) has_page_size = get_input_page_size (image->input_context, parity, & page_size); - printf ("file '%s' image %d", - get_input_filename (image->input_context), - i); + transparency = get_input_transparency (image->input_context, parity); + fn = get_input_filename (image->input_context); + if (fn) + printf ("file '%s' image %d", fn, i); + else + printf ("blank image %d", i); if (has_rotation) printf (" rotation %d", rotation); + if (transparency) + printf (" transparency %d..%d, %d..%d, %d..%d", + transparency->red.first, transparency->red.last, + transparency->green.first, transparency->green.last, + transparency->blue.first, transparency->blue.last); if (has_page_size) printf (" size %f, %f", page_size.width, page_size.height); printf ("\n"); @@ -578,6 +646,7 @@ void dump_output_tree (void) for (i = page->range.first; i <= page->range.last; i++) { page_label_t *label = get_output_page_label (page->output_context); + colormap_t *colormap = get_output_colormap (page->output_context); printf ("file \"%s\" ", get_output_filename (page->output_context)); if (label) { @@ -587,6 +656,10 @@ void dump_output_tree (void) if (label->style) printf ("'%c' ", label->style); } + if (colormap) + printf ("colormap (%d %d %d) (%d %d %d) ", + colormap->black_map.red, colormap->black_map.green, colormap->black_map.blue, + colormap->white_map.red, colormap->white_map.green, colormap->white_map.blue); printf ("page %d\n", i); } } @@ -630,7 +703,7 @@ bool parse_control_file (char *fn) goto fail; } - fprintf (stderr, "%d pages specified\n", first_input_context->image_count); + fprintf (stderr, "%d images specified\n", first_input_context->image_count); result = 1; @@ -646,6 +719,22 @@ bool parse_control_file (char *fn) return (result); } +bool omit_label (page_label_t *page_label) +{ + static page_label_t *last_page_label; + bool unneeded; + + unneeded = ( (last_page_label != NULL) && + page_label->prefix && + last_page_label->prefix && + (strcmp (page_label->prefix, last_page_label->prefix) == 0) && + (page_label->style == last_page_label->style) && + (page_label->base == last_page_label->base + 1) ); + + last_page_label = page_label; + + return unneeded; +} bool process_controls (void) { @@ -672,7 +761,12 @@ bool process_controls (void) i = 0; input_fn = get_input_filename (image->input_context); if (verbose) - fprintf (stderr, "opening input file '%s'\n", input_fn); + { + if (input_fn) + fprintf (stderr, "opening input file '%s'\n", input_fn); + else + fprintf (stderr, "generating blank image\n"); + } if (! open_input_file (input_fn)) { fprintf (stderr, "error opening input file '%s'\n", input_fn); @@ -712,6 +806,12 @@ bool process_controls (void) parity, & input_attributes.page_size); + input_attributes.transparency = get_input_transparency (image->input_context, parity); + + + // really an output attribute, but we don't have such an thing + input_attributes.colormap = get_output_colormap (page->output_context); + if (verbose) fprintf (stderr, "processing image %d\n", image->range.first + i); @@ -725,20 +825,27 @@ bool process_controls (void) page_label->page_index = page_index; page_label->base = page->range.first; page_label->count = range_count (page->range); + + if (omit_label (page_label)) + page_label = NULL; } } if (! process_page (image->range.first + i, input_attributes, p ? NULL : page->bookmark_list, - page_label)) + page_label, + page->has_overlay ? & page->overlay : NULL, + input_attributes.transparency)) { fprintf (stderr, "error processing image %d\n", image->range.first + i); return (0); } i++; p++; - page_index++; + + if (! page->has_overlay) + page_index++; } } #endif /* CTL_LANG */ diff --git a/semantics.h b/semantics.h index e829e0c..ed13034 100644 --- a/semantics.h +++ b/semantics.h @@ -19,14 +19,14 @@ * 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 + * + * 2009-03-13 [JDB] Add support for blank pages, overlay images, color + * mapping, color-key masking, and push/pop of input + * contexts. */ - -typedef struct -{ - double width; - double height; -} page_size_t; +// HACK! rgb_t and colormap_t have to appear here and in pdf_g4! See pdf.h +#define SEMANTICS typedef struct { @@ -34,6 +34,46 @@ typedef struct int last; } range_t; +typedef struct +{ + int red; + int green; + int blue; +} rgb_t; + +typedef struct +{ + range_t red; + range_t green; + range_t blue; +} rgb_range_t; + +typedef struct +{ + rgb_t black_map; + rgb_t white_map; +} colormap_t; + +// end of HACK + +typedef struct +{ + double x; + double y; +} position_t; + +typedef struct +{ + double left; + double top; +} overlay_t; + +typedef struct +{ + double width; + double height; +} page_size_t; + typedef struct { double left; @@ -74,12 +114,17 @@ extern int line; /* line number in spec file */ extern int bookmark_level; +/* Bison interface */ +extern int yyparse (void); +void yyerror (char *s); + /* semantic routines for input statements */ void input_push_context (void); void input_pop_context (void); void input_set_modifier_context (input_modifier_type_t type); void input_set_file (char *name); void input_set_rotation (int rotation); +void input_set_transparency (rgb_range_t rgb_range); void input_set_page_size (page_size_t size); void input_images (range_t range); @@ -97,6 +142,9 @@ void output_set_keywords (char *keywords); void output_set_bookmark (char *name); void output_set_page_label (page_label_t label); void output_pages (range_t range); +void output_overlay (overlay_t overlay); +void output_transparency (rgb_range_t rgb_range); +void output_set_colormap (rgb_t black_color, rgb_t white_color); /* functions to be called from main program: */ diff --git a/tumble.c b/tumble.c index d594cd4..7781702 100644 --- a/tumble.c +++ b/tumble.c @@ -19,6 +19,9 @@ * 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 + * + * 2009-03-02 [JDB] Add support for overlay images and color key masking. + * 2014-02-18 [JDB] Add -V option to print the program version. */ @@ -53,7 +56,7 @@ typedef struct output_file_t } output_file_t; -int verbose; +int verbose, version; output_file_t *output_files; @@ -81,9 +84,11 @@ void usage (void) fprintf (stderr, " %s [options] -c \n", progname); #endif fprintf (stderr, " %s [options] ... -o \n", progname); + fprintf (stderr, " %s -V\n", progname); fprintf (stderr, "options:\n"); fprintf (stderr, " -v verbose\n"); fprintf (stderr, " -b create bookmarks\n"); + fprintf (stderr, " -V print program version\n"); fprintf (stderr, "bookmark format:\n"); fprintf (stderr, " %%F file name (sans suffix)\n"); fprintf (stderr, " %%p page number\n"); @@ -191,25 +196,54 @@ bool open_pdf_output_file (char *name, #define MAX_BOOKMARK_LEVEL 20 static pdf_bookmark_handle bookmark_vector [MAX_BOOKMARK_LEVEL + 1] = { NULL }; +static pdf_page_handle last_page = NULL; +static page_size_t last_size; bool process_page (int image, /* range 1 .. n */ input_attributes_t input_attributes, bookmark_t *bookmarks, - page_label_t *page_label) + page_label_t *page_label, + overlay_t *overlay, + rgb_range_t *transparency) { pdf_page_handle page; image_info_t image_info; + position_t position; if (! get_image_info (image, input_attributes, & image_info)) return (0); - page = pdf_new_page (out->pdf, - image_info.width_points, - image_info.height_points); + if (overlay) + { + page = last_page; + position.x = overlay->left * POINTS_PER_INCH; + position.y = last_size.height - image_info.height_points - overlay->top * POINTS_PER_INCH; + + if (verbose) + fprintf (stderr, "overlaying image at %.3f, %.3f\n", position.x, position.y); + + if (transparency) + { + input_attributes.transparency = transparency; + } + } + else + { + last_page = page = pdf_new_page (out->pdf, + image_info.width_points, + image_info.height_points); + last_size.width = image_info.width_points; + last_size.height = image_info.height_points; + position.x = 0.0; + position.y = 0.0; + } - if (! process_image (image, input_attributes, & image_info, page)) + if (! process_image (image, input_attributes, & image_info, page, position)) return (0); + if (overlay) + return (page != NULL); + while (bookmarks) { if (bookmarks->level <= MAX_BOOKMARK_LEVEL) @@ -338,6 +372,8 @@ void main_args (char *out_fn, ip); if (! process_page (ip, input_attributes, bookmark_fmt ? & bookmark : NULL, + NULL, + NULL, NULL)) fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]); if (last_input_page ()) @@ -381,12 +417,18 @@ int main (int argc, char *argv[]) init_tiff_handler (); init_jpeg_handler (); init_pbm_handler (); + init_png_handler (); while (--argc) { if (argv [1][0] == '-') { - if (strcmp (argv [1], "-v") == 0) + if (strcmp (argv [1], "-V") == 0) + { + version++; + break; + } + else if (strcmp (argv [1], "-v") == 0) verbose++; else if (strcmp (argv [1], "-o") == 0) { @@ -433,6 +475,12 @@ int main (int argc, char *argv[]) argv++; } + if (version) + { + puts (PDF_PRODUCER); + exit (0); + } + #ifdef CTL_LANG if (! ((! out_fn) ^ (! control_fn))) fatal (1, "either a control file or an output file (but not both) must be specified\n"); diff --git a/tumble.h b/tumble.h index 79ec793..3a528be 100644 --- a/tumble.h +++ b/tumble.h @@ -18,6 +18,8 @@ * 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 + * + * 2009-03-02 [JDB] Add support for overlay images and color key masking. */ @@ -38,6 +40,11 @@ typedef struct bool has_crop; crop_t crop; + + rgb_range_t *transparency; + + colormap_t *colormap; // really an output attribute, but we don't have such a thing + } input_attributes_t; @@ -61,4 +68,6 @@ bool open_pdf_output_file (char *name, bool process_page (int image, /* range 1 .. n */ input_attributes_t input_attributes, bookmark_t *bookmarks, - page_label_t *page_label); + page_label_t *page_label, + overlay_t *overlay, + rgb_range_t *transparency); diff --git a/tumble_blank.c b/tumble_blank.c new file mode 100644 index 0000000..50b031c --- /dev/null +++ b/tumble_blank.c @@ -0,0 +1,149 @@ +/* + * tumble: build a PDF file from image files + * + * $Id: tumble_blank.c ... Exp $ + * Copyright ... + * + * 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 + * + * 2007-05-07 [JDB] New file to add support for blank pages. + */ + + +#include +#include +#include + + +#include "semantics.h" +#include "tumble.h" +#include "bitblt.h" +#include "pdf.h" +#include "pdf_util.h" +#include "pdf_prim.h" +#include "pdf_private.h" +#include "tumble_input.h" + + +struct pdf_blank_page +{ + double width; + double height; + double x; + double y; + double red; + double green; + double blue; +}; + + +static bool match_blank_suffix (char *suffix) +{ + return (0); +} + +static bool close_blank_input_file (void) +{ + return (1); +} + + +static bool open_blank_input_file (FILE *f, char *name) +{ + return (1); +} + + +static bool last_blank_input_page (void) +{ + return (1); +} + + +static bool get_blank_image_info (int image, + input_attributes_t input_attributes, + image_info_t *image_info) +{ + if (input_attributes.has_page_size) + { + image_info->width_points = input_attributes.page_size.width * POINTS_PER_INCH; + image_info->height_points = input_attributes.page_size.height * POINTS_PER_INCH; + return (1); + } + else + return (0); +} + + +static void pdf_write_blank_content_callback (pdf_file_handle pdf_file, + struct pdf_obj *stream, + void *app_data) +{ + struct pdf_blank_page *page = app_data; + + pdf_stream_printf (pdf_file, stream, + "%g %g %g rg\r\n%g %g %g %g re\r\nf\r\n", + page->red, page->green, page->blue, + page->x, page->y, + page->width, page->height); +} + + +static bool process_blank_image (int image, /* range 1 .. n */ + input_attributes_t input_attributes, + image_info_t *image_info, + pdf_page_handle pdf_page, + position_t position) +{ + struct pdf_blank_page *page; + struct pdf_obj *content_stream; + +/* If colormap set, use "white" color and draw rectangle to cover page. */ + + if (input_attributes.colormap) + { + page = pdf_calloc (1, sizeof (struct pdf_blank_page)); + + page->width = image_info->width_points; + page->height = image_info->height_points; + page->x = 0; + page->y = 0; + page->red = (double) input_attributes.colormap->white_map.red / 255.0; + page->green = (double) input_attributes.colormap->white_map.green / 255.0; + page->blue = (double) input_attributes.colormap->white_map.blue / 255.0; + + content_stream = pdf_new_ind_ref (pdf_page->pdf_file, + pdf_new_stream (pdf_page->pdf_file, + pdf_new_obj (PT_DICTIONARY), + & pdf_write_blank_content_callback, + page)); + + pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); + pdf_write_ind_obj (pdf_page->pdf_file, content_stream); + } + return (1); +} + + +input_handler_t blank_handler = + { + match_blank_suffix, + open_blank_input_file, + close_blank_input_file, + last_blank_input_page, + get_blank_image_info, + process_blank_image + }; diff --git a/tumble_input.c b/tumble_input.c index bec6723..2dccca0 100644 --- a/tumble_input.c +++ b/tumble_input.c @@ -19,6 +19,9 @@ * 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 + * + * 2007-05-07 [JDB] Add support for blank pages and fix a bug that caused a + * double free. */ @@ -66,10 +69,19 @@ bool match_input_suffix (char *suffix) return (0); } + bool open_input_file (char *name) { int i; + if (name == NULL) + { + if (in) + close_input_file (); + current_input_handler = & blank_handler; + return (1); + } + if (in) { if (strcmp (name, in_filename) == 0) @@ -117,8 +129,10 @@ bool close_input_file (void) result = current_input_handler->close_input_file (); current_input_handler = NULL; } - if (in_filename) + if (in_filename) { free (in_filename); + in_filename = NULL; + } if (in) { fclose (in); @@ -151,12 +165,14 @@ bool get_image_info (int image, bool process_image (int image, input_attributes_t input_attributes, image_info_t *image_info, - pdf_page_handle page) + pdf_page_handle page, + position_t position) { if (! current_input_handler) return (0); return (current_input_handler->process_image (image, input_attributes, image_info, - page)); + page, + position)); } diff --git a/tumble_input.h b/tumble_input.h index c16dd13..d65094c 100644 --- a/tumble_input.h +++ b/tumble_input.h @@ -18,12 +18,15 @@ * 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 + * + * 2010-09-02 [JDB] Added support for min-is-black TIFF images. */ typedef struct { bool color; + bool negative; uint32_t width_samples, height_samples; double width_points, height_points; double x_resolution, y_resolution; @@ -42,7 +45,8 @@ typedef struct bool (*process_image) (int image, input_attributes_t input_attributes, image_info_t *image_info, - pdf_page_handle page); + pdf_page_handle page, + position_t position); } input_handler_t; @@ -59,9 +63,13 @@ bool get_image_info (int image, bool process_image (int image, input_attributes_t input_attributes, image_info_t *image_info, - pdf_page_handle page); + pdf_page_handle page, + position_t position); void init_tiff_handler (void); void init_jpeg_handler (void); void init_pbm_handler (void); +void init_png_handler (void); + +input_handler_t blank_handler; diff --git a/tumble_jpeg.c b/tumble_jpeg.c index aa737c3..adaf55f 100644 --- a/tumble_jpeg.c +++ b/tumble_jpeg.c @@ -18,6 +18,8 @@ * 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 + * + * 2009-03-02 [JDB] Add support for overlay images. */ @@ -165,15 +167,17 @@ static bool get_jpeg_image_info (int image, static bool process_jpeg_image (int image, /* range 1 .. n */ input_attributes_t input_attributes, image_info_t *image_info, - pdf_page_handle page) + pdf_page_handle page, + position_t position) { pdf_write_jpeg_image (page, - 0, 0, /* x, y */ + position.x, position.y, image_info->width_points, image_info->height_points, image_info->color, image_info->width_samples, image_info->height_samples, + input_attributes.transparency, jpeg_f); return (1); diff --git a/tumble_pbm.c b/tumble_pbm.c index d6a1f8b..9a19f4f 100644 --- a/tumble_pbm.c +++ b/tumble_pbm.c @@ -18,6 +18,8 @@ * 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 + * + * 2009-03-02 [JDB] Add support for overlay images. */ @@ -125,6 +127,8 @@ static bool get_pbm_image_info (int image, return (0); } + image_info->negative = false; + return (1); } @@ -132,7 +136,8 @@ static bool get_pbm_image_info (int image, static bool process_pbm_image (int image, /* range 1 .. n */ input_attributes_t input_attributes, image_info_t *image_info, - pdf_page_handle page) + pdf_page_handle page, + position_t position) { bool result = 0; Rect rect; @@ -192,12 +197,12 @@ static bool process_pbm_image (int image, /* range 1 .. n */ #endif pdf_write_g4_fax_image (page, - 0, 0, /* x, y */ + position.x, position.y, image_info->width_points, image_info->height_points, + image_info->negative, bitmap, - 0, /* ImageMask */ - 0, 0, 0, /* r, g, b */ - 0); /* BlackIs1 */ + NULL, + NULL); result = 1; diff --git a/tumble_png.c b/tumble_png.c new file mode 100644 index 0000000..78ad719 --- /dev/null +++ b/tumble_png.c @@ -0,0 +1,237 @@ +/* + * tumble: build a PDF file from image files + * + * Copyright 2004 Daniel Gloeckner + * + * Derived from tumble_jpeg.c written 2003 by 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 + * + * 2009-03-13 [JDB] New module to add PNG image support. + */ + + +#include +#include +#include +#include /* strcasecmp() is a BSDism */ + + +#include "semantics.h" +#include "tumble.h" +#include "bitblt.h" +#include "pdf.h" +#include "tumble_input.h" + + +static FILE *png_f; + +static struct { + uint32_t palent; + uint8_t bpp; + uint8_t color; + char pal[256*3]; +} cinfo; + + +static bool match_png_suffix (char *suffix) +{ + return (strcasecmp (suffix, ".png") == 0); +} + +static bool close_png_input_file (void) +{ + return (1); +} + +#define BENUM(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3]) + +static bool open_png_input_file (FILE *f, char *name) +{ + const char sig [8]="\211PNG\r\n\032\n"; + uint8_t buf [8]; + int l; + + l = fread (buf, 1, sizeof (sig), f); + if (l != sizeof (sig) || memcmp(buf,sig,sizeof(sig))) { + rewind(f); + return 0; + } + + png_f = f; + + return 1; +} + + +static bool last_png_input_page (void) +{ + return 1; +} + + +static bool get_png_image_info (int image, + input_attributes_t input_attributes, + image_info_t *image_info) +{ + uint8_t buf [20], unit; + uint32_t width,height,xppu,yppu; + size_t l; + bool seen_IHDR,seen_PLTE,seen_pHYs; + + seen_IHDR=seen_PLTE=seen_pHYs=false; + memset(&cinfo,0,sizeof(cinfo)); + unit=0; + xppu=yppu=1; + + for(;;) + { + l = fread (buf, 1, 8, png_f); + if(l != 8) + return 0; + l=BENUM(buf); + if(!memcmp(buf+4,"IHDR",4)) { + if(seen_IHDR || l!=13) + return 0; + seen_IHDR=true; + l = fread (buf, 1, 17, png_f); + if(l!=17) + return 0; + width=BENUM(buf); + height=BENUM(buf+4); + cinfo.bpp=buf[8]; + cinfo.color=buf[9]; + if(buf[8]>8 || buf[10] || buf[11] || buf[12]) + return 0; + continue; + } + if(!seen_IHDR) + return 0; + if(!memcmp(buf+4,"PLTE",4)) { + size_t i; + if(seen_PLTE || l>256*3 || l%3 || !cinfo.color) + return 0; + seen_PLTE=true; + i = fread (cinfo.pal, 1, l, png_f); + if(i != l) + return 0; + cinfo.palent=l/3; + fseek(png_f,4,SEEK_CUR); + } else if(!memcmp(buf+4,"pHYs",4)) { + if(seen_pHYs || l!=9) + return 0; + seen_pHYs=true; + l = fread (buf, 1, 13, png_f); + if(l != 13) + return 0; + xppu=BENUM(buf); + yppu=BENUM(buf+4); + unit=buf[8]; + } else if(!memcmp(buf+4,"IDAT",4)) { + fseek(png_f,-8,SEEK_CUR); + break; + } else { + fseek(png_f,l+4,SEEK_CUR); + } + } + if(cinfo.color==3 && !seen_PLTE) + return 0; + +#ifdef DEBUG_JPEG + printf ("color type: %d\n", cinfo.color); + printf ("bit depth: %d\n", cinfo.bpp); + printf ("density unit: %d\n", unit); + printf ("x density: %d\n", xppu); + printf ("y density: %d\n", yppu); + printf ("width: %d\n", width); + printf ("height: %d\n", height); +#endif + + switch (cinfo.color) + { + case 0: + image_info->color = 0; + break; + case 2: + case 3: + image_info->color = 1; + break; + default: + fprintf (stderr, "PNG color type %d not supported\n", cinfo.color); + return (0); + } + image_info->width_samples = width; + image_info->height_samples = height; + + switch (unit==1) + { + case 1: + image_info->width_points = ((image_info->width_samples * POINTS_PER_INCH) / + (xppu * 0.0254)); + image_info->height_points = ((image_info->height_samples * POINTS_PER_INCH) / + (yppu * 0.0254)); + break; + case 0: + /* assume 300 DPI - not great, but what else can we do? */ + image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0; + image_info->height_points = ((double) yppu * image_info->height_samples * POINTS_PER_INCH) / ( 300.0 * xppu); + break; + default: + fprintf (stderr, "PNG pHYs unit %d not supported\n", unit); + } + + return 1; +} + + +static bool process_png_image (int image, /* range 1 .. n */ + input_attributes_t input_attributes, + image_info_t *image_info, + pdf_page_handle page, + position_t position) +{ + pdf_write_png_image (page, + position.x, position.y, + image_info->width_points, + image_info->height_points, + cinfo.color, + cinfo.color==3?cinfo.pal:NULL, + cinfo.palent, + cinfo.bpp, + image_info->width_samples, + image_info->height_samples, + input_attributes.transparency, + png_f); + + return (1); +} + + +input_handler_t png_handler = + { + match_png_suffix, + open_png_input_file, + close_png_input_file, + last_png_input_page, + get_png_image_info, + process_png_image + }; + + +void init_png_handler (void) +{ + install_input_handler (& png_handler); +} diff --git a/tumble_tiff.c b/tumble_tiff.c index cb8584c..fb98ae0 100644 --- a/tumble_tiff.c +++ b/tumble_tiff.c @@ -18,6 +18,8 @@ * 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 + * + * 2010-09-02 [JDB] Added support for min-is-black TIFF images. */ @@ -102,6 +104,7 @@ static bool get_tiff_image_info (int image, uint32_t image_height, image_width; uint16_t samples_per_pixel; uint16_t bits_per_sample; + uint16_t photometric_interpretation; uint16_t planar_config; uint16_t resolution_unit; @@ -149,6 +152,19 @@ static bool get_tiff_image_info (int image, return (0); } + if (1 != TIFFGetField (tiff_in, TIFFTAG_PHOTOMETRIC, & photometric_interpretation)) + { + fprintf(stderr, "warning: photometric interpretation not specified, assuming min-is-white\n"); + photometric_interpretation = PHOTOMETRIC_MINISWHITE; + } + + else if ((photometric_interpretation != PHOTOMETRIC_MINISWHITE) && + (photometric_interpretation != PHOTOMETRIC_MINISBLACK)) + { + fprintf(stderr, "photometric interpretation value %u is invalid\n", photometric_interpretation); + return (0); + } + if (1 != TIFFGetField (tiff_in, TIFFTAG_PLANARCONFIG, & planar_config)) planar_config = 1; @@ -217,6 +233,8 @@ static bool get_tiff_image_info (int image, return (0); } + image_info->negative = (photometric_interpretation == PHOTOMETRIC_MINISBLACK); + return (1); } @@ -267,7 +285,8 @@ static void rotate_bitmap (Bitmap *src, static bool process_tiff_image (int image, /* range 1 .. n */ input_attributes_t input_attributes, image_info_t *image_info, - pdf_page_handle page) + pdf_page_handle page, + position_t position) { bool result = 0; Rect rect; @@ -328,12 +347,12 @@ static bool process_tiff_image (int image, /* range 1 .. n */ pdf_write_text (page); #else pdf_write_g4_fax_image (page, - 0, 0, /* x, y */ + position.x, position.y, image_info->width_points, image_info->height_points, + image_info->negative, bitmap, - 0, /* ImageMask */ - 0, 0, 0, /* r, g, b */ - 0); /* BlackIs1 */ + input_attributes.colormap, + input_attributes.transparency); #endif result = 1;