libtiff: Significant update in functionsality as per bug

From 5e557c568746f95f7421f0be8bca3fdcd86ebc13 Mon Sep 17 00:00:00 2001
From: Andrey Kiselev <[EMAIL REDACTED]>
Date: Fri, 6 Apr 2007 14:22:39 +0000
Subject: [PATCH] Significant update in functionsality as per bug
 http://bugzilla.remotesensing.org/show_bug.cgi?id=1525

---
 tools/tiffcrop.c | 3029 ++++++++++++++++++++++++++--------------------
 1 file changed, 1731 insertions(+), 1298 deletions(-)

diff --git a/tools/tiffcrop.c b/tools/tiffcrop.c
index 9cd5f133..5a8b86f2 100644
--- a/tools/tiffcrop.c
+++ b/tools/tiffcrop.c
@@ -1,6 +1,8 @@
-/* $Id: tiffcrop.c,v 1.3 2007-02-24 15:57:01 dron Exp $ */
+/* $Id: tiffcrop.c,v 1.3.2.1 2007-04-06 14:22:39 dron Exp $ */
 
 /* tiffcrop.c -- a port of tiffcp.c extended to include cropping of selections
+ *
+ * Original code:
  *
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -24,34 +26,45 @@
  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  * OF THIS SOFTWARE.
  *
- * Richard Nolde  10/2006 Add support for the options below to extract 
+ * Richard Nolde  Updated 2/2007 Add support for the options below to extract 
  * sections of image(s) and to modify the whole image or selected portion
  * with rotations, mirroring, and colorscale/colormap inversion of selected
  * types of TIFF images when appropriate
  *
  * Options: 
- * -u units     [in, cm, px ] inches, centimeters or pixels
- * -x #         horizontal dimension of region to extract expressed in current
+ * -U units     [in, cm, px ] inches, centimeters or pixels
+ * -H #         set horizontal resolution of output images to #
+ * -V #         set vertical resolution of output images to #
+ * -J #         set horizontal margin of output page to # expressed in current
+ *              units
+ * -K #         set vertical margin of output page to # expressed in current
  *              units
- * -y #         vertical dimension of region to extract expressed in current
+ * -X #         horizontal dimension of region to extract expressed in current
  *              units
- * -e t|l|r|b   edge to use as origin
+ * -Y #         vertical dimension of region to extract expressed in current
+ *              units
+ * -O orient    orientation for output image, portrait, landscape, auto
+ * -P page      page size for output image segments, eg letter, legal, tabloid,
+ *              etc.
+ * -S cols:rows divide the image into equal sized segments using cols across
+ *              and rows down
+ * -E t|l|r|b   edge to use as origin
  * -m #,#,#,#   margins from edges for selection: top, left, bottom, right
  *              (commas separated)
- * -z #:#,#:#   up to six zones of the image designated as zone X of Y,
+ * -Z #:#,#:#   zones of the image designated as zone X of Y, 
  *              eg 1:3 would be first of three equal portions measured
  *              from reference edge
- * -n odd|even|#,#-#,#|last sequences and ranges of images within file to
- *              process the words odd or even may be used to specify all odd
- *              or even numbered images the word last may be used in place
- *              of a number in the sequence to indicate the final image in
- *              the file without knowing how many images there are
+ * -N odd|even|#,#-#,#|last sequences and ranges of images within file
+ *              to process the words odd or even may be used to specify
+ *              all odd or even numbered images the word last may be used
+ *              in place of a number in the sequence to indicate the final
+ *              image in the file without knowing how many images there are
  * -R #         rotate image or crop selection by 90,180,or 270 degrees
  *              clockwise  
  * -F h|v       flip (mirror) image or crop selection horizontally
- *              or vertically
- * -I           invert the colormap, black to white, for bilevel
- *              and grayscale images
+ *              or vertically 
+ * -I           invert the colormap, black to white, for bilevel and grayscale
+ *              images
  */
 
 #include "tif_config.h"
@@ -68,20 +81,28 @@
 # include <unistd.h>
 #endif
 
-#include "tiffio.h"
-
 #ifndef HAVE_GETOPT
 extern int getopt(int, char**, char*);
 #endif
 
+#include "tiffio.h"
+
+#if defined(VMS)
+# define unlink delete
+#endif
+
 #define	streq(a,b)	(strcmp((a),(b)) == 0)
 #define	strneq(a,b,n)	(strncmp((a),(b),(n)) == 0)
 
+/* NB: the uint32 casts are to silence certain ANSI-C compilers */
+#define TIFFhowmany(x, y) ((((uint32)(x))+(((uint32)(y))-1))/((uint32)(y)))
+#define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
+
 #define	TRUE	1
 #define	FALSE	0
 
 /*
- * Definitions and data structures required to support cropping and image
+ * Definitions and data structures required to support cropping and inmage
  * manipulations.
  */
 
@@ -89,6 +110,7 @@ extern int getopt(int, char**, char*);
 #define EDGE_LEFT     2
 #define EDGE_BOTTOM   3
 #define EDGE_RIGHT    4
+
 #define MIRROR_HORIZ  1
 #define MIRROR_VERT   2
 
@@ -104,7 +126,8 @@ extern int getopt(int, char**, char*);
 #define STRIP    1
 #define TILE     2
 
-#define MAX_ZONES     6  /* number of sections on a singel page */
+#define MAX_ZONES    32  /* number of sections to extract from a single page */
+#define MAX_SECTIONS 64  /* number of sections from one page to write to output */
 #define MAX_IMAGES  256  /* number of images in descrete list */
 
 /* Offsets into buffer for margins and fixed width and length */
@@ -131,33 +154,142 @@ struct  pageseg {
   uint32 x2;        /* index of right edge */
   uint32 y1;        /* index of top edge */
   uint32 y2;        /* index of bottom edge */
-  int    total;     /* total equal sized divisions of crop area */
-  int    position;  /* ordinal of segment to be extracted */
   uint32 buffsize;  /* size of buffer needed to hold the cropped region */
+  int    position;  /* ordinal of segment to be extracted */
+  int    total;     /* total equal sized divisions of crop area */
 };
 
 /* Cropping parameters from command line and image data */
 struct crop_mask {
-  uint16 crop_mode;       /* Crop options to be applied */
-  uint16 res_unit;        /* Resolution unit for margins and selections */
-  uint16 edge_ref;        /* Reference edge from which zones are calculated */
-  uint16 rotation;        /* Clockwise rotation of the extracted region or image */
-  uint16 mirror;          /* Mirror extracted region or image horizontally or vertically */
-  uint16 invert;          /* Invert the color map of image or region */
   double width;           /* Selection width for master crop region in requested units */
   double length;          /* Selection length for master crop region in requesed units */
   double margins[4];      /* Top, left, bottom, right margins */
+  float  xres;            /* Horizontal resolution read from image*/
+  float  yres;            /* Vertical resolution read from image */
   uint32 combined_width;  /* Width of combined cropped zones */
   uint32 combined_length; /* Length of combined cropped zones */
   uint32 bufftotal;       /* size of buffer needed to hold all the cropped region */
-  int    zones;           /* Number of zones requested */
+  uint32 zones;           /* Number of zones requested */
+  uint16 crop_mode;       /* Crop options to be applied */
+  uint16 res_unit;        /* Resolution unit for margins and selections */
+  uint16 edge_ref;        /* Reference edge from which zones are calculated */
+  uint16 rotation;        /* Clockwise rotation of the extracted region or image */
+  uint16 mirror;          /* Mirror extracted region or image horizontally or vertically */
+  uint16 invert;          /* Invert the color map of image or region */
   struct pageseg zonelist[MAX_ZONES]; /* Zones within page or master crop region */
 };
 
-/* 
- * Global variables saving state of the program.
- */
-static  int outtiled = -1;
+#define MAX_PAPERNAMES 49
+#define MAX_PAPERNAME_LENGTH 15
+#define DEFAULT_RESUNIT      RESUNIT_INCH
+#define DEFAULT_PAGE_HEIGHT   14.0
+#define DEFAULT_PAGE_WIDTH     8.5
+#define DEFAULT_RESOLUTION   300
+#define DEFAULT_PAPER_SIZE  "legal"
+
+#define ORIENTATION_NONE       0
+#define ORIENTATION_PORTRAIT   1
+#define ORIENTATION_LANDSCAPE  2
+#define ORIENTATION_SEASCAPE   4
+#define ORIENTATION_AUTO      16
+
+#define PAGE_MODE_NONE         0
+#define PAGE_MODE_RESOLUTION   1
+#define PAGE_MODE_PAPERSIZE    2
+#define PAGE_MODE_MARGINS      4
+#define PAGE_MODE_ROWSCOLS     8
+
+
+struct paperdef {
+  char   name[MAX_PAPERNAME_LENGTH];
+  double width;
+  double length;
+  double asratio;
+  };
+
+/* Paper Size       Width   Length  Aspect Ratio */
+struct paperdef PaperTable[MAX_PAPERNAMES] = {
+  {"default",         8.500,  14.000,  0.607},
+  {"pa4",             8.264,  11.000,  0.751},
+  {"letter",          8.500,  11.000,  0.773},
+  {"legal",           8.500,  14.000,  0.607},
+  {"half-letter",     8.500,   5.514,  1.542},
+  {"executive",       7.264,  10.528,  0.690},
+  {"tabloid",        11.000,  17.000,  0.647},
+  {"11x17",          11.000,  17.000,  0.647},
+  {"ledger",         17.000,  11.000,  1.545},
+  {"archa",           9.000,  12.000,  0.750},
+  {"archb",          12.000,  18.000,  0.667},
+  {"archc",          18.000,  24.000,  0.750},
+  {"archd",          24.000,  36.000,  0.667},
+  {"arche",          36.000,  48.000,  0.750},
+  {"csheet",         17.000,  22.000,  0.773},
+  {"dsheet",         22.000,  34.000,  0.647},
+  {"esheet",         34.000,  44.000,  0.773},
+  {"superb",         11.708,  17.042,  0.687},
+  {"commercial",      4.139,   9.528,  0.434},
+  {"monarch",         3.889,   7.528,  0.517},
+  {"envelope-dl",     4.333,   8.681,  0.499},
+  {"envelope-c5",     6.389,   9.028,  0.708},
+  {"europostcard",    4.139,   5.833,  0.710},
+  {"a0",             33.111,  46.806,  0.707},
+  {"a1",             23.389,  33.111,  0.706},
+  {"a2",             16.542,  23.389,  0.707},
+  {"a3",             11.694,  16.542,  0.707},
+  {"a4",              8.264,  11.694,  0.707},
+  {"a5",              5.833,   8.264,  0.706},
+  {"a6",              4.125,   5.833,  0.707},
+  {"a7",              2.917,   4.125,  0.707},
+  {"a8",              2.056,   2.917,  0.705},
+  {"a9",              1.458,   2.056,  0.709},
+  {"a10",             1.014,   1.458,  0.695},
+  {"b0",             39.375,  55.667,  0.707},
+  {"b1",             27.833,  39.375,  0.707},
+  {"b2",             19.681,  27.833,  0.707},
+  {"b3",             13.903,  19.681,  0.706},
+  {"b4",              9.847,  13.903,  0.708},
+  {"b5",              6.931,   9.847,  0.704},
+  {"b6",              4.917,   6.931,  0.709},
+  {"c0",             36.097,  51.069,  0.707},
+  {"c1",             25.514,  36.097,  0.707},
+  {"c2",             18.028,  25.514,  0.707},
+  {"c3",             12.750,  18.028,  0.707},
+  {"c4",              9.014,  12.750,  0.707},
+  {"c5",              6.375,   9.014,  0.707},
+  {"c6",              4.486,   6.375,  0.704},
+  {"",                0.000,   0.000,  1.000},
+};
+
+/* Structure to define in input image parameters */
+struct image_data {
+  float  xres;
+  float  yres;
+  uint32 width;
+  uint32 length;
+  uint16 res_unit;
+  uint16 bps;
+  uint16 spp;
+  uint16 planar;
+  uint16 photometric;
+};
+
+/* Structure to define the output image modifiers */
+struct pagedef {
+  char          name[16];
+  double        width;    /* width in pixels */
+  double        length;   /* length in pixels */
+  double        hmargin;  /* margins to subtract from width of sections */
+  double        vmargin;  /* margins to subtract from height of sections */
+  double        hres;     /* horizontal resolution for output */
+  double        vres;     /* vertical resolution for output */
+  uint32        mode;     /* bitmask of modifiers to page format */
+  uint16        res_unit; /* resolution unit for output image */
+  unsigned int  rows;     /* number of section rows */
+  unsigned int  cols;     /* number of section cols */
+  unsigned int  orient;   /* portrait, landscape, seascape, auto */
+};
+
+static  int    outtiled = -1;
 static  uint32 tilewidth;
 static  uint32 tilelength;
 
@@ -168,33 +300,48 @@ static	uint16 fillorder;
 static	uint16 orientation;
 static	uint32 rowsperstrip;
 static	uint32 g3opts;
-static	int ignore = FALSE;		/* if true, ignore read errors */
+static	int    ignore = FALSE;		/* if true, ignore read errors */
 static	uint32 defg3opts = (uint32) -1;
-static	int quality = 75;		/* JPEG quality */
-static	int jpegcolormode = JPEGCOLORMODE_RGB;
+static	int    quality = 75;		/* JPEG quality */
+static	int    jpegcolormode = JPEGCOLORMODE_RGB;
 static	uint16 defcompression = (uint16) -1;
 static	uint16 defpredictor = (uint16) -1;
-static  TIFF* bias = NULL;
-static  int pageNum = 0;
 
-/* 
- * Helper functions declarations.
- */
 static	int processCompressOptions(char*);
-static	int tiffcp(TIFF*, TIFF*);
 static	void usage(void);
 
-static  int initCropMasks (struct crop_mask *cps);
-static  int computeOffsets(uint16, float, float, uint32, uint32, 
-			   struct crop_mask *, struct offset *);
-static  int getCropOffsets(TIFF*, struct crop_mask *);
-static  int loadImage(TIFF*, unsigned char **);
-static  int extractCropRegions(TIFF *, struct crop_mask *, unsigned char *, unsigned char *);
-static  int createCroppedImage(TIFF*, struct crop_mask *, unsigned char **, unsigned char **);
-static  int rotateImage(uint16, uint16, uint16, uint32 *, uint32 *, unsigned char **);
-static  int mirrorImage(uint16, uint16, uint16, uint32, uint32, unsigned char *);
-static  int invertImage(uint16, uint16, uint16, uint32, uint32, unsigned char *);
-static  int writeCroppedImage(TIFF *, TIFF *, struct crop_mask *, unsigned char *);
+/* New functions by Richard Nolde  not found in tiffcp */
+static  void initImageData (struct image_data *);
+static  void initCropMasks (struct crop_mask *);
+static  void initPageSetup (struct pagedef *, struct pageseg *);
+static  int  get_page_geometry (char *, struct pagedef*);
+static  int  computeInputPixelOffsets(struct crop_mask *, struct image_data *, 
+                                      struct offset *);
+static  int  computeOutputPixelOffsets (struct crop_mask *, struct image_data *,
+					struct pagedef *, struct pageseg *);
+static  int  loadImage(TIFF *, struct image_data *, unsigned char **);
+static  int  getCropOffsets(struct image_data *, struct crop_mask *);
+static  int  extractCropRegions(TIFF *, struct crop_mask *,
+				unsigned char *, unsigned char *);
+static  int  createCroppedImage(TIFF*, struct image_data *, struct crop_mask *, 
+                               unsigned char **, unsigned char **);
+static  int  rotateImage(uint16, struct image_data *, uint32 *, uint32 *,
+			 unsigned char **);
+static  int  mirrorImage(uint16, uint16, uint16, uint32, uint32,
+			 unsigned char *);
+static  int  invertImage(uint16, uint16, uint16, uint32, uint32,
+			 unsigned char *);
+static  int  writeCroppedImage(TIFF *, TIFF *, struct crop_mask *,
+			       unsigned char *);
+static  int  createImageSection(uint32, unsigned char **);
+static  int  extractImageSection(struct image_data *, struct pageseg *, 
+                                 unsigned char *, unsigned char *);
+static  int  writeSingleSection(TIFF *, TIFF *, uint32, uint32,
+				double, double, unsigned char *);
+static  int  writeImageSections(TIFF *, TIFF *, struct image_data *,
+				struct pagedef *, struct pageseg *,
+				unsigned char *, unsigned char **);
+static int   pageNum = 0;
 
 int
 main(int argc, char* argv[])
@@ -205,68 +352,44 @@ main(int argc, char* argv[])
   uint32 deftilelength = (uint32) -1;
   uint32 defrowsperstrip = (uint32) 0;
   uint32 dirnum = 0;
-  uint32 bias_image = 0;
+
   TIFF* in = NULL;
-  TIFF* out;
-  char mode[10];
+  TIFF* out = NULL;
+  char  mode[10];
   char* mp = mode;
-  struct  crop_mask    crop_data;       /* Cropping parameters for image */
-  unsigned char *read_buff    = NULL;   /* input image data buffer */
-  unsigned char *crop_buff    = NULL;   /* crop area buffer */
-  char *opt_offset   = NULL;            /* Postion in string of value sought */
-  char *opt_ptr      = NULL;        /* Pointer to next token in option set */
-  char *sep          = NULL;            /* pointer to a token separator */
-  uint32 start, end, image_count = 0, next_image = 0;
-  uint32 imagelist[MAX_IMAGES + 1];     /* individually specified images */
-  uint32 i, j;
-  int c;
+  int   c;
+
+  /** RJN additions **/
+  struct image_data   image;     /* Image parameters for one image */
+  struct crop_mask    crop_data; /* Cropping parameters for all images */
+  struct pagedef      page;      /* Page definition for output pages */
+  struct pageseg sections[MAX_SECTIONS]; /* Sections of one output page */
+  unsigned char *read_buff    = NULL;      /* Input image data buffer */
+  unsigned char *crop_buff    = NULL;      /* Crop area buffer */
+  unsigned char *sect_buff    = NULL;      /* Image section buffer */
+  unsigned char *sect_src     = NULL;      /* Image section buffer pointer */
+  char *opt_offset   = NULL;    /* Position in string of value sought */
+  char *opt_ptr      = NULL;    /* Pointer to next token in option set */
+  char *sep          = NULL;    /* Pointer to a token separator */
+  unsigned int  i, j, start, end;
+  unsigned int  image_count = 0;
+  unsigned int  next_image  = 0;
+  unsigned int  imagelist[MAX_IMAGES + 1]; /* individually specified images */
 
   extern int optind;
   extern char* optarg;
 
+  initImageData(&image);
   initCropMasks(&crop_data);
+  initPageSetup(&page, sections);
 
   *mp++ = 'w';
   *mp = '\0';
-  while ((c = getopt(argc, argv, "ab:c:d:f:il:p:r:st:w:BLMCE:F:IN:m:R:U:X:Y:")) != -1)
+  while ((c = getopt(argc, argv, "ac:d:f:il:m:p:r:st:w:BCE:F:H:IJ:K:LMN:O:P:R:S:U:V:X:Y:Z:")) != -1)
     switch (c) {
-      case 'b': if (bias) {  /* this file is bias image subtracted from others */
-                  fputs ("Only 1 bias image may be specified\n", stderr);
-                  exit (-2);
-                }
-                {
-                uint16    samples = (uint16) -1;
-                char *biasFn = optarg;
-	        sep = strpbrk(biasFn, ":");
-	        if (sep)
-                  {
-                  *sep = '\0';
-                  bias_image = atoi(sep +1);
-		  }
-                bias = TIFFOpen (biasFn, "r");
-                if (!bias)
-                  exit (-5);
-                if (bias_image) {
-                  if (!TIFFSetDirectory(bias, bias_image))
-                    {
-		    fputs ("Invalid IFD for bias image", stderr);
-                    exit (-7);
-                    }
-		  } 
-                if (TIFFIsTiled (bias)) {
-                  fputs ("Bias image must be organized in strips\n", stderr);
-                  exit (-7);
-                }
-	        TIFFGetField(bias, TIFFTAG_SAMPLESPERPIXEL, &samples);
-                if (samples != 1) {
-                  fputs ("Bias image must be monochrome\n", stderr);
-                  exit (-7);
-                  }
-	        }
-                break;
-      case 'a': mode[0] = 'a'; 	/* append to output */
+      case 'a': mode[0] = 'a';	/* append to output */
 		break;
-      case 'c':	if (!processCompressOptions(optarg)) 	/* compression scheme */
+      case 'c':	if (!processCompressOptions(optarg))	/* compression scheme */
 		   usage();
 		break;
       case 'd':	dirnum = strtoul(optarg, NULL, 0); /* initial directory offset */
@@ -278,7 +401,7 @@ main(int argc, char* argv[])
 		else
 		  usage();
 		break;
-      case 'i':	ignore = TRUE; 		/* ignore errors */
+      case 'i':	ignore = TRUE;		/* ignore errors */
 		break;
       case 'l':	outtiled = TRUE;	 /* tile length */
 		deftilelength = atoi(optarg);
@@ -314,7 +437,7 @@ main(int argc, char* argv[])
       case 'C': *mp++ = 'c'; *mp = '\0';
 		break;
       /* image manipulation routine options */
-      case 'm': /* margins to exclude from selection*/
+      case 'm': /* margins to exclude from selection, uppercase M was already used */
 		/* order of values must be TOP, LEFT, BOTTOM, RIGHT */
 		crop_data.crop_mode |= CROP_MARGINS;
                 for (i = 0, opt_ptr = strtok (optarg, ",:");
@@ -324,6 +447,9 @@ main(int argc, char* argv[])
 		    crop_data.margins[i] = atof(opt_ptr);
                     }
 		break;
+      case 'D':	/* down sample */
+		fprintf (stderr, "Down sampling not yet implemented\n");
+		break;
       case 'E':	/* edge reference */
 		switch (tolower(optarg[0]))
                   {
@@ -338,7 +464,7 @@ main(int argc, char* argv[])
 		  default:  fprintf (stderr, "Edge reference must be top, bottom, left, or right.\n");
 			    usage();
 		  }
-	        break;
+		break;
       case 'F': /* flip eg mirror image or cropped segment, M was already used */
 		crop_data.crop_mode |= CROP_MIRROR;
 		switch (tolower(optarg[0]))
@@ -348,12 +474,24 @@ main(int argc, char* argv[])
                   case  'v': crop_data.mirror = MIRROR_VERT;
                              break;
 		  default:   fprintf (stderr, "Flip mode must be h or v.\n");
-	                     usage();
+			     usage();
 		  }
 		break;
+      case 'H': /* set horizontal resolution to new value */
+		page.hres = atof (optarg);
+                page.mode |= PAGE_MODE_RESOLUTION;
+		break;
       case 'I': /* invert the color space, eg black to white */
 		crop_data.crop_mode |= CROP_INVERT;
 		break;
+      case 'J': /* horizontal margin for sectioned ouput pages */ 
+		page.hmargin = atof(optarg);
+                page.mode |= PAGE_MODE_MARGINS;
+		break;
+      case 'K': /* vertical margin for sectioned ouput pages*/ 
+                page.vmargin = atof(optarg);
+                page.mode |= PAGE_MODE_MARGINS;
+		break;
       case 'N':	/* list of images to process */
                 for (i = 0, opt_ptr = strtok (optarg, ",");
                     ((opt_ptr != NULL) &&  (i < MAX_IMAGES));
@@ -377,10 +515,10 @@ main(int argc, char* argv[])
 			   imagelist[i++] = j;
                            image_count = MAX_IMAGES / 2;
                            break;
-		         }
+			 }
 		       else
                          {
- 			 if (streq(opt_ptr, "last"))
+			 if (streq(opt_ptr, "last"))
 			   imagelist[i++] = MAX_IMAGES;
 			 else  /* single value between commas */
 			   {
@@ -399,11 +537,51 @@ main(int argc, char* argv[])
 			       imagelist[i++] = j;
 			     }
 			   }
-		         }
+			 }
 		      }
 		    }
-                 image_count = i;
-		 break;
+                image_count = i;
+		break;
+      case 'O': /* page orientation */ 
+		switch (tolower(optarg[0]))
+                  {
+		  case  'a': page.orient = ORIENTATION_AUTO;
+                             break;
+		  case  'p': page.orient = ORIENTATION_PORTRAIT;
+                             break;
+		  case  'l': page.orient = ORIENTATION_LANDSCAPE;
+                             break;
+		  default:  fprintf (stderr, 
+                            "Orientation must be portrait, landscape, or auto.\n\n");
+			    usage();
+		  }
+		break;
+      case 'P': /* page size selection */ 
+                if (get_page_geometry (optarg, &page))
+                  {
+		  if (!strcmp(optarg, "list"))
+                    {
+		    fprintf (stderr, "Name            Width   Length (in inches)\n");
+                    for (i = 0; i < MAX_PAPERNAMES - 1; i++)
+                      fprintf (stderr, "%-15.15s %5.2f   %5.2f%s", 
+			       PaperTable[i].name, PaperTable[i].width, 
+                               PaperTable[i].length, i % 2 ? "       " : "\n");
+                    fprintf (stderr, "\n\n");
+		    exit (-1);                   
+                    }
+     
+		  fprintf (stderr, "Invalid papersize %s\n\n", optarg);
+                  fprintf (stderr, "Select one of:\n");
+                  for (i = 0; i < MAX_PAPERNAMES; i++)
+                    fprintf (stderr, "%-15.15s%s", PaperTable[i].name, i % 5 ? "  " : "\n");
+                  fprintf (stderr, "\n\n");
+		  exit (-1);
+		  }
+		else
+                  {
+                  page.mode |= PAGE_MODE_PAPERSIZE;
+		  }
+		break;
       case 'R': /* rotate image or cropped segment */
 		crop_data.crop_mode |= CROP_ROTATE;
 		switch (strtoul(optarg, NULL, 0))
@@ -414,20 +592,57 @@ main(int argc, char* argv[])
                              break;
                   case  270: crop_data.rotation = (uint16)270;
                              break;
-		  default:  fprintf (stderr,
-                    "Rotation must be 90, 180, or 270 degrees clockwise.\n");
+		  default:  fprintf (stderr, 
+                            "Rotation must be 90, 180, or 270 degrees clockwise.\n\n");
 			    usage();
 		  }
 		break;
+      case 'S':	/* subdivide into Cols:Rows sections, eg 3,2 would be 3 across and 2 down */
+		sep = strpbrk(optarg, ",:");
+		if (sep)
+                  {
+                  *sep = '\0';
+                  page.cols = atoi(optarg);
+                  page.rows = atoi(sep +1);
+		  }
+                else
+                  {
+                  page.cols = atoi(optarg);
+                  page.rows = atoi(optarg);
+		  }
+                if ((page.cols * page.rows) > MAX_SECTIONS)
+                  {
+		  fprintf (stderr, 
+                  "Limit of %d subdivisions, ie rows x columns, exceeded\n", MAX_SECTIONS);
+		  exit (-1);
+                  }
+                page.mode |= PAGE_MODE_ROWSCOLS;
+		break;
       case 'U':	/* units for measurements and offsets */
 		if (streq(optarg, "in"))
+                  {
 		  crop_data.res_unit = RESUNIT_INCH;
+		  page.res_unit = RESUNIT_INCH;
+		  }
 		else if (streq(optarg, "cm"))
+		  {
 		  crop_data.res_unit = RESUNIT_CENTIMETER;
+		  page.res_unit = RESUNIT_CENTIMETER;
+		  }
 		else if (streq(optarg, "px"))
-		   crop_data.res_unit = RESUNIT_NONE;
+		  {
+		  crop_data.res_unit = RESUNIT_NONE;
+		  page.res_unit = RESUNIT_NONE;
+		  }
 		else
+                  {
+		  fprintf (stderr, "Illegal unit of measure: %s\n\n", optarg);
 		  usage();
+		  }
+		break;
+      case 'V': /* set vertical resolution to new value */
+		page.vres = atof (optarg);
+                page.mode |= PAGE_MODE_RESOLUTION;
 		break;
       case 'X':	/* selection width */
 		crop_data.crop_mode |= CROP_WIDTH;
@@ -440,15 +655,21 @@ main(int argc, char* argv[])
       case 'Z': /* zones of an image X:Y read as zone X of Y */
 		crop_data.crop_mode |= CROP_ZONES;
 		for (i = 0, opt_ptr = strtok (optarg, ",");
-		     ((opt_ptr != NULL) &&  (i < MAX_ZONES));
-		     (opt_ptr = strtok (NULL, ",")), i++)
-			{
-			    crop_data.zones++;
-			    opt_offset = strchr(opt_ptr, ':');
-			    *opt_offset = '\0';
-			    crop_data.zonelist[i].position = atoi(opt_ptr);
-			    crop_data.zonelist[i].total = atoi(opt_offset + 1);
-			}
+                   ((opt_ptr != NULL) &&  (i < MAX_ZONES));
+                    (opt_ptr = strtok (NULL, ",")), i++)
+                    {
+		    crop_data.zones++;
+		    opt_offset = strchr(opt_ptr, ':');
+                    *opt_offset = '\0';
+                    crop_data.zonelist[i].position = atoi(opt_ptr);
+                    crop_data.zonelist[i].total    = atoi(opt_offset + 1);
+                    }
+                /*  check for remaining elements over MAX_ZONES */
+                if ((opt_ptr != NULL) && (i >= MAX_ZONES))
+                  {
+		  fprintf (stderr, "Zone list exceed limit of %d zones\n", MAX_ZONES);
+		  exit (-1);
+                  }
 		break;
       case '?':	usage();
 		/*NOTREACHED*/
@@ -481,7 +702,7 @@ main(int argc, char* argv[])
     if (dirnum == MAX_IMAGES - 1)
       dirnum = TIFFNumberOfDirectories(in) - 1;
 
-    if (dirnum != 0 && !TIFFSetDirectory(in, dirnum))
+    if (dirnum != 0 && !TIFFSetDirectory(in, (tdir_t)dirnum))
       {
       TIFFError(TIFFFileName(in),"Error, setting subdirectory at %#x", dirnum);
       (void) TIFFClose(out);
@@ -499,38 +720,55 @@ main(int argc, char* argv[])
       tilelength = deftilelength;
       g3opts = defg3opts;
 
+      if (loadImage(in, &image, &read_buff))
+        {
+        TIFFError("main", "Unable to load source image");
+        exit (-1);
+        }
+
       if (crop_data.crop_mode != CROP_NONE)
         {
-	if (getCropOffsets(in, &crop_data))
+	if (getCropOffsets(&image, &crop_data))
           {
           TIFFError("main", "Unable to define crop regions");
           exit (-1);
 	  }
 
-        if (loadImage(in, &read_buff))
+        /* Does this even need to be called if we are not cropping? */
+        if (createCroppedImage(in, &image, &crop_data, &read_buff, &crop_buff))
           {
-          TIFFError("main", "Unable to load source image");
+          TIFFError("main", "Unable to create output image");
           exit (-1);
 	  }
-        if (createCroppedImage(in, &crop_data, &read_buff, &crop_buff))
+        }
+
+      /* Check output qualifiers and build loops to break output image into pages */
+      if (page.mode != PAGE_MODE_NONE)
+        {
+	if (crop_data.crop_mode == CROP_NONE)
+	  sect_src = read_buff;
+        else
+	  sect_src = crop_buff;
+
+        if (computeOutputPixelOffsets(&crop_data, &image, &page, sections))
           {
-          TIFFError("main", "Unable to create output image");
+          TIFFError("main", "Unable to compute output section data");
           exit (-1);
 	  }
-        if (writeCroppedImage(in, out, &crop_data, crop_buff))
+
+	if (writeImageSections(in, out, &image, &page, sections, sect_src, &sect_buff))
           {
-          TIFFError("main", "Unable to write new image");
+          TIFFError("main", "Unable to write image sections");
           exit (-1);
 	  }
         }
       else
-        { 
-        if (!tiffcp(in, out) || !TIFFWriteDirectory(out))
+        if (writeCroppedImage(in, out, &crop_data, crop_buff))
           {
-          TIFFClose(out);
-          return (1);
+          TIFFError("main", "Unable to write new image");
+          exit (-1);
 	  }
-        }
+
       /* No image list specified, just read the next image */
       if (image_count == 0)
         dirnum++;
@@ -543,7 +781,7 @@ main(int argc, char* argv[])
       if (dirnum == MAX_IMAGES - 1)
         dirnum = TIFFNumberOfDirectories(in) - 1;
 
-      if (!TIFFSetDirectory(in, dirnum))
+      if (!TIFFSetDirectory(in, (tdir_t)dirnum))
          break;
       }
     }
@@ -552,11 +790,14 @@ main(int argc, char* argv[])
 
   /* If we did not use the read buffer as the crop buffer */
   if (read_buff)
-        _TIFFfree(read_buff);
+    _TIFFfree(read_buff);
 
   if (crop_buff)
     _TIFFfree(crop_buff);
 
+  if (sect_buff)
+    _TIFFfree(sect_buff);
+
   TIFFClose(out);
 
   return (0);
@@ -635,11 +876,6 @@ char* stuff[] = {
 " -s		write output in strips",
 " -t		write output in tiles",
 " -i		ignore read errors",
-" -b file:#	bias (dark) monochrome image to be subtracted from all others",
-"               Note that bias filename may be of the form filename:#",
-"               where # specifies image file directory for bias filename.",
-" For example:  tiffcrop -b bias.tif:1  esp.tif test.tif",
-"  subtract 2nd image in bias.tif from all images in esp.tiff producing test.tif",
 "",
 " -r #		make each strip have no more than # rows",
 " -w #		set output tile width (pixels)",
@@ -679,12 +915,21 @@ char* stuff[] = {
 " -E t|l|r|b  edge to use as origin for width and length of crop region",
 " -U units    [in, cm, px ] inches, centimeters or pixels",
 "",
-" -M #,#,#,#  margins from edges for selection: top, left, bottom, right separated by commas",
+" -m #,#,#,#  margins from edges f

(Patch may be truncated, please check the link at the top of this post.)