[PATCH] PNG & JPEG 2000 support

Daniel Glöckner daniel-gl at gmx.net
Sun Oct 17 14:22:32 PDT 2004


Once again without PGP signature and compression to make the list
server happy.

  Daniel

-----------------------------------------------------------------------

The attached patch adds PNG and JP2 support to tumble.

PNG:
As the deflated data is directly copied into the PDF, there are some
limitations to the list of supported images:
- bit depth <= 8
- no alpha channel
- no interlace

JP2:
The PDF Reference says JP2 is just a subset of the allowed JPX
format. I don't have a copy of the official standard, so I don't know
what to change to cover JPXes as well.
You'll need the Adobe Acrobat Reader 6 to display those images.
Xpdf and Ghostscript are missing the ColorSpace key in the image
dictionary, which is optional for JPXDecode and IMHO not just a matter
of a few lines of code.
One thing left to do is to change the PDF version to 1.5 if a JP2 file
has been given to tumble - maybe using the Version key in the Catalog
if seeking is not possible.
Using the resolution info in a JP2 (resc/resd boxes) is implemented but
untested. Jasper doesn't write those boxes.

I had to change the string handling to allow black in PNG palettes.
And there was a double free in tumble_input.c which happens when not
using control files.

  Daniel
-------------- next part --------------
diff -rwudN tumble-0.33/Makefile tumble-0.33-my/Makefile
--- tumble-0.33/Makefile	Tue Dec  9 18:54:48 2003
+++ tumble-0.33-my/Makefile	Sun Oct 17 03:05:10 2004
@@ -65,11 +65,12 @@
 TARGETS = tumble
 
 CSRCS = tumble.c semantics.c \
-	tumble_input.c tumble_tiff.c tumble_jpeg.c tumble_pbm.c \
+	tumble_input.c tumble_tiff.c tumble_jpeg.c \
+	tumble_pbm.c tumble_png.c tumble_jp2.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 pdf_jp2.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
@@ -107,11 +108,12 @@
 
 
 TUMBLE_OBJS = tumble.o semantics.o \
-		tumble_input.o tumble_tiff.o tumble_jpeg.o tumble_pbm.o \
+		tumble_input.o tumble_tiff.o tumble_jpeg.o \
+		tumble_pbm.o tumble_png.o tumble_jp2.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 pdf_jp2.o
 
 ifdef CTL_LANG
 TUMBLE_OBJS += scanner.o parser.tab.o
diff -rwudN tumble-0.33/pdf.h tumble-0.33-my/pdf.h
--- tumble-0.33/pdf.h	Tue Dec  9 17:45:41 2003
+++ tumble-0.33-my/pdf.h	Sun Oct 17 03:05:10 2004
@@ -92,6 +92,27 @@
 			   uint32_t height_samples,
 			   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,
+			   FILE *f);
+
+void pdf_write_jp2_image (pdf_page_handle pdf_page,
+			   double x,
+			   double y,
+			   double width,
+			   double height,
+			   uint32_t width_samples,
+			   uint32_t height_samples,
+			   FILE *f);
 
 void pdf_set_page_number (pdf_page_handle pdf_page, char *page_number);
 
diff -rwudN tumble-0.33/pdf_jp2.c tumble-0.33-my/pdf_jp2.c
--- tumble-0.33/pdf_jp2.c	Thu Jan  1 01:00:00 1970
+++ tumble-0.33-my/pdf_jp2.c	Sun Oct 17 03:05:44 2004
@@ -0,0 +1,161 @@
+/*
+ * tumble: build a PDF file from image files
+ *
+ * PDF routines
+ * Copyright 2004 Daniel Gloeckner
+ *
+ * Derived from pdf_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "bitblt.h"
+#include "pdf.h"
+#include "pdf_util.h"
+#include "pdf_prim.h"
+#include "pdf_private.h"
+
+
+struct pdf_jp2_image
+{
+  double width, height;
+  double x, y;
+  uint32_t width_samples, height_samples;
+  FILE *f;
+  char XObject_name [4];
+};
+
+
+static void pdf_write_jp2_content_callback (pdf_file_handle pdf_file,
+					     struct pdf_obj *stream,
+					     void *app_data)
+{
+  struct pdf_jp2_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_stream_printf (pdf_file, stream, "/%s Do Q\r\n",
+		     image->XObject_name);
+}
+
+
+#define JPEG_BUFFER_SIZE 8192
+
+static void pdf_write_jp2_image_callback (pdf_file_handle pdf_file,
+					   struct pdf_obj *stream,
+					   void *app_data)
+{
+  struct pdf_jp2_image *image = app_data;
+  int rlen, wlen;
+  uint8_t *wp;
+  uint8_t buffer [8192];
+
+  while (! feof (image->f))
+    {
+      rlen = fread (& buffer [0], 1, JPEG_BUFFER_SIZE, image->f);
+      wp = & buffer [0];
+      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");
+    }
+}
+
+
+void pdf_write_jp2_image (pdf_page_handle pdf_page,
+			   double x,
+			   double y,
+			   double width,
+			   double height,
+			   uint32_t width_samples,
+			   uint32_t height_samples,
+			   FILE *f)
+{
+  struct pdf_jp2_image *image;
+
+  struct pdf_obj *stream;
+  struct pdf_obj *stream_dict;
+
+  struct pdf_obj *content_stream;
+
+  image = pdf_calloc (1, sizeof (struct pdf_jp2_image));
+
+  image->width = width;
+  image->height = height;
+  image->x = x;
+  image->y = y;
+
+  image->f = f;
+
+  image->width_samples = width_samples;
+  image->height_samples = height_samples;
+
+  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_jp2_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"));
+// 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));
+
+  // not required for JPXDecode, but 
+  pdf_set_dict_entry (stream_dict, "BitsPerComponent",  pdf_new_integer (8));
+
+  pdf_stream_add_filter (stream, "JPXDecode", NULL);
+
+  /* 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_jp2_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 -rwudN tumble-0.33/pdf_png.c tumble-0.33-my/pdf_png.c
--- tumble-0.33/pdf_png.c	Thu Jan  1 01:00:00 1970
+++ tumble-0.33-my/pdf_png.c	Sun Oct 17 03:05:41 2004
@@ -0,0 +1,200 @@
+/*
+ * tumble: build a PDF file from image files
+ *
+ * PDF routines
+ * Copyright 2004 Daniel Gloeckner
+ *
+ * Derived from pdf_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#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_stream_printf (pdf_file, stream, "/%s Do Q\r\n",
+		     image->XObject_name);
+}
+
+
+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, (clen<sizeof(buffer))?clen:sizeof(buffer), image->f);
+	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,
+			   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;
+
+  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(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));
+
+  pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream);
+
+  pdf_write_ind_obj (pdf_page->pdf_file, content_stream);
+}
diff -rwudN tumble-0.33/pdf_prim.c tumble-0.33-my/pdf_prim.c
--- tumble-0.33/pdf_prim.c	Tue Dec  9 17:45:39 2003
+++ tumble-0.33-my/pdf_prim.c	Sun Oct 17 03:05:10 2004
@@ -90,7 +90,10 @@
   union {
     bool              boolean;
     char              *name;
-    char              *string;
+    struct {
+      char            *content;
+      int             length;
+    }                 string;
     long              integer;
     double            real;
     struct pdf_obj    *ind_ref;
@@ -243,7 +246,18 @@
 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 +411,16 @@
 	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 +450,51 @@
 }
 
 
-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 +612,7 @@
       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 -rwudN tumble-0.33/pdf_prim.h tumble-0.33-my/pdf_prim.h
--- tumble-0.33/pdf_prim.h	Tue Dec  9 17:45:39 2003
+++ tumble-0.33-my/pdf_prim.h	Sun Oct 17 03:05:10 2004
@@ -79,6 +79,8 @@
 
 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);
diff -rwudN tumble-0.33/tumble.c tumble-0.33-my/tumble.c
--- tumble-0.33/tumble.c	Tue Dec  9 18:17:19 2003
+++ tumble-0.33-my/tumble.c	Sun Oct 17 03:05:10 2004
@@ -381,6 +381,8 @@
   init_tiff_handler ();
   init_jpeg_handler ();
   init_pbm_handler ();
+  init_png_handler ();
+  init_jp2_handler ();
 
   while (--argc)
     {
diff -rwudN tumble-0.33/tumble_input.c tumble-0.33-my/tumble_input.c
--- tumble-0.33/tumble_input.c	Tue Dec  9 17:45:40 2003
+++ tumble-0.33-my/tumble_input.c	Sun Oct 17 03:06:41 2004
@@ -117,8 +117,10 @@
       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);
diff -rwudN tumble-0.33/tumble_input.h tumble-0.33-my/tumble_input.h
--- tumble-0.33/tumble_input.h	Tue Dec  9 17:45:40 2003
+++ tumble-0.33-my/tumble_input.h	Sun Oct 17 03:05:10 2004
@@ -65,3 +65,5 @@
 void init_tiff_handler (void);
 void init_jpeg_handler (void);
 void init_pbm_handler  (void);
+void init_png_handler  (void);
+void init_jp2_handler  (void);
diff -rwudN tumble-0.33/tumble_jp2.c tumble-0.33-my/tumble_jp2.c
--- tumble-0.33/tumble_jp2.c	Thu Jan  1 01:00:00 1970
+++ tumble-0.33-my/tumble_jp2.c	Sun Oct 17 03:11:45 2004
@@ -0,0 +1,275 @@
+/*
+ * tumble: build a PDF file from image files
+ *
+ * Copyright 2004 Daniel Gloeckner
+ *
+ * Derived from tumble_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <math.h>
+#include <strings.h>  /* strcasecmp() is a BSDism */
+
+
+#include "semantics.h"
+#include "tumble.h"
+#include "bitblt.h"
+#include "pdf.h"
+#include "tumble_input.h"
+
+
+static bool match_jp2_suffix (char *suffix)
+{
+  return (strcasecmp (suffix, ".jp2") == 0);
+}
+
+static bool close_jp2_input_file (void)
+{
+  return (1);
+}
+
+static struct {
+  FILE *f;
+  uint32_t width,height;
+  struct {
+    double VR,HR;
+  } res[2];
+} jp2info;
+
+struct box {
+  char TBox[4];
+  bool (*func)(uint64_t size, void *cparam);
+  void *cparam;
+};
+
+#define BE32(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3])
+#define BE16(p) (((p)[2]<<8)+(p)[3])
+
+static bool skipbox(uint64_t size, void *cparam)
+{
+  if(size==~0)
+    fseek(jp2info.f,0,SEEK_END);
+  else {
+    if(cparam)
+      size-=*(int *)cparam;
+    fseek(jp2info.f,size,SEEK_CUR); /*FIXME: size is 64bit*/
+  }
+  return 1;
+}
+
+static bool read_res(uint64_t size, void *cparam)
+{
+  int read=0;
+  bool ret=1;
+  if(size>=10) {
+    int i;
+    unsigned char buf[10];
+
+    ret=0;
+    i=(int)cparam;
+    read=fread(buf,1,10,jp2info.f);
+    if(read==10) {
+      uint16_t vrn,vrd,hrn,hrd;
+      int8_t vre,hre;
+      vrn=BE16(buf);
+      vrd=BE16(buf+2);
+      hrn=BE16(buf+4);
+      hrd=BE16(buf+6);
+      vre=((signed char *)buf)[8];
+      hre=((signed char *)buf)[9];
+      if(vrn && vrd && hrn && hrd) {
+	jp2info.res[i].VR=vrn*pow(10.0,vre)/vrd;
+	jp2info.res[i].HR=hrn*pow(10.0,hre)/hrd;
+	ret=1;
+      }
+    }
+  }
+  skipbox(size,&read);
+  return ret;
+}
+
+static bool read_ihdr(uint64_t size, void *cparam)
+{
+  int read=0;
+  bool ret=1;
+  if(size>=8) {
+    unsigned char buf[8];
+    read=fread(buf,1,8,jp2info.f);
+    if(read==8) {
+      jp2info.height=BE32(buf);
+      jp2info.width=BE32(buf+4);
+    } else
+      ret=0;
+  }
+  skipbox(size,&read);
+  return ret;
+}
+
+static bool superbox(uint64_t size, void *cparam)
+{ 
+  while(size>=8) {
+    static unsigned char buf[12];
+    int r;
+    uint64_t s;
+    struct box *l;
+
+    r=fread(buf+4,1,8,jp2info.f);
+    if(r<8)
+      return (size==~0 && r==0);
+ 
+    s=BE32(buf+4);
+    if(s==1 && size>=16) {
+      r=fread(buf,1,8,jp2info.f);
+      if(r<8)
+        return 0;
+      s=((uint64_t)BE32(buf)<<32)+BE32(buf+4);
+    }
+    if(s && s<8)
+      return 0;
+    if(size!=~0) {
+      if(!s)
+        return 0;
+      size-=s;
+    } else if(!s)
+      size=0;
+    s=s?s-8:~0;
+
+    for(l=(struct box *)cparam;l->func;l++)
+      if(!memcmp(l->TBox,buf+8,4))
+        break;
+    if(l->func) {
+      if(!l->func(s,l->cparam))
+	return 0;
+    }else
+      if(!skipbox(s,NULL))
+	return 0;
+  }
+  
+  return size==0;
+}
+
+static const struct box res_boxes[]={
+  {"resc",read_res,(void *)0},
+  {"resd",read_res,(void *)1},
+  {"",NULL,NULL}
+};
+
+static const struct box jp2h_boxes[]={
+  {"ihdr",read_ihdr,NULL},
+  {"res ",superbox,(void *)res_boxes},
+  {"",NULL,NULL}
+};
+
+static const struct box root[]={
+  {"jp2h",superbox,(void *)jp2h_boxes},
+  {"",NULL,NULL}
+};
+
+static bool open_jp2_input_file (FILE *f, char *name)
+{
+  int s;
+  const char sig[12]="\0\0\0\xCjP  \r\n\x87\n";
+  char buf[12];
+
+  memset(&jp2info,0,sizeof(jp2info));
+  jp2info.f=f;
+  
+  s=fread(buf,1,12,f);
+  rewind(f);
+  return (s==12 && !memcmp(buf,sig,12));
+}
+
+
+static bool last_jp2_input_page (void)
+{
+  return 1;
+}
+
+
+static bool get_jp2_image_info (int image,
+				 input_attributes_t input_attributes,
+				 image_info_t *image_info)
+{
+  int i;
+
+  if(!superbox(~0,(void *)root))
+    return 0;
+  rewind(jp2info.f);
+  
+#ifdef DEBUG_JPEG
+  printf ("capture x density: %d pixel/m\n", jp2info.res[0].HR);
+  printf ("capture y density: %d pixel/m\n", jp2info.res[0].VR);
+  printf ("display x density: %d pixel/m\n", jp2info.res[1].HR);
+  printf ("display y density: %d pixel/m\n", jp2info.res[1].VR);
+  printf ("width: %d\n", jp2info.width);
+  printf ("height: %d\n", jp2info.height);
+#endif
+
+  image_info->color = 1;
+  image_info->width_samples = jp2info.width;
+  image_info->height_samples = jp2info.height;
+
+  image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0;
+  image_info->height_points = (image_info->height_samples * POINTS_PER_INCH) / 300.0;
+
+  for(i=2;i--;)
+    if(jp2info.res[i].VR > 0.0 && jp2info.res[i].HR > 0.0) {
+      image_info->width_points = (image_info->width_samples * POINTS_PER_INCH * 10000)/(jp2info.res[i].HR * 254);
+      image_info->height_points = (image_info->height_samples * POINTS_PER_INCH * 10000)/(jp2info.res[i].VR * 254);
+      break;
+    }
+
+  return 1;
+}
+
+
+static bool process_jp2_image (int image,  /* range 1 .. n */
+				input_attributes_t input_attributes,
+				image_info_t *image_info,
+				pdf_page_handle page)
+{
+  pdf_write_jp2_image (page,
+			0, 0,  /* x, y */
+			image_info->width_points,
+			image_info->height_points,
+			image_info->width_samples,
+			image_info->height_samples,
+			jp2info.f);
+
+  return (1);
+}
+
+
+input_handler_t jp2_handler =
+  {
+    match_jp2_suffix,
+    open_jp2_input_file,
+    close_jp2_input_file,
+    last_jp2_input_page,
+    get_jp2_image_info,
+    process_jp2_image
+  };
+
+
+void init_jp2_handler (void)
+{
+  install_input_handler (& jp2_handler);
+}
diff -rwudN tumble-0.33/tumble_png.c tumble-0.33-my/tumble_png.c
--- tumble-0.33/tumble_png.c	Thu Jan  1 01:00:00 1970
+++ tumble-0.33-my/tumble_png.c	Sun Oct 17 03:05:38 2004
@@ -0,0 +1,233 @@
+/*
+ * tumble: build a PDF file from image files
+ *
+ * Copyright 2004 Daniel Gloeckner
+ * 
+ * Derived from tumble_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <strings.h>  /* 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)
+{
+  pdf_write_png_image (page,
+			0, 0,  /* x, 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,
+			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);
+}


More information about the Tumble-devel mailing list